From nobody Thu Apr 2 17:18:54 2026 Received: from mail-pg1-f180.google.com (mail-pg1-f180.google.com [209.85.215.180]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 91FDB344DA9 for ; Wed, 11 Feb 2026 03:31:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780688; cv=none; b=EFdYIO6kF6cxRPT4isDkb1lSnxHb3qf2F6r8si7KnY1Xw5rHfBoYQFW8+fwy1A/rIrpYI3emSMUCSYfnZeK7O6y2b8WkzHjKIngl3oflh0z8D2m6uaiPVxmjvReAsOnqtI+xY0ScvaEhKHQE6hQmMAAukG67o6AIfwiDk92gmEU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780688; c=relaxed/simple; bh=F0QPRi4Cr/kqrQqoZ0RP0Sczcg8ZftafBi6pfegt1k8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Nrm2YqLLBVPkjmjbFlOkQx9sI62E9iTeeMR9vAMhwa6cVZ96csuNbvznATMwM7ziClPKAzBQYed9IaG/nuDoomEj1dfUWdxLseSR0hqfCKT9irOnTHq+Yxcv7nSbrZ7dpxLwbaClXoOlV3u2h9jhWcQZuUPfu7ApmlTY+uuSA5Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=ncGWTr9/; arc=none smtp.client-ip=209.85.215.180 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ncGWTr9/" Received: by mail-pg1-f180.google.com with SMTP id 41be03b00d2f7-c62239decbeso1788006a12.2 for ; Tue, 10 Feb 2026 19:31:26 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780686; x=1771385486; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=rq+I562YFt2ssteRsuZO53DNGRtLr+SzLx+58OWHihw=; b=ncGWTr9/g4DW3/ZN6SPEK/CjpSH21NoRAGaDWjucUPfb8xmgJIs8nkuVkYYCoKX5dD BbKzwicB/tzXiNNCyRG5Z3gPNzTp1XDGtFlMF+qcgNgmjhjKI9/ahe39o/knRd7CYb9f fULFsfkWl+5TaRl0ONmSi87tMo8aAhYuF/LJdsB7lZ6H8nszcX/iZBsgafEW+f+CO3Dh 6xMUCRI0SWJQu4RIGlCFBJipeZAMlMI66PxWPKYZE5r/JLcQuNQS+RKAzGbH4DTeFUl4 d05d6JN5LZf5K1JJxS0RprDM/m0bvrAcCNl2NYS+IT7G7e2LuTq5cytcFTdj9YeUfE0B wr0g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780686; x=1771385486; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=rq+I562YFt2ssteRsuZO53DNGRtLr+SzLx+58OWHihw=; b=uzUdeH9FZ6GDVsrCrn1D1y7T3KsVsaDkYYJFWAjzvGiRMy6B/BwuF6h+QgRvy3GnOT 63FP83rzqL3u1rEAq1dkFK/eG/Ls/JIJ6NBWYEX+AGlX/Z+CEOcM9XPsTOKqhGGCuMft 1qIC8ZGpV/2iREgLVeEfSyiEI2zAlRiPYHRzsUcz80qWgJ66kCU4vvfqBtOyFppOKi/3 GVFzGBNuQlKIU8/9+T5QkGwbXjn4OHj61V27Uyy8FLqJqKmhd5dWOLCt/PANvPjeY4lP QM60YMJ2Wg21keAGdqIWNSI4FL0PcvUg/rQdSu1xvMF/Y0/+RIy76fCo0pCUg7I8nDCk txQQ== X-Forwarded-Encrypted: i=1; AJvYcCUZKS933Cu+7EZKaT6M7UMJL5k1/QGikm5dn1/cwjItKq0e84yyIcIDKuPd5L4GOYrwVT/KtGiQcRLZWCg=@vger.kernel.org X-Gm-Message-State: AOJu0YxC7KkvZI0kixRre3RiZHJ2xPi7iHIbzkRuk8HAeCxpnqDoUGkD CecF13t4bm3EfBt5gXVB8cyABmV59H1oxpE+A9gd++rsMaFNXO3HCHaV X-Gm-Gg: AZuq6aK4Qzp24BxKSG5dTzsYh3ruW5fHqgGIqPfNCx2jBh5jU4/txEp2Y9YuswrSmhv J5UsO73L2Pq6hp00aWlG/hjb/Ts5zT5d24do6pPokwk+X4QziDSh/NMQAlZrO+zy1GYlQHsc82e B4ntQe2eYTMRVYwprFvMCXpvOqCk5E6/lP/ypIJ3TGRGZrWeB/236kauzey8Bj/oe6h8LDF+eSk KIbqD5eXIv5pSrD3iwj5xI/+Akl0LDFm+/HNEME4l33OX9ziSWodthwnoIDLUZzl++/KgVOEmYy UCG6IPVV8uuWnbzFhNG11vOOYO8lGsiXZAJHWtP0lPvZsvJ/+22GQtjoa48YtAQpR+hTGyFXouI 31TEYO0qNQnYq7qc0R/OAGfpy7OtpjYGSpmSCGMs8JYzEubU10dsfJhFBCV/8efSvZMUVAyKiiH U8ZCq5RfmVwOTrrWYccZl1hOvGUmH4jx0yFPdcjYHZRA== X-Received: by 2002:a05:6a20:258d:b0:35f:b96d:af11 with SMTP id adf61e73a8af0-3942e384f48mr1325597637.5.1770780685920; Tue, 10 Feb 2026 19:31:25 -0800 (PST) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-c6e197d63c9sm464856a12.20.2026.02.10.19.31.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:31:25 -0800 (PST) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: bhelgaas@google.com, lukas@wunner.de, rust-for-linux@vger.kernel.org, akpm@linux-foundation.org, linux-pci@vger.kernel.org, Jonathan.Cameron@huawei.com, linux-cxl@vger.kernel.org, linux-kernel@vger.kernel.org Cc: alex.gaynor@gmail.com, benno.lossin@proton.me, boqun.feng@gmail.com, a.hindborg@kernel.org, gary@garyguo.net, bjorn3_gh@protonmail.com, tmgross@umich.edu, alistair23@gmail.com, ojeda@kernel.org, wilfred.mallawa@wdc.com, aliceryhl@google.com, Alistair Francis Subject: [RFC v3 14/27] lib: rspdm: Support SPDM negotiate_algorithms Date: Wed, 11 Feb 2026 13:29:21 +1000 Message-ID: <20260211032935.2705841-15-alistair.francis@wdc.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260211032935.2705841-1-alistair.francis@wdc.com> References: <20260211032935.2705841-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alistair Francis Support the NEGOTIATE_ALGORITHMS SPDM command. Signed-off-by: Alistair Francis --- lib/rspdm/consts.rs | 53 ++++++++++ lib/rspdm/lib.rs | 16 ++- lib/rspdm/state.rs | 214 ++++++++++++++++++++++++++++++++++++++++- lib/rspdm/validator.rs | 115 +++++++++++++++++++++- 4 files changed, 391 insertions(+), 7 deletions(-) diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs index a6a66e2af1b5..e8a05fd4299b 100644 --- a/lib/rspdm/consts.rs +++ b/lib/rspdm/consts.rs @@ -143,6 +143,59 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> cor= e::fmt::Result { =20 pub(crate) const SPDM_CERT_CAP: u32 =3D 1 << 1; pub(crate) const SPDM_CHAL_CAP: u32 =3D 1 << 2; +pub(crate) const SPDM_MEAS_CAP_MASK: u32 =3D 3 << 3; +pub(crate) const SPDM_KEY_EX_CAP: u32 =3D 1 << 9; =20 pub(crate) const SPDM_REQ_CAPS: u32 =3D SPDM_CERT_CAP | SPDM_CHAL_CAP; pub(crate) const SPDM_RSP_MIN_CAPS: u32 =3D SPDM_CERT_CAP | SPDM_CHAL_CAP; + +pub(crate) const SPDM_NEGOTIATE_ALGS: u8 =3D 0xe3; + +pub(crate) const SPDM_MEAS_SPEC_DMTF: u8 =3D 1 << 0; + +pub(crate) const SPDM_ASYM_RSASSA_2048: u32 =3D 1 << 0; +pub(crate) const _SPDM_ASYM_RSAPSS_2048: u32 =3D 1 << 1; +pub(crate) const SPDM_ASYM_RSASSA_3072: u32 =3D 1 << 2; +pub(crate) const _SPDM_ASYM_RSAPSS_3072: u32 =3D 1 << 3; +pub(crate) const SPDM_ASYM_ECDSA_ECC_NIST_P256: u32 =3D 1 << 4; +pub(crate) const SPDM_ASYM_RSASSA_4096: u32 =3D 1 << 5; +pub(crate) const _SPDM_ASYM_RSAPSS_4096: u32 =3D 1 << 6; +pub(crate) const SPDM_ASYM_ECDSA_ECC_NIST_P384: u32 =3D 1 << 7; +pub(crate) const SPDM_ASYM_ECDSA_ECC_NIST_P521: u32 =3D 1 << 8; +pub(crate) const _SPDM_ASYM_SM2_ECC_SM2_P256: u32 =3D 1 << 9; +pub(crate) const _SPDM_ASYM_EDDSA_ED25519: u32 =3D 1 << 10; +pub(crate) const _SPDM_ASYM_EDDSA_ED448: u32 =3D 1 << 11; + +pub(crate) const SPDM_HASH_SHA_256: u32 =3D 1 << 0; +pub(crate) const SPDM_HASH_SHA_384: u32 =3D 1 << 1; +pub(crate) const SPDM_HASH_SHA_512: u32 =3D 1 << 2; + +#[cfg(CONFIG_CRYPTO_RSA)] +pub(crate) const SPDM_ASYM_RSA: u32 =3D + SPDM_ASYM_RSASSA_2048 | SPDM_ASYM_RSASSA_3072 | SPDM_ASYM_RSASSA_4096; +#[cfg(not(CONFIG_CRYPTO_RSA))] +pub(crate) const SPDM_ASYM_RSA: u32 =3D 0; + +#[cfg(CONFIG_CRYPTO_ECDSA)] +pub(crate) const SPDM_ASYM_ECDSA: u32 =3D + SPDM_ASYM_ECDSA_ECC_NIST_P256 | SPDM_ASYM_ECDSA_ECC_NIST_P384 | SPDM_A= SYM_ECDSA_ECC_NIST_P521; +#[cfg(not(CONFIG_CRYPTO_ECDSA))] +pub(crate) const SPDM_ASYM_ECDSA: u32 =3D 0; + +#[cfg(CONFIG_CRYPTO_SHA256)] +pub(crate) const SPDM_HASH_SHA2_256: u32 =3D SPDM_HASH_SHA_256; +#[cfg(not(CONFIG_CRYPTO_SHA256))] +pub(crate) const SPDM_HASH_SHA2_256: u32 =3D 0; + +#[cfg(CONFIG_CRYPTO_SHA512)] +pub(crate) const SPDM_HASH_SHA2_384_512: u32 =3D SPDM_HASH_SHA_384 | SPDM_= HASH_SHA_512; +#[cfg(not(CONFIG_CRYPTO_SHA512))] +pub(crate) const SPDM_HASH_SHA2_384_512: u32 =3D 0; + +pub(crate) const SPDM_ASYM_ALGOS: u32 =3D SPDM_ASYM_RSA | SPDM_ASYM_ECDSA; +pub(crate) const SPDM_HASH_ALGOS: u32 =3D SPDM_HASH_SHA2_256 | SPDM_HASH_S= HA2_384_512; + +/* Maximum number of ReqAlgStructs sent by this implementation */ +// pub(crate) const SPDM_MAX_REQ_ALG_STRUCT: usize =3D 4; + +pub(crate) const SPDM_OPAQUE_DATA_FMT_GENERAL: u8 =3D 1 << 1; diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs index 5f6653ada59d..33f706a55e09 100644 --- a/lib/rspdm/lib.rs +++ b/lib/rspdm/lib.rs @@ -117,6 +117,10 @@ return e.to_errno() as c_int; } =20 + if let Err(e) =3D state.negotiate_algs() { + return e.to_errno() as c_int; + } + 0 } =20 @@ -124,4 +128,14 @@ /// /// @spdm_state: SPDM session state #[no_mangle] -pub unsafe extern "C" fn spdm_destroy(_state: &'static mut SpdmState) {} +pub unsafe extern "C" fn spdm_destroy(state: &'static mut SpdmState) { + if let Some(desc) =3D &mut state.desc { + unsafe { + bindings::kfree(*desc as *mut _ as *mut c_void); + } + } + + unsafe { + bindings::crypto_free_shash(state.shash); + } +} diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs index 0efad7f341cd..d0b10f27cd9c 100644 --- a/lib/rspdm/state.rs +++ b/lib/rspdm/state.rs @@ -12,16 +12,22 @@ use kernel::prelude::*; use kernel::{ bindings, - error::{code::EINVAL, to_result, Error}, + error::{code::EINVAL, from_err_ptr, to_result, Error}, + str::CStr, validate::Untrusted, }; =20 use crate::consts::{ - SpdmErrorCode, SPDM_ERROR, SPDM_GET_VERSION_LEN, SPDM_MAX_VER, SPDM_MI= N_DATA_TRANSFER_SIZE, - SPDM_MIN_VER, SPDM_REQ, SPDM_RSP_MIN_CAPS, SPDM_VER_10, SPDM_VER_11, S= PDM_VER_12, + SpdmErrorCode, SPDM_ASYM_ALGOS, SPDM_ASYM_ECDSA_ECC_NIST_P256, SPDM_AS= YM_ECDSA_ECC_NIST_P384, + SPDM_ASYM_ECDSA_ECC_NIST_P521, SPDM_ASYM_RSASSA_2048, SPDM_ASYM_RSASSA= _3072, + SPDM_ASYM_RSASSA_4096, SPDM_ERROR, SPDM_GET_VERSION_LEN, SPDM_HASH_ALG= OS, SPDM_HASH_SHA_256, + SPDM_HASH_SHA_384, SPDM_HASH_SHA_512, SPDM_KEY_EX_CAP, SPDM_MAX_VER, S= PDM_MEAS_CAP_MASK, + SPDM_MEAS_SPEC_DMTF, SPDM_MIN_DATA_TRANSFER_SIZE, SPDM_MIN_VER, SPDM_O= PAQUE_DATA_FMT_GENERAL, + SPDM_REQ, SPDM_RSP_MIN_CAPS, SPDM_VER_10, SPDM_VER_11, SPDM_VER_12, }; use crate::validator::{ - GetCapabilitiesReq, GetCapabilitiesRsp, GetVersionReq, GetVersionRsp, = SpdmErrorRsp, SpdmHeader, + GetCapabilitiesReq, GetCapabilitiesRsp, GetVersionReq, GetVersionRsp, = NegotiateAlgsReq, + NegotiateAlgsRsp, RegAlg, SpdmErrorRsp, SpdmHeader, }; =20 /// The current SPDM session state for a device. Based on the @@ -40,6 +46,28 @@ /// Negotiated during GET_VERSION exchange. /// @rsp_caps: Cached capabilities of responder. /// Received during GET_CAPABILITIES exchange. +/// @base_asym_alg: Asymmetric key algorithm for signature verification of +/// CHALLENGE_AUTH and MEASUREMENTS messages. +/// Selected by responder during NEGOTIATE_ALGORITHMS exchange. +/// @base_hash_alg: Hash algorithm for signature verification of +/// CHALLENGE_AUTH and MEASUREMENTS messages. +/// Selected by responder during NEGOTIATE_ALGORITHMS exchange. +/// @meas_hash_alg: Hash algorithm for measurement blocks. +/// Selected by responder during NEGOTIATE_ALGORITHMS exchange. +/// @base_asym_enc: Human-readable name of @base_asym_alg's signature enco= ding. +/// Passed to crypto subsystem when calling verify_signature(). +/// @sig_len: Signature length of @base_asym_alg (in bytes). +/// S or SigLen in SPDM specification. +/// @base_hash_alg_name: Human-readable name of @base_hash_alg. +/// Passed to crypto subsystem when calling crypto_alloc_shash() and +/// verify_signature(). +/// @base_hash_alg_name: Human-readable name of @base_hash_alg. +/// Passed to crypto subsystem when calling crypto_alloc_shash() and +/// verify_signature(). +/// @shash: Synchronous hash handle for @base_hash_alg computation. +/// @desc: Synchronous hash context for @base_hash_alg computation. +/// @hash_len: Hash length of @base_hash_alg (in bytes). +/// H in SPDM specification. #[expect(dead_code)] pub struct SpdmState { pub(crate) dev: *mut bindings::device, @@ -52,6 +80,19 @@ pub struct SpdmState { // Negotiated state pub(crate) version: u8, pub(crate) rsp_caps: u32, + pub(crate) base_asym_alg: u32, + pub(crate) base_hash_alg: u32, + pub(crate) meas_hash_alg: u32, + + /* Signature algorithm */ + base_asym_enc: &'static CStr, + sig_len: usize, + + /* Hash algorithm */ + base_hash_alg_name: &'static CStr, + pub(crate) shash: *mut bindings::crypto_shash, + pub(crate) desc: Option<&'static mut bindings::shash_desc>, + pub(crate) hash_len: usize, } =20 impl SpdmState { @@ -72,6 +113,15 @@ pub(crate) fn new( validate, version: SPDM_MIN_VER, rsp_caps: 0, + base_asym_alg: 0, + base_hash_alg: 0, + meas_hash_alg: 0, + base_asym_enc: unsafe { CStr::from_bytes_with_nul_unchecked(b"= \0") }, + sig_len: 0, + base_hash_alg_name: unsafe { CStr::from_bytes_with_nul_uncheck= ed(b"\0") }, + shash: core::ptr::null_mut(), + desc: None, + hash_len: 0, } } =20 @@ -333,4 +383,160 @@ pub(crate) fn get_capabilities(&mut self) -> Result<(= ), Error> { =20 Ok(()) } + + fn update_response_algs(&mut self) -> Result<(), Error> { + match self.base_asym_alg { + SPDM_ASYM_RSASSA_2048 =3D> { + self.sig_len =3D 256; + self.base_asym_enc =3D CStr::from_bytes_with_nul(b"pkcs1\0= ")?; + } + SPDM_ASYM_RSASSA_3072 =3D> { + self.sig_len =3D 384; + self.base_asym_enc =3D CStr::from_bytes_with_nul(b"pkcs1\0= ")?; + } + SPDM_ASYM_RSASSA_4096 =3D> { + self.sig_len =3D 512; + self.base_asym_enc =3D CStr::from_bytes_with_nul(b"pkcs1\0= ")?; + } + SPDM_ASYM_ECDSA_ECC_NIST_P256 =3D> { + self.sig_len =3D 64; + self.base_asym_enc =3D CStr::from_bytes_with_nul(b"p1363\0= ")?; + } + SPDM_ASYM_ECDSA_ECC_NIST_P384 =3D> { + self.sig_len =3D 96; + self.base_asym_enc =3D CStr::from_bytes_with_nul(b"p1363\0= ")?; + } + SPDM_ASYM_ECDSA_ECC_NIST_P521 =3D> { + self.sig_len =3D 132; + self.base_asym_enc =3D CStr::from_bytes_with_nul(b"p1363\0= ")?; + } + _ =3D> { + pr_err!("Unknown asym algorithm\n"); + return Err(EINVAL); + } + } + + match self.base_hash_alg { + SPDM_HASH_SHA_256 =3D> { + self.base_hash_alg_name =3D CStr::from_bytes_with_nul(b"sh= a256\0")?; + } + SPDM_HASH_SHA_384 =3D> { + self.base_hash_alg_name =3D CStr::from_bytes_with_nul(b"sh= a384\0")?; + } + SPDM_HASH_SHA_512 =3D> { + self.base_hash_alg_name =3D CStr::from_bytes_with_nul(b"sh= a512\0")?; + } + _ =3D> { + pr_err!("Unknown hash algorithm\n"); + return Err(EINVAL); + } + } + + /* + * shash and desc allocations are reused for subsequent measurement + * retrieval, hence are not freed until spdm_reset(). + */ + self.shash =3D + unsafe { bindings::crypto_alloc_shash(self.base_hash_alg_name.= as_char_ptr(), 0, 0) }; + from_err_ptr(self.shash)?; + + let desc_len =3D core::mem::size_of::() + + unsafe { bindings::crypto_shash_descsize(self.shash) } as us= ize; + + let mut desc_vec: KVec =3D KVec::with_capacity(desc_len, GFP_K= ERNEL)?; + // SAFETY: `desc_vec` is `desc_len` long + let desc_buf =3D unsafe { from_raw_parts_mut(desc_vec.as_mut_ptr()= , desc_len) }; + + let desc =3D unsafe { + core::mem::transmute::<*mut c_void, &mut bindings::shash_desc>( + desc_buf.as_mut_ptr() as *mut c_void + ) + }; + desc.tfm =3D self.shash; + + self.desc =3D Some(desc); + + /* Used frequently to compute offsets, so cache H */ + self.hash_len =3D unsafe { bindings::crypto_shash_digestsize(self.= shash) as usize }; + + if let Some(desc) =3D &mut self.desc { + unsafe { to_result(bindings::crypto_shash_init(*desc)) } + } else { + Err(ENOMEM) + } + } + + pub(crate) fn negotiate_algs(&mut self) -> Result<(), Error> { + let mut request =3D NegotiateAlgsReq::default(); + request.version =3D self.version; + + if self.version >=3D SPDM_VER_12 && (self.rsp_caps & SPDM_KEY_EX_C= AP) =3D=3D SPDM_KEY_EX_CAP { + request.other_params_support =3D SPDM_OPAQUE_DATA_FMT_GENERAL; + } + + // TODO support more algs + let reg_alg_entries =3D 0; + + let req_sz =3D core::mem::size_of::() + + core::mem::size_of::() * reg_alg_entries; + let rsp_sz =3D core::mem::size_of::() + + core::mem::size_of::() * reg_alg_entries; + + request.length =3D req_sz as u16; + request.param1 =3D reg_alg_entries as u8; + + // SAFETY: `request` is repr(C) and packed, so we can convert it t= o a slice + let request_buf =3D unsafe { from_raw_parts_mut(&mut request as *m= ut _ as *mut u8, req_sz) }; + + let mut response_vec: KVec =3D KVec::with_capacity(rsp_sz, GFP= _KERNEL)?; + // SAFETY: `request` is repr(C) and packed, so we can convert it t= o a slice + let response_buf =3D unsafe { from_raw_parts_mut(response_vec.as_m= ut_ptr(), rsp_sz) }; + + let rc =3D self.spdm_exchange(request_buf, response_buf)?; + + if rc < (rsp_sz as i32) { + pr_err!("Truncated capabilities response\n"); + to_result(-(bindings::EIO as i32))?; + } + + // SAFETY: `rc` is the length of data read, which will be smaller + // then the capacity of the vector + unsafe { response_vec.inc_len(rc as usize) }; + + let response: &mut NegotiateAlgsRsp =3D + Untrusted::new_mut(&mut response_vec).validate_mut()?; + + self.base_asym_alg =3D response.base_asym_sel; + self.base_hash_alg =3D response.base_hash_sel; + self.meas_hash_alg =3D response.measurement_hash_algo; + + if self.base_asym_alg & SPDM_ASYM_ALGOS =3D=3D 0 || self.base_hash= _alg & SPDM_HASH_ALGOS =3D=3D 0 { + pr_err!("No common supported algorithms\n"); + to_result(-(bindings::EPROTO as i32))?; + } + + // /* Responder shall select exactly 1 alg (SPDM 1.0.0 table 14) */ + if self.base_asym_alg.count_ones() !=3D 1 + || self.base_hash_alg.count_ones() !=3D 1 + || response.ext_asym_sel_count !=3D 0 + || response.ext_hash_sel_count !=3D 0 + || response.param1 > request.param1 + || response.other_params_sel !=3D request.other_params_support + { + pr_err!("Malformed algorithms response\n"); + to_result(-(bindings::EPROTO as i32))?; + } + + if self.rsp_caps & SPDM_MEAS_CAP_MASK =3D=3D SPDM_MEAS_CAP_MASK + && (self.meas_hash_alg.count_ones() !=3D 1 + || response.measurement_specification_sel !=3D SPDM_MEAS_S= PEC_DMTF) + { + pr_err!("Malformed algorithms response\n"); + to_result(-(bindings::EPROTO as i32))?; + } + + self.update_response_algs()?; + + Ok(()) + } } diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs index cd792c46767a..036a077c71c3 100644 --- a/lib/rspdm/validator.rs +++ b/lib/rspdm/validator.rs @@ -7,7 +7,7 @@ //! Rust implementation of the DMTF Security Protocol and Data Model (SPDM) //! =20 -use crate::bindings::{__IncompleteArrayField, __le16}; +use crate::bindings::{__IncompleteArrayField, __le16, __le32}; use crate::consts::SpdmErrorCode; use core::mem; use kernel::prelude::*; @@ -16,7 +16,10 @@ validate::{Unvalidated, Validate}, }; =20 -use crate::consts::{SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, SPDM_GET_VERSI= ON, SPDM_REQ_CAPS}; +use crate::consts::{ + SPDM_ASYM_ALGOS, SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, SPDM_GET_VERS= ION, SPDM_HASH_ALGOS, + SPDM_MEAS_SPEC_DMTF, SPDM_NEGOTIATE_ALGS, SPDM_REQ_CAPS, +}; =20 #[repr(C, packed)] pub(crate) struct SpdmHeader { @@ -205,3 +208,111 @@ fn validate(unvalidated: &mut Unvalidated>) = -> Result Ok(rsp) } } + +#[repr(C, packed)] +pub(crate) struct RegAlg { + pub(crate) alg_type: u8, + pub(crate) alg_count: u8, + pub(crate) alg_supported: u16, + pub(crate) alg_external: __IncompleteArrayField<__le32>, +} + +#[repr(C, packed)] +pub(crate) struct NegotiateAlgsReq { + pub(crate) version: u8, + pub(crate) code: u8, + pub(crate) param1: u8, + pub(crate) param2: u8, + + pub(crate) length: u16, + pub(crate) measurement_specification: u8, + pub(crate) other_params_support: u8, + + pub(crate) base_asym_algo: u32, + pub(crate) base_hash_algo: u32, + + reserved1: [u8; 12], + + pub(crate) ext_asym_count: u8, + pub(crate) ext_hash_count: u8, + reserved2: u8, + pub(crate) mel_specification: u8, + + pub(crate) ext_asym: __IncompleteArrayField<__le32>, + pub(crate) ext_hash: __IncompleteArrayField<__le32>, + pub(crate) req_alg_struct: __IncompleteArrayField, +} + +impl Default for NegotiateAlgsReq { + fn default() -> Self { + NegotiateAlgsReq { + version: 0, + code: SPDM_NEGOTIATE_ALGS, + param1: 0, + param2: 0, + length: 0, + measurement_specification: SPDM_MEAS_SPEC_DMTF, + other_params_support: 0, + base_asym_algo: SPDM_ASYM_ALGOS.to_le(), + base_hash_algo: SPDM_HASH_ALGOS.to_le(), + reserved1: [0u8; 12], + ext_asym_count: 0, + ext_hash_count: 0, + reserved2: 0, + mel_specification: 0, + ext_asym: __IncompleteArrayField::new(), + ext_hash: __IncompleteArrayField::new(), + req_alg_struct: __IncompleteArrayField::new(), + } + } +} + +#[repr(C, packed)] +pub(crate) struct NegotiateAlgsRsp { + pub(crate) version: u8, + pub(crate) code: u8, + pub(crate) param1: u8, + pub(crate) param2: u8, + + pub(crate) length: u16, + pub(crate) measurement_specification_sel: u8, + pub(crate) other_params_sel: u8, + + pub(crate) measurement_hash_algo: u32, + pub(crate) base_asym_sel: u32, + pub(crate) base_hash_sel: u32, + + reserved1: [u8; 11], + + pub(crate) mel_specification_sel: u8, + pub(crate) ext_asym_sel_count: u8, + pub(crate) ext_hash_sel_count: u8, + reserved2: [u8; 2], + + pub(crate) ext_asym: __IncompleteArrayField<__le32>, + pub(crate) ext_hash: __IncompleteArrayField<__le32>, + pub(crate) req_alg_struct: __IncompleteArrayField, +} + +impl Validate<&mut Unvalidated>> for &mut NegotiateAlgsRsp { + type Err =3D Error; + + fn validate(unvalidated: &mut Unvalidated>) -> Result { + let raw =3D unvalidated.raw_mut(); + if raw.len() < mem::size_of::() { + return Err(EINVAL); + } + + let ptr =3D raw.as_mut_ptr(); + // CAST: `NegotiateAlgsRsp` only contains integers and has `repr(C= )`. + let ptr =3D ptr.cast::(); + // SAFETY: `ptr` came from a reference and the cast above is valid. + let rsp: &mut NegotiateAlgsRsp =3D unsafe { &mut *ptr }; + + rsp.base_asym_sel =3D rsp.base_asym_sel.to_le(); + rsp.base_hash_sel =3D rsp.base_hash_sel.to_le(); + rsp.measurement_hash_algo =3D rsp.measurement_hash_algo.to_le(); + + Ok(rsp) + } +} --=20 2.52.0