public inbox for linux-cxl@vger.kernel.org
 help / color / mirror / Atom feed
From: Alexey Kardashevskiy <aik@amd.com>
To: Lukas Wunner <lukas@wunner.de>,
	Jonathan Cameron <Jonathan.Cameron@huawei.com>,
	Bjorn Helgaas <helgaas@kernel.org>,
	David Howells <dhowells@redhat.com>,
	Herbert Xu <herbert@gondor.apana.org.au>,
	"David S. Miller" <davem@davemloft.net>,
	David Woodhouse <dwmw2@infradead.org>,
	James Bottomley <James.Bottomley@HansenPartnership.com>,
	linux-pci@vger.kernel.org, linux-cxl@vger.kernel.org,
	linux-coco@lists.linux.dev, keyrings@vger.kernel.org,
	linux-crypto@vger.kernel.org
Cc: linuxarm@huawei.com, David Box <david.e.box@intel.com>,
	Dan Williams <dan.j.williams@intel.com>,
	"Li, Ming" <ming4.li@intel.com>,
	Ilpo Jarvinen <ilpo.jarvinen@linux.intel.com>,
	Alistair Francis <alistair.francis@wdc.com>,
	Wilfred Mallawa <wilfred.mallawa@wdc.com>,
	Damien Le Moal <dlemoal@kernel.org>,
	Dhaval Giani <dhaval.giani@amd.com>,
	Gobikrishna Dhanuskodi <gdhanuskodi@nvidia.com>,
	Jason Gunthorpe <jgg@nvidia.com>, Peter Gonda <pgonda@google.com>,
	Jerome Glisse <jglisse@google.com>,
	Sean Christopherson <seanjc@google.com>,
	Alexander Graf <graf@amazon.com>,
	Samuel Ortiz <sameo@rivosinc.com>,
	Eric Biggers <ebiggers@google.com>,
	Stefan Berger <stefanb@linux.ibm.com>
Subject: Re: [PATCH v2 07/18] spdm: Introduce library to authenticate devices
Date: Mon, 8 Jul 2024 19:57:02 +1000	[thread overview]
Message-ID: <26715537-5dc4-46c1-bdcd-c760696dd418@amd.com> (raw)
In-Reply-To: <bbbea6e1b7d27463243a0fcb871ad2953312fe3a.1719771133.git.lukas@wunner.de>



