From nobody Thu Apr 2 17:17:47 2026 Received: from mail-pf1-f174.google.com (mail-pf1-f174.google.com [209.85.210.174]) (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 22312347FE6 for ; Wed, 11 Feb 2026 03:32:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780744; cv=none; b=Dpva0mVH6a26/BHivQD/PKW6QWZLpL/lVJ3gudKnFmT+s8khAAXZ+lDYqHVeth6Ly2aWUJHye5tvx4IqPp9jUB71MIgie4tT7YIT50GI/UlxaXOqIlcbqBCqCZM9NBC5FHVAa7VWe8CJL45/YGu30mFOb6p3KyFqbiAdQ8rVCxM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780744; c=relaxed/simple; bh=refyXz8stY99dvGr7uUo6qpIJdnH8vinCei9HfJ4AoE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=YZ4gKt5Um01lOgoKrSwkGpcvGzeKTfn1Jh0ywctefM3lxtHUNNuAEXUuLi/cQxMLQL4hE6nHg+p5aIyvSuBjE39uv/B7tQxs/Cq/iDn3QVg95+zNKlwGnAAoM1clGZ3jB9Qnwoamt+1v3GLxs2RNI4fq1IZFdrc03bVSFGMcZGk= 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=dZEC5PEg; arc=none smtp.client-ip=209.85.210.174 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="dZEC5PEg" Received: by mail-pf1-f174.google.com with SMTP id d2e1a72fcca58-82318702afbso4411952b3a.1 for ; Tue, 10 Feb 2026 19:32:22 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780742; x=1771385542; 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=F7X8Upc/bbFH0ZPwDB1OWkHogmxT+7SWa7vhKFXLS+g=; b=dZEC5PEgV4EsbWOQS9wGyG6Gr0uckJ/I0CIKcYjNjEjKMz3GnjHyYVk1lwrrEgQCF+ wDNZaVJi9CBqvtzfJXd+DhuVM0Ys9bA4Y2uwei9FgFz7ovR+pQUd88ANl2Ihy3ZHJWM2 Ato6DizSXCMGTuAmq3ZqWG79Ek8MfL6XYWBwGHaoz/g3UzbnPvFGeL+pg5HY5kOOLxqt 8fzyUikw01eYPPpY4uQQ1jZ5JwjScygP/vBuMKfF3NDiSjTXhPW0qhmtlF6ywJQo186e aYyNC3fX124tbWKd9jRxZnI2WzUYsSa/P7iM9bxPWCfZs9oAF+DlLpElqDCckdc4zSgM ir/Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780742; x=1771385542; 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=F7X8Upc/bbFH0ZPwDB1OWkHogmxT+7SWa7vhKFXLS+g=; b=VwbMuQiAn9xNjNsADvjx0ySi7GPPxT77CZveES2DzkDFo7mPTJ4RNkvDC6onVBtoEk 8LGNBkTTXgg3plvejMuS6rOENss5w+a9Rw7Me8DJd39yUUA+MxXKKI+3B4sFvaPyHTjS KsRmcF8EwRPtpyAnUqoc3J4z4s4FjEmRfBJES4hVbFxT8ORKU7I8uIX2qhV2hycZzreY 3rVGdIcanRsD9NZZPRzDxCiUDVf51/krl0x/GdDLyGv/ggGjZ221lRzPruSbUB7EuKoc TLS6j7j+2u3i7pFPfalt0gj5h3bj+xGofCX6mggIxsYUhDD4cEYUbEbJoZT79/uSYCkL GTgA== X-Forwarded-Encrypted: i=1; AJvYcCVmcAvHmgtLihHYHUzoSjIuXF5kEIqmgvXiZm26AJ4zDVDhwtjgCHVO2bgk3zg3afO3cIgbLIqvhcgDn08=@vger.kernel.org X-Gm-Message-State: AOJu0Yw+NzdNztNdG/kajTCjHu2HNOGXKPFU/DI+d6NtheN5+/m4LLtb V2ccz9tuZJ+pr1VP+ojptRj2VjdBSLd6O9ynd2EEvMovPfNaDIJFvgrk X-Gm-Gg: AZuq6aL1EUUPpDmvbrXBZvJvCRdUjK/faBXk6IjOOAL0+riRUF5crINGYg6/ThqWTxf JrF1wtk+H4qRZAQ7Do5yPM9Wwt/iCI2uHmOb/wtWV2HAHP7h8/Jzom+XDmmezygg+Spsc8cGqqN ao4OFKtCoWMvsr87D9kxKHtx7t8Rvfm7g1WkdY4PvTU7KOBxG1qVaLYRHMT+SFGwFwe1OBxaOWn sSaesVmiKJBJjyBOcbuoh4JijxiOPsQRffeZ7jIl8ARq+Ek/t3NE4elYeKjJun9F140AyrW+y0c YHLFIVwAR/JbvyyGHElY1vtoFm9qZarw0BJoOCYRXUaTp3RdZPAZL6elRejwBGJGYjPk9MseMgm lJgw42Ttba8/oqxwKyfqeNcIwbyWhhmLGZWOGQBdGnCMXYlIiRj4OOdp4f8Jl5i6EZbjevbAr/c aHsPndt+HzpLByl7a1ANtD8TpwHAMUQh6H6cfUssTfYw== X-Received: by 2002:a05:6a21:1fc5:b0:342:a7cd:9211 with SMTP id adf61e73a8af0-3942e3b5d3emr1317494637.34.1770780742090; Tue, 10 Feb 2026 19:32:22 -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.32.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:32:21 -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 22/27] lib: rspdm: Support SPDM certificate validation Date: Wed, 11 Feb 2026 13:29:29 +1000 Message-ID: <20260211032935.2705841-23-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 validating the SPDM certificate chain. Signed-off-by: Alistair Francis --- lib/rspdm/lib.rs | 17 ++++++ lib/rspdm/state.rs | 93 ++++++++++++++++++++++++++++++++- rust/bindings/bindings_helper.h | 2 + 3 files changed, 111 insertions(+), 1 deletion(-) diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs index b065b3f70356..c016306116a3 100644 --- a/lib/rspdm/lib.rs +++ b/lib/rspdm/lib.rs @@ -136,6 +136,17 @@ provisioned_slots &=3D !(1 << slot); } =20 + let mut provisioned_slots =3D state.provisioned_slots; + while (provisioned_slots as usize) > 0 { + let slot =3D provisioned_slots.trailing_zeros() as u8; + + if let Err(e) =3D state.validate_cert_chain(slot) { + return e.to_errno() as c_int; + } + + provisioned_slots &=3D !(1 << slot); + } + 0 } =20 @@ -144,6 +155,12 @@ /// @spdm_state: SPDM session state #[no_mangle] pub unsafe extern "C" fn spdm_destroy(state: &'static mut SpdmState) { + if let Some(leaf_key) =3D &mut state.leaf_key { + unsafe { + bindings::public_key_free(*leaf_key); + } + } + if let Some(desc) =3D &mut state.desc { unsafe { bindings::kfree(*desc as *mut _ as *mut c_void); diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs index 1e5656144611..728b920beace 100644 --- a/lib/rspdm/state.rs +++ b/lib/rspdm/state.rs @@ -76,7 +76,8 @@ /// @slot: Certificate chain in each of the 8 slots. NULL pointer if a sl= ot is /// not populated. Prefixed by the 4 + H header per SPDM 1.0.0 table 15. /// @slot_sz: Certificate chain size (in bytes). -#[expect(dead_code)] +/// @leaf_key: Public key portion of leaf certificate against which to che= ck +/// responder's signatures. pub struct SpdmState { pub(crate) dev: *mut bindings::device, pub(crate) transport: bindings::spdm_transport, @@ -106,6 +107,7 @@ pub struct SpdmState { =20 // Certificates pub(crate) certs: [KVec; SPDM_SLOTS], + pub(crate) leaf_key: Option<*mut bindings::public_key>, } =20 #[repr(C, packed)] @@ -146,6 +148,7 @@ pub(crate) fn new( desc: None, hash_len: 0, certs: [const { KVec::new() }; SPDM_SLOTS], + leaf_key: None, } } =20 @@ -743,4 +746,92 @@ pub(crate) fn get_certificate(&mut self, slot: u8) -> = Result<(), Error> { =20 Ok(()) } + + pub(crate) fn validate_cert_chain(&mut self, slot: u8) -> Result<(), E= rror> { + let cert_chain_buf =3D &self.certs[slot as usize]; + let cert_chain_len =3D cert_chain_buf.len(); + let header_len =3D 4 + self.hash_len; + + let mut offset =3D header_len; + let mut prev_cert: Option<*mut bindings::x509_certificate> =3D Non= e; + + while offset < cert_chain_len { + let cert_len =3D unsafe { + bindings::x509_get_certificate_length( + &cert_chain_buf[offset..] as *const _ as *const u8, + cert_chain_len - offset, + ) + }; + + if cert_len < 0 { + pr_err!("Invalid certificate length\n"); + to_result(cert_len as i32)?; + } + + let _is_leaf_cert =3D if offset + cert_len as usize =3D=3D cer= t_chain_len { + true + } else { + false + }; + + let cert_ptr =3D unsafe { + from_err_ptr(bindings::x509_cert_parse( + &cert_chain_buf[offset..] as *const _ as *const c_void, + cert_len as usize, + ))? + }; + let cert =3D unsafe { *cert_ptr }; + + if cert.unsupported_sig || cert.blacklisted { + to_result(-(bindings::EKEYREJECTED as i32))?; + } + + if let Some(prev) =3D prev_cert { + // Check against previous certificate + let rc =3D unsafe { bindings::public_key_verify_signature(= (*prev).pub_, cert.sig) }; + + if rc < 0 { + pr_err!("Signature validation error\n"); + to_result(rc)?; + } + } else { + // Check aginst root keyring + let key =3D unsafe { + from_err_ptr(bindings::find_asymmetric_key( + self.keyring, + (*cert.sig).auth_ids[0], + (*cert.sig).auth_ids[1], + (*cert.sig).auth_ids[2], + false, + ))? + }; + + let rc =3D unsafe { bindings::verify_signature(key, cert.s= ig) }; + unsafe { bindings::key_put(key) }; + + if rc < 0 { + pr_err!("Root signature validation error\n"); + to_result(rc)?; + } + } + + if let Some(prev) =3D prev_cert { + unsafe { bindings::x509_free_certificate(prev) }; + } + + prev_cert =3D Some(cert_ptr); + offset +=3D cert_len as usize; + } + + if let Some(prev) =3D prev_cert { + if let Some(validate) =3D self.validate { + let rc =3D unsafe { validate(self.dev, slot, prev) }; + to_result(rc)?; + } + + self.leaf_key =3D unsafe { Some((*prev).pub_) }; + } + + Ok(()) + } } diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 5043eee2a8d6..52ad3e98e036 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -64,6 +64,8 @@ #include #include #include +#include +#include #include #include #include --=20 2.52.0