Messages in this thread Patch in this message |  | | From | alistair23@gmail ... | | Subject | [RFC v3 22/27] lib: rspdm: Support SPDM certificate validation | | Date | Wed, 11 Feb 2026 13:29:29 +1000 |
| |
From: Alistair Francis <alistair@alistair23.me>
Support validating the SPDM certificate chain.
Signed-off-by: Alistair Francis <alistair@alistair23.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
|  |