linux-pci.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC v2 00/20] lib: Rust implementation of SPDM
@ 2025-02-27  3:09 Alistair Francis
  2025-02-27  3:09 ` [RFC v2 01/20] X.509: Make certificate parser public Alistair Francis
                   ` (20 more replies)
  0 siblings, 21 replies; 47+ messages in thread
From: Alistair Francis @ 2025-02-27  3:09 UTC (permalink / raw)
  To: linux-cxl, linux-kernel, lukas, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm
  Cc: boqun.feng, bjorn3_gh, wilfred.mallawa, aliceryhl, ojeda,
	alistair23, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

Security Protocols and Data Models (SPDM) [1] is used for authentication,
attestation and key exchange. SPDM is generally used over a range of
transports, such as PCIe, MCTP/SMBus/I3C, ATA, SCSI, NVMe or TCP.

From the kernels perspective SPDM is used to authenticate and attest devices.
In this threat model a device is considered untrusted until it can be verified
by the kernel and userspace using SPDM. As such SPDM data is untrusted data
that can be mallicious.

The SPDM specification is also complex, with the 1.2.1 spec being almost 200
pages and the 1.3.0 spec being almost 250 pages long.

As such we have the kernel parsing untrusted responses from a complex
specification, which sounds like a possible exploit vector. This is the type
of place where Rust excels!

This series implements a SPDM requester in Rust.

This is very similar to Lukas' implementation [2]. This series includes patches
and files from Lukas' C SPDM implementation, which isn't in mainline.

This is a standalone series and doesn't depend on Lukas' implementation, although
we do still rely on Lukas' crypto preperation patches, not all of which are
upstream yet.

To help with maintaining compatibility it's designed in a way to match Lukas'
design and the state struct stores the same information, although in a Rust
struct instead of the original C one.

This series doesn't expose the data to userspace (except for a single sysfs
bool) to avoid the debate about how to do that. I'm planning to do that in
the future though.

This series is based on the latest rust-next tree.

This seris depends on the Untrusted abstraction work [4].

This seris also depends on the recent bindgen support for static inlines  [5].

The entire tree can be seen here: https://github.com/alistair23/linux/tree/alistair/spdm-rust

based-on: https://lore.kernel.org/rust-for-linux/20240925205244.873020-1-benno.lossin@proton.me/
based-on: https://lore.kernel.org/rust-for-linux/20250107035058.818539-1-alistair@alistair23.me/

1: https://www.dmtf.org/standards/spdm
2: https://lore.kernel.org/all/cover.1719771133.git.lukas@wunner.de/
3: https://github.com/l1k/linux/commits/spdm-future/
4: https://lore.kernel.org/rust-for-linux/20240925205244.873020-1-benno.lossin@proton.me/
5: https://lore.kernel.org/rust-for-linux/20250107035058.818539-1-alistair@alistair23.me/

v2:
 - Drop support for Rust and C implementations
 - Include patches from Lukas to reduce series deps
 - Large code cleanups based on more testing
 - Support support for authentication

Alistair Francis (12):
  lib: rspdm: Initial commit of Rust SPDM
  lib: rspdm: Support SPDM get_version
  lib: rspdm: Support SPDM get_capabilities
  lib: rspdm: Support SPDM negotiate_algorithms
  lib: rspdm: Support SPDM get_digests
  lib: rspdm: Support SPDM get_certificate
  crypto: asymmetric_keys - Load certificate parsing early in boot
  KEYS: Load keyring and certificates early in boot
  PCI/CMA: Support built in X.509 certificates
  lib: rspdm: Support SPDM certificate validation
  rust: allow extracting the buffer from a CString
  lib: rspdm: Support SPDM challenge

Jonathan Cameron (1):
  PCI/CMA: Authenticate devices on enumeration

Lukas Wunner (7):
  X.509: Make certificate parser public
  X.509: Parse Subject Alternative Name in certificates
  X.509: Move certificate length retrieval into new helper
  certs: Create blacklist keyring earlier
  PCI/CMA: Validate Subject Alternative Name in certificates
  PCI/CMA: Reauthenticate devices on reset and resume
  PCI/CMA: Expose in sysfs whether devices are authenticated

 Documentation/ABI/testing/sysfs-devices-spdm |   31 +
 MAINTAINERS                                  |   14 +
 certs/blacklist.c                            |    4 +-
 certs/system_keyring.c                       |    4 +-
 crypto/asymmetric_keys/asymmetric_type.c     |    2 +-
 crypto/asymmetric_keys/x509_cert_parser.c    |    9 +
 crypto/asymmetric_keys/x509_loader.c         |   38 +-
 crypto/asymmetric_keys/x509_parser.h         |   40 +-
 crypto/asymmetric_keys/x509_public_key.c     |    2 +-
 drivers/pci/Kconfig                          |   13 +
 drivers/pci/Makefile                         |    4 +
 drivers/pci/cma.asn1                         |   41 +
 drivers/pci/cma.c                            |  272 +++++
 drivers/pci/doe.c                            |    5 +-
 drivers/pci/pci-driver.c                     |    1 +
 drivers/pci/pci-sysfs.c                      |    3 +
 drivers/pci/pci.c                            |   12 +-
 drivers/pci/pci.h                            |   15 +
 drivers/pci/pcie/err.c                       |    3 +
 drivers/pci/probe.c                          |    1 +
 drivers/pci/remove.c                         |    1 +
 include/keys/asymmetric-type.h               |    2 +
 include/keys/x509-parser.h                   |   55 +
 include/linux/oid_registry.h                 |    3 +
 include/linux/pci-doe.h                      |    4 +
 include/linux/pci.h                          |   16 +
 include/linux/spdm.h                         |   39 +
 lib/Kconfig                                  |   16 +
 lib/Makefile                                 |    2 +
 lib/rspdm/Makefile                           |   11 +
 lib/rspdm/consts.rs                          |  135 +++
 lib/rspdm/lib.rs                             |  180 +++
 lib/rspdm/req-sysfs.c                        |   97 ++
 lib/rspdm/state.rs                           | 1037 ++++++++++++++++++
 lib/rspdm/sysfs.rs                           |   28 +
 lib/rspdm/validator.rs                       |  489 +++++++++
 rust/bindgen_static_functions                |    5 +
 rust/bindings/bindings_helper.h              |    7 +
 rust/kernel/error.rs                         |    3 +
 rust/kernel/str.rs                           |    5 +
 40 files changed, 2587 insertions(+), 62 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-devices-spdm
 create mode 100644 drivers/pci/cma.asn1
 create mode 100644 drivers/pci/cma.c
 create mode 100644 include/keys/x509-parser.h
 create mode 100644 include/linux/spdm.h
 create mode 100644 lib/rspdm/Makefile
 create mode 100644 lib/rspdm/consts.rs
 create mode 100644 lib/rspdm/lib.rs
 create mode 100644 lib/rspdm/req-sysfs.c
 create mode 100644 lib/rspdm/state.rs
 create mode 100644 lib/rspdm/sysfs.rs
 create mode 100644 lib/rspdm/validator.rs

-- 
2.48.1


^ permalink raw reply	[flat|nested] 47+ messages in thread

* [RFC v2 01/20] X.509: Make certificate parser public
  2025-02-27  3:09 [RFC v2 00/20] lib: Rust implementation of SPDM Alistair Francis
@ 2025-02-27  3:09 ` Alistair Francis
  2025-02-27  3:09 ` [RFC v2 02/20] X.509: Parse Subject Alternative Name in certificates Alistair Francis
                   ` (19 subsequent siblings)
  20 siblings, 0 replies; 47+ messages in thread
From: Alistair Francis @ 2025-02-27  3:09 UTC (permalink / raw)
  To: linux-cxl, linux-kernel, lukas, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm
  Cc: boqun.feng, bjorn3_gh, wilfred.mallawa, aliceryhl, ojeda,
	alistair23, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Dan Williams, Alistair Francis, Ilpo Järvinen

From: Lukas Wunner <lukas@wunner.de>

The upcoming support for PCI device authentication with CMA-SPDM
(PCIe r6.1 sec 6.31) requires validating the Subject Alternative Name
in X.509 certificates.

High-level functions for X.509 parsing such as key_create_or_update()
throw away the internal, low-level struct x509_certificate after
extracting the struct public_key and public_key_signature from it.
The Subject Alternative Name is thus inaccessible when using those
functions.

Afford CMA-SPDM access to the Subject Alternative Name by making struct
x509_certificate public, together with the functions for parsing an
X.509 certificate into such a struct and freeing such a struct.

The private header file x509_parser.h previously included <linux/time.h>
for the definition of time64_t.  That definition was since moved to
<linux/time64.h> by commit 361a3bf00582 ("time64: Add time64.h header
and define struct timespec64"), so adjust the #include directive as part
of the move to the new public header file <keys/x509-parser.h>.

No functional change intended.

Signed-off-by: Lukas Wunner <lukas@wunner.de>
Reviewed-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 crypto/asymmetric_keys/x509_parser.h | 40 +--------------------
 include/keys/x509-parser.h           | 53 ++++++++++++++++++++++++++++
 2 files changed, 54 insertions(+), 39 deletions(-)
 create mode 100644 include/keys/x509-parser.h

diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h
index 0688c222806b..39f1521b773d 100644
--- a/crypto/asymmetric_keys/x509_parser.h
+++ b/crypto/asymmetric_keys/x509_parser.h
@@ -5,49 +5,11 @@
  * Written by David Howells (dhowells@redhat.com)
  */
 
-#include <linux/cleanup.h>
-#include <linux/time.h>
-#include <crypto/public_key.h>
-#include <keys/asymmetric-type.h>
-
-struct x509_certificate {
-	struct x509_certificate *next;
-	struct x509_certificate *signer;	/* Certificate that signed this one */
-	struct public_key *pub;			/* Public key details */
-	struct public_key_signature *sig;	/* Signature parameters */
-	char		*issuer;		/* Name of certificate issuer */
-	char		*subject;		/* Name of certificate subject */
-	struct asymmetric_key_id *id;		/* Issuer + Serial number */
-	struct asymmetric_key_id *skid;		/* Subject + subjectKeyId (optional) */
-	time64_t	valid_from;
-	time64_t	valid_to;
-	const void	*tbs;			/* Signed data */
-	unsigned	tbs_size;		/* Size of signed data */
-	unsigned	raw_sig_size;		/* Size of signature */
-	const void	*raw_sig;		/* Signature data */
-	const void	*raw_serial;		/* Raw serial number in ASN.1 */
-	unsigned	raw_serial_size;
-	unsigned	raw_issuer_size;
-	const void	*raw_issuer;		/* Raw issuer name in ASN.1 */
-	const void	*raw_subject;		/* Raw subject name in ASN.1 */
-	unsigned	raw_subject_size;
-	unsigned	raw_skid_size;
-	const void	*raw_skid;		/* Raw subjectKeyId in ASN.1 */
-	unsigned	index;
-	bool		seen;			/* Infinite recursion prevention */
-	bool		verified;
-	bool		self_signed;		/* T if self-signed (check unsupported_sig too) */
-	bool		unsupported_sig;	/* T if signature uses unsupported crypto */
-	bool		blacklisted;
-};
+#include <keys/x509-parser.h>
 
 /*
  * x509_cert_parser.c
  */
-extern void x509_free_certificate(struct x509_certificate *cert);
-DEFINE_FREE(x509_free_certificate, struct x509_certificate *,
-	    if (!IS_ERR(_T)) x509_free_certificate(_T))
-extern struct x509_certificate *x509_cert_parse(const void *data, size_t datalen);
 extern int x509_decode_time(time64_t *_t,  size_t hdrlen,
 			    unsigned char tag,
 			    const unsigned char *value, size_t vlen);
diff --git a/include/keys/x509-parser.h b/include/keys/x509-parser.h
new file mode 100644
index 000000000000..37436a5c7526
--- /dev/null
+++ b/include/keys/x509-parser.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* X.509 certificate parser
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#ifndef _KEYS_X509_PARSER_H
+#define _KEYS_X509_PARSER_H
+
+#include <crypto/public_key.h>
+#include <keys/asymmetric-type.h>
+#include <linux/cleanup.h>
+#include <linux/time64.h>
+
+struct x509_certificate {
+	struct x509_certificate *next;
+	struct x509_certificate *signer;	/* Certificate that signed this one */
+	struct public_key *pub;			/* Public key details */
+	struct public_key_signature *sig;	/* Signature parameters */
+	char		*issuer;		/* Name of certificate issuer */
+	char		*subject;		/* Name of certificate subject */
+	struct asymmetric_key_id *id;		/* Issuer + Serial number */
+	struct asymmetric_key_id *skid;		/* Subject + subjectKeyId (optional) */
+	time64_t	valid_from;
+	time64_t	valid_to;
+	const void	*tbs;			/* Signed data */
+	unsigned	tbs_size;		/* Size of signed data */
+	unsigned	raw_sig_size;		/* Size of signature */
+	const void	*raw_sig;		/* Signature data */
+	const void	*raw_serial;		/* Raw serial number in ASN.1 */
+	unsigned	raw_serial_size;
+	unsigned	raw_issuer_size;
+	const void	*raw_issuer;		/* Raw issuer name in ASN.1 */
+	const void	*raw_subject;		/* Raw subject name in ASN.1 */
+	unsigned	raw_subject_size;
+	unsigned	raw_skid_size;
+	const void	*raw_skid;		/* Raw subjectKeyId in ASN.1 */
+	unsigned	index;
+	bool		seen;			/* Infinite recursion prevention */
+	bool		verified;
+	bool		self_signed;		/* T if self-signed (check unsupported_sig too) */
+	bool		unsupported_sig;	/* T if signature uses unsupported crypto */
+	bool		blacklisted;
+};
+
+struct x509_certificate *x509_cert_parse(const void *data, size_t datalen);
+void x509_free_certificate(struct x509_certificate *cert);
+
+DEFINE_FREE(x509_free_certificate, struct x509_certificate *,
+	    if (!IS_ERR(_T)) x509_free_certificate(_T))
+
+#endif /* _KEYS_X509_PARSER_H */
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [RFC v2 02/20] X.509: Parse Subject Alternative Name in certificates
  2025-02-27  3:09 [RFC v2 00/20] lib: Rust implementation of SPDM Alistair Francis
  2025-02-27  3:09 ` [RFC v2 01/20] X.509: Make certificate parser public Alistair Francis
@ 2025-02-27  3:09 ` Alistair Francis
  2025-02-27  3:09 ` [RFC v2 03/20] X.509: Move certificate length retrieval into new helper Alistair Francis
                   ` (18 subsequent siblings)
  20 siblings, 0 replies; 47+ messages in thread
From: Alistair Francis @ 2025-02-27  3:09 UTC (permalink / raw)
  To: linux-cxl, linux-kernel, lukas, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm
  Cc: boqun.feng, bjorn3_gh, wilfred.mallawa, aliceryhl, ojeda,
	alistair23, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis, Ilpo Järvinen, Dan Williams

From: Lukas Wunner <lukas@wunner.de>

The upcoming support for PCI device authentication with CMA-SPDM
(PCIe r6.1 sec 6.31) requires validating the Subject Alternative Name
in X.509 certificates.

Store a pointer to the Subject Alternative Name upon parsing for
consumption by CMA-SPDM.

Signed-off-by: Lukas Wunner <lukas@wunner.de>
Reviewed-by: Wilfred Mallawa <wilfred.mallawa@wdc.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Acked-by: Dan Williams <dan.j.williams@intel.com>
---
 crypto/asymmetric_keys/x509_cert_parser.c | 9 +++++++++
 include/keys/x509-parser.h                | 2 ++
 2 files changed, 11 insertions(+)

diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
index ee2fdab42334..ff1db59d4037 100644
--- a/crypto/asymmetric_keys/x509_cert_parser.c
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
@@ -572,6 +572,15 @@ int x509_process_extension(void *context, size_t hdrlen,
 		return 0;
 	}
 
+	if (ctx->last_oid == OID_subjectAltName) {
+		if (ctx->cert->raw_san)
+			return -EBADMSG;
+
+		ctx->cert->raw_san = v;
+		ctx->cert->raw_san_size = vlen;
+		return 0;
+	}
+
 	if (ctx->last_oid == OID_keyUsage) {
 		/*
 		 * Get hold of the keyUsage bit string
diff --git a/include/keys/x509-parser.h b/include/keys/x509-parser.h
index 37436a5c7526..8e450befe3b9 100644
--- a/include/keys/x509-parser.h
+++ b/include/keys/x509-parser.h
@@ -36,6 +36,8 @@ struct x509_certificate {
 	unsigned	raw_subject_size;
 	unsigned	raw_skid_size;
 	const void	*raw_skid;		/* Raw subjectKeyId in ASN.1 */
+	const void	*raw_san;		/* Raw subjectAltName in ASN.1 */
+	unsigned	raw_san_size;
 	unsigned	index;
 	bool		seen;			/* Infinite recursion prevention */
 	bool		verified;
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [RFC v2 03/20] X.509: Move certificate length retrieval into new helper
  2025-02-27  3:09 [RFC v2 00/20] lib: Rust implementation of SPDM Alistair Francis
  2025-02-27  3:09 ` [RFC v2 01/20] X.509: Make certificate parser public Alistair Francis
  2025-02-27  3:09 ` [RFC v2 02/20] X.509: Parse Subject Alternative Name in certificates Alistair Francis
@ 2025-02-27  3:09 ` Alistair Francis
  2025-02-27  3:09 ` [RFC v2 04/20] certs: Create blacklist keyring earlier Alistair Francis
                   ` (17 subsequent siblings)
  20 siblings, 0 replies; 47+ messages in thread
From: Alistair Francis @ 2025-02-27  3:09 UTC (permalink / raw)
  To: linux-cxl, linux-kernel, lukas, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm
  Cc: boqun.feng, bjorn3_gh, wilfred.mallawa, aliceryhl, ojeda,
	alistair23, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Dan Williams, Alistair Francis

From: Lukas Wunner <lukas@wunner.de>

The upcoming in-kernel SPDM library (Security Protocol and Data Model,
https://www.dmtf.org/dsp/DSP0274) needs to retrieve the length from
ASN.1 DER-encoded X.509 certificates.

Such code already exists in x509_load_certificate_list(), so move it
into a new helper for reuse by SPDM.

Export the helper so that SPDM can be tristate.  (Some upcoming users of
the SPDM libray may be modular, such as SCSI and ATA.)

No functional change intended.

Signed-off-by: Lukas Wunner <lukas@wunner.de>
Reviewed-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 crypto/asymmetric_keys/x509_loader.c | 38 +++++++++++++++++++---------
 include/keys/asymmetric-type.h       |  2 ++
 2 files changed, 28 insertions(+), 12 deletions(-)

diff --git a/crypto/asymmetric_keys/x509_loader.c b/crypto/asymmetric_keys/x509_loader.c
index a41741326998..25ff027fad1d 100644
--- a/crypto/asymmetric_keys/x509_loader.c
+++ b/crypto/asymmetric_keys/x509_loader.c
@@ -4,28 +4,42 @@
 #include <linux/key.h>
 #include <keys/asymmetric-type.h>
 
+ssize_t x509_get_certificate_length(const u8 *p, unsigned long buflen)
+{
+	ssize_t plen;
+
+	/* Each cert begins with an ASN.1 SEQUENCE tag and must be more
+	 * than 256 bytes in size.
+	 */
+	if (buflen < 4)
+		return -EINVAL;
+
+	if (p[0] != 0x30 &&
+	    p[1] != 0x82)
+		return -EINVAL;
+
+	plen = (p[2] << 8) | p[3];
+	plen += 4;
+	if (plen > buflen)
+		return -EINVAL;
+
+	return plen;
+}
+EXPORT_SYMBOL_GPL(x509_get_certificate_length);
+
 int x509_load_certificate_list(const u8 cert_list[],
 			       const unsigned long list_size,
 			       const struct key *keyring)
 {
 	key_ref_t key;
 	const u8 *p, *end;
-	size_t plen;
+	ssize_t plen;
 
 	p = cert_list;
 	end = p + list_size;
 	while (p < end) {
-		/* Each cert begins with an ASN.1 SEQUENCE tag and must be more
-		 * than 256 bytes in size.
-		 */
-		if (end - p < 4)
-			goto dodgy_cert;
-		if (p[0] != 0x30 &&
-		    p[1] != 0x82)
-			goto dodgy_cert;
-		plen = (p[2] << 8) | p[3];
-		plen += 4;
-		if (plen > end - p)
+		plen = x509_get_certificate_length(p, end - p);
+		if (plen < 0)
 			goto dodgy_cert;
 
 		key = key_create_or_update(make_key_ref(keyring, 1),
diff --git a/include/keys/asymmetric-type.h b/include/keys/asymmetric-type.h
index 69a13e1e5b2e..e2af07fec3c6 100644
--- a/include/keys/asymmetric-type.h
+++ b/include/keys/asymmetric-type.h
@@ -84,6 +84,8 @@ extern struct key *find_asymmetric_key(struct key *keyring,
 				       const struct asymmetric_key_id *id_2,
 				       bool partial);
 
+ssize_t x509_get_certificate_length(const u8 *p, unsigned long buflen);
+
 int x509_load_certificate_list(const u8 cert_list[], const unsigned long list_size,
 			       const struct key *keyring);
 
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [RFC v2 04/20] certs: Create blacklist keyring earlier
  2025-02-27  3:09 [RFC v2 00/20] lib: Rust implementation of SPDM Alistair Francis
                   ` (2 preceding siblings ...)
  2025-02-27  3:09 ` [RFC v2 03/20] X.509: Move certificate length retrieval into new helper Alistair Francis
@ 2025-02-27  3:09 ` Alistair Francis
  2025-02-27  3:09 ` [RFC v2 05/20] lib: rspdm: Initial commit of Rust SPDM Alistair Francis
                   ` (16 subsequent siblings)
  20 siblings, 0 replies; 47+ messages in thread
From: Alistair Francis @ 2025-02-27  3:09 UTC (permalink / raw)
  To: linux-cxl, linux-kernel, lukas, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm
  Cc: boqun.feng, bjorn3_gh, wilfred.mallawa, aliceryhl, ojeda,
	alistair23, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Dan Williams, Alistair Francis, Ilpo Järvinen

From: Lukas Wunner <lukas@wunner.de>

The upcoming support for PCI device authentication with CMA-SPDM
(PCIe r6.2 sec 6.31) requires parsing X.509 certificates upon
device enumeration, which happens in a subsys_initcall().

Parsing X.509 certificates accesses the blacklist keyring:
x509_cert_parse()
  x509_get_sig_params()
    is_hash_blacklisted()
      keyring_search()

So far the keyring is created much later in a device_initcall().  Avoid
a NULL pointer dereference on access to the keyring by creating it one
initcall level earlier than PCI device enumeration, i.e. in an
arch_initcall().

Signed-off-by: Lukas Wunner <lukas@wunner.de>
Reviewed-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Wilfred Mallawa <wilfred.mallawa@wdc.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 certs/blacklist.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/certs/blacklist.c b/certs/blacklist.c
index 675dd7a8f07a..34185415d451 100644
--- a/certs/blacklist.c
+++ b/certs/blacklist.c
@@ -311,7 +311,7 @@ static int restrict_link_for_blacklist(struct key *dest_keyring,
  * Initialise the blacklist
  *
  * The blacklist_init() function is registered as an initcall via
- * device_initcall().  As a result if the blacklist_init() function fails for
+ * arch_initcall().  As a result if the blacklist_init() function fails for
  * any reason the kernel continues to execute.  While cleanly returning -ENODEV
  * could be acceptable for some non-critical kernel parts, if the blacklist
  * keyring fails to load it defeats the certificate/key based deny list for
@@ -356,7 +356,7 @@ static int __init blacklist_init(void)
 /*
  * Must be initialised before we try and load the keys into the keyring.
  */
-device_initcall(blacklist_init);
+arch_initcall(blacklist_init);
 
 #ifdef CONFIG_SYSTEM_REVOCATION_LIST
 /*
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [RFC v2 05/20] lib: rspdm: Initial commit of Rust SPDM
  2025-02-27  3:09 [RFC v2 00/20] lib: Rust implementation of SPDM Alistair Francis
                   ` (3 preceding siblings ...)
  2025-02-27  3:09 ` [RFC v2 04/20] certs: Create blacklist keyring earlier Alistair Francis
@ 2025-02-27  3:09 ` Alistair Francis
  2025-02-27  3:09 ` [RFC v2 06/20] PCI/CMA: Authenticate devices on enumeration Alistair Francis
                   ` (15 subsequent siblings)
  20 siblings, 0 replies; 47+ messages in thread
From: Alistair Francis @ 2025-02-27  3:09 UTC (permalink / raw)
  To: linux-cxl, linux-kernel, lukas, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm
  Cc: boqun.feng, bjorn3_gh, wilfred.mallawa, aliceryhl, ojeda,
	alistair23, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

This is the initial commit of the Rust SPDM library. It is based on and
compatible with the C SPDM library in the kernel (lib/spdm).

Signed-off-by: Alistair Francis <alistair@alistair23.me>
---
 MAINTAINERS                     |  12 ++
 include/linux/spdm.h            |  39 ++++++
 lib/Kconfig                     |  16 +++
 lib/Makefile                    |   2 +
 lib/rspdm/Makefile              |  10 ++
 lib/rspdm/consts.rs             |  39 ++++++
 lib/rspdm/lib.rs                | 119 +++++++++++++++++
 lib/rspdm/state.rs              | 225 ++++++++++++++++++++++++++++++++
 lib/rspdm/validator.rs          |  66 ++++++++++
 rust/bindings/bindings_helper.h |   2 +
 rust/kernel/error.rs            |   3 +
 11 files changed, 533 insertions(+)
 create mode 100644 include/linux/spdm.h
 create mode 100644 lib/rspdm/Makefile
 create mode 100644 lib/rspdm/consts.rs
 create mode 100644 lib/rspdm/lib.rs
 create mode 100644 lib/rspdm/state.rs
 create mode 100644 lib/rspdm/validator.rs

diff --git a/MAINTAINERS b/MAINTAINERS
index 1b0cc181db74..0f37dbdcfd77 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21376,6 +21376,18 @@ 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>
+M:	Alistair Francis <alistair@alistair23.me>
+L:	linux-coco@lists.linux.dev
+L:	linux-cxl@vger.kernel.org
+L:	linux-pci@vger.kernel.org
+S:	Maintained
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/devsec/spdm.git
+F:	include/linux/spdm.h
+F:	lib/rspdm/
+
 SECURITY SUBSYSTEM
 M:	Paul Moore <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..9835a3202a0e
--- /dev/null
+++ b/include/linux/spdm.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * DMTF Security Protocol and Data Model (SPDM)
+ * https://www.dmtf.org/dsp/DSP0274
+ *
+ * Copyright (C) 2021-22 Huawei
+ *     Jonathan Cameron <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;
+struct x509_certificate;
+
+typedef ssize_t (spdm_transport)(void *priv, struct device *dev,
+				 const void *request, size_t request_sz,
+				 void *response, size_t response_sz);
+
+typedef int (spdm_validate)(struct device *dev, u8 slot,
+			    struct x509_certificate *leaf_cert);
+
+struct spdm_state *spdm_create(struct device *dev, spdm_transport *transport,
+			       void *transport_priv, u32 transport_sz,
+			       struct key *keyring, spdm_validate *validate);
+
+int spdm_authenticate(struct spdm_state *spdm_state);
+
+void spdm_destroy(struct spdm_state *spdm_state);
+
+extern const struct attribute_group spdm_attr_group;
+
+#endif
diff --git a/lib/Kconfig b/lib/Kconfig
index dccb61b7d698..df9c96a440c5 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -714,6 +714,22 @@ config LWQ_TEST
 	help
           Run boot-time test of light-weight queuing.
 
+config RSPDM
+	bool "Rust SPDM"
+	select CRYPTO
+	select KEYS
+	select ASYMMETRIC_KEY_TYPE
+	select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+	select X509_CERTIFICATE_PARSER
+	help
+	  The Rust implementation of the Security Protocol and Data Model (SPDM)
+	  allows for device authentication, measurement, key exchange and
+	  encrypted sessions.
+
+	  Crypto algorithms negotiated with SPDM are limited to those enabled
+	  in .config.  Users of SPDM therefore need to also select
+	  any algorithms they deem mandatory.
+
 endmenu
 
 config GENERIC_IOREMAP
diff --git a/lib/Makefile b/lib/Makefile
index d5cfc7afbbb8..09e1cfe413ef 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -303,6 +303,8 @@ obj-$(CONFIG_PERCPU_TEST) += percpu_test.o
 obj-$(CONFIG_ASN1) += asn1_decoder.o
 obj-$(CONFIG_ASN1_ENCODER) += asn1_encoder.o
 
+obj-$(CONFIG_RSPDM) += rspdm/
+
 obj-$(CONFIG_FONT_SUPPORT) += fonts/
 
 hostprogs	:= gen_crc32table
diff --git a/lib/rspdm/Makefile b/lib/rspdm/Makefile
new file mode 100644
index 000000000000..1f62ee2a882d
--- /dev/null
+++ b/lib/rspdm/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Rust implementation of the DMTF Security Protocol and Data Model (SPDM)
+# https://www.dmtf.org/dsp/DSP0274
+#
+# Copyright (C) 2024 Western Digital
+
+obj-$(CONFIG_RSPDM) += spdm.o
+
+spdm-y := lib.o
diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs
new file mode 100644
index 000000000000..311e34c7fae7
--- /dev/null
+++ b/lib/rspdm/consts.rs
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2024 Western Digital
+
+//! Constants used by the library
+//!
+//! Rust implementation of the DMTF Security Protocol and Data Model (SPDM)
+//! <https://www.dmtf.org/dsp/DSP0274>
+
+pub(crate) const SPDM_REQ: u8 = 0x80;
+pub(crate) const SPDM_ERROR: u8 = 0x7f;
+
+#[expect(dead_code)]
+#[derive(Clone, Copy)]
+pub(crate) enum SpdmErrorCode {
+    InvalidRequest = 0x01,
+    InvalidSession = 0x02,
+    Busy = 0x03,
+    UnexpectedRequest = 0x04,
+    Unspecified = 0x05,
+    DecryptError = 0x06,
+    UnsupportedRequest = 0x07,
+    RequestInFlight = 0x08,
+    InvalidResponseCode = 0x09,
+    SessionLimitExceeded = 0x0a,
+    SessionRequired = 0x0b,
+    ResetRequired = 0x0c,
+    ResponseTooLarge = 0x0d,
+    RequestTooLarge = 0x0e,
+    LargeResponse = 0x0f,
+    MessageLost = 0x10,
+    InvalidPolicy = 0x11,
+    VersionMismatch = 0x41,
+    ResponseNotReady = 0x42,
+    RequestResynch = 0x43,
+    OperationFailed = 0x44,
+    NoPendingRequests = 0x45,
+    VendorDefinedError = 0xff,
+}
diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs
new file mode 100644
index 000000000000..2bb716140e0a
--- /dev/null
+++ b/lib/rspdm/lib.rs
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2024 Western Digital
+
+//! Top level library for SPDM
+//!
+//! Rust implementation of the DMTF Security Protocol and Data Model (SPDM)
+//! <https://www.dmtf.org/dsp/DSP0274>
+//!
+//! Top level library, including C compatible public functions to be called
+//! from other subsytems.
+//!
+//! This mimics the C SPDM implementation in the kernel
+
+use core::ffi::{c_int, c_void};
+use core::ptr;
+use core::slice::from_raw_parts_mut;
+use kernel::prelude::*;
+use kernel::{alloc::flags, bindings};
+
+use crate::state::SpdmState;
+
+const __LOG_PREFIX: &[u8] = b"spdm\0";
+
+mod consts;
+mod state;
+mod validator;
+
+/// spdm_create() - Allocate SPDM session
+///
+/// `dev`: Responder device
+/// `transport`: Transport function to perform one message exchange
+/// `transport_priv`: Transport private data
+/// `transport_sz`: Maximum message size the transport is capable of (in bytes)
+/// `keyring`: Trusted root certificates
+/// `validate`: Function to validate additional leaf certificate requirements
+///  (optional, may be %NULL)
+///
+/// Return a pointer to the allocated SPDM session state or NULL on error.
+#[no_mangle]
+pub unsafe extern "C" fn spdm_create(
+    dev: *mut bindings::device,
+    transport: bindings::spdm_transport,
+    transport_priv: *mut c_void,
+    transport_sz: u32,
+    keyring: *mut bindings::key,
+    validate: bindings::spdm_validate,
+) -> *mut SpdmState {
+    match KBox::new(
+        SpdmState::new(
+            dev,
+            transport,
+            transport_priv,
+            transport_sz,
+            keyring,
+            validate,
+        ),
+        flags::GFP_KERNEL,
+    ) {
+        Ok(ret) => KBox::into_raw(ret) as *mut SpdmState,
+        Err(_) => ptr::null_mut(),
+    }
+}
+
+/// spdm_exchange() - Perform SPDM message exchange with device
+///
+/// @spdm_state: SPDM session state
+/// @req: Request message
+/// @req_sz: Size of @req
+/// @rsp: Response message
+/// @rsp_sz: Size of @rsp
+///
+/// Send the request @req to the device via the @transport in @spdm_state and
+/// receive the response into @rsp, respecting the maximum buffer size @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.
+#[no_mangle]
+pub unsafe extern "C" fn spdm_exchange(
+    state: &'static mut SpdmState,
+    req: *mut c_void,
+    req_sz: usize,
+    rsp: *mut c_void,
+    rsp_sz: usize,
+) -> isize {
+    let request_buf: &mut [u8] = unsafe { from_raw_parts_mut(req as *mut u8, req_sz) };
+    let response_buf: &mut [u8] = unsafe { from_raw_parts_mut(rsp as *mut u8, rsp_sz) };
+
+    match state.spdm_exchange(request_buf, response_buf) {
+        Ok(ret) => ret as isize,
+        Err(e) => e.to_errno() as isize,
+    }
+}
+
+/// 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.
+#[no_mangle]
+pub unsafe extern "C" fn spdm_authenticate(_state: &'static mut SpdmState) -> c_int {
+    0
+}
+
+/// spdm_destroy() - Destroy SPDM session
+///
+/// @spdm_state: SPDM session state
+#[no_mangle]
+pub unsafe extern "C" fn spdm_destroy(_state: &'static mut SpdmState) {}
diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs
new file mode 100644
index 000000000000..9ebd87603454
--- /dev/null
+++ b/lib/rspdm/state.rs
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2024 Western Digital
+
+//! The `SpdmState` struct and implementation.
+//!
+//! Rust implementation of the DMTF Security Protocol and Data Model (SPDM)
+//! <https://www.dmtf.org/dsp/DSP0274>
+
+use core::ffi::c_void;
+use kernel::prelude::*;
+use kernel::{
+    bindings,
+    error::{code::EINVAL, to_result, Error},
+    validate::Untrusted,
+};
+
+use crate::consts::{SpdmErrorCode, SPDM_ERROR, SPDM_REQ};
+use crate::validator::{SpdmErrorRsp, SpdmHeader};
+
+/// The current SPDM session state for a device. Based on the
+/// C `struct spdm_state`.
+///
+/// `dev`: Responder device.  Used for error reporting and passed to @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.
+/// `keyring`: Keyring against which to check the first certificate in
+///  responder's certificate chain.
+/// `validate`: Function to validate additional leaf certificate requirements.
+///
+/// `version`: Maximum common supported version of requester and responder.
+///  Negotiated during GET_VERSION exchange.
+///
+/// `authenticated`: Whether device was authenticated successfully.
+#[expect(dead_code)]
+pub struct SpdmState {
+    pub(crate) dev: *mut bindings::device,
+    pub(crate) transport: bindings::spdm_transport,
+    pub(crate) transport_priv: *mut c_void,
+    pub(crate) transport_sz: u32,
+    pub(crate) keyring: *mut bindings::key,
+    pub(crate) validate: bindings::spdm_validate,
+
+    // Negotiated state
+    pub(crate) version: u8,
+
+    pub(crate) authenticated: bool,
+}
+
+impl SpdmState {
+    pub(crate) fn new(
+        dev: *mut bindings::device,
+        transport: bindings::spdm_transport,
+        transport_priv: *mut c_void,
+        transport_sz: u32,
+        keyring: *mut bindings::key,
+        validate: bindings::spdm_validate,
+    ) -> Self {
+        SpdmState {
+            dev,
+            transport,
+            transport_priv,
+            transport_sz,
+            keyring,
+            validate,
+            version: 0x10,
+            authenticated: false,
+        }
+    }
+
+    fn spdm_err(&self, rsp: &SpdmErrorRsp) -> Result<(), Error> {
+        match rsp.error_code {
+            SpdmErrorCode::InvalidRequest => {
+                pr_err!("Invalid request\n");
+                Err(EINVAL)
+            }
+            SpdmErrorCode::InvalidSession => {
+                if rsp.version == 0x11 {
+                    pr_err!("Invalid session {:#x}\n", rsp.error_data);
+                    Err(EINVAL)
+                } else {
+                    pr_err!("Undefined error {:#x}\n", rsp.error_code as u8);
+                    Err(EINVAL)
+                }
+            }
+            SpdmErrorCode::Busy => {
+                pr_err!("Busy\n");
+                Err(EBUSY)
+            }
+            SpdmErrorCode::UnexpectedRequest => {
+                pr_err!("Unexpected request\n");
+                Err(EINVAL)
+            }
+            SpdmErrorCode::Unspecified => {
+                pr_err!("Unspecified error\n");
+                Err(EINVAL)
+            }
+            SpdmErrorCode::DecryptError => {
+                pr_err!("Decrypt error\n");
+                Err(EIO)
+            }
+            SpdmErrorCode::UnsupportedRequest => {
+                pr_err!("Unsupported request {:#x}\n", rsp.error_data);
+                Err(EINVAL)
+            }
+            SpdmErrorCode::RequestInFlight => {
+                pr_err!("Request in flight\n");
+                Err(EINVAL)
+            }
+            SpdmErrorCode::InvalidResponseCode => {
+                pr_err!("Invalid response code\n");
+                Err(EINVAL)
+            }
+            SpdmErrorCode::SessionLimitExceeded => {
+                pr_err!("Session limit exceeded\n");
+                Err(EBUSY)
+            }
+            SpdmErrorCode::SessionRequired => {
+                pr_err!("Session required\n");
+                Err(EINVAL)
+            }
+            SpdmErrorCode::ResetRequired => {
+                pr_err!("Reset required\n");
+                Err(ECONNRESET)
+            }
+            SpdmErrorCode::ResponseTooLarge => {
+                pr_err!("Response too large\n");
+                Err(EINVAL)
+            }
+            SpdmErrorCode::RequestTooLarge => {
+                pr_err!("Request too large\n");
+                Err(EINVAL)
+            }
+            SpdmErrorCode::LargeResponse => {
+                pr_err!("Large response\n");
+                Err(EMSGSIZE)
+            }
+            SpdmErrorCode::MessageLost => {
+                pr_err!("Message lost\n");
+                Err(EIO)
+            }
+            SpdmErrorCode::InvalidPolicy => {
+                pr_err!("Invalid policy\n");
+                Err(EINVAL)
+            }
+            SpdmErrorCode::VersionMismatch => {
+                pr_err!("Version mismatch\n");
+                Err(EINVAL)
+            }
+            SpdmErrorCode::ResponseNotReady => {
+                pr_err!("Response not ready\n");
+                Err(EINPROGRESS)
+            }
+            SpdmErrorCode::RequestResynch => {
+                pr_err!("Request resynchronization\n");
+                Err(ECONNRESET)
+            }
+            SpdmErrorCode::OperationFailed => {
+                pr_err!("Operation failed\n");
+                Err(EINVAL)
+            }
+            SpdmErrorCode::NoPendingRequests => Err(ENOENT),
+            SpdmErrorCode::VendorDefinedError => {
+                pr_err!("Vendor defined error\n");
+                Err(EINVAL)
+            }
+        }
+    }
+
+    /// Start a SPDM exchange
+    ///
+    /// The data in `request_buf` is sent to the device and the response is
+    /// stored in `response_buf`.
+    pub(crate) fn spdm_exchange(
+        &self,
+        request_buf: &mut [u8],
+        response_buf: &mut [u8],
+    ) -> Result<i32, Error> {
+        let header_size = core::mem::size_of::<SpdmHeader>();
+        let request: &mut SpdmHeader = Untrusted::new_mut(request_buf).validate_mut()?;
+        let response: &SpdmHeader = Untrusted::new_ref(response_buf).validate()?;
+
+        let transport_function = self.transport.ok_or(EINVAL)?;
+        // SAFETY: `transport_function` is provided by the new(), we are
+        // calling the function.
+        let length = unsafe {
+            transport_function(
+                self.transport_priv,
+                self.dev,
+                request_buf.as_ptr() as *const c_void,
+                request_buf.len(),
+                response_buf.as_mut_ptr() as *mut c_void,
+                response_buf.len(),
+            ) as i32
+        };
+        to_result(length)?;
+
+        if (length as usize) < header_size {
+            return Ok(length); // Truncated response is handled by callers
+        }
+        if response.code == SPDM_ERROR {
+            if length as usize >= core::mem::size_of::<SpdmErrorRsp>() {
+                // SAFETY: The response buffer will be at at least as large as
+                // `SpdmErrorRsp` so we can cast the buffer to `SpdmErrorRsp` which
+                // is a packed struct.
+                self.spdm_err(unsafe { &*(response_buf.as_ptr() as *const SpdmErrorRsp) })?;
+            } else {
+                return Err(EINVAL);
+            }
+        }
+
+        if response.code != request.code & !SPDM_REQ {
+            pr_err!(
+                "Response code {:#x} does not match request code {:#x}\n",
+                response.code,
+                request.code
+            );
+            to_result(-(bindings::EPROTO as i32))?;
+        }
+
+        Ok(length)
+    }
+}
diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs
new file mode 100644
index 000000000000..a0a3a2f46952
--- /dev/null
+++ b/lib/rspdm/validator.rs
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2024 Western Digital
+
+//! Related structs and their Validate implementations.
+//!
+//! Rust implementation of the DMTF Security Protocol and Data Model (SPDM)
+//! <https://www.dmtf.org/dsp/DSP0274>
+
+use crate::consts::SpdmErrorCode;
+use core::mem;
+use kernel::prelude::*;
+use kernel::{
+    error::{code::EINVAL, Error},
+    validate::{Unvalidated, Validate},
+};
+
+#[repr(C, packed)]
+pub(crate) struct SpdmHeader {
+    pub(crate) version: u8,
+    pub(crate) code: u8, /* RequestResponseCode */
+    pub(crate) param1: u8,
+    pub(crate) param2: u8,
+}
+
+impl Validate<&Unvalidated<[u8]>> for &SpdmHeader {
+    type Err = Error;
+
+    fn validate(unvalidated: &Unvalidated<[u8]>) -> Result<Self, Self::Err> {
+        let raw = unvalidated.raw();
+        if raw.len() < mem::size_of::<SpdmHeader>() {
+            return Err(EINVAL);
+        }
+
+        let ptr = raw.as_ptr();
+        // CAST: `SpdmHeader` only contains integers and has `repr(C)`.
+        let ptr = ptr.cast::<SpdmHeader>();
+        // SAFETY: `ptr` came from a reference and the cast above is valid.
+        Ok(unsafe { &*ptr })
+    }
+}
+
+impl Validate<&mut Unvalidated<[u8]>> for &mut SpdmHeader {
+    type Err = Error;
+
+    fn validate(unvalidated: &mut Unvalidated<[u8]>) -> Result<Self, Self::Err> {
+        let raw = unvalidated.raw_mut();
+        if raw.len() < mem::size_of::<SpdmHeader>() {
+            return Err(EINVAL);
+        }
+
+        let ptr = raw.as_mut_ptr();
+        // CAST: `SpdmHeader` only contains integers and has `repr(C)`.
+        let ptr = ptr.cast::<SpdmHeader>();
+        // SAFETY: `ptr` came from a reference and the cast above is valid.
+        Ok(unsafe { &mut *ptr })
+    }
+}
+
+#[repr(C, packed)]
+pub(crate) struct SpdmErrorRsp {
+    pub(crate) version: u8,
+    pub(crate) code: u8,
+    pub(crate) error_code: SpdmErrorCode,
+    pub(crate) error_data: u8,
+}
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index f46cf3bb7069..42aa62f0c8f5 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -33,6 +33,8 @@
 #include <linux/security.h>
 #include <linux/slab.h>
 #include <linux/tracepoint.h>
+#include <linux/spdm.h>
+#include <linux/uaccess.h>
 #include <linux/wait.h>
 #include <linux/workqueue.h>
 #include <trace/events/rust_sample.h>
diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
index f6ecf09cb65f..8ee8674ec485 100644
--- a/rust/kernel/error.rs
+++ b/rust/kernel/error.rs
@@ -83,6 +83,9 @@ macro_rules! declare_err {
     declare_err!(EIOCBQUEUED, "iocb queued, will get completion event.");
     declare_err!(ERECALLCONFLICT, "Conflict with recalled state.");
     declare_err!(ENOGRACE, "NFS file lock reclaim refused.");
+    declare_err!(ECONNRESET, "Connection reset by peer.");
+    declare_err!(EMSGSIZE, "Message too long.");
+    declare_err!(EINPROGRESS, "Operation now in progress.");
 }
 
 /// Generic integer kernel error.
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [RFC v2 06/20] PCI/CMA: Authenticate devices on enumeration
  2025-02-27  3:09 [RFC v2 00/20] lib: Rust implementation of SPDM Alistair Francis
                   ` (4 preceding siblings ...)
  2025-02-27  3:09 ` [RFC v2 05/20] lib: rspdm: Initial commit of Rust SPDM Alistair Francis
@ 2025-02-27  3:09 ` Alistair Francis
  2025-02-27  3:09 ` [RFC v2 07/20] PCI/CMA: Validate Subject Alternative Name in certificates Alistair Francis
                   ` (14 subsequent siblings)
  20 siblings, 0 replies; 47+ messages in thread
From: Alistair Francis @ 2025-02-27  3:09 UTC (permalink / raw)
  To: linux-cxl, linux-kernel, lukas, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm
  Cc: boqun.feng, bjorn3_gh, wilfred.mallawa, aliceryhl, ojeda,
	alistair23, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin

From: Jonathan Cameron <Jonathan.Cameron@huawei.com>

Component Measurement and Authentication (CMA, PCIe r6.2 sec 6.31)
allows for measurement and authentication of PCIe devices.  It is
based on the Security Protocol and Data Model specification (SPDM,
https://www.dmtf.org/dsp/DSP0274).

CMA-SPDM in turn forms the basis for Integrity and Data Encryption
(IDE, PCIe r6.2 sec 6.33) because the key material used by IDE is
transmitted over a CMA-SPDM session.

As a first step, authenticate CMA-capable devices on enumeration.
A subsequent commit will expose the result in sysfs.

When allocating SPDM session state with spdm_create(), the maximum SPDM
message length needs to be passed.  Make the PCI_DOE_MAX_LENGTH macro
public and calculate the maximum payload length from it.

Credits:  Jonathan wrote a proof-of-concept of this CMA implementation.
Lukas reworked it for upstream.  Wilfred contributed fixes for issues
discovered during testing.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Co-developed-by: Wilfred Mallawa <wilfred.mallawa@wdc.com>
Signed-off-by: Wilfred Mallawa <wilfred.mallawa@wdc.com>
Co-developed-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Lukas Wunner <lukas@wunner.de>
---
 MAINTAINERS             |   1 +
 drivers/pci/Kconfig     |  13 ++++++
 drivers/pci/Makefile    |   2 +
 drivers/pci/cma.c       | 101 ++++++++++++++++++++++++++++++++++++++++
 drivers/pci/doe.c       |   3 --
 drivers/pci/pci.h       |   8 ++++
 drivers/pci/probe.c     |   1 +
 drivers/pci/remove.c    |   1 +
 include/linux/pci-doe.h |   4 ++
 include/linux/pci.h     |   4 ++
 10 files changed, 135 insertions(+), 3 deletions(-)
 create mode 100644 drivers/pci/cma.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 0f37dbdcfd77..abb3b603299f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21385,6 +21385,7 @@ L:	linux-cxl@vger.kernel.org
 L:	linux-pci@vger.kernel.org
 S:	Maintained
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/devsec/spdm.git
+F:	drivers/pci/cma.c
 F:	include/linux/spdm.h
 F:	lib/rspdm/
 
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 2fbd379923fd..80954adbff7d 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -121,6 +121,19 @@ config XEN_PCIDEV_FRONTEND
 config PCI_ATS
 	bool
 
+config PCI_CMA
+	bool "Component Measurement and Authentication (CMA-SPDM)"
+	select CRYPTO_ECDSA
+	select CRYPTO_RSA
+	select CRYPTO_SHA256
+	select CRYPTO_SHA512
+	select PCI_DOE
+	select RSPDM
+	help
+	  Authenticate devices on enumeration per PCIe r6.2 sec 6.31.
+	  A PCI DOE mailbox is used as transport for DMTF SPDM based
+	  authentication, measurement and secure channel establishment.
+
 config PCI_DOE
 	bool
 
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 67647f1880fb..3cbf2c226a2d 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -38,6 +38,8 @@ obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) += of_property.o
 obj-$(CONFIG_PCI_NPEM)		+= npem.o
 obj-$(CONFIG_PCIE_TPH)		+= tph.o
 
+obj-$(CONFIG_PCI_CMA)		+= cma.o
+
 # Endpoint library must be initialized before its users
 obj-$(CONFIG_PCI_ENDPOINT)	+= endpoint/
 
diff --git a/drivers/pci/cma.c b/drivers/pci/cma.c
new file mode 100644
index 000000000000..7463cd1179f0
--- /dev/null
+++ b/drivers/pci/cma.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Component Measurement and Authentication (CMA-SPDM, PCIe r6.2 sec 6.31)
+ *
+ * Copyright (C) 2021 Huawei
+ *     Jonathan Cameron <Jonathan.Cameron@huawei.com>
+ *
+ * Copyright (C) 2022-24 Intel Corporation
+ */
+
+#define dev_fmt(fmt) "CMA: " fmt
+
+#include <linux/pci.h>
+#include <linux/pci-doe.h>
+#include <linux/pm_runtime.h>
+#include <linux/spdm.h>
+
+#include "pci.h"
+
+/* Keyring that userspace can poke certs into */
+static struct key *pci_cma_keyring;
+
+#define PCI_DOE_FEATURE_CMA 1
+
+static ssize_t pci_doe_transport(void *priv, struct device *dev,
+				 const void *request, size_t request_sz,
+				 void *response, size_t response_sz)
+{
+	struct pci_doe_mb *doe = priv;
+	ssize_t rc;
+
+	/*
+	 * CMA-SPDM operation in non-D0 states is optional (PCIe r6.2
+	 * sec 6.31.3).  The spec does not define a way to determine
+	 * if it's supported, so resume to D0 unconditionally.
+	 */
+	rc = pm_runtime_resume_and_get(dev);
+	if (rc)
+		return rc;
+
+	rc = pci_doe(doe, PCI_VENDOR_ID_PCI_SIG, PCI_DOE_FEATURE_CMA,
+		     request, request_sz, response, response_sz);
+
+	pm_runtime_put(dev);
+
+	return rc;
+}
+
+void pci_cma_init(struct pci_dev *pdev)
+{
+	struct pci_doe_mb *doe;
+
+	if (IS_ERR(pci_cma_keyring))
+		return;
+
+	if (!pci_is_pcie(pdev))
+		return;
+
+	doe = pci_find_doe_mailbox(pdev, PCI_VENDOR_ID_PCI_SIG,
+				   PCI_DOE_FEATURE_CMA);
+	if (!doe)
+		return;
+
+	pdev->spdm_state = spdm_create(&pdev->dev, pci_doe_transport, doe,
+				       PCI_DOE_MAX_PAYLOAD, pci_cma_keyring,
+				       NULL);
+	if (!pdev->spdm_state)
+		return;
+
+	/*
+	 * Keep spdm_state allocated even if initial authentication fails
+	 * to allow for provisioning of certificates and reauthentication.
+	 */
+	spdm_authenticate(pdev->spdm_state);
+}
+
+void pci_cma_destroy(struct pci_dev *pdev)
+{
+	if (!pdev->spdm_state)
+		return;
+
+	spdm_destroy(pdev->spdm_state);
+}
+
+__init static int pci_cma_keyring_init(void)
+{
+	pci_cma_keyring = keyring_alloc(".cma", KUIDT_INIT(0), KGIDT_INIT(0),
+					current_cred(),
+					(KEY_POS_ALL & ~KEY_POS_SETATTR) |
+					KEY_USR_VIEW | KEY_USR_READ |
+					KEY_USR_WRITE | KEY_USR_SEARCH,
+					KEY_ALLOC_NOT_IN_QUOTA |
+					KEY_ALLOC_SET_KEEP, NULL, NULL);
+	if (IS_ERR(pci_cma_keyring)) {
+		pr_err("PCI: Could not allocate .cma keyring\n");
+		return PTR_ERR(pci_cma_keyring);
+	}
+
+	return 0;
+}
+arch_initcall(pci_cma_keyring_init);
diff --git a/drivers/pci/doe.c b/drivers/pci/doe.c
index 7bd7892c5222..8ea37698082a 100644
--- a/drivers/pci/doe.c
+++ b/drivers/pci/doe.c
@@ -31,9 +31,6 @@
 #define PCI_DOE_FLAG_CANCEL	0
 #define PCI_DOE_FLAG_DEAD	1
 
-/* Max data object length is 2^18 dwords */
-#define PCI_DOE_MAX_LENGTH	(1 << 18)
-
 /**
  * struct pci_doe_mb - State for a single DOE mailbox
  *
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 01e51db8d285..69ba2ae9a0cd 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -448,6 +448,14 @@ static inline void pci_doe_destroy(struct pci_dev *pdev) { }
 static inline void pci_doe_disconnected(struct pci_dev *pdev) { }
 #endif
 
+#ifdef CONFIG_PCI_CMA
+void pci_cma_init(struct pci_dev *pdev);
+void pci_cma_destroy(struct pci_dev *pdev);
+#else
+static inline void pci_cma_init(struct pci_dev *pdev) { }
+static inline void pci_cma_destroy(struct pci_dev *pdev) { }
+#endif
+
 #ifdef CONFIG_PCI_NPEM
 void pci_npem_create(struct pci_dev *dev);
 void pci_npem_remove(struct pci_dev *dev);
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 246744d8d268..edf06055e805 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -2565,6 +2565,7 @@ static void pci_init_capabilities(struct pci_dev *dev)
 	pci_rcec_init(dev);		/* Root Complex Event Collector */
 	pci_doe_init(dev);		/* Data Object Exchange */
 	pci_tph_init(dev);		/* TLP Processing Hints */
+	pci_cma_init(dev);		/* Component Measurement & Auth */
 
 	pcie_report_downtraining(dev);
 	pci_init_reset_methods(dev);
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index efc37fcb73e2..e6feab4e3c61 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -61,6 +61,7 @@ static void pci_destroy_dev(struct pci_dev *dev)
 	list_del(&dev->bus_list);
 	up_write(&pci_bus_sem);
 
+	pci_cma_destroy(dev);
 	pci_doe_destroy(dev);
 	pcie_aspm_exit_link_state(dev);
 	pci_bridge_d3_update(dev);
diff --git a/include/linux/pci-doe.h b/include/linux/pci-doe.h
index 1f14aed4354b..0d3d7656c456 100644
--- a/include/linux/pci-doe.h
+++ b/include/linux/pci-doe.h
@@ -15,6 +15,10 @@
 
 struct pci_doe_mb;
 
+/* Max data object length is 2^18 dwords (including 2 dwords for header) */
+#define PCI_DOE_MAX_LENGTH	(1 << 18)
+#define PCI_DOE_MAX_PAYLOAD	((PCI_DOE_MAX_LENGTH - 2) * sizeof(u32))
+
 struct pci_doe_mb *pci_find_doe_mailbox(struct pci_dev *pdev, u16 vendor,
 					u8 type);
 
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 47b31ad724fa..50c43546a32b 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -39,6 +39,7 @@
 #include <linux/io.h>
 #include <linux/resource_ext.h>
 #include <linux/msi_api.h>
+#include <linux/spdm.h>
 #include <uapi/linux/pci.h>
 
 #include <linux/pci_ids.h>
@@ -528,6 +529,9 @@ struct pci_dev {
 #ifdef CONFIG_PCI_DOE
 	struct xarray	doe_mbs;	/* Data Object Exchange mailboxes */
 #endif
+#ifdef CONFIG_PCI_CMA
+	struct spdm_state *spdm_state;	/* Security Protocol and Data Model */
+#endif
 #ifdef CONFIG_PCI_NPEM
 	struct npem	*npem;		/* Native PCIe Enclosure Management */
 #endif
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [RFC v2 07/20] PCI/CMA: Validate Subject Alternative Name in certificates
  2025-02-27  3:09 [RFC v2 00/20] lib: Rust implementation of SPDM Alistair Francis
                   ` (5 preceding siblings ...)
  2025-02-27  3:09 ` [RFC v2 06/20] PCI/CMA: Authenticate devices on enumeration Alistair Francis
@ 2025-02-27  3:09 ` Alistair Francis
  2025-02-27  3:09 ` [RFC v2 08/20] PCI/CMA: Reauthenticate devices on reset and resume Alistair Francis
                   ` (13 subsequent siblings)
  20 siblings, 0 replies; 47+ messages in thread
From: Alistair Francis @ 2025-02-27  3:09 UTC (permalink / raw)
  To: linux-cxl, linux-kernel, lukas, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm
  Cc: boqun.feng, bjorn3_gh, wilfred.mallawa, aliceryhl, ojeda,
	alistair23, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin

From: Lukas Wunner <lukas@wunner.de>

PCIe r6.1 sec 6.31.3 stipulates requirements for Leaf Certificates
presented by devices, in particular the presence of a Subject Alternative
Name which encodes the Vendor ID, Device ID, Device Serial Number, etc.

This prevents a mismatch between the device identity in Config Space and
the certificate.  A device cannot misappropriate a certificate from a
different device without also spoofing Config Space.  As a corollary,
it cannot dupe an arbitrary driver into binding to it.  Only drivers
which bind to the device identity in the Subject Alternative Name work
(PCIe r6.1 sec 6.31 "Implementation Note: Overview of Threat Model").

The Subject Alternative Name is signed, hence constitutes a signed copy
of a Config Space portion.  It's the same concept as web certificates
which contain a set of domain names in the Subject Alternative Name for
identity verification.

Parse the Subject Alternative Name using a small ASN.1 module and
validate its contents.  The theory of operation is explained in a
comment at the top of the newly inserted code.

This functionality is introduced in a separate commit on top of basic
CMA-SPDM support to split the code into digestible, reviewable chunks.

The CMA OID added here is taken from the official OID Repository
(it's not documented in the PCIe Base Spec):
https://oid-rep.orange-labs.fr/get/2.23.147

Side notes:

* PCIe r6.2 removes the spec language on the Subject Alternative Name.
  It still "requires the leaf certificate to include the information
  typically used by system software for device driver binding", but no
  longer specifies how that information is encoded into the certificate.

  According to the editor of the PCIe Base Spec and the author of the
  CMA 1.1 ECN (which caused this change), FPGA cards which mutate their
  device identity at runtime (due to a firmware update) were thought as
  unable to satisfy the previous spec language.  The Protocol Working
  Group could not agree on a better solution and therefore dropped the
  spec language entirely.  They acknowledge that the requirement is now
  under-spec'd.  Because products already exist which adhere to the
  Subject Alternative Name requirement per PCIe r6.1 sec 6.31.3, they
  recommended to "push through" and use it as the de facto standard.

  The FPGA concerns are easily overcome by reauthenticating the device
  after a firmware update, either via sysfs or pci_cma_reauthenticate()
  (added by a subsequent commit).

* PCIe r6.1 sec 6.31.3 strongly recommends to verify that "the
  information provided in the Subject Alternative Name entry is signed
  by the vendor indicated by the Vendor ID."  In other words, the root
  certificate on pci_cma_keyring which signs the device's certificate
  chain must have been created for a particular Vendor ID.

  Unfortunately the spec neglects to define how the Vendor ID shall be
  encoded into the root certificate.  So the recommendation cannot be
  implemented at this point and it is thus possible that a vendor signs
  device certificates of a different vendor.

* Instead of a Subject Alternative Name, Leaf Certificates may include
  "a Reference Integrity Manifest, e.g., see Trusted Computing Group" or
  "a pointer to a location where such a Reference Integrity Manifest can
  be obtained" (PCIe r6.1 sec 6.31.3).

  A Reference Integrity Manifest contains "golden" measurements which
  can be compared to actual measurements retrieved from a device.
  It serves a different purpose than the Subject Alternative Name,
  hence it is unclear why the spec says only either of them is necessary.
  It is also unclear how a Reference Integrity Manifest shall be encoded
  into a certificate.

  Hence ignore the Reference Integrity Manifest requirement.

Signed-off-by: Lukas Wunner <lukas@wunner.de>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> # except ASN.1
---
 drivers/pci/Makefile         |   4 +-
 drivers/pci/cma.asn1         |  41 ++++++++++++
 drivers/pci/cma.c            | 123 ++++++++++++++++++++++++++++++++++-
 include/linux/oid_registry.h |   3 +
 4 files changed, 169 insertions(+), 2 deletions(-)
 create mode 100644 drivers/pci/cma.asn1

diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 3cbf2c226a2d..0b9c2f3ed84a 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -38,7 +38,9 @@ obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) += of_property.o
 obj-$(CONFIG_PCI_NPEM)		+= npem.o
 obj-$(CONFIG_PCIE_TPH)		+= tph.o
 
-obj-$(CONFIG_PCI_CMA)		+= cma.o
+obj-$(CONFIG_PCI_CMA)		+= cma.o cma.asn1.o
+$(obj)/cma.o:			$(obj)/cma.asn1.h
+$(obj)/cma.asn1.o:		$(obj)/cma.asn1.c $(obj)/cma.asn1.h
 
 # Endpoint library must be initialized before its users
 obj-$(CONFIG_PCI_ENDPOINT)	+= endpoint/
diff --git a/drivers/pci/cma.asn1 b/drivers/pci/cma.asn1
new file mode 100644
index 000000000000..da41421d4085
--- /dev/null
+++ b/drivers/pci/cma.asn1
@@ -0,0 +1,41 @@
+-- SPDX-License-Identifier: BSD-3-Clause
+--
+-- Component Measurement and Authentication (CMA-SPDM, PCIe r6.1 sec 6.31.3)
+-- X.509 Subject Alternative Name (RFC 5280 sec 4.2.1.6)
+--
+-- Copyright (C) 2008 IETF Trust and the persons identified as authors
+-- of the code
+--
+-- https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.6
+--
+-- The ASN.1 module in RFC 5280 appendix A.1 uses EXPLICIT TAGS whereas the one
+-- in appendix A.2 uses IMPLICIT TAGS.  The kernel's simplified asn1_compiler.c
+-- always uses EXPLICIT TAGS, hence this ASN.1 module differs from RFC 5280 in
+-- that it adds IMPLICIT to definitions from appendix A.2 (such as GeneralName)
+-- and omits EXPLICIT in those definitions.
+
+SubjectAltName ::= GeneralNames
+
+GeneralNames ::= SEQUENCE OF GeneralName
+
+GeneralName ::= CHOICE {
+	otherName			[0] IMPLICIT OtherName,
+	rfc822Name			[1] IMPLICIT IA5String,
+	dNSName				[2] IMPLICIT IA5String,
+	x400Address			[3] ANY,
+	directoryName			[4] ANY,
+	ediPartyName			[5] IMPLICIT EDIPartyName,
+	uniformResourceIdentifier	[6] IMPLICIT IA5String,
+	iPAddress			[7] IMPLICIT OCTET STRING,
+	registeredID			[8] IMPLICIT OBJECT IDENTIFIER
+	}
+
+OtherName ::= SEQUENCE {
+	type-id			OBJECT IDENTIFIER ({ pci_cma_note_oid }),
+	value			[0] ANY ({ pci_cma_note_san })
+	}
+
+EDIPartyName ::= SEQUENCE {
+	nameAssigner		[0] ANY OPTIONAL,
+	partyName		[1] ANY
+	}
diff --git a/drivers/pci/cma.c b/drivers/pci/cma.c
index 7463cd1179f0..e974d489c7a2 100644
--- a/drivers/pci/cma.c
+++ b/drivers/pci/cma.c
@@ -10,16 +10,137 @@
 
 #define dev_fmt(fmt) "CMA: " fmt
 
+#include <keys/x509-parser.h>
+#include <linux/asn1_decoder.h>
+#include <linux/oid_registry.h>
 #include <linux/pci.h>
 #include <linux/pci-doe.h>
 #include <linux/pm_runtime.h>
 #include <linux/spdm.h>
 
+#include "cma.asn1.h"
 #include "pci.h"
 
 /* Keyring that userspace can poke certs into */
 static struct key *pci_cma_keyring;
 
+/*
+ * The spdm_requester.c library calls pci_cma_validate() to check requirements
+ * for Leaf Certificates per PCIe r6.1 sec 6.31.3.
+ *
+ * pci_cma_validate() parses the Subject Alternative Name using the ASN.1
+ * module cma.asn1, which calls pci_cma_note_oid() and pci_cma_note_san()
+ * to compare an OtherName against the expected name.
+ *
+ * The expected name is constructed beforehand by pci_cma_construct_san().
+ *
+ * PCIe r6.2 drops the Subject Alternative Name spec language, even though
+ * it continues to require "the leaf certificate to include the information
+ * typically used by system software for device driver binding".  Use the
+ * Subject Alternative Name per PCIe r6.1 for lack of a replacement and
+ * because it is the de facto standard among existing products.
+ */
+#define CMA_NAME_MAX sizeof("Vendor=1234:Device=1234:CC=123456:"	  \
+			    "REV=12:SSVID=1234:SSID=1234:1234567890123456")
+
+struct pci_cma_x509_context {
+	struct pci_dev *pdev;
+	u8 slot;
+	enum OID last_oid;
+	char expected_name[CMA_NAME_MAX];
+	unsigned int expected_len;
+	unsigned int found:1;
+};
+
+int pci_cma_note_oid(void *context, size_t hdrlen, unsigned char tag,
+		     const void *value, size_t vlen)
+{
+	struct pci_cma_x509_context *ctx = context;
+
+	ctx->last_oid = look_up_OID(value, vlen);
+
+	return 0;
+}
+
+int pci_cma_note_san(void *context, size_t hdrlen, unsigned char tag,
+		     const void *value, size_t vlen)
+{
+	struct pci_cma_x509_context *ctx = context;
+
+	/* These aren't the drOIDs we're looking for. */
+	if (ctx->last_oid != OID_CMA)
+		return 0;
+
+	if (tag != ASN1_UTF8STR ||
+	    vlen != ctx->expected_len ||
+	    memcmp(value, ctx->expected_name, vlen) != 0) {
+		pci_err(ctx->pdev, "Leaf certificate of slot %u "
+			"has invalid Subject Alternative Name\n", ctx->slot);
+		return -EINVAL;
+	}
+
+	ctx->found = true;
+
+	return 0;
+}
+
+static unsigned int pci_cma_construct_san(struct pci_dev *pdev, char *name)
+{
+	unsigned int len;
+	u64 serial;
+
+	len = snprintf(name, CMA_NAME_MAX,
+		       "Vendor=%04hx:Device=%04hx:CC=%06x:REV=%02hhx",
+		       pdev->vendor, pdev->device, pdev->class, pdev->revision);
+
+	if (pdev->hdr_type == PCI_HEADER_TYPE_NORMAL)
+		len += snprintf(name + len, CMA_NAME_MAX - len,
+				":SSVID=%04hx:SSID=%04hx",
+				pdev->subsystem_vendor, pdev->subsystem_device);
+
+	serial = pci_get_dsn(pdev);
+	if (serial)
+		len += snprintf(name + len, CMA_NAME_MAX - len,
+				":%016llx", serial);
+
+	return len;
+}
+
+static int pci_cma_validate(struct device *dev, u8 slot,
+			    struct x509_certificate *leaf_cert)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct pci_cma_x509_context ctx;
+	int ret;
+
+	if (!leaf_cert->raw_san) {
+		pci_err(pdev, "Leaf certificate of slot %u "
+			"has no Subject Alternative Name\n", slot);
+		return -EINVAL;
+	}
+
+	ctx.pdev = pdev;
+	ctx.slot = slot;
+	ctx.found = false;
+	ctx.expected_len = pci_cma_construct_san(pdev, ctx.expected_name);
+
+	ret = asn1_ber_decoder(&cma_decoder, &ctx, leaf_cert->raw_san,
+			       leaf_cert->raw_san_size);
+	if (ret == -EBADMSG || ret == -EMSGSIZE)
+		pci_err(pdev, "Leaf certificate of slot %u "
+			"has malformed Subject Alternative Name\n", slot);
+	if (ret < 0)
+		return ret;
+
+	if (!ctx.found) {
+		pci_err(pdev, "Leaf certificate of slot %u "
+			"has no OtherName with CMA OID\n", slot);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 #define PCI_DOE_FEATURE_CMA 1
 
 static ssize_t pci_doe_transport(void *priv, struct device *dev,
@@ -63,7 +184,7 @@ void pci_cma_init(struct pci_dev *pdev)
 
 	pdev->spdm_state = spdm_create(&pdev->dev, pci_doe_transport, doe,
 				       PCI_DOE_MAX_PAYLOAD, pci_cma_keyring,
-				       NULL);
+				       pci_cma_validate);
 	if (!pdev->spdm_state)
 		return;
 
diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h
index 6f9242259edc..44679f0a3fd6 100644
--- a/include/linux/oid_registry.h
+++ b/include/linux/oid_registry.h
@@ -145,6 +145,9 @@ enum OID {
 	OID_id_rsassa_pkcs1_v1_5_with_sha3_384, /* 2.16.840.1.101.3.4.3.15 */
 	OID_id_rsassa_pkcs1_v1_5_with_sha3_512, /* 2.16.840.1.101.3.4.3.16 */
 
+	/* PCI */
+	OID_CMA,			/* 2.23.147 */
+
 	OID__NR
 };
 
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [RFC v2 08/20] PCI/CMA: Reauthenticate devices on reset and resume
  2025-02-27  3:09 [RFC v2 00/20] lib: Rust implementation of SPDM Alistair Francis
                   ` (6 preceding siblings ...)
  2025-02-27  3:09 ` [RFC v2 07/20] PCI/CMA: Validate Subject Alternative Name in certificates Alistair Francis
@ 2025-02-27  3:09 ` Alistair Francis
  2025-02-27  3:09 ` [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated Alistair Francis
                   ` (12 subsequent siblings)
  20 siblings, 0 replies; 47+ messages in thread
From: Alistair Francis @ 2025-02-27  3:09 UTC (permalink / raw)
  To: linux-cxl, linux-kernel, lukas, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm
  Cc: boqun.feng, bjorn3_gh, wilfred.mallawa, aliceryhl, ojeda,
	alistair23, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

From: Lukas Wunner <lukas@wunner.de>

CMA-SPDM state is lost when a device undergoes a Conventional Reset.
(But not a Function Level Reset, PCIe r6.2 sec 6.6.2.)  A D3cold to D0
transition implies a Conventional Reset (PCIe r6.2 sec 5.8).

Thus, reauthenticate devices on resume from D3cold and on recovery from
a Secondary Bus Reset or DPC-induced Hot Reset.

The requirement to reauthenticate devices on resume from system sleep
(and in the future reestablish IDE encryption) is the reason why SPDM
needs to be in-kernel:  During ->resume_noirq, which is the first phase
after system sleep, the PCI core walks down the hierarchy, puts each
device in D0, restores its config space and invokes the driver's
->resume_noirq callback.  The driver is afforded the right to access the
device already during this phase.

To retain this usage model in the face of authentication and encryption,
CMA-SPDM reauthentication and IDE reestablishment must happen during the
->resume_noirq phase, before the driver's first access to the device.
The driver is thus afforded seamless authenticated and encrypted access
until the last moment before suspend and from the first moment after
resume.

During the ->resume_noirq phase, device interrupts are not yet enabled.
It is thus impossible to defer CMA-SPDM reauthentication to a user space
component on an attached disk or on the network, making an in-kernel
SPDM implementation mandatory.

The same catch-22 exists on recovery from a Conventional Reset:  A user
space SPDM implementation might live on a device which underwent reset,
rendering its execution impossible.

Signed-off-by: Lukas Wunner <lukas@wunner.de>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
---
 drivers/pci/cma.c        | 15 +++++++++++++++
 drivers/pci/pci-driver.c |  1 +
 drivers/pci/pci.c        | 12 ++++++++++--
 drivers/pci/pci.h        |  2 ++
 drivers/pci/pcie/err.c   |  3 +++
 5 files changed, 31 insertions(+), 2 deletions(-)

diff --git a/drivers/pci/cma.c b/drivers/pci/cma.c
index e974d489c7a2..f2c435b04b92 100644
--- a/drivers/pci/cma.c
+++ b/drivers/pci/cma.c
@@ -195,6 +195,21 @@ void pci_cma_init(struct pci_dev *pdev)
 	spdm_authenticate(pdev->spdm_state);
 }
 
+/**
+ * pci_cma_reauthenticate() - Perform CMA-SPDM authentication again
+ * @pdev: Device to reauthenticate
+ *
+ * Can be called by drivers after device identity has mutated,
+ * e.g. after downloading firmware to an FPGA device.
+ */
+void pci_cma_reauthenticate(struct pci_dev *pdev)
+{
+	if (!pdev->spdm_state)
+		return;
+
+	spdm_authenticate(pdev->spdm_state);
+}
+
 void pci_cma_destroy(struct pci_dev *pdev)
 {
 	if (!pdev->spdm_state)
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index f57ea36d125d..6dd24ae2d1ee 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -566,6 +566,7 @@ static void pci_pm_default_resume_early(struct pci_dev *pci_dev)
 	pci_pm_power_up_and_verify_state(pci_dev);
 	pci_restore_state(pci_dev);
 	pci_pme_restore(pci_dev);
+	pci_cma_reauthenticate(pci_dev);
 }
 
 static void pci_pm_bridge_power_up_actions(struct pci_dev *pci_dev)
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 869d204a70a3..b462bab597f7 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -5074,8 +5074,16 @@ static int pci_reset_bus_function(struct pci_dev *dev, bool probe)
 
 	rc = pci_dev_reset_slot_function(dev, probe);
 	if (rc != -ENOTTY)
-		return rc;
-	return pci_parent_bus_reset(dev, probe);
+		goto done;
+
+	rc = pci_parent_bus_reset(dev, probe);
+
+done:
+	/* CMA-SPDM state is lost upon a Conventional Reset */
+	if (!probe)
+		pci_cma_reauthenticate(dev);
+
+	return rc;
 }
 
 static int cxl_reset_bus_function(struct pci_dev *dev, bool probe)
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 69ba2ae9a0cd..fa6e3ae10b67 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -451,9 +451,11 @@ static inline void pci_doe_disconnected(struct pci_dev *pdev) { }
 #ifdef CONFIG_PCI_CMA
 void pci_cma_init(struct pci_dev *pdev);
 void pci_cma_destroy(struct pci_dev *pdev);
+void pci_cma_reauthenticate(struct pci_dev *pdev);
 #else
 static inline void pci_cma_init(struct pci_dev *pdev) { }
 static inline void pci_cma_destroy(struct pci_dev *pdev) { }
+static inline void pci_cma_reauthenticate(struct pci_dev *pdev) { }
 #endif
 
 #ifdef CONFIG_PCI_NPEM
diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c
index 31090770fffc..0028582f0590 100644
--- a/drivers/pci/pcie/err.c
+++ b/drivers/pci/pcie/err.c
@@ -133,6 +133,9 @@ static int report_slot_reset(struct pci_dev *dev, void *data)
 	pci_ers_result_t vote, *result = data;
 	const struct pci_error_handlers *err_handler;
 
+	/* CMA-SPDM state is lost upon a Conventional Reset */
+	pci_cma_reauthenticate(dev);
+
 	device_lock(&dev->dev);
 	pdrv = dev->driver;
 	if (!pdrv || !pdrv->err_handler || !pdrv->err_handler->slot_reset)
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated
  2025-02-27  3:09 [RFC v2 00/20] lib: Rust implementation of SPDM Alistair Francis
                   ` (7 preceding siblings ...)
  2025-02-27  3:09 ` [RFC v2 08/20] PCI/CMA: Reauthenticate devices on reset and resume Alistair Francis
@ 2025-02-27  3:09 ` Alistair Francis
  2025-02-27 11:16   ` Greg KH
  2025-02-27  3:09 ` [RFC v2 10/20] lib: rspdm: Support SPDM get_version Alistair Francis
                   ` (11 subsequent siblings)
  20 siblings, 1 reply; 47+ messages in thread
From: Alistair Francis @ 2025-02-27  3:09 UTC (permalink / raw)
  To: linux-cxl, linux-kernel, lukas, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm
  Cc: boqun.feng, bjorn3_gh, wilfred.mallawa, aliceryhl, ojeda,
	alistair23, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

From: Lukas Wunner <lukas@wunner.de>

The PCI core has just been amended to authenticate CMA-capable devices
on enumeration and store the result in an "authenticated" bit in struct
pci_dev->spdm_state.

Expose the bit to user space through an eponymous sysfs attribute.

Allow user space to trigger reauthentication (e.g. after it has updated
the CMA keyring) by writing to the sysfs attribute.

Implement the attribute in the SPDM library so that other bus types
besides PCI may take advantage of it.  They just need to add
spdm_attr_group to the attribute groups of their devices and amend the
dev_to_spdm_state() helper which retrieves the spdm_state for a given
device.

The helper may return an ERR_PTR if it couldn't be determined whether
SPDM is supported by the device.  The sysfs attribute is visible in that
case but returns an error on access.  This prevents downgrade attacks
where an attacker disturbs memory allocation or DOE communication
in order to create the appearance that SPDM is unsupported.

Subject to further discussion, a future commit might add a user-defined
policy to forbid driver binding to devices which failed authentication,
similar to the "authorized" attribute for USB.

Alternatively, authentication success might be signaled to user space
through a uevent, whereupon it may bind a (blacklisted) driver.
A uevent signaling authentication failure might similarly cause user
space to unbind or outright remove the potentially malicious device.

Traffic from devices which failed authentication could also be filtered
through ACS I/O Request Blocking Enable (PCIe r6.2 sec 7.7.11.3) or
through Link Disable (PCIe r6.2 sec 7.5.3.7).  Unlike an IOMMU, that
will not only protect the host, but also prevent malicious peer-to-peer
traffic to other devices.

Signed-off-by: Lukas Wunner <lukas@wunner.de>
[ Changes by AF:
 - Drop lib/spdm changes and replace with Rust implementation
]
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
---
 Documentation/ABI/testing/sysfs-devices-spdm | 31 +++++++
 MAINTAINERS                                  |  3 +-
 drivers/pci/cma.c                            | 12 ++-
 drivers/pci/doe.c                            |  2 +
 drivers/pci/pci-sysfs.c                      |  3 +
 drivers/pci/pci.h                            |  5 +
 include/linux/pci.h                          | 12 +++
 lib/rspdm/Makefile                           |  1 +
 lib/rspdm/lib.rs                             |  1 +
 lib/rspdm/req-sysfs.c                        | 97 ++++++++++++++++++++
 lib/rspdm/sysfs.rs                           | 28 ++++++
 11 files changed, 190 insertions(+), 5 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-devices-spdm
 create mode 100644 lib/rspdm/req-sysfs.c
 create mode 100644 lib/rspdm/sysfs.rs

diff --git a/Documentation/ABI/testing/sysfs-devices-spdm b/Documentation/ABI/testing/sysfs-devices-spdm
new file mode 100644
index 000000000000..2d6e5d513231
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-devices-spdm
@@ -0,0 +1,31 @@
+What:		/sys/devices/.../authenticated
+Date:		June 2024
+Contact:	Lukas Wunner <lukas@wunner.de>
+Description:
+		This file contains 1 if the device authenticated successfully
+		with SPDM (Security Protocol and Data Model).  It contains 0 if
+		the device failed authentication (and may thus be malicious).
+
+		Writing "re" to this file causes reauthentication.
+		That may be opportune after updating the device keyring.
+		The device keyring of the PCI bus is named ".cma"
+		(Component Measurement and Authentication).
+
+		Reauthentication may also be necessary after device identity
+		has mutated, e.g. after downloading firmware to an FPGA device.
+
+		The file is not visible if authentication is unsupported
+		by the device.
+
+		If the kernel could not determine whether authentication is
+		supported because memory was low or communication with the
+		device was not working, the file is visible but accessing it
+		fails with error code ENOTTY.
+
+		This prevents downgrade attacks where an attacker consumes
+		memory or disturbs communication in order to create the
+		appearance that a device does not support authentication.
+
+		The reason why authentication support could not be determined
+		is apparent from "dmesg".  To re-probe authentication support
+		of PCI devices, exercise the "remove" and "rescan" attributes.
diff --git a/MAINTAINERS b/MAINTAINERS
index abb3b603299f..03e1076f915a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21385,7 +21385,8 @@ L:	linux-cxl@vger.kernel.org
 L:	linux-pci@vger.kernel.org
 S:	Maintained
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/devsec/spdm.git
-F:	drivers/pci/cma.c
+F:	Documentation/ABI/testing/sysfs-devices-spdm
+F:	drivers/pci/cma*
 F:	include/linux/spdm.h
 F:	lib/rspdm/
 
diff --git a/drivers/pci/cma.c b/drivers/pci/cma.c
index f2c435b04b92..59558714f143 100644
--- a/drivers/pci/cma.c
+++ b/drivers/pci/cma.c
@@ -171,8 +171,10 @@ void pci_cma_init(struct pci_dev *pdev)
 {
 	struct pci_doe_mb *doe;
 
-	if (IS_ERR(pci_cma_keyring))
+	if (IS_ERR(pci_cma_keyring)) {
+		pdev->spdm_state = ERR_PTR(-ENOTTY);
 		return;
+	}
 
 	if (!pci_is_pcie(pdev))
 		return;
@@ -185,8 +187,10 @@ void pci_cma_init(struct pci_dev *pdev)
 	pdev->spdm_state = spdm_create(&pdev->dev, pci_doe_transport, doe,
 				       PCI_DOE_MAX_PAYLOAD, pci_cma_keyring,
 				       pci_cma_validate);
-	if (!pdev->spdm_state)
+	if (!pdev->spdm_state) {
+		pdev->spdm_state = ERR_PTR(-ENOTTY);
 		return;
+	}
 
 	/*
 	 * Keep spdm_state allocated even if initial authentication fails
@@ -204,7 +208,7 @@ void pci_cma_init(struct pci_dev *pdev)
  */
 void pci_cma_reauthenticate(struct pci_dev *pdev)
 {
-	if (!pdev->spdm_state)
+	if (IS_ERR_OR_NULL(pdev->spdm_state))
 		return;
 
 	spdm_authenticate(pdev->spdm_state);
@@ -212,7 +216,7 @@ void pci_cma_reauthenticate(struct pci_dev *pdev)
 
 void pci_cma_destroy(struct pci_dev *pdev)
 {
-	if (!pdev->spdm_state)
+	if (IS_ERR_OR_NULL(pdev->spdm_state))
 		return;
 
 	spdm_destroy(pdev->spdm_state);
diff --git a/drivers/pci/doe.c b/drivers/pci/doe.c
index 8ea37698082a..e4b609f613da 100644
--- a/drivers/pci/doe.c
+++ b/drivers/pci/doe.c
@@ -706,6 +706,7 @@ void pci_doe_init(struct pci_dev *pdev)
 		if (IS_ERR(doe_mb)) {
 			pci_err(pdev, "[%x] failed to create mailbox: %ld\n",
 				offset, PTR_ERR(doe_mb));
+			pci_cma_disable(pdev);
 			continue;
 		}
 
@@ -714,6 +715,7 @@ void pci_doe_init(struct pci_dev *pdev)
 			pci_err(pdev, "[%x] failed to insert mailbox: %d\n",
 				offset, rc);
 			pci_doe_destroy_mb(doe_mb);
+			pci_cma_disable(pdev);
 		}
 	}
 }
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index b46ce1a2c554..0645935fbcaf 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -1804,6 +1804,9 @@ const struct attribute_group *pci_dev_attr_groups[] = {
 #endif
 #ifdef CONFIG_PCIEASPM
 	&aspm_ctrl_attr_group,
+#endif
+#ifdef CONFIG_PCI_CMA
+	&spdm_attr_group,
 #endif
 	NULL,
 };
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index fa6e3ae10b67..ad646b69a9c7 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -452,10 +452,15 @@ static inline void pci_doe_disconnected(struct pci_dev *pdev) { }
 void pci_cma_init(struct pci_dev *pdev);
 void pci_cma_destroy(struct pci_dev *pdev);
 void pci_cma_reauthenticate(struct pci_dev *pdev);
+static inline void pci_cma_disable(struct pci_dev *pdev)
+{
+	pdev->spdm_state = ERR_PTR(-ENOTTY);
+}
 #else
 static inline void pci_cma_init(struct pci_dev *pdev) { }
 static inline void pci_cma_destroy(struct pci_dev *pdev) { }
 static inline void pci_cma_reauthenticate(struct pci_dev *pdev) { }
+static inline void pci_cma_disable(struct pci_dev *pdev) { }
 #endif
 
 #ifdef CONFIG_PCI_NPEM
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 50c43546a32b..696f5ce971f4 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -2704,6 +2704,18 @@ static inline bool pci_is_thunderbolt_attached(struct pci_dev *pdev)
 void pci_uevent_ers(struct pci_dev *pdev, enum  pci_ers_result err_type);
 #endif
 
+#ifdef CONFIG_PCI_CMA
+static inline struct spdm_state *pci_dev_to_spdm_state(struct pci_dev *pdev)
+{
+	return pdev->spdm_state;
+}
+#else
+static inline struct spdm_state *pci_dev_to_spdm_state(struct pci_dev *pdev)
+{
+	return NULL;
+}
+#endif
+
 #include <linux/dma-mapping.h>
 
 #define pci_printk(level, pdev, fmt, arg...) \
diff --git a/lib/rspdm/Makefile b/lib/rspdm/Makefile
index 1f62ee2a882d..f15b1437196b 100644
--- a/lib/rspdm/Makefile
+++ b/lib/rspdm/Makefile
@@ -8,3 +8,4 @@
 obj-$(CONFIG_RSPDM) += spdm.o
 
 spdm-y := lib.o
+spdm-$(CONFIG_SYSFS) += req-sysfs.o
diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs
index 2bb716140e0a..24064d3c4669 100644
--- a/lib/rspdm/lib.rs
+++ b/lib/rspdm/lib.rs
@@ -24,6 +24,7 @@
 
 mod consts;
 mod state;
+pub mod sysfs;
 mod validator;
 
 /// spdm_create() - Allocate SPDM session
diff --git a/lib/rspdm/req-sysfs.c b/lib/rspdm/req-sysfs.c
new file mode 100644
index 000000000000..11bacb04f08f
--- /dev/null
+++ b/lib/rspdm/req-sysfs.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Rust implementation of the DMTF Security Protocol and Data Model (SPDM)
+ * https://www.dmtf.org/dsp/DSP0274
+ *
+ * Requester role: sysfs interface
+ *
+ * Copyright (C) 2023-24 Intel Corporation
+ * Copyright (C) 2024 Western Digital
+ */
+
+#include <linux/pci.h>
+
+int rust_authenticated_show(void *spdm_state, char *buf);
+
+/**
+ * dev_to_spdm_state() - Retrieve SPDM session state for given device
+ *
+ * @dev: Responder device
+ *
+ * Returns a pointer to the device's SPDM session state,
+ *	   %NULL if the device doesn't have one or
+ *	   %ERR_PTR if it couldn't be determined whether SPDM is supported.
+ *
+ * In the %ERR_PTR case, attributes are visible but return an error on access.
+ * This prevents downgrade attacks where an attacker disturbs memory allocation
+ * or communication with the device in order to create the appearance that SPDM
+ * is unsupported.  E.g. with PCI devices, the attacker may foil CMA or DOE
+ * initialization by simply hogging memory.
+ */
+static void *dev_to_spdm_state(struct device *dev)
+{
+	if (dev_is_pci(dev))
+		return pci_dev_to_spdm_state(to_pci_dev(dev));
+
+	/* Insert mappers for further bus types here. */
+
+	return NULL;
+}
+
+/* authenticated attribute */
+
+static umode_t spdm_attrs_are_visible(struct kobject *kobj,
+				      struct attribute *a, int n)
+{
+	struct device *dev = kobj_to_dev(kobj);
+	void *spdm_state = dev_to_spdm_state(dev);
+
+	if (IS_ERR_OR_NULL(spdm_state))
+		return SYSFS_GROUP_INVISIBLE;
+
+	return a->mode;
+}
+
+static ssize_t authenticated_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count)
+{
+	void *spdm_state = dev_to_spdm_state(dev);
+	int rc;
+
+	if (IS_ERR_OR_NULL(spdm_state))
+		return PTR_ERR(spdm_state);
+
+	if (sysfs_streq(buf, "re")) {
+		rc = spdm_authenticate(spdm_state);
+		if (rc)
+			return rc;
+	} else {
+		return -EINVAL;
+	}
+
+	return count;
+}
+
+static ssize_t authenticated_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	void *spdm_state = dev_to_spdm_state(dev);
+
+	if (IS_ERR_OR_NULL(spdm_state))
+		return PTR_ERR(spdm_state);
+
+	return rust_authenticated_show(spdm_state, buf);
+}
+static DEVICE_ATTR_RW(authenticated);
+
+static struct attribute *spdm_attrs[] = {
+	&dev_attr_authenticated.attr,
+	NULL
+};
+
+const struct attribute_group spdm_attr_group = {
+	.attrs = spdm_attrs,
+	.is_visible = spdm_attrs_are_visible,
+};
diff --git a/lib/rspdm/sysfs.rs b/lib/rspdm/sysfs.rs
new file mode 100644
index 000000000000..50901c55b8f7
--- /dev/null
+++ b/lib/rspdm/sysfs.rs
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2024 Western Digital
+
+//! Rust sysfs helper functions
+//!
+//! Rust implementation of the DMTF Security Protocol and Data Model (SPDM)
+//! <https://www.dmtf.org/dsp/DSP0274>
+
+use crate::SpdmState;
+use kernel::prelude::*;
+use kernel::{bindings, fmt, str::CString};
+
+/// Helper function for the sysfs `authenticated_show()`.
+#[no_mangle]
+pub extern "C" fn rust_authenticated_show(spdm_state: *mut SpdmState, buf: *mut u8) -> isize {
+    // SAFETY: The opaque pointer will be directly from the `spdm_create()`
+    // function, so we can safely reconstruct it.
+    let state = unsafe { KBox::from_raw(spdm_state) };
+
+    let fmt = match CString::try_from_fmt(fmt!("{}\n", state.authenticated)) {
+        Ok(f) => f,
+        Err(_e) => return 0,
+    };
+
+    // SAFETY: Calling a kernel C function with valid arguments
+    unsafe { bindings::sysfs_emit(buf, fmt.as_char_ptr()) as isize }
+}
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [RFC v2 10/20] lib: rspdm: Support SPDM get_version
  2025-02-27  3:09 [RFC v2 00/20] lib: Rust implementation of SPDM Alistair Francis
                   ` (8 preceding siblings ...)
  2025-02-27  3:09 ` [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated Alistair Francis
@ 2025-02-27  3:09 ` Alistair Francis
  2025-02-27  3:09 ` [RFC v2 11/20] lib: rspdm: Support SPDM get_capabilities Alistair Francis
                   ` (10 subsequent siblings)
  20 siblings, 0 replies; 47+ messages in thread
From: Alistair Francis @ 2025-02-27  3:09 UTC (permalink / raw)
  To: linux-cxl, linux-kernel, lukas, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm
  Cc: boqun.feng, bjorn3_gh, wilfred.mallawa, aliceryhl, ojeda,
	alistair23, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

Support the GET_VERSION SPDM command.

Signed-off-by: Alistair Francis <alistair@alistair23.me>
---
 lib/rspdm/consts.rs    | 17 +++++++++++
 lib/rspdm/lib.rs       |  6 +++-
 lib/rspdm/state.rs     | 57 +++++++++++++++++++++++++++++++++--
 lib/rspdm/validator.rs | 67 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 143 insertions(+), 4 deletions(-)

diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs
index 311e34c7fae7..da7b0810f103 100644
--- a/lib/rspdm/consts.rs
+++ b/lib/rspdm/consts.rs
@@ -7,6 +7,20 @@
 //! Rust implementation of the DMTF Security Protocol and Data Model (SPDM)
 //! <https://www.dmtf.org/dsp/DSP0274>
 
+use crate::validator::SpdmHeader;
+use core::mem;
+
+/* SPDM versions supported by this implementation */
+pub(crate) const SPDM_VER_10: u8 = 0x10;
+#[allow(dead_code)]
+pub(crate) const SPDM_VER_11: u8 = 0x11;
+#[allow(dead_code)]
+pub(crate) const SPDM_VER_12: u8 = 0x12;
+pub(crate) const SPDM_VER_13: u8 = 0x13;
+
+pub(crate) const SPDM_MIN_VER: u8 = SPDM_VER_10;
+pub(crate) const SPDM_MAX_VER: u8 = SPDM_VER_13;
+
 pub(crate) const SPDM_REQ: u8 = 0x80;
 pub(crate) const SPDM_ERROR: u8 = 0x7f;
 
@@ -37,3 +51,6 @@ pub(crate) enum SpdmErrorCode {
     NoPendingRequests = 0x45,
     VendorDefinedError = 0xff,
 }
+
+pub(crate) const SPDM_GET_VERSION: u8 = 0x84;
+pub(crate) const SPDM_GET_VERSION_LEN: usize = mem::size_of::<SpdmHeader>() + 255;
diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs
index 24064d3c4669..f8cb9766ed62 100644
--- a/lib/rspdm/lib.rs
+++ b/lib/rspdm/lib.rs
@@ -109,7 +109,11 @@
 /// Return 0 on success or a negative errno.  In particular, -EPROTONOSUPPORT
 /// indicates authentication is not supported by the device.
 #[no_mangle]
-pub unsafe extern "C" fn spdm_authenticate(_state: &'static mut SpdmState) -> c_int {
+pub unsafe extern "C" fn spdm_authenticate(state: &'static mut SpdmState) -> c_int {
+    if let Err(e) = state.get_version() {
+        return e.to_errno() as c_int;
+    }
+
     0
 }
 
diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs
index 9ebd87603454..dd1ccdfd166a 100644
--- a/lib/rspdm/state.rs
+++ b/lib/rspdm/state.rs
@@ -8,6 +8,7 @@
 //! <https://www.dmtf.org/dsp/DSP0274>
 
 use core::ffi::c_void;
+use core::slice::from_raw_parts_mut;
 use kernel::prelude::*;
 use kernel::{
     bindings,
@@ -15,8 +16,10 @@
     validate::Untrusted,
 };
 
-use crate::consts::{SpdmErrorCode, SPDM_ERROR, SPDM_REQ};
-use crate::validator::{SpdmErrorRsp, SpdmHeader};
+use crate::consts::{
+    SpdmErrorCode, SPDM_ERROR, SPDM_GET_VERSION_LEN, SPDM_MAX_VER, SPDM_MIN_VER, SPDM_REQ,
+};
+use crate::validator::{GetVersionReq, GetVersionRsp, SpdmErrorRsp, SpdmHeader};
 
 /// The current SPDM session state for a device. Based on the
 /// C `struct spdm_state`.
@@ -65,7 +68,7 @@ pub(crate) fn new(
             transport_sz,
             keyring,
             validate,
-            version: 0x10,
+            version: SPDM_MIN_VER,
             authenticated: false,
         }
     }
@@ -222,4 +225,52 @@ pub(crate) fn spdm_exchange(
 
         Ok(length)
     }
+
+    /// Negoiate a supported SPDM version and store the information
+    /// in the `SpdmState`.
+    pub(crate) fn get_version(&mut self) -> Result<(), Error> {
+        let mut request = GetVersionReq::default();
+        request.version = self.version;
+
+        // SAFETY: `request` is repr(C) and packed, so we can convert it to a slice
+        let request_buf = unsafe {
+            from_raw_parts_mut(
+                &mut request as *mut _ as *mut u8,
+                core::mem::size_of::<GetVersionReq>(),
+            )
+        };
+
+        let mut response_vec: KVec<u8> = KVec::with_capacity(SPDM_GET_VERSION_LEN, GFP_KERNEL)?;
+        // SAFETY: `request` is repr(C) and packed, so we can convert it to a slice
+        let response_buf =
+            unsafe { from_raw_parts_mut(response_vec.as_mut_ptr(), SPDM_GET_VERSION_LEN) };
+
+        let rc = self.spdm_exchange(request_buf, response_buf)?;
+
+        // SAFETY: `rc` bytes where inserted to the raw pointer by spdm_exchange
+        unsafe { response_vec.set_len(rc as usize) };
+
+        let response: &mut GetVersionRsp = Untrusted::new_mut(&mut response_vec).validate_mut()?;
+
+        let mut foundver = false;
+        for i in 0..response.version_number_entry_count {
+            // Creating a reference on a packed struct will result in
+            // undefined behaviour, so we operate on the raw data directly
+            let unaligned = core::ptr::addr_of_mut!(response.version_number_entries) as *mut u16;
+            let addr = unaligned.wrapping_add(i as usize);
+            let version = (unsafe { core::ptr::read_unaligned::<u16>(addr) } >> 8) as u8;
+
+            if version >= self.version && version <= SPDM_MAX_VER {
+                self.version = version;
+                foundver = true;
+            }
+        }
+
+        if !foundver {
+            pr_err!("No common supported version\n");
+            to_result(-(bindings::EPROTO as i32))?;
+        }
+
+        Ok(())
+    }
 }
diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs
index a0a3a2f46952..f69be6aa6280 100644
--- a/lib/rspdm/validator.rs
+++ b/lib/rspdm/validator.rs
@@ -7,6 +7,7 @@
 //! Rust implementation of the DMTF Security Protocol and Data Model (SPDM)
 //! <https://www.dmtf.org/dsp/DSP0274>
 
+use crate::bindings::{__IncompleteArrayField, __le16};
 use crate::consts::SpdmErrorCode;
 use core::mem;
 use kernel::prelude::*;
@@ -15,6 +16,8 @@
     validate::{Unvalidated, Validate},
 };
 
+use crate::consts::SPDM_GET_VERSION;
+
 #[repr(C, packed)]
 pub(crate) struct SpdmHeader {
     pub(crate) version: u8,
@@ -64,3 +67,67 @@ pub(crate) struct SpdmErrorRsp {
     pub(crate) error_code: SpdmErrorCode,
     pub(crate) error_data: u8,
 }
+
+#[repr(C, packed)]
+pub(crate) struct GetVersionReq {
+    pub(crate) version: u8,
+    pub(crate) code: u8,
+    pub(crate) param1: u8,
+    pub(crate) param2: u8,
+}
+
+impl Default for GetVersionReq {
+    fn default() -> Self {
+        GetVersionReq {
+            version: 0,
+            code: SPDM_GET_VERSION,
+            param1: 0,
+            param2: 0,
+        }
+    }
+}
+
+#[repr(C, packed)]
+pub(crate) struct GetVersionRsp {
+    pub(crate) version: u8,
+    pub(crate) code: u8,
+    param1: u8,
+    param2: u8,
+    reserved: u8,
+    pub(crate) version_number_entry_count: u8,
+    pub(crate) version_number_entries: __IncompleteArrayField<__le16>,
+}
+
+impl Validate<&mut Unvalidated<KVec<u8>>> for &mut GetVersionRsp {
+    type Err = Error;
+
+    fn validate(unvalidated: &mut Unvalidated<KVec<u8>>) -> Result<Self, Self::Err> {
+        let raw = unvalidated.raw_mut();
+        if raw.len() < mem::size_of::<GetVersionRsp>() {
+            return Err(EINVAL);
+        }
+
+        let version_number_entries = *(raw.get(5).ok_or(ENOMEM))? as usize;
+        let total_expected_size = version_number_entries * 2 + 6;
+        if raw.len() < total_expected_size {
+            return Err(EINVAL);
+        }
+
+        let ptr = raw.as_mut_ptr();
+        // CAST: `GetVersionRsp` only contains integers and has `repr(C)`.
+        let ptr = ptr.cast::<GetVersionRsp>();
+        // SAFETY: `ptr` came from a reference and the cast above is valid.
+        let rsp: &mut GetVersionRsp = unsafe { &mut *ptr };
+
+        // Creating a reference on a packed struct will result in
+        // undefined behaviour, so we operate on the raw data directly
+        let unaligned = core::ptr::addr_of_mut!(rsp.version_number_entries) as *mut u16;
+        for version_offset in 0..version_number_entries {
+            let addr = unaligned.wrapping_add(version_offset);
+            let version = unsafe { core::ptr::read_unaligned::<u16>(addr) };
+            unsafe { core::ptr::write_unaligned::<u16>(addr, version.to_le()) }
+        }
+
+        Ok(rsp)
+    }
+}
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [RFC v2 11/20] lib: rspdm: Support SPDM get_capabilities
  2025-02-27  3:09 [RFC v2 00/20] lib: Rust implementation of SPDM Alistair Francis
                   ` (9 preceding siblings ...)
  2025-02-27  3:09 ` [RFC v2 10/20] lib: rspdm: Support SPDM get_version Alistair Francis
@ 2025-02-27  3:09 ` Alistair Francis
  2025-02-27  3:09 ` [RFC v2 12/20] lib: rspdm: Support SPDM negotiate_algorithms Alistair Francis
                   ` (9 subsequent siblings)
  20 siblings, 0 replies; 47+ messages in thread
From: Alistair Francis @ 2025-02-27  3:09 UTC (permalink / raw)
  To: linux-cxl, linux-kernel, lukas, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm
  Cc: boqun.feng, bjorn3_gh, wilfred.mallawa, aliceryhl, ojeda,
	alistair23, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

Support the GET_CAPABILITIES SPDM command.

Signed-off-by: Alistair Francis <alistair@alistair23.me>
---
 lib/rspdm/consts.rs    | 18 ++++++++--
 lib/rspdm/lib.rs       |  4 +++
 lib/rspdm/state.rs     | 67 +++++++++++++++++++++++++++++++++++--
 lib/rspdm/validator.rs | 76 +++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 160 insertions(+), 5 deletions(-)

diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs
index da7b0810f103..8a58351bf268 100644
--- a/lib/rspdm/consts.rs
+++ b/lib/rspdm/consts.rs
@@ -12,9 +12,7 @@
 
 /* SPDM versions supported by this implementation */
 pub(crate) const SPDM_VER_10: u8 = 0x10;
-#[allow(dead_code)]
 pub(crate) const SPDM_VER_11: u8 = 0x11;
-#[allow(dead_code)]
 pub(crate) const SPDM_VER_12: u8 = 0x12;
 pub(crate) const SPDM_VER_13: u8 = 0x13;
 
@@ -54,3 +52,19 @@ pub(crate) enum SpdmErrorCode {
 
 pub(crate) const SPDM_GET_VERSION: u8 = 0x84;
 pub(crate) const SPDM_GET_VERSION_LEN: usize = mem::size_of::<SpdmHeader>() + 255;
+
+pub(crate) const SPDM_GET_CAPABILITIES: u8 = 0xe1;
+pub(crate) const SPDM_MIN_DATA_TRANSFER_SIZE: u32 = 42;
+
+// SPDM cryptographic timeout of this implementation:
+// Assume calculations may take up to 1 sec on a busy machine, which equals
+// roughly 1 << 20.  That's within the limits mandated for responders by 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.
+pub(crate) const SPDM_CTEXPONENT: u8 = 20;
+
+pub(crate) const SPDM_CERT_CAP: u32 = 1 << 1;
+pub(crate) const SPDM_CHAL_CAP: u32 = 1 << 2;
+
+pub(crate) const SPDM_REQ_CAPS: u32 = SPDM_CERT_CAP | SPDM_CHAL_CAP;
+pub(crate) const SPDM_RSP_MIN_CAPS: u32 = SPDM_CERT_CAP | SPDM_CHAL_CAP;
diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs
index f8cb9766ed62..fe36333b2d24 100644
--- a/lib/rspdm/lib.rs
+++ b/lib/rspdm/lib.rs
@@ -114,6 +114,10 @@
         return e.to_errno() as c_int;
     }
 
+    if let Err(e) = state.get_capabilities() {
+        return e.to_errno() as c_int;
+    }
+
     0
 }
 
diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs
index dd1ccdfd166a..288a77b3ab56 100644
--- a/lib/rspdm/state.rs
+++ b/lib/rspdm/state.rs
@@ -17,9 +17,12 @@
 };
 
 use crate::consts::{
-    SpdmErrorCode, SPDM_ERROR, SPDM_GET_VERSION_LEN, SPDM_MAX_VER, SPDM_MIN_VER, SPDM_REQ,
+    SpdmErrorCode, SPDM_ERROR, SPDM_GET_VERSION_LEN, SPDM_MAX_VER, SPDM_MIN_DATA_TRANSFER_SIZE,
+    SPDM_MIN_VER, SPDM_REQ, SPDM_RSP_MIN_CAPS, SPDM_VER_10, SPDM_VER_11, SPDM_VER_12,
+};
+use crate::validator::{
+    GetCapabilitiesReq, GetCapabilitiesRsp, GetVersionReq, GetVersionRsp, SpdmErrorRsp, SpdmHeader,
 };
-use crate::validator::{GetVersionReq, GetVersionRsp, SpdmErrorRsp, SpdmHeader};
 
 /// The current SPDM session state for a device. Based on the
 /// C `struct spdm_state`.
@@ -35,6 +38,8 @@
 ///
 /// `version`: Maximum common supported version of requester and responder.
 ///  Negotiated during GET_VERSION exchange.
+/// @rsp_caps: Cached capabilities of responder.
+///  Received during GET_CAPABILITIES exchange.
 ///
 /// `authenticated`: Whether device was authenticated successfully.
 #[expect(dead_code)]
@@ -48,6 +53,7 @@ pub struct SpdmState {
 
     // Negotiated state
     pub(crate) version: u8,
+    pub(crate) rsp_caps: u32,
 
     pub(crate) authenticated: bool,
 }
@@ -69,6 +75,7 @@ pub(crate) fn new(
             keyring,
             validate,
             version: SPDM_MIN_VER,
+            rsp_caps: 0,
             authenticated: false,
         }
     }
@@ -273,4 +280,60 @@ pub(crate) fn get_version(&mut self) -> Result<(), Error> {
 
         Ok(())
     }
+
+    /// Obtain the supported capabilities from an SPDM session and store the
+    /// information in the `SpdmState`.
+    pub(crate) fn get_capabilities(&mut self) -> Result<(), Error> {
+        let mut request = GetCapabilitiesReq::default();
+        request.version = self.version;
+
+        let (req_sz, rsp_sz) = match self.version {
+            SPDM_VER_10 => (4, 8),
+            SPDM_VER_11 => (8, 8),
+            _ => {
+                request.data_transfer_size = self.transport_sz.to_le();
+                request.max_spdm_msg_size = request.data_transfer_size;
+
+                (
+                    core::mem::size_of::<GetCapabilitiesReq>(),
+                    core::mem::size_of::<GetCapabilitiesRsp>(),
+                )
+            }
+        };
+
+        // SAFETY: `request` is repr(C) and packed, so we can convert it to a slice
+        let request_buf = unsafe { from_raw_parts_mut(&mut request as *mut _ as *mut u8, req_sz) };
+
+        let mut response_vec: KVec<u8> = KVec::with_capacity(rsp_sz, GFP_KERNEL)?;
+        // SAFETY: `request` is repr(C) and packed, so we can convert it to a slice
+        let response_buf = unsafe { from_raw_parts_mut(response_vec.as_mut_ptr(), rsp_sz) };
+
+        let rc = self.spdm_exchange(request_buf, response_buf)?;
+
+        if rc < (rsp_sz as i32) {
+            pr_err!("Truncated capabilities response\n");
+            to_result(-(bindings::EIO as i32))?;
+        }
+
+        // SAFETY: `rc` bytes where inserted to the raw pointer by spdm_exchange
+        unsafe { response_vec.set_len(rc as usize) };
+
+        let response: &mut GetCapabilitiesRsp =
+            Untrusted::new_mut(&mut response_vec).validate_mut()?;
+
+        self.rsp_caps = u32::from_le(response.flags);
+        if (self.rsp_caps & SPDM_RSP_MIN_CAPS) != SPDM_RSP_MIN_CAPS {
+            to_result(-(bindings::EPROTONOSUPPORT as i32))?;
+        }
+
+        if self.version >= SPDM_VER_12 {
+            if response.data_transfer_size < SPDM_MIN_DATA_TRANSFER_SIZE {
+                pr_err!("Malformed capabilities response\n");
+                to_result(-(bindings::EPROTO as i32))?;
+            }
+            self.transport_sz = self.transport_sz.min(response.data_transfer_size);
+        }
+
+        Ok(())
+    }
 }
diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs
index f69be6aa6280..cd792c46767a 100644
--- a/lib/rspdm/validator.rs
+++ b/lib/rspdm/validator.rs
@@ -16,7 +16,7 @@
     validate::{Unvalidated, Validate},
 };
 
-use crate::consts::SPDM_GET_VERSION;
+use crate::consts::{SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, SPDM_GET_VERSION, SPDM_REQ_CAPS};
 
 #[repr(C, packed)]
 pub(crate) struct SpdmHeader {
@@ -131,3 +131,77 @@ fn validate(unvalidated: &mut Unvalidated<KVec<u8>>) -> Result<Self, Self::Err>
         Ok(rsp)
     }
 }
+
+#[repr(C, packed)]
+pub(crate) struct GetCapabilitiesReq {
+    pub(crate) version: u8,
+    pub(crate) code: u8,
+    pub(crate) param1: u8,
+    pub(crate) param2: u8,
+
+    reserved1: u8,
+    pub(crate) ctexponent: u8,
+    reserved2: u16,
+
+    pub(crate) flags: u32,
+
+    /* End of SPDM 1.1 structure */
+    pub(crate) data_transfer_size: u32,
+    pub(crate) max_spdm_msg_size: u32,
+}
+
+impl Default for GetCapabilitiesReq {
+    fn default() -> Self {
+        GetCapabilitiesReq {
+            version: 0,
+            code: SPDM_GET_CAPABILITIES,
+            param1: 0,
+            param2: 0,
+            reserved1: 0,
+            ctexponent: SPDM_CTEXPONENT,
+            reserved2: 0,
+            flags: (SPDM_REQ_CAPS as u32).to_le(),
+            data_transfer_size: 0,
+            max_spdm_msg_size: 0,
+        }
+    }
+}
+
+#[repr(C, packed)]
+pub(crate) struct GetCapabilitiesRsp {
+    pub(crate) version: u8,
+    pub(crate) code: u8,
+    pub(crate) param1: u8,
+    pub(crate) param2: u8,
+
+    reserved1: u8,
+    pub(crate) ctexponent: u8,
+    reserved2: u16,
+
+    pub(crate) flags: u32,
+
+    /* End of SPDM 1.1 structure */
+    pub(crate) data_transfer_size: u32,
+    pub(crate) max_spdm_msg_size: u32,
+
+    pub(crate) supported_algorithms: __IncompleteArrayField<__le16>,
+}
+
+impl Validate<&mut Unvalidated<KVec<u8>>> for &mut GetCapabilitiesRsp {
+    type Err = Error;
+
+    fn validate(unvalidated: &mut Unvalidated<KVec<u8>>) -> Result<Self, Self::Err> {
+        let raw = unvalidated.raw_mut();
+        if raw.len() < mem::size_of::<GetCapabilitiesRsp>() {
+            return Err(EINVAL);
+        }
+
+        let ptr = raw.as_mut_ptr();
+        // CAST: `GetCapabilitiesRsp` only contains integers and has `repr(C)`.
+        let ptr = ptr.cast::<GetCapabilitiesRsp>();
+        // SAFETY: `ptr` came from a reference and the cast above is valid.
+        let rsp: &mut GetCapabilitiesRsp = unsafe { &mut *ptr };
+
+        Ok(rsp)
+    }
+}
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [RFC v2 12/20] lib: rspdm: Support SPDM negotiate_algorithms
  2025-02-27  3:09 [RFC v2 00/20] lib: Rust implementation of SPDM Alistair Francis
                   ` (10 preceding siblings ...)
  2025-02-27  3:09 ` [RFC v2 11/20] lib: rspdm: Support SPDM get_capabilities Alistair Francis
@ 2025-02-27  3:09 ` Alistair Francis
  2025-02-27  3:09 ` [RFC v2 13/20] lib: rspdm: Support SPDM get_digests Alistair Francis
                   ` (8 subsequent siblings)
  20 siblings, 0 replies; 47+ messages in thread
From: Alistair Francis @ 2025-02-27  3:09 UTC (permalink / raw)
  To: linux-cxl, linux-kernel, lukas, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm
  Cc: boqun.feng, bjorn3_gh, wilfred.mallawa, aliceryhl, ojeda,
	alistair23, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

Support the NEGOTIATE_ALGORITHMS SPDM command.

Signed-off-by: Alistair Francis <alistair@alistair23.me>
---
 lib/rspdm/consts.rs             |  53 ++++++++
 lib/rspdm/lib.rs                |  16 ++-
 lib/rspdm/state.rs              | 213 +++++++++++++++++++++++++++++++-
 lib/rspdm/validator.rs          | 115 ++++++++++++++++-
 rust/bindgen_static_functions   |   5 +
 rust/bindings/bindings_helper.h |   2 +
 6 files changed, 398 insertions(+), 6 deletions(-)

diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs
index 8a58351bf268..d2f245c858cc 100644
--- a/lib/rspdm/consts.rs
+++ b/lib/rspdm/consts.rs
@@ -65,6 +65,59 @@ pub(crate) enum SpdmErrorCode {
 
 pub(crate) const SPDM_CERT_CAP: u32 = 1 << 1;
 pub(crate) const SPDM_CHAL_CAP: u32 = 1 << 2;
+pub(crate) const SPDM_MEAS_CAP_MASK: u32 = 3 << 3;
+pub(crate) const SPDM_KEY_EX_CAP: u32 = 1 << 9;
 
 pub(crate) const SPDM_REQ_CAPS: u32 = SPDM_CERT_CAP | SPDM_CHAL_CAP;
 pub(crate) const SPDM_RSP_MIN_CAPS: u32 = SPDM_CERT_CAP | SPDM_CHAL_CAP;
+
+pub(crate) const SPDM_NEGOTIATE_ALGS: u8 = 0xe3;
+
+pub(crate) const SPDM_MEAS_SPEC_DMTF: u8 = 1 << 0;
+
+pub(crate) const SPDM_ASYM_RSASSA_2048: u32 = 1 << 0;
+pub(crate) const _SPDM_ASYM_RSAPSS_2048: u32 = 1 << 1;
+pub(crate) const SPDM_ASYM_RSASSA_3072: u32 = 1 << 2;
+pub(crate) const _SPDM_ASYM_RSAPSS_3072: u32 = 1 << 3;
+pub(crate) const SPDM_ASYM_ECDSA_ECC_NIST_P256: u32 = 1 << 4;
+pub(crate) const SPDM_ASYM_RSASSA_4096: u32 = 1 << 5;
+pub(crate) const _SPDM_ASYM_RSAPSS_4096: u32 = 1 << 6;
+pub(crate) const SPDM_ASYM_ECDSA_ECC_NIST_P384: u32 = 1 << 7;
+pub(crate) const SPDM_ASYM_ECDSA_ECC_NIST_P521: u32 = 1 << 8;
+pub(crate) const _SPDM_ASYM_SM2_ECC_SM2_P256: u32 = 1 << 9;
+pub(crate) const _SPDM_ASYM_EDDSA_ED25519: u32 = 1 << 10;
+pub(crate) const _SPDM_ASYM_EDDSA_ED448: u32 = 1 << 11;
+
+pub(crate) const SPDM_HASH_SHA_256: u32 = 1 << 0;
+pub(crate) const SPDM_HASH_SHA_384: u32 = 1 << 1;
+pub(crate) const SPDM_HASH_SHA_512: u32 = 1 << 2;
+
+#[cfg(CONFIG_CRYPTO_RSA)]
+pub(crate) const SPDM_ASYM_RSA: u32 =
+    SPDM_ASYM_RSASSA_2048 | SPDM_ASYM_RSASSA_3072 | SPDM_ASYM_RSASSA_4096;
+#[cfg(not(CONFIG_CRYPTO_RSA))]
+pub(crate) const SPDM_ASYM_RSA: u32 = 0;
+
+#[cfg(CONFIG_CRYPTO_ECDSA)]
+pub(crate) const SPDM_ASYM_ECDSA: u32 =
+    SPDM_ASYM_ECDSA_ECC_NIST_P256 | SPDM_ASYM_ECDSA_ECC_NIST_P384 | SPDM_ASYM_ECDSA_ECC_NIST_P521;
+#[cfg(not(CONFIG_CRYPTO_ECDSA))]
+pub(crate) const SPDM_ASYM_ECDSA: u32 = 0;
+
+#[cfg(CONFIG_CRYPTO_SHA256)]
+pub(crate) const SPDM_HASH_SHA2_256: u32 = SPDM_HASH_SHA_256;
+#[cfg(not(CONFIG_CRYPTO_SHA256))]
+pub(crate) const SPDM_HASH_SHA2_256: u32 = 0;
+
+#[cfg(CONFIG_CRYPTO_SHA512)]
+pub(crate) const SPDM_HASH_SHA2_384_512: u32 = SPDM_HASH_SHA_384 | SPDM_HASH_SHA_512;
+#[cfg(not(CONFIG_CRYPTO_SHA512))]
+pub(crate) const SPDM_HASH_SHA2_384_512: u32 = 0;
+
+pub(crate) const SPDM_ASYM_ALGOS: u32 = SPDM_ASYM_RSA | SPDM_ASYM_ECDSA;
+pub(crate) const SPDM_HASH_ALGOS: u32 = SPDM_HASH_SHA2_256 | SPDM_HASH_SHA2_384_512;
+
+/* Maximum number of ReqAlgStructs sent by this implementation */
+// pub(crate) const SPDM_MAX_REQ_ALG_STRUCT: usize = 4;
+
+pub(crate) const SPDM_OPAQUE_DATA_FMT_GENERAL: u8 = 1 << 1;
diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs
index fe36333b2d24..5eee5116d791 100644
--- a/lib/rspdm/lib.rs
+++ b/lib/rspdm/lib.rs
@@ -118,6 +118,10 @@
         return e.to_errno() as c_int;
     }
 
+    if let Err(e) = state.negotiate_algs() {
+        return e.to_errno() as c_int;
+    }
+
     0
 }
 
@@ -125,4 +129,14 @@
 ///
 /// @spdm_state: SPDM session state
 #[no_mangle]
-pub unsafe extern "C" fn spdm_destroy(_state: &'static mut SpdmState) {}
+pub unsafe extern "C" fn spdm_destroy(state: &'static mut SpdmState) {
+    if let Some(desc) = &mut state.desc {
+        unsafe {
+            bindings::kfree(*desc as *mut _ as *mut c_void);
+        }
+    }
+
+    unsafe {
+        bindings::crypto_free_shash(state.shash);
+    }
+}
diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs
index 288a77b3ab56..0aff45339e0b 100644
--- a/lib/rspdm/state.rs
+++ b/lib/rspdm/state.rs
@@ -13,15 +13,21 @@
 use kernel::{
     bindings,
     error::{code::EINVAL, to_result, Error},
+    str::CStr,
     validate::Untrusted,
 };
 
 use crate::consts::{
-    SpdmErrorCode, SPDM_ERROR, SPDM_GET_VERSION_LEN, SPDM_MAX_VER, SPDM_MIN_DATA_TRANSFER_SIZE,
-    SPDM_MIN_VER, SPDM_REQ, SPDM_RSP_MIN_CAPS, SPDM_VER_10, SPDM_VER_11, SPDM_VER_12,
+    SpdmErrorCode, SPDM_ASYM_ALGOS, SPDM_ASYM_ECDSA_ECC_NIST_P256, SPDM_ASYM_ECDSA_ECC_NIST_P384,
+    SPDM_ASYM_ECDSA_ECC_NIST_P521, SPDM_ASYM_RSASSA_2048, SPDM_ASYM_RSASSA_3072,
+    SPDM_ASYM_RSASSA_4096, SPDM_ERROR, SPDM_GET_VERSION_LEN, SPDM_HASH_ALGOS, SPDM_HASH_SHA_256,
+    SPDM_HASH_SHA_384, SPDM_HASH_SHA_512, SPDM_KEY_EX_CAP, SPDM_MAX_VER, SPDM_MEAS_CAP_MASK,
+    SPDM_MEAS_SPEC_DMTF, SPDM_MIN_DATA_TRANSFER_SIZE, SPDM_MIN_VER, SPDM_OPAQUE_DATA_FMT_GENERAL,
+    SPDM_REQ, SPDM_RSP_MIN_CAPS, SPDM_VER_10, SPDM_VER_11, SPDM_VER_12,
 };
 use crate::validator::{
-    GetCapabilitiesReq, GetCapabilitiesRsp, GetVersionReq, GetVersionRsp, SpdmErrorRsp, SpdmHeader,
+    GetCapabilitiesReq, GetCapabilitiesRsp, GetVersionReq, GetVersionRsp, NegotiateAlgsReq,
+    NegotiateAlgsRsp, RegAlg, SpdmErrorRsp, SpdmHeader,
 };
 
 /// The current SPDM session state for a device. Based on the
@@ -40,6 +46,28 @@
 ///  Negotiated during GET_VERSION exchange.
 /// @rsp_caps: Cached capabilities of responder.
 ///  Received during GET_CAPABILITIES exchange.
+/// @base_asym_alg: Asymmetric key algorithm for signature verification of
+///  CHALLENGE_AUTH and MEASUREMENTS messages.
+///  Selected by responder during NEGOTIATE_ALGORITHMS exchange.
+/// @base_hash_alg: Hash algorithm for signature verification of
+///  CHALLENGE_AUTH and MEASUREMENTS messages.
+///  Selected by responder during NEGOTIATE_ALGORITHMS exchange.
+/// @meas_hash_alg: Hash algorithm for measurement blocks.
+///  Selected by responder during NEGOTIATE_ALGORITHMS exchange.
+/// @base_asym_enc: Human-readable name of @base_asym_alg's signature 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().
+/// @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.
 ///
 /// `authenticated`: Whether device was authenticated successfully.
 #[expect(dead_code)]
@@ -54,6 +82,19 @@ pub struct SpdmState {
     // Negotiated state
     pub(crate) version: u8,
     pub(crate) rsp_caps: u32,
+    pub(crate) base_asym_alg: u32,
+    pub(crate) base_hash_alg: u32,
+    pub(crate) meas_hash_alg: u32,
+
+    /* Signature algorithm */
+    base_asym_enc: &'static CStr,
+    sig_len: usize,
+
+    /* Hash algorithm */
+    base_hash_alg_name: &'static CStr,
+    pub(crate) shash: *mut bindings::crypto_shash,
+    pub(crate) desc: Option<&'static mut bindings::shash_desc>,
+    pub(crate) hash_len: usize,
 
     pub(crate) authenticated: bool,
 }
@@ -76,6 +117,15 @@ pub(crate) fn new(
             validate,
             version: SPDM_MIN_VER,
             rsp_caps: 0,
+            base_asym_alg: 0,
+            base_hash_alg: 0,
+            meas_hash_alg: 0,
+            base_asym_enc: unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") },
+            sig_len: 0,
+            base_hash_alg_name: unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") },
+            shash: core::ptr::null_mut(),
+            desc: None,
+            hash_len: 0,
             authenticated: false,
         }
     }
@@ -336,4 +386,161 @@ pub(crate) fn get_capabilities(&mut self) -> Result<(), Error> {
 
         Ok(())
     }
+
+    fn update_response_algs(&mut self) -> Result<(), Error> {
+        match self.base_asym_alg {
+            SPDM_ASYM_RSASSA_2048 => {
+                self.sig_len = 256;
+                self.base_asym_enc = CStr::from_bytes_with_nul(b"pkcs1\0")?;
+            }
+            SPDM_ASYM_RSASSA_3072 => {
+                self.sig_len = 384;
+                self.base_asym_enc = CStr::from_bytes_with_nul(b"pkcs1\0")?;
+            }
+            SPDM_ASYM_RSASSA_4096 => {
+                self.sig_len = 512;
+                self.base_asym_enc = CStr::from_bytes_with_nul(b"pkcs1\0")?;
+            }
+            SPDM_ASYM_ECDSA_ECC_NIST_P256 => {
+                self.sig_len = 64;
+                self.base_asym_enc = CStr::from_bytes_with_nul(b"p1363\0")?;
+            }
+            SPDM_ASYM_ECDSA_ECC_NIST_P384 => {
+                self.sig_len = 96;
+                self.base_asym_enc = CStr::from_bytes_with_nul(b"p1363\0")?;
+            }
+            SPDM_ASYM_ECDSA_ECC_NIST_P521 => {
+                self.sig_len = 132;
+                self.base_asym_enc = CStr::from_bytes_with_nul(b"p1363\0")?;
+            }
+            _ => {
+                pr_err!("Unknown asym algorithm\n");
+                return Err(EINVAL);
+            }
+        }
+
+        match self.base_hash_alg {
+            SPDM_HASH_SHA_256 => {
+                self.base_hash_alg_name = CStr::from_bytes_with_nul(b"sha256\0")?;
+            }
+            SPDM_HASH_SHA_384 => {
+                self.base_hash_alg_name = CStr::from_bytes_with_nul(b"sha384\0")?;
+            }
+            SPDM_HASH_SHA_512 => {
+                self.base_hash_alg_name = CStr::from_bytes_with_nul(b"sha512\0")?;
+            }
+            _ => {
+                pr_err!("Unknown hash algorithm\n");
+                return Err(EINVAL);
+            }
+        }
+
+        /*
+         * shash and desc allocations are reused for subsequent measurement
+         * retrieval, hence are not freed until spdm_reset().
+         */
+        self.shash =
+            unsafe { bindings::crypto_alloc_shash(self.base_hash_alg_name.as_char_ptr(), 0, 0) };
+        if self.shash.is_null() {
+            return Err(ENOMEM);
+        }
+
+        let desc_len = core::mem::size_of::<bindings::shash_desc>()
+            + unsafe { bindings::crypto_shash_descsize(self.shash) } as usize;
+
+        let mut desc_vec: KVec<u8> = KVec::with_capacity(desc_len, GFP_KERNEL)?;
+        // SAFETY: `desc_vec` is `desc_len` long
+        let desc_buf = unsafe { from_raw_parts_mut(desc_vec.as_mut_ptr(), desc_len) };
+
+        let desc = unsafe {
+            core::mem::transmute::<*mut c_void, &mut bindings::shash_desc>(
+                desc_buf.as_mut_ptr() as *mut c_void
+            )
+        };
+        desc.tfm = self.shash;
+
+        self.desc = Some(desc);
+
+        /* Used frequently to compute offsets, so cache H */
+        self.hash_len = unsafe { bindings::crypto_shash_digestsize(self.shash) as usize };
+
+        if let Some(desc) = &mut self.desc {
+            unsafe { to_result(bindings::crypto_shash_init(*desc)) }
+        } else {
+            Err(ENOMEM)
+        }
+    }
+
+    pub(crate) fn negotiate_algs(&mut self) -> Result<(), Error> {
+        let mut request = NegotiateAlgsReq::default();
+        request.version = self.version;
+
+        if self.version >= SPDM_VER_12 && (self.rsp_caps & SPDM_KEY_EX_CAP) == SPDM_KEY_EX_CAP {
+            request.other_params_support = SPDM_OPAQUE_DATA_FMT_GENERAL;
+        }
+
+        // TODO support more algs
+        let reg_alg_entries = 0;
+
+        let req_sz = core::mem::size_of::<NegotiateAlgsReq>()
+            + core::mem::size_of::<RegAlg>() * reg_alg_entries;
+        let rsp_sz = core::mem::size_of::<NegotiateAlgsRsp>()
+            + core::mem::size_of::<RegAlg>() * reg_alg_entries;
+
+        request.length = req_sz as u16;
+        request.param1 = reg_alg_entries as u8;
+
+        // SAFETY: `request` is repr(C) and packed, so we can convert it to a slice
+        let request_buf = unsafe { from_raw_parts_mut(&mut request as *mut _ as *mut u8, req_sz) };
+
+        let mut response_vec: KVec<u8> = KVec::with_capacity(rsp_sz, GFP_KERNEL)?;
+        // SAFETY: `request` is repr(C) and packed, so we can convert it to a slice
+        let response_buf = unsafe { from_raw_parts_mut(response_vec.as_mut_ptr(), rsp_sz) };
+
+        let rc = self.spdm_exchange(request_buf, response_buf)?;
+
+        if rc < (rsp_sz as i32) {
+            pr_err!("Truncated capabilities response\n");
+            to_result(-(bindings::EIO as i32))?;
+        }
+
+        // SAFETY: `rc` bytes where inserted to the raw pointer by spdm_exchange
+        unsafe { response_vec.set_len(rc as usize) };
+
+        let response: &mut NegotiateAlgsRsp =
+            Untrusted::new_mut(&mut response_vec).validate_mut()?;
+
+        self.base_asym_alg = response.base_asym_sel;
+        self.base_hash_alg = response.base_hash_sel;
+        self.meas_hash_alg = response.measurement_hash_algo;
+
+        if self.base_asym_alg & SPDM_ASYM_ALGOS == 0 || self.base_hash_alg & SPDM_HASH_ALGOS == 0 {
+            pr_err!("No common supported algorithms\n");
+            to_result(-(bindings::EPROTO as i32))?;
+        }
+
+        // /* Responder shall select exactly 1 alg (SPDM 1.0.0 table 14) */
+        if self.base_asym_alg.count_ones() != 1
+            || self.base_hash_alg.count_ones() != 1
+            || response.ext_asym_sel_count != 0
+            || response.ext_hash_sel_count != 0
+            || response.param1 > request.param1
+            || response.other_params_sel != request.other_params_support
+        {
+            pr_err!("Malformed algorithms response\n");
+            to_result(-(bindings::EPROTO as i32))?;
+        }
+
+        if self.rsp_caps & SPDM_MEAS_CAP_MASK == SPDM_MEAS_CAP_MASK
+            && (self.meas_hash_alg.count_ones() != 1
+                || response.measurement_specification_sel != SPDM_MEAS_SPEC_DMTF)
+        {
+            pr_err!("Malformed algorithms response\n");
+            to_result(-(bindings::EPROTO as i32))?;
+        }
+
+        self.update_response_algs()?;
+
+        Ok(())
+    }
 }
diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs
index cd792c46767a..036a077c71c3 100644
--- a/lib/rspdm/validator.rs
+++ b/lib/rspdm/validator.rs
@@ -7,7 +7,7 @@
 //! Rust implementation of the DMTF Security Protocol and Data Model (SPDM)
 //! <https://www.dmtf.org/dsp/DSP0274>
 
-use crate::bindings::{__IncompleteArrayField, __le16};
+use crate::bindings::{__IncompleteArrayField, __le16, __le32};
 use crate::consts::SpdmErrorCode;
 use core::mem;
 use kernel::prelude::*;
@@ -16,7 +16,10 @@
     validate::{Unvalidated, Validate},
 };
 
-use crate::consts::{SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, SPDM_GET_VERSION, SPDM_REQ_CAPS};
+use crate::consts::{
+    SPDM_ASYM_ALGOS, SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, SPDM_GET_VERSION, SPDM_HASH_ALGOS,
+    SPDM_MEAS_SPEC_DMTF, SPDM_NEGOTIATE_ALGS, SPDM_REQ_CAPS,
+};
 
 #[repr(C, packed)]
 pub(crate) struct SpdmHeader {
@@ -205,3 +208,111 @@ fn validate(unvalidated: &mut Unvalidated<KVec<u8>>) -> Result<Self, Self::Err>
         Ok(rsp)
     }
 }
+
+#[repr(C, packed)]
+pub(crate) struct RegAlg {
+    pub(crate) alg_type: u8,
+    pub(crate) alg_count: u8,
+    pub(crate) alg_supported: u16,
+    pub(crate) alg_external: __IncompleteArrayField<__le32>,
+}
+
+#[repr(C, packed)]
+pub(crate) struct NegotiateAlgsReq {
+    pub(crate) version: u8,
+    pub(crate) code: u8,
+    pub(crate) param1: u8,
+    pub(crate) param2: u8,
+
+    pub(crate) length: u16,
+    pub(crate) measurement_specification: u8,
+    pub(crate) other_params_support: u8,
+
+    pub(crate) base_asym_algo: u32,
+    pub(crate) base_hash_algo: u32,
+
+    reserved1: [u8; 12],
+
+    pub(crate) ext_asym_count: u8,
+    pub(crate) ext_hash_count: u8,
+    reserved2: u8,
+    pub(crate) mel_specification: u8,
+
+    pub(crate) ext_asym: __IncompleteArrayField<__le32>,
+    pub(crate) ext_hash: __IncompleteArrayField<__le32>,
+    pub(crate) req_alg_struct: __IncompleteArrayField<RegAlg>,
+}
+
+impl Default for NegotiateAlgsReq {
+    fn default() -> Self {
+        NegotiateAlgsReq {
+            version: 0,
+            code: SPDM_NEGOTIATE_ALGS,
+            param1: 0,
+            param2: 0,
+            length: 0,
+            measurement_specification: SPDM_MEAS_SPEC_DMTF,
+            other_params_support: 0,
+            base_asym_algo: SPDM_ASYM_ALGOS.to_le(),
+            base_hash_algo: SPDM_HASH_ALGOS.to_le(),
+            reserved1: [0u8; 12],
+            ext_asym_count: 0,
+            ext_hash_count: 0,
+            reserved2: 0,
+            mel_specification: 0,
+            ext_asym: __IncompleteArrayField::new(),
+            ext_hash: __IncompleteArrayField::new(),
+            req_alg_struct: __IncompleteArrayField::new(),
+        }
+    }
+}
+
+#[repr(C, packed)]
+pub(crate) struct NegotiateAlgsRsp {
+    pub(crate) version: u8,
+    pub(crate) code: u8,
+    pub(crate) param1: u8,
+    pub(crate) param2: u8,
+
+    pub(crate) length: u16,
+    pub(crate) measurement_specification_sel: u8,
+    pub(crate) other_params_sel: u8,
+
+    pub(crate) measurement_hash_algo: u32,
+    pub(crate) base_asym_sel: u32,
+    pub(crate) base_hash_sel: u32,
+
+    reserved1: [u8; 11],
+
+    pub(crate) mel_specification_sel: u8,
+    pub(crate) ext_asym_sel_count: u8,
+    pub(crate) ext_hash_sel_count: u8,
+    reserved2: [u8; 2],
+
+    pub(crate) ext_asym: __IncompleteArrayField<__le32>,
+    pub(crate) ext_hash: __IncompleteArrayField<__le32>,
+    pub(crate) req_alg_struct: __IncompleteArrayField<RegAlg>,
+}
+
+impl Validate<&mut Unvalidated<KVec<u8>>> for &mut NegotiateAlgsRsp {
+    type Err = Error;
+
+    fn validate(unvalidated: &mut Unvalidated<KVec<u8>>) -> Result<Self, Self::Err> {
+        let raw = unvalidated.raw_mut();
+        if raw.len() < mem::size_of::<NegotiateAlgsRsp>() {
+            return Err(EINVAL);
+        }
+
+        let ptr = raw.as_mut_ptr();
+        // CAST: `NegotiateAlgsRsp` only contains integers and has `repr(C)`.
+        let ptr = ptr.cast::<NegotiateAlgsRsp>();
+        // SAFETY: `ptr` came from a reference and the cast above is valid.
+        let rsp: &mut NegotiateAlgsRsp = unsafe { &mut *ptr };
+
+        rsp.base_asym_sel = rsp.base_asym_sel.to_le();
+        rsp.base_hash_sel = rsp.base_hash_sel.to_le();
+        rsp.measurement_hash_algo = rsp.measurement_hash_algo.to_le();
+
+        Ok(rsp)
+    }
+}
diff --git a/rust/bindgen_static_functions b/rust/bindgen_static_functions
index ec48ad2e8c78..978276e17754 100644
--- a/rust/bindgen_static_functions
+++ b/rust/bindgen_static_functions
@@ -30,3 +30,8 @@
 
 --allowlist-function copy_from_user
 --allowlist-function copy_to_user
+
+--allowlist-function crypto_shash_descsize
+--allowlist-function crypto_shash_init
+--allowlist-function crypto_shash_digestsize
+--allowlist-function crypto_free_shash
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 42aa62f0c8f5..5ba1191429f8 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -6,6 +6,7 @@
  * Sorted alphabetically.
  */
 
+#include <crypto/hash.h>
 #include <kunit/test.h>
 #include <linux/blk-mq.h>
 #include <linux/blk_types.h>
@@ -17,6 +18,7 @@
 #include <linux/file.h>
 #include <linux/firmware.h>
 #include <linux/fs.h>
+#include <linux/hash.h>
 #include <linux/jiffies.h>
 #include <linux/jump_label.h>
 #include <linux/mdio.h>
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [RFC v2 13/20] lib: rspdm: Support SPDM get_digests
  2025-02-27  3:09 [RFC v2 00/20] lib: Rust implementation of SPDM Alistair Francis
                   ` (11 preceding siblings ...)
  2025-02-27  3:09 ` [RFC v2 12/20] lib: rspdm: Support SPDM negotiate_algorithms Alistair Francis
@ 2025-02-27  3:09 ` Alistair Francis
  2025-02-27  3:09 ` [RFC v2 14/20] lib: rspdm: Support SPDM get_certificate Alistair Francis
                   ` (7 subsequent siblings)
  20 siblings, 0 replies; 47+ messages in thread
From: Alistair Francis @ 2025-02-27  3:09 UTC (permalink / raw)
  To: linux-cxl, linux-kernel, lukas, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm
  Cc: boqun.feng, bjorn3_gh, wilfred.mallawa, aliceryhl, ojeda,
	alistair23, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

Signed-off-by: Alistair Francis <alistair@alistair23.me>
---
 lib/rspdm/consts.rs    |  4 ++
 lib/rspdm/lib.rs       |  4 ++
 lib/rspdm/state.rs     | 89 ++++++++++++++++++++++++++++++++++++++++--
 lib/rspdm/validator.rs | 52 +++++++++++++++++++++++-
 4 files changed, 144 insertions(+), 5 deletions(-)

diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs
index d2f245c858cc..9714fa52e2be 100644
--- a/lib/rspdm/consts.rs
+++ b/lib/rspdm/consts.rs
@@ -16,6 +16,8 @@
 pub(crate) const SPDM_VER_12: u8 = 0x12;
 pub(crate) const SPDM_VER_13: u8 = 0x13;
 
+pub(crate) const SPDM_SLOTS: usize = 8;
+
 pub(crate) const SPDM_MIN_VER: u8 = SPDM_VER_10;
 pub(crate) const SPDM_MAX_VER: u8 = SPDM_VER_13;
 
@@ -92,6 +94,8 @@ pub(crate) enum SpdmErrorCode {
 pub(crate) const SPDM_HASH_SHA_384: u32 = 1 << 1;
 pub(crate) const SPDM_HASH_SHA_512: u32 = 1 << 2;
 
+pub(crate) const SPDM_GET_DIGESTS: u8 = 0x81;
+
 #[cfg(CONFIG_CRYPTO_RSA)]
 pub(crate) const SPDM_ASYM_RSA: u32 =
     SPDM_ASYM_RSASSA_2048 | SPDM_ASYM_RSASSA_3072 | SPDM_ASYM_RSASSA_4096;
diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs
index 5eee5116d791..bf5f7c72f885 100644
--- a/lib/rspdm/lib.rs
+++ b/lib/rspdm/lib.rs
@@ -122,6 +122,10 @@
         return e.to_errno() as c_int;
     }
 
+    if let Err(e) = state.get_digests() {
+        return e.to_errno() as c_int;
+    }
+
     0
 }
 
diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs
index 0aff45339e0b..7889214c6aa7 100644
--- a/lib/rspdm/state.rs
+++ b/lib/rspdm/state.rs
@@ -23,11 +23,11 @@
     SPDM_ASYM_RSASSA_4096, SPDM_ERROR, SPDM_GET_VERSION_LEN, SPDM_HASH_ALGOS, SPDM_HASH_SHA_256,
     SPDM_HASH_SHA_384, SPDM_HASH_SHA_512, SPDM_KEY_EX_CAP, SPDM_MAX_VER, SPDM_MEAS_CAP_MASK,
     SPDM_MEAS_SPEC_DMTF, SPDM_MIN_DATA_TRANSFER_SIZE, SPDM_MIN_VER, SPDM_OPAQUE_DATA_FMT_GENERAL,
-    SPDM_REQ, SPDM_RSP_MIN_CAPS, SPDM_VER_10, SPDM_VER_11, SPDM_VER_12,
+    SPDM_REQ, SPDM_RSP_MIN_CAPS, SPDM_SLOTS, SPDM_VER_10, SPDM_VER_11, SPDM_VER_12,
 };
 use crate::validator::{
-    GetCapabilitiesReq, GetCapabilitiesRsp, GetVersionReq, GetVersionRsp, NegotiateAlgsReq,
-    NegotiateAlgsRsp, RegAlg, SpdmErrorRsp, SpdmHeader,
+    GetCapabilitiesReq, GetCapabilitiesRsp, GetDigestsReq, GetDigestsRsp, GetVersionReq,
+    GetVersionRsp, NegotiateAlgsReq, NegotiateAlgsRsp, RegAlg, SpdmErrorRsp, SpdmHeader,
 };
 
 /// The current SPDM session state for a device. Based on the
@@ -54,6 +54,10 @@
 ///  Selected by responder during NEGOTIATE_ALGORITHMS exchange.
 /// @meas_hash_alg: Hash algorithm for measurement blocks.
 ///  Selected by responder during NEGOTIATE_ALGORITHMS exchange.
+/// @supported_slots: Bitmask of responder's supported certificate slots.
+///  Received during GET_DIGESTS exchange (from SPDM 1.3).
+/// @provisioned_slots: Bitmask of responder's provisioned certificate 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).
@@ -68,6 +72,9 @@
 /// @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).
 ///
 /// `authenticated`: Whether device was authenticated successfully.
 #[expect(dead_code)]
@@ -85,6 +92,8 @@ pub struct SpdmState {
     pub(crate) base_asym_alg: u32,
     pub(crate) base_hash_alg: u32,
     pub(crate) meas_hash_alg: u32,
+    pub(crate) supported_slots: u8,
+    pub(crate) provisioned_slots: u8,
 
     /* Signature algorithm */
     base_asym_enc: &'static CStr,
@@ -97,6 +106,10 @@ pub struct SpdmState {
     pub(crate) hash_len: usize,
 
     pub(crate) authenticated: bool,
+
+    // Certificates
+    pub(crate) slot: [Option<KVec<u8>>; SPDM_SLOTS],
+    slot_sz: [usize; SPDM_SLOTS],
 }
 
 impl SpdmState {
@@ -120,6 +133,8 @@ pub(crate) fn new(
             base_asym_alg: 0,
             base_hash_alg: 0,
             meas_hash_alg: 0,
+            supported_slots: 0,
+            provisioned_slots: 0,
             base_asym_enc: unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") },
             sig_len: 0,
             base_hash_alg_name: unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") },
@@ -127,6 +142,8 @@ pub(crate) fn new(
             desc: None,
             hash_len: 0,
             authenticated: false,
+            slot: [const { None }; SPDM_SLOTS],
+            slot_sz: [0; SPDM_SLOTS],
         }
     }
 
@@ -543,4 +560,70 @@ pub(crate) fn negotiate_algs(&mut self) -> Result<(), Error> {
 
         Ok(())
     }
+
+    pub(crate) fn get_digests(&mut self) -> Result<(), Error> {
+        let mut request = GetDigestsReq::default();
+        request.version = self.version;
+
+        let req_sz = core::mem::size_of::<GetDigestsReq>();
+        let rsp_sz = core::mem::size_of::<GetDigestsRsp>() + SPDM_SLOTS * self.hash_len;
+
+        // SAFETY: `request` is repr(C) and packed, so we can convert it to a slice
+        let request_buf = unsafe { from_raw_parts_mut(&mut request as *mut _ as *mut u8, req_sz) };
+
+        let mut response_vec: KVec<u8> = KVec::with_capacity(rsp_sz, GFP_KERNEL)?;
+        // SAFETY: `request` is repr(C) and packed, so we can convert it to a slice
+        let response_buf = unsafe { from_raw_parts_mut(response_vec.as_mut_ptr(), rsp_sz) };
+
+        let rc = self.spdm_exchange(request_buf, response_buf)?;
+
+        if rc < (core::mem::size_of::<GetDigestsRsp>() as i32) {
+            pr_err!("Truncated digests response\n");
+            to_result(-(bindings::EIO as i32))?;
+        }
+
+        // SAFETY: `rc` bytes where inserted to the raw pointer by spdm_exchange
+        unsafe { response_vec.set_len(rc as usize) };
+
+        let response: &mut GetDigestsRsp = Untrusted::new_mut(&mut response_vec).validate_mut()?;
+
+        if rc
+            < (core::mem::size_of::<GetDigestsReq>()
+                + response.param2.count_ones() as usize * self.hash_len) as i32
+        {
+            pr_err!("Truncated digests response\n");
+            to_result(-(bindings::EIO as i32))?;
+        }
+
+        let mut deprovisioned_slots = self.provisioned_slots & !response.param2;
+        while (deprovisioned_slots.trailing_zeros() as usize) < SPDM_SLOTS {
+            let slot = deprovisioned_slots.trailing_zeros() as usize;
+            self.slot[slot] = None;
+            self.slot_sz[slot] = 0;
+            deprovisioned_slots &= !(1 << slot);
+        }
+
+        self.provisioned_slots = response.param2;
+        if self.provisioned_slots == 0 {
+            pr_err!("No certificates provisioned\n");
+            to_result(-(bindings::EPROTO as i32))?;
+        }
+
+        if self.version >= 0x13 && (response.param2 & !response.param1 != 0) {
+            pr_err!("Malformed digests response\n");
+            to_result(-(bindings::EPROTO as i32))?;
+        }
+
+        let supported_slots = if self.version >= 0x13 {
+            response.param1
+        } else {
+            0xFF
+        };
+
+        if self.supported_slots != supported_slots {
+            self.supported_slots = supported_slots;
+        }
+
+        Ok(())
+    }
 }
diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs
index 036a077c71c3..2150a23997db 100644
--- a/lib/rspdm/validator.rs
+++ b/lib/rspdm/validator.rs
@@ -17,8 +17,8 @@
 };
 
 use crate::consts::{
-    SPDM_ASYM_ALGOS, SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, SPDM_GET_VERSION, SPDM_HASH_ALGOS,
-    SPDM_MEAS_SPEC_DMTF, SPDM_NEGOTIATE_ALGS, SPDM_REQ_CAPS,
+    SPDM_ASYM_ALGOS, SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, SPDM_GET_DIGESTS, SPDM_GET_VERSION,
+    SPDM_HASH_ALGOS, SPDM_MEAS_SPEC_DMTF, SPDM_NEGOTIATE_ALGS, SPDM_REQ_CAPS,
 };
 
 #[repr(C, packed)]
@@ -316,3 +316,51 @@ fn validate(unvalidated: &mut Unvalidated<KVec<u8>>) -> Result<Self, Self::Err>
         Ok(rsp)
     }
 }
+
+#[repr(C, packed)]
+pub(crate) struct GetDigestsReq {
+    pub(crate) version: u8,
+    pub(crate) code: u8,
+    pub(crate) param1: u8,
+    pub(crate) param2: u8,
+}
+
+impl Default for GetDigestsReq {
+    fn default() -> Self {
+        GetDigestsReq {
+            version: 0,
+            code: SPDM_GET_DIGESTS,
+            param1: 0,
+            param2: 0,
+        }
+    }
+}
+
+#[repr(C, packed)]
+pub(crate) struct GetDigestsRsp {
+    pub(crate) version: u8,
+    pub(crate) code: u8,
+    pub(crate) param1: u8,
+    pub(crate) param2: u8,
+
+    pub(crate) digests: __IncompleteArrayField<u8>,
+}
+
+impl Validate<&mut Unvalidated<KVec<u8>>> for &mut GetDigestsRsp {
+    type Err = Error;
+
+    fn validate(unvalidated: &mut Unvalidated<KVec<u8>>) -> Result<Self, Self::Err> {
+        let raw = unvalidated.raw_mut();
+        if raw.len() < mem::size_of::<GetDigestsRsp>() {
+            return Err(EINVAL);
+        }
+
+        let ptr = raw.as_mut_ptr();
+        // CAST: `GetDigestsRsp` only contains integers and has `repr(C)`.
+        let ptr = ptr.cast::<GetDigestsRsp>();
+        // SAFETY: `ptr` came from a reference and the cast above is valid.
+        let rsp: &mut GetDigestsRsp = unsafe { &mut *ptr };
+
+        Ok(rsp)
+    }
+}
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [RFC v2 14/20] lib: rspdm: Support SPDM get_certificate
  2025-02-27  3:09 [RFC v2 00/20] lib: Rust implementation of SPDM Alistair Francis
                   ` (12 preceding siblings ...)
  2025-02-27  3:09 ` [RFC v2 13/20] lib: rspdm: Support SPDM get_digests Alistair Francis
@ 2025-02-27  3:09 ` Alistair Francis
  2025-02-27 10:58   ` Greg KH
  2025-02-27  3:09 ` [RFC v2 15/20] crypto: asymmetric_keys - Load certificate parsing early in boot Alistair Francis
                   ` (6 subsequent siblings)
  20 siblings, 1 reply; 47+ messages in thread
From: Alistair Francis @ 2025-02-27  3:09 UTC (permalink / raw)
  To: linux-cxl, linux-kernel, lukas, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm
  Cc: boqun.feng, bjorn3_gh, wilfred.mallawa, aliceryhl, ojeda,
	alistair23, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

Signed-off-by: Alistair Francis <alistair@alistair23.me>
---
 lib/rspdm/consts.rs    |   2 +
 lib/rspdm/lib.rs       |  11 ++++
 lib/rspdm/state.rs     | 123 ++++++++++++++++++++++++++++++++++++++++-
 lib/rspdm/validator.rs |  64 ++++++++++++++++++++-
 4 files changed, 196 insertions(+), 4 deletions(-)

diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs
index 9714fa52e2be..ffaf36ec583b 100644
--- a/lib/rspdm/consts.rs
+++ b/lib/rspdm/consts.rs
@@ -96,6 +96,8 @@ pub(crate) enum SpdmErrorCode {
 
 pub(crate) const SPDM_GET_DIGESTS: u8 = 0x81;
 
+pub(crate) const SPDM_GET_CERTIFICATE: u8 = 0x82;
+
 #[cfg(CONFIG_CRYPTO_RSA)]
 pub(crate) const SPDM_ASYM_RSA: u32 =
     SPDM_ASYM_RSASSA_2048 | SPDM_ASYM_RSASSA_3072 | SPDM_ASYM_RSASSA_4096;
diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs
index bf5f7c72f885..1535e3a69518 100644
--- a/lib/rspdm/lib.rs
+++ b/lib/rspdm/lib.rs
@@ -126,6 +126,17 @@
         return e.to_errno() as c_int;
     }
 
+    let mut provisioned_slots = state.provisioned_slots;
+    while (provisioned_slots as usize) > 0 {
+        let slot = provisioned_slots.trailing_zeros() as u8;
+
+        if let Err(e) = state.get_certificate(slot) {
+            return e.to_errno() as c_int;
+        }
+
+        provisioned_slots &= !(1 << slot);
+    }
+
     0
 }
 
diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs
index 7889214c6aa7..ae259623fe50 100644
--- a/lib/rspdm/state.rs
+++ b/lib/rspdm/state.rs
@@ -26,8 +26,9 @@
     SPDM_REQ, SPDM_RSP_MIN_CAPS, SPDM_SLOTS, SPDM_VER_10, SPDM_VER_11, SPDM_VER_12,
 };
 use crate::validator::{
-    GetCapabilitiesReq, GetCapabilitiesRsp, GetDigestsReq, GetDigestsRsp, GetVersionReq,
-    GetVersionRsp, NegotiateAlgsReq, NegotiateAlgsRsp, RegAlg, SpdmErrorRsp, SpdmHeader,
+    GetCapabilitiesReq, GetCapabilitiesRsp, GetCertificateReq, GetCertificateRsp, GetDigestsReq,
+    GetDigestsRsp, GetVersionReq, GetVersionRsp, NegotiateAlgsReq, NegotiateAlgsRsp, RegAlg,
+    SpdmErrorRsp, SpdmHeader,
 };
 
 /// The current SPDM session state for a device. Based on the
@@ -112,6 +113,14 @@ pub struct SpdmState {
     slot_sz: [usize; SPDM_SLOTS],
 }
 
+#[repr(C, packed)]
+pub(crate) struct SpdmCertChain {
+    length: u16,
+    _reserved: [u8; 2],
+    root_hash: bindings::__IncompleteArrayField<u8>,
+    certificates: bindings::__IncompleteArrayField<u8>,
+}
+
 impl SpdmState {
     pub(crate) fn new(
         dev: *mut bindings::device,
@@ -626,4 +635,114 @@ pub(crate) fn get_digests(&mut self) -> Result<(), Error> {
 
         Ok(())
     }
+
+    fn get_cert_exchange(
+        &mut self,
+        request_buf: &mut [u8],
+        response_vec: &mut KVec<u8>,
+        rsp_sz: usize,
+    ) -> Result<&mut GetCertificateRsp, Error> {
+        // SAFETY: `request` is repr(C) and packed, so we can convert it to a slice
+        let response_buf = unsafe { from_raw_parts_mut(response_vec.as_mut_ptr(), rsp_sz) };
+
+        let rc = self.spdm_exchange(request_buf, response_buf)?;
+
+        if rc < (core::mem::size_of::<GetCertificateReq>() as i32) {
+            pr_err!("Truncated certificate response\n");
+            to_result(-(bindings::EIO as i32))?;
+        }
+
+        // SAFETY: `rc` bytes where inserted to the raw pointer by spdm_exchange
+        unsafe { response_vec.set_len(rc as usize) };
+
+        let response: &mut GetCertificateRsp = Untrusted::new_mut(response_vec).validate_mut()?;
+
+        if rc
+            < (core::mem::size_of::<GetCertificateRsp>() + response.portion_length as usize) as i32
+        {
+            pr_err!("Truncated certificate response\n");
+            to_result(-(bindings::EIO as i32))?;
+        }
+
+        Ok(response)
+    }
+
+    pub(crate) fn get_certificate(&mut self, slot: u8) -> Result<(), Error> {
+        let mut request = GetCertificateReq::default();
+        request.version = self.version;
+        request.param1 = slot;
+
+        let req_sz = core::mem::size_of::<GetCertificateReq>();
+        let rsp_sz = ((core::mem::size_of::<GetCertificateRsp>() + 0xffff) as u32)
+            .min(self.transport_sz) as usize;
+
+        // SAFETY: `request` is repr(C) and packed, so we can convert it to a slice
+        let request_buf = unsafe { from_raw_parts_mut(&mut request as *mut _ as *mut u8, req_sz) };
+
+        let mut response_vec: KVec<u8> = KVec::with_capacity(rsp_sz, GFP_KERNEL)?;
+
+        request.offset = 0;
+        request.length = (rsp_sz - core::mem::size_of::<GetCertificateRsp>()).to_le() as u16;
+
+        let response = self.get_cert_exchange(request_buf, &mut response_vec, rsp_sz)?;
+
+        let total_cert_len =
+            ((response.portion_length + response.remainder_length) & 0xFFFF) as usize;
+
+        let mut certs_buf: KVec<u8> = KVec::new();
+
+        certs_buf.extend_from_slice(
+            &response_vec[8..(8 + response.portion_length as usize)],
+            GFP_KERNEL,
+        )?;
+
+        let mut offset: usize = response.portion_length as usize;
+        let mut remainder_length = response.remainder_length as usize;
+
+        while remainder_length > 0 {
+            request.offset = offset.to_le() as u16;
+            request.length = (remainder_length
+                .min(rsp_sz - core::mem::size_of::<GetCertificateRsp>()))
+            .to_le() as u16;
+
+            let response = self.get_cert_exchange(request_buf, &mut response_vec, rsp_sz)?;
+
+            if response.portion_length == 0
+                || (response.param1 & 0xF) != slot
+                || offset as u16 + response.portion_length + response.remainder_length
+                    != total_cert_len as u16
+            {
+                pr_err!("Malformed certificate response\n");
+                to_result(-(bindings::EPROTO as i32))?;
+            }
+
+            certs_buf.extend_from_slice(
+                &response_vec[8..(8 + response.portion_length as usize)],
+                GFP_KERNEL,
+            )?;
+            offset += response.portion_length as usize;
+            remainder_length = response.remainder_length as usize;
+        }
+
+        let header_length = core::mem::size_of::<SpdmCertChain>() + self.hash_len;
+
+        let ptr = certs_buf.as_mut_ptr();
+        // SAFETY: `SpdmCertChain` is repr(C) and packed, so we can convert it from a slice
+        let ptr = ptr.cast::<SpdmCertChain>();
+        // SAFETY: `ptr` came from a reference and the cast above is valid.
+        let certs: &mut SpdmCertChain = unsafe { &mut *ptr };
+
+        if total_cert_len < header_length
+            || total_cert_len != usize::from_le(certs.length as usize)
+            || total_cert_len != certs_buf.len()
+        {
+            pr_err!("Malformed certificate chain in slot {slot}\n");
+            to_result(-(bindings::EPROTO as i32))?;
+        }
+
+        self.slot_sz[slot as usize] = total_cert_len;
+        self.slot[slot as usize] = Some(certs_buf);
+
+        Ok(())
+    }
 }
diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs
index 2150a23997db..a8bc3378676f 100644
--- a/lib/rspdm/validator.rs
+++ b/lib/rspdm/validator.rs
@@ -17,8 +17,9 @@
 };
 
 use crate::consts::{
-    SPDM_ASYM_ALGOS, SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, SPDM_GET_DIGESTS, SPDM_GET_VERSION,
-    SPDM_HASH_ALGOS, SPDM_MEAS_SPEC_DMTF, SPDM_NEGOTIATE_ALGS, SPDM_REQ_CAPS,
+    SPDM_ASYM_ALGOS, SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, SPDM_GET_CERTIFICATE,
+    SPDM_GET_DIGESTS, SPDM_GET_VERSION, SPDM_HASH_ALGOS, SPDM_MEAS_SPEC_DMTF, SPDM_NEGOTIATE_ALGS,
+    SPDM_REQ_CAPS,
 };
 
 #[repr(C, packed)]
@@ -364,3 +365,62 @@ fn validate(unvalidated: &mut Unvalidated<KVec<u8>>) -> Result<Self, Self::Err>
         Ok(rsp)
     }
 }
+
+#[repr(C, packed)]
+pub(crate) struct GetCertificateReq {
+    pub(crate) version: u8,
+    pub(crate) code: u8,
+    pub(crate) param1: u8,
+    pub(crate) param2: u8,
+
+    pub(crate) offset: u16,
+    pub(crate) length: u16,
+}
+
+impl Default for GetCertificateReq {
+    fn default() -> Self {
+        GetCertificateReq {
+            version: 0,
+            code: SPDM_GET_CERTIFICATE,
+            param1: 0,
+            param2: 0,
+            offset: 0,
+            length: 0,
+        }
+    }
+}
+
+#[repr(C, packed)]
+pub(crate) struct GetCertificateRsp {
+    pub(crate) version: u8,
+    pub(crate) code: u8,
+    pub(crate) param1: u8,
+    pub(crate) param2: u8,
+
+    pub(crate) portion_length: u16,
+    pub(crate) remainder_length: u16,
+
+    pub(crate) cert_chain: __IncompleteArrayField<u8>,
+}
+
+impl Validate<&mut Unvalidated<KVec<u8>>> for &mut GetCertificateRsp {
+    type Err = Error;
+
+    fn validate(unvalidated: &mut Unvalidated<KVec<u8>>) -> Result<Self, Self::Err> {
+        let raw = unvalidated.raw_mut();
+        if raw.len() < mem::size_of::<GetCertificateRsp>() {
+            return Err(EINVAL);
+        }
+
+        let ptr = raw.as_mut_ptr();
+        // CAST: `GetCertificateRsp` only contains integers and has `repr(C)`.
+        let ptr = ptr.cast::<GetCertificateRsp>();
+        // SAFETY: `ptr` came from a reference and the cast above is valid.
+        let rsp: &mut GetCertificateRsp = unsafe { &mut *ptr };
+
+        rsp.portion_length = rsp.portion_length.to_le();
+        rsp.remainder_length = rsp.remainder_length.to_le();
+
+        Ok(rsp)
+    }
+}
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [RFC v2 15/20] crypto: asymmetric_keys - Load certificate parsing early in boot
  2025-02-27  3:09 [RFC v2 00/20] lib: Rust implementation of SPDM Alistair Francis
                   ` (13 preceding siblings ...)
  2025-02-27  3:09 ` [RFC v2 14/20] lib: rspdm: Support SPDM get_certificate Alistair Francis
@ 2025-02-27  3:09 ` Alistair Francis
  2025-02-27  3:09 ` [RFC v2 16/20] KEYS: Load keyring and certificates " Alistair Francis
                   ` (5 subsequent siblings)
  20 siblings, 0 replies; 47+ messages in thread
From: Alistair Francis @ 2025-02-27  3:09 UTC (permalink / raw)
  To: linux-cxl, linux-kernel, lukas, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm
  Cc: boqun.feng, bjorn3_gh, wilfred.mallawa, aliceryhl, ojeda,
	alistair23, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

Work is ongoing to support PCIe device attestation and authentication.
As part of this a PCIe device will provide a X.509 certificate chain
via the SPDM protocol to the kernel.

Linux should verify the chain before enabling the device, which means we
need the certificate store ready before arch initilisation (where PCIe
init happens). Move the certificate and keyring init to postcore to
ensure it's loaded before PCIe devices.

This patch enables X.509 certificate parsing and asymmetric key support
early in the boot process so that it can be used by the key store and
SPDM to verify the certificate chain provided by a PCIe device
via SPDM before we enable it.

Signed-off-by: Alistair Francis <alistair@alistair23.me>
---
 crypto/asymmetric_keys/asymmetric_type.c | 2 +-
 crypto/asymmetric_keys/x509_public_key.c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c
index ba2d9d1ea235..44ebae5c059c 100644
--- a/crypto/asymmetric_keys/asymmetric_type.c
+++ b/crypto/asymmetric_keys/asymmetric_type.c
@@ -671,5 +671,5 @@ static void __exit asymmetric_key_cleanup(void)
 	unregister_key_type(&key_type_asymmetric);
 }
 
-module_init(asymmetric_key_init);
+postcore_initcall(asymmetric_key_init);
 module_exit(asymmetric_key_cleanup);
diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c
index 8409d7d36cb4..997f3e7910d8 100644
--- a/crypto/asymmetric_keys/x509_public_key.c
+++ b/crypto/asymmetric_keys/x509_public_key.c
@@ -246,7 +246,7 @@ static void __exit x509_key_exit(void)
 	unregister_asymmetric_key_parser(&x509_key_parser);
 }
 
-module_init(x509_key_init);
+postcore_initcall(x509_key_init);
 module_exit(x509_key_exit);
 
 MODULE_DESCRIPTION("X.509 certificate parser");
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [RFC v2 16/20] KEYS: Load keyring and certificates early in boot
  2025-02-27  3:09 [RFC v2 00/20] lib: Rust implementation of SPDM Alistair Francis
                   ` (14 preceding siblings ...)
  2025-02-27  3:09 ` [RFC v2 15/20] crypto: asymmetric_keys - Load certificate parsing early in boot Alistair Francis
@ 2025-02-27  3:09 ` Alistair Francis
  2025-02-27  3:09 ` [RFC v2 17/20] PCI/CMA: Support built in X.509 certificates Alistair Francis
                   ` (4 subsequent siblings)
  20 siblings, 0 replies; 47+ messages in thread
From: Alistair Francis @ 2025-02-27  3:09 UTC (permalink / raw)
  To: linux-cxl, linux-kernel, lukas, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm
  Cc: boqun.feng, bjorn3_gh, wilfred.mallawa, aliceryhl, ojeda,
	alistair23, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

Work is ongoing to support PCIe device attestation and authentication.
As part of this a PCIe device will provide a certificate chain via the
SPDM protocol to the kernel.

Linux should verify the chain before enabling the device, which means we
need the certificate store ready before arch initilisation (where PCIe
init happens). Move the certificate and keyring init to postcore to
ensure it's loaded before PCIe devices.

This allows us to verify the certificate chain provided by a PCIe device
via SPDM before we enable it.

Signed-off-by: Alistair Francis <alistair@alistair23.me>
---
 certs/system_keyring.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/certs/system_keyring.c b/certs/system_keyring.c
index 9de610bf1f4b..f3d8ea4f70b4 100644
--- a/certs/system_keyring.c
+++ b/certs/system_keyring.c
@@ -260,7 +260,7 @@ static __init int system_trusted_keyring_init(void)
 /*
  * Must be initialised before we try and load the keys into the keyring.
  */
-device_initcall(system_trusted_keyring_init);
+postcore_initcall(system_trusted_keyring_init);
 
 __init int load_module_cert(struct key *keyring)
 {
@@ -293,7 +293,7 @@ static __init int load_system_certificate_list(void)
 
 	return x509_load_certificate_list(p, size, builtin_trusted_keys);
 }
-late_initcall(load_system_certificate_list);
+postcore_initcall(load_system_certificate_list);
 
 #ifdef CONFIG_SYSTEM_DATA_VERIFICATION
 
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [RFC v2 17/20] PCI/CMA: Support built in X.509 certificates
  2025-02-27  3:09 [RFC v2 00/20] lib: Rust implementation of SPDM Alistair Francis
                   ` (15 preceding siblings ...)
  2025-02-27  3:09 ` [RFC v2 16/20] KEYS: Load keyring and certificates " Alistair Francis
@ 2025-02-27  3:09 ` Alistair Francis
  2025-02-27  3:09 ` [RFC v2 18/20] lib: rspdm: Support SPDM certificate validation Alistair Francis
                   ` (3 subsequent siblings)
  20 siblings, 0 replies; 47+ messages in thread
From: Alistair Francis @ 2025-02-27  3:09 UTC (permalink / raw)
  To: linux-cxl, linux-kernel, lukas, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm
  Cc: boqun.feng, bjorn3_gh, wilfred.mallawa, aliceryhl, ojeda,
	alistair23, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

Support building the X.509 certificates into the CMA certificate store.
This allows certificates to be built into the kernel which can be used
to authenticate PCIe devices via SPDM.

Signed-off-by: Alistair Francis <alistair@alistair23.me>
---
 drivers/pci/cma.c | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/drivers/pci/cma.c b/drivers/pci/cma.c
index 59558714f143..381d8f32a5a7 100644
--- a/drivers/pci/cma.c
+++ b/drivers/pci/cma.c
@@ -24,6 +24,10 @@
 /* Keyring that userspace can poke certs into */
 static struct key *pci_cma_keyring;
 
+extern __initconst const u8 system_certificate_list[];
+extern __initconst const unsigned long system_certificate_list_size;
+extern __initconst const unsigned long module_cert_size;
+
 /*
  * The spdm_requester.c library calls pci_cma_validate() to check requirements
  * for Leaf Certificates per PCIe r6.1 sec 6.31.3.
@@ -222,8 +226,31 @@ void pci_cma_destroy(struct pci_dev *pdev)
 	spdm_destroy(pdev->spdm_state);
 }
 
+/*
+ * Load the compiled-in list of X.509 certificates.
+ */
+static int load_system_certificate_list(void)
+{
+	const u8 *p;
+	unsigned long size;
+
+	pr_notice("Loading compiled-in X.509 certificates for CMA\n");
+
+#ifdef CONFIG_MODULE_SIG
+	p = system_certificate_list;
+	size = system_certificate_list_size;
+#else
+	p = system_certificate_list + module_cert_size;
+	size = system_certificate_list_size - module_cert_size;
+#endif
+
+	return x509_load_certificate_list(p, size, pci_cma_keyring);
+}
+
 __init static int pci_cma_keyring_init(void)
 {
+	int rc;
+
 	pci_cma_keyring = keyring_alloc(".cma", KUIDT_INIT(0), KGIDT_INIT(0),
 					current_cred(),
 					(KEY_POS_ALL & ~KEY_POS_SETATTR) |
@@ -236,6 +263,10 @@ __init static int pci_cma_keyring_init(void)
 		return PTR_ERR(pci_cma_keyring);
 	}
 
+	rc = load_system_certificate_list();
+	if (rc)
+		return rc;
+
 	return 0;
 }
 arch_initcall(pci_cma_keyring_init);
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [RFC v2 18/20] lib: rspdm: Support SPDM certificate validation
  2025-02-27  3:09 [RFC v2 00/20] lib: Rust implementation of SPDM Alistair Francis
                   ` (16 preceding siblings ...)
  2025-02-27  3:09 ` [RFC v2 17/20] PCI/CMA: Support built in X.509 certificates Alistair Francis
@ 2025-02-27  3:09 ` Alistair Francis
  2025-02-27  3:09 ` [RFC v2 19/20] rust: allow extracting the buffer from a CString Alistair Francis
                   ` (2 subsequent siblings)
  20 siblings, 0 replies; 47+ messages in thread
From: Alistair Francis @ 2025-02-27  3:09 UTC (permalink / raw)
  To: linux-cxl, linux-kernel, lukas, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm
  Cc: boqun.feng, bjorn3_gh, wilfred.mallawa, aliceryhl, ojeda,
	alistair23, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

Signed-off-by: Alistair Francis <alistair@alistair23.me>
---
 lib/rspdm/lib.rs                | 17 ++++++
 lib/rspdm/state.rs              | 95 ++++++++++++++++++++++++++++++++-
 rust/bindings/bindings_helper.h |  2 +
 3 files changed, 112 insertions(+), 2 deletions(-)

diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs
index 1535e3a69518..b7ad3b8c659e 100644
--- a/lib/rspdm/lib.rs
+++ b/lib/rspdm/lib.rs
@@ -137,6 +137,17 @@
         provisioned_slots &= !(1 << slot);
     }
 
+    let mut provisioned_slots = state.provisioned_slots;
+    while (provisioned_slots as usize) > 0 {
+        let slot = provisioned_slots.trailing_zeros() as u8;
+
+        if let Err(e) = state.validate_cert_chain(slot) {
+            return e.to_errno() as c_int;
+        }
+
+        provisioned_slots &= !(1 << slot);
+    }
+
     0
 }
 
@@ -145,6 +156,12 @@
 /// @spdm_state: SPDM session state
 #[no_mangle]
 pub unsafe extern "C" fn spdm_destroy(state: &'static mut SpdmState) {
+    if let Some(leaf_key) = &mut state.leaf_key {
+        unsafe {
+            bindings::public_key_free(*leaf_key);
+        }
+    }
+
     if let Some(desc) = &mut state.desc {
         unsafe {
             bindings::kfree(*desc as *mut _ as *mut c_void);
diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs
index ae259623fe50..974d2ee8c0ce 100644
--- a/lib/rspdm/state.rs
+++ b/lib/rspdm/state.rs
@@ -12,7 +12,7 @@
 use kernel::prelude::*;
 use kernel::{
     bindings,
-    error::{code::EINVAL, to_result, Error},
+    error::{code::EINVAL, from_err_ptr, to_result, Error},
     str::CStr,
     validate::Untrusted,
 };
@@ -76,9 +76,10 @@
 /// @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.
 ///
 /// `authenticated`: Whether device was authenticated successfully.
-#[expect(dead_code)]
 pub struct SpdmState {
     pub(crate) dev: *mut bindings::device,
     pub(crate) transport: bindings::spdm_transport,
@@ -111,6 +112,7 @@ pub struct SpdmState {
     // Certificates
     pub(crate) slot: [Option<KVec<u8>>; SPDM_SLOTS],
     slot_sz: [usize; SPDM_SLOTS],
+    pub(crate) leaf_key: Option<*mut bindings::public_key>,
 }
 
 #[repr(C, packed)]
@@ -153,6 +155,7 @@ pub(crate) fn new(
             authenticated: false,
             slot: [const { None }; SPDM_SLOTS],
             slot_sz: [0; SPDM_SLOTS],
+            leaf_key: None,
         }
     }
 
@@ -745,4 +748,92 @@ pub(crate) fn get_certificate(&mut self, slot: u8) -> Result<(), Error> {
 
         Ok(())
     }
+
+    pub(crate) fn validate_cert_chain(&mut self, slot: u8) -> Result<(), Error> {
+        let cert_chain_buf = self.slot[slot as usize].as_ref().ok_or(ENOMEM)?;
+        let cert_chain_len = self.slot_sz[slot as usize];
+        let header_len = 4 + self.hash_len;
+
+        let mut offset = header_len;
+        let mut prev_cert: Option<*mut bindings::x509_certificate> = None;
+
+        while offset < cert_chain_len {
+            let cert_len = unsafe {
+                bindings::x509_get_certificate_length(
+                    &cert_chain_buf[offset..] as *const _ as *const u8,
+                    cert_chain_len - offset,
+                )
+            };
+
+            if cert_len < 0 {
+                pr_err!("Invalid certificate length\n");
+                to_result(cert_len as i32)?;
+            }
+
+            let _is_leaf_cert = if offset + cert_len as usize == cert_chain_len {
+                true
+            } else {
+                false
+            };
+
+            let cert_ptr = unsafe {
+                from_err_ptr(bindings::x509_cert_parse(
+                    &cert_chain_buf[offset..] as *const _ as *const c_void,
+                    cert_len as usize,
+                ))?
+            };
+            let cert = unsafe { *cert_ptr };
+
+            if cert.unsupported_sig || cert.blacklisted {
+                to_result(-(bindings::EKEYREJECTED as i32))?;
+            }
+
+            if let Some(prev) = prev_cert {
+                // Check against previous certificate
+                let rc = unsafe { bindings::public_key_verify_signature((*prev).pub_, cert.sig) };
+
+                if rc < 0 {
+                    pr_err!("Signature validation error\n");
+                    to_result(rc)?;
+                }
+            } else {
+                // Check aginst root keyring
+                let key = unsafe {
+                    from_err_ptr(bindings::find_asymmetric_key(
+                        self.keyring,
+                        (*cert.sig).auth_ids[0],
+                        (*cert.sig).auth_ids[1],
+                        (*cert.sig).auth_ids[2],
+                        false,
+                    ))?
+                };
+
+                let rc = unsafe { bindings::verify_signature(key, cert.sig) };
+                unsafe { bindings::key_put(key) };
+
+                if rc < 0 {
+                    pr_err!("Root signature validation error\n");
+                    to_result(rc)?;
+                }
+            }
+
+            if let Some(prev) = prev_cert {
+                unsafe { bindings::x509_free_certificate(prev) };
+            }
+
+            prev_cert = Some(cert_ptr);
+            offset += cert_len as usize;
+        }
+
+        if let Some(prev) = prev_cert {
+            if let Some(validate) = self.validate {
+                let rc = unsafe { validate(self.dev, slot, prev) };
+                to_result(rc)?;
+            }
+
+            self.leaf_key = unsafe { Some((*prev).pub_) };
+        }
+
+        Ok(())
+    }
 }
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 5ba1191429f8..daeb599fb990 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -21,6 +21,8 @@
 #include <linux/hash.h>
 #include <linux/jiffies.h>
 #include <linux/jump_label.h>
+#include <keys/asymmetric-type.h>
+#include <keys/x509-parser.h>
 #include <linux/mdio.h>
 #include <linux/miscdevice.h>
 #include <linux/of_device.h>
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [RFC v2 19/20] rust: allow extracting the buffer from a CString
  2025-02-27  3:09 [RFC v2 00/20] lib: Rust implementation of SPDM Alistair Francis
                   ` (17 preceding siblings ...)
  2025-02-27  3:09 ` [RFC v2 18/20] lib: rspdm: Support SPDM certificate validation Alistair Francis
@ 2025-02-27  3:09 ` Alistair Francis
  2025-02-27  7:52   ` Alice Ryhl
  2025-02-27  3:09 ` [RFC v2 20/20] lib: rspdm: Support SPDM challenge Alistair Francis
  2025-06-11 13:37 ` [RFC v2 00/20] lib: Rust implementation of SPDM Jonathan Cameron
  20 siblings, 1 reply; 47+ messages in thread
From: Alistair Francis @ 2025-02-27  3:09 UTC (permalink / raw)
  To: linux-cxl, linux-kernel, lukas, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm
  Cc: boqun.feng, bjorn3_gh, wilfred.mallawa, aliceryhl, ojeda,
	alistair23, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

The kernel CString is a wrapper aroud a KVec. This patch allows
retrieving the underlying buffer and consuming the CString. This allows
users to create a CString from a string and then retrieve the underlying
buffer.

Signed-off-by: Alistair Francis <alistair@alistair23.me>
---
 rust/kernel/str.rs | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
index 28e2201604d6..76862e91b81a 100644
--- a/rust/kernel/str.rs
+++ b/rust/kernel/str.rs
@@ -855,6 +855,11 @@ pub fn try_from_fmt(args: fmt::Arguments<'_>) -> Result<Self, Error> {
         // exist in the buffer.
         Ok(Self { buf })
     }
+
+    /// Return the internal buffer while consuming the original [`CString`]
+    pub fn take_buffer(self) -> KVec<u8> {
+        self.buf
+    }
 }
 
 impl Deref for CString {
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [RFC v2 20/20] lib: rspdm: Support SPDM challenge
  2025-02-27  3:09 [RFC v2 00/20] lib: Rust implementation of SPDM Alistair Francis
                   ` (18 preceding siblings ...)
  2025-02-27  3:09 ` [RFC v2 19/20] rust: allow extracting the buffer from a CString Alistair Francis
@ 2025-02-27  3:09 ` Alistair Francis
  2025-06-11 13:37 ` [RFC v2 00/20] lib: Rust implementation of SPDM Jonathan Cameron
  20 siblings, 0 replies; 47+ messages in thread
From: Alistair Francis @ 2025-02-27  3:09 UTC (permalink / raw)
  To: linux-cxl, linux-kernel, lukas, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm
  Cc: boqun.feng, bjorn3_gh, wilfred.mallawa, aliceryhl, ojeda,
	alistair23, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

Signed-off-by: Alistair Francis <alistair@alistair23.me>
---
 lib/rspdm/consts.rs             |   6 +
 lib/rspdm/lib.rs                |   8 +-
 lib/rspdm/state.rs              | 212 ++++++++++++++++++++++++++++++--
 lib/rspdm/validator.rs          |  65 +++++++++-
 rust/bindings/bindings_helper.h |   1 +
 5 files changed, 283 insertions(+), 9 deletions(-)

diff --git a/lib/rspdm/consts.rs b/lib/rspdm/consts.rs
index ffaf36ec583b..af4e1a98dc5b 100644
--- a/lib/rspdm/consts.rs
+++ b/lib/rspdm/consts.rs
@@ -98,6 +98,8 @@ pub(crate) enum SpdmErrorCode {
 
 pub(crate) const SPDM_GET_CERTIFICATE: u8 = 0x82;
 
+pub(crate) const SPDM_CHALLENGE: u8 = 0x83;
+
 #[cfg(CONFIG_CRYPTO_RSA)]
 pub(crate) const SPDM_ASYM_RSA: u32 =
     SPDM_ASYM_RSASSA_2048 | SPDM_ASYM_RSASSA_3072 | SPDM_ASYM_RSASSA_4096;
@@ -127,3 +129,7 @@ pub(crate) enum SpdmErrorCode {
 // pub(crate) const SPDM_MAX_REQ_ALG_STRUCT: usize = 4;
 
 pub(crate) const SPDM_OPAQUE_DATA_FMT_GENERAL: u8 = 1 << 1;
+
+pub(crate) const SPDM_PREFIX_SZ: usize = 64;
+pub(crate) const SPDM_COMBINED_PREFIX_SZ: usize = 100;
+pub(crate) const SPDM_MAX_OPAQUE_DATA: usize = 1024;
diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs
index b7ad3b8c659e..c0e3e039c14f 100644
--- a/lib/rspdm/lib.rs
+++ b/lib/rspdm/lib.rs
@@ -137,17 +137,23 @@
         provisioned_slots &= !(1 << slot);
     }
 
+    let mut verify = true;
     let mut provisioned_slots = state.provisioned_slots;
     while (provisioned_slots as usize) > 0 {
         let slot = provisioned_slots.trailing_zeros() as u8;
 
         if let Err(e) = state.validate_cert_chain(slot) {
-            return e.to_errno() as c_int;
+            pr_debug!("Certificate in slot {slot} failed to verify: {e:?}");
+            verify = false;
         }
 
         provisioned_slots &= !(1 << slot);
     }
 
+    if let Err(e) = state.challenge(state.provisioned_slots.trailing_zeros() as u8, verify) {
+        return e.to_errno() as c_int;
+    }
+
     0
 }
 
diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs
index 974d2ee8c0ce..4629c12a1b81 100644
--- a/lib/rspdm/state.rs
+++ b/lib/rspdm/state.rs
@@ -14,23 +14,27 @@
     bindings,
     error::{code::EINVAL, from_err_ptr, to_result, Error},
     str::CStr,
+    str::CString,
     validate::Untrusted,
 };
 
 use crate::consts::{
     SpdmErrorCode, SPDM_ASYM_ALGOS, SPDM_ASYM_ECDSA_ECC_NIST_P256, SPDM_ASYM_ECDSA_ECC_NIST_P384,
     SPDM_ASYM_ECDSA_ECC_NIST_P521, SPDM_ASYM_RSASSA_2048, SPDM_ASYM_RSASSA_3072,
-    SPDM_ASYM_RSASSA_4096, SPDM_ERROR, SPDM_GET_VERSION_LEN, SPDM_HASH_ALGOS, SPDM_HASH_SHA_256,
-    SPDM_HASH_SHA_384, SPDM_HASH_SHA_512, SPDM_KEY_EX_CAP, SPDM_MAX_VER, SPDM_MEAS_CAP_MASK,
-    SPDM_MEAS_SPEC_DMTF, SPDM_MIN_DATA_TRANSFER_SIZE, SPDM_MIN_VER, SPDM_OPAQUE_DATA_FMT_GENERAL,
+    SPDM_ASYM_RSASSA_4096, SPDM_COMBINED_PREFIX_SZ, SPDM_ERROR, SPDM_GET_VERSION_LEN,
+    SPDM_HASH_ALGOS, SPDM_HASH_SHA_256, SPDM_HASH_SHA_384, SPDM_HASH_SHA_512, SPDM_KEY_EX_CAP,
+    SPDM_MAX_OPAQUE_DATA, SPDM_MAX_VER, SPDM_MEAS_CAP_MASK, SPDM_MEAS_SPEC_DMTF,
+    SPDM_MIN_DATA_TRANSFER_SIZE, SPDM_MIN_VER, SPDM_OPAQUE_DATA_FMT_GENERAL, SPDM_PREFIX_SZ,
     SPDM_REQ, SPDM_RSP_MIN_CAPS, SPDM_SLOTS, SPDM_VER_10, SPDM_VER_11, SPDM_VER_12,
 };
 use crate::validator::{
-    GetCapabilitiesReq, GetCapabilitiesRsp, GetCertificateReq, GetCertificateRsp, GetDigestsReq,
-    GetDigestsRsp, GetVersionReq, GetVersionRsp, NegotiateAlgsReq, NegotiateAlgsRsp, RegAlg,
-    SpdmErrorRsp, SpdmHeader,
+    ChallengeReq, ChallengeRsp, GetCapabilitiesReq, GetCapabilitiesRsp, GetCertificateReq,
+    GetCertificateRsp, GetDigestsReq, GetDigestsRsp, GetVersionReq, GetVersionRsp,
+    NegotiateAlgsReq, NegotiateAlgsRsp, RegAlg, SpdmErrorRsp, SpdmHeader,
 };
 
+const SPDM_CONTEXT: &str = "responder-challenge_auth signing";
+
 /// The current SPDM session state for a device. Based on the
 /// C `struct spdm_state`.
 ///
@@ -78,6 +82,12 @@
 /// @slot_sz: Certificate chain size (in bytes).
 /// @leaf_key: Public key portion of leaf certificate against which to check
 ///  responder's signatures.
+/// @transcript: Concatenation of all SPDM messages exchanged during an
+///  authentication or measurement sequence.  Used to verify the signature,
+///  as it is computed over the hashed transcript.
+/// @next_nonce: Requester nonce to be used for the next authentication
+///  sequence.  Populated from user space through sysfs.
+///  If user space does not provide a nonce, the kernel uses a random one.
 ///
 /// `authenticated`: Whether device was authenticated successfully.
 pub struct SpdmState {
@@ -113,6 +123,10 @@ pub struct SpdmState {
     pub(crate) slot: [Option<KVec<u8>>; SPDM_SLOTS],
     slot_sz: [usize; SPDM_SLOTS],
     pub(crate) leaf_key: Option<*mut bindings::public_key>,
+
+    transcript: KVec<u8>,
+
+    next_nonce: Option<&'static mut [u8]>,
 }
 
 #[repr(C, packed)]
@@ -156,6 +170,8 @@ pub(crate) fn new(
             slot: [const { None }; SPDM_SLOTS],
             slot_sz: [0; SPDM_SLOTS],
             leaf_key: None,
+            transcript: KVec::new(),
+            next_nonce: None,
         }
     }
 
@@ -263,7 +279,7 @@ fn spdm_err(&self, rsp: &SpdmErrorRsp) -> Result<(), Error> {
     /// The data in `request_buf` is sent to the device and the response is
     /// stored in `response_buf`.
     pub(crate) fn spdm_exchange(
-        &self,
+        &mut self,
         request_buf: &mut [u8],
         response_buf: &mut [u8],
     ) -> Result<i32, Error> {
@@ -271,6 +287,8 @@ pub(crate) fn spdm_exchange(
         let request: &mut SpdmHeader = Untrusted::new_mut(request_buf).validate_mut()?;
         let response: &SpdmHeader = Untrusted::new_ref(response_buf).validate()?;
 
+        self.transcript.extend_from_slice(request_buf, GFP_KERNEL)?;
+
         let transport_function = self.transport.ok_or(EINVAL)?;
         // SAFETY: `transport_function` is provided by the new(), we are
         // calling the function.
@@ -337,6 +355,12 @@ pub(crate) fn get_version(&mut self) -> Result<(), Error> {
         unsafe { response_vec.set_len(rc as usize) };
 
         let response: &mut GetVersionRsp = Untrusted::new_mut(&mut response_vec).validate_mut()?;
+        let rsp_sz = core::mem::size_of::<SpdmHeader>()
+            + 2
+            + response.version_number_entry_count as usize * 2;
+
+        self.transcript
+            .extend_from_slice(&response_vec[..rsp_sz], GFP_KERNEL)?;
 
         let mut foundver = false;
         for i in 0..response.version_number_entry_count {
@@ -400,6 +424,9 @@ pub(crate) fn get_capabilities(&mut self) -> Result<(), Error> {
         let response: &mut GetCapabilitiesRsp =
             Untrusted::new_mut(&mut response_vec).validate_mut()?;
 
+        self.transcript
+            .extend_from_slice(&response_vec[..rsp_sz], GFP_KERNEL)?;
+
         self.rsp_caps = u32::from_le(response.flags);
         if (self.rsp_caps & SPDM_RSP_MIN_CAPS) != SPDM_RSP_MIN_CAPS {
             to_result(-(bindings::EPROTONOSUPPORT as i32))?;
@@ -539,6 +566,9 @@ pub(crate) fn negotiate_algs(&mut self) -> Result<(), Error> {
         let response: &mut NegotiateAlgsRsp =
             Untrusted::new_mut(&mut response_vec).validate_mut()?;
 
+        self.transcript
+            .extend_from_slice(&response_vec[..rsp_sz], GFP_KERNEL)?;
+
         self.base_asym_alg = response.base_asym_sel;
         self.base_hash_alg = response.base_hash_sel;
         self.meas_hash_alg = response.measurement_hash_algo;
@@ -598,6 +628,10 @@ pub(crate) fn get_digests(&mut self) -> Result<(), Error> {
         unsafe { response_vec.set_len(rc as usize) };
 
         let response: &mut GetDigestsRsp = Untrusted::new_mut(&mut response_vec).validate_mut()?;
+        let rsp_sz = core::mem::size_of::<SpdmHeader>() + response.param2 as usize * self.hash_len;
+
+        self.transcript
+            .extend_from_slice(&response_vec[..rsp_sz], GFP_KERNEL)?;
 
         if rc
             < (core::mem::size_of::<GetDigestsReq>()
@@ -659,6 +693,10 @@ fn get_cert_exchange(
         unsafe { response_vec.set_len(rc as usize) };
 
         let response: &mut GetCertificateRsp = Untrusted::new_mut(response_vec).validate_mut()?;
+        let rsp_sz = core::mem::size_of::<SpdmHeader>() + 4 + response.portion_length as usize;
+
+        self.transcript
+            .extend_from_slice(&response_vec[..rsp_sz], GFP_KERNEL)?;
 
         if rc
             < (core::mem::size_of::<GetCertificateRsp>() + response.portion_length as usize) as i32
@@ -836,4 +874,164 @@ pub(crate) fn validate_cert_chain(&mut self, slot: u8) -> Result<(), Error> {
 
         Ok(())
     }
+
+    pub(crate) fn challenge_rsp_len(&mut self, nonce_len: usize, opaque_len: usize) -> usize {
+        let mut length =
+            core::mem::size_of::<SpdmHeader>() + self.hash_len + nonce_len + opaque_len + 2;
+
+        if self.version >= 0x13 {
+            length += 8;
+        }
+
+        length + self.sig_len
+    }
+
+    fn verify_signature(&mut self, response_vec: &mut [u8]) -> Result<(), Error> {
+        let sig_start = response_vec.len() - self.sig_len;
+        let mut sig = bindings::public_key_signature::default();
+        let mut mhash: KVec<u8> = KVec::new();
+
+        sig.s = &mut response_vec[sig_start..] as *mut _ as *mut u8;
+        sig.s_size = self.sig_len as u32;
+        sig.encoding = self.base_asym_enc.as_ptr();
+        sig.hash_algo = self.base_hash_alg_name.as_ptr();
+
+        let mut m: KVec<u8> = KVec::new();
+        m.extend_with(SPDM_COMBINED_PREFIX_SZ + self.hash_len, 0, GFP_KERNEL)?;
+
+        if let Some(desc) = &mut self.desc {
+            desc.tfm = self.shash;
+
+            unsafe {
+                to_result(bindings::crypto_shash_digest(
+                    *desc,
+                    self.transcript.as_ptr(),
+                    (self.transcript.len() - self.sig_len) as u32,
+                    m[SPDM_COMBINED_PREFIX_SZ..].as_mut_ptr(),
+                ))?;
+            };
+        } else {
+            to_result(-(bindings::EPROTO as i32))?;
+        }
+
+        if self.version <= 0x11 {
+            sig.digest = m[SPDM_COMBINED_PREFIX_SZ..].as_mut_ptr();
+        } else {
+            let major = self.version >> 4;
+            let minor = self.version & 0xF;
+
+            let output = CString::try_from_fmt(fmt!("dmtf-spdm-v{major:x}.{minor:x}.*dmtf-spdm-v{major:x}.{minor:x}.*dmtf-spdm-v{major:x}.{minor:x}.*dmtf-spdm-v{major:x}.{minor:x}.*"))?;
+            let mut buf = output.take_buffer();
+            let zero_pad_len = SPDM_COMBINED_PREFIX_SZ - SPDM_PREFIX_SZ - SPDM_CONTEXT.len() - 1;
+
+            buf.extend_with(zero_pad_len, 0, GFP_KERNEL)?;
+            buf.extend_from_slice(SPDM_CONTEXT.as_bytes(), GFP_KERNEL)?;
+
+            m[..SPDM_COMBINED_PREFIX_SZ].copy_from_slice(&buf);
+
+            mhash.extend_with(self.hash_len, 0, GFP_KERNEL)?;
+
+            if let Some(desc) = &mut self.desc {
+                desc.tfm = self.shash;
+
+                unsafe {
+                    to_result(bindings::crypto_shash_digest(
+                        *desc,
+                        m.as_ptr(),
+                        m.len() as u32,
+                        mhash.as_mut_ptr(),
+                    ))?;
+                };
+            } else {
+                to_result(-(bindings::EPROTO as i32))?;
+            }
+
+            sig.digest = mhash.as_mut_ptr();
+        }
+
+        sig.digest_size = self.hash_len as u32;
+
+        if let Some(leaf_key) = self.leaf_key {
+            unsafe { to_result(bindings::public_key_verify_signature(leaf_key, &sig)) }
+        } else {
+            to_result(-(bindings::EPROTO as i32))
+        }
+    }
+
+    pub(crate) fn challenge(&mut self, slot: u8, verify: bool) -> Result<(), Error> {
+        let mut request = ChallengeReq::default();
+        request.version = self.version;
+        request.param1 = slot;
+
+        let nonce_len = request.nonce.len();
+
+        if let Some(nonce) = &self.next_nonce {
+            request.nonce.copy_from_slice(&nonce);
+            self.next_nonce = None;
+        } else {
+            unsafe {
+                bindings::get_random_bytes(&mut request.nonce as *mut _ as *mut c_void, nonce_len)
+            };
+        }
+
+        let req_sz = if self.version <= 0x12 {
+            core::mem::size_of::<ChallengeReq>() - 8
+        } else {
+            core::mem::size_of::<ChallengeReq>()
+        };
+
+        let rsp_sz = self.challenge_rsp_len(nonce_len, SPDM_MAX_OPAQUE_DATA);
+
+        // SAFETY: `request` is repr(C) and packed, so we can convert it to a slice
+        let request_buf = unsafe { from_raw_parts_mut(&mut request as *mut _ as *mut u8, req_sz) };
+
+        let mut response_vec: KVec<u8> = KVec::with_capacity(rsp_sz, GFP_KERNEL)?;
+        // SAFETY: `request` is repr(C) and packed, so we can convert it to a slice
+        let response_buf = unsafe { from_raw_parts_mut(response_vec.as_mut_ptr(), rsp_sz) };
+
+        let rc = self.spdm_exchange(request_buf, response_buf)?;
+
+        if rc < (core::mem::size_of::<ChallengeRsp>() as i32) {
+            pr_err!("Truncated challenge response\n");
+            to_result(-(bindings::EIO as i32))?;
+        }
+
+        // SAFETY: `rc` bytes where inserted to the raw pointer by spdm_exchange
+        unsafe { response_vec.set_len(rc as usize) };
+
+        let _response: &mut ChallengeRsp = Untrusted::new_mut(&mut response_vec).validate_mut()?;
+
+        let opaque_len_offset = core::mem::size_of::<SpdmHeader>() + self.hash_len + nonce_len;
+        let opaque_len = u16::from_le_bytes(
+            response_vec[opaque_len_offset..(opaque_len_offset + 2)]
+                .try_into()
+                .unwrap_or([0, 0]),
+        );
+
+        let rsp_sz = self.challenge_rsp_len(nonce_len, opaque_len as usize);
+
+        if rc < rsp_sz as i32 {
+            pr_err!("Truncated challenge response\n");
+            to_result(-(bindings::EIO as i32))?;
+        }
+
+        self.transcript
+            .extend_from_slice(&response_vec[..rsp_sz], GFP_KERNEL)?;
+
+        if verify {
+            /* Verify signature at end of transcript against leaf key */
+            match self.verify_signature(&mut response_vec[..rsp_sz]) {
+                Ok(()) => {
+                    pr_info!("Authenticated with certificate slot {slot}");
+                    self.authenticated = true;
+                }
+                Err(e) => {
+                    pr_err!("Cannot verify challenge_auth signature: {e:?}");
+                    self.authenticated = false;
+                }
+            };
+        }
+
+        Ok(())
+    }
 }
diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs
index a8bc3378676f..f8a5337841f0 100644
--- a/lib/rspdm/validator.rs
+++ b/lib/rspdm/validator.rs
@@ -17,7 +17,7 @@
 };
 
 use crate::consts::{
-    SPDM_ASYM_ALGOS, SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, SPDM_GET_CERTIFICATE,
+    SPDM_ASYM_ALGOS, SPDM_CHALLENGE, SPDM_CTEXPONENT, SPDM_GET_CAPABILITIES, SPDM_GET_CERTIFICATE,
     SPDM_GET_DIGESTS, SPDM_GET_VERSION, SPDM_HASH_ALGOS, SPDM_MEAS_SPEC_DMTF, SPDM_NEGOTIATE_ALGS,
     SPDM_REQ_CAPS,
 };
@@ -424,3 +424,66 @@ fn validate(unvalidated: &mut Unvalidated<KVec<u8>>) -> Result<Self, Self::Err>
         Ok(rsp)
     }
 }
+
+#[repr(C, packed)]
+pub(crate) struct ChallengeReq {
+    pub(crate) version: u8,
+    pub(crate) code: u8,
+    pub(crate) param1: u8,
+    pub(crate) param2: u8,
+
+    pub(crate) nonce: [u8; 32],
+    pub(crate) context: [u8; 8],
+}
+
+impl Default for ChallengeReq {
+    fn default() -> Self {
+        ChallengeReq {
+            version: 0,
+            code: SPDM_CHALLENGE,
+            param1: 0,
+            param2: 0,
+            nonce: [0; 32],
+            context: [0; 8],
+        }
+    }
+}
+
+#[repr(C, packed)]
+pub(crate) struct ChallengeRsp {
+    pub(crate) version: u8,
+    pub(crate) code: u8,
+    pub(crate) param1: u8,
+    pub(crate) param2: u8,
+
+    pub(crate) cert_chain_hash: __IncompleteArrayField<u8>,
+    pub(crate) nonce: [u8; 32],
+    pub(crate) message_summary_hash: __IncompleteArrayField<u8>,
+
+    pub(crate) opaque_data_len: u16,
+    pub(crate) opaque_data: __IncompleteArrayField<u8>,
+
+    pub(crate) context: [u8; 8],
+    pub(crate) signature: __IncompleteArrayField<u8>,
+}
+
+impl Validate<&mut Unvalidated<KVec<u8>>> for &mut ChallengeRsp {
+    type Err = Error;
+
+    fn validate(unvalidated: &mut Unvalidated<KVec<u8>>) -> Result<Self, Self::Err> {
+        let raw = unvalidated.raw_mut();
+        if raw.len() < mem::size_of::<ChallengeRsp>() {
+            return Err(EINVAL);
+        }
+
+        let ptr = raw.as_mut_ptr();
+        // CAST: `ChallengeRsp` only contains integers and has `repr(C)`.
+        let ptr = ptr.cast::<ChallengeRsp>();
+        // SAFETY: `ptr` came from a reference and the cast above is valid.
+        let rsp: &mut ChallengeRsp = unsafe { &mut *ptr };
+
+        // rsp.opaque_data_len = rsp.opaque_data_len.to_le();
+
+        Ok(rsp)
+    }
+}
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index daeb599fb990..16b8602c6d1a 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -7,6 +7,7 @@
  */
 
 #include <crypto/hash.h>
+#include <crypto/public_key.h>
 #include <kunit/test.h>
 #include <linux/blk-mq.h>
 #include <linux/blk_types.h>
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* Re: [RFC v2 19/20] rust: allow extracting the buffer from a CString
  2025-02-27  3:09 ` [RFC v2 19/20] rust: allow extracting the buffer from a CString Alistair Francis
@ 2025-02-27  7:52   ` Alice Ryhl
  0 siblings, 0 replies; 47+ messages in thread
From: Alice Ryhl @ 2025-02-27  7:52 UTC (permalink / raw)
  To: Alistair Francis
  Cc: linux-cxl, linux-kernel, lukas, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm, boqun.feng, bjorn3_gh,
	wilfred.mallawa, ojeda, alistair23, a.hindborg, tmgross, gary,
	alex.gaynor, benno.lossin

On Thu, Feb 27, 2025 at 4:12 AM Alistair Francis <alistair@alistair23.me> wrote:
>
> The kernel CString is a wrapper aroud a KVec. This patch allows
> retrieving the underlying buffer and consuming the CString. This allows
> users to create a CString from a string and then retrieve the underlying
> buffer.
>
> Signed-off-by: Alistair Francis <alistair@alistair23.me>

I believe the idiomatic naming for a method like this is `into_vec`.

Alice

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC v2 14/20] lib: rspdm: Support SPDM get_certificate
  2025-02-27  3:09 ` [RFC v2 14/20] lib: rspdm: Support SPDM get_certificate Alistair Francis
@ 2025-02-27 10:58   ` Greg KH
  0 siblings, 0 replies; 47+ messages in thread
From: Greg KH @ 2025-02-27 10:58 UTC (permalink / raw)
  To: Alistair Francis
  Cc: linux-cxl, linux-kernel, lukas, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm, boqun.feng, bjorn3_gh,
	wilfred.mallawa, aliceryhl, ojeda, alistair23, a.hindborg,
	tmgross, gary, alex.gaynor, benno.lossin

On Thu, Feb 27, 2025 at 01:09:46PM +1000, Alistair Francis wrote:
> Signed-off-by: Alistair Francis <alistair@alistair23.me>
> ---
>  lib/rspdm/consts.rs    |   2 +
>  lib/rspdm/lib.rs       |  11 ++++
>  lib/rspdm/state.rs     | 123 ++++++++++++++++++++++++++++++++++++++++-
>  lib/rspdm/validator.rs |  64 ++++++++++++++++++++-
>  4 files changed, 196 insertions(+), 4 deletions(-)

Nit, I know I can't take patches without any changelog text, so that's
probably the same for most other maintainers, so you might want to fix
that for ths change on your next submission :)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated
  2025-02-27  3:09 ` [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated Alistair Francis
@ 2025-02-27 11:16   ` Greg KH
  2025-02-27 11:52     ` Alice Ryhl
  2025-02-27 22:42     ` Lukas Wunner
  0 siblings, 2 replies; 47+ messages in thread
From: Greg KH @ 2025-02-27 11:16 UTC (permalink / raw)
  To: Alistair Francis
  Cc: linux-cxl, linux-kernel, lukas, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm, boqun.feng, bjorn3_gh,
	wilfred.mallawa, aliceryhl, ojeda, alistair23, a.hindborg,
	tmgross, gary, alex.gaynor, benno.lossin, Alistair Francis

On Thu, Feb 27, 2025 at 01:09:41PM +1000, Alistair Francis wrote:
> From: Lukas Wunner <lukas@wunner.de>
> 
> The PCI core has just been amended to authenticate CMA-capable devices
> on enumeration and store the result in an "authenticated" bit in struct
> pci_dev->spdm_state.
> 
> Expose the bit to user space through an eponymous sysfs attribute.
> 
> Allow user space to trigger reauthentication (e.g. after it has updated
> the CMA keyring) by writing to the sysfs attribute.
> 
> Implement the attribute in the SPDM library so that other bus types
> besides PCI may take advantage of it.  They just need to add
> spdm_attr_group to the attribute groups of their devices and amend the
> dev_to_spdm_state() helper which retrieves the spdm_state for a given
> device.
> 
> The helper may return an ERR_PTR if it couldn't be determined whether
> SPDM is supported by the device.  The sysfs attribute is visible in that
> case but returns an error on access.  This prevents downgrade attacks
> where an attacker disturbs memory allocation or DOE communication
> in order to create the appearance that SPDM is unsupported.

I don't like this "if it's present we still don't know if the device
supports this", as that is not normally the "sysfs way" here.  Why must
it be present in those situations?  What is going to happen to suddenly
allow it to come back to be "alive" and working while the device is
still present in the system?

I'd prefer it to be "if the file is there, this is supported by the
device.  If the file is not there, it is not supported", as that makes
things so much simpler for userspace (i.e. you don't want userspace to
have to both test if the file is there AND read all entries just to see
if the kernel knows what is going on or not.)

Also, how will userspace know if the state changes from "unknown" to
"now it might work, try it again"?

> Subject to further discussion, a future commit might add a user-defined
> policy to forbid driver binding to devices which failed authentication,
> similar to the "authorized" attribute for USB.

That user-defined policy belongs in userspace, just like USB has it.
Why would the kernel need this?  Or do you mean that the kernel provides
a default like USB does and then requires userspace to manually enable a
device before binding a driver to the device is allowed?

> Alternatively, authentication success might be signaled to user space
> through a uevent, whereupon it may bind a (blacklisted) driver.

How will that happen?

> A uevent signaling authentication failure might similarly cause user
> space to unbind or outright remove the potentially malicious device.

Again how?  Who will be sending nthose uevents?  Who will be listening
to them?  What in the kernel is going to change to know to send those
events?  Why is any of this needed at all?  :)

> Traffic from devices which failed authentication could also be filtered
> through ACS I/O Request Blocking Enable (PCIe r6.2 sec 7.7.11.3) or
> through Link Disable (PCIe r6.2 sec 7.5.3.7).  Unlike an IOMMU, that
> will not only protect the host, but also prevent malicious peer-to-peer
> traffic to other devices.
> 
> Signed-off-by: Lukas Wunner <lukas@wunner.de>
> [ Changes by AF:
>  - Drop lib/spdm changes and replace with Rust implementation
> ]
> Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
> ---
>  Documentation/ABI/testing/sysfs-devices-spdm | 31 +++++++
>  MAINTAINERS                                  |  3 +-
>  drivers/pci/cma.c                            | 12 ++-
>  drivers/pci/doe.c                            |  2 +
>  drivers/pci/pci-sysfs.c                      |  3 +
>  drivers/pci/pci.h                            |  5 +
>  include/linux/pci.h                          | 12 +++
>  lib/rspdm/Makefile                           |  1 +
>  lib/rspdm/lib.rs                             |  1 +
>  lib/rspdm/req-sysfs.c                        | 97 ++++++++++++++++++++
>  lib/rspdm/sysfs.rs                           | 28 ++++++
>  11 files changed, 190 insertions(+), 5 deletions(-)
>  create mode 100644 Documentation/ABI/testing/sysfs-devices-spdm
>  create mode 100644 lib/rspdm/req-sysfs.c
>  create mode 100644 lib/rspdm/sysfs.rs
> 
> diff --git a/Documentation/ABI/testing/sysfs-devices-spdm b/Documentation/ABI/testing/sysfs-devices-spdm
> new file mode 100644
> index 000000000000..2d6e5d513231
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-devices-spdm
> @@ -0,0 +1,31 @@
> +What:		/sys/devices/.../authenticated
> +Date:		June 2024
> +Contact:	Lukas Wunner <lukas@wunner.de>
> +Description:
> +		This file contains 1 if the device authenticated successfully
> +		with SPDM (Security Protocol and Data Model).  It contains 0 if
> +		the device failed authentication (and may thus be malicious).
> +
> +		Writing "re" to this file causes reauthentication.
> +		That may be opportune after updating the device keyring.
> +		The device keyring of the PCI bus is named ".cma"
> +		(Component Measurement and Authentication).
> +
> +		Reauthentication may also be necessary after device identity
> +		has mutated, e.g. after downloading firmware to an FPGA device.
> +
> +		The file is not visible if authentication is unsupported
> +		by the device.

Good.

> +		If the kernel could not determine whether authentication is
> +		supported because memory was low or communication with the
> +		device was not working, the file is visible but accessing it
> +		fails with error code ENOTTY.

Not good.  So this means that userspace can not trust that if the file
is there it actually works, so it has to do more work to determine if it
is working (as mentioned above).  Just either have the file if it works,
or not if it does not please.

> +		This prevents downgrade attacks where an attacker consumes
> +		memory or disturbs communication in order to create the
> +		appearance that a device does not support authentication.

If an attacker can consume kernel memory to cause this to happen you
have bigger problems.  That's not the kernel's issue here at all.

And "disable communication" means "we just don't support it as the
device doesn't say it does", so again, why does that matter?

> +		The reason why authentication support could not be determined
> +		is apparent from "dmesg".  To re-probe authentication support
> +		of PCI devices, exercise the "remove" and "rescan" attributes.

Don't make userspace parse kernel logs for this type of thing.  And
asking userspace to rely on remove and recan is a mess, either show it
works or not.

> diff --git a/MAINTAINERS b/MAINTAINERS
> index abb3b603299f..03e1076f915a 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -21385,7 +21385,8 @@ L:	linux-cxl@vger.kernel.org
>  L:	linux-pci@vger.kernel.org
>  S:	Maintained
>  T:	git git://git.kernel.org/pub/scm/linux/kernel/git/devsec/spdm.git
> -F:	drivers/pci/cma.c
> +F:	Documentation/ABI/testing/sysfs-devices-spdm
> +F:	drivers/pci/cma*
>  F:	include/linux/spdm.h
>  F:	lib/rspdm/
>  
> diff --git a/drivers/pci/cma.c b/drivers/pci/cma.c
> index f2c435b04b92..59558714f143 100644
> --- a/drivers/pci/cma.c
> +++ b/drivers/pci/cma.c
> @@ -171,8 +171,10 @@ void pci_cma_init(struct pci_dev *pdev)
>  {
>  	struct pci_doe_mb *doe;
>  
> -	if (IS_ERR(pci_cma_keyring))
> +	if (IS_ERR(pci_cma_keyring)) {
> +		pdev->spdm_state = ERR_PTR(-ENOTTY);

Why ENOTTY?

>  		return;
> +	}
>  
>  	if (!pci_is_pcie(pdev))
>  		return;
> @@ -185,8 +187,10 @@ void pci_cma_init(struct pci_dev *pdev)
>  	pdev->spdm_state = spdm_create(&pdev->dev, pci_doe_transport, doe,
>  				       PCI_DOE_MAX_PAYLOAD, pci_cma_keyring,
>  				       pci_cma_validate);
> -	if (!pdev->spdm_state)
> +	if (!pdev->spdm_state) {
> +		pdev->spdm_state = ERR_PTR(-ENOTTY);

Again, why ENOTTY?  This isn't a tty device :)

>  		return;
> +	}
>  
>  	/*
>  	 * Keep spdm_state allocated even if initial authentication fails
> @@ -204,7 +208,7 @@ void pci_cma_init(struct pci_dev *pdev)
>   */
>  void pci_cma_reauthenticate(struct pci_dev *pdev)
>  {
> -	if (!pdev->spdm_state)
> +	if (IS_ERR_OR_NULL(pdev->spdm_state))

Why does this matter, it should either be NULL or not, adding an error
value just makes it more complex and if you make the changes I mention
above, I don't think it's needed, right?

> --- a/lib/rspdm/lib.rs
> +++ b/lib/rspdm/lib.rs
> @@ -24,6 +24,7 @@
>  
>  mod consts;
>  mod state;
> +pub mod sysfs;

Hey, a sysfs binding!

Well, not really, it's a hack as mentioned below, that I do not think
you really want to do just yet.


>  mod validator;
>  
>  /// spdm_create() - Allocate SPDM session
> diff --git a/lib/rspdm/req-sysfs.c b/lib/rspdm/req-sysfs.c
> new file mode 100644
> index 000000000000..11bacb04f08f
> --- /dev/null
> +++ b/lib/rspdm/req-sysfs.c
> @@ -0,0 +1,97 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/*
> + * Rust implementation of the DMTF Security Protocol and Data Model (SPDM)
> + * https://www.dmtf.org/dsp/DSP0274
> + *
> + * Requester role: sysfs interface
> + *
> + * Copyright (C) 2023-24 Intel Corporation
> + * Copyright (C) 2024 Western Digital
> + */
> +
> +#include <linux/pci.h>
> +
> +int rust_authenticated_show(void *spdm_state, char *buf);

Don't put externs in a .c file, didn't checkpatch catch this?  More on
this below...

> +
> +/**
> + * dev_to_spdm_state() - Retrieve SPDM session state for given device
> + *
> + * @dev: Responder device
> + *
> + * Returns a pointer to the device's SPDM session state,
> + *	   %NULL if the device doesn't have one or
> + *	   %ERR_PTR if it couldn't be determined whether SPDM is supported.
> + *
> + * In the %ERR_PTR case, attributes are visible but return an error on access.
> + * This prevents downgrade attacks where an attacker disturbs memory allocation
> + * or communication with the device in order to create the appearance that SPDM
> + * is unsupported.  E.g. with PCI devices, the attacker may foil CMA or DOE
> + * initialization by simply hogging memory.
> + */
> +static void *dev_to_spdm_state(struct device *dev)
> +{
> +	if (dev_is_pci(dev))
> +		return pci_dev_to_spdm_state(to_pci_dev(dev));
> +
> +	/* Insert mappers for further bus types here. */
> +
> +	return NULL;
> +}
> +
> +/* authenticated attribute */
> +
> +static umode_t spdm_attrs_are_visible(struct kobject *kobj,
> +				      struct attribute *a, int n)
> +{
> +	struct device *dev = kobj_to_dev(kobj);
> +	void *spdm_state = dev_to_spdm_state(dev);
> +
> +	if (IS_ERR_OR_NULL(spdm_state))
> +		return SYSFS_GROUP_INVISIBLE;
> +
> +	return a->mode;
> +}
> +
> +static ssize_t authenticated_store(struct device *dev,
> +				   struct device_attribute *attr,
> +				   const char *buf, size_t count)
> +{
> +	void *spdm_state = dev_to_spdm_state(dev);
> +	int rc;
> +
> +	if (IS_ERR_OR_NULL(spdm_state))
> +		return PTR_ERR(spdm_state);
> +
> +	if (sysfs_streq(buf, "re")) {

I don't like sysfs files that when reading show a binary, but require a
"magic string" to be written to them to have them do something else.
that way lies madness.  What would you do if each sysfs file had a
custom language that you had to look up for each one?  Be consistant
here.  But again, I don't think you need a store function at all, either
the device supports this, or it doesn't.

> +		rc = spdm_authenticate(spdm_state);

Call into Rust code?  More on that below...

> +		if (rc)
> +			return rc;
> +	} else {
> +		return -EINVAL;
> +	}
> +
> +	return count;
> +}
> +
> +static ssize_t authenticated_show(struct device *dev,
> +				  struct device_attribute *attr, char *buf)
> +{
> +	void *spdm_state = dev_to_spdm_state(dev);
> +
> +	if (IS_ERR_OR_NULL(spdm_state))
> +		return PTR_ERR(spdm_state);

Again, this check can go away if the file just isn't there.


> +
> +	return rust_authenticated_show(spdm_state, buf);

Here you have C code calling into Rust code.  I'm not complaining about
it, but I think it will be the first use of this, and I didn't think
that the rust maintainers were willing to do that just yet.

Has that policy changed?

The issue here is that the C signature for this is not being
auto-generated, you have to manually keep it in sync (as you did above),
with the Rust side.  That's not going to scale over time at all, you
MUST have a .h file somewhere for C to know how to call into this and
for the compiler to check that all is sane on both sides.

And you are passing a random void * into the Rust side, what could go
wrong?  I think this needs more thought as this is fragile-as-f***.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated
  2025-02-27 11:16   ` Greg KH
@ 2025-02-27 11:52     ` Alice Ryhl
  2025-02-27 12:00       ` Greg KH
  2025-02-27 22:42     ` Lukas Wunner
  1 sibling, 1 reply; 47+ messages in thread
From: Alice Ryhl @ 2025-02-27 11:52 UTC (permalink / raw)
  To: Greg KH
  Cc: Alistair Francis, linux-cxl, linux-kernel, lukas, linux-pci,
	bhelgaas, Jonathan.Cameron, rust-for-linux, akpm, boqun.feng,
	bjorn3_gh, wilfred.mallawa, ojeda, alistair23, a.hindborg,
	tmgross, gary, alex.gaynor, benno.lossin, Alistair Francis

On Thu, Feb 27, 2025 at 12:17 PM Greg KH <gregkh@linuxfoundation.org> wrote:
>
> On Thu, Feb 27, 2025 at 01:09:41PM +1000, Alistair Francis wrote:
> > +     return rust_authenticated_show(spdm_state, buf);
>
> Here you have C code calling into Rust code.  I'm not complaining about
> it, but I think it will be the first use of this, and I didn't think
> that the rust maintainers were willing to do that just yet.
>
> Has that policy changed?
>
> The issue here is that the C signature for this is not being
> auto-generated, you have to manually keep it in sync (as you did above),
> with the Rust side.  That's not going to scale over time at all, you
> MUST have a .h file somewhere for C to know how to call into this and
> for the compiler to check that all is sane on both sides.
>
> And you are passing a random void * into the Rust side, what could go
> wrong?  I think this needs more thought as this is fragile-as-f***.

I don't think we have a policy against it? I'm pretty sure the QR code
thing does it.

Alice

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated
  2025-02-27 11:52     ` Alice Ryhl
@ 2025-02-27 12:00       ` Greg KH
  2025-02-27 12:11         ` Alice Ryhl
  2025-02-27 16:45         ` Miguel Ojeda
  0 siblings, 2 replies; 47+ messages in thread
From: Greg KH @ 2025-02-27 12:00 UTC (permalink / raw)
  To: Alice Ryhl
  Cc: Alistair Francis, linux-cxl, linux-kernel, lukas, linux-pci,
	bhelgaas, Jonathan.Cameron, rust-for-linux, akpm, boqun.feng,
	bjorn3_gh, wilfred.mallawa, ojeda, alistair23, a.hindborg,
	tmgross, gary, alex.gaynor, benno.lossin, Alistair Francis

On Thu, Feb 27, 2025 at 12:52:02PM +0100, Alice Ryhl wrote:
> On Thu, Feb 27, 2025 at 12:17 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> >
> > On Thu, Feb 27, 2025 at 01:09:41PM +1000, Alistair Francis wrote:
> > > +     return rust_authenticated_show(spdm_state, buf);
> >
> > Here you have C code calling into Rust code.  I'm not complaining about
> > it, but I think it will be the first use of this, and I didn't think
> > that the rust maintainers were willing to do that just yet.
> >
> > Has that policy changed?
> >
> > The issue here is that the C signature for this is not being
> > auto-generated, you have to manually keep it in sync (as you did above),
> > with the Rust side.  That's not going to scale over time at all, you
> > MUST have a .h file somewhere for C to know how to call into this and
> > for the compiler to check that all is sane on both sides.
> >
> > And you are passing a random void * into the Rust side, what could go
> > wrong?  I think this needs more thought as this is fragile-as-f***.
> 
> I don't think we have a policy against it? I'm pretty sure the QR code
> thing does it.

Sorry, you are right, it does, and of course it happens (otherwise how
would bindings work), but for small functions like this, how is the C
code kept in sync with the rust side?  Where is the .h file that C
should include?

thanks,

greg "I need more coffee" k-h

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated
  2025-02-27 12:00       ` Greg KH
@ 2025-02-27 12:11         ` Alice Ryhl
  2025-02-27 14:03           ` Greg KH
  2025-02-27 16:46           ` Miguel Ojeda
  2025-02-27 16:45         ` Miguel Ojeda
  1 sibling, 2 replies; 47+ messages in thread
From: Alice Ryhl @ 2025-02-27 12:11 UTC (permalink / raw)
  To: Greg KH
  Cc: Alistair Francis, linux-cxl, linux-kernel, lukas, linux-pci,
	bhelgaas, Jonathan.Cameron, rust-for-linux, akpm, boqun.feng,
	bjorn3_gh, wilfred.mallawa, ojeda, alistair23, a.hindborg,
	tmgross, gary, alex.gaynor, benno.lossin, Alistair Francis

On Thu, Feb 27, 2025 at 1:01 PM Greg KH <gregkh@linuxfoundation.org> wrote:
>
> On Thu, Feb 27, 2025 at 12:52:02PM +0100, Alice Ryhl wrote:
> > On Thu, Feb 27, 2025 at 12:17 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> > >
> > > On Thu, Feb 27, 2025 at 01:09:41PM +1000, Alistair Francis wrote:
> > > > +     return rust_authenticated_show(spdm_state, buf);
> > >
> > > Here you have C code calling into Rust code.  I'm not complaining about
> > > it, but I think it will be the first use of this, and I didn't think
> > > that the rust maintainers were willing to do that just yet.
> > >
> > > Has that policy changed?
> > >
> > > The issue here is that the C signature for this is not being
> > > auto-generated, you have to manually keep it in sync (as you did above),
> > > with the Rust side.  That's not going to scale over time at all, you
> > > MUST have a .h file somewhere for C to know how to call into this and
> > > for the compiler to check that all is sane on both sides.
> > >
> > > And you are passing a random void * into the Rust side, what could go
> > > wrong?  I think this needs more thought as this is fragile-as-f***.
> >
> > I don't think we have a policy against it? I'm pretty sure the QR code
> > thing does it.
>
> Sorry, you are right, it does, and of course it happens (otherwise how
> would bindings work), but for small functions like this, how is the C
> code kept in sync with the rust side?  Where is the .h file that C
> should include?

I don't think there is tooling for it today. We need the opposite of
bindgen, which does exist in a tool called cbindgen. Unfortunately,
cbindgen is written to only work in cargo-based build systems, so we
cannot use it.

One trick you could do is write the signature in a header file, and
then compare what bindgen generates to the real signature like this:

const _: () = {
    if true {
        bindings::my_function
    } else {
        my_function
    };
};

This would only compile if the two function pointers have the same signature.

Alice

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated
  2025-02-27 12:11         ` Alice Ryhl
@ 2025-02-27 14:03           ` Greg KH
  2025-02-27 16:47             ` Miguel Ojeda
  2025-02-27 16:46           ` Miguel Ojeda
  1 sibling, 1 reply; 47+ messages in thread
From: Greg KH @ 2025-02-27 14:03 UTC (permalink / raw)
  To: Alice Ryhl
  Cc: Alistair Francis, linux-cxl, linux-kernel, lukas, linux-pci,
	bhelgaas, Jonathan.Cameron, rust-for-linux, akpm, boqun.feng,
	bjorn3_gh, wilfred.mallawa, ojeda, alistair23, a.hindborg,
	tmgross, gary, alex.gaynor, benno.lossin, Alistair Francis

On Thu, Feb 27, 2025 at 01:11:01PM +0100, Alice Ryhl wrote:
> On Thu, Feb 27, 2025 at 1:01 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> >
> > On Thu, Feb 27, 2025 at 12:52:02PM +0100, Alice Ryhl wrote:
> > > On Thu, Feb 27, 2025 at 12:17 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> > > >
> > > > On Thu, Feb 27, 2025 at 01:09:41PM +1000, Alistair Francis wrote:
> > > > > +     return rust_authenticated_show(spdm_state, buf);
> > > >
> > > > Here you have C code calling into Rust code.  I'm not complaining about
> > > > it, but I think it will be the first use of this, and I didn't think
> > > > that the rust maintainers were willing to do that just yet.
> > > >
> > > > Has that policy changed?
> > > >
> > > > The issue here is that the C signature for this is not being
> > > > auto-generated, you have to manually keep it in sync (as you did above),
> > > > with the Rust side.  That's not going to scale over time at all, you
> > > > MUST have a .h file somewhere for C to know how to call into this and
> > > > for the compiler to check that all is sane on both sides.
> > > >
> > > > And you are passing a random void * into the Rust side, what could go
> > > > wrong?  I think this needs more thought as this is fragile-as-f***.
> > >
> > > I don't think we have a policy against it? I'm pretty sure the QR code
> > > thing does it.
> >
> > Sorry, you are right, it does, and of course it happens (otherwise how
> > would bindings work), but for small functions like this, how is the C
> > code kept in sync with the rust side?  Where is the .h file that C
> > should include?
> 
> I don't think there is tooling for it today. We need the opposite of
> bindgen, which does exist in a tool called cbindgen. Unfortunately,
> cbindgen is written to only work in cargo-based build systems, so we
> cannot use it.
> 
> One trick you could do is write the signature in a header file, and
> then compare what bindgen generates to the real signature like this:
> 
> const _: () = {
>     if true {
>         bindings::my_function
>     } else {
>         my_function
>     };
> };
> 
> This would only compile if the two function pointers have the same signature.

That feels just wrong :(

As this seems like it's going to be a longer-term issue, has anyone
thought of how it's going to be handled?  Build time errors when
functions change is the key here, no one remembers to manually verify
each caller to verify the variables are correct anymore, that would be a
big step backwards.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated
  2025-02-27 12:00       ` Greg KH
  2025-02-27 12:11         ` Alice Ryhl
@ 2025-02-27 16:45         ` Miguel Ojeda
  2025-02-27 19:32           ` Greg KH
  1 sibling, 1 reply; 47+ messages in thread
From: Miguel Ojeda @ 2025-02-27 16:45 UTC (permalink / raw)
  To: Greg KH
  Cc: Alice Ryhl, Alistair Francis, linux-cxl, linux-kernel, lukas,
	linux-pci, bhelgaas, Jonathan.Cameron, rust-for-linux, akpm,
	boqun.feng, bjorn3_gh, wilfred.mallawa, ojeda, alistair23,
	a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

On Thu, Feb 27, 2025 at 1:01 PM Greg KH <gregkh@linuxfoundation.org> wrote:
>
> Sorry, you are right, it does, and of course it happens (otherwise how
> would bindings work), but for small functions like this, how is the C
> code kept in sync with the rust side?  Where is the .h file that C
> should include?

What you were probably remembering is that it still needs to be
justified, i.e. we don't want to generally/freely start replacing
"individual functions" and doing FFI both ways everywhere, i.e. the
goal is to build safe abstractions wherever possible.

Cheers,
Miguel

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated
  2025-02-27 12:11         ` Alice Ryhl
  2025-02-27 14:03           ` Greg KH
@ 2025-02-27 16:46           ` Miguel Ojeda
  1 sibling, 0 replies; 47+ messages in thread
From: Miguel Ojeda @ 2025-02-27 16:46 UTC (permalink / raw)
  To: Alice Ryhl
  Cc: Greg KH, Alistair Francis, linux-cxl, linux-kernel, lukas,
	linux-pci, bhelgaas, Jonathan.Cameron, rust-for-linux, akpm,
	boqun.feng, bjorn3_gh, wilfred.mallawa, ojeda, alistair23,
	a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

On Thu, Feb 27, 2025 at 1:11 PM Alice Ryhl <aliceryhl@google.com> wrote:
>
> I don't think there is tooling for it today. We need the opposite of
> bindgen, which does exist in a tool called cbindgen. Unfortunately,
> cbindgen is written to only work in cargo-based build systems, so we
> cannot use it.

It doesn't require Cargo, e.g. see my reply to Daniel:

    https://rust-for-linux.zulipchat.com/#narrow/channel/288089-General/topic/What.20is.20the.20status.20on.20C.20APIs.20for.20Rust.20kernel.20code.3F/near/420712657

Even if it did require something extra for complex usage or similar,
we could ask the maintainers or I could perhaps come up with something
to generate whatever inputs they need.

Cheers,
Miguel

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated
  2025-02-27 14:03           ` Greg KH
@ 2025-02-27 16:47             ` Miguel Ojeda
  2025-02-27 19:31               ` Greg KH
  0 siblings, 1 reply; 47+ messages in thread
From: Miguel Ojeda @ 2025-02-27 16:47 UTC (permalink / raw)
  To: Greg KH
  Cc: Alice Ryhl, Alistair Francis, linux-cxl, linux-kernel, lukas,
	linux-pci, bhelgaas, Jonathan.Cameron, rust-for-linux, akpm,
	boqun.feng, bjorn3_gh, wilfred.mallawa, ojeda, alistair23,
	a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis, Emilio Cobos Álvarez

On Thu, Feb 27, 2025 at 3:04 PM Greg KH <gregkh@linuxfoundation.org> wrote:
>
> As this seems like it's going to be a longer-term issue, has anyone
> thought of how it's going to be handled?  Build time errors when
> functions change is the key here, no one remembers to manually verify
> each caller to verify the variables are correct anymore, that would be a
> big step backwards.

I can look into it, after other build system things are done.

I talked to Emilio some months ago about this and he told me Firefox
solves the problem both ways, so we may be able to do something
similar.

Cheers,
Miguel

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated
  2025-02-27 16:47             ` Miguel Ojeda
@ 2025-02-27 19:31               ` Greg KH
  2025-02-28  8:49                 ` Miguel Ojeda
  0 siblings, 1 reply; 47+ messages in thread
From: Greg KH @ 2025-02-27 19:31 UTC (permalink / raw)
  To: Miguel Ojeda
  Cc: Alice Ryhl, Alistair Francis, linux-cxl, linux-kernel, lukas,
	linux-pci, bhelgaas, Jonathan.Cameron, rust-for-linux, akpm,
	boqun.feng, bjorn3_gh, wilfred.mallawa, ojeda, alistair23,
	a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis, Emilio Cobos Álvarez

On Thu, Feb 27, 2025 at 05:47:01PM +0100, Miguel Ojeda wrote:
> On Thu, Feb 27, 2025 at 3:04 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> >
> > As this seems like it's going to be a longer-term issue, has anyone
> > thought of how it's going to be handled?  Build time errors when
> > functions change is the key here, no one remembers to manually verify
> > each caller to verify the variables are correct anymore, that would be a
> > big step backwards.
> 
> I can look into it, after other build system things are done.

Looks like Alice already sent a series to do this, so no need.

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated
  2025-02-27 16:45         ` Miguel Ojeda
@ 2025-02-27 19:32           ` Greg KH
  2025-02-28  2:27             ` Alistair Francis
  0 siblings, 1 reply; 47+ messages in thread
From: Greg KH @ 2025-02-27 19:32 UTC (permalink / raw)
  To: Miguel Ojeda
  Cc: Alice Ryhl, Alistair Francis, linux-cxl, linux-kernel, lukas,
	linux-pci, bhelgaas, Jonathan.Cameron, rust-for-linux, akpm,
	boqun.feng, bjorn3_gh, wilfred.mallawa, ojeda, alistair23,
	a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

On Thu, Feb 27, 2025 at 05:45:02PM +0100, Miguel Ojeda wrote:
> On Thu, Feb 27, 2025 at 1:01 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> >
> > Sorry, you are right, it does, and of course it happens (otherwise how
> > would bindings work), but for small functions like this, how is the C
> > code kept in sync with the rust side?  Where is the .h file that C
> > should include?
> 
> What you were probably remembering is that it still needs to be
> justified, i.e. we don't want to generally/freely start replacing
> "individual functions" and doing FFI both ways everywhere, i.e. the
> goal is to build safe abstractions wherever possible.

Ah, ok, that's what I was remembering.

Anyway, the "pass a void blob from C into rust" that this patch is doing
feels really odd to me, and hard to verify it is "safe" at a simple
glance.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated
  2025-02-27 11:16   ` Greg KH
  2025-02-27 11:52     ` Alice Ryhl
@ 2025-02-27 22:42     ` Lukas Wunner
  2025-02-28  1:39       ` Greg KH
  1 sibling, 1 reply; 47+ messages in thread
From: Lukas Wunner @ 2025-02-27 22:42 UTC (permalink / raw)
  To: Greg KH
  Cc: Alistair Francis, linux-cxl, linux-kernel, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm, boqun.feng, bjorn3_gh,
	wilfred.mallawa, aliceryhl, ojeda, alistair23, a.hindborg,
	tmgross, gary, alex.gaynor, benno.lossin, Alistair Francis

On Thu, Feb 27, 2025 at 03:16:40AM -0800, Greg KH wrote:
> On Thu, Feb 27, 2025 at 01:09:41PM +1000, Alistair Francis wrote:
> > The PCI core has just been amended to authenticate CMA-capable devices
> > on enumeration and store the result in an "authenticated" bit in struct
> > pci_dev->spdm_state.
> > 
> > Expose the bit to user space through an eponymous sysfs attribute.
> > 
> > Allow user space to trigger reauthentication (e.g. after it has updated
> > the CMA keyring) by writing to the sysfs attribute.
> > 
> > Implement the attribute in the SPDM library so that other bus types
> > besides PCI may take advantage of it.  They just need to add
> > spdm_attr_group to the attribute groups of their devices and amend the
> > dev_to_spdm_state() helper which retrieves the spdm_state for a given
> > device.
> > 
> > The helper may return an ERR_PTR if it couldn't be determined whether
> > SPDM is supported by the device.  The sysfs attribute is visible in that
> > case but returns an error on access.  This prevents downgrade attacks
> > where an attacker disturbs memory allocation or DOE communication
> > in order to create the appearance that SPDM is unsupported.
> 
> I don't like this "if it's present we still don't know if the device
> supports this", as that is not normally the "sysfs way" here.  Why must
> it be present in those situations?

That's explained above.

Unfortunately there is no (signed) bit in Config Space which tells us
whether authentication is supported by a PCI device.  Rather, it is
necessary to exchange several messages with the device through a
DOE mailbox in config space to determine that.  I'm worried that an
attacker deliberately "glitches" those DOE exchanges and thus creates
the appearance that the device does not support authentication.

Let's say the user's policy is to trust legacy devices which do not
support authentication, but require authentication for newer NVMe drives
from a certain vendor.  An attacker may manipulate an authentication-capable
NVMe drive from that vendor, whereupon it will fail authentication.
But the attacker can trick the user into trusting the device by glitching
the DOE exchanges.

Of course, this is an abnormal situation that users won't encounter
unless they're being attacked.  Normally the attribute is only present
if authentication is supported.

I disagree with your assessment that we have bigger problems.
For security protocols like this we have to be very careful
to prevent trivial circumvention.  We cannot just shrug this off
as unimportant.

The "authenticated" attribute tells user space whether the device
is authenticated.  User space needs to handle errors anyway when
reading the attribute.  Users will get an error if authentication
support could not be determined.  Simple.


> What is going to happen to suddenly
> allow it to come back to be "alive" and working while the device is
> still present in the system?

The device needs to be re-enumerated by the PCI core to retry
determining its authentication capability.  That's why the
sysfs documentation says the user may exercise the "remove"
and "rescan" attributes to retry authentication.


> I'd prefer it to be "if the file is there, this is supported by the
> device.  If the file is not there, it is not supported", as that makes
> things so much simpler for userspace (i.e. you don't want userspace to
> have to both test if the file is there AND read all entries just to see
> if the kernel knows what is going on or not.)

Huh?  Read all entries?  The attribute contains only 0 or 1!

Or you'll get an error reading it.


> Also, how will userspace know if the state changes from "unknown" to
> "now it might work, try it again"?

User space has to explicitly remove and rescan the device.
Otherwise its authentication capability remains unknown.

Again, this is in the abnormal situation when the user is being attacked.
There is no automatic resolution to this scenario, deliberately so.

The user needs to know that there may be an attack going on.
The user may then act on it at their own discretion.


> > Subject to further discussion, a future commit might add a user-defined
> > policy to forbid driver binding to devices which failed authentication,
> > similar to the "authorized" attribute for USB.
> 
> That user-defined policy belongs in userspace, just like USB has it.
> Why would the kernel need this?  Or do you mean that the kernel provides
> a default like USB does and then requires userspace to manually enable a
> device before binding a driver to the device is allowed?

The idea is to bring up PCI device authentication.
And as a first step, expose to user space whether authentication succeeded.

We can have a discussion what other functionality is desirable to make
this useful.  I am open to any ideas you might have.  The commit
message merely makes suggestions as to what might be interesting
going forward.


> > Alternatively, authentication success might be signaled to user space
> > through a uevent, whereupon it may bind a (blacklisted) driver.
> 
> How will that happen?

The SPDM library can be amended to signal a uevent when authentication
succeeds or fails and user space can then act on it.  I imagine systemd
or some other daemon might listen to such events and do interesting things,
such as binding a driver once authentication succeeds.


> > A uevent signaling authentication failure might similarly cause user
> > space to unbind or outright remove the potentially malicious device.
> 
> Again how?  Who will be sending nthose uevents?  Who will be listening
> to them?  What in the kernel is going to change to know to send those
> events?  Why is any of this needed at all?  :)

None of this is needed.  It's just a suggestion.

Maybe you have better ideas.  Be constructive!  Make suggestions!


> > +		If the kernel could not determine whether authentication is
> > +		supported because memory was low or communication with the
> > +		device was not working, the file is visible but accessing it
> > +		fails with error code ENOTTY.
> 
> Not good.  So this means that userspace can not trust that if the file
> is there it actually works, so it has to do more work to determine if it
> is working (as mentioned above).  Just either have the file if it works,
> or not if it does not please.

No no no.

Quoting Destiny's Child here. ;)


> > +		This prevents downgrade attacks where an attacker consumes
> > +		memory or disturbs communication in order to create the
> > +		appearance that a device does not support authentication.
> 
> If an attacker can consume kernel memory to cause this to happen you
> have bigger problems.  That's not the kernel's issue here at all.
> 
> And "disable communication" means "we just don't support it as the
> device doesn't say it does", so again, why does that matter?

Reacting to potential attacks sure is the kernel's business.


> > +		The reason why authentication support could not be determined
> > +		is apparent from "dmesg".  To re-probe authentication support
> > +		of PCI devices, exercise the "remove" and "rescan" attributes.
> 
> Don't make userspace parse kernel logs for this type of thing.  And
> asking userspace to rely on remove and recan is a mess, either show it
> works or not.

I'd say looking in dmesg to determine whether the user is being attacked
is perfectly fine, as is requiring the user to explicitly act on a
potential attack.


> > --- a/drivers/pci/cma.c
> > +++ b/drivers/pci/cma.c
> > @@ -171,8 +171,10 @@ void pci_cma_init(struct pci_dev *pdev)
> >  {
> >  	struct pci_doe_mb *doe;
> >  
> > -	if (IS_ERR(pci_cma_keyring))
> > +	if (IS_ERR(pci_cma_keyring)) {
> > +		pdev->spdm_state = ERR_PTR(-ENOTTY);
> 
> Why ENOTTY?

We use -ENOTTY as return value for unsupported reset methods in the
PCI core, see e.g. pcie_reset_flr(), pcie_af_flr(), pci_pm_reset(),
pci_parent_bus_reset(), pci_reset_hotplug_slot(), ...

We also use -ENOTTY in pci_bridge_wait_for_secondary_bus() and
pci_dev_wait().

It was used here to be consistent with those existing occurrences
in the PCI core.

If you'd prefer something else, please make a suggestion.


> > +static ssize_t authenticated_store(struct device *dev,
> > +				   struct device_attribute *attr,
> > +				   const char *buf, size_t count)
> > +{
> > +	void *spdm_state = dev_to_spdm_state(dev);
> > +	int rc;
> > +
> > +	if (IS_ERR_OR_NULL(spdm_state))
> > +		return PTR_ERR(spdm_state);
> > +
> > +	if (sysfs_streq(buf, "re")) {
> 
> I don't like sysfs files that when reading show a binary, but require a
> "magic string" to be written to them to have them do something else.
> that way lies madness.  What would you do if each sysfs file had a
> custom language that you had to look up for each one?  Be consistant
> here.  But again, I don't think you need a store function at all, either
> the device supports this, or it doesn't.

I'm not sure if you've even read the ABI documentation in full.

The store method is needed to reauthenticate the device,
e.g. after a new trusted root certificate was added to the
kernel's .cma keyring.

Originally I allowed any value to be written to the "authenticated"
attribute to trigger reauthentication.  I changed that to "re"
(actually any word that starts with "re") because I envision that
we may need additional verbs to force authentication with a specific
certificate slot or to force-trust a device despite missing its
root certificate in the .cma keyring.

Thanks,

Lukas

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated
  2025-02-27 22:42     ` Lukas Wunner
@ 2025-02-28  1:39       ` Greg KH
  2025-02-28  2:55         ` Alistair Francis
  2025-03-01 18:01         ` Lukas Wunner
  0 siblings, 2 replies; 47+ messages in thread
From: Greg KH @ 2025-02-28  1:39 UTC (permalink / raw)
  To: Lukas Wunner
  Cc: Alistair Francis, linux-cxl, linux-kernel, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm, boqun.feng, bjorn3_gh,
	wilfred.mallawa, aliceryhl, ojeda, alistair23, a.hindborg,
	tmgross, gary, alex.gaynor, benno.lossin, Alistair Francis

On Thu, Feb 27, 2025 at 11:42:46PM +0100, Lukas Wunner wrote:
> On Thu, Feb 27, 2025 at 03:16:40AM -0800, Greg KH wrote:
> > On Thu, Feb 27, 2025 at 01:09:41PM +1000, Alistair Francis wrote:
> > > The PCI core has just been amended to authenticate CMA-capable devices
> > > on enumeration and store the result in an "authenticated" bit in struct
> > > pci_dev->spdm_state.
> > > 
> > > Expose the bit to user space through an eponymous sysfs attribute.
> > > 
> > > Allow user space to trigger reauthentication (e.g. after it has updated
> > > the CMA keyring) by writing to the sysfs attribute.
> > > 
> > > Implement the attribute in the SPDM library so that other bus types
> > > besides PCI may take advantage of it.  They just need to add
> > > spdm_attr_group to the attribute groups of their devices and amend the
> > > dev_to_spdm_state() helper which retrieves the spdm_state for a given
> > > device.
> > > 
> > > The helper may return an ERR_PTR if it couldn't be determined whether
> > > SPDM is supported by the device.  The sysfs attribute is visible in that
> > > case but returns an error on access.  This prevents downgrade attacks
> > > where an attacker disturbs memory allocation or DOE communication
> > > in order to create the appearance that SPDM is unsupported.
> > 
> > I don't like this "if it's present we still don't know if the device
> > supports this", as that is not normally the "sysfs way" here.  Why must
> > it be present in those situations?
> 
> That's explained above.

Not really, you just say "downgrade attacks", which is not something
that we need to worry about, right?  If so, I think this bit is the
least of our worries.

> Unfortunately there is no (signed) bit in Config Space which tells us
> whether authentication is supported by a PCI device.  Rather, it is
> necessary to exchange several messages with the device through a
> DOE mailbox in config space to determine that.  I'm worried that an
> attacker deliberately "glitches" those DOE exchanges and thus creates
> the appearance that the device does not support authentication.

That's a hardware glitch, and if that happens, then it will show a 0 and
that's the same as not being present at all, right?  Otherwise you just
pound on the file to try to see if the glitch was not real?  That's not
going to go over well.

> Let's say the user's policy is to trust legacy devices which do not
> support authentication, but require authentication for newer NVMe drives
> from a certain vendor.  An attacker may manipulate an authentication-capable
> NVMe drive from that vendor, whereupon it will fail authentication.
> But the attacker can trick the user into trusting the device by glitching
> the DOE exchanges.

Again, are we now claiming that Linux needs to support "hardware
glitching"?  Is that required somewhere?  I think if the DOE exchanges
fail, we just trust the device as we have to trust something, right?

> Of course, this is an abnormal situation that users won't encounter
> unless they're being attacked.  Normally the attribute is only present
> if authentication is supported.
> 
> I disagree with your assessment that we have bigger problems.
> For security protocols like this we have to be very careful
> to prevent trivial circumvention.  We cannot just shrug this off
> as unimportant.

hardware glitching is not trivial.  Let's only worry about that if the
hardware people somehow require it, and if so, we can push back and say
"stop making us fix your broken designs" :)

> The "authenticated" attribute tells user space whether the device
> is authenticated.  User space needs to handle errors anyway when
> reading the attribute.  Users will get an error if authentication
> support could not be determined.  Simple.

No, if it's not determined, it shouldn't be present.

> > What is going to happen to suddenly
> > allow it to come back to be "alive" and working while the device is
> > still present in the system?
> 
> The device needs to be re-enumerated by the PCI core to retry
> determining its authentication capability.  That's why the
> sysfs documentation says the user may exercise the "remove"
> and "rescan" attributes to retry authentication.

But how does it know that?  remove and recan is a huge sledgehammer, and
an amazing one if it even works on most hardware.  Don't make it part of
any normal process please.

> > I'd prefer it to be "if the file is there, this is supported by the
> > device.  If the file is not there, it is not supported", as that makes
> > things so much simpler for userspace (i.e. you don't want userspace to
> > have to both test if the file is there AND read all entries just to see
> > if the kernel knows what is going on or not.)
> 
> Huh?  Read all entries?  The attribute contains only 0 or 1!
> 
> Or you'll get an error reading it.

It's the error, don't do that.  If an error is going to happen, then
don't have the file there.  That's the way sysfs works, it's not a
"let's add all possible files and then make userspace open them all and
see if an error happens to determine what really is present for this
device" model.  It's a "if a file is there, that attribute is there and
we can read it".

> > > Alternatively, authentication success might be signaled to user space
> > > through a uevent, whereupon it may bind a (blacklisted) driver.
> > 
> > How will that happen?
> 
> The SPDM library can be amended to signal a uevent when authentication
> succeeds or fails and user space can then act on it.  I imagine systemd
> or some other daemon might listen to such events and do interesting things,
> such as binding a driver once authentication succeeds.

That's a new user/kernel api and should be designed ONLY if you actually
need it and have a user.  Otherwise let's just wait for later for that.

> Maybe you have better ideas.  Be constructive!  Make suggestions!

Again, have the file there only if this is something that the hardware
supports.  Don't fail a read just because the hardware does not seem to
support it, but it might sometime in the future if you just happen to
unplug/plug it back in.

> > > +		This prevents downgrade attacks where an attacker consumes
> > > +		memory or disturbs communication in order to create the
> > > +		appearance that a device does not support authentication.
> > 
> > If an attacker can consume kernel memory to cause this to happen you
> > have bigger problems.  That's not the kernel's issue here at all.
> > 
> > And "disable communication" means "we just don't support it as the
> > device doesn't say it does", so again, why does that matter?
> 
> Reacting to potential attacks sure is the kernel's business.

Reacting to real, software attacks is the kernel's business.  Reacting
to possible hardware issues that are just theoretical is not.

> > > +		The reason why authentication support could not be determined
> > > +		is apparent from "dmesg".  To re-probe authentication support
> > > +		of PCI devices, exercise the "remove" and "rescan" attributes.
> > 
> > Don't make userspace parse kernel logs for this type of thing.  And
> > asking userspace to rely on remove and recan is a mess, either show it
> > works or not.
> 
> I'd say looking in dmesg to determine whether the user is being attacked
> is perfectly fine, as is requiring the user to explicitly act on a
> potential attack.
> 
> 
> > > --- a/drivers/pci/cma.c
> > > +++ b/drivers/pci/cma.c
> > > @@ -171,8 +171,10 @@ void pci_cma_init(struct pci_dev *pdev)
> > >  {
> > >  	struct pci_doe_mb *doe;
> > >  
> > > -	if (IS_ERR(pci_cma_keyring))
> > > +	if (IS_ERR(pci_cma_keyring)) {
> > > +		pdev->spdm_state = ERR_PTR(-ENOTTY);
> > 
> > Why ENOTTY?
> 
> We use -ENOTTY as return value for unsupported reset methods in the
> PCI core, see e.g. pcie_reset_flr(), pcie_af_flr(), pci_pm_reset(),
> pci_parent_bus_reset(), pci_reset_hotplug_slot(), ...
> 
> We also use -ENOTTY in pci_bridge_wait_for_secondary_bus() and
> pci_dev_wait().
> 
> It was used here to be consistent with those existing occurrences
> in the PCI core.
> 
> If you'd prefer something else, please make a suggestion.

Ah, didn't realize that was a pci thing, ok, nevermind.

> > > +static ssize_t authenticated_store(struct device *dev,
> > > +				   struct device_attribute *attr,
> > > +				   const char *buf, size_t count)
> > > +{
> > > +	void *spdm_state = dev_to_spdm_state(dev);
> > > +	int rc;
> > > +
> > > +	if (IS_ERR_OR_NULL(spdm_state))
> > > +		return PTR_ERR(spdm_state);
> > > +
> > > +	if (sysfs_streq(buf, "re")) {
> > 
> > I don't like sysfs files that when reading show a binary, but require a
> > "magic string" to be written to them to have them do something else.
> > that way lies madness.  What would you do if each sysfs file had a
> > custom language that you had to look up for each one?  Be consistant
> > here.  But again, I don't think you need a store function at all, either
> > the device supports this, or it doesn't.
> 
> I'm not sure if you've even read the ABI documentation in full.
> 
> The store method is needed to reauthenticate the device,
> e.g. after a new trusted root certificate was added to the
> kernel's .cma keyring.

Why not have a different file called "reauthentication" that only allows
a write to it of a 1/Y/y to do the reauthentication.  sysfs is a "one
file per thing" interface, not a "parse a command and do something but
when read from return a different value" interface.

Let's keep it dirt simple please.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated
  2025-02-27 19:32           ` Greg KH
@ 2025-02-28  2:27             ` Alistair Francis
  2025-03-01  4:27               ` Greg KH
  2025-03-05 19:54               ` Dan Williams
  0 siblings, 2 replies; 47+ messages in thread
From: Alistair Francis @ 2025-02-28  2:27 UTC (permalink / raw)
  To: Greg KH
  Cc: Miguel Ojeda, Alice Ryhl, Alistair Francis, linux-cxl,
	linux-kernel, lukas, linux-pci, bhelgaas, Jonathan.Cameron,
	rust-for-linux, akpm, boqun.feng, bjorn3_gh, wilfred.mallawa,
	ojeda, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

On Fri, Feb 28, 2025 at 5:33 AM Greg KH <gregkh@linuxfoundation.org> wrote:
>
> On Thu, Feb 27, 2025 at 05:45:02PM +0100, Miguel Ojeda wrote:
> > On Thu, Feb 27, 2025 at 1:01 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> > >
> > > Sorry, you are right, it does, and of course it happens (otherwise how
> > > would bindings work), but for small functions like this, how is the C
> > > code kept in sync with the rust side?  Where is the .h file that C
> > > should include?

This I can address with something like Alice mentioned earlier to
ensure the C and Rust functions stay in sync.

> >
> > What you were probably remembering is that it still needs to be
> > justified, i.e. we don't want to generally/freely start replacing
> > "individual functions" and doing FFI both ways everywhere, i.e. the
> > goal is to build safe abstractions wherever possible.
>
> Ah, ok, that's what I was remembering.
>
> Anyway, the "pass a void blob from C into rust" that this patch is doing
> feels really odd to me, and hard to verify it is "safe" at a simple
> glance.

I agree, it's a bit odd. Ideally I would like to use a sysfs binding,
but there isn't one today.

I had a quick look and a Rust sysfs binding implementation seems like
a lot of work, which I wasn't convinced I wanted to invest in for this
series. This is only a single sysfs attribute and I didn't want to
slow down this series on a whole sysfs Rust implementation.

If this approach isn't ok for now, I will just drop the sysfs changes
from the series so the SPDM implementation doesn't stall on sysfs
changes. Then come back to the sysfs attributes in the future.

So the high level question, is "pass[ing] a void blob from C into
rust" ok or should I defer for a future safer implementation?

Alistair

>
> thanks,
>
> greg k-h

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated
  2025-02-28  1:39       ` Greg KH
@ 2025-02-28  2:55         ` Alistair Francis
  2025-03-01  4:33           ` Greg KH
  2025-03-01 18:01         ` Lukas Wunner
  1 sibling, 1 reply; 47+ messages in thread
From: Alistair Francis @ 2025-02-28  2:55 UTC (permalink / raw)
  To: Greg KH
  Cc: Lukas Wunner, Alistair Francis, linux-cxl, linux-kernel,
	linux-pci, bhelgaas, Jonathan.Cameron, rust-for-linux, akpm,
	boqun.feng, bjorn3_gh, wilfred.mallawa, aliceryhl, ojeda,
	a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

On Fri, Feb 28, 2025 at 11:41 AM Greg KH <gregkh@linuxfoundation.org> wrote:
>
> On Thu, Feb 27, 2025 at 11:42:46PM +0100, Lukas Wunner wrote:
> > On Thu, Feb 27, 2025 at 03:16:40AM -0800, Greg KH wrote:
> > > On Thu, Feb 27, 2025 at 01:09:41PM +1000, Alistair Francis wrote:
> > > > The PCI core has just been amended to authenticate CMA-capable devices
> > > > on enumeration and store the result in an "authenticated" bit in struct
> > > > pci_dev->spdm_state.
> > > >
> > > > Expose the bit to user space through an eponymous sysfs attribute.
> > > >
> > > > Allow user space to trigger reauthentication (e.g. after it has updated
> > > > the CMA keyring) by writing to the sysfs attribute.
> > > >
> > > > Implement the attribute in the SPDM library so that other bus types
> > > > besides PCI may take advantage of it.  They just need to add
> > > > spdm_attr_group to the attribute groups of their devices and amend the
> > > > dev_to_spdm_state() helper which retrieves the spdm_state for a given
> > > > device.
> > > >
> > > > The helper may return an ERR_PTR if it couldn't be determined whether
> > > > SPDM is supported by the device.  The sysfs attribute is visible in that
> > > > case but returns an error on access.  This prevents downgrade attacks
> > > > where an attacker disturbs memory allocation or DOE communication
> > > > in order to create the appearance that SPDM is unsupported.
> > >
> > > I don't like this "if it's present we still don't know if the device
> > > supports this", as that is not normally the "sysfs way" here.  Why must
> > > it be present in those situations?

I do think there are 4 situations

 1. Device supports authentication and was authenticated
 2.
  a) Device supports authentication, but the certificate chain can't
be authenticated
  b) Device supports authentication, but the communication was interrupted
 3. Device doesn't support authentication

Case 1 and 3 are easy

Case 2 (a) means that the kernel doesn't trust the certificate. This
could be for a range of reasons, including the user just hasn't
installed the cert yet. So it makes sense to pass that information to
the user as they can act on it. I think it's a different case to 3
(where there is no action for the user to take).

> >
> > That's explained above.
>
> Not really, you just say "downgrade attacks", which is not something
> that we need to worry about, right?  If so, I think this bit is the
> least of our worries.
>
> > Unfortunately there is no (signed) bit in Config Space which tells us
> > whether authentication is supported by a PCI device.  Rather, it is
> > necessary to exchange several messages with the device through a
> > DOE mailbox in config space to determine that.  I'm worried that an
> > attacker deliberately "glitches" those DOE exchanges and thus creates
> > the appearance that the device does not support authentication.
>
> That's a hardware glitch, and if that happens, then it will show a 0 and
> that's the same as not being present at all, right?  Otherwise you just
> pound on the file to try to see if the glitch was not real?  That's not
> going to go over well.
>
> > Let's say the user's policy is to trust legacy devices which do not
> > support authentication, but require authentication for newer NVMe drives
> > from a certain vendor.  An attacker may manipulate an authentication-capable
> > NVMe drive from that vendor, whereupon it will fail authentication.
> > But the attacker can trick the user into trusting the device by glitching
> > the DOE exchanges.
>
> Again, are we now claiming that Linux needs to support "hardware
> glitching"?  Is that required somewhere?  I think if the DOE exchanges
> fail, we just trust the device as we have to trust something, right?

This is case 2 (b) above. If an attacker could flip a bit or drop a
byte in the communication path they could cause the authentication to
stop.

The process to authenticate a device involves sending a lot of data,
so it's not impossible that an attacker could interrupt some of the
traffic.

If the DOE exchange fails I don't think we want to trust the device,
as that's the point of SPDM. So being able to communicate that to
userspace does seem really useful.

>
> > Of course, this is an abnormal situation that users won't encounter
> > unless they're being attacked.  Normally the attribute is only present
> > if authentication is supported.
> >
> > I disagree with your assessment that we have bigger problems.
> > For security protocols like this we have to be very careful
> > to prevent trivial circumvention.  We cannot just shrug this off
> > as unimportant.
>
> hardware glitching is not trivial.  Let's only worry about that if the
> hardware people somehow require it, and if so, we can push back and say
> "stop making us fix your broken designs" :)

Hardware glitching is hard to do, but SPDM is designed to protect
against a range of hard attacks. The types of attacks that nation
states would do. So I think we should do our best to protect against
them.Obviously there is only so much that software can do to protect
against a hardware glitch, but this seems like a good in between.

I don't think the design is overall broken though. At some point when
all devices support SPDM it becomes a non issue as you just fail if
authentication fails, but in the mean time we need to handle devices
with and without authentication.

>
> > The "authenticated" attribute tells user space whether the device
> > is authenticated.  User space needs to handle errors anyway when
> > reading the attribute.  Users will get an error if authentication
> > support could not be determined.  Simple.
>
> No, if it's not determined, it shouldn't be present.

That doesn't allow us to differentiate the cases I mentioned above though.

Another option is we could create multiple attributes "spdm",
"authenticated" and "spdm_err" for example to convey the information
with just yes/no attributes?

>
> > > What is going to happen to suddenly
> > > allow it to come back to be "alive" and working while the device is
> > > still present in the system?
> >
> > The device needs to be re-enumerated by the PCI core to retry
> > determining its authentication capability.  That's why the
> > sysfs documentation says the user may exercise the "remove"
> > and "rescan" attributes to retry authentication.
>
> But how does it know that?  remove and recan is a huge sledgehammer, and
> an amazing one if it even works on most hardware.  Don't make it part of
> any normal process please.
>
> > > I'd prefer it to be "if the file is there, this is supported by the
> > > device.  If the file is not there, it is not supported", as that makes
> > > things so much simpler for userspace (i.e. you don't want userspace to
> > > have to both test if the file is there AND read all entries just to see
> > > if the kernel knows what is going on or not.)
> >
> > Huh?  Read all entries?  The attribute contains only 0 or 1!
> >
> > Or you'll get an error reading it.
>
> It's the error, don't do that.  If an error is going to happen, then
> don't have the file there.  That's the way sysfs works, it's not a
> "let's add all possible files and then make userspace open them all and
> see if an error happens to determine what really is present for this
> device" model.  It's a "if a file is there, that attribute is there and
> we can read it".
>
> > > > Alternatively, authentication success might be signaled to user space
> > > > through a uevent, whereupon it may bind a (blacklisted) driver.
> > >
> > > How will that happen?
> >
> > The SPDM library can be amended to signal a uevent when authentication
> > succeeds or fails and user space can then act on it.  I imagine systemd
> > or some other daemon might listen to such events and do interesting things,
> > such as binding a driver once authentication succeeds.
>
> That's a new user/kernel api and should be designed ONLY if you actually
> need it and have a user.  Otherwise let's just wait for later for that.
>
> > Maybe you have better ideas.  Be constructive!  Make suggestions!
>
> Again, have the file there only if this is something that the hardware
> supports.  Don't fail a read just because the hardware does not seem to
> support it, but it might sometime in the future if you just happen to
> unplug/plug it back in.
>
> > > > +         This prevents downgrade attacks where an attacker consumes
> > > > +         memory or disturbs communication in order to create the
> > > > +         appearance that a device does not support authentication.
> > >
> > > If an attacker can consume kernel memory to cause this to happen you
> > > have bigger problems.  That's not the kernel's issue here at all.
> > >
> > > And "disable communication" means "we just don't support it as the
> > > device doesn't say it does", so again, why does that matter?
> >
> > Reacting to potential attacks sure is the kernel's business.
>
> Reacting to real, software attacks is the kernel's business.  Reacting
> to possible hardware issues that are just theoretical is not.

Generally I would agree, but the idea of SPDM is to react to hardware
attacks, so it's something that the kernel should be conscious of

>
> > > > +         The reason why authentication support could not be determined
> > > > +         is apparent from "dmesg".  To re-probe authentication support
> > > > +         of PCI devices, exercise the "remove" and "rescan" attributes.
> > >
> > > Don't make userspace parse kernel logs for this type of thing.  And
> > > asking userspace to rely on remove and recan is a mess, either show it
> > > works or not.
> >
> > I'd say looking in dmesg to determine whether the user is being attacked
> > is perfectly fine, as is requiring the user to explicitly act on a
> > potential attack.
> >
> >
> > > > --- a/drivers/pci/cma.c
> > > > +++ b/drivers/pci/cma.c
> > > > @@ -171,8 +171,10 @@ void pci_cma_init(struct pci_dev *pdev)
> > > >  {
> > > >   struct pci_doe_mb *doe;
> > > >
> > > > - if (IS_ERR(pci_cma_keyring))
> > > > + if (IS_ERR(pci_cma_keyring)) {
> > > > +         pdev->spdm_state = ERR_PTR(-ENOTTY);
> > >
> > > Why ENOTTY?
> >
> > We use -ENOTTY as return value for unsupported reset methods in the
> > PCI core, see e.g. pcie_reset_flr(), pcie_af_flr(), pci_pm_reset(),
> > pci_parent_bus_reset(), pci_reset_hotplug_slot(), ...
> >
> > We also use -ENOTTY in pci_bridge_wait_for_secondary_bus() and
> > pci_dev_wait().
> >
> > It was used here to be consistent with those existing occurrences
> > in the PCI core.
> >
> > If you'd prefer something else, please make a suggestion.
>
> Ah, didn't realize that was a pci thing, ok, nevermind.
>
> > > > +static ssize_t authenticated_store(struct device *dev,
> > > > +                            struct device_attribute *attr,
> > > > +                            const char *buf, size_t count)
> > > > +{
> > > > + void *spdm_state = dev_to_spdm_state(dev);
> > > > + int rc;
> > > > +
> > > > + if (IS_ERR_OR_NULL(spdm_state))
> > > > +         return PTR_ERR(spdm_state);
> > > > +
> > > > + if (sysfs_streq(buf, "re")) {
> > >
> > > I don't like sysfs files that when reading show a binary, but require a
> > > "magic string" to be written to them to have them do something else.
> > > that way lies madness.  What would you do if each sysfs file had a
> > > custom language that you had to look up for each one?  Be consistant
> > > here.  But again, I don't think you need a store function at all, either
> > > the device supports this, or it doesn't.
> >
> > I'm not sure if you've even read the ABI documentation in full.
> >
> > The store method is needed to reauthenticate the device,
> > e.g. after a new trusted root certificate was added to the
> > kernel's .cma keyring.
>
> Why not have a different file called "reauthentication" that only allows
> a write to it of a 1/Y/y to do the reauthentication.  sysfs is a "one
> file per thing" interface, not a "parse a command and do something but
> when read from return a different value" interface.

That works for me as well

Alistair

>
> Let's keep it dirt simple please.
>
> thanks,
>
> greg k-h

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated
  2025-02-27 19:31               ` Greg KH
@ 2025-02-28  8:49                 ` Miguel Ojeda
  0 siblings, 0 replies; 47+ messages in thread
From: Miguel Ojeda @ 2025-02-28  8:49 UTC (permalink / raw)
  To: Greg KH
  Cc: Alice Ryhl, Alistair Francis, linux-cxl, linux-kernel, lukas,
	linux-pci, bhelgaas, Jonathan.Cameron, rust-for-linux, akpm,
	boqun.feng, bjorn3_gh, wilfred.mallawa, ojeda, alistair23,
	a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis, Emilio Cobos Álvarez

On Thu, Feb 27, 2025 at 8:32 PM Greg KH <gregkh@linuxfoundation.org> wrote:
>
> On Thu, Feb 27, 2025 at 05:47:01PM +0100, Miguel Ojeda wrote:
> >
> > I can look into it, after other build system things are done.
>
> Looks like Alice already sent a series to do this, so no need.

Above I meant the having bidirectional bindings as automated as
possible, which I suspect we could eventually want if Rust keeps
growing. For the time being checking is more than enough, yeah.

Cheers,
Miguel

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated
  2025-02-28  2:27             ` Alistair Francis
@ 2025-03-01  4:27               ` Greg KH
  2025-03-05 19:54               ` Dan Williams
  1 sibling, 0 replies; 47+ messages in thread
From: Greg KH @ 2025-03-01  4:27 UTC (permalink / raw)
  To: Alistair Francis
  Cc: Miguel Ojeda, Alice Ryhl, Alistair Francis, linux-cxl,
	linux-kernel, lukas, linux-pci, bhelgaas, Jonathan.Cameron,
	rust-for-linux, akpm, boqun.feng, bjorn3_gh, wilfred.mallawa,
	ojeda, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

On Fri, Feb 28, 2025 at 12:27:36PM +1000, Alistair Francis wrote:
> On Fri, Feb 28, 2025 at 5:33 AM Greg KH <gregkh@linuxfoundation.org> wrote:
> >
> > On Thu, Feb 27, 2025 at 05:45:02PM +0100, Miguel Ojeda wrote:
> > > On Thu, Feb 27, 2025 at 1:01 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> > > >
> > > > Sorry, you are right, it does, and of course it happens (otherwise how
> > > > would bindings work), but for small functions like this, how is the C
> > > > code kept in sync with the rust side?  Where is the .h file that C
> > > > should include?
> 
> This I can address with something like Alice mentioned earlier to
> ensure the C and Rust functions stay in sync.

Yes, that looks to be fixed up now and should not be an issue.

> > > What you were probably remembering is that it still needs to be
> > > justified, i.e. we don't want to generally/freely start replacing
> > > "individual functions" and doing FFI both ways everywhere, i.e. the
> > > goal is to build safe abstractions wherever possible.
> >
> > Ah, ok, that's what I was remembering.
> >
> > Anyway, the "pass a void blob from C into rust" that this patch is doing
> > feels really odd to me, and hard to verify it is "safe" at a simple
> > glance.
> 
> I agree, it's a bit odd. Ideally I would like to use a sysfs binding,
> but there isn't one today.
> 
> I had a quick look and a Rust sysfs binding implementation seems like
> a lot of work, which I wasn't convinced I wanted to invest in for this
> series. This is only a single sysfs attribute and I didn't want to
> slow down this series on a whole sysfs Rust implementation.
> 
> If this approach isn't ok for now, I will just drop the sysfs changes
> from the series so the SPDM implementation doesn't stall on sysfs
> changes. Then come back to the sysfs attributes in the future.

Please do that, we can revisit the sysfs stuff later.

> So the high level question, is "pass[ing] a void blob from C into
> rust" ok or should I defer for a future safer implementation?

I don't think we want random void * blobs being passed between C and
Rust like that as ensuring that both sides really know what is happening
and keep that in sync is going to be impossible over time.  Type safety
is our friend :)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated
  2025-02-28  2:55         ` Alistair Francis
@ 2025-03-01  4:33           ` Greg KH
  0 siblings, 0 replies; 47+ messages in thread
From: Greg KH @ 2025-03-01  4:33 UTC (permalink / raw)
  To: Alistair Francis
  Cc: Lukas Wunner, Alistair Francis, linux-cxl, linux-kernel,
	linux-pci, bhelgaas, Jonathan.Cameron, rust-for-linux, akpm,
	boqun.feng, bjorn3_gh, wilfred.mallawa, aliceryhl, ojeda,
	a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

On Fri, Feb 28, 2025 at 12:55:30PM +1000, Alistair Francis wrote:
> On Fri, Feb 28, 2025 at 11:41 AM Greg KH <gregkh@linuxfoundation.org> wrote:
> >
> > On Thu, Feb 27, 2025 at 11:42:46PM +0100, Lukas Wunner wrote:
> > > On Thu, Feb 27, 2025 at 03:16:40AM -0800, Greg KH wrote:
> > > > On Thu, Feb 27, 2025 at 01:09:41PM +1000, Alistair Francis wrote:
> > > > > The PCI core has just been amended to authenticate CMA-capable devices
> > > > > on enumeration and store the result in an "authenticated" bit in struct
> > > > > pci_dev->spdm_state.
> > > > >
> > > > > Expose the bit to user space through an eponymous sysfs attribute.
> > > > >
> > > > > Allow user space to trigger reauthentication (e.g. after it has updated
> > > > > the CMA keyring) by writing to the sysfs attribute.
> > > > >
> > > > > Implement the attribute in the SPDM library so that other bus types
> > > > > besides PCI may take advantage of it.  They just need to add
> > > > > spdm_attr_group to the attribute groups of their devices and amend the
> > > > > dev_to_spdm_state() helper which retrieves the spdm_state for a given
> > > > > device.
> > > > >
> > > > > The helper may return an ERR_PTR if it couldn't be determined whether
> > > > > SPDM is supported by the device.  The sysfs attribute is visible in that
> > > > > case but returns an error on access.  This prevents downgrade attacks
> > > > > where an attacker disturbs memory allocation or DOE communication
> > > > > in order to create the appearance that SPDM is unsupported.
> > > >
> > > > I don't like this "if it's present we still don't know if the device
> > > > supports this", as that is not normally the "sysfs way" here.  Why must
> > > > it be present in those situations?
> 
> I do think there are 4 situations
> 
>  1. Device supports authentication and was authenticated
>  2.
>   a) Device supports authentication, but the certificate chain can't
> be authenticated
>   b) Device supports authentication, but the communication was interrupted
>  3. Device doesn't support authentication
> 
> Case 1 and 3 are easy
> 
> Case 2 (a) means that the kernel doesn't trust the certificate. This
> could be for a range of reasons, including the user just hasn't
> installed the cert yet. So it makes sense to pass that information to
> the user as they can act on it. I think it's a different case to 3
> (where there is no action for the user to take).

Ok, that's fine, but as a "default" you should fail such that the device
is not allowed to actually do anything if 2) happens.  I think you are
doing that, it's the whole use of sysfs as the user/kernel api here in
an odd way that I am objecting to.

> > Again, are we now claiming that Linux needs to support "hardware
> > glitching"?  Is that required somewhere?  I think if the DOE exchanges
> > fail, we just trust the device as we have to trust something, right?
> 
> This is case 2 (b) above. If an attacker could flip a bit or drop a
> byte in the communication path they could cause the authentication to
> stop.
> 
> The process to authenticate a device involves sending a lot of data,
> so it's not impossible that an attacker could interrupt some of the
> traffic.
> 
> If the DOE exchange fails I don't think we want to trust the device,
> as that's the point of SPDM. So being able to communicate that to
> userspace does seem really useful.

Agreed, but again, let's come up with a better api than "a random sysfs
file is present but reading it gets an error to convey this is an
unvalidated device" feels wrong to me.

Why not just report the "state" of the device?  You have 3 states, just
report that?  Should be much simpler overall.

> > > Of course, this is an abnormal situation that users won't encounter
> > > unless they're being attacked.  Normally the attribute is only present
> > > if authentication is supported.
> > >
> > > I disagree with your assessment that we have bigger problems.
> > > For security protocols like this we have to be very careful
> > > to prevent trivial circumvention.  We cannot just shrug this off
> > > as unimportant.
> >
> > hardware glitching is not trivial.  Let's only worry about that if the
> > hardware people somehow require it, and if so, we can push back and say
> > "stop making us fix your broken designs" :)
> 
> Hardware glitching is hard to do, but SPDM is designed to protect
> against a range of hard attacks. The types of attacks that nation
> states would do. So I think we should do our best to protect against
> them.Obviously there is only so much that software can do to protect
> against a hardware glitch, but this seems like a good in between.
> 
> I don't think the design is overall broken though. At some point when
> all devices support SPDM it becomes a non issue as you just fail if
> authentication fails, but in the mean time we need to handle devices
> with and without authentication.

Agreed, again it's the user/kernel api I am objecting to here the most.

> > > The "authenticated" attribute tells user space whether the device
> > > is authenticated.  User space needs to handle errors anyway when
> > > reading the attribute.  Users will get an error if authentication
> > > support could not be determined.  Simple.
> >
> > No, if it's not determined, it shouldn't be present.
> 
> That doesn't allow us to differentiate the cases I mentioned above though.
> 
> Another option is we could create multiple attributes "spdm",
> "authenticated" and "spdm_err" for example to convey the information
> with just yes/no attributes?

Yes!  Or a simple "spdm_state" file that has one of these values in it?
sysfs files do not need to only return "1 or 0", they can return
different strings, but only 1 at a time.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated
  2025-02-28  1:39       ` Greg KH
  2025-02-28  2:55         ` Alistair Francis
@ 2025-03-01 18:01         ` Lukas Wunner
  1 sibling, 0 replies; 47+ messages in thread
From: Lukas Wunner @ 2025-03-01 18:01 UTC (permalink / raw)
  To: Greg KH
  Cc: Alistair Francis, linux-cxl, linux-kernel, linux-pci, bhelgaas,
	Jonathan.Cameron, rust-for-linux, akpm, boqun.feng, bjorn3_gh,
	wilfred.mallawa, aliceryhl, ojeda, alistair23, a.hindborg,
	tmgross, gary, alex.gaynor, benno.lossin, Alistair Francis

On Thu, Feb 27, 2025 at 05:39:53PM -0800, Greg KH wrote:
> On Thu, Feb 27, 2025 at 11:42:46PM +0100, Lukas Wunner wrote:
> > On Thu, Feb 27, 2025 at 03:16:40AM -0800, Greg KH wrote:
> > > I don't like this "if it's present we still don't know if the device
> > > supports this", as that is not normally the "sysfs way" here.  Why must
> > > it be present in those situations?
> > 
> > That's explained above.
> 
> Not really, you just say "downgrade attacks", which is not something
> that we need to worry about, right?

A downgrade attack means duping the victim into believing that only
a weaker security mode is supported.  E.g. only sha1, but not sha256.

In this context, downgrade attack means duping the kernel or user
into believing that SPDM authentication is unsupported, even though it is.

https://en.wikipedia.org/wiki/Downgrade_attack

That's definitely something we need to be aware of and guard against,
otherwise what's the point of authenticating in the first place.


> > Unfortunately there is no (signed) bit in Config Space which tells us
> > whether authentication is supported by a PCI device.  Rather, it is
> > necessary to exchange several messages with the device through a
> > DOE mailbox in config space to determine that.  I'm worried that an
> > attacker deliberately "glitches" those DOE exchanges and thus creates
> > the appearance that the device does not support authentication.
> 
> That's a hardware glitch, and if that happens, then it will show a 0 and
> that's the same as not being present at all, right?

No, the "authenticated" attribute is not present in sysfs if authentication
is unsupported.

The downgrade attack protection comprises exposing the attribute if it
could not be determined whether authentication is supported or not,
and returning an error (ENOTTY) on read or write.

User space applications need to check anyway whether read() or write()
failed for some reason.  E.g. if the device is hot-removed concurrently,
the read() system call returns ENODEV.  So returning ENOTTY is just
another error that can occur on access to the attribute.

The idea is that user space typically wants to check whether the attribute
contains "1", signifying that the device was authenticated successfully.
Hence a return value of "0" or any error code signifies that the device
is not authenticated.

And if user space wants to check whether authentication is supported at all,
it checks for presence of the sysfs attribute.  Hence exposing the attribute
if support could not be determined is a safety net to not mislead user space
that the device does not support authentication.

For PCIe, glitching the hardware (the electric signals exchanged with
the device) is indeed one way to disrupt the DOE and SPDM exchanges.

However the SPDM protocol has not only been adopted by PCIe, but also
other buses, in particular SCSI and ATA.  And in those cases, glitching
the SPDM exchanges may be a pure software thing.  (Think iSCSI communication
with storage devices in a remote rack or data center.)

Damien Le Moal has explicitly requested that the user space ABI for SPDM
is consistent across buses.  So the downgrade attack protection can be
taken advantage of by those other buses as well.


> > Let's say the user's policy is to trust legacy devices which do not
> > support authentication, but require authentication for newer NVMe drives
> > from a certain vendor.  An attacker may manipulate an authentication-capable
> > NVMe drive from that vendor, whereupon it will fail authentication.
> > But the attacker can trick the user into trusting the device by glitching
> > the DOE exchanges.
> 
> Again, are we now claiming that Linux needs to support "hardware
> glitching"?  Is that required somewhere?

Required?  It's simply prudent to protect users from being duped into
thinking the device doesn't support authentication.


> I think if the DOE exchanges
> fail, we just trust the device as we have to trust something, right?

If the DOE exchanges fail, something fishy is going on.
Why should we hide that fact from the user?


> > The device needs to be re-enumerated by the PCI core to retry
> > determining its authentication capability.  That's why the
> > sysfs documentation says the user may exercise the "remove"
> > and "rescan" attributes to retry authentication.
> 
> But how does it know that?

Because reads and writes to the attribute return ENOTTY.

> remove and recan is a huge sledgehammer, and
> an amazing one if it even works on most hardware.  Don't make it part of
> any normal process please.

It's not a normal process.  It's manual recovery in case of a
potential attack.  The user can also choose to unplug the device
or reboot the machine.  That's arguably a bigger sledgehammer.


> It's the error, don't do that.  If an error is going to happen, then
> don't have the file there.  That's the way sysfs works, it's not a
> "let's add all possible files and then make userspace open them all and
> see if an error happens to determine what really is present for this
> device" model.  It's a "if a file is there, that attribute is there and
> we can read it".

The point is that if the file isn't there even though the device might
support authentication, we're creating a false and dangerous illusion.
This is different from other attributes which don't have that quality.


> > > > Alternatively, authentication success might be signaled to user space
> > > > through a uevent, whereupon it may bind a (blacklisted) driver.
> > > 
> > > How will that happen?
> > 
> > The SPDM library can be amended to signal a uevent when authentication
> > succeeds or fails and user space can then act on it.  I imagine systemd
> > or some other daemon might listen to such events and do interesting things,
> > such as binding a driver once authentication succeeds.
> 
> That's a new user/kernel api and should be designed ONLY if you actually
> need it and have a user.  Otherwise let's just wait for later for that.

Of course.  Again, the commit message makes suggestions for future
extensions to justify the change.  Those are just ideas.  Whether
and how they are implemented remains to be seen.  Signaling a uevent
on authentication success or failure seems like an obvious idea,
hence I included it in the commit message.

I fear if I don't include those ideas in the commit message, someone
will come along and ask "why do you need this at all?", thus putting
into question the whole set of authentication patches.


> > > If an attacker can consume kernel memory to cause this to happen you
> > > have bigger problems.  That's not the kernel's issue here at all.
> > > 
> > > And "disable communication" means "we just don't support it as the
> > > device doesn't say it does", so again, why does that matter?
> > 
> > Reacting to potential attacks sure is the kernel's business.
> 
> Reacting to real, software attacks is the kernel's business.  Reacting
> to possible hardware issues that are just theoretical is not.

We have fundamental disagreement whether certain attacks need to be taken
seriously.  Which reminds me of...

   "the final topic on the agenda was the corporate attempt at
    security consciousness raising; a shouting match ensued,
    in the course of which several and various reputations
    were sullied, certain paranoid reactions were taken less
    than seriously, and no great meeting of the minds was met."

   [minutes of the uucp-lovers interest group, 20 April 1983,
    from "A Quarter Century of UNIX" (1994) page 113]
    https://wiki.tuhs.org/lib/exe/fetch.php?media=publications:qcu.pdf

Looks like we're just upholding the time honored tradition of
UNIX security disagreements!

Thanks,

Lukas

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated
  2025-02-28  2:27             ` Alistair Francis
  2025-03-01  4:27               ` Greg KH
@ 2025-03-05 19:54               ` Dan Williams
  2025-03-07  1:04                 ` Alistair Francis
  1 sibling, 1 reply; 47+ messages in thread
From: Dan Williams @ 2025-03-05 19:54 UTC (permalink / raw)
  To: Alistair Francis, Greg KH
  Cc: Miguel Ojeda, Alice Ryhl, Alistair Francis, linux-cxl,
	linux-kernel, lukas, linux-pci, bhelgaas, Jonathan.Cameron,
	rust-for-linux, akpm, boqun.feng, bjorn3_gh, wilfred.mallawa,
	ojeda, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

Alistair Francis wrote:
> On Fri, Feb 28, 2025 at 5:33 AM Greg KH <gregkh@linuxfoundation.org> wrote:
> >
> > On Thu, Feb 27, 2025 at 05:45:02PM +0100, Miguel Ojeda wrote:
> > > On Thu, Feb 27, 2025 at 1:01 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> > > >
> > > > Sorry, you are right, it does, and of course it happens (otherwise how
> > > > would bindings work), but for small functions like this, how is the C
> > > > code kept in sync with the rust side?  Where is the .h file that C
> > > > should include?
> 
> This I can address with something like Alice mentioned earlier to
> ensure the C and Rust functions stay in sync.
> 
> > >
> > > What you were probably remembering is that it still needs to be
> > > justified, i.e. we don't want to generally/freely start replacing
> > > "individual functions" and doing FFI both ways everywhere, i.e. the
> > > goal is to build safe abstractions wherever possible.
> >
> > Ah, ok, that's what I was remembering.
> >
> > Anyway, the "pass a void blob from C into rust" that this patch is doing
> > feels really odd to me, and hard to verify it is "safe" at a simple
> > glance.
> 
> I agree, it's a bit odd. Ideally I would like to use a sysfs binding,
> but there isn't one today.
> 
> I had a quick look and a Rust sysfs binding implementation seems like
> a lot of work, which I wasn't convinced I wanted to invest in for this
> series. This is only a single sysfs attribute and I didn't want to
> slow down this series on a whole sysfs Rust implementation.
> 
> If this approach isn't ok for now, I will just drop the sysfs changes
> from the series so the SPDM implementation doesn't stall on sysfs
> changes. Then come back to the sysfs attributes in the future.

This highlights a concern I have about what this means for ongoing
collaboration between this native PCI device-authentication (CMA)
enabling and the platform TEE Security Manager (TSM) based
device-security enabling.

First, I think Rust for a security protocol like SPDM makes a lot of
sense. However, I have also been anticipating overlap between the ABIs
for conveying security collateral like measurements, transcripts, nonces
etc between PCI CMA and PCI TSM. I.e. potential opportunities to
refactor SPDM core helpers for reuse. A language barrier and an ABI
barrier (missing Rust integrations for sysfs and netlink ABIs that were
discussed at Plumbers) limits that potential collaboration.

Now if you told me the C SPDM work will continue and the Rust SPDM
implementation will follow in behind until this space settles down in a
year or so, great. I am not sure it works the other way, drop the C
side, when the Rust side is not ready / able to invest in some ABI
integrations that C consumers need.

Otherwise this thread seems to be suggesting that people like me need to
accelerate nascent cross C / Rust refactoring skills, or lean on Rust
folks to care about that refactoring work.

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated
  2025-03-05 19:54               ` Dan Williams
@ 2025-03-07  1:04                 ` Alistair Francis
  2025-03-07 23:37                   ` Dan Williams
  0 siblings, 1 reply; 47+ messages in thread
From: Alistair Francis @ 2025-03-07  1:04 UTC (permalink / raw)
  To: Dan Williams
  Cc: Greg KH, Miguel Ojeda, Alice Ryhl, Alistair Francis, linux-cxl,
	linux-kernel, lukas, linux-pci, bhelgaas, Jonathan.Cameron,
	rust-for-linux, akpm, boqun.feng, bjorn3_gh, wilfred.mallawa,
	ojeda, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

On Thu, Mar 6, 2025 at 5:55 AM Dan Williams <dan.j.williams@intel.com> wrote:
>
> Alistair Francis wrote:
> > On Fri, Feb 28, 2025 at 5:33 AM Greg KH <gregkh@linuxfoundation.org> wrote:
> > >
> > > On Thu, Feb 27, 2025 at 05:45:02PM +0100, Miguel Ojeda wrote:
> > > > On Thu, Feb 27, 2025 at 1:01 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> > > > >
> > > > > Sorry, you are right, it does, and of course it happens (otherwise how
> > > > > would bindings work), but for small functions like this, how is the C
> > > > > code kept in sync with the rust side?  Where is the .h file that C
> > > > > should include?
> >
> > This I can address with something like Alice mentioned earlier to
> > ensure the C and Rust functions stay in sync.
> >
> > > >
> > > > What you were probably remembering is that it still needs to be
> > > > justified, i.e. we don't want to generally/freely start replacing
> > > > "individual functions" and doing FFI both ways everywhere, i.e. the
> > > > goal is to build safe abstractions wherever possible.
> > >
> > > Ah, ok, that's what I was remembering.
> > >
> > > Anyway, the "pass a void blob from C into rust" that this patch is doing
> > > feels really odd to me, and hard to verify it is "safe" at a simple
> > > glance.
> >
> > I agree, it's a bit odd. Ideally I would like to use a sysfs binding,
> > but there isn't one today.
> >
> > I had a quick look and a Rust sysfs binding implementation seems like
> > a lot of work, which I wasn't convinced I wanted to invest in for this
> > series. This is only a single sysfs attribute and I didn't want to
> > slow down this series on a whole sysfs Rust implementation.
> >
> > If this approach isn't ok for now, I will just drop the sysfs changes
> > from the series so the SPDM implementation doesn't stall on sysfs
> > changes. Then come back to the sysfs attributes in the future.
>
> This highlights a concern I have about what this means for ongoing
> collaboration between this native PCI device-authentication (CMA)
> enabling and the platform TEE Security Manager (TSM) based
> device-security enabling.
>
> First, I think Rust for a security protocol like SPDM makes a lot of
> sense. However, I have also been anticipating overlap between the ABIs
> for conveying security collateral like measurements, transcripts, nonces
> etc between PCI CMA and PCI TSM. I.e. potential opportunities to
> refactor SPDM core helpers for reuse. A language barrier and an ABI
> barrier (missing Rust integrations for sysfs and netlink ABIs that were
> discussed at Plumbers) limits that potential collaboration.

I see your concern, but I'm not sure it's as bad as you think.

We will need to expose the Rust code to C no matter what. The CMA,
NVMe, SATA and SAS is all C code, so the Rust library will have a nice
C style ABI to allow those subsystems to call the code.

The sysfs issue is mostly because I am trying to write as much of the
sysfs code in Rust, but there aren't bindings yet.

So if we want to re-use code (such as measurements, transcripts or
nonces) we just need to expose a C style function in Rust which can
then can then be used.

So I don't think this really limits code re-use. Obviously there might
need to be some refactoring to share the code, but that will be true
of C or Rust code.

>
> Now if you told me the C SPDM work will continue and the Rust SPDM
> implementation will follow in behind until this space settles down in a
> year or so, great. I am not sure it works the other way, drop the C

That was kind of my original plan (see the first RFC), but maintaining
both, with at least one being out of tree, will be a huge pain and
prone to breakage.

Also I suspect the Rust implementation will struggle to keep up if the
C version is merged (and hence has more people looking at it) compared
to just me working on the Rust code.

> side, when the Rust side is not ready / able to invest in some ABI
> integrations that C consumers need.
>
> Otherwise this thread seems to be suggesting that people like me need to
> accelerate nascent cross C / Rust refactoring skills, or lean on Rust
> folks to care about that refactoring work.

I'm happy to help maintain the C / Rust code and help refactor as
required. The current ABI to this library is all "C style" Rust to
keep it simple and easily compatible with the out of tree C SPDM
implementation and callers of the code.

So hopefully it doesn't require much Rust knowledge to refactor the ABI.

This is the type of feedback that I wanted to get though. Before this
goes too far is it something that is going to be accepted upstream?

Alistair

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated
  2025-03-07  1:04                 ` Alistair Francis
@ 2025-03-07 23:37                   ` Dan Williams
  2025-03-09 22:57                     ` Alistair Francis
  0 siblings, 1 reply; 47+ messages in thread
From: Dan Williams @ 2025-03-07 23:37 UTC (permalink / raw)
  To: Alistair Francis, Dan Williams
  Cc: Greg KH, Miguel Ojeda, Alice Ryhl, Alistair Francis, linux-cxl,
	linux-kernel, lukas, linux-pci, bhelgaas, Jonathan.Cameron,
	rust-for-linux, akpm, boqun.feng, bjorn3_gh, wilfred.mallawa,
	ojeda, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

Alistair Francis wrote:
> On Thu, Mar 6, 2025 at 5:55 AM Dan Williams <dan.j.williams@intel.com> wrote:
> >
> > Alistair Francis wrote:
> > > On Fri, Feb 28, 2025 at 5:33 AM Greg KH <gregkh@linuxfoundation.org> wrote:
> > > >
> > > > On Thu, Feb 27, 2025 at 05:45:02PM +0100, Miguel Ojeda wrote:
> > > > > On Thu, Feb 27, 2025 at 1:01 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> > > > > >
> > > > > > Sorry, you are right, it does, and of course it happens (otherwise how
> > > > > > would bindings work), but for small functions like this, how is the C
> > > > > > code kept in sync with the rust side?  Where is the .h file that C
> > > > > > should include?
> > >
> > > This I can address with something like Alice mentioned earlier to
> > > ensure the C and Rust functions stay in sync.
> > >
> > > > >
> > > > > What you were probably remembering is that it still needs to be
> > > > > justified, i.e. we don't want to generally/freely start replacing
> > > > > "individual functions" and doing FFI both ways everywhere, i.e. the
> > > > > goal is to build safe abstractions wherever possible.
> > > >
> > > > Ah, ok, that's what I was remembering.
> > > >
> > > > Anyway, the "pass a void blob from C into rust" that this patch is doing
> > > > feels really odd to me, and hard to verify it is "safe" at a simple
> > > > glance.
> > >
> > > I agree, it's a bit odd. Ideally I would like to use a sysfs binding,
> > > but there isn't one today.
> > >
> > > I had a quick look and a Rust sysfs binding implementation seems like
> > > a lot of work, which I wasn't convinced I wanted to invest in for this
> > > series. This is only a single sysfs attribute and I didn't want to
> > > slow down this series on a whole sysfs Rust implementation.
> > >
> > > If this approach isn't ok for now, I will just drop the sysfs changes
> > > from the series so the SPDM implementation doesn't stall on sysfs
> > > changes. Then come back to the sysfs attributes in the future.
> >
> > This highlights a concern I have about what this means for ongoing
> > collaboration between this native PCI device-authentication (CMA)
> > enabling and the platform TEE Security Manager (TSM) based
> > device-security enabling.
> >
> > First, I think Rust for a security protocol like SPDM makes a lot of
> > sense. However, I have also been anticipating overlap between the ABIs
> > for conveying security collateral like measurements, transcripts, nonces
> > etc between PCI CMA and PCI TSM. I.e. potential opportunities to
> > refactor SPDM core helpers for reuse. A language barrier and an ABI
> > barrier (missing Rust integrations for sysfs and netlink ABIs that were
> > discussed at Plumbers) limits that potential collaboration.
> 
> I see your concern, but I'm not sure it's as bad as you think.
> 
> We will need to expose the Rust code to C no matter what. The CMA,
> NVMe, SATA and SAS is all C code, so the Rust library will have a nice
> C style ABI to allow those subsystems to call the code.

That indeed alleviates a significant amount of the concern. I.e.
multiple planned C users makes it less likely that the needs of yet one
more C user, PCI TSM, get buried deep in the Rust implementation.

> The sysfs issue is mostly because I am trying to write as much of the
> sysfs code in Rust, but there aren't bindings yet.
> 
> So if we want to re-use code (such as measurements, transcripts or
> nonces) we just need to expose a C style function in Rust which can
> then can then be used.

Makes sense.

> > Now if you told me the C SPDM work will continue and the Rust SPDM
> > implementation will follow in behind until this space settles down in a
> > year or so, great. I am not sure it works the other way, drop the C
> 
> That was kind of my original plan (see the first RFC), but maintaining
> both, with at least one being out of tree, will be a huge pain and
> prone to breakage.
> 
> Also I suspect the Rust implementation will struggle to keep up if the
> C version is merged (and hence has more people looking at it) compared
> to just me working on the Rust code.

The practical questions that come to mind are:

   Do we have time?:
   I.e. How long can we continue to develop both out of tree? Presumably,
   until the device ecosystem matures, when is that?

   Are all users served by Rust SPDM?:
   Once the device ecosystem matures can all architectures and
   distributions get their needs met with a Rust dependency?

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated
  2025-03-07 23:37                   ` Dan Williams
@ 2025-03-09 22:57                     ` Alistair Francis
  0 siblings, 0 replies; 47+ messages in thread
From: Alistair Francis @ 2025-03-09 22:57 UTC (permalink / raw)
  To: Dan Williams
  Cc: Greg KH, Miguel Ojeda, Alice Ryhl, Alistair Francis, linux-cxl,
	linux-kernel, lukas, linux-pci, bhelgaas, Jonathan.Cameron,
	rust-for-linux, akpm, boqun.feng, bjorn3_gh, wilfred.mallawa,
	ojeda, a.hindborg, tmgross, gary, alex.gaynor, benno.lossin,
	Alistair Francis

On Sat, Mar 8, 2025 at 9:38 AM Dan Williams <dan.j.williams@intel.com> wrote:
>
> Alistair Francis wrote:
> > On Thu, Mar 6, 2025 at 5:55 AM Dan Williams <dan.j.williams@intel.com> wrote:
> > >
> > > Alistair Francis wrote:
> > > > On Fri, Feb 28, 2025 at 5:33 AM Greg KH <gregkh@linuxfoundation.org> wrote:
> > > > >
> > > > > On Thu, Feb 27, 2025 at 05:45:02PM +0100, Miguel Ojeda wrote:
> > > > > > On Thu, Feb 27, 2025 at 1:01 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> > > > > > >
> > > > > > > Sorry, you are right, it does, and of course it happens (otherwise how
> > > > > > > would bindings work), but for small functions like this, how is the C
> > > > > > > code kept in sync with the rust side?  Where is the .h file that C
> > > > > > > should include?
> > > >
> > > > This I can address with something like Alice mentioned earlier to
> > > > ensure the C and Rust functions stay in sync.
> > > >
> > > > > >
> > > > > > What you were probably remembering is that it still needs to be
> > > > > > justified, i.e. we don't want to generally/freely start replacing
> > > > > > "individual functions" and doing FFI both ways everywhere, i.e. the
> > > > > > goal is to build safe abstractions wherever possible.
> > > > >
> > > > > Ah, ok, that's what I was remembering.
> > > > >
> > > > > Anyway, the "pass a void blob from C into rust" that this patch is doing
> > > > > feels really odd to me, and hard to verify it is "safe" at a simple
> > > > > glance.
> > > >
> > > > I agree, it's a bit odd. Ideally I would like to use a sysfs binding,
> > > > but there isn't one today.
> > > >
> > > > I had a quick look and a Rust sysfs binding implementation seems like
> > > > a lot of work, which I wasn't convinced I wanted to invest in for this
> > > > series. This is only a single sysfs attribute and I didn't want to
> > > > slow down this series on a whole sysfs Rust implementation.
> > > >
> > > > If this approach isn't ok for now, I will just drop the sysfs changes
> > > > from the series so the SPDM implementation doesn't stall on sysfs
> > > > changes. Then come back to the sysfs attributes in the future.
> > >
> > > This highlights a concern I have about what this means for ongoing
> > > collaboration between this native PCI device-authentication (CMA)
> > > enabling and the platform TEE Security Manager (TSM) based
> > > device-security enabling.
> > >
> > > First, I think Rust for a security protocol like SPDM makes a lot of
> > > sense. However, I have also been anticipating overlap between the ABIs
> > > for conveying security collateral like measurements, transcripts, nonces
> > > etc between PCI CMA and PCI TSM. I.e. potential opportunities to
> > > refactor SPDM core helpers for reuse. A language barrier and an ABI
> > > barrier (missing Rust integrations for sysfs and netlink ABIs that were
> > > discussed at Plumbers) limits that potential collaboration.
> >
> > I see your concern, but I'm not sure it's as bad as you think.
> >
> > We will need to expose the Rust code to C no matter what. The CMA,
> > NVMe, SATA and SAS is all C code, so the Rust library will have a nice
> > C style ABI to allow those subsystems to call the code.
>
> That indeed alleviates a significant amount of the concern. I.e.
> multiple planned C users makes it less likely that the needs of yet one
> more C user, PCI TSM, get buried deep in the Rust implementation.
>
> > The sysfs issue is mostly because I am trying to write as much of the
> > sysfs code in Rust, but there aren't bindings yet.
> >
> > So if we want to re-use code (such as measurements, transcripts or
> > nonces) we just need to expose a C style function in Rust which can
> > then can then be used.
>
> Makes sense.
>
> > > Now if you told me the C SPDM work will continue and the Rust SPDM
> > > implementation will follow in behind until this space settles down in a
> > > year or so, great. I am not sure it works the other way, drop the C
> >
> > That was kind of my original plan (see the first RFC), but maintaining
> > both, with at least one being out of tree, will be a huge pain and
> > prone to breakage.
> >
> > Also I suspect the Rust implementation will struggle to keep up if the
> > C version is merged (and hence has more people looking at it) compared
> > to just me working on the Rust code.
>
> The practical questions that come to mind are:
>
>    Do we have time?:
>    I.e. How long can we continue to develop both out of tree? Presumably,
>    until the device ecosystem matures, when is that?

I guess that's a difficult question to answer. There are SPDM capable
devices on the market now ([1] and [2] for example).

While the ecosystem isn't mature, having something upstream makes it a
lot easier to build and improve on. For example MCTP/I2C/SMBus support
could be built on top of this series by someone if/when they are
interested. We are also working on SPDM storage support, which builds
on this. It is always harder to work with when the code is out of
tree.

The other big plus of having something upstream is it will encourage
vendors to test against the kernel implementation. Which might help
catch buggy devices before they ship.

Note that at the moment the C and Rust implementations are ABI
compatible and can be easily swapped out.

>
>    Are all users served by Rust SPDM?:

That's my hope

>    Once the device ecosystem matures can all architectures and
>    distributions get their needs met with a Rust dependency?

That's a good question. I don't have a complete answer. I do think all
architectures that are interested in SPDM (think servers and PC class)
support Rust, so that should be ok.

As for distributions, I also think every major distro supports Rust,
so extending that to the kernel shouldn't be too difficult if it isn't
already supported today.

The decision is really up to the kernel community if you are ready for
this. Note that this series can't be merged today as it depends on
some other patches which aren't yet upstream. So this is still a
release or two away from being actually mergable.

1: https://www.microchip.com/en-us/tools-resources/reference-designs/ocp-power-supply-spdm-solution-demonstration-application
2: https://docs.nvidia.com/networking/display/nvidiadeviceattestationandcorimbasedreferencemeasurementsharingv20

Alistair

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC v2 00/20] lib: Rust implementation of SPDM
  2025-02-27  3:09 [RFC v2 00/20] lib: Rust implementation of SPDM Alistair Francis
                   ` (19 preceding siblings ...)
  2025-02-27  3:09 ` [RFC v2 20/20] lib: rspdm: Support SPDM challenge Alistair Francis
@ 2025-06-11 13:37 ` Jonathan Cameron
  2025-06-12  5:57   ` Alistair Francis
  20 siblings, 1 reply; 47+ messages in thread
From: Jonathan Cameron @ 2025-06-11 13:37 UTC (permalink / raw)
  To: Alistair Francis
  Cc: linux-cxl, linux-kernel, lukas, linux-pci, bhelgaas,
	rust-for-linux, akpm, boqun.feng, bjorn3_gh, wilfred.mallawa,
	aliceryhl, ojeda, alistair23, a.hindborg, tmgross, gary,
	alex.gaynor, benno.lossin

On Thu, 27 Feb 2025 13:09:32 +1000
Alistair Francis <alistair@alistair23.me> wrote:

> Security Protocols and Data Models (SPDM) [1] is used for authentication,
> attestation and key exchange. SPDM is generally used over a range of
> transports, such as PCIe, MCTP/SMBus/I3C, ATA, SCSI, NVMe or TCP.
> 
> From the kernels perspective SPDM is used to authenticate and attest devices.
> In this threat model a device is considered untrusted until it can be verified
> by the kernel and userspace using SPDM. As such SPDM data is untrusted data
> that can be mallicious.
> 
> The SPDM specification is also complex, with the 1.2.1 spec being almost 200
> pages and the 1.3.0 spec being almost 250 pages long.
> 
> As such we have the kernel parsing untrusted responses from a complex
> specification, which sounds like a possible exploit vector. This is the type
> of place where Rust excels!
> 
> This series implements a SPDM requester in Rust.
> 
> This is very similar to Lukas' implementation [2]. This series includes patches
> and files from Lukas' C SPDM implementation, which isn't in mainline.
> 
> This is a standalone series and doesn't depend on Lukas' implementation, although
> we do still rely on Lukas' crypto preperation patches, not all of which are
> upstream yet.
> 
> To help with maintaining compatibility it's designed in a way to match Lukas'
> design and the state struct stores the same information, although in a Rust
> struct instead of the original C one.
> 
> This series doesn't expose the data to userspace (except for a single sysfs
> bool) to avoid the debate about how to do that. I'm planning to do that in
> the future though.
> 
> This series is based on the latest rust-next tree.
> 
> This seris depends on the Untrusted abstraction work [4].
> 
> This seris also depends on the recent bindgen support for static inlines  [5].
> 
> The entire tree can be seen here: https://github.com/alistair23/linux/tree/alistair/spdm-rust
> 
> based-on: https://lore.kernel.org/rust-for-linux/20240925205244.873020-1-benno.lossin@proton.me/
> based-on: https://lore.kernel.org/rust-for-linux/20250107035058.818539-1-alistair@alistair23.me/

Hi Alastair,

I've completely failed to find time to actually pick up enough rust to review
this :(  Also failed to find anyone else who has the rust skills and enough of
the background.

Ideally I'll get up to speed at some point, but in the meantime wanted to revisit
the question of whether we want to go this way from day 1 rather than trying to
deal with C version and later this?

What's your current thoughts?  I know Lukas mentioned he was going to spin a
new version shortly (in one of the TSM threads) so are we waiting on that?

For now I'm going to take this off my review queue. Sorry!

Jonathan



> 
> 1: https://www.dmtf.org/standards/spdm
> 2: https://lore.kernel.org/all/cover.1719771133.git.lukas@wunner.de/
> 3: https://github.com/l1k/linux/commits/spdm-future/
> 4: https://lore.kernel.org/rust-for-linux/20240925205244.873020-1-benno.lossin@proton.me/
> 5: https://lore.kernel.org/rust-for-linux/20250107035058.818539-1-alistair@alistair23.me/
> 
> v2:
>  - Drop support for Rust and C implementations
>  - Include patches from Lukas to reduce series deps
>  - Large code cleanups based on more testing
>  - Support support for authentication
> 
> Alistair Francis (12):
>   lib: rspdm: Initial commit of Rust SPDM
>   lib: rspdm: Support SPDM get_version
>   lib: rspdm: Support SPDM get_capabilities
>   lib: rspdm: Support SPDM negotiate_algorithms
>   lib: rspdm: Support SPDM get_digests
>   lib: rspdm: Support SPDM get_certificate
>   crypto: asymmetric_keys - Load certificate parsing early in boot
>   KEYS: Load keyring and certificates early in boot
>   PCI/CMA: Support built in X.509 certificates
>   lib: rspdm: Support SPDM certificate validation
>   rust: allow extracting the buffer from a CString
>   lib: rspdm: Support SPDM challenge
> 
> Jonathan Cameron (1):
>   PCI/CMA: Authenticate devices on enumeration
> 
> Lukas Wunner (7):
>   X.509: Make certificate parser public
>   X.509: Parse Subject Alternative Name in certificates
>   X.509: Move certificate length retrieval into new helper
>   certs: Create blacklist keyring earlier
>   PCI/CMA: Validate Subject Alternative Name in certificates
>   PCI/CMA: Reauthenticate devices on reset and resume
>   PCI/CMA: Expose in sysfs whether devices are authenticated
> 
>  Documentation/ABI/testing/sysfs-devices-spdm |   31 +
>  MAINTAINERS                                  |   14 +
>  certs/blacklist.c                            |    4 +-
>  certs/system_keyring.c                       |    4 +-
>  crypto/asymmetric_keys/asymmetric_type.c     |    2 +-
>  crypto/asymmetric_keys/x509_cert_parser.c    |    9 +
>  crypto/asymmetric_keys/x509_loader.c         |   38 +-
>  crypto/asymmetric_keys/x509_parser.h         |   40 +-
>  crypto/asymmetric_keys/x509_public_key.c     |    2 +-
>  drivers/pci/Kconfig                          |   13 +
>  drivers/pci/Makefile                         |    4 +
>  drivers/pci/cma.asn1                         |   41 +
>  drivers/pci/cma.c                            |  272 +++++
>  drivers/pci/doe.c                            |    5 +-
>  drivers/pci/pci-driver.c                     |    1 +
>  drivers/pci/pci-sysfs.c                      |    3 +
>  drivers/pci/pci.c                            |   12 +-
>  drivers/pci/pci.h                            |   15 +
>  drivers/pci/pcie/err.c                       |    3 +
>  drivers/pci/probe.c                          |    1 +
>  drivers/pci/remove.c                         |    1 +
>  include/keys/asymmetric-type.h               |    2 +
>  include/keys/x509-parser.h                   |   55 +
>  include/linux/oid_registry.h                 |    3 +
>  include/linux/pci-doe.h                      |    4 +
>  include/linux/pci.h                          |   16 +
>  include/linux/spdm.h                         |   39 +
>  lib/Kconfig                                  |   16 +
>  lib/Makefile                                 |    2 +
>  lib/rspdm/Makefile                           |   11 +
>  lib/rspdm/consts.rs                          |  135 +++
>  lib/rspdm/lib.rs                             |  180 +++
>  lib/rspdm/req-sysfs.c                        |   97 ++
>  lib/rspdm/state.rs                           | 1037 ++++++++++++++++++
>  lib/rspdm/sysfs.rs                           |   28 +
>  lib/rspdm/validator.rs                       |  489 +++++++++
>  rust/bindgen_static_functions                |    5 +
>  rust/bindings/bindings_helper.h              |    7 +
>  rust/kernel/error.rs                         |    3 +
>  rust/kernel/str.rs                           |    5 +
>  40 files changed, 2587 insertions(+), 62 deletions(-)
>  create mode 100644 Documentation/ABI/testing/sysfs-devices-spdm
>  create mode 100644 drivers/pci/cma.asn1
>  create mode 100644 drivers/pci/cma.c
>  create mode 100644 include/keys/x509-parser.h
>  create mode 100644 include/linux/spdm.h
>  create mode 100644 lib/rspdm/Makefile
>  create mode 100644 lib/rspdm/consts.rs
>  create mode 100644 lib/rspdm/lib.rs
>  create mode 100644 lib/rspdm/req-sysfs.c
>  create mode 100644 lib/rspdm/state.rs
>  create mode 100644 lib/rspdm/sysfs.rs
>  create mode 100644 lib/rspdm/validator.rs
> 


^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC v2 00/20] lib: Rust implementation of SPDM
  2025-06-11 13:37 ` [RFC v2 00/20] lib: Rust implementation of SPDM Jonathan Cameron
@ 2025-06-12  5:57   ` Alistair Francis
  0 siblings, 0 replies; 47+ messages in thread
From: Alistair Francis @ 2025-06-12  5:57 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Alistair Francis, linux-cxl, linux-kernel, lukas, linux-pci,
	bhelgaas, rust-for-linux, akpm, boqun.feng, bjorn3_gh,
	wilfred.mallawa, aliceryhl, ojeda, a.hindborg, tmgross, gary,
	alex.gaynor, benno.lossin

On Wed, Jun 11, 2025 at 11:56 PM Jonathan Cameron
<Jonathan.Cameron@huawei.com> wrote:
>
> On Thu, 27 Feb 2025 13:09:32 +1000
> Alistair Francis <alistair@alistair23.me> wrote:
>
> > Security Protocols and Data Models (SPDM) [1] is used for authentication,
> > attestation and key exchange. SPDM is generally used over a range of
> > transports, such as PCIe, MCTP/SMBus/I3C, ATA, SCSI, NVMe or TCP.
> >
> > From the kernels perspective SPDM is used to authenticate and attest devices.
> > In this threat model a device is considered untrusted until it can be verified
> > by the kernel and userspace using SPDM. As such SPDM data is untrusted data
> > that can be mallicious.
> >
> > The SPDM specification is also complex, with the 1.2.1 spec being almost 200
> > pages and the 1.3.0 spec being almost 250 pages long.
> >
> > As such we have the kernel parsing untrusted responses from a complex
> > specification, which sounds like a possible exploit vector. This is the type
> > of place where Rust excels!
> >
> > This series implements a SPDM requester in Rust.
> >
> > This is very similar to Lukas' implementation [2]. This series includes patches
> > and files from Lukas' C SPDM implementation, which isn't in mainline.
> >
> > This is a standalone series and doesn't depend on Lukas' implementation, although
> > we do still rely on Lukas' crypto preperation patches, not all of which are
> > upstream yet.
> >
> > To help with maintaining compatibility it's designed in a way to match Lukas'
> > design and the state struct stores the same information, although in a Rust
> > struct instead of the original C one.
> >
> > This series doesn't expose the data to userspace (except for a single sysfs
> > bool) to avoid the debate about how to do that. I'm planning to do that in
> > the future though.
> >
> > This series is based on the latest rust-next tree.
> >
> > This seris depends on the Untrusted abstraction work [4].
> >
> > This seris also depends on the recent bindgen support for static inlines  [5].
> >
> > The entire tree can be seen here: https://github.com/alistair23/linux/tree/alistair/spdm-rust
> >
> > based-on: https://lore.kernel.org/rust-for-linux/20240925205244.873020-1-benno.lossin@proton.me/
> > based-on: https://lore.kernel.org/rust-for-linux/20250107035058.818539-1-alistair@alistair23.me/
>
> Hi Alastair,
>
> I've completely failed to find time to actually pick up enough rust to review
> this :(  Also failed to find anyone else who has the rust skills and enough of
> the background.
>
> Ideally I'll get up to speed at some point, but in the meantime wanted to revisit
> the question of whether we want to go this way from day 1 rather than trying to
> deal with C version and later this?

My feeling is that if the C version is merged first, there is going to
be very little appetite to later replace it with a Rust version. So if
we do want a Rust version, then it should be merged soon-ish.

But, no one seems super excited by a Rust implementation. I didn't get
a resounding yes from anyone, including Rust devs, but I did get a few
no's.

So in the current environment I don't see this progressing.

>
> What's your current thoughts?  I know Lukas mentioned he was going to spin a
> new version shortly (in one of the TSM threads) so are we waiting on that?

I saw that Lukas is working on a netlink version. That's great to see
and hopefully will address the comments from LPC last year. That
should also pave the way to getting SPDM support upstream.

I currently don't have time to convert the Rust version to use netlink
and I'm not convinced it's worth the effort as there appears to be
little enthusiasm for this at the moment.

Maybe in the future this is something that can be picked up

>
> For now I'm going to take this off my review queue. Sorry!

No worries! Thanks for letting me know

Alistair

>
> Jonathan
>
>
>
> >
> > 1: https://www.dmtf.org/standards/spdm
> > 2: https://lore.kernel.org/all/cover.1719771133.git.lukas@wunner.de/
> > 3: https://github.com/l1k/linux/commits/spdm-future/
> > 4: https://lore.kernel.org/rust-for-linux/20240925205244.873020-1-benno.lossin@proton.me/
> > 5: https://lore.kernel.org/rust-for-linux/20250107035058.818539-1-alistair@alistair23.me/
> >
> > v2:
> >  - Drop support for Rust and C implementations
> >  - Include patches from Lukas to reduce series deps
> >  - Large code cleanups based on more testing
> >  - Support support for authentication
> >
> > Alistair Francis (12):
> >   lib: rspdm: Initial commit of Rust SPDM
> >   lib: rspdm: Support SPDM get_version
> >   lib: rspdm: Support SPDM get_capabilities
> >   lib: rspdm: Support SPDM negotiate_algorithms
> >   lib: rspdm: Support SPDM get_digests
> >   lib: rspdm: Support SPDM get_certificate
> >   crypto: asymmetric_keys - Load certificate parsing early in boot
> >   KEYS: Load keyring and certificates early in boot
> >   PCI/CMA: Support built in X.509 certificates
> >   lib: rspdm: Support SPDM certificate validation
> >   rust: allow extracting the buffer from a CString
> >   lib: rspdm: Support SPDM challenge
> >
> > Jonathan Cameron (1):
> >   PCI/CMA: Authenticate devices on enumeration
> >
> > Lukas Wunner (7):
> >   X.509: Make certificate parser public
> >   X.509: Parse Subject Alternative Name in certificates
> >   X.509: Move certificate length retrieval into new helper
> >   certs: Create blacklist keyring earlier
> >   PCI/CMA: Validate Subject Alternative Name in certificates
> >   PCI/CMA: Reauthenticate devices on reset and resume
> >   PCI/CMA: Expose in sysfs whether devices are authenticated
> >
> >  Documentation/ABI/testing/sysfs-devices-spdm |   31 +
> >  MAINTAINERS                                  |   14 +
> >  certs/blacklist.c                            |    4 +-
> >  certs/system_keyring.c                       |    4 +-
> >  crypto/asymmetric_keys/asymmetric_type.c     |    2 +-
> >  crypto/asymmetric_keys/x509_cert_parser.c    |    9 +
> >  crypto/asymmetric_keys/x509_loader.c         |   38 +-
> >  crypto/asymmetric_keys/x509_parser.h         |   40 +-
> >  crypto/asymmetric_keys/x509_public_key.c     |    2 +-
> >  drivers/pci/Kconfig                          |   13 +
> >  drivers/pci/Makefile                         |    4 +
> >  drivers/pci/cma.asn1                         |   41 +
> >  drivers/pci/cma.c                            |  272 +++++
> >  drivers/pci/doe.c                            |    5 +-
> >  drivers/pci/pci-driver.c                     |    1 +
> >  drivers/pci/pci-sysfs.c                      |    3 +
> >  drivers/pci/pci.c                            |   12 +-
> >  drivers/pci/pci.h                            |   15 +
> >  drivers/pci/pcie/err.c                       |    3 +
> >  drivers/pci/probe.c                          |    1 +
> >  drivers/pci/remove.c                         |    1 +
> >  include/keys/asymmetric-type.h               |    2 +
> >  include/keys/x509-parser.h                   |   55 +
> >  include/linux/oid_registry.h                 |    3 +
> >  include/linux/pci-doe.h                      |    4 +
> >  include/linux/pci.h                          |   16 +
> >  include/linux/spdm.h                         |   39 +
> >  lib/Kconfig                                  |   16 +
> >  lib/Makefile                                 |    2 +
> >  lib/rspdm/Makefile                           |   11 +
> >  lib/rspdm/consts.rs                          |  135 +++
> >  lib/rspdm/lib.rs                             |  180 +++
> >  lib/rspdm/req-sysfs.c                        |   97 ++
> >  lib/rspdm/state.rs                           | 1037 ++++++++++++++++++
> >  lib/rspdm/sysfs.rs                           |   28 +
> >  lib/rspdm/validator.rs                       |  489 +++++++++
> >  rust/bindgen_static_functions                |    5 +
> >  rust/bindings/bindings_helper.h              |    7 +
> >  rust/kernel/error.rs                         |    3 +
> >  rust/kernel/str.rs                           |    5 +
> >  40 files changed, 2587 insertions(+), 62 deletions(-)
> >  create mode 100644 Documentation/ABI/testing/sysfs-devices-spdm
> >  create mode 100644 drivers/pci/cma.asn1
> >  create mode 100644 drivers/pci/cma.c
> >  create mode 100644 include/keys/x509-parser.h
> >  create mode 100644 include/linux/spdm.h
> >  create mode 100644 lib/rspdm/Makefile
> >  create mode 100644 lib/rspdm/consts.rs
> >  create mode 100644 lib/rspdm/lib.rs
> >  create mode 100644 lib/rspdm/req-sysfs.c
> >  create mode 100644 lib/rspdm/state.rs
> >  create mode 100644 lib/rspdm/sysfs.rs
> >  create mode 100644 lib/rspdm/validator.rs
> >
>

^ permalink raw reply	[flat|nested] 47+ messages in thread

end of thread, other threads:[~2025-06-12  5:58 UTC | newest]

Thread overview: 47+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-02-27  3:09 [RFC v2 00/20] lib: Rust implementation of SPDM Alistair Francis
2025-02-27  3:09 ` [RFC v2 01/20] X.509: Make certificate parser public Alistair Francis
2025-02-27  3:09 ` [RFC v2 02/20] X.509: Parse Subject Alternative Name in certificates Alistair Francis
2025-02-27  3:09 ` [RFC v2 03/20] X.509: Move certificate length retrieval into new helper Alistair Francis
2025-02-27  3:09 ` [RFC v2 04/20] certs: Create blacklist keyring earlier Alistair Francis
2025-02-27  3:09 ` [RFC v2 05/20] lib: rspdm: Initial commit of Rust SPDM Alistair Francis
2025-02-27  3:09 ` [RFC v2 06/20] PCI/CMA: Authenticate devices on enumeration Alistair Francis
2025-02-27  3:09 ` [RFC v2 07/20] PCI/CMA: Validate Subject Alternative Name in certificates Alistair Francis
2025-02-27  3:09 ` [RFC v2 08/20] PCI/CMA: Reauthenticate devices on reset and resume Alistair Francis
2025-02-27  3:09 ` [RFC v2 09/20] PCI/CMA: Expose in sysfs whether devices are authenticated Alistair Francis
2025-02-27 11:16   ` Greg KH
2025-02-27 11:52     ` Alice Ryhl
2025-02-27 12:00       ` Greg KH
2025-02-27 12:11         ` Alice Ryhl
2025-02-27 14:03           ` Greg KH
2025-02-27 16:47             ` Miguel Ojeda
2025-02-27 19:31               ` Greg KH
2025-02-28  8:49                 ` Miguel Ojeda
2025-02-27 16:46           ` Miguel Ojeda
2025-02-27 16:45         ` Miguel Ojeda
2025-02-27 19:32           ` Greg KH
2025-02-28  2:27             ` Alistair Francis
2025-03-01  4:27               ` Greg KH
2025-03-05 19:54               ` Dan Williams
2025-03-07  1:04                 ` Alistair Francis
2025-03-07 23:37                   ` Dan Williams
2025-03-09 22:57                     ` Alistair Francis
2025-02-27 22:42     ` Lukas Wunner
2025-02-28  1:39       ` Greg KH
2025-02-28  2:55         ` Alistair Francis
2025-03-01  4:33           ` Greg KH
2025-03-01 18:01         ` Lukas Wunner
2025-02-27  3:09 ` [RFC v2 10/20] lib: rspdm: Support SPDM get_version Alistair Francis
2025-02-27  3:09 ` [RFC v2 11/20] lib: rspdm: Support SPDM get_capabilities Alistair Francis
2025-02-27  3:09 ` [RFC v2 12/20] lib: rspdm: Support SPDM negotiate_algorithms Alistair Francis
2025-02-27  3:09 ` [RFC v2 13/20] lib: rspdm: Support SPDM get_digests Alistair Francis
2025-02-27  3:09 ` [RFC v2 14/20] lib: rspdm: Support SPDM get_certificate Alistair Francis
2025-02-27 10:58   ` Greg KH
2025-02-27  3:09 ` [RFC v2 15/20] crypto: asymmetric_keys - Load certificate parsing early in boot Alistair Francis
2025-02-27  3:09 ` [RFC v2 16/20] KEYS: Load keyring and certificates " Alistair Francis
2025-02-27  3:09 ` [RFC v2 17/20] PCI/CMA: Support built in X.509 certificates Alistair Francis
2025-02-27  3:09 ` [RFC v2 18/20] lib: rspdm: Support SPDM certificate validation Alistair Francis
2025-02-27  3:09 ` [RFC v2 19/20] rust: allow extracting the buffer from a CString Alistair Francis
2025-02-27  7:52   ` Alice Ryhl
2025-02-27  3:09 ` [RFC v2 20/20] lib: rspdm: Support SPDM challenge Alistair Francis
2025-06-11 13:37 ` [RFC v2 00/20] lib: Rust implementation of SPDM Jonathan Cameron
2025-06-12  5:57   ` Alistair Francis

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).