RE: [PATCH 07/12] spdm: Introduce library to authenticate devices

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Lukas Wunner wrote:
> From: Jonathan Cameron <Jonathan.Cameron@xxxxxxxxxx>
> 
> The Security Protocol and Data Model (SPDM) allows for authentication,
> measurement, key exchange and encrypted sessions with devices.
> 
> A commonly used term for authentication and measurement is attestation.
> 
> 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
> two different SPDM-capable transports:
> 
> * PCIe Data Object Exchange (PCIe r6.1 sec 6.30, drivers/pci/doe.c)
> * Management Component Transport Protocol (MCTP,
>   Documentation/networking/mctp.rst)
> 
> Use cases for SPDM include, but are not limited to:
> 
> * PCIe Component Measurement and Authentication (PCIe r6.1 sec 6.31)
> * Compute Express Link (CXL r3.0 sec 14.11.6)
> * Open Compute Project (Attestation of System Components r1.0)
>   https://www.opencompute.org/documents/attestation-v1-0-20201104-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.
> 
> 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.1 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().  Those can be moved to
> spdm_common.{h,c} files upon introduction of the responder role.
> For now, all is kept in a single source file to avoid polluting the
> global namespace with unnecessary symbols.

Since you are raising design considerations for the future reuse of this
code in the responder role, I will raise some considerations for future
reuse of this code with platform security modules (the TDISP specification
calls them TSMs).

> 
> Credits:  Jonathan wrote a proof-of-concept of this SPDM implementation.
> Lukas reworked it for upstream.
> 
> Signed-off-by: Jonathan Cameron <Jonathan.Cameron@xxxxxxxxxx>
> Signed-off-by: Lukas Wunner <lukas@xxxxxxxxx>
> ---
>  MAINTAINERS          |    9 +
>  include/linux/spdm.h |   35 +
>  lib/Kconfig          |   15 +
>  lib/Makefile         |    2 +
>  lib/spdm_requester.c | 1487 ++++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 1548 insertions(+)
>  create mode 100644 include/linux/spdm.h
>  create mode 100644 lib/spdm_requester.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 90f13281d297..2591d2217d65 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -19299,6 +19299,15 @@ M:	Security Officers <security@xxxxxxxxxx>
>  S:	Supported
>  F:	Documentation/process/security-bugs.rst
>  
> +SECURITY PROTOCOL AND DATA MODEL (SPDM)
> +M:	Jonathan Cameron <jic23@xxxxxxxxxx>
> +M:	Lukas Wunner <lukas@xxxxxxxxx>
> +L:	linux-cxl@xxxxxxxxxxxxxxx
> +L:	linux-pci@xxxxxxxxxxxxxxx
> +S:	Maintained
> +F:	include/linux/spdm.h
> +F:	lib/spdm*
> +
>  SECURITY SUBSYSTEM
>  M:	Paul Moore <paul@xxxxxxxxxxxxxx>
>  M:	James Morris <jmorris@xxxxxxxxx>
> diff --git a/include/linux/spdm.h b/include/linux/spdm.h
> new file mode 100644
> index 000000000000..e824063793a7
> --- /dev/null
> +++ b/include/linux/spdm.h
> @@ -0,0 +1,35 @@
> +/* 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@xxxxxxxxxx>
> + *
> + * Copyright (C) 2022-23 Intel Corporation
> + */
> +
> +#ifndef _SPDM_H_
> +#define _SPDM_H_
> +
> +#include <linux/types.h>
> +
> +struct key;
> +struct device;
> +struct spdm_state;
> +
> +typedef int (spdm_transport)(void *priv, struct device *dev,
> +			     const void *request, size_t request_sz,
> +			     void *response, size_t response_sz);

TIL that even though "typedef <type> (*<name>)" is the predominant kernel
style of declaring function pointer types, significant parts of the kernel
use this "typedef <type> (<name>)" and declare it a pointer at the usage
site.

> +
> +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);
> +
> +bool spdm_authenticated(struct spdm_state *spdm_state);
> +
> +void spdm_destroy(struct spdm_state *spdm_state);
> +
> +#endif
> diff --git a/lib/Kconfig b/lib/Kconfig
> index c686f4adc124..3516cf1dad16 100644
> --- a/lib/Kconfig
> +++ b/lib/Kconfig
> @@ -764,3 +764,18 @@ config ASN1_ENCODER
>  
>  config POLYNOMIAL
>         tristate
> +
> +config SPDM_REQUESTER
> +	tristate
> +	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 authentication,
> +	 measurement, key exchange and encrypted sessions with devices.  This
> +	 option enables support for the SPDM requester role.
> +
> +	 Crypto algorithms offered to SPDM responders are limited to those
> +	 enabled in .config.  Drivers selecting SPDM_REQUESTER need to also
> +	 select any algorithms they deem mandatory.
> diff --git a/lib/Makefile b/lib/Makefile
> index 740109b6e2c8..d9ae58a9ca83 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -315,6 +315,8 @@ obj-$(CONFIG_PERCPU_TEST) += percpu_test.o
>  obj-$(CONFIG_ASN1) += asn1_decoder.o
>  obj-$(CONFIG_ASN1_ENCODER) += asn1_encoder.o
>  
> +obj-$(CONFIG_SPDM_REQUESTER) += spdm_requester.o
> +
>  obj-$(CONFIG_FONT_SUPPORT) += fonts/
>  
>  hostprogs	:= gen_crc32table
> diff --git a/lib/spdm_requester.c b/lib/spdm_requester.c
> new file mode 100644
> index 000000000000..407041036599
> --- /dev/null
> +++ b/lib/spdm_requester.c
[..]
> +struct spdm_error_rsp {
> +	u8 version;
> +	u8 code;
> +	enum spdm_error_code error_code:8;
> +	u8 error_data;
> +
> +	u8 extended_error_data[];
> +} __packed;
> +
> +static int spdm_err(struct device *dev, struct spdm_error_rsp *rsp)
> +{

Why not an error_code_to_string() helper and then use dev_err() directly at
the call site? rsp->error_data could be conveyed uncoditionally, but maybe
that belies that I do not understand the need for filtering ->error_data.

> +	switch (rsp->error_code) {
> +	case spdm_invalid_request:
> +		dev_err(dev, "Invalid request\n");

Setting the above comment aside, do you suspect these need to be
dev_err_ratelimited() if only because it is unclear whether a user of this
library will trigger screaming error messages?


> +		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 -ERESTART;
> +	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 -ERESTART;
> +	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_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 that authentication is not supported by the device.
> + */
> +int spdm_authenticate(struct spdm_state *spdm_state)
> +{
> +	size_t transcript_sz;
> +	void *transcript;
> +	int rc = -ENOMEM;
> +	u8 slot;
> +
> +	mutex_lock(&spdm_state->lock);
> +	spdm_reset(spdm_state);
> +
> +	/*
> +	 * For CHALLENGE_AUTH signature verification, a hash is computed over
> +	 * all exchanged messages to detect modification by a man-in-the-middle
> +	 * or media error.  However the hash algorithm is not known until the
> +	 * NEGOTIATE_ALGORITHMS response has been received.  The preceding
> +	 * GET_VERSION and GET_CAPABILITIES exchanges are therefore stashed
> +	 * in a transcript buffer and consumed once the algorithm is known.
> +	 * The buffer size is sufficient for the largest possible messages with
> +	 * 255 version entries and the capability fields added by SPDM 1.2.
> +	 */
> +	transcript = kzalloc(struct_size_t(struct spdm_get_version_rsp,
> +					   version_number_entries, 255) +
> +			     sizeof(struct spdm_get_capabilities_reqrsp) * 2,
> +			     GFP_KERNEL);
> +	if (!transcript)
> +		goto unlock;
> +
> +	rc = spdm_get_version(spdm_state, transcript, &transcript_sz);
> +	if (rc)
> +		goto unlock;
> +
> +	rc = spdm_get_capabilities(spdm_state, transcript + transcript_sz,
> +				   &transcript_sz);
> +	if (rc)
> +		goto unlock;
> +
> +	rc = spdm_negotiate_algs(spdm_state, transcript, transcript_sz);
> +	if (rc)
> +		goto unlock;
> +
> +	rc = spdm_get_digests(spdm_state);
> +	if (rc)
> +		goto unlock;
> +
> +	for_each_set_bit(slot, &spdm_state->slot_mask, SPDM_SLOTS) {
> +		rc = spdm_get_certificate(spdm_state, slot);

A forward looking comment here, how to structure this code for reuse when
end users opt their kernel into coordinating with a platform TSM? Since the
DOE mailbox can only be owned by 1 entity, I expect sdpdm_state could grow
additional operations beyond the raw transport. These operations would be
for higher-order flows, like "get certificates", where that operation may
be forwarded from guest-to-VMM-to-TSM, and where VMM and TSM manage the raw
transport to return the result to the guest.

Otherwise no other implementation comments from me, my eyes are not well
trained to spot misuse of the crypto apis.



[Index of Archives]     [Kernel]     [Gnu Classpath]     [Gnu Crypto]     [DM Crypt]     [Netfilter]     [Bugtraq]
  Powered by Linux