[PATCH v6 25/25] lib: rspdm: Expose public Rust API
From: alistair23
Date: Mon Nov 09 2026 - 23:45:04 EST
From: Alistair Francis <alistair.francis@xxxxxxx>
Expose a public Rust API and an example that can be used to authenticate
an SPDM responder.
Signed-off-by: Alistair Francis <alistair.francis@xxxxxxx>
---
lib/rspdm/lib.rs | 69 ++++++++++++++++------------------------------
lib/rspdm/state.rs | 43 +++++++++++++++++++++++++++--
2 files changed, 63 insertions(+), 49 deletions(-)
diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs
index 6bfe1eedc4121..6b1f864fe7266 100644
--- a/lib/rspdm/lib.rs
+++ b/lib/rspdm/lib.rs
@@ -9,6 +9,26 @@
//!
//! Top level library, including C compatible public functions to be called
//! from other subsytems.
+//!
+//! ```rust,ignore
+//! struct MyTransport { }
+//!
+//! impl Transport for MyTransport {
+//! fn exchange(
+//! &self,
+//! dev: &Device,
+//! request: &[u8],
+//! response: &mut [u8],
+//! ) -> Result<usize, Error> {
+//! todo!()
+//! }
+//! }
+//!
+//! fn authenticate_device(dev: &Device, transport: &MyTransport) -> Result {
+//! let mut state = SpdmState::new(ARef::from(dev), transport, 512, None);
+//! state.authenticate()
+//! }
+//! ```
use crate::bindings::{
spdm_state, //
@@ -30,7 +50,7 @@
};
use crate::consts::SPDM_MIN_DATA_TRANSFER_SIZE;
-use crate::state::{
+pub use crate::state::{
SpdmState,
Transport, //
};
@@ -263,51 +283,8 @@ fn from(code: SpdmErrorCode) -> Self {
let mut state = mutex.lock();
- if let Err(e) = state.get_version() {
- return e.to_errno() as c_int;
- }
-
- if let Err(e) = state.get_capabilities() {
- return e.to_errno() as c_int;
- }
-
- if let Err(e) = state.negotiate_algs() {
- return e.to_errno() as c_int;
- }
-
- if let Err(e) = state.get_digests() {
- return e.to_errno() as c_int;
- }
-
- if state.provisioned_slots == 0 {
- return -(bindings::EIO as c_int);
- }
-
- 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.get_certificate(slot) {
- return e.to_errno() as c_int;
- }
-
- 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) {
- pr_err!("Certificate in slot {slot} failed to verify: {e:?}\n");
- return e.to_errno() as c_int;
- }
-
- provisioned_slots &= !(1 << slot);
- }
-
- let provisioned_slots = state.provisioned_slots.trailing_zeros();
- if let Err(e) = state.challenge(provisioned_slots as u8) {
+ if let Err(e) = state.authenticate() {
+ dev_err!(&state.dev, "Authentication failed: {e:?}\n");
return e.to_errno() as c_int;
}
diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs
index 059398e95b92d..0fe99caf27958 100644
--- a/lib/rspdm/state.rs
+++ b/lib/rspdm/state.rs
@@ -96,7 +96,7 @@
///
/// This trait abstracts the bus-specific message transport (for example
/// a DOE mailbox for PCIe CMA) from the SPDM session logic.
-pub(crate) trait Transport {
+pub trait Transport {
/// Perform a single SPDM message exchange.
///
/// Send `request` to the responder and write the response into
@@ -115,7 +115,7 @@ pub(crate) trait Transport {
/// it does not need to stay bound to its driver. `T` is data and an
/// exchange function owned by the caller, so the borrow checker enforces
/// that the transport data outlives `SpdmState`.
-pub(crate) struct SpdmState<T: Transport> {
+pub struct SpdmState<T: Transport> {
/// Responder device (reference counted). Used for error reporting and
/// passed to `transport`.
pub(crate) dev: ARef<Device>,
@@ -194,7 +194,7 @@ impl<T: Transport> SpdmState<T> {
///
/// `transport_sz` is the maximum message size the transport can carry,
/// and must be at least the SPDM minimum data transfer size (42 bytes).
- pub(crate) fn new(
+ pub fn new(
dev: ARef<Device>,
transport: T,
transport_sz: u32,
@@ -232,6 +232,43 @@ pub(crate) fn hash_len(&self) -> usize {
.unwrap_or(0)
}
+ /// Authenticates the responder through a sequence of GET_VERSION,
+ /// GET_CAPABILITIES, NEGOTIATE_ALGORITHMS, GET_DIGESTS, GET_CERTIFICATE
+ /// and CHALLENGE exchanges.
+ pub fn authenticate(&mut self) -> Result {
+ self.get_version()?;
+ self.get_capabilities()?;
+ self.negotiate_algs()?;
+ self.get_digests()?;
+
+ if self.provisioned_slots == 0 {
+ return Err(EIO);
+ }
+
+ let mut provisioned_slots = self.provisioned_slots;
+ while (provisioned_slots as usize) > 0 {
+ let slot = provisioned_slots.trailing_zeros() as u8;
+
+ self.get_certificate(slot)?;
+
+ provisioned_slots &= !(1 << slot);
+ }
+
+ let mut provisioned_slots = self.provisioned_slots;
+ while (provisioned_slots as usize) > 0 {
+ let slot = provisioned_slots.trailing_zeros() as u8;
+
+ self.validate_cert_chain(slot)?;
+
+ provisioned_slots &= !(1 << slot);
+ }
+
+ let provisioned_slots = self.provisioned_slots.trailing_zeros();
+ self.challenge(provisioned_slots as u8)?;
+
+ Ok(())
+ }
+
/// Start a SPDM exchange
///
/// The data in `request_buf` is sent to the device and the response
--
2.56.0