{"id":2234748,"url":"http://patchwork.ozlabs.org/api/1.2/patches/2234748/?format=json","web_url":"http://patchwork.ozlabs.org/project/linux-pci/patch/20260508031710.514574-14-alistair.francis@wdc.com/","project":{"id":28,"url":"http://patchwork.ozlabs.org/api/1.2/projects/28/?format=json","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-14-alistair.francis@wdc.com>","list_archive_url":null,"date":"2026-05-08T03:17:05","name":"[13/18] lib: rspdm: Support SPDM negotiate_algorithms","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"baecb3c31b2ee4957bb5fff0e9db56361e89864a","submitter":{"id":64571,"url":"http://patchwork.ozlabs.org/api/1.2/people/64571/?format=json","name":"Alistair Francis","email":"alistair23@gmail.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/linux-pci/patch/20260508031710.514574-14-alistair.francis@wdc.com/mbox/","series":[{"id":503312,"url":"http://patchwork.ozlabs.org/api/1.2/series/503312/?format=json","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/2234748/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2234748/checks/","tags":{},"related":[],"headers":{"Return-Path":"\n <linux-pci+bounces-54168-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=iA/eIkm7;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=172.105.105.114; helo=tor.lore.kernel.org;\n envelope-from=linux-pci+bounces-54168-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=\"iA/eIkm7\"","smtp.subspace.kernel.org;\n arc=none smtp.client-ip=209.85.214.178","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 tor.lore.kernel.org (tor.lore.kernel.org [172.105.105.114])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4gBZFR5kLQz1yK7\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 08 May 2026 13:23:07 +1000 (AEST)","from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby tor.lore.kernel.org (Postfix) with ESMTP id 108FA309DA2E\n\tfor <incoming@patchwork.ozlabs.org>; Fri,  8 May 2026 03:19:37 +0000 (UTC)","from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id 43CB631326B;\n\tFri,  8 May 2026 03:19:09 +0000 (UTC)","from mail-pl1-f178.google.com (mail-pl1-f178.google.com\n [209.85.214.178])\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 EABEF2FA0C6\n\tfor <linux-pci@vger.kernel.org>; Fri,  8 May 2026 03:19:03 +0000 (UTC)","by mail-pl1-f178.google.com with SMTP id\n d9443c01a7336-2b4650d5f5cso5724635ad.0\n        for <linux-pci@vger.kernel.org>; Thu, 07 May 2026 20:19:03 -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.18.54\n        (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n        Thu, 07 May 2026 20:19:01 -0700 (PDT)"],"ARC-Seal":"i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1778210348; cv=none;\n b=PAWyvNAFi9a1IHXohj71wzfr4Xq/EoF+KwNspSVmpU0Cg3/ztyVCfbYHr3Su34+wZsUqnkJkw3wBFglZ9JvGCtBoREjZIAUYnS8hgce1W2tFRzbqL5rlMYg/lGLPjC6JdeSTZOQIczb/WVQH0tGcbiKCjXse1RpMEY0U99WNgKI=","ARC-Message-Signature":"i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1778210348; c=relaxed/simple;\n\tbh=AEIRQMFJ78obs/Fig6dF1+21hQUwsD8gqjiemnD2yGk=;\n\th=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:\n\t MIME-Version;\n b=fOQGQhbMPXkHD19u4NcWnL30KbuH0hd9LBJ68KZAGgU/MANkAeiI+SduUVU/O9kGiA3vpOzER7l1ZfBc/CfiZTsbqDSB1dg0UnTzUMYOqlKMBkSY4OWvHtTS/DCjdjlD8wJrfeiEekbzO9fgndCAc8ZoSwIFL6vCZqA7pqq7MGw=","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=iA/eIkm7; arc=none smtp.client-ip=209.85.214.178","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=gmail.com; s=20251104; t=1778210342; x=1778815142;\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=67SIRNlN4DFhJcQDNjNjH5wMdIlffhQXlWGApt2+Y0k=;\n        b=iA/eIkm7wnGuzAdFikXNwpvsyoNZ7NMjztzH8sDawquOmCqwzIAaqzbvQCr8qDpArq\n         BMWyvsuXXxBve/Xzgdagevj2iULFZoD37rZQLgLnTNQe//bhaIjfUhnD/OgWrowTiFIz\n         gzIIiJBsQf3nD2bPT2E3T+eHwhUPkEpNzSp5iG+o9EYhafostVMyTptHfeXwpjyWSVcA\n         luucz3v9qZvyNAVBFp/m/2WRNpq/afWkJ9fjG6YP+Y7uHIK6Y2LlCNE7tEeo8aSb0urJ\n         4cQrNlvioUA/+hMMJvkUXfZ4nhsYnYt1FTHe2roEbjXCl1ROthApvT1U4yoIWsoVucco\n         nxEw==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=1e100.net; s=20251104; t=1778210342; x=1778815142;\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=67SIRNlN4DFhJcQDNjNjH5wMdIlffhQXlWGApt2+Y0k=;\n        b=YAIznyR+a4CwkrcRFGSNllhr9qLRsS5EkixMzbBxdxRacWLnHJYil2RmMnWkOkxacp\n         j+8yP2BTkPFH+2f545jkz2T8f3sn493G9DDZp5HVkykF+z6cUepj+Es+Nxm/f5QThBT1\n         dx4T1bnkCUr4zf10VzPr3vC45DDnRB+9ZXXoROIpQf8VkCfk0GmXfU1hUXfgf1q7w2MQ\n         qC5TPiMzA7X1ESPdJrE0wK1nLxDKs1D8noThhWyAWNU4uk8+oOasDofKu9pKC4NKKDqv\n         HkpqYmb4uszHhaT5+EGysz8uNmMlVUFWHXBURtA267BZS7DYBgAb1tUzNw60h4opqQ/q\n         L38g==","X-Forwarded-Encrypted":"i=1;\n AFNElJ/3CCNa05gFT8Yf0ZuHZl5yLmjD3xOg8ssbnTWFZ3sb1ayqB2Qthx2iyXu0MDEiwIQ/A234AXqHg8c=@vger.kernel.org","X-Gm-Message-State":"AOJu0YxtN9EzdifQ/hUmQqovFSLVoEw2cFuDrXtb5JvMipPz42hk2gzU\n\tm8cVyvgsRs3/o2o1F/M5/3hY3G+q/Zo+8XVYiM6QLGulrIseqgx9V0+M","X-Gm-Gg":"Acq92OEOfqascKWV7/YRebWcn/DZhjq8QsDZqkTBoS2kUFFHrgHGrBJQF4mmaLaerOI\n\t7uSMph/4LPnlNb9kR1xokBwKf2OS4P3wfF9SFe8drBur/i/HpkyrlB30E1Rd+tBW+wBJ/2cmqoh\n\tP2pJziQNJxKlVZp24aIpeYI3alWN5fGJ1rPM1e6rRT1SLTOjNikBA6t5pHLreGhX2RLt6nbRyY4\n\t7B1AyhPyqHOdvfdURvhiEnmYGsVMrR+/NVLYNvGBxuMjo53VegS8sr7ZTUZ5TmKWcY0g9GvId/A\n\tA33zS6g0mG2cy3d/7jJTWIFmIMA/frpu0w9NaB5Bqw0wiQSevQHD33amc8bs3HZY7L3sFQDEwq/\n\tp9ZoNik41eVa5sBQ/jIGp4LcSbgAew/NrcBL70wGsJx6J10lqRS1/P5QiqC3OmlTqAIWJShfh4A\n\t+UYR4EP95kMw6jzeEbVNO75j6aCVhIUc2pbely4RvW","X-Received":"by 2002:a17:903:1c8:b0:2b4:65d8:6a20 with SMTP id\n d9443c01a7336-2ba78f50177mr116458145ad.2.1778210341840;\n        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,\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 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-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 NEGOTIATE_ALGORITHMS SPDM command.\n\nSigned-off-by: Alistair Francis <alistair@alistair23.me>\n---\n lib/rspdm/consts.rs    |  65 ++++++++++++-\n lib/rspdm/lib.rs       |  18 +++-\n lib/rspdm/state.rs     | 211 +++++++++++++++++++++++++++++++++++++++++\n lib/rspdm/validator.rs | 115 +++++++++++++++++++++-\n 4 files changed, 404 insertions(+), 5 deletions(-)","diff":"diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs\nindex ef7d2d1d8e6e..e4652b18eb2a 100644\n--- a/lib/rspdm/consts.rs\n+++ b/lib/rspdm/consts.rs\n@@ -9,7 +9,11 @@\n \n use crate::validator::SpdmHeader;\n use core::mem;\n-use kernel::bits::bit_u32;\n+use kernel::bits::{\n+    bit_u32,\n+    bit_u8,\n+    genmask_u32, //\n+};\n \n /* SPDM versions supported by this implementation */\n pub(crate) const SPDM_VER_10: u8 = 0x10;\n@@ -73,13 +77,68 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n \n // SPDM cryptographic timeout of this implementation:\n // Assume calculations may take up to 1 sec on a busy machine, which equals\n-// roughly 1 << 20.  That's within the limits mandated for responders by CMA\n-// (1 << 23 usec, PCIe r6.2 sec 6.31.3) and DOE (1 sec, PCIe r6.2 sec 6.30.2).\n+// roughly bit_u32(20.  That's within the limits mandated for responders by CMA\n+// (bit_u32(23 usec, PCIe r6.2 sec 6.31.3) and DOE (1 sec, PCIe r6.2 sec 6.30.2).\n // Used in GET_CAPABILITIES exchange.\n pub(crate) const SPDM_CTEXPONENT: u8 = 20;\n \n pub(crate) const SPDM_CERT_CAP: u32 = bit_u32(1);\n pub(crate) const SPDM_CHAL_CAP: u32 = bit_u32(2);\n+pub(crate) const SPDM_MEAS_CAP_MASK: u32 = genmask_u32(3..=4);\n+pub(crate) const SPDM_KEY_EX_CAP: u32 = bit_u32(9);\n \n pub(crate) const SPDM_REQ_CAPS: u32 = SPDM_CERT_CAP | SPDM_CHAL_CAP;\n pub(crate) const SPDM_RSP_MIN_CAPS: u32 = SPDM_CERT_CAP | SPDM_CHAL_CAP;\n+\n+pub(crate) const SPDM_NEGOTIATE_ALGS: u8 = 0xe3;\n+\n+pub(crate) const SPDM_MEAS_SPEC_DMTF: u8 = bit_u8(0);\n+\n+pub(crate) const SPDM_ASYM_RSASSA_2048: u32 = bit_u32(0);\n+pub(crate) const _SPDM_ASYM_RSAPSS_2048: u32 = bit_u32(1);\n+pub(crate) const SPDM_ASYM_RSASSA_3072: u32 = bit_u32(2);\n+pub(crate) const _SPDM_ASYM_RSAPSS_3072: u32 = bit_u32(3);\n+pub(crate) const SPDM_ASYM_ECDSA_ECC_NIST_P256: u32 = bit_u32(4);\n+pub(crate) const SPDM_ASYM_RSASSA_4096: u32 = bit_u32(5);\n+pub(crate) const _SPDM_ASYM_RSAPSS_4096: u32 = bit_u32(6);\n+pub(crate) const SPDM_ASYM_ECDSA_ECC_NIST_P384: u32 = bit_u32(7);\n+pub(crate) const SPDM_ASYM_ECDSA_ECC_NIST_P521: u32 = bit_u32(8);\n+pub(crate) const _SPDM_ASYM_SM2_ECC_SM2_P256: u32 = bit_u32(9);\n+pub(crate) const _SPDM_ASYM_EDDSA_ED25519: u32 = bit_u32(10);\n+pub(crate) const _SPDM_ASYM_EDDSA_ED448: u32 = bit_u32(11);\n+\n+pub(crate) const SPDM_HASH_SHA_256: u32 = bit_u32(0);\n+pub(crate) const SPDM_HASH_SHA_384: u32 = bit_u32(1);\n+pub(crate) const SPDM_HASH_SHA_512: u32 = bit_u32(2);\n+\n+// If the crypto support isn't enabled don't offer the algorithms\n+// to the responder\n+#[cfg(CONFIG_CRYPTO_RSA)]\n+pub(crate) const SPDM_ASYM_RSA: u32 =\n+    SPDM_ASYM_RSASSA_2048 | SPDM_ASYM_RSASSA_3072 | SPDM_ASYM_RSASSA_4096;\n+#[cfg(not(CONFIG_CRYPTO_RSA))]\n+pub(crate) const SPDM_ASYM_RSA: u32 = 0;\n+\n+#[cfg(CONFIG_CRYPTO_ECDSA)]\n+pub(crate) const SPDM_ASYM_ECDSA: u32 =\n+    SPDM_ASYM_ECDSA_ECC_NIST_P256 | SPDM_ASYM_ECDSA_ECC_NIST_P384 | SPDM_ASYM_ECDSA_ECC_NIST_P521;\n+#[cfg(not(CONFIG_CRYPTO_ECDSA))]\n+pub(crate) const SPDM_ASYM_ECDSA: u32 = 0;\n+\n+#[cfg(CONFIG_CRYPTO_SHA256)]\n+pub(crate) const SPDM_HASH_SHA2_256: u32 = SPDM_HASH_SHA_256;\n+#[cfg(not(CONFIG_CRYPTO_SHA256))]\n+pub(crate) const SPDM_HASH_SHA2_256: u32 = 0;\n+\n+#[cfg(CONFIG_CRYPTO_SHA512)]\n+pub(crate) const SPDM_HASH_SHA2_384_512: u32 = SPDM_HASH_SHA_384 | SPDM_HASH_SHA_512;\n+#[cfg(not(CONFIG_CRYPTO_SHA512))]\n+pub(crate) const SPDM_HASH_SHA2_384_512: u32 = 0;\n+\n+pub(crate) const SPDM_ASYM_ALGOS: u32 = SPDM_ASYM_RSA | SPDM_ASYM_ECDSA;\n+pub(crate) const SPDM_HASH_ALGOS: u32 = SPDM_HASH_SHA2_256 | SPDM_HASH_SHA2_384_512;\n+\n+/* Maximum number of ReqAlgStructs sent by this implementation */\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);\ndiff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs\nindex 9628f258854c..72886a5dfd69 100644\n--- a/lib/rspdm/lib.rs\n+++ b/lib/rspdm/lib.rs\n@@ -116,6 +116,10 @@\n         return e.to_errno() as c_int;\n     }\n \n+    if let Err(e) = state.negotiate_algs() {\n+        return e.to_errno() as c_int;\n+    }\n+\n     0\n }\n \n@@ -123,4 +127,16 @@\n ///\n /// @spdm_state: SPDM session state\n #[export]\n-pub unsafe extern \"C\" fn spdm_destroy(_state_ptr: *mut spdm_state) {}\n+pub unsafe extern \"C\" fn spdm_destroy(state_ptr: *mut spdm_state) {\n+    let state: &mut SpdmState = unsafe { &mut (*(state_ptr as *mut SpdmState)) };\n+\n+    if let Some(desc) = &mut state.desc {\n+        unsafe {\n+            bindings::kfree(*desc as *mut _ as *mut c_void);\n+        }\n+    }\n+\n+    unsafe {\n+        bindings::crypto_free_shash(state.shash);\n+    }\n+}\ndiff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs\nindex e7119ffa9a69..34676744e509 100644\n--- a/lib/rspdm/state.rs\n+++ b/lib/rspdm/state.rs\n@@ -14,19 +14,36 @@\n     bindings,\n     error::{\n         code::EINVAL,\n+        from_err_ptr,\n         to_result,\n         Error, //\n     },\n+    str::CStr,\n     validate::Untrusted,\n };\n \n use crate::consts::{\n     SpdmErrorCode,\n+    SPDM_ASYM_ALGOS,\n+    SPDM_ASYM_ECDSA_ECC_NIST_P256,\n+    SPDM_ASYM_ECDSA_ECC_NIST_P384,\n+    SPDM_ASYM_ECDSA_ECC_NIST_P521,\n+    SPDM_ASYM_RSASSA_2048,\n+    SPDM_ASYM_RSASSA_3072,\n+    SPDM_ASYM_RSASSA_4096,\n     SPDM_ERROR,\n     SPDM_GET_VERSION_LEN,\n+    SPDM_HASH_ALGOS,\n+    SPDM_HASH_SHA_256,\n+    SPDM_HASH_SHA_384,\n+    SPDM_HASH_SHA_512,\n+    SPDM_KEY_EX_CAP,\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_REQ,\n     SPDM_RSP_MIN_CAPS,\n     SPDM_VER_10,\n@@ -38,6 +55,8 @@\n     GetCapabilitiesRsp,\n     GetVersionReq,\n     GetVersionRsp,\n+    NegotiateAlgsReq,\n+    NegotiateAlgsRsp,\n     SpdmErrorRsp,\n     SpdmHeader, //\n };\n@@ -56,6 +75,25 @@\n ///  Negotiated during GET_VERSION exchange.\n /// @rsp_caps: Cached capabilities of responder.\n ///  Received during GET_CAPABILITIES exchange.\n+/// @base_asym_alg: Asymmetric key algorithm for signature verification of\n+///  CHALLENGE_AUTH and MEASUREMENTS messages.\n+///  Selected by responder during NEGOTIATE_ALGORITHMS exchange.\n+/// @base_hash_alg: Hash algorithm for signature verification of\n+///  CHALLENGE_AUTH and MEASUREMENTS messages.\n+///  Selected by responder during NEGOTIATE_ALGORITHMS exchange.\n+/// @meas_hash_alg: Hash algorithm for measurement blocks.\n+///  Selected by responder during NEGOTIATE_ALGORITHMS exchange.\n+/// @base_asym_enc: Human-readable name of @base_asym_alg's signature encoding.\n+///  Passed to crypto subsystem when calling verify_signature().\n+/// @sig_len: Signature length of @base_asym_alg (in bytes).\n+///  S or SigLen in SPDM specification.\n+/// @base_hash_alg_name: Human-readable name of @base_hash_alg.\n+///  Passed to crypto subsystem when calling crypto_alloc_shash() and\n+///  verify_signature().\n+/// @shash: Synchronous hash handle for @base_hash_alg computation.\n+/// @desc: Synchronous hash context for @base_hash_alg computation.\n+/// @hash_len: Hash length of @base_hash_alg (in bytes).\n+///  H in SPDM specification.\n #[expect(dead_code)]\n pub struct SpdmState {\n     pub(crate) dev: *mut bindings::device,\n@@ -67,6 +105,19 @@ pub struct SpdmState {\n     // Negotiated state\n     pub(crate) version: u8,\n     pub(crate) rsp_caps: u32,\n+    pub(crate) base_asym_alg: u32,\n+    pub(crate) base_hash_alg: u32,\n+    pub(crate) meas_hash_alg: u32,\n+\n+    /* Signature algorithm */\n+    base_asym_enc: &'static CStr,\n+    sig_len: usize,\n+\n+    /* Hash algorithm */\n+    base_hash_alg_name: &'static CStr,\n+    pub(crate) shash: *mut bindings::crypto_shash,\n+    pub(crate) desc: Option<&'static mut bindings::shash_desc>,\n+    pub(crate) hash_len: usize,\n }\n \n impl SpdmState {\n@@ -85,6 +136,15 @@ pub(crate) fn new(\n             validate,\n             version: SPDM_MIN_VER,\n             rsp_caps: 0,\n+            base_asym_alg: 0,\n+            base_hash_alg: 0,\n+            meas_hash_alg: 0,\n+            base_asym_enc: unsafe { CStr::from_bytes_with_nul_unchecked(b\"\\0\") },\n+            sig_len: 0,\n+            base_hash_alg_name: unsafe { CStr::from_bytes_with_nul_unchecked(b\"\\0\") },\n+            shash: core::ptr::null_mut(),\n+            desc: None,\n+            hash_len: 0,\n         }\n     }\n \n@@ -367,4 +427,155 @@ pub(crate) fn get_capabilities(&mut self) -> Result<(), Error> {\n \n         Ok(())\n     }\n+\n+    fn update_response_algs(&mut self) -> Result<(), Error> {\n+        match self.base_asym_alg {\n+            SPDM_ASYM_RSASSA_2048 => {\n+                self.sig_len = 256;\n+                self.base_asym_enc = CStr::from_bytes_with_nul(b\"pkcs1\\0\")?;\n+            }\n+            SPDM_ASYM_RSASSA_3072 => {\n+                self.sig_len = 384;\n+                self.base_asym_enc = CStr::from_bytes_with_nul(b\"pkcs1\\0\")?;\n+            }\n+            SPDM_ASYM_RSASSA_4096 => {\n+                self.sig_len = 512;\n+                self.base_asym_enc = CStr::from_bytes_with_nul(b\"pkcs1\\0\")?;\n+            }\n+            SPDM_ASYM_ECDSA_ECC_NIST_P256 => {\n+                self.sig_len = 64;\n+                self.base_asym_enc = CStr::from_bytes_with_nul(b\"p1363\\0\")?;\n+            }\n+            SPDM_ASYM_ECDSA_ECC_NIST_P384 => {\n+                self.sig_len = 96;\n+                self.base_asym_enc = CStr::from_bytes_with_nul(b\"p1363\\0\")?;\n+            }\n+            SPDM_ASYM_ECDSA_ECC_NIST_P521 => {\n+                self.sig_len = 132;\n+                self.base_asym_enc = CStr::from_bytes_with_nul(b\"p1363\\0\")?;\n+            }\n+            _ => {\n+                pr_err!(\"Unknown asym algorithm\\n\");\n+                return Err(EINVAL);\n+            }\n+        }\n+\n+        match self.base_hash_alg {\n+            SPDM_HASH_SHA_256 => {\n+                self.base_hash_alg_name = CStr::from_bytes_with_nul(b\"sha256\\0\")?;\n+            }\n+            SPDM_HASH_SHA_384 => {\n+                self.base_hash_alg_name = CStr::from_bytes_with_nul(b\"sha384\\0\")?;\n+            }\n+            SPDM_HASH_SHA_512 => {\n+                self.base_hash_alg_name = CStr::from_bytes_with_nul(b\"sha512\\0\")?;\n+            }\n+            _ => {\n+                pr_err!(\"Unknown hash algorithm\\n\");\n+                return Err(EINVAL);\n+            }\n+        }\n+\n+        /*\n+         * shash and desc allocations are reused for subsequent measurement\n+         * retrieval, hence are not freed until spdm_reset().\n+         */\n+        self.shash =\n+            unsafe { bindings::crypto_alloc_shash(self.base_hash_alg_name.as_char_ptr(), 0, 0) };\n+        from_err_ptr(self.shash)?;\n+\n+        let desc_len = core::mem::size_of::<bindings::shash_desc>()\n+            + unsafe { bindings::crypto_shash_descsize(self.shash) } as usize;\n+\n+        let mut desc_vec: KVec<u8> = KVec::with_capacity(desc_len, GFP_KERNEL)?;\n+        // SAFETY: `desc_vec` is `desc_len` long\n+        let desc_buf = unsafe { from_raw_parts_mut(desc_vec.as_mut_ptr(), desc_len) };\n+\n+        let desc = unsafe {\n+            core::mem::transmute::<*mut c_void, &mut bindings::shash_desc>(\n+                desc_buf.as_mut_ptr() as *mut c_void\n+            )\n+        };\n+        desc.tfm = self.shash;\n+\n+        self.desc = Some(desc);\n+\n+        /* Used frequently to compute offsets, so cache H */\n+        self.hash_len = unsafe { bindings::crypto_shash_digestsize(self.shash) as usize };\n+\n+        if let Some(desc) = &mut self.desc {\n+            unsafe { to_result(bindings::crypto_shash_init(*desc)) }\n+        } else {\n+            Err(ENOMEM)\n+        }\n+    }\n+\n+    pub(crate) fn negotiate_algs(&mut self) -> Result<(), Error> {\n+        let mut request = NegotiateAlgsReq::default();\n+        request.version = self.version;\n+\n+        if self.version >= SPDM_VER_12 && (self.rsp_caps & SPDM_KEY_EX_CAP) == SPDM_KEY_EX_CAP {\n+            request.other_params_support = SPDM_OPAQUE_DATA_FMT_GENERAL;\n+        }\n+\n+        let req_sz = core::mem::size_of::<NegotiateAlgsReq>();\n+        let rsp_sz = core::mem::size_of::<NegotiateAlgsRsp>();\n+\n+        request.length = req_sz as u16;\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 < (rsp_sz as i32) {\n+            pr_err!(\"Truncated capabilities 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 NegotiateAlgsRsp =\n+            Untrusted::new_mut(&mut response_vec).validate_mut()?;\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+\n+        if self.base_asym_alg & SPDM_ASYM_ALGOS == 0 || self.base_hash_alg & SPDM_HASH_ALGOS == 0 {\n+            pr_err!(\"No common supported algorithms\\n\");\n+            to_result(-(bindings::EPROTO as i32))?;\n+        }\n+\n+        // /* Responder shall select exactly 1 alg (SPDM 1.0.0 table 14) */\n+        if self.base_asym_alg.count_ones() != 1\n+            || self.base_hash_alg.count_ones() != 1\n+            || response.ext_asym_sel_count != 0\n+            || response.ext_hash_sel_count != 0\n+            || response.param1 > request.param1\n+            || response.other_params_sel != request.other_params_support\n+        {\n+            pr_err!(\"Malformed algorithms response\\n\");\n+            to_result(-(bindings::EPROTO as i32))?;\n+        }\n+\n+        if self.rsp_caps & SPDM_MEAS_CAP_MASK == SPDM_MEAS_CAP_MASK\n+            && (self.meas_hash_alg.count_ones() != 1\n+                || response.measurement_specification_sel != SPDM_MEAS_SPEC_DMTF)\n+        {\n+            pr_err!(\"Malformed algorithms response\\n\");\n+            to_result(-(bindings::EPROTO as i32))?;\n+        }\n+\n+        self.update_response_algs()?;\n+\n+        Ok(())\n+    }\n }\ndiff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs\nindex 7dc55882c880..9d738133399d 100644\n--- a/lib/rspdm/validator.rs\n+++ b/lib/rspdm/validator.rs\n@@ -9,7 +9,8 @@\n \n use crate::bindings::{\n     __IncompleteArrayField,\n-    __le16, //\n+    __le16,\n+    __le32, //\n };\n use crate::consts::SpdmErrorCode;\n use core::mem;\n@@ -26,10 +27,14 @@\n };\n \n use crate::consts::{\n+    SPDM_ASYM_ALGOS,\n     SPDM_CTEXPONENT,\n     SPDM_GET_CAPABILITIES,\n     SPDM_GET_VERSION,\n+    SPDM_HASH_ALGOS,\n+    SPDM_MEAS_SPEC_DMTF,\n     SPDM_MIN_VER,\n+    SPDM_NEGOTIATE_ALGS,\n     SPDM_REQ_CAPS, //\n     SPDM_VER_11,\n };\n@@ -236,3 +241,111 @@ 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 RegAlg {\n+    pub(crate) alg_type: u8,\n+    pub(crate) alg_count: u8,\n+    pub(crate) alg_supported: u16,\n+    pub(crate) alg_external: __IncompleteArrayField<__le32>,\n+}\n+\n+#[repr(C, packed)]\n+pub(crate) struct NegotiateAlgsReq {\n+    pub(crate) version: u8,\n+    pub(crate) code: u8,\n+    pub(crate) param1: u8, // size of resp_alg_struct\n+    param2: u8,\n+\n+    pub(crate) length: u16,\n+    pub(crate) measurement_specification: u8,\n+    pub(crate) other_params_support: u8,\n+\n+    pub(crate) base_asym_algo: u32,\n+    pub(crate) base_hash_algo: u32,\n+\n+    reserved1: [u8; 12],\n+\n+    pub(crate) ext_asym_count: u8,\n+    pub(crate) ext_hash_count: u8,\n+    reserved2: u8,\n+    pub(crate) mel_specification: u8,\n+\n+    pub(crate) ext_asym: __IncompleteArrayField<__le32>,\n+    pub(crate) ext_hash: __IncompleteArrayField<__le32>,\n+    pub(crate) resp_alg_struct: __IncompleteArrayField<RegAlg>,\n+}\n+\n+impl Default for NegotiateAlgsReq {\n+    fn default() -> Self {\n+        NegotiateAlgsReq {\n+            version: 0,\n+            code: SPDM_NEGOTIATE_ALGS,\n+            param1: 0, // Size of resp_alg_struct\n+            param2: 0,\n+            length: 32,\n+            measurement_specification: SPDM_MEAS_SPEC_DMTF,\n+            other_params_support: 0,\n+            base_asym_algo: SPDM_ASYM_ALGOS.to_le(),\n+            base_hash_algo: SPDM_HASH_ALGOS.to_le(),\n+            reserved1: [0u8; 12],\n+            ext_asym_count: 0,\n+            ext_hash_count: 0,\n+            reserved2: 0,\n+            mel_specification: 0,\n+            ext_asym: __IncompleteArrayField::new(),\n+            ext_hash: __IncompleteArrayField::new(),\n+            resp_alg_struct: __IncompleteArrayField::new(),\n+        }\n+    }\n+}\n+\n+#[repr(C, packed)]\n+pub(crate) struct NegotiateAlgsRsp {\n+    pub(crate) version: u8,\n+    pub(crate) code: u8,\n+    pub(crate) param1: u8,\n+    pub(crate) param2: u8,\n+\n+    pub(crate) length: u16,\n+    pub(crate) measurement_specification_sel: u8,\n+    pub(crate) other_params_sel: u8,\n+\n+    pub(crate) measurement_hash_algo: u32,\n+    pub(crate) base_asym_sel: u32,\n+    pub(crate) base_hash_sel: u32,\n+\n+    reserved1: [u8; 11],\n+\n+    pub(crate) mel_specification_sel: u8,\n+    pub(crate) ext_asym_sel_count: u8,\n+    pub(crate) ext_hash_sel_count: u8,\n+    reserved2: [u8; 2],\n+\n+    pub(crate) ext_asym: __IncompleteArrayField<__le32>,\n+    pub(crate) ext_hash: __IncompleteArrayField<__le32>,\n+    pub(crate) resp_alg_struct: __IncompleteArrayField<RegAlg>,\n+}\n+\n+impl Validate<&mut Unvalidated<KVec<u8>>> for &mut NegotiateAlgsRsp {\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::<NegotiateAlgsRsp>() {\n+            return Err(EINVAL);\n+        }\n+\n+        let ptr = raw.as_mut_ptr();\n+        // CAST: `NegotiateAlgsRsp` only contains integers and has `repr(C)`.\n+        let ptr = ptr.cast::<NegotiateAlgsRsp>();\n+        // SAFETY: `ptr` came from a reference and the cast above is valid.\n+        let rsp: &mut NegotiateAlgsRsp = unsafe { &mut *ptr };\n+\n+        rsp.base_asym_sel = rsp.base_asym_sel.to_le();\n+        rsp.base_hash_sel = rsp.base_hash_sel.to_le();\n+        rsp.measurement_hash_algo = rsp.measurement_hash_algo.to_le();\n+\n+        Ok(rsp)\n+    }\n+}\n","prefixes":["13/18"]}