From nobody Sat Jun 13 10:31:02 2026 Received: from mail-pl1-f170.google.com (mail-pl1-f170.google.com [209.85.214.170]) (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 012A3175A76 for ; Fri, 8 May 2026 03:17:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210253; cv=none; b=bAiZyjA2JoWw4bGwosXnZBbgwzyAtheimDKSTEUl+z+53AMef9bqVx9h6U+I3Zd+K477Vk0j/JAtLnqiEJ9Jz3YYJiqVfoFlxat7E1YRwQ9GSLfhtDxurn3O+c+mNNH5mbATDlKJcz6hn4mrwd4fkdB4gHS2adqaf0VNawDlL5k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210253; c=relaxed/simple; bh=LQg/AZA7eVbbVVKpeO2cgjGa7sODOFzfELG435RZYi4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=TfZ7cUZxVet1zd7HYjpuMnncfn/V0qK6VyflvJ2+UQCb2Oo0pNmZB1RJaF6Dgb5MZMiY/wXN49kB5t6SzIEQnw9zy1O8Vfd/0VZ0FwAqOjSdnEZ3EeiElhQuSprOCus0jjUppZAZd8241D9vlEQPMY9FEoh2eO9mq4lnc0RxTsE= 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=mG3M7B5A; arc=none smtp.client-ip=209.85.214.170 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="mG3M7B5A" Received: by mail-pl1-f170.google.com with SMTP id d9443c01a7336-2aaed195901so7820525ad.0 for ; Thu, 07 May 2026 20:17:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778210250; x=1778815050; 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=PJurxHK2KR6NiQggIlFIcPfQ0S77CgxMQQO4qIElRQ0=; b=mG3M7B5AG2HIaWiWqfJ7HuVkeIrqIhufKNONt2eIIukdKvdpb7YGtiCq/7/4tyj3Op N1Do0dHLoXhXEKZh1P0XaLEYg6s9XTZ2TBY6wPnmA+PiqlFBcUg+hEqXkqbv06a8EIuS nENLeHcjjWxTeWFEG0NRu0pNPTH03A21C/S8ZQpPwUmz/3cLAnRNbdEy8a3zhiyb5za0 CANh7riiJZp5cWEn38OFEaRJfyJuPWl8W33mZA3lxPD0YA21aCxEMxWYFd7JgaHgHUex 0j2t/KGR75lFjvi+iMMZvNamZezEbh3jP7QchpXlZ4ORs3R+P5hBLzC4KFeqqkpSJ1hd XYag== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778210250; x=1778815050; 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=PJurxHK2KR6NiQggIlFIcPfQ0S77CgxMQQO4qIElRQ0=; b=Iic7RTTTRYw38zFGa9BgbtWzXhDp1RirqL797cOy8JqZt/UucJ1/8je459Ke32XVOa 2n1alFaegNK+ZFtN3Sm4LseHDL4SfIDyjwpEFYmZt514/OvLcT/BESts5LAlFMNeEwj5 Mt4jBbmSMEChyS/SIl+4AInlFmXrIbenIbnIgxtdFaC52NKiapjZn5eas78tNq3QxU5m RhPwLlz+vqLrjTK+PkXSuCYVHkoM6XB+FnXglRRlXSgsv+AtjUJue7JeY9oL7ip2ijJx 6bQPyuzzLKekgeZy673zcCBT5iefTf42w+f6cysPiZ/7VKxgzDzloEAxcAsph1P5q2Rq BmTQ== X-Forwarded-Encrypted: i=1; AFNElJ8fjusDwSd7Zn0exX5yYuAyiLta4wNVTf+L6jAs9DscOlfOkBJJHSNoj3UdRcw2E+F3AZwMngpMyzSnqto=@vger.kernel.org X-Gm-Message-State: AOJu0YwYBQzneU52/skl57ZTtQqsLFqaN5IQ3cN94YIJEv6YPAQRf5TZ Ctbs4AvqQXQ6PeVL94IH8GNLr38wMDgHvFNOQcuPg/DJu5VkpbOYRB7P X-Gm-Gg: Acq92OFw313WjphgbinX38GNMEQduJFo1J70tSEVLQoX9YwakBSo5sdTy++gq3DAL+x ekE86kYY5Z7lypeKVmELMhfacN5tWET6tNhrZFXTmzsZVzF3q7lzWSxQXD1tcqjy58XwUEJ7NOI jiHy3BYTLyvECeIlK1MUZg159St987XvE4G2E8bW9gM1UIYyEg+ToOWHaSI0o9IKhtlzEDvMU/+ 4mgisniU2kUHiAN4FVGQMitZ0iwOSFQ8+6XKqCsp2Wca1fu3ERI5QQM8HWZar/Q1j0X+qz88zGG OIpF0Vf4ZpKG4vkNBzxtTM93Ode98WtAZ62DEsdn5Yolqc6KcGCDMOeojZSofsk6sb/9cvauRgs 1A5qi/1GS42iAyFTTNXAbehnUWokSrHqwijNuDnkP5+RnKDtiYcexV3Cfd0bqmTKBPvIGlLHBi8 Wwx2LykeffpYWUIAo5GDq2b79jVkfuojTKoemTQtp+ X-Received: by 2002:a17:902:f08a:b0:2b9:8ee1:73ac with SMTP id d9443c01a7336-2ba78e3f13fmr68717545ad.11.1778210250078; Thu, 07 May 2026 20:17:30 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2baf1eafa62sm3220685ad.74.2026.05.07.20.17.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 May 2026 20:17:29 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: alistair@alistair23.me, linux-kernel@vger.kernel.org, lukas@wunner.de, Jonathan.Cameron@huawei.com, bhelgaas@google.com, rust-for-linux@vger.kernel.org, akpm@linux-foundation.org, linux-cxl@vger.kernel.org, djbw@kernel.org, linux-pci@vger.kernel.org Cc: alex.gaynor@gmail.com, wilfred.mallawa@wdc.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, aliceryhl@google.com, boqun.feng@gmail.com, a.hindborg@kernel.org, tmgross@umich.edu, ojeda@kernel.org, alistair23@gmail.com, Simona Vetter Subject: [PATCH 01/18] rust: add untrusted data abstraction Date: Fri, 8 May 2026 13:16:53 +1000 Message-ID: <20260508031710.514574-2-alistair.francis@wdc.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260508031710.514574-1-alistair.francis@wdc.com> References: <20260508031710.514574-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 b72b2fbe046d..fe580fde609f 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -133,6 +133,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 Sat Jun 13 10:31:02 2026 Received: from mail-pl1-f172.google.com (mail-pl1-f172.google.com [209.85.214.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 0CF0F2EB874 for ; Fri, 8 May 2026 03:17:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210260; cv=none; b=nQ8C/jTVhat41gPVlz6wU/7+EdEZQF3aWbCSkxCwPEv1w+LDMcmM9f8iVtRgZPLBPIFgMXqi5GTEd6fbWzTOKwyDwPdw6+ElouJee2maxTjFuJd/e7XgCQFpCuOtN2tKp+erDeVd2OkfzwYyDVUBg0Sngyakzbx9ST4guvLhcW8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210260; c=relaxed/simple; bh=z5H7IwFWyQ0i2GyTWoOMohX8+M5T1CFBH5xA9uQkq98=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Uk+V5f0VFfwJHJijB+ndRZmVNdzVhixBreqwXeXuVz0h4zbgGGvn0ZBI9u32g+H8Av6eRPLFiKIwxZLRA9un2tAWhlgaumffuDNj1y/6xbFl4ymGeE/K0YWrwhZsvwgzypNmoFUAApS5DbodMeFFdval3FvM3UiraZgDkgJQWd4= 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=Y3J9ZhQD; arc=none smtp.client-ip=209.85.214.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="Y3J9ZhQD" Received: by mail-pl1-f172.google.com with SMTP id d9443c01a7336-2ba21d32776so11073735ad.2 for ; Thu, 07 May 2026 20:17:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778210258; x=1778815058; 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=Y3J9ZhQDAFQUR9WNkcoL7BK/wimN/GK/b4PIa2EaSphEsW0hBaalXoc4/w/XtQmF1I K+7zoz9nWZBwM0iqoxh8faf678FELgVXjMN44M9A4IFk6TLoUHW7CXu6YAIY3WNG/jjV q+EZx8kYEw9Ky7Z6dA1Iz2I8PnUYzzqjULVpqGmuN0r6IClNjP/SLftN0W8RaRprrF/O dtP1hr5yateyftfmFIjqXl2c+sF2Js4zZ6GJHv8coeodROKEnF4YTAVE4OYRZIW3Yfvj UCGmT+4hhXk/GHyOTrUM74eszY+FwwZpb6YsLFtjRNlI2xAyUUgq0hExDZ/p7zvedflM atXA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778210258; x=1778815058; 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=X/vOWFGED078fgqGAdFH8j0LQqb8O7ZP3nM3zaYhQyfh1WJ/92W+f/HKD7JyCzzLWk UGAvUsjpaR2lNCTrvuTRGlKuBPmcxduSsjgrxBaBiWErgE2NejM2w03bz5O6dnClmwY6 wzbwzDoALoKHJgGZFMOQztQuK4GI/KwmhFvVPGl0QMlikj/nLNICnET0q1TE/O/DaCzj T3o3iNAQJnyXSL+ygfN/zUpygop9GbpdjizhifK8JGjOFBBpskgQjfuYnyWhwKfiN1TY +FSL+0FMtaf3mwdkF+NHmwVamkSIKAtlbvsnVt9qa/PumpXjAXKx2aDrhKU2syQ5EPCP /1Vw== X-Forwarded-Encrypted: i=1; AFNElJ8qdvYNpl+mmTRjuA5NPzpdRbNpppT4mcda/soXCCh3L0fTqLohVUw0fOYV3m3sc/p+AKLkYQxYc+W822g=@vger.kernel.org X-Gm-Message-State: AOJu0YwWcvjsRY4qF7IjDlHUQxEh675QsVtVNMCmfkcSqXlm30yo01au g9J9Tgp3Z70wp4pn3RbwDnENJqc5loDykTOxgIXQbWjittfyikEpJFgh X-Gm-Gg: Acq92OGCVf4SO/s31L38kf7/fl+YFLTPBz9dxbwkQ3huG74rkk0fBHJX1FqVVz1rKYG D/vPIK8NGR+lkiDbwcKEy0NGFL9eLzgIyXgn7znjmvBafknfDznSKlSLdwkifIQcsvCcEKSK74h uKDCFIfueF7OS4JAr/TyfPftdzp6bZaTDulBYa8XZ0O6sVC+o9IuJu7QKHHL4q/xrHVOYXkjLTV pXhvsRbcC3Dv7rqrb7tJds9M71azc08PoyPVj4pEwygDt3bZYsDeWcLdieSV/NB41qqrT8w0Fcy 4vWQBeISXiVC59gzyi6xE6Annb0QITUpgTzJNdDRp6wbxG09sU/83MZTTPmhDg2yY+VkYuC+24p Ta8mT+iXNw+E3R4baF8jvQfwS+KDdvxLTk+SdamkquE5xbIDTdptEs86lNcZSJHYJqMnEx8ebLZ 6r079yF9K7Rs/v0e85ZHNhWeirJgXBO1q4hvaDBMggsQwKJmuxGsM= X-Received: by 2002:a17:903:124f:b0:2b4:5c0d:314b with SMTP id d9443c01a7336-2ba798a8fc5mr108654285ad.38.1778210258226; Thu, 07 May 2026 20:17:38 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2baf1eafa62sm3220685ad.74.2026.05.07.20.17.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 May 2026 20:17:37 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: alistair@alistair23.me, linux-kernel@vger.kernel.org, lukas@wunner.de, Jonathan.Cameron@huawei.com, bhelgaas@google.com, rust-for-linux@vger.kernel.org, akpm@linux-foundation.org, linux-cxl@vger.kernel.org, djbw@kernel.org, linux-pci@vger.kernel.org Cc: alex.gaynor@gmail.com, wilfred.mallawa@wdc.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, aliceryhl@google.com, boqun.feng@gmail.com, a.hindborg@kernel.org, tmgross@umich.edu, ojeda@kernel.org, alistair23@gmail.com, Dan Williams , Alistair Francis , =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Subject: [PATCH 02/18] X.509: Make certificate parser public Date: Fri, 8 May 2026 13:16:54 +1000 Message-ID: <20260508031710.514574-3-alistair.francis@wdc.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260508031710.514574-1-alistair.francis@wdc.com> References: <20260508031710.514574-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 Sat Jun 13 10:31:02 2026 Received: from mail-pl1-f175.google.com (mail-pl1-f175.google.com [209.85.214.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 0F2372EB874 for ; Fri, 8 May 2026 03:17:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.175 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210268; cv=none; b=Miznt1bgbgqj1ZcmVIRjSDxQ7yozgnmgAaMD+N/gFnbifUWJ9+RpoxD/OX2zGgjRZBgmcOHEtQZIhPA6D3slSC5KMnvaVS4c50ZhWYdybqSfu2VTQTmqPlIKoQQgip2ow8vkxSqfcbDFebyxHq8ewEGwE0x+QHnKB5qjWmtrkys= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210268; c=relaxed/simple; bh=KQNEGxY9INaXySHb00KAS0/IBHXY6dy/wI2exWO1eig=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=DWstk7XkAW39fJimRvQ7s2Bv78GMn+JP1cElqX9Mcf4lolwi7iHRT7PGHMevpZRUMKL+R96r+DpOMh9rJxUWXBT6LIrblrzRxV7QzUIRenl9dN4H7k5CNXvi+e26vdzt5b5Bin6ghfx71M9kVdkNQdcyOkfPX7iWq4xWzi+YqLY= 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=JjAfJZju; arc=none smtp.client-ip=209.85.214.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="JjAfJZju" Received: by mail-pl1-f175.google.com with SMTP id d9443c01a7336-2baef9f5ecdso1756475ad.1 for ; Thu, 07 May 2026 20:17:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778210266; x=1778815066; 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=+exDoq3R7sORPn6CGzFv/4xCKwv+Zs0uPc325B74MH4=; b=JjAfJZjuCp3AqFLkx0KYP3+v+FYdh6/AMit6bMvm/eB7cVDO0hVqeiJ1NnJAJ7QpEo 1Etlj16bnBEWTm7Ecw/sFz7hMRWybOVNKou22kDL8mkV/IpdJP0hpSE1pCSWxEo4rLGL B9a6YH3Ovw+qzTWcv/QpC9MzR3D/F6Et2wy/XlzamW+ARC4ls3JHJGDXkp8O6oIRvAwM /fx7GX/nInP1smdOC3RIP67O0kD0nWuvRynZKER8N3CJGFEoz/nrS2Qa5aYJVKbptQUb sQ3WG3gQmtCy3GO4AFKs4TMVFNFXISFWlapvDN6Zktdg9d28C39WBSPrEUMnf/jxr+R5 Fovg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778210266; x=1778815066; 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=+exDoq3R7sORPn6CGzFv/4xCKwv+Zs0uPc325B74MH4=; b=ePprFBj2555ygToggY3RMmAwgcBT41EAyQAPhbUJDXbEG4XyDd4zyzT0ks5plglNVe BuFnRQYpVIHFpnsF8kcvNeZc/jeiVMDCeZJtUqO85ljQV/wQp1gb8/5+W6zzbFQMumSV wVZNfQXS9SzLL7uSjXVdQ8OpxQQ9cGfpjinhiQ3zp8NPA83CaShqVu8P3gi8UmCWkLEO ApBzfj6hgd/CtpP1ZZSlL3mlXm0TJq/13NRTq6p0zjCCet+42dOwMmXO/ScNO9OiCwzl +Oh1haZTIlDnrtvbGkhDuHBh/cWy/3hkX5VJ874pH77GwG7kYsxB7e1C8ZVeTOdrmV4/ De3A== X-Forwarded-Encrypted: i=1; AFNElJ/vPrE8akTkxr3vQBWGTkd7Sma3c0kb2zaiVw+c9ZkW6zVqRjQywHn2AxoB/nCOWbspwHPm5l1YC3sTxYc=@vger.kernel.org X-Gm-Message-State: AOJu0YxL6V7KaUaHKH2NrGW24ArnS/lQSu5yVPEGPPWBfkfFJiB6UmpQ Szx+Ma0LnG2lWB4TdAYLzPFbrcpMtib0Hx0RunDtGJ3U7uoy/gGfoMSb X-Gm-Gg: Acq92OG3znduIWJ0CHrG9t5CB4TeopnMZmOue0tVZyAJhNoKAVuH3UXYg4VBOMnYcKp GeUkQ/jV4HWEPL9mU9xpXWWDF9+QCZefbwZj7xM98UYJ5TN9tjJSJqwS8B4SOKOy5Zm6XdkVDYk 4xmvd3ZZL0Jfd0mjaI0U6ig1oEK3FsshX9oVSE8ofGN5goDHFiSIA4PlHoR8izccAAgOsN0swf6 KJIuVNQajPyudBUn2BfYYVAp/0tp25FebcRkOh1gE8rIU8vfISXOTFkr8b4T0OLNk1c+OWTxEUp 9M9pDKKhgboGoj88ucrLx3ZJSWNKGZ4vNmYoFFgqF838jLRX2pNauR+YefIT7Syu/rFJ0nmIqpR kc+9/m8K6wCwEvTCwfEsHPoQkF38CjOBBQNHro5PEUh8hw5+oisl2YYfCwxNRpc7uvDK9ZvNs/A NACHc4je3F43k/7luKdIKKz+nVQkhNOGV3ocClvMD5xsqhEBjWt7I= X-Received: by 2002:a17:902:fb43:b0:2b2:489a:f46a with SMTP id d9443c01a7336-2ba798d2797mr77415615ad.36.1778210266365; Thu, 07 May 2026 20:17:46 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2baf1eafa62sm3220685ad.74.2026.05.07.20.17.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 May 2026 20:17:45 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: alistair@alistair23.me, linux-kernel@vger.kernel.org, lukas@wunner.de, Jonathan.Cameron@huawei.com, bhelgaas@google.com, rust-for-linux@vger.kernel.org, akpm@linux-foundation.org, linux-cxl@vger.kernel.org, djbw@kernel.org, linux-pci@vger.kernel.org Cc: alex.gaynor@gmail.com, wilfred.mallawa@wdc.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, aliceryhl@google.com, boqun.feng@gmail.com, a.hindborg@kernel.org, tmgross@umich.edu, ojeda@kernel.org, alistair23@gmail.com, Alistair Francis , =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= , Dan Williams Subject: [PATCH 03/18] X.509: Parse Subject Alternative Name in certificates Date: Fri, 8 May 2026 13:16:55 +1000 Message-ID: <20260508031710.514574-4-alistair.francis@wdc.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260508031710.514574-1-alistair.francis@wdc.com> References: <20260508031710.514574-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 bfd10f0195e0..c3ec2846695a 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 Sat Jun 13 10:31:02 2026 Received: from mail-pl1-f170.google.com (mail-pl1-f170.google.com [209.85.214.170]) (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 DEF5015687D for ; Fri, 8 May 2026 03:17:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210276; cv=none; b=uFVY/ZshvoHSYQLihjuwAZLupQoS6WEkMma9cxxrWyCBbt2SHQIwJLF66uDawoL+22n5OzE8j9D7CnxR+hmEVCUC+hBcVFWXbNLcsjbK0bXuyYHdN6Xl+2JuU1OhVnljiY97Oxdl8z9VvTuKzXvZ5F4jjVRwqMWiLViv6OSbFDM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210276; c=relaxed/simple; bh=ywX2QQkJINgcF5rI6352/2UoVw6y6LLcc5xcEUpnPLE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=G2MtNrnDwoKc2v5GLB8NDAxPUrR1rYrP4bunM4DThOD0/O0FdZbWs9OS8YCvt+FSvXwh5F5+w8ifN8QDgNGRp9pUCYaa2FbjD7CDJIQFDJSFggkLycSF/SMhTD6xcWu02RQjgHouTpQCOzlxPOChVzzwK6b4HL3eBatFRphe/JM= 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=rP/T5/ml; arc=none smtp.client-ip=209.85.214.170 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="rP/T5/ml" Received: by mail-pl1-f170.google.com with SMTP id d9443c01a7336-2ba6485d219so10110495ad.3 for ; Thu, 07 May 2026 20:17:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778210274; x=1778815074; 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=rP/T5/mlsDra0O6cXplImVIOLvzvSFBrU/Tg956etLmgh58OVygXbxhIMeZEd7m7hl 8QAm8Gxb52TYfDryfGb+TOiAAwzLMgkLzBLL7X31CT8FMBDMmYQWKoiUM1Ra7YfI+NKd nESs345e5jrX/pARwvR42hKCto4cni2xVT1ouIzN4ghNnfph7z/WY0C0Jm8D+qC46uM/ Ny9nwVQ398h5J9ohs50/4p/0vBcQeI5M4+RGuvktn7FrjGlWJuMO5nuKG5iQL68LyX5/ VQMZjovMl/MVGwBFfYmigw7gXVP2e0ore1aX2wks27WrCHbwYbb25wqbzrz3YlsUclmS H9kA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778210274; x=1778815074; 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=F8avAR/KfitifUfgdSp+ySho/C9YFH01dCoUVZ/t7IeQiMeZbIn76p1SMimENFtAKW Ytg2GkEc8iwxVgb63WAk/FpoNiub83lAiNusMr8GwkcRVTOPb1aa+mgUkjBVINzwGkYF qpdgGGLc9SHxtfEkj32ANiLiGX1zlFL3C4pbSc/ZV2Ek7+ss1z6h/C3c5L43cgM8oZaR WP8bDkRkCsTmYusPjTE2vc/WVcDKBChEYuLtnlv5V4uugYp4U6nnV4y+EHv/Avt6tfso 8ZNP3QlzHnMdr3s9gaAah0FnAS79v/+dbLB28qP2f+MpcF+tnG5orKPfwmJIia2ZdMOr GxwQ== X-Forwarded-Encrypted: i=1; AFNElJ8f3waXmQWnZHPlp24g8/uYlvDqtvVCTHv+50NRFcMX63abr30iWi7A64shUCLHiiTjEDAFUiiOuZXLRXE=@vger.kernel.org X-Gm-Message-State: AOJu0YzBLqIiXKdkNR86saVvlUr02W61CuSRdMCgMlxt17ZrD76su10x Lx87L9SyuXYbFehaWczyi7nk/ZQYNpxlu0pphNxduBoe2yW1oqPRwEmv X-Gm-Gg: Acq92OHcWugJEmjB9X81bjOzvg2zuAATsmVj+ZklUkFrfbYf0zPo9F2aLgh/f7rSzX/ e5+ak9UkfU4B1Cx5LIB5x77T3PzZnwJIfwlYHMZNUxfdI48CaRFoTsCMHRuzkAG2uN05KxN6d8v GR3VOxfBUVnMe+W3xRrOeKANzlzmlXrV5tTl2lGx5Ahmjgq1c4liMMCk53da0DbJ/VybLzuXD4M m4jSXHXwpNu3VgLMZXtMXAcj1COGCChoT5yL8JJ1jBZEKqGfQh66iAUhPoeh8MdFn9jadGt4Vex m979mEFTiNdHxypLXvY4bGKS3d4UDiBQwrP2L6AN1vTszjdZRBqI+zl4z8YtPfdiCDO4bGTXj// fZbrXZeVsVAZMp8vtPdAO/mav/++MCtnOPwHVvj5Gm4BlDLeJv1xiEDDHn3S1q893AtWTSDXcY0 yRxMDCj8kKn7O80bwzhmheRyfbj4N6PgoJsQoKbuMQ X-Received: by 2002:a17:903:90e:b0:2b0:b016:773f with SMTP id d9443c01a7336-2ba7908443cmr108576085ad.11.1778210274288; Thu, 07 May 2026 20:17:54 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2baf1eafa62sm3220685ad.74.2026.05.07.20.17.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 May 2026 20:17:53 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: alistair@alistair23.me, linux-kernel@vger.kernel.org, lukas@wunner.de, Jonathan.Cameron@huawei.com, bhelgaas@google.com, rust-for-linux@vger.kernel.org, akpm@linux-foundation.org, linux-cxl@vger.kernel.org, djbw@kernel.org, linux-pci@vger.kernel.org Cc: alex.gaynor@gmail.com, wilfred.mallawa@wdc.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, aliceryhl@google.com, boqun.feng@gmail.com, a.hindborg@kernel.org, tmgross@umich.edu, ojeda@kernel.org, alistair23@gmail.com, Dan Williams , Alistair Francis Subject: [PATCH 04/18] X.509: Move certificate length retrieval into new helper Date: Fri, 8 May 2026 13:16:56 +1000 Message-ID: <20260508031710.514574-5-alistair.francis@wdc.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260508031710.514574-1-alistair.francis@wdc.com> References: <20260508031710.514574-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 Sat Jun 13 10:31:02 2026 Received: from mail-pl1-f171.google.com (mail-pl1-f171.google.com [209.85.214.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 797EA1F3B85 for ; Fri, 8 May 2026 03:18:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210283; cv=none; b=PVgLiEAywUZfhViSLGTR4cDrZpF3ZAGom7Uk/jwGRhA4bFdcvcshwQ5E8OONryTlylJHSeax/XMnCuM5vjvCmfwsrsi9uwd8+dKpt3ITwrLm9hsP2V0BdGA37LGNy7gLM3HH1nkKq7Bpe+0flZpoIIXMM4bwbFcKjNPaR2SOfF4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210283; c=relaxed/simple; bh=UJEK4Y1OhR+erOD7cv7y2WCfgF+MLsewlMFQ+GFgGZ4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=L9JfjY2VyD/VCP8A5OXyc6ETkSktfxRU6Tr3xA6IMz4WFkCE8U7eOfNLwP9z+Nz5o8rQ45kBRMQNBGm+AmFYfOFXKu38LBvGrXg1aVYZHuBIhQYLNqurAmckwswd8uX1RG8atS51VdgXWVpsphk2yKvuSgnCTdw5ntgC0rjf1jM= 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=KAatbeSc; arc=none smtp.client-ip=209.85.214.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="KAatbeSc" Received: by mail-pl1-f171.google.com with SMTP id d9443c01a7336-2ba856db1c0so10676245ad.3 for ; Thu, 07 May 2026 20:18:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778210282; x=1778815082; 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=+Ea9CQEMVfcK8wVMyEmmugnqFDUXoP6diCNpcvc9CFc=; b=KAatbeScxFvridVADwksd9gbbG5nTUFqMbvunoN8z7guixrtGU+2LMpreIEfZO3dPn SRESKEXFzKT7MFT3ZBAP5qX1HLz2Ccsy+x+dMHTcUtsp4ZefKx8qcVoMgnUKraeRh+hX vAp2zw6Px8fbeUZYEHLJS6BwnXRDnb/L/dYkWbMJipLe3xB6Np8w0xW3yQw88dIHDkBR yr+R4e18z9zsWZ08WvvKsGRXF5p3cXDhf3hUsPX5SHmvClRBX0ET1g0G+PNMTi7wds3/ joFS4xDU6anoh420k3HKoVqii8cGDNW06uUO0Iifq3Qrr2R/oeB+PnmppBm3+go+lWLG sN6Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778210282; x=1778815082; 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=+Ea9CQEMVfcK8wVMyEmmugnqFDUXoP6diCNpcvc9CFc=; b=KYExO+vxejTlC+oAVqVp9nPvKCIZXpQ9QQ+4xa40xT4FJOykOaKVGJDdpMCKOaFC7o Uv853MtdHXkftqQo14FBEuJh/TqyaKtJ9gvQLQIDTj1/nwbPoZhr9DfxTHjBJlzJ7Sak yh7XfscMFoD2R6ursOLjtZF711XziQX3GOZLEqHQY9o6J5hhqS0+6xnj1J85RiYOtBuF 6MQUHE128CNhO3yw6Kblawwxq06Po5gbQVEe/itTXf1dSyV7hRuqh6XhxoCu5WZxlBxv BN8shxC6KC9/N58rWob4zvCGIsO3imLpreI4wk5cQJWnP76OHyWtmZaUM4p0pHZQkHzh PowQ== X-Forwarded-Encrypted: i=1; AFNElJ/cZh1sro8hlWFKwNErz9BD5aNR6UglV+RenI3MyC9gZDj0GqDKgmmP66msR66G7D/GpC2aLiXg86dIU40=@vger.kernel.org X-Gm-Message-State: AOJu0YxSFTPJPI4Td2bDzK4L4kWX9ybNsFin2kuw9LNCfawKq+PwZ3pp DpOm9AwxR44/YQldQQ7sfFzAbEeXtDzvRhfKH48WTXMcCl3U8/sM8hmv X-Gm-Gg: Acq92OE0lZLh2V6SuSfJOHPM6gl3hbeO6sOF/IFmx3Ihw4aPd6txnfVgOmOn2dI/9QX cRobd7br8y9LjLWnjivIw5FtbvZjk+pFLy9g/zEfTNDfW3LiCS6qx/Rpwpvj7RrH3hc5Fi2bA1S ddlEm0l/BO6pa7+869qjHD8y/WP5K7ZXwxCnDRK6oX5Yp8Lkl4OfGUd+u6zrE0MvMzMP2cUoDvm r+rGFzxfer7UB8AzSgk0/EJizu772EeNiir4vbAK5OwdDzis0N5fBVgp82NHI3j9EFlAplUVHQm jcLWYc5Nh3+GFwin76fRCrLfV4o7Wo5yGQeqEDe/tVaWRUxbYKOLn9dv2Fc7VNH5+X7eyCDMiHy AmPaER2D3MFeQ7YN3kJy0Y2gVHRnfz0mAM1rjwfBGsZudcZVE+RHxZuqbTlHu+PBbZhcBH9vzTv OimSmjGxEbhteWh/kevq9y8qjWVCzxJbvvi7H1tSSX X-Received: by 2002:a17:903:1a88:b0:2b9:6458:1a2c with SMTP id d9443c01a7336-2ba78f54deamr115997225ad.13.1778210281888; Thu, 07 May 2026 20:18:01 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2baf1eafa62sm3220685ad.74.2026.05.07.20.17.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 May 2026 20:18:01 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: alistair@alistair23.me, linux-kernel@vger.kernel.org, lukas@wunner.de, Jonathan.Cameron@huawei.com, bhelgaas@google.com, rust-for-linux@vger.kernel.org, akpm@linux-foundation.org, linux-cxl@vger.kernel.org, djbw@kernel.org, linux-pci@vger.kernel.org Cc: alex.gaynor@gmail.com, wilfred.mallawa@wdc.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, aliceryhl@google.com, boqun.feng@gmail.com, a.hindborg@kernel.org, tmgross@umich.edu, ojeda@kernel.org, alistair23@gmail.com, Alistair Francis Subject: [PATCH 05/18] rust: add bindings for hash.h Date: Fri, 8 May 2026 13:16:57 +1000 Message-ID: <20260508031710.514574-6-alistair.francis@wdc.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260508031710.514574-1-alistair.francis@wdc.com> References: <20260508031710.514574-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 446dbeaf0866..d73142078240 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -30,6 +30,7 @@ =20 #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..23a63618a370 --- /dev/null +++ b/rust/helpers/hash.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +__rust_helper unsigned int rust_helper_crypto_shash_descsize(struct crypto= _shash *tfm) +{ + return crypto_shash_descsize(tfm); +} + +__rust_helper unsigned int rust_helper_crypto_shash_digestsize(struct cryp= to_shash *tfm) +{ + return crypto_shash_digestsize(tfm); +} + +__rust_helper 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 625921e27dfb..6372e14f8419 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -60,6 +60,7 @@ #include "dma-resv.c" #include "drm.c" #include "err.c" +#include "hash.c" #include "irq.c" #include "fs.c" #include "gpu.c" --=20 2.52.0 From nobody Sat Jun 13 10:31:02 2026 Received: from mail-pj1-f46.google.com (mail-pj1-f46.google.com [209.85.216.46]) (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 1B2692E5B2D for ; Fri, 8 May 2026 03:18:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210291; cv=none; b=OZnzO+W+HeuyBQ0H8FWS/NFOwIrAqYMO9oJlH7panAgr3UgNtEmzlJYL/TLhFuGzW5YLBSM508o6dtV2P7BZiMd4Yb/luuqBGwBUXIzOBJWtYLAeTD1NwY+dB7J5pfhzRgSF3P9lK7sKphszBYawe4NMK8VcHBnuL04lAzPRHrA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210291; c=relaxed/simple; bh=ERpyWsMPE9+dpw78Rz8jzNcLrivxWjKPOe5Lqw+ayKs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZAoLSeP+96hJjJ5NkyEoFnDxb+dHpu6CwPU3+BUrDqOurE9ueP8Yz11YYFtI+hPwNJtd+ZHwYax/2rXeFsQzNevUo4wjGW7V02ebijezl0DmwOtqaS/6VmQAe7wvTJ+PL7xx8HqCnVEPxniPyW4tyyH2SM2OwXkoo7/VemNqU1s= 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=Yj6Z0kmp; arc=none smtp.client-ip=209.85.216.46 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="Yj6Z0kmp" Received: by mail-pj1-f46.google.com with SMTP id 98e67ed59e1d1-3664df32e91so104422a91.3 for ; Thu, 07 May 2026 20:18:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778210289; x=1778815089; 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=9dr4Me0446pI9Lo+v8fcchBIKe8OPGzCDfNPYgvjsMc=; b=Yj6Z0kmpi9AVb6Nd8h0wWN5TlgvMLiVNfReWogBnZH4w+otegDoFB5XF2212PfcZ4u bCqNS2GxYyMlHd2PTOezbhqkYMycoEuDnnYRh6H4fAHqzDsubjkFdJOFzgC8YWK1bhrB Wtg3Wo4wuCg8dkwC3N5FeuNHJFo3fXNnofhPNYgLCg/DFRNnALlGntMPdokrDyb6/T94 s8Oz9/7E5nborARV7YccVyWYfW54QLNzALiKSXqW/oz/BjFOFrRfHWhp/e6v9SyrjkeC pJUj2imTw+UIyyNpBh7OrpI4n136yyhAqUnoVncFUmvmuuxaS+IgCU/5IQWalPXFaCOt m4Qw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778210289; x=1778815089; 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=9dr4Me0446pI9Lo+v8fcchBIKe8OPGzCDfNPYgvjsMc=; b=GDj/eaARYzEDGDnZeT28gDzKAu26cmsoYQTmLcw3rou0DPdce7o/nUcUYqhg/58r0J Mc0lfnv5sUUHYC4XEFFUABdxkWWlCoHqDBHqT9ZwHGsHJ3Ro96Pbr1xKDdkAzcyWy5ie zcHtEtVi1XFkXak1cQhVw39VMjAz9jmanqKaL1QaGjkDWMpjovErLQ1KUj+OtJRpx5OI Prxf3ivGxAMW99mNjNLvUpFLgyvSsVhtWncLA5O/kIaLTUpQu3FOh7pNZmtXC7f12PME xN6y64tETwOR1IriMSXLiEIW+0R5+XVZTBgyPBSxpuoyEGkRV6jzxL+mPlWUcStigvRb RqFQ== X-Forwarded-Encrypted: i=1; AFNElJ+P6wT+Pts6MKDa+710vg/VHTTCAdH7EBtZbyPSSmu2U707TBNctwU835AKawq9k4HIXkEwpi1CKMC+Ioc=@vger.kernel.org X-Gm-Message-State: AOJu0Yxvrm2Ife8lnUogB+52PvswoDZ6etk2rk0pEmqCMtF4YGtmWW/M jmnrRhzTKVUZQa2qjt+5UrcWqZJP53cBA2oidWF+oHJ2PVCEZ8W7AgJX X-Gm-Gg: Acq92OEDfIW7VhAmYWo8GB3iSdQwr8OD+pqnS1Tvfdbr9gzlJOHVXGWSgALfUKEoPAB JRjk9WAYJNyFKBm1k3Hk9oDqHhyL7/uUeSYE24FtPnsfvLdxiKdEPDZCR8Ph6ISpWjWanCICbmL FQSqfTp33Af3bQmoKv8y5x3RmwLQIgY2wvGHd0Fp/rFB+/MQyMj82J4RcmGupCSR3k4x/uYDq+u ah9qESGXYWfl+dXAlBclizUeCyysbXZj3RgfqQEp9hhyS1/Vogh+EX4OCIn7wcZqSKMTzM4l4Xw LKR3mlbYWiIIOGbqzOokWzIM/6GCrLIV+kLXV2vIXcjryv+Mc4yUbIGFpl3ODgWTNaboioq10c4 xWsQg+04c+vr/a9L1F+BP2XaC6T5YebW5nbakiGw5WkkmomjhjH/QD9xNKRmBlWfbmwwiShfA7Y m1slXLyDCkUT0V5ituDJJ+N1AEespms0Vn1zAvi8F0 X-Received: by 2002:a17:902:be05:b0:2ba:4a19:fc89 with SMTP id d9443c01a7336-2ba78b485d7mr69407855ad.5.1778210289439; Thu, 07 May 2026 20:18:09 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2baf1eafa62sm3220685ad.74.2026.05.07.20.18.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 May 2026 20:18:08 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: alistair@alistair23.me, linux-kernel@vger.kernel.org, lukas@wunner.de, Jonathan.Cameron@huawei.com, bhelgaas@google.com, rust-for-linux@vger.kernel.org, akpm@linux-foundation.org, linux-cxl@vger.kernel.org, djbw@kernel.org, linux-pci@vger.kernel.org Cc: alex.gaynor@gmail.com, wilfred.mallawa@wdc.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, aliceryhl@google.com, boqun.feng@gmail.com, a.hindborg@kernel.org, tmgross@umich.edu, ojeda@kernel.org, alistair23@gmail.com, Alistair Francis Subject: [PATCH 06/18] rust: error: impl From for Kernel Error Date: Fri, 8 May 2026 13:16:58 +1000 Message-ID: <20260508031710.514574-7-alistair.francis@wdc.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260508031710.514574-1-alistair.francis@wdc.com> References: <20260508031710.514574-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 | 8 ++++++++ rust/kernel/str.rs | 7 ------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index 05cf869ac090..5463addbfd95 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; @@ -258,6 +259,13 @@ fn from(e: core::convert::Infallible) -> Error { } } =20 +impl From for Error { + #[inline] + 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. diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs index 8311d91549e1..17070dde0e35 100644 --- a/rust/kernel/str.rs +++ b/rust/kernel/str.rs @@ -419,13 +419,6 @@ macro_rules! c_str { mod tests { use super::*; =20 - impl From for Error { - #[inline] - fn from(_: core::ffi::FromBytesWithNulError) -> Error { - EINVAL - } - } - macro_rules! format { ($($f:tt)*) =3D> ({ CString::try_from_fmt(fmt!($($f)*))?.to_str()? --=20 2.52.0 From nobody Sat Jun 13 10:31:02 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 7640B2ED860 for ; Fri, 8 May 2026 03:18:18 +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=1778210304; cv=none; b=vEUP8gqZs/uNvbEgcdQPaepngn7wtK+sqpFsAnBMxvHKyeeGBU8WHvaN2LeDyyCQFLh7sdp0jm/mv0LGNlKiF00mSP9+ziXCTsPt586KZ6PWl5Xs9jGYhKytzg9YRdWP6D232qri+69gz/eC7G1TgmlmTTSk6WnCZxpbPSMlkcQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210304; c=relaxed/simple; bh=qJWHDK+N4LoOGpeaEwJKkRsDeF4WOI9vEFrJsALVy7E=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jMfdGPueWKUSRFIoFRWPzLs1al10CravZNRMPTTScIs1Ttjaioh+kfBRTMBT0v3sRpshDoWT72dPgrvR7JTW1/46Gjqvahu0ZGMk2ZycEDhfwYPVL79grz0HEJ9TI7v6PaxUwEQ2EZiPu6Vzl27VkVcJD8DtXHgqm48cnZNvpWE= 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=cmDYUJHZ; 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="cmDYUJHZ" Received: by mail-pj1-f52.google.com with SMTP id 98e67ed59e1d1-365eecc5885so1443774a91.0 for ; Thu, 07 May 2026 20:18:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778210297; x=1778815097; 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=kJ731pPUpjKXMohpExLc/2RX1wlDvWGPLBQ6aRYUkzA=; b=cmDYUJHZv4Q0G0aAq5VxHaekaqC6hmwk1ID+EYKjQv3WXSJ78DJerBt8fABph21lo8 H+pssP25hXRfRDyATNOOwYlsSisSIKV4xHIRPCLB7H1vkyouXCfIUQeSNU+fNSadlokC 2Vw768EcRQvibMKEtz7bxnv9n1/KMO3hsmTe6V1GFr+dM2t1PQ01Q+KrFlW8y/hFQpUl J4MXTli+8ScvVPqsYx1PrGuIbcQUGRHagPnHvaDYxsxI67cSwMpsFoe8zJPeM8q/NWt3 IHquxXcOB5WmuGcC/+/awV7XkF2HpVjDS+/u62t6K0URb13u7E9g4DLy7/P9zfJkSBZl /i8Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778210297; x=1778815097; 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=kJ731pPUpjKXMohpExLc/2RX1wlDvWGPLBQ6aRYUkzA=; b=tOSeOVq3i0qfH/kJBp4lL2UCbzTlfavlH3N72i0LFuYN9zMOVu3EivmGTbT8Rbcr+1 hEwNJSQ+P2fyrWJqyXt6hfuc5tJVqSdtmbdAviItUIhUe0lB6KxZ7OfBBGl3n2U1wU64 406mRnMTKLl/nERitBEew40qQOYV30uo978d8adTDDyUtJe5VlgXgYYxCYLsCNgc4kat G92U1kJlvwYQUfp5hwPXWc9tE6cEMLIdOz6IxLhMcpHaiOMD7+82cnrvfJHdobpfH2F4 oiOu+ziN3X6FvadoM388n1p+/CcKSIJ5Flo/gpnFqz+9qf1KqLShtPS7kR8LefisUXu8 ZEfw== X-Forwarded-Encrypted: i=1; AFNElJ+uBU4FJeoRVDH1+wAe+FoEF9xWoTDtjYqFBf8Mml/LOi1tdWLFNfAe269tL/XjqEZN1yr8ptF3USwY8gg=@vger.kernel.org X-Gm-Message-State: AOJu0YxSJUiAut3Skcps2B9LDHCckbhuuuJl/ZnIPd2/gA0V80IU6W/o cWaHUpSJJHcSvwU/W37HWALBD1CuGbUm+6UagpjlStEU4jftMxnKXzyj X-Gm-Gg: Acq92OEWenGp2YGHIkiQzSa72j6w0D4j8u/eLZCxLanZuiwBUllGZGTlGcUUYLTk+CU W0SH6hPrN9QKnS3udJKTxRVCw7dsp34sEd4qMj+FCSb1B87AmzCewZhjHn37Ez91De23nV6R3qC 5aTqjIybSqWFSUGEo0+B7bw5fcIuECXVxb0UwlZJM5M+UAADzURg8qFW+JcGW/1A+8hlx1+vMlA BFgbyP8Qq0pDrTC2GllPl6yiZVpe9/XWdSUMNiSyhY/nOFgnZB3a09Wl99oEriIRmHchPcPPbTA OksL0OiCx7w4YA7ECfVTo4Q/+mO1aboOHnGJaSpHM/ebS6v6DlgYVM9Mh0dNwd1/DWdlm9m0Sub AcqO1Jv0YKBBpCm+bzzEcRpTDnDjKOAsTYARNsPQF6wfb0ojf7P1abTEXKmayhq1fvV34pTo20x fSqVqioSO5J4K3bspUS425RQDynKVbKDA24hoiKqbv14/l/HxPSyY= X-Received: by 2002:a17:903:3887:b0:2ba:83f8:7b7b with SMTP id d9443c01a7336-2ba83f87cfcmr97037775ad.33.1778210296937; Thu, 07 May 2026 20:18:16 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2baf1eafa62sm3220685ad.74.2026.05.07.20.18.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 May 2026 20:18:16 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: alistair@alistair23.me, linux-kernel@vger.kernel.org, lukas@wunner.de, Jonathan.Cameron@huawei.com, bhelgaas@google.com, rust-for-linux@vger.kernel.org, akpm@linux-foundation.org, linux-cxl@vger.kernel.org, djbw@kernel.org, linux-pci@vger.kernel.org Cc: alex.gaynor@gmail.com, wilfred.mallawa@wdc.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, aliceryhl@google.com, boqun.feng@gmail.com, a.hindborg@kernel.org, tmgross@umich.edu, ojeda@kernel.org, alistair23@gmail.com Subject: [PATCH 07/18] lib: rspdm: Initial commit of Rust SPDM Date: Fri, 8 May 2026 13:16:59 +1000 Message-ID: <20260508031710.514574-8-alistair.francis@wdc.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260508031710.514574-1-alistair.francis@wdc.com> References: <20260508031710.514574-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 | 56 ++++++++ lib/rspdm/lib.rs | 116 ++++++++++++++++ lib/rspdm/state.rs | 235 ++++++++++++++++++++++++++++++++ lib/rspdm/validator.rs | 73 ++++++++++ rust/bindings/bindings_helper.h | 2 + rust/kernel/error.rs | 2 + 11 files changed, 564 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 882214b0e7db..2e8ad57fec5d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -24164,6 +24164,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..e23e386ed97a --- /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, + 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 00a9509636c1..5a248709132e 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -592,6 +592,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 f33a24bf1c19..ef2d33224d89 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -282,6 +282,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..2feddde67885 --- /dev/null +++ b/lib/rspdm/consts.rs @@ -0,0 +1,56 @@ +// 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) +//! + +/* SPDM versions supported by this implementation */ +pub(crate) const SPDM_VER_10: u8 =3D 0x10; + +pub(crate) const SPDM_MIN_VER: u8 =3D SPDM_VER_10; + +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, + /// This was removed in version 1.2.0 and is now reserved + 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, + RequestSessionTerminated =3D 0x46, + InvalidState =3D 0x47, + 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 { + writeln!(f, "{:#x}", *self as u8)?; + + Ok(()) + } +} diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs new file mode 100644 index 000000000000..758d43fba5cb --- /dev/null +++ b/lib/rspdm/lib.rs @@ -0,0 +1,116 @@ +// 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. + +use crate::bindings::spdm_state; +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. +#[export] +pub unsafe extern "C" fn spdm_create( + dev: *mut bindings::device, + transport: bindings::spdm_transport, + transport_priv: *mut c_void, + transport_sz: u32, + validate: bindings::spdm_validate, +) -> *mut spdm_state { + match KBox::new( + SpdmState::new(dev, transport, transport_priv, transport_sz, valid= ate), + flags::GFP_KERNEL, + ) { + Ok(ret) =3D> KBox::into_raw(ret) as *mut spdm_state, + 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. +#[export] +pub unsafe extern "C" fn spdm_authenticate(_state_ptr: *mut spdm_state) ->= c_int { + 0 +} + +/// spdm_destroy() - Destroy SPDM session +/// +/// @spdm_state: SPDM session state +#[export] +pub unsafe extern "C" fn spdm_destroy(_state_ptr: *mut spdm_state) {} diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs new file mode 100644 index 000000000000..18e81f24c724 --- /dev/null +++ b/lib/rspdm/state.rs @@ -0,0 +1,235 @@ +// 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_MIN_VER, + 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. +/// `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) 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, + validate: bindings::spdm_validate, + ) -> Self { + SpdmState { + dev, + transport, + transport_priv, + transport_sz, + validate, + version: SPDM_MIN_VER, + } + } + + 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) + } + SpdmErrorCode::RequestSessionTerminated =3D> { + pr_err!("Request session terminated\n"); + Err(EINVAL) + } + SpdmErrorCode::InvalidState =3D> { + pr_err!("Invalid State\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..58039f532b7d --- /dev/null +++ b/lib/rspdm/validator.rs @@ -0,0 +1,73 @@ +// 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, + /// This will always be SPDM_ERROR (0x7F) + 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 d73142078240..0231e4f87f20 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -87,10 +87,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index 5463addbfd95..386110a1e19f 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -89,6 +89,8 @@ 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!(EINPROGRESS, "Operation now in progress."); } =20 /// Generic integer kernel error. --=20 2.52.0 From nobody Sat Jun 13 10:31:02 2026 Received: from mail-pj1-f44.google.com (mail-pj1-f44.google.com [209.85.216.44]) (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 62E8A2F0680 for ; Fri, 8 May 2026 03:18:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210309; cv=none; b=ECLISMlL6ZlYVGVCzR6dLKrLIwlbl4ST6DVIKKMW1wehVIvnlkixtjL9BCp7PmGIuVslRYO6p1K5EkJ+DrruEAXfvYnx0VoxAa3qNAh75CW/tPB08SJ4tATTKWThE+i4zbcC7Cq8+5AHyie/a0eeUkwDx55UP43L2ximePOzKoc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210309; c=relaxed/simple; bh=L++LTy00lMS0Z9lJGdmdFlMR2op6hVxOgn4kFHOj63g=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ux0sGKxuTDAeuqiO0xC1tfC+KMbNg8+ZCoNxbzfBHkfmSkYDDlj6jKR4CJSkOFmyglXr6bnZwYt2v0bz+4tHQiEP8BjeDgqn7AQF4FWSlKkqxBX71WoRkXperBlHVCW1/l2LE74X6PL1DMuUfJKhdopdWlfRCh0tKEFQhK8eZrg= 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=luJNN9US; arc=none smtp.client-ip=209.85.216.44 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="luJNN9US" Received: by mail-pj1-f44.google.com with SMTP id 98e67ed59e1d1-364f7c42c62so1289588a91.0 for ; Thu, 07 May 2026 20:18:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778210305; x=1778815105; 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=DaECM3afZbtSVstcoN8c/Bxd1jLK8MEltAPqU9LiKz4=; b=luJNN9UStWQ9eytibJ2OU3m1xDtPOGuAPbgmn3NRUqFn2RXKSB372N7kP2zS/O6u1C tgqLNmqNVEx2lqTs9BpR4OGT+zvIfLsVMXA3BHVMPtyXBy/augp/ozTMwDHKEypEduug YH5q7aSwCCMWsne4haDQHyqksFulFOgJwTMzemcbKisWxGdSRr9shmy1NvPbuy0IdWBs IksE5SdWBACGQCSMph0m2zskToxWqo8/W/VJ23gsYl4lOr3AYqUDx+nkMoh0rgHKSa6r R+VjIpbJxzK1cOREGT4plIlq0prwY7DZFxi96aBpU9smY8SjAsIQqOPlFTWLEmTZBO3z K+sg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778210305; x=1778815105; 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=DaECM3afZbtSVstcoN8c/Bxd1jLK8MEltAPqU9LiKz4=; b=j7m9q838zGhh/Pyo/8bRXdDrOD0jSk9an1T3kd08SfCpzM8wmmW8jLdhOACj+X3uO4 EwW+UdiF3rpujpwlWDnw0Q+jVJ7MWiYpUlfJZL8BWHhhbMT61NWwHHMlXgIf14E7yaOo EpY5MBD8dkqVy/bB8BYW6ZTB6cIFiFuiBlLXUzzGilgLNp6QYijEQIBf96ULyhYLcmLB /fLzSiKzje9IDXdzv+TBRHD0kMQhDqUn2p7jVPhKEy3NxrcX9jNt0jap3mdRmxT/a6KX T2kxAfaxYEIITYspriyK+MJCbK6L/Zz7HqKx7veGaWZiVoztdOV24WzGuTcOaoo5j6c8 K20w== X-Forwarded-Encrypted: i=1; AFNElJ+RIXjeSBzvuwAECLfcqCdVkxFOzdN+Z6xCsZrEf3rvwxUiGeTHosJzVC0+vCOEaMG4koaEZqLw8DLTBW4=@vger.kernel.org X-Gm-Message-State: AOJu0YwkEtJdkBhnftuqheV2S5mIaVldP0whcohuFOOqbaION6Q1/JT3 4+qBu6H34LSCcz48NKN8DUeS8G3fzuOp6eoE77XqLzNLrcvlElldCZwu7WkxYQ== X-Gm-Gg: Acq92OHG/fjY8VrQTdlR8tMmJ4xfXk08GX/sr+6y9E0vTjWxBRYeqr8XZFG7hZ2KeVW W17dFHu3q+yvNl/imMv6tCGd7LihbYEZsEVEPT8m5c/jdg283g0Dv5bNYfh3Gtr2ZVkhDTXC0Vu ZNurQ13q/8/IiHmkb5IA1Awe9At6446P5SdfYV6ge3iAanvr1zM0nllBYGunJ3bnbHmD1FmASed t379nF7A4RTY6tm7kpAfNhzIdIJYetBPDK/dxJu6twHcvbTzkTzNPwDmclY7kgJxyJnt8OG59J/ DNLRPDkc5ph20XRyBz2158x36oV4g7qKWcvfg3IR5OV8QdEEivzgZWl0wTlRG5Nt7EIpWM2zBiL EUQ+ctl+RuUieSJheAiweG7S0eMRp3XCxDyddBiyC5P7UvW4S+1cgMKegV09By56fD14HVv5yfw 1yKRUH7qBIAcINcHaP0Fdf0c9/dh8i6hEfvMp/SwjT X-Received: by 2002:a17:903:3bcc:b0:2ba:4e84:976 with SMTP id d9443c01a7336-2ba78f45b67mr104924825ad.13.1778210304563; Thu, 07 May 2026 20:18:24 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2baf1eafa62sm3220685ad.74.2026.05.07.20.18.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 May 2026 20:18:24 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: alistair@alistair23.me, linux-kernel@vger.kernel.org, lukas@wunner.de, Jonathan.Cameron@huawei.com, bhelgaas@google.com, rust-for-linux@vger.kernel.org, akpm@linux-foundation.org, linux-cxl@vger.kernel.org, djbw@kernel.org, linux-pci@vger.kernel.org Cc: alex.gaynor@gmail.com, wilfred.mallawa@wdc.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, aliceryhl@google.com, boqun.feng@gmail.com, a.hindborg@kernel.org, tmgross@umich.edu, ojeda@kernel.org, alistair23@gmail.com, Alistair Francis Subject: [PATCH 08/18] PCI/TSM: Support connecting to PCIe CMA devices Date: Fri, 8 May 2026 13:17:00 +1000 Message-ID: <20260508031710.514574-9-alistair.francis@wdc.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260508031710.514574-1-alistair.francis@wdc.com> References: <20260508031710.514574-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 In the next patch we are going to add a PCIe CMA TSM driver, as such we need to ensure that is_pci_tsm_pf0() will allow us to connect to CMA capable devices. These devices don't necessarily has DEVCAP_TEE or IDE support. As such for Root Complex Integrated Endpoint (PCI_EXP_TYPE_RC_END) we also check for the CMA DOE feature. Signed-off-by: Alistair Francis --- include/linux/pci-tsm.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/include/linux/pci-tsm.h b/include/linux/pci-tsm.h index a6435aba03f9..5059954e4853 100644 --- a/include/linux/pci-tsm.h +++ b/include/linux/pci-tsm.h @@ -3,6 +3,7 @@ #define __PCI_TSM_H #include #include +#include #include =20 struct pci_tsm; @@ -129,6 +130,8 @@ struct pci_tsm_pf0 { /* physical function0 and capable of 'connect' */ static inline bool is_pci_tsm_pf0(struct pci_dev *pdev) { + struct pci_doe_mb *doe; + if (!pdev) return false; =20 @@ -146,9 +149,15 @@ static inline bool is_pci_tsm_pf0(struct pci_dev *pdev) * switch. */ switch (pci_pcie_type(pdev)) { + case PCI_EXP_TYPE_RC_END: + doe =3D pci_find_doe_mailbox(pdev, PCI_VENDOR_ID_PCI_SIG, + PCI_DOE_FEATURE_CMA); + + if (doe) + break; + fallthrough; case PCI_EXP_TYPE_ENDPOINT: case PCI_EXP_TYPE_UPSTREAM: - case PCI_EXP_TYPE_RC_END: if (pdev->ide_cap || (pdev->devcap & PCI_EXP_DEVCAP_TEE)) break; fallthrough; --=20 2.52.0 From nobody Sat Jun 13 10:31:02 2026 Received: from mail-pl1-f175.google.com (mail-pl1-f175.google.com [209.85.214.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 0FF112F3C37 for ; Fri, 8 May 2026 03:18:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.175 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210318; cv=none; b=Df0tAb3zz2BIVNStINPUlPnWU7mip9lsaqepm75hQHflxueQX7Iw6RLK0FIYQnJzJlmYWEVjYhzibqd26dzPwmuljDfgIS2tByV1pTDiMPuHFd5r+UkOVpfirQne0M5PHi5Y6EJmKJsC0H2s1bWi6K+ZBOHvWVErpJxbDjXdQa8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210318; c=relaxed/simple; bh=MkGBnlqcPkrjbetz6CR3OU3ygvsKKQd9u7prbxNnQb8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=KDi6Bfl2wS95bZGpS+ODLskfu0bbW9ejLlyqUH7Ljc6d8YERLXVHIm/PL+1boagkriTeZDDJ1wKGhsCI4/RQBx/BkVuJX3AnHjy3sQo35KhvFHtrH7IWlqsG+RisHOo4blFdxV4NmrEPwTO/mVe5uws+CSSejf9Bt+kbwxNRwBk= 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=UtV3RSiq; arc=none smtp.client-ip=209.85.214.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="UtV3RSiq" Received: by mail-pl1-f175.google.com with SMTP id d9443c01a7336-2ad9a9be502so8902735ad.0 for ; Thu, 07 May 2026 20:18:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778210312; x=1778815112; 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=4ZDNMlvL+OebdDkipENIfQ0eTX10FcJgg4MaM4qp9cc=; b=UtV3RSiqhG63kRqMzxQOPtj7OqmR2fYfNs/2aS0tY8Sm03/Rtf33JmDOqNpjXKwM04 8G/sZFlwkYHJjMqK08rWNsn9SIfZXCWR12fU94y01n9M71OxS0nWXEBFYtSB3ZAIgGjp qapoy95EcpagTR1ojAPbQ4+NXrEADnrK/GSmR6T5PTX+TNU0pU8Kz7F5Ic4uNQLLdl8X bRHY3HCAdO1grCWBj21mHdmcK0kgjVy1qxL4LtDjHor8H+sUPukMS472VEgGFB1ND922 N2fn2U8YyLder5GHTsdzdJBv/LafZMOXflM90LiLmcHB8CEuQzOEUybhZlaq/Qpk7z+J OQCA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778210312; x=1778815112; 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=4ZDNMlvL+OebdDkipENIfQ0eTX10FcJgg4MaM4qp9cc=; b=YlRwW1jDc1GutEhiGB9dN1ZBaQg0wXd3dt+weBLAnb8z9zQiR9l5nqfnmTKqW4Egkm sGnPQl0FUhiofV33ZVQmBkCCuliZjodaH6LeOFLM1oamsjo4fgpuucEMztxz8+Zw1mbb V/DFle0f8XQOcT33x62t0SLA+rEVnZb99lUAIIBXewwnhZsUFIYze7eBV4WAsF8wW/j2 6DyyPPo5fOaCmcVyn9YaiEtE5NgpivKDfD/LQfu87EOwtf6Fs8DejFvkg9+TsJQM202g fsQcusrCWTIxySM+KpkrB9G31H5mgvmg5CwlTHsmj2BwNR51XVH+cUud/juH3P21MLSt zfQA== X-Forwarded-Encrypted: i=1; AFNElJ99TNUkfko60EuLj6gbzWMaahEpr9sB6xjRhaYZFJ9KMk6qhM1JOMZNC0fUJEBs8JChLmprwOVqtqDPyKQ=@vger.kernel.org X-Gm-Message-State: AOJu0Yx3SHjquNjzY1TTIsz4qBHnow/0k121R61u+FdZt1kNxzfvcqU9 N+qygtFuozE5CsWWopx5HPLqg7r8RG3OXjNPpLvQ8EGH44dysG3J+xsV X-Gm-Gg: Acq92OH//hO+GWOBb4ybVL+n0kilj7e4bitXzKRAdQvuXyzjkZCqzRJc14S3pPyWMbs cfQ1HDSYKzOp42GcyNExVXuzq1xqL58+w7YKxDVRmR6/r882/64+obIueCEEgdChEb0LDj6Qit2 E1/6TwUt5+Yiw5lC4iVfmBChFbZfP90tFxx++dwdtG5048ZarIss/dVtViwqksl6tdzNTGMUkIE RI97UEYJPlOBxV6BIT26u/SLjrhXdTxFX44GjDWTj1uWILBbuIZ9yv8xD0H7P9I/v4hKla9iciO 2Ypk8eK3i0T2+n1Fdku7Gp0trz/YGtwdfAMLNFIMktQAysBSHtIJ/4sait7cYWtP/3JAr2f8beB jQIMxzee5c6/BtM3G/bLvgz/y1de6inmkJ853D12Qi6cDlY6i4rjXPatMUkSJ4curzl7+mQQmdY Uy4c9P1NEGmaya6wugW/VL73zHvC+43mT5MemOk0Z2 X-Received: by 2002:a17:903:22c4:b0:2ba:e66b:ce34 with SMTP id d9443c01a7336-2bae66bd563mr27232095ad.21.1778210312457; Thu, 07 May 2026 20:18:32 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2baf1eafa62sm3220685ad.74.2026.05.07.20.18.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 May 2026 20:18:31 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: alistair@alistair23.me, linux-kernel@vger.kernel.org, lukas@wunner.de, Jonathan.Cameron@huawei.com, bhelgaas@google.com, rust-for-linux@vger.kernel.org, akpm@linux-foundation.org, linux-cxl@vger.kernel.org, djbw@kernel.org, linux-pci@vger.kernel.org Cc: alex.gaynor@gmail.com, wilfred.mallawa@wdc.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, aliceryhl@google.com, boqun.feng@gmail.com, a.hindborg@kernel.org, tmgross@umich.edu, ojeda@kernel.org, alistair23@gmail.com, Alistair Francis Subject: [PATCH 09/18] PCI/CMA: Add a PCI TSM CMA driver using SPDM Date: Fri, 8 May 2026 13:17:01 +1000 Message-ID: <20260508031710.514574-10-alistair.francis@wdc.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260508031710.514574-1-alistair.francis@wdc.com> References: <20260508031710.514574-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 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, add support for authentication via a CMA TSM driver. This was previously discusd here: http://lore.kernel.org/69976d7d39c60_2f4a1009@dwillia2-mobl4.notmuch By utilising a TSM driver we get a lot of the TSM driver probe policies "for free". Currently there is no mechanism to provide evidence to userspace, as the TSM system doesn't support that at the moment. That can be added later when support by TSM. Credits: Jonathan wrote the original proof-of-concept for a CMA implementat= ion. Lukas reworked that for upstream. Wilfred contributed fixes for issues discovered during testing. Alistair reworked it as a TSM driver. 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 Signed-off-by: Alistair Francis --- MAINTAINERS | 1 + drivers/pci/Kconfig | 14 ++++ drivers/pci/Makefile | 2 + drivers/pci/cma.c | 141 ++++++++++++++++++++++++++++++++++++++++ drivers/pci/doe.c | 3 - include/linux/pci-doe.h | 4 ++ 6 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 drivers/pci/cma.c diff --git a/MAINTAINERS b/MAINTAINERS index 2e8ad57fec5d..c99fc6ae2d03 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -24173,6 +24173,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 33c88432b728..dcf4170381f2 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -124,6 +124,20 @@ 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 PCI_TSM + 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_TSM bool "PCI TSM: Device security protocol support" select PCI_IDE diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 41ebc3b9a518..16abfd0e17e1 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -41,6 +41,8 @@ obj-$(CONFIG_PCI_NPEM) +=3D npem.o obj-$(CONFIG_PCIE_TPH) +=3D tph.o obj-$(CONFIG_CARDBUS) +=3D setup-cardbus.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..998fde6366fb --- /dev/null +++ b/drivers/pci/cma.c @@ -0,0 +1,141 @@ +// 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 + * Copyright (C) 2026 Western Digital + * Alistair Francis + */ + +#define dev_fmt(fmt) "CMA: " fmt + +#include +#include +#include +#include +#include +#include + +#include "pci.h" + +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; + + rc =3D pci_doe(doe, PCI_VENDOR_ID_PCI_SIG, PCI_DOE_FEATURE_CMA, + request, request_sz, response, response_sz); + + return rc; +} + +/** + * struct pci_cma_tsm - CMA SPDM TSM driver context + * @pf0: base pci_tsm_pf0 context (must be first) + * @spdm: SPDM session for this device + */ +struct pci_cma_tsm { + struct pci_tsm_pf0 pf0; + struct spdm_state *spdm; +}; + +static struct pci_cma_tsm *cma_tsm_from_tsm(struct pci_tsm *tsm) +{ + struct pci_tsm_pf0 *pf0 =3D container_of(tsm, struct pci_tsm_pf0, base_ts= m); + + return container_of(pf0, struct pci_cma_tsm, pf0); +} + +/** + * struct pci_cma_devsec - CMA SPDM devsec TSM context + * @spdm: SPDM session for this device + */ +struct pci_cma_devsec { + struct spdm_state *spdm; +}; + +static struct pci_tsm *pci_cma_tsm_probe(struct tsm_dev *tsm_dev, + struct pci_dev *pdev) +{ + struct pci_doe_mb *doe; + struct pci_cma_tsm *cma; + + doe =3D pci_find_doe_mailbox(pdev, PCI_VENDOR_ID_PCI_SIG, + PCI_DOE_FEATURE_CMA); + if (!doe) + return NULL; + + cma =3D kzalloc(sizeof(*cma), GFP_KERNEL); + if (!cma) + return NULL; + + mutex_init(&cma->pf0.lock); + cma->pf0.doe_mb =3D doe; + cma->pf0.base_tsm.pdev =3D pdev; + cma->pf0.base_tsm.dsm_dev =3D pdev; + cma->pf0.base_tsm.tsm_dev =3D tsm_dev; + + cma->spdm =3D spdm_create(&pdev->dev, pci_doe_transport, doe, + PCI_DOE_MAX_PAYLOAD, NULL); + if (!cma->spdm) { + mutex_destroy(&cma->pf0.lock); + kfree(cma); + return NULL; + } + + return &cma->pf0.base_tsm; +} + +static void pci_cma_tsm_remove(struct pci_tsm *tsm) +{ + struct pci_cma_tsm *cma =3D cma_tsm_from_tsm(tsm); + + spdm_destroy(cma->spdm); + mutex_destroy(&cma->pf0.lock); + kfree(cma); +} + +static int pci_cma_tsm_connect(struct pci_dev *pdev) +{ + struct pci_cma_tsm *cma =3D cma_tsm_from_tsm(pdev->tsm); + int rc; + + rc =3D spdm_authenticate(cma->spdm); + if (rc) + return rc; + + return 0; +} + +static void pci_cma_tsm_disconnect(struct pci_dev *pdev) +{ + /* SPDM state is freed in pci_cma_tsm_remove() */ +} + +static const struct pci_tsm_ops pci_cma_tsm_ops =3D { + .link_ops =3D { + .probe =3D pci_cma_tsm_probe, + .remove =3D pci_cma_tsm_remove, + .connect =3D pci_cma_tsm_connect, + .disconnect =3D pci_cma_tsm_disconnect, + }, +}; + +static struct tsm_dev *pci_cma_tsm_dev; + +static int __init pci_cma_tsm_init(void) +{ + struct tsm_dev *tsm_dev; + + tsm_dev =3D tsm_register(NULL, (struct pci_tsm_ops *)&pci_cma_tsm_ops); + if (IS_ERR(tsm_dev)) + return PTR_ERR(tsm_dev); + + pci_cma_tsm_dev =3D tsm_dev; + return 0; +} +late_initcall(pci_cma_tsm_init); diff --git a/drivers/pci/doe.c b/drivers/pci/doe.c index 7b41da4ec11a..f236942660a3 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/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 --=20 2.52.0 From nobody Sat Jun 13 10:31:02 2026 Received: from mail-pj1-f46.google.com (mail-pj1-f46.google.com [209.85.216.46]) (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 7B0402DECBA for ; Fri, 8 May 2026 03:18:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210327; cv=none; b=szth9gkwtowiUq29BIBKWcK9JRea1i73XyLWg1Ikzwk/xR8na1kcHduILbFSMSlUWsLQzrQLlzMozg44vBog5GKdzcU5VPZtEizota6ZuYDU3W5dP0WkPNUF8ZiHw7cS9hvaUAiMEHArqLMzbXBeUs4vOCs8MjmctJ42oxZhgYc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210327; c=relaxed/simple; bh=dxOLfnidXHIPF44qYjA6iMj3D0ywoc3U+N9lkLLSMyY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Ntt63ITyFQzVJhAcZuNlmGsmuUuEPJ7vyyz/vCdaLbqgK/4ktRqc+LB+Pq3IIewcESnFtUQghJMHluWwXU/Nv/UdpJRHNRzVhNWI+UV4ITowSvzyShcR3yMseHYiBXTHWZi/FuceDaTVT+8Vr0WP9WK4WEGFy537t46FZdZNPQw= 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=OWFzOtHH; arc=none smtp.client-ip=209.85.216.46 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="OWFzOtHH" Received: by mail-pj1-f46.google.com with SMTP id 98e67ed59e1d1-36505450d0dso1658835a91.1 for ; Thu, 07 May 2026 20:18:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778210320; x=1778815120; 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=5fOXX2qAUnj9LLpneLJIQV+9d5i1z0dWTv1dvOWS9wE=; b=OWFzOtHHsq+B8bgFW1EtQfnerOpTFEtZfUIFFgyusJa3w66MF3RuxQQQyHK23Y2Oui Oz83v07gyHSnjG5w3dNUSKoRdPLol9FiXwLTmRvmrLmlFYF93lUDDhtKkNxsN6ywG371 WyVVHrIST0STErN4Lrmgtz/fg7JnCJ8C82XncgwEkMeYkjqe7Hw2v9irqFpJKttG1lNl i3BJyd4WUZn3FqDcwfMbMDzsCPDWXlAYu2h55A+Lw0xsSkRul5XNki4UNzR0O/a1gTOV hvIUialmYlrDUS3eRAO8YKsET0CA+fjSlEdi0Rn/zbHvxFLZ+z9s/lI8ytDxyHc4sND9 dJmw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778210320; x=1778815120; 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=5fOXX2qAUnj9LLpneLJIQV+9d5i1z0dWTv1dvOWS9wE=; b=lDvy5EHIOlZWlc7Lyjv6sGqMjmwOPEZnmtQf9SKNeAtKY6nu9OmYUynm4gQ7kR7Fqa 5B3B1gKuI6+wAQgpmAgZYTaLt7jYFa5Xgbdyd66ZXXne5DCLJMtKIWGenyzL8fi5Y5GK PdccFd1+B9mLx903SPLTgRle4wAwdDF97VSi4GVyOrQbhQN6RrGB9eAwH4v292rhwjmR 1LtQR5nxQN2ptcIrzVINRF7IFlkhFxEPJRGjnZaGmFIFE6Uah7Yg8IwsrRTcmNO1OQWt ZohCaYV/Dk47eqQT3Zj9+mQE0ZMtdcI21QtmCD+R9v0NVm8QNX0xTlrYrKKuqtDfLviL qsgg== X-Forwarded-Encrypted: i=1; AFNElJ+UaTQHtG3ADK1gw1xryV8O7IyIIgfbT+BPisKp99PeT09TQzgJSsiqpMMERrxXW5y4K13lNmECgU1FcJ0=@vger.kernel.org X-Gm-Message-State: AOJu0YxOURsDjnyABnwHmGXpa+Cjy6ZRW/de1TT/v1IVzLLZp4T0GuSS G67acZcqce32aYQJ1u+XOxzD67AC6mQZNqdgB/eOlG7DQnxGJHKuQwfw X-Gm-Gg: Acq92OEnxMmI59KFdg9VldhEkAh7JjXT/sDAgpVO+/mOKIK67jYA7gC5yrQYq4sItTk LazRImm3b0R9j0Pgjdvrg55n/Fy9phYhUytAfDK2ZruXZpTC2IRUdY7ckBP+8QHfuI2Srt0ubjU ApHnYwNXYtH52ncHfO8qwnMMyhRrQb8ETYaVvmx9r6TKjRwwNX+0Jsi8X5VVdUR73p4Yng0KLBl GQOy2boMOdh+rZVgEKMqXFaI/miK53aoL9a7EjJ8Pv2kgZg4FLuXL9alKYNIy/7CAlw4wMhPMiu nv+XxrmC8thPP73Ey4MgON7WzqXj0X1KA39zFVxiON4PtSfCGIrCRBM3sudoxozh73LdJhh/Bg9 WEvAgxrF9SyQVRyhBBIO4Zb75JMfEHRuot1S/nP8/WVqsKVSSJErhSvtQwy7Z4xI2C6Lvxg5BVa qxiVmLsCXCWtodwwe+1tY/m3gbD79CXPJHocG7K26H X-Received: by 2002:a17:90b:2d0d:b0:35f:bd29:75b9 with SMTP id 98e67ed59e1d1-365ac670f60mr11374479a91.22.1778210319770; Thu, 07 May 2026 20:18:39 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2baf1eafa62sm3220685ad.74.2026.05.07.20.18.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 May 2026 20:18:39 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: alistair@alistair23.me, linux-kernel@vger.kernel.org, lukas@wunner.de, Jonathan.Cameron@huawei.com, bhelgaas@google.com, rust-for-linux@vger.kernel.org, akpm@linux-foundation.org, linux-cxl@vger.kernel.org, djbw@kernel.org, linux-pci@vger.kernel.org Cc: alex.gaynor@gmail.com, wilfred.mallawa@wdc.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, aliceryhl@google.com, boqun.feng@gmail.com, a.hindborg@kernel.org, tmgross@umich.edu, ojeda@kernel.org, alistair23@gmail.com Subject: [PATCH 10/18] PCI/CMA: Validate Subject Alternative Name in certificates Date: Fri, 8 May 2026 13:17:02 +1000 Message-ID: <20260508031710.514574-11-alistair.francis@wdc.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260508031710.514574-1-alistair.francis@wdc.com> References: <20260508031710.514574-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 16abfd0e17e1..15512512fce7 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -41,7 +41,9 @@ obj-$(CONFIG_PCI_NPEM) +=3D npem.o obj-$(CONFIG_PCIE_TPH) +=3D tph.o obj-$(CONFIG_CARDBUS) +=3D setup-cardbus.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 998fde6366fb..ee186f361940 100644 --- a/drivers/pci/cma.c +++ b/drivers/pci/cma.c @@ -11,6 +11,9 @@ =20 #define dev_fmt(fmt) "CMA: " fmt =20 +#include +#include +#include #include #include #include @@ -18,8 +21,126 @@ #include #include =20 +#include "cma.asn1.h" #include "pci.h" =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; +} + static ssize_t pci_doe_transport(void *priv, struct device *dev, const void *request, size_t request_sz, void *response, size_t response_sz) @@ -80,7 +201,7 @@ static struct pci_tsm *pci_cma_tsm_probe(struct tsm_dev = *tsm_dev, cma->pf0.base_tsm.tsm_dev =3D tsm_dev; =20 cma->spdm =3D spdm_create(&pdev->dev, pci_doe_transport, doe, - PCI_DOE_MAX_PAYLOAD, NULL); + PCI_DOE_MAX_PAYLOAD, pci_cma_validate); if (!cma->spdm) { mutex_destroy(&cma->pf0.lock); kfree(cma); 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 Sat Jun 13 10:31:02 2026 Received: from mail-pl1-f177.google.com (mail-pl1-f177.google.com [209.85.214.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 67076305E19 for ; Fri, 8 May 2026 03:18:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.177 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210332; cv=none; b=Vt3oyRjO/IkFWHWovQg/XRlOHH4aKZLX98vwtBZs+tP9Ts250COfLWzz56Fff+90Bd1NmC99qrcxz96wDiDjoEgmhig80DLo6WWY/bt6EShBq6p+jyajG8qaBKfYafN8dYuFjnxeoipl1Vg5mbKRG2UyFnv0Iey1E7q9mlLoE/I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210332; c=relaxed/simple; bh=oxNrGjcZCazizcSxSSIR96HPVNGWilr6D9NtWMsT9rw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=gHP1TCb0kogIWy8WJX5Jq52Mv62GCGi7Vl3XR9W0GZmP7OiaihAWg+Dz8EDiRjlwGqmCZHuJ1j3exQYwUma5jtC2rPyvUfB7Z+AZJ8eYoZ0lMFqZx4rZX3zRFZRLGvu+dQt7KO2cW0TV7DZf6wO3KYQqb7wWn2YRwhxYbu3bMx4= 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=a+a6o51L; arc=none smtp.client-ip=209.85.214.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="a+a6o51L" Received: by mail-pl1-f177.google.com with SMTP id d9443c01a7336-2ad9a9be502so8903285ad.0 for ; Thu, 07 May 2026 20:18:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778210327; x=1778815127; 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=sdaY8XVDz6HV3abuC4HHO5UewMGlDvcsArdslgC4GX0=; b=a+a6o51LnORkrj8YRpE5Xy8JhOmLgr/EPwjAL1nxVAAUF7J/76zW8cfLLpMBpc+jns XcNxlEblsg+g11U2adOV+fhOn2uS9A6CY3/bNdrFONHCmqaIAsgl0q/hr5WfpCg8bM5b 6vf/vijWohGSxuTGnjTDAbGb0ACVYYMjaGSu0RObcl2b5X+i20WV80p62/qFMKtB6821 RRr5M3CCWanHzG5v+6a+O1729SgJnff7wzrEFHwSxKZetRdHfXOAhzH2ojVYlwoIoJ2a 0QMvd14MRAivzBwj11Ad1oHAyb9kYXAvUf8hmH9dkE1ixQNdqyt0N1XL+SmuK+9NffPp NNlQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778210327; x=1778815127; 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=sdaY8XVDz6HV3abuC4HHO5UewMGlDvcsArdslgC4GX0=; b=CYIsm162alknCVBnaLTgQwQ8HztdXDYFVF1Y3QQti4nBqqWXPiguJLDA/gsozoy8Rs g9B4WdEKxA0oEy104jzsR4QsGjQ7m5q0wCav7fzbJ8rZcfIGu8TvHLMNcQdJLg8lXx+O EoLGZSRi8yvnoRWTV+QoPREyayaCxNbLThsqDeTfLzqXKO04FzpyqDLZSHvsXLcGjd9V Dh8spwa45upwXsPNAiAWo+MS7ST3/hxxR30lm0cEqBAyrODuVbsN9XZ+q64yKbNL7GJt LIk73DhGuku6EStjJOoXX91D4a85uFvUGq/aKLjV+oQ3yF0FnfIdEBeqUSjWju8EB4Px TdcQ== X-Forwarded-Encrypted: i=1; AFNElJ88UubX3e2AtDbuKpGVlzBn1+mbquyf/oALS1AAQoLQ4jTaiQXuo79XMgynYBHWmuX6wWpeDLWcRRJn3WI=@vger.kernel.org X-Gm-Message-State: AOJu0YwB41iR3E2Ikywjv75m+cJomjd/L9j8vvI1z9DfQWR4ZHEKD9VZ Wj/x2L07feLJOkHCcM9SiLWInbfec7juS0FyJPaDSpMrQ4Fv6zCWGIcb X-Gm-Gg: Acq92OEq5ryIlL4l3pCDh6WsGXiuk2Mrj8jbKd3JFYee0IJxnQPNT62ZiyTTeI/A2vY IkZwJ5Y6xBiyIhagFOJ91kHFqClRGq2IRf+M+nV3F99M3aNrNQ91NsnwfxpviHQqYNRfSJW7r5p vg4+hPn4FApiIwtwoKfaTRyZV2hPr5qBRn58btajajhmEoh9PB+kSVdtOS6hzpOxfHMxJPMROB8 BdE5GOBXKDtX1u/8EZg7DUCDXScz6rWryEehPsT7bdg0rGFvgD1/vS0ijXiSRn/l18/qVEb+Jdu 8IMtGIgrRmhoA3Qu0u4stA22fxiHfdd8gLL+6zI40A4CKmfYWbpRLwRlY2jWErqofLgcY99Cves 6U5J0jtCIRERH7BxHG8b/uAb2wCzx8SNm2QtlFAF3/w+5rwKrZVq5t0mVLLt8t+8DrOEwrwnvU9 X8iyxhjJ3O1cjEhwYlPEu7eQcEw+FI251H9GQ+fDbn X-Received: by 2002:a17:903:1af0:b0:2ad:bd4c:a5 with SMTP id d9443c01a7336-2ba78f63c82mr112714875ad.1.1778210327123; Thu, 07 May 2026 20:18:47 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2baf1eafa62sm3220685ad.74.2026.05.07.20.18.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 May 2026 20:18:46 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: alistair@alistair23.me, linux-kernel@vger.kernel.org, lukas@wunner.de, Jonathan.Cameron@huawei.com, bhelgaas@google.com, rust-for-linux@vger.kernel.org, akpm@linux-foundation.org, linux-cxl@vger.kernel.org, djbw@kernel.org, linux-pci@vger.kernel.org Cc: alex.gaynor@gmail.com, wilfred.mallawa@wdc.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, aliceryhl@google.com, boqun.feng@gmail.com, a.hindborg@kernel.org, tmgross@umich.edu, ojeda@kernel.org, alistair23@gmail.com Subject: [PATCH 11/18] lib: rspdm: Support SPDM get_version Date: Fri, 8 May 2026 13:17:03 +1000 Message-ID: <20260508031710.514574-12-alistair.francis@wdc.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260508031710.514574-1-alistair.francis@wdc.com> References: <20260508031710.514574-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 | 14 +++++++++ lib/rspdm/lib.rs | 8 ++++- lib/rspdm/state.rs | 61 ++++++++++++++++++++++++++++++++++++ lib/rspdm/validator.rs | 70 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 152 insertions(+), 1 deletion(-) diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs index 2feddde67885..5482a0f6cee0 100644 --- a/lib/rspdm/consts.rs +++ b/lib/rspdm/consts.rs @@ -7,10 +7,21 @@ //! 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; +#[allow(dead_code)] +pub(crate) const SPDM_VER_13: u8 =3D 0x13; +pub(crate) const SPDM_VER_14: u8 =3D 0x14; =20 pub(crate) const SPDM_MIN_VER: u8 =3D SPDM_VER_10; +pub(crate) const SPDM_MAX_VER: u8 =3D SPDM_VER_14; =20 pub(crate) const SPDM_REQ: u8 =3D 0x80; pub(crate) const SPDM_ERROR: u8 =3D 0x7f; @@ -54,3 +65,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::() + u8::MAX as usize; diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs index 758d43fba5cb..1cc6c33516fb 100644 --- a/lib/rspdm/lib.rs +++ b/lib/rspdm/lib.rs @@ -105,7 +105,13 @@ /// Return 0 on success or a negative errno. In particular, -EPROTONOSUPP= ORT /// indicates authentication is not supported by the device. #[export] -pub unsafe extern "C" fn spdm_authenticate(_state_ptr: *mut spdm_state) ->= c_int { +pub unsafe extern "C" fn spdm_authenticate(state_ptr: *mut spdm_state) -> = c_int { + let state: &mut SpdmState =3D unsafe { &mut (*(state_ptr as *mut SpdmS= tate)) }; + + 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 18e81f24c724..26b90942b298 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, @@ -22,10 +23,14 @@ use crate::consts::{ SpdmErrorCode, SPDM_ERROR, + SPDM_GET_VERSION_LEN, + SPDM_MAX_VER, SPDM_MIN_VER, SPDM_REQ, // }; use crate::validator::{ + GetVersionReq, + GetVersionRsp, SpdmErrorRsp, SpdmHeader, // }; @@ -232,4 +237,60 @@ pub(crate) fn spdm_exchange( =20 Ok(length) } + + /// Negotiate 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: `response_vec` is SPDM_GET_VERSION_LEN length, initiali= sed, aligned + // and won't be mutated + 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 + // than 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; + let unaligned =3D core::ptr::addr_of_mut!(response.version_number_= entries) as *mut u16; + + 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 addr =3D unaligned.wrapping_add(i as usize); + let alpha_version =3D (unsafe { core::ptr::read_unaligned::(addr) } & 0xF) as u8; + let version =3D (unsafe { core::ptr::read_unaligned::(add= r) } >> 8) as u8; + + if alpha_version > 0 { + pr_warn!("Alpha version {alpha_version} is unsupported\n"); + } + + 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 58039f532b7d..8f45bafd4d69 100644 --- a/lib/rspdm/validator.rs +++ b/lib/rspdm/validator.rs @@ -7,6 +7,10 @@ //! 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::*; @@ -21,6 +25,11 @@ }, }; =20 +use crate::consts::{ + SPDM_GET_VERSION, + SPDM_MIN_VER, // +}; + #[repr(C, packed)] pub(crate) struct SpdmHeader { pub(crate) version: u8, @@ -71,3 +80,64 @@ 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 =3D *(raw.get(0).ok_or(ENOMEM))? as usize; + if version !=3D SPDM_MIN_VER.into() { + 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 * mem::size_of::<__le16>() + mem::size_= of::(); + 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 }; + + Ok(rsp) + } +} --=20 2.52.0 From nobody Sat Jun 13 10:31:02 2026 Received: from mail-pl1-f180.google.com (mail-pl1-f180.google.com [209.85.214.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 AFF813101B9 for ; Fri, 8 May 2026 03:18:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210339; cv=none; b=gIqt1OSeE9dQYffIA+1HQ1kKNUWmNc8IjkBMOJttoFjAbtzna1wBuUSvfYKalDd4PwuenYl5OBAfZ2hZZsIQyRIcJIa4oMwUjElFbwIBjI/7rEE8XtT1agbHzRAbSwh87IA3wqtWpyT7SLs4d8LTHuScwcfQfrMzjbNdgs7OSEA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210339; c=relaxed/simple; bh=pgki4w7RWc81YTCI0ulFXlX7HnZjkRUDYahaKFjwBrY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RyLszExbdeXQa4Mgketky6Zoly0XlEDPlacpnhW2j9H7ApYJYT+XBKvc6zXj5DRyCZHNfnPEX8Y5h/TnqEyci+IoMWIlCgL/2L1r5qhs9WFe6rpcy81NXoGP2dBb+Ptk6StE2GfDrTdM3swyMtL0nO3SVzGX11j4MnuvGDWWyNU= 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=s5yyXS3T; arc=none smtp.client-ip=209.85.214.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="s5yyXS3T" Received: by mail-pl1-f180.google.com with SMTP id d9443c01a7336-2a7a9b8ed69so13938505ad.2 for ; Thu, 07 May 2026 20:18:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778210334; x=1778815134; 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=VcbWqg6jBmtAgedbxHyaToXZ0xvL9n8c+k1cA5Ncp9c=; b=s5yyXS3Tq4hRomHX1+ENW+n73wPP/CbxuD5Z8LCDEw94z6LeEdsEortXwfia4YNHBc SCp52PXZzYkRqZ9fJH0zSeHgLFV37EmA1Bdls8ffa59xfqstqR10e9P7KOmbb4dQ1TcB LZ3MmpZfkaHaW+kAluIGe8VVjvmfJg2gCb+k22Z8LKpRJgZvnXBLhXWyCKPtM1JfvBSE mq+q4x0VnncjdqnT6x96kRQO8tfvFW7aA2ItuIV7bEBuiE3j6j7BavoPSNkwUyec+90g 80p5kRgWWMTz/WIX6xGYRLA1m7K404OXvjheprNDtMgThPc7BUs1yuv80QoA1xDWLweP GlKQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778210334; x=1778815134; 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=VcbWqg6jBmtAgedbxHyaToXZ0xvL9n8c+k1cA5Ncp9c=; b=oHHPeHPC1S/fIiQ74h3Fw9t4xB4mA8YRCzhRGGZQ7MrIL8xkKkbDX7s/AyUAvTC/aQ LpSBZ2dY4p6dj9OVQkbtakpWfApdPebbe7cWi3oGcN+TvmJKbmnuz67v3TiY18w8S+nJ Vvz3OTceNCLn/DuHYwJPgiqPERMQ6pR5oc1oz3lOYelN42JdJzgOFW69NvNWWlbGaedg k/QLphpA79Fq/5kNqwxBZXdD/EVdMPnRwkwZ8Qt/Ysvmm+TjO6gGP8gV068n4OlIk5KY wNj8BuGQkB8jfxU0V2dCVCvUQ+9tkvObzjkIRg+yLWlgWlgix++g7v27jheAsvS2aVAO 27xg== X-Forwarded-Encrypted: i=1; AFNElJ9G9WHCFbFDR7Na29j0JdADJxL39ZYwv0pmKl/sLrlEk9vWdpV93Ln5OWbEccdT3KR3RYivLiNVk5qICZ0=@vger.kernel.org X-Gm-Message-State: AOJu0YzVmiVaVqsRGPPaBKe/vpahdc/IhMm+FaW944dV2MuAxuDmb4AC IdiGUzXQbsT1GGmlc+gvg+rLiLgZ08y72wgfDa550HXbK/97Du/36h3Z X-Gm-Gg: Acq92OGjO+YhPBsgi0Z9rWdsEbvdppuObPj8lGJM3KyPB6BAmBfSDy/0BP0nlNbfxef vCvAcKRmtjfEu8s6aEiR7w4zvjyX6RgKuh/2+3I7oIlGDTgMLMrKeIvR8B/toY6G9kTyHoSnDUS 6kIOI2CO2ELhaUsYUMBiHnWgOOKaV0tvbQ7cQVK0aw3GC64Jlwe5tH9c7/RjoMtv1dfnJASrXGC iROQJiN+00XZROnFuAmuU8mVJp/KF7HAEwGcs9aF2UU3f4BjKj2k/QhQsXsZclloJss6lpaubxJ 4OPSUr0Rw2BVsMoFo5TOKToHVioxLc35fw+LtDrvWDvT+RWIqn6QUy6l2O/fDzpHUC4QQyHotnF IqNlWTbvV6O9EKeq/l13wSJQadnUV7iIgL/D7MJHNjrbJCFeGwX01HEa8Xa1/C6ryVO5Q/E35YK OZMOuShtX61S42ltXiZavE0EuhmN3PfV3Xu4lbpLR+WSn3dthgOPE= X-Received: by 2002:a17:902:fa07:b0:2b2:549f:7d2b with SMTP id d9443c01a7336-2ba794b9766mr73480655ad.11.1778210334411; Thu, 07 May 2026 20:18:54 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2baf1eafa62sm3220685ad.74.2026.05.07.20.18.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 May 2026 20:18:53 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: alistair@alistair23.me, linux-kernel@vger.kernel.org, lukas@wunner.de, Jonathan.Cameron@huawei.com, bhelgaas@google.com, rust-for-linux@vger.kernel.org, akpm@linux-foundation.org, linux-cxl@vger.kernel.org, djbw@kernel.org, linux-pci@vger.kernel.org Cc: alex.gaynor@gmail.com, wilfred.mallawa@wdc.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, aliceryhl@google.com, boqun.feng@gmail.com, a.hindborg@kernel.org, tmgross@umich.edu, ojeda@kernel.org, alistair23@gmail.com Subject: [PATCH 12/18] lib: rspdm: Support SPDM get_capabilities Date: Fri, 8 May 2026 13:17:04 +1000 Message-ID: <20260508031710.514574-13-alistair.francis@wdc.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260508031710.514574-1-alistair.francis@wdc.com> References: <20260508031710.514574-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 | 19 ++++++++- lib/rspdm/lib.rs | 4 ++ lib/rspdm/state.rs | 76 ++++++++++++++++++++++++++++++++- lib/rspdm/validator.rs | 97 +++++++++++++++++++++++++++++++++++++++++- 4 files changed, 192 insertions(+), 4 deletions(-) diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs index 5482a0f6cee0..ef7d2d1d8e6e 100644 --- a/lib/rspdm/consts.rs +++ b/lib/rspdm/consts.rs @@ -9,12 +9,11 @@ =20 use crate::validator::SpdmHeader; use core::mem; +use kernel::bits::bit_u32; =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; #[allow(dead_code)] pub(crate) const SPDM_VER_13: u8 =3D 0x13; @@ -68,3 +67,19 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core:= :fmt::Result { =20 pub(crate) const SPDM_GET_VERSION: u8 =3D 0x84; pub(crate) const SPDM_GET_VERSION_LEN: usize =3D mem::size_of::() + u8::MAX as usize; + +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 bit_u32(1); +pub(crate) const SPDM_CHAL_CAP: u32 =3D bit_u32(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 1cc6c33516fb..9628f258854c 100644 --- a/lib/rspdm/lib.rs +++ b/lib/rspdm/lib.rs @@ -112,6 +112,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 26b90942b298..e7119ffa9a69 100644 --- a/lib/rspdm/state.rs +++ b/lib/rspdm/state.rs @@ -25,10 +25,17 @@ SPDM_ERROR, SPDM_GET_VERSION_LEN, SPDM_MAX_VER, + SPDM_MIN_DATA_TRANSFER_SIZE, SPDM_MIN_VER, - SPDM_REQ, // + SPDM_REQ, + SPDM_RSP_MIN_CAPS, + SPDM_VER_10, + SPDM_VER_11, + SPDM_VER_12, // }; use crate::validator::{ + GetCapabilitiesReq, + GetCapabilitiesRsp, GetVersionReq, GetVersionRsp, SpdmErrorRsp, @@ -47,6 +54,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, @@ -57,6 +66,7 @@ pub struct SpdmState { =20 // Negotiated state pub(crate) version: u8, + pub(crate) rsp_caps: u32, } =20 impl SpdmState { @@ -74,6 +84,7 @@ pub(crate) fn new( transport_sz, validate, version: SPDM_MIN_VER, + rsp_caps: 0, } } =20 @@ -293,4 +304,67 @@ 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: `response_vec` is rsp_sz length, initialised, aligned + // and won't be mutated + 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 + // or the same as 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 { + pr_err!( + "{:#x} capabilities are supported, which don't meet requir= ed {:#x}", + self.rsp_caps, + 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 8f45bafd4d69..7dc55882c880 100644 --- a/lib/rspdm/validator.rs +++ b/lib/rspdm/validator.rs @@ -26,8 +26,12 @@ }; =20 use crate::consts::{ + SPDM_CTEXPONENT, + SPDM_GET_CAPABILITIES, SPDM_GET_VERSION, - SPDM_MIN_VER, // + SPDM_MIN_VER, + SPDM_REQ_CAPS, // + SPDM_VER_11, }; =20 #[repr(C, packed)] @@ -141,3 +145,94 @@ 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: [u8; 2], + + 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; 2], + 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, + param2: u8, + + reserved1: u8, + pub(crate) ctexponent: u8, + reserved2: [u8; 2], + + 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::() { + let version =3D *(raw.get(0).ok_or(ENOMEM))?; + let version_1_1_len =3D mem::size_of::() + - mem::size_of::<__IncompleteArrayField<__le16>>() + - mem::size_of::() + - mem::size_of::(); + + if version =3D=3D SPDM_VER_11 && raw.len() =3D=3D version_1_1_= len { + // Version 1.1 of the spec doesn't include all of the fiel= ds + // So let's extend the KVec with 0s so we can cast the + // vector to GetCapabilitiesRsp + + for _i in version_1_1_len..mem::size_of::() { + raw.push(0, GFP_KERNEL)?; + } + } else { + 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 Sat Jun 13 10:31:02 2026 Received: from mail-pl1-f171.google.com (mail-pl1-f171.google.com [209.85.214.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 5228B304BDC for ; Fri, 8 May 2026 03:19:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210349; cv=none; b=o7T7twbHAr+UtAZkbX0VXBnii7gdD1yqjZyDRq09FUdPBYl6U0YGAUxhhy5Xx32QsuETFzPkZD45ihd0aUqffgyCXEojW6TnlNHJr9tHn1RHtmynKTXswVw1KvgFceOq2qImvATxlfEfEobA1WVbqb+AxyF03EfUfXKWao92QBI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210349; c=relaxed/simple; bh=AEIRQMFJ78obs/Fig6dF1+21hQUwsD8gqjiemnD2yGk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ShPbDPPzzSgpKR7ApjI7Ah0CAacnh5qCMuX9vQ04uRAMu5pafYGXAfdc93decDcPMHOzfvx3nT2J9tFQbi8u35qp2419Zr0PAJRJsRlzJ+Yi7czlps1w1LyT9YdMg0YxcY0fpEUtW0Zr89ZEFYuuLyvctZ4si3wwHd3izSTgrPc= 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=iA/eIkm7; arc=none smtp.client-ip=209.85.214.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="iA/eIkm7" Received: by mail-pl1-f171.google.com with SMTP id d9443c01a7336-2ba6485d219so10114335ad.3 for ; Thu, 07 May 2026 20:19:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778210342; x=1778815142; 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=67SIRNlN4DFhJcQDNjNjH5wMdIlffhQXlWGApt2+Y0k=; b=iA/eIkm7wnGuzAdFikXNwpvsyoNZ7NMjztzH8sDawquOmCqwzIAaqzbvQCr8qDpArq BMWyvsuXXxBve/Xzgdagevj2iULFZoD37rZQLgLnTNQe//bhaIjfUhnD/OgWrowTiFIz gzIIiJBsQf3nD2bPT2E3T+eHwhUPkEpNzSp5iG+o9EYhafostVMyTptHfeXwpjyWSVcA luucz3v9qZvyNAVBFp/m/2WRNpq/afWkJ9fjG6YP+Y7uHIK6Y2LlCNE7tEeo8aSb0urJ 4cQrNlvioUA/+hMMJvkUXfZ4nhsYnYt1FTHe2roEbjXCl1ROthApvT1U4yoIWsoVucco nxEw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778210342; x=1778815142; 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=67SIRNlN4DFhJcQDNjNjH5wMdIlffhQXlWGApt2+Y0k=; b=ZfSr5Su3Z0eh4+rIESPPLHu0RmO8D1Ul2BnL099AQFSnEAjO7d1YP4zjAKCwVDISKI BRHAStfyvgVgrfCJiiesVRJ9P6eywMFPKxdX0O1Ax8UDsNpW99+A5kmfRlq9fPvCQyXR uWO4ww9UY2wmMICX1LuRVQYS4MHL3IZXeRg917BUtSlPBu78wOVWCn2o+rSbgF0yYs3R 7EzpZGGZF+EIk918GM7jN2PJd9cxVok7YlLccjfHEPeVWRRX7OhlKAZGQZaKBMEJ/tAO QgowSejIxJmAvEvQ0m4TnfX0KurjHRbKnJFMMj1ggxpZfnmWpeQOFdJeD073MtxOqSDR lHnQ== X-Forwarded-Encrypted: i=1; AFNElJ8tI/YCdAZ9o078n+Bt2pI6XQOTapu5nygC0nQzIjAQN7/7vR/jHt6ZI2RNCipeQ9OPx7X/Wwr08O2DtXo=@vger.kernel.org X-Gm-Message-State: AOJu0YyMVzUtyy3DbXkItR2CODCg+NGhiUTbgp2NI5QFQBqUG1bbrMYw zYVnRVpKfxEWCiAbCSTaX7OtPkzRtyfuv1gpVQsRKZUhn20ggEdR9wvV X-Gm-Gg: Acq92OE2SlGNzlX71Zd+aro/tjfzR/F4ySKKll/234DTZSzjL9ALie9t/uUntsBl7XH 9HMcpdcWIArhwaZplceKnmcjx2MOwXtSnFCWLGj1hgr5ssE8jvPWvRwDdzx5iZTVShlwTBjkleD DsbuNju6IVkvrSqh2bk0cjrr6GavLyVvuDHk2y4s9YAkRIC8qi018C6HdEVxNdEzEluYkTraoZ1 pUTBXhwwGR1B6VhGMNvcaskwrZi7qsw86T+UtY1Wi4OuQozt0QiBiQ8YkiZRwDB4kKJbjblqgNt 8FwLmQ9K+LgDZlrMJpYdWKAL/K4kF2Rh4QkB4ddgBpYIA36lcmPrjDR5JlpDJ9lBd/5+rcmBZYw c1XH/NRjeIqxBDEIQmk4kx4PbA7fBYD8rhL+9I6YXVG6aiQtylQFN20tD1INoU8VUVZyDWyWDAr kUu9gy0NuyhVQlA99DxzXNeZZOvGPXkm5vyc9Ide1/ X-Received: by 2002:a17:903:1c8:b0:2b4:65d8:6a20 with SMTP id d9443c01a7336-2ba78f50177mr116458145ad.2.1778210341840; Thu, 07 May 2026 20:19:01 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2baf1eafa62sm3220685ad.74.2026.05.07.20.18.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 May 2026 20:19:01 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: alistair@alistair23.me, linux-kernel@vger.kernel.org, lukas@wunner.de, Jonathan.Cameron@huawei.com, bhelgaas@google.com, rust-for-linux@vger.kernel.org, akpm@linux-foundation.org, linux-cxl@vger.kernel.org, djbw@kernel.org, linux-pci@vger.kernel.org Cc: alex.gaynor@gmail.com, wilfred.mallawa@wdc.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, aliceryhl@google.com, boqun.feng@gmail.com, a.hindborg@kernel.org, tmgross@umich.edu, ojeda@kernel.org, alistair23@gmail.com Subject: [PATCH 13/18] lib: rspdm: Support SPDM negotiate_algorithms Date: Fri, 8 May 2026 13:17:05 +1000 Message-ID: <20260508031710.514574-14-alistair.francis@wdc.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260508031710.514574-1-alistair.francis@wdc.com> References: <20260508031710.514574-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 | 65 ++++++++++++- lib/rspdm/lib.rs | 18 +++- lib/rspdm/state.rs | 211 +++++++++++++++++++++++++++++++++++++++++ lib/rspdm/validator.rs | 115 +++++++++++++++++++++- 4 files changed, 404 insertions(+), 5 deletions(-) diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs index ef7d2d1d8e6e..e4652b18eb2a 100644 --- a/lib/rspdm/consts.rs +++ b/lib/rspdm/consts.rs @@ -9,7 +9,11 @@ =20 use crate::validator::SpdmHeader; use core::mem; -use kernel::bits::bit_u32; +use kernel::bits::{ + bit_u32, + bit_u8, + genmask_u32, // +}; =20 /* SPDM versions supported by this implementation */ pub(crate) const SPDM_VER_10: u8 =3D 0x10; @@ -73,13 +77,68 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core= ::fmt::Result { =20 // 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). +// roughly bit_u32(20. That's within the limits mandated for responders b= y CMA +// (bit_u32(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; =20 pub(crate) const SPDM_CERT_CAP: u32 =3D bit_u32(1); pub(crate) const SPDM_CHAL_CAP: u32 =3D bit_u32(2); +pub(crate) const SPDM_MEAS_CAP_MASK: u32 =3D genmask_u32(3..=3D4); +pub(crate) const SPDM_KEY_EX_CAP: u32 =3D bit_u32(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 bit_u8(0); + +pub(crate) const SPDM_ASYM_RSASSA_2048: u32 =3D bit_u32(0); +pub(crate) const _SPDM_ASYM_RSAPSS_2048: u32 =3D bit_u32(1); +pub(crate) const SPDM_ASYM_RSASSA_3072: u32 =3D bit_u32(2); +pub(crate) const _SPDM_ASYM_RSAPSS_3072: u32 =3D bit_u32(3); +pub(crate) const SPDM_ASYM_ECDSA_ECC_NIST_P256: u32 =3D bit_u32(4); +pub(crate) const SPDM_ASYM_RSASSA_4096: u32 =3D bit_u32(5); +pub(crate) const _SPDM_ASYM_RSAPSS_4096: u32 =3D bit_u32(6); +pub(crate) const SPDM_ASYM_ECDSA_ECC_NIST_P384: u32 =3D bit_u32(7); +pub(crate) const SPDM_ASYM_ECDSA_ECC_NIST_P521: u32 =3D bit_u32(8); +pub(crate) const _SPDM_ASYM_SM2_ECC_SM2_P256: u32 =3D bit_u32(9); +pub(crate) const _SPDM_ASYM_EDDSA_ED25519: u32 =3D bit_u32(10); +pub(crate) const _SPDM_ASYM_EDDSA_ED448: u32 =3D bit_u32(11); + +pub(crate) const SPDM_HASH_SHA_256: u32 =3D bit_u32(0); +pub(crate) const SPDM_HASH_SHA_384: u32 =3D bit_u32(1); +pub(crate) const SPDM_HASH_SHA_512: u32 =3D bit_u32(2); + +// If the crypto support isn't enabled don't offer the algorithms +// to the responder +#[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 bit_u8(1); diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs index 9628f258854c..72886a5dfd69 100644 --- a/lib/rspdm/lib.rs +++ b/lib/rspdm/lib.rs @@ -116,6 +116,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 @@ -123,4 +127,16 @@ /// /// @spdm_state: SPDM session state #[export] -pub unsafe extern "C" fn spdm_destroy(_state_ptr: *mut spdm_state) {} +pub unsafe extern "C" fn spdm_destroy(state_ptr: *mut spdm_state) { + let state: &mut SpdmState =3D unsafe { &mut (*(state_ptr as *mut SpdmS= tate)) }; + + 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 e7119ffa9a69..34676744e509 100644 --- a/lib/rspdm/state.rs +++ b/lib/rspdm/state.rs @@ -14,19 +14,36 @@ bindings, error::{ code::EINVAL, + from_err_ptr, to_result, Error, // }, + str::CStr, validate::Untrusted, }; =20 use crate::consts::{ SpdmErrorCode, + SPDM_ASYM_ALGOS, + SPDM_ASYM_ECDSA_ECC_NIST_P256, + SPDM_ASYM_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_ALGOS, + SPDM_HASH_SHA_256, + SPDM_HASH_SHA_384, + SPDM_HASH_SHA_512, + SPDM_KEY_EX_CAP, SPDM_MAX_VER, + SPDM_MEAS_CAP_MASK, + SPDM_MEAS_SPEC_DMTF, SPDM_MIN_DATA_TRANSFER_SIZE, SPDM_MIN_VER, + SPDM_OPAQUE_DATA_FMT_GENERAL, SPDM_REQ, SPDM_RSP_MIN_CAPS, SPDM_VER_10, @@ -38,6 +55,8 @@ GetCapabilitiesRsp, GetVersionReq, GetVersionRsp, + NegotiateAlgsReq, + NegotiateAlgsRsp, SpdmErrorRsp, SpdmHeader, // }; @@ -56,6 +75,25 @@ /// 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(). +/// @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, @@ -67,6 +105,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 { @@ -85,6 +136,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 @@ -367,4 +427,155 @@ 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; + } + + let req_sz =3D core::mem::size_of::(); + let rsp_sz =3D core::mem::size_of::(); + + request.length =3D req_sz 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)?; + // SAFETY: `response_vec` is rsp_sz length, initialised, aligned + // and won't be mutated + 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 7dc55882c880..9d738133399d 100644 --- a/lib/rspdm/validator.rs +++ b/lib/rspdm/validator.rs @@ -9,7 +9,8 @@ =20 use crate::bindings::{ __IncompleteArrayField, - __le16, // + __le16, + __le32, // }; use crate::consts::SpdmErrorCode; use core::mem; @@ -26,10 +27,14 @@ }; =20 use crate::consts::{ + SPDM_ASYM_ALGOS, SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, SPDM_GET_VERSION, + SPDM_HASH_ALGOS, + SPDM_MEAS_SPEC_DMTF, SPDM_MIN_VER, + SPDM_NEGOTIATE_ALGS, SPDM_REQ_CAPS, // SPDM_VER_11, }; @@ -236,3 +241,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, // size of resp_alg_struct + 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) resp_alg_struct: __IncompleteArrayField, +} + +impl Default for NegotiateAlgsReq { + fn default() -> Self { + NegotiateAlgsReq { + version: 0, + code: SPDM_NEGOTIATE_ALGS, + param1: 0, // Size of resp_alg_struct + param2: 0, + length: 32, + 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(), + resp_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) resp_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 Sat Jun 13 10:31:02 2026 Received: from mail-pj1-f45.google.com (mail-pj1-f45.google.com [209.85.216.45]) (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 28363313537 for ; Fri, 8 May 2026 03:19:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210355; cv=none; b=IEGBv/vhOa4a9UCQV7IX6gT9F5XRjkIYDczYC2Ls8zMY5Qj/zUQstGIf1OL/QIPqQhqM7kE6WY440VVFmJRRwkCm/923P4eGUoxnk1ErIhgoSzUPS0Urw7R/rk9tguBzHUL30nbpOj74yu+yz4tBybVTmhnwWLg43UPHDqI92ks= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210355; c=relaxed/simple; bh=T0+ZvWaKl6aU1RnDTQJKVo5xqdWjeysfv86NbgJZTQk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=BmJKOyDWtTQzq7Yq7tDvaurFfgN4yXFRIKzptHnKL7sBv6VJLMmKBX3owsG7S07yoJD427K89cj+Og64r2Sv/37n0qwmc7+HwXcsQemWQiXHesKJKNibHmEvFqTyp2Mtio6mnAhDdHgnMUGCYokxUPTengYCqR5bfG92W8m3dE0= 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=aCXaTQ7o; arc=none smtp.client-ip=209.85.216.45 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="aCXaTQ7o" Received: by mail-pj1-f45.google.com with SMTP id 98e67ed59e1d1-366087480d8so1277414a91.3 for ; Thu, 07 May 2026 20:19:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778210349; x=1778815149; 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=/ZVy9zCp2FVtT/BdK26xWCodS3E+jo75mkR1y+4KXx8=; b=aCXaTQ7o+PznnzUrtURa26K5jIefT04nUXVWmmxICywIpG2qkIVTyRAfu7HYSFyb4/ 5fWzfBy6YInOD4XOy/d4OsPZrlz772mSpEi0vDQdsr6vC+aMRBjhjr72/GcQoOh54cOo 4Cg5TbjUix6JUC7zMmtgdz6mq2SRqV5kGNaZYcw+FDdeGvHaRVsv5EkDbF6e7bYHxDK6 fm0BIiEWAHxiDCJVnKsJdYAogSVGc2O4dj6sj3K4H3+d205a9i7nDmDgRUxTkj11Wy0B IyA1JR5hty/EKgQJlBzR7IDRKzsOzIbDqp2cMe6M1Dz3IqXqIRE1e/8WpWAm2lSdKf9f A5Yg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778210349; x=1778815149; 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=/ZVy9zCp2FVtT/BdK26xWCodS3E+jo75mkR1y+4KXx8=; b=s7luhfM2x/ndAcyCpMwcEUIOg9v+WBlbTXgKsz5J6BmM9CNke52PnZeEukk+/zddtB 135qJtkqqm3F6REoy329ml8Jy9/HxFxZNEtXnewWhIAqP/+UgTgj08HP5i57quy1JJqJ 6NKbPBWZAmzH0oVDzeM3VgBK8WL3BXQHZB9DdE+kFXxPeJXWQGv+c1YQ9qqcoraGVBrO KQSRPR+7dmHJwiQXp8jMMn1oZfEnIl4Uzcjvu2Uei5hY+G+58cbVhd195DDE7ttoU8iQ f8wzNia+F3KvM0Dm8steC36OJqz7pAxZ2bFXeh1UmTRIJwxlf/bylangPnpOiV1nrJ0g NodA== X-Forwarded-Encrypted: i=1; AFNElJ/Q9Adnal5VL3hLoPQxWeeNyWWPksdPC/HgrqFsn9Qx4m0ejw7h4qMhV3F6mXsTdBst1GdhVI629Ylpolo=@vger.kernel.org X-Gm-Message-State: AOJu0YzPRUUT/2u2grIKiCTCKQUH5mKPF8e0BLDKadDeOE2WbFyby4gg KiuynsiouoCAN1PyuMLXiOuUTR+zRsaNmST5PVoFv/LfSeY62FMyFy6r X-Gm-Gg: Acq92OFQvoxmIQGBFN2sWo8+Go6Q7K3EHDq2OC7Mqdd+tGGgKd1LApi9yeM8twm49EO wVuMl7gad2DPjx6HxR8UbJ0+CMp/AI6SfGVJw60R0BM93DmDKtLGT5++T7lTWIDeBMsvluMzxOJ /3zQlLWrz+4x7dXE8BmzMOSLpEk2vGhFAH7eQSnenL8LnqnU27HPxt5KBB7sfa4+uz+PIsq2q9O Rh4Nu0PSyObFTLPyk0l9k4/m4/v4rrt4+E3Uzf8LSdAT0wuMsRL/YFmRbv3Mk6tTN22qJ0ImBqN wXj+Zy/pTpFrLYZrh108cBjYeNZqcBNVXFxDzWnGRAPM2gm3l7KFFwMP1YxTybd4LtzzxfiXpLP eTMbnYdPCqIJCUUn9eYLTs2xF0sRMrcLYn88qajFtQpw8lPqjuvPwRBq6B+txTM73SeYV1FGEfV ZUMpjdm1seMmtvL+tRRmhkb4+Wabusvxd1qD2/ZlcJ X-Received: by 2002:a17:903:1450:b0:2b4:5cd0:b6c3 with SMTP id d9443c01a7336-2ba79be16c0mr123514345ad.29.1778210349207; Thu, 07 May 2026 20:19:09 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2baf1eafa62sm3220685ad.74.2026.05.07.20.19.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 May 2026 20:19:08 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: alistair@alistair23.me, linux-kernel@vger.kernel.org, lukas@wunner.de, Jonathan.Cameron@huawei.com, bhelgaas@google.com, rust-for-linux@vger.kernel.org, akpm@linux-foundation.org, linux-cxl@vger.kernel.org, djbw@kernel.org, linux-pci@vger.kernel.org Cc: alex.gaynor@gmail.com, wilfred.mallawa@wdc.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, aliceryhl@google.com, boqun.feng@gmail.com, a.hindborg@kernel.org, tmgross@umich.edu, ojeda@kernel.org, alistair23@gmail.com Subject: [PATCH 14/18] lib: rspdm: Support SPDM get_digests Date: Fri, 8 May 2026 13:17:06 +1000 Message-ID: <20260508031710.514574-15-alistair.francis@wdc.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260508031710.514574-1-alistair.francis@wdc.com> References: <20260508031710.514574-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 | 7 +++- lib/rspdm/lib.rs | 4 ++ lib/rspdm/state.rs | 85 +++++++++++++++++++++++++++++++++++++++++- lib/rspdm/validator.rs | 54 +++++++++++++++++++++++++++ 4 files changed, 147 insertions(+), 3 deletions(-) diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs index e4652b18eb2a..092205dab74d 100644 --- a/lib/rspdm/consts.rs +++ b/lib/rspdm/consts.rs @@ -19,10 +19,11 @@ pub(crate) const SPDM_VER_10: u8 =3D 0x10; pub(crate) const SPDM_VER_11: u8 =3D 0x11; pub(crate) const SPDM_VER_12: u8 =3D 0x12; -#[allow(dead_code)] pub(crate) const SPDM_VER_13: u8 =3D 0x13; pub(crate) const SPDM_VER_14: u8 =3D 0x14; =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_14; =20 @@ -73,7 +74,7 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::= fmt::Result { pub(crate) const SPDM_GET_VERSION_LEN: usize =3D mem::size_of::() + u8::MAX as usize; =20 pub(crate) const SPDM_GET_CAPABILITIES: u8 =3D 0xe1; -pub(crate) const SPDM_MIN_DATA_TRANSFER_SIZE: u32 =3D 42; +pub(crate) const SPDM_MIN_DATA_TRANSFER_SIZE: u32 =3D 42; // SPDM 1.2.0 ma= rgin no 226 =20 // SPDM cryptographic timeout of this implementation: // Assume calculations may take up to 1 sec on a busy machine, which equals @@ -111,6 +112,8 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core= ::fmt::Result { pub(crate) const SPDM_HASH_SHA_384: u32 =3D bit_u32(1); pub(crate) const SPDM_HASH_SHA_512: u32 =3D bit_u32(2); =20 +pub(crate) const SPDM_GET_DIGESTS: u8 =3D 0x81; + // If the crypto support isn't enabled don't offer the algorithms // to the responder #[cfg(CONFIG_CRYPTO_RSA)] diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs index 72886a5dfd69..e42cfdd35524 100644 --- a/lib/rspdm/lib.rs +++ b/lib/rspdm/lib.rs @@ -120,6 +120,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 34676744e509..bcb1cc955c4c 100644 --- a/lib/rspdm/state.rs +++ b/lib/rspdm/state.rs @@ -46,13 +46,17 @@ SPDM_OPAQUE_DATA_FMT_GENERAL, SPDM_REQ, SPDM_RSP_MIN_CAPS, + SPDM_SLOTS, SPDM_VER_10, SPDM_VER_11, - SPDM_VER_12, // + SPDM_VER_12, + SPDM_VER_13, // }; use crate::validator::{ GetCapabilitiesReq, GetCapabilitiesRsp, + GetDigestsReq, + GetDigestsRsp, GetVersionReq, GetVersionRsp, NegotiateAlgsReq, @@ -83,6 +87,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). @@ -94,6 +102,8 @@ /// @desc: Synchronous hash context for @base_hash_alg computation. /// @hash_len: Hash length of @base_hash_alg (in bytes). /// H in SPDM specification. +/// @certs: Certificate chain in each of the 8 slots. Empty KVec if a slot= is +/// not populated. Prefixed by the 4 + H header per SPDM 1.0.0 table 15. #[expect(dead_code)] pub struct SpdmState { pub(crate) dev: *mut bindings::device, @@ -108,6 +118,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, @@ -118,6 +130,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 { @@ -139,12 +154,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 @@ -578,4 +596,69 @@ 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: `response_vec` is rsp_sz length, initialised, aligned + // and won't be mutated + let response_buf =3D unsafe { from_raw_parts_mut(response_vec.as_m= ut_ptr(), rsp_sz) }; + + let len =3D self.spdm_exchange(request_buf, response_buf)?; + + if len < (core::mem::size_of::() as i32) { + pr_err!("Truncated digests response\n"); + to_result(-(bindings::EIO as i32))?; + } + + // SAFETY: `len` is the length of data read, which will be smaller + // then the capacity of the vector + unsafe { response_vec.inc_len(len as usize) }; + + let response: &mut GetDigestsRsp =3D Untrusted::new_mut(&mut respo= nse_vec).validate_mut()?; + + if len + < (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 SPDM_VER_13 && (response.param2 & !response.p= aram1 !=3D 0) { + pr_err!("Malformed digests response\n"); + to_result(-(bindings::EPROTO as i32))?; + } + + let supported_slots =3D if self.version >=3D SPDM_VER_13 { + response.param1 + } else { + 0xFF + }; + + self.supported_slots =3D supported_slots; + + Ok(()) + } } diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs index 9d738133399d..1e5ee8a7582b 100644 --- a/lib/rspdm/validator.rs +++ b/lib/rspdm/validator.rs @@ -30,6 +30,7 @@ SPDM_ASYM_ALGOS, SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, + SPDM_GET_DIGESTS, SPDM_GET_VERSION, SPDM_HASH_ALGOS, SPDM_MEAS_SPEC_DMTF, @@ -349,3 +350,56 @@ 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, + // KeyPairIDs, added in 1.3 + + // CertificatInfo, added in 1.3 + + // KeyUsageMask, added in 1.3 +} + +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 Sat Jun 13 10:31:02 2026 Received: from mail-pl1-f170.google.com (mail-pl1-f170.google.com [209.85.214.170]) (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 205CD2FF67A for ; Fri, 8 May 2026 03:19:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210361; cv=none; b=f+0uQMaWNBJTTlT7AOJjUqw2nqQGPzfnz9VlUgxMLOaRYTaxFuRW+MVnwd+gSRr2liWcgfQPvtkTCr9SxyvqX39BxLTg3HRPGZRFRyZdr4xTjbT+SAVoxy1NhNo0K5vcFdR4TAsuH5HJTzsizmhjsFw/NRPhRAzlcvNV9sCP7Bs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210361; c=relaxed/simple; bh=+u7xQEzNizVtFbuUgfS6lhYyyTH742ns1cL9wfJM1LM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=CoT4wZGiM/yh97lDWfFV5E5S1OBHMIIuboIWlj/MQZbOpw9gxAd9lBYF1f+qIXPKrqgMF+szVidqf2r820CJ2sDF4khaOCe4DYCV+zF5mFqtM5h2D6GlAPt+xuKiwRsFdsAcH44ERE2ZKG6FL4xZ6OW67jq3mRWzBiDH6O8o0s8= 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=I3ck/rJI; arc=none smtp.client-ip=209.85.214.170 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="I3ck/rJI" Received: by mail-pl1-f170.google.com with SMTP id d9443c01a7336-2b4583f0a1aso9821695ad.3 for ; Thu, 07 May 2026 20:19:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778210357; x=1778815157; 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=u5yFibE0g6zKCTQxNoAPCceSLwiIHsT8Mf+vzQJztPA=; b=I3ck/rJICN2ZFPqPiH+rpsijlFvCQoQKQlzdLmOJ3lXyUbXrgHj+Clcxn1s+QwzdBk qpLDJe58hAcVvs2lhQfTa8geuFB2ZxY2K7/Rqn7gA3GRzwv818d1xCdvDtaxGO9nDiUU nytMs9SgQAEWLvJyhYURzElcOo8Na2xsvEMcHWnYfK/O4uJA9DUmLLZ6z+SO2AOPF6aY WPtS+62nWKy0zSv9UHlPqwi/h+1k8oNOQcxBfhmwZXMA1ceEBErQkSv9kq/uOsvnqDdV dm2bhcvT+h6Gd1mMP8HkVdm27E4/XqmZ2Du3G6kBXLaUbg4cUyWfizUle2THSm+c7i8M B98A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778210357; x=1778815157; 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=u5yFibE0g6zKCTQxNoAPCceSLwiIHsT8Mf+vzQJztPA=; b=Nqx19ou6Z4a5iRP12Zq8T6jYS4jw9QDlbehWMwtZI4OzX6tqcCAsZjCYaTUMNyHy0c eRWFckDgSVsVeSyqJBeoftuAC8iscPE0QCl0W8iq95mJKoyhwoBwqe69CKBzLVGKu2m1 6rLSLbb3XB/r4hnEMdNeQT2yDDxF9jwLwXDY3RkFc0P0+pDFT21Jav2ZPNcdiuwzPjlF 7x7wO3iWkwTgveaLZLfYFV9SBeYaP2snFtqyKWIk97MAhYAdrvEZhps+Oz0GCcDDNxFD uoOoIQAwuWRAGVSV+efTh2bDpTnvJOqchkVQrswCKSRsUU9B0SGTcWXzDKN7K7kSqiNz 41Gg== X-Forwarded-Encrypted: i=1; AFNElJ+DBTQG+j9jth6h4FKi1gtVY7eYXsC+j0K/buz34Xo2wQ/w3cqgZ+GjQ8s5LGVe1coil4nJVQN0AwsoVJw=@vger.kernel.org X-Gm-Message-State: AOJu0Yz7a403xQhLW32VyR7/ph7nRdYHKZ1JPAtv+7ukrhwnzuEF30PM rb4KC04DxkAZ6GhRDFUCC9z3qTXPwebPC5IPN39d2x7zay8CwD1y2upn X-Gm-Gg: Acq92OE3lPT/HIsGqIdzqbBdMGSRq0pZwk9kR72KiVtfazyjiJkMQILsNfwYxlZNL2q k+O3zyEzRkAbz9VQKN2bMRYKuOGBq9u2zSKyFySvMuhR1b4TJWhNwNiibrEDplRbPT7AUl8UDRW kE0FVM5GFPL9rggELDcsUd8bblO5Nzh1pyYlOQKuCxILinwqXTR/S8wuG/LyBV5fHoS6q7j1Gp7 dWhlSofYr8+d0o420W2DjA8eeD+3ED83g0p4IVcVDBNW+eUES6I5xNfHrMr6DwDHCdYAH9u1gwx ccbae6E80bekIZMvVvCEwJ6h/ozPvFjrs9Ez68o8dD4tckl6sD5KzU109zKcwfCL5E9J2aNYXcS ziik3Mpvao04DnbVT/wkhmfYCLTh0R5AZ+FgVbtLyGgX8iaGgHjwPzjW4LB0YaC2ZL+8V5/QmLR Bia76pQEn8/U6Q/sLBM92GM0jDUv1oACtOsyhxCmSi X-Received: by 2002:a17:903:4b2b:b0:2b7:aa20:3c61 with SMTP id d9443c01a7336-2ba798bb914mr119191635ad.33.1778210356592; Thu, 07 May 2026 20:19:16 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2baf1eafa62sm3220685ad.74.2026.05.07.20.19.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 May 2026 20:19:16 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: alistair@alistair23.me, linux-kernel@vger.kernel.org, lukas@wunner.de, Jonathan.Cameron@huawei.com, bhelgaas@google.com, rust-for-linux@vger.kernel.org, akpm@linux-foundation.org, linux-cxl@vger.kernel.org, djbw@kernel.org, linux-pci@vger.kernel.org Cc: alex.gaynor@gmail.com, wilfred.mallawa@wdc.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, aliceryhl@google.com, boqun.feng@gmail.com, a.hindborg@kernel.org, tmgross@umich.edu, ojeda@kernel.org, alistair23@gmail.com Subject: [PATCH 15/18] lib: rspdm: Support SPDM get_certificate Date: Fri, 8 May 2026 13:17:07 +1000 Message-ID: <20260508031710.514574-16-alistair.francis@wdc.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260508031710.514574-1-alistair.francis@wdc.com> References: <20260508031710.514574-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 | 125 +++++++++++++++++++++++++++++++++++++++++ lib/rspdm/validator.rs | 57 +++++++++++++++++++ 4 files changed, 195 insertions(+) diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs index 092205dab74d..302bc0285478 100644 --- a/lib/rspdm/consts.rs +++ b/lib/rspdm/consts.rs @@ -114,6 +114,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; + // If the crypto support isn't enabled don't offer the algorithms // to the responder #[cfg(CONFIG_CRYPTO_RSA)] diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs index e42cfdd35524..d5aee761003a 100644 --- a/lib/rspdm/lib.rs +++ b/lib/rspdm/lib.rs @@ -124,6 +124,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 bcb1cc955c4c..69b6f67a6ef5 100644 --- a/lib/rspdm/state.rs +++ b/lib/rspdm/state.rs @@ -55,6 +55,8 @@ use crate::validator::{ GetCapabilitiesReq, GetCapabilitiesRsp, + GetCertificateReq, + GetCertificateRsp, GetDigestsReq, GetDigestsRsp, GetVersionReq, @@ -135,6 +137,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, @@ -661,4 +671,119 @@ 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: `response_vec` is rsp_sz length, initialised, aligned + // and won't be mutated + 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::() as u32 += u16::MAX as u32) + .min(self.transport_sz) as usize; + + request.offset =3D 0; + request.length =3D (rsp_sz - core::mem::size_of::()) 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::())) as u16) + .to_le(); + + 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 as u16 !=3D certs.length + || 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 1e5ee8a7582b..8b44a056b335 100644 --- a/lib/rspdm/validator.rs +++ b/lib/rspdm/validator.rs @@ -30,6 +30,7 @@ SPDM_ASYM_ALGOS, SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, + SPDM_GET_CERTIFICATE, SPDM_GET_DIGESTS, SPDM_GET_VERSION, SPDM_HASH_ALGOS, @@ -403,3 +404,59 @@ 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 }; + + Ok(rsp) + } +} --=20 2.52.0 From nobody Sat Jun 13 10:31:02 2026 Received: from mail-pl1-f170.google.com (mail-pl1-f170.google.com [209.85.214.170]) (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 9561E31D371 for ; Fri, 8 May 2026 03:19:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210366; cv=none; b=krgOihgDK2Taw1gnOzeIBphnsprJboeLtQANBNzruSnUwU7pxSx5TCjSKk+LQ5bJN+xH9pUYKkoZ6oBDFaSg1o8G3uzT7HY2aA3D1SF6DFkPPDfp6nwKWFt8Lqc9ZaHsnI4iQdeR4OFLX+nt8Uo3vNjOVJSmo6pSAa4PXbisOHc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210366; c=relaxed/simple; bh=q9J06bxDr2w+m1apTO1ZVdf/ov6RslPYWBs92GaXEUI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=mC0BSDKasymklyDlVUCOBrf6tmxSj6ZxfkALgZV+k2heku/r8fWWPjVFnLwxZDHMCNdo0FfeP56SJJzyOViMbiF1Hn08YNBJYPt+ihHgoZQjo67n98E8mjc8xlqRzX7x2629x6j+BcXDX1doF983fBTI7X0iOzhBYbK8XIsHhUY= 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=EnGtF3ln; arc=none smtp.client-ip=209.85.214.170 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="EnGtF3ln" Received: by mail-pl1-f170.google.com with SMTP id d9443c01a7336-2b9fcf7c91bso14658325ad.0 for ; Thu, 07 May 2026 20:19:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778210364; x=1778815164; 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=KAyFv2zvkihNKJLa3E0b/381Ywpzx1UIj7t6rujOZAw=; b=EnGtF3lnPaXxK3DaWsQkkHEhJ/OA3hQZv4FJg7yDR0nwMvuKVId2ESl9XDTGHL4JqF VKUtGWE6R5L6f4B7hibNw5xwYr6s7cfIUIvnEzF2jXoyx5xLwmNfyY9TJ3mCPL1CzXXs ZfU89H3Udu3pl2QLf2fP7eMwHOzy9DsvJOPzcJyqFhkIU8hhoJn+FFl58K7EVcCJLssU u4h2jciyekadqqxfLfVUfsKePdBXGNdlgX/6k6Gr384ROoxC9bl6FEK9F6pLQWmJdn0s VTW+e8IdTP9gfAdmlrtKyArw8Nvf9iK7H+H1QPc4L3c6Y8A8W+FXfodVjHQDA88Df8lN yOWA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778210364; x=1778815164; 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=KAyFv2zvkihNKJLa3E0b/381Ywpzx1UIj7t6rujOZAw=; b=cWge8PNrwhtVYT6zt/Ax6aXEcO/kVzqC7EGl2UkGGUysx2Jv5DoUMyOVCifQGI1mbB XZb52g+QIf+oMBvzDFSehXGdGcZDodF/tCUNHr3I4GAOBPjrQGDKVraFpfSAO0Ve+d2v Cx67a8Qptxx1OWoPgrNRBWRQ6fe0ay4p4GI5TsNDtOJLAWU0s4QuJmvk0GqiRR/f3WzK OqQFJr870YwG9T/RX2YWUT6bZRscyqtSMD/iIGYdVqfBIW+7Bu9Opq7vZxXWOGGrJ8gh YoYR20rxjvzQsnUv/cP/0nzw8E4NbDe28cSv3CVMoIBL/avXX7j09oVJsFh7jfWgEK6h rrEw== X-Forwarded-Encrypted: i=1; AFNElJ9RBoLMjXMM0zePTB7Nmh/E5VEs/gIBc285U/EulG8A45tK2JhF7PlURuYqlAuhzTsfYx98VQHxHxq1/s8=@vger.kernel.org X-Gm-Message-State: AOJu0Yxm0a/sqVkpSBcDo9dKV34sLDmL3ZniQ0W8hrKN8VOfrMlHa5VC /hJlXsi2nLzjxBM14bLg5jQERvojHmY6sHpFPCJ5pfb/tyqW+U/CA0bf X-Gm-Gg: Acq92OE6HhYkslJcrqr/ilaApCNLJH0ogbTmwO30yBE5yw3ZWFZ+7YddntNcmPFmVxF tBDxbzVtNLmP5C+68vzTZ0hkflwdW4o0ORw88eQzE2gOkPYiBgzgXEvx0/34YdNXqBaiyoL/ibw JvemFPZSchYqXI99RSSx+XHSGV/dOlYAEAFEPGiMbESopbp0xkteNHKy6aOs5v5YVJZP2zu4tIs nrzVTbx1Y39lJGHO6mMsbvpphIbaC7XLHHF9Yd6+SEQzheoeojYePLUqgspsbcVeMHHze8fdh1E WSZ+1ebU1OTv3Hvx0joOqAobX8zzTfkrCSB01jnOV2orJJay1fmvKBhm99bqiI5B7Gq4+Pug0b5 fSWwWrOd9eNdTtkE1U8fpWDFxERjeJwWoR2+8Vu56gOVQcUp7CvvUpPCo4NOq7XeWSgA+UBbVMt ADF2a2c514cBBZeRXAQMncRoN0OCzXzsiQhKgiVYan X-Received: by 2002:a17:903:b0d:b0:2b4:5ddf:24f with SMTP id d9443c01a7336-2ba793bf9fbmr121352905ad.10.1778210363900; Thu, 07 May 2026 20:19:23 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2baf1eafa62sm3220685ad.74.2026.05.07.20.19.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 May 2026 20:19:23 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: alistair@alistair23.me, linux-kernel@vger.kernel.org, lukas@wunner.de, Jonathan.Cameron@huawei.com, bhelgaas@google.com, rust-for-linux@vger.kernel.org, akpm@linux-foundation.org, linux-cxl@vger.kernel.org, djbw@kernel.org, linux-pci@vger.kernel.org Cc: alex.gaynor@gmail.com, wilfred.mallawa@wdc.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, aliceryhl@google.com, boqun.feng@gmail.com, a.hindborg@kernel.org, tmgross@umich.edu, ojeda@kernel.org, alistair23@gmail.com Subject: [PATCH 16/18] lib: rspdm: Support SPDM certificate validation Date: Fri, 8 May 2026 13:17:08 +1000 Message-ID: <20260508031710.514574-17-alistair.francis@wdc.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260508031710.514574-1-alistair.francis@wdc.com> References: <20260508031710.514574-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. This only performs basic sanity checks on the chain before we continue on. This does not ensure that the root CA is trusted, we leave that for userspace to check and enforce. Instead we just make sure that the chain is correct, uses supported signatures and that it isn't blacklisted in the kernel. Signed-off-by: Alistair Francis --- lib/rspdm/lib.rs | 17 ++++++++ lib/rspdm/state.rs | 69 ++++++++++++++++++++++++++++++++- rust/bindings/bindings_helper.h | 2 + 3 files changed, 87 insertions(+), 1 deletion(-) diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs index d5aee761003a..d6421b2fab7d 100644 --- a/lib/rspdm/lib.rs +++ b/lib/rspdm/lib.rs @@ -135,6 +135,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 @@ -145,6 +156,12 @@ pub unsafe extern "C" fn spdm_destroy(state_ptr: *mut spdm_state) { let state: &mut SpdmState =3D unsafe { &mut (*(state_ptr as *mut SpdmS= tate)) }; =20 + 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 69b6f67a6ef5..4c8ee553bb69 100644 --- a/lib/rspdm/state.rs +++ b/lib/rspdm/state.rs @@ -106,7 +106,8 @@ /// H in SPDM specification. /// @certs: Certificate chain in each of the 8 slots. Empty KVec if a slot= is /// not populated. Prefixed by the 4 + H header per SPDM 1.0.0 table 15. -#[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, @@ -135,6 +136,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)] @@ -173,6 +175,7 @@ pub(crate) fn new( desc: None, hash_len: 0, certs: [const { KVec::new() }; SPDM_SLOTS], + leaf_key: None, } } =20 @@ -786,4 +789,68 @@ 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 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 { + pr_err!("Certificate was rejected\n"); + 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)?; + } + } + + 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 0231e4f87f20..4e1519b2382d 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -69,6 +69,8 @@ #include #include #include +#include +#include #include #include #include --=20 2.52.0 From nobody Sat Jun 13 10:31:02 2026 Received: from mail-pl1-f170.google.com (mail-pl1-f170.google.com [209.85.214.170]) (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 DD61C30AAA6 for ; Fri, 8 May 2026 03:19:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210373; cv=none; b=jNRxj+vuCUGenxEeSwhIO5RUO+5Jhr0ET+GFHOFvGMD4154+CNYo5jz/IU5sXU1vSebGfDjQ6enaAw5JwV9yPxggmlBfOGcgoHfKw8COF+xpmhzEGfcFhykKgsFpMP7OJo8zaABQ0eL0sFPDfZkV4svmXQFEcwPfswV5adQZOU4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210373; c=relaxed/simple; bh=j3lxGogAEu2m4ODiCYf4W/cmhvK2xVIezXSiFbrV+fY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=eE2Ygod8Dr5lDHHKlDqGjoXLoubbi4bJHuS7CyeLZ9BPmjeQ1FwJyfk8E/VtVJmAnrg+vhEzdgjTFOnyBK66dcbvV6hpq0AfVsRz3Wufire2Eob5/Bm1PcyJcYXrJ71cB9DwevIgfLfcjPmiKF4620eR9EMQx6ENNWBNIQog+ak= 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=SYPzEgPP; arc=none smtp.client-ip=209.85.214.170 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="SYPzEgPP" Received: by mail-pl1-f170.google.com with SMTP id d9443c01a7336-2b7d3ecc10dso14679465ad.2 for ; Thu, 07 May 2026 20:19:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778210371; x=1778815171; 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=KV+KKNzPOZuwOP9jOwich0HOmZDeWpgxJKGJYxOCc1E=; b=SYPzEgPPEAISbTP4ZX6A+emoUo22JWeiwLrPlZlR9DklqTVIl8Z557D3b2AJ3AEb/W RX8DqmMrTG3QX6anU7D5OmBzEnwOLlwAFDhPFCXLylLZXegujZmengVO4xPbqLqpF5Cb S3QpS6vZEkzsY3oKH9Tj3gFmItjnpHVR7iCNog1CHZovwXVHlNfrTIk1m3eAwTdKFLUz Dsm8mx7xaFU2zv326821qnQsrMJOpv6D7YokAZs8Lq+tDsyNwkDrC9GaQUvyDN/vPXfi f+6eNnwDKYc0LpEvFaUGOAiqTyk6qSKDSi6YfV191oQnTqmuYsBLwYSRelm6SRZbxE+5 VjGQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778210371; x=1778815171; 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=KV+KKNzPOZuwOP9jOwich0HOmZDeWpgxJKGJYxOCc1E=; b=YTWqAaM+t3t/P2ai8OpU7qc9wQ94NPrwqok9e2lSjaJDGZHXASJUVsfCV+wZbiwTL1 IYyayXhO15tsRnApI/YTgi0eID/lEv9gGM/Z/CAmxqSpDO1omBtnv5bO0OrgTSymmU/l yEIkNHQVjA4I9RB1pArDAqFjSUcbC9+MzNwUOUGiPe3plwbj+YL0+bOg7uq7g7XhS26b 9HNFAU8ut719fdfANtWiFUeDSdOLuKHjkUGqfnYpR2scUMKGiOmN3sQgU69unfpN1IVh 6zgT6KOAyn5y7m8tEc4bbExe/ahQ49DR/aEyjurygOg1/9XmLPgHfXzkNXSubeYNnqEg iuKQ== X-Forwarded-Encrypted: i=1; AFNElJ+bP2kHSFBcuzeELFdYvDhMj0JOQWs/d917ZUfL8TVC1XeMXl2oJqeHpdvNMxGZC2uCMOij+Dh9SLBpVZU=@vger.kernel.org X-Gm-Message-State: AOJu0YzPecOMEHwMEUx3ug3q5C/2Fw+0hUeXfk0+W2UJuQrAhAPNc4F1 2oN28rgEJDEpIYpV+KIz5V4xOegERsnEdiZGDJgJhnNuisf5oOc2Hv+8 X-Gm-Gg: Acq92OFnBCLp+meL7NZeoBjnluNN68VGMPvxb+6cz6YhnGAPnk8xGeN9rrHhVITaWXh wmfUHxOtH2zWGaumw4U5fempXFAlspFcHJBzF1swPcrG5aUQNFNnbs+kvMFuCgDnY1rfHPMspTR Zf+KiUCkWerZBrH7ZPx1BfsF2Nb23X8NK000UJEav1sxFMpl9lxEu8MbfeFLcih3TI1ZCP1toCc 6FXHCqCGgqxdh6YWfl7tIMs1ymqmV2j/s9zNqwqruvX2ldj8NEfdPPSdZPgG1J0Q1Q9Ymj4vYGY 0GKK0t550HnO79YCgXLBMtxQjx6HFTBzAPBO2LZliG2HwBtjnDtGEg6Z800bwB6wo4auL8VVzN6 GnGEWrIYPo2PAOo6pL5brd9lMknnUPHOqc2YJ9y3SpXIe5nkHEebmd+bXFaZAMkY8WmXgNz3OVu hs7s9lhPtedMKN/UMJ5crPUWZ9CWGJKuEf3AKm4JZrWOzbneFTKPU= X-Received: by 2002:a17:903:3887:b0:2ba:83f8:7b7b with SMTP id d9443c01a7336-2ba83f87cfcmr97081925ad.33.1778210371221; Thu, 07 May 2026 20:19:31 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2baf1eafa62sm3220685ad.74.2026.05.07.20.19.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 May 2026 20:19:30 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: alistair@alistair23.me, linux-kernel@vger.kernel.org, lukas@wunner.de, Jonathan.Cameron@huawei.com, bhelgaas@google.com, rust-for-linux@vger.kernel.org, akpm@linux-foundation.org, linux-cxl@vger.kernel.org, djbw@kernel.org, linux-pci@vger.kernel.org Cc: alex.gaynor@gmail.com, wilfred.mallawa@wdc.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, aliceryhl@google.com, boqun.feng@gmail.com, a.hindborg@kernel.org, tmgross@umich.edu, ojeda@kernel.org, alistair23@gmail.com Subject: [PATCH 17/18] rust: allow extracting the buffer from a CString Date: Fri, 8 May 2026 13:17:09 +1000 Message-ID: <20260508031710.514574-18-alistair.francis@wdc.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260508031710.514574-1-alistair.francis@wdc.com> References: <20260508031710.514574-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 Reviewed-by: Gary Guo --- rust/kernel/str.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs index 17070dde0e35..764dd5c695f6 100644 --- a/rust/kernel/str.rs +++ b/rust/kernel/str.rs @@ -856,6 +856,12 @@ 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`] + #[inline] + pub fn into_vec(self) -> KVec { + self.buf + } } =20 impl Deref for CString { --=20 2.52.0 From nobody Sat Jun 13 10:31:02 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 40ABE2FF66A for ; Fri, 8 May 2026 03:19:39 +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=1778210381; cv=none; b=raXrLbcrJCJIlW3RVBtqiZ0wzc1/ZzkIxdeKoUgDR25IElFPk0l/Ll90S9+QxlIj92+mMYmyIXm9z9AhjIriHWhCVL3IqYzhDnQzRjFytWsSsSmD4FrqRMj3qBxrtK8Od5sJNTXjHCmliJXSIwiA6zU64xKCm5nojWFgNIxCPKk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778210381; c=relaxed/simple; bh=48LvjpVIfzqjq0xGyaF+qPR4OSFXmF4Q/GbzHzxPS+s=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=pBUEDT6WxaJslUM7zN9OUekhMcAVWIIkBlicb3OYyWynpe5d4JYRWzCvduNDgC7Qdp1QQcXUgUXtPVRVyPgsrfpEvDHE7T8bF/9TQOk2U1V89FrMQ6OvTQpCh84ou0fCLX21/jqesLciNRHSwpOq12eXNhNwjV8FkljtcKP0Nfc= 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=plWTWO3o; 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="plWTWO3o" Received: by mail-pg1-f175.google.com with SMTP id 41be03b00d2f7-c8021c8c42fso563060a12.3 for ; Thu, 07 May 2026 20:19:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778210379; x=1778815179; 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=pprsevTDOKLZVgl88a8cDB8UwOK0KvhkdrcSb/4STD4=; b=plWTWO3ouZ2OFZF9f3MfW+mFw+xX8jGk6Ci/BYA53GTUZexHVkel8cEloM7Fcw2sG5 5DDq1OQyyiNJ3wOltYRMa2R15q8FK1l20SUKr0G8zY4ai6s9+umvLKPY8xkxgppxPM9x TmsfCWGq5GcdYlZ54+VRWxr6/Plg2MNqWYNj4aTGTfX7WNuHreM2j2ypFd0MFfrjK9ZV fSzQR2VYbP5cOPP6h+eNYJp4vyhUQPhkDoc3PsfNUaLHjJkhhQbgP3OHUTqMijBaIfDC R5d2QYRuQQ29M/IzB7cp55yPDK8pBsxtxksmg9eQQ5goMKqm6yXt7l06owVI+zj20kw8 mOXA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778210379; x=1778815179; 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=pprsevTDOKLZVgl88a8cDB8UwOK0KvhkdrcSb/4STD4=; b=NgKehGA9be3OaF3vaUcEjdqWPZQKAZrQx/Z0e8bhIUUwKjZcxruDsapOPKOh2ytezz RkzV+dNr9Ai0RNp4mU50tZZUaFPmJx88Vhb9UMnJ7Hc5qgBNcK504Vfqe2VEithRx+Gc MkGDeMyu5/qQMUoojTaF3IgesmKHEbSmS9FODgSseIyiI6QvPoXtYxLW/EuyDEW0gP7O /qHmhx88SxjL41aKKRzegxCbz9+3ESO2b+Hh5U7dW4D4hn+WJj02CQxr4avI+EnnSZcZ ZyUod+FBy5dNV7SR11+zIkjmpSYAEqSXu+bjjUVB19F5loreh08iJjclFiTrud2Kxiu/ PDcQ== X-Forwarded-Encrypted: i=1; AFNElJ+oS6WeFbA0eftCOqWNro1bvQcs2xHIeTiEivmM+rvz38OAOTAnV0RgEmdml7KqakNYCXyCF5041o5Y8Yk=@vger.kernel.org X-Gm-Message-State: AOJu0Yxv0yrFMuQCJlUS6Uo9BBE9ccv4tkgTa2t/Z04ojY4OzJ7FzcqW 1VblAbW5mVq2tLVDfHA572Miv+z9dbWGhwENi95mVz2Nhq7C0ROY2TTP X-Gm-Gg: Acq92OG6DDOTC8CIaYUdp7U8TybRgZ0hu3AssIrQurRPotdFcUzr02OukzjUBB9shAP s1jxq1sBS6n/Ks+v/6e7jC27fKWsJ4uNxOmQieR+Mbs7+2JZEE3VL+b0CuQs3RSxqd3l0cc+eYq GlAHctPUgD2Ox6u7EYEk7c3QFEkk1yEkzHhkENJvO291GzdxIPrSTmQMddjD+tUzsDjk8AvUbO9 ksvl/gSBLtH/Lq0l/dW8TLwJMYhm81Bh+uLEblR/M5VLtILNpNdvLZFFNvjRO9pPR36jQgEpON5 a033fe1d+P97d7oEz+f/3YhnMQuToyEauhDGDO+A0uVioTovKQeJRJINyDg+nl9T4icSkoAXT1Q Z978g7564g79VZ5uGavTD/t0ncXBVwJLiBW2CGBsMowPSLEa2X9n1ConxA6lj/TA5Gsjbzu0gR1 SFjiORaC4x+udZMVIDxOdnTP6K85PntElXLNlZMuT1jymAMyyWasg= X-Received: by 2002:a17:903:1b28:b0:2ba:881f:6192 with SMTP id d9443c01a7336-2baf0dd629cmr8678865ad.22.1778210378477; Thu, 07 May 2026 20:19:38 -0700 (PDT) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2baf1eafa62sm3220685ad.74.2026.05.07.20.19.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 May 2026 20:19:38 -0700 (PDT) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: alistair@alistair23.me, linux-kernel@vger.kernel.org, lukas@wunner.de, Jonathan.Cameron@huawei.com, bhelgaas@google.com, rust-for-linux@vger.kernel.org, akpm@linux-foundation.org, linux-cxl@vger.kernel.org, djbw@kernel.org, linux-pci@vger.kernel.org Cc: alex.gaynor@gmail.com, wilfred.mallawa@wdc.com, gary@garyguo.net, bjorn3_gh@protonmail.com, benno.lossin@proton.me, aliceryhl@google.com, boqun.feng@gmail.com, a.hindborg@kernel.org, tmgross@umich.edu, ojeda@kernel.org, alistair23@gmail.com Subject: [PATCH 18/18] lib: rspdm: Support SPDM challenge Date: Fri, 8 May 2026 13:17:10 +1000 Message-ID: <20260508031710.514574-19-alistair.francis@wdc.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260508031710.514574-1-alistair.francis@wdc.com> References: <20260508031710.514574-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 | 216 +++++++++++++++++++++++++++++++- lib/rspdm/validator.rs | 62 +++++++++ rust/bindings/bindings_helper.h | 1 + 5 files changed, 291 insertions(+), 2 deletions(-) diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs index 302bc0285478..9d41928da0c6 100644 --- a/lib/rspdm/consts.rs +++ b/lib/rspdm/consts.rs @@ -116,6 +116,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; + // If the crypto support isn't enabled don't offer the algorithms // to the responder #[cfg(CONFIG_CRYPTO_RSA)] @@ -147,3 +149,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 bit_u8(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 d6421b2fab7d..7fcf5a2d3071 100644 --- a/lib/rspdm/lib.rs +++ b/lib/rspdm/lib.rs @@ -135,17 +135,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_err!("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 4c8ee553bb69..3cf7236af7b2 100644 --- a/lib/rspdm/state.rs +++ b/lib/rspdm/state.rs @@ -8,6 +8,7 @@ //! =20 use core::ffi::c_void; +use core::mem::offset_of; use core::slice::from_raw_parts_mut; use kernel::prelude::*; use kernel::{ @@ -19,6 +20,7 @@ Error, // }, str::CStr, + str::CString, validate::Untrusted, }; =20 @@ -31,6 +33,7 @@ SPDM_ASYM_RSASSA_2048, SPDM_ASYM_RSASSA_3072, SPDM_ASYM_RSASSA_4096, + SPDM_COMBINED_PREFIX_SZ, SPDM_ERROR, SPDM_GET_VERSION_LEN, SPDM_HASH_ALGOS, @@ -38,12 +41,14 @@ SPDM_HASH_SHA_384, SPDM_HASH_SHA_512, 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_GENERAL, + SPDM_PREFIX_SZ, SPDM_REQ, SPDM_RSP_MIN_CAPS, SPDM_SLOTS, @@ -53,6 +58,8 @@ SPDM_VER_13, // }; use crate::validator::{ + ChallengeReq, + ChallengeRsp, GetCapabilitiesReq, GetCapabilitiesRsp, GetCertificateReq, @@ -67,6 +74,8 @@ 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`. /// @@ -108,6 +117,14 @@ /// not populated. Prefixed by the 4 + H header per SPDM 1.0.0 table 15. /// @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, @@ -134,9 +151,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, + + pub(crate) next_nonce: KVec, } =20 #[repr(C, packed)] @@ -174,8 +197,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: KVec::new(), } } =20 @@ -291,7 +317,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 { @@ -299,6 +325,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. @@ -367,6 +395,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; let unaligned =3D core::ptr::addr_of_mut!(response.version_number_= entries) as *mut u16; @@ -438,6 +472,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 { pr_err!( @@ -576,6 +613,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; @@ -637,6 +677,10 @@ pub(crate) fn get_digests(&mut self) -> Result<(), Err= or> { unsafe { response_vec.inc_len(len 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 len < (core::mem::size_of::() @@ -697,6 +741,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 @@ -853,4 +901,170 @@ 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 { + // No measurement summary hash requested (MSHLength =3D=3D 0) + 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, signature: &mut [u8]) -> Result<(), Err= or> { + let mut sig =3D bindings::public_key_signature::default(); + let mut mhash: KVec =3D KVec::new(); + + sig.s =3D signature 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 self.next_nonce.len() > 0 { + request.nonce.copy_from_slice(&self.next_nonce); + self.next_nonce.clear(); + } 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 { + offset_of!(ChallengeReq, context) + } 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: `response_vec` is rsp_sz length, initialised, aligned + // and won't be mutated + 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()?; + + // MSHLength is 0 as no measurement summary hash requested + 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 */ + let sig_start =3D response_vec.len() - self.sig_len; + let signature =3D &mut response_vec[sig_start..rsp_sz]; + + match self.verify_signature(signature) { + 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 8b44a056b335..1975eca81be3 100644 --- a/lib/rspdm/validator.rs +++ b/lib/rspdm/validator.rs @@ -28,6 +28,7 @@ =20 use crate::consts::{ SPDM_ASYM_ALGOS, + SPDM_CHALLENGE, SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, SPDM_GET_CERTIFICATE, @@ -460,3 +461,64 @@ 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 }; + + Ok(rsp) + } +} diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 4e1519b2382d..ed2377be1b44 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include --=20 2.52.0