public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Jonathan Cameron <jonathan.cameron@huawei.com>
To: <alistair23@gmail.com>
Cc: <bhelgaas@google.com>, <lukas@wunner.de>,
	<rust-for-linux@vger.kernel.org>, <akpm@linux-foundation.org>,
	<linux-pci@vger.kernel.org>, <linux-cxl@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>, <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>,
	<ojeda@kernel.org>, <wilfred.mallawa@wdc.com>,
	<aliceryhl@google.com>, Alistair Francis <alistair@alistair23.me>
Subject: Re: [RFC v3 08/27] lib: rspdm: Initial commit of Rust SPDM
Date: Mon, 2 Mar 2026 17:09:33 +0000	[thread overview]
Message-ID: <20260302170933.000016ad@huawei.com> (raw)
In-Reply-To: <20260211032935.2705841-9-alistair.francis@wdc.com>

On Wed, 11 Feb 2026 13:29:15 +1000
alistair23@gmail.com wrote:

> From: Alistair Francis <alistair@alistair23.me>
> 
> This is the initial commit of the Rust SPDM library. It is based on and
> compatible with the C SPDM library in the kernel (lib/spdm).
> 
> Signed-off-by: Alistair Francis <alistair@alistair23.me>
The comments that follow are based on my very limited rust knowledge.
Hence may be garbage.

> ---
>  MAINTAINERS                     |  12 ++
>  include/linux/spdm.h            |  39 ++++++
>  lib/Kconfig                     |  17 +++
>  lib/Makefile                    |   2 +
>  lib/rspdm/Makefile              |  10 ++
>  lib/rspdm/consts.rs             | 117 +++++++++++++++++
>  lib/rspdm/lib.rs                | 119 +++++++++++++++++
>  lib/rspdm/state.rs              | 220 ++++++++++++++++++++++++++++++++
>  lib/rspdm/validator.rs          |  66 ++++++++++
>  rust/bindings/bindings_helper.h |   2 +
>  rust/kernel/error.rs            |   3 +
>  11 files changed, 607 insertions(+)
>  create mode 100644 include/linux/spdm.h
>  create mode 100644 lib/rspdm/Makefile
>  create mode 100644 lib/rspdm/consts.rs
>  create mode 100644 lib/rspdm/lib.rs
>  create mode 100644 lib/rspdm/state.rs
>  create mode 100644 lib/rspdm/validator.rs
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 149deedafe2c..a5c4ec16081c 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -23693,6 +23693,18 @@ M:	Security Officers <security@kernel.org>
>  S:	Supported
>  F:	Documentation/process/security-bugs.rst
>  
> +SECURITY PROTOCOL AND DATA MODEL (SPDM)
> +M:	Jonathan Cameron <jic23@kernel.org>

Evil way to get me to learn rust. Ah well, I guess I can't put it
off for ever.

> +M:	Lukas Wunner <lukas@wunner.de>
> +M:	Alistair Francis <alistair@alistair23.me>
> +L:	linux-coco@lists.linux.dev
> +L:	linux-cxl@vger.kernel.org
> +L:	linux-pci@vger.kernel.org
> +S:	Maintained
> +T:	git git://git.kernel.org/pub/scm/linux/kernel/git/devsec/spdm.git
> +F:	include/linux/spdm.h
> +F:	lib/rspdm/


> diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs
> new file mode 100644
> index 000000000000..40ce60eba2f3
> --- /dev/null
> +++ b/lib/rspdm/consts.rs
> @@ -0,0 +1,117 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +// Copyright (C) 2024 Western Digital
> +
> +//! Constants used by the library
> +//!
> +//! Rust implementation of the DMTF Security Protocol and Data Model (SPDM)
> +//! <https://www.dmtf.org/dsp/DSP0274>
> +
> +pub(crate) const SPDM_REQ: u8 = 0x80;
> +pub(crate) const SPDM_ERROR: u8 = 0x7f;
> +
> +#[expect(dead_code)]
> +#[derive(Clone, Copy)]
> +pub(crate) enum SpdmErrorCode {
> +    InvalidRequest = 0x01,
> +    InvalidSession = 0x02,

This is reserved by the time we reach 1.2.1 and disappeared somewhere
in the middle of 1.1.0 (present) and 1.1.2 (reserved)
Probably good to leave some breadcrumbs for what spec versions could use
this error code.  That will reduce confusion for future readers.
You have this info in the parsing code, but I'd like it here as well.

> +    Busy = 0x03,
> +    UnexpectedRequest = 0x04,
> +    Unspecified = 0x05,
> +    DecryptError = 0x06,
> +    UnsupportedRequest = 0x07,
> +    RequestInFlight = 0x08,
> +    InvalidResponseCode = 0x09,
> +    SessionLimitExceeded = 0x0a,
> +    SessionRequired = 0x0b,
> +    ResetRequired = 0x0c,
> +    ResponseTooLarge = 0x0d,
> +    RequestTooLarge = 0x0e,
> +    LargeResponse = 0x0f,
> +    MessageLost = 0x10,
> +    InvalidPolicy = 0x11,
> +    VersionMismatch = 0x41,
> +    ResponseNotReady = 0x42,
> +    RequestResynch = 0x43,
> +    OperationFailed = 0x44,
> +    NoPendingRequests = 0x45,
> +    VendorDefinedError = 0xff,
> +}
> +
> +impl core::fmt::LowerHex for SpdmErrorCode {
> +    /// A debug print format for the SpdmSessionInfo struct
> +    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
> +        match self {
> +            SpdmErrorCode::InvalidRequest => {
> +                writeln!(f, "0x01")?;

No way to get a string from an enum value in rust?
Having to check these against the enum above is a bit tedious.

> +            }

> +        }
> +        Ok(())
> +    }
> +}
> diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs
> new file mode 100644
> index 000000000000..2bb716140e0a
> --- /dev/null
> +++ b/lib/rspdm/lib.rs
> @@ -0,0 +1,119 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +// Copyright (C) 2024 Western Digital
> +
> +//! Top level library for SPDM
> +//!
> +//! Rust implementation of the DMTF Security Protocol and Data Model (SPDM)
> +//! <https://www.dmtf.org/dsp/DSP0274>
> +//!
> +//! Top level library, including C compatible public functions to be called
> +//! from other subsytems.
> +//!
> +//! This mimics the C SPDM implementation in the kernel

The one that never gets merged if this goes according to plan ;)



> diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs
> new file mode 100644
> index 000000000000..68861f30e3fa
> --- /dev/null
> +++ b/lib/rspdm/state.rs
> @@ -0,0 +1,220 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +// Copyright (C) 2024 Western Digital
> +
> +//! The `SpdmState` struct and implementation.
> +//!
> +//! Rust implementation of the DMTF Security Protocol and Data Model (SPDM)
> +//! <https://www.dmtf.org/dsp/DSP0274>
> +
> +use core::ffi::c_void;
> +use kernel::prelude::*;
> +use kernel::{
> +    bindings,
> +    error::{code::EINVAL, to_result, Error},
> +    validate::Untrusted,
> +};
> +
> +use crate::consts::{SpdmErrorCode, SPDM_ERROR, SPDM_REQ};
> +use crate::validator::{SpdmErrorRsp, SpdmHeader};
> +
> +/// The current SPDM session state for a device. Based on the
> +/// C `struct spdm_state`.
> +///
> +/// `dev`: Responder device.  Used for error reporting and passed to @transport.
> +/// `transport`: Transport function to perform one message exchange.
> +/// `transport_priv`: Transport private data.
> +/// `transport_sz`: Maximum message size the transport is capable of (in bytes).
> +///  Used as DataTransferSize in GET_CAPABILITIES exchange.
> +/// `keyring`: Keyring against which to check the first certificate in
> +///  responder's certificate chain.

Given the discussions, seems this will go away (for now anyway).

> +/// `validate`: Function to validate additional leaf certificate requirements.
> +///
> +/// `version`: Maximum common supported version of requester and responder.
> +///  Negotiated during GET_VERSION exchange.
> +#[expect(dead_code)]
> +pub struct SpdmState {
> +    pub(crate) dev: *mut bindings::device,
> +    pub(crate) transport: bindings::spdm_transport,
> +    pub(crate) transport_priv: *mut c_void,
> +    pub(crate) transport_sz: u32,
> +    pub(crate) keyring: *mut bindings::key,
> +    pub(crate) validate: bindings::spdm_validate,
> +
> +    // Negotiated state
> +    pub(crate) version: u8,
> +}
> +
> +impl SpdmState {
> +    pub(crate) fn new(
> +        dev: *mut bindings::device,
> +        transport: bindings::spdm_transport,
> +        transport_priv: *mut c_void,
> +        transport_sz: u32,
> +        keyring: *mut bindings::key,
> +        validate: bindings::spdm_validate,
> +    ) -> Self {
> +        SpdmState {
> +            dev,
> +            transport,
> +            transport_priv,
> +            transport_sz,
> +            keyring,
> +            validate,
> +            version: 0x10,
> +        }
> +    }

> +
> +    /// Start a SPDM exchange
> +    ///
> +    /// The data in `request_buf` is sent to the device and the response is
> +    /// stored in `response_buf`.
> +    pub(crate) fn spdm_exchange(
> +        &self,
> +        request_buf: &mut [u8],
> +        response_buf: &mut [u8],
> +    ) -> Result<i32, Error> {
> +        let header_size = core::mem::size_of::<SpdmHeader>();
> +        let request: &mut SpdmHeader = Untrusted::new_mut(request_buf).validate_mut()?;

Why are we treating the request, which doesn't come from the device as untrusted?
Just for convenience on checking we formatted it right at the upper levels or is
the idea that might ultimately be coming from userspace?

> +        let response: &SpdmHeader = Untrusted::new_ref(response_buf).validate()?;
> +
> +        let transport_function = self.transport.ok_or(EINVAL)?;
> +        // SAFETY: `transport_function` is provided by the new(), we are
> +        // calling the function.
> +        let length = unsafe {
> +            transport_function(
> +                self.transport_priv,
> +                self.dev,
> +                request_buf.as_ptr() as *const c_void,
> +                request_buf.len(),
> +                response_buf.as_mut_ptr() as *mut c_void,
> +                response_buf.len(),
> +            ) as i32
> +        };
> +        to_result(length)?;
> +
> +        if (length as usize) < header_size {
> +            return Ok(length); // Truncated response is handled by callers
> +        }
> +        if response.code == SPDM_ERROR {
> +            if length as usize >= core::mem::size_of::<SpdmErrorRsp>() {
> +                // SAFETY: The response buffer will be at least as large as
> +                // `SpdmErrorRsp` so we can cast the buffer to `SpdmErrorRsp` which
> +                // is a packed struct.
> +                self.spdm_err(unsafe { &*(response_buf.as_ptr() as *const SpdmErrorRsp) })?;
> +            } else {
> +                return Err(EINVAL);
> +            }
> +        }
> +
> +        if response.code != request.code & !SPDM_REQ {
> +            pr_err!(
> +                "Response code {:#x} does not match request code {:#x}\n",
> +                response.code,
> +                request.code
> +            );
> +            to_result(-(bindings::EPROTO as i32))?;
> +        }
> +
> +        Ok(length)
> +    }
> +}
> diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs
> new file mode 100644
> index 000000000000..a0a3a2f46952
> --- /dev/null
> +++ b/lib/rspdm/validator.rs
> @@ -0,0 +1,66 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +// Copyright (C) 2024 Western Digital
> +
> +//! Related structs and their Validate implementations.
> +//!
> +//! Rust implementation of the DMTF Security Protocol and Data Model (SPDM)
> +//! <https://www.dmtf.org/dsp/DSP0274>
> +
> +use crate::consts::SpdmErrorCode;
> +use core::mem;
> +use kernel::prelude::*;
> +use kernel::{
> +    error::{code::EINVAL, Error},
> +    validate::{Unvalidated, Validate},
> +};
> +
> +#[repr(C, packed)]
> +pub(crate) struct SpdmHeader {
> +    pub(crate) version: u8,
> +    pub(crate) code: u8, /* RequestResponseCode */
> +    pub(crate) param1: u8,
> +    pub(crate) param2: u8,
> +}
> +
> +impl Validate<&Unvalidated<[u8]>> for &SpdmHeader {
> +    type Err = Error;
> +
> +    fn validate(unvalidated: &Unvalidated<[u8]>) -> Result<Self, Self::Err> {
> +        let raw = unvalidated.raw();
> +        if raw.len() < mem::size_of::<SpdmHeader>() {
> +            return Err(EINVAL);
> +        }
> +
> +        let ptr = raw.as_ptr();
> +        // CAST: `SpdmHeader` only contains integers and has `repr(C)`.
> +        let ptr = ptr.cast::<SpdmHeader>();
> +        // SAFETY: `ptr` came from a reference and the cast above is valid.
> +        Ok(unsafe { &*ptr })
> +    }
> +}
> +
> +impl Validate<&mut Unvalidated<[u8]>> for &mut SpdmHeader {
> +    type Err = Error;
> +
> +    fn validate(unvalidated: &mut Unvalidated<[u8]>) -> Result<Self, Self::Err> {
> +        let raw = unvalidated.raw_mut();
> +        if raw.len() < mem::size_of::<SpdmHeader>() {
> +            return Err(EINVAL);
> +        }
> +
> +        let ptr = raw.as_mut_ptr();
> +        // CAST: `SpdmHeader` only contains integers and has `repr(C)`.
> +        let ptr = ptr.cast::<SpdmHeader>();
> +        // SAFETY: `ptr` came from a reference and the cast above is valid.
> +        Ok(unsafe { &mut *ptr })
> +    }
> +}
> +
> +#[repr(C, packed)]
> +pub(crate) struct SpdmErrorRsp {
> +    pub(crate) version: u8,
> +    pub(crate) code: u8,

Maybe document here that this will always be SPDM_ERROR 0x7F


> +    pub(crate) error_code: SpdmErrorCode,
> +    pub(crate) error_data: u8,
> +}
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index 0075c4b62c29..5043eee2a8d6 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -84,7 +84,9 @@
>  #include <linux/slab.h>
>  #include <linux/task_work.h>
>  #include <linux/tracepoint.h>
> +#include <linux/spdm.h>

Smells like this is alphabetical order. So move it up a bit?

>  #include <linux/usb.h>
> +#include <linux/uaccess.h>
>  #include <linux/wait.h>
>  #include <linux/workqueue.h>
>  #include <linux/xarray.h>


  reply	other threads:[~2026-03-02 17:09 UTC|newest]

Thread overview: 99+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-02-11  3:29 [RFC v3 00/27] lib: Rust implementation of SPDM alistair23
2026-02-11  3:29 ` [RFC v3 01/27] rust: add untrusted data abstraction alistair23
2026-02-11  3:29 ` [RFC v3 02/27] X.509: Make certificate parser public alistair23
2026-02-11  3:29 ` [RFC v3 03/27] X.509: Parse Subject Alternative Name in certificates alistair23
2026-02-11  3:29 ` [RFC v3 04/27] X.509: Move certificate length retrieval into new helper alistair23
2026-02-11  3:29 ` [RFC v3 05/27] certs: Create blacklist keyring earlier alistair23
2026-02-11  3:29 ` [RFC v3 06/27] rust: add bindings for hash.h alistair23
2026-02-19 14:48   ` Gary Guo
2026-03-02 16:18   ` Jonathan Cameron
2026-02-11  3:29 ` [RFC v3 07/27] rust: error: impl From<FromBytesWithNulError> for Kernel Error alistair23
2026-02-19 14:49   ` Gary Guo
2026-03-13  2:20     ` Alistair Francis
2026-03-13 10:35       ` Alice Ryhl
2026-02-11  3:29 ` [RFC v3 08/27] lib: rspdm: Initial commit of Rust SPDM alistair23
2026-03-02 17:09   ` Jonathan Cameron [this message]
2026-03-13  3:44     ` Alistair Francis
2026-02-11  3:29 ` [RFC v3 09/27] PCI/CMA: Authenticate devices on enumeration alistair23
2026-02-16  4:25   ` Aksh Garg
2026-02-11  3:29 ` [RFC v3 10/27] PCI/CMA: Validate Subject Alternative Name in certificates alistair23
2026-02-11  3:29 ` [RFC v3 11/27] PCI/CMA: Reauthenticate devices on reset and resume alistair23
2026-02-11  3:29 ` [RFC v3 12/27] lib: rspdm: Support SPDM get_version alistair23
2026-02-11  4:00   ` Wilfred Mallawa
2026-03-03 11:36   ` Jonathan Cameron
2026-03-13  5:35     ` Alistair Francis
2026-03-13  5:53       ` Miguel Ojeda
2026-03-13  5:55         ` Miguel Ojeda
2026-03-16 17:16       ` Jonathan Cameron
2026-02-11  3:29 ` [RFC v3 13/27] lib: rspdm: Support SPDM get_capabilities alistair23
2026-02-11  4:08   ` Wilfred Mallawa
2026-03-03 12:09   ` Jonathan Cameron
2026-03-03 18:07     ` Miguel Ojeda
2026-03-20  4:32     ` Alistair Francis
2026-02-11  3:29 ` [RFC v3 14/27] lib: rspdm: Support SPDM negotiate_algorithms alistair23
2026-03-03 13:46   ` Jonathan Cameron
2026-02-11  3:29 ` [RFC v3 15/27] lib: rspdm: Support SPDM get_digests alistair23
2026-03-03 14:29   ` Jonathan Cameron
2026-02-11  3:29 ` [RFC v3 16/27] lib: rspdm: Support SPDM get_certificate alistair23
2026-03-03 14:51   ` Jonathan Cameron
2026-02-11  3:29 ` [RFC v3 17/27] crypto: asymmetric_keys - Load certificate parsing early in boot alistair23
2026-02-11  3:29 ` [RFC v3 18/27] KEYS: Load keyring and certificates " alistair23
2026-02-11  3:29 ` [RFC v3 19/27] PCI/CMA: Support built in X.509 certificates alistair23
2026-02-11  3:29 ` [RFC v3 20/27] crypto: sha: Load early in boot alistair23
2026-03-03 14:52   ` Jonathan Cameron
2026-02-11  3:29 ` [RFC v3 21/27] crypto: ecdsa: " alistair23
2026-03-03 14:54   ` Jonathan Cameron
2026-02-11  3:29 ` [RFC v3 22/27] lib: rspdm: Support SPDM certificate validation alistair23
2026-03-03 15:00   ` Jonathan Cameron
2026-02-11  3:29 ` [RFC v3 23/27] rust: allow extracting the buffer from a CString alistair23
2026-02-19 14:50   ` Gary Guo
2026-02-11  3:29 ` [RFC v3 24/27] lib: rspdm: Support SPDM challenge alistair23
2026-03-03 16:54   ` Jonathan Cameron
2026-02-11  3:29 ` [RFC v3 25/27] PCI/CMA: Expose in sysfs whether devices are authenticated alistair23
2026-02-11  3:29 ` [RFC v3 26/27] rust: add bindings for hash_info alistair23
2026-02-11  3:29 ` [RFC v3 27/27] rspdm: Multicast received signatures via netlink alistair23
2026-02-19 10:19   ` Lukas Wunner
2026-02-12  5:56 ` [RFC v3 00/27] lib: Rust implementation of SPDM dan.j.williams
2026-02-18  2:12   ` Alistair Francis
2026-02-17 23:56 ` Jason Gunthorpe
2026-02-18  2:17   ` Alistair Francis
2026-02-18 23:40     ` dan.j.williams
2026-02-19  0:56       ` Jason Gunthorpe
2026-02-19  5:05         ` dan.j.williams
2026-02-19 12:41           ` Jason Gunthorpe
2026-02-19 14:15             ` Lukas Wunner
2026-02-19 14:31               ` Jason Gunthorpe
2026-02-19 15:07                 ` Lukas Wunner
2026-02-19 17:39                   ` Jason Gunthorpe
2026-02-19 20:07                     ` dan.j.williams
2026-02-20  8:30                     ` Lukas Wunner
2026-02-20 14:10                       ` Jason Gunthorpe
2026-02-21 18:46                         ` Lukas Wunner
2026-02-21 23:29                           ` dan.j.williams
2026-02-23 17:15                             ` Jonathan Cameron
2026-02-23 19:11                               ` dan.j.williams
2026-02-24 14:33                                 ` Jason Gunthorpe
2026-03-05  4:17                                 ` dan.j.williams
2026-03-05 12:48                                   ` Jason Gunthorpe
2026-03-05 19:49                                     ` dan.j.williams
2026-03-09 11:39                                       ` Jonathan Cameron
2026-03-09 12:31                                         ` Jason Gunthorpe
2026-03-09 15:33                                           ` Jonathan Cameron
2026-03-09 15:59                                             ` Jason Gunthorpe
2026-03-09 18:00                                               ` Jonathan Cameron
2026-03-09 20:40                                                 ` Jason Gunthorpe
2026-03-09 23:11                                                   ` DanX Williams
2026-02-24 14:16                           ` Jason Gunthorpe
2026-02-24 15:54                             ` Lukas Wunner
2026-02-25 14:50                               ` Jason Gunthorpe
2026-02-19 14:40               ` Greg KH
2026-02-20  7:46                 ` Lukas Wunner
2026-02-20  9:14                   ` Greg KH
2026-02-20 11:45                     ` Lukas Wunner
2026-02-20 11:57                       ` Greg KH
2026-02-19  9:34         ` Lukas Wunner
2026-02-19 12:43           ` Jason Gunthorpe
2026-02-19 18:48           ` dan.j.williams
2026-02-19  9:13       ` Lukas Wunner
2026-02-19 18:42         ` dan.j.williams
2026-02-19 11:24   ` Jonathan Cameron

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260302170933.000016ad@huawei.com \
    --to=jonathan.cameron@huawei.com \
    --cc=a.hindborg@kernel.org \
    --cc=akpm@linux-foundation.org \
    --cc=alex.gaynor@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=alistair23@gmail.com \
    --cc=alistair@alistair23.me \
    --cc=benno.lossin@proton.me \
    --cc=bhelgaas@google.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun.feng@gmail.com \
    --cc=gary@garyguo.net \
    --cc=linux-cxl@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=lukas@wunner.de \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=tmgross@umich.edu \
    --cc=wilfred.mallawa@wdc.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox