From nobody Thu Apr 2 15:42:27 2026 Received: from mail-pg1-f173.google.com (mail-pg1-f173.google.com [209.85.215.173]) (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 28BB8344D9B for ; Wed, 11 Feb 2026 03:29:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780598; cv=none; b=SrpFJp24s1iOi8Xa2oZpAC5u+MA4M9Dd5CTZKnrdAgxcRTwB7mB7YjPR3wIfk5xQIdLg2r47kT9qF8IHqfWFmByNMnuhmq7GDS3uKp8VDxSJ+dXbSkahZtc4ka5/eblCmaPQif1cQZ+RX8pX6/cLeMwtJoLmrQi963UKkq/3gdg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780598; c=relaxed/simple; bh=s85C2OKbKyG80Gcr37rqh3lRTMRzMhKOlhnVwYinKmo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=H/M7f7aZdu5cq4p9QYhAIbXXwMnZWPAQUDX7uaqMmfwPb6+S8PmthIynAxcPdJbpfpkVJ9Cu/JzdTCMhONNA3+aKfYpRF1iNzPDC+a9CWWminP3c5VkUf+/tgvR3xcZTvEM7yFE8xBpKIGT08+tVqmizeBns1ixbvM2SK5B8ruA= 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=Je8ICFxC; arc=none smtp.client-ip=209.85.215.173 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="Je8ICFxC" Received: by mail-pg1-f173.google.com with SMTP id 41be03b00d2f7-c6c444e89bcso582645a12.2 for ; Tue, 10 Feb 2026 19:29:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780593; x=1771385393; 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=t3JSMx43NpMgl9bRcYgyX8dtvUWeN3vQot3jC8EV0N4=; b=Je8ICFxCSo7Mny16fvuG9JIfGZKxjcJBLvp55eRv2reQQuLw0OWxUla9snJfB8+JmJ E0wj7Xs6vtwEmswuqb/16RescIFQOt/4RHZDXLq92aC396CtEnodaAigPAT8Jg3ax2Vz zhS5IlqeI20DsIkUhbSVF0z0044g85ebh+/BR1ofvmZXzlhX3J9lNtkibXHubLi36DHX 6zozR56q5rxVyCejsm7dnlkatGiDu6e7REdgPCQHJlzGGPV6Ph94E9hWsVzlHvWyyUVW 2nYBy9CCTqhy7+4TlEdm6WMv3JT2HXyJqPatE8eBt2mVjLvTpv3qfPdXVM7MWK/tMUEE J72A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780593; x=1771385393; 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=t3JSMx43NpMgl9bRcYgyX8dtvUWeN3vQot3jC8EV0N4=; b=VSPeyaFqfoY3/vLsi23YLxkjFsTU3+HPEcgKKeRlVF9PPKs0r0NAQj/BLcp9M60hPA zp6HjyWE5qI6GCYH/b+tkEIm1a++NMcjY/HqmhYyqKrTiTg2gwAEBQG2NeBgenprkIkv 2A8EHWTsd/yU4pvcl3qtc4t430ueqcuhRO+hmYVyeTjROEUh8METLuPmB0jwXDSUMWLV u5u0NQQru5Uz9SqNAuqzj0VJU5WnOPJ2/9hvpp9wmeWDpPtkW/oU0ccwTf0xz2ebrrdH Yer5+8Cmw7eZ/D/kQCLv3U8dK79SZp3vaPnjIfb/M+14h74fqaz46FO/o5sdh9M39pwN zd4A== X-Forwarded-Encrypted: i=1; AJvYcCX3mTTzK8/w+8bICYezu1M14EVfFz4HnZgd9AJLy9mkrF7OepYZ8KvQMjdB6vwe9RtYE+1X0voQ59xOoZU=@vger.kernel.org X-Gm-Message-State: AOJu0YxG9AWRJG3oJMo8+7TDeCsNEbsSDkbSwG0D6cl4bxR5GsfKiVFv 1t9QLG9+NcAPuI1zPSK3/il2ZZrPvtYX4pp+T/krBe42fJSvxDVZmn33 X-Gm-Gg: AZuq6aLdACk5Xf/jfyi1T+Ltgt0Rexrg4BdJkyw/yGLIfufkipZ+BAf26g4Bdj2xzdO ufkeCDcgdRdtLlJ4yLVdHWPleUT934uw5hA+k9E152Qcuc+x45P1ai46xlr7p9OW9yqlVJ5qlMX iWicw7E9xbDP8nwKuDpkEBN3yXDzLkAeB2475m7ej5oGoVVajGfw68emEg4mEUau/VkeFW5wBLV ENIeS9cySkF0FsFE3ALWGG0RcKYq3EniXXByUIbFX2scvfFtmWs+i33jf8EKDdFeVDJHIadyZBb fkMrEcxd4y2vSwjF++ESSS4kb1a6dED16VhyuephfEoZpF0F+d3tCHMbr6Tv/Hd7ICtfxvHkARC uyJUq8c6+B5rt9WNnDACs3Fp6MEc5NjfFaf8M0qjfVntmQS8o1QyADg3evL8dg2N21/1zlo2UEH pcTM3Nv65aYVGvOTeQ/WphRZZsmlWdIQz3l3O+2PuxCg== X-Received: by 2002:a05:6a21:9ccb:b0:38d:edd4:2fc7 with SMTP id adf61e73a8af0-393ad002927mr16562707637.26.1770780593127; Tue, 10 Feb 2026 19:29:53 -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.29.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:29:52 -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, Simona Vetter Subject: [RFC v3 01/27] rust: add untrusted data abstraction Date: Wed, 11 Feb 2026 13:29:08 +1000 Message-ID: <20260211032935.2705841-2-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: Benno Lossin When reading data from userspace, hardware or other external untrusted sources, the data must be validated before it is used for logic within the kernel. This abstraction provides a generic newtype wrapper `Untrusted`; it prevents direct access to the inner type. The only way to use the underlying data is to call `.validate()` on such a value. Doing so utilizes the new `Validate` trait that is responsible for all of the validation logic. This trait gives access to the inner value of `Untrusted` by means of another newtype wrapper `Unvalidated`. In contrast to `Untrusted`, `Unvalidated` allows direct access and additionally provides several helper functions for slices. Having these two different newtype wrappers is an idea from Simona Vetter. It has several benefits: it fully prevents safe access to the underlying value of `Untrusted` without going through the `Validate` API. Additionally, it allows one to grep for validation logic by simply looking for `Unvalidated<`. Any API that reads data from an untrusted source should return `Untrusted` where `T` is the type of the underlying untrusted data. This generic allows other abstractions to return their custom type wrapped by `Untrusted`, signaling to the caller that the data must be validated before use. This allows those abstractions to be used both in a trusted and untrusted manner, increasing their generality. Additionally, using the arbitrary self types feature, APIs can be designed to explicitly read untrusted data: impl MyCustomDataSource { pub fn read(self: &Untrusted) -> &Untrusted<[u8]>; } Cc: Simona Vetter Signed-off-by: Benno Lossin Message-ID: <20240925205244.873020-2-benno.lossin@proton.me> --- rust/kernel/lib.rs | 1 + rust/kernel/validate.rs | 605 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 606 insertions(+) create mode 100644 rust/kernel/validate.rs diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 696f62f85eb5..915499704405 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -154,6 +154,7 @@ pub mod uaccess; #[cfg(CONFIG_USB =3D "y")] pub mod usb; +pub mod validate; pub mod workqueue; pub mod xarray; =20 diff --git a/rust/kernel/validate.rs b/rust/kernel/validate.rs new file mode 100644 index 000000000000..ae0aa20e27b4 --- /dev/null +++ b/rust/kernel/validate.rs @@ -0,0 +1,605 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Types for handling and validating untrusted data. +//! +//! # Overview +//! +//! Untrusted data is marked using the [`Untrusted`] type. See [Rationa= le](#rationale) for the +//! reasons to mark untrusted data throughout the kernel. It is a totally = opaque wrapper, it is not +//! possible to read the data inside; but it is possible to [`Untrusted::w= rite`] into it. +//! +//! The only way to "access" the data inside an [`Untrusted`] is to [`U= ntrusted::validate`] it; +//! turning it into a different form using the [`Validate`] trait. That tr= ait receives the data in +//! the form of [`Unvalidated`], which in contrast to [`Untrusted`],= allows access to the +//! underlying data. It additionally provides several utility functions to= simplify validation. +//! +//! # Rationale +//! +//! When reading data from an untrusted source, it must be validated befor= e it can be used for +//! logic. For example, this is a very bad idea: +//! +//! ``` +//! # fn read_bytes_from_network() -> Box<[u8]> { +//! # Box::new([1, 0], kernel::alloc::flags::GFP_KERNEL).unwrap() +//! # } +//! let bytes: Box<[u8]> =3D read_bytes_from_network(); +//! let data_index =3D bytes[0]; +//! let data =3D bytes[usize::from(data_index)]; +//! ``` +//! +//! While this will not lead to a memory violation (because the array inde= x checks the bounds), it +//! might result in a kernel panic. For this reason, all untrusted data mu= st be wrapped in +//! [`Untrusted`]. This type only allows validating the data or passing= it along, since copying +//! data from one userspace buffer into another is allowed for untrusted d= ata. + +use crate::prelude::Init; +use core::{ + mem::MaybeUninit, + ops::{Index, IndexMut}, + ptr, slice, +}; + +/// Untrusted data of type `T`. +/// +/// When reading data from userspace, hardware or other external untrusted= sources, the data must +/// be validated before it is used for logic within the kernel. To do so, = the [`validate()`] +/// function exists and uses the [`Validate`] trait. +/// +/// Also see the [module] description. +/// +/// [`validate()`]: Self::validate +/// [module]: self +#[repr(transparent)] +pub struct Untrusted(Unvalidated); + +impl Untrusted { + /// Marks the given value as untrusted. + /// + /// # Examples + /// + /// ``` + /// use kernel::validate::Untrusted; + /// + /// # mod bindings { pub(crate) unsafe fn read_foo_info() -> [u8; 4] {= todo!() } }; + /// fn read_foo_info() -> Untrusted<[u8; 4]> { + /// // SAFETY: just an FFI call without preconditions. + /// Untrusted::new(unsafe { bindings::read_foo_info() }) + /// } + /// ``` + pub fn new(value: T) -> Self + where + T: Sized, + { + Self(Unvalidated::new(value)) + } + + /// Marks the value behind the reference as untrusted. + /// + /// # Examples + /// + /// In this imaginary example there exists the `foo_hardware` struct o= n the C side, as well as + /// a `foo_hardware_read` function that reads some data directly from = the hardware. + /// ``` + /// use kernel::{error, types::Opaque, validate::Untrusted}; + /// use core::ptr; + /// + /// # #[allow(non_camel_case_types)] + /// # mod bindings { + /// # pub(crate) struct foo_hardware; + /// # pub(crate) unsafe fn foo_hardware_read(_foo: *mut foo_hardwa= re, _len: &mut usize) -> *mut u8 { + /// # todo!() + /// # } + /// # } + /// struct Foo(Opaque); + /// + /// impl Foo { + /// fn read(&mut self, mut len: usize) -> Result<&Untrusted<[u8]>>= { + /// // SAFETY: just an FFI call without preconditions. + /// let data: *mut u8 =3D unsafe { bindings::foo_hardware_read= (self.0.get(), &mut len) }; + /// let data =3D error::from_err_ptr(data)?; + /// let data =3D ptr::slice_from_raw_parts(data, len); + /// // SAFETY: `data` returned by `foo_hardware_read` is valid= for reads as long as the + /// // `foo_hardware` object exists. That function updated the + /// let data =3D unsafe { &*data }; + /// Ok(Untrusted::new_ref(data)) + /// } + /// } + /// ``` + pub fn new_ref(value: &T) -> &Self { + let ptr: *const T =3D value; + // CAST: `Self` and `Unvalidated` are `repr(transparent)` and cont= ain a `T`. + let ptr =3D ptr as *const Self; + // SAFETY: `ptr` came from a shared reference valid for `'a`. + unsafe { &*ptr } + } + + /// Marks the value behind the reference as untrusted. + /// + /// # Examples + /// + /// In this imaginary example there exists the `foo_hardware` struct o= n the C side, as well as + /// a `foo_hardware_read` function that reads some data directly from = the hardware. + /// ``` + /// use kernel::{error, types::Opaque, validate::Untrusted}; + /// use core::ptr; + /// + /// # #[allow(non_camel_case_types)] + /// # mod bindings { + /// # pub(crate) struct foo_hardware; + /// # pub(crate) unsafe fn foo_hardware_read(_foo: *mut foo_hardwa= re, _len: &mut usize) -> *mut u8 { + /// # todo!() + /// # } + /// # } + /// struct Foo(Opaque); + /// + /// impl Foo { + /// fn read(&mut self, mut len: usize) -> Result<&mut Untrusted<[u= 8]>> { + /// // SAFETY: just an FFI call without preconditions. + /// let data: *mut u8 =3D unsafe { bindings::foo_hardware_read= (self.0.get(), &mut len) }; + /// let data =3D error::from_err_ptr(data)?; + /// let data =3D ptr::slice_from_raw_parts_mut(data, len); + /// // SAFETY: `data` returned by `foo_hardware_read` is valid= for reads as long as the + /// // `foo_hardware` object exists. That function updated the + /// let data =3D unsafe { &mut *data }; + /// Ok(Untrusted::new_mut(data)) + /// } + /// } + /// ``` + pub fn new_mut(value: &mut T) -> &mut Self { + let ptr: *mut T =3D value; + // CAST: `Self` and `Unvalidated` are `repr(transparent)` and cont= ain a `T`. + let ptr =3D ptr as *mut Self; + // SAFETY: `ptr` came from a mutable reference valid for `'a`. + unsafe { &mut *ptr } + } + + /// Validates and parses the untrusted data. + /// + /// See the [`Validate`] trait on how to implement it. + pub fn validate<'a, V: Validate<&'a Unvalidated>>(&'a self) -> Resu= lt { + V::validate(&self.0) + } + + /// Validates and parses the untrusted data. + /// + /// See the [`Validate`] trait on how to implement it. + pub fn validate_mut<'a, V: Validate<&'a mut Unvalidated>>( + &'a mut self, + ) -> Result { + V::validate(&mut self.0) + } + + /// Sets the underlying untrusted value. + /// + /// # Examples + /// + /// ``` + /// use kernel::validate::Untrusted; + /// + /// let mut untrusted =3D Untrusted::new(42); + /// untrusted.write(24); + /// ``` + pub fn write(&mut self, value: impl Init) { + let ptr: *mut T =3D &mut self.0 .0; + // SAFETY: `ptr` came from a mutable reference and the value is ov= erwritten before it is + // read. + unsafe { ptr::drop_in_place(ptr) }; + // SAFETY: `ptr` came from a mutable reference and the initializer= cannot error. + match unsafe { value.__init(ptr) } { + Ok(()) =3D> {} + Err(_) =3D> unreachable!(), + } + } + + /// Turns a slice of untrusted values into an untrusted slice of value= s. + pub fn transpose_slice(slice: &[Untrusted]) -> &Untrusted<[T]> + where + T: Sized, + { + let ptr =3D slice.as_ptr().cast::(); + // SAFETY: `ptr` and `len` come from the same slice reference. + let slice =3D unsafe { slice::from_raw_parts(ptr, slice.len()) }; + Untrusted::new_ref(slice) + } + + /// Turns a slice of uninitialized, untrusted values into an untrusted= slice of uninitialized + /// values. + pub fn transpose_slice_uninit( + slice: &[MaybeUninit>], + ) -> &Untrusted<[MaybeUninit]> + where + T: Sized, + { + let ptr =3D slice.as_ptr().cast::>(); + // SAFETY: `ptr` and `len` come from the same mutable slice refere= nce. + let slice =3D unsafe { slice::from_raw_parts(ptr, slice.len()) }; + Untrusted::new_ref(slice) + } + + /// Turns a slice of uninitialized, untrusted values into an untrusted= slice of uninitialized + /// values. + pub fn transpose_slice_uninit_mut( + slice: &mut [MaybeUninit>], + ) -> &mut Untrusted<[MaybeUninit]> + where + T: Sized, + { + // CAST: `MaybeUninit` and `MaybeUninit>` have the= same layout. + let ptr =3D slice.as_mut_ptr().cast::>(); + // SAFETY: `ptr` and `len` come from the same mutable slice refere= nce. + let slice =3D unsafe { slice::from_raw_parts_mut(ptr, slice.len())= }; + Untrusted::new_mut(slice) + } +} + +impl Untrusted> { + /// Sets the underlying untrusted value. + /// + /// # Examples + /// + /// ``` + /// use kernel::validate::Untrusted; + /// + /// let mut untrusted =3D Untrusted::new(42); + /// untrusted.write(24); + /// ``` + pub fn write_uninit(&mut self, value: impl Init) -> Result<&m= ut Untrusted, E> { + let ptr: *mut MaybeUninit =3D &mut self.0 .0; + // CAST: `MaybeUninit` is `repr(transparent)`. + let ptr =3D ptr.cast::(); + // SAFETY: `ptr` came from a reference and if `Err` is returned, t= he underlying memory is + // considered uninitialized. + unsafe { value.__init(ptr) }.map(|()| { + let this =3D self.0.raw_mut(); + // SAFETY: we initialized the memory above. + Untrusted::new_mut(unsafe { this.assume_init_mut() }) + }) + } +} + +impl Untrusted<[MaybeUninit]> { + /// Sets the underlying untrusted value. + /// + /// # Examples + /// + /// ``` + /// use kernel::validate::Untrusted; + /// + /// let mut untrusted =3D Untrusted::new(42); + /// untrusted.write(24); + /// ``` + pub fn write_uninit_slice( + &mut self, + value: impl Init<[T], E>, + ) -> Result<&mut Untrusted<[T]>, E> { + let ptr: *mut [MaybeUninit] =3D &mut self.0 .0; + // CAST: `MaybeUninit` is `repr(transparent)`. + let ptr =3D ptr as *mut [T]; + // SAFETY: `ptr` came from a reference and if `Err` is returned, t= he underlying memory is + // considered uninitialized. + unsafe { value.__init(ptr) }.map(|()| { + let this =3D self.0.raw_mut().as_mut_ptr(); + // CAST: `MaybeUninit` is `repr(transparent)`. + let this =3D this.cast::(); + // SAFETY: `this` and `len` came from the same slice reference. + let this =3D unsafe { slice::from_raw_parts_mut(this, self.0.l= en()) }; + Untrusted::new_mut(this) + }) + } +} + +/// Marks types that can be used as input to [`Validate::validate`]. +pub trait ValidateInput: private::Sealed + Sized {} + +mod private { + pub trait Sealed {} +} + +impl<'a, T: ?Sized> private::Sealed for &'a Unvalidated {} +impl<'a, T: ?Sized> ValidateInput for &'a Unvalidated {} + +impl<'a, T: ?Sized> private::Sealed for &'a mut Unvalidated {} +impl<'a, T: ?Sized> ValidateInput for &'a mut Unvalidated {} + +/// Validates untrusted data. +/// +/// # Examples +/// +/// The simplest way to validate data is to just implement `Validate<&Unva= lidated<[u8]>>` for the +/// type that you wish to validate: +/// +/// ``` +/// use kernel::{ +/// error::{code::EINVAL, Error}, +/// str::{CStr, CString}, +/// validate::{Unvalidated, Validate}, +/// }; +/// +/// struct Data { +/// flags: u8, +/// name: CString, +/// } +/// +/// impl Validate<&Unvalidated<[u8]>> for Data { +/// type Err =3D Error; +/// +/// fn validate(unvalidated: &Unvalidated<[u8]>) -> Result { +/// let raw =3D unvalidated.raw(); +/// let (&flags, name) =3D raw.split_first().ok_or(EINVAL)?; +/// let name =3D CStr::from_bytes_with_nul(name)?.to_cstring()?; +/// Ok(Data { flags, name }) +/// } +/// } +/// ``` +/// +/// This approach copies the data and requires allocation. If you want to = avoid the allocation and +/// copying the data, you can borrow from the input like this: +/// +/// ``` +/// use kernel::{ +/// error::{code::EINVAL, Error}, +/// str::CStr, +/// validate::{Unvalidated, Validate}, +/// }; +/// +/// struct Data<'a> { +/// flags: u8, +/// name: &'a CStr, +/// } +/// +/// impl<'a> Validate<&'a Unvalidated<[u8]>> for Data<'a> { +/// type Err =3D Error; +/// +/// fn validate(unvalidated: &'a Unvalidated<[u8]>) -> Result { +/// let raw =3D unvalidated.raw(); +/// let (&flags, name) =3D raw.split_first().ok_or(EINVAL)?; +/// let name =3D CStr::from_bytes_with_nul(name)?; +/// Ok(Data { flags, name }) +/// } +/// } +/// ``` +/// +/// If you need to in-place validate your data, you currently need to reso= rt to `unsafe`: +/// +/// ``` +/// use kernel::{ +/// error::{code::EINVAL, Error}, +/// str::CStr, +/// validate::{Unvalidated, Validate}, +/// }; +/// use core::mem; +/// +/// // Important: use `repr(C)`, this ensures a linear layout of this type. +/// #[repr(C)] +/// struct Data { +/// version: u8, +/// flags: u8, +/// _reserved: [u8; 2], +/// count: u64, +/// // lots of other fields... +/// } +/// +/// impl Validate<&Unvalidated<[u8]>> for &Data { +/// type Err =3D Error; +/// +/// fn validate(unvalidated: &Unvalidated<[u8]>) -> Result { +/// let raw =3D unvalidated.raw(); +/// if raw.len() < mem::size_of::() { +/// return Err(EINVAL); +/// } +/// // can only handle version 0 +/// if raw[0] !=3D 0 { +/// return Err(EINVAL); +/// } +/// // version 0 only uses the lower 4 bits of flags +/// if raw[1] & 0xf0 !=3D 0 { +/// return Err(EINVAL); +/// } +/// let ptr =3D raw.as_ptr(); +/// // CAST: `Data` only contains integers and has `repr(C)`. +/// let ptr =3D ptr.cast::(); +/// // SAFETY: `ptr` came from a reference and the cast above is v= alid. +/// Ok(unsafe { &*ptr }) +/// } +/// } +/// ``` +/// +/// To be able to modify the parsed data, while still supporting zero-copy= , you can implement +/// `Validate<&mut Unvalidated<[u8]>>`: +/// +/// ``` +/// use kernel::{ +/// error::{code::EINVAL, Error}, +/// str::CStr, +/// validate::{Unvalidated, Validate}, +/// }; +/// use core::mem; +/// +/// // Important: use `repr(C)`, this ensures a linear layout of this type. +/// #[repr(C)] +/// struct Data { +/// version: u8, +/// flags: u8, +/// _reserved: [u8; 2], +/// count: u64, +/// // lots of other fields... +/// } +/// +/// impl Validate<&mut Unvalidated<[u8]>> for &Data { +/// type Err =3D Error; +/// +/// fn validate(unvalidated: &mut Unvalidated<[u8]>) -> Result { +/// let raw =3D unvalidated.raw_mut(); +/// if raw.len() < mem::size_of::() { +/// return Err(EINVAL); +/// } +/// match raw[0] { +/// 0 =3D> {}, +/// 1 =3D> { +/// // version 1 implicitly sets the first bit. +/// raw[1] |=3D 1; +/// }, +/// // can only handle version 0 and 1 +/// _ =3D> return Err(EINVAL), +/// } +/// // version 0 and 1 only use the lower 4 bits of flags +/// if raw[1] & 0xf0 !=3D 0 { +/// return Err(EINVAL); +/// } +/// if raw[1] =3D=3D 0 {} +/// let ptr =3D raw.as_ptr(); +/// // CAST: `Data` only contains integers and has `repr(C)`. +/// let ptr =3D ptr.cast::(); +/// // SAFETY: `ptr` came from a reference and the cast above is v= alid. +/// Ok(unsafe { &*ptr }) +/// } +/// } +/// ``` +pub trait Validate: Sized { + /// Validation error. + type Err; + + /// Validate the given untrusted data and parse it into the output typ= e. + fn validate(unvalidated: I) -> Result; +} + +/// Unvalidated data of type `T`. +#[repr(transparent)] +pub struct Unvalidated(T); + +impl Unvalidated { + fn new(value: T) -> Self + where + T: Sized, + { + Self(value) + } + + fn new_ref(value: &T) -> &Self { + let ptr: *const T =3D value; + // CAST: `Self` is `repr(transparent)` and contains a `T`. + let ptr =3D ptr as *const Self; + // SAFETY: `ptr` came from a mutable reference valid for `'a`. + unsafe { &*ptr } + } + + fn new_mut(value: &mut T) -> &mut Self { + let ptr: *mut T =3D value; + // CAST: `Self` is `repr(transparent)` and contains a `T`. + let ptr =3D ptr as *mut Self; + // SAFETY: `ptr` came from a mutable reference valid for `'a`. + unsafe { &mut *ptr } + } + + /// Validates and parses the untrusted data. + /// + /// See the [`Validate`] trait on how to implement it. + pub fn validate_ref<'a, V: Validate<&'a Unvalidated>>(&'a self) -> = Result { + V::validate(self) + } + + /// Validates and parses the untrusted data. + /// + /// See the [`Validate`] trait on how to implement it. + pub fn validate_mut<'a, V: Validate<&'a mut Unvalidated>>( + &'a mut self, + ) -> Result { + V::validate(self) + } + + /// Gives immutable access to the underlying value. + pub fn raw(&self) -> &T { + &self.0 + } + + /// Gives mutable access to the underlying value. + pub fn raw_mut(&mut self) -> &mut T { + &mut self.0 + } +} + +impl Index for Unvalidated<[T]> +where + I: slice::SliceIndex<[T]>, +{ + type Output =3D Unvalidated; + + fn index(&self, index: I) -> &Self::Output { + Unvalidated::new_ref(self.0.index(index)) + } +} + +impl IndexMut for Unvalidated<[T]> +where + I: slice::SliceIndex<[T]>, +{ + fn index_mut(&mut self, index: I) -> &mut Self::Output { + Unvalidated::new_mut(self.0.index_mut(index)) + } +} + +/// Immutable unvalidated slice iterator. +pub struct Iter<'a, T>(slice::Iter<'a, T>); + +/// Mutable unvalidated slice iterator. +pub struct IterMut<'a, T>(slice::IterMut<'a, T>); + +impl<'a, T> Iterator for Iter<'a, T> { + type Item =3D &'a Unvalidated; + + fn next(&mut self) -> Option { + self.0.next().map(Unvalidated::new_ref) + } +} + +impl<'a, T> IntoIterator for &'a Unvalidated<[T]> { + type Item =3D &'a Unvalidated; + type IntoIter =3D Iter<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + Iter(self.0.iter()) + } +} + +impl<'a, T> Iterator for IterMut<'a, T> { + type Item =3D &'a mut Unvalidated; + + fn next(&mut self) -> Option { + self.0.next().map(Unvalidated::new_mut) + } +} + +impl<'a, T> IntoIterator for &'a mut Unvalidated<[T]> { + type Item =3D &'a mut Unvalidated; + type IntoIter =3D IterMut<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + IterMut(self.0.iter_mut()) + } +} + +impl Unvalidated<[T]> { + /// Returns the number of elements in the underlying slice. + pub fn len(&self) -> usize { + self.0.len() + } + + /// Returns true if the underlying slice has a length of 0. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Iterates over all items and validates each of them individually. + pub fn validate_iter<'a, V: Validate<&'a Unvalidated>>( + &'a self, + ) -> impl Iterator> + 'a { + self.into_iter().map(|item| V::validate(item)) + } + + /// Iterates over all items and validates each of them individually. + pub fn validate_iter_mut<'a, V: Validate<&'a mut Unvalidated>>( + &'a mut self, + ) -> impl Iterator> + 'a { + self.into_iter().map(|item| V::validate(item)) + } +} --=20 2.52.0 From nobody Thu Apr 2 15:42:27 2026 Received: from mail-pg1-f174.google.com (mail-pg1-f174.google.com [209.85.215.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 A74A8344DB8 for ; Wed, 11 Feb 2026 03:30:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780605; cv=none; b=UfYHbk9fQoIIPO5VrBsCb5aqOiSR3p+HlJ4HlMRVG6s8WBZ3g0E7S2Whja6XIc9vLwN2GzNyVpiFpt12LOBB35I2RoWrJlcYWalKbuDYeOmB+EeIkebiKP6B/A40VZyE0+H7R4mhDnVUPXHH9ngiIUNGLBfC8CynsKj/vaECUnM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780605; c=relaxed/simple; bh=z5H7IwFWyQ0i2GyTWoOMohX8+M5T1CFBH5xA9uQkq98=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=m1po/b6POfSa02v2yuU2YbTCvXmxOKoEtuw0pgXqSzjwvHLHY8wAi6Pk32QQJTDZAvwZKHiNLr3bENl4ryiqyVY8PplpIQuQjc5k5FIIL1jbSQ/zo+2XxAgGSU8BxVCjhGu89WxRcUypj7plw6BDuoLD31eEH+nAs6DxhLmkX/Q= 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=CqbBJRg5; arc=none smtp.client-ip=209.85.215.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="CqbBJRg5" Received: by mail-pg1-f174.google.com with SMTP id 41be03b00d2f7-c6e167e3051so128583a12.2 for ; Tue, 10 Feb 2026 19:30:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780601; x=1771385401; 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=WNztetKq9XMpM5m+98P429Tts0AUJfNMeagdulZTgAo=; b=CqbBJRg5eOrR1wC0PwQZUFUFwDfUR08HA+votC+wxakVbLHWeZEnqb9Mxt8lldghyt Vf8NxLIdJX3as1z7GCZz+iO1CA+o5Ei4gGk0YbHBYkOjKFVCIXRZsokIPWdp9pIe6ob3 Ur85ARnjFLYL4a/bmflMd/VVdh2q41jjPyybT/3RaXu6F6gw00eqlTf28MzsqoIqAPut d6Z6tyhr6Tcv+TTfXqxFLk3MBDki83qSToAIxtp4r5mCfsVE6JK5Ncbwr4eh9pwAZ1xe ekjRo4WGsH69F3RU2RfBepdaHBOIUy5ExUCzuaQF47VtUi0EyN38vFISTvb65cX9wKsg mk8w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780601; x=1771385401; 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=WNztetKq9XMpM5m+98P429Tts0AUJfNMeagdulZTgAo=; b=wy5EwsZvzQDmARBTNOsXvGHnoZGHSBlEDqOaHJnlXCKqsJfbIT19LziE6xKMRS47Os BWP1xsheUsDM2lGFQW+L26N8dFbx9ft243W7v+YFArLsAaBdCIPjbO8cxwg9Jo6peVqF Qiah/iIU/S6oYQhX2p4U3W93SWvmKkfehUA2/SNj1Mem+IHKuDIhSYvx0IaGB3ClLstK WZ7jmSuS7XRTKzrJMsy9NbtwGhlbdJ6E7xh//8eEJSMZy6sLzoYFqERxSGQ72uZ/2tep miDewHUVimjxLMd2ZcCs1mRy/XQ7TzcxEYKlfCAd/DJ5FyJZ6HoNpqjSqwemfqLJwQAw Td6w== X-Forwarded-Encrypted: i=1; AJvYcCVJHXiKaTSgzBaP+PQL6p8wLdcJBeqL7tkZDZ1pVwukeacBFh07LfbmM1c4M2Br8gBqLpzidgRH7I/rzv4=@vger.kernel.org X-Gm-Message-State: AOJu0YyjomzOnjJSQM/JVpnxOKnGGbzepX9QJz8GaslDAabVhtYaIO7N 7g4Cgs1cbuZa5TFXGC03WmokOlCbrIz+9cDBqiKMr4B7pDiiIYRadOle X-Gm-Gg: AZuq6aIdFL/SipqODp5+E3bN6z4L3/fCxv1WXDVT6umjVuyalw4pGUCbYRyXy1FFBo0 R7pMmmCsn2I5mRdcHeO/e1fgGEZS8KYE4d+zI427u05/gqtkzvrINwA1VtbM11mYOm+y4lyGnE9 gsdU5MxpGXIZbNGpvB6DVBmSAW0bvbJI7mH46izjt3og55SKzP2B+3a5xRl2ZyJBHzQsDKOeoD2 vYvpVnHt3XIU0Jpeh/VqAzlOpo6oI00PLc86+W/XbJ4NDx4eN4/X6zsZkwi/sFWzlTUPA2rq+rl 6356lijgoesYuBppDFkx23nFnkkn9o3uPiVzZ6DsU5sEtM1Iy0lapYnjGrajYauAXNCQ3sRfsDC KlbPIhvVxt+yeL5GCesTK+qhGF+RrkzDXuSJFEqTX3LOBFuEJ79W+EACluT2lt2Wz7lwFOkARPU vzUkaVUmPCd/qgj8xuyf4CBIsBEbPABifO2GM+9RZhMg== X-Received: by 2002:a05:6a21:3988:b0:387:9522:b667 with SMTP id adf61e73a8af0-393ad4398edmr16857908637.78.1770780600697; Tue, 10 Feb 2026 19:30:00 -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.29.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:30:00 -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, Dan Williams , Alistair Francis , =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Subject: [RFC v3 02/27] X.509: Make certificate parser public Date: Wed, 11 Feb 2026 13:29:09 +1000 Message-ID: <20260211032935.2705841-3-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-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Lukas Wunner The upcoming support for PCI device authentication with CMA-SPDM (PCIe r6.1 sec 6.31) requires validating the Subject Alternative Name in X.509 certificates. High-level functions for X.509 parsing such as key_create_or_update() throw away the internal, low-level struct x509_certificate after extracting the struct public_key and public_key_signature from it. The Subject Alternative Name is thus inaccessible when using those functions. Afford CMA-SPDM access to the Subject Alternative Name by making struct x509_certificate public, together with the functions for parsing an X.509 certificate into such a struct and freeing such a struct. The private header file x509_parser.h previously included for the definition of time64_t. That definition was since moved to by commit 361a3bf00582 ("time64: Add time64.h header and define struct timespec64"), so adjust the #include directive as part of the move to the new public header file . No functional change intended. Signed-off-by: Lukas Wunner Reviewed-by: Dan Williams Reviewed-by: Alistair Francis Reviewed-by: Ilpo J=C3=A4rvinen Reviewed-by: Jonathan Cameron --- crypto/asymmetric_keys/x509_parser.h | 42 +-------------------- include/keys/x509-parser.h | 55 ++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 41 deletions(-) create mode 100644 include/keys/x509-parser.h diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/= x509_parser.h index b7aeebdddb36..39f1521b773d 100644 --- a/crypto/asymmetric_keys/x509_parser.h +++ b/crypto/asymmetric_keys/x509_parser.h @@ -5,51 +5,11 @@ * Written by David Howells (dhowells@redhat.com) */ =20 -#include -#include -#include -#include -#include - -struct x509_certificate { - struct x509_certificate *next; - struct x509_certificate *signer; /* Certificate that signed this one */ - struct public_key *pub; /* Public key details */ - struct public_key_signature *sig; /* Signature parameters */ - u8 sha256[SHA256_DIGEST_SIZE]; /* Hash for blacklist purposes */ - char *issuer; /* Name of certificate issuer */ - char *subject; /* Name of certificate subject */ - struct asymmetric_key_id *id; /* Issuer + Serial number */ - struct asymmetric_key_id *skid; /* Subject + subjectKeyId (optional) */ - time64_t valid_from; - time64_t valid_to; - const void *tbs; /* Signed data */ - unsigned tbs_size; /* Size of signed data */ - unsigned raw_sig_size; /* Size of signature */ - const void *raw_sig; /* Signature data */ - const void *raw_serial; /* Raw serial number in ASN.1 */ - unsigned raw_serial_size; - unsigned raw_issuer_size; - const void *raw_issuer; /* Raw issuer name in ASN.1 */ - const void *raw_subject; /* Raw subject name in ASN.1 */ - unsigned raw_subject_size; - unsigned raw_skid_size; - const void *raw_skid; /* Raw subjectKeyId in ASN.1 */ - unsigned index; - bool seen; /* Infinite recursion prevention */ - bool verified; - bool self_signed; /* T if self-signed (check unsupported_sig too) */ - bool unsupported_sig; /* T if signature uses unsupported crypto */ - bool blacklisted; -}; +#include =20 /* * x509_cert_parser.c */ -extern void x509_free_certificate(struct x509_certificate *cert); -DEFINE_FREE(x509_free_certificate, struct x509_certificate *, - if (!IS_ERR(_T)) x509_free_certificate(_T)) -extern struct x509_certificate *x509_cert_parse(const void *data, size_t d= atalen); extern int x509_decode_time(time64_t *_t, size_t hdrlen, unsigned char tag, const unsigned char *value, size_t vlen); diff --git a/include/keys/x509-parser.h b/include/keys/x509-parser.h new file mode 100644 index 000000000000..8b68e720693a --- /dev/null +++ b/include/keys/x509-parser.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* X.509 certificate parser + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#ifndef _KEYS_X509_PARSER_H +#define _KEYS_X509_PARSER_H + +#include +#include +#include +#include +#include + +struct x509_certificate { + struct x509_certificate *next; + struct x509_certificate *signer; /* Certificate that signed this one */ + struct public_key *pub; /* Public key details */ + struct public_key_signature *sig; /* Signature parameters */ + u8 sha256[SHA256_DIGEST_SIZE]; /* Hash for blacklist purposes */ + char *issuer; /* Name of certificate issuer */ + char *subject; /* Name of certificate subject */ + struct asymmetric_key_id *id; /* Issuer + Serial number */ + struct asymmetric_key_id *skid; /* Subject + subjectKeyId (optional) */ + time64_t valid_from; + time64_t valid_to; + const void *tbs; /* Signed data */ + unsigned tbs_size; /* Size of signed data */ + unsigned raw_sig_size; /* Size of signature */ + const void *raw_sig; /* Signature data */ + const void *raw_serial; /* Raw serial number in ASN.1 */ + unsigned raw_serial_size; + unsigned raw_issuer_size; + const void *raw_issuer; /* Raw issuer name in ASN.1 */ + const void *raw_subject; /* Raw subject name in ASN.1 */ + unsigned raw_subject_size; + unsigned raw_skid_size; + const void *raw_skid; /* Raw subjectKeyId in ASN.1 */ + unsigned index; + bool seen; /* Infinite recursion prevention */ + bool verified; + bool self_signed; /* T if self-signed (check unsupported_sig too) */ + bool unsupported_sig; /* T if signature uses unsupported crypto */ + bool blacklisted; +}; + +struct x509_certificate *x509_cert_parse(const void *data, size_t datalen); +void x509_free_certificate(struct x509_certificate *cert); + +DEFINE_FREE(x509_free_certificate, struct x509_certificate *, + if (!IS_ERR(_T)) x509_free_certificate(_T)) + +#endif /* _KEYS_X509_PARSER_H */ --=20 2.52.0 From nobody Thu Apr 2 15:42:27 2026 Received: from mail-pg1-f171.google.com (mail-pg1-f171.google.com [209.85.215.171]) (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 DDA2A345CAF for ; Wed, 11 Feb 2026 03:30:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780610; cv=none; b=juvTfF0pP1jUDDdVBfUrPeiPLnZaWAIu7o/pzn4MjsJ20+dr/MSviOJj9wIU3ukDMcdavZgDC97m0NeGOfSi73nhgEbSQ7CA5IKcYQngRLWdp23quuvsTL3Bmw8/y3kBmTOdanBODvolL7CKZv9Ver6Qnt0Bj+H5zZaUirJXdAM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780610; c=relaxed/simple; bh=ozbIZy3q85Dwef2HPMNXXszYSCcI73lO3Bhup+v9oFs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=BHwL+NE7zMJawIkizKjU2UokdUQdAvuhvwVtWvrPkcqzIDB++WvJsvG1XQbwPXbQVg3CTnyMO+GrdoEYooXGOA5JP5wMo9hyx+bhNzgoFcYNINwY1scvNftFvKNBZAkAlANN6oFYc7KdU/tu8T+OkbShwapAFVsHKAHnzMcNoBc= 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=Srje6/uh; arc=none smtp.client-ip=209.85.215.171 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="Srje6/uh" Received: by mail-pg1-f171.google.com with SMTP id 41be03b00d2f7-c648bc907ebso4095875a12.3 for ; Tue, 10 Feb 2026 19:30:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780608; x=1771385408; 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=Fa0FmCWNCDjXutoddDzbqHV8AHD3cr+ph1+8htfRvzI=; b=Srje6/uhIfA5S2tYOicqviw7lqiEUIzTSW093J/3rzJBjj2IfwI10j2jKx4L2HS3Sh Wa2TX2Jp/CBejexUsJcJzEbqcWwYnautmAAub+0swK3PGzcikj0wUzw/5woki4M7hHY2 GYDB4SKNQNirHbrquo56wg2zfiDaRVi+BjUm7wb4WT/rdtcJ2ZpMubJ2f5R95ZGYWMSd EyDJPkdzm86Ef7t3iVNL36AWHBP97qLAV4wnYQDvcEK932HQTtgs8I03xalLbl++vyZJ iLdDayrCqKwX1oN3z3rxi7BLSZUDJWsHDXT5M8rxByvjQ4brFjCjWsiRLP3wm5/AIZzX +AuQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780608; x=1771385408; 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=Fa0FmCWNCDjXutoddDzbqHV8AHD3cr+ph1+8htfRvzI=; b=wIMTEw35VNP+3gcOaWTZOPXu3sufxYRa6U8DBjnHXseumWB09YtxNxOmC9NT7b+qbK FzEMvwe94H4fUUQEOMyDksKmgVmIsMb7i04fNQlpCje0kWevBz9kqLMlkpv4+QHu2dbw d4cV8YRwxAF9aHZhMiRdHicFg9Xic5HiIdiSs7mhK2kT/Nc5S47ZSwDFT+PF2iYdt/by 0w1WifbtsRywq4Tcb/rqoxCsYyMhZLJdpZpM2mrIMV0iOPMKQxSHDQiRsf48lByz2JoS HyQPnOmTgnQ+5ci7gDcTd7M/xIRPXB2ddl9oaNtDaIYeWlfpTMWd7jM1bYXu7tmCaGzV XMCg== X-Forwarded-Encrypted: i=1; AJvYcCWUYESxgcovasZQvkVvdOvdxYIITGUKStwAbAsGzC11WnWXnGuHX1LdBFGd33sQIilA835PBWZ+A1GPzL4=@vger.kernel.org X-Gm-Message-State: AOJu0YzAhoV7IJfsocdRYAtyQnk+f2V/JasIo0fQVI0dyTBnTcczY+Hj NdDov3Uvh/5XnsGohUCIwSlHmFpplHMkScKpvL2DSQFOnirkqlN1tdJD X-Gm-Gg: AZuq6aJbZXWmPtstUb6YLrZk6Od712l90Kvq2U8GnHI0Z+2KpAa8yQp8Y8RREwxTim3 yjqME2ouL1yGSeBQv1aUZE9xxPvSCgUBNEQTN4lQxHB3QtmGUEcYBv7rIhOEVA9KKe5aW7VyYiI Vap5VTW1a/3642YXwpDA4sesenC26mXgiZhnn5Ck9Mf15ebJOQhDcMOYId/3B1Z3+mDPS5pIe4a ibC/zGpcQvre6DLiyIF0sjymoROMhuVlOTp9iLsqIVlYSJ0OVRo+K26XCPY/czJZFhdYtoDFavO EuDNi+uJ00602Osn2eiaSZ5wG+K8d4eh9wl/3EQQxBqFBJ4WpL6dpItm6ktxq1sScTz6wZIOlNP fDyc37zNk+3Ag9EeSXxsfhkMYvyHaHzvAO9hTROe8XBOWGIzpadMEXECzSPdtvMAhc5ZtL4tXts Ex+Pzy92XeFR03JlCQmkdBgxRM07tDaZukhutC3dFDXw== X-Received: by 2002:a05:6a21:4592:b0:366:1934:5234 with SMTP id adf61e73a8af0-3943229a8c9mr549814637.2.1770780608223; Tue, 10 Feb 2026 19:30:08 -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.30.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:30:07 -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 , =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= , Dan Williams Subject: [RFC v3 03/27] X.509: Parse Subject Alternative Name in certificates Date: Wed, 11 Feb 2026 13:29:10 +1000 Message-ID: <20260211032935.2705841-4-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-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Lukas Wunner The upcoming support for PCI device authentication with CMA-SPDM (PCIe r6.1 sec 6.31) requires validating the Subject Alternative Name in X.509 certificates. Store a pointer to the Subject Alternative Name upon parsing for consumption by CMA-SPDM. Signed-off-by: Lukas Wunner Reviewed-by: Wilfred Mallawa Reviewed-by: Alistair Francis Reviewed-by: Ilpo J=C3=A4rvinen Reviewed-by: Jonathan Cameron Acked-by: Dan Williams --- crypto/asymmetric_keys/x509_cert_parser.c | 9 +++++++++ include/keys/x509-parser.h | 2 ++ 2 files changed, 11 insertions(+) diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_= keys/x509_cert_parser.c index 2fe094f5caf3..363acd87dba1 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -596,6 +596,15 @@ int x509_process_extension(void *context, size_t hdrle= n, return 0; } =20 + if (ctx->last_oid =3D=3D OID_subjectAltName) { + if (ctx->cert->raw_san) + return -EBADMSG; + + ctx->cert->raw_san =3D v; + ctx->cert->raw_san_size =3D vlen; + return 0; + } + if (ctx->last_oid =3D=3D OID_keyUsage) { /* * Get hold of the keyUsage bit string diff --git a/include/keys/x509-parser.h b/include/keys/x509-parser.h index 8b68e720693a..4e6a05a8c7a6 100644 --- a/include/keys/x509-parser.h +++ b/include/keys/x509-parser.h @@ -38,6 +38,8 @@ struct x509_certificate { unsigned raw_subject_size; unsigned raw_skid_size; const void *raw_skid; /* Raw subjectKeyId in ASN.1 */ + const void *raw_san; /* Raw subjectAltName in ASN.1 */ + unsigned raw_san_size; unsigned index; bool seen; /* Infinite recursion prevention */ bool verified; --=20 2.52.0 From nobody Thu Apr 2 15:42:27 2026 Received: from mail-pg1-f175.google.com (mail-pg1-f175.google.com [209.85.215.175]) (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 1E0571A238F for ; Wed, 11 Feb 2026 03:30:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.175 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780617; cv=none; b=cmPXg9n879OMVhvoWUVWOOHC7b/2QA/QJaWhaOblSEL/XDY73/WcpQAKjDachEon2FSJUGydp5ZHZhocnusncXjPfQu+uUvwgC5Hvv7XAX6S2fwVyuXSeopqOYHGOJLpozja05AG8g8AnnUb0JWkJqIw5K3lBbdMc0bouXb5OlQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780617; c=relaxed/simple; bh=ywX2QQkJINgcF5rI6352/2UoVw6y6LLcc5xcEUpnPLE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=rl+a3D+IR52IsrxRZsk3+3oQB8hBqbCvzroLk5DJOomQbkFPhZgbJsZDXrk7lIPuqe8g/LEX8uB4J0zkLeZ8CArl6U+lUm/KljczaTJcd4uci9zIqNFGLB6xK1ynzeKHpucTwuzn4uc/tb1TJRFJjxJLvpXcpjrah1a6GWgDJdU= 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=lgBBR4jU; arc=none smtp.client-ip=209.85.215.175 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="lgBBR4jU" Received: by mail-pg1-f175.google.com with SMTP id 41be03b00d2f7-c6788f3db37so573784a12.1 for ; Tue, 10 Feb 2026 19:30:16 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780615; x=1771385415; 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=0pamfLAjX62QtHOtvvGRA+RHxyD1vV0e3dMD4KWIMlE=; b=lgBBR4jUmLdS/ZJqcJEbs3xwcm2d3xrOv86huUcb+L6/yMG7cKGeV6PIfLNEUbYnay piwLgQCJuVyexOn0lJS2+ZLbFIYEViTN1sYiIg0DbQJoJbzm+bnw6itwtTFK6tZj9FJB NCebZvynBcnZ3RSrh1xBJrUAWqaPuNGZCeOZOjiceiYv6aVm7B7iMuT1KGmOXnQsysUc UHooQ841IS2IqLBAGIy+wBYMl5I+HrRuDLF7uy0pPhjbqxMx8oGjwtUtwGjW2TQTIu7P ywGKm87Q1eI3IbkWj6SkQtKFtqzP2eUrdU9Z4IEf9bCzAeiMaeBFDTPRG8X1eOBNnhPp fkyw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780615; x=1771385415; 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=0pamfLAjX62QtHOtvvGRA+RHxyD1vV0e3dMD4KWIMlE=; b=eqjgV1eAOsUqohMDzWUjNzzyCWLovPA2Su6L871H5WYJGyGyqNS5nuyqeYEq8wfAIv ENKExWPXmpvCptdZMTRR7mHcvw+LzbAmRDgPzfTUjkoYtgmowfviTzzm3Fg0G8M2wQE4 QNIjlBA5+laRQCwGcgKmpdWZ7NShJqswRcaQycIfbWJTmp/dYWkDM0jaszrMNnMciryu /PQGfXfUzW/+716kRylavF2Avt6hcaQKrgsEaZnP5xEJHiBrVsvmCfJF2v2/r/8ndJao Qmu+mdI7Vsbiu7AktQmrE1DqnkXToSiQp1XpxKj4trgknJeTtsopXWfHQL26kUZTu0Ww Piuw== X-Forwarded-Encrypted: i=1; AJvYcCXP6RXSpZ06+0BWCNTDYw7T2uvCLlilVylmYn4tqfvjm7lLOha/1dJYUmZR0zYmyE7vGQyTKUB9RGDJpYs=@vger.kernel.org X-Gm-Message-State: AOJu0YzFEpZSOte/WG2U9x0oAem1bcnJbTHEisF4nUa2QbQ1SBdVD/yI Qdg/6aiwLVQbeYX5ygFrqJ6k93lozdkLNREp8vMZOgKP63ACKVScZDla X-Gm-Gg: AZuq6aLDmLa+VHCfyrH3Mx6AdTONZCPgMbMfkKoAmGhVUegLb9neFFGnD7S4TqWc2Uy aRKNrbMkqUADj5qQUihWJrxITzeUIKZkfKQjcxhdnENZLOTHKoRzi5BCKHpJf5k8vjP3pM55Nm8 ZlEdqtUvgI6vOSA7iOnRkfBY/IiYhz0pS2SXpRXGs0mhuDtfcTMJRWAUWLbJGvarCdAF8hZhR/2 eF3GHznjxhXdAjtn9G1t5q2PZtu5iFUwd5arwYo4vxAdOS3tuPAduQqCb2zOIgK4PKxactfDaJ7 PMI5y1y50AVFCTBZK6zmK+rKmJ7YpEvcdhwUnc+9A5rxaqZYdOf0taMpeHTk142rSYkoym9qU2Q juQXJPk0uIWnX/UVvmuz9KKBNleQmyi3WR5xbG7NEQAvKSh9g7VKIg0+DbI9VvTVLqabxkuRRCE APIrUGZw64LtzmxQv6yA6eW23KcOzXXH5kcLaQ4VCMXQ== X-Received: by 2002:a05:6a21:6016:b0:366:14af:9bd8 with SMTP id adf61e73a8af0-393ad3ec860mr17499551637.78.1770780615453; Tue, 10 Feb 2026 19:30:15 -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.30.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:30:15 -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, Dan Williams , Alistair Francis Subject: [RFC v3 04/27] X.509: Move certificate length retrieval into new helper Date: Wed, 11 Feb 2026 13:29:11 +1000 Message-ID: <20260211032935.2705841-5-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: Lukas Wunner The upcoming in-kernel SPDM library (Security Protocol and Data Model, https://www.dmtf.org/dsp/DSP0274) needs to retrieve the length from ASN.1 DER-encoded X.509 certificates. Such code already exists in x509_load_certificate_list(), so move it into a new helper for reuse by SPDM. Export the helper so that SPDM can be tristate. (Some upcoming users of the SPDM libray may be modular, such as SCSI and ATA.) No functional change intended. Signed-off-by: Lukas Wunner Reviewed-by: Dan Williams Reviewed-by: Alistair Francis Reviewed-by: Jonathan Cameron --- crypto/asymmetric_keys/x509_loader.c | 38 +++++++++++++++++++--------- include/keys/asymmetric-type.h | 2 ++ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/crypto/asymmetric_keys/x509_loader.c b/crypto/asymmetric_keys/= x509_loader.c index a41741326998..25ff027fad1d 100644 --- a/crypto/asymmetric_keys/x509_loader.c +++ b/crypto/asymmetric_keys/x509_loader.c @@ -4,28 +4,42 @@ #include #include =20 +ssize_t x509_get_certificate_length(const u8 *p, unsigned long buflen) +{ + ssize_t plen; + + /* Each cert begins with an ASN.1 SEQUENCE tag and must be more + * than 256 bytes in size. + */ + if (buflen < 4) + return -EINVAL; + + if (p[0] !=3D 0x30 && + p[1] !=3D 0x82) + return -EINVAL; + + plen =3D (p[2] << 8) | p[3]; + plen +=3D 4; + if (plen > buflen) + return -EINVAL; + + return plen; +} +EXPORT_SYMBOL_GPL(x509_get_certificate_length); + int x509_load_certificate_list(const u8 cert_list[], const unsigned long list_size, const struct key *keyring) { key_ref_t key; const u8 *p, *end; - size_t plen; + ssize_t plen; =20 p =3D cert_list; end =3D p + list_size; while (p < end) { - /* Each cert begins with an ASN.1 SEQUENCE tag and must be more - * than 256 bytes in size. - */ - if (end - p < 4) - goto dodgy_cert; - if (p[0] !=3D 0x30 && - p[1] !=3D 0x82) - goto dodgy_cert; - plen =3D (p[2] << 8) | p[3]; - plen +=3D 4; - if (plen > end - p) + plen =3D x509_get_certificate_length(p, end - p); + if (plen < 0) goto dodgy_cert; =20 key =3D key_create_or_update(make_key_ref(keyring, 1), diff --git a/include/keys/asymmetric-type.h b/include/keys/asymmetric-type.h index 1b91c8f98688..301efa952e26 100644 --- a/include/keys/asymmetric-type.h +++ b/include/keys/asymmetric-type.h @@ -84,6 +84,8 @@ extern struct key *find_asymmetric_key(struct key *keyrin= g, const struct asymmetric_key_id *id_2, bool partial); =20 +ssize_t x509_get_certificate_length(const u8 *p, unsigned long buflen); + int x509_load_certificate_list(const u8 cert_list[], const unsigned long l= ist_size, const struct key *keyring); =20 --=20 2.52.0 From nobody Thu Apr 2 15:42:27 2026 Received: from mail-pg1-f178.google.com (mail-pg1-f178.google.com [209.85.215.178]) (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 A20DA344054 for ; Wed, 11 Feb 2026 03:30:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780624; cv=none; b=R14//oQtG1jsynCIDE3AnDdGiAWKRSZr7FDR8Ku6eojZ9D/fGhYtA7u7IZnqrL/8eBDNtOOgZqQyda9jAc45dM8dK78aFgfRKa4+gmbVUxAPCNLu5fuKANiKe/vecFzAdxTT7WeSa3f6uWR5a5CIaGqpRqB9S2qKMYOprsigKFg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780624; c=relaxed/simple; bh=CmwKf7zbMS8N3K/wJq8ojeQQeHe1E74I8u66XXYuYnc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=oWSeSlqiMjUSKRQwzIcb+IA5SmfJs4wxr7x/OsOurqyXa0RiRtYZ7J8txCK4JOtU5E0Mn3X4akVqJmZjvrtKH12UgygSBtPWyyGWQ8v3mFqpQ54F1/XrkOP0Vfy3Ii2NFlrNFbKN1iwlzKuJM8XoDknHf+jjZocA49ZQrFfqUQg= 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=PJR2FQNe; arc=none smtp.client-ip=209.85.215.178 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="PJR2FQNe" Received: by mail-pg1-f178.google.com with SMTP id 41be03b00d2f7-c2af7d09533so4343901a12.1 for ; Tue, 10 Feb 2026 19:30:23 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780623; x=1771385423; 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=wadzZAQKDnG/6H+AFZHuWAhAOhXcknipq6nDnMb1Z+c=; b=PJR2FQNeHOJhP4on+3hI5sPz7IVtDNiZsIREurNI0H5fYMtaDZ90jBBSiaEprSGd1E 7RkeLvvwUMGa6yH4sNRXFBTDuIMWDkS/PxsNrtbirzVAgP15Kc3QuS22km1CUVwKj66L vtOzNvAdwikyf2/Z/eQZWkAVrUTXJykNMqJ/DB8UbMaXGNnTEE+vJXN3XlEybAoFC7IM c5LkM7QzuSpAjAjood9xMtzqvCUb2jOqz0KT74Qg5XvISlq9n9fJDIpzruKU8OJv4k68 qwDtudv//pJtpKBUiPO4+7Z0swXZuQeU4WDTmpyLrHDmS1MHkvX7i7+NBZu25Wr0lOJq 1WUw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780623; x=1771385423; 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=wadzZAQKDnG/6H+AFZHuWAhAOhXcknipq6nDnMb1Z+c=; b=rYy4yZKN9DKyXniylp5rj+XM2D2bkvbCmz4Fnw2K7D9RPmEkz1eNr4RLgrLQ7Q3cei 0c67+vgJSQ1LflKmDy+ZeFl0FqvdiVxuJ2A43SKtnUGM7nyGgTkGOPFbcn6s42rf6kVs tlI2O3qJVsoNAVp5NaPZnznKRbG9xRY4tLYyWkZH9F3vYBuagcnJs9MBzAs8HuiFHr+m iKbfCFVZ4nQNIMeQyERf1APxWM54kmPZlWL5qwMkBT+KAHnY7SUjXOkmh6FLaOawrsvE 40cDf4haxmjJRvfE0b7y5CepBNuDXZG52t7gyzJVNrsCF+6kPu7Jot80/t4Ql8ui7sSx a5Jw== X-Forwarded-Encrypted: i=1; AJvYcCUW28bl7yBBCa7T9BFAmQDSlwEnhP9NpZ638fQjXrN/K7BC2nm0O1b0VKKaMArz1xTIpb36bu/dalEtOtA=@vger.kernel.org X-Gm-Message-State: AOJu0YwLkKaFxky05L9Xle4JzG9vc0wvOS6iCjr+SRKEEpVUmJuic9hL fb5VDg69aMfyA7o2CwQ6DC9cu3BqNi41Pgf87zs5J33/Wf46npkzQabK X-Gm-Gg: AZuq6aIj1nl668ZHhvb2RZh5N6A0dAIFnaYGn33/1AZLz/Bwi1U8DHYo4mPvRBsZULs IRP9G/8vCOvtjjvys7InyInmOYwtGZTHvVxZr5ZYPQJ/l8G0GDyclyMNkPk6dB2EvkVFLr8VrZK SxZmxwc0u/vFit0/8/jNGpBxxFJK0W8hZihj9XwV9AeqEBscuPBnJ12tWSlmOvX6/WybWcvIw+X 49sgZ22c+xWvpjdUmHEOGStaDgb4V+mThKkCimUhPbYlygznGguDMPyrodRUnjTrAEBIQFnwWjl raygIIldlJLrRKXO0vdQZ4havlbYF+z0DesWlPR5weppiSMctwPvOi7LbjLkxcuFKd0kjy0VNw5 o2qcJ4G6CKyezaHs62ZtKvNQ/I7ZFE/rWe+FMnwW4VQVZ20Uiwl9kBRmJoG9d+xPpzvUmTP15e9 AeZqQRPBtTt1+4bOmiG39J4OUL3WMG8ruwmza3jYmPdg== X-Received: by 2002:a05:6a21:2d42:b0:366:19a5:b492 with SMTP id adf61e73a8af0-3943229a6c2mr704286637.5.1770780622980; Tue, 10 Feb 2026 19:30: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.30.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:30:22 -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, Dan Williams , Alistair Francis , =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Subject: [RFC v3 05/27] certs: Create blacklist keyring earlier Date: Wed, 11 Feb 2026 13:29:12 +1000 Message-ID: <20260211032935.2705841-6-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-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Lukas Wunner The upcoming support for PCI device authentication with CMA-SPDM (PCIe r6.2 sec 6.31) requires parsing X.509 certificates upon device enumeration, which happens in a subsys_initcall(). Parsing X.509 certificates accesses the blacklist keyring: x509_cert_parse() x509_get_sig_params() is_hash_blacklisted() keyring_search() So far the keyring is created much later in a device_initcall(). Avoid a NULL pointer dereference on access to the keyring by creating it one initcall level earlier than PCI device enumeration, i.e. in an arch_initcall(). Signed-off-by: Lukas Wunner Reviewed-by: Dan Williams Reviewed-by: Wilfred Mallawa Reviewed-by: Alistair Francis Reviewed-by: Ilpo J=C3=A4rvinen Reviewed-by: Jonathan Cameron --- certs/blacklist.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/certs/blacklist.c b/certs/blacklist.c index 675dd7a8f07a..34185415d451 100644 --- a/certs/blacklist.c +++ b/certs/blacklist.c @@ -311,7 +311,7 @@ static int restrict_link_for_blacklist(struct key *dest= _keyring, * Initialise the blacklist * * The blacklist_init() function is registered as an initcall via - * device_initcall(). As a result if the blacklist_init() function fails = for + * arch_initcall(). As a result if the blacklist_init() function fails for * any reason the kernel continues to execute. While cleanly returning -E= NODEV * could be acceptable for some non-critical kernel parts, if the blacklist * keyring fails to load it defeats the certificate/key based deny list for @@ -356,7 +356,7 @@ static int __init blacklist_init(void) /* * Must be initialised before we try and load the keys into the keyring. */ -device_initcall(blacklist_init); +arch_initcall(blacklist_init); =20 #ifdef CONFIG_SYSTEM_REVOCATION_LIST /* --=20 2.52.0 From nobody Thu Apr 2 15:42:27 2026 Received: from mail-pf1-f171.google.com (mail-pf1-f171.google.com [209.85.210.171]) (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 9C8C3344D8C for ; Wed, 11 Feb 2026 03:30:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780631; cv=none; b=COLMNIhcLEQuHvDm29BJ4t4MC0yQSghLO4bHQ9dkRcxzRlYp9Lc/1aixGuAF92NpGnLcFIp4WahA+nKhjfbwmDy1BNIouzxDaBxboFeZ4AuosrASvFUXo9fQKUvkOPOFekVbgzS7/FpvFxs1nRTqy6fxoAtD/mOP87Mzaav0YwY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780631; c=relaxed/simple; bh=3KwgssT0qLSW0xBZrdG5K1brZw1i/pcHLAEb0PzMGv4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=fXjfokY4oPflorKZpvSm5BX7GOMgJ/ndsXk5hUqLoObCYOR7/AUnuKpNiMRIvijtxl/eMiYTenFhG17ehK8W9no0pUV9crRSdnaGXbeZ5BDS/KiHO6e/63TCqqpDFVlX17SAXKXbR9oahrm0k0zQWwacVgeWM9dvlBv8FVRpcvk= 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=YquY9jk2; arc=none smtp.client-ip=209.85.210.171 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="YquY9jk2" Received: by mail-pf1-f171.google.com with SMTP id d2e1a72fcca58-824a2888de5so5864b3a.0 for ; Tue, 10 Feb 2026 19:30:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780630; x=1771385430; 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=dg944eAYwPSQ7NjEWDDm+/KZG6xsl7b2l82fMEgSYcM=; b=YquY9jk2WfObP4B0UhrgKBsE5Xsdah95vDEj1c2QbseqZ5rmtp9Ay3OW1mcYUA7Hdw rjuskHzsqqCMLdDfMfYkkvT2ZxlTNfrLxie5R/ojvaeJrZ9IlME0s2peFAP79tBFIsQV MxxdnLJeDeU/D3CbQZDj3j0/9kHX1T4zGIx7otee6HZCiKaTBK0v/dszkhL1fuqxs/CX AxwzoHFnwj/0ib8X+DUFAKXzQSLEWNCFAUZwdSQYlySC4S7vIvjJUnypBnqAX/wMXNDL dd4WcAW1Bj9tufjzt5k1JSZ2CuFOCbAvxCp+5TwqbyZ/kUpXs3vY1Q2VqvNZ5tzeRy2T 4N4A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780630; x=1771385430; 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=dg944eAYwPSQ7NjEWDDm+/KZG6xsl7b2l82fMEgSYcM=; b=PcCgMAG18qn5J/lWgI6uqilw/lU6OfdQT/kzevap/tR3HkgI/cnklmFfXLHvQkxSRj gzrrhgswN4sU27mJppTdkhSi8LqPyw1ffs/RcmIDWYHaFe6P/E+SZmEGkpRYgv7ebxuQ b94u8wt1dnZY3YMZNAiyB3sQoU9myP3/T4nYrlGmCaY7J+FO38+XhKgaYtYoN+hdAfzL PGC6sHoxF8mQfwbrMuEX694sFPR/BZgwe0kAjbVEqHO4VJbsFKD/ePuY1UBgzG5ZOG1G FdsA7kpoUtZDyctIoyqeZyxhfemNelGI4l7sMQu0rLZl5blSoyuaxnkf6BpZuw8R/tim WYrw== X-Forwarded-Encrypted: i=1; AJvYcCU2JXESPq9sAbmAZmRCg0omldcs6HLjdl+v36ZZX2zwj2wPQF0+7dOWyImrW8MWwHGZqSNbdKfov/0hT+o=@vger.kernel.org X-Gm-Message-State: AOJu0YwR6vI+y0bPuLScAn7VUzygAc4EJHsbyPayJ+FxQfpRM4Vbzqqa EE4jHyETUi/oP/1SCV5mtCAsHizwn9Y9AbWQgVkLihejkXju3jzHJG+0 X-Gm-Gg: AZuq6aJA87lR15lf1viY+Bt8Yk0H7S3XWNep68D62mF3WIc1pjbLibiee9gA/c4rpxA MK6dn9pCODAfe77Nxh8lyzQoXRnAOt/pha+6kQY/pjsDBrsNtfBRr3tF9JyXESCx+JabssBxrcE lZdnJ0IcNAxi4DbSSMc0su/NwIaE56kPuL4WCF8DEDLz+B4Ux46Pm1SgEggIomaue++6CXyx/6d N+h5k3GLLF/dcon/JDn+8lzylvcKVBwdXy6+YlxOdnMtxY3xXL3CTu7+xzDrZJsKRiu9Im9Wq59 ovE8yVUE26FZL0e6VO5iqQL3HeUCOnklv0S1d+08HGTT0i5hWCNqsd38DsZqRusRAJKNPbB2xbb EdubmFTDYvjtM5PMiE4mkt72VgEafV2c4PMuLvIycIY6QDqqnfRnWnPs7yyJh9IyxH57PWgBEtE H08dZUH2CrQUNgoaYyxzFQs7l3dpvceRe4b8pPXm+jzw== X-Received: by 2002:a05:6a21:e58b:b0:375:4503:ef0e with SMTP id adf61e73a8af0-3941556bee6mr4592807637.14.1770780629942; Tue, 10 Feb 2026 19:30:29 -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.30.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:30:29 -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 06/27] rust: add bindings for hash.h Date: Wed, 11 Feb 2026 13:29:13 +1000 Message-ID: <20260211032935.2705841-7-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 Make the functions crypto_shash_descsize(), crypto_shash_digestsize() and crypto_free_shash() available to Rust. Signed-off-by: Alistair Francis --- rust/bindings/bindings_helper.h | 2 ++ rust/helpers/hash.c | 18 ++++++++++++++++++ rust/helpers/helpers.c | 1 + 3 files changed, 21 insertions(+) create mode 100644 rust/helpers/hash.c diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index a067038b4b42..0075c4b62c29 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -60,6 +61,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/hash.c b/rust/helpers/hash.c new file mode 100644 index 000000000000..8ddb84668841 --- /dev/null +++ b/rust/helpers/hash.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +unsigned int rust_helper_crypto_shash_descsize(struct crypto_shash *tfm) +{ + return crypto_shash_descsize(tfm); +} + +unsigned int rust_helper_crypto_shash_digestsize(struct crypto_shash *tfm) +{ + return crypto_shash_digestsize(tfm); +} + +void rust_helper_crypto_free_shash(struct crypto_shash *tfm) +{ + crypto_free_shash(tfm); +} diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index a3c42e51f00a..4b08b4f1d3a3 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -30,6 +30,7 @@ #include "dma.c" #include "drm.c" #include "err.c" +#include "hash.c" #include "irq.c" #include "fs.c" #include "io.c" --=20 2.52.0 From nobody Thu Apr 2 15:42:27 2026 Received: from mail-pg1-f171.google.com (mail-pg1-f171.google.com [209.85.215.171]) (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 915FB1A238F for ; Wed, 11 Feb 2026 03:30:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780639; cv=none; b=kgiI1denXzMYWZnt+z1K2QsGVutUsJO0WjoDEYZ0rZJyHNEDS1vDnqTfsNdwxF2FgR20XjYDwkqfkhdHKmPDM0xFy+rQRuVWinzM5sqHE0BuUGDcE5wabwU9LX32VFWcDVzPOvUWNbIl8FwHzCFyzf4n9V3It1ZPhoLkXdWwhIs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780639; c=relaxed/simple; bh=FppKemrwtWk7l1BEg98yFq7nk3dgLMRXUW1h9vBpl4s=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=iMY0rRVU/jj+9d6gGAocFg1hre5LJyFKoHGnKoAVZNPgoUjxehp2EQtvbacrXJAXSNDVbtN++npUe/2+RNVk44s+CiUCOxHESK/cwRAoVwuWOvzzJrWjrWl4jdO7Ge9jKeCaO1EUyifV6vAJ093uf+TiGnKb7GsWiamhcrGnH0g= 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=AqI6ICbz; arc=none smtp.client-ip=209.85.215.171 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="AqI6ICbz" Received: by mail-pg1-f171.google.com with SMTP id 41be03b00d2f7-c6e167e3051so128740a12.2 for ; Tue, 10 Feb 2026 19:30:37 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780637; x=1771385437; 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=viBpYkVWc9cE68UFiQJOVrqrE/EpXnGovXyJD9hjSPs=; b=AqI6ICbzsb2BaM28dkfJzC1s0VCKby2Jjh0sYOshnzToGY8D4kJ+W80yGkWmBMtNpq uxV0fch3G3Canly3z/cAKYQdhFpd8wfRtpwc345q9o2hIX9EeiW9dmZdQZYgWNBBWby3 W6oVJDGwtLxxeKosU8VGgQrR6PTQxTmgugZZt3n0X4XqlJw9mR9RknD6FbrT1emihhtd aA3ZLWp0V5xaYgDBLmWyOE3JmD+falHdgj7vwLcksen/3XzqW2Fyw/ULq39gZ80+9hUq yysM1hPLQppWvNe1q1G4m8EQ4ofLDLTKthYHFzydo7x3N+sDtBwc08mdUt0T2bQi569V nMPg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780637; x=1771385437; 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=viBpYkVWc9cE68UFiQJOVrqrE/EpXnGovXyJD9hjSPs=; b=PvtmKXaxgfaZeE6Uvskhdj+DLPbnHiBaldZr9hH455so5yvF+agOAx0HKj2KX4DZs8 FcIEFotNSFopcRNIuPojYuT2+n5IwcolaC2fgRCnSUnR4axgECqb9eFxfclxpwMGdpAP KnxQS1xvKI7ygka3LXjw5xFkDGz1IPwXnlJAmGITMrWsmj37vPhHEmzz9XjZI/ofHWI7 9IbjoMvdod1EWrJkueDygT7B6n1FbJOkG+stRtkEUe2JK4YAZIIrOA++hbrFXCTxtyfy ijN/30TIiU+tsUCVk7zFiEGX5GcinbVXMYaa8nkKmcPDzYwrT1z8RtOVHt7DIm2tmN7t JBFQ== X-Forwarded-Encrypted: i=1; AJvYcCWUvcwsNoCTUKuHGPejvRcvEQIBsJnZcJSecOuqV5E6a8vxNTWh04YdOwVq85FwguZHLbE09DKlQG52n7M=@vger.kernel.org X-Gm-Message-State: AOJu0Yxakl191lc8Y4zUmlYow7gCdNidG0g9s40lRPuaT/R9lDRKaaPI +MDDhJHK8tRsx1RDVlM4h+GSKX6MDLJzE0biD0AUWIjADmhxRm2XIoNX X-Gm-Gg: AZuq6aLsz7H1EYSZCVBQ0c+w7G+mBOe/+zoS1b0U5OU0NKjNpxkFa8KwJJpuA3gU4x3 noXuiQbLkX6t6R5Gf3UBw0UFGzKJhh/YByUEZe06gDN9ZTLxudD7L61PlOpQ65i9d4Tgrbmj1hH GitpIBCaa35ACQCNC0OdQ2CqtwcR2qg9TTVZqvFtUsQd+KH2e3MTIu1xqCFTAPG/UGQLAxzZtrU WQvT+YpM+/tI/gGEB815amUWVK+dfEqtY78TQElo//OivLQ3qz3mxKniX0KgXkIOZvc3ysMNrw8 HNE6zTp3auf5gCLrDNnS14aKrMHp7kR7SzaOe3vRB5Ijg8nlrhA2g8dOSKI5aEt1SH7XFP7Kr3h yyYg3W8PxxnE+PfWSuzIJSeFd7pn6y1On1bJnayd66PrDrz0+wei7r9PtufRT6xap6Q/1imkdDa lNDmrEBA3y6eLmZhXcD0iylzN/7CJ//iAOoIwdEK6MTA== X-Received: by 2002:a05:6a21:a92:b0:342:9cb7:649d with SMTP id adf61e73a8af0-393ad01e8f0mr17914368637.26.1770780636948; Tue, 10 Feb 2026 19:30:36 -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.30.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:30:36 -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 07/27] rust: error: impl From for Kernel Error Date: Wed, 11 Feb 2026 13:29:14 +1000 Message-ID: <20260211032935.2705841-8-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 Implement From for the Kernel Error type Signed-off-by: Alistair Francis --- rust/kernel/error.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index 258b12afdcba..569d9d032ab3 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -12,6 +12,7 @@ str::CStr, }; =20 +use core::ffi::FromBytesWithNulError; use core::num::NonZeroI32; use core::num::TryFromIntError; use core::str::Utf8Error; @@ -251,6 +252,12 @@ fn from(e: core::convert::Infallible) -> Error { } } =20 +impl From for Error { + fn from(_: FromBytesWithNulError) -> Error { + code::EINVAL + } +} + /// A [`Result`] with an [`Error`] error type. /// /// To be used as the return type for functions that may fail. --=20 2.52.0 From nobody Thu Apr 2 15:42:27 2026 Received: from mail-pg1-f169.google.com (mail-pg1-f169.google.com [209.85.215.169]) (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 D465A3446B2 for ; Wed, 11 Feb 2026 03:30:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780647; cv=none; b=sq4/Z0p/6u5y1blKJjETIL5xo21Wf7INfyABYs0k+A9aEXSuznNaWMgItG+0mGEFZwDE52oI/X/UE3NbANotRXvQ9qsVlI1i7IAykLEizkTAjrUy3DEAby86Q4Wh+VP1aEm0hxrXTcFWEwr0NLD1RZNGCscVdV7U0gk+pF1TfFo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780647; c=relaxed/simple; bh=C7hFCBJ4jy1LC61UrDjuGzjl8PoRstenDjFFfmrnCKI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=YVNt6kTRwUpKXQdhwdjYdo3VZrFgnrAyCs64dBl4L165asB6nr0Zfj3uIXhQi1VotncRTxC9+6JFa7tlYPiiPDMmeteD8/wgICp+NkOIDlOdagN0tzEtResU7dxSHK/gt5aZY3pOf6CZVGr5H4u4boaG65RLyaaXHs1vLAqswcI= 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=WOJ1Deb9; arc=none smtp.client-ip=209.85.215.169 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="WOJ1Deb9" Received: by mail-pg1-f169.google.com with SMTP id 41be03b00d2f7-c6de0364915so2214158a12.2 for ; Tue, 10 Feb 2026 19:30:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780644; x=1771385444; 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=ecqWhffp6slyER88bcn38vxCbbcm7/8ZALH5p22zzrY=; b=WOJ1Deb96Lc0TiVnvYlaCaGvUKXdYuCtBHaijTj2xLe/SR/kDlR3xp1YLHAe4ks5sK 0HBhDEg2BH9CXdL7lWYxqQNUtG79SL7EDQBpBREuCKRJjOVpQ1TAUQ3UNU/mJ7AXr3Oo i19kOFN0zUrAN7sG3BNIlO0C3Mhvnd5/vESNRTmo6xovfcHOM2XDAK58fFVrqGc3dXTZ kKNquT66rhQAiXczF8e10N4bHcwqOkDcUoCW8tlRrHY8sYywLUfjiYWbqYpjgxMLiXxD Ycv2wJVAkkjRW55Hbhx/Nm9UNt77oQCqplmnnRwwMGX53erTaMtraCoYOj60z33TkF/P RysA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780644; x=1771385444; 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=ecqWhffp6slyER88bcn38vxCbbcm7/8ZALH5p22zzrY=; b=HdQcFUGbNvur9VoWLoa89Mlxa/Xhs+olc4JM5DO6rKJQqSrO4FMHe9POTA15HiD+e+ 8dw7Qw14ZHE0XhqBGVrlbqH28WU9CMd4gZugmTddDGQxbVylQJuM4AFsRcn0bSF4O4Ih FYZA6DCUVz70b3iGfyY8FrH/FKC4oMY18LqoSXpffukeAjC89AWqHzmy5lryjzP0zH65 KrdyGSxo8aV5WNZvi+bClgi3U96UfpXtHJ/qAnsh5EOlUbMwCMavHryFo0HjYyEa2+Xr mRg4SLceqa1cvpiMx2Vptr9K/JEaxibXAQwqX9t03ONRbvToiNqNA7/zkI8VpYvjNUbN mg4Q== X-Forwarded-Encrypted: i=1; AJvYcCV3SOhvDem9jRwtD1d238/rjwGuKXPhphcAjzBBfPYCNDS94Fl4U6TbnBohCw76mdoHA0a5OtApUy6xH0I=@vger.kernel.org X-Gm-Message-State: AOJu0YxvKaewaq8G2zycUiACYHobpguHkPemIwWTt3Cu+KK26M/H7mps K4sAiwHxdRSpOvxctICC0Q7MGVC+VjfFxeGGwwU8CTE/ksp8j2t+GwJs X-Gm-Gg: AZuq6aLLd/xgHWh+gkaH7iRb1Zf3ymvdyUznkFhOWmPV2pKuYjzUQ/B4CyBvi0pZnQN CzXkneNo36nddUTaLwrAPESGtCzAckS2Vyqk6XTUNcWgxuULOPkOcj6KstoXsLFSX7Y91nV21oe ifkyktF+CkbNirNICDMGn2mBA1wHOXogPoAcgYfCDW271NKLcx2N5dIpBaE9pyn/2eIRUnvpLZm r04V91BFbbSwXyk7Vmg1boRzAcZ7ibOOv9H5ZnPuljed2c+KVHdLcdWcty92k6vPnU8gVrBMmmw JIuh05QkCuBTXjfKvCySwN+PQbBHjGnGNzO/IsGF3lNJGkpbia4eFqxmBlhwA2d33WzxYjoSP7U BmSZbGeMvPmM4Vc1BXepXWKzoghDe5rXiDn7R4fMfAm7eUp2D41T/TyTv0B7NrRnXHrQEFQlvap luuCv30jV3Z9AiR/dJUarPEHT3xZYGjShBPg1K3QffQA== X-Received: by 2002:a05:6a20:258a:b0:366:14ac:e1f9 with SMTP id adf61e73a8af0-394325037e1mr772915637.75.1770780643986; Tue, 10 Feb 2026 19:30:43 -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.30.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:30:43 -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 08/27] lib: rspdm: Initial commit of Rust SPDM Date: Wed, 11 Feb 2026 13:29:15 +1000 Message-ID: <20260211032935.2705841-9-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 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 --- 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 S: Supported F: Documentation/process/security-bugs.rst =20 +SECURITY PROTOCOL AND DATA MODEL (SPDM) +M: Jonathan Cameron +M: Lukas Wunner +M: Alistair Francis +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/ + SECURITY SUBSYSTEM M: Paul Moore M: James Morris diff --git a/include/linux/spdm.h b/include/linux/spdm.h new file mode 100644 index 000000000000..9835a3202a0e --- /dev/null +++ b/include/linux/spdm.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * DMTF Security Protocol and Data Model (SPDM) + * https://www.dmtf.org/dsp/DSP0274 + * + * Copyright (C) 2021-22 Huawei + * Jonathan Cameron + * + * Copyright (C) 2022-24 Intel Corporation + */ + +#ifndef _SPDM_H_ +#define _SPDM_H_ + +#include + +struct key; +struct device; +struct spdm_state; +struct x509_certificate; + +typedef ssize_t (spdm_transport)(void *priv, struct device *dev, + const void *request, size_t request_sz, + void *response, size_t response_sz); + +typedef int (spdm_validate)(struct device *dev, u8 slot, + struct x509_certificate *leaf_cert); + +struct spdm_state *spdm_create(struct device *dev, spdm_transport *transpo= rt, + void *transport_priv, u32 transport_sz, + struct key *keyring, spdm_validate *validate); + +int spdm_authenticate(struct spdm_state *spdm_state); + +void spdm_destroy(struct spdm_state *spdm_state); + +extern const struct attribute_group spdm_attr_group; + +#endif diff --git a/lib/Kconfig b/lib/Kconfig index 2923924bea78..c21f9f9a5221 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -604,6 +604,23 @@ config LWQ_TEST help Run boot-time test of light-weight queuing. =20 +config RSPDM + bool "Rust SPDM" + select RUST + select CRYPTO + select KEYS + select ASYMMETRIC_KEY_TYPE + select ASYMMETRIC_PUBLIC_KEY_SUBTYPE + select X509_CERTIFICATE_PARSER + help + The Rust implementation of the Security Protocol and Data Model (SPDM) + allows for device authentication, measurement, key exchange and + encrypted sessions. + + Crypto algorithms negotiated with SPDM are limited to those enabled + in .config. Users of SPDM therefore need to also select + any algorithms they deem mandatory. + endmenu =20 config GENERIC_IOREMAP diff --git a/lib/Makefile b/lib/Makefile index 22d8742bba57..2f530471e8dc 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -286,6 +286,8 @@ obj-$(CONFIG_PERCPU_TEST) +=3D percpu_test.o obj-$(CONFIG_ASN1) +=3D asn1_decoder.o obj-$(CONFIG_ASN1_ENCODER) +=3D asn1_encoder.o =20 +obj-$(CONFIG_RSPDM) +=3D rspdm/ + obj-$(CONFIG_FONT_SUPPORT) +=3D fonts/ =20 # diff --git a/lib/rspdm/Makefile b/lib/rspdm/Makefile new file mode 100644 index 000000000000..1f62ee2a882d --- /dev/null +++ b/lib/rspdm/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Rust implementation of the DMTF Security Protocol and Data Model (SPDM) +# https://www.dmtf.org/dsp/DSP0274 +# +# Copyright (C) 2024 Western Digital + +obj-$(CONFIG_RSPDM) +=3D spdm.o + +spdm-y :=3D lib.o 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) +//! + +pub(crate) const SPDM_REQ: u8 =3D 0x80; +pub(crate) const SPDM_ERROR: u8 =3D 0x7f; + +#[expect(dead_code)] +#[derive(Clone, Copy)] +pub(crate) enum SpdmErrorCode { + InvalidRequest =3D 0x01, + InvalidSession =3D 0x02, + Busy =3D 0x03, + UnexpectedRequest =3D 0x04, + Unspecified =3D 0x05, + DecryptError =3D 0x06, + UnsupportedRequest =3D 0x07, + RequestInFlight =3D 0x08, + InvalidResponseCode =3D 0x09, + SessionLimitExceeded =3D 0x0a, + SessionRequired =3D 0x0b, + ResetRequired =3D 0x0c, + ResponseTooLarge =3D 0x0d, + RequestTooLarge =3D 0x0e, + LargeResponse =3D 0x0f, + MessageLost =3D 0x10, + InvalidPolicy =3D 0x11, + VersionMismatch =3D 0x41, + ResponseNotReady =3D 0x42, + RequestResynch =3D 0x43, + OperationFailed =3D 0x44, + NoPendingRequests =3D 0x45, + VendorDefinedError =3D 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 =3D> { + writeln!(f, "0x01")?; + } + SpdmErrorCode::InvalidSession =3D> { + writeln!(f, "0x02")?; + } + SpdmErrorCode::Busy =3D> { + writeln!(f, "0x03")?; + } + SpdmErrorCode::UnexpectedRequest =3D> { + writeln!(f, "0x04")?; + } + SpdmErrorCode::Unspecified =3D> { + writeln!(f, "0x05")?; + } + SpdmErrorCode::DecryptError =3D> { + writeln!(f, "0x06")?; + } + SpdmErrorCode::UnsupportedRequest =3D> { + writeln!(f, "0x07")?; + } + SpdmErrorCode::RequestInFlight =3D> { + writeln!(f, "0x08")?; + } + SpdmErrorCode::InvalidResponseCode =3D> { + writeln!(f, "0x09")?; + } + SpdmErrorCode::SessionLimitExceeded =3D> { + writeln!(f, "0x0a")?; + } + SpdmErrorCode::SessionRequired =3D> { + writeln!(f, "0x0b")?; + } + SpdmErrorCode::ResetRequired =3D> { + writeln!(f, "0x0c")?; + } + SpdmErrorCode::ResponseTooLarge =3D> { + writeln!(f, "0x0d")?; + } + SpdmErrorCode::RequestTooLarge =3D> { + writeln!(f, "0x0e")?; + } + SpdmErrorCode::LargeResponse =3D> { + writeln!(f, "0x0f")?; + } + SpdmErrorCode::MessageLost =3D> { + writeln!(f, "0x10")?; + } + SpdmErrorCode::InvalidPolicy =3D> { + writeln!(f, "0x11")?; + } + SpdmErrorCode::VersionMismatch =3D> { + writeln!(f, "0x41")?; + } + SpdmErrorCode::ResponseNotReady =3D> { + writeln!(f, "0x42")?; + } + SpdmErrorCode::RequestResynch =3D> { + writeln!(f, "0x43")?; + } + SpdmErrorCode::OperationFailed =3D> { + writeln!(f, "0x44")?; + } + SpdmErrorCode::NoPendingRequests =3D> { + writeln!(f, "0x45")?; + } + SpdmErrorCode::VendorDefinedError =3D> { + writeln!(f, "0xff")?; + } + } + 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) +//! +//! +//! Top level library, including C compatible public functions to be called +//! from other subsytems. +//! +//! This mimics the C SPDM implementation in the kernel + +use core::ffi::{c_int, c_void}; +use core::ptr; +use core::slice::from_raw_parts_mut; +use kernel::prelude::*; +use kernel::{alloc::flags, bindings}; + +use crate::state::SpdmState; + +const __LOG_PREFIX: &[u8] =3D b"spdm\0"; + +mod consts; +mod state; +mod validator; + +/// spdm_create() - Allocate SPDM session +/// +/// `dev`: Responder device +/// `transport`: Transport function to perform one message exchange +/// `transport_priv`: Transport private data +/// `transport_sz`: Maximum message size the transport is capable of (in b= ytes) +/// `keyring`: Trusted root certificates +/// `validate`: Function to validate additional leaf certificate requireme= nts +/// (optional, may be %NULL) +/// +/// Return a pointer to the allocated SPDM session state or NULL on error. +#[no_mangle] +pub unsafe extern "C" fn spdm_create( + dev: *mut bindings::device, + transport: bindings::spdm_transport, + transport_priv: *mut c_void, + transport_sz: u32, + keyring: *mut bindings::key, + validate: bindings::spdm_validate, +) -> *mut SpdmState { + match KBox::new( + SpdmState::new( + dev, + transport, + transport_priv, + transport_sz, + keyring, + validate, + ), + flags::GFP_KERNEL, + ) { + Ok(ret) =3D> KBox::into_raw(ret) as *mut SpdmState, + Err(_) =3D> ptr::null_mut(), + } +} + +/// spdm_exchange() - Perform SPDM message exchange with device +/// +/// @spdm_state: SPDM session state +/// @req: Request message +/// @req_sz: Size of @req +/// @rsp: Response message +/// @rsp_sz: Size of @rsp +/// +/// Send the request @req to the device via the @transport in @spdm_state = and +/// receive the response into @rsp, respecting the maximum buffer size @rs= p_sz. +/// The request version is automatically populated. +/// +/// Return response size on success or a negative errno. Response size ma= y be +/// less than @rsp_sz and the caller is responsible for checking that. It= may +/// also be more than expected (though never more than @rsp_sz), e.g. if t= he +/// transport receives only dword-sized chunks. +#[no_mangle] +pub unsafe extern "C" fn spdm_exchange( + state: &'static mut SpdmState, + req: *mut c_void, + req_sz: usize, + rsp: *mut c_void, + rsp_sz: usize, +) -> isize { + let request_buf: &mut [u8] =3D unsafe { from_raw_parts_mut(req as *mut= u8, req_sz) }; + let response_buf: &mut [u8] =3D unsafe { from_raw_parts_mut(rsp as *mu= t u8, rsp_sz) }; + + match state.spdm_exchange(request_buf, response_buf) { + Ok(ret) =3D> ret as isize, + Err(e) =3D> e.to_errno() as isize, + } +} + +/// spdm_authenticate() - Authenticate device +/// +/// @spdm_state: SPDM session state +/// +/// Authenticate a device through a sequence of GET_VERSION, GET_CAPABILIT= IES, +/// NEGOTIATE_ALGORITHMS, GET_DIGESTS, GET_CERTIFICATE and CHALLENGE excha= nges. +/// +/// Perform internal locking to serialize multiple concurrent invocations. +/// Can be called repeatedly for reauthentication. +/// +/// Return 0 on success or a negative errno. In particular, -EPROTONOSUPP= ORT +/// indicates authentication is not supported by the device. +#[no_mangle] +pub unsafe extern "C" fn spdm_authenticate(_state: &'static mut SpdmState)= -> c_int { + 0 +} + +/// spdm_destroy() - Destroy SPDM session +/// +/// @spdm_state: SPDM session state +#[no_mangle] +pub unsafe extern "C" fn spdm_destroy(_state: &'static mut SpdmState) {} 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) +//! + +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 @tran= sport. +/// `transport`: Transport function to perform one message exchange. +/// `transport_priv`: Transport private data. +/// `transport_sz`: Maximum message size the transport is capable of (in b= ytes). +/// Used as DataTransferSize in GET_CAPABILITIES exchange. +/// `keyring`: Keyring against which to check the first certificate in +/// responder's certificate chain. +/// `validate`: Function to validate additional leaf certificate requireme= nts. +/// +/// `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, + } + } + + fn spdm_err(&self, rsp: &SpdmErrorRsp) -> Result<(), Error> { + match rsp.error_code { + SpdmErrorCode::InvalidRequest =3D> { + pr_err!("Invalid request\n"); + Err(EINVAL) + } + SpdmErrorCode::InvalidSession =3D> { + if rsp.version =3D=3D 0x11 { + pr_err!("Invalid session {:#x}\n", rsp.error_data); + Err(EINVAL) + } else { + pr_err!("Undefined error {:#x}\n", rsp.error_code); + Err(EINVAL) + } + } + SpdmErrorCode::Busy =3D> { + pr_err!("Busy\n"); + Err(EBUSY) + } + SpdmErrorCode::UnexpectedRequest =3D> { + pr_err!("Unexpected request\n"); + Err(EINVAL) + } + SpdmErrorCode::Unspecified =3D> { + pr_err!("Unspecified error\n"); + Err(EINVAL) + } + SpdmErrorCode::DecryptError =3D> { + pr_err!("Decrypt error\n"); + Err(EIO) + } + SpdmErrorCode::UnsupportedRequest =3D> { + pr_err!("Unsupported request {:#x}\n", rsp.error_data); + Err(EINVAL) + } + SpdmErrorCode::RequestInFlight =3D> { + pr_err!("Request in flight\n"); + Err(EINVAL) + } + SpdmErrorCode::InvalidResponseCode =3D> { + pr_err!("Invalid response code\n"); + Err(EINVAL) + } + SpdmErrorCode::SessionLimitExceeded =3D> { + pr_err!("Session limit exceeded\n"); + Err(EBUSY) + } + SpdmErrorCode::SessionRequired =3D> { + pr_err!("Session required\n"); + Err(EINVAL) + } + SpdmErrorCode::ResetRequired =3D> { + pr_err!("Reset required\n"); + Err(ECONNRESET) + } + SpdmErrorCode::ResponseTooLarge =3D> { + pr_err!("Response too large\n"); + Err(EINVAL) + } + SpdmErrorCode::RequestTooLarge =3D> { + pr_err!("Request too large\n"); + Err(EINVAL) + } + SpdmErrorCode::LargeResponse =3D> { + pr_err!("Large response\n"); + Err(EMSGSIZE) + } + SpdmErrorCode::MessageLost =3D> { + pr_err!("Message lost\n"); + Err(EIO) + } + SpdmErrorCode::InvalidPolicy =3D> { + pr_err!("Invalid policy\n"); + Err(EINVAL) + } + SpdmErrorCode::VersionMismatch =3D> { + pr_err!("Version mismatch\n"); + Err(EINVAL) + } + SpdmErrorCode::ResponseNotReady =3D> { + pr_err!("Response not ready\n"); + Err(EINPROGRESS) + } + SpdmErrorCode::RequestResynch =3D> { + pr_err!("Request resynchronization\n"); + Err(ECONNRESET) + } + SpdmErrorCode::OperationFailed =3D> { + pr_err!("Operation failed\n"); + Err(EINVAL) + } + SpdmErrorCode::NoPendingRequests =3D> Err(ENOENT), + SpdmErrorCode::VendorDefinedError =3D> { + pr_err!("Vendor defined error\n"); + Err(EINVAL) + } + } + } + + /// 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 { + let header_size =3D core::mem::size_of::(); + let request: &mut SpdmHeader =3D Untrusted::new_mut(request_buf).v= alidate_mut()?; + let response: &SpdmHeader =3D Untrusted::new_ref(response_buf).val= idate()?; + + let transport_function =3D self.transport.ok_or(EINVAL)?; + // SAFETY: `transport_function` is provided by the new(), we are + // calling the function. + let length =3D 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 =3D=3D SPDM_ERROR { + if length as usize >=3D core::mem::size_of::() { + // SAFETY: The response buffer will be at least as large as + // `SpdmErrorRsp` so we can cast the buffer to `SpdmErrorR= sp` which + // is a packed struct. + self.spdm_err(unsafe { &*(response_buf.as_ptr() as *const = SpdmErrorRsp) })?; + } else { + return Err(EINVAL); + } + } + + if response.code !=3D 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) +//! + +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 =3D Error; + + fn validate(unvalidated: &Unvalidated<[u8]>) -> Result { + let raw =3D unvalidated.raw(); + if raw.len() < mem::size_of::() { + return Err(EINVAL); + } + + let ptr =3D raw.as_ptr(); + // CAST: `SpdmHeader` only contains integers and has `repr(C)`. + let ptr =3D ptr.cast::(); + // 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 =3D Error; + + fn validate(unvalidated: &mut Unvalidated<[u8]>) -> Result { + let raw =3D unvalidated.raw_mut(); + if raw.len() < mem::size_of::() { + return Err(EINVAL); + } + + let ptr =3D raw.as_mut_ptr(); + // CAST: `SpdmHeader` only contains integers and has `repr(C)`. + let ptr =3D ptr.cast::(); + // 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, + pub(crate) error_code: SpdmErrorCode, + pub(crate) error_data: u8, +} diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 0075c4b62c29..5043eee2a8d6 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -84,7 +84,9 @@ #include #include #include +#include #include +#include #include #include #include diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index 569d9d032ab3..c9f57082e481 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -88,6 +88,9 @@ macro_rules! declare_err { declare_err!(EIOCBQUEUED, "iocb queued, will get completion event."); declare_err!(ERECALLCONFLICT, "Conflict with recalled state."); declare_err!(ENOGRACE, "NFS file lock reclaim refused."); + declare_err!(ECONNRESET, "Connection reset by peer."); + declare_err!(EMSGSIZE, "Message too long."); + declare_err!(EINPROGRESS, "Operation now in progress."); } =20 /// Generic integer kernel error. --=20 2.52.0 From nobody Thu Apr 2 15:42:27 2026 Received: from mail-pg1-f179.google.com (mail-pg1-f179.google.com [209.85.215.179]) (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 6527A346E40 for ; Wed, 11 Feb 2026 03:30:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.179 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780652; cv=none; b=J/0ijcjTme8PTXJ/gThtl/h7492pzpSmbZaeTvcPimel1q7gC/oJzPJapryLZKtcitimrxl1IH4LbqExf2BXS/MJTQLT63JmmIs4lijw3cIlJLFPThMGMMr229xJJCrVgGKuLGg046/3vjH1UjDrutPBFicqzH+qnDn480gdPRY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780652; c=relaxed/simple; bh=xkGNE2ohHTD4CfhfPzdzFZFTRcEJitPxHq9ykzoPMGI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=TecT+CgrJsUJgjlAUnn9KKSb9KVF5hvPCgQ4rhjDkI7R310aVEkPnATyY44V9zYKYAHtIExPzqNFr7S1W8AKQ0ZH4oe1oiChBPQXJ0Zo7sZi38XJrdop6LEk28oeYDOGeYzMBQlhDhtqZIV6SU6VctnFnqwP8ivqTqp0MDChBgM= 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=K4flZCmV; arc=none smtp.client-ip=209.85.215.179 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="K4flZCmV" Received: by mail-pg1-f179.google.com with SMTP id 41be03b00d2f7-c6788f3db37so573977a12.1 for ; Tue, 10 Feb 2026 19:30:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780651; x=1771385451; 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=XkGUjPlY8y9Yk5Nqgh7hT+M8qtzIfjwHeltAbD4eTuM=; b=K4flZCmVS2TdZmQ/YkDipj30lezCecZxJ9j+y0hJ+8v5E62zSruUP+/+m2yXse9x3P pti5tdrByZf2SF5PXT9m7MOEuPIZxIDYMtxTZHNPq+CQHOeanKrwLnw6R8GWWL67/1jN sC0I1NuCwUEBm+B26QF3cOpcSrCdtg5quvIX9XdEaodmY9sw3AymUL5a1Az7qPlxNu+v /KHJnH39qKIoe+DqFHoHwiDE7fo5iOItIZ5xRFr1a3Mngfb/P05OQ1VmyRGQuwqfxR23 gWgLeIpXJ5DlBHCn78nxsshrTwm66FE4FVU7XYmHabIP9WTjNmnGiQC/FCV0GzonJ9/J pJCQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780651; x=1771385451; 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=XkGUjPlY8y9Yk5Nqgh7hT+M8qtzIfjwHeltAbD4eTuM=; b=pLyxd4JF1sEPuJse86hL/9PIy3nZuYusKzuh8aw5X0Y+mlr+rmvFoaGEtlueRscmuE tKq0suiRqToY6Ks1afZqT+nLPM21CG45bELWDmM7VaAnDXqIQlxMD2i4JopdyxEsIxLL bcfxNJtWpRugfs3//762TeRVJ/BUi2uTP7X9y8HeqGGdTpCcxsran7yn9j9qRLKnDloU yCxsmqEl9Sa0bnJQtujxYQT0AcS0btDKYGYe9LbNg1SX1XAOJ7fIZ3uOcp5TZohdGNFT p4dyOSZNXazW/wEzIQy8YI7ifC2JovkqKBh6xnFah0338k+9/s6EATGTtwMTEBFdUS2h nyCQ== X-Forwarded-Encrypted: i=1; AJvYcCW387aXQIH65cgQPgLGMMqB/msT7RbgWrba6EiAN4hovzJlR6+2QEzSCk92k2KSWjIo1tkOB7Qy1MpQ2N8=@vger.kernel.org X-Gm-Message-State: AOJu0YxQzlwnStHr8CVv7fMcFnoKBFImr3vX5aewhj7qNp8eriX2Pzpf rTp6ny30IpWZmcYNr6R4hOIQa5YrGMhMNMkn5bre96B8pZy2d9YLScKs X-Gm-Gg: AZuq6aKPRDhR8x8fjkTQEIAw1x7wRnsV+AR224RdacPtNnuU0M9Z1m0TiSq95l93cdE 4LhZgixFhhbvvOguXvU9lTx/L1RRNSP0JQVgAr4sBjUsW7TOt+YyvfVDCpMsRodTYGVk3Ve2RVp ss84ZDqYLpV3GRKN+WBXXI8fddCH3kMMmqoDjP+hcGuRs8Ei/IWh/9TUmnWsTy1CdLbGTrpSBRK bthOZTO9dz+XMl4EBg83c/DrPi+wV+Julb1We0Ic19awBJEDssTgO4o21IgjP1m+YcKB3qYXTky tIOHELz7yr1jjSUx87s40zidJhYPazNG2HM6veeWNxRzrK9bGD+BPQC5BGITyPh6pJm5znZjtRU obGmUApMyDCEta8I+KRB/cT7uXdtPiS5XJpi4jDZ2Bd8azWPgTaamK3COXV/lXscvfVcqdRl7Ez DRXbCbioww+2ObvHZydeBv1cPXWhIwgx5SQ/qy1++FIQ== X-Received: by 2002:a05:6a21:a08:b0:38d:e87c:48c8 with SMTP id adf61e73a8af0-393ad36a5ddmr16559978637.60.1770780650790; Tue, 10 Feb 2026 19:30:50 -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.30.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:30:50 -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 Subject: [RFC v3 09/27] PCI/CMA: Authenticate devices on enumeration Date: Wed, 11 Feb 2026 13:29:16 +1000 Message-ID: <20260211032935.2705841-10-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: Jonathan Cameron Component Measurement and Authentication (CMA, PCIe r6.2 sec 6.31) allows for measurement and authentication of PCIe devices. It is based on the Security Protocol and Data Model specification (SPDM, https://www.dmtf.org/dsp/DSP0274). CMA-SPDM in turn forms the basis for Integrity and Data Encryption (IDE, PCIe r6.2 sec 6.33) because the key material used by IDE is transmitted over a CMA-SPDM session. As a first step, authenticate CMA-capable devices on enumeration. A subsequent commit will expose the result in sysfs. When allocating SPDM session state with spdm_create(), the maximum SPDM message length needs to be passed. Make the PCI_DOE_MAX_LENGTH macro public and calculate the maximum payload length from it. Credits: Jonathan wrote a proof-of-concept of this CMA implementation. Lukas reworked it for upstream. Wilfred contributed fixes for issues discovered during testing. Signed-off-by: Jonathan Cameron Co-developed-by: Wilfred Mallawa Signed-off-by: Wilfred Mallawa Co-developed-by: Lukas Wunner Signed-off-by: Lukas Wunner --- MAINTAINERS | 1 + drivers/pci/Kconfig | 16 +++++++ drivers/pci/Makefile | 2 + drivers/pci/cma.c | 101 ++++++++++++++++++++++++++++++++++++++++ drivers/pci/doe.c | 3 -- drivers/pci/pci.h | 8 ++++ drivers/pci/probe.c | 1 + drivers/pci/remove.c | 1 + include/linux/pci-doe.h | 4 ++ include/linux/pci.h | 4 ++ 10 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 drivers/pci/cma.c diff --git a/MAINTAINERS b/MAINTAINERS index a5c4ec16081c..58898260fde8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23702,6 +23702,7 @@ 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: drivers/pci/cma.c F: include/linux/spdm.h F: lib/rspdm/ =20 diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index e3f848ffb52a..7ea403799d78 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -125,6 +125,22 @@ config PCI_ATS config PCI_IDE bool =20 +config PCI_CMA + bool "Component Measurement and Authentication (CMA-SPDM)" + select CRYPTO_ECDSA + select CRYPTO_RSA + select CRYPTO_SHA256 + select CRYPTO_SHA512 + select PCI_DOE + select RSPDM + help + Authenticate devices on enumeration per PCIe r6.2 sec 6.31. + A PCI DOE mailbox is used as transport for DMTF SPDM based + authentication, measurement and secure channel establishment. + +config PCI_DOE + bool + config PCI_TSM bool "PCI TSM: Device security protocol support" select PCI_IDE diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index e10cfe5a280b..f026f5dbb938 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -40,6 +40,8 @@ obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) +=3D of_property.o obj-$(CONFIG_PCI_NPEM) +=3D npem.o obj-$(CONFIG_PCIE_TPH) +=3D tph.o =20 +obj-$(CONFIG_PCI_CMA) +=3D cma.o + # Endpoint library must be initialized before its users obj-$(CONFIG_PCI_ENDPOINT) +=3D endpoint/ =20 diff --git a/drivers/pci/cma.c b/drivers/pci/cma.c new file mode 100644 index 000000000000..7463cd1179f0 --- /dev/null +++ b/drivers/pci/cma.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Component Measurement and Authentication (CMA-SPDM, PCIe r6.2 sec 6.31) + * + * Copyright (C) 2021 Huawei + * Jonathan Cameron + * + * Copyright (C) 2022-24 Intel Corporation + */ + +#define dev_fmt(fmt) "CMA: " fmt + +#include +#include +#include +#include + +#include "pci.h" + +/* Keyring that userspace can poke certs into */ +static struct key *pci_cma_keyring; + +#define PCI_DOE_FEATURE_CMA 1 + +static ssize_t pci_doe_transport(void *priv, struct device *dev, + const void *request, size_t request_sz, + void *response, size_t response_sz) +{ + struct pci_doe_mb *doe =3D priv; + ssize_t rc; + + /* + * CMA-SPDM operation in non-D0 states is optional (PCIe r6.2 + * sec 6.31.3). The spec does not define a way to determine + * if it's supported, so resume to D0 unconditionally. + */ + rc =3D pm_runtime_resume_and_get(dev); + if (rc) + return rc; + + rc =3D pci_doe(doe, PCI_VENDOR_ID_PCI_SIG, PCI_DOE_FEATURE_CMA, + request, request_sz, response, response_sz); + + pm_runtime_put(dev); + + return rc; +} + +void pci_cma_init(struct pci_dev *pdev) +{ + struct pci_doe_mb *doe; + + if (IS_ERR(pci_cma_keyring)) + return; + + if (!pci_is_pcie(pdev)) + return; + + doe =3D pci_find_doe_mailbox(pdev, PCI_VENDOR_ID_PCI_SIG, + PCI_DOE_FEATURE_CMA); + if (!doe) + return; + + pdev->spdm_state =3D spdm_create(&pdev->dev, pci_doe_transport, doe, + PCI_DOE_MAX_PAYLOAD, pci_cma_keyring, + NULL); + if (!pdev->spdm_state) + return; + + /* + * Keep spdm_state allocated even if initial authentication fails + * to allow for provisioning of certificates and reauthentication. + */ + spdm_authenticate(pdev->spdm_state); +} + +void pci_cma_destroy(struct pci_dev *pdev) +{ + if (!pdev->spdm_state) + return; + + spdm_destroy(pdev->spdm_state); +} + +__init static int pci_cma_keyring_init(void) +{ + pci_cma_keyring =3D keyring_alloc(".cma", KUIDT_INIT(0), KGIDT_INIT(0), + current_cred(), + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ | + KEY_USR_WRITE | KEY_USR_SEARCH, + KEY_ALLOC_NOT_IN_QUOTA | + KEY_ALLOC_SET_KEEP, NULL, NULL); + if (IS_ERR(pci_cma_keyring)) { + pr_err("PCI: Could not allocate .cma keyring\n"); + return PTR_ERR(pci_cma_keyring); + } + + return 0; +} +arch_initcall(pci_cma_keyring_init); diff --git a/drivers/pci/doe.c b/drivers/pci/doe.c index 62be9c8dbc52..344e01496a8e 100644 --- a/drivers/pci/doe.c +++ b/drivers/pci/doe.c @@ -31,9 +31,6 @@ #define PCI_DOE_FLAG_CANCEL 0 #define PCI_DOE_FLAG_DEAD 1 =20 -/* Max data object length is 2^18 dwords */ -#define PCI_DOE_MAX_LENGTH (1 << 18) - /** * struct pci_doe_mb - State for a single DOE mailbox * diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 0e67014aa001..1ab1a39e8df3 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -599,6 +599,14 @@ static inline void pci_doe_destroy(struct pci_dev *pde= v) { } static inline void pci_doe_disconnected(struct pci_dev *pdev) { } #endif =20 +#ifdef CONFIG_PCI_CMA +void pci_cma_init(struct pci_dev *pdev); +void pci_cma_destroy(struct pci_dev *pdev); +#else +static inline void pci_cma_init(struct pci_dev *pdev) { } +static inline void pci_cma_destroy(struct pci_dev *pdev) { } +#endif + #ifdef CONFIG_PCI_NPEM void pci_npem_create(struct pci_dev *dev); void pci_npem_remove(struct pci_dev *dev); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 41183aed8f5d..665532a07cfc 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2706,6 +2706,7 @@ static void pci_init_capabilities(struct pci_dev *dev) pci_rebar_init(dev); /* Resizable BAR */ pci_dev3_init(dev); /* Device 3 capabilities */ pci_ide_init(dev); /* Link Integrity and Data Encryption */ + pci_cma_init(dev); /* Component Measurement & Auth */ =20 pcie_report_downtraining(dev); pci_init_reset_methods(dev); diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 417a9ea59117..b98d96919089 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -69,6 +69,7 @@ static void pci_destroy_dev(struct pci_dev *dev) list_del(&dev->bus_list); up_write(&pci_bus_sem); =20 + pci_cma_destroy(dev); pci_doe_destroy(dev); pci_ide_destroy(dev); pcie_aspm_exit_link_state(dev); diff --git a/include/linux/pci-doe.h b/include/linux/pci-doe.h index bd4346a7c4e7..7540396336de 100644 --- a/include/linux/pci-doe.h +++ b/include/linux/pci-doe.h @@ -19,6 +19,10 @@ struct pci_doe_mb; #define PCI_DOE_FEATURE_CMA 1 #define PCI_DOE_FEATURE_SSESSION 2 =20 +/* Max data object length is 2^18 dwords (including 2 dwords for header) */ +#define PCI_DOE_MAX_LENGTH (1 << 18) +#define PCI_DOE_MAX_PAYLOAD ((PCI_DOE_MAX_LENGTH - 2) * sizeof(u32)) + struct pci_doe_mb *pci_find_doe_mailbox(struct pci_dev *pdev, u16 vendor, u8 type); =20 diff --git a/include/linux/pci.h b/include/linux/pci.h index 9357e9b00e1c..7384846ade19 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -39,6 +39,7 @@ #include #include #include +#include #include =20 #include @@ -542,6 +543,9 @@ struct pci_dev { #ifdef CONFIG_PCI_DOE struct xarray doe_mbs; /* Data Object Exchange mailboxes */ #endif +#ifdef CONFIG_PCI_CMA + struct spdm_state *spdm_state; /* Security Protocol and Data Model */ +#endif #ifdef CONFIG_PCI_NPEM struct npem *npem; /* Native PCIe Enclosure Management */ #endif --=20 2.52.0 From nobody Thu Apr 2 15:42:27 2026 Received: from mail-pf1-f173.google.com (mail-pf1-f173.google.com [209.85.210.173]) (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 91A75346AC5 for ; Wed, 11 Feb 2026 03:30:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780660; cv=none; b=q8GlZ40nquN8zZjBJ93kdWi4We+3lDdutw3I+eBpDuK8iAXgACiZER/9kNk8kAZrlTsgZNUBdfEd6jH5Rd5vuWQKIrud7vS3Uz2pea1nLF2D4z8D0cRFfnr8ctPorRD3dIZqEayqfMUu3OKvHhNcEzC9ykE4hJBjyUoKxdfaDSg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780660; c=relaxed/simple; bh=ARGSOlCkoORZq596vNp643CMeCdzMBN/FkFLVxmVE7E=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=NeX84pag/83nejBqIZoIMsk75l0995VLL1xnFBJPhVWS4+9Cg7pO7IEf67IBtivMucu6exaw9SEBgnOYlEpxFt3XRD3xrcQ6G5+iKs+Aai3uRSCDrNfM9lzcklpGZ16N1wa7ogutxYJ8L9KnfNxDradRa2i14bW04uKrldtpqso= 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=dBKgKwTb; arc=none smtp.client-ip=209.85.210.173 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="dBKgKwTb" Received: by mail-pf1-f173.google.com with SMTP id d2e1a72fcca58-82310b74496so3231153b3a.3 for ; Tue, 10 Feb 2026 19:30:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780658; x=1771385458; 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=K28hAp/PXBGbD4YDFq33twrTUu6X7QnQ69F/ZWI6Jd4=; b=dBKgKwTbl6H+FFNb26wK5/DvzlMmXbHJcPhn4VIkRF3WtXQHW2XjDHlrlqvNbqEteV xk/EQcAgX+BXPMwHug86G+2IcpDKgjYb/Fb3tSlzBd6RNpA+dLCBlO4zI6eg9cMd9ZxG wUB0SlMcBW7NHbp/vAkDsfN/iipxzYX3n/udesSwAaoflJ7qZ+ntebUUqpXm74rFSy1s UL1OFtLc9KyVySOMsNSWuVJaeHCc3buPyjaJxWSN74qB+3vgBUrUGVTZaZMHw/dl3V6R r4Gkq0Lq32IfPQ7B/sJKDQ0c+aNniDTXJVVPHwd1ZEss4v/e/7UP7YvTFmUX8cKi2k4i OGjQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780658; x=1771385458; 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=K28hAp/PXBGbD4YDFq33twrTUu6X7QnQ69F/ZWI6Jd4=; b=YfMRooFXVBEbBJmYTrSJmStuQuLISQl9+oIgiK0oRepblO+SecCrbPvcU+qYX6iUVX 8FvFqF3FlwAiPaAJIafNyVc9tfk4/v/SSXWrq0k6Qebus7hihtps1viNgXJGcAI+iBh0 W+FVD4QrC3jxUTyu+0vRMRHrbgBceBjIXdw4giZRmdVYM+MpLC4E3+pmb2qzaazNZioA mMxpcpCrafksXCbYoD9/zSFTTSFX/0iFbDuz5CXLztSNFHXUgnC9Cx7qG2x/JIUQckDA vor0w4JJpgRIp5NLY/m3P3wteT0RlJU1LeVjKPRI4dsIx94SjUYIg5CmeXZFRA0U8nfY /FJg== X-Forwarded-Encrypted: i=1; AJvYcCXWslNEVLDvv0J8JqMBg0Xx13yClI5WeEv29h8tKZQCykqlrZpcqrHZlmVFt+tqwVWBDooxCwMeBOp0wNw=@vger.kernel.org X-Gm-Message-State: AOJu0YzxeD5+9svEiCbZDbtgbAiUZ8OdpTC0wBOQqK7JUOaxjoGqIUAe Vf0fhNk7JKz/5RIde0dFueYJ4jXowJBQpZrarrNzk0HCIdTgZvin+HQC X-Gm-Gg: AZuq6aL1j/y/CfypEjY7+b6jR5knE5IDoDgcCPqMIxVfuOLlZWQnFNHxTts949+CSwg yp/pivardoYSMTI37/DWqfnCPgQGSIcpKQlZPixiR21MgrujJJvqBxuR6kD5aNJt/evAZohRt/v x7emj06JYr8UtS6zWJXcbo6YT/dsev5wCcglZvXE0Onr+AoRNzbnJr0oMgOo0hU8d5njlZuWh3a F5bF67d4d8cSZZYFa8fxJAEWvKkkCKyEw6UECbnSv+b94oEgZWjpB0aYLNsPOzX7bateEDy3VLB e8hb/16AgIZMof6DMqHn6KzFJ76EPyP515lcKJGH1D2EOMmuSWi8QX8KMFKykdrbGcQldCOzjJI /ZRRsxZnRgil6Nd6HPIS6IaDJ8OEhOe00Ym8fIeWu68NZJihxzeEul80HLh93jqhn5MKEu0XP2i Cbj5DxL+134XE73Fy6/LM30kojVcS72CMJQ/55pw7EMRnaEbgb3qE8 X-Received: by 2002:a05:6a21:e58b:b0:343:af1:9a57 with SMTP id adf61e73a8af0-39417f7879fmr3799996637.56.1770780657651; Tue, 10 Feb 2026 19:30:57 -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.30.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:30:57 -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 Subject: [RFC v3 10/27] PCI/CMA: Validate Subject Alternative Name in certificates Date: Wed, 11 Feb 2026 13:29:17 +1000 Message-ID: <20260211032935.2705841-11-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: Lukas Wunner PCIe r6.1 sec 6.31.3 stipulates requirements for Leaf Certificates presented by devices, in particular the presence of a Subject Alternative Name which encodes the Vendor ID, Device ID, Device Serial Number, etc. This prevents a mismatch between the device identity in Config Space and the certificate. A device cannot misappropriate a certificate from a different device without also spoofing Config Space. As a corollary, it cannot dupe an arbitrary driver into binding to it. Only drivers which bind to the device identity in the Subject Alternative Name work (PCIe r6.1 sec 6.31 "Implementation Note: Overview of Threat Model"). The Subject Alternative Name is signed, hence constitutes a signed copy of a Config Space portion. It's the same concept as web certificates which contain a set of domain names in the Subject Alternative Name for identity verification. Parse the Subject Alternative Name using a small ASN.1 module and validate its contents. The theory of operation is explained in a comment at the top of the newly inserted code. This functionality is introduced in a separate commit on top of basic CMA-SPDM support to split the code into digestible, reviewable chunks. The CMA OID added here is taken from the official OID Repository (it's not documented in the PCIe Base Spec): https://oid-rep.orange-labs.fr/get/2.23.147 Side notes: * PCIe r6.2 removes the spec language on the Subject Alternative Name. It still "requires the leaf certificate to include the information typically used by system software for device driver binding", but no longer specifies how that information is encoded into the certificate. According to the editor of the PCIe Base Spec and the author of the CMA 1.1 ECN (which caused this change), FPGA cards which mutate their device identity at runtime (due to a firmware update) were thought as unable to satisfy the previous spec language. The Protocol Working Group could not agree on a better solution and therefore dropped the spec language entirely. They acknowledge that the requirement is now under-spec'd. Because products already exist which adhere to the Subject Alternative Name requirement per PCIe r6.1 sec 6.31.3, they recommended to "push through" and use it as the de facto standard. The FPGA concerns are easily overcome by reauthenticating the device after a firmware update, either via sysfs or pci_cma_reauthenticate() (added by a subsequent commit). * PCIe r6.1 sec 6.31.3 strongly recommends to verify that "the information provided in the Subject Alternative Name entry is signed by the vendor indicated by the Vendor ID." In other words, the root certificate on pci_cma_keyring which signs the device's certificate chain must have been created for a particular Vendor ID. Unfortunately the spec neglects to define how the Vendor ID shall be encoded into the root certificate. So the recommendation cannot be implemented at this point and it is thus possible that a vendor signs device certificates of a different vendor. * Instead of a Subject Alternative Name, Leaf Certificates may include "a Reference Integrity Manifest, e.g., see Trusted Computing Group" or "a pointer to a location where such a Reference Integrity Manifest can be obtained" (PCIe r6.1 sec 6.31.3). A Reference Integrity Manifest contains "golden" measurements which can be compared to actual measurements retrieved from a device. It serves a different purpose than the Subject Alternative Name, hence it is unclear why the spec says only either of them is necessary. It is also unclear how a Reference Integrity Manifest shall be encoded into a certificate. Hence ignore the Reference Integrity Manifest requirement. Signed-off-by: Lukas Wunner Reviewed-by: Jonathan Cameron # except ASN.1 --- drivers/pci/Makefile | 4 +- drivers/pci/cma.asn1 | 41 ++++++++++++ drivers/pci/cma.c | 123 ++++++++++++++++++++++++++++++++++- include/linux/oid_registry.h | 3 + 4 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 drivers/pci/cma.asn1 diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index f026f5dbb938..2fc161e15670 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -40,7 +40,9 @@ obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) +=3D of_property.o obj-$(CONFIG_PCI_NPEM) +=3D npem.o obj-$(CONFIG_PCIE_TPH) +=3D tph.o =20 -obj-$(CONFIG_PCI_CMA) +=3D cma.o +obj-$(CONFIG_PCI_CMA) +=3D cma.o cma.asn1.o +$(obj)/cma.o: $(obj)/cma.asn1.h +$(obj)/cma.asn1.o: $(obj)/cma.asn1.c $(obj)/cma.asn1.h =20 # Endpoint library must be initialized before its users obj-$(CONFIG_PCI_ENDPOINT) +=3D endpoint/ diff --git a/drivers/pci/cma.asn1 b/drivers/pci/cma.asn1 new file mode 100644 index 000000000000..da41421d4085 --- /dev/null +++ b/drivers/pci/cma.asn1 @@ -0,0 +1,41 @@ +-- SPDX-License-Identifier: BSD-3-Clause +-- +-- Component Measurement and Authentication (CMA-SPDM, PCIe r6.1 sec 6.31.= 3) +-- X.509 Subject Alternative Name (RFC 5280 sec 4.2.1.6) +-- +-- Copyright (C) 2008 IETF Trust and the persons identified as authors +-- of the code +-- +-- https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.6 +-- +-- The ASN.1 module in RFC 5280 appendix A.1 uses EXPLICIT TAGS whereas th= e one +-- in appendix A.2 uses IMPLICIT TAGS. The kernel's simplified asn1_compi= ler.c +-- always uses EXPLICIT TAGS, hence this ASN.1 module differs from RFC 528= 0 in +-- that it adds IMPLICIT to definitions from appendix A.2 (such as General= Name) +-- and omits EXPLICIT in those definitions. + +SubjectAltName ::=3D GeneralNames + +GeneralNames ::=3D SEQUENCE OF GeneralName + +GeneralName ::=3D CHOICE { + otherName [0] IMPLICIT OtherName, + rfc822Name [1] IMPLICIT IA5String, + dNSName [2] IMPLICIT IA5String, + x400Address [3] ANY, + directoryName [4] ANY, + ediPartyName [5] IMPLICIT EDIPartyName, + uniformResourceIdentifier [6] IMPLICIT IA5String, + iPAddress [7] IMPLICIT OCTET STRING, + registeredID [8] IMPLICIT OBJECT IDENTIFIER + } + +OtherName ::=3D SEQUENCE { + type-id OBJECT IDENTIFIER ({ pci_cma_note_oid }), + value [0] ANY ({ pci_cma_note_san }) + } + +EDIPartyName ::=3D SEQUENCE { + nameAssigner [0] ANY OPTIONAL, + partyName [1] ANY + } diff --git a/drivers/pci/cma.c b/drivers/pci/cma.c index 7463cd1179f0..e974d489c7a2 100644 --- a/drivers/pci/cma.c +++ b/drivers/pci/cma.c @@ -10,16 +10,137 @@ =20 #define dev_fmt(fmt) "CMA: " fmt =20 +#include +#include +#include #include #include #include #include =20 +#include "cma.asn1.h" #include "pci.h" =20 /* Keyring that userspace can poke certs into */ static struct key *pci_cma_keyring; =20 +/* + * The spdm_requester.c library calls pci_cma_validate() to check requirem= ents + * for Leaf Certificates per PCIe r6.1 sec 6.31.3. + * + * pci_cma_validate() parses the Subject Alternative Name using the ASN.1 + * module cma.asn1, which calls pci_cma_note_oid() and pci_cma_note_san() + * to compare an OtherName against the expected name. + * + * The expected name is constructed beforehand by pci_cma_construct_san(). + * + * PCIe r6.2 drops the Subject Alternative Name spec language, even though + * it continues to require "the leaf certificate to include the information + * typically used by system software for device driver binding". Use the + * Subject Alternative Name per PCIe r6.1 for lack of a replacement and + * because it is the de facto standard among existing products. + */ +#define CMA_NAME_MAX sizeof("Vendor=3D1234:Device=3D1234:CC=3D123456:" \ + "REV=3D12:SSVID=3D1234:SSID=3D1234:1234567890123456") + +struct pci_cma_x509_context { + struct pci_dev *pdev; + u8 slot; + enum OID last_oid; + char expected_name[CMA_NAME_MAX]; + unsigned int expected_len; + unsigned int found:1; +}; + +int pci_cma_note_oid(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct pci_cma_x509_context *ctx =3D context; + + ctx->last_oid =3D look_up_OID(value, vlen); + + return 0; +} + +int pci_cma_note_san(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct pci_cma_x509_context *ctx =3D context; + + /* These aren't the drOIDs we're looking for. */ + if (ctx->last_oid !=3D OID_CMA) + return 0; + + if (tag !=3D ASN1_UTF8STR || + vlen !=3D ctx->expected_len || + memcmp(value, ctx->expected_name, vlen) !=3D 0) { + pci_err(ctx->pdev, "Leaf certificate of slot %u " + "has invalid Subject Alternative Name\n", ctx->slot); + return -EINVAL; + } + + ctx->found =3D true; + + return 0; +} + +static unsigned int pci_cma_construct_san(struct pci_dev *pdev, char *name) +{ + unsigned int len; + u64 serial; + + len =3D snprintf(name, CMA_NAME_MAX, + "Vendor=3D%04hx:Device=3D%04hx:CC=3D%06x:REV=3D%02hhx", + pdev->vendor, pdev->device, pdev->class, pdev->revision); + + if (pdev->hdr_type =3D=3D PCI_HEADER_TYPE_NORMAL) + len +=3D snprintf(name + len, CMA_NAME_MAX - len, + ":SSVID=3D%04hx:SSID=3D%04hx", + pdev->subsystem_vendor, pdev->subsystem_device); + + serial =3D pci_get_dsn(pdev); + if (serial) + len +=3D snprintf(name + len, CMA_NAME_MAX - len, + ":%016llx", serial); + + return len; +} + +static int pci_cma_validate(struct device *dev, u8 slot, + struct x509_certificate *leaf_cert) +{ + struct pci_dev *pdev =3D to_pci_dev(dev); + struct pci_cma_x509_context ctx; + int ret; + + if (!leaf_cert->raw_san) { + pci_err(pdev, "Leaf certificate of slot %u " + "has no Subject Alternative Name\n", slot); + return -EINVAL; + } + + ctx.pdev =3D pdev; + ctx.slot =3D slot; + ctx.found =3D false; + ctx.expected_len =3D pci_cma_construct_san(pdev, ctx.expected_name); + + ret =3D asn1_ber_decoder(&cma_decoder, &ctx, leaf_cert->raw_san, + leaf_cert->raw_san_size); + if (ret =3D=3D -EBADMSG || ret =3D=3D -EMSGSIZE) + pci_err(pdev, "Leaf certificate of slot %u " + "has malformed Subject Alternative Name\n", slot); + if (ret < 0) + return ret; + + if (!ctx.found) { + pci_err(pdev, "Leaf certificate of slot %u " + "has no OtherName with CMA OID\n", slot); + return -EINVAL; + } + + return 0; +} + #define PCI_DOE_FEATURE_CMA 1 =20 static ssize_t pci_doe_transport(void *priv, struct device *dev, @@ -63,7 +184,7 @@ void pci_cma_init(struct pci_dev *pdev) =20 pdev->spdm_state =3D spdm_create(&pdev->dev, pci_doe_transport, doe, PCI_DOE_MAX_PAYLOAD, pci_cma_keyring, - NULL); + pci_cma_validate); if (!pdev->spdm_state) return; =20 diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h index ebce402854de..113f4e802ec4 100644 --- a/include/linux/oid_registry.h +++ b/include/linux/oid_registry.h @@ -150,6 +150,9 @@ enum OID { OID_id_ml_dsa_65, /* 2.16.840.1.101.3.4.3.18 */ OID_id_ml_dsa_87, /* 2.16.840.1.101.3.4.3.19 */ =20 + /* PCI */ + OID_CMA, /* 2.23.147 */ + OID__NR }; =20 --=20 2.52.0 From nobody Thu Apr 2 15:42:27 2026 Received: from mail-pf1-f177.google.com (mail-pf1-f177.google.com [209.85.210.177]) (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 7D534346AC4 for ; Wed, 11 Feb 2026 03:31:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.177 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780666; cv=none; b=BUlFUYIEDfRzW47mIx6ebV3bQDAI4Nhio4vyKJob8TkQn/cNp9SN6naJ/RKr9P1OWbRPYvWuat2gngbitzBsoQrLYzxcMTBqkPw469Fi3kpHEjSvJPGgwm3NyVP7iHrxWQvvQGRtPBRTJ9HqdDb7yx4z40PheP0gUQMmjfm7ME0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780666; c=relaxed/simple; bh=nEGbuxBRykRFhxcsk3nfLY/F3Mx9sTgZjjxcuTK+f0E=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=mZRyT8+oq3Hyu7H5sFWrz44wF8x+KB1/0LwzMf2w5tApkO/gHMQ0zF8KLSSH6Io31AWoLmpJszBcxj4ULC21noCwaUzHG+o02O4ODFp/73oijEohMq0j1JPDOy7mrJ6gXADpC2pQ/66rJozolv8JiDDdtls+XBWxCFeiVQK53Gk= 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=fxj5BO4g; arc=none smtp.client-ip=209.85.210.177 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="fxj5BO4g" Received: by mail-pf1-f177.google.com with SMTP id d2e1a72fcca58-82491fbf02cso463949b3a.1 for ; Tue, 10 Feb 2026 19:31:05 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780665; x=1771385465; 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=+2+pbfB22AAo9Fg9A+J5YEYX8Vl3inVVO54MfCnuT58=; b=fxj5BO4gvTnsK0Ow4irLuMjhcMEnmpMTKmsZ2CX15z4h3bJqNgmF7KCQtoCiSp7F2a HclUnhNiVZBN0RDU/OUElTj2nbpF4SDIymOgR9ctUC9GSxuHQtAoh7iz5ovrh6zv6PGF 0UR2un9DwAWdNHUQLRfzvjwQiguF2cigxszJGDOx5xEa6jSbb89d/UbmGhR+gBhcdkNJ cHD5IF1SR+Ioy5Jow4wvFfjZBahegKdHQ+azHU7ArQ9nFJIlJME1Dwf6sqVAUE9lr/WP 5ZZmXEqer6iEm2nVvdX7V5hrvX0lD5gBGOjaejoldugfTCnj0mKp+C/lE7T8nzGHrtct ml2w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780665; x=1771385465; 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=+2+pbfB22AAo9Fg9A+J5YEYX8Vl3inVVO54MfCnuT58=; b=JpDzdxtbV09dvnTX9ypr6+rqgxGAfiGnZohTsaPaF1P9JDJyKDDl/e2y94z8uaKivC 1CQfvlnLFZZfJnu07y3CeTpXVoHt8y3dAtVxdXNgrdzSrE3EQzSoDL4OW5ddj7/KpJB+ rOJrF1rrTRoh7ejkvXOY3FNhLUDNWi1I5RiuUz84lT03Q0WOQAApiJKFeBdTcD3LyhKQ m50jbhHFCVa56ODoeQwIP2p3Pd/X1k7gojtalg2SOjb/10qVJ/UQTFBuYzzX9bCvgzj+ TJd1wQqqSz4wT0rpeG2cXmzICe1mpm4bevpmw9DTd+ot4OpNd0uaYrCnEB3T2WCSCMfO 3vzg== X-Forwarded-Encrypted: i=1; AJvYcCXzIg1qvXcojQYZFZ6r4gfw6d+epkzF7LIa2VlP/PbejBHM2xFQP873g/LvHCTjgcrQ0arpwDK/6EO2TPA=@vger.kernel.org X-Gm-Message-State: AOJu0YzWm0eVtJoqJBMWSkuagyIQMzo4r4uqIqp2Rgq7nlkr0VDiebjq Zt5/SBRkXx+ov4Opi0DTUXpRWw4JEjixIrWt/vA+XmRltc45xk+UlVi9 X-Gm-Gg: AZuq6aJf5gZ+e3Q9GRUBKEGBdN2KJBusoqSqDGJE7kiqO4dAaqan07LCofUTi4n/hFj seNdjVJnq2jj1yl22Dia1SSSNjuhMBPiK534g4QO0Qe3wtxJgr61WuzHGQ6meaTJyrfsI7OJpep yzW5BynG4A7D0X3hbO9XsDUbDbyiPfVVaniKLoQ9vJA+AD7czY/8rQyavmWUy5gQbJdxgdrqYDk DgsfQ9tqjLQiBv0cGk/KOfg2Bd2OJ8x98TRh/sv06beBzsuca6m/PLU4pxjb2+YtVWkK0TZCssi JjhlIVfgLGyYBL9b/fzh2218hLsToeoGd+RWrxJ9dtvVabWeX0xf4phh9uIDwV4dzAwe/HJPD8f LaeA/jGznDDtsJNRkUuU1/6gYzbKaInXkiXeIgO01vNznghnoIHXLuZfDwdZwn39UsDqsEfJHZD 111NQdUJAWd0ZyFXUZ0C8ix/cKylERRwGqXVUktYugCLnyM5lLtbjd X-Received: by 2002:a05:6a20:4308:b0:34f:2f38:cad9 with SMTP id adf61e73a8af0-3942e6994f7mr1430450637.53.1770780664657; Tue, 10 Feb 2026 19:31:04 -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.30.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:31:04 -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 11/27] PCI/CMA: Reauthenticate devices on reset and resume Date: Wed, 11 Feb 2026 13:29:18 +1000 Message-ID: <20260211032935.2705841-12-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: Lukas Wunner CMA-SPDM state is lost when a device undergoes a Conventional Reset. (But not a Function Level Reset, PCIe r6.2 sec 6.6.2.) A D3cold to D0 transition implies a Conventional Reset (PCIe r6.2 sec 5.8). Thus, reauthenticate devices on resume from D3cold and on recovery from a Secondary Bus Reset or DPC-induced Hot Reset. The requirement to reauthenticate devices on resume from system sleep (and in the future reestablish IDE encryption) is the reason why SPDM needs to be in-kernel: During ->resume_noirq, which is the first phase after system sleep, the PCI core walks down the hierarchy, puts each device in D0, restores its config space and invokes the driver's ->resume_noirq callback. The driver is afforded the right to access the device already during this phase. To retain this usage model in the face of authentication and encryption, CMA-SPDM reauthentication and IDE reestablishment must happen during the ->resume_noirq phase, before the driver's first access to the device. The driver is thus afforded seamless authenticated and encrypted access until the last moment before suspend and from the first moment after resume. During the ->resume_noirq phase, device interrupts are not yet enabled. It is thus impossible to defer CMA-SPDM reauthentication to a user space component on an attached disk or on the network, making an in-kernel SPDM implementation mandatory. The same catch-22 exists on recovery from a Conventional Reset: A user space SPDM implementation might live on a device which underwent reset, rendering its execution impossible. Signed-off-by: Lukas Wunner Reviewed-by: Alistair Francis --- drivers/pci/cma.c | 15 +++++++++++++++ drivers/pci/pci-driver.c | 1 + drivers/pci/pci.c | 12 ++++++++++-- drivers/pci/pci.h | 2 ++ drivers/pci/pcie/err.c | 3 +++ 5 files changed, 31 insertions(+), 2 deletions(-) diff --git a/drivers/pci/cma.c b/drivers/pci/cma.c index e974d489c7a2..f2c435b04b92 100644 --- a/drivers/pci/cma.c +++ b/drivers/pci/cma.c @@ -195,6 +195,21 @@ void pci_cma_init(struct pci_dev *pdev) spdm_authenticate(pdev->spdm_state); } =20 +/** + * pci_cma_reauthenticate() - Perform CMA-SPDM authentication again + * @pdev: Device to reauthenticate + * + * Can be called by drivers after device identity has mutated, + * e.g. after downloading firmware to an FPGA device. + */ +void pci_cma_reauthenticate(struct pci_dev *pdev) +{ + if (!pdev->spdm_state) + return; + + spdm_authenticate(pdev->spdm_state); +} + void pci_cma_destroy(struct pci_dev *pdev) { if (!pdev->spdm_state) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index a9590601835a..d1a574e21b0b 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -589,6 +589,7 @@ static void pci_pm_default_resume_early(struct pci_dev = *pci_dev) pci_pm_power_up_and_verify_state(pci_dev); pci_restore_state(pci_dev); pci_pme_restore(pci_dev); + pci_cma_reauthenticate(pci_dev); } =20 static void pci_pm_bridge_power_up_actions(struct pci_dev *pci_dev) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 13dbb405dc31..7f03e07150a0 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4887,8 +4887,16 @@ static int pci_reset_bus_function(struct pci_dev *de= v, bool probe) =20 rc =3D pci_dev_reset_slot_function(dev, probe); if (rc !=3D -ENOTTY) - return rc; - return pci_parent_bus_reset(dev, probe); + goto done; + + rc =3D pci_parent_bus_reset(dev, probe); + +done: + /* CMA-SPDM state is lost upon a Conventional Reset */ + if (!probe) + pci_cma_reauthenticate(dev); + + return rc; } =20 static int cxl_reset_bus_function(struct pci_dev *dev, bool probe) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 1ab1a39e8df3..f87a4b7c92b8 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -602,9 +602,11 @@ static inline void pci_doe_disconnected(struct pci_dev= *pdev) { } #ifdef CONFIG_PCI_CMA void pci_cma_init(struct pci_dev *pdev); void pci_cma_destroy(struct pci_dev *pdev); +void pci_cma_reauthenticate(struct pci_dev *pdev); #else static inline void pci_cma_init(struct pci_dev *pdev) { } static inline void pci_cma_destroy(struct pci_dev *pdev) { } +static inline void pci_cma_reauthenticate(struct pci_dev *pdev) { } #endif =20 #ifdef CONFIG_PCI_NPEM diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c index bebe4bc111d7..700663c38e31 100644 --- a/drivers/pci/pcie/err.c +++ b/drivers/pci/pcie/err.c @@ -151,6 +151,9 @@ static int report_slot_reset(struct pci_dev *dev, void = *data) pci_ers_result_t vote, *result =3D data; const struct pci_error_handlers *err_handler; =20 + /* CMA-SPDM state is lost upon a Conventional Reset */ + pci_cma_reauthenticate(dev); + device_lock(&dev->dev); pdrv =3D dev->driver; if (!pci_dev_set_io_state(dev, pci_channel_io_normal) || --=20 2.52.0 From nobody Thu Apr 2 15:42:27 2026 Received: from mail-pf1-f179.google.com (mail-pf1-f179.google.com [209.85.210.179]) (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 781653451C8 for ; Wed, 11 Feb 2026 03:31:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.179 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780673; cv=none; b=BF7SwyIDcTqm4dOTpT55hxbocv+nVXX4oPFi48U0cci3Z5WDzR/kqVEgEAWODheu2zWSdVtB6wmBcndktfXGrJYftr+nsudrxXWVZF/slTCZP7G8Z99EJoohPy5nJpOfnrNQTD5wzaLF/Nl5hyDOC9sSBYu6D9sH41c+ncRDesY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780673; c=relaxed/simple; bh=ggZ0casIP8pm1D8jNoCOP6ICbzQ2CQUo7npwmTUPjDc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RWSpUFWnPZS+YiqPzexxCmTJtoCRhlCefRGfyz8lbJ7R7m8BMvU2H1bbbw8nyJ48omLghQ3Ys9tA0uI/mlC/HM2qTRZKlqjiiY3p4YlDdDHSb5LX4BDO9IsoBF/o6IdtgIzf4fILmxMtCX7W+acGWq0b2LuiQAzV08UJhKv+iQ8= 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=lun180/L; arc=none smtp.client-ip=209.85.210.179 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="lun180/L" Received: by mail-pf1-f179.google.com with SMTP id d2e1a72fcca58-824a2888de5so6072b3a.0 for ; Tue, 10 Feb 2026 19:31:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780672; x=1771385472; 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=Nd79arduRCDAlNKbcxg57zbXwsYD/G+5bc7S2bWrry0=; b=lun180/LZBG+3fBHdkLXtA5NoQ+zx/YEXDY5uu4QqgO9FXm9EY7TLORS76aLtNALIn F3fJG9WmBliLBy5Oo7jMyCZK5knu2RT0wTi77PQZpR4dKb8Gi8gqWKKOrN9+7TlxQQHq tUhjZPz/vSph5gxwkgCUyp+G0fvYrKSAcmKm8t8IeBWHVs1aAKDzywVjyS3n4aCbj5fj 3hUtI0zLAdKVS7YPC/uni85TCHhJRMdYLnDVGgdTb6QUcevaNSnVTopUximr4xq7uSJp iaeNRCFZ12hqIopzkkdAJCw2jnK5Nr7dGrvWdwsDq6akbBpXLwVnG9E+T6JfCCbKAEw7 IH4w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780672; x=1771385472; 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=Nd79arduRCDAlNKbcxg57zbXwsYD/G+5bc7S2bWrry0=; b=rAQriX39jhJkd/YTnxfFcn3MXGXN94Nt/KpZoAt+0vGzwF4Va9JKBHrgnX8jCsKEgs 7SnAprVP161eZiymfFR1hvuoLVCOF5r7m59J7RPU58YlIHXUDpGcDliT95io5+vLbl6I pjgklGnodqIofDcmqKb8kNdtuBdY/whAGWuz/wmEmuTH7U7uQXZ15Svysjqp9H6xdNJG gnwx9EwyH1XkKWyMNeSSKUXFNg3mVwlAfV5QBnmuFUwcgzDZZT7s11zfQW7Y0jIWQTvH IUFKWe383PMdtoq408qVMhMGjojjkN2C/uzBPmz9VXa2Tz3opvI9V213nZP4vxkjgR2Y aw8A== X-Forwarded-Encrypted: i=1; AJvYcCVr6COcfvswaYLNamw3YzqNLJkX0c1IRS0KAKmfFkvnr/QDh+T2twt4rIXp221Stjq5KS9meUol+FT6pPM=@vger.kernel.org X-Gm-Message-State: AOJu0YzvR9pABW9P52+pb5pShKgecVsCqiMO6zGqzxiEx9rft/4gLJ15 K7nOosH4POA+9989hh2GOTXzfIFBWjwlGjVc/8T59w6s+pDnBR6UaMVx X-Gm-Gg: AZuq6aJJrcDsFsDRB43GvtIBrMpPxvdAIAQJV17mbENQzcIglQ56sb5bS/HjV4qw9Ov UgQjp9kQdO9xInjkiatzXOnpdE4FbpKOC7cfYU47md21RDw9aiQs5Sxyaw5XQ5e+SEvLepLSZGI 2juR3/8yj3G1rkt/8vMlNekYaerov4Nm9POlCR8L1Gr5Hzh6ylfH5zRGMqshuSRtplD5mFrlmx3 aduOkDohZdbv8uggvP9/R6/8Uc7jdeGgqp3/cBew06cciTe8BqOvuVOVv87dPXH/CuFcEWlFovl DIif+UoYq59cbph2WpR6IhpbMrbkHvovfgu+pTJlt0RdIEpF4iMdaYyZoyo++enTyprShU4Hlbb OPjxUamP4bmpC4glhp6+SBh34rG7F+phsN9cqJpAzKjoLTVhY7YnprjgSsZaUa+6UVhe3CaklIe nSyOGzyHb60QzaPrk74wxlNv9aV1i4S6P4nSrlrwf1nQ== X-Received: by 2002:a05:6a20:2585:b0:38e:95f9:8cc1 with SMTP id adf61e73a8af0-39415bdbdbdmr4618591637.12.1770780671775; Tue, 10 Feb 2026 19:31:11 -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.31.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:31:11 -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 12/27] lib: rspdm: Support SPDM get_version Date: Wed, 11 Feb 2026 13:29:19 +1000 Message-ID: <20260211032935.2705841-13-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 the GET_VERSION SPDM command. Signed-off-by: Alistair Francis --- lib/rspdm/consts.rs | 17 +++++++++++ lib/rspdm/lib.rs | 6 +++- lib/rspdm/state.rs | 58 ++++++++++++++++++++++++++++++++++-- lib/rspdm/validator.rs | 67 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 144 insertions(+), 4 deletions(-) diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs index 40ce60eba2f3..38f48f0863e2 100644 --- a/lib/rspdm/consts.rs +++ b/lib/rspdm/consts.rs @@ -7,6 +7,20 @@ //! Rust implementation of the DMTF Security Protocol and Data Model (SPDM) //! =20 +use crate::validator::SpdmHeader; +use core::mem; + +/* SPDM versions supported by this implementation */ +pub(crate) const SPDM_VER_10: u8 =3D 0x10; +#[allow(dead_code)] +pub(crate) const SPDM_VER_11: u8 =3D 0x11; +#[allow(dead_code)] +pub(crate) const SPDM_VER_12: u8 =3D 0x12; +pub(crate) const SPDM_VER_13: u8 =3D 0x13; + +pub(crate) const SPDM_MIN_VER: u8 =3D SPDM_VER_10; +pub(crate) const SPDM_MAX_VER: u8 =3D SPDM_VER_13; + pub(crate) const SPDM_REQ: u8 =3D 0x80; pub(crate) const SPDM_ERROR: u8 =3D 0x7f; =20 @@ -115,3 +129,6 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core= ::fmt::Result { Ok(()) } } + +pub(crate) const SPDM_GET_VERSION: u8 =3D 0x84; +pub(crate) const SPDM_GET_VERSION_LEN: usize =3D mem::size_of::() + 255; diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs index 2bb716140e0a..08abcbb21247 100644 --- a/lib/rspdm/lib.rs +++ b/lib/rspdm/lib.rs @@ -108,7 +108,11 @@ /// Return 0 on success or a negative errno. In particular, -EPROTONOSUPP= ORT /// indicates authentication is not supported by the device. #[no_mangle] -pub unsafe extern "C" fn spdm_authenticate(_state: &'static mut SpdmState)= -> c_int { +pub unsafe extern "C" fn spdm_authenticate(state: &'static mut SpdmState) = -> c_int { + if let Err(e) =3D state.get_version() { + return e.to_errno() as c_int; + } + 0 } =20 diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs index 68861f30e3fa..3ad53ec05044 100644 --- a/lib/rspdm/state.rs +++ b/lib/rspdm/state.rs @@ -8,6 +8,7 @@ //! =20 use core::ffi::c_void; +use core::slice::from_raw_parts_mut; use kernel::prelude::*; use kernel::{ bindings, @@ -15,8 +16,10 @@ validate::Untrusted, }; =20 -use crate::consts::{SpdmErrorCode, SPDM_ERROR, SPDM_REQ}; -use crate::validator::{SpdmErrorRsp, SpdmHeader}; +use crate::consts::{ + SpdmErrorCode, SPDM_ERROR, SPDM_GET_VERSION_LEN, SPDM_MAX_VER, SPDM_MI= N_VER, SPDM_REQ, +}; +use crate::validator::{GetVersionReq, GetVersionRsp, SpdmErrorRsp, SpdmHea= der}; =20 /// The current SPDM session state for a device. Based on the /// C `struct spdm_state`. @@ -61,7 +64,7 @@ pub(crate) fn new( transport_sz, keyring, validate, - version: 0x10, + version: SPDM_MIN_VER, } } =20 @@ -217,4 +220,53 @@ pub(crate) fn spdm_exchange( =20 Ok(length) } + + /// Negoiate a supported SPDM version and store the information + /// in the `SpdmState`. + pub(crate) fn get_version(&mut self) -> Result<(), Error> { + let mut request =3D GetVersionReq::default(); + request.version =3D self.version; + + // SAFETY: `request` is repr(C) and packed, so we can convert it t= o a slice + let request_buf =3D unsafe { + from_raw_parts_mut( + &mut request as *mut _ as *mut u8, + core::mem::size_of::(), + ) + }; + + let mut response_vec: KVec =3D KVec::with_capacity(SPDM_GET_VE= RSION_LEN, GFP_KERNEL)?; + // SAFETY: `request` is repr(C) and packed, so we can convert it t= o a slice + let response_buf =3D + unsafe { from_raw_parts_mut(response_vec.as_mut_ptr(), SPDM_GE= T_VERSION_LEN) }; + + let rc =3D self.spdm_exchange(request_buf, response_buf)?; + + // SAFETY: `rc` is the length of data read, which will be smaller + // then the capacity of the vector + unsafe { response_vec.inc_len(rc as usize) }; + + let response: &mut GetVersionRsp =3D Untrusted::new_mut(&mut respo= nse_vec).validate_mut()?; + + let mut foundver =3D false; + for i in 0..response.version_number_entry_count { + // Creating a reference on a packed struct will result in + // undefined behaviour, so we operate on the raw data directly + let unaligned =3D core::ptr::addr_of_mut!(response.version_num= ber_entries) as *mut u16; + let addr =3D unaligned.wrapping_add(i as usize); + let version =3D (unsafe { core::ptr::read_unaligned::(add= r) } >> 8) as u8; + + if version >=3D self.version && version <=3D SPDM_MAX_VER { + self.version =3D version; + foundver =3D true; + } + } + + if !foundver { + pr_err!("No common supported version\n"); + to_result(-(bindings::EPROTO as i32))?; + } + + Ok(()) + } } diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs index a0a3a2f46952..f69be6aa6280 100644 --- a/lib/rspdm/validator.rs +++ b/lib/rspdm/validator.rs @@ -7,6 +7,7 @@ //! Rust implementation of the DMTF Security Protocol and Data Model (SPDM) //! =20 +use crate::bindings::{__IncompleteArrayField, __le16}; use crate::consts::SpdmErrorCode; use core::mem; use kernel::prelude::*; @@ -15,6 +16,8 @@ validate::{Unvalidated, Validate}, }; =20 +use crate::consts::SPDM_GET_VERSION; + #[repr(C, packed)] pub(crate) struct SpdmHeader { pub(crate) version: u8, @@ -64,3 +67,67 @@ pub(crate) struct SpdmErrorRsp { pub(crate) error_code: SpdmErrorCode, pub(crate) error_data: u8, } + +#[repr(C, packed)] +pub(crate) struct GetVersionReq { + pub(crate) version: u8, + pub(crate) code: u8, + pub(crate) param1: u8, + pub(crate) param2: u8, +} + +impl Default for GetVersionReq { + fn default() -> Self { + GetVersionReq { + version: 0, + code: SPDM_GET_VERSION, + param1: 0, + param2: 0, + } + } +} + +#[repr(C, packed)] +pub(crate) struct GetVersionRsp { + pub(crate) version: u8, + pub(crate) code: u8, + param1: u8, + param2: u8, + reserved: u8, + pub(crate) version_number_entry_count: u8, + pub(crate) version_number_entries: __IncompleteArrayField<__le16>, +} + +impl Validate<&mut Unvalidated>> for &mut GetVersionRsp { + type Err =3D Error; + + fn validate(unvalidated: &mut Unvalidated>) -> Result { + let raw =3D unvalidated.raw_mut(); + if raw.len() < mem::size_of::() { + return Err(EINVAL); + } + + let version_number_entries =3D *(raw.get(5).ok_or(ENOMEM))? as usi= ze; + let total_expected_size =3D version_number_entries * 2 + 6; + if raw.len() < total_expected_size { + return Err(EINVAL); + } + + let ptr =3D raw.as_mut_ptr(); + // CAST: `GetVersionRsp` only contains integers and has `repr(C)`. + let ptr =3D ptr.cast::(); + // SAFETY: `ptr` came from a reference and the cast above is valid. + let rsp: &mut GetVersionRsp =3D unsafe { &mut *ptr }; + + // Creating a reference on a packed struct will result in + // undefined behaviour, so we operate on the raw data directly + let unaligned =3D core::ptr::addr_of_mut!(rsp.version_number_entri= es) as *mut u16; + for version_offset in 0..version_number_entries { + let addr =3D unaligned.wrapping_add(version_offset); + let version =3D unsafe { core::ptr::read_unaligned::(addr= ) }; + unsafe { core::ptr::write_unaligned::(addr, version.to_le= ()) } + } + + Ok(rsp) + } +} --=20 2.52.0 From nobody Thu Apr 2 15:42:27 2026 Received: from mail-pg1-f172.google.com (mail-pg1-f172.google.com [209.85.215.172]) (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 60B3E346E41 for ; Wed, 11 Feb 2026 03:31:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780680; cv=none; b=lv3r64o25REbmPP+25G3S0A/4oOrSLar66ilfTuUoregaxqq/ixjLPjlrTS5lUKVlP1jZ6mgnDKqDGzs8Po/wvkJ5kajpyWotiudz0Ih2mA/vSk+MRB7ULV62VPIVgJFrn3V18qj9IqmrYS+CsOD0OARMeyNQnPeegb7I8TV//8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780680; c=relaxed/simple; bh=toTNJudqEcjkoiXvBRgz/9Q2oWXvwkah3SRR3afs6KI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=SXlgbh6+ANI9UFtE19mdpmC7GYJdad6mfV/IlH4mgmiNOQRQT46bd3Ql4hVJo8OSroRX6W87/D8wmR+89s4Ka9FFYTNm/PpAZE/SOeso2BE9YKjsPMc+uLhRV/k88qIJwZHnlGDH8bmuMR9nLTxshWD46Ob4OiA/7AJMW3PSM+E= 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=XUUNuXzp; arc=none smtp.client-ip=209.85.215.172 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="XUUNuXzp" Received: by mail-pg1-f172.google.com with SMTP id 41be03b00d2f7-c636487ccaeso2189136a12.1 for ; Tue, 10 Feb 2026 19:31:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780679; x=1771385479; 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=7fSuzEVVGWU561gVbAJ53yu4VEf4z/D49690CUVMVKw=; b=XUUNuXzp9NUrPSsN3S5OsIVrX4vywggMJ+Ya08MjOEvbrw58iABYnbWmxy/rFqrhup PqX5d977fftNlbb/eXLBi/AaGCzkUPyrmVSe2fUUP0h01/M+sLZNQR89WgPdpu+H67gh O2Zr2hXOZJww4sEstmienYh3cyrN6qipu44o2nFy/8O7KlzEzWxZ5IPS1SF5UiQwPe4K CIiavm3OqLR/jr9LJMkKdnulxC+ENzS0SHVE+iXvYEpDvorsDqnzvn6ahMyb5F+7OGUX 7ikc/g/88W4o8U7L1zsPw37XfN0WDdo7ljFG4MQmR+mibVZE2+R47yaFSkHLlTfOloO/ ZhCQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780679; x=1771385479; 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=7fSuzEVVGWU561gVbAJ53yu4VEf4z/D49690CUVMVKw=; b=UZ5IJ1oAf2+JJpXoHz6d8+P8mo/HkuawIypIoACx8pc/PIos6Shq1EtAAblnSmqS8D ol1mWeY7CCtuBDJwAJ+hxuyDI3OhupSsNrHhfW+bbyyn8oOZPKGldYkzwRUHenYVzBHV sqftRGBfeEm8cuv9QaeiVox/+iCMvTz7UDo6J1YX9hU6lDpxH1c6IbXZqy5Vnhjg4fpL bvfdm5RfdSCs+a7W2dU92pGo+IjCWvbWO9aUx/zxMpMzYNndVBcpRLJpxsEcGHINyGs8 5sFKAD6UZSqerLC2r0KQ4y8qjjvNhx/O2a+CjEpiyPWZXEuPx7/8YTHHduJzBzENy3oW rYUg== X-Forwarded-Encrypted: i=1; AJvYcCWYX5xkd3JP1jJHCjQgXZN1F+Gtr8up+ux79JQqOTREP0DJFO5p6V/LdVd4bF+j2kBu/8gND6NirEVK3XA=@vger.kernel.org X-Gm-Message-State: AOJu0YytJJVDtF/EQgFK3pxwn4RNmcMezplvHm9h3B4Biuwm6CVQgcQD yGJHEN1eOjjWTzSte+u4Oe4cRgm2PSgpuRVSuWr9CR6T0CtdLDzGOPYt X-Gm-Gg: AZuq6aJ6iI0a1eQYKD/8NKzar+KeP3wRE3OVTODkpr9U4brkG7eVgXngduFvyUq6ePy rsJ3ETePR8d+eZtqqVvpoqOEDMQZOEJTChXWF4hF5MV3NVhRYczBn1hfHhXSACa2m0AKn2i5Mkr sLdu4ljmL7X5Pm4ionIHMz2tttO35KN6qk06hZGLRrTkw8mFiCcfKXsF1BMmv2K5g7UgQ03w9N3 AgKVOIv2pOoHfoUtt9FsY1ggCCzsENKm8RqH1YDFYJSUsWLA/BHGdvmr6uxzuP8HyRApEhD5IMu 4VGZA5lE73K55PXhB0uriCWnU4TYOd3HOxWX2MnE5iFsRV3dwpHlwRS2OL43TSH6qUG5bWyfrQe xsQ//mhcvG5JpYIATP/BB+I2zaEjI1jW2dT8TRm31+0XJB+5xK9qeV/r8Yljq56qjOPuJrtFYMA nrAjZMZ8L1Ekk5qAsl+z5SgtTfFbbdwQP1rlzxnnokfwSukcjhOik0 X-Received: by 2002:a05:6a21:4d17:b0:392:e5eb:efe with SMTP id adf61e73a8af0-394324b73c1mr799709637.78.1770780678809; Tue, 10 Feb 2026 19:31:18 -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.31.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:31:18 -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 13/27] lib: rspdm: Support SPDM get_capabilities Date: Wed, 11 Feb 2026 13:29:20 +1000 Message-ID: <20260211032935.2705841-14-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 the GET_CAPABILITIES SPDM command. Signed-off-by: Alistair Francis --- lib/rspdm/consts.rs | 18 ++++++++-- lib/rspdm/lib.rs | 4 +++ lib/rspdm/state.rs | 68 +++++++++++++++++++++++++++++++++++-- lib/rspdm/validator.rs | 76 +++++++++++++++++++++++++++++++++++++++++- 4 files changed, 161 insertions(+), 5 deletions(-) diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs index 38f48f0863e2..a6a66e2af1b5 100644 --- a/lib/rspdm/consts.rs +++ b/lib/rspdm/consts.rs @@ -12,9 +12,7 @@ =20 /* SPDM versions supported by this implementation */ pub(crate) const SPDM_VER_10: u8 =3D 0x10; -#[allow(dead_code)] pub(crate) const SPDM_VER_11: u8 =3D 0x11; -#[allow(dead_code)] pub(crate) const SPDM_VER_12: u8 =3D 0x12; pub(crate) const SPDM_VER_13: u8 =3D 0x13; =20 @@ -132,3 +130,19 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> cor= e::fmt::Result { =20 pub(crate) const SPDM_GET_VERSION: u8 =3D 0x84; pub(crate) const SPDM_GET_VERSION_LEN: usize =3D mem::size_of::() + 255; + +pub(crate) const SPDM_GET_CAPABILITIES: u8 =3D 0xe1; +pub(crate) const SPDM_MIN_DATA_TRANSFER_SIZE: u32 =3D 42; + +// SPDM cryptographic timeout of this implementation: +// Assume calculations may take up to 1 sec on a busy machine, which equals +// roughly 1 << 20. That's within the limits mandated for responders by C= MA +// (1 << 23 usec, PCIe r6.2 sec 6.31.3) and DOE (1 sec, PCIe r6.2 sec 6.30= .2). +// Used in GET_CAPABILITIES exchange. +pub(crate) const SPDM_CTEXPONENT: u8 =3D 20; + +pub(crate) const SPDM_CERT_CAP: u32 =3D 1 << 1; +pub(crate) const SPDM_CHAL_CAP: u32 =3D 1 << 2; + +pub(crate) const SPDM_REQ_CAPS: u32 =3D SPDM_CERT_CAP | SPDM_CHAL_CAP; +pub(crate) const SPDM_RSP_MIN_CAPS: u32 =3D SPDM_CERT_CAP | SPDM_CHAL_CAP; diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs index 08abcbb21247..5f6653ada59d 100644 --- a/lib/rspdm/lib.rs +++ b/lib/rspdm/lib.rs @@ -113,6 +113,10 @@ return e.to_errno() as c_int; } =20 + if let Err(e) =3D state.get_capabilities() { + return e.to_errno() as c_int; + } + 0 } =20 diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs index 3ad53ec05044..0efad7f341cd 100644 --- a/lib/rspdm/state.rs +++ b/lib/rspdm/state.rs @@ -17,9 +17,12 @@ }; =20 use crate::consts::{ - SpdmErrorCode, SPDM_ERROR, SPDM_GET_VERSION_LEN, SPDM_MAX_VER, SPDM_MI= N_VER, SPDM_REQ, + SpdmErrorCode, SPDM_ERROR, SPDM_GET_VERSION_LEN, SPDM_MAX_VER, SPDM_MI= N_DATA_TRANSFER_SIZE, + SPDM_MIN_VER, SPDM_REQ, SPDM_RSP_MIN_CAPS, SPDM_VER_10, SPDM_VER_11, S= PDM_VER_12, +}; +use crate::validator::{ + GetCapabilitiesReq, GetCapabilitiesRsp, GetVersionReq, GetVersionRsp, = SpdmErrorRsp, SpdmHeader, }; -use crate::validator::{GetVersionReq, GetVersionRsp, SpdmErrorRsp, SpdmHea= der}; =20 /// The current SPDM session state for a device. Based on the /// C `struct spdm_state`. @@ -35,6 +38,8 @@ /// /// `version`: Maximum common supported version of requester and responder. /// Negotiated during GET_VERSION exchange. +/// @rsp_caps: Cached capabilities of responder. +/// Received during GET_CAPABILITIES exchange. #[expect(dead_code)] pub struct SpdmState { pub(crate) dev: *mut bindings::device, @@ -46,6 +51,7 @@ pub struct SpdmState { =20 // Negotiated state pub(crate) version: u8, + pub(crate) rsp_caps: u32, } =20 impl SpdmState { @@ -65,6 +71,7 @@ pub(crate) fn new( keyring, validate, version: SPDM_MIN_VER, + rsp_caps: 0, } } =20 @@ -269,4 +276,61 @@ pub(crate) fn get_version(&mut self) -> Result<(), Err= or> { =20 Ok(()) } + + /// Obtain the supported capabilities from an SPDM session and store t= he + /// information in the `SpdmState`. + pub(crate) fn get_capabilities(&mut self) -> Result<(), Error> { + let mut request =3D GetCapabilitiesReq::default(); + request.version =3D self.version; + + let (req_sz, rsp_sz) =3D match self.version { + SPDM_VER_10 =3D> (4, 8), + SPDM_VER_11 =3D> (8, 8), + _ =3D> { + request.data_transfer_size =3D self.transport_sz.to_le(); + request.max_spdm_msg_size =3D request.data_transfer_size; + + ( + core::mem::size_of::(), + core::mem::size_of::(), + ) + } + }; + + // SAFETY: `request` is repr(C) and packed, so we can convert it t= o a slice + let request_buf =3D unsafe { from_raw_parts_mut(&mut request as *m= ut _ as *mut u8, req_sz) }; + + let mut response_vec: KVec =3D KVec::with_capacity(rsp_sz, GFP= _KERNEL)?; + // SAFETY: `request` is repr(C) and packed, so we can convert it t= o a slice + let response_buf =3D unsafe { from_raw_parts_mut(response_vec.as_m= ut_ptr(), rsp_sz) }; + + let rc =3D self.spdm_exchange(request_buf, response_buf)?; + + if rc < (rsp_sz as i32) { + pr_err!("Truncated capabilities response\n"); + to_result(-(bindings::EIO as i32))?; + } + + // SAFETY: `rc` is the length of data read, which will be smaller + // then the capacity of the vector + unsafe { response_vec.inc_len(rc as usize) }; + + let response: &mut GetCapabilitiesRsp =3D + Untrusted::new_mut(&mut response_vec).validate_mut()?; + + self.rsp_caps =3D u32::from_le(response.flags); + if (self.rsp_caps & SPDM_RSP_MIN_CAPS) !=3D SPDM_RSP_MIN_CAPS { + to_result(-(bindings::EPROTONOSUPPORT as i32))?; + } + + if self.version >=3D SPDM_VER_12 { + if response.data_transfer_size < SPDM_MIN_DATA_TRANSFER_SIZE { + pr_err!("Malformed capabilities response\n"); + to_result(-(bindings::EPROTO as i32))?; + } + self.transport_sz =3D self.transport_sz.min(response.data_tran= sfer_size); + } + + Ok(()) + } } diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs index f69be6aa6280..cd792c46767a 100644 --- a/lib/rspdm/validator.rs +++ b/lib/rspdm/validator.rs @@ -16,7 +16,7 @@ validate::{Unvalidated, Validate}, }; =20 -use crate::consts::SPDM_GET_VERSION; +use crate::consts::{SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, SPDM_GET_VERSI= ON, SPDM_REQ_CAPS}; =20 #[repr(C, packed)] pub(crate) struct SpdmHeader { @@ -131,3 +131,77 @@ fn validate(unvalidated: &mut Unvalidated>) -= > Result Ok(rsp) } } + +#[repr(C, packed)] +pub(crate) struct GetCapabilitiesReq { + pub(crate) version: u8, + pub(crate) code: u8, + pub(crate) param1: u8, + pub(crate) param2: u8, + + reserved1: u8, + pub(crate) ctexponent: u8, + reserved2: u16, + + pub(crate) flags: u32, + + /* End of SPDM 1.1 structure */ + pub(crate) data_transfer_size: u32, + pub(crate) max_spdm_msg_size: u32, +} + +impl Default for GetCapabilitiesReq { + fn default() -> Self { + GetCapabilitiesReq { + version: 0, + code: SPDM_GET_CAPABILITIES, + param1: 0, + param2: 0, + reserved1: 0, + ctexponent: SPDM_CTEXPONENT, + reserved2: 0, + flags: (SPDM_REQ_CAPS as u32).to_le(), + data_transfer_size: 0, + max_spdm_msg_size: 0, + } + } +} + +#[repr(C, packed)] +pub(crate) struct GetCapabilitiesRsp { + pub(crate) version: u8, + pub(crate) code: u8, + pub(crate) param1: u8, + pub(crate) param2: u8, + + reserved1: u8, + pub(crate) ctexponent: u8, + reserved2: u16, + + pub(crate) flags: u32, + + /* End of SPDM 1.1 structure */ + pub(crate) data_transfer_size: u32, + pub(crate) max_spdm_msg_size: u32, + + pub(crate) supported_algorithms: __IncompleteArrayField<__le16>, +} + +impl Validate<&mut Unvalidated>> for &mut GetCapabilitiesRsp { + type Err =3D Error; + + fn validate(unvalidated: &mut Unvalidated>) -> Result { + let raw =3D unvalidated.raw_mut(); + if raw.len() < mem::size_of::() { + return Err(EINVAL); + } + + let ptr =3D raw.as_mut_ptr(); + // CAST: `GetCapabilitiesRsp` only contains integers and has `repr= (C)`. + let ptr =3D ptr.cast::(); + // SAFETY: `ptr` came from a reference and the cast above is valid. + let rsp: &mut GetCapabilitiesRsp =3D unsafe { &mut *ptr }; + + Ok(rsp) + } +} --=20 2.52.0 From nobody Thu Apr 2 15:42:27 2026 Received: from mail-pg1-f180.google.com (mail-pg1-f180.google.com [209.85.215.180]) (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 91FDB344DA9 for ; Wed, 11 Feb 2026 03:31:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780688; cv=none; b=EFdYIO6kF6cxRPT4isDkb1lSnxHb3qf2F6r8si7KnY1Xw5rHfBoYQFW8+fwy1A/rIrpYI3emSMUCSYfnZeK7O6y2b8WkzHjKIngl3oflh0z8D2m6uaiPVxmjvReAsOnqtI+xY0ScvaEhKHQE6hQmMAAukG67o6AIfwiDk92gmEU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780688; c=relaxed/simple; bh=F0QPRi4Cr/kqrQqoZ0RP0Sczcg8ZftafBi6pfegt1k8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Nrm2YqLLBVPkjmjbFlOkQx9sI62E9iTeeMR9vAMhwa6cVZ96csuNbvznATMwM7ziClPKAzBQYed9IaG/nuDoomEj1dfUWdxLseSR0hqfCKT9irOnTHq+Yxcv7nSbrZ7dpxLwbaClXoOlV3u2h9jhWcQZuUPfu7ApmlTY+uuSA5Q= 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=ncGWTr9/; arc=none smtp.client-ip=209.85.215.180 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="ncGWTr9/" Received: by mail-pg1-f180.google.com with SMTP id 41be03b00d2f7-c62239decbeso1788006a12.2 for ; Tue, 10 Feb 2026 19:31:26 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780686; x=1771385486; 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=rq+I562YFt2ssteRsuZO53DNGRtLr+SzLx+58OWHihw=; b=ncGWTr9/g4DW3/ZN6SPEK/CjpSH21NoRAGaDWjucUPfb8xmgJIs8nkuVkYYCoKX5dD BbKzwicB/tzXiNNCyRG5Z3gPNzTp1XDGtFlMF+qcgNgmjhjKI9/ahe39o/knRd7CYb9f fULFsfkWl+5TaRl0ONmSi87tMo8aAhYuF/LJdsB7lZ6H8nszcX/iZBsgafEW+f+CO3Dh 6xMUCRI0SWJQu4RIGlCFBJipeZAMlMI66PxWPKYZE5r/JLcQuNQS+RKAzGbH4DTeFUl4 d05d6JN5LZf5K1JJxS0RprDM/m0bvrAcCNl2NYS+IT7G7e2LuTq5cytcFTdj9YeUfE0B wr0g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780686; x=1771385486; 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=rq+I562YFt2ssteRsuZO53DNGRtLr+SzLx+58OWHihw=; b=uzUdeH9FZ6GDVsrCrn1D1y7T3KsVsaDkYYJFWAjzvGiRMy6B/BwuF6h+QgRvy3GnOT 63FP83rzqL3u1rEAq1dkFK/eG/Ls/JIJ6NBWYEX+AGlX/Z+CEOcM9XPsTOKqhGGCuMft 1qIC8ZGpV/2iREgLVeEfSyiEI2zAlRiPYHRzsUcz80qWgJ66kCU4vvfqBtOyFppOKi/3 GVFzGBNuQlKIU8/9+T5QkGwbXjn4OHj61V27Uyy8FLqJqKmhd5dWOLCt/PANvPjeY4lP QM60YMJ2Wg21keAGdqIWNSI4FL0PcvUg/rQdSu1xvMF/Y0/+RIy76fCo0pCUg7I8nDCk txQQ== X-Forwarded-Encrypted: i=1; AJvYcCUZKS933Cu+7EZKaT6M7UMJL5k1/QGikm5dn1/cwjItKq0e84yyIcIDKuPd5L4GOYrwVT/KtGiQcRLZWCg=@vger.kernel.org X-Gm-Message-State: AOJu0YxC7KkvZI0kixRre3RiZHJ2xPi7iHIbzkRuk8HAeCxpnqDoUGkD CecF13t4bm3EfBt5gXVB8cyABmV59H1oxpE+A9gd++rsMaFNXO3HCHaV X-Gm-Gg: AZuq6aK4Qzp24BxKSG5dTzsYh3ruW5fHqgGIqPfNCx2jBh5jU4/txEp2Y9YuswrSmhv J5UsO73L2Pq6hp00aWlG/hjb/Ts5zT5d24do6pPokwk+X4QziDSh/NMQAlZrO+zy1GYlQHsc82e B4ntQe2eYTMRVYwprFvMCXpvOqCk5E6/lP/ypIJ3TGRGZrWeB/236kauzey8Bj/oe6h8LDF+eSk KIbqD5eXIv5pSrD3iwj5xI/+Akl0LDFm+/HNEME4l33OX9ziSWodthwnoIDLUZzl++/KgVOEmYy UCG6IPVV8uuWnbzFhNG11vOOYO8lGsiXZAJHWtP0lPvZsvJ/+22GQtjoa48YtAQpR+hTGyFXouI 31TEYO0qNQnYq7qc0R/OAGfpy7OtpjYGSpmSCGMs8JYzEubU10dsfJhFBCV/8efSvZMUVAyKiiH U8ZCq5RfmVwOTrrWYccZl1hOvGUmH4jx0yFPdcjYHZRA== X-Received: by 2002:a05:6a20:258d:b0:35f:b96d:af11 with SMTP id adf61e73a8af0-3942e384f48mr1325597637.5.1770780685920; Tue, 10 Feb 2026 19:31:25 -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.31.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:31:25 -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 14/27] lib: rspdm: Support SPDM negotiate_algorithms Date: Wed, 11 Feb 2026 13:29:21 +1000 Message-ID: <20260211032935.2705841-15-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 the NEGOTIATE_ALGORITHMS SPDM command. Signed-off-by: Alistair Francis --- lib/rspdm/consts.rs | 53 ++++++++++ lib/rspdm/lib.rs | 16 ++- lib/rspdm/state.rs | 214 ++++++++++++++++++++++++++++++++++++++++- lib/rspdm/validator.rs | 115 +++++++++++++++++++++- 4 files changed, 391 insertions(+), 7 deletions(-) diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs index a6a66e2af1b5..e8a05fd4299b 100644 --- a/lib/rspdm/consts.rs +++ b/lib/rspdm/consts.rs @@ -143,6 +143,59 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> cor= e::fmt::Result { =20 pub(crate) const SPDM_CERT_CAP: u32 =3D 1 << 1; pub(crate) const SPDM_CHAL_CAP: u32 =3D 1 << 2; +pub(crate) const SPDM_MEAS_CAP_MASK: u32 =3D 3 << 3; +pub(crate) const SPDM_KEY_EX_CAP: u32 =3D 1 << 9; =20 pub(crate) const SPDM_REQ_CAPS: u32 =3D SPDM_CERT_CAP | SPDM_CHAL_CAP; pub(crate) const SPDM_RSP_MIN_CAPS: u32 =3D SPDM_CERT_CAP | SPDM_CHAL_CAP; + +pub(crate) const SPDM_NEGOTIATE_ALGS: u8 =3D 0xe3; + +pub(crate) const SPDM_MEAS_SPEC_DMTF: u8 =3D 1 << 0; + +pub(crate) const SPDM_ASYM_RSASSA_2048: u32 =3D 1 << 0; +pub(crate) const _SPDM_ASYM_RSAPSS_2048: u32 =3D 1 << 1; +pub(crate) const SPDM_ASYM_RSASSA_3072: u32 =3D 1 << 2; +pub(crate) const _SPDM_ASYM_RSAPSS_3072: u32 =3D 1 << 3; +pub(crate) const SPDM_ASYM_ECDSA_ECC_NIST_P256: u32 =3D 1 << 4; +pub(crate) const SPDM_ASYM_RSASSA_4096: u32 =3D 1 << 5; +pub(crate) const _SPDM_ASYM_RSAPSS_4096: u32 =3D 1 << 6; +pub(crate) const SPDM_ASYM_ECDSA_ECC_NIST_P384: u32 =3D 1 << 7; +pub(crate) const SPDM_ASYM_ECDSA_ECC_NIST_P521: u32 =3D 1 << 8; +pub(crate) const _SPDM_ASYM_SM2_ECC_SM2_P256: u32 =3D 1 << 9; +pub(crate) const _SPDM_ASYM_EDDSA_ED25519: u32 =3D 1 << 10; +pub(crate) const _SPDM_ASYM_EDDSA_ED448: u32 =3D 1 << 11; + +pub(crate) const SPDM_HASH_SHA_256: u32 =3D 1 << 0; +pub(crate) const SPDM_HASH_SHA_384: u32 =3D 1 << 1; +pub(crate) const SPDM_HASH_SHA_512: u32 =3D 1 << 2; + +#[cfg(CONFIG_CRYPTO_RSA)] +pub(crate) const SPDM_ASYM_RSA: u32 =3D + SPDM_ASYM_RSASSA_2048 | SPDM_ASYM_RSASSA_3072 | SPDM_ASYM_RSASSA_4096; +#[cfg(not(CONFIG_CRYPTO_RSA))] +pub(crate) const SPDM_ASYM_RSA: u32 =3D 0; + +#[cfg(CONFIG_CRYPTO_ECDSA)] +pub(crate) const SPDM_ASYM_ECDSA: u32 =3D + SPDM_ASYM_ECDSA_ECC_NIST_P256 | SPDM_ASYM_ECDSA_ECC_NIST_P384 | SPDM_A= SYM_ECDSA_ECC_NIST_P521; +#[cfg(not(CONFIG_CRYPTO_ECDSA))] +pub(crate) const SPDM_ASYM_ECDSA: u32 =3D 0; + +#[cfg(CONFIG_CRYPTO_SHA256)] +pub(crate) const SPDM_HASH_SHA2_256: u32 =3D SPDM_HASH_SHA_256; +#[cfg(not(CONFIG_CRYPTO_SHA256))] +pub(crate) const SPDM_HASH_SHA2_256: u32 =3D 0; + +#[cfg(CONFIG_CRYPTO_SHA512)] +pub(crate) const SPDM_HASH_SHA2_384_512: u32 =3D SPDM_HASH_SHA_384 | SPDM_= HASH_SHA_512; +#[cfg(not(CONFIG_CRYPTO_SHA512))] +pub(crate) const SPDM_HASH_SHA2_384_512: u32 =3D 0; + +pub(crate) const SPDM_ASYM_ALGOS: u32 =3D SPDM_ASYM_RSA | SPDM_ASYM_ECDSA; +pub(crate) const SPDM_HASH_ALGOS: u32 =3D SPDM_HASH_SHA2_256 | SPDM_HASH_S= HA2_384_512; + +/* Maximum number of ReqAlgStructs sent by this implementation */ +// pub(crate) const SPDM_MAX_REQ_ALG_STRUCT: usize =3D 4; + +pub(crate) const SPDM_OPAQUE_DATA_FMT_GENERAL: u8 =3D 1 << 1; diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs index 5f6653ada59d..33f706a55e09 100644 --- a/lib/rspdm/lib.rs +++ b/lib/rspdm/lib.rs @@ -117,6 +117,10 @@ return e.to_errno() as c_int; } =20 + if let Err(e) =3D state.negotiate_algs() { + return e.to_errno() as c_int; + } + 0 } =20 @@ -124,4 +128,14 @@ /// /// @spdm_state: SPDM session state #[no_mangle] -pub unsafe extern "C" fn spdm_destroy(_state: &'static mut SpdmState) {} +pub unsafe extern "C" fn spdm_destroy(state: &'static mut SpdmState) { + if let Some(desc) =3D &mut state.desc { + unsafe { + bindings::kfree(*desc as *mut _ as *mut c_void); + } + } + + unsafe { + bindings::crypto_free_shash(state.shash); + } +} diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs index 0efad7f341cd..d0b10f27cd9c 100644 --- a/lib/rspdm/state.rs +++ b/lib/rspdm/state.rs @@ -12,16 +12,22 @@ use kernel::prelude::*; use kernel::{ bindings, - error::{code::EINVAL, to_result, Error}, + error::{code::EINVAL, from_err_ptr, to_result, Error}, + str::CStr, validate::Untrusted, }; =20 use crate::consts::{ - SpdmErrorCode, SPDM_ERROR, SPDM_GET_VERSION_LEN, SPDM_MAX_VER, SPDM_MI= N_DATA_TRANSFER_SIZE, - SPDM_MIN_VER, SPDM_REQ, SPDM_RSP_MIN_CAPS, SPDM_VER_10, SPDM_VER_11, S= PDM_VER_12, + SpdmErrorCode, SPDM_ASYM_ALGOS, SPDM_ASYM_ECDSA_ECC_NIST_P256, SPDM_AS= YM_ECDSA_ECC_NIST_P384, + SPDM_ASYM_ECDSA_ECC_NIST_P521, SPDM_ASYM_RSASSA_2048, SPDM_ASYM_RSASSA= _3072, + SPDM_ASYM_RSASSA_4096, SPDM_ERROR, SPDM_GET_VERSION_LEN, SPDM_HASH_ALG= OS, SPDM_HASH_SHA_256, + SPDM_HASH_SHA_384, SPDM_HASH_SHA_512, SPDM_KEY_EX_CAP, SPDM_MAX_VER, S= PDM_MEAS_CAP_MASK, + SPDM_MEAS_SPEC_DMTF, SPDM_MIN_DATA_TRANSFER_SIZE, SPDM_MIN_VER, SPDM_O= PAQUE_DATA_FMT_GENERAL, + SPDM_REQ, SPDM_RSP_MIN_CAPS, SPDM_VER_10, SPDM_VER_11, SPDM_VER_12, }; use crate::validator::{ - GetCapabilitiesReq, GetCapabilitiesRsp, GetVersionReq, GetVersionRsp, = SpdmErrorRsp, SpdmHeader, + GetCapabilitiesReq, GetCapabilitiesRsp, GetVersionReq, GetVersionRsp, = NegotiateAlgsReq, + NegotiateAlgsRsp, RegAlg, SpdmErrorRsp, SpdmHeader, }; =20 /// The current SPDM session state for a device. Based on the @@ -40,6 +46,28 @@ /// Negotiated during GET_VERSION exchange. /// @rsp_caps: Cached capabilities of responder. /// Received during GET_CAPABILITIES exchange. +/// @base_asym_alg: Asymmetric key algorithm for signature verification of +/// CHALLENGE_AUTH and MEASUREMENTS messages. +/// Selected by responder during NEGOTIATE_ALGORITHMS exchange. +/// @base_hash_alg: Hash algorithm for signature verification of +/// CHALLENGE_AUTH and MEASUREMENTS messages. +/// Selected by responder during NEGOTIATE_ALGORITHMS exchange. +/// @meas_hash_alg: Hash algorithm for measurement blocks. +/// Selected by responder during NEGOTIATE_ALGORITHMS exchange. +/// @base_asym_enc: Human-readable name of @base_asym_alg's signature enco= ding. +/// Passed to crypto subsystem when calling verify_signature(). +/// @sig_len: Signature length of @base_asym_alg (in bytes). +/// S or SigLen in SPDM specification. +/// @base_hash_alg_name: Human-readable name of @base_hash_alg. +/// Passed to crypto subsystem when calling crypto_alloc_shash() and +/// verify_signature(). +/// @base_hash_alg_name: Human-readable name of @base_hash_alg. +/// Passed to crypto subsystem when calling crypto_alloc_shash() and +/// verify_signature(). +/// @shash: Synchronous hash handle for @base_hash_alg computation. +/// @desc: Synchronous hash context for @base_hash_alg computation. +/// @hash_len: Hash length of @base_hash_alg (in bytes). +/// H in SPDM specification. #[expect(dead_code)] pub struct SpdmState { pub(crate) dev: *mut bindings::device, @@ -52,6 +80,19 @@ pub struct SpdmState { // Negotiated state pub(crate) version: u8, pub(crate) rsp_caps: u32, + pub(crate) base_asym_alg: u32, + pub(crate) base_hash_alg: u32, + pub(crate) meas_hash_alg: u32, + + /* Signature algorithm */ + base_asym_enc: &'static CStr, + sig_len: usize, + + /* Hash algorithm */ + base_hash_alg_name: &'static CStr, + pub(crate) shash: *mut bindings::crypto_shash, + pub(crate) desc: Option<&'static mut bindings::shash_desc>, + pub(crate) hash_len: usize, } =20 impl SpdmState { @@ -72,6 +113,15 @@ pub(crate) fn new( validate, version: SPDM_MIN_VER, rsp_caps: 0, + base_asym_alg: 0, + base_hash_alg: 0, + meas_hash_alg: 0, + base_asym_enc: unsafe { CStr::from_bytes_with_nul_unchecked(b"= \0") }, + sig_len: 0, + base_hash_alg_name: unsafe { CStr::from_bytes_with_nul_uncheck= ed(b"\0") }, + shash: core::ptr::null_mut(), + desc: None, + hash_len: 0, } } =20 @@ -333,4 +383,160 @@ pub(crate) fn get_capabilities(&mut self) -> Result<(= ), Error> { =20 Ok(()) } + + fn update_response_algs(&mut self) -> Result<(), Error> { + match self.base_asym_alg { + SPDM_ASYM_RSASSA_2048 =3D> { + self.sig_len =3D 256; + self.base_asym_enc =3D CStr::from_bytes_with_nul(b"pkcs1\0= ")?; + } + SPDM_ASYM_RSASSA_3072 =3D> { + self.sig_len =3D 384; + self.base_asym_enc =3D CStr::from_bytes_with_nul(b"pkcs1\0= ")?; + } + SPDM_ASYM_RSASSA_4096 =3D> { + self.sig_len =3D 512; + self.base_asym_enc =3D CStr::from_bytes_with_nul(b"pkcs1\0= ")?; + } + SPDM_ASYM_ECDSA_ECC_NIST_P256 =3D> { + self.sig_len =3D 64; + self.base_asym_enc =3D CStr::from_bytes_with_nul(b"p1363\0= ")?; + } + SPDM_ASYM_ECDSA_ECC_NIST_P384 =3D> { + self.sig_len =3D 96; + self.base_asym_enc =3D CStr::from_bytes_with_nul(b"p1363\0= ")?; + } + SPDM_ASYM_ECDSA_ECC_NIST_P521 =3D> { + self.sig_len =3D 132; + self.base_asym_enc =3D CStr::from_bytes_with_nul(b"p1363\0= ")?; + } + _ =3D> { + pr_err!("Unknown asym algorithm\n"); + return Err(EINVAL); + } + } + + match self.base_hash_alg { + SPDM_HASH_SHA_256 =3D> { + self.base_hash_alg_name =3D CStr::from_bytes_with_nul(b"sh= a256\0")?; + } + SPDM_HASH_SHA_384 =3D> { + self.base_hash_alg_name =3D CStr::from_bytes_with_nul(b"sh= a384\0")?; + } + SPDM_HASH_SHA_512 =3D> { + self.base_hash_alg_name =3D CStr::from_bytes_with_nul(b"sh= a512\0")?; + } + _ =3D> { + pr_err!("Unknown hash algorithm\n"); + return Err(EINVAL); + } + } + + /* + * shash and desc allocations are reused for subsequent measurement + * retrieval, hence are not freed until spdm_reset(). + */ + self.shash =3D + unsafe { bindings::crypto_alloc_shash(self.base_hash_alg_name.= as_char_ptr(), 0, 0) }; + from_err_ptr(self.shash)?; + + let desc_len =3D core::mem::size_of::() + + unsafe { bindings::crypto_shash_descsize(self.shash) } as us= ize; + + let mut desc_vec: KVec =3D KVec::with_capacity(desc_len, GFP_K= ERNEL)?; + // SAFETY: `desc_vec` is `desc_len` long + let desc_buf =3D unsafe { from_raw_parts_mut(desc_vec.as_mut_ptr()= , desc_len) }; + + let desc =3D unsafe { + core::mem::transmute::<*mut c_void, &mut bindings::shash_desc>( + desc_buf.as_mut_ptr() as *mut c_void + ) + }; + desc.tfm =3D self.shash; + + self.desc =3D Some(desc); + + /* Used frequently to compute offsets, so cache H */ + self.hash_len =3D unsafe { bindings::crypto_shash_digestsize(self.= shash) as usize }; + + if let Some(desc) =3D &mut self.desc { + unsafe { to_result(bindings::crypto_shash_init(*desc)) } + } else { + Err(ENOMEM) + } + } + + pub(crate) fn negotiate_algs(&mut self) -> Result<(), Error> { + let mut request =3D NegotiateAlgsReq::default(); + request.version =3D self.version; + + if self.version >=3D SPDM_VER_12 && (self.rsp_caps & SPDM_KEY_EX_C= AP) =3D=3D SPDM_KEY_EX_CAP { + request.other_params_support =3D SPDM_OPAQUE_DATA_FMT_GENERAL; + } + + // TODO support more algs + let reg_alg_entries =3D 0; + + let req_sz =3D core::mem::size_of::() + + core::mem::size_of::() * reg_alg_entries; + let rsp_sz =3D core::mem::size_of::() + + core::mem::size_of::() * reg_alg_entries; + + request.length =3D req_sz as u16; + request.param1 =3D reg_alg_entries as u8; + + // SAFETY: `request` is repr(C) and packed, so we can convert it t= o a slice + let request_buf =3D unsafe { from_raw_parts_mut(&mut request as *m= ut _ as *mut u8, req_sz) }; + + let mut response_vec: KVec =3D KVec::with_capacity(rsp_sz, GFP= _KERNEL)?; + // SAFETY: `request` is repr(C) and packed, so we can convert it t= o a slice + let response_buf =3D unsafe { from_raw_parts_mut(response_vec.as_m= ut_ptr(), rsp_sz) }; + + let rc =3D self.spdm_exchange(request_buf, response_buf)?; + + if rc < (rsp_sz as i32) { + pr_err!("Truncated capabilities response\n"); + to_result(-(bindings::EIO as i32))?; + } + + // SAFETY: `rc` is the length of data read, which will be smaller + // then the capacity of the vector + unsafe { response_vec.inc_len(rc as usize) }; + + let response: &mut NegotiateAlgsRsp =3D + Untrusted::new_mut(&mut response_vec).validate_mut()?; + + self.base_asym_alg =3D response.base_asym_sel; + self.base_hash_alg =3D response.base_hash_sel; + self.meas_hash_alg =3D response.measurement_hash_algo; + + if self.base_asym_alg & SPDM_ASYM_ALGOS =3D=3D 0 || self.base_hash= _alg & SPDM_HASH_ALGOS =3D=3D 0 { + pr_err!("No common supported algorithms\n"); + to_result(-(bindings::EPROTO as i32))?; + } + + // /* Responder shall select exactly 1 alg (SPDM 1.0.0 table 14) */ + if self.base_asym_alg.count_ones() !=3D 1 + || self.base_hash_alg.count_ones() !=3D 1 + || response.ext_asym_sel_count !=3D 0 + || response.ext_hash_sel_count !=3D 0 + || response.param1 > request.param1 + || response.other_params_sel !=3D request.other_params_support + { + pr_err!("Malformed algorithms response\n"); + to_result(-(bindings::EPROTO as i32))?; + } + + if self.rsp_caps & SPDM_MEAS_CAP_MASK =3D=3D SPDM_MEAS_CAP_MASK + && (self.meas_hash_alg.count_ones() !=3D 1 + || response.measurement_specification_sel !=3D SPDM_MEAS_S= PEC_DMTF) + { + pr_err!("Malformed algorithms response\n"); + to_result(-(bindings::EPROTO as i32))?; + } + + self.update_response_algs()?; + + Ok(()) + } } diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs index cd792c46767a..036a077c71c3 100644 --- a/lib/rspdm/validator.rs +++ b/lib/rspdm/validator.rs @@ -7,7 +7,7 @@ //! Rust implementation of the DMTF Security Protocol and Data Model (SPDM) //! =20 -use crate::bindings::{__IncompleteArrayField, __le16}; +use crate::bindings::{__IncompleteArrayField, __le16, __le32}; use crate::consts::SpdmErrorCode; use core::mem; use kernel::prelude::*; @@ -16,7 +16,10 @@ validate::{Unvalidated, Validate}, }; =20 -use crate::consts::{SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, SPDM_GET_VERSI= ON, SPDM_REQ_CAPS}; +use crate::consts::{ + SPDM_ASYM_ALGOS, SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, SPDM_GET_VERS= ION, SPDM_HASH_ALGOS, + SPDM_MEAS_SPEC_DMTF, SPDM_NEGOTIATE_ALGS, SPDM_REQ_CAPS, +}; =20 #[repr(C, packed)] pub(crate) struct SpdmHeader { @@ -205,3 +208,111 @@ fn validate(unvalidated: &mut Unvalidated>) = -> Result Ok(rsp) } } + +#[repr(C, packed)] +pub(crate) struct RegAlg { + pub(crate) alg_type: u8, + pub(crate) alg_count: u8, + pub(crate) alg_supported: u16, + pub(crate) alg_external: __IncompleteArrayField<__le32>, +} + +#[repr(C, packed)] +pub(crate) struct NegotiateAlgsReq { + pub(crate) version: u8, + pub(crate) code: u8, + pub(crate) param1: u8, + pub(crate) param2: u8, + + pub(crate) length: u16, + pub(crate) measurement_specification: u8, + pub(crate) other_params_support: u8, + + pub(crate) base_asym_algo: u32, + pub(crate) base_hash_algo: u32, + + reserved1: [u8; 12], + + pub(crate) ext_asym_count: u8, + pub(crate) ext_hash_count: u8, + reserved2: u8, + pub(crate) mel_specification: u8, + + pub(crate) ext_asym: __IncompleteArrayField<__le32>, + pub(crate) ext_hash: __IncompleteArrayField<__le32>, + pub(crate) req_alg_struct: __IncompleteArrayField, +} + +impl Default for NegotiateAlgsReq { + fn default() -> Self { + NegotiateAlgsReq { + version: 0, + code: SPDM_NEGOTIATE_ALGS, + param1: 0, + param2: 0, + length: 0, + measurement_specification: SPDM_MEAS_SPEC_DMTF, + other_params_support: 0, + base_asym_algo: SPDM_ASYM_ALGOS.to_le(), + base_hash_algo: SPDM_HASH_ALGOS.to_le(), + reserved1: [0u8; 12], + ext_asym_count: 0, + ext_hash_count: 0, + reserved2: 0, + mel_specification: 0, + ext_asym: __IncompleteArrayField::new(), + ext_hash: __IncompleteArrayField::new(), + req_alg_struct: __IncompleteArrayField::new(), + } + } +} + +#[repr(C, packed)] +pub(crate) struct NegotiateAlgsRsp { + pub(crate) version: u8, + pub(crate) code: u8, + pub(crate) param1: u8, + pub(crate) param2: u8, + + pub(crate) length: u16, + pub(crate) measurement_specification_sel: u8, + pub(crate) other_params_sel: u8, + + pub(crate) measurement_hash_algo: u32, + pub(crate) base_asym_sel: u32, + pub(crate) base_hash_sel: u32, + + reserved1: [u8; 11], + + pub(crate) mel_specification_sel: u8, + pub(crate) ext_asym_sel_count: u8, + pub(crate) ext_hash_sel_count: u8, + reserved2: [u8; 2], + + pub(crate) ext_asym: __IncompleteArrayField<__le32>, + pub(crate) ext_hash: __IncompleteArrayField<__le32>, + pub(crate) req_alg_struct: __IncompleteArrayField, +} + +impl Validate<&mut Unvalidated>> for &mut NegotiateAlgsRsp { + type Err =3D Error; + + fn validate(unvalidated: &mut Unvalidated>) -> Result { + let raw =3D unvalidated.raw_mut(); + if raw.len() < mem::size_of::() { + return Err(EINVAL); + } + + let ptr =3D raw.as_mut_ptr(); + // CAST: `NegotiateAlgsRsp` only contains integers and has `repr(C= )`. + let ptr =3D ptr.cast::(); + // SAFETY: `ptr` came from a reference and the cast above is valid. + let rsp: &mut NegotiateAlgsRsp =3D unsafe { &mut *ptr }; + + rsp.base_asym_sel =3D rsp.base_asym_sel.to_le(); + rsp.base_hash_sel =3D rsp.base_hash_sel.to_le(); + rsp.measurement_hash_algo =3D rsp.measurement_hash_algo.to_le(); + + Ok(rsp) + } +} --=20 2.52.0 From nobody Thu Apr 2 15:42:27 2026 Received: from mail-pj1-f52.google.com (mail-pj1-f52.google.com [209.85.216.52]) (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 B9228346FA2 for ; Wed, 11 Feb 2026 03:31:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.52 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780695; cv=none; b=dqvI6JfhLXnlKBniUt3C911sn1AcmQ68Uxx/R0wksVIKbgX63cnXOM0rBVJIdSo445X0j5N2hCu9VF4JSS+6DGGhMWnQQNg0lRqwhQOILutFbWegOop4oAufvcKm44ZyuS26q7BoHPmQqA0was1CuZCXUhOLWwTKZajrXvl0y1U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780695; c=relaxed/simple; bh=CWbi7ALF7uXVajfdmBOV0S2JNV4FoET3qqXamDPH+P8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Sqr2E3kHWjPyavusyO569TkT9KFzcoHqsuxJa2n3ruEA+BqAaV2AnBNZlpd3AkdNx/m9ZJiIbuDdBfpPpBFrjNXVZHOKNWGrqgHU9hEafFaPVjZMaepVQJ+6iqTGAo+uWyPhahX/H/0AgQvelX1tewXRC7y1XLKzYxPB5gkmKhY= 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=RbrmKSgh; arc=none smtp.client-ip=209.85.216.52 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="RbrmKSgh" Received: by mail-pj1-f52.google.com with SMTP id 98e67ed59e1d1-3566af9900eso759917a91.2 for ; Tue, 10 Feb 2026 19:31:33 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780693; x=1771385493; 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=qj5rwsWyL707FPhO2cj5eD0A9z9ELu50StQ0l12ODmY=; b=RbrmKSghA9aC9myCEnjAI6fgEDO33Hk1lxbYOvLR6YqEywSIVwyIXy0qjNuK/ZoLq8 bfXiSlSkH+XhgJYmkfErOM38woxQfm+ooVHgeCR5xTzZAIm0OKlEEzpsTNc4fxBh79VY PFZvrMLGYQ2dVG6D2zT0Nm0X5uazDLvVfaYCi6wNBpEUvBxK6KJtYz1QelBrmWDznKQ4 C33KuSYLzH/8stqekGxk4MBnDN2Ct4vW52t5GVBSFYKzwXYgulM/49JNCetnt3/Ltlb3 2W1WnWzkSFAS5y9grVwuw507URpCdeg2gvNl//P+ZS7MxT33l/QiEH22y4xZkAYD9n2L 68OQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780693; x=1771385493; 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=qj5rwsWyL707FPhO2cj5eD0A9z9ELu50StQ0l12ODmY=; b=ktXZMKYkMqFaPfN/jy7yJPUpMZwPuYIwD5TlEFBQKIoNx/Uob09yXyqkHu0fRLBPzd 0Lek0PzAtuLfdapKIY93OHgtbPm/8u2xPqN53/uGuMcp6zAgP8neklx6dsYfK7eu5hk8 5T7bBHM8dLgoaF7SWtJ08kd//8L+DhhNvD7ty/owMnPjJ2gsCJHnOz4BlvvxQ9CyNxHN IhclXybN3+tMWkXoTZQ7hMFZd2N6P6BMQgz9JKORfkOCNACr1Y/l1Sd/ZxedCALIYVrW Q1Uh27ajDJElAgLVvJKrfID2pSXNUdfOCOBJv1bO0UohaOfVBMOgrAMT28Y7O+0FqNS/ /10g== X-Forwarded-Encrypted: i=1; AJvYcCVz2xwSpMWdjkJz+H6hMCirKs0WkwxU+FVP23YiaftCV5ewJZt8dmtNA7XgM8XjfEXd50wBGC4iJZt0CAM=@vger.kernel.org X-Gm-Message-State: AOJu0YySq/PPNZmZyEEYeNYbxsG6UA6mNmFw+G5V6cESn6qICpvwLhRl AK2tvRk1RzqBKqRejEg/X0FweLDg/d/b1IqRpIvnukwyUW7BUTmSP5Uj X-Gm-Gg: AZuq6aKO8DkWsopraEPk8B4PFVjRA2a4BOxlVNR0kG5BZbOZXc9lqPAaeE0T0hy7/oN k3yQHadP4A+Gt/XggXvUQM2Sq3iHrlKwbvYs98YJdaHD88xcm9pbBJOl2uzzgjwnlG8ZwqzeAx9 R9TCC7JIT5lHi3hAqS0g9sq+Fqmo7QhlYE5avTeEVkaGMssIvKcii2ADEkDuDTgKaK2fJjv+Ndy fv2/C9o2RBit6t3iMsApxa8Z6EflLUroRIPCNqxEvUEzcZ/ictn2oUnA62ags1Nbo+DGhpW3YKK X//gCLqPBPF2kRDZ+p/Dw1n6a/V5LyVPevk+5QiVWCCKpCJQL416sLLP2U7Cvb8FBtCsN8gsyPO RX42SQ1ZBXPm8T5rtJXlystvF3uoMToWycypEDnre5JdACr8pGGqR2Heyb8ciXC3Bc+Tb/ESPIu fBR0RllgZ24v2bEnCxfsxa9ZVkWmECPvRuj7AysS161A== X-Received: by 2002:a17:90b:240e:b0:356:1edc:b68 with SMTP id 98e67ed59e1d1-3561edc10a7mr8858788a91.12.1770780693030; Tue, 10 Feb 2026 19:31:33 -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.31.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:31:32 -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 15/27] lib: rspdm: Support SPDM get_digests Date: Wed, 11 Feb 2026 13:29:22 +1000 Message-ID: <20260211032935.2705841-16-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 the GET_DIGESTS SPDM command. Signed-off-by: Alistair Francis --- lib/rspdm/consts.rs | 4 ++ lib/rspdm/lib.rs | 4 ++ lib/rspdm/state.rs | 87 ++++++++++++++++++++++++++++++++++++++++-- lib/rspdm/validator.rs | 52 ++++++++++++++++++++++++- 4 files changed, 142 insertions(+), 5 deletions(-) diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs index e8a05fd4299b..4d39ca2cb584 100644 --- a/lib/rspdm/consts.rs +++ b/lib/rspdm/consts.rs @@ -16,6 +16,8 @@ pub(crate) const SPDM_VER_12: u8 =3D 0x12; pub(crate) const SPDM_VER_13: u8 =3D 0x13; =20 +pub(crate) const SPDM_SLOTS: usize =3D 8; + pub(crate) const SPDM_MIN_VER: u8 =3D SPDM_VER_10; pub(crate) const SPDM_MAX_VER: u8 =3D SPDM_VER_13; =20 @@ -170,6 +172,8 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core= ::fmt::Result { pub(crate) const SPDM_HASH_SHA_384: u32 =3D 1 << 1; pub(crate) const SPDM_HASH_SHA_512: u32 =3D 1 << 2; =20 +pub(crate) const SPDM_GET_DIGESTS: u8 =3D 0x81; + #[cfg(CONFIG_CRYPTO_RSA)] pub(crate) const SPDM_ASYM_RSA: u32 =3D SPDM_ASYM_RSASSA_2048 | SPDM_ASYM_RSASSA_3072 | SPDM_ASYM_RSASSA_4096; diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs index 33f706a55e09..a7af86d1dc0b 100644 --- a/lib/rspdm/lib.rs +++ b/lib/rspdm/lib.rs @@ -121,6 +121,10 @@ return e.to_errno() as c_int; } =20 + if let Err(e) =3D state.get_digests() { + return e.to_errno() as c_int; + } + 0 } =20 diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs index d0b10f27cd9c..2606b825c494 100644 --- a/lib/rspdm/state.rs +++ b/lib/rspdm/state.rs @@ -23,11 +23,11 @@ SPDM_ASYM_RSASSA_4096, SPDM_ERROR, SPDM_GET_VERSION_LEN, SPDM_HASH_ALG= OS, SPDM_HASH_SHA_256, SPDM_HASH_SHA_384, SPDM_HASH_SHA_512, SPDM_KEY_EX_CAP, SPDM_MAX_VER, S= PDM_MEAS_CAP_MASK, SPDM_MEAS_SPEC_DMTF, SPDM_MIN_DATA_TRANSFER_SIZE, SPDM_MIN_VER, SPDM_O= PAQUE_DATA_FMT_GENERAL, - SPDM_REQ, SPDM_RSP_MIN_CAPS, SPDM_VER_10, SPDM_VER_11, SPDM_VER_12, + SPDM_REQ, SPDM_RSP_MIN_CAPS, SPDM_SLOTS, SPDM_VER_10, SPDM_VER_11, SPD= M_VER_12, }; use crate::validator::{ - GetCapabilitiesReq, GetCapabilitiesRsp, GetVersionReq, GetVersionRsp, = NegotiateAlgsReq, - NegotiateAlgsRsp, RegAlg, SpdmErrorRsp, SpdmHeader, + GetCapabilitiesReq, GetCapabilitiesRsp, GetDigestsReq, GetDigestsRsp, = GetVersionReq, + GetVersionRsp, NegotiateAlgsReq, NegotiateAlgsRsp, RegAlg, SpdmErrorRs= p, SpdmHeader, }; =20 /// The current SPDM session state for a device. Based on the @@ -54,6 +54,10 @@ /// Selected by responder during NEGOTIATE_ALGORITHMS exchange. /// @meas_hash_alg: Hash algorithm for measurement blocks. /// Selected by responder during NEGOTIATE_ALGORITHMS exchange. +/// @supported_slots: Bitmask of responder's supported certificate slots. +/// Received during GET_DIGESTS exchange (from SPDM 1.3). +/// @provisioned_slots: Bitmask of responder's provisioned certificate slo= ts. +/// Received during GET_DIGESTS exchange. /// @base_asym_enc: Human-readable name of @base_asym_alg's signature enco= ding. /// Passed to crypto subsystem when calling verify_signature(). /// @sig_len: Signature length of @base_asym_alg (in bytes). @@ -68,6 +72,9 @@ /// @desc: Synchronous hash context for @base_hash_alg computation. /// @hash_len: Hash length of @base_hash_alg (in bytes). /// H in SPDM specification. +/// @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)] pub struct SpdmState { pub(crate) dev: *mut bindings::device, @@ -83,6 +90,8 @@ pub struct SpdmState { pub(crate) base_asym_alg: u32, pub(crate) base_hash_alg: u32, pub(crate) meas_hash_alg: u32, + pub(crate) supported_slots: u8, + pub(crate) provisioned_slots: u8, =20 /* Signature algorithm */ base_asym_enc: &'static CStr, @@ -93,6 +102,9 @@ pub struct SpdmState { pub(crate) shash: *mut bindings::crypto_shash, pub(crate) desc: Option<&'static mut bindings::shash_desc>, pub(crate) hash_len: usize, + + // Certificates + pub(crate) certs: [KVec; SPDM_SLOTS], } =20 impl SpdmState { @@ -116,12 +128,15 @@ pub(crate) fn new( base_asym_alg: 0, base_hash_alg: 0, meas_hash_alg: 0, + supported_slots: 0, + provisioned_slots: 0, base_asym_enc: unsafe { CStr::from_bytes_with_nul_unchecked(b"= \0") }, sig_len: 0, base_hash_alg_name: unsafe { CStr::from_bytes_with_nul_uncheck= ed(b"\0") }, shash: core::ptr::null_mut(), desc: None, hash_len: 0, + certs: [const { KVec::new() }; SPDM_SLOTS], } } =20 @@ -539,4 +554,70 @@ pub(crate) fn negotiate_algs(&mut self) -> Result<(), = Error> { =20 Ok(()) } + + pub(crate) fn get_digests(&mut self) -> Result<(), Error> { + let mut request =3D GetDigestsReq::default(); + request.version =3D self.version; + + let req_sz =3D core::mem::size_of::(); + let rsp_sz =3D core::mem::size_of::() + SPDM_SLOTS = * self.hash_len; + + // SAFETY: `request` is repr(C) and packed, so we can convert it t= o a slice + let request_buf =3D unsafe { from_raw_parts_mut(&mut request as *m= ut _ as *mut u8, req_sz) }; + + let mut response_vec: KVec =3D KVec::with_capacity(rsp_sz, GFP= _KERNEL)?; + // SAFETY: `request` is repr(C) and packed, so we can convert it t= o a slice + let response_buf =3D unsafe { from_raw_parts_mut(response_vec.as_m= ut_ptr(), rsp_sz) }; + + let rc =3D self.spdm_exchange(request_buf, response_buf)?; + + if rc < (core::mem::size_of::() as i32) { + pr_err!("Truncated digests response\n"); + to_result(-(bindings::EIO as i32))?; + } + + // SAFETY: `rc` is the length of data read, which will be smaller + // then the capacity of the vector + unsafe { response_vec.inc_len(rc as usize) }; + + let response: &mut GetDigestsRsp =3D Untrusted::new_mut(&mut respo= nse_vec).validate_mut()?; + + if rc + < (core::mem::size_of::() + + response.param2.count_ones() as usize * self.hash_len) a= s i32 + { + pr_err!("Truncated digests response\n"); + to_result(-(bindings::EIO as i32))?; + } + + let mut deprovisioned_slots =3D self.provisioned_slots & !response= .param2; + while (deprovisioned_slots.trailing_zeros() as usize) < SPDM_SLOTS= { + let slot =3D deprovisioned_slots.trailing_zeros() as usize; + self.certs[slot].clear(); + deprovisioned_slots &=3D !(1 << slot); + } + + self.provisioned_slots =3D response.param2; + if self.provisioned_slots =3D=3D 0 { + pr_err!("No certificates provisioned\n"); + to_result(-(bindings::EPROTO as i32))?; + } + + if self.version >=3D 0x13 && (response.param2 & !response.param1 != =3D 0) { + pr_err!("Malformed digests response\n"); + to_result(-(bindings::EPROTO as i32))?; + } + + let supported_slots =3D if self.version >=3D 0x13 { + response.param1 + } else { + 0xFF + }; + + if self.supported_slots !=3D supported_slots { + self.supported_slots =3D supported_slots; + } + + Ok(()) + } } diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs index 036a077c71c3..2150a23997db 100644 --- a/lib/rspdm/validator.rs +++ b/lib/rspdm/validator.rs @@ -17,8 +17,8 @@ }; =20 use crate::consts::{ - SPDM_ASYM_ALGOS, SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, SPDM_GET_VERS= ION, SPDM_HASH_ALGOS, - SPDM_MEAS_SPEC_DMTF, SPDM_NEGOTIATE_ALGS, SPDM_REQ_CAPS, + SPDM_ASYM_ALGOS, SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, SPDM_GET_DIGE= STS, SPDM_GET_VERSION, + SPDM_HASH_ALGOS, SPDM_MEAS_SPEC_DMTF, SPDM_NEGOTIATE_ALGS, SPDM_REQ_CA= PS, }; =20 #[repr(C, packed)] @@ -316,3 +316,51 @@ fn validate(unvalidated: &mut Unvalidated>) -= > Result Ok(rsp) } } + +#[repr(C, packed)] +pub(crate) struct GetDigestsReq { + pub(crate) version: u8, + pub(crate) code: u8, + pub(crate) param1: u8, + pub(crate) param2: u8, +} + +impl Default for GetDigestsReq { + fn default() -> Self { + GetDigestsReq { + version: 0, + code: SPDM_GET_DIGESTS, + param1: 0, + param2: 0, + } + } +} + +#[repr(C, packed)] +pub(crate) struct GetDigestsRsp { + pub(crate) version: u8, + pub(crate) code: u8, + pub(crate) param1: u8, + pub(crate) param2: u8, + + pub(crate) digests: __IncompleteArrayField, +} + +impl Validate<&mut Unvalidated>> for &mut GetDigestsRsp { + type Err =3D Error; + + fn validate(unvalidated: &mut Unvalidated>) -> Result { + let raw =3D unvalidated.raw_mut(); + if raw.len() < mem::size_of::() { + return Err(EINVAL); + } + + let ptr =3D raw.as_mut_ptr(); + // CAST: `GetDigestsRsp` only contains integers and has `repr(C)`. + let ptr =3D ptr.cast::(); + // SAFETY: `ptr` came from a reference and the cast above is valid. + let rsp: &mut GetDigestsRsp =3D unsafe { &mut *ptr }; + + Ok(rsp) + } +} --=20 2.52.0 From nobody Thu Apr 2 15:42:27 2026 Received: from mail-pg1-f169.google.com (mail-pg1-f169.google.com [209.85.215.169]) (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 C2275344050 for ; Wed, 11 Feb 2026 03:31:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780702; cv=none; b=t/+ySFsMCbs7Ptt+0BBZl5CI6FEnpPk7tzRkmpASwYQNTIRZBllbt9FkPNi0f7y4U4K+CUx6/9LKfqc2MELt2Us2jip5gJ/QO3xGsOalNPA08Fb+xxXMVGKZrDq86MCRiRefXM3464LBkhY4SIhsAW+/RI2kS9+ulxk7vyABcqU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780702; c=relaxed/simple; bh=wwQdaFdguyOpRhGX726OhemxFT7tG742bz91Nmlzmjs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=M7B5FBswMFQMlYlACxoMsTIlGBZ0GDi65sVkdmqe+X016Eau1MqFGs6SNjcJshLWOr64/ehpRcbGJ6jkH6svzf2AC/O/9+lDZTtXYD27F7D6A0/Rt81wLlzCpn9TMNe7QdsxE+EGhv3JAPqKrcSwJlqGD5ul+yIZEO/jj+gzrKs= 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=AaGl4XBK; arc=none smtp.client-ip=209.85.215.169 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="AaGl4XBK" Received: by mail-pg1-f169.google.com with SMTP id 41be03b00d2f7-c0ec27cad8cso569746a12.1 for ; Tue, 10 Feb 2026 19:31:40 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780700; x=1771385500; 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=HJ/8xHDMdhMHTTYwlL4evaNX3TwyhC8RfbkwEhmL2HQ=; b=AaGl4XBKbX7Qk4WYmWHHwL4g+UxHNI1P31054D2YiaGE7t5N7LFR5QyCC+Us04U9zX QsT7NkO80dIuQEPN3dBNZfsymoxf6Vic1PSqJH6fQcylhqC45JXXmlMFW+FVuheupK5b ksZlYL3HTWPKt4FOIn7pa+BFTfCxFVvBdFRC5MyGlykmZ/e5ObpcOuXOI3TSy+aqacn9 CCP9DxtWN1gUoHqKwKqVrr8NJIqniGzfzh6pSOabASLG6W5D90q33j8Iv/fmigp+DpL4 7gx/AagMK4RK3M4Kvv5NFxjUrcwJIemLy1JskhOetqvqwvp5HutHJoTMILYqfjT3WObW WtRw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780700; x=1771385500; 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=HJ/8xHDMdhMHTTYwlL4evaNX3TwyhC8RfbkwEhmL2HQ=; b=NKf9i/aFPVjuzibiAylvL8pFfZnCxy9xEoPbSRBfxD0gG1PqYErlf/f9H/OycUwUCe Ro5x8luakpQEv9rwbBcWSOzpB96uEXl9YAEluAjCBpnmobVspPSEUAN++AO557FSqgTV MVRXwYEUCI89YXmiqdtaqDwXdItXbGq9Hc/qQF8rNNoQ8U8I1qEpHxQvZiEY5jwLUXxj 8Itz4YPTAhEkL5ZRik5pklnnYvD7tq1G8KI3Kk4UtkzVc/B1TzQULSx3FN8FmdIGsAS8 0mYx16aoTm4AHcMs904C0cm3QljMSSO5nezlvQJUBrpbyf1uGmYg7UzDYMz1Kni/8/yc vWMQ== X-Forwarded-Encrypted: i=1; AJvYcCVH02caAWp+VviVj4YJ/dg0/3MNy6MIB/xyTnv9ytGiQJgEupNDmCxrOYRMzgwxdofaLaIhrNiraUu6twQ=@vger.kernel.org X-Gm-Message-State: AOJu0YxEjqvAvV1Lxrrd9Yh/RJPktCIOGoDN+5G6KIXUBkTZ5pVJHbKk iwtXtC3/EHSyT++r6f3N7q0xejGgbJsPhm++EbdRxBmhRGlwJKEAzd7+ X-Gm-Gg: AZuq6aKUQw/rjrihVIP/VMcXRaN8Wq6j5+0g5VecF1EWPLrjnaAqfpfzsmZ2qWsE0XK e67J/NG/aL7PYq+LIIrWqth4R+6lrt2Qq0+TMEtXvQxN5hPsIkv7CMPhrlmYr9ITVwOFiuWLx85 zF4KysmTFANo09fByQMxGFE2cD2hWhhrRRdPKOz1GGrRw/AX2+rTS65/mmf/aWQt9KH0zHVF4hT hisG0IPShWTWvjUZxMQhLWsnod01l2PCuiPDped7BZSyY0KpY+ZekX3Q0CbB3W00IAKz3jUzIy4 vmHVbFHTicZ2dNyktlwN3dWqB3iuggRcnfAgCDtkZH4VzGFd6uiqJF98bBBK7RGkDF1TRkY6FGZ izBoDrPrArVT+/wd5lQdHhHvQp/sclP1kX71F3oksLr5/u6/S6vfNi1PtlNhuvebDzS4k4LX6e3 /O8DEV6S7/Dcc/mgIoVUtXdbxLRVo156+XIPFtUkFA1jvxHdGRVOix X-Received: by 2002:a05:6a21:38d:b0:38e:9220:ebbe with SMTP id adf61e73a8af0-393ad01ed9emr14265861637.23.1770780700142; Tue, 10 Feb 2026 19:31:40 -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.31.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:31:39 -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 16/27] lib: rspdm: Support SPDM get_certificate Date: Wed, 11 Feb 2026 13:29:23 +1000 Message-ID: <20260211032935.2705841-17-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 the GET_CERTIFICATE SPDM command. Signed-off-by: Alistair Francis --- lib/rspdm/consts.rs | 2 + lib/rspdm/lib.rs | 11 ++++ lib/rspdm/state.rs | 127 ++++++++++++++++++++++++++++++++++++++++- lib/rspdm/validator.rs | 64 ++++++++++++++++++++- 4 files changed, 200 insertions(+), 4 deletions(-) diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs index 4d39ca2cb584..eaf2132da290 100644 --- a/lib/rspdm/consts.rs +++ b/lib/rspdm/consts.rs @@ -174,6 +174,8 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core= ::fmt::Result { =20 pub(crate) const SPDM_GET_DIGESTS: u8 =3D 0x81; =20 +pub(crate) const SPDM_GET_CERTIFICATE: u8 =3D 0x82; + #[cfg(CONFIG_CRYPTO_RSA)] pub(crate) const SPDM_ASYM_RSA: u32 =3D SPDM_ASYM_RSASSA_2048 | SPDM_ASYM_RSASSA_3072 | SPDM_ASYM_RSASSA_4096; diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs index a7af86d1dc0b..b065b3f70356 100644 --- a/lib/rspdm/lib.rs +++ b/lib/rspdm/lib.rs @@ -125,6 +125,17 @@ return e.to_errno() as c_int; } =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.get_certificate(slot) { + return e.to_errno() as c_int; + } + + provisioned_slots &=3D !(1 << slot); + } + 0 } =20 diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs index 2606b825c494..1e5656144611 100644 --- a/lib/rspdm/state.rs +++ b/lib/rspdm/state.rs @@ -26,8 +26,9 @@ SPDM_REQ, SPDM_RSP_MIN_CAPS, SPDM_SLOTS, SPDM_VER_10, SPDM_VER_11, SPD= M_VER_12, }; use crate::validator::{ - GetCapabilitiesReq, GetCapabilitiesRsp, GetDigestsReq, GetDigestsRsp, = GetVersionReq, - GetVersionRsp, NegotiateAlgsReq, NegotiateAlgsRsp, RegAlg, SpdmErrorRs= p, SpdmHeader, + GetCapabilitiesReq, GetCapabilitiesRsp, GetCertificateReq, GetCertific= ateRsp, GetDigestsReq, + GetDigestsRsp, GetVersionReq, GetVersionRsp, NegotiateAlgsReq, Negotia= teAlgsRsp, RegAlg, + SpdmErrorRsp, SpdmHeader, }; =20 /// The current SPDM session state for a device. Based on the @@ -107,6 +108,14 @@ pub struct SpdmState { pub(crate) certs: [KVec; SPDM_SLOTS], } =20 +#[repr(C, packed)] +pub(crate) struct SpdmCertChain { + length: u16, + _reserved: [u8; 2], + root_hash: bindings::__IncompleteArrayField, + certificates: bindings::__IncompleteArrayField, +} + impl SpdmState { pub(crate) fn new( dev: *mut bindings::device, @@ -620,4 +629,118 @@ pub(crate) fn get_digests(&mut self) -> Result<(), Er= ror> { =20 Ok(()) } + + fn get_cert_exchange( + &mut self, + request_buf: &mut [u8], + response_vec: &mut KVec, + rsp_sz: usize, + ) -> Result<&mut GetCertificateRsp, Error> { + // SAFETY: `request` is repr(C) and packed, so we can convert it t= o a slice + let response_buf =3D unsafe { from_raw_parts_mut(response_vec.as_m= ut_ptr(), rsp_sz) }; + + let rc =3D self.spdm_exchange(request_buf, response_buf)?; + + if rc < (core::mem::size_of::() as i32) { + pr_err!("Truncated certificate response\n"); + to_result(-(bindings::EIO as i32))?; + } + + // SAFETY: `rc` is the length of data read, which will be smaller + // then the capacity of the vector + unsafe { response_vec.inc_len(rc as usize) }; + + let response: &mut GetCertificateRsp =3D Untrusted::new_mut(respon= se_vec).validate_mut()?; + + if rc + < (core::mem::size_of::() + response.portio= n_length as usize) as i32 + { + pr_err!("Truncated certificate response\n"); + to_result(-(bindings::EIO as i32))?; + } + + Ok(response) + } + + pub(crate) fn get_certificate(&mut self, slot: u8) -> Result<(), Error= > { + let mut request =3D GetCertificateReq::default(); + request.version =3D self.version; + request.param1 =3D slot; + + let req_sz =3D core::mem::size_of::(); + let rsp_sz =3D ((core::mem::size_of::() + 0xfff= f) as u32) + .min(self.transport_sz) as usize; + + request.offset =3D 0; + request.length =3D (rsp_sz - core::mem::size_of::()).to_le() as u16; + + // SAFETY: `request` is repr(C) and packed, so we can convert it t= o a slice + let request_buf =3D unsafe { from_raw_parts_mut(&mut request as *m= ut _ as *mut u8, req_sz) }; + + let mut response_vec: KVec =3D KVec::with_capacity(rsp_sz, GFP= _KERNEL)?; + + let response =3D self.get_cert_exchange(request_buf, &mut response= _vec, rsp_sz)?; + + let total_cert_len =3D + ((response.portion_length + response.remainder_length) & 0xFFF= F) as usize; + + let mut certs_buf: KVec =3D KVec::new(); + + certs_buf.extend_from_slice( + &response_vec[8..(8 + response.portion_length as usize)], + GFP_KERNEL, + )?; + + let mut offset: usize =3D response.portion_length as usize; + let mut remainder_length =3D response.remainder_length as usize; + + while remainder_length > 0 { + request.offset =3D offset.to_le() as u16; + request.length =3D (remainder_length + .min(rsp_sz - core::mem::size_of::())) + .to_le() as u16; + + let request_buf =3D + unsafe { from_raw_parts_mut(&mut request as *mut _ as *mut= u8, req_sz) }; + + let response =3D self.get_cert_exchange(request_buf, &mut resp= onse_vec, rsp_sz)?; + + if response.portion_length =3D=3D 0 + || (response.param1 & 0xF) !=3D slot + || offset as u16 + response.portion_length + response.rema= inder_length + !=3D total_cert_len as u16 + { + pr_err!("Malformed certificate response\n"); + to_result(-(bindings::EPROTO as i32))?; + } + + certs_buf.extend_from_slice( + &response_vec[8..(8 + response.portion_length as usize)], + GFP_KERNEL, + )?; + offset +=3D response.portion_length as usize; + remainder_length =3D response.remainder_length as usize; + } + + let header_length =3D core::mem::size_of::() + self= .hash_len; + + let ptr =3D certs_buf.as_mut_ptr(); + // SAFETY: `SpdmCertChain` is repr(C) and packed, so we can conver= t it from a slice + let ptr =3D ptr.cast::(); + // SAFETY: `ptr` came from a reference and the cast above is valid. + let certs: &mut SpdmCertChain =3D unsafe { &mut *ptr }; + + if total_cert_len < header_length + || total_cert_len !=3D usize::from_le(certs.length as usize) + || total_cert_len !=3D certs_buf.len() + { + pr_err!("Malformed certificate chain in slot {slot}\n"); + to_result(-(bindings::EPROTO as i32))?; + } + + self.certs[slot as usize].clear(); + self.certs[slot as usize].extend_from_slice(&certs_buf, GFP_KERNEL= )?; + + Ok(()) + } } diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs index 2150a23997db..a8bc3378676f 100644 --- a/lib/rspdm/validator.rs +++ b/lib/rspdm/validator.rs @@ -17,8 +17,9 @@ }; =20 use crate::consts::{ - SPDM_ASYM_ALGOS, SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, SPDM_GET_DIGE= STS, SPDM_GET_VERSION, - SPDM_HASH_ALGOS, SPDM_MEAS_SPEC_DMTF, SPDM_NEGOTIATE_ALGS, SPDM_REQ_CA= PS, + SPDM_ASYM_ALGOS, SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, SPDM_GET_CERT= IFICATE, + SPDM_GET_DIGESTS, SPDM_GET_VERSION, SPDM_HASH_ALGOS, SPDM_MEAS_SPEC_DM= TF, SPDM_NEGOTIATE_ALGS, + SPDM_REQ_CAPS, }; =20 #[repr(C, packed)] @@ -364,3 +365,62 @@ fn validate(unvalidated: &mut Unvalidated>) -= > Result Ok(rsp) } } + +#[repr(C, packed)] +pub(crate) struct GetCertificateReq { + pub(crate) version: u8, + pub(crate) code: u8, + pub(crate) param1: u8, + pub(crate) param2: u8, + + pub(crate) offset: u16, + pub(crate) length: u16, +} + +impl Default for GetCertificateReq { + fn default() -> Self { + GetCertificateReq { + version: 0, + code: SPDM_GET_CERTIFICATE, + param1: 0, + param2: 0, + offset: 0, + length: 0, + } + } +} + +#[repr(C, packed)] +pub(crate) struct GetCertificateRsp { + pub(crate) version: u8, + pub(crate) code: u8, + pub(crate) param1: u8, + pub(crate) param2: u8, + + pub(crate) portion_length: u16, + pub(crate) remainder_length: u16, + + pub(crate) cert_chain: __IncompleteArrayField, +} + +impl Validate<&mut Unvalidated>> for &mut GetCertificateRsp { + type Err =3D Error; + + fn validate(unvalidated: &mut Unvalidated>) -> Result { + let raw =3D unvalidated.raw_mut(); + if raw.len() < mem::size_of::() { + return Err(EINVAL); + } + + let ptr =3D raw.as_mut_ptr(); + // CAST: `GetCertificateRsp` only contains integers and has `repr(= C)`. + let ptr =3D ptr.cast::(); + // SAFETY: `ptr` came from a reference and the cast above is valid. + let rsp: &mut GetCertificateRsp =3D unsafe { &mut *ptr }; + + rsp.portion_length =3D rsp.portion_length.to_le(); + rsp.remainder_length =3D rsp.remainder_length.to_le(); + + Ok(rsp) + } +} --=20 2.52.0 From nobody Thu Apr 2 15:42:27 2026 Received: from mail-pg1-f182.google.com (mail-pg1-f182.google.com [209.85.215.182]) (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 B9E793446C9 for ; Wed, 11 Feb 2026 03:31:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780708; cv=none; b=pZYtBYBVK9qrzOsaKYIusdEu3hcwnzAc2esiBqX6M5iTHc2vjyI7odSxyz7jLTw9YKOgPQD9MwQo7sLQ32JLkO6LPiqZ0DzItC+/0NzvutMOf24/OKW2m5oli5RP4yvA2e5AgBZmaDgm15SODgN2C/n6PDOSVOBc7jW2dR/uGYU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780708; c=relaxed/simple; bh=+Dv6Jp5OhwC7087KAYCtSukovH0C5t6UM5K7Pi3a9Lk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MYLNuFEyMmWVwcm04lGPnjsLif9IygJlfgQykl920DcM50XlAmE2Ctbr+Rd/tdr6eXVgTRzY1VKLsD1VLVVPkdCDmdRiZuwWql/XrCgETQZzdFNzTAqtmyhb01EOwVNPl1hT6b8HXvXBYLqXelws4LUCTsNx3LNZBFNL5ogBwGY= 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=iDBYRPJQ; arc=none smtp.client-ip=209.85.215.182 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="iDBYRPJQ" Received: by mail-pg1-f182.google.com with SMTP id 41be03b00d2f7-c648bc907ebso4096622a12.3 for ; Tue, 10 Feb 2026 19:31:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780707; x=1771385507; 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=3mB5YXChxH7A1l1HnU2HjGhx6UZiL0/lHVVqKO5gi7I=; b=iDBYRPJQBzfiFaGZ9rh2PBQ3hmQLykXxB94fynOLlV4iaOfnt1TG/OOUgWpr8liY6E ZRB+M2z2PHMCzeccm5nvMb5ciFPgx0mNA+oXyviAbeA7OlB3EQgD/qCjj1i1Nr2Cwwyh DSbDiIteS0UglJBv1FsIVgTjHWBCqzoDbdQnN/IP2kCe9CR6kuBrMcfYQmIMoYeR+PR4 fGkQrjTB3dgpy5US6JOCRbLsg6LhMObK6lv/jttxq5JU0ZJek9RAroXoMchEPcD81CG5 5QAa//34HJYaus7BWWPlBRW2WVcmppp9nrEzCe6rd6WpKPnGFphsRcFTOND5woPwP9lM 5AqQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780707; x=1771385507; 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=3mB5YXChxH7A1l1HnU2HjGhx6UZiL0/lHVVqKO5gi7I=; b=qlghTKNTDfE5ZVlaM+Gva2Tcc6ciPCDPVWMDXyC4FRnUX1vwy52QtogWix41eJm9Sk lYGbEP/Ordlome9NUuWgJpvPmzsa2a57DfdkdErNgoKqCrUwYMJ0UwfiT9/tOfjNRNeP bL7+f80tmKQ916RtVsoqMqwmp7Mwfc9wlIg4UpUv/jlYg4h6rNCGmRkLqoyzdmE7UMrv PmqZZarS845N2v23aLmIsKV9y2zvOZtsjXcFZkk5yNPX7Uvi99h95phkBZmYoNEgRQz4 4BzQc6BrB7PVS1YRJgKcivMn85Cfy+3fNt7NuOe7U0g4GaT1XN5FfYQSTmXEF8YK9syN tElg== X-Forwarded-Encrypted: i=1; AJvYcCXOIbFAOIY0gzl/yAPZgCTifNtd/DsMq8YtjvqmWEbrplshRTeBWqEXIOpJlBQd9FiJtTIO1eKRqYTNxO0=@vger.kernel.org X-Gm-Message-State: AOJu0YyAReZJoxBo/oCuQZShVnuvIr+SiNji4Lt5zqfoaff20+qgnd3h /e6xXzUyw5xIIj9zqebYzKjKKoBkzHYEAwdZESojAaFTMX54fv52sdrL X-Gm-Gg: AZuq6aKezCn8tKsL4fglH2+VMHZYKZVoNg75YZ1AM3Q+eCYOQ+FGE855kjFPJP3UZYL gKr4h/qf5Cfxfyh9BzxVV8/DVIc9PbIPQr0kZuu4ujjw2BM/vqgPjNiLyPSTAtZqT6RdtYjpTzg L34owFcezCCD88M57LQUNJVt3nCGFFk+7G+034meLZXOBX5DSV12myxCwpyGEYAVBvbrmmYmt08 zHquVTXhp4DMjQBQgkZk19zLNweJ3hmEk2XNYkp39HpbItksIzvb8SZjnxM9XhnsjGmCo5JgLfD hwEVdIJQ9jedpzVFIKx3ir/pREwO/pg/tBbaRLbHBDY0XmWuN8DQNJcK5YzkZ6U4b4XLGxGY8b9 F+RjpqjE6RYztljJVXJ0gG7wu4S58STVM2iYsubLpquGWS10oTotMzLkBeVDrRRqWdUU3wFfOf7 jIPPXBJwz3ootHh49cCSffONpORgVF202Axum/3k2ohg== X-Received: by 2002:a05:6a20:3ca5:b0:38b:ebdd:919f with SMTP id adf61e73a8af0-3943229ad32mr804189637.1.1770780707132; Tue, 10 Feb 2026 19:31:47 -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.31.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:31:46 -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 17/27] crypto: asymmetric_keys - Load certificate parsing early in boot Date: Wed, 11 Feb 2026 13:29:24 +1000 Message-ID: <20260211032935.2705841-18-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 Work is ongoing to support PCIe device attestation and authentication. As part of this a PCIe device will provide a X.509 certificate chain via the SPDM protocol to the kernel. Linux should verify the chain before enabling the device, which means we need the certificate store ready before arch initilisation (where PCIe init happens). Move the certificate and keyring init to postcore to ensure it's loaded before PCIe devices. This patch enables X.509 certificate parsing and asymmetric key support early in the boot process so that it can be used by the key store and SPDM to verify the certificate chain provided by a PCIe device via SPDM before we enable it. Signed-off-by: Alistair Francis --- crypto/asymmetric_keys/asymmetric_type.c | 2 +- crypto/asymmetric_keys/x509_public_key.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_k= eys/asymmetric_type.c index 2326743310b1..3f209299ee3a 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -677,5 +677,5 @@ static void __exit asymmetric_key_cleanup(void) unregister_key_type(&key_type_asymmetric); } =20 -module_init(asymmetric_key_init); +postcore_initcall(asymmetric_key_init); module_exit(asymmetric_key_cleanup); diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_k= eys/x509_public_key.c index 27b4fea37845..2b308345bc6f 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -258,7 +258,7 @@ static void __exit x509_key_exit(void) unregister_asymmetric_key_parser(&x509_key_parser); } =20 -module_init(x509_key_init); +postcore_initcall(x509_key_init); module_exit(x509_key_exit); =20 MODULE_DESCRIPTION("X.509 certificate parser"); --=20 2.52.0 From nobody Thu Apr 2 15:42:27 2026 Received: from mail-pg1-f171.google.com (mail-pg1-f171.google.com [209.85.215.171]) (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 98FEB1A238F for ; Wed, 11 Feb 2026 03:31:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780715; cv=none; b=awvZ5EBVb1a+/fUlD04EWwcRIXJfay56n6siZ0yM870k0Fc6rm6cBJhRpl8gTgpQNT2PR4tk82iNfb3Upvk2Dxz1I/FRMFZagFuACHu8lA4TCAADV5/CbJaJd4l7uUcMepi2ep1/hdRcG0BZqYC2sQLiFvMrJq5yThiynb5lNpk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780715; c=relaxed/simple; bh=KNLtsCq5Xccubow9+9/OB82GtlxnV14UrMdv6HEW9qY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LR/pOfI0m5UplL2p+jRBfM4wW965aYBFQ+MiVu+HhyWzSOfgmxADHjvt7greyWotT+WQRR5jHeHDYJ5AUvwRe+0f/wfaT3dTl4ZwGT+IRetzoiJU00n3IpB2KFk493yn2+7K+q+wNpqfsicPhYWhjg48JC55fO11Org14Sl/9dw= 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=IPQj/TJg; arc=none smtp.client-ip=209.85.215.171 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="IPQj/TJg" Received: by mail-pg1-f171.google.com with SMTP id 41be03b00d2f7-c2dd0c24e5cso2342319a12.3 for ; Tue, 10 Feb 2026 19:31:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780714; x=1771385514; 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=M5JPAdrKU7Ml6bcvlYeqQgOx8uh8kBG9Pa/UFqehRTk=; b=IPQj/TJgDXvdp9r6uulaZ9dh6nW9RJgW0rXU02yDqejExTrF6hA24AgQVkMucTf0ro dDk2Wyf3auC0wr2EmgpbbVGk/uPvJLUyPRYF/MrnAFUwClvDjYCAtt0LAkonm2L75RD7 8mZJcm/a36We7KySCUIezcJwpoBNU1jiLt6+Ly7V9+eWdlX6Cj+duGMKnltIwC6mFtdN QOFZcX7+6azOuTgqlpBnP8mtb4T3XlN+PoYiEQ+Ok8XmrBmzXC8qncF27AVuZquM8a/e H+gByshmd1Dh+pYC1YDA2+RnJ0n8Eyg/nqTfDhAxCKiQF/rwMSvjtenNaIXp5iIKKCLD k5kg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780714; x=1771385514; 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=M5JPAdrKU7Ml6bcvlYeqQgOx8uh8kBG9Pa/UFqehRTk=; b=uqPIrDbFw8SnJaVvk2J6mgVuIbGh2xziwzXpyznu1lMxwTHhfi2XFMXuww/arEf0Xt o2n0CmCdC1+pHtM/xp1TByVAcRe42GKcjzMuEWGZnp8acD9n0HFHoBMWWkjtLl4cCvLy hz8PyFcsy7FRhZz31n26E5lPGtfRySuXZr4JpDaKjketS+w89GPZ7ui6To5iBjMMAkMP UxgiNfBHVEvtBkmgi1gxbk8umbKjcr7sq1tnxITDDW++j4zfrHj9P+qR08V+6BrHeXkH CUsWlvTRS3KHvc1Y0ujQshwbfAMxpuXf75stJcH6lxD+7xlJf7BZeFLkZQymYnpqFde6 0z7g== X-Forwarded-Encrypted: i=1; AJvYcCWiFn+LKVHBga9H8vsCgyE/ml5kDrwK//ayIKhx0e15levgXo1TdSEqJ0xFm4Cq2HI5KRthS7XF8GKXJBE=@vger.kernel.org X-Gm-Message-State: AOJu0YysxsoxTXj6HamXzZPsyLsxf5P0xYkGNn+Wdzv+4bGbdoqmyObr LsKts2ha3wOAR+RgxDvDHquN5WrO5WgPQ4G7gGJbGMh3/Oab32kIuWFX X-Gm-Gg: AZuq6aLQGMui+iR/MIEXUUTcZ0hiNiHhdxl/arLHfxekSYxtK45L+wy8ONN9V+5arVP qKHkR3S7tgdkipWvW/lhMX/yZ8Akv5tkUsArBCl0PUZGGHihPbjCJ2cgm1WJfUFclJL15sIBQcy qWhF4xG1/bgEEYbodpDzRd/m+hcliEsZKRaztcHWn86e8sBSyWxjsSO2ahQtGGQQxNLeTmnFo7a S6kAF2F+er0vttQG0XkCMOxe6dJNjvplCcFSUj2L3zQDoZ8/zqkZ8KKtxxLHIgHEm+l1orrj8b+ vIj2RmYaVrfL7t+wEK4SwUtE58wn11rtoOeS1xvzm4fQVsKvodFQneHh5VM25Rv5zLbTceTr9H0 f2bEi0QjKruEQEbfZ6mzgUyipyzTBgvxUs8c8+W7wvbAwZkULBWMOSZjtQbIFn3OF76ZSd4ygs+ ado0Hp0CDdHrN9T4A/0NMbh3lvTwdIny70W63SOs7tNw== X-Received: by 2002:a05:6a21:2d42:b0:38d:ef23:12cf with SMTP id adf61e73a8af0-3943243dba2mr720441637.61.1770780714136; Tue, 10 Feb 2026 19:31:54 -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.31.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:31:53 -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 18/27] KEYS: Load keyring and certificates early in boot Date: Wed, 11 Feb 2026 13:29:25 +1000 Message-ID: <20260211032935.2705841-19-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 Work is ongoing to support PCIe device attestation and authentication. As part of this a PCIe device will provide a certificate chain via the SPDM protocol to the kernel. Linux should verify the chain before enabling the device, which means we need the certificate store ready before arch initilisation (where PCIe init happens). Move the certificate and keyring init to postcore to ensure it's loaded before PCIe devices. This allows us to verify the certificate chain provided by a PCIe device via SPDM before we enable it. Signed-off-by: Alistair Francis --- certs/system_keyring.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/certs/system_keyring.c b/certs/system_keyring.c index 9de610bf1f4b..f3d8ea4f70b4 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -260,7 +260,7 @@ static __init int system_trusted_keyring_init(void) /* * Must be initialised before we try and load the keys into the keyring. */ -device_initcall(system_trusted_keyring_init); +postcore_initcall(system_trusted_keyring_init); =20 __init int load_module_cert(struct key *keyring) { @@ -293,7 +293,7 @@ static __init int load_system_certificate_list(void) =20 return x509_load_certificate_list(p, size, builtin_trusted_keys); } -late_initcall(load_system_certificate_list); +postcore_initcall(load_system_certificate_list); =20 #ifdef CONFIG_SYSTEM_DATA_VERIFICATION =20 --=20 2.52.0 From nobody Thu Apr 2 15:42:27 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 977FC346FAB for ; Wed, 11 Feb 2026 03:32:01 +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=1770780722; cv=none; b=RZCnepp/XMQ7+SuxIZfgBq+dSzEN4FjZiXn4IvyBD87lYsJ72Clv+ShKa4yN5r8o90pzL5xUa8+F6E/mKNwd2lnRBUlMlfWhpwYUiZIcAX5kReIIqGk4B2lo6awFKUiiYvpdw3gWSZZNaot03QCRwk2nSyZV7KY0CNV7GPzzT7c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780722; c=relaxed/simple; bh=Z69UWGSO1FnIFTybZW2ptY5C1ZsV33yAkveZ4NGbphA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Ur0rxkj+vaSfTCzGfubwSo1tmcH25JyCS97KKQJYH5Pix6kEAxyvdsWyqtBpNtNgNysa8heh2sSsfgsZQLIvGA0kCTTQJd5WC0fTHH4jye9LgZPsPssQfEU3MLkhSqUsRVftipNEREtWG/UDA/fKeDAYyciPcTWDgmVjiruPKeg= 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=Mr6rQlEl; 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="Mr6rQlEl" Received: by mail-pf1-f174.google.com with SMTP id d2e1a72fcca58-82458495219so1956607b3a.3 for ; Tue, 10 Feb 2026 19:32:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780721; x=1771385521; 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=5sfIotc7x/caqwXPwf+nk7Gcfnkj95FCCwlgWI7Ef+4=; b=Mr6rQlElHo1gqS78wcZTyeup5THY7lSLHcPWChE01TknCEKrn1eCD0Grj9iU2C8rsm XvP296reOTZl4ixWbiKtD4aFq8G0opqku07f8rzUTwm/l+1NNuRUTu4yDr+3CGwfxdAu vHShWYw3Y9dN+2/YYgVNqn05MAygKBd5aJZXv1M890ZAgERi3wSSoW7nkYAIgnrV8hh+ A44syj5b1T0miAhEOlNI0fY3i0M/UGdoXe0zXu/SCfIFbLMytLP2qhnkI1tfoloQdo3M h4e4WjPbZ0r9sWOy4EzHLeMXXqH7XmKg3Ba5WexPir45XQpAFvhLHSDDP0VmxYeDIObN mx1g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780721; x=1771385521; 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=5sfIotc7x/caqwXPwf+nk7Gcfnkj95FCCwlgWI7Ef+4=; b=HouPl1zpB5cQeGDRNZq1hUJUScyRBYsUfz2ccKCEVfAtgmNTFAg5dlbQXfSCx+5CAk 8ibsr2IPEFc8ojSN0BysyMmhvIcw+7MEKqIQupW9GHHBYfBO0crPB0An1cNBQbN/NX35 LUMK7Yoq1ODPXIA2fwIx/fFsZcu5DatbEz7qfKI71+PgqwQq/+cE2wt/C+6gfp8jrW07 uGWgs1gcnMoxyTa/JF4JC5IM/foo0sXJwc0q0i3FM/v3zA4ax0ZbnI8LjJW5Qk18XBW4 pDucUtXGkH46wkniukIoY+f6I9JBlmrUIXamZPN7V2Ey9UMDqawmh9UbpeMHAmS1OwJh sQ/A== X-Forwarded-Encrypted: i=1; AJvYcCUyqrQFq79rVf0dVjACBC0hz524XCvpKOJAN8javYqDwHEHe7kra3mHCrzHrFBnv8RtE1p7b+qjdMy7lE0=@vger.kernel.org X-Gm-Message-State: AOJu0YyScNHLBButwzCO6g0enhKDW/b+YTBmuM1H1h3/eKP43kmGnK9z hV5BFqDlW/mLbNJ2GDPEuGjz2YF0f6EpxiejG9ya0AvfCd5cVahpteRO X-Gm-Gg: AZuq6aJMr9zmuHZRQha6eB/um5KK6jqUgvP8PqLLY0GjOeRBvxR+4QLSKimKeFTOBDd 6ty4Wd4lijzSHVKY77mkTazvQPwQiVlJCGEvYVxhquS+1mLldcJKwtnbHtjoIMSYNShIW3ekkuR yyhxylE6Kzn7EVGVSWuDUZCy34CsmMZNndlqseBfwwCbmKjxoW4Rx6NsxcDsG1x5rR9zANQ3Iho UVyYm4Mrc2h2rqo6QyrazpEhP8m+cW9E5QchRmacWfQcEkphj4Sr3gMZyrB1jEf+pjyKw8PRkNI 0F03g8/0Opz5o1wYjJEd9rGbrSYy9IDzwlZrr2DR4xo2IsIIDSXCG4sGX6jYDcA78uz479UnSua Y400q/HgEsag4RMFvtUpT6dCL8YVfDxrqpEVuXtGL2vDMkQzVTEB7GpCgAJhmYgyRiBD2N1v0X+ TuQsrr0vIjHNiPwQlaUHDaJmZ6aDd8l7K+uCcMIoeRNw== X-Received: by 2002:a05:6a20:2443:b0:394:3001:8b59 with SMTP id adf61e73a8af0-39432412df1mr820541637.53.1770780721130; Tue, 10 Feb 2026 19:32:01 -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.31.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:32:00 -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 19/27] PCI/CMA: Support built in X.509 certificates Date: Wed, 11 Feb 2026 13:29:26 +1000 Message-ID: <20260211032935.2705841-20-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 building the X.509 certificates into the CMA certificate store. This allows certificates to be built into the kernel which can be used to authenticate PCIe devices via SPDM. Signed-off-by: Alistair Francis --- certs/system_keyring.c | 4 ---- drivers/pci/cma.c | 28 ++++++++++++++++++++++++++++ include/keys/system_keyring.h | 4 ++++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/certs/system_keyring.c b/certs/system_keyring.c index f3d8ea4f70b4..adfc24139133 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -28,10 +28,6 @@ static struct key *machine_trusted_keys; static struct key *platform_trusted_keys; #endif =20 -extern __initconst const u8 system_certificate_list[]; -extern __initconst const unsigned long system_certificate_list_size; -extern __initconst const unsigned long module_cert_size; - /** * restrict_link_by_builtin_trusted - Restrict keyring addition by built-i= n CA * @dest_keyring: Keyring being linked to. diff --git a/drivers/pci/cma.c b/drivers/pci/cma.c index f2c435b04b92..8d64008594e2 100644 --- a/drivers/pci/cma.c +++ b/drivers/pci/cma.c @@ -10,6 +10,7 @@ =20 #define dev_fmt(fmt) "CMA: " fmt =20 +#include #include #include #include @@ -218,8 +219,31 @@ void pci_cma_destroy(struct pci_dev *pdev) spdm_destroy(pdev->spdm_state); } =20 +/* + * Load the compiled-in list of X.509 certificates. + */ +static int load_system_certificate_list(void) +{ + const u8 *p; + unsigned long size; + + pr_notice("Loading compiled-in X.509 certificates for CMA\n"); + +#ifdef CONFIG_MODULE_SIG + p =3D system_certificate_list; + size =3D system_certificate_list_size; +#else + p =3D system_certificate_list + module_cert_size; + size =3D system_certificate_list_size - module_cert_size; +#endif + + return x509_load_certificate_list(p, size, pci_cma_keyring); +} + __init static int pci_cma_keyring_init(void) { + int rc; + pci_cma_keyring =3D keyring_alloc(".cma", KUIDT_INIT(0), KGIDT_INIT(0), current_cred(), (KEY_POS_ALL & ~KEY_POS_SETATTR) | @@ -232,6 +256,10 @@ __init static int pci_cma_keyring_init(void) return PTR_ERR(pci_cma_keyring); } =20 + rc =3D load_system_certificate_list(); + if (rc) + return rc; + return 0; } arch_initcall(pci_cma_keyring_init); diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h index a6c2897bcc63..35a33412e175 100644 --- a/include/keys/system_keyring.h +++ b/include/keys/system_keyring.h @@ -130,4 +130,8 @@ static inline void set_platform_trusted_keys(struct key= *keyring) } #endif =20 +extern __initconst const u8 system_certificate_list[]; +extern __initconst const unsigned long system_certificate_list_size; +extern __initconst const unsigned long module_cert_size; + #endif /* _KEYS_SYSTEM_KEYRING_H */ --=20 2.52.0 From nobody Thu Apr 2 15:42:27 2026 Received: from mail-pf1-f171.google.com (mail-pf1-f171.google.com [209.85.210.171]) (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 ACA35347BA8 for ; Wed, 11 Feb 2026 03:32:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780729; cv=none; b=jFGnBnii4wqJFlpaaO2zeDbgeW+m4JY9+ClRcczBXe5FDnPkeH8430RN2etUXrOtFTOZhRXf71V2ZoCcJzIGwJyRx+XS2/ljvV063LjOhI0Ep01rexlL1xLKrvsGwOzLPXE2Qm1OIPXtN1TuCZpsz+wwaT0rBp70wmElYvdydDo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780729; c=relaxed/simple; bh=BS4wcIwoXxB/BfRlgobZUgb7fvQ0v0BNsMCQMjGrn0Y=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=AP2886alShW1OXDwXyu2ixKoYZ3qn3fIMtjT+XaoPqyKyE0rsgrRkvKIuJKF9lUjsbCQRknopRvm0buMx8f6WfZKfSu4VMkYohDWUJCnOm5sMbXnDFdM/6nkPCxuyMDe4++gH4ik6JegfBvNR+iTt6vbHhNAziH5n4iXHD1sroc= 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=jprI8o00; arc=none smtp.client-ip=209.85.210.171 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="jprI8o00" Received: by mail-pf1-f171.google.com with SMTP id d2e1a72fcca58-823081bb15fso2474376b3a.3 for ; Tue, 10 Feb 2026 19:32:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780728; x=1771385528; 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=oi2wlCfDaGRwhyBd7+2iWudqGoJb/SncUXJkMt9402Q=; b=jprI8o00x/piRHbGbfv+vhlDiRy7bl8rTIZubxCtUkcFnIk0PG/z/o2Z6SN9QlNpC2 AK2g1TctXDWK4gQwcA/UzFx7bzUCfzBj5JUa/Jb0MHCLfsMkUCf46ETIBRt4nH4dNSW0 9NR1oAr6WzmsOpiyfFCLBikDE0qU0JgH2EGpuOR5DrK/hPjP5WhGzBvl/FJCpt75sF5H qeqGN02Skl204CHdm8ms/3H3msbpQhqQWfVZodl7V3/jTUB++ZhmnKmCiroIDWPa4esa 34I0qfySLErFdOot3YpQ1ImNu4rKFzwMKxMoTNmT5Pexct15VusPiDax7O0hwVoaXLg2 oKTA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780728; x=1771385528; 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=oi2wlCfDaGRwhyBd7+2iWudqGoJb/SncUXJkMt9402Q=; b=YK3FIU/4XFryeodrLoZObqE6jzyQtF4vkelxbkBRF76dWb2hMkfxIMaB0zRc3/6Avy QbDsrDaJNWbwtkZevotwDHx+fPZDIODWSBj+tU5GKJV7zpqkqsyaCWIW3jey+IJ8MTBG wl6AYWJ1aAtw1ybHjdSCHj5GA7FCnNG/rJlMiWvvLvEGbgM1GGOHqmWCeUM3iUN6YrpN LT5GK0YcUfizWlyqGi+WfzPV7FopChExKZh70nu2VBcg5LS/PBPVPOYSvpPEItS+HVZ9 gv73Nk4vuW6mEEpLgu9tMWNYtIZZx8He97J/gJeBYrQfthtQLZRtILutMKB9wFpoDI/Z QV9A== X-Forwarded-Encrypted: i=1; AJvYcCXaVZpWA2cnuDGDAQBQtxu3zSDf4Ai7ReExn1qHXe+w4ohwCHajiXJM64VU4TYWZzeTRg1qiz1cVmh2Bj4=@vger.kernel.org X-Gm-Message-State: AOJu0YxtOws7tadYldIMUHqcb8j7FXB4WidHNAio/NAN/EJRe77vF7cL PusHJFWLTFX6L/E+C5IhSgeNrKxf868gUA2JruamqfVN3PmuD29/Pw4S X-Gm-Gg: AZuq6aKr7Vt09p+ruD9/h1/NqN/ZC6NtnG71pivvdHLjopaN4ZhqRNLdnRMbwSe2u9S Pb66cehVt4ab8RkxzNOUbU2KRtkARYCvEKwVMr+BzQb0c9KEF5B/0m1vB4mP1OQJliy6toiIVca 0WT+qgrmH8d3XQZqQJQ2Kv/2JCUlmoQZcl6cWbwHNE+oZ9slRr0mOdaTfcO3FLBckesVYmWkJYk lIvm87NGFq2ZuZoRR0qTXUmmIa8S2O2rJ91WeB92+OmCNDPWtmUC8JygVrDf2IFUDVRHVqogCGK 4SxCgdU33ubZCnCnM1+Mzts6RxoEK25A6BH21oTXCi7g08TcKEHEfyx+MH5/aLvoNe62bviuSX4 LxaggHe8+iFFKMeaMg9KvungzdwE42HvqU2++QBDIFCCiTaK4ZBNtLx/0MFKiGF0WQSeKzldzzp cbNxpw/++fQAy78fMhwi6c6LRSjHbqPfipYHnaWoxE+A== X-Received: by 2002:a05:6a20:cfa9:b0:334:87c2:445 with SMTP id adf61e73a8af0-3942e3c472amr1397613637.36.1770780728067; Tue, 10 Feb 2026 19:32:08 -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.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:32:07 -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 20/27] crypto: sha: Load early in boot Date: Wed, 11 Feb 2026 13:29:27 +1000 Message-ID: <20260211032935.2705841-21-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 Work is ongoing to support PCIe device attestation and authentication. As part of this probing a PCIe device will require generating hashes via the SPDM protocol to the kernel. Linux should verify the device before enabling the device, which means we need the crypto functions to be ready before arch initilisation (where PCIe init happens). Move the crypto init to postcore to ensure it's loaded before PCIe devices. This allows us to verify the certificate chain provided by a PCIe device via SPDM before we enable it. Signed-off-by: Alistair Francis Reviewed-by: Jonathan Cameron --- crypto/sha256.c | 2 +- crypto/sha3.c | 2 +- crypto/sha512.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crypto/sha256.c b/crypto/sha256.c index fb81defe084c..0ebe96f89733 100644 --- a/crypto/sha256.c +++ b/crypto/sha256.c @@ -398,7 +398,7 @@ static int __init crypto_sha256_mod_init(void) { return crypto_register_shashes(algs, ARRAY_SIZE(algs)); } -module_init(crypto_sha256_mod_init); +postcore_initcall(crypto_sha256_mod_init); =20 static void __exit crypto_sha256_mod_exit(void) { diff --git a/crypto/sha3.c b/crypto/sha3.c index 8f364979ec89..d37ce694b50a 100644 --- a/crypto/sha3.c +++ b/crypto/sha3.c @@ -145,7 +145,7 @@ static int __init crypto_sha3_mod_init(void) { return crypto_register_shashes(algs, ARRAY_SIZE(algs)); } -module_init(crypto_sha3_mod_init); +postcore_initcall(crypto_sha3_mod_init); =20 static void __exit crypto_sha3_mod_exit(void) { diff --git a/crypto/sha512.c b/crypto/sha512.c index d320fe53913f..6effa6043b55 100644 --- a/crypto/sha512.c +++ b/crypto/sha512.c @@ -404,7 +404,7 @@ static int __init crypto_sha512_mod_init(void) { return crypto_register_shashes(algs, ARRAY_SIZE(algs)); } -module_init(crypto_sha512_mod_init); +postcore_initcall(crypto_sha512_mod_init); =20 static void __exit crypto_sha512_mod_exit(void) { --=20 2.52.0 From nobody Thu Apr 2 15:42:27 2026 Received: from mail-pg1-f181.google.com (mail-pg1-f181.google.com [209.85.215.181]) (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 B673C348866 for ; Wed, 11 Feb 2026 03:32:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.181 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780737; cv=none; b=S7uBobiLYouS8MeQkaVVlb3JbJF9FIApScAfs59S9Uqgvm+UArmdAeal7v3hbnDiYeuMX4JyLWMTJbR1/Rdo/r6aTGApwDVkA0CLB2P8u7bXdXwMZkKlovSEh8JIT9B6QC5io+VuatnVgROy2aRRIe5d7+ad4WDwpbmFsAtOfWU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780737; c=relaxed/simple; bh=9VdUnjWUzIRc5MRPzIsJU//SqqlQjG40MUyiPknL2gs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cS0Bdn8DePpcPtaIjbaFag81S/ViO6RxBDpRjvqc1RfAAghihRn+9b/pFeV+x3Rvk2MFhg5SPjZeo+MLR49EDB6PJsaSAcnkAUa0I/NTg19Ez04utq8OWd0a8dXDArNSZICn/3VjElCpoHBLFlQ5ZYyATitGPh/yY5eDEs2gyCE= 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=DB1Dbdll; arc=none smtp.client-ip=209.85.215.181 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="DB1Dbdll" Received: by mail-pg1-f181.google.com with SMTP id 41be03b00d2f7-c5513f598c0so2278311a12.0 for ; Tue, 10 Feb 2026 19:32:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780735; x=1771385535; 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=c4AFw0Ke6Jdsl0upZ9PbC8XO1EFiCkPW7RyBX/Dx3Ac=; b=DB1Dbdll2K5Lm5FX54944Tv++KRUsg60jNmVbZT1174jH5ASUE5MB9JfcR+RQBhrgD vFa4HhfXsu55Wlvv1gGQiDNBalLw+QFqU8zTvDlPP7ThCOrx6hAPxkrKxGF//mhMMfAf z9AmAvkJXBcLlEkXTPHeomVTBDkTC4Dfag/H9TckkhwJDVAFRN7lh/Ko2FfqDouzfGfq zHJxboARQJ4257ICQlFXUVFakdQ9eDWOPZrz/17/Jpx7nRi35Ep7l+oqTTIF6r/gCtL/ 1Wo7nCudZKYoXnbDpOlmgb90SvLuCthM3p4576xt+8iUhGViZ3p5eO9iu5AB6N/brAPH P7oQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780735; x=1771385535; 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=c4AFw0Ke6Jdsl0upZ9PbC8XO1EFiCkPW7RyBX/Dx3Ac=; b=wWHqj/sNH7B+VraXvrAV1ZxGuhcwvZkIDKUP0mmqhZlRfGE5GlDP9ovDZfcdkDnUVe 5bcJl6p5S9oqFKIwFBHmnjFgwCEpwqclOTNmrbRM0vMMaLKwOcgYLba7NVivueBsZK/A tE424elotd/uIEdW9Ur+vG0ywz34QIMjWKUKlzEv7ZVtS61iJaBbC90nPE7SEjpa0yBl j0sPjHHr7daWLcL1V/dxeb/sDCeI0XCMmes6OozsNthbLg77olGN4Gp3ncPTwjYoCeAp A3wV6qbGhfcgk0tlAHbEK68wwHSTqs4CHNzksRQQ22namxFE6zYFKpomXhNKRApGINMA 9Erw== X-Forwarded-Encrypted: i=1; AJvYcCW7tmUym3Hc2XyNQJwi/QKwQCxd/YA7AIl55zJQcNNES0ubst0g81zhknnOere7jsz6JwFSQssYpI95BPk=@vger.kernel.org X-Gm-Message-State: AOJu0YyAVMI084hWKeGeig8t5/vezAAuyT2UROCm+THIZ1zAEMUY8Y6K BGt+VN4XjQi3ABDA/psbLcjv5tU7sev+jJxDDYjfLQrSJ3Z0rtv6lxW/pMFdP2oqVdk= X-Gm-Gg: AZuq6aIO60LWkp1TfqEkZB6rgZb45el1B0MmR/YvhChnIIHTqWVTeFgWaU+E66HRmE3 7VrWf74nUBw0kPIBdWkABjfKGtt7jxAtdbt/JHSYmhZHEigct2+kKrjI5O8roixtik0O+WrU/Ex Ti6lTqWfJH4UeNn814w6w2syM3fTqvnvF/a4X3o0fDLMyoFUFbI6o4B2wmBFA9jLrJ9bmKPLg6W oEuMn9JrxJ5t/OuEjT27d6n2LkYTArM8XowZrxZVJVHdSAswyj3lQ2r+xlo0YpNiaw42sJ56DZq 2WdbL3PJ71Jx82KMwieghpKEjBQ2QyS2RCyTSnSgPGIbO0FwlYvbtX7Bi3/kjbvNuFT8rYWB0d2 BP/8rwKw3Nm1amTPKFZja9CLqLQZ7fvaPryxpnz3QPuGlkzoPzChV4Y/R8/sXFKxtixEiK0lOvN s+Sxn109+y3SrWyX2CFgZ/4bV5UBZJWGVXswPRb8oKBw== X-Received: by 2002:a05:6a20:1589:b0:364:14f3:22a7 with SMTP id adf61e73a8af0-394323c170amr719059637.42.1770780735134; Tue, 10 Feb 2026 19:32:15 -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.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:32:14 -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 21/27] crypto: ecdsa: Load early in boot Date: Wed, 11 Feb 2026 13:29:28 +1000 Message-ID: <20260211032935.2705841-22-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 Work is ongoing to support PCIe device attestation and authentication. As part of this probing a PCIe device will require signing via the SPDM protocol to the kernel. Linux should verify the device before enabling the device, which means we need the crypto functions to be ready before arch initilisation (where PCIe init happens). Move the crypto init to postcore to ensure it's loaded before PCIe devices. This allows us to verify the certificate chain provided by a PCIe device via SPDM before we enable it. Signed-off-by: Alistair Francis Reviewed-by: Jonathan Cameron --- crypto/ecdsa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/ecdsa.c b/crypto/ecdsa.c index ce8e4364842f..b225911c5266 100644 --- a/crypto/ecdsa.c +++ b/crypto/ecdsa.c @@ -334,7 +334,7 @@ static void __exit ecdsa_exit(void) crypto_unregister_sig(&ecdsa_nist_p521); } =20 -module_init(ecdsa_init); +postcore_initcall(ecdsa_init); module_exit(ecdsa_exit); =20 MODULE_LICENSE("GPL"); --=20 2.52.0 From nobody Thu Apr 2 15:42:27 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 From nobody Thu Apr 2 15:42:27 2026 Received: from mail-pf1-f171.google.com (mail-pf1-f171.google.com [209.85.210.171]) (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 9F792348466 for ; Wed, 11 Feb 2026 03:32:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780751; cv=none; b=fegD4H2Tni3HpiIoYNQlH4efrol71Q5Ia608JlOYk9xXgYvj3o/Kur3EtEc0cccZd2dKPrbuUMxyWJHYr1twe7jMKO8gHCgESkOX7HfkcVdGmydsiL6Ld/Pv9QQwouuac8925e6ATWSZHlfhqtBoyrA5yGbIB+qQHMtimTMuptw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780751; c=relaxed/simple; bh=dNpi1NMAaQ5h9qqbmQ1Z0VBLphRlK4MYeHJdgBkEl2s=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MT/0llrzYO3QEmSAYEidQBAzj4HCE0eXDtRCbnWbzCSDodnt+ATxQEp05nQ0E9r2/MtU7oqaMqP/DutAOINc4p2Qh1ABZAKjZ03KoSVPFLEDP4QCihRjW6m8cwj1bo2o4gQSnNoZ+G8SdCRVfJ1ea1kLEwEXkXM+9VS5jWP5Qx8= 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=mY0cd+sb; arc=none smtp.client-ip=209.85.210.171 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="mY0cd+sb" Received: by mail-pf1-f171.google.com with SMTP id d2e1a72fcca58-82418b0178cso924504b3a.1 for ; Tue, 10 Feb 2026 19:32:29 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780749; x=1771385549; 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=9Hd4JNZlcn7oVuweMnFnRxHFMq+VGi5u1+C/ZJTSOAc=; b=mY0cd+sbu8JVYqnTxyljkX7xHNpQ/WPcfvw+VJNyBREzjLBE0ICSuNdsX8PfeCCfND bLdUpVoNjPwMKHNmKk+kcY1WbhfZ2Kb/ADA8pgXRqjoIbNzOduTiLZAcrNmHt7ToxL+L RlAhYQFBYHXcbeth2C6tUXMEpYfqhrmHt4MJafaUhq/jpw/WTF8IfOIK/lqVWr3LJKBI xLyg7g5jwSn7HtQ82Q6R28NY5TNFeNaLUGnHxco9zn6ykzQP2Z+NWysX2XnzQHAUKD58 cCkgYCjIJ0m/ol+SlFTuVNcCdFP+D42ab2XIRwRzX86FLYZwe8EnTuF6QnwNwBI4ZfEt hjng== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780749; x=1771385549; 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=9Hd4JNZlcn7oVuweMnFnRxHFMq+VGi5u1+C/ZJTSOAc=; b=BhLj/8JDxviVWDiw516bt7187F8fEOUOJFXuNzErENGnWDqrJeKaCDwll+/Mh7Vv4q cuK/RSC+4782f7Q4WZd1PBLx/4WDcJPHinKg0NqENch0h64kk+d7BovcBBXckBNNHLu5 QQc5Nk9GAx/2K6Y+xNyi1IZg+G0olnW8O8kpPBIfatKCcuAhJBYHIjHT/dVUYic7no8t XqhrMi++WARSpDOGs7AusSNmAyJVblJaCfithxl29MfRZhY/f2ui4Nw3QPtN+yIgdJy+ wMw3H2EmiWB/P/bmJeqspRDIv8o0VtdCHPrduXyZwz09tgyu29ezJS92N3JbEM2baynt oYaw== X-Forwarded-Encrypted: i=1; AJvYcCWwt7XNf6RfKwC0N6G+5yddXWULzDXI4LfIu8evc+dWun5u+ltBwSdCde1m14c1OHDBOcnHZCmfEiZz4cU=@vger.kernel.org X-Gm-Message-State: AOJu0Ywsi59Mil71dSNjWaioHufIqRF+yAAb+zt8ptKwZMW89kTydo2T XP9adT9GpfDI1fiHrgtUH6uNjGvdpVPzl5kLf7PwwpOY4aqwWSJgiBbK X-Gm-Gg: AZuq6aJ4F4aljdnwgWny6pWl4sAmD38PEvyzG1utxtFerOTPz+naCOoaZK9BeG0N8Us 0W9Tt2gJBc33CHypHkuo2hKPQ1M9fNDGqclB1DFsGX1sqxAd0OmU65Ox7eT+7cTOFlgIla/EBIl mCqZKM9gXQfrVouXkJ4kaHrBZhxFpXQgqwX1hNeL3FGRQHXI/qHVnTtWh+dv7zmylIljiglPH3e uxEK0ichBJyQeydiZJDiAVgj2+u9SmR+FymWIiZ3QuxYZfPFsyOaWGhKS3Qx+kjzkqpOi3HUWw6 HH1ReCRhrDteB6V2/x+MIouIYJSUeQl9V9f4Xss5zjdwKN/st5z/1cU0bMonqG7ILnlH0azWZoB Do2VB5ncaK4IdKRfqfgHv953dwJuzr42pyO5oFAYizdgiWSOXWlSxFiNCZq6VdrqpKmXpz7Zx4B BiFJpt8qra4/LqQfULi7G8vbMtGRP55TWsMAsZy1rUt3w4iys5oe4m X-Received: by 2002:a05:6a20:c793:b0:38e:9405:beff with SMTP id adf61e73a8af0-393ad3b6d6bmr16299563637.65.1770780749068; Tue, 10 Feb 2026 19:32:29 -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.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:32:28 -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 23/27] rust: allow extracting the buffer from a CString Date: Wed, 11 Feb 2026 13:29:30 +1000 Message-ID: <20260211032935.2705841-24-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 The kernel CString is a wrapper aroud a KVec. This patch allows retrieving the underlying buffer and consuming the CString. This allows users to create a CString from a string and then retrieve the underlying buffer. Signed-off-by: Alistair Francis --- rust/kernel/str.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs index fa87779d2253..17426db83c9f 100644 --- a/rust/kernel/str.rs +++ b/rust/kernel/str.rs @@ -844,6 +844,11 @@ pub fn try_from_fmt(args: fmt::Arguments<'_>) -> Resul= t { // exist in the buffer. Ok(Self { buf }) } + + /// Return the internal buffer while consuming the original [`CString`] + pub fn into_vec(self) -> KVec { + self.buf + } } =20 impl Deref for CString { --=20 2.52.0 From nobody Thu Apr 2 15:42:27 2026 Received: from mail-pf1-f173.google.com (mail-pf1-f173.google.com [209.85.210.173]) (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 C446C345CAF for ; Wed, 11 Feb 2026 03:32:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780759; cv=none; b=oELX2gE7fPlZYmwnHNf6BfCBwe1G968Hsp/nQrhRGxuu6IFh2rjEnMy5UahTyUB2VnSQ2ssEnvZ1tiX9p/7czBuGouuJ57dle14NMXLH7NYHG/v1Z2YmUx9ICd0I8icDcVr0lIqZhC2gt/hxj6ExFb0pwYaNmm+GYGXx925Y4ok= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780759; c=relaxed/simple; bh=OHYeXBOEWikJ5tM3JO1c9NWGzCdzDjoaW7Gx9KKZKx4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=qQZN730m3qMK3JthMMRCqlu4vL+HstLAfCwQnFCq6osVGgSGPK059qhdetT9PkiSS47PkZXnIO8CqHdr8bHb8DI8uZGTvGOEJAd0hLdg/iY3TGbDizQ/owUnPk/UZqN2jV2panI1tAMPGxQF+bcC64U5pIQ8lxKZEQXdwWLzX6k= 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=QIeZhnER; arc=none smtp.client-ip=209.85.210.173 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="QIeZhnER" Received: by mail-pf1-f173.google.com with SMTP id d2e1a72fcca58-81f4ba336b4so5140808b3a.1 for ; Tue, 10 Feb 2026 19:32:36 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780756; x=1771385556; 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=gBL0ioZW+u5dvBBFyAK8qPVd1Z14bb+2hhlX3XvqGrU=; b=QIeZhnER28hP6UgNV0hd6NQ2DCUMiwDDCFWLkhFBo/rtbS2BwAs4ZUyNIfoie8IWHM WnQ6p/3EOoGOBRP1y4fE6WojiqnTGzp2obuD3dijZ5Nac++h0nt2IVAc/r8IGlnWoUZA iyRxMl/dhFKJ5P9ut7tOertyGtk0xh8+BstcP4eMwlTWuCNsA9RUgTdkREPPjoPTnrE1 TWFliBLObGijLuombhz9/On9T7fzzcLbcIlljr2vXRqln8BgD2UUyGSvgiwWnKTe5zf6 Zw3eF5f3XeraX2Iu3f60lfeH7LMclaM1SvPTz09VcM1BfbP6gAC+mbCn1dnvWc3DA1Eh ZfpA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780756; x=1771385556; 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=gBL0ioZW+u5dvBBFyAK8qPVd1Z14bb+2hhlX3XvqGrU=; b=ltk+dhB1MKEwyqCvh+iSOe0LCL0rzdLnZctScCHqXZiGC5hfYfKpVyFTHc+xs9orUh 14nBGKi4I2t1BI/DgoYoENMvsSF9C6hzwoTCFuXF/aUN3eujHsnhSgnpImFnjRandbQj PPqMp0fkdcWRQKY+OzWjggE9OfYTVa/wJdizH2OC7QbPWlsrOJrPiRhachLLaC7A5e7+ f9gOsP2XzcAlNVZmK+PiFaXbsq3kN4dEoq3vdFJscE6Lul0EmqjdvO3cYC3JApme75JK 7/7xWJ/usr0yCFC27MC6wN765XV2+jMfMh0aJ4p9r4wHVD1s1HTGtL9LPlGSj4iLF7+O RdUg== X-Forwarded-Encrypted: i=1; AJvYcCVadwCXNLgqYwDgu6w7BtS3DyAvQR9c0nqCDpIlILXA9astppmAsi2QasiyeMk18X6AATQLc11rwIGCyP4=@vger.kernel.org X-Gm-Message-State: AOJu0YwGiK18m7NAO+N/FJJo2qU3fshUq25xVOHXKDz1LYRF1RNZz/0+ TvH8VWhcCb14QhMkb6F9yfFQT2VXmD4qRWOcroEV3VKsW6wqvVB2m4/t X-Gm-Gg: AZuq6aKFiJ3ukhjTAnVwmRRq9pPQOfFpGEq/wVaLaP5q66rzXTD8XiDIJd4mY6uDsYz wXgWMYcs/SmSOVjlQDsHUm0VbRlvSPZuMQpcXASknieRZsiRJP8lrI/UqHE7jOYXxT1NG4QvhAL PukckBPQgRgfD8JNZx/4QguQ0R3VPtTcwOn2HY2AvCDA1stSyhuTDy2e2FBSHj9F+VTESRQawh0 oeijJgn1IOv/KIhAJYAAnvreZKc2RAqF8j6QjkWFKJ3UFYkRB4FRqYoE0xnhnsiRkQP1Je8CWNc 584uCclpeU7bpT6Dv46MmbIPoMOV0M+6xtCxyG/QV4GuIZsY8vrBEfMKxfOYPgls2+u40UhAh5O TJe0jfNG4aMlFJy+rK52Rmk6SYq+kFUc1hq8PlcK+3Loy8Rrt7YLHPXNjG/jeI1VTflaizbu39V QRGQcL+8w3G9cCOs9YkWpaz9BDIoInm6KxHyfXZ4F1ZA== X-Received: by 2002:a05:6a20:cfa6:b0:366:14ac:e1ee with SMTP id adf61e73a8af0-394324a76d9mr666383637.64.1770780756092; Tue, 10 Feb 2026 19:32:36 -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.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:32:35 -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 24/27] lib: rspdm: Support SPDM challenge Date: Wed, 11 Feb 2026 13:29:31 +1000 Message-ID: <20260211032935.2705841-25-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 the CHALLENGE SPDM command. Signed-off-by: Alistair Francis --- lib/rspdm/consts.rs | 6 + lib/rspdm/lib.rs | 8 +- lib/rspdm/state.rs | 218 +++++++++++++++++++++++++++++++- lib/rspdm/validator.rs | 65 +++++++++- rust/bindings/bindings_helper.h | 1 + 5 files changed, 289 insertions(+), 9 deletions(-) diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs index eaf2132da290..904d8272a1d0 100644 --- a/lib/rspdm/consts.rs +++ b/lib/rspdm/consts.rs @@ -176,6 +176,8 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core= ::fmt::Result { =20 pub(crate) const SPDM_GET_CERTIFICATE: u8 =3D 0x82; =20 +pub(crate) const SPDM_CHALLENGE: u8 =3D 0x83; + #[cfg(CONFIG_CRYPTO_RSA)] pub(crate) const SPDM_ASYM_RSA: u32 =3D SPDM_ASYM_RSASSA_2048 | SPDM_ASYM_RSASSA_3072 | SPDM_ASYM_RSASSA_4096; @@ -205,3 +207,7 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core= ::fmt::Result { // pub(crate) const SPDM_MAX_REQ_ALG_STRUCT: usize =3D 4; =20 pub(crate) const SPDM_OPAQUE_DATA_FMT_GENERAL: u8 =3D 1 << 1; + +pub(crate) const SPDM_PREFIX_SZ: usize =3D 64; +pub(crate) const SPDM_COMBINED_PREFIX_SZ: usize =3D 100; +pub(crate) const SPDM_MAX_OPAQUE_DATA: usize =3D 1024; diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs index c016306116a3..05fa471bb1e2 100644 --- a/lib/rspdm/lib.rs +++ b/lib/rspdm/lib.rs @@ -136,17 +136,23 @@ provisioned_slots &=3D !(1 << slot); } =20 + let mut verify =3D true; let mut provisioned_slots =3D state.provisioned_slots; while (provisioned_slots as usize) > 0 { let slot =3D provisioned_slots.trailing_zeros() as u8; =20 if let Err(e) =3D state.validate_cert_chain(slot) { - return e.to_errno() as c_int; + pr_debug!("Certificate in slot {slot} failed to verify: {e:?}"= ); + verify =3D false; } =20 provisioned_slots &=3D !(1 << slot); } =20 + if let Err(e) =3D state.challenge(state.provisioned_slots.trailing_zer= os() as u8, verify) { + return e.to_errno() as c_int; + } + 0 } =20 diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs index 728b920beace..a4d803af48fe 100644 --- a/lib/rspdm/state.rs +++ b/lib/rspdm/state.rs @@ -14,23 +14,27 @@ bindings, error::{code::EINVAL, from_err_ptr, to_result, Error}, str::CStr, + str::CString, validate::Untrusted, }; =20 use crate::consts::{ SpdmErrorCode, SPDM_ASYM_ALGOS, SPDM_ASYM_ECDSA_ECC_NIST_P256, SPDM_AS= YM_ECDSA_ECC_NIST_P384, SPDM_ASYM_ECDSA_ECC_NIST_P521, SPDM_ASYM_RSASSA_2048, SPDM_ASYM_RSASSA= _3072, - SPDM_ASYM_RSASSA_4096, SPDM_ERROR, SPDM_GET_VERSION_LEN, SPDM_HASH_ALG= OS, SPDM_HASH_SHA_256, - SPDM_HASH_SHA_384, SPDM_HASH_SHA_512, SPDM_KEY_EX_CAP, SPDM_MAX_VER, S= PDM_MEAS_CAP_MASK, - SPDM_MEAS_SPEC_DMTF, SPDM_MIN_DATA_TRANSFER_SIZE, SPDM_MIN_VER, SPDM_O= PAQUE_DATA_FMT_GENERAL, + SPDM_ASYM_RSASSA_4096, SPDM_COMBINED_PREFIX_SZ, SPDM_ERROR, SPDM_GET_V= ERSION_LEN, + SPDM_HASH_ALGOS, SPDM_HASH_SHA_256, SPDM_HASH_SHA_384, SPDM_HASH_SHA_5= 12, SPDM_KEY_EX_CAP, + SPDM_MAX_OPAQUE_DATA, SPDM_MAX_VER, SPDM_MEAS_CAP_MASK, SPDM_MEAS_SPEC= _DMTF, + SPDM_MIN_DATA_TRANSFER_SIZE, SPDM_MIN_VER, SPDM_OPAQUE_DATA_FMT_GENERA= L, SPDM_PREFIX_SZ, SPDM_REQ, SPDM_RSP_MIN_CAPS, SPDM_SLOTS, SPDM_VER_10, SPDM_VER_11, SPD= M_VER_12, }; use crate::validator::{ - GetCapabilitiesReq, GetCapabilitiesRsp, GetCertificateReq, GetCertific= ateRsp, GetDigestsReq, - GetDigestsRsp, GetVersionReq, GetVersionRsp, NegotiateAlgsReq, Negotia= teAlgsRsp, RegAlg, - SpdmErrorRsp, SpdmHeader, + ChallengeReq, ChallengeRsp, GetCapabilitiesReq, GetCapabilitiesRsp, Ge= tCertificateReq, + GetCertificateRsp, GetDigestsReq, GetDigestsRsp, GetVersionReq, GetVer= sionRsp, + NegotiateAlgsReq, NegotiateAlgsRsp, RegAlg, SpdmErrorRsp, SpdmHeader, }; =20 +const SPDM_CONTEXT: &str =3D "responder-challenge_auth signing"; + /// The current SPDM session state for a device. Based on the /// C `struct spdm_state`. /// @@ -78,6 +82,14 @@ /// @slot_sz: Certificate chain size (in bytes). /// @leaf_key: Public key portion of leaf certificate against which to che= ck /// responder's signatures. +/// @transcript: Concatenation of all SPDM messages exchanged during an +/// authentication or measurement sequence. Used to verify the signature, +/// as it is computed over the hashed transcript. +/// @next_nonce: Requester nonce to be used for the next authentication +/// sequence. Populated from user space through sysfs. +/// If user space does not provide a nonce, the kernel uses a random one. +/// +/// `authenticated`: Whether device was authenticated successfully. pub struct SpdmState { pub(crate) dev: *mut bindings::device, pub(crate) transport: bindings::spdm_transport, @@ -105,9 +117,15 @@ pub struct SpdmState { pub(crate) desc: Option<&'static mut bindings::shash_desc>, pub(crate) hash_len: usize, =20 + pub(crate) authenticated: bool, + // Certificates pub(crate) certs: [KVec; SPDM_SLOTS], pub(crate) leaf_key: Option<*mut bindings::public_key>, + + transcript: VVec, + + next_nonce: Option<&'static mut [u8]>, } =20 #[repr(C, packed)] @@ -147,8 +165,11 @@ pub(crate) fn new( shash: core::ptr::null_mut(), desc: None, hash_len: 0, + authenticated: false, certs: [const { KVec::new() }; SPDM_SLOTS], leaf_key: None, + transcript: VVec::new(), + next_nonce: None, } } =20 @@ -256,7 +277,7 @@ fn spdm_err(&self, rsp: &SpdmErrorRsp) -> Result<(), Er= ror> { /// The data in `request_buf` is sent to the device and the response is /// stored in `response_buf`. pub(crate) fn spdm_exchange( - &self, + &mut self, request_buf: &mut [u8], response_buf: &mut [u8], ) -> Result { @@ -264,6 +285,8 @@ pub(crate) fn spdm_exchange( let request: &mut SpdmHeader =3D Untrusted::new_mut(request_buf).v= alidate_mut()?; let response: &SpdmHeader =3D Untrusted::new_ref(response_buf).val= idate()?; =20 + self.transcript.extend_from_slice(request_buf, GFP_KERNEL)?; + let transport_function =3D self.transport.ok_or(EINVAL)?; // SAFETY: `transport_function` is provided by the new(), we are // calling the function. @@ -331,6 +354,12 @@ pub(crate) fn get_version(&mut self) -> Result<(), Err= or> { unsafe { response_vec.inc_len(rc as usize) }; =20 let response: &mut GetVersionRsp =3D Untrusted::new_mut(&mut respo= nse_vec).validate_mut()?; + let rsp_sz =3D core::mem::size_of::() + + 2 + + response.version_number_entry_count as usize * 2; + + self.transcript + .extend_from_slice(&response_vec[..rsp_sz], GFP_KERNEL)?; =20 let mut foundver =3D false; for i in 0..response.version_number_entry_count { @@ -395,6 +424,9 @@ pub(crate) fn get_capabilities(&mut self) -> Result<(),= Error> { let response: &mut GetCapabilitiesRsp =3D Untrusted::new_mut(&mut response_vec).validate_mut()?; =20 + self.transcript + .extend_from_slice(&response_vec[..rsp_sz], GFP_KERNEL)?; + self.rsp_caps =3D u32::from_le(response.flags); if (self.rsp_caps & SPDM_RSP_MIN_CAPS) !=3D SPDM_RSP_MIN_CAPS { to_result(-(bindings::EPROTONOSUPPORT as i32))?; @@ -533,6 +565,9 @@ pub(crate) fn negotiate_algs(&mut self) -> Result<(), E= rror> { let response: &mut NegotiateAlgsRsp =3D Untrusted::new_mut(&mut response_vec).validate_mut()?; =20 + self.transcript + .extend_from_slice(&response_vec, GFP_KERNEL)?; + self.base_asym_alg =3D response.base_asym_sel; self.base_hash_alg =3D response.base_hash_sel; self.meas_hash_alg =3D response.measurement_hash_algo; @@ -593,6 +628,10 @@ pub(crate) fn get_digests(&mut self) -> Result<(), Err= or> { unsafe { response_vec.inc_len(rc as usize) }; =20 let response: &mut GetDigestsRsp =3D Untrusted::new_mut(&mut respo= nse_vec).validate_mut()?; + let rsp_sz =3D core::mem::size_of::() + response.param= 2 as usize * self.hash_len; + + self.transcript + .extend_from_slice(&response_vec[..rsp_sz], GFP_KERNEL)?; =20 if rc < (core::mem::size_of::() @@ -654,6 +693,10 @@ fn get_cert_exchange( unsafe { response_vec.inc_len(rc as usize) }; =20 let response: &mut GetCertificateRsp =3D Untrusted::new_mut(respon= se_vec).validate_mut()?; + let rsp_sz =3D core::mem::size_of::() + 4 + response.p= ortion_length as usize; + + self.transcript + .extend_from_slice(&response_vec[..rsp_sz], GFP_KERNEL)?; =20 if rc < (core::mem::size_of::() + response.portio= n_length as usize) as i32 @@ -834,4 +877,165 @@ pub(crate) fn validate_cert_chain(&mut self, slot: u8= ) -> Result<(), Error> { =20 Ok(()) } + + pub(crate) fn challenge_rsp_len(&mut self, nonce_len: usize, opaque_le= n: usize) -> usize { + let mut length =3D + core::mem::size_of::() + self.hash_len + nonce_len= + opaque_len + 2; + + if self.version >=3D 0x13 { + length +=3D 8; + } + + length + self.sig_len + } + + fn verify_signature(&mut self, response_vec: &mut [u8]) -> Result<(), = Error> { + let sig_start =3D response_vec.len() - self.sig_len; + let mut sig =3D bindings::public_key_signature::default(); + let mut mhash: KVec =3D KVec::new(); + + sig.s =3D &mut response_vec[sig_start..] as *mut _ as *mut u8; + sig.s_size =3D self.sig_len as u32; + sig.encoding =3D self.base_asym_enc.as_ptr() as *const u8; + sig.hash_algo =3D self.base_hash_alg_name.as_ptr() as *const u8; + + let mut m: KVec =3D KVec::new(); + m.extend_with(SPDM_COMBINED_PREFIX_SZ + self.hash_len, 0, GFP_KERN= EL)?; + + if let Some(desc) =3D &mut self.desc { + desc.tfm =3D self.shash; + + unsafe { + to_result(bindings::crypto_shash_digest( + *desc, + self.transcript.as_ptr(), + (self.transcript.len() - self.sig_len) as u32, + m[SPDM_COMBINED_PREFIX_SZ..].as_mut_ptr(), + ))?; + }; + } else { + to_result(-(bindings::EPROTO as i32))?; + } + + if self.version <=3D 0x11 { + sig.m =3D m[SPDM_COMBINED_PREFIX_SZ..].as_mut_ptr(); + } else { + let major =3D self.version >> 4; + let minor =3D self.version & 0xF; + + let output =3D CString::try_from_fmt(fmt!("dmtf-spdm-v{major:x= }.{minor:x}.*dmtf-spdm-v{major:x}.{minor:x}.*dmtf-spdm-v{major:x}.{minor:x}= .*dmtf-spdm-v{major:x}.{minor:x}.*"))?; + let mut buf =3D output.into_vec(); + let zero_pad_len =3D SPDM_COMBINED_PREFIX_SZ - SPDM_PREFIX_SZ = - SPDM_CONTEXT.len() - 1; + + buf.extend_with(zero_pad_len, 0, GFP_KERNEL)?; + buf.extend_from_slice(SPDM_CONTEXT.as_bytes(), GFP_KERNEL)?; + + m[..SPDM_COMBINED_PREFIX_SZ].copy_from_slice(&buf); + + mhash.extend_with(self.hash_len, 0, GFP_KERNEL)?; + + if let Some(desc) =3D &mut self.desc { + desc.tfm =3D self.shash; + + unsafe { + to_result(bindings::crypto_shash_digest( + *desc, + m.as_ptr(), + m.len() as u32, + mhash.as_mut_ptr(), + ))?; + }; + } else { + to_result(-(bindings::EPROTO as i32))?; + } + + sig.m =3D mhash.as_mut_ptr(); + } + + sig.m_size =3D self.hash_len as u32; + + if let Some(leaf_key) =3D self.leaf_key { + unsafe { to_result(bindings::public_key_verify_signature(leaf_= key, &sig)) } + } else { + to_result(-(bindings::EPROTO as i32)) + } + } + + pub(crate) fn challenge(&mut self, slot: u8, verify: bool) -> Result<(= ), Error> { + let mut request =3D ChallengeReq::default(); + request.version =3D self.version; + request.param1 =3D slot; + + let nonce_len =3D request.nonce.len(); + + if let Some(nonce) =3D &self.next_nonce { + request.nonce.copy_from_slice(&nonce); + self.next_nonce =3D None; + } else { + unsafe { + bindings::get_random_bytes(&mut request.nonce as *mut _ as= *mut c_void, nonce_len) + }; + } + + let req_sz =3D if self.version <=3D 0x12 { + core::mem::size_of::() - 8 + } else { + core::mem::size_of::() + }; + + let rsp_sz =3D self.challenge_rsp_len(nonce_len, SPDM_MAX_OPAQUE_D= ATA); + + // SAFETY: `request` is repr(C) and packed, so we can convert it t= o a slice + let request_buf =3D unsafe { from_raw_parts_mut(&mut request as *m= ut _ as *mut u8, req_sz) }; + + let mut response_vec: KVec =3D KVec::with_capacity(rsp_sz, GFP= _KERNEL)?; + // SAFETY: `request` is repr(C) and packed, so we can convert it t= o a slice + let response_buf =3D unsafe { from_raw_parts_mut(response_vec.as_m= ut_ptr(), rsp_sz) }; + + let rc =3D self.spdm_exchange(request_buf, response_buf)?; + + if rc < (core::mem::size_of::() as i32) { + pr_err!("Truncated challenge response\n"); + to_result(-(bindings::EIO as i32))?; + } + + // SAFETY: `rc` is the length of data read, which will be smaller + // then the capacity of the vector + unsafe { response_vec.inc_len(rc as usize) }; + + let _response: &mut ChallengeRsp =3D Untrusted::new_mut(&mut respo= nse_vec).validate_mut()?; + + let opaque_len_offset =3D core::mem::size_of::() + sel= f.hash_len + nonce_len; + let opaque_len =3D u16::from_le_bytes( + response_vec[opaque_len_offset..(opaque_len_offset + 2)] + .try_into() + .unwrap_or([0, 0]), + ); + + let rsp_sz =3D self.challenge_rsp_len(nonce_len, opaque_len as usi= ze); + + if rc < rsp_sz as i32 { + pr_err!("Truncated challenge response\n"); + to_result(-(bindings::EIO as i32))?; + } + + self.transcript + .extend_from_slice(&response_vec[..rsp_sz], GFP_KERNEL)?; + + if verify { + /* Verify signature at end of transcript against leaf key */ + match self.verify_signature(&mut response_vec[..rsp_sz]) { + Ok(()) =3D> { + pr_info!("Authenticated with certificate slot {slot}"); + self.authenticated =3D true; + } + Err(e) =3D> { + pr_err!("Cannot verify challenge_auth signature: {e:?}= "); + self.authenticated =3D false; + } + }; + } + + Ok(()) + } } diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs index a8bc3378676f..f8a5337841f0 100644 --- a/lib/rspdm/validator.rs +++ b/lib/rspdm/validator.rs @@ -17,7 +17,7 @@ }; =20 use crate::consts::{ - SPDM_ASYM_ALGOS, SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, SPDM_GET_CERT= IFICATE, + SPDM_ASYM_ALGOS, SPDM_CHALLENGE, SPDM_CTEXPONENT, SPDM_GET_CAPABILITIE= S, SPDM_GET_CERTIFICATE, SPDM_GET_DIGESTS, SPDM_GET_VERSION, SPDM_HASH_ALGOS, SPDM_MEAS_SPEC_DM= TF, SPDM_NEGOTIATE_ALGS, SPDM_REQ_CAPS, }; @@ -424,3 +424,66 @@ fn validate(unvalidated: &mut Unvalidated>) -= > Result Ok(rsp) } } + +#[repr(C, packed)] +pub(crate) struct ChallengeReq { + pub(crate) version: u8, + pub(crate) code: u8, + pub(crate) param1: u8, + pub(crate) param2: u8, + + pub(crate) nonce: [u8; 32], + pub(crate) context: [u8; 8], +} + +impl Default for ChallengeReq { + fn default() -> Self { + ChallengeReq { + version: 0, + code: SPDM_CHALLENGE, + param1: 0, + param2: 0, + nonce: [0; 32], + context: [0; 8], + } + } +} + +#[repr(C, packed)] +pub(crate) struct ChallengeRsp { + pub(crate) version: u8, + pub(crate) code: u8, + pub(crate) param1: u8, + pub(crate) param2: u8, + + pub(crate) cert_chain_hash: __IncompleteArrayField, + pub(crate) nonce: [u8; 32], + pub(crate) message_summary_hash: __IncompleteArrayField, + + pub(crate) opaque_data_len: u16, + pub(crate) opaque_data: __IncompleteArrayField, + + pub(crate) context: [u8; 8], + pub(crate) signature: __IncompleteArrayField, +} + +impl Validate<&mut Unvalidated>> for &mut ChallengeRsp { + type Err =3D Error; + + fn validate(unvalidated: &mut Unvalidated>) -> Result { + let raw =3D unvalidated.raw_mut(); + if raw.len() < mem::size_of::() { + return Err(EINVAL); + } + + let ptr =3D raw.as_mut_ptr(); + // CAST: `ChallengeRsp` only contains integers and has `repr(C)`. + let ptr =3D ptr.cast::(); + // SAFETY: `ptr` came from a reference and the cast above is valid. + let rsp: &mut ChallengeRsp =3D unsafe { &mut *ptr }; + + // rsp.opaque_data_len =3D rsp.opaque_data_len.to_le(); + + Ok(rsp) + } +} diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 52ad3e98e036..35e4378fb9dc 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include --=20 2.52.0 From nobody Thu Apr 2 15:42:27 2026 Received: from mail-pf1-f182.google.com (mail-pf1-f182.google.com [209.85.210.182]) (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 EF4C634A784 for ; Wed, 11 Feb 2026 03:32:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780765; cv=none; b=m3aJAK4RNLk4tUyooWN+MfMeQBc3o/vpSHH5+kOSCZbUJoO8hpCTEh0Jl5If3TLAXc9fUSupuERgt9HFJFhQRITVtlTu4+mtFQezuCRPu3nix2xL05WB3i7jt0kZILLUWt5SbKnmHieFDfcWCJVy9hjsAIWs3Rob0tKFU2nHxAs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780765; c=relaxed/simple; bh=kxAaH/R88X1dnCESSe1LeIaJnXEvtoow32TWakEjvNk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=PZ6zGN1+ijEX2wk9WYCkcOE5ShYVqrDpLOaXPi6891efVIEKdwFXNzqTsCdob7Dxlb43IPTIHr3PqobecIyoXxttU9sPW5NY38si2XgnzQXwX6rdXZQJEvSrqm2BqahRXG9KUvgOO7EA1hJwkZ2mAWcacmqsfsrDvwfn9bJxEYU= 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=UGomaO7o; arc=none smtp.client-ip=209.85.210.182 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="UGomaO7o" Received: by mail-pf1-f182.google.com with SMTP id d2e1a72fcca58-8249aca0affso180462b3a.3 for ; Tue, 10 Feb 2026 19:32:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780763; x=1771385563; 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=uQmEdlkVMUMCwxAKuTSKI45sx1LLRiTRlNgCvCrwMvE=; b=UGomaO7oVQbLwjxr4hlWHnUKLe8OFZBqhBRAkJIGxpaKWMsVYocc2sNegQk2zuJFuh ker0TZBtHULEw6oAlHL8KD0SYqaQU4BqSPoymOX3GQJ/s/l0SC4GJ0e3nj4zQkuIsO6Y 1nK/IzAMirt3bqeSdzAQvqa3dAN5dNGbpztLMQjqSyiqtEMVkSwo6dysg5uGPfSAkb3X ge10dxxUB2hXGswAYdXONM2p386VQ6X/jcrGzLlRz6VCHLndGlXEQUqyEHsACaHCE15N h6J1SClQHC5zapOYyAaTS8W3uGR4ksK1DqB+ym7T07FxEIATkHnS7AlSRCWfSsEyKMwW wBGg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780763; x=1771385563; 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=uQmEdlkVMUMCwxAKuTSKI45sx1LLRiTRlNgCvCrwMvE=; b=kKdiDq/UlWVdV3++Qo/W01SAb3rNa+3vGBkld92E6eIjKpuYZKNgxgR8SY7GfSMBuy xnVf9kQ4lTX+Ai8gVfWibAKg2gOv/pfWuFZnjlhAcYAnA+LDjhT3bXdMlJ6glWwyMhWo A4PuvEns22hg9mle3E5itZovwKhH+uwkP1GJt5UEXl+nmzDn92CpoQXFy45pLi9fDwqo uG1+p+nB3dxbMfGmJ7HTOf++Ar2ddhcmx1nMXe+zjmRFG0lPpG19cZzAUnbNm+G4lexf Cp/k45CKVM2/8OJ7nkgG+AHWBWrCV3dACneMoc/qvB7UmN5nLTOutm367fNPfZtYsyaq lnlg== X-Forwarded-Encrypted: i=1; AJvYcCUTLvMzAcdYe61HLG7mlT4eRF8YJPLcaY/0IGKhoHDM9W5fU/rprCHFeM96gOF32lFZdGG22slF4ASlNIA=@vger.kernel.org X-Gm-Message-State: AOJu0YwSo15J+LNwwP6o/LemCS3CNvYAnmXPgdDgRK2d9vPbET4QQ4wB ELsdINpt04aGK5QEx9Nv4xtJ8IQSWfotq2VuRg+2volkqd/DLoLPirnY X-Gm-Gg: AZuq6aJxGSiLLwn98Querkb8fUbT8EDYCClCY25TsAmR5sUH/jHhjBwx6bwJ5/3Hp7B tX+TfLnYj2n9xeyaCgnNQ5DK+Odwx+f+F/VujTyR7s81/HouoABBeAclsR2S6A74iLFEcZLBFYk UCcPWbnQ8jRc+2B1p2y53jvlDOmBww9wQcITHsmtGGh4Fwg7TWWl0dHTpBIc1/NRa8Qe0t/9ZdP rNjXGguCRf5KPlKToHjmf3ea8lAD+8XcwxqWiyie6jFXaPm/PcfGJaDwtQlzdenjXJ8HvvaDZUM kA7jfUXtBuwL+HgLiQa5/L/9DvHyS2YcexhwGonpk4BW4LO5njJOJ0hl8haWoziD9XmtyohfwSC oCCES1DT5hHDdQU0nWhE7d6GeyJdhnq6bF6TJupCToD3L9/1ooWLurIjfj45trWNzacbVsq0U6e jqCmjY6ytQ+CYFTWVN+J0brhBMeFtGlHGiBjeGCJpXow== X-Received: by 2002:a05:6a21:b91:b0:37e:4319:d7c8 with SMTP id adf61e73a8af0-3942e6d5e66mr1179185637.75.1770780763333; Tue, 10 Feb 2026 19:32:43 -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.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:32:42 -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 25/27] PCI/CMA: Expose in sysfs whether devices are authenticated Date: Wed, 11 Feb 2026 13:29:32 +1000 Message-ID: <20260211032935.2705841-26-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 From: Lukas Wunner The PCI core has just been amended to authenticate CMA-capable devices on enumeration and store the result in an "authenticated" bit in struct pci_dev->spdm_state. Expose the bit to user space through an eponymous sysfs attribute. Allow user space to trigger reauthentication (e.g. after it has updated the CMA keyring) by writing to the sysfs attribute. Implement the attribute in the SPDM library so that other bus types besides PCI may take advantage of it. They just need to add spdm_attr_group to the attribute groups of their devices and amend the dev_to_spdm_state() helper which retrieves the spdm_state for a given device. The helper may return an ERR_PTR if it couldn't be determined whether SPDM is supported by the device. The sysfs attribute is visible in that case but returns an error on access. This prevents downgrade attacks where an attacker disturbs memory allocation or DOE communication in order to create the appearance that SPDM is unsupported. Subject to further discussion, a future commit might add a user-defined policy to forbid driver binding to devices which failed authentication, similar to the "authorized" attribute for USB. Alternatively, authentication success might be signaled to user space through a uevent, whereupon it may bind a (blacklisted) driver. A uevent signaling authentication failure might similarly cause user space to unbind or outright remove the potentially malicious device. Traffic from devices which failed authentication could also be filtered through ACS I/O Request Blocking Enable (PCIe r6.2 sec 7.7.11.3) or through Link Disable (PCIe r6.2 sec 7.5.3.7). Unlike an IOMMU, that will not only protect the host, but also prevent malicious peer-to-peer traffic to other devices. Signed-off-by: Lukas Wunner [ Changes by AF: - Drop lib/spdm changes and replace with Rust implementation ] Signed-off-by: Alistair Francis --- Documentation/ABI/testing/sysfs-devices-spdm | 31 +++++++ MAINTAINERS | 3 +- drivers/pci/cma.c | 12 ++- drivers/pci/doe.c | 2 + drivers/pci/pci-sysfs.c | 3 + drivers/pci/pci.h | 5 + include/linux/pci.h | 12 +++ lib/rspdm/Makefile | 1 + lib/rspdm/lib.rs | 1 + lib/rspdm/req-sysfs.c | 98 ++++++++++++++++++++ lib/rspdm/spdm.h | 17 ++++ lib/rspdm/sysfs.rs | 38 ++++++++ 12 files changed, 218 insertions(+), 5 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-devices-spdm create mode 100644 lib/rspdm/req-sysfs.c create mode 100644 lib/rspdm/spdm.h create mode 100644 lib/rspdm/sysfs.rs diff --git a/Documentation/ABI/testing/sysfs-devices-spdm b/Documentation/A= BI/testing/sysfs-devices-spdm new file mode 100644 index 000000000000..018acde950ff --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-spdm @@ -0,0 +1,31 @@ +What: /sys/devices/.../authenticated +Date: June 2025 +Contact: Lukas Wunner +Description: + This file contains 1 if the device authenticated successfully + with SPDM (Security Protocol and Data Model). It contains 0 if + the device failed authentication (and may thus be malicious). + + Writing "re" to this file causes reauthentication. + That may be opportune after updating the device keyring. + The device keyring of the PCI bus is named ".cma" + (Component Measurement and Authentication). + + Reauthentication may also be necessary after device identity + has mutated, e.g. after downloading firmware to an FPGA device. + + The file is not visible if authentication is unsupported + by the device. + + If the kernel could not determine whether authentication is + supported because memory was low or communication with the + device was not working, the file is visible but accessing it + fails with error code ENOTTY. + + This prevents downgrade attacks where an attacker consumes + memory or disturbs communication in order to create the + appearance that a device does not support authentication. + + The reason why authentication support could not be determined + is apparent from "dmesg". To re-probe authentication support + of PCI devices, exercise the "remove" and "rescan" attributes. diff --git a/MAINTAINERS b/MAINTAINERS index 58898260fde8..eae4722e40bf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23702,7 +23702,8 @@ 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: drivers/pci/cma.c +F: Documentation/ABI/testing/sysfs-devices-spdm +F: drivers/pci/cma* F: include/linux/spdm.h F: lib/rspdm/ =20 diff --git a/drivers/pci/cma.c b/drivers/pci/cma.c index 8d64008594e2..55b8fe6d3121 100644 --- a/drivers/pci/cma.c +++ b/drivers/pci/cma.c @@ -172,8 +172,10 @@ void pci_cma_init(struct pci_dev *pdev) { struct pci_doe_mb *doe; =20 - if (IS_ERR(pci_cma_keyring)) + if (IS_ERR(pci_cma_keyring)) { + pdev->spdm_state =3D ERR_PTR(-ENOTTY); return; + } =20 if (!pci_is_pcie(pdev)) return; @@ -186,8 +188,10 @@ void pci_cma_init(struct pci_dev *pdev) pdev->spdm_state =3D spdm_create(&pdev->dev, pci_doe_transport, doe, PCI_DOE_MAX_PAYLOAD, pci_cma_keyring, pci_cma_validate); - if (!pdev->spdm_state) + if (!pdev->spdm_state) { + pdev->spdm_state =3D ERR_PTR(-ENOTTY); return; + } =20 /* * Keep spdm_state allocated even if initial authentication fails @@ -205,7 +209,7 @@ void pci_cma_init(struct pci_dev *pdev) */ void pci_cma_reauthenticate(struct pci_dev *pdev) { - if (!pdev->spdm_state) + if (IS_ERR_OR_NULL(pdev->spdm_state)) return; =20 spdm_authenticate(pdev->spdm_state); @@ -213,7 +217,7 @@ void pci_cma_reauthenticate(struct pci_dev *pdev) =20 void pci_cma_destroy(struct pci_dev *pdev) { - if (!pdev->spdm_state) + if (IS_ERR_OR_NULL(pdev->spdm_state)) return; =20 spdm_destroy(pdev->spdm_state); diff --git a/drivers/pci/doe.c b/drivers/pci/doe.c index 344e01496a8e..96ca77ecba90 100644 --- a/drivers/pci/doe.c +++ b/drivers/pci/doe.c @@ -857,6 +857,7 @@ void pci_doe_init(struct pci_dev *pdev) if (IS_ERR(doe_mb)) { pci_err(pdev, "[%x] failed to create mailbox: %ld\n", offset, PTR_ERR(doe_mb)); + pci_cma_disable(pdev); continue; } =20 @@ -865,6 +866,7 @@ void pci_doe_init(struct pci_dev *pdev) pci_err(pdev, "[%x] failed to insert mailbox: %d\n", offset, rc); pci_doe_destroy_mb(doe_mb); + pci_cma_disable(pdev); } } } diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index c2df915ad2d2..b2b2096f8162 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1853,6 +1853,9 @@ const struct attribute_group *pci_dev_attr_groups[] = =3D { #ifdef CONFIG_PCIEASPM &aspm_ctrl_attr_group, #endif +#ifdef CONFIG_PCI_CMA + &spdm_attr_group, +#endif #ifdef CONFIG_PCI_DOE &pci_doe_sysfs_group, #endif diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index f87a4b7c92b8..80bd998e77e4 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -603,10 +603,15 @@ static inline void pci_doe_disconnected(struct pci_de= v *pdev) { } void pci_cma_init(struct pci_dev *pdev); void pci_cma_destroy(struct pci_dev *pdev); void pci_cma_reauthenticate(struct pci_dev *pdev); +static inline void pci_cma_disable(struct pci_dev *pdev) +{ + pdev->spdm_state =3D ERR_PTR(-ENOTTY); +} #else static inline void pci_cma_init(struct pci_dev *pdev) { } static inline void pci_cma_destroy(struct pci_dev *pdev) { } static inline void pci_cma_reauthenticate(struct pci_dev *pdev) { } +static inline void pci_cma_disable(struct pci_dev *pdev) { } #endif =20 #ifdef CONFIG_PCI_NPEM diff --git a/include/linux/pci.h b/include/linux/pci.h index 7384846ade19..44e7bd6f2076 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2827,6 +2827,18 @@ static inline bool pci_is_thunderbolt_attached(struc= t pci_dev *pdev) void pci_uevent_ers(struct pci_dev *pdev, enum pci_ers_result err_type); #endif =20 +#ifdef CONFIG_PCI_CMA +static inline struct spdm_state *pci_dev_to_spdm_state(struct pci_dev *pde= v) +{ + return pdev->spdm_state; +} +#else +static inline struct spdm_state *pci_dev_to_spdm_state(struct pci_dev *pde= v) +{ + return NULL; +} +#endif + #include =20 #define pci_emerg(pdev, fmt, arg...) dev_emerg(&(pdev)->dev, fmt, ##arg) diff --git a/lib/rspdm/Makefile b/lib/rspdm/Makefile index 1f62ee2a882d..f15b1437196b 100644 --- a/lib/rspdm/Makefile +++ b/lib/rspdm/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_RSPDM) +=3D spdm.o =20 spdm-y :=3D lib.o +spdm-$(CONFIG_SYSFS) +=3D req-sysfs.o diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs index 05fa471bb1e2..c0e3e039c14f 100644 --- a/lib/rspdm/lib.rs +++ b/lib/rspdm/lib.rs @@ -24,6 +24,7 @@ =20 mod consts; mod state; +pub mod sysfs; mod validator; =20 /// spdm_create() - Allocate SPDM session diff --git a/lib/rspdm/req-sysfs.c b/lib/rspdm/req-sysfs.c new file mode 100644 index 000000000000..7971be291627 --- /dev/null +++ b/lib/rspdm/req-sysfs.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Rust implementation of the DMTF Security Protocol and Data Model (SPDM) + * https://www.dmtf.org/dsp/DSP0274 + * + * Requester role: sysfs interface + * + * Copyright (C) 2023-24 Intel Corporation + * Copyright (C) 2024 Western Digital + */ + +#include +#include "spdm.h" + +int rust_authenticated_show(void *spdm_state, char *buf); + +/** + * dev_to_spdm_state() - Retrieve SPDM session state for given device + * + * @dev: Responder device + * + * Returns a pointer to the device's SPDM session state, + * %NULL if the device doesn't have one or + * %ERR_PTR if it couldn't be determined whether SPDM is supported. + * + * In the %ERR_PTR case, attributes are visible but return an error on acc= ess. + * This prevents downgrade attacks where an attacker disturbs memory alloc= ation + * or communication with the device in order to create the appearance that= SPDM + * is unsupported. E.g. with PCI devices, the attacker may foil CMA or DOE + * initialization by simply hogging memory. + */ +static void *dev_to_spdm_state(struct device *dev) +{ + if (dev_is_pci(dev)) + return pci_dev_to_spdm_state(to_pci_dev(dev)); + + /* Insert mappers for further bus types here. */ + + return NULL; +} + +/* authenticated attribute */ + +static umode_t spdm_attrs_are_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev =3D kobj_to_dev(kobj); + void *spdm_state =3D dev_to_spdm_state(dev); + + if (IS_ERR_OR_NULL(spdm_state)) + return SYSFS_GROUP_INVISIBLE; + + return a->mode; +} + +static ssize_t authenticated_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + void *spdm_state =3D dev_to_spdm_state(dev); + int rc; + + if (IS_ERR_OR_NULL(spdm_state)) + return PTR_ERR(spdm_state); + + if (sysfs_streq(buf, "re")) { + rc =3D spdm_chall(spdm_state); + if (rc) + return rc; + } else { + return -EINVAL; + } + + return count; +} + +static ssize_t authenticated_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + void *spdm_state =3D dev_to_spdm_state(dev); + + if (IS_ERR_OR_NULL(spdm_state)) + return PTR_ERR(spdm_state); + + return rust_authenticated_show(spdm_state, buf); +} +static DEVICE_ATTR_RW(authenticated); + +static struct attribute *spdm_attrs[] =3D { + &dev_attr_authenticated.attr, + NULL +}; + +const struct attribute_group spdm_attr_group =3D { + .attrs =3D spdm_attrs, + .is_visible =3D spdm_attrs_are_visible, +}; diff --git a/lib/rspdm/spdm.h b/lib/rspdm/spdm.h new file mode 100644 index 000000000000..43ef56a073c0 --- /dev/null +++ b/lib/rspdm/spdm.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * DMTF Security Protocol and Data Model (SPDM) + * https://www.dmtf.org/dsp/DSP0274 + * + * Copyright (C) 2021-22 Huawei + * Jonathan Cameron + * + * Copyright (C) 2022-25 Intel Corporation + */ + +#ifndef _LIB_SPDM_H_ +#define _LIB_SPDM_H_ + +int spdm_chall(struct spdm_state *spdm_state); + +#endif /* _LIB_SPDM_H_ */ diff --git a/lib/rspdm/sysfs.rs b/lib/rspdm/sysfs.rs new file mode 100644 index 000000000000..d0e7f6b3de40 --- /dev/null +++ b/lib/rspdm/sysfs.rs @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (C) 2024 Western Digital + +//! Rust sysfs helper functions +//! +//! Rust implementation of the DMTF Security Protocol and Data Model (SPDM) +//! + +use crate::SpdmState; +use kernel::prelude::*; +use kernel::{bindings, str::CString}; + +/// Helper function for the sysfs `authenticated_show()`. +#[no_mangle] +pub extern "C" fn rust_authenticated_show(spdm_state: *mut SpdmState, buf:= *mut u8) -> isize { + // SAFETY: The opaque pointer will be directly from the `spdm_create()` + // function, so we can safely reconstruct it. + let state =3D unsafe { KBox::from_raw(spdm_state) }; + + let fmt =3D match CString::try_from_fmt(fmt!("{}\n", state.authenticat= ed)) { + Ok(f) =3D> f, + Err(_e) =3D> return 0, + }; + + // SAFETY: Calling a kernel C function with valid arguments + unsafe { bindings::sysfs_emit(buf, fmt.as_char_ptr()) as isize } +} + +/// Helper function to trigger a SPDM challenge +#[no_mangle] +pub unsafe extern "C" fn spdm_chall(state: &'static mut SpdmState) -> c_in= t { + if let Err(e) =3D state.challenge(state.provisioned_slots.trailing_zer= os() as u8, false) { + return e.to_errno() as c_int; + } + + 0 +} --=20 2.52.0 From nobody Thu Apr 2 15:42:27 2026 Received: from mail-pf1-f179.google.com (mail-pf1-f179.google.com [209.85.210.179]) (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 1175334A784 for ; Wed, 11 Feb 2026 03:32:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.179 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780772; cv=none; b=S3zJOSn5d+7BpoO4DjwbJIC75onVnA6zkMVwSFSAfmOx3IG1DZZpdPmdqmg59bCnm/jgpws5m1RU+18b+iWTLqmCpT/SDgQ9L3c5p961MUSiTCyuaaCohhu35ccnNXSywDrzrjZ54dO6S/sCqfKSkydKzxIoFICFvw++zbTi4js= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780772; c=relaxed/simple; bh=vSBHsAMqmP0mTWh5XLC6s4jgQYugzTdX4cOLY716pds=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Qa+oWy0tPH4i0TOnq3xHjtXMx88uiZIKOSbnPCnSjfhLxW/38NRBHYvUZ7Yl8L/QsBbzRu24dy40H6ha+4V5AE0R8kI2cSEka0uxNtLkm+0t6mx/uo+yDDtuLF/6LMWgga2QkqephrmkA4abK1alxxcxL3/f4S7T6fMQXEGEzqE= 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=Zox2dZZa; arc=none smtp.client-ip=209.85.210.179 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="Zox2dZZa" Received: by mail-pf1-f179.google.com with SMTP id d2e1a72fcca58-81f4dfa82edso2335416b3a.0 for ; Tue, 10 Feb 2026 19:32:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780770; x=1771385570; 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=dF6bX/QVONfWDQrBCiwnvcbeLdCbDl1IQ4EC8a9rjVk=; b=Zox2dZZaeOEmRU3KE/QgDrV8zctrDPtHoKmPoXeYbu8I+pDBVaZ2Rwjsxmlcm4JNmQ 33Lm13+sbsQ940Ul/9B3K5eilSYAz5awblnZHWIeIP+SDidJ9NWUB8I+geJmpdXmuEpX CD7517TXo/+KS9H9Zs5vKiUWvR3V0NjdWBYbI/Qf3JSVdoTK9/EXIgxtR688mxpssVOq mcgl8FLgt4A2vcz5oN17PIYVv/3tFCu0d3EM+FVdFcDGmjXAW3IVKRuzt0c53RFydqFA 91nMutKIqVSASx/S0ku4ISfmrSxa3gmYFD2wReracFk62e89Meuhdd9YYtV/aJzOt6CN lFAQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780770; x=1771385570; 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=dF6bX/QVONfWDQrBCiwnvcbeLdCbDl1IQ4EC8a9rjVk=; b=nxQBwSc1jHClgGYP2YCPE9XZegcBBaKiTULTkLMRzA0UNKY5Lk/HCQidhgPlPTHndF 8ZV4pKHLaq11C0j+FjlcNrf1m4QGtZBJY5hcEuCr29rUewREz27sNPxMT94ILd36Br6E w3CKItc9MNNHBCC14epmmZK/pPB9wnQMECHbgEXqkpexcG2T6jF6+mkCldkgirW4dtPb XIm1CrAhrif4CyTMVI9JXqD6b5nwYKPo3XCh6QT8NAhYvkDoGQe96UMj7l8tWkaL5J5F TFrDzoE4bx5vF8f2pKIsHL6GPA0jCJ7EtCia+87JnDJTiNoC/taYbRe7uKHVXmA4Y5Om 0Uig== X-Forwarded-Encrypted: i=1; AJvYcCXxqqGtUrdWlY2s7AxynAiUwjy0n+7ARA2ZWQnbk2vNDiwz6sL/vjEU1rYH0iJ5oXOCDW5Ua5iQ6Ipo+3w=@vger.kernel.org X-Gm-Message-State: AOJu0Yy7545Ea7V/JKdjBghkCq5dKsm3CRIf7ksFPY+uZ/2ix3ERz3NV zwcvFZ4I/xP5nH2V7Z1wDoRIHwdEFUIFR/0EVUmuPxe4enB5A3Z5uGji X-Gm-Gg: AZuq6aJzdXAlUg7HEbeZCgH0jJboVQKznLZXP3gHjqVT1NU4kRmg4sxpMgwn4PueGx5 IjSDkCZq9FKD2oHU1xiRRuL/pdVTz+tpBXNHSyzlHB2ckjDxKTx+zlzMn/x20/NgSY5FPvm1e1Z TSi9QGpzSuGoKgdh1xC4HPz18L0CFEjXccq+1iUI6Q7kQZiTIiBfDOAXJpWiLQK5EOgxX98kvde tlXGSuC3s4mitFZnxQBXr/wsDFLp8zAd9luSNnCylhLMtSh77U9Amx4m/0Rxp3s66x4CaF5dWdY BVc0QJBwng0JvAnxBAYUAY/jbDWDNDW9Va+qCb1izHbZb7PtPoizOdihVEbNwy4nGha3s7XZLnG gwg5uhHe0IbhguOY9W+IRYiVImvForz/sQKb1cgvFQUgeUng7tV03r2SnI3bQSsWT5wjtGDYDDj 3/wk/wwY6FuTbr9kkkqshM/Jrgsm+ABcFddVR7P5hZiw== X-Received: by 2002:a05:6a20:9f86:b0:35d:d477:a7d6 with SMTP id adf61e73a8af0-3942e35f73cmr1201854637.13.1770780770454; Tue, 10 Feb 2026 19:32:50 -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.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:32:49 -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 26/27] rust: add bindings for hash_info Date: Wed, 11 Feb 2026 13:29:33 +1000 Message-ID: <20260211032935.2705841-27-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 C code can get the hash_digest_size by indexing into the global hash_digest_size array. We can't do that in the Rust code, so let's add a Rust helper function to allow us to get the hash_digest_size. Signed-off-by: Alistair Francis --- rust/helpers/hash_info.c | 8 ++++++++ rust/helpers/helpers.c | 1 + 2 files changed, 9 insertions(+) create mode 100644 rust/helpers/hash_info.c diff --git a/rust/helpers/hash_info.c b/rust/helpers/hash_info.c new file mode 100644 index 000000000000..d2cbc08b57d1 --- /dev/null +++ b/rust/helpers/hash_info.c @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +int rust_helper_get_hash_digest_size(uint32_t offset) +{ + return hash_digest_size[offset]; +} diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 4b08b4f1d3a3..34db9eb5e43e 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -31,6 +31,7 @@ #include "drm.c" #include "err.c" #include "hash.c" +#include "hash_info.c" #include "irq.c" #include "fs.c" #include "io.c" --=20 2.52.0 From nobody Thu Apr 2 15:42:27 2026 Received: from mail-pg1-f174.google.com (mail-pg1-f174.google.com [209.85.215.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 02B8F34AAE6 for ; Wed, 11 Feb 2026 03:32:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780782; cv=none; b=hjUxsMFingFKgmftl7HoDkaPtO8s6rlS/4gVh9JU48zp+n32pDoeWoEbb6xvL1G78MpX3f0Rh2GMFzrapejiFnEb6KV+AFvD2UYB8NI1YUGmU4bf+I9r7TVM+KCTJnsQO0939YuPGl77TyFQeQkDJAcmo+IsCbHhis45SaEy1YU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780782; c=relaxed/simple; bh=LeL9rMOoVhCYJDLJkyeUhx+7gewNtABy5Yqg9bwzEIw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=YTU/7YLp/gbjyTZcz6CIbr3UvVciwByIIhekf1QaZcd+CRwpkRJKeQbkpStzfHT0VDqsmSZjAt8Rl7qhhOcafcgOPHSfnyOP+tqhv4gDIf6L1FlM4Y0aYwIMWZ67oxXdBz7l/wcldXb0MJXbOAm3KPx9mdxYEgln3m+frLacb+4= 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=FTShwKvq; arc=none smtp.client-ip=209.85.215.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="FTShwKvq" Received: by mail-pg1-f174.google.com with SMTP id 41be03b00d2f7-c6c444e89bcso583487a12.2 for ; Tue, 10 Feb 2026 19:32:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780778; x=1771385578; 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=94DHUwq8TzwnOUxAyFNL8ck6qlmcEGwk0NGsy0qgL20=; b=FTShwKvqwIrPKd6U3EjJxwAF008fBB6OJUISw15YoqS5H+/aMBuM6ZFnDDYptkeMdO INF9H1Fne2FqDBnbDdby+CFW6IaVJndLHMPgL8CfafNW5xM9AjuSoLi1+tFZ1FTqOxMx /cZMy4PE1hK+SHpeapjQuauBmIr6OdW6uI87F6iDbBhpolhhFrrCnwOJPd1p+DzYxjVz Rnq6kQ6FKUUF6s5co963JsugZZoPh7AKShC9L8tInB9utgr5oLbcCLpIVRPxsCwl6V8v OfzIYcqvenjpRdCijMlOXXGwJCPECTxQlZSN7gYNxYuftZejEPHWNErZ1FUfe8sjjI2G hCNw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780778; x=1771385578; 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=94DHUwq8TzwnOUxAyFNL8ck6qlmcEGwk0NGsy0qgL20=; b=eHSiTRSdjEGQQCdQGDlAgy8Xa02/1gT0N1bpCUZfnigtFn36MMxVhiPdX2feRpbXU1 9YqTBO3p3/kO9gB0XcHBAkjVJlqquvunBQk58lPRTon/0bVZTF2iSd6fVd+15Y0PSNYQ W+5eI4sXY34YyC0w/Kh1kgvu8+zJWebrn3tV4196jvR61h1hDuGBZDoaYlIFCOjSa57l mRQNwiWTuHjPGjMFxR114RAUco9hc5JCJBEVUd+TYteBv9yCe7kv4wIhV/P8Qy/X+cM9 uG4bzI/wwl4KLcrVQFKv60+sryxgTBgTmRXH0JOGg2yL62jgv4FRU8Wi0nYYymHCohFI HD4w== X-Forwarded-Encrypted: i=1; AJvYcCVRyLYUNkqFt4iNEAv2BRqtHpynD3orJp3+xuiUGuykWvDd6CBE30gn/RZM/N0OKii1pScRw8lr843EB5s=@vger.kernel.org X-Gm-Message-State: AOJu0YzOa2zH1RO/C93/ohhpsnl8UREQTrZ9CUhOpphH2y8ZVQvj3NSh oFVZIK9IJeVrjxDWjHEOeSS6lTT1yrEoHJZC3JVYkBdUb+Ai631JHzYL X-Gm-Gg: AZuq6aL7f+U0UBMJ29jcrzxcyQc2muBd2MfJkFtO8AIylRxDMoYoDJF+1D5go6/sCG8 4Wp+k2RSSEt4OpLKPNx5ZDOjjZUtBnxDURjxPTsDfMxv826PA4OzKwy7F0KymYqR4WnUpX2I672 zO4NSj5APNq6vyieMSE+tz8CYvHpPkS/HynuJQcDAKFfXUxMiw3GP8er2/THTlW2tT8fn81S/Ry CQb+34+qBmZg2PXG7XIA6n9pdU5AiF9vF8NlV0Bp0k5/pohU1u1PH1Kz29jXQqYUBhiO4CaAmPM KDfFYyX/mLVtDhTJchjxz6jQ1K5k+204wOysCppdea/8BX2ikaZFes4wJnnHK/Aw8piBlRjr+JF XR2JUCswniArcv1s1H5UXwQJP07247d1QeyiHVKjQBOfXdags2CMDtpDdASXU4xkIg4ZhwB5eLR Ee7D+D4uiiBl2TLkX5K4LrVHBhVUbNGy2b2MSTsrya0w== X-Received: by 2002:a05:6300:2206:b0:366:14af:9bd2 with SMTP id adf61e73a8af0-393ad3a27aamr17344339637.72.1770780778321; Tue, 10 Feb 2026 19:32:58 -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.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:32:57 -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, James Bottomley , =?UTF-8?q?J=C3=A9r=C3=B4me=20Glisse?= , Jason Gunthorpe , Alistair Francis Subject: [RFC v3 27/27] rspdm: Multicast received signatures via netlink Date: Wed, 11 Feb 2026 13:29:34 +1000 Message-ID: <20260211032935.2705841-28-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-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Lukas Wunner This is based on Lukas's patch from [1]. This exposes all of the SPDM information to userspace via netlink. This includes the certificate chain and communication transcript. 1: https://github.com/l1k/linux/commit/fe90b5700ee9bc595a21c030192eac4060ea= eae1 Signed-off-by: Lukas Wunner Cc: James Bottomley Cc: J=C3=A9r=C3=B4me Glisse Cc: Jason Gunthorpe [ Change by AF: - Fixup yaml spec issues - Include certificate chain - Port to support Rust SPDM ] Signed-off-by: Alistair Francis --- Documentation/netlink/specs/spdm.yaml | 136 ++++++++++++++++++ include/uapi/linux/spdm_netlink.h | 49 +++++++ lib/rspdm/Makefile | 1 + lib/rspdm/netlink-autogen.c | 33 +++++ lib/rspdm/netlink-autogen.h | 22 +++ lib/rspdm/req-netlink.c | 197 ++++++++++++++++++++++++++ lib/rspdm/spdm.h | 28 ++++ lib/rspdm/state.rs | 52 +++++++ rust/bindings/bindings_helper.h | 4 + 9 files changed, 522 insertions(+) create mode 100644 Documentation/netlink/specs/spdm.yaml create mode 100644 include/uapi/linux/spdm_netlink.h create mode 100644 lib/rspdm/netlink-autogen.c create mode 100644 lib/rspdm/netlink-autogen.h create mode 100644 lib/rspdm/req-netlink.c diff --git a/Documentation/netlink/specs/spdm.yaml b/Documentation/netlink/= specs/spdm.yaml new file mode 100644 index 000000000000..1eb349bdc0d1 --- /dev/null +++ b/Documentation/netlink/specs/spdm.yaml @@ -0,0 +1,136 @@ +# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Cla= use) + +name: spdm + +doc: | + DMTF Security Protocol and Data Model (SPDM) + https://www.dmtf.org/dsp/DSP0274 + +protocol: genetlink + +uapi-header: linux/spdm_netlink.h + +definitions: + - + type: enum + name: spdm-reqrsp-code + doc: SPDM request or response code of a signed message (SPDM 1.0.0 tab= le 4) + entries: + - + name: challenge-auth + - + name: endpoint-info + - + name: measurements + - + name: key-exchange-rsp + - + name: finish + - + type: enum + name: hash-algo + doc: SPDM-supported hash algorithm (SPDM 1.0.0 table 13) + entries: + - + name: sha256 + - + name: sha384 + - + name: sha512 + - + name: sha3-256 + - + name: sha3-384 + - + name: sha3-512 + header: uapi/linux/hash_info.h + +attribute-sets: + - + name: sig + doc: | + Signature received from a device, together with all ancillary data + needed for re-verification. + + Meant for remote attestation services which do not trust the kernel + to have verified the signature correctly or which want to apply + policy constraints of their own. + attributes: + - + name: device + doc: | + Path under sysfs of the device generating the signature. + type: string + - + name: rsp-code + doc: | + SPDM response code of the message containing the signature, + to determine what kind of event caused signature generation. + Equivalent to the "context" string of SPDM 1.2.0 sec 15, + but represented numerically for easier parsing. + type: u8 + enum: spdm-reqrsp-code + - + name: slot + doc: | + Certificate slot used for signature generation. Note that + if the slot has since been provisioned with a different + certificate chain, re-verification of the signature will fail. + type: u8 + - + name: hash-algo + doc: | + Hash algorithm used for signature generation. + type: u16 + enum: hash-algo + - + name: sig-offset + doc: | + Offset of signature in @transcript. The signature is located + at the end of @transcript, hence its size equals @transcript + size minus this offset. + type: u32 + - + name: req-nonce-offset + doc: | + Offset of 32 byte nonce chosen by requester in @transcript. + Allows remote attestation services to verify freshness + (uniqueness) and entropy adequacy of the nonce. + type: u32 + - + name: rsp-nonce-offset + doc: | + Offset of 32 byte nonce chosen by responder in @transcript. + Allows remote attestation services to verify freshness + (uniqueness) and entropy adequacy of the nonce. + type: u32 + - + name: combined-spdm-prefix + doc: | + Only included in the message with SPDM version 1.2.0 or newer. + type: binary + checks: + exact-len: 100 + - + name: certificate-chain + type: binary + - + name: transcript + type: binary + multi-attr: true + +operations: + list: + - + name: sig + doc: Signature event + attribute-set: sig + event: + attributes: + - sig + mcgrp: sig + +mcast-groups: + list: + - + name: sig diff --git a/include/uapi/linux/spdm_netlink.h b/include/uapi/linux/spdm_ne= tlink.h new file mode 100644 index 000000000000..a7fa183757db --- /dev/null +++ b/include/uapi/linux/spdm_netlink.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Cl= ause) */ +/* Do not edit directly, auto-generated from: */ +/* Documentation/netlink/specs/spdm.yaml */ +/* YNL-GEN uapi header */ +/* To regenerate run: tools/net/ynl/ynl-regen.sh */ + +#ifndef _UAPI_LINUX_SPDM_NETLINK_H +#define _UAPI_LINUX_SPDM_NETLINK_H + +#define SPDM_FAMILY_NAME "spdm" +#define SPDM_FAMILY_VERSION 1 + +/* + * SPDM request or response code of a signed message (SPDM 1.0.0 table 4) + */ +enum spdm_spdm_reqrsp_code { + SPDM_SPDM_REQRSP_CODE_CHALLENGE_AUTH, + SPDM_SPDM_REQRSP_CODE_ENDPOINT_INFO, + SPDM_SPDM_REQRSP_CODE_MEASUREMENTS, + SPDM_SPDM_REQRSP_CODE_KEY_EXCHANGE_RSP, + SPDM_SPDM_REQRSP_CODE_FINISH, +}; + +enum { + SPDM_A_SIG_DEVICE =3D 1, + SPDM_A_SIG_RSP_CODE, + SPDM_A_SIG_SLOT, + SPDM_A_SIG_HASH_ALGO, + SPDM_A_SIG_SIG_OFFSET, + SPDM_A_SIG_REQ_NONCE_OFFSET, + SPDM_A_SIG_RSP_NONCE_OFFSET, + SPDM_A_SIG_COMBINED_SPDM_PREFIX, + SPDM_A_SIG_CERTIFICATE_CHAIN, + SPDM_A_SIG_TRANSCRIPT, + + __SPDM_A_SIG_MAX, + SPDM_A_SIG_MAX =3D (__SPDM_A_SIG_MAX - 1) +}; + +enum { + SPDM_CMD_SIG =3D 1, + + __SPDM_CMD_MAX, + SPDM_CMD_MAX =3D (__SPDM_CMD_MAX - 1) +}; + +#define SPDM_MCGRP_SIG "sig" + +#endif /* _UAPI_LINUX_SPDM_NETLINK_H */ diff --git a/lib/rspdm/Makefile b/lib/rspdm/Makefile index f15b1437196b..2f29d0a62c1e 100644 --- a/lib/rspdm/Makefile +++ b/lib/rspdm/Makefile @@ -8,4 +8,5 @@ obj-$(CONFIG_RSPDM) +=3D spdm.o =20 spdm-y :=3D lib.o +spdm-$(CONFIG_NET) +=3D req-netlink.o netlink-autogen.o spdm-$(CONFIG_SYSFS) +=3D req-sysfs.o diff --git a/lib/rspdm/netlink-autogen.c b/lib/rspdm/netlink-autogen.c new file mode 100644 index 000000000000..4dc950133514 --- /dev/null +++ b/lib/rspdm/netlink-autogen.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Cl= ause) +/* Do not edit directly, auto-generated from: */ +/* Documentation/netlink/specs/spdm.yaml */ +/* YNL-GEN kernel source */ +/* To regenerate run: tools/net/ynl/ynl-regen.sh */ + +#include +#include + +#include "netlink-autogen.h" + +#include +#include + +/* Ops table for spdm */ +static const struct genl_split_ops spdm_nl_ops[] =3D { +}; + +static const struct genl_multicast_group spdm_nl_mcgrps[] =3D { + [SPDM_NLGRP_SIG] =3D { "sig", }, +}; + +struct genl_family spdm_nl_family __ro_after_init =3D { + .name =3D SPDM_FAMILY_NAME, + .version =3D SPDM_FAMILY_VERSION, + .netnsok =3D true, + .parallel_ops =3D true, + .module =3D THIS_MODULE, + .split_ops =3D spdm_nl_ops, + .n_split_ops =3D ARRAY_SIZE(spdm_nl_ops), + .mcgrps =3D spdm_nl_mcgrps, + .n_mcgrps =3D ARRAY_SIZE(spdm_nl_mcgrps), +}; diff --git a/lib/rspdm/netlink-autogen.h b/lib/rspdm/netlink-autogen.h new file mode 100644 index 000000000000..2797d194604f --- /dev/null +++ b/lib/rspdm/netlink-autogen.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Cl= ause) */ +/* Do not edit directly, auto-generated from: */ +/* Documentation/netlink/specs/spdm.yaml */ +/* YNL-GEN kernel header */ +/* To regenerate run: tools/net/ynl/ynl-regen.sh */ + +#ifndef _LINUX_SPDM_GEN_H +#define _LINUX_SPDM_GEN_H + +#include +#include + +#include +#include + +enum { + SPDM_NLGRP_SIG, +}; + +extern struct genl_family spdm_nl_family; + +#endif /* _LINUX_SPDM_GEN_H */ diff --git a/lib/rspdm/req-netlink.c b/lib/rspdm/req-netlink.c new file mode 100644 index 000000000000..65db5ec6a16c --- /dev/null +++ b/lib/rspdm/req-netlink.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DMTF Security Protocol and Data Model (SPDM) + * https://www.dmtf.org/dsp/DSP0274 + * + * Requester role: netlink interface + * + * Copyright (C) 2025 Intel Corporation + * Copyright (C) 2026 WD Corporation + */ + +#include "netlink-autogen.h" + +#include +#include +#include + +#include + +#include "spdm.h" + +#define SPDM_NONCE_SZ 32 /* SPDM 1.0.0 table 20 */ +#define SPDM_PREFIX_SZ 64 /* SPDM 1.2.0 margin no 803 */ +#define SPDM_COMBINED_PREFIX_SZ 100 /* SPDM 1.2.0 margin no 806 */ +#define SPDM_MAX_OPAQUE_DATA 1024 /* SPDM 1.0.0 table 21 */ + +static void spdm_create_combined_prefix(u8 version, const char *spdm_conte= xt, + void *buf) +{ + u8 major =3D FIELD_GET(0xf0, version); + u8 minor =3D FIELD_GET(0x0f, version); + size_t len =3D strlen(spdm_context); + int rc, zero_pad; + + rc =3D snprintf(buf, SPDM_PREFIX_SZ + 1, + "dmtf-spdm-v%hhx.%hhx.*dmtf-spdm-v%hhx.%hhx.*dmtf-spdm-v%hhx.%hhx.= *dmtf-spdm-v%hhx.%hhx.*", + major, minor, major, minor, major, minor, major, minor); + WARN_ON(rc !=3D SPDM_PREFIX_SZ); + + zero_pad =3D SPDM_COMBINED_PREFIX_SZ - SPDM_PREFIX_SZ - 1 - len; + WARN_ON(zero_pad < 0); + + memset(buf + SPDM_PREFIX_SZ + 1, 0, zero_pad); + memcpy(buf + SPDM_PREFIX_SZ + 1 + zero_pad, spdm_context, len); +} + +int spdm_netlink_sig_event(struct device *dev, + u8 version, + const void *transcript, + size_t transcript_len, + const void *cert_chain, + size_t cert_chain_len, + enum hash_algo base_hash_alg, + size_t sig_len, + int rsp_code, u8 slot, + size_t req_nonce_off, size_t rsp_nonce_off, + const char *spdm_context) +{ + unsigned int seq, msg_sz, nr_msgs, nr_pages, nr_frags; + struct sk_buff *msg; + struct nlattr *nla; + void *hdr; + const void *ptr; + int rc, i; + + if (!genl_has_listeners(&spdm_nl_family, &init_net, SPDM_NLGRP_SIG)) + return 0; + + char *devpath __free(kfree) =3D kobject_get_path(&dev->kobj, + GFP_KERNEL); + if (!devpath) + return -ENOMEM; + + nr_pages =3D transcript_len / PAGE_SIZE; + nr_msgs =3D DIV_ROUND_UP(nr_pages, MAX_SKB_FRAGS); + + /* Calculate exact size to avoid reallocation by netlink_trim() */ + msg_sz =3D nlmsg_total_size(genlmsg_msg_size( + nla_total_size(strlen(devpath)) + + nla_total_size(sizeof(u8)) + + nla_total_size(sizeof(u8)) + + nla_total_size(sizeof(u16)) + + nla_total_size(sizeof(u32)) + + nla_total_size(sizeof(u32)) + + nla_total_size(sizeof(u32)) + + nla_total_size(SPDM_COMBINED_PREFIX_SZ) + + nla_total_size(cert_chain_len) + + nla_total_size(0))); + + msg =3D genlmsg_new(msg_sz, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr =3D genlmsg_put(msg, 0, 0, &spdm_nl_family, + nr_msgs > 1 ? NLM_F_MULTI : 0, SPDM_CMD_SIG); + if (!hdr) { + rc =3D -EMSGSIZE; + goto err_free_msg; + } + + if (nla_put_string(msg, SPDM_A_SIG_DEVICE, devpath) || + nla_put_u8(msg, SPDM_A_SIG_RSP_CODE, rsp_code) || + nla_put_u8(msg, SPDM_A_SIG_SLOT, slot) || + nla_put_u16(msg, SPDM_A_SIG_HASH_ALGO, + base_hash_alg) || + nla_put_u32(msg, SPDM_A_SIG_SIG_OFFSET, + transcript_len - sig_len) || + nla_put_u32(msg, SPDM_A_SIG_REQ_NONCE_OFFSET, req_nonce_off) || + nla_put_u32(msg, SPDM_A_SIG_RSP_NONCE_OFFSET, rsp_nonce_off)) { + rc =3D -EMSGSIZE; + goto err_cancel_msg; + } + + if (version >=3D 0x12) { + nla =3D nla_reserve(msg, SPDM_A_SIG_COMBINED_SPDM_PREFIX, + SPDM_COMBINED_PREFIX_SZ); + if (!nla) { + rc =3D -EMSGSIZE; + goto err_cancel_msg; + } + + spdm_create_combined_prefix(version, spdm_context, + nla_data(nla)); + } + + if (cert_chain_len >=3D 0) + nla_put(msg, SPDM_A_SIG_CERTIFICATE_CHAIN, cert_chain_len, cert_chain); + + ptr =3D transcript; + + /* Loop over Netlink messages - break condition is in loop body */ + for (seq =3D 1; ; seq++) { + nla =3D nla_reserve(msg, SPDM_A_SIG_TRANSCRIPT, 0); + if (!nla) { + rc =3D -EMSGSIZE; + goto err_cancel_msg; + } + + nr_frags =3D min(nr_pages, MAX_SKB_FRAGS); + nla->nla_len =3D nr_frags * PAGE_SIZE; + nr_pages -=3D nr_frags; + + /* Loop over fragments of this Netlink message */ + for (i =3D 0; i < nr_frags; i++) { + struct page *page =3D vmalloc_to_page(ptr); + size_t sz =3D min(transcript_len, PAGE_SIZE); + + skb_add_rx_frag(msg, i, page, 0, sz, sz); + ptr +=3D PAGE_SIZE; + transcript_len -=3D PAGE_SIZE; + } + + genlmsg_end(msg, hdr); + rc =3D genlmsg_multicast(&spdm_nl_family, msg, 0, + SPDM_NLGRP_SIG, GFP_KERNEL); + if (rc) + return rc; + + if (nr_pages =3D=3D 0) /* End of loop - entire transcript sent */ + break; + + /* Start new message for remainder of transcript */ + msg_sz =3D nlmsg_total_size(genlmsg_msg_size(nla_total_size(0))); + + msg =3D genlmsg_new(msg_sz, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr =3D genlmsg_put(msg, 0, seq, &spdm_nl_family, + NLM_F_MULTI, SPDM_CMD_SIG); + if (!hdr) { + rc =3D -EMSGSIZE; + goto err_free_msg; + } + } + + return 0; + +err_cancel_msg: + nlmsg_cancel(msg, hdr); +err_free_msg: + nlmsg_free(msg); + return rc; +} + +static int __init spdm_netlink_init(void) +{ + return genl_register_family(&spdm_nl_family); +} + +static void __exit spdm_netlink_exit(void) +{ + genl_unregister_family(&spdm_nl_family); +} + +arch_initcall(spdm_netlink_init); +module_exit(spdm_netlink_exit); diff --git a/lib/rspdm/spdm.h b/lib/rspdm/spdm.h index 43ef56a073c0..36bc1b47a796 100644 --- a/lib/rspdm/spdm.h +++ b/lib/rspdm/spdm.h @@ -12,6 +12,34 @@ #ifndef _LIB_SPDM_H_ #define _LIB_SPDM_H_ =20 +#include + +#ifdef CONFIG_NET +int spdm_netlink_sig_event(struct device *dev, + u8 version, + const void *transcript, + size_t transcript_len, + const void *cert_chain, + size_t cert_chain_len, + enum hash_algo base_hash_alg, + size_t sig_len, + int rsp_code, u8 slot, + size_t req_nonce_off, size_t rsp_nonce_off, + const char *spdm_context); +#else +static inline int spdm_netlink_sig_event(struct device *dev, + u8 version, + const void *transcript, + size_t transcript_len, + const void *cert_chain, + size_t cert_chain_len, + enum hash_algo base_hash_alg, + size_t sig_len, + int rsp_code, u8 slot, + size_t req_nonce_off, size_t rsp_nonce_off, + const char *spdm_context) { return 0; } +#endif + int spdm_chall(struct spdm_state *spdm_state); =20 #endif /* _LIB_SPDM_H_ */ diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs index a4d803af48fe..8b18e415d4d5 100644 --- a/lib/rspdm/state.rs +++ b/lib/rspdm/state.rs @@ -13,6 +13,7 @@ use kernel::{ bindings, error::{code::EINVAL, from_err_ptr, to_result, Error}, + page::PAGE_SIZE, str::CStr, str::CString, validate::Untrusted, @@ -1036,6 +1037,57 @@ pub(crate) fn challenge(&mut self, slot: u8, verify:= bool) -> Result<(), Error> }; } =20 + let spdm_context =3D b"responder-challenge_auth signing\0"; + + let hash_digest_size =3D if self.base_hash_alg < bindings::hash_al= go_HASH_ALGO__LAST { + // SAFETY: `base_hash_alg` is a valid offset into `hash_digest= _size` + (unsafe { bindings::get_hash_digest_size(self.base_hash_alg) }= ) as usize + } else { + to_result(-(bindings::EIO as i32))?; + 0 + }; + + let req_nonce_off =3D self.transcript.len() + core::mem::offset_of= !(ChallengeReq, nonce); + let rsp_nonce_off =3D + self.transcript.len() + core::mem::size_of::() += hash_digest_size; + + // This is the actual transcript length + let transcript_len =3D self.transcript.len(); + + // This is how much extra capacity we need to page align the trans= cript buffer + let extra_cap =3D PAGE_SIZE - transcript_len.rem_euclid(PAGE_SIZE); + // Ensure we have the capacity + self.transcript.reserve(extra_cap, GFP_KERNEL)?; + + // We know the buffer is this long and this value will be PAGE_SIZ= E aligned + let transcript_buf_len =3D transcript_len + extra_cap; + + let cert_chain_len =3D self.certs[slot as usize].len(); + + let cert_chain =3D if cert_chain_len > 0 { + self.certs[slot as usize].as_ptr() as *const c_void + } else { + core::ptr::null_mut() + }; + + unsafe { + bindings::spdm_netlink_sig_event( + self.dev, + self.version, + self.transcript.as_ptr() as *const c_void, + transcript_buf_len, + cert_chain, + cert_chain_len, + self.base_hash_alg, + self.sig_len, + 0x03, + slot, + req_nonce_off, + rsp_nonce_off, + spdm_context as *const _ as *const u8, + ); + } + Ok(()) } } diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 35e4378fb9dc..64326e5f2490 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -156,3 +156,7 @@ const vm_flags_t RUST_CONST_HELPER_VM_NOHUGEPAGE =3D VM= _NOHUGEPAGE; #include "../../drivers/android/binder/rust_binder_events.h" #include "../../drivers/android/binder/page_range_helper.h" #endif + +#if IS_ENABLED(CONFIG_RSPDM) +#include "../../lib/rspdm/spdm.h" +#endif --=20 2.52.0