On 1/7/24 05:42, Lukas Wunner wrote:
> From: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> 
> The Security Protocol and Data Model (SPDM) allows for device
> authentication, measurement, key exchange and encrypted sessions.
> 
> SPDM was conceived by the Distributed Management Task Force (DMTF).
> Its specification defines a request/response protocol spoken between
> host and attached devices over a variety of transports:
> 
>    https://www.dmtf.org/dsp/DSP0274
> 
> This implementation supports SPDM 1.0 through 1.3 (the latest version).
> It is designed to be transport-agnostic as the kernel already supports
> four different SPDM-capable transports:
> 
> * PCIe Data Object Exchange, which is a mailbox in PCI config space
>    (PCIe r6.2 sec 6.30, drivers/pci/doe.c)
> * Management Component Transport Protocol
>    (MCTP, Documentation/networking/mctp.rst)
> * TCP/IP (in draft stage)
>    https://www.dmtf.org/sites/default/files/standards/documents/DSP0287_1.0.0WIP99.pdf
> * SCSI and ATA (in draft stage)
>    "SECURITY PROTOCOL IN/OUT" and "TRUSTED SEND/RECEIVE" commands
> 
> Use cases for SPDM include, but are not limited to:
> 
> * PCIe Component Measurement and Authentication (PCIe r6.2 sec 6.31)
> * Compute Express Link (CXL r3.0 sec 14.11.6)
> * Open Compute Project (Attestation of System Components v1.0)
>    https://www.opencompute.org/documents/attestation-v1-0-20201104-pdf
> * Open Compute Project (Datacenter NVMe SSD Specification v2.0)
>    https://www.opencompute.org/documents/datacenter-nvme-ssd-specification-v2-0r21-pdf
> 
> The initial focus of this implementation is enabling PCIe CMA device
> authentication.  As such, only a subset of the SPDM specification is
> contained herein, namely the request/response sequence GET_VERSION,
> GET_CAPABILITIES, NEGOTIATE_ALGORITHMS, GET_DIGESTS, GET_CERTIFICATE
> and CHALLENGE.
> 
> This sequence first negotiates the SPDM protocol version, capabilities
> and algorithms with the device.  It then retrieves the up to eight
> certificate chains which may be provisioned on the device.  Finally it
> performs challenge-response authentication with the device using one of
> those eight certificate chains and the algorithms negotiated before.
> The challenge-response authentication comprises computing a hash over
> all exchanged messages to detect modification by a man-in-the-middle
> or media error.  The hash is then signed with the device's private key
> and the resulting signature is verified by the kernel using the device's
> public key from the certificate chain.  Nonces are included in the
> message sequence to protect against replay attacks.
> 
> A simple API is provided for subsystems wishing to authenticate devices:
> spdm_create(), spdm_authenticate() (can be called repeatedly for
> reauthentication) and spdm_destroy().  Certificates presented by devices
> are validated against an in-kernel keyring of trusted root certificates.
> A pointer to the keyring is passed to spdm_create().
> 
> The set of supported cryptographic algorithms is limited to those
> declared mandatory in PCIe r6.2 sec 6.31.3.  Adding more algorithms
> is straightforward as long as the crypto subsystem supports them.
> 
> Future commits will extend this implementation with support for
> measurement, key exchange and encrypted sessions.
> 
> So far, only the SPDM requester role is implemented.  Care was taken to
> allow for effortless addition of the responder role at a later stage.
> This could be needed for a PCIe host bridge operating in endpoint mode.
> The responder role will be able to reuse struct definitions and helpers
> such as spdm_create_combined_prefix().
> 
> Credits:  Jonathan wrote a proof-of-concept of this SPDM implementation.
> Lukas reworked it for upstream.
> 
> Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> Co-developed-by: Lukas Wunner <lukas@wunner.de>
> Signed-off-by: Lukas Wunner <lukas@wunner.de>
> ---
>   MAINTAINERS                 |  11 +
>   include/linux/spdm.h        |  33 ++
>   lib/Kconfig                 |  15 +
>   lib/Makefile                |   2 +
>   lib/spdm/Makefile           |  10 +
>   lib/spdm/core.c             | 425 ++++++++++++++++++++++
>   lib/spdm/req-authenticate.c | 704 ++++++++++++++++++++++++++++++++++++
>   lib/spdm/spdm.h             | 520 ++++++++++++++++++++++++++
>   8 files changed, 1720 insertions(+)
>   create mode 100644 include/linux/spdm.h
>   create mode 100644 lib/spdm/Makefile
>   create mode 100644 lib/spdm/core.c
>   create mode 100644 lib/spdm/req-authenticate.c
>   create mode 100644 lib/spdm/spdm.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index d6c90161c7bf..dbe16eea8818 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -20145,6 +20145,17 @@ M:	Security Officers <security@kernel.org>
>   S:	Supported
>   F:	Documentation/process/security-bugs.rst
>   
> +SECURITY PROTOCOL AND DATA MODEL (SPDM)
> +M:	Jonathan Cameron <jic23@kernel.org>
> +M:	Lukas Wunner <lukas@wunner.de>
> +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/spdm/
> +
>   SECURITY SUBSYSTEM
>   M:	Paul Moore <paul@paul-moore.com>
>   M:	James Morris <jmorris@namei.org>
> diff --git a/include/linux/spdm.h b/include/linux/spdm.h
> new file mode 100644
> index 000000000000..0da7340020c4
> --- /dev/null
> +++ b/include/linux/spdm.h
> @@ -0,0 +1,33 @@
> +/* 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 <Jonathan.Cameron@huawei.com>
> + *
> + * Copyright (C) 2022-24 Intel Corporation
> + */
> +
> +#ifndef _SPDM_H_
> +#define _SPDM_H_
> +
> +#include <linux/types.h>
> +
> +struct key;
> +struct device;
> +struct spdm_state;
> +
> +typedef ssize_t (spdm_transport)(void *priv, struct device *dev,
> +				 const void *request, size_t request_sz,
> +				 void *response, size_t response_sz);
> +
> +struct spdm_state *spdm_create(struct device *dev, spdm_transport *transport,
> +			       void *transport_priv, u32 transport_sz,
> +			       struct key *keyring);
> +
> +int spdm_authenticate(struct spdm_state *spdm_state);
> +
> +void spdm_destroy(struct spdm_state *spdm_state);
> +
> +#endif
> diff --git a/lib/Kconfig b/lib/Kconfig
> index d33a268bc256..9011fa32af45 100644
> --- a/lib/Kconfig
> +++ b/lib/Kconfig
> @@ -782,3 +782,18 @@ config POLYNOMIAL
>   
>   config FIRMWARE_TABLE
>   	bool
> +
> +config SPDM
> +	tristate
> +	select CRYPTO
> +	select KEYS
> +	select ASYMMETRIC_KEY_TYPE
> +	select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
> +	select X509_CERTIFICATE_PARSER
> +	help
> +	  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.  Drivers selecting SPDM therefore need to also select
> +	  any algorithms they deem mandatory.
> diff --git a/lib/Makefile b/lib/Makefile
> index 3b1769045651..b2ef14d1fa71 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -301,6 +301,8 @@ obj-$(CONFIG_PERCPU_TEST) += percpu_test.o
>   obj-$(CONFIG_ASN1) += asn1_decoder.o
>   obj-$(CONFIG_ASN1_ENCODER) += asn1_encoder.o
>   
> +obj-$(CONFIG_SPDM) += spdm/
> +
>   obj-$(CONFIG_FONT_SUPPORT) += fonts/
>   
>   hostprogs	:= gen_crc32table
> diff --git a/lib/spdm/Makefile b/lib/spdm/Makefile
> new file mode 100644
> index 000000000000..f579cc898dbc
> --- /dev/null
> +++ b/lib/spdm/Makefile
> @@ -0,0 +1,10 @@
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# DMTF Security Protocol and Data Model (SPDM)
> +# https://www.dmtf.org/dsp/DSP0274
> +#
> +# Copyright (C) 2024 Intel Corporation
> +
> +obj-$(CONFIG_SPDM) += spdm.o
> +
> +spdm-y := core.o req-authenticate.o
> diff --git a/lib/spdm/core.c b/lib/spdm/core.c
> new file mode 100644
> index 000000000000..f06402f6d127
> --- /dev/null
> +++ b/lib/spdm/core.c
> @@ -0,0 +1,425 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * DMTF Security Protocol and Data Model (SPDM)
> + * https://www.dmtf.org/dsp/DSP0274
> + *
> + * Core routines for message exchange, message transcript,
> + * signature verification and session state lifecycle
> + *
> + * Copyright (C) 2021-22 Huawei
> + *     Jonathan Cameron <Jonathan.Cameron@huawei.com>
> + *
> + * Copyright (C) 2022-24 Intel Corporation
> + */
> +
> +#include "spdm.h"
> +
> +#include <linux/dev_printk.h>
> +#include <linux/module.h>
> +
> +#include <crypto/hash.h>
> +#include <crypto/public_key.h>
> +
> +static int spdm_err(struct device *dev, struct spdm_error_rsp *rsp)
> +{
> +	switch (rsp->error_code) {
> +	case SPDM_INVALID_REQUEST:
> +		dev_err(dev, "Invalid request\n");
> +		return -EINVAL;
> +	case SPDM_INVALID_SESSION:
> +		if (rsp->version == 0x11) {
> +			dev_err(dev, "Invalid session %#x\n", rsp->error_data);
> +			return -EINVAL;
> +		}
> +		break;
> +	case SPDM_BUSY:
> +		dev_err(dev, "Busy\n");
> +		return -EBUSY;
> +	case SPDM_UNEXPECTED_REQUEST:
> +		dev_err(dev, "Unexpected request\n");
> +		return -EINVAL;
> +	case SPDM_UNSPECIFIED:
> +		dev_err(dev, "Unspecified error\n");
> +		return -EINVAL;
> +	case SPDM_DECRYPT_ERROR:
> +		dev_err(dev, "Decrypt error\n");
> +		return -EIO;
> +	case SPDM_UNSUPPORTED_REQUEST:
> +		dev_err(dev, "Unsupported request %#x\n", rsp->error_data);
> +		return -EINVAL;
> +	case SPDM_REQUEST_IN_FLIGHT:
> +		dev_err(dev, "Request in flight\n");
> +		return -EINVAL;
> +	case SPDM_INVALID_RESPONSE_CODE:
> +		dev_err(dev, "Invalid response code\n");
> +		return -EINVAL;
> +	case SPDM_SESSION_LIMIT_EXCEEDED:
> +		dev_err(dev, "Session limit exceeded\n");
> +		return -EBUSY;
> +	case SPDM_SESSION_REQUIRED:
> +		dev_err(dev, "Session required\n");
> +		return -EINVAL;
> +	case SPDM_RESET_REQUIRED:
> +		dev_err(dev, "Reset required\n");
> +		return -ECONNRESET;
> +	case SPDM_RESPONSE_TOO_LARGE:
> +		dev_err(dev, "Response too large\n");
> +		return -EINVAL;
> +	case SPDM_REQUEST_TOO_LARGE:
> +		dev_err(dev, "Request too large\n");
> +		return -EINVAL;
> +	case SPDM_LARGE_RESPONSE:
> +		dev_err(dev, "Large response\n");
> +		return -EMSGSIZE;
> +	case SPDM_MESSAGE_LOST:
> +		dev_err(dev, "Message lost\n");
> +		return -EIO;
> +	case SPDM_INVALID_POLICY:
> +		dev_err(dev, "Invalid policy\n");
> +		return -EINVAL;
> +	case SPDM_VERSION_MISMATCH:
> +		dev_err(dev, "Version mismatch\n");
> +		return -EINVAL;
> +	case SPDM_RESPONSE_NOT_READY:
> +		dev_err(dev, "Response not ready\n");
> +		return -EINPROGRESS;
> +	case SPDM_REQUEST_RESYNCH:
> +		dev_err(dev, "Request resynchronization\n");
> +		return -ECONNRESET;
> +	case SPDM_OPERATION_FAILED:
> +		dev_err(dev, "Operation failed\n");
> +		return -EINVAL;
> +	case SPDM_NO_PENDING_REQUESTS:
> +		return -ENOENT;
> +	case SPDM_VENDOR_DEFINED_ERROR:
> +		dev_err(dev, "Vendor defined error\n");
> +		return -EINVAL;
> +	}
> +
> +	dev_err(dev, "Undefined error %#x\n", rsp->error_code);
> +	return -EINVAL;
> +}
> +
> +/**
> + * 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 @rsp_sz.
> + * The request version is automatically populated.
> + *
> + * Return response size on success or a negative errno.  Response size may 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 the
> + * transport receives only dword-sized chunks.
> + */
> +ssize_t spdm_exchange(struct spdm_state *spdm_state,
> +		      void *req, size_t req_sz, void *rsp, size_t rsp_sz)
> +{
> +	struct spdm_header *request = req;
> +	struct spdm_header *response = rsp;
> +	ssize_t rc, length;
> +
> +	if (req_sz < sizeof(struct spdm_header) ||
> +	    rsp_sz < sizeof(struct spdm_header))
> +		return -EINVAL;
> +
> +	request->version = spdm_state->version;
> +
> +	rc = spdm_state->transport(spdm_state->transport_priv, spdm_state->dev,
> +				   req, req_sz, rsp, rsp_sz);
> +	if (rc < 0)
> +		return rc;
> +
> +	length = rc;
> +	if (length < sizeof(struct spdm_header))
> +		return length; /* Truncated response is handled by callers */
> +
> +	if (response->code == SPDM_ERROR)
> +		return spdm_err(spdm_state->dev, (struct spdm_error_rsp *)rsp);
> +
> +	if (response->code != (request->code & ~SPDM_REQ)) {
> +		dev_err(spdm_state->dev,
> +			"Response code %#x does not match request code %#x\n",
> +			response->code, request->code);
> +		return -EPROTO;
> +	}
> +
> +	return length;
> +}
> +
> +/**
> + * spdm_alloc_transcript() - Allocate transcript buffer
> + *
> + * @spdm_state: SPDM session state
> + *
> + * Allocate a buffer to accommodate the concatenation of all SPDM messages
> + * exchanged during an authentication sequence.  Used to verify the signature,
> + * as it is computed over the hashed transcript.
> + *
> + * Transcript size is initially one page.  It grows by additional pages as
> + * needed.  Minimum size of an authentication sequence is 1k (only one slot
> + * occupied, only one ECC P256 certificate in chain, SHA 256 hash selected).
> + * Maximum can be several MBytes.  Between 4k and 64k is probably typical.
> + *
> + * Return 0 on success or a negative errno.
> + */
> +int spdm_alloc_transcript(struct spdm_state *spdm_state)
> +{
> +	spdm_state->transcript = kvmalloc(PAGE_SIZE, GFP_KERNEL);
> +	if (!spdm_state->transcript)
> +		return -ENOMEM;
> +
> +	spdm_state->transcript_end = spdm_state->transcript;
> +	spdm_state->transcript_max = PAGE_SIZE;
> +
> +	return 0;
> +}
> +
> +/**
> + * spdm_free_transcript() - Free transcript buffer
> + *
> + * @spdm_state: SPDM session state
> + *
> + * Free the transcript buffer after performing authentication.  Reset the
> + * pointer to the current end of transcript as well as the allocation size.
> + */
> +void spdm_free_transcript(struct spdm_state *spdm_state)
> +{
> +	kvfree(spdm_state->transcript);
> +	spdm_state->transcript_end = NULL;
> +	spdm_state->transcript_max = 0;
> +}
> +
> +/**
> + * spdm_append_transcript() - Append a message to transcript buffer
> + *
> + * @spdm_state: SPDM session state
> + * @msg: SPDM message
> + * @msg_sz: Size of @msg
> + *
> + * Append an SPDM message to the transcript after reception or transmission.
> + * Reallocate a larger transcript buffer if the message exceeds its current
> + * allocation size.
> + *
> + * If the message to be appended is known to fit into the allocation size,
> + * it may be directly received into or transmitted from the transcript buffer
> + * instead of calling this function:  Simply use the @transcript_end pointer in
> + * struct spdm_state as the position to store the message, then advance the
> + * pointer by the message size.
> + *
> + * Return 0 on success or a negative errno.
> + */
> +int spdm_append_transcript(struct spdm_state *spdm_state,
> +			   const void *msg, size_t msg_sz)
> +{
> +	size_t transcript_sz = spdm_state->transcript_end -
> +			       spdm_state->transcript;
> +
> +	if (transcript_sz + msg_sz > spdm_state->transcript_max) {
> +		size_t new_sz = round_up(transcript_sz + msg_sz, PAGE_SIZE);
> +		void *new = kvrealloc(spdm_state->transcript,
> +				      spdm_state->transcript_max,
> +				      new_sz, GFP_KERNEL);
> +		if (!new)
> +			return -ENOMEM;
> +
> +		spdm_state->transcript = new;
> +		spdm_state->transcript_end = new + transcript_sz;
> +		spdm_state->transcript_max = new_sz;
> +	}
> +
> +	memcpy(spdm_state->transcript_end, msg, msg_sz);
> +	spdm_state->transcript_end += msg_sz;
> +
> +	return 0;
> +}
> +
> +/**
> + * spdm_create_combined_prefix() - Create combined_spdm_prefix for a hash
> + *
> + * @version: SPDM version negotiated during GET_VERSION exchange
> + * @spdm_context: SPDM context of signature generation (or verification)
> + * @buf: Buffer to receive combined_spdm_prefix (100 bytes)
> + *
> + * From SPDM 1.2, a hash is prefixed with the SPDM version and context before
> + * a signature is generated (or verified) over the resulting concatenation
> + * (SPDM 1.2.0 section 15).  Create that prefix.
> + */
> +void spdm_create_combined_prefix(u8 version, const char *spdm_context,
> +				 void *buf)
> +{
> +	u8 major = FIELD_GET(0xf0, version);
> +	u8 minor = FIELD_GET(0x0f, version);
> +	size_t len = strlen(spdm_context);
> +	int rc, zero_pad;
> +
> +	rc = snprintf(buf, SPDM_PREFIX_SZ + 1,
> +		      "dmtf-spdm-v%hhx.%hhx.*dmtf-spdm-v%hhx.%hhx.*"
> +		      "dmtf-spdm-v%hhx.%hhx.*dmtf-spdm-v%hhx.%hhx.*",
> +		      major, minor, major, minor, major, minor, major, minor);
> +	WARN_ON(rc != SPDM_PREFIX_SZ);
> +
> +	zero_pad = SPDM_COMBINED_PREFIX_SZ - SPDM_PREFIX_SZ - 1 - len;
> +	WARN_ON(zero_pad < 0);
> +
> +	memset(buf + SPDM_PREFIX_SZ + 1, 0, zero_pad);
> +	memcpy(buf + SPDM_PREFIX_SZ + 1 + zero_pad, spdm_context, len);
> +}
> +
> +/**
> + * spdm_verify_signature() - Verify signature against leaf key
> + *
> + * @spdm_state: SPDM session state
> + * @spdm_context: SPDM context (used to create combined_spdm_prefix)
> + *
> + * Implementation of the abstract SPDMSignatureVerify() function described in
> + * SPDM 1.2.0 section 16:  Compute the hash over @spdm_state->transcript and
> + * verify that the signature at the end of the transcript was generated by
> + * @spdm_state->leaf_key.  Hashing the entire transcript allows detection
> + * of message modification by a man-in-the-middle or media error.
> + *
> + * Return 0 on success or a negative errno.
> + */
> +int spdm_verify_signature(struct spdm_state *spdm_state,
> +			  const char *spdm_context)
> +{
> +	struct public_key_signature sig = {
> +		.s = spdm_state->transcript_end - spdm_state->sig_len,
> +		.s_size = spdm_state->sig_len,
> +		.encoding = spdm_state->base_asym_enc,
> +		.hash_algo = spdm_state->base_hash_alg_name,
> +	};
> +	u8 *mhash __free(kfree) = NULL;
> +	u8 *m __free(kfree);
> +	int rc;
> +
> +	m = kmalloc(SPDM_COMBINED_PREFIX_SZ + spdm_state->hash_len, GFP_KERNEL);
> +	if (!m)
> +		return -ENOMEM;
> +
> +	/* Hash the transcript (sans trailing signature) */
> +	rc = crypto_shash_digest(spdm_state->desc, spdm_state->transcript,
> +				 (void *)sig.s - spdm_state->transcript,
> +				 m + SPDM_COMBINED_PREFIX_SZ);
> +	if (rc)
> +		return rc;
> +
> +	if (spdm_state->version <= 0x11) {
> +		/*
> +		 * SPDM 1.0 and 1.1 compute the signature only over the hash
> +		 * (SPDM 1.0.0 section 4.9.2.7).
> +		 */
> +		sig.digest = m + SPDM_COMBINED_PREFIX_SZ;
> +		sig.digest_size = spdm_state->hash_len;
> +	} else {
> +		/*
> +		 * From SPDM 1.2, the hash is prefixed with spdm_context before
> +		 * computing the signature over the resulting message M
> +		 * (SPDM 1.2.0 sec 15).
> +		 */
> +		spdm_create_combined_prefix(spdm_state->version, spdm_context,
> +					    m);
> +
> +		/*
> +		 * RSA and ECDSA algorithms require that M is hashed once more.
> +		 * EdDSA and SM2 algorithms omit that step.
> +		 * The switch statement prepares for their introduction.
> +		 */
> +		switch (spdm_state->base_asym_alg) {
> +		default:
> +			mhash = kmalloc(spdm_state->hash_len, GFP_KERNEL);
> +			if (!mhash)
> +				return -ENOMEM;
> +
> +			rc = crypto_shash_digest(spdm_state->desc, m,
> +				SPDM_COMBINED_PREFIX_SZ + spdm_state->hash_len,
> +				mhash);
> +			if (rc)
> +				return rc;
> +
> +			sig.digest = mhash;
> +			sig.digest_size = spdm_state->hash_len;
> +			break;
> +		}
> +	}
> +
> +	return public_key_verify_signature(spdm_state->leaf_key, &sig);
> +}
> +
> +/**
> + * spdm_reset() - Free cryptographic data structures
> + *
> + * @spdm_state: SPDM session state
> + *
> + * Free cryptographic data structures when an SPDM session is destroyed or
> + * when the device is reauthenticated.
> + */
> +void spdm_reset(struct spdm_state *spdm_state)
> +{
> +	public_key_free(spdm_state->leaf_key);
> +	spdm_state->leaf_key = NULL;
> +
> +	kfree(spdm_state->desc);
> +	spdm_state->desc = NULL;
> +
> +	crypto_free_shash(spdm_state->shash);
> +	spdm_state->shash = NULL;
> +}
> +
> +/**
> + * 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 bytes)
> + * @keyring: Trusted root certificates
> + *
> + * Return a pointer to the allocated SPDM session state or NULL on error.
> + */
> +struct spdm_state *spdm_create(struct device *dev, spdm_transport *transport,
> +			       void *transport_priv, u32 transport_sz,
> +			       struct key *keyring)
> +{
> +	struct spdm_state *spdm_state = kzalloc(sizeof(*spdm_state), GFP_KERNEL);
> +
> +	if (!spdm_state)
> +		return NULL;
> +
> +	spdm_state->dev = dev;
> +	spdm_state->transport = transport;
> +	spdm_state->transport_priv = transport_priv;
> +	spdm_state->transport_sz = transport_sz;
> +	spdm_state->root_keyring = keyring;
> +
> +	mutex_init(&spdm_state->lock);
> +
> +	return spdm_state;
> +}
> +EXPORT_SYMBOL_GPL(spdm_create);
> +
> +/**
> + * spdm_destroy() - Destroy SPDM session
> + *
> + * @spdm_state: SPDM session state
> + */
> +void spdm_destroy(struct spdm_state *spdm_state)
> +{
> +	u8 slot;
> +
> +	for_each_set_bit(slot, &spdm_state->provisioned_slots, SPDM_SLOTS)
> +		kvfree(spdm_state->slot[slot]);
> +
> +	spdm_reset(spdm_state);
> +	mutex_destroy(&spdm_state->lock);
> +	kfree(spdm_state);
> +}
> +EXPORT_SYMBOL_GPL(spdm_destroy);
> +
> +MODULE_LICENSE("GPL");
> diff --git a/lib/spdm/req-authenticate.c b/lib/spdm/req-authenticate.c
> new file mode 100644
> index 000000000000..51fdb88f519b
> --- /dev/null
> +++ b/lib/spdm/req-authenticate.c
> @@ -0,0 +1,704 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * DMTF Security Protocol and Data Model (SPDM)
> + * https://www.dmtf.org/dsp/DSP0274
> + *
> + * Requester role: Authenticate a device
> + *
> + * Copyright (C) 2021-22 Huawei
> + *     Jonathan Cameron <Jonathan.Cameron@huawei.com>
> + *
> + * Copyright (C) 2022-24 Intel Corporation
> + */
> +
> +#include "spdm.h"
> +
> +#include <linux/dev_printk.h>
> +#include <linux/key.h>
> +#include <linux/random.h>
> +
> +#include <asm/unaligned.h>
> +#include <crypto/hash.h>
> +#include <crypto/hash_info.h>
> +#include <keys/asymmetric-type.h>
> +#include <keys/x509-parser.h>
> +
> +/* SPDM 1.2.0 margin no 359 and 803 */
> +static const char *spdm_context = "responder-challenge_auth signing";
> +
> +/*
> + * All SPDM messages exchanged during an authentication sequence up to and
> + * including GET_DIGESTS fit into a single page, hence are stored in the
> + * transcript without bounds checking.  Only subsequent GET_CERTIFICATE
> + * and CHALLENGE exchanges may exceed one page.
> + */
> +static_assert(PAGE_SIZE >=
> +	sizeof(struct spdm_get_version_req) +
> +	struct_size_t(struct spdm_get_version_rsp,
> +		      version_number_entries, 255) +
> +	sizeof(struct spdm_get_capabilities_req) +
> +	sizeof(struct spdm_get_capabilities_rsp) +
> +	sizeof(struct spdm_negotiate_algs_req) +
> +	sizeof(struct spdm_negotiate_algs_rsp) +
> +	sizeof(struct spdm_req_alg_struct) * 2 * SPDM_MAX_REQ_ALG_STRUCT +
> +	sizeof(struct spdm_get_digests_req) +
> +	struct_size_t(struct spdm_get_digests_rsp,
> +		      digests, SPDM_SLOTS * SHA512_DIGEST_SIZE));
> +
> +static int spdm_get_version(struct spdm_state *spdm_state)
> +{
> +	struct spdm_get_version_req *req = spdm_state->transcript;
> +	struct spdm_get_version_rsp *rsp;
> +	bool foundver = false;
> +	int rc, length, i;
> +
> +	spdm_state->version = 0x10;
> +
> +	*req = (struct spdm_get_version_req) {
> +		.code = SPDM_GET_VERSION,
> +	};
> +
> +	rsp = spdm_state->transcript_end += sizeof(*req);
> +
> +	rc = spdm_exchange(spdm_state, req, sizeof(*req), rsp,
> +			   struct_size(rsp, version_number_entries, 255));
> +	if (rc < 0)
> +		return rc;
> +
> +	length = rc;
> +	if (length < sizeof(*rsp) ||
> +	    length < struct_size(rsp, version_number_entries,
> +				 rsp->version_number_entry_count)) {
> +		dev_err(spdm_state->dev, "Truncated version response\n");
> +		return -EIO;
> +	}
> +
> +	spdm_state->transcript_end +=
> +		     struct_size(rsp, version_number_entries,
> +				 rsp->version_number_entry_count);
> +
> +	for (i = 0; i < rsp->version_number_entry_count; i++) {
> +		u8 ver = le16_to_cpu(rsp->version_number_entries[i]) >> 8;
> +
> +		if (ver >= spdm_state->version && ver <= SPDM_MAX_VER) {
> +			spdm_state->version = ver;
> +			foundver = true;
> +		}
> +	}
> +	if (!foundver) {
> +		dev_err(spdm_state->dev, "No common supported version\n");
> +		return -EPROTO;
> +	}
> +
> +	return 0;
> +}
> +
> +static int spdm_get_capabilities(struct spdm_state *spdm_state)
> +{
> +	struct spdm_get_capabilities_req *req = spdm_state->transcript_end;
> +	struct spdm_get_capabilities_rsp *rsp;
> +	size_t req_sz, rsp_sz;
> +	int rc, length;
> +
> +	*req = (struct spdm_get_capabilities_req) {
> +		.code = SPDM_GET_CAPABILITIES,
> +		.ctexponent = SPDM_CTEXPONENT,
> +		.flags = cpu_to_le32(SPDM_REQ_CAPS),
> +	};
> +
> +	if (spdm_state->version == 0x10) {
> +		req_sz = offsetofend(typeof(*req), param2);
> +		rsp_sz = offsetofend(typeof(*rsp), flags);
> +	} else if (spdm_state->version == 0x11) {
> +		req_sz = offsetofend(typeof(*req), flags);
> +		rsp_sz = offsetofend(typeof(*rsp), flags);
> +	} else {
> +		req_sz = sizeof(*req);
> +		rsp_sz = sizeof(*rsp);
> +		req->data_transfer_size = cpu_to_le32(spdm_state->transport_sz);
> +		req->max_spdm_msg_size = cpu_to_le32(spdm_state->transport_sz);
> +	}
> +
> +	rsp = spdm_state->transcript_end += req_sz;
> +
> +	rc = spdm_exchange(spdm_state, req, req_sz, rsp, rsp_sz);
> +	if (rc < 0)
> +		return rc;
> +
> +	length = rc;
> +	if (length < rsp_sz) {
> +		dev_err(spdm_state->dev, "Truncated capabilities response\n");
> +		return -EIO;
> +	}
> +
> +	spdm_state->transcript_end += rsp_sz;
> +
> +	spdm_state->rsp_caps = le32_to_cpu(rsp->flags);
> +	if ((spdm_state->rsp_caps & SPDM_RSP_MIN_CAPS) != SPDM_RSP_MIN_CAPS)
> +		return -EPROTONOSUPPORT;
> +
> +	if (spdm_state->version >= 0x12) {
> +		u32 data_transfer_size = le32_to_cpu(rsp->data_transfer_size);
> +		if (data_transfer_size < SPDM_MIN_DATA_TRANSFER_SIZE) {
> +			dev_err(spdm_state->dev,
> +				"Malformed capabilities response\n");
> +			return -EPROTO;
> +		}
> +		spdm_state->transport_sz = min(spdm_state->transport_sz,
> +					       data_transfer_size);
> +	}
> +
> +	return 0;
> +}
> +
> +static int spdm_parse_algs(struct spdm_state *spdm_state)
> +{
> +	switch (spdm_state->base_asym_alg) {
> +	case SPDM_ASYM_RSASSA_2048:
> +		spdm_state->sig_len = 256;
> +		spdm_state->base_asym_enc = "pkcs1";
> +		break;
> +	case SPDM_ASYM_RSASSA_3072:
> +		spdm_state->sig_len = 384;
> +		spdm_state->base_asym_enc = "pkcs1";
> +		break;
> +	case SPDM_ASYM_RSASSA_4096:
> +		spdm_state->sig_len = 512;
> +		spdm_state->base_asym_enc = "pkcs1";
> +		break;
> +	case SPDM_ASYM_ECDSA_ECC_NIST_P256:
> +		spdm_state->sig_len = 64;
> +		spdm_state->base_asym_enc = "p1363";
> +		break;
> +	case SPDM_ASYM_ECDSA_ECC_NIST_P384:
> +		spdm_state->sig_len = 96;
> +		spdm_state->base_asym_enc = "p1363";
> +		break;
> +	case SPDM_ASYM_ECDSA_ECC_NIST_P521:
> +		spdm_state->sig_len = 132;
> +		spdm_state->base_asym_enc = "p1363";
> +		break;
> +	default:
> +		dev_err(spdm_state->dev, "Unknown asym algorithm\n");
> +		return -EINVAL;
> +	}
> +
> +	switch (spdm_state->base_hash_alg) {
> +	case SPDM_HASH_SHA_256:
> +		spdm_state->base_hash_alg_name = "sha256";
> +		break;
> +	case SPDM_HASH_SHA_384:
> +		spdm_state->base_hash_alg_name = "sha384";
> +		break;
> +	case SPDM_HASH_SHA_512:
> +		spdm_state->base_hash_alg_name = "sha512";
> +		break;
> +	default:
> +		dev_err(spdm_state->dev, "Unknown hash algorithm\n");
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * shash and desc allocations are reused for subsequent measurement
> +	 * retrieval, hence are not freed until spdm_reset().
> +	 */
> +	spdm_state->shash = crypto_alloc_shash(spdm_state->base_hash_alg_name,
> +					       0, 0);
> +	if (!spdm_state->shash)
> +		return -ENOMEM;
> +
> +	spdm_state->desc = kzalloc(sizeof(*spdm_state->desc) +
> +				   crypto_shash_descsize(spdm_state->shash),
> +				   GFP_KERNEL);
> +	if (!spdm_state->desc)
> +		return -ENOMEM;
> +
> +	spdm_state->desc->tfm = spdm_state->shash;
> +
> +	/* Used frequently to compute offsets, so cache H */
> +	spdm_state->hash_len = crypto_shash_digestsize(spdm_state->shash);
> +
> +	return crypto_shash_init(spdm_state->desc);
> +}
> +
> +static int spdm_negotiate_algs(struct spdm_state *spdm_state)
> +{
> +	struct spdm_negotiate_algs_req *req = spdm_state->transcript_end;
> +	struct spdm_negotiate_algs_rsp *rsp;
> +	struct spdm_req_alg_struct *req_alg_struct;
> +	size_t req_sz = sizeof(*req);
> +	size_t rsp_sz = sizeof(*rsp);
> +	int rc, length;
> +
> +	/* Request length shall be <= 128 bytes (SPDM 1.1.0 margin no 185) */
> +	BUILD_BUG_ON(req_sz > 128);
> +
> +	*req = (struct spdm_negotiate_algs_req) {
> +		.code = SPDM_NEGOTIATE_ALGS,
> +		.length = cpu_to_le16(req_sz),
> +		.base_asym_algo = cpu_to_le32(SPDM_ASYM_ALGOS),
> +		.base_hash_algo = cpu_to_le32(SPDM_HASH_ALGOS),
> +	};
> +
> +	rsp = spdm_state->transcript_end += req_sz;

= and += in one statament just hurts to read but ok :)

> +
> +	rc = spdm_exchange(spdm_state, req, req_sz, rsp, rsp_sz);

rsp_sz is 36 bytes here. And spdm_exchange() cannot return more than 36 
because this is how pci_doe() works...

> +	if (rc < 0)
> +		return rc;
> +
> +	length = rc;
> +	if (length < sizeof(*rsp) ||
> +	    length < sizeof(*rsp) + rsp->param1 * sizeof(*req_alg_struct)) {
> +		dev_err(spdm_state->dev, "Truncated algorithms response\n");

... but here you expect more than 36 as realistically rsp->param1 > 0.
How was this tested and what do I miss here? Thanks,



> +		return -EIO;
> +	}
> +
> +	/*
> +	 * If request contained a ReqAlgStruct not supported by responder,
> +	 * the corresponding RespAlgStruct may be omitted in response.
> +	 * Calculate the actual (possibly shorter) response length:
> +	 */
> +	spdm_state->transcript_end +=
> +		     sizeof(*rsp) + rsp->param1 * sizeof(*req_alg_struct);
> +
> +	spdm_state->base_asym_alg = le32_to_cpu(rsp->base_asym_sel);
> +	spdm_state->base_hash_alg = le32_to_cpu(rsp->base_hash_sel);
> +
> +	if ((spdm_state->base_asym_alg & SPDM_ASYM_ALGOS) == 0 ||
> +	    (spdm_state->base_hash_alg & SPDM_HASH_ALGOS) == 0) {
> +		dev_err(spdm_state->dev, "No common supported algorithms\n");
> +		return -EPROTO;
> +	}
> +
> +	/* Responder shall select exactly 1 alg (SPDM 1.0.0 table 14) */
> +	if (hweight32(spdm_state->base_asym_alg) != 1 ||
> +	    hweight32(spdm_state->base_hash_alg) != 1 ||
> +	    rsp->ext_asym_sel_count != 0 ||
> +	    rsp->ext_hash_sel_count != 0 ||
> +	    rsp->param1 > req->param1) {
> +		dev_err(spdm_state->dev, "Malformed algorithms response\n");
> +		return -EPROTO;
> +	}
> +
> +	return spdm_parse_algs(spdm_state);
> +}
> +
> +static int spdm_get_digests(struct spdm_state *spdm_state)
> +{
> +	struct spdm_get_digests_req *req = spdm_state->transcript_end;
> +	struct spdm_get_digests_rsp *rsp;
> +	unsigned long deprovisioned_slots;
> +	int rc, length;
> +	size_t rsp_sz;
> +	u8 slot;
> +
> +	*req = (struct spdm_get_digests_req) {
> +		.code = SPDM_GET_DIGESTS,
> +	};
> +
> +	rsp = spdm_state->transcript_end += sizeof(*req);
> +
> +	/*
> +	 * Assume all 8 slots are populated.  We know the hash length (and thus
> +	 * the response size) because the responder only returns digests for
> +	 * the hash algorithm selected during the NEGOTIATE_ALGORITHMS exchange
> +	 * (SPDM 1.1.2 margin no 206).
> +	 */
> +	rsp_sz = sizeof(*rsp) + SPDM_SLOTS * spdm_state->hash_len;
> +
> +	rc = spdm_exchange(spdm_state, req, sizeof(*req), rsp, rsp_sz);
> +	if (rc < 0)
> +		return rc;
> +
> +	length = rc;
> +	if (length < sizeof(*rsp) ||
> +	    length < sizeof(*rsp) + hweight8(rsp->param2) *
> +				    spdm_state->hash_len) {
> +		dev_err(spdm_state->dev, "Truncated digests response\n");
> +		return -EIO;
> +	}
> +
> +	spdm_state->transcript_end += sizeof(*rsp) + hweight8(rsp->param2) *
> +						     spdm_state->hash_len;
> +
> +	deprovisioned_slots = spdm_state->provisioned_slots & ~rsp->param2;
> +	for_each_set_bit(slot, &deprovisioned_slots, SPDM_SLOTS) {
> +		kvfree(spdm_state->slot[slot]);
> +		spdm_state->slot_sz[slot] = 0;
> +		spdm_state->slot[slot] = NULL;
> +	}
> +
> +	/*
> +	 * Authentication-capable endpoints must carry at least 1 cert chain
> +	 * (SPDM 1.0.0 section 4.9.2.1).
> +	 */
> +	spdm_state->provisioned_slots = rsp->param2;
> +	if (!spdm_state->provisioned_slots) {
> +		dev_err(spdm_state->dev, "No certificates provisioned\n");
> +		return -EPROTO;
> +	}
> +
> +	return 0;
> +}
> +
> +static int spdm_get_certificate(struct spdm_state *spdm_state, u8 slot)
> +{
> +	struct spdm_cert_chain *certs __free(kvfree) = NULL;
> +	struct spdm_get_certificate_rsp *rsp __free(kvfree);
> +	struct spdm_get_certificate_req req = {
> +		.code = SPDM_GET_CERTIFICATE,
> +		.param1 = slot,
> +	};
> +	size_t rsp_sz, total_length, header_length;
> +	u16 remainder_length = 0xffff;
> +	u16 portion_length;
> +	u16 offset = 0;
> +	int rc, length;
> +
> +	/*
> +	 * It is legal for the responder to send more bytes than requested.
> +	 * (Note the "should" in SPDM 1.0.0 table 19.)  If we allocate a
> +	 * too small buffer, we can't calculate the hash over the (truncated)
> +	 * response.  Only choice is thus to allocate the maximum possible 64k.
> +	 */
> +	rsp_sz = min_t(u32, sizeof(*rsp) + 0xffff, spdm_state->transport_sz);
> +	rsp = kvmalloc(rsp_sz, GFP_KERNEL);
> +	if (!rsp)
> +		return -ENOMEM;
> +
> +	do {
> +		/*
> +		 * If transport_sz is sufficiently large, first request will be
> +		 * for offset 0 and length 0xffff, which means entire cert
> +		 * chain (SPDM 1.0.0 table 18).
> +		 */
> +		req.offset = cpu_to_le16(offset);
> +		req.length = cpu_to_le16(min_t(size_t, remainder_length,
> +					       rsp_sz - sizeof(*rsp)));
> +
> +		rc = spdm_exchange(spdm_state, &req, sizeof(req), rsp, rsp_sz);
> +		if (rc < 0)
> +			return rc;
> +
> +		length = rc;
> +		if (length < sizeof(*rsp) ||
> +		    length < sizeof(*rsp) + le16_to_cpu(rsp->portion_length)) {
> +			dev_err(spdm_state->dev,
> +				"Truncated certificate response\n");
> +			return -EIO;
> +		}
> +
> +		portion_length = le16_to_cpu(rsp->portion_length);
> +		remainder_length = le16_to_cpu(rsp->remainder_length);
> +
> +		rc = spdm_append_transcript(spdm_state, &req, sizeof(req));
> +		if (rc)
> +			return rc;
> +
> +		rc = spdm_append_transcript(spdm_state, rsp,
> +					    sizeof(*rsp) + portion_length);
> +		if (rc)
> +			return rc;
> +
> +		/*
> +		 * On first response we learn total length of cert chain.
> +		 * Should portion_length + remainder_length exceed 0xffff,
> +		 * the min() ensures that the malformed check triggers below.
> +		 */
> +		if (!certs) {
> +			total_length = min(portion_length + remainder_length,
> +					   0xffff);
> +			certs = kvmalloc(total_length, GFP_KERNEL);
> +			if (!certs)
> +				return -ENOMEM;
> +		}
> +
> +		if (!portion_length ||
> +		    (rsp->param1 & 0xf) != slot ||
> +		    offset + portion_length + remainder_length != total_length)
> +		{
> +			dev_err(spdm_state->dev,
> +				"Malformed certificate response\n");
> +			return -EPROTO;
> +		}
> +
> +		memcpy((u8 *)certs + offset, rsp->cert_chain, portion_length);
> +		offset += portion_length;
> +	} while (remainder_length > 0);
> +
> +	header_length = sizeof(struct spdm_cert_chain) + spdm_state->hash_len;
> +
> +	if (total_length < header_length ||
> +	    total_length != le16_to_cpu(certs->length)) {
> +		dev_err(spdm_state->dev,
> +			"Malformed certificate chain in slot %u\n", slot);
> +		return -EPROTO;
> +	}
> +
> +	kvfree(spdm_state->slot[slot]);
> +	spdm_state->slot_sz[slot] = total_length;
> +	spdm_state->slot[slot] = no_free_ptr(certs);
> +
> +	return 0;
> +}
> +
> +static int spdm_validate_cert_chain(struct spdm_state *spdm_state, u8 slot)
> +{
> +	struct x509_certificate *cert __free(x509_free_certificate) = NULL;
> +	struct x509_certificate *prev __free(x509_free_certificate) = NULL;
> +	size_t header_length, total_length;
> +	bool is_leaf_cert;
> +	size_t offset = 0;
> +	struct key *key;
> +	int rc, length;
> +	u8 *certs;
> +
> +	header_length = sizeof(struct spdm_cert_chain) + spdm_state->hash_len;
> +	total_length = spdm_state->slot_sz[slot] - header_length;
> +	certs = (u8 *)spdm_state->slot[slot] + header_length;
> +
> +	do {
> +		rc = x509_get_certificate_length(certs + offset,
> +						 total_length - offset);
> +		if (rc < 0) {
> +			dev_err(spdm_state->dev, "Invalid certificate length "
> +				"at slot %u offset %zu\n", slot, offset);
> +			return rc;
> +		}
> +
> +		length = rc;
> +		is_leaf_cert = offset + length == total_length;
> +
> +		cert = x509_cert_parse(certs + offset, length);
> +		if (IS_ERR(cert)) {
> +			dev_err(spdm_state->dev, "Certificate parse error %pe "
> +				"at slot %u offset %zu\n", cert, slot, offset);
> +			return PTR_ERR(cert);
> +		}
> +		if (cert->unsupported_sig) {
> +			dev_err(spdm_state->dev, "Unsupported signature "
> +				"at slot %u offset %zu\n", slot, offset);
> +			return -EKEYREJECTED;
> +		}
> +		if (cert->blacklisted)
> +			return -EKEYREJECTED;
> +
> +		/*
> +		 * Basic Constraints CA value shall be false for leaf cert,
> +		 * true for intermediate and root certs (SPDM 1.3.0 table 42).
> +		 * Key Usage bit for digital signature shall be set, except
> +		 * for GenericCert in slot > 0 (SPDM 1.3.0 margin no 354).
> +		 * KeyCertSign bit must be 0 for non-CA (RFC 5280 sec 4.2.1.9).
> +		 */
> +		if ((is_leaf_cert ==
> +		     test_bit(KEY_EFLAG_CA, &cert->pub->key_eflags)) ||
> +		    (is_leaf_cert && slot == 0 &&
> +		     !test_bit(KEY_EFLAG_DIGITALSIG, &cert->pub->key_eflags)) ||
> +		    (is_leaf_cert &&
> +		     test_bit(KEY_EFLAG_KEYCERTSIGN, &cert->pub->key_eflags))) {
> +			dev_err(spdm_state->dev, "Malformed certificate "
> +				"at slot %u offset %zu\n", slot, offset);
> +			return -EKEYREJECTED;
> +		}
> +
> +		if (!prev) {
> +			/* First cert in chain, check against root_keyring */
> +			key = find_asymmetric_key(spdm_state->root_keyring,
> +						  cert->sig->auth_ids[0],
> +						  cert->sig->auth_ids[1],
> +						  cert->sig->auth_ids[2],
> +						  false);
> +			if (IS_ERR(key)) {
> +				dev_info(spdm_state->dev, "Root certificate "
> +					 "of slot %u not found in %s "
> +					 "keyring: %s\n", slot,
> +					 spdm_state->root_keyring->description,
> +					 cert->issuer);
> +				return PTR_ERR(key);
> +			}
> +
> +			rc = verify_signature(key, cert->sig);
> +			key_put(key);
> +		} else {
> +			/* Subsequent cert in chain, check against previous */
> +			rc = public_key_verify_signature(prev->pub, cert->sig);
> +		}
> +
> +		if (rc) {
> +			dev_err(spdm_state->dev, "Signature validation error "
> +				"%d at slot %u offset %zu\n", rc, slot, offset);
> +			return rc;
> +		}
> +
> +		x509_free_certificate(prev);
> +		prev = cert;
> +		cert = ERR_PTR(-ENOKEY);
> +
> +		offset += length;
> +	} while (offset < total_length);
> +
> +	/* Steal pub pointer ahead of x509_free_certificate() */
> +	spdm_state->leaf_key = prev->pub;
> +	prev->pub = NULL;
> +
> +	return 0;
> +}
> +
> +/**
> + * spdm_challenge_rsp_sz() - Calculate CHALLENGE_AUTH response size
> + *
> + * @spdm_state: SPDM session state
> + * @rsp: CHALLENGE_AUTH response (optional)
> + *
> + * A CHALLENGE_AUTH response contains multiple variable-length fields
> + * as well as optional fields.  This helper eases calculating its size.
> + *
> + * If @rsp is %NULL, assume the maximum OpaqueDataLength of 1024 bytes
> + * (SPDM 1.0.0 table 21).  Otherwise read OpaqueDataLength from @rsp.
> + * OpaqueDataLength can only be > 0 for SPDM 1.0 and 1.1, as they lack
> + * the OtherParamsSupport field in the NEGOTIATE_ALGORITHMS request.
> + * For SPDM 1.2+, we do not offer any Opaque Data Formats in that field,
> + * which forces OpaqueDataLength to 0 (SPDM 1.2.0 margin no 261).
> + */
> +static size_t spdm_challenge_rsp_sz(struct spdm_state *spdm_state,
> +				    struct spdm_challenge_rsp *rsp)
> +{
> +	size_t  size  = sizeof(*rsp)		/* Header */
> +		      + spdm_state->hash_len	/* CertChainHash */
> +		      + SPDM_NONCE_SZ;		/* Nonce */
> +
> +	if (rsp)
> +		/* May be unaligned if hash algorithm has odd length */
> +		size += get_unaligned_le16((u8 *)rsp + size);
> +	else
> +		size += SPDM_MAX_OPAQUE_DATA;	/* OpaqueData */
> +
> +	size += 2;				/* OpaqueDataLength */
> +
> +	if (spdm_state->version >= 0x13)
> +		size += 8;			/* RequesterContext */
> +
> +	return  size  + spdm_state->sig_len;	/* Signature */
> +}
> +
> +static int spdm_challenge(struct spdm_state *spdm_state, u8 slot)
> +{
> +	struct spdm_challenge_rsp *rsp __free(kfree);
> +	struct spdm_challenge_req req = {
> +		.code = SPDM_CHALLENGE,
> +		.param1 = slot,
> +		.param2 = 0, /* No measurement summary hash */
> +	};
> +	size_t req_sz, rsp_sz, rsp_sz_max;
> +	int rc, length;
> +
> +	get_random_bytes(&req.nonce, sizeof(req.nonce));
> +
> +	if (spdm_state->version <= 0x12)
> +		req_sz = offsetofend(typeof(req), nonce);
> +	else
> +		req_sz = sizeof(req);
> +
> +	rsp_sz_max = spdm_challenge_rsp_sz(spdm_state, NULL);
> +	rsp = kzalloc(rsp_sz_max, GFP_KERNEL);
> +	if (!rsp)
> +		return -ENOMEM;
> +
> +	rc = spdm_exchange(spdm_state, &req, req_sz, rsp, rsp_sz_max);
> +	if (rc < 0)
> +		return rc;
> +
> +	length = rc;
> +	rsp_sz = spdm_challenge_rsp_sz(spdm_state, rsp);
> +	if (length < rsp_sz) {
> +		dev_err(spdm_state->dev, "Truncated challenge_auth response\n");
> +		return -EIO;
> +	}
> +
> +	rc = spdm_append_transcript(spdm_state, &req, req_sz);
> +	if (rc)
> +		return rc;
> +
> +	rc = spdm_append_transcript(spdm_state, rsp, rsp_sz);
> +	if (rc)
> +		return rc;
> +
> +	/* Verify signature at end of transcript against leaf key */
> +	rc = spdm_verify_signature(spdm_state, spdm_context);
> +	if (rc)
> +		dev_err(spdm_state->dev,
> +			"Cannot verify challenge_auth signature: %d\n", rc);
> +	else
> +		dev_info(spdm_state->dev,
> +			 "Authenticated with certificate slot %u\n", slot);
> +
> +	return rc;
> +}
> +
> +/**
> + * spdm_authenticate() - Authenticate device
> + *
> + * @spdm_state: SPDM session state
> + *
> + * Authenticate a device through a sequence of GET_VERSION, GET_CAPABILITIES,
> + * NEGOTIATE_ALGORITHMS, GET_DIGESTS, GET_CERTIFICATE and CHALLENGE exchanges.
> + *
> + * Perform internal locking to serialize multiple concurrent invocations.
> + * Can be called repeatedly for reauthentication.
> + *
> + * Return 0 on success or a negative errno.  In particular, -EPROTONOSUPPORT
> + * indicates authentication is not supported by the device.
> + */
> +int spdm_authenticate(struct spdm_state *spdm_state)
> +{
> +	u8 slot;
> +	int rc;
> +
> +	mutex_lock(&spdm_state->lock);
> +	spdm_reset(spdm_state);
> +
> +	rc = spdm_alloc_transcript(spdm_state);
> +	if (rc)
> +		goto unlock;
> +
> +	rc = spdm_get_version(spdm_state);
> +	if (rc)
> +		goto unlock;
> +
> +	rc = spdm_get_capabilities(spdm_state);
> +	if (rc)
> +		goto unlock;
> +
> +	rc = spdm_negotiate_algs(spdm_state);
> +	if (rc)
> +		goto unlock;
> +
> +	rc = spdm_get_digests(spdm_state);
> +	if (rc)
> +		goto unlock;
> +
> +	for_each_set_bit(slot, &spdm_state->provisioned_slots, SPDM_SLOTS) {
> +		rc = spdm_get_certificate(spdm_state, slot);
> +		if (rc)
> +			goto unlock;
> +	}
> +
> +	for_each_set_bit(slot, &spdm_state->provisioned_slots, SPDM_SLOTS) {
> +		rc = spdm_validate_cert_chain(spdm_state, slot);
> +		if (rc == 0)
> +			break;
> +	}
> +	if (rc)
> +		goto unlock;
> +
> +	rc = spdm_challenge(spdm_state, slot);
> +
> +unlock:
> +	if (rc)
> +		spdm_reset(spdm_state);
> +	spdm_state->authenticated = !rc;
> +	spdm_free_transcript(spdm_state);
> +	mutex_unlock(&spdm_state->lock);
> +	return rc;
> +}
> +EXPORT_SYMBOL_GPL(spdm_authenticate);
> diff --git a/lib/spdm/spdm.h b/lib/spdm/spdm.h
> new file mode 100644
> index 000000000000..3a104959ad53
> --- /dev/null
> +++ b/lib/spdm/spdm.h
> @@ -0,0 +1,520 @@
> +/* 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 <Jonathan.Cameron@huawei.com>
> + *
> + * Copyright (C) 2022-24 Intel Corporation
> + */
> +
> +#ifndef _LIB_SPDM_H_
> +#define _LIB_SPDM_H_
> +
> +#undef  DEFAULT_SYMBOL_NAMESPACE
> +#define DEFAULT_SYMBOL_NAMESPACE SPDM
> +
> +#define dev_fmt(fmt) "SPDM: " fmt
> +
> +#include <linux/bitfield.h>
> +#include <linux/mutex.h>
> +#include <linux/spdm.h>
> +
> +/* SPDM versions supported by this implementation */
> +#define SPDM_MIN_VER 0x10
> +#define SPDM_MAX_VER 0x13
> +
> +/* SPDM capabilities (SPDM 1.1.0 margin no 177, 178) */
> +#define SPDM_CACHE_CAP			BIT(0)		/* 1.0 resp only */
> +#define SPDM_CERT_CAP			BIT(1)		/* 1.0 */
> +#define SPDM_CHAL_CAP			BIT(2)		/* 1.0 */
> +#define SPDM_MEAS_CAP_MASK		GENMASK(4, 3)	/* 1.0 resp only */
> +#define   SPDM_MEAS_CAP_NO		0		/* 1.0 resp only */
> +#define   SPDM_MEAS_CAP_MEAS		1		/* 1.0 resp only */
> +#define   SPDM_MEAS_CAP_MEAS_SIG	2		/* 1.0 resp only */
> +#define SPDM_MEAS_FRESH_CAP		BIT(5)		/* 1.0 resp only */
> +#define SPDM_ENCRYPT_CAP		BIT(6)		/* 1.1 */
> +#define SPDM_MAC_CAP			BIT(7)		/* 1.1 */
> +#define SPDM_MUT_AUTH_CAP		BIT(8)		/* 1.1 */
> +#define SPDM_KEY_EX_CAP			BIT(9)		/* 1.1 */
> +#define SPDM_PSK_CAP_MASK		GENMASK(11, 10)	/* 1.1 */
> +#define   SPDM_PSK_CAP_NO		0		/* 1.1 */
> +#define   SPDM_PSK_CAP_PSK		1		/* 1.1 */
> +#define   SPDM_PSK_CAP_PSK_CTX		2		/* 1.1 resp only */
> +#define SPDM_ENCAP_CAP			BIT(12)		/* 1.1 */
> +#define SPDM_HBEAT_CAP			BIT(13)		/* 1.1 */
> +#define SPDM_KEY_UPD_CAP		BIT(14)		/* 1.1 */
> +#define SPDM_HANDSHAKE_ITC_CAP		BIT(15)		/* 1.1 */
> +#define SPDM_PUB_KEY_ID_CAP		BIT(16)		/* 1.1 */
> +#define SPDM_CHUNK_CAP			BIT(17)		/* 1.2 */
> +#define SPDM_ALIAS_CERT_CAP		BIT(18)		/* 1.2 resp only */
> +#define SPDM_SET_CERT_CAP		BIT(19)		/* 1.2 resp only */
> +#define SPDM_CSR_CAP			BIT(20)		/* 1.2 resp only */
> +#define SPDM_CERT_INST_RESET_CAP	BIT(21)		/* 1.2 resp only */
> +#define SPDM_EP_INFO_CAP_MASK		GENMASK(23, 22) /* 1.3 */
> +#define   SPDM_EP_INFO_CAP_NO		0		/* 1.3 */
> +#define   SPDM_EP_INFO_CAP_RSP		1		/* 1.3 */
> +#define   SPDM_EP_INFO_CAP_RSP_SIG	2		/* 1.3 */
> +#define SPDM_MEL_CAP			BIT(24)		/* 1.3 resp only */
> +#define SPDM_EVENT_CAP			BIT(25)		/* 1.3 */
> +#define SPDM_MULTI_KEY_CAP_MASK		GENMASK(27, 26)	/* 1.3 */
> +#define   SPDM_MULTI_KEY_CAP_NO		0		/* 1.3 */
> +#define   SPDM_MULTI_KEY_CAP_ONLY	1		/* 1.3 */
> +#define   SPDM_MULTI_KEY_CAP_SEL	2		/* 1.3 */
> +#define SPDM_GET_KEY_PAIR_INFO_CAP	BIT(28)		/* 1.3 resp only */
> +#define SPDM_SET_KEY_PAIR_INFO_CAP	BIT(29)		/* 1.3 resp only */
> +
> +/* SPDM capabilities supported by this implementation */
> +#define SPDM_REQ_CAPS			(SPDM_CERT_CAP | SPDM_CHAL_CAP)
> +
> +/* SPDM capabilities required from responders */
> +#define SPDM_RSP_MIN_CAPS		(SPDM_CERT_CAP | SPDM_CHAL_CAP)
> +
> +/*
> + * 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 CMA
> + * (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.
> + */
> +#define SPDM_CTEXPONENT			20
> +
> +/* SPDM asymmetric key signature algorithms (SPDM 1.0.0 table 13) */
> +#define SPDM_ASYM_RSASSA_2048		BIT(0)		/* 1.0 */
> +#define SPDM_ASYM_RSAPSS_2048		BIT(1)		/* 1.0 */
> +#define SPDM_ASYM_RSASSA_3072		BIT(2)		/* 1.0 */
> +#define SPDM_ASYM_RSAPSS_3072		BIT(3)		/* 1.0 */
> +#define SPDM_ASYM_ECDSA_ECC_NIST_P256	BIT(4)		/* 1.0 */
> +#define SPDM_ASYM_RSASSA_4096		BIT(5)		/* 1.0 */
> +#define SPDM_ASYM_RSAPSS_4096		BIT(6)		/* 1.0 */
> +#define SPDM_ASYM_ECDSA_ECC_NIST_P384	BIT(7)		/* 1.0 */
> +#define SPDM_ASYM_ECDSA_ECC_NIST_P521	BIT(8)		/* 1.0 */
> +#define SPDM_ASYM_SM2_ECC_SM2_P256	BIT(9)		/* 1.2 */
> +#define SPDM_ASYM_EDDSA_ED25519		BIT(10)		/* 1.2 */
> +#define SPDM_ASYM_EDDSA_ED448		BIT(11)		/* 1.2 */
> +
> +/* SPDM hash algorithms (SPDM 1.0.0 table 13) */
> +#define SPDM_HASH_SHA_256		BIT(0)		/* 1.0 */
> +#define SPDM_HASH_SHA_384		BIT(1)		/* 1.0 */
> +#define SPDM_HASH_SHA_512		BIT(2)		/* 1.0 */
> +#define SPDM_HASH_SHA3_256		BIT(3)		/* 1.0 */
> +#define SPDM_HASH_SHA3_384		BIT(4)		/* 1.0 */
> +#define SPDM_HASH_SHA3_512		BIT(5)		/* 1.0 */
> +#define SPDM_HASH_SM3_256		BIT(6)		/* 1.2 */
> +
> +#if IS_ENABLED(CONFIG_CRYPTO_RSA)
> +#define SPDM_ASYM_RSA			SPDM_ASYM_RSASSA_2048 |		\
> +					SPDM_ASYM_RSASSA_3072 |		\
> +					SPDM_ASYM_RSASSA_4096
> +#else
> +#define SPDM_ASYM_RSA			0
> +#endif
> +
> +#if IS_ENABLED(CONFIG_CRYPTO_ECDSA)
> +#define SPDM_ASYM_ECDSA			SPDM_ASYM_ECDSA_ECC_NIST_P256 |	\
> +					SPDM_ASYM_ECDSA_ECC_NIST_P384 | \
> +					SPDM_ASYM_ECDSA_ECC_NIST_P521
> +#else
> +#define SPDM_ASYM_ECDSA			0
> +#endif
> +
> +#if IS_ENABLED(CONFIG_CRYPTO_SHA256)
> +#define SPDM_HASH_SHA2_256		SPDM_HASH_SHA_256
> +#else
> +#define SPDM_HASH_SHA2_256		0
> +#endif
> +
> +#if IS_ENABLED(CONFIG_CRYPTO_SHA512)
> +#define SPDM_HASH_SHA2_384_512		SPDM_HASH_SHA_384 |		\
> +					SPDM_HASH_SHA_512
> +#else
> +#define SPDM_HASH_SHA2_384_512		0
> +#endif
> +
> +/* SPDM algorithms supported by this implementation */
> +#define SPDM_ASYM_ALGOS		       (SPDM_ASYM_RSA |			\
> +					SPDM_ASYM_ECDSA)
> +
> +#define SPDM_HASH_ALGOS		       (SPDM_HASH_SHA2_256 |		\
> +					SPDM_HASH_SHA2_384_512)
> +
> +/*
> + * Common header shared by all messages.
> + * Note that the meaning of param1 and param2 is message dependent.
> + */
> +struct spdm_header {
> +	u8 version;
> +	u8 code;  /* RequestResponseCode */
> +	u8 param1;
> +	u8 param2;
> +} __packed;
> +
> +#define SPDM_REQ	 0x80
> +#define SPDM_GET_VERSION 0x84
> +
> +struct spdm_get_version_req {
> +	u8 version;
> +	u8 code;
> +	u8 param1;
> +	u8 param2;
> +} __packed;
> +
> +struct spdm_get_version_rsp {
> +	u8 version;
> +	u8 code;
> +	u8 param1;
> +	u8 param2;
> +
> +	u8 reserved;
> +	u8 version_number_entry_count;
> +	__le16 version_number_entries[] __counted_by(version_number_entry_count);
> +} __packed;
> +
> +#define SPDM_GET_CAPABILITIES 0xe1
> +#define SPDM_MIN_DATA_TRANSFER_SIZE 42 /* SPDM 1.2.0 margin no 226 */
> +
> +/*
> + * Newer SPDM versions insert fields at the end of messages (enlarging them)
> + * or use reserved space for new fields (leaving message size unchanged).
> + */
> +struct spdm_get_capabilities_req {
> +	u8 version;
> +	u8 code;
> +	u8 param1;
> +	u8 param2;
> +	/* End of SPDM 1.0 structure */
> +
> +	u8 reserved1;					/* 1.1 */
> +	u8 ctexponent;					/* 1.1 */
> +	u16 reserved2;					/* 1.1 */
> +	__le32 flags;					/* 1.1 */
> +	/* End of SPDM 1.1 structure */
> +
> +	__le32 data_transfer_size;			/* 1.2 */
> +	__le32 max_spdm_msg_size;			/* 1.2 */
> +} __packed;
> +
> +struct spdm_get_capabilities_rsp {
> +	u8 version;
> +	u8 code;
> +	u8 param1;
> +	u8 param2;
> +
> +	u8 reserved1;
> +	u8 ctexponent;
> +	u16 reserved2;
> +	__le32 flags;
> +	/* End of SPDM 1.0 structure */
> +
> +	__le32 data_transfer_size;			/* 1.2 */
> +	__le32 max_spdm_msg_size;			/* 1.2 */
> +	/* End of SPDM 1.2 structure */
> +
> +	/*
> +	 * Additional optional fields at end of this structure:
> +	 * - SupportedAlgorithms: variable size		 * 1.3 *
> +	 */
> +} __packed;
> +
> +#define SPDM_NEGOTIATE_ALGS 0xe3
> +
> +struct spdm_negotiate_algs_req {
> +	u8 version;
> +	u8 code;
> +	u8 param1; /* Number of ReqAlgStruct entries at end */
> +	u8 param2;
> +
> +	__le16 length;
> +	u8 measurement_specification;
> +	u8 other_params_support;			/* 1.2 */
> +
> +	__le32 base_asym_algo;
> +	__le32 base_hash_algo;
> +
> +	u8 reserved1[12];
> +	u8 ext_asym_count;
> +	u8 ext_hash_count;
> +	u8 reserved2;
> +	u8 mel_specification;				/* 1.3 */
> +
> +	/*
> +	 * Additional optional fields at end of this structure:
> +	 * - ExtAsym: 4 bytes * ext_asym_count
> +	 * - ExtHash: 4 bytes * ext_hash_count
> +	 * - ReqAlgStruct: variable size * param1	 * 1.1 *
> +	 */
> +} __packed;
> +
> +struct spdm_negotiate_algs_rsp {
> +	u8 version;
> +	u8 code;
> +	u8 param1; /* Number of RespAlgStruct entries at end */
> +	u8 param2;
> +
> +	__le16 length;
> +	u8 measurement_specification_sel;
> +	u8 other_params_sel;				/* 1.2 */
> +
> +	__le32 measurement_hash_algo;
> +	__le32 base_asym_sel;
> +	__le32 base_hash_sel;
> +
> +	u8 reserved1[11];
> +	u8 mel_specification_sel;			/* 1.3 */
> +	u8 ext_asym_sel_count; /* Either 0 or 1 */
> +	u8 ext_hash_sel_count; /* Either 0 or 1 */
> +	u8 reserved2[2];
> +
> +	/*
> +	 * Additional optional fields at end of this structure:
> +	 * - ExtAsym: 4 bytes * ext_asym_count
> +	 * - ExtHash: 4 bytes * ext_hash_count
> +	 * - RespAlgStruct: variable size * param1	 * 1.1 *
> +	 */
> +} __packed;
> +
> +/* Maximum number of ReqAlgStructs sent by this implementation */
> +#define SPDM_MAX_REQ_ALG_STRUCT 0
> +
> +struct spdm_req_alg_struct {
> +	u8 alg_type;
> +	u8 alg_count; /* 0x2K where K is number of alg_external entries */
> +	__le16 alg_supported; /* Size is in alg_count[7:4], always 2 */
> +	__le32 alg_external[];
> +} __packed;
> +
> +#define SPDM_GET_DIGESTS 0x81
> +
> +struct spdm_get_digests_req {
> +	u8 version;
> +	u8 code;
> +	u8 param1; /* Reserved */
> +	u8 param2; /* Reserved */
> +} __packed;
> +
> +struct spdm_get_digests_rsp {
> +	u8 version;
> +	u8 code;
> +	u8 param1; /* SupportedSlotMask */		/* 1.3 */
> +	u8 param2; /* ProvisionedSlotMask */
> +	u8 digests[]; /* Hash of struct spdm_cert_chain for each slot */
> +	/* End of SPDM 1.2 (and earlier) structure */
> +
> +	/*
> +	 * Additional optional fields at end of this structure:
> +	 * (omitted as long as we do not advertise MULTI_KEY_CAP)
> +	 * - KeyPairID: 1 byte for each slot		 * 1.3 *
> +	 * - CertificateInfo: 1 byte for each slot	 * 1.3 *
> +	 * - KeyUsageMask: 2 bytes for each slot	 * 1.3 *
> +	 */
> +} __packed;
> +
> +#define SPDM_GET_CERTIFICATE 0x82
> +#define SPDM_SLOTS 8 /* SPDM 1.0.0 section 4.9.2.1 */
> +
> +struct spdm_get_certificate_req {
> +	u8 version;
> +	u8 code;
> +	u8 param1; /* Slot number 0..7 */
> +	u8 param2; /* SlotSizeRequested */		/* 1.3 */
> +	__le16 offset;
> +	__le16 length;
> +} __packed;
> +
> +struct spdm_get_certificate_rsp {
> +	u8 version;
> +	u8 code;
> +	u8 param1; /* Slot number 0..7 */
> +	u8 param2; /* CertificateInfo */		/* 1.3 */
> +	__le16 portion_length;
> +	__le16 remainder_length;
> +	u8 cert_chain[]; /* PortionLength long */
> +} __packed;
> +
> +struct spdm_cert_chain {
> +	__le16 length;
> +	u8 reserved[2];
> +	/*
> +	 * Additional fields at end of this structure:
> +	 * - RootHash: Digest of Root Certificate
> +	 * - Certificates: Chain of ASN.1 DER-encoded X.509 v3 certificates
> +	 */
> +} __packed;
> +
> +#define SPDM_CHALLENGE 0x83
> +#define SPDM_NONCE_SZ 32 /* SPDM 1.0.0 table 20 */
> +#define SPDM_PREFIX_SZ 64 /* SPDM 1.2.0 margin no 803 */
> +#define SPDM_COMBINED_PREFIX_SZ 100 /* SPDM 1.2.0 margin no 806 */
> +#define SPDM_MAX_OPAQUE_DATA 1024 /* SPDM 1.0.0 table 21 */
> +
> +struct spdm_challenge_req {
> +	u8 version;
> +	u8 code;
> +	u8 param1; /* Slot number 0..7 */
> +	u8 param2; /* MeasurementSummaryHash type */
> +	u8 nonce[SPDM_NONCE_SZ];
> +	/* End of SPDM 1.2 (and earlier) structure */
> +
> +	u8 context[8];					/* 1.3 */
> +} __packed;
> +
> +struct spdm_challenge_rsp {
> +	u8 version;
> +	u8 code;
> +	u8 param1; /* Slot number 0..7 */
> +	u8 param2; /* Slot mask */
> +	/*
> +	 * Additional fields at end of this structure:
> +	 * - CertChainHash: Hash of struct spdm_cert_chain for selected slot
> +	 * - Nonce: 32 bytes long
> +	 * - MeasurementSummaryHash: Optional hash of selected measurements
> +	 * - OpaqueDataLength: 2 bytes long
> +	 * - OpaqueData: Up to 1024 bytes long
> +	 * - RequesterContext: 8 bytes long		 * 1.3 *
> +	 *   (inserted, moves Signature field)
> +	 * - Signature
> +	 */
> +} __packed;
> +
> +#define SPDM_ERROR 0x7f
> +
> +enum spdm_error_code {
> +	SPDM_INVALID_REQUEST		= 0x01,		/* 1.0 */
> +	SPDM_INVALID_SESSION		= 0x02,		/* 1.1 only */
> +	SPDM_BUSY			= 0x03,		/* 1.0 */
> +	SPDM_UNEXPECTED_REQUEST		= 0x04,		/* 1.0 */
> +	SPDM_UNSPECIFIED		= 0x05,		/* 1.0 */
> +	SPDM_DECRYPT_ERROR		= 0x06,		/* 1.1 */
> +	SPDM_UNSUPPORTED_REQUEST	= 0x07,		/* 1.0 */
> +	SPDM_REQUEST_IN_FLIGHT		= 0x08,		/* 1.1 */
> +	SPDM_INVALID_RESPONSE_CODE	= 0x09,		/* 1.1 */
> +	SPDM_SESSION_LIMIT_EXCEEDED	= 0x0a,		/* 1.1 */
> +	SPDM_SESSION_REQUIRED		= 0x0b,		/* 1.2 */
> +	SPDM_RESET_REQUIRED		= 0x0c,		/* 1.2 */
> +	SPDM_RESPONSE_TOO_LARGE		= 0x0d,		/* 1.2 */
> +	SPDM_REQUEST_TOO_LARGE		= 0x0e,		/* 1.2 */
> +	SPDM_LARGE_RESPONSE		= 0x0f,		/* 1.2 */
> +	SPDM_MESSAGE_LOST		= 0x10,		/* 1.2 */
> +	SPDM_INVALID_POLICY		= 0x11,		/* 1.3 */
> +	SPDM_VERSION_MISMATCH		= 0x41,		/* 1.0 */
> +	SPDM_RESPONSE_NOT_READY		= 0x42,		/* 1.0 */
> +	SPDM_REQUEST_RESYNCH		= 0x43,		/* 1.0 */
> +	SPDM_OPERATION_FAILED		= 0x44,		/* 1.3 */
> +	SPDM_NO_PENDING_REQUESTS	= 0x45,		/* 1.3 */
> +	SPDM_VENDOR_DEFINED_ERROR	= 0xff,		/* 1.0 */
> +};
> +
> +struct spdm_error_rsp {
> +	u8 version;
> +	u8 code;
> +	enum spdm_error_code error_code:8;
> +	u8 error_data;
> +
> +	u8 extended_error_data[];
> +} __packed;
> +
> +/**
> + * struct spdm_state - SPDM session state
> + *
> + * @dev: Responder device.  Used for error reporting and passed to @transport.
> + * @lock: Serializes multiple concurrent spdm_authenticate() calls.
> + * @authenticated: Whether device was authenticated successfully.
> + * @dev: Responder device.  Used for error reporting and passed to @transport.
> + * @transport: Transport function to perform one message exchange.
> + * @transport_priv: Transport private data.
> + * @transport_sz: Maximum message size the transport is capable of (in bytes).
> + *	Used as DataTransferSize in GET_CAPABILITIES exchange.
> + * @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.
> + * @base_asym_alg: Asymmetric key algorithm for signature verification of
> + *	CHALLENGE_AUTH messages.
> + *	Selected by responder during NEGOTIATE_ALGORITHMS exchange.
> + * @base_hash_alg: Hash algorithm for signature verification of
> + *	CHALLENGE_AUTH messages.
> + *	Selected by responder during NEGOTIATE_ALGORITHMS exchange.
> + * @provisioned_slots: Bitmask of responder's provisioned certificate slots.
> + *	Received during GET_DIGESTS exchange.
> + * @base_asym_enc: Human-readable name of @base_asym_alg's signature encoding.
> + *	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.
> + * @slot: Certificate chain in each of the 8 slots.  NULL pointer if a slot is
> + *	not populated.  Prefixed by the 4 + H header per SPDM 1.0.0 table 15.
> + * @slot_sz: Certificate chain size (in bytes).
> + * @leaf_key: Public key portion of leaf certificate against which to check
> + *	responder's signatures.
> + * @root_keyring: Keyring against which to check the first certificate in
> + *	responder's certificate chain.
> + * @transcript: Concatenation of all SPDM messages exchanged during an
> + *	authentication sequence.  Used to verify the signature, as it is
> + *	computed over the hashed transcript.
> + * @transcript_end: Pointer into the @transcript buffer.  Marks the current
> + *	end of transcript.  If another message is transmitted, it is appended
> + *	at this position.
> + * @transcript_max: Allocation size of @transcript.  Multiple of PAGE_SIZE.
> + */
> +struct spdm_state {
> +	struct device *dev;
> +	struct mutex lock;
> +	unsigned int authenticated:1;
> +
> +	/* Transport */
> +	spdm_transport *transport;
> +	void *transport_priv;
> +	u32 transport_sz;
> +
> +	/* Negotiated state */
> +	u8 version;
> +	u32 rsp_caps;
> +	u32 base_asym_alg;
> +	u32 base_hash_alg;
> +	unsigned long provisioned_slots;
> +
> +	/* Signature algorithm */
> +	const char *base_asym_enc;
> +	size_t sig_len;
> +
> +	/* Hash algorithm */
> +	const char *base_hash_alg_name;
> +	struct crypto_shash *shash;
> +	struct shash_desc *desc;
> +	size_t hash_len;
> +
> +	/* Certificates */
> +	struct spdm_cert_chain *slot[SPDM_SLOTS];
> +	size_t slot_sz[SPDM_SLOTS];
> +	struct public_key *leaf_key;
> +	struct key *root_keyring;
> +
> +	/* Transcript */
> +	void *transcript;
> +	void *transcript_end;
> +	size_t transcript_max;
> +};
> +
> +ssize_t spdm_exchange(struct spdm_state *spdm_state,
> +		      void *req, size_t req_sz, void *rsp, size_t rsp_sz);
> +
> +int spdm_alloc_transcript(struct spdm_state *spdm_state);
> +void spdm_free_transcript(struct spdm_state *spdm_state);
> +int spdm_append_transcript(struct spdm_state *spdm_state,
> +			   const void *msg, size_t msg_sz);
> +
> +void spdm_create_combined_prefix(u8 version, const char *spdm_context,
> +				 void *buf);
> +int spdm_verify_signature(struct spdm_state *spdm_state,
> +			  const char *spdm_context);
> +
> +void spdm_reset(struct spdm_state *spdm_state);
> +
> +#endif /* _LIB_SPDM_H_ */

