Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.2/patches/2234754/?format=api
{ "id": 2234754, "url": "http://patchwork.ozlabs.org/api/1.2/patches/2234754/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-pci/patch/20260508031710.514574-19-alistair.francis@wdc.com/", "project": { "id": 28, "url": "http://patchwork.ozlabs.org/api/1.2/projects/28/?format=api", "name": "Linux PCI development", "link_name": "linux-pci", "list_id": "linux-pci.vger.kernel.org", "list_email": "linux-pci@vger.kernel.org", "web_url": null, "scm_url": null, "webscm_url": null, "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<20260508031710.514574-19-alistair.francis@wdc.com>", "list_archive_url": null, "date": "2026-05-08T03:17:10", "name": "[18/18] lib: rspdm: Support SPDM challenge", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "13e0fc34c95491a1a724690150e8eebed18ed829", "submitter": { "id": 64571, "url": "http://patchwork.ozlabs.org/api/1.2/people/64571/?format=api", "name": "Alistair Francis", "email": "alistair23@gmail.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/linux-pci/patch/20260508031710.514574-19-alistair.francis@wdc.com/mbox/", "series": [ { "id": 503312, "url": "http://patchwork.ozlabs.org/api/1.2/series/503312/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-pci/list/?series=503312", "date": "2026-05-08T03:16:52", "name": "lib: Rust implementation of SPDM", "version": 1, "mbox": "http://patchwork.ozlabs.org/series/503312/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2234754/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2234754/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "\n <linux-pci+bounces-54173-incoming=patchwork.ozlabs.org@vger.kernel.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "linux-pci@vger.kernel.org" ], "Delivered-To": "patchwork-incoming@legolas.ozlabs.org", "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20251104 header.b=plWTWO3o;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=2600:3c15:e001:75::12fc:5321; helo=sin.lore.kernel.org;\n envelope-from=linux-pci+bounces-54173-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)", "smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com\n header.b=\"plWTWO3o\"", "smtp.subspace.kernel.org;\n arc=none smtp.client-ip=209.85.214.169", "smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=gmail.com", "smtp.subspace.kernel.org;\n spf=pass smtp.mailfrom=gmail.com" ], "Received": [ "from sin.lore.kernel.org (sin.lore.kernel.org\n [IPv6:2600:3c15:e001:75::12fc:5321])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4gBZKX1y93z1yCg\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 08 May 2026 13:26:40 +1000 (AEST)", "from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby sin.lore.kernel.org (Postfix) with ESMTP id A301A304ABDC\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 8 May 2026 03:20:25 +0000 (UTC)", "from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id 26FDB2FFFB5;\n\tFri, 8 May 2026 03:19:41 +0000 (UTC)", "from mail-pl1-f169.google.com (mail-pl1-f169.google.com\n [209.85.214.169])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id 39D4B2F83A0\n\tfor <linux-pci@vger.kernel.org>; Fri, 8 May 2026 03:19:39 +0000 (UTC)", "by mail-pl1-f169.google.com with SMTP id\n d9443c01a7336-2b458ca2296so9970715ad.0\n for <linux-pci@vger.kernel.org>; Thu, 07 May 2026 20:19:39 -0700 (PDT)", "from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7])\n by smtp.gmail.com with ESMTPSA id\n d9443c01a7336-2baf1eafa62sm3220685ad.74.2026.05.07.20.19.31\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Thu, 07 May 2026 20:19:38 -0700 (PDT)" ], "ARC-Seal": "i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1778210381; cv=none;\n b=Lk46zMmHMrxEypOSnjGaigVyS2LGGrjUcWmr4+Bx8kbHeDDGlO8XOKEkNXpPwdcKcNfGVuLTRjIa7FEuX6s3tzAihTYeu/TUZxdStOTsh4H3WShv80dnkqG9rqEdG66O0k/30UTJr1GeV8cFRVin7MujbXTD5QWHrhvxhDP9JF8=", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1778210381; c=relaxed/simple;\n\tbh=48LvjpVIfzqjq0xGyaF+qPR4OSFXmF4Q/GbzHzxPS+s=;\n\th=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:\n\t MIME-Version;\n b=pBUEDT6WxaJslUM7zN9OUekhMcAVWIIkBlicb3OYyWynpe5d4JYRWzCvduNDgC7Qdp1QQcXUgUXtPVRVyPgsrfpEvDHE7T8bF/9TQOk2U1V89FrMQ6OvTQpCh84ou0fCLX21/jqesLciNRHSwpOq12eXNhNwjV8FkljtcKP0Nfc=", "ARC-Authentication-Results": "i=1; smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=gmail.com;\n spf=pass smtp.mailfrom=gmail.com;\n dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com\n header.b=plWTWO3o; arc=none smtp.client-ip=209.85.214.169", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=gmail.com; s=20251104; t=1778210379; x=1778815179;\n darn=vger.kernel.org;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:from:to:cc:subject:date\n :message-id:reply-to;\n bh=pprsevTDOKLZVgl88a8cDB8UwOK0KvhkdrcSb/4STD4=;\n b=plWTWO3ouZ2OFZF9f3MfW+mFw+xX8jGk6Ci/BYA53GTUZexHVkel8cEloM7Fcw2sG5\n 5DDq1OQyyiNJ3wOltYRMa2R15q8FK1l20SUKr0G8zY4ai6s9+umvLKPY8xkxgppxPM9x\n TmsfCWGq5GcdYlZ54+VRWxr6/Plg2MNqWYNj4aTGTfX7WNuHreM2j2ypFd0MFfrjK9ZV\n fSzQR2VYbP5cOPP6h+eNYJp4vyhUQPhkDoc3PsfNUaLHjJkhhQbgP3OHUTqMijBaIfDC\n R5d2QYRuQQ29M/IzB7cp55yPDK8pBsxtxksmg9eQQ5goMKqm6yXt7l06owVI+zj20kw8\n mOXA==", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1778210379; x=1778815179;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from\n :to:cc:subject:date:message-id:reply-to;\n bh=pprsevTDOKLZVgl88a8cDB8UwOK0KvhkdrcSb/4STD4=;\n b=rqtoQAylEYK0hzakv+fdnTvfiVtlY9E30uDoxapuRcdXjgwBwa4PlF2RthZGIk4xq3\n agmG0LSsX/PwGoUOaEnuCUuxQq5NlQldQeCnZ/gzwdWlLvGlJEDfQEzXcHDD4D24K/2U\n zeg1gaWgvOT25Eu/WJ8p/oICSOpFUyp6iK7zSD1CCFKWuEL+ets9JVD/Abn33Z+31YfU\n 1e7v8jgfxILonQV9eyJq8bdbxRXQXDgVHFGI5a2e+NrpCSRDvVijt/z3BrEq7TE8ri3u\n vOiGtN5B3sFizwZDntc1dHrU2gHRtueFlZGXSK4/+oFJGaAO/IBMXDawLcg0RC1DdSjl\n jWOQ==", "X-Forwarded-Encrypted": "i=1;\n AFNElJ/dvvh3SCRh4+jzzWsuFda47NJH9oy2yxeq7O0D3DBs9E5rmLvbpq22a5jFLsLkvgiVzi+jOlgZ8K4=@vger.kernel.org", "X-Gm-Message-State": "AOJu0YxBnPKniEDo/YA4wM0PjfdrfeFdHv/3u7h1qDq7np3jKrCtB+dl\n\tqwjB3ejGGdJRJYSSWIiPpmwX3pjPxsyM0x8gc/+9KpdtrkQd6O6fPkWw", "X-Gm-Gg": "Acq92OGiKsk4HFq8vVORzfQzrKbj7j3EsiRIHYWPRDfRV+55E6EU7J2I2XpzWBfbu3g\n\tP5kPXkoxxpdUBLBdMaiC+2lH/w+xHKD82Kdi3X/q9/AESKHxXr5KZQYMvXFXW9htyOZfu2Gz6gg\n\tL4+9n/iUzX85Co68XApzSz5ASTrK8wggkmV0ejsnFZ66q5H2DEZjE4+KsEewaPPGvYwHPeLb5Ft\n\tlD0whMjtxq7rwFxvAGS9qJZ7GGKTJNjALHc7ZJyg4K+qVYkRL/IoiAg6KOxGOXh2JpXuEVLp3PQ\n\tJGvFXVVr0GqxNJguaJzuVOsiKHuefb57j1TZ/0253ksx2QAFs2GXAVUgZiPXAwXVSdFFlTMxD2q\n\tDcNtGN5yh5lHLq2jR5iPL3ufaK/y8XzngsX6v/qGEUXPC+Sn36wzoog5geITZL2fGXs1mhcVaCk\n\tyXNBtC72bckBmFOVRDKj+nTbOQZP3lPKqnBksWA9o2TmafTaiMnAs=", "X-Received": "by 2002:a17:903:1b28:b0:2ba:881f:6192 with SMTP id\n d9443c01a7336-2baf0dd629cmr8678865ad.22.1778210378477;\n 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,\n\tlinux-kernel@vger.kernel.org,\n\tlukas@wunner.de,\n\tJonathan.Cameron@huawei.com,\n\tbhelgaas@google.com,\n\trust-for-linux@vger.kernel.org,\n\takpm@linux-foundation.org,\n\tlinux-cxl@vger.kernel.org,\n\tdjbw@kernel.org,\n\tlinux-pci@vger.kernel.org", "Cc": "alex.gaynor@gmail.com,\n\twilfred.mallawa@wdc.com,\n\tgary@garyguo.net,\n\tbjorn3_gh@protonmail.com,\n\tbenno.lossin@proton.me,\n\taliceryhl@google.com,\n\tboqun.feng@gmail.com,\n\ta.hindborg@kernel.org,\n\ttmgross@umich.edu,\n\tojeda@kernel.org,\n\talistair23@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-pci@vger.kernel.org", "List-Id": "<linux-pci.vger.kernel.org>", "List-Subscribe": "<mailto:linux-pci+subscribe@vger.kernel.org>", "List-Unsubscribe": "<mailto:linux-pci+unsubscribe@vger.kernel.org>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit" }, "content": "From: Alistair Francis <alistair@alistair23.me>\n\nSupport the CHALLENGE SPDM command.\n\nSigned-off-by: Alistair Francis <alistair@alistair23.me>\n---\n lib/rspdm/consts.rs | 6 +\n lib/rspdm/lib.rs | 8 +-\n lib/rspdm/state.rs | 216 +++++++++++++++++++++++++++++++-\n lib/rspdm/validator.rs | 62 +++++++++\n rust/bindings/bindings_helper.h | 1 +\n 5 files changed, 291 insertions(+), 2 deletions(-)", "diff": "diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs\nindex 302bc0285478..9d41928da0c6 100644\n--- a/lib/rspdm/consts.rs\n+++ b/lib/rspdm/consts.rs\n@@ -116,6 +116,8 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n \n pub(crate) const SPDM_GET_CERTIFICATE: u8 = 0x82;\n \n+pub(crate) const SPDM_CHALLENGE: u8 = 0x83;\n+\n // If the crypto support isn't enabled don't offer the algorithms\n // to the responder\n #[cfg(CONFIG_CRYPTO_RSA)]\n@@ -147,3 +149,7 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n // pub(crate) const SPDM_MAX_REQ_ALG_STRUCT: usize = 4;\n \n pub(crate) const SPDM_OPAQUE_DATA_FMT_GENERAL: u8 = bit_u8(1);\n+\n+pub(crate) const SPDM_PREFIX_SZ: usize = 64;\n+pub(crate) const SPDM_COMBINED_PREFIX_SZ: usize = 100;\n+pub(crate) const SPDM_MAX_OPAQUE_DATA: usize = 1024;\ndiff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs\nindex d6421b2fab7d..7fcf5a2d3071 100644\n--- a/lib/rspdm/lib.rs\n+++ b/lib/rspdm/lib.rs\n@@ -135,17 +135,23 @@\n provisioned_slots &= !(1 << slot);\n }\n \n+ let mut verify = true;\n let mut provisioned_slots = state.provisioned_slots;\n while (provisioned_slots as usize) > 0 {\n let slot = provisioned_slots.trailing_zeros() as u8;\n \n if let Err(e) = state.validate_cert_chain(slot) {\n- return e.to_errno() as c_int;\n+ pr_err!(\"Certificate in slot {slot} failed to verify: {e:?}\");\n+ verify = false;\n }\n \n provisioned_slots &= !(1 << slot);\n }\n \n+ if let Err(e) = state.challenge(state.provisioned_slots.trailing_zeros() as u8, verify) {\n+ return e.to_errno() as c_int;\n+ }\n+\n 0\n }\n \ndiff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs\nindex 4c8ee553bb69..3cf7236af7b2 100644\n--- a/lib/rspdm/state.rs\n+++ b/lib/rspdm/state.rs\n@@ -8,6 +8,7 @@\n //! <https://www.dmtf.org/dsp/DSP0274>\n \n use core::ffi::c_void;\n+use core::mem::offset_of;\n use core::slice::from_raw_parts_mut;\n use kernel::prelude::*;\n use kernel::{\n@@ -19,6 +20,7 @@\n Error, //\n },\n str::CStr,\n+ str::CString,\n validate::Untrusted,\n };\n \n@@ -31,6 +33,7 @@\n SPDM_ASYM_RSASSA_2048,\n SPDM_ASYM_RSASSA_3072,\n SPDM_ASYM_RSASSA_4096,\n+ SPDM_COMBINED_PREFIX_SZ,\n SPDM_ERROR,\n SPDM_GET_VERSION_LEN,\n SPDM_HASH_ALGOS,\n@@ -38,12 +41,14 @@\n SPDM_HASH_SHA_384,\n SPDM_HASH_SHA_512,\n SPDM_KEY_EX_CAP,\n+ SPDM_MAX_OPAQUE_DATA,\n SPDM_MAX_VER,\n SPDM_MEAS_CAP_MASK,\n SPDM_MEAS_SPEC_DMTF,\n SPDM_MIN_DATA_TRANSFER_SIZE,\n SPDM_MIN_VER,\n SPDM_OPAQUE_DATA_FMT_GENERAL,\n+ SPDM_PREFIX_SZ,\n SPDM_REQ,\n SPDM_RSP_MIN_CAPS,\n SPDM_SLOTS,\n@@ -53,6 +58,8 @@\n SPDM_VER_13, //\n };\n use crate::validator::{\n+ ChallengeReq,\n+ ChallengeRsp,\n GetCapabilitiesReq,\n GetCapabilitiesRsp,\n GetCertificateReq,\n@@ -67,6 +74,8 @@\n SpdmHeader, //\n };\n \n+const SPDM_CONTEXT: &str = \"responder-challenge_auth signing\";\n+\n /// The current SPDM session state for a device. Based on the\n /// C `struct spdm_state`.\n ///\n@@ -108,6 +117,14 @@\n /// not populated. Prefixed by the 4 + H header per SPDM 1.0.0 table 15.\n /// @leaf_key: Public key portion of leaf certificate against which to check\n /// responder's signatures.\n+/// @transcript: Concatenation of all SPDM messages exchanged during an\n+/// authentication or measurement sequence. Used to verify the signature,\n+/// as it is computed over the hashed transcript.\n+/// @next_nonce: Requester nonce to be used for the next authentication\n+/// sequence. Populated from user space through sysfs.\n+/// If user space does not provide a nonce, the kernel uses a random one.\n+///\n+/// `authenticated`: Whether device was authenticated successfully.\n pub struct SpdmState {\n pub(crate) dev: *mut bindings::device,\n pub(crate) transport: bindings::spdm_transport,\n@@ -134,9 +151,15 @@ pub struct SpdmState {\n pub(crate) desc: Option<&'static mut bindings::shash_desc>,\n pub(crate) hash_len: usize,\n \n+ pub(crate) authenticated: bool,\n+\n // Certificates\n pub(crate) certs: [KVec<u8>; SPDM_SLOTS],\n pub(crate) leaf_key: Option<*mut bindings::public_key>,\n+\n+ transcript: VVec<u8>,\n+\n+ pub(crate) next_nonce: KVec<u8>,\n }\n \n #[repr(C, packed)]\n@@ -174,8 +197,11 @@ pub(crate) fn new(\n shash: core::ptr::null_mut(),\n desc: None,\n hash_len: 0,\n+ authenticated: false,\n certs: [const { KVec::new() }; SPDM_SLOTS],\n leaf_key: None,\n+ transcript: VVec::new(),\n+ next_nonce: KVec::new(),\n }\n }\n \n@@ -291,7 +317,7 @@ fn spdm_err(&self, rsp: &SpdmErrorRsp) -> Result<(), Error> {\n /// The data in `request_buf` is sent to the device and the response is\n /// stored in `response_buf`.\n pub(crate) fn spdm_exchange(\n- &self,\n+ &mut self,\n request_buf: &mut [u8],\n response_buf: &mut [u8],\n ) -> Result<i32, Error> {\n@@ -299,6 +325,8 @@ pub(crate) fn spdm_exchange(\n let request: &mut SpdmHeader = Untrusted::new_mut(request_buf).validate_mut()?;\n let response: &SpdmHeader = Untrusted::new_ref(response_buf).validate()?;\n \n+ self.transcript.extend_from_slice(request_buf, GFP_KERNEL)?;\n+\n let transport_function = self.transport.ok_or(EINVAL)?;\n // SAFETY: `transport_function` is provided by the new(), we are\n // calling the function.\n@@ -367,6 +395,12 @@ pub(crate) fn get_version(&mut self) -> Result<(), Error> {\n unsafe { response_vec.inc_len(rc as usize) };\n \n let response: &mut GetVersionRsp = Untrusted::new_mut(&mut response_vec).validate_mut()?;\n+ let rsp_sz = core::mem::size_of::<SpdmHeader>()\n+ + 2\n+ + response.version_number_entry_count as usize * 2;\n+\n+ self.transcript\n+ .extend_from_slice(&response_vec[..rsp_sz], GFP_KERNEL)?;\n \n let mut foundver = false;\n let unaligned = core::ptr::addr_of_mut!(response.version_number_entries) as *mut u16;\n@@ -438,6 +472,9 @@ pub(crate) fn get_capabilities(&mut self) -> Result<(), Error> {\n let response: &mut GetCapabilitiesRsp =\n Untrusted::new_mut(&mut response_vec).validate_mut()?;\n \n+ self.transcript\n+ .extend_from_slice(&response_vec[..rsp_sz], GFP_KERNEL)?;\n+\n self.rsp_caps = u32::from_le(response.flags);\n if (self.rsp_caps & SPDM_RSP_MIN_CAPS) != SPDM_RSP_MIN_CAPS {\n pr_err!(\n@@ -576,6 +613,9 @@ pub(crate) fn negotiate_algs(&mut self) -> Result<(), Error> {\n let response: &mut NegotiateAlgsRsp =\n Untrusted::new_mut(&mut response_vec).validate_mut()?;\n \n+ self.transcript\n+ .extend_from_slice(&response_vec, GFP_KERNEL)?;\n+\n self.base_asym_alg = response.base_asym_sel;\n self.base_hash_alg = response.base_hash_sel;\n self.meas_hash_alg = response.measurement_hash_algo;\n@@ -637,6 +677,10 @@ pub(crate) fn get_digests(&mut self) -> Result<(), Error> {\n unsafe { response_vec.inc_len(len as usize) };\n \n let response: &mut GetDigestsRsp = Untrusted::new_mut(&mut response_vec).validate_mut()?;\n+ let rsp_sz = core::mem::size_of::<SpdmHeader>() + response.param2 as usize * self.hash_len;\n+\n+ self.transcript\n+ .extend_from_slice(&response_vec[..rsp_sz], GFP_KERNEL)?;\n \n if len\n < (core::mem::size_of::<GetDigestsReq>()\n@@ -697,6 +741,10 @@ fn get_cert_exchange(\n unsafe { response_vec.inc_len(rc as usize) };\n \n let response: &mut GetCertificateRsp = Untrusted::new_mut(response_vec).validate_mut()?;\n+ let rsp_sz = core::mem::size_of::<SpdmHeader>() + 4 + response.portion_length as usize;\n+\n+ self.transcript\n+ .extend_from_slice(&response_vec[..rsp_sz], GFP_KERNEL)?;\n \n if rc\n < (core::mem::size_of::<GetCertificateRsp>() + response.portion_length as usize) as i32\n@@ -853,4 +901,170 @@ pub(crate) fn validate_cert_chain(&mut self, slot: u8) -> Result<(), Error> {\n \n Ok(())\n }\n+\n+ pub(crate) fn challenge_rsp_len(&mut self, nonce_len: usize, opaque_len: usize) -> usize {\n+ // No measurement summary hash requested (MSHLength == 0)\n+ let mut length =\n+ core::mem::size_of::<SpdmHeader>() + self.hash_len + nonce_len + opaque_len + 2;\n+\n+ if self.version >= 0x13 {\n+ length += 8;\n+ }\n+\n+ length + self.sig_len\n+ }\n+\n+ fn verify_signature(&mut self, signature: &mut [u8]) -> Result<(), Error> {\n+ let mut sig = bindings::public_key_signature::default();\n+ let mut mhash: KVec<u8> = KVec::new();\n+\n+ sig.s = signature as *mut _ as *mut u8;\n+ sig.s_size = self.sig_len as u32;\n+ sig.encoding = self.base_asym_enc.as_ptr() as *const u8;\n+ sig.hash_algo = self.base_hash_alg_name.as_ptr() as *const u8;\n+\n+ let mut m: KVec<u8> = KVec::new();\n+ m.extend_with(SPDM_COMBINED_PREFIX_SZ + self.hash_len, 0, GFP_KERNEL)?;\n+\n+ if let Some(desc) = &mut self.desc {\n+ desc.tfm = self.shash;\n+\n+ unsafe {\n+ to_result(bindings::crypto_shash_digest(\n+ *desc,\n+ self.transcript.as_ptr(),\n+ (self.transcript.len() - self.sig_len) as u32,\n+ m[SPDM_COMBINED_PREFIX_SZ..].as_mut_ptr(),\n+ ))?;\n+ };\n+ } else {\n+ to_result(-(bindings::EPROTO as i32))?;\n+ }\n+\n+ if self.version <= 0x11 {\n+ sig.m = m[SPDM_COMBINED_PREFIX_SZ..].as_mut_ptr();\n+ } else {\n+ let major = self.version >> 4;\n+ let minor = self.version & 0xF;\n+\n+ let output = 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}.*\"))?;\n+ let mut buf = output.into_vec();\n+ let zero_pad_len = SPDM_COMBINED_PREFIX_SZ - SPDM_PREFIX_SZ - SPDM_CONTEXT.len() - 1;\n+\n+ buf.extend_with(zero_pad_len, 0, GFP_KERNEL)?;\n+ buf.extend_from_slice(SPDM_CONTEXT.as_bytes(), GFP_KERNEL)?;\n+\n+ m[..SPDM_COMBINED_PREFIX_SZ].copy_from_slice(&buf);\n+\n+ mhash.extend_with(self.hash_len, 0, GFP_KERNEL)?;\n+\n+ if let Some(desc) = &mut self.desc {\n+ desc.tfm = self.shash;\n+\n+ unsafe {\n+ to_result(bindings::crypto_shash_digest(\n+ *desc,\n+ m.as_ptr(),\n+ m.len() as u32,\n+ mhash.as_mut_ptr(),\n+ ))?;\n+ };\n+ } else {\n+ to_result(-(bindings::EPROTO as i32))?;\n+ }\n+\n+ sig.m = mhash.as_mut_ptr();\n+ }\n+\n+ sig.m_size = self.hash_len as u32;\n+\n+ if let Some(leaf_key) = self.leaf_key {\n+ unsafe { to_result(bindings::public_key_verify_signature(leaf_key, &sig)) }\n+ } else {\n+ to_result(-(bindings::EPROTO as i32))\n+ }\n+ }\n+\n+ pub(crate) fn challenge(&mut self, slot: u8, verify: bool) -> Result<(), Error> {\n+ let mut request = ChallengeReq::default();\n+ request.version = self.version;\n+ request.param1 = slot;\n+\n+ let nonce_len = request.nonce.len();\n+\n+ if self.next_nonce.len() > 0 {\n+ request.nonce.copy_from_slice(&self.next_nonce);\n+ self.next_nonce.clear();\n+ } else {\n+ unsafe {\n+ bindings::get_random_bytes(&mut request.nonce as *mut _ as *mut c_void, nonce_len)\n+ };\n+ }\n+\n+ let req_sz = if self.version <= 0x12 {\n+ offset_of!(ChallengeReq, context)\n+ } else {\n+ core::mem::size_of::<ChallengeReq>()\n+ };\n+\n+ let rsp_sz = self.challenge_rsp_len(nonce_len, SPDM_MAX_OPAQUE_DATA);\n+\n+ // SAFETY: `request` is repr(C) and packed, so we can convert it to a slice\n+ let request_buf = unsafe { from_raw_parts_mut(&mut request as *mut _ as *mut u8, req_sz) };\n+\n+ let mut response_vec: KVec<u8> = KVec::with_capacity(rsp_sz, GFP_KERNEL)?;\n+ // SAFETY: `response_vec` is rsp_sz length, initialised, aligned\n+ // and won't be mutated\n+ let response_buf = unsafe { from_raw_parts_mut(response_vec.as_mut_ptr(), rsp_sz) };\n+\n+ let rc = self.spdm_exchange(request_buf, response_buf)?;\n+\n+ if rc < (core::mem::size_of::<ChallengeRsp>() as i32) {\n+ pr_err!(\"Truncated challenge response\\n\");\n+ to_result(-(bindings::EIO as i32))?;\n+ }\n+\n+ // SAFETY: `rc` is the length of data read, which will be smaller\n+ // then the capacity of the vector\n+ unsafe { response_vec.inc_len(rc as usize) };\n+\n+ let _response: &mut ChallengeRsp = Untrusted::new_mut(&mut response_vec).validate_mut()?;\n+\n+ // MSHLength is 0 as no measurement summary hash requested\n+ let opaque_len_offset = core::mem::size_of::<SpdmHeader>() + self.hash_len + nonce_len;\n+ let opaque_len = u16::from_le_bytes(\n+ response_vec[opaque_len_offset..(opaque_len_offset + 2)]\n+ .try_into()\n+ .unwrap_or([0, 0]),\n+ );\n+\n+ let rsp_sz = self.challenge_rsp_len(nonce_len, opaque_len as usize);\n+\n+ if rc < rsp_sz as i32 {\n+ pr_err!(\"Truncated challenge response\\n\");\n+ to_result(-(bindings::EIO as i32))?;\n+ }\n+\n+ self.transcript\n+ .extend_from_slice(&response_vec[..rsp_sz], GFP_KERNEL)?;\n+\n+ if verify {\n+ /* Verify signature at end of transcript against leaf key */\n+ let sig_start = response_vec.len() - self.sig_len;\n+ let signature = &mut response_vec[sig_start..rsp_sz];\n+\n+ match self.verify_signature(signature) {\n+ Ok(()) => {\n+ pr_info!(\"Authenticated with certificate slot {slot}\");\n+ self.authenticated = true;\n+ }\n+ Err(e) => {\n+ pr_err!(\"Cannot verify challenge_auth signature: {e:?}\");\n+ self.authenticated = false;\n+ }\n+ };\n+ }\n+\n+ Ok(())\n+ }\n }\ndiff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs\nindex 8b44a056b335..1975eca81be3 100644\n--- a/lib/rspdm/validator.rs\n+++ b/lib/rspdm/validator.rs\n@@ -28,6 +28,7 @@\n \n use crate::consts::{\n SPDM_ASYM_ALGOS,\n+ SPDM_CHALLENGE,\n SPDM_CTEXPONENT,\n SPDM_GET_CAPABILITIES,\n SPDM_GET_CERTIFICATE,\n@@ -460,3 +461,64 @@ fn validate(unvalidated: &mut Unvalidated<KVec<u8>>) -> Result<Self, Self::Err>\n Ok(rsp)\n }\n }\n+\n+#[repr(C, packed)]\n+pub(crate) struct ChallengeReq {\n+ pub(crate) version: u8,\n+ pub(crate) code: u8,\n+ pub(crate) param1: u8,\n+ pub(crate) param2: u8,\n+\n+ pub(crate) nonce: [u8; 32],\n+ pub(crate) context: [u8; 8],\n+}\n+\n+impl Default for ChallengeReq {\n+ fn default() -> Self {\n+ ChallengeReq {\n+ version: 0,\n+ code: SPDM_CHALLENGE,\n+ param1: 0,\n+ param2: 0,\n+ nonce: [0; 32],\n+ context: [0; 8],\n+ }\n+ }\n+}\n+\n+#[repr(C, packed)]\n+pub(crate) struct ChallengeRsp {\n+ pub(crate) version: u8,\n+ pub(crate) code: u8,\n+ pub(crate) param1: u8,\n+ pub(crate) param2: u8,\n+\n+ pub(crate) cert_chain_hash: __IncompleteArrayField<u8>,\n+ pub(crate) nonce: [u8; 32],\n+ pub(crate) message_summary_hash: __IncompleteArrayField<u8>,\n+\n+ pub(crate) opaque_data_len: u16,\n+ pub(crate) opaque_data: __IncompleteArrayField<u8>,\n+\n+ pub(crate) context: [u8; 8],\n+ pub(crate) signature: __IncompleteArrayField<u8>,\n+}\n+\n+impl Validate<&mut Unvalidated<KVec<u8>>> for &mut ChallengeRsp {\n+ type Err = Error;\n+\n+ fn validate(unvalidated: &mut Unvalidated<KVec<u8>>) -> Result<Self, Self::Err> {\n+ let raw = unvalidated.raw_mut();\n+ if raw.len() < mem::size_of::<ChallengeRsp>() {\n+ return Err(EINVAL);\n+ }\n+\n+ let ptr = raw.as_mut_ptr();\n+ // CAST: `ChallengeRsp` only contains integers and has `repr(C)`.\n+ let ptr = ptr.cast::<ChallengeRsp>();\n+ // SAFETY: `ptr` came from a reference and the cast above is valid.\n+ let rsp: &mut ChallengeRsp = unsafe { &mut *ptr };\n+\n+ Ok(rsp)\n+ }\n+}\ndiff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h\nindex 4e1519b2382d..ed2377be1b44 100644\n--- a/rust/bindings/bindings_helper.h\n+++ b/rust/bindings/bindings_helper.h\n@@ -31,6 +31,7 @@\n #include <linux/acpi.h>\n #include <linux/gpu_buddy.h>\n #include <crypto/hash.h>\n+#include <crypto/public_key.h>\n #include <drm/drm_device.h>\n #include <drm/drm_drv.h>\n #include <drm/drm_file.h>\n", "prefixes": [ "18/18" ] }