[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20260211032935.2705841-23-alistair.francis@wdc.com>
Date: Wed, 11 Feb 2026 13:29:29 +1000
From: alistair23@...il.com
To: bhelgaas@...gle.com,
lukas@...ner.de,
rust-for-linux@...r.kernel.org,
akpm@...ux-foundation.org,
linux-pci@...r.kernel.org,
Jonathan.Cameron@...wei.com,
linux-cxl@...r.kernel.org,
linux-kernel@...r.kernel.org
Cc: alex.gaynor@...il.com,
benno.lossin@...ton.me,
boqun.feng@...il.com,
a.hindborg@...nel.org,
gary@...yguo.net,
bjorn3_gh@...tonmail.com,
tmgross@...ch.edu,
alistair23@...il.com,
ojeda@...nel.org,
wilfred.mallawa@....com,
aliceryhl@...gle.com,
Alistair Francis <alistair@...stair23.me>
Subject: [RFC v3 22/27] lib: rspdm: Support SPDM certificate validation
From: Alistair Francis <alistair@...stair23.me>
Support validating the SPDM certificate chain.
Signed-off-by: Alistair Francis <alistair@...stair23.me>
---
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 &= !(1 << slot);
}
+ let mut provisioned_slots = state.provisioned_slots;
+ while (provisioned_slots as usize) > 0 {
+ let slot = provisioned_slots.trailing_zeros() as u8;
+
+ if let Err(e) = state.validate_cert_chain(slot) {
+ return e.to_errno() as c_int;
+ }
+
+ provisioned_slots &= !(1 << slot);
+ }
+
0
}
@@ -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) = &mut state.leaf_key {
+ unsafe {
+ bindings::public_key_free(*leaf_key);
+ }
+ }
+
if let Some(desc) = &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 slot 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 check
+/// 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 {
// Certificates
pub(crate) certs: [KVec<u8>; SPDM_SLOTS],
+ pub(crate) leaf_key: Option<*mut bindings::public_key>,
}
#[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,
}
}
@@ -743,4 +746,92 @@ pub(crate) fn get_certificate(&mut self, slot: u8) -> Result<(), Error> {
Ok(())
}
+
+ pub(crate) fn validate_cert_chain(&mut self, slot: u8) -> Result<(), Error> {
+ let cert_chain_buf = &self.certs[slot as usize];
+ let cert_chain_len = cert_chain_buf.len();
+ let header_len = 4 + self.hash_len;
+
+ let mut offset = header_len;
+ let mut prev_cert: Option<*mut bindings::x509_certificate> = None;
+
+ while offset < cert_chain_len {
+ let cert_len = 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 = if offset + cert_len as usize == cert_chain_len {
+ true
+ } else {
+ false
+ };
+
+ let cert_ptr = unsafe {
+ from_err_ptr(bindings::x509_cert_parse(
+ &cert_chain_buf[offset..] as *const _ as *const c_void,
+ cert_len as usize,
+ ))?
+ };
+ let cert = unsafe { *cert_ptr };
+
+ if cert.unsupported_sig || cert.blacklisted {
+ to_result(-(bindings::EKEYREJECTED as i32))?;
+ }
+
+ if let Some(prev) = prev_cert {
+ // Check against previous certificate
+ let rc = 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 = 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 = unsafe { bindings::verify_signature(key, cert.sig) };
+ unsafe { bindings::key_put(key) };
+
+ if rc < 0 {
+ pr_err!("Root signature validation error\n");
+ to_result(rc)?;
+ }
+ }
+
+ if let Some(prev) = prev_cert {
+ unsafe { bindings::x509_free_certificate(prev) };
+ }
+
+ prev_cert = Some(cert_ptr);
+ offset += cert_len as usize;
+ }
+
+ if let Some(prev) = prev_cert {
+ if let Some(validate) = self.validate {
+ let rc = unsafe { validate(self.dev, slot, prev) };
+ to_result(rc)?;
+ }
+
+ self.leaf_key = unsafe { Some((*prev).pub_) };
+ }
+
+ Ok(())
+ }
}
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 5043eee2a8d6..52ad3e98e036 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -64,6 +64,8 @@
#include <linux/hash.h>
#include <linux/jiffies.h>
#include <linux/jump_label.h>
+#include <keys/asymmetric-type.h>
+#include <keys/x509-parser.h>
#include <linux/mdio.h>
#include <linux/mm.h>
#include <linux/miscdevice.h>
--
2.52.0
Powered by blists - more mailing lists