-- 
Alexey


  parent reply	other threads:[~2024-07-08  9:57 UTC|newest]

Thread overview: 89+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-06-30 19:35 [PATCH v2 00/18] PCI device authentication Lukas Wunner
2024-06-30 19:36 ` [PATCH v2 01/18] X.509: Make certificate parser public Lukas Wunner
2024-07-10  2:46   ` Alistair Francis
2024-06-30 19:37 ` [PATCH v2 02/18] X.509: Parse Subject Alternative Name in certificates Lukas Wunner
2024-07-10  2:48   ` Alistair Francis
2024-06-30 19:38 ` [PATCH v2 03/18] X.509: Move certificate length retrieval into new helper Lukas Wunner
2024-07-10  2:49   ` Alistair Francis
2024-07-18 11:04   ` Jonathan Cameron
2024-06-30 19:39 ` [PATCH v2 04/18] certs: Create blacklist keyring earlier Lukas Wunner
2024-07-10  2:52   ` Alistair Francis
2024-06-30 19:40 ` [PATCH v2 05/18] crypto: akcipher - Support more than one signature encoding Lukas Wunner
2024-06-30 19:41 ` [PATCH v2 06/18] crypto: ecdsa - Support P1363 " Lukas Wunner
2024-06-30 22:10   ` Herbert Xu
2024-07-29 14:27     ` Lukas Wunner
2024-06-30 19:42 ` [PATCH v2 07/18] spdm: Introduce library to authenticate devices Lukas Wunner
2024-06-30 21:29   ` Jeff Johnson
2024-07-08  9:57   ` Alexey Kardashevskiy [this message]
2024-07-08 12:54     ` Lukas Wunner
2024-07-09  0:45       ` Alexey Kardashevskiy
2024-07-09  8:49         ` Lukas Wunner
2024-07-09  5:09   ` Dan Williams
2024-07-18 11:42     ` Jonathan Cameron
2024-07-09 15:00   ` Jeff Johnson
2024-07-18 14:24   ` Jonathan Cameron
2024-06-30 19:43 ` [PATCH v2 08/18] PCI/CMA: Authenticate devices on enumeration Lukas Wunner
2024-07-09 18:10   ` Dan Williams
2024-07-09 19:32     ` Lukas Wunner
2024-07-09 23:31       ` Dan Williams
2024-07-11 15:00         ` Lukas Wunner
2024-07-11 17:50           ` Dan Williams
2024-07-12  0:50             ` Damien Le Moal
2024-07-14  8:42             ` Lukas Wunner
2024-07-15 17:21               ` Kees Cook
2024-07-15 18:12                 ` Jason Gunthorpe
2024-07-15 20:36                   ` Dan Williams
2024-07-15 22:02                     ` Jason Gunthorpe
2024-07-15 22:17                       ` Damien Le Moal
2024-07-15 23:03                         ` Jason Gunthorpe
2024-07-15 23:26                           ` Damien Le Moal
2024-07-15 23:42                             ` Jason Gunthorpe
2024-07-15 23:57                               ` Damien Le Moal
2024-07-16  0:11                                 ` Jason Gunthorpe
2024-07-16  1:23                                   ` Dan Williams
2024-07-15 22:50                       ` Dan Williams
2024-07-15 23:21                         ` Jason Gunthorpe
2024-07-15 23:37                           ` Dan Williams
2024-07-15 23:55                             ` Jason Gunthorpe
2024-07-16  1:35                               ` Dan Williams
2024-07-22 10:19                               ` Alexey Kardashevskiy
2024-07-22 12:06                                 ` Jason Gunthorpe
2024-07-23  4:26                                   ` Alexey Kardashevskiy
2024-07-23 12:58                                     ` Jason Gunthorpe
2024-07-15 20:19                 ` Dan Williams
2024-07-15 20:08               ` Dan Williams
2024-06-30 19:44 ` [PATCH v2 09/18] PCI/CMA: Validate Subject Alternative Name in certificates Lukas Wunner
2024-07-10 20:35   ` Dan Williams
2024-06-30 19:45 ` [PATCH v2 10/18] PCI/CMA: Reauthenticate devices on reset and resume Lukas Wunner
2024-07-10  3:40   ` Alistair Francis
2024-07-10 23:23   ` Dan Williams
2024-07-18 15:01     ` Jonathan Cameron
2024-06-30 19:46 ` [PATCH v2 11/18] PCI/CMA: Expose in sysfs whether devices are authenticated Lukas Wunner
2024-07-17 23:17   ` Dan Williams
2024-07-18 15:11   ` Jonathan Cameron
2024-06-30 19:47 ` [PATCH v2 12/18] PCI/CMA: Expose certificates in sysfs Lukas Wunner
2024-07-18  2:43   ` Dan Williams
2024-07-18 15:16     ` Jonathan Cameron
2024-07-18 15:19   ` Jonathan Cameron
2024-06-30 19:48 ` [PATCH v2 13/18] sysfs: Allow bin_attributes to be added to groups Lukas Wunner
2024-07-04 10:13   ` Greg Kroah-Hartman
2024-07-12  3:49   ` Alistair Francis
2024-07-18 15:22   ` Jonathan Cameron
2024-06-30 19:49 ` [PATCH v2 14/18] sysfs: Allow symlinks to be added between sibling groups Lukas Wunner
2024-07-04 10:14   ` Greg Kroah-Hartman
2024-07-18 15:36   ` Jonathan Cameron
2024-06-30 19:50 ` [PATCH v2 15/18] PCI/CMA: Expose a log of received signatures in sysfs Lukas Wunner
2024-07-18 15:56   ` Jonathan Cameron
2024-06-30 19:51 ` [PATCH v2 16/18] spdm: Limit memory consumed by log of received signatures Lukas Wunner
2024-07-18 16:03   ` Jonathan Cameron
2024-06-30 19:52 ` [PATCH v2 17/18] spdm: Authenticate devices despite invalid certificate chain Lukas Wunner
2024-07-18 16:08   ` Jonathan Cameron
2024-06-30 19:53 ` [PATCH v2 18/18] spdm: Allow control of next requester nonce through sysfs Lukas Wunner
2024-07-18 16:11   ` Jonathan Cameron
2024-07-08  9:47 ` [PATCH v2 00/18] PCI device authentication Alexey Kardashevskiy
2024-07-08 13:35   ` Lukas Wunner
2025-02-11  1:30     ` Alexey Kardashevskiy
2025-02-12 16:36       ` Lukas Wunner
2025-05-20  8:35         ` Alexey Kardashevskiy
2025-05-29  5:29           ` Alexey Kardashevskiy
2025-05-29  9:40             ` Lukas Wunner

Reply instructions:

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

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

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

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

  git send-email \
    --in-reply-to=26715537-5dc4-46c1-bdcd-c760696dd418@amd.com \
    --to=aik@amd.com \
    --cc=James.Bottomley@HansenPartnership.com \
    --cc=Jonathan.Cameron@huawei.com \
    --cc=alistair.francis@wdc.com \
    --cc=dan.j.williams@intel.com \
    --cc=davem@davemloft.net \
    --cc=david.e.box@intel.com \
    --cc=dhaval.giani@amd.com \
    --cc=dhowells@redhat.com \
    --cc=dlemoal@kernel.org \
    --cc=dwmw2@infradead.org \
    --cc=ebiggers@google.com \
    --cc=gdhanuskodi@nvidia.com \
    --cc=graf@amazon.com \
    --cc=helgaas@kernel.org \
    --cc=herbert@gondor.apana.org.au \
    --cc=ilpo.jarvinen@linux.intel.com \
    --cc=jgg@nvidia.com \
    --cc=jglisse@google.com \
    --cc=keyrings@vger.kernel.org \
    --cc=linux-coco@lists.linux.dev \
    --cc=linux-crypto@vger.kernel.org \
    --cc=linux-cxl@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=linuxarm@huawei.com \
    --cc=lukas@wunner.de \
    --cc=ming4.li@intel.com \
    --cc=pgonda@google.com \
    --cc=sameo@rivosinc.com \
    --cc=seanjc@google.com \
    --cc=stefanb@linux.ibm.com \
    --cc=wilfred.mallawa@wdc.com \
    /path/to/YOUR_REPLY

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

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