[PATCH v2 17/18] spdm: Authenticate devices despite invalid certificate chain

Lukas Wunner
2024-06-30T20:31:38Z

The SPDM library has just been amended to keep a log of received
signatures from a device and expose it in sysfs.

Currently challenge-response authentication with a device is only
performed if one of its up to 8 certificate chains is considered valid
by the kernel.

Valid means several things:

* That the certificate chain adheres to requirements in the SPDM
specification (e.g. each certificate in the chain is signed by the
preceding certificate),
* that the certificate chain adheres to requirements in other
specifications such as PCIe r6.1 sec 6.31.3,
* that the first certificate in the chain is signed by a trusted root
certificate on the kernel's keyring
* or that none of the certificates in the chain is on the kernel's
blacklist_keyring.

User space should be given the chance to make up its own mind on the
validity of a certificate chain and the signature generated with it.
So if none of the 8 certificate chains is considered valid by the
kernel, pick one of them and perform challenge-response authentication
with it for the sole purpose of exposing a signature to user space.

Do not verify that signature because if the kernel considers the
certificate chain invalid, the signature implicitly is as well.

Arbitrarily select the certificate chain in the first provisioned slot
(which is normally slot 0) for such "for user space only" authentication
attempts.

Signed-off-by: Lukas Wunner <lukas@wunner.de>
---
I'd like to know whether people actually find this feature useful.
The patch is somewhat tentative and I may drop it if there is no interest,
so comments welcome!

Documentation/ABI/testing/sysfs-devices-spdm | 5 +++
lib/spdm/req-authenticate.c | 38 +++++++++++++-------
2 files changed, 31 insertions(+), 12 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-devices-spdm b/Documentation/ABI/testing/sysfs-devices-spdm
index 8d8ee01672e1..5ce34ce10b9c 100644
--- a/Documentation/ABI/testing/sysfs-devices-spdm
+++ b/Documentation/ABI/testing/sysfs-devices-spdm
@@ -162,6 +162,11 @@ Description:
dissector needs to be fed the concatenation of "transcript"
and "signature".

+ Signatures are added to the log even if the kernel was unable
+ to verify them (e.g. due to a missing trusted root certificate
+ or forged signature). Thereby, remote attestation services
+ may make up their own mind on the signature's validity.
+
Because the number prefixed to the filenames is 32 bit, it
wraps around to 0 after 4,294,967,295 signatures. The kernel
avoids filename collisions on wraparound by purging old files,
diff --git a/lib/spdm/req-authenticate.c b/lib/spdm/req-authenticate.c
index 0c74dc0e5cf4..7c977f5835c1 100644
--- a/lib/spdm/req-authenticate.c
+++ b/lib/spdm/req-authenticate.c
@@ -615,7 +615,7 @@ static size_t spdm_challenge_rsp_sz(struct spdm_state *spdm_state,
return size + spdm_state->sig_len; /* Signature */
}

-static int spdm_challenge(struct spdm_state *spdm_state, u8 slot)
+static int spdm_challenge(struct spdm_state *spdm_state, u8 slot, bool verify)
{
size_t req_sz, rsp_sz, rsp_sz_max, req_nonce_off, rsp_nonce_off;
struct spdm_challenge_rsp *rsp __free(kfree);
@@ -661,14 +661,19 @@ static int spdm_challenge(struct spdm_state *spdm_state, u8 slot)
if (rc)
return rc;

- /* Verify signature at end of transcript against leaf key */
- rc = spdm_verify_signature(spdm_state, spdm_context);
- if (rc)
- dev_err(spdm_state->dev,
- "Cannot verify challenge_auth signature: %d\n", rc);
- else
- dev_info(spdm_state->dev,
- "Authenticated with certificate slot %u\n", slot);
+ rc = -EKEYREJECTED;
+ if (verify) {
+ /* Verify signature at end of transcript against leaf key */
+ rc = spdm_verify_signature(spdm_state, spdm_context);
+ if (rc)
+ dev_err(spdm_state->dev,
+ "Cannot verify challenge_auth signature: %d\n",
+ rc);
+ else
+ dev_info(spdm_state->dev,
+ "Authenticated with certificate slot %u\n",
+ slot);
+ }

spdm_create_log_entry(spdm_state, spdm_context, slot,
req_nonce_off, rsp_nonce_off);
@@ -692,6 +697,7 @@ static int spdm_challenge(struct spdm_state *spdm_state, u8 slot)
*/
int spdm_authenticate(struct spdm_state *spdm_state)
{
+ bool verify = false;
u8 slot;
int rc;

@@ -726,13 +732,21 @@ int spdm_authenticate(struct spdm_state *spdm_state)

for_each_set_bit(slot, &spdm_state->provisioned_slots, SPDM_SLOTS) {
rc = spdm_validate_cert_chain(spdm_state, slot);
- if (rc == 0)
+ if (rc == 0) {
+ verify = true;
break;
+ }
}
+
+ /*
+ * If no cert chain validates, perform challenge-response with
+ * arbitrary slot to be able to expose a signature in sysfs
+ * about which user space can make up its own mind.
+ */
if (rc)
- goto unlock;
+ slot = __ffs(spdm_state->provisioned_slots);

- rc = spdm_challenge(spdm_state, slot);
+ rc = spdm_challenge(spdm_state, slot, verify);

unlock:
if (rc)
--
2.43.0