From 94dababb736890ed630d70c94e3b7d58b7dafaef Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Tue, 20 Aug 2024 07:18:41 +0200 Subject: [PATCH 01/47] crypto: ecdsa - Update Kconfig entry for NIST P521 Commit a7d45ba77d3d ("crypto: ecdsa - Register NIST P521 and extend test suite") added support for ECDSA signature verification using NIST P521, but forgot to amend the Kconfig entry. Fix it. Fixes: a7d45ba77d3d ("crypto: ecdsa - Register NIST P521 and extend test suite") Signed-off-by: Lukas Wunner Cc: Stefan Berger --- crypto/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/Kconfig b/crypto/Kconfig index a779cab668c24c..777b53ed2d31cc 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -295,7 +295,7 @@ config CRYPTO_ECDSA help ECDSA (Elliptic Curve Digital Signature Algorithm) (FIPS 186, ISO/IEC 14888-3) - using curves P-192, P-256, and P-384 + using curves P-192, P-256, P-384 and P-521 Only signature verification is implemented. From 76f020d5eb73921cf815404e9aa71fd5294b1de4 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Tue, 20 Aug 2024 08:01:55 +0200 Subject: [PATCH 02/47] crypto: ecdsa - Drop unused test vector elements The ECDSA test vectors contain "params", "param_len" and "algo" elements even though ecdsa.c doesn't make any use of them. The only algorithm implementation using those elements is ecrdsa.c. Drop the unused test vector elements. For the curious, "params" is an ASN.1 SEQUENCE of OID_id_ecPublicKey and a second OID identifying the curve. For example: "\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48" "\xce\x3d\x03\x01\x01" ... decodes to: SEQUENCE (OID_id_ecPublicKey, OID_id_prime192v1) The curve OIDs used in those "params" elements are unsurprisingly: OID_id_prime192v1 (2a8648ce3d030101) OID_id_prime256v1 (2a8648ce3d030107) OID_id_ansip384r1 (2b81040022) OID_id_ansip521r1 (2b81040023) Those are just different names for secp192r1, secp256r1, secp384r1 and secp521r1, respectively, per RFC 8422 appendix A: https://www.rfc-editor.org/rfc/rfc8422#appendix-A The entries for secp384r1 and secp521r1 curves contain a useful code comment calling out the curve and hash. Add analogous code comments to secp192r1 and secp256r1 curve entries. Signed-off-by: Lukas Wunner Reviewed-by: Stefan Berger --- crypto/testmgr.h | 115 +++++------------------------------------------ 1 file changed, 10 insertions(+), 105 deletions(-) diff --git a/crypto/testmgr.h b/crypto/testmgr.h index 9b38501a17b2a0..ed1640f3e35246 100644 --- a/crypto/testmgr.h +++ b/crypto/testmgr.h @@ -652,21 +652,16 @@ static const struct akcipher_testvec rsa_tv_template[] = { */ static const struct akcipher_testvec ecdsa_nist_p192_tv_template[] = { { - .key = + .key = /* secp192r1(sha1) */ "\x04\xf7\x46\xf8\x2f\x15\xf6\x22\x8e\xd7\x57\x4f\xcc\xe7\xbb\xc1" "\xd4\x09\x73\xcf\xea\xd0\x15\x07\x3d\xa5\x8a\x8a\x95\x43\xe4\x68" "\xea\xc6\x25\xc1\xc1\x01\x25\x4c\x7e\xc3\x3c\xa6\x04\x0a\xe7\x08" "\x98", .key_len = 49, - .params = - "\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48" - "\xce\x3d\x03\x01\x01", - .param_len = 21, .m = "\xcd\xb9\xd2\x1c\xb7\x6f\xcd\x44\xb3\xfd\x63\xea\xa3\x66\x7f\xae" "\x63\x85\xe7\x82", .m_size = 20, - .algo = OID_id_ecdsa_with_sha1, .c = "\x30\x35\x02\x19\x00\xba\xe5\x93\x83\x6e\xb6\x3b\x63\xa0\x27\x91" "\xc6\xf6\x7f\xc3\x09\xad\x59\xad\x88\x27\xd6\x92\x6b\x02\x18\x10" @@ -676,21 +671,16 @@ static const struct akcipher_testvec ecdsa_nist_p192_tv_template[] = { .public_key_vec = true, .siggen_sigver_test = true, }, { - .key = + .key = /* secp192r1(sha224) */ "\x04\xb6\x4b\xb1\xd1\xac\xba\x24\x8f\x65\xb2\x60\x00\x90\xbf\xbd" "\x78\x05\x73\xe9\x79\x1d\x6f\x7c\x0b\xd2\xc3\x93\xa7\x28\xe1\x75" "\xf7\xd5\x95\x1d\x28\x10\xc0\x75\x50\x5c\x1a\x4f\x3f\x8f\xa5\xee" "\xa3", .key_len = 49, - .params = - "\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48" - "\xce\x3d\x03\x01\x01", - .param_len = 21, .m = "\x8d\xd6\xb8\x3e\xe5\xff\x23\xf6\x25\xa2\x43\x42\x74\x45\xa7\x40" "\x3a\xff\x2f\xe1\xd3\xf6\x9f\xe8\x33\xcb\x12\x11", .m_size = 28, - .algo = OID_id_ecdsa_with_sha224, .c = "\x30\x34\x02\x18\x5a\x8b\x82\x69\x7e\x8a\x0a\x09\x14\xf8\x11\x2b" "\x55\xdc\xae\x37\x83\x7b\x12\xe6\xb6\x5b\xcb\xd4\x02\x18\x6a\x14" @@ -700,21 +690,16 @@ static const struct akcipher_testvec ecdsa_nist_p192_tv_template[] = { .public_key_vec = true, .siggen_sigver_test = true, }, { - .key = + .key = /* secp192r1(sha256) */ "\x04\xe2\x51\x24\x9b\xf7\xb6\x32\x82\x39\x66\x3d\x5b\xec\x3b\xae" "\x0c\xd5\xf2\x67\xd1\xc7\xe1\x02\xe4\xbf\x90\x62\xb8\x55\x75\x56" "\x69\x20\x5e\xcb\x4e\xca\x33\xd6\xcb\x62\x6b\x94\xa9\xa2\xe9\x58" "\x91", .key_len = 49, - .params = - "\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48" - "\xce\x3d\x03\x01\x01", - .param_len = 21, .m = "\x35\xec\xa1\xa0\x9e\x14\xde\x33\x03\xb6\xf6\xbd\x0c\x2f\xb2\xfd" "\x1f\x27\x82\xa5\xd7\x70\x3f\xef\xa0\x82\x69\x8e\x73\x31\x8e\xd7", .m_size = 32, - .algo = OID_id_ecdsa_with_sha256, .c = "\x30\x35\x02\x18\x3f\x72\x3f\x1f\x42\xd2\x3f\x1d\x6b\x1a\x58\x56" "\xf1\x8f\xf7\xfd\x01\x48\xfb\x5f\x72\x2a\xd4\x8f\x02\x19\x00\xb3" @@ -724,22 +709,17 @@ static const struct akcipher_testvec ecdsa_nist_p192_tv_template[] = { .public_key_vec = true, .siggen_sigver_test = true, }, { - .key = + .key = /* secp192r1(sha384) */ "\x04\x5a\x13\xfe\x68\x86\x4d\xf4\x17\xc7\xa4\xe5\x8c\x65\x57\xb7" "\x03\x73\x26\x57\xfb\xe5\x58\x40\xd8\xfd\x49\x05\xab\xf1\x66\x1f" "\xe2\x9d\x93\x9e\xc2\x22\x5a\x8b\x4f\xf3\x77\x22\x59\x7e\xa6\x4e" "\x8b", .key_len = 49, - .params = - "\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48" - "\xce\x3d\x03\x01\x01", - .param_len = 21, .m = "\x9d\x2e\x1a\x8f\xed\x6c\x4b\x61\xae\xac\xd5\x19\x79\xce\x67\xf9" "\xa0\x34\xeb\xb0\x81\xf9\xd9\xdc\x6e\xb3\x5c\xa8\x69\xfc\x8a\x61" "\x39\x81\xfb\xfd\x5c\x30\x6b\xa8\xee\xed\x89\xaf\xa3\x05\xe4\x78", .m_size = 48, - .algo = OID_id_ecdsa_with_sha384, .c = "\x30\x35\x02\x19\x00\xf0\xa3\x38\xce\x2b\xf8\x9d\x1a\xcf\x7f\x34" "\xb4\xb4\xe5\xc5\x00\xdd\x15\xbb\xd6\x8c\xa7\x03\x78\x02\x18\x64" @@ -749,23 +729,18 @@ static const struct akcipher_testvec ecdsa_nist_p192_tv_template[] = { .public_key_vec = true, .siggen_sigver_test = true, }, { - .key = + .key = /* secp192r1(sha512) */ "\x04\xd5\xf2\x6e\xc3\x94\x5c\x52\xbc\xdf\x86\x6c\x14\xd1\xca\xea" "\xcc\x72\x3a\x8a\xf6\x7a\x3a\x56\x36\x3b\xca\xc6\x94\x0e\x17\x1d" "\x9e\xa0\x58\x28\xf9\x4b\xe6\xd1\xa5\x44\x91\x35\x0d\xe7\xf5\x11" "\x57", .key_len = 49, - .params = - "\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48" - "\xce\x3d\x03\x01\x01", - .param_len = 21, .m = "\xd5\x4b\xe9\x36\xda\xd8\x6e\xc0\x50\x03\xbe\x00\x43\xff\xf0\x23" "\xac\xa2\x42\xe7\x37\x77\x79\x52\x8f\x3e\xc0\x16\xc1\xfc\x8c\x67" "\x16\xbc\x8a\x5d\x3b\xd3\x13\xbb\xb6\xc0\x26\x1b\xeb\x33\xcc\x70" "\x4a\xf2\x11\x37\xe8\x1b\xba\x55\xac\x69\xe1\x74\x62\x7c\x6e\xb5", .m_size = 64, - .algo = OID_id_ecdsa_with_sha512, .c = "\x30\x35\x02\x19\x00\x88\x5b\x8f\x59\x43\xbf\xcf\xc6\xdd\x3f\x07" "\x87\x12\xa0\xd4\xac\x2b\x11\x2d\x1c\xb6\x06\xc9\x6c\x02\x18\x73" @@ -779,22 +754,17 @@ static const struct akcipher_testvec ecdsa_nist_p192_tv_template[] = { static const struct akcipher_testvec ecdsa_nist_p256_tv_template[] = { { - .key = + .key = /* secp256r1(sha1) */ "\x04\xb9\x7b\xbb\xd7\x17\x64\xd2\x7e\xfc\x81\x5d\x87\x06\x83\x41" "\x22\xd6\x9a\xaa\x87\x17\xec\x4f\x63\x55\x2f\x94\xba\xdd\x83\xe9" "\x34\x4b\xf3\xe9\x91\x13\x50\xb6\xcb\xca\x62\x08\xe7\x3b\x09\xdc" "\xc3\x63\x4b\x2d\xb9\x73\x53\xe4\x45\xe6\x7c\xad\xe7\x6b\xb0\xe8" "\xaf", .key_len = 65, - .params = - "\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48" - "\xce\x3d\x03\x01\x07", - .param_len = 21, .m = "\xc2\x2b\x5f\x91\x78\x34\x26\x09\x42\x8d\x6f\x51\xb2\xc5\xaf\x4c" "\x0b\xde\x6a\x42", .m_size = 20, - .algo = OID_id_ecdsa_with_sha1, .c = "\x30\x46\x02\x21\x00\xf9\x25\xce\x9f\x3a\xa6\x35\x81\xcf\xd4\xe7" "\xb7\xf0\x82\x56\x41\xf7\xd4\xad\x8d\x94\x5a\x69\x89\xee\xca\x6a" @@ -805,22 +775,17 @@ static const struct akcipher_testvec ecdsa_nist_p256_tv_template[] = { .public_key_vec = true, .siggen_sigver_test = true, }, { - .key = + .key = /* secp256r1(sha224) */ "\x04\x8b\x6d\xc0\x33\x8e\x2d\x8b\x67\xf5\xeb\xc4\x7f\xa0\xf5\xd9" "\x7b\x03\xa5\x78\x9a\xb5\xea\x14\xe4\x23\xd0\xaf\xd7\x0e\x2e\xa0" "\xc9\x8b\xdb\x95\xf8\xb3\xaf\xac\x00\x2c\x2c\x1f\x7a\xfd\x95\x88" "\x43\x13\xbf\xf3\x1c\x05\x1a\x14\x18\x09\x3f\xd6\x28\x3e\xc5\xa0" "\xd4", .key_len = 65, - .params = - "\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48" - "\xce\x3d\x03\x01\x07", - .param_len = 21, .m = "\x1a\x15\xbc\xa3\xe4\xed\x3a\xb8\x23\x67\xc6\xc4\x34\xf8\x6c\x41" "\x04\x0b\xda\xc5\x77\xfa\x1c\x2d\xe6\x2c\x3b\xe0", .m_size = 28, - .algo = OID_id_ecdsa_with_sha224, .c = "\x30\x44\x02\x20\x20\x43\xfa\xc0\x9f\x9d\x7b\xe7\xae\xce\x77\x59" "\x1a\xdb\x59\xd5\x34\x62\x79\xcb\x6a\x91\x67\x2e\x7d\x25\xd8\x25" @@ -831,22 +796,17 @@ static const struct akcipher_testvec ecdsa_nist_p256_tv_template[] = { .public_key_vec = true, .siggen_sigver_test = true, }, { - .key = + .key = /* secp256r1(sha256) */ "\x04\xf1\xea\xc4\x53\xf3\xb9\x0e\x9f\x7e\xad\xe3\xea\xd7\x0e\x0f" "\xd6\x98\x9a\xca\x92\x4d\x0a\x80\xdb\x2d\x45\xc7\xec\x4b\x97\x00" "\x2f\xe9\x42\x6c\x29\xdc\x55\x0e\x0b\x53\x12\x9b\x2b\xad\x2c\xe9" "\x80\xe6\xc5\x43\xc2\x1d\x5e\xbb\x65\x21\x50\xb6\x37\xb0\x03\x8e" "\xb8", .key_len = 65, - .params = - "\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48" - "\xce\x3d\x03\x01\x07", - .param_len = 21, .m = "\x8f\x43\x43\x46\x64\x8f\x6b\x96\xdf\x89\xdd\xa9\x01\xc5\x17\x6b" "\x10\xa6\xd8\x39\x61\xdd\x3c\x1a\xc8\x8b\x59\xb2\xdc\x32\x7a\xa4", .m_size = 32, - .algo = OID_id_ecdsa_with_sha256, .c = "\x30\x45\x02\x20\x08\x31\xfa\x74\x0d\x1d\x21\x5d\x09\xdc\x29\x63" "\xa8\x1a\xad\xfc\xac\x44\xc3\xe8\x24\x11\x2d\xa4\x91\xdc\x02\x67" @@ -857,23 +817,18 @@ static const struct akcipher_testvec ecdsa_nist_p256_tv_template[] = { .public_key_vec = true, .siggen_sigver_test = true, }, { - .key = + .key = /* secp256r1(sha384) */ "\x04\xc5\xc6\xea\x60\xc9\xce\xad\x02\x8d\xf5\x3e\x24\xe3\x52\x1d" "\x28\x47\x3b\xc3\x6b\xa4\x99\x35\x99\x11\x88\x88\xc8\xf4\xee\x7e" "\x8c\x33\x8f\x41\x03\x24\x46\x2b\x1a\x82\xf9\x9f\xe1\x97\x1b\x00" "\xda\x3b\x24\x41\xf7\x66\x33\x58\x3d\x3a\x81\xad\xcf\x16\xe9\xe2" "\x7c", .key_len = 65, - .params = - "\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48" - "\xce\x3d\x03\x01\x07", - .param_len = 21, .m = "\x3e\x78\x70\xfb\xcd\x66\xba\x91\xa1\x79\xff\x1e\x1c\x6b\x78\xe6" "\xc0\x81\x3a\x65\x97\x14\x84\x36\x14\x1a\x9a\xb7\xc5\xab\x84\x94" "\x5e\xbb\x1b\x34\x71\xcb\x41\xe1\xf6\xfc\x92\x7b\x34\xbb\x86\xbb", .m_size = 48, - .algo = OID_id_ecdsa_with_sha384, .c = "\x30\x46\x02\x21\x00\x8e\xf3\x6f\xdc\xf8\x69\xa6\x2e\xd0\x2e\x95" "\x54\xd1\x95\x64\x93\x08\xb2\x6b\x24\x94\x48\x46\x5e\xf2\xe4\x6c" @@ -884,24 +839,19 @@ static const struct akcipher_testvec ecdsa_nist_p256_tv_template[] = { .public_key_vec = true, .siggen_sigver_test = true, }, { - .key = + .key = /* secp256r1(sha512) */ "\x04\xd7\x27\x46\x49\xf6\x26\x85\x12\x40\x76\x8e\xe2\xe6\x2a\x7a" "\x83\xb1\x4e\x7a\xeb\x3b\x5c\x67\x4a\xb5\xa4\x92\x8c\x69\xff\x38" "\xee\xd9\x4e\x13\x29\x59\xad\xde\x6b\xbb\x45\x31\xee\xfd\xd1\x1b" "\x64\xd3\xb5\xfc\xaf\x9b\x4b\x88\x3b\x0e\xb7\xd6\xdf\xf1\xd5\x92" "\xbf", .key_len = 65, - .params = - "\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48" - "\xce\x3d\x03\x01\x07", - .param_len = 21, .m = "\x57\xb7\x9e\xe9\x05\x0a\x8c\x1b\xc9\x13\xe5\x4a\x24\xc7\xe2\xe9" "\x43\xc3\xd1\x76\x62\xf4\x98\x1a\x9c\x13\xb0\x20\x1b\xe5\x39\xca" "\x4f\xd9\x85\x34\x95\xa2\x31\xbc\xbb\xde\xdd\x76\xbb\x61\xe3\xcf" "\x9d\xc0\x49\x7a\xf3\x7a\xc4\x7d\xa8\x04\x4b\x8d\xb4\x4d\x5b\xd6", .m_size = 64, - .algo = OID_id_ecdsa_with_sha512, .c = "\x30\x45\x02\x21\x00\xb8\x6d\x87\x81\x43\xdf\xfb\x9f\x40\xea\x44" "\x81\x00\x4e\x29\x08\xed\x8c\x73\x30\x6c\x22\xb3\x97\x76\xf6\x04" @@ -925,15 +875,10 @@ static const struct akcipher_testvec ecdsa_nist_p384_tv_template[] = { "\x0b\x25\xd6\x80\x5c\x3b\xe6\x1a\x98\x48\x91\x45\x7a\x73\xb0\xc3" "\xf1", .key_len = 97, - .params = - "\x30\x10\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x05\x2b\x81\x04" - "\x00\x22", - .param_len = 18, .m = "\x12\x55\x28\xf0\x77\xd5\xb6\x21\x71\x32\x48\xcd\x28\xa8\x25\x22" "\x3a\x69\xc1\x93", .m_size = 20, - .algo = OID_id_ecdsa_with_sha1, .c = "\x30\x66\x02\x31\x00\xf5\x0f\x24\x4c\x07\x93\x6f\x21\x57\x55\x07" "\x20\x43\x30\xde\xa0\x8d\x26\x8e\xae\x63\x3f\xbc\x20\x3a\xc6\xf1" @@ -955,15 +900,10 @@ static const struct akcipher_testvec ecdsa_nist_p384_tv_template[] = { "\x6b\x93\x99\x6c\x66\x4c\x42\x3f\x65\x60\x6c\x1c\x0b\x93\x9b\x9d" "\xe0", .key_len = 97, - .params = - "\x30\x10\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x05\x2b\x81\x04" - "\x00\x22", - .param_len = 18, .m = "\x12\x80\xb6\xeb\x25\xe2\x3d\xf0\x21\x32\x96\x17\x3a\x38\x39\xfd" "\x1f\x05\x34\x7b\xb8\xf9\x71\x66\x03\x4f\xd5\xe5", .m_size = 28, - .algo = OID_id_ecdsa_with_sha224, .c = "\x30\x66\x02\x31\x00\x8a\x51\x84\xce\x13\x1e\xd2\xdc\xec\xcb\xe4" "\x89\x47\xb2\xf7\xbc\x97\xf1\xc8\x72\x26\xcf\x5a\x5e\xc5\xda\xb4" @@ -985,15 +925,10 @@ static const struct akcipher_testvec ecdsa_nist_p384_tv_template[] = { "\x17\xc3\x34\x29\xd6\x40\xea\x5c\xb9\x3f\xfb\x32\x2e\x12\x33\xbc" "\xab", .key_len = 97, - .params = - "\x30\x10\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x05\x2b\x81\x04" - "\x00\x22", - .param_len = 18, .m = "\xaa\xe7\xfd\x03\x26\xcb\x94\x71\xe4\xce\x0f\xc5\xff\xa6\x29\xa3" "\xe1\xcc\x4c\x35\x4e\xde\xca\x80\xab\x26\x0c\x25\xe6\x68\x11\xc2", .m_size = 32, - .algo = OID_id_ecdsa_with_sha256, .c = "\x30\x64\x02\x30\x08\x09\x12\x9d\x6e\x96\x64\xa6\x8e\x3f\x7e\xce" "\x0a\x9b\xaa\x59\xcc\x47\x53\x87\xbc\xbd\x83\x3f\xaf\x06\x3f\x84" @@ -1015,16 +950,11 @@ static const struct akcipher_testvec ecdsa_nist_p384_tv_template[] = { "\x21\x67\xe5\x1b\x5a\x52\x31\x68\xd6\xee\xf0\x19\xb0\x55\xed\x89" "\x9e", .key_len = 97, - .params = - "\x30\x10\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x05\x2b\x81\x04" - "\x00\x22", - .param_len = 18, .m = "\x8d\xf2\xc0\xe9\xa8\xf3\x8e\x44\xc4\x8c\x1a\xa0\xb8\xd7\x17\xdf" "\xf2\x37\x1b\xc6\xe3\xf5\x62\xcc\x68\xf5\xd5\x0b\xbf\x73\x2b\xb1" "\xb0\x4c\x04\x00\x31\xab\xfe\xc8\xd6\x09\xc8\xf2\xea\xd3\x28\xff", .m_size = 48, - .algo = OID_id_ecdsa_with_sha384, .c = "\x30\x66\x02\x31\x00\x9b\x28\x68\xc0\xa1\xea\x8c\x50\xee\x2e\x62" "\x35\x46\xfa\x00\xd8\x2d\x7a\x91\x5f\x49\x2d\x22\x08\x29\xe6\xfb" @@ -1046,17 +976,12 @@ static const struct akcipher_testvec ecdsa_nist_p384_tv_template[] = { "\xdf\x42\x5c\xc2\x5a\xc7\x0c\xf4\x15\xf7\x1b\xa3\x2e\xd7\x00\xac" "\xa3", .key_len = 97, - .params = - "\x30\x10\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x05\x2b\x81\x04" - "\x00\x22", - .param_len = 18, .m = "\xe8\xb7\x52\x7d\x1a\x44\x20\x05\x53\x6b\x3a\x68\xf2\xe7\x6c\xa1" "\xae\x9d\x84\xbb\xba\x52\x43\x3e\x2c\x42\x78\x49\xbf\x78\xb2\x71" "\xeb\xe1\xe0\xe8\x42\x7b\x11\xad\x2b\x99\x05\x1d\x36\xe6\xac\xfc" "\x55\x73\xf0\x15\x63\x39\xb8\x6a\x6a\xc5\x91\x5b\xca\x6a\xa8\x0e", .m_size = 64, - .algo = OID_id_ecdsa_with_sha512, .c = "\x30\x63\x02\x2f\x1d\x20\x94\x77\xfe\x31\xfa\x4d\xc6\xef\xda\x02" "\xe7\x0f\x52\x9a\x02\xde\x93\xe8\x83\xe4\x84\x4c\xfc\x6f\x80\xe3" @@ -1084,15 +1009,10 @@ static const struct akcipher_testvec ecdsa_nist_p521_tv_template[] = { "\xed\x37\x0f\x99\x3f\x26\xba\xa3\x8e\xff\x79\x34\x7c\x3a\xfe\x1f" "\x3b\x83\x82\x2f\x14", .key_len = 133, - .params = - "\x30\x10\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x05\x2b\x81\x04" - "\x00\x23", - .param_len = 18, .m = "\xa2\x3a\x6a\x8c\x7b\x3c\xf2\x51\xf8\xbe\x5f\x4f\x3b\x15\x05\xc4" "\xb5\xbc\x19\xe7\x21\x85\xe9\x23\x06\x33\x62\xfb", .m_size = 28, - .algo = OID_id_ecdsa_with_sha224, .c = "\x30\x81\x86\x02\x41\x01\xd6\x43\xe7\xff\x42\xb2\xba\x74\x35\xf6" "\xdc\x6d\x02\x7b\x22\xac\xe2\xef\x07\x92\xee\x60\x94\x06\xf8\x3f" @@ -1119,15 +1039,10 @@ static const struct akcipher_testvec ecdsa_nist_p521_tv_template[] = { "\x8a\xe9\x53\xa8\xcf\xce\x43\x0e\x82\x20\x86\xbc\x88\x9c\xb7\xe3" "\xe6\x77\x1e\x1f\x8a", .key_len = 133, - .params = - "\x30\x10\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x05\x2b\x81\x04" - "\x00\x23", - .param_len = 18, .m = "\xcc\x97\x73\x0c\x73\xa2\x53\x2b\xfa\xd7\x83\x1d\x0c\x72\x1b\x39" "\x80\x71\x8d\xdd\xc5\x9b\xff\x55\x32\x98\x25\xa2\x58\x2e\xb7\x73", .m_size = 32, - .algo = OID_id_ecdsa_with_sha256, .c = "\x30\x81\x88\x02\x42\x00\xcd\xa5\x5f\x57\x52\x27\x78\x3a\xb5\x06" "\x0f\xfd\x83\xfc\x0e\xd9\xce\x50\x9f\x7d\x1f\xca\x8b\xa8\x2d\x56" @@ -1154,16 +1069,11 @@ static const struct akcipher_testvec ecdsa_nist_p521_tv_template[] = { "\x22\x6e\xd7\x35\xc7\x23\xb7\x13\xae\xb6\x34\xff\xd7\x80\xe5\x39" "\xb3\x3b\x5b\x1b\x94", .key_len = 133, - .params = - "\x30\x10\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x05\x2b\x81\x04" - "\x00\x23", - .param_len = 18, .m = "\x36\x98\xd6\x82\xfa\xad\xed\x3c\xb9\x40\xb6\x4d\x9e\xb7\x04\x26" "\xad\x72\x34\x44\xd2\x81\xb4\x9b\xbe\x01\x04\x7a\xd8\x50\xf8\x59" "\xba\xad\x23\x85\x6b\x59\xbe\xfb\xf6\x86\xd4\x67\xa8\x43\x28\x76", .m_size = 48, - .algo = OID_id_ecdsa_with_sha384, .c = "\x30\x81\x88\x02\x42\x00\x93\x96\x76\x3c\x27\xea\xaa\x9c\x26\xec" "\x51\xdc\xe8\x35\x5e\xae\x16\xf2\x4b\x64\x98\xf7\xec\xda\xc7\x7e" @@ -1190,17 +1100,12 @@ static const struct akcipher_testvec ecdsa_nist_p521_tv_template[] = { "\xfe\x3a\x05\x1a\xdb\xa9\x0f\xc0\x6c\x76\x30\x8c\xd8\xde\x44\xae" "\xd0\x17\xdf\x49\x6a", .key_len = 133, - .params = - "\x30\x10\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x05\x2b\x81\x04" - "\x00\x23", - .param_len = 18, .m = "\x5c\xa6\xbc\x79\xb8\xa0\x1e\x11\x83\xf7\xe9\x05\xdf\xba\xf7\x69" "\x97\x22\x32\xe4\x94\x7c\x65\xbd\x74\xc6\x9a\x8b\xbd\x0d\xdc\xed" "\xf5\x9c\xeb\xe1\xc5\x68\x40\xf2\xc7\x04\xde\x9e\x0d\x76\xc5\xa3" "\xf9\x3c\x6c\x98\x08\x31\xbd\x39\xe8\x42\x7f\x80\x39\x6f\xfe\x68", .m_size = 64, - .algo = OID_id_ecdsa_with_sha512, .c = "\x30\x81\x88\x02\x42\x01\x5c\x71\x86\x96\xac\x21\x33\x7e\x4e\xaa" "\x86\xec\xa8\x05\x03\x52\x56\x63\x0e\x02\xcc\x94\xa9\x05\xb9\xfb" From 43d2ff8e37f956b07643d6cc3894f8385e52dab3 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sun, 18 Aug 2024 21:02:59 +0200 Subject: [PATCH 03/47] crypto: sig - Introduce sig_alg backend Commit 6cb8815f41a9 ("crypto: sig - Add interface for sign/verify") began a transition of asymmetric sign/verify operations from crypto_akcipher to a new crypto_sig frontend. Internally, the crypto_sig frontend still uses akcipher_alg as backend, however: "The link between sig and akcipher is meant to be temporary. The plan is to create a new low-level API for sig and then migrate the signature code over to that from akcipher." https://lore.kernel.org/r/ZrG6w9wsb-iiLZIF@gondor.apana.org.au/ "having a separate alg for sig is definitely where we want to be since there is very little that the two types actually share." https://lore.kernel.org/r/ZrHlpz4qnre0zWJO@gondor.apana.org.au/ Take the next step of that migration and augment the crypto_sig frontend with a sig_alg backend to which all algorithms can be moved. During the migration, there will briefly be signature algorithms that are still based on crypto_akcipher, whilst others are already based on crypto_sig. Allow for that by building a fork into crypto_sig_*() API calls (i.e. crypto_sig_maxsize() and friends) such that one of the two backends is selected based on the transform's cra_type. Signed-off-by: Lukas Wunner --- Documentation/crypto/api-sig.rst | 15 +++ Documentation/crypto/api.rst | 1 + Documentation/crypto/architecture.rst | 2 + crypto/sig.c | 143 +++++++++++++++++++++++++- crypto/testmgr.c | 115 +++++++++++++++++++++ crypto/testmgr.h | 13 +++ include/crypto/internal/sig.h | 80 ++++++++++++++ include/crypto/sig.h | 61 +++++++++++ include/uapi/linux/cryptouser.h | 5 + 9 files changed, 433 insertions(+), 2 deletions(-) create mode 100644 Documentation/crypto/api-sig.rst diff --git a/Documentation/crypto/api-sig.rst b/Documentation/crypto/api-sig.rst new file mode 100644 index 00000000000000..e5e87e106884f5 --- /dev/null +++ b/Documentation/crypto/api-sig.rst @@ -0,0 +1,15 @@ +Asymmetric Signature Algorithm Definitions +------------------------------------------ + +.. kernel-doc:: include/crypto/sig.h + :functions: sig_alg + +Asymmetric Signature API +------------------------ + +.. kernel-doc:: include/crypto/sig.h + :doc: Generic Public Key Signature API + +.. kernel-doc:: include/crypto/sig.h + :functions: crypto_alloc_sig crypto_free_sig crypto_sig_set_pubkey crypto_sig_set_privkey crypto_sig_maxsize crypto_sig_sign crypto_sig_verify + diff --git a/Documentation/crypto/api.rst b/Documentation/crypto/api.rst index ff31c30561d4f6..8b2a905218862d 100644 --- a/Documentation/crypto/api.rst +++ b/Documentation/crypto/api.rst @@ -10,4 +10,5 @@ Programming Interface api-digest api-rng api-akcipher + api-sig api-kpp diff --git a/Documentation/crypto/architecture.rst b/Documentation/crypto/architecture.rst index 646c3380a7edc4..15dcd62fd22f26 100644 --- a/Documentation/crypto/architecture.rst +++ b/Documentation/crypto/architecture.rst @@ -214,6 +214,8 @@ the aforementioned cipher types: - CRYPTO_ALG_TYPE_AKCIPHER Asymmetric cipher +- CRYPTO_ALG_TYPE_SIG Asymmetric signature + - CRYPTO_ALG_TYPE_PCOMPRESS Enhanced version of CRYPTO_ALG_TYPE_COMPRESS allowing for segmented compression / decompression instead of performing the operation on one segment diff --git a/crypto/sig.c b/crypto/sig.c index 7645bedf3a1fd4..4f36ceb7a90be5 100644 --- a/crypto/sig.c +++ b/crypto/sig.c @@ -21,14 +21,38 @@ static const struct crypto_type crypto_sig_type; +static void crypto_sig_exit_tfm(struct crypto_tfm *tfm) +{ + struct crypto_sig *sig = __crypto_sig_tfm(tfm); + struct sig_alg *alg = crypto_sig_alg(sig); + + alg->exit(sig); +} + static int crypto_sig_init_tfm(struct crypto_tfm *tfm) { if (tfm->__crt_alg->cra_type != &crypto_sig_type) return crypto_init_akcipher_ops_sig(tfm); + struct crypto_sig *sig = __crypto_sig_tfm(tfm); + struct sig_alg *alg = crypto_sig_alg(sig); + + if (alg->exit) + sig->base.exit = crypto_sig_exit_tfm; + + if (alg->init) + return alg->init(sig); + return 0; } +static void crypto_sig_free_instance(struct crypto_instance *inst) +{ + struct sig_instance *sig = sig_instance(inst); + + sig->free(sig); +} + static void __maybe_unused crypto_sig_show(struct seq_file *m, struct crypto_alg *alg) { @@ -38,16 +62,17 @@ static void __maybe_unused crypto_sig_show(struct seq_file *m, static int __maybe_unused crypto_sig_report(struct sk_buff *skb, struct crypto_alg *alg) { - struct crypto_report_akcipher rsig = {}; + struct crypto_report_sig rsig = {}; strscpy(rsig.type, "sig", sizeof(rsig.type)); - return nla_put(skb, CRYPTOCFGA_REPORT_AKCIPHER, sizeof(rsig), &rsig); + return nla_put(skb, CRYPTOCFGA_REPORT_SIG, sizeof(rsig), &rsig); } static const struct crypto_type crypto_sig_type = { .extsize = crypto_alg_extsize, .init_tfm = crypto_sig_init_tfm, + .free = crypto_sig_free_instance, #ifdef CONFIG_PROC_FS .show = crypto_sig_show, #endif @@ -68,6 +93,14 @@ EXPORT_SYMBOL_GPL(crypto_alloc_sig); int crypto_sig_maxsize(struct crypto_sig *tfm) { + if (crypto_sig_tfm(tfm)->__crt_alg->cra_type != &crypto_sig_type) + goto akcipher; + + struct sig_alg *alg = crypto_sig_alg(tfm); + + return alg->max_size(tfm); + +akcipher: struct crypto_akcipher **ctx = crypto_sig_ctx(tfm); return crypto_akcipher_maxsize(*ctx); @@ -78,6 +111,14 @@ int crypto_sig_sign(struct crypto_sig *tfm, const void *src, unsigned int slen, void *dst, unsigned int dlen) { + if (crypto_sig_tfm(tfm)->__crt_alg->cra_type != &crypto_sig_type) + goto akcipher; + + struct sig_alg *alg = crypto_sig_alg(tfm); + + return alg->sign(tfm, src, slen, dst, dlen); + +akcipher: struct crypto_akcipher **ctx = crypto_sig_ctx(tfm); struct crypto_akcipher_sync_data data = { .tfm = *ctx, @@ -97,6 +138,14 @@ int crypto_sig_verify(struct crypto_sig *tfm, const void *src, unsigned int slen, const void *digest, unsigned int dlen) { + if (crypto_sig_tfm(tfm)->__crt_alg->cra_type != &crypto_sig_type) + goto akcipher; + + struct sig_alg *alg = crypto_sig_alg(tfm); + + return alg->verify(tfm, src, slen, digest, dlen); + +akcipher: struct crypto_akcipher **ctx = crypto_sig_ctx(tfm); struct crypto_akcipher_sync_data data = { .tfm = *ctx, @@ -120,6 +169,14 @@ EXPORT_SYMBOL_GPL(crypto_sig_verify); int crypto_sig_set_pubkey(struct crypto_sig *tfm, const void *key, unsigned int keylen) { + if (crypto_sig_tfm(tfm)->__crt_alg->cra_type != &crypto_sig_type) + goto akcipher; + + struct sig_alg *alg = crypto_sig_alg(tfm); + + return alg->set_pub_key(tfm, key, keylen); + +akcipher: struct crypto_akcipher **ctx = crypto_sig_ctx(tfm); return crypto_akcipher_set_pub_key(*ctx, key, keylen); @@ -129,11 +186,93 @@ EXPORT_SYMBOL_GPL(crypto_sig_set_pubkey); int crypto_sig_set_privkey(struct crypto_sig *tfm, const void *key, unsigned int keylen) { + if (crypto_sig_tfm(tfm)->__crt_alg->cra_type != &crypto_sig_type) + goto akcipher; + + struct sig_alg *alg = crypto_sig_alg(tfm); + + return alg->set_priv_key(tfm, key, keylen); + +akcipher: struct crypto_akcipher **ctx = crypto_sig_ctx(tfm); return crypto_akcipher_set_priv_key(*ctx, key, keylen); } EXPORT_SYMBOL_GPL(crypto_sig_set_privkey); +static void sig_prepare_alg(struct sig_alg *alg) +{ + struct crypto_alg *base = &alg->base; + + base->cra_type = &crypto_sig_type; + base->cra_flags &= ~CRYPTO_ALG_TYPE_MASK; + base->cra_flags |= CRYPTO_ALG_TYPE_SIG; +} + +static int sig_default_sign(struct crypto_sig *tfm, + const void *src, unsigned int slen, + void *dst, unsigned int dlen) +{ + return -ENOSYS; +} + +static int sig_default_verify(struct crypto_sig *tfm, + const void *src, unsigned int slen, + const void *dst, unsigned int dlen) +{ + return -ENOSYS; +} + +static int sig_default_set_key(struct crypto_sig *tfm, + const void *key, unsigned int keylen) +{ + return -ENOSYS; +} + +int crypto_register_sig(struct sig_alg *alg) +{ + struct crypto_alg *base = &alg->base; + + if (!alg->sign) + alg->sign = sig_default_sign; + if (!alg->verify) + alg->verify = sig_default_verify; + if (!alg->set_priv_key) + alg->set_priv_key = sig_default_set_key; + if (!alg->set_pub_key) + return -EINVAL; + if (!alg->max_size) + return -EINVAL; + + sig_prepare_alg(alg); + return crypto_register_alg(base); +} +EXPORT_SYMBOL_GPL(crypto_register_sig); + +void crypto_unregister_sig(struct sig_alg *alg) +{ + crypto_unregister_alg(&alg->base); +} +EXPORT_SYMBOL_GPL(crypto_unregister_sig); + +int sig_register_instance(struct crypto_template *tmpl, + struct sig_instance *inst) +{ + if (WARN_ON(!inst->free)) + return -EINVAL; + sig_prepare_alg(&inst->alg); + return crypto_register_instance(tmpl, sig_crypto_instance(inst)); +} +EXPORT_SYMBOL_GPL(sig_register_instance); + +int crypto_grab_sig(struct crypto_sig_spawn *spawn, + struct crypto_instance *inst, + const char *name, u32 type, u32 mask) +{ + spawn->base.frontend = &crypto_sig_type; + return crypto_grab_spawn(&spawn->base, inst, name, type, mask); +} +EXPORT_SYMBOL_GPL(crypto_grab_sig); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Public Key Signature Algorithms"); diff --git a/crypto/testmgr.c b/crypto/testmgr.c index ee8da628e9da46..50c8d3e46e2b01 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -131,6 +132,11 @@ struct akcipher_test_suite { unsigned int count; }; +struct sig_test_suite { + const struct sig_testvec *vecs; + unsigned int count; +}; + struct kpp_test_suite { const struct kpp_testvec *vecs; unsigned int count; @@ -151,6 +157,7 @@ struct alg_test_desc { struct cprng_test_suite cprng; struct drbg_test_suite drbg; struct akcipher_test_suite akcipher; + struct sig_test_suite sig; struct kpp_test_suite kpp; } suite; }; @@ -4338,6 +4345,114 @@ static int alg_test_akcipher(const struct alg_test_desc *desc, return err; } +static int test_sig_one(struct crypto_sig *tfm, const struct sig_testvec *vecs) +{ + u8 *ptr, *key __free(kfree); + int err, sig_size; + + key = kmalloc(vecs->key_len + 2 * sizeof(u32) + vecs->param_len, + GFP_KERNEL); + if (!key) + return -ENOMEM; + + /* ecrdsa expects additional parameters appended to the key */ + memcpy(key, vecs->key, vecs->key_len); + ptr = key + vecs->key_len; + ptr = test_pack_u32(ptr, vecs->algo); + ptr = test_pack_u32(ptr, vecs->param_len); + memcpy(ptr, vecs->params, vecs->param_len); + + if (vecs->public_key_vec) + err = crypto_sig_set_pubkey(tfm, key, vecs->key_len); + else + err = crypto_sig_set_privkey(tfm, key, vecs->key_len); + if (err) + return err; + + /* + * Run asymmetric signature verification first + * (which does not require a private key) + */ + err = crypto_sig_verify(tfm, vecs->c, vecs->c_size, + vecs->m, vecs->m_size); + if (err) { + pr_err("alg: sig: verify test failed: err %d\n", err); + return err; + } + + /* + * Don't invoke sign test (which requires a private key) + * for vectors with only a public key. + */ + if (vecs->public_key_vec) + return 0; + + sig_size = crypto_sig_maxsize(tfm); + if (sig_size < vecs->c_size) { + pr_err("alg: sig: invalid maxsize %u\n", sig_size); + return -EINVAL; + } + + u8 *sig __free(kfree) = kzalloc(sig_size, GFP_KERNEL); + if (!sig) + return -ENOMEM; + + /* Run asymmetric signature generation */ + err = crypto_sig_sign(tfm, vecs->m, vecs->m_size, sig, sig_size); + if (err) { + pr_err("alg: sig: sign test failed: err %d\n", err); + return err; + } + + /* Verify that generated signature equals cooked signature */ + if (memcmp(sig, vecs->c, vecs->c_size) || + memchr_inv(sig + vecs->c_size, 0, sig_size - vecs->c_size)) { + pr_err("alg: sig: sign test failed: invalid output\n"); + hexdump(sig, sig_size); + return -EINVAL; + } + + return 0; +} + +static int test_sig(struct crypto_sig *tfm, const char *alg, + const struct sig_testvec *vecs, unsigned int tcount) +{ + const char *algo = crypto_tfm_alg_driver_name(crypto_sig_tfm(tfm)); + int ret, i; + + for (i = 0; i < tcount; i++) { + ret = test_sig_one(tfm, vecs++); + if (ret) { + pr_err("alg: sig: test %d failed for %s: err %d\n", + i + 1, algo, ret); + return ret; + } + } + return 0; +} + +__maybe_unused +static int alg_test_sig(const struct alg_test_desc *desc, const char *driver, + u32 type, u32 mask) +{ + struct crypto_sig *tfm; + int err = 0; + + tfm = crypto_alloc_sig(driver, type, mask); + if (IS_ERR(tfm)) { + pr_err("alg: sig: Failed to load tfm for %s: %ld\n", + driver, PTR_ERR(tfm)); + return PTR_ERR(tfm); + } + if (desc->suite.sig.vecs) + err = test_sig(tfm, desc->alg, desc->suite.sig.vecs, + desc->suite.sig.count); + + crypto_free_sig(tfm); + return err; +} + static int alg_test_null(const struct alg_test_desc *desc, const char *driver, u32 type, u32 mask) { diff --git a/crypto/testmgr.h b/crypto/testmgr.h index ed1640f3e35246..39dd1d55888393 100644 --- a/crypto/testmgr.h +++ b/crypto/testmgr.h @@ -162,6 +162,19 @@ struct akcipher_testvec { enum OID algo; }; +struct sig_testvec { + const unsigned char *key; + const unsigned char *params; + const unsigned char *m; + const unsigned char *c; + unsigned int key_len; + unsigned int param_len; + unsigned int m_size; + unsigned int c_size; + bool public_key_vec; + enum OID algo; +}; + struct kpp_testvec { const unsigned char *secret; const unsigned char *b_secret; diff --git a/include/crypto/internal/sig.h b/include/crypto/internal/sig.h index 97cb26ef8115d2..b16648c1a986f5 100644 --- a/include/crypto/internal/sig.h +++ b/include/crypto/internal/sig.h @@ -10,8 +10,88 @@ #include #include +struct sig_instance { + void (*free)(struct sig_instance *inst); + union { + struct { + char head[offsetof(struct sig_alg, base)]; + struct crypto_instance base; + }; + struct sig_alg alg; + }; +}; + +struct crypto_sig_spawn { + struct crypto_spawn base; +}; + static inline void *crypto_sig_ctx(struct crypto_sig *tfm) { return crypto_tfm_ctx(&tfm->base); } + +/** + * crypto_register_sig() -- Register public key signature algorithm + * + * Function registers an implementation of a public key signature algorithm + * + * @alg: algorithm definition + * + * Return: zero on success; error code in case of error + */ +int crypto_register_sig(struct sig_alg *alg); + +/** + * crypto_unregister_sig() -- Unregister public key signature algorithm + * + * Function unregisters an implementation of a public key signature algorithm + * + * @alg: algorithm definition + */ +void crypto_unregister_sig(struct sig_alg *alg); + +int sig_register_instance(struct crypto_template *tmpl, + struct sig_instance *inst); + +static inline struct sig_instance *sig_instance(struct crypto_instance *inst) +{ + return container_of(&inst->alg, struct sig_instance, alg.base); +} + +static inline struct sig_instance *sig_alg_instance(struct crypto_sig *tfm) +{ + return sig_instance(crypto_tfm_alg_instance(&tfm->base)); +} + +static inline struct crypto_instance *sig_crypto_instance(struct sig_instance + *inst) +{ + return container_of(&inst->alg.base, struct crypto_instance, alg); +} + +static inline void *sig_instance_ctx(struct sig_instance *inst) +{ + return crypto_instance_ctx(sig_crypto_instance(inst)); +} + +int crypto_grab_sig(struct crypto_sig_spawn *spawn, + struct crypto_instance *inst, + const char *name, u32 type, u32 mask); + +static inline struct crypto_sig *crypto_spawn_sig(struct crypto_sig_spawn + *spawn) +{ + return crypto_spawn_tfm2(&spawn->base); +} + +static inline void crypto_drop_sig(struct crypto_sig_spawn *spawn) +{ + crypto_drop_spawn(&spawn->base); +} + +static inline struct sig_alg *crypto_spawn_sig_alg(struct crypto_sig_spawn + *spawn) +{ + return container_of(spawn->base.alg, struct sig_alg, base); +} #endif diff --git a/include/crypto/sig.h b/include/crypto/sig.h index d25186bb2be31d..f0f52a7c5ae759 100644 --- a/include/crypto/sig.h +++ b/include/crypto/sig.h @@ -19,6 +19,52 @@ struct crypto_sig { struct crypto_tfm base; }; +/** + * struct sig_alg - generic public key signature algorithm + * + * @sign: Function performs a sign operation as defined by public key + * algorithm. Optional. + * @verify: Function performs a complete verify operation as defined by + * public key algorithm, returning verification status. Optional. + * @set_pub_key: Function invokes the algorithm specific set public key + * function, which knows how to decode and interpret + * the BER encoded public key and parameters. Mandatory. + * @set_priv_key: Function invokes the algorithm specific set private key + * function, which knows how to decode and interpret + * the BER encoded private key and parameters. Optional. + * @max_size: Function returns key size. Mandatory. + * @init: Initialize the cryptographic transformation object. + * This function is used to initialize the cryptographic + * transformation object. This function is called only once at + * the instantiation time, right after the transformation context + * was allocated. In case the cryptographic hardware has some + * special requirements which need to be handled by software, this + * function shall check for the precise requirement of the + * transformation and put any software fallbacks in place. + * @exit: Deinitialize the cryptographic transformation object. This is a + * counterpart to @init, used to remove various changes set in + * @init. + * + * @base: Common crypto API algorithm data structure + */ +struct sig_alg { + int (*sign)(struct crypto_sig *tfm, + const void *src, unsigned int slen, + void *dst, unsigned int dlen); + int (*verify)(struct crypto_sig *tfm, + const void *src, unsigned int slen, + const void *digest, unsigned int dlen); + int (*set_pub_key)(struct crypto_sig *tfm, + const void *key, unsigned int keylen); + int (*set_priv_key)(struct crypto_sig *tfm, + const void *key, unsigned int keylen); + unsigned int (*max_size)(struct crypto_sig *tfm); + int (*init)(struct crypto_sig *tfm); + void (*exit)(struct crypto_sig *tfm); + + struct crypto_alg base; +}; + /** * DOC: Generic Public Key Signature API * @@ -47,6 +93,21 @@ static inline struct crypto_tfm *crypto_sig_tfm(struct crypto_sig *tfm) return &tfm->base; } +static inline struct crypto_sig *__crypto_sig_tfm(struct crypto_tfm *tfm) +{ + return container_of(tfm, struct crypto_sig, base); +} + +static inline struct sig_alg *__crypto_sig_alg(struct crypto_alg *alg) +{ + return container_of(alg, struct sig_alg, base); +} + +static inline struct sig_alg *crypto_sig_alg(struct crypto_sig *tfm) +{ + return __crypto_sig_alg(crypto_sig_tfm(tfm)->__crt_alg); +} + /** * crypto_free_sig() - free signature tfm handle * diff --git a/include/uapi/linux/cryptouser.h b/include/uapi/linux/cryptouser.h index 20a6c0fc149e23..db05e04199726c 100644 --- a/include/uapi/linux/cryptouser.h +++ b/include/uapi/linux/cryptouser.h @@ -64,6 +64,7 @@ enum crypto_attr_type_t { CRYPTOCFGA_STAT_AKCIPHER, /* No longer supported, do not use. */ CRYPTOCFGA_STAT_KPP, /* No longer supported, do not use. */ CRYPTOCFGA_STAT_ACOMP, /* No longer supported, do not use. */ + CRYPTOCFGA_REPORT_SIG, /* struct crypto_report_sig */ __CRYPTOCFGA_MAX #define CRYPTOCFGA_MAX (__CRYPTOCFGA_MAX - 1) @@ -207,6 +208,10 @@ struct crypto_report_acomp { char type[CRYPTO_MAX_NAME]; }; +struct crypto_report_sig { + char type[CRYPTO_MAX_NAME]; +}; + #define CRYPTO_REPORT_MAXSIZE (sizeof(struct crypto_user_alg) + \ sizeof(struct crypto_report_blkcipher)) From 4a72eb9e83368ff85e4e2ae57411c60103397522 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sun, 18 Aug 2024 21:55:31 +0200 Subject: [PATCH 04/47] crypto: ecdsa - Migrate to sig_alg backend A sig_alg backend has just been introduced with the intent of moving all asymmetric sign/verify algorithms to it one by one. Migrate ecdsa.c to the new backend. One benefit of the new API is the use of kernel buffers instead of sglists, which avoids the overhead of copying signature and digest sglists back into kernel buffers. ecdsa.c is thus simplified quite a bit. Signed-off-by: Lukas Wunner --- crypto/Kconfig | 2 +- crypto/ecdsa.c | 99 ++++++++++++++++++++---------------------------- crypto/testmgr.c | 17 ++++----- crypto/testmgr.h | 27 ++----------- 4 files changed, 55 insertions(+), 90 deletions(-) diff --git a/crypto/Kconfig b/crypto/Kconfig index 777b53ed2d31cc..c17d10b1397257 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -290,7 +290,7 @@ config CRYPTO_ECDH config CRYPTO_ECDSA tristate "ECDSA (Elliptic Curve Digital Signature Algorithm)" select CRYPTO_ECC - select CRYPTO_AKCIPHER + select CRYPTO_SIG select ASN1 help ECDSA (Elliptic Curve Digital Signature Algorithm) (FIPS 186, diff --git a/crypto/ecdsa.c b/crypto/ecdsa.c index d5a10959ec281c..3b9873f56b0a64 100644 --- a/crypto/ecdsa.c +++ b/crypto/ecdsa.c @@ -4,12 +4,11 @@ */ #include -#include #include -#include +#include #include +#include #include -#include #include "ecdsasignature.asn1.h" @@ -126,46 +125,31 @@ static int _ecdsa_verify(struct ecc_ctx *ctx, const u64 *hash, const u64 *r, con /* * Verify an ECDSA signature. */ -static int ecdsa_verify(struct akcipher_request *req) +static int ecdsa_verify(struct crypto_sig *tfm, + const void *src, unsigned int slen, + const void *digest, unsigned int dlen) { - struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req); - struct ecc_ctx *ctx = akcipher_tfm_ctx(tfm); + struct ecc_ctx *ctx = crypto_sig_ctx(tfm); size_t bufsize = ctx->curve->g.ndigits * sizeof(u64); struct ecdsa_signature_ctx sig_ctx = { .curve = ctx->curve, }; u64 hash[ECC_MAX_DIGITS]; - unsigned char *buffer; int ret; if (unlikely(!ctx->pub_key_set)) return -EINVAL; - buffer = kmalloc(req->src_len + req->dst_len, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - sg_pcopy_to_buffer(req->src, - sg_nents_for_len(req->src, req->src_len + req->dst_len), - buffer, req->src_len + req->dst_len, 0); - - ret = asn1_ber_decoder(&ecdsasignature_decoder, &sig_ctx, - buffer, req->src_len); + ret = asn1_ber_decoder(&ecdsasignature_decoder, &sig_ctx, src, slen); if (ret < 0) - goto error; - - if (bufsize > req->dst_len) - bufsize = req->dst_len; - - ecc_digits_from_bytes(buffer + req->src_len, bufsize, - hash, ctx->curve->g.ndigits); + return ret; - ret = _ecdsa_verify(ctx, hash, sig_ctx.r, sig_ctx.s); + if (bufsize > dlen) + bufsize = dlen; -error: - kfree(buffer); + ecc_digits_from_bytes(digest, bufsize, hash, ctx->curve->g.ndigits); - return ret; + return _ecdsa_verify(ctx, hash, sig_ctx.r, sig_ctx.s); } static int ecdsa_ecc_ctx_init(struct ecc_ctx *ctx, unsigned int curve_id) @@ -201,9 +185,10 @@ static int ecdsa_ecc_ctx_reset(struct ecc_ctx *ctx) * Set the public ECC key as defined by RFC5480 section 2.2 "Subject Public * Key". Only the uncompressed format is supported. */ -static int ecdsa_set_pub_key(struct crypto_akcipher *tfm, const void *key, unsigned int keylen) +static int ecdsa_set_pub_key(struct crypto_sig *tfm, const void *key, + unsigned int keylen) { - struct ecc_ctx *ctx = akcipher_tfm_ctx(tfm); + struct ecc_ctx *ctx = crypto_sig_ctx(tfm); unsigned int digitlen, ndigits; const unsigned char *d = key; int ret; @@ -237,28 +222,28 @@ static int ecdsa_set_pub_key(struct crypto_akcipher *tfm, const void *key, unsig return ret; } -static void ecdsa_exit_tfm(struct crypto_akcipher *tfm) +static void ecdsa_exit_tfm(struct crypto_sig *tfm) { - struct ecc_ctx *ctx = akcipher_tfm_ctx(tfm); + struct ecc_ctx *ctx = crypto_sig_ctx(tfm); ecdsa_ecc_ctx_deinit(ctx); } -static unsigned int ecdsa_max_size(struct crypto_akcipher *tfm) +static unsigned int ecdsa_max_size(struct crypto_sig *tfm) { - struct ecc_ctx *ctx = akcipher_tfm_ctx(tfm); + struct ecc_ctx *ctx = crypto_sig_ctx(tfm); return DIV_ROUND_UP(ctx->curve->nbits, 8); } -static int ecdsa_nist_p521_init_tfm(struct crypto_akcipher *tfm) +static int ecdsa_nist_p521_init_tfm(struct crypto_sig *tfm) { - struct ecc_ctx *ctx = akcipher_tfm_ctx(tfm); + struct ecc_ctx *ctx = crypto_sig_ctx(tfm); return ecdsa_ecc_ctx_init(ctx, ECC_CURVE_NIST_P521); } -static struct akcipher_alg ecdsa_nist_p521 = { +static struct sig_alg ecdsa_nist_p521 = { .verify = ecdsa_verify, .set_pub_key = ecdsa_set_pub_key, .max_size = ecdsa_max_size, @@ -273,14 +258,14 @@ static struct akcipher_alg ecdsa_nist_p521 = { }, }; -static int ecdsa_nist_p384_init_tfm(struct crypto_akcipher *tfm) +static int ecdsa_nist_p384_init_tfm(struct crypto_sig *tfm) { - struct ecc_ctx *ctx = akcipher_tfm_ctx(tfm); + struct ecc_ctx *ctx = crypto_sig_ctx(tfm); return ecdsa_ecc_ctx_init(ctx, ECC_CURVE_NIST_P384); } -static struct akcipher_alg ecdsa_nist_p384 = { +static struct sig_alg ecdsa_nist_p384 = { .verify = ecdsa_verify, .set_pub_key = ecdsa_set_pub_key, .max_size = ecdsa_max_size, @@ -295,14 +280,14 @@ static struct akcipher_alg ecdsa_nist_p384 = { }, }; -static int ecdsa_nist_p256_init_tfm(struct crypto_akcipher *tfm) +static int ecdsa_nist_p256_init_tfm(struct crypto_sig *tfm) { - struct ecc_ctx *ctx = akcipher_tfm_ctx(tfm); + struct ecc_ctx *ctx = crypto_sig_ctx(tfm); return ecdsa_ecc_ctx_init(ctx, ECC_CURVE_NIST_P256); } -static struct akcipher_alg ecdsa_nist_p256 = { +static struct sig_alg ecdsa_nist_p256 = { .verify = ecdsa_verify, .set_pub_key = ecdsa_set_pub_key, .max_size = ecdsa_max_size, @@ -317,14 +302,14 @@ static struct akcipher_alg ecdsa_nist_p256 = { }, }; -static int ecdsa_nist_p192_init_tfm(struct crypto_akcipher *tfm) +static int ecdsa_nist_p192_init_tfm(struct crypto_sig *tfm) { - struct ecc_ctx *ctx = akcipher_tfm_ctx(tfm); + struct ecc_ctx *ctx = crypto_sig_ctx(tfm); return ecdsa_ecc_ctx_init(ctx, ECC_CURVE_NIST_P192); } -static struct akcipher_alg ecdsa_nist_p192 = { +static struct sig_alg ecdsa_nist_p192 = { .verify = ecdsa_verify, .set_pub_key = ecdsa_set_pub_key, .max_size = ecdsa_max_size, @@ -345,42 +330,42 @@ static int __init ecdsa_init(void) int ret; /* NIST p192 may not be available in FIPS mode */ - ret = crypto_register_akcipher(&ecdsa_nist_p192); + ret = crypto_register_sig(&ecdsa_nist_p192); ecdsa_nist_p192_registered = ret == 0; - ret = crypto_register_akcipher(&ecdsa_nist_p256); + ret = crypto_register_sig(&ecdsa_nist_p256); if (ret) goto nist_p256_error; - ret = crypto_register_akcipher(&ecdsa_nist_p384); + ret = crypto_register_sig(&ecdsa_nist_p384); if (ret) goto nist_p384_error; - ret = crypto_register_akcipher(&ecdsa_nist_p521); + ret = crypto_register_sig(&ecdsa_nist_p521); if (ret) goto nist_p521_error; return 0; nist_p521_error: - crypto_unregister_akcipher(&ecdsa_nist_p384); + crypto_unregister_sig(&ecdsa_nist_p384); nist_p384_error: - crypto_unregister_akcipher(&ecdsa_nist_p256); + crypto_unregister_sig(&ecdsa_nist_p256); nist_p256_error: if (ecdsa_nist_p192_registered) - crypto_unregister_akcipher(&ecdsa_nist_p192); + crypto_unregister_sig(&ecdsa_nist_p192); return ret; } static void __exit ecdsa_exit(void) { if (ecdsa_nist_p192_registered) - crypto_unregister_akcipher(&ecdsa_nist_p192); - crypto_unregister_akcipher(&ecdsa_nist_p256); - crypto_unregister_akcipher(&ecdsa_nist_p384); - crypto_unregister_akcipher(&ecdsa_nist_p521); + crypto_unregister_sig(&ecdsa_nist_p192); + crypto_unregister_sig(&ecdsa_nist_p256); + crypto_unregister_sig(&ecdsa_nist_p384); + crypto_unregister_sig(&ecdsa_nist_p521); } subsys_initcall(ecdsa_init); diff --git a/crypto/testmgr.c b/crypto/testmgr.c index 50c8d3e46e2b01..4e6f2eb7c49626 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -4432,7 +4432,6 @@ static int test_sig(struct crypto_sig *tfm, const char *alg, return 0; } -__maybe_unused static int alg_test_sig(const struct alg_test_desc *desc, const char *driver, u32 type, u32 mask) { @@ -5242,30 +5241,30 @@ static const struct alg_test_desc alg_test_descs[] = { } }, { .alg = "ecdsa-nist-p192", - .test = alg_test_akcipher, + .test = alg_test_sig, .suite = { - .akcipher = __VECS(ecdsa_nist_p192_tv_template) + .sig = __VECS(ecdsa_nist_p192_tv_template) } }, { .alg = "ecdsa-nist-p256", - .test = alg_test_akcipher, + .test = alg_test_sig, .fips_allowed = 1, .suite = { - .akcipher = __VECS(ecdsa_nist_p256_tv_template) + .sig = __VECS(ecdsa_nist_p256_tv_template) } }, { .alg = "ecdsa-nist-p384", - .test = alg_test_akcipher, + .test = alg_test_sig, .fips_allowed = 1, .suite = { - .akcipher = __VECS(ecdsa_nist_p384_tv_template) + .sig = __VECS(ecdsa_nist_p384_tv_template) } }, { .alg = "ecdsa-nist-p521", - .test = alg_test_akcipher, + .test = alg_test_sig, .fips_allowed = 1, .suite = { - .akcipher = __VECS(ecdsa_nist_p521_tv_template) + .sig = __VECS(ecdsa_nist_p521_tv_template) } }, { .alg = "ecrdsa", diff --git a/crypto/testmgr.h b/crypto/testmgr.h index 39dd1d55888393..a4987610fcb5cb 100644 --- a/crypto/testmgr.h +++ b/crypto/testmgr.h @@ -663,7 +663,7 @@ static const struct akcipher_testvec rsa_tv_template[] = { /* * ECDSA test vectors. */ -static const struct akcipher_testvec ecdsa_nist_p192_tv_template[] = { +static const struct sig_testvec ecdsa_nist_p192_tv_template[] = { { .key = /* secp192r1(sha1) */ "\x04\xf7\x46\xf8\x2f\x15\xf6\x22\x8e\xd7\x57\x4f\xcc\xe7\xbb\xc1" @@ -682,7 +682,6 @@ static const struct akcipher_testvec ecdsa_nist_p192_tv_template[] = { "\x80\x6f\xa5\x79\x77\xda\xd0", .c_size = 55, .public_key_vec = true, - .siggen_sigver_test = true, }, { .key = /* secp192r1(sha224) */ "\x04\xb6\x4b\xb1\xd1\xac\xba\x24\x8f\x65\xb2\x60\x00\x90\xbf\xbd" @@ -701,7 +700,6 @@ static const struct akcipher_testvec ecdsa_nist_p192_tv_template[] = { "\x5c\x99\xdb\x92\x5b\x36", .c_size = 54, .public_key_vec = true, - .siggen_sigver_test = true, }, { .key = /* secp192r1(sha256) */ "\x04\xe2\x51\x24\x9b\xf7\xb6\x32\x82\x39\x66\x3d\x5b\xec\x3b\xae" @@ -720,7 +718,6 @@ static const struct akcipher_testvec ecdsa_nist_p192_tv_template[] = { "\x3a\x97\xd9\xcd\x1a\x6a\x49", .c_size = 55, .public_key_vec = true, - .siggen_sigver_test = true, }, { .key = /* secp192r1(sha384) */ "\x04\x5a\x13\xfe\x68\x86\x4d\xf4\x17\xc7\xa4\xe5\x8c\x65\x57\xb7" @@ -740,7 +737,6 @@ static const struct akcipher_testvec ecdsa_nist_p192_tv_template[] = { "\x12\x3b\x3b\x28\xfb\x6d\xe1", .c_size = 55, .public_key_vec = true, - .siggen_sigver_test = true, }, { .key = /* secp192r1(sha512) */ "\x04\xd5\xf2\x6e\xc3\x94\x5c\x52\xbc\xdf\x86\x6c\x14\xd1\xca\xea" @@ -761,11 +757,10 @@ static const struct akcipher_testvec ecdsa_nist_p192_tv_template[] = { "\x6a\xdf\x97\xfd\x82\x76\x24", .c_size = 55, .public_key_vec = true, - .siggen_sigver_test = true, }, }; -static const struct akcipher_testvec ecdsa_nist_p256_tv_template[] = { +static const struct sig_testvec ecdsa_nist_p256_tv_template[] = { { .key = /* secp256r1(sha1) */ "\x04\xb9\x7b\xbb\xd7\x17\x64\xd2\x7e\xfc\x81\x5d\x87\x06\x83\x41" @@ -786,7 +781,6 @@ static const struct akcipher_testvec ecdsa_nist_p256_tv_template[] = { "\xfb\x9d\x8b\xde\xd4\x8d\x6f\xad", .c_size = 72, .public_key_vec = true, - .siggen_sigver_test = true, }, { .key = /* secp256r1(sha224) */ "\x04\x8b\x6d\xc0\x33\x8e\x2d\x8b\x67\xf5\xeb\xc4\x7f\xa0\xf5\xd9" @@ -807,7 +801,6 @@ static const struct akcipher_testvec ecdsa_nist_p256_tv_template[] = { "\x2e\x8b\xde\x5a\x04\x0e", .c_size = 70, .public_key_vec = true, - .siggen_sigver_test = true, }, { .key = /* secp256r1(sha256) */ "\x04\xf1\xea\xc4\x53\xf3\xb9\x0e\x9f\x7e\xad\xe3\xea\xd7\x0e\x0f" @@ -828,7 +821,6 @@ static const struct akcipher_testvec ecdsa_nist_p256_tv_template[] = { "\x2a\x65\x35\x23\xe3\x1d\xfa", .c_size = 71, .public_key_vec = true, - .siggen_sigver_test = true, }, { .key = /* secp256r1(sha384) */ "\x04\xc5\xc6\xea\x60\xc9\xce\xad\x02\x8d\xf5\x3e\x24\xe3\x52\x1d" @@ -850,7 +842,6 @@ static const struct akcipher_testvec ecdsa_nist_p256_tv_template[] = { "\xc0\x60\x11\x92\xdc\x17\x89\x12", .c_size = 72, .public_key_vec = true, - .siggen_sigver_test = true, }, { .key = /* secp256r1(sha512) */ "\x04\xd7\x27\x46\x49\xf6\x26\x85\x12\x40\x76\x8e\xe2\xe6\x2a\x7a" @@ -873,11 +864,10 @@ static const struct akcipher_testvec ecdsa_nist_p256_tv_template[] = { "\x31\x79\x4a\xe9\x81\x6a\xee", .c_size = 71, .public_key_vec = true, - .siggen_sigver_test = true, }, }; -static const struct akcipher_testvec ecdsa_nist_p384_tv_template[] = { +static const struct sig_testvec ecdsa_nist_p384_tv_template[] = { { .key = /* secp384r1(sha1) */ "\x04\x89\x25\xf3\x97\x88\xcb\xb0\x78\xc5\x72\x9a\x14\x6e\x7a\xb1" @@ -902,7 +892,6 @@ static const struct akcipher_testvec ecdsa_nist_p384_tv_template[] = { "\x79\x12\x2a\xb7\xc5\x15\x92\xc5", .c_size = 104, .public_key_vec = true, - .siggen_sigver_test = true, }, { .key = /* secp384r1(sha224) */ "\x04\x69\x6c\xcf\x62\xee\xd0\x0d\xe5\xb5\x2f\x70\x54\xcf\x26\xa0" @@ -927,7 +916,6 @@ static const struct akcipher_testvec ecdsa_nist_p384_tv_template[] = { "\x88\x2b\x82\x26\x5e\x1c\xda\xfb", .c_size = 104, .public_key_vec = true, - .siggen_sigver_test = true, }, { .key = /* secp384r1(sha256) */ "\x04\xee\xd6\xda\x3e\x94\x90\x00\x27\xed\xf8\x64\x55\xd6\x51\x9a" @@ -952,7 +940,6 @@ static const struct akcipher_testvec ecdsa_nist_p384_tv_template[] = { "\xf4\x1f\x39\xca\x4d\x43", .c_size = 102, .public_key_vec = true, - .siggen_sigver_test = true, }, { .key = /* secp384r1(sha384) */ "\x04\x3a\x2f\x62\xe7\x1a\xcf\x24\xd0\x0b\x7c\xe0\xed\x46\x0a\x4f" @@ -978,7 +965,6 @@ static const struct akcipher_testvec ecdsa_nist_p384_tv_template[] = { "\xab\x8d\x4e\xde\xe6\x6d\x9b\x66", .c_size = 104, .public_key_vec = true, - .siggen_sigver_test = true, }, { .key = /* secp384r1(sha512) */ "\x04\xb4\xe7\xc1\xeb\x64\x25\x22\x46\xc3\x86\x61\x80\xbe\x1e\x46" @@ -1005,11 +991,10 @@ static const struct akcipher_testvec ecdsa_nist_p384_tv_template[] = { "\x3c\x93\xff\x50\x5d", .c_size = 101, .public_key_vec = true, - .siggen_sigver_test = true, }, }; -static const struct akcipher_testvec ecdsa_nist_p521_tv_template[] = { +static const struct sig_testvec ecdsa_nist_p521_tv_template[] = { { .key = /* secp521r1(sha224) */ "\x04\x01\x4f\x43\x18\xb6\xa9\xc9\x5d\x68\xd3\xa9\x42\xf8\x98\xc0" @@ -1038,7 +1023,6 @@ static const struct akcipher_testvec ecdsa_nist_p521_tv_template[] = { "\xa3\x50\xb1\xa5\x98\x92\x2a\xa5\x52", .c_size = 137, .public_key_vec = true, - .siggen_sigver_test = true, }, { .key = /* secp521r1(sha256) */ @@ -1068,7 +1052,6 @@ static const struct akcipher_testvec ecdsa_nist_p521_tv_template[] = { "\xb7\x1d\x91\x55\x38\xb6\xf6\x34\x65\xc7\xbd", .c_size = 139, .public_key_vec = true, - .siggen_sigver_test = true, }, { .key = /* secp521r1(sha384) */ @@ -1099,7 +1082,6 @@ static const struct akcipher_testvec ecdsa_nist_p521_tv_template[] = { "\x8f\xb4\x22\xc6\x4f\xab\x2b\x62\xc1\x42\xb1", .c_size = 139, .public_key_vec = true, - .siggen_sigver_test = true, }, { .key = /* secp521r1(sha512) */ @@ -1131,7 +1113,6 @@ static const struct akcipher_testvec ecdsa_nist_p521_tv_template[] = { "\xa6\xe5\x25\x46\x1e\x77\x44\x78\xe0\xd1\x04", .c_size = 139, .public_key_vec = true, - .siggen_sigver_test = true, }, }; From ca1c9ebef2d1be549ac83be55a7fd77360374238 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 19 Aug 2024 07:09:53 +0200 Subject: [PATCH 05/47] crypto: ecrdsa - Migrate to sig_alg backend A sig_alg backend has just been introduced with the intent of moving all asymmetric sign/verify algorithms to it one by one. Migrate ecrdsa.c to the new backend. One benefit of the new API is the use of kernel buffers instead of sglists, which avoids the overhead of copying signature and digest sglists back into kernel buffers. ecrdsa.c is thus simplified quite a bit. Signed-off-by: Lukas Wunner --- crypto/Kconfig | 2 +- crypto/ecrdsa.c | 56 +++++++++++++++++++++--------------------------- crypto/testmgr.c | 4 ++-- crypto/testmgr.h | 7 +----- 4 files changed, 28 insertions(+), 41 deletions(-) diff --git a/crypto/Kconfig b/crypto/Kconfig index c17d10b1397257..479df95116ca80 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -302,7 +302,7 @@ config CRYPTO_ECDSA config CRYPTO_ECRDSA tristate "EC-RDSA (Elliptic Curve Russian Digital Signature Algorithm)" select CRYPTO_ECC - select CRYPTO_AKCIPHER + select CRYPTO_SIG select CRYPTO_STREEBOG select OID_REGISTRY select ASN1 diff --git a/crypto/ecrdsa.c b/crypto/ecrdsa.c index 3811f3805b5d88..7383dd11089b08 100644 --- a/crypto/ecrdsa.c +++ b/crypto/ecrdsa.c @@ -18,12 +18,11 @@ #include #include +#include #include -#include #include -#include +#include #include -#include #include "ecrdsa_params.asn1.h" #include "ecrdsa_pub_key.asn1.h" #include "ecrdsa_defs.h" @@ -68,13 +67,12 @@ static const struct ecc_curve *get_curve_by_oid(enum OID oid) } } -static int ecrdsa_verify(struct akcipher_request *req) +static int ecrdsa_verify(struct crypto_sig *tfm, + const void *src, unsigned int slen, + const void *digest, unsigned int dlen) { - struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req); - struct ecrdsa_ctx *ctx = akcipher_tfm_ctx(tfm); - unsigned char sig[ECRDSA_MAX_SIG_SIZE]; - unsigned char digest[STREEBOG512_DIGEST_SIZE]; - unsigned int ndigits = req->dst_len / sizeof(u64); + struct ecrdsa_ctx *ctx = crypto_sig_ctx(tfm); + unsigned int ndigits = dlen / sizeof(u64); u64 r[ECRDSA_MAX_DIGITS]; /* witness (r) */ u64 _r[ECRDSA_MAX_DIGITS]; /* -r */ u64 s[ECRDSA_MAX_DIGITS]; /* second part of sig (s) */ @@ -91,25 +89,19 @@ static int ecrdsa_verify(struct akcipher_request *req) */ if (!ctx->curve || !ctx->digest || - !req->src || + !src || + !digest || !ctx->pub_key.x || - req->dst_len != ctx->digest_len || - req->dst_len != ctx->curve->g.ndigits * sizeof(u64) || + dlen != ctx->digest_len || + dlen != ctx->curve->g.ndigits * sizeof(u64) || ctx->pub_key.ndigits != ctx->curve->g.ndigits || - req->dst_len * 2 != req->src_len || - WARN_ON(req->src_len > sizeof(sig)) || - WARN_ON(req->dst_len > sizeof(digest))) + dlen * 2 != slen || + WARN_ON(slen > ECRDSA_MAX_SIG_SIZE) || + WARN_ON(dlen > STREEBOG512_DIGEST_SIZE)) return -EBADMSG; - sg_copy_to_buffer(req->src, sg_nents_for_len(req->src, req->src_len), - sig, req->src_len); - sg_pcopy_to_buffer(req->src, - sg_nents_for_len(req->src, - req->src_len + req->dst_len), - digest, req->dst_len, req->src_len); - - vli_from_be64(s, sig, ndigits); - vli_from_be64(r, sig + ndigits * sizeof(u64), ndigits); + vli_from_be64(s, src, ndigits); + vli_from_be64(r, src + ndigits * sizeof(u64), ndigits); /* Step 1: verify that 0 < r < q, 0 < s < q */ if (vli_is_zero(r, ndigits) || @@ -188,10 +180,10 @@ static u8 *ecrdsa_unpack_u32(u32 *dst, void *src) } /* Parse BER encoded subjectPublicKey. */ -static int ecrdsa_set_pub_key(struct crypto_akcipher *tfm, const void *key, +static int ecrdsa_set_pub_key(struct crypto_sig *tfm, const void *key, unsigned int keylen) { - struct ecrdsa_ctx *ctx = akcipher_tfm_ctx(tfm); + struct ecrdsa_ctx *ctx = crypto_sig_ctx(tfm); unsigned int ndigits; u32 algo, paramlen; u8 *params; @@ -249,9 +241,9 @@ static int ecrdsa_set_pub_key(struct crypto_akcipher *tfm, const void *key, return 0; } -static unsigned int ecrdsa_max_size(struct crypto_akcipher *tfm) +static unsigned int ecrdsa_max_size(struct crypto_sig *tfm) { - struct ecrdsa_ctx *ctx = akcipher_tfm_ctx(tfm); + struct ecrdsa_ctx *ctx = crypto_sig_ctx(tfm); /* * Verify doesn't need any output, so it's just informational @@ -260,11 +252,11 @@ static unsigned int ecrdsa_max_size(struct crypto_akcipher *tfm) return ctx->pub_key.ndigits * sizeof(u64); } -static void ecrdsa_exit_tfm(struct crypto_akcipher *tfm) +static void ecrdsa_exit_tfm(struct crypto_sig *tfm) { } -static struct akcipher_alg ecrdsa_alg = { +static struct sig_alg ecrdsa_alg = { .verify = ecrdsa_verify, .set_pub_key = ecrdsa_set_pub_key, .max_size = ecrdsa_max_size, @@ -280,12 +272,12 @@ static struct akcipher_alg ecrdsa_alg = { static int __init ecrdsa_mod_init(void) { - return crypto_register_akcipher(&ecrdsa_alg); + return crypto_register_sig(&ecrdsa_alg); } static void __exit ecrdsa_mod_fini(void) { - crypto_unregister_akcipher(&ecrdsa_alg); + crypto_unregister_sig(&ecrdsa_alg); } module_init(ecrdsa_mod_init); diff --git a/crypto/testmgr.c b/crypto/testmgr.c index 4e6f2eb7c49626..ed971d857057b6 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -5268,9 +5268,9 @@ static const struct alg_test_desc alg_test_descs[] = { } }, { .alg = "ecrdsa", - .test = alg_test_akcipher, + .test = alg_test_sig, .suite = { - .akcipher = __VECS(ecrdsa_tv_template) + .sig = __VECS(ecrdsa_tv_template) } }, { .alg = "essiv(authenc(hmac(sha256),cbc(aes)),sha256)", diff --git a/crypto/testmgr.h b/crypto/testmgr.h index a4987610fcb5cb..fd4823c26d93eb 100644 --- a/crypto/testmgr.h +++ b/crypto/testmgr.h @@ -1119,7 +1119,7 @@ static const struct sig_testvec ecdsa_nist_p521_tv_template[] = { /* * EC-RDSA test vectors are generated by gost-engine. */ -static const struct akcipher_testvec ecrdsa_tv_template[] = { +static const struct sig_testvec ecrdsa_tv_template[] = { { .key = "\x04\x40\xd5\xa7\x77\xf9\x26\x2f\x8c\xbd\xcc\xe3\x1f\x01\x94\x05" @@ -1144,7 +1144,6 @@ static const struct akcipher_testvec ecrdsa_tv_template[] = { "\x79\xd2\x76\x64\xa3\xbd\x66\x10\x79\x05\x5a\x06\x42\xec\xb9\xc9", .m_size = 32, .public_key_vec = true, - .siggen_sigver_test = true, }, { .key = @@ -1170,7 +1169,6 @@ static const struct akcipher_testvec ecrdsa_tv_template[] = { "\x11\x23\x4a\x70\x43\x52\x7a\x68\x11\x65\x45\x37\xbb\x25\xb7\x40", .m_size = 32, .public_key_vec = true, - .siggen_sigver_test = true, }, { .key = @@ -1196,7 +1194,6 @@ static const struct akcipher_testvec ecrdsa_tv_template[] = { "\x9f\x16\xc6\x1c\xb1\x3f\x84\x41\x69\xec\x34\xfd\xf1\xf9\xa3\x39", .m_size = 32, .public_key_vec = true, - .siggen_sigver_test = true, }, { .key = @@ -1231,7 +1228,6 @@ static const struct akcipher_testvec ecrdsa_tv_template[] = { "\xa8\xf6\x80\x01\xb9\x27\xac\xd8\x45\x96\x66\xa1\xee\x48\x08\x3f", .m_size = 64, .public_key_vec = true, - .siggen_sigver_test = true, }, { .key = @@ -1266,7 +1262,6 @@ static const struct akcipher_testvec ecrdsa_tv_template[] = { "\x6d\xf4\xd2\x45\xc2\x83\xa0\x42\x95\x05\x9d\x89\x8e\x0a\xca\xcc", .m_size = 64, .public_key_vec = true, - .siggen_sigver_test = true, }, }; From b632d0120da4eac7b10ddc1bbe6ee0796c973923 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 19 Aug 2024 07:20:12 +0200 Subject: [PATCH 06/47] crypto: rsa-pkcs1pad - Deduplicate set_{pub,priv}_key callbacks pkcs1pad_set_pub_key() and pkcs1pad_set_priv_key() are almost identical. The upcoming migration of sign/verify operations from rsa-pkcs1pad.c into a separate crypto_template will require another copy of the exact same functions. When RSASSA-PSS and RSAES-OAEP are introduced, each will need yet another copy. Deduplicate the functions into a single one which lives in a common header file for reuse by RSASSA-PKCS1-v1_5, RSASSA-PSS and RSAES-OAEP. Signed-off-by: Lukas Wunner Reviewed-by: Stefan Berger --- crypto/rsa-pkcs1pad.c | 30 ++---------------------------- include/crypto/internal/rsa.h | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/crypto/rsa-pkcs1pad.c b/crypto/rsa-pkcs1pad.c index cd501195f34a1a..3c5fe8c93938eb 100644 --- a/crypto/rsa-pkcs1pad.c +++ b/crypto/rsa-pkcs1pad.c @@ -131,42 +131,16 @@ static int pkcs1pad_set_pub_key(struct crypto_akcipher *tfm, const void *key, unsigned int keylen) { struct pkcs1pad_ctx *ctx = akcipher_tfm_ctx(tfm); - int err; - - ctx->key_size = 0; - err = crypto_akcipher_set_pub_key(ctx->child, key, keylen); - if (err) - return err; - - /* Find out new modulus size from rsa implementation */ - err = crypto_akcipher_maxsize(ctx->child); - if (err > PAGE_SIZE) - return -ENOTSUPP; - - ctx->key_size = err; - return 0; + return rsa_set_key(ctx->child, &ctx->key_size, RSA_PUB, key, keylen); } static int pkcs1pad_set_priv_key(struct crypto_akcipher *tfm, const void *key, unsigned int keylen) { struct pkcs1pad_ctx *ctx = akcipher_tfm_ctx(tfm); - int err; - - ctx->key_size = 0; - err = crypto_akcipher_set_priv_key(ctx->child, key, keylen); - if (err) - return err; - - /* Find out new modulus size from rsa implementation */ - err = crypto_akcipher_maxsize(ctx->child); - if (err > PAGE_SIZE) - return -ENOTSUPP; - - ctx->key_size = err; - return 0; + return rsa_set_key(ctx->child, &ctx->key_size, RSA_PRIV, key, keylen); } static unsigned int pkcs1pad_get_max_size(struct crypto_akcipher *tfm) diff --git a/include/crypto/internal/rsa.h b/include/crypto/internal/rsa.h index e870133f4b7751..754f687134df74 100644 --- a/include/crypto/internal/rsa.h +++ b/include/crypto/internal/rsa.h @@ -8,6 +8,7 @@ #ifndef _RSA_HELPER_ #define _RSA_HELPER_ #include +#include /** * rsa_key - RSA key structure @@ -53,5 +54,32 @@ int rsa_parse_pub_key(struct rsa_key *rsa_key, const void *key, int rsa_parse_priv_key(struct rsa_key *rsa_key, const void *key, unsigned int key_len); +#define RSA_PUB (true) +#define RSA_PRIV (false) + +static inline int rsa_set_key(struct crypto_akcipher *child, + unsigned int *key_size, bool is_pubkey, + const void *key, unsigned int keylen) +{ + int err; + + *key_size = 0; + + if (is_pubkey) + err = crypto_akcipher_set_pub_key(child, key, keylen); + else + err = crypto_akcipher_set_priv_key(child, key, keylen); + if (err) + return err; + + /* Find out new modulus size from rsa implementation */ + err = crypto_akcipher_maxsize(child); + if (err > PAGE_SIZE) + return -ENOTSUPP; + + *key_size = err; + return 0; +} + extern struct crypto_template rsa_pkcs1pad_tmpl; #endif From 3b87ceb5dea68be8c42ff1bc207ee18b5817d077 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 19 Aug 2024 07:47:50 +0200 Subject: [PATCH 07/47] crypto: rsassa-pkcs1 - Migrate to sig_alg backend A sig_alg backend has just been introduced with the intent of moving all asymmetric sign/verify algorithms to it one by one. Migrate the sign/verify operations from rsa-pkcs1pad.c to a separate rsassa-pkcs1.c which uses the new backend. Consequently there are now two templates which build on the "rsa" akcipher_alg: * The existing "pkcs1pad" template, which is instantiated as an akcipher_instance and retains the encrypt/decrypt operations of RSAES-PKCS1-v1_5 (RFC 8017 sec 7.2). * The new "pkcs1" template, which is instantiated as a sig_instance and contains the sign/verify operations of RSASSA-PKCS1-v1_5 (RFC 8017 sec 8.2). In a separate step, rsa-pkcs1pad.c could optionally be renamed to rsaes-pkcs1.c for clarity. Additional "oaep" and "pss" templates could be added for RSAES-OAEP and RSASSA-PSS. Note that it's currently allowed to allocate a "pkcs1pad(rsa)" transform without specifying a hash algorithm. That makes sense if the transform is only used for encrypt/decrypt and continues to be supported. But for sign/verify, such transforms previously did not insert the Full Hash Prefix into the padding. The resulting message encoding was incompliant with EMSA-PKCS1-v1_5 (RFC 8017 sec 9.2) and therefore nonsensical. From here on out, it's no longer allowed to allocate a transform without specifying a hash algorithm if the transform is used for sign/verify operations. This simplifies the code because the insertion of the Full Hash Prefix is no longer optional, so various "if (digest_info)" clauses can be removed. There has been a previous attempt to forbid transform allocation without specifying a hash algorithm, namely by commit c0d20d22e0ad ("crypto: rsa-pkcs1pad - Require hash to be present"). It had to be rolled back with commit b3a8c8a5ebb5 ("crypto: rsa-pkcs1pad: Allow hash to be optional [ver #2]"), presumably because it broke allocation of a transform which was solely used for encrypt/decrypt, not sign/verify. Avoid such breakage by allowing transform allocation for encrypt/decrypt with and without specifying a hash algorithm (and simply ignoring the hash algorithm in the former case). So again, specifying a hash algorithm is now mandatory for sign/verify, but optional and ignored for encrypt/decrypt. The new sig_alg API uses kernel buffers instead of sglists, which avoids the overhead of copying signature and digest from sglists back into kernel buffers. rsassa-pkcs1.c is thus simplified quite a bit. sig_alg is always synchronous, whereas the underlying "rsa" akcipher_alg may be asynchronous. So await the result of the akcipher_alg, similar to crypto_akcipher_sync_{en,de}crypt(). As part of the migration, rename "rsa_digest_info" to "hash_prefix" to adhere to the spec language in RFC 9580. Otherwise keep the code unmodified wherever possible to ease reviewing and bisecting. Leave several simplification and hardening opportunities to separate commits. rsassa-pkcs1.c uses modern __free() syntax for allocation of buffers which need to be freed by kfree_sensitive(), hence a DEFINE_FREE() clause for kfree_sensitive() is introduced herein as a byproduct. Signed-off-by: Lukas Wunner --- crypto/Kconfig | 1 + crypto/Makefile | 1 + crypto/asymmetric_keys/public_key.c | 10 +- crypto/rsa-pkcs1pad.c | 341 ++-------------------- crypto/rsa.c | 17 +- crypto/rsassa-pkcs1.c | 422 ++++++++++++++++++++++++++++ crypto/testmgr.c | 22 +- crypto/testmgr.h | 3 +- include/crypto/internal/rsa.h | 1 + include/linux/slab.h | 1 + security/integrity/ima/ima_main.c | 6 +- 11 files changed, 480 insertions(+), 345 deletions(-) create mode 100644 crypto/rsassa-pkcs1.c diff --git a/crypto/Kconfig b/crypto/Kconfig index 479df95116ca80..6b0bfbccac08b8 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -250,6 +250,7 @@ config CRYPTO_RSA tristate "RSA (Rivest-Shamir-Adleman)" select CRYPTO_AKCIPHER select CRYPTO_MANAGER + select CRYPTO_SIG select MPILIB select ASN1 help diff --git a/crypto/Makefile b/crypto/Makefile index 4c99e5d376f687..7de29bf843e94c 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -48,6 +48,7 @@ rsa_generic-y += rsaprivkey.asn1.o rsa_generic-y += rsa.o rsa_generic-y += rsa_helper.o rsa_generic-y += rsa-pkcs1pad.o +rsa_generic-y += rsassa-pkcs1.o obj-$(CONFIG_CRYPTO_RSA) += rsa_generic.o $(obj)/ecdsasignature.asn1.o: $(obj)/ecdsasignature.asn1.c $(obj)/ecdsasignature.asn1.h diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c index 422940a6706a7f..3fb27ecd65f660 100644 --- a/crypto/asymmetric_keys/public_key.c +++ b/crypto/asymmetric_keys/public_key.c @@ -83,13 +83,19 @@ software_key_determine_akcipher(const struct public_key *pkey, if (strcmp(encoding, "pkcs1") == 0) { *sig = op == kernel_pkey_sign || op == kernel_pkey_verify; - if (!hash_algo) { + if (!*sig) { + /* + * For encrypt/decrypt, hash_algo is not used + * but allowed to be set for historic reasons. + */ n = snprintf(alg_name, CRYPTO_MAX_ALG_NAME, "pkcs1pad(%s)", pkey->pkey_algo); } else { + if (!hash_algo) + return -EINVAL; n = snprintf(alg_name, CRYPTO_MAX_ALG_NAME, - "pkcs1pad(%s,%s)", + "pkcs1(%s,%s)", pkey->pkey_algo, hash_algo); } return n >= CRYPTO_MAX_ALG_NAME ? -EINVAL : 0; diff --git a/crypto/rsa-pkcs1pad.c b/crypto/rsa-pkcs1pad.c index 3c5fe8c93938eb..50bdb18e7b4837 100644 --- a/crypto/rsa-pkcs1pad.c +++ b/crypto/rsa-pkcs1pad.c @@ -16,101 +16,6 @@ #include #include -/* - * Hash algorithm OIDs plus ASN.1 DER wrappings [RFC4880 sec 5.2.2]. - */ -static const u8 rsa_digest_info_md5[] = { - 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, - 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, /* OID */ - 0x05, 0x00, 0x04, 0x10 -}; - -static const u8 rsa_digest_info_sha1[] = { - 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, - 0x2b, 0x0e, 0x03, 0x02, 0x1a, - 0x05, 0x00, 0x04, 0x14 -}; - -static const u8 rsa_digest_info_rmd160[] = { - 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, - 0x2b, 0x24, 0x03, 0x02, 0x01, - 0x05, 0x00, 0x04, 0x14 -}; - -static const u8 rsa_digest_info_sha224[] = { - 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, - 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, - 0x05, 0x00, 0x04, 0x1c -}; - -static const u8 rsa_digest_info_sha256[] = { - 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, - 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, - 0x05, 0x00, 0x04, 0x20 -}; - -static const u8 rsa_digest_info_sha384[] = { - 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, - 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, - 0x05, 0x00, 0x04, 0x30 -}; - -static const u8 rsa_digest_info_sha512[] = { - 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, - 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, - 0x05, 0x00, 0x04, 0x40 -}; - -static const u8 rsa_digest_info_sha3_256[] = { - 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, - 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x08, - 0x05, 0x00, 0x04, 0x20 -}; - -static const u8 rsa_digest_info_sha3_384[] = { - 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, - 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x09, - 0x05, 0x00, 0x04, 0x30 -}; - -static const u8 rsa_digest_info_sha3_512[] = { - 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, - 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0A, - 0x05, 0x00, 0x04, 0x40 -}; - -static const struct rsa_asn1_template { - const char *name; - const u8 *data; - size_t size; -} rsa_asn1_templates[] = { -#define _(X) { #X, rsa_digest_info_##X, sizeof(rsa_digest_info_##X) } - _(md5), - _(sha1), - _(rmd160), - _(sha256), - _(sha384), - _(sha512), - _(sha224), -#undef _ -#define _(X) { "sha3-" #X, rsa_digest_info_sha3_##X, sizeof(rsa_digest_info_sha3_##X) } - _(256), - _(384), - _(512), -#undef _ - { NULL } -}; - -static const struct rsa_asn1_template *rsa_lookup_asn1(const char *name) -{ - const struct rsa_asn1_template *p; - - for (p = rsa_asn1_templates; p->name; p++) - if (strcmp(name, p->name) == 0) - return p; - return NULL; -} - struct pkcs1pad_ctx { struct crypto_akcipher *child; unsigned int key_size; @@ -118,7 +23,6 @@ struct pkcs1pad_ctx { struct pkcs1pad_inst_ctx { struct crypto_akcipher_spawn spawn; - const struct rsa_asn1_template *digest_info; }; struct pkcs1pad_request { @@ -148,9 +52,9 @@ static unsigned int pkcs1pad_get_max_size(struct crypto_akcipher *tfm) struct pkcs1pad_ctx *ctx = akcipher_tfm_ctx(tfm); /* - * The maximum destination buffer size for the encrypt/sign operations + * The maximum destination buffer size for the encrypt operation * will be the same as for RSA, even though it's smaller for - * decrypt/verify. + * decrypt. */ return ctx->key_size; @@ -168,7 +72,7 @@ static void pkcs1pad_sg_set_buf(struct scatterlist *sg, void *buf, size_t len, sg_chain(sg, nsegs, next); } -static int pkcs1pad_encrypt_sign_complete(struct akcipher_request *req, int err) +static int pkcs1pad_encrypt_complete(struct akcipher_request *req, int err) { struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req); struct pkcs1pad_ctx *ctx = akcipher_tfm_ctx(tfm); @@ -207,14 +111,14 @@ static int pkcs1pad_encrypt_sign_complete(struct akcipher_request *req, int err) return err; } -static void pkcs1pad_encrypt_sign_complete_cb(void *data, int err) +static void pkcs1pad_encrypt_complete_cb(void *data, int err) { struct akcipher_request *req = data; if (err == -EINPROGRESS) goto out; - err = pkcs1pad_encrypt_sign_complete(req, err); + err = pkcs1pad_encrypt_complete(req, err); out: akcipher_request_complete(req, err); @@ -255,7 +159,7 @@ static int pkcs1pad_encrypt(struct akcipher_request *req) akcipher_request_set_tfm(&req_ctx->child_req, ctx->child); akcipher_request_set_callback(&req_ctx->child_req, req->base.flags, - pkcs1pad_encrypt_sign_complete_cb, req); + pkcs1pad_encrypt_complete_cb, req); /* Reuse output buffer */ akcipher_request_set_crypt(&req_ctx->child_req, req_ctx->in_sg, @@ -263,7 +167,7 @@ static int pkcs1pad_encrypt(struct akcipher_request *req) err = crypto_akcipher_encrypt(&req_ctx->child_req); if (err != -EINPROGRESS && err != -EBUSY) - return pkcs1pad_encrypt_sign_complete(req, err); + return pkcs1pad_encrypt_complete(req, err); return err; } @@ -368,195 +272,6 @@ static int pkcs1pad_decrypt(struct akcipher_request *req) return err; } -static int pkcs1pad_sign(struct akcipher_request *req) -{ - struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req); - struct pkcs1pad_ctx *ctx = akcipher_tfm_ctx(tfm); - struct pkcs1pad_request *req_ctx = akcipher_request_ctx(req); - struct akcipher_instance *inst = akcipher_alg_instance(tfm); - struct pkcs1pad_inst_ctx *ictx = akcipher_instance_ctx(inst); - const struct rsa_asn1_template *digest_info = ictx->digest_info; - int err; - unsigned int ps_end, digest_info_size = 0; - - if (!ctx->key_size) - return -EINVAL; - - if (digest_info) - digest_info_size = digest_info->size; - - if (req->src_len + digest_info_size > ctx->key_size - 11) - return -EOVERFLOW; - - if (req->dst_len < ctx->key_size) { - req->dst_len = ctx->key_size; - return -EOVERFLOW; - } - - req_ctx->in_buf = kmalloc(ctx->key_size - 1 - req->src_len, - GFP_KERNEL); - if (!req_ctx->in_buf) - return -ENOMEM; - - ps_end = ctx->key_size - digest_info_size - req->src_len - 2; - req_ctx->in_buf[0] = 0x01; - memset(req_ctx->in_buf + 1, 0xff, ps_end - 1); - req_ctx->in_buf[ps_end] = 0x00; - - if (digest_info) - memcpy(req_ctx->in_buf + ps_end + 1, digest_info->data, - digest_info->size); - - pkcs1pad_sg_set_buf(req_ctx->in_sg, req_ctx->in_buf, - ctx->key_size - 1 - req->src_len, req->src); - - akcipher_request_set_tfm(&req_ctx->child_req, ctx->child); - akcipher_request_set_callback(&req_ctx->child_req, req->base.flags, - pkcs1pad_encrypt_sign_complete_cb, req); - - /* Reuse output buffer */ - akcipher_request_set_crypt(&req_ctx->child_req, req_ctx->in_sg, - req->dst, ctx->key_size - 1, req->dst_len); - - err = crypto_akcipher_decrypt(&req_ctx->child_req); - if (err != -EINPROGRESS && err != -EBUSY) - return pkcs1pad_encrypt_sign_complete(req, err); - - return err; -} - -static int pkcs1pad_verify_complete(struct akcipher_request *req, int err) -{ - struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req); - struct pkcs1pad_ctx *ctx = akcipher_tfm_ctx(tfm); - struct pkcs1pad_request *req_ctx = akcipher_request_ctx(req); - struct akcipher_instance *inst = akcipher_alg_instance(tfm); - struct pkcs1pad_inst_ctx *ictx = akcipher_instance_ctx(inst); - const struct rsa_asn1_template *digest_info = ictx->digest_info; - const unsigned int sig_size = req->src_len; - const unsigned int digest_size = req->dst_len; - unsigned int dst_len; - unsigned int pos; - u8 *out_buf; - - if (err) - goto done; - - err = -EINVAL; - dst_len = req_ctx->child_req.dst_len; - if (dst_len < ctx->key_size - 1) - goto done; - - out_buf = req_ctx->out_buf; - if (dst_len == ctx->key_size) { - if (out_buf[0] != 0x00) - /* Decrypted value had no leading 0 byte */ - goto done; - - dst_len--; - out_buf++; - } - - err = -EBADMSG; - if (out_buf[0] != 0x01) - goto done; - - for (pos = 1; pos < dst_len; pos++) - if (out_buf[pos] != 0xff) - break; - - if (pos < 9 || pos == dst_len || out_buf[pos] != 0x00) - goto done; - pos++; - - if (digest_info) { - if (digest_info->size > dst_len - pos) - goto done; - if (crypto_memneq(out_buf + pos, digest_info->data, - digest_info->size)) - goto done; - - pos += digest_info->size; - } - - err = 0; - - if (digest_size != dst_len - pos) { - err = -EKEYREJECTED; - req->dst_len = dst_len - pos; - goto done; - } - /* Extract appended digest. */ - sg_pcopy_to_buffer(req->src, - sg_nents_for_len(req->src, sig_size + digest_size), - req_ctx->out_buf + ctx->key_size, - digest_size, sig_size); - /* Do the actual verification step. */ - if (memcmp(req_ctx->out_buf + ctx->key_size, out_buf + pos, - digest_size) != 0) - err = -EKEYREJECTED; -done: - kfree_sensitive(req_ctx->out_buf); - - return err; -} - -static void pkcs1pad_verify_complete_cb(void *data, int err) -{ - struct akcipher_request *req = data; - - if (err == -EINPROGRESS) - goto out; - - err = pkcs1pad_verify_complete(req, err); - -out: - akcipher_request_complete(req, err); -} - -/* - * The verify operation is here for completeness similar to the verification - * defined in RFC2313 section 10.2 except that block type 0 is not accepted, - * as in RFC2437. RFC2437 section 9.2 doesn't define any operation to - * retrieve the DigestInfo from a signature, instead the user is expected - * to call the sign operation to generate the expected signature and compare - * signatures instead of the message-digests. - */ -static int pkcs1pad_verify(struct akcipher_request *req) -{ - struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req); - struct pkcs1pad_ctx *ctx = akcipher_tfm_ctx(tfm); - struct pkcs1pad_request *req_ctx = akcipher_request_ctx(req); - const unsigned int sig_size = req->src_len; - const unsigned int digest_size = req->dst_len; - int err; - - if (WARN_ON(req->dst) || WARN_ON(!digest_size) || - !ctx->key_size || sig_size != ctx->key_size) - return -EINVAL; - - req_ctx->out_buf = kmalloc(ctx->key_size + digest_size, GFP_KERNEL); - if (!req_ctx->out_buf) - return -ENOMEM; - - pkcs1pad_sg_set_buf(req_ctx->out_sg, req_ctx->out_buf, - ctx->key_size, NULL); - - akcipher_request_set_tfm(&req_ctx->child_req, ctx->child); - akcipher_request_set_callback(&req_ctx->child_req, req->base.flags, - pkcs1pad_verify_complete_cb, req); - - /* Reuse input buffer, output to a new buffer */ - akcipher_request_set_crypt(&req_ctx->child_req, req->src, - req_ctx->out_sg, sig_size, ctx->key_size); - - err = crypto_akcipher_encrypt(&req_ctx->child_req); - if (err != -EINPROGRESS && err != -EBUSY) - return pkcs1pad_verify_complete(req, err); - - return err; -} - static int pkcs1pad_init_tfm(struct crypto_akcipher *tfm) { struct akcipher_instance *inst = akcipher_alg_instance(tfm); @@ -598,7 +313,6 @@ static int pkcs1pad_create(struct crypto_template *tmpl, struct rtattr **tb) struct akcipher_instance *inst; struct pkcs1pad_inst_ctx *ctx; struct akcipher_alg *rsa_alg; - const char *hash_name; int err; err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_AKCIPHER, &mask); @@ -624,36 +338,15 @@ static int pkcs1pad_create(struct crypto_template *tmpl, struct rtattr **tb) } err = -ENAMETOOLONG; - hash_name = crypto_attr_alg_name(tb[2]); - if (IS_ERR(hash_name)) { - if (snprintf(inst->alg.base.cra_name, - CRYPTO_MAX_ALG_NAME, "pkcs1pad(%s)", - rsa_alg->base.cra_name) >= CRYPTO_MAX_ALG_NAME) - goto err_free_inst; - - if (snprintf(inst->alg.base.cra_driver_name, - CRYPTO_MAX_ALG_NAME, "pkcs1pad(%s)", - rsa_alg->base.cra_driver_name) >= - CRYPTO_MAX_ALG_NAME) - goto err_free_inst; - } else { - ctx->digest_info = rsa_lookup_asn1(hash_name); - if (!ctx->digest_info) { - err = -EINVAL; - goto err_free_inst; - } - - if (snprintf(inst->alg.base.cra_name, CRYPTO_MAX_ALG_NAME, - "pkcs1pad(%s,%s)", rsa_alg->base.cra_name, - hash_name) >= CRYPTO_MAX_ALG_NAME) - goto err_free_inst; - - if (snprintf(inst->alg.base.cra_driver_name, - CRYPTO_MAX_ALG_NAME, "pkcs1pad(%s,%s)", - rsa_alg->base.cra_driver_name, - hash_name) >= CRYPTO_MAX_ALG_NAME) - goto err_free_inst; - } + if (snprintf(inst->alg.base.cra_name, + CRYPTO_MAX_ALG_NAME, "pkcs1pad(%s)", + rsa_alg->base.cra_name) >= CRYPTO_MAX_ALG_NAME) + goto err_free_inst; + + if (snprintf(inst->alg.base.cra_driver_name, + CRYPTO_MAX_ALG_NAME, "pkcs1pad(%s)", + rsa_alg->base.cra_driver_name) >= CRYPTO_MAX_ALG_NAME) + goto err_free_inst; inst->alg.base.cra_priority = rsa_alg->base.cra_priority; inst->alg.base.cra_ctxsize = sizeof(struct pkcs1pad_ctx); @@ -663,8 +356,6 @@ static int pkcs1pad_create(struct crypto_template *tmpl, struct rtattr **tb) inst->alg.encrypt = pkcs1pad_encrypt; inst->alg.decrypt = pkcs1pad_decrypt; - inst->alg.sign = pkcs1pad_sign; - inst->alg.verify = pkcs1pad_verify; inst->alg.set_pub_key = pkcs1pad_set_pub_key; inst->alg.set_priv_key = pkcs1pad_set_priv_key; inst->alg.max_size = pkcs1pad_get_max_size; diff --git a/crypto/rsa.c b/crypto/rsa.c index 78b28d14ced397..b7d21529c55229 100644 --- a/crypto/rsa.c +++ b/crypto/rsa.c @@ -407,16 +407,25 @@ static int __init rsa_init(void) return err; err = crypto_register_template(&rsa_pkcs1pad_tmpl); - if (err) { - crypto_unregister_akcipher(&rsa); - return err; - } + if (err) + goto err_unregister_rsa; + + err = crypto_register_template(&rsassa_pkcs1_tmpl); + if (err) + goto err_unregister_rsa_pkcs1pad; return 0; + +err_unregister_rsa_pkcs1pad: + crypto_unregister_template(&rsa_pkcs1pad_tmpl); +err_unregister_rsa: + crypto_unregister_akcipher(&rsa); + return err; } static void __exit rsa_exit(void) { + crypto_unregister_template(&rsassa_pkcs1_tmpl); crypto_unregister_template(&rsa_pkcs1pad_tmpl); crypto_unregister_akcipher(&rsa); } diff --git a/crypto/rsassa-pkcs1.c b/crypto/rsassa-pkcs1.c new file mode 100644 index 00000000000000..779c080fc01363 --- /dev/null +++ b/crypto/rsassa-pkcs1.c @@ -0,0 +1,422 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * RSA Signature Scheme with Appendix - PKCS #1 v1.5 (RFC 8017 sec 8.2) + * + * https://www.rfc-editor.org/rfc/rfc8017#section-8.2 + * + * Copyright (c) 2015 - 2024 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Full Hash Prefix for EMSA-PKCS1-v1_5 encoding method (RFC 9580 table 24) + * + * RSA keys are usually much larger than the hash of the message to be signed. + * The hash is therefore prepended by the Full Hash Prefix and a 0xff padding. + * The Full Hash Prefix is an ASN.1 SEQUENCE containing the hash algorithm OID. + * + * https://www.rfc-editor.org/rfc/rfc9580#table-24 + */ + +static const u8 hash_prefix_md5[] = { + 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, /* SEQUENCE (SEQUENCE (OID */ + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, /* , */ + 0x05, 0x00, 0x04, 0x10 /* NULL), OCTET STRING ) */ +}; + +static const u8 hash_prefix_sha1[] = { + 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, + 0x2b, 0x0e, 0x03, 0x02, 0x1a, + 0x05, 0x00, 0x04, 0x14 +}; + +static const u8 hash_prefix_rmd160[] = { + 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, + 0x2b, 0x24, 0x03, 0x02, 0x01, + 0x05, 0x00, 0x04, 0x14 +}; + +static const u8 hash_prefix_sha224[] = { + 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, + 0x05, 0x00, 0x04, 0x1c +}; + +static const u8 hash_prefix_sha256[] = { + 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, + 0x05, 0x00, 0x04, 0x20 +}; + +static const u8 hash_prefix_sha384[] = { + 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, + 0x05, 0x00, 0x04, 0x30 +}; + +static const u8 hash_prefix_sha512[] = { + 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, + 0x05, 0x00, 0x04, 0x40 +}; + +static const u8 hash_prefix_sha3_256[] = { + 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x08, + 0x05, 0x00, 0x04, 0x20 +}; + +static const u8 hash_prefix_sha3_384[] = { + 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x09, + 0x05, 0x00, 0x04, 0x30 +}; + +static const u8 hash_prefix_sha3_512[] = { + 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0a, + 0x05, 0x00, 0x04, 0x40 +}; + +static const struct hash_prefix { + const char *name; + const u8 *data; + size_t size; +} hash_prefixes[] = { +#define _(X) { #X, hash_prefix_##X, sizeof(hash_prefix_##X) } + _(md5), + _(sha1), + _(rmd160), + _(sha256), + _(sha384), + _(sha512), + _(sha224), +#undef _ +#define _(X) { "sha3-" #X, hash_prefix_sha3_##X, sizeof(hash_prefix_sha3_##X) } + _(256), + _(384), + _(512), +#undef _ + { NULL } +}; + +static const struct hash_prefix *rsassa_pkcs1_find_hash_prefix(const char *name) +{ + const struct hash_prefix *p; + + for (p = hash_prefixes; p->name; p++) + if (strcmp(name, p->name) == 0) + return p; + return NULL; +} + +struct rsassa_pkcs1_ctx { + struct crypto_akcipher *child; + unsigned int key_size; +}; + +struct rsassa_pkcs1_inst_ctx { + struct crypto_akcipher_spawn spawn; + const struct hash_prefix *hash_prefix; +}; + +static int rsassa_pkcs1_sign(struct crypto_sig *tfm, + const void *src, unsigned int slen, + void *dst, unsigned int dlen) +{ + struct sig_instance *inst = sig_alg_instance(tfm); + struct rsassa_pkcs1_inst_ctx *ictx = sig_instance_ctx(inst); + const struct hash_prefix *hash_prefix = ictx->hash_prefix; + struct rsassa_pkcs1_ctx *ctx = crypto_sig_ctx(tfm); + unsigned int child_reqsize = crypto_akcipher_reqsize(ctx->child); + struct akcipher_request *child_req __free(kfree_sensitive) = NULL; + struct scatterlist in_sg[2], out_sg; + struct crypto_wait cwait; + unsigned int pad_len; + unsigned int ps_end; + unsigned int len; + u8 *in_buf; + int err; + + if (!ctx->key_size) + return -EINVAL; + + if (dlen < ctx->key_size) + return -EOVERFLOW; + + if (slen + hash_prefix->size > ctx->key_size - 11) + return -EOVERFLOW; + + child_req = kmalloc(sizeof(*child_req) + child_reqsize + + ctx->key_size - 1 - slen, GFP_KERNEL); + if (!child_req) + return -ENOMEM; + + /* RFC 8017 sec 8.2.1 step 1 - EMSA-PKCS1-v1_5 encoding generation */ + in_buf = (u8 *)(child_req + 1) + child_reqsize; + ps_end = ctx->key_size - hash_prefix->size - slen - 2; + in_buf[0] = 0x01; + memset(in_buf + 1, 0xff, ps_end - 1); + in_buf[ps_end] = 0x00; + memcpy(in_buf + ps_end + 1, hash_prefix->data, hash_prefix->size); + + /* RFC 8017 sec 8.2.1 step 2 - RSA signature */ + crypto_init_wait(&cwait); + sg_init_table(in_sg, 2); + sg_set_buf(&in_sg[0], in_buf, ctx->key_size - 1 - slen); + sg_set_buf(&in_sg[1], src, slen); + sg_init_one(&out_sg, dst, dlen); + akcipher_request_set_tfm(child_req, ctx->child); + akcipher_request_set_crypt(child_req, in_sg, &out_sg, + ctx->key_size - 1, dlen); + akcipher_request_set_callback(child_req, CRYPTO_TFM_REQ_MAY_SLEEP, + crypto_req_done, &cwait); + + err = crypto_akcipher_decrypt(child_req); + err = crypto_wait_req(err, &cwait); + if (err) + return err; + + len = child_req->dst_len; + pad_len = ctx->key_size - len; + + /* Four billion to one */ + if (unlikely(pad_len)) { + memmove(dst + pad_len, dst, len); + memset(dst, 0, pad_len); + } + + return 0; +} + +static int rsassa_pkcs1_verify(struct crypto_sig *tfm, + const void *src, unsigned int slen, + const void *digest, unsigned int dlen) +{ + struct sig_instance *inst = sig_alg_instance(tfm); + struct rsassa_pkcs1_inst_ctx *ictx = sig_instance_ctx(inst); + const struct hash_prefix *hash_prefix = ictx->hash_prefix; + struct rsassa_pkcs1_ctx *ctx = crypto_sig_ctx(tfm); + unsigned int child_reqsize = crypto_akcipher_reqsize(ctx->child); + struct akcipher_request *child_req __free(kfree_sensitive) = NULL; + struct scatterlist in_sg, out_sg; + struct crypto_wait cwait; + unsigned int dst_len; + unsigned int pos; + u8 *out_buf; + int err; + + /* RFC 8017 sec 8.2.2 step 1 - length checking */ + if (!ctx->key_size || + slen != ctx->key_size || + !dlen) + return -EINVAL; + + /* RFC 8017 sec 8.2.2 step 2 - RSA verification */ + child_req = kmalloc(sizeof(*child_req) + child_reqsize + ctx->key_size, + GFP_KERNEL); + if (!child_req) + return -ENOMEM; + + out_buf = (u8 *)(child_req + 1) + child_reqsize; + + crypto_init_wait(&cwait); + sg_init_one(&in_sg, src, slen); + sg_init_one(&out_sg, out_buf, ctx->key_size); + akcipher_request_set_tfm(child_req, ctx->child); + akcipher_request_set_crypt(child_req, &in_sg, &out_sg, + slen, ctx->key_size); + akcipher_request_set_callback(child_req, CRYPTO_TFM_REQ_MAY_SLEEP, + crypto_req_done, &cwait); + + err = crypto_akcipher_encrypt(child_req); + err = crypto_wait_req(err, &cwait); + if (err) + return err; + + /* RFC 8017 sec 8.2.2 step 3 - EMSA-PKCS1-v1_5 encoding verification */ + dst_len = child_req->dst_len; + if (dst_len < ctx->key_size - 1) + return -EINVAL; + + if (dst_len == ctx->key_size) { + if (out_buf[0] != 0x00) + /* Encrypted value had no leading 0 byte */ + return -EINVAL; + + dst_len--; + out_buf++; + } + + if (out_buf[0] != 0x01) + return -EBADMSG; + + for (pos = 1; pos < dst_len; pos++) + if (out_buf[pos] != 0xff) + break; + + if (pos < 9 || pos == dst_len || out_buf[pos] != 0x00) + return -EBADMSG; + pos++; + + if (hash_prefix->size > dst_len - pos) + return -EBADMSG; + if (crypto_memneq(out_buf + pos, hash_prefix->data, hash_prefix->size)) + return -EBADMSG; + pos += hash_prefix->size; + + /* RFC 8017 sec 8.2.2 step 4 - comparison of digest with out_buf */ + if (dlen != dst_len - pos) + return -EKEYREJECTED; + if (memcmp(digest, out_buf + pos, dlen) != 0) + return -EKEYREJECTED; + + return 0; +} + +static unsigned int rsassa_pkcs1_max_size(struct crypto_sig *tfm) +{ + struct rsassa_pkcs1_ctx *ctx = crypto_sig_ctx(tfm); + + return ctx->key_size; +} + +static int rsassa_pkcs1_set_pub_key(struct crypto_sig *tfm, + const void *key, unsigned int keylen) +{ + struct rsassa_pkcs1_ctx *ctx = crypto_sig_ctx(tfm); + + return rsa_set_key(ctx->child, &ctx->key_size, RSA_PUB, key, keylen); +} + +static int rsassa_pkcs1_set_priv_key(struct crypto_sig *tfm, + const void *key, unsigned int keylen) +{ + struct rsassa_pkcs1_ctx *ctx = crypto_sig_ctx(tfm); + + return rsa_set_key(ctx->child, &ctx->key_size, RSA_PRIV, key, keylen); +} + +static int rsassa_pkcs1_init_tfm(struct crypto_sig *tfm) +{ + struct sig_instance *inst = sig_alg_instance(tfm); + struct rsassa_pkcs1_inst_ctx *ictx = sig_instance_ctx(inst); + struct rsassa_pkcs1_ctx *ctx = crypto_sig_ctx(tfm); + struct crypto_akcipher *child_tfm; + + child_tfm = crypto_spawn_akcipher(&ictx->spawn); + if (IS_ERR(child_tfm)) + return PTR_ERR(child_tfm); + + ctx->child = child_tfm; + + return 0; +} + +static void rsassa_pkcs1_exit_tfm(struct crypto_sig *tfm) +{ + struct rsassa_pkcs1_ctx *ctx = crypto_sig_ctx(tfm); + + crypto_free_akcipher(ctx->child); +} + +static void rsassa_pkcs1_free(struct sig_instance *inst) +{ + struct rsassa_pkcs1_inst_ctx *ctx = sig_instance_ctx(inst); + struct crypto_akcipher_spawn *spawn = &ctx->spawn; + + crypto_drop_akcipher(spawn); + kfree(inst); +} + +static int rsassa_pkcs1_create(struct crypto_template *tmpl, struct rtattr **tb) +{ + struct rsassa_pkcs1_inst_ctx *ctx; + struct akcipher_alg *rsa_alg; + struct sig_instance *inst; + const char *hash_name; + u32 mask; + int err; + + err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_SIG, &mask); + if (err) + return err; + + inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + ctx = sig_instance_ctx(inst); + + err = crypto_grab_akcipher(&ctx->spawn, sig_crypto_instance(inst), + crypto_attr_alg_name(tb[1]), 0, mask); + if (err) + goto err_free_inst; + + rsa_alg = crypto_spawn_akcipher_alg(&ctx->spawn); + + if (strcmp(rsa_alg->base.cra_name, "rsa") != 0) { + err = -EINVAL; + goto err_free_inst; + } + + hash_name = crypto_attr_alg_name(tb[2]); + if (IS_ERR(hash_name)) { + err = PTR_ERR(hash_name); + goto err_free_inst; + } + + ctx->hash_prefix = rsassa_pkcs1_find_hash_prefix(hash_name); + if (!ctx->hash_prefix) { + err = -EINVAL; + goto err_free_inst; + } + + err = -ENAMETOOLONG; + if (snprintf(inst->alg.base.cra_name, CRYPTO_MAX_ALG_NAME, + "pkcs1(%s,%s)", rsa_alg->base.cra_name, + hash_name) >= CRYPTO_MAX_ALG_NAME) + goto err_free_inst; + + if (snprintf(inst->alg.base.cra_driver_name, CRYPTO_MAX_ALG_NAME, + "pkcs1(%s,%s)", rsa_alg->base.cra_driver_name, + hash_name) >= CRYPTO_MAX_ALG_NAME) + goto err_free_inst; + + inst->alg.base.cra_priority = rsa_alg->base.cra_priority; + inst->alg.base.cra_ctxsize = sizeof(struct rsassa_pkcs1_ctx); + + inst->alg.init = rsassa_pkcs1_init_tfm; + inst->alg.exit = rsassa_pkcs1_exit_tfm; + + inst->alg.sign = rsassa_pkcs1_sign; + inst->alg.verify = rsassa_pkcs1_verify; + inst->alg.max_size = rsassa_pkcs1_max_size; + inst->alg.set_pub_key = rsassa_pkcs1_set_pub_key; + inst->alg.set_priv_key = rsassa_pkcs1_set_priv_key; + + inst->free = rsassa_pkcs1_free; + + err = sig_register_instance(tmpl, inst); + if (err) { +err_free_inst: + rsassa_pkcs1_free(inst); + } + return err; +} + +struct crypto_template rsassa_pkcs1_tmpl = { + .name = "pkcs1", + .create = rsassa_pkcs1_create, + .module = THIS_MODULE, +}; + +MODULE_ALIAS_CRYPTO("pkcs1"); diff --git a/crypto/testmgr.c b/crypto/testmgr.c index ed971d857057b6..76401b3b634f83 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -5569,34 +5569,38 @@ static const struct alg_test_desc alg_test_descs[] = { .cipher = __VECS(fcrypt_pcbc_tv_template) } }, { - .alg = "pkcs1pad(rsa,sha224)", + .alg = "pkcs1(rsa,sha224)", .test = alg_test_null, .fips_allowed = 1, }, { - .alg = "pkcs1pad(rsa,sha256)", - .test = alg_test_akcipher, + .alg = "pkcs1(rsa,sha256)", + .test = alg_test_sig, .fips_allowed = 1, .suite = { - .akcipher = __VECS(pkcs1pad_rsa_tv_template) + .sig = __VECS(pkcs1_rsa_tv_template) } }, { - .alg = "pkcs1pad(rsa,sha3-256)", + .alg = "pkcs1(rsa,sha3-256)", + .test = alg_test_null, + .fips_allowed = 1, + }, { + .alg = "pkcs1(rsa,sha3-384)", .test = alg_test_null, .fips_allowed = 1, }, { - .alg = "pkcs1pad(rsa,sha3-384)", + .alg = "pkcs1(rsa,sha3-512)", .test = alg_test_null, .fips_allowed = 1, }, { - .alg = "pkcs1pad(rsa,sha3-512)", + .alg = "pkcs1(rsa,sha384)", .test = alg_test_null, .fips_allowed = 1, }, { - .alg = "pkcs1pad(rsa,sha384)", + .alg = "pkcs1(rsa,sha512)", .test = alg_test_null, .fips_allowed = 1, }, { - .alg = "pkcs1pad(rsa,sha512)", + .alg = "pkcs1pad(rsa)", .test = alg_test_null, .fips_allowed = 1, }, { diff --git a/crypto/testmgr.h b/crypto/testmgr.h index fd4823c26d93eb..d29d03fec85222 100644 --- a/crypto/testmgr.h +++ b/crypto/testmgr.h @@ -1268,7 +1268,7 @@ static const struct sig_testvec ecrdsa_tv_template[] = { /* * PKCS#1 RSA test vectors. Obtained from CAVS testing. */ -static const struct akcipher_testvec pkcs1pad_rsa_tv_template[] = { +static const struct sig_testvec pkcs1_rsa_tv_template[] = { { .key = "\x30\x82\x04\xa5\x02\x01\x00\x02\x82\x01\x01\x00\xd7\x1e\x77\x82" @@ -1380,7 +1380,6 @@ static const struct akcipher_testvec pkcs1pad_rsa_tv_template[] = { "\xda\x62\x8d\xe1\x2a\x71\x91\x43\x40\x61\x3c\x5a\xbe\x86\xfc\x5b" "\xe6\xf9\xa9\x16\x31\x1f\xaf\x25\x6d\xc2\x4a\x23\x6e\x63\x02\xa2", .c_size = 256, - .siggen_sigver_test = true, } }; diff --git a/include/crypto/internal/rsa.h b/include/crypto/internal/rsa.h index 754f687134df74..071a1951b992c1 100644 --- a/include/crypto/internal/rsa.h +++ b/include/crypto/internal/rsa.h @@ -82,4 +82,5 @@ static inline int rsa_set_key(struct crypto_akcipher *child, } extern struct crypto_template rsa_pkcs1pad_tmpl; +extern struct crypto_template rsassa_pkcs1_tmpl; #endif diff --git a/include/linux/slab.h b/include/linux/slab.h index b35e2db7eb0ecc..0268ea7abf8b9f 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -448,6 +448,7 @@ void kfree_sensitive(const void *objp); size_t __ksize(const void *objp); DEFINE_FREE(kfree, void *, if (!IS_ERR_OR_NULL(_T)) kfree(_T)) +DEFINE_FREE(kfree_sensitive, void *, if (_T) kfree_sensitive(_T)) /** * ksize - Report actual allocation size of associated object diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 06132cf47016da..34ea02addb70b2 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -1114,7 +1114,7 @@ EXPORT_SYMBOL_GPL(ima_measure_critical_data); #ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS /** - * ima_kernel_module_request - Prevent crypto-pkcs1pad(rsa,*) requests + * ima_kernel_module_request - Prevent crypto-pkcs1(rsa,*) requests * @kmod_name: kernel module name * * Avoid a verification loop where verifying the signature of the modprobe @@ -1128,7 +1128,7 @@ EXPORT_SYMBOL_GPL(ima_measure_critical_data); * algorithm on the fly, but crypto_larval_lookup() will try to use alg_name * in order to load a kernel module with same name. * - * Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules, + * Since we don't have any real "crypto-pkcs1(rsa,*)" kernel modules, * we are safe to fail such module request from crypto_larval_lookup(), and * avoid the verification loop. * @@ -1136,7 +1136,7 @@ EXPORT_SYMBOL_GPL(ima_measure_critical_data); */ static int ima_kernel_module_request(char *kmod_name) { - if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0) + if (strncmp(kmod_name, "crypto-pkcs1(rsa,", 17) == 0) return -EINVAL; return 0; From 5ed9dc63531598239c161f1a678042b96d20eb6b Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Tue, 20 Aug 2024 09:56:36 +0200 Subject: [PATCH 08/47] crypto: rsassa-pkcs1 - Harden digest length verification The RSASSA-PKCS1-v1_5 sign operation currently only checks that the digest length is less than "key_size - hash_prefix->size - 11". The verify operation merely checks that it's more than zero. Actually the precise digest length is known because the hash algorithm is specified upon instance creation and the digest length is encoded into the final byte of the hash algorithm's Full Hash Prefix. So check for the exact digest length rather than solely relying on imprecise maximum/minimum checks. Keep the maximum length check for the sign operation as a safety net, but drop the now unnecessary minimum check for the verify operation. Signed-off-by: Lukas Wunner --- crypto/rsassa-pkcs1.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/crypto/rsassa-pkcs1.c b/crypto/rsassa-pkcs1.c index 779c080fc01363..8f42a571280633 100644 --- a/crypto/rsassa-pkcs1.c +++ b/crypto/rsassa-pkcs1.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -118,6 +119,20 @@ static const struct hash_prefix *rsassa_pkcs1_find_hash_prefix(const char *name) return NULL; } +static unsigned int rsassa_pkcs1_hash_len(const struct hash_prefix *p) +{ + /* + * The final byte of the Full Hash Prefix encodes the hash length. + * + * This needs to be revisited should hash algorithms with more than + * 1016 bits (127 bytes * 8) ever be added. The length would then + * be encoded into more than one byte by ASN.1. + */ + static_assert(HASH_MAX_DIGESTSIZE <= 127); + + return p->data[p->size - 1]; +} + struct rsassa_pkcs1_ctx { struct crypto_akcipher *child; unsigned int key_size; @@ -152,6 +167,9 @@ static int rsassa_pkcs1_sign(struct crypto_sig *tfm, if (dlen < ctx->key_size) return -EOVERFLOW; + if (slen != rsassa_pkcs1_hash_len(hash_prefix)) + return -EINVAL; + if (slen + hash_prefix->size > ctx->key_size - 11) return -EOVERFLOW; @@ -217,7 +235,7 @@ static int rsassa_pkcs1_verify(struct crypto_sig *tfm, /* RFC 8017 sec 8.2.2 step 1 - length checking */ if (!ctx->key_size || slen != ctx->key_size || - !dlen) + dlen != rsassa_pkcs1_hash_len(hash_prefix)) return -EINVAL; /* RFC 8017 sec 8.2.2 step 2 - RSA verification */ From 2439812a6c5af02fcc0f6203ddad5cb6034fedd9 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Tue, 20 Aug 2024 10:44:19 +0200 Subject: [PATCH 09/47] crypto: rsassa-pkcs1 - Avoid copying hash prefix When constructing the EMSA-PKCS1-v1_5 padding for the sign operation, a buffer for the padding is allocated and the Full Hash Prefix is copied into it. The padding is then passed to the RSA decrypt operation as an sglist entry which is succeeded by a second sglist entry for the hash. Actually copying the hash prefix around is completely unnecessary. It can simply be referenced from a third sglist entry which sits in-between the padding and the digest. Signed-off-by: Lukas Wunner --- crypto/rsassa-pkcs1.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/crypto/rsassa-pkcs1.c b/crypto/rsassa-pkcs1.c index 8f42a571280633..b291ec0944a262 100644 --- a/crypto/rsassa-pkcs1.c +++ b/crypto/rsassa-pkcs1.c @@ -153,7 +153,7 @@ static int rsassa_pkcs1_sign(struct crypto_sig *tfm, struct rsassa_pkcs1_ctx *ctx = crypto_sig_ctx(tfm); unsigned int child_reqsize = crypto_akcipher_reqsize(ctx->child); struct akcipher_request *child_req __free(kfree_sensitive) = NULL; - struct scatterlist in_sg[2], out_sg; + struct scatterlist in_sg[3], out_sg; struct crypto_wait cwait; unsigned int pad_len; unsigned int ps_end; @@ -173,24 +173,26 @@ static int rsassa_pkcs1_sign(struct crypto_sig *tfm, if (slen + hash_prefix->size > ctx->key_size - 11) return -EOVERFLOW; - child_req = kmalloc(sizeof(*child_req) + child_reqsize + - ctx->key_size - 1 - slen, GFP_KERNEL); + pad_len = ctx->key_size - slen - hash_prefix->size - 1; + + child_req = kmalloc(sizeof(*child_req) + child_reqsize + pad_len, + GFP_KERNEL); if (!child_req) return -ENOMEM; /* RFC 8017 sec 8.2.1 step 1 - EMSA-PKCS1-v1_5 encoding generation */ in_buf = (u8 *)(child_req + 1) + child_reqsize; - ps_end = ctx->key_size - hash_prefix->size - slen - 2; + ps_end = pad_len - 1; in_buf[0] = 0x01; memset(in_buf + 1, 0xff, ps_end - 1); in_buf[ps_end] = 0x00; - memcpy(in_buf + ps_end + 1, hash_prefix->data, hash_prefix->size); /* RFC 8017 sec 8.2.1 step 2 - RSA signature */ crypto_init_wait(&cwait); - sg_init_table(in_sg, 2); - sg_set_buf(&in_sg[0], in_buf, ctx->key_size - 1 - slen); - sg_set_buf(&in_sg[1], src, slen); + sg_init_table(in_sg, 3); + sg_set_buf(&in_sg[0], in_buf, pad_len); + sg_set_buf(&in_sg[1], hash_prefix->data, hash_prefix->size); + sg_set_buf(&in_sg[2], src, slen); sg_init_one(&out_sg, dst, dlen); akcipher_request_set_tfm(child_req, ctx->child); akcipher_request_set_crypt(child_req, in_sg, &out_sg, From 80b902e9fc3f82ae1adbfca59201af2a890ef08e Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Fri, 6 Sep 2024 10:20:38 +0200 Subject: [PATCH 10/47] crypto: virtio - Drop sign/verify operations The virtio crypto driver exposes akcipher sign/verify operations in a user space ABI. This blocks removal of sign/verify from akcipher_alg. Herbert opines: "I would say that this is something that we can break. Breaking it is no different to running virtio on a host that does not support these algorithms. After all, a software implementation must always be present. I deliberately left akcipher out of crypto_user because the API is still in flux. We should not let virtio constrain ourselves." https://lore.kernel.org/all/ZtqoNAgcnXnrYhZZ@gondor.apana.org.au/ "I would remove virtio akcipher support in its entirety. This API was never meant to be exposed outside of the kernel." https://lore.kernel.org/all/Ztqql_gqgZiMW8zz@gondor.apana.org.au/ Drop sign/verify support from virtio crypto. There's no strong reason to also remove encrypt/decrypt support, so keep it. A key selling point of virtio crypto is to allow guest access to crypto accelerators on the host. So far the only akcipher algorithm supported by virtio crypto is RSA. Dropping sign/verify merely means that the PKCS#1 padding is now always generated or verified inside the guest, but the actual signature generation/verification (which is an RSA decrypt/encrypt operation) may still use an accelerator on the host. Generating or verifying the PKCS#1 padding is cheap, so a hardware accelerator won't be of much help there. Which begs the question whether virtio crypto support for sign/verify makes sense at all. It would make sense for the sign operation if the host has a security chip to store asymmetric private keys. But the kernel doesn't even have an asymmetric_key_subtype yet for hardware-based private keys. There's at least one rudimentary driver for such chips (atmel-ecc.c for ATECC508A), but it doesn't implement the sign operation. The kernel would first have to grow support for a hardware asymmetric_key_subtype and at least one driver implementing the sign operation before exposure to guests via virtio makes sense. Signed-off-by: Lukas Wunner --- .../virtio/virtio_crypto_akcipher_algs.c | 65 ++++++------------- include/uapi/linux/virtio_crypto.h | 1 + 2 files changed, 22 insertions(+), 44 deletions(-) diff --git a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c index cb92b7fa99c6f8..d3fa0fed54ff47 100644 --- a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c +++ b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c @@ -83,23 +83,16 @@ static void virtio_crypto_dataq_akcipher_callback(struct virtio_crypto_request * case VIRTIO_CRYPTO_BADMSG: error = -EBADMSG; break; - - case VIRTIO_CRYPTO_KEY_REJECTED: - error = -EKEYREJECTED; - break; - default: error = -EIO; break; } akcipher_req = vc_akcipher_req->akcipher_req; - if (vc_akcipher_req->opcode != VIRTIO_CRYPTO_AKCIPHER_VERIFY) { - /* actuall length maybe less than dst buffer */ - akcipher_req->dst_len = len - sizeof(vc_req->status); - sg_copy_from_buffer(akcipher_req->dst, sg_nents(akcipher_req->dst), - vc_akcipher_req->dst_buf, akcipher_req->dst_len); - } + /* actual length may be less than dst buffer */ + akcipher_req->dst_len = len - sizeof(vc_req->status); + sg_copy_from_buffer(akcipher_req->dst, sg_nents(akcipher_req->dst), + vc_akcipher_req->dst_buf, akcipher_req->dst_len); virtio_crypto_akcipher_finalize_req(vc_akcipher_req, akcipher_req, error); } @@ -230,36 +223,27 @@ static int __virtio_crypto_akcipher_do_req(struct virtio_crypto_akcipher_request int node = dev_to_node(&vcrypto->vdev->dev); unsigned long flags; int ret; - bool verify = vc_akcipher_req->opcode == VIRTIO_CRYPTO_AKCIPHER_VERIFY; - unsigned int src_len = verify ? req->src_len + req->dst_len : req->src_len; /* out header */ sg_init_one(&outhdr_sg, req_data, sizeof(*req_data)); sgs[num_out++] = &outhdr_sg; /* src data */ - src_buf = kcalloc_node(src_len, 1, GFP_KERNEL, node); + src_buf = kcalloc_node(req->src_len, 1, GFP_KERNEL, node); if (!src_buf) return -ENOMEM; - if (verify) { - /* for verify operation, both src and dst data work as OUT direction */ - sg_copy_to_buffer(req->src, sg_nents(req->src), src_buf, src_len); - sg_init_one(&srcdata_sg, src_buf, src_len); - sgs[num_out++] = &srcdata_sg; - } else { - sg_copy_to_buffer(req->src, sg_nents(req->src), src_buf, src_len); - sg_init_one(&srcdata_sg, src_buf, src_len); - sgs[num_out++] = &srcdata_sg; + sg_copy_to_buffer(req->src, sg_nents(req->src), src_buf, req->src_len); + sg_init_one(&srcdata_sg, src_buf, req->src_len); + sgs[num_out++] = &srcdata_sg; - /* dst data */ - dst_buf = kcalloc_node(req->dst_len, 1, GFP_KERNEL, node); - if (!dst_buf) - goto free_src; + /* dst data */ + dst_buf = kcalloc_node(req->dst_len, 1, GFP_KERNEL, node); + if (!dst_buf) + goto free_src; - sg_init_one(&dstdata_sg, dst_buf, req->dst_len); - sgs[num_out + num_in++] = &dstdata_sg; - } + sg_init_one(&dstdata_sg, dst_buf, req->dst_len); + sgs[num_out + num_in++] = &dstdata_sg; vc_akcipher_req->src_buf = src_buf; vc_akcipher_req->dst_buf = dst_buf; @@ -352,16 +336,6 @@ static int virtio_crypto_rsa_decrypt(struct akcipher_request *req) return virtio_crypto_rsa_req(req, VIRTIO_CRYPTO_AKCIPHER_DECRYPT); } -static int virtio_crypto_rsa_sign(struct akcipher_request *req) -{ - return virtio_crypto_rsa_req(req, VIRTIO_CRYPTO_AKCIPHER_SIGN); -} - -static int virtio_crypto_rsa_verify(struct akcipher_request *req) -{ - return virtio_crypto_rsa_req(req, VIRTIO_CRYPTO_AKCIPHER_VERIFY); -} - static int virtio_crypto_rsa_set_key(struct crypto_akcipher *tfm, const void *key, unsigned int keylen, @@ -524,16 +498,19 @@ static struct virtio_crypto_akcipher_algo virtio_crypto_akcipher_algs[] = { .algo.base = { .encrypt = virtio_crypto_rsa_encrypt, .decrypt = virtio_crypto_rsa_decrypt, - .sign = virtio_crypto_rsa_sign, - .verify = virtio_crypto_rsa_verify, + /* + * Must specify an arbitrary hash algorithm upon + * set_{pub,priv}_key (even though it's not used + * by encrypt/decrypt) because qemu checks for it. + */ .set_pub_key = virtio_crypto_p1pad_rsa_sha1_set_pub_key, .set_priv_key = virtio_crypto_p1pad_rsa_sha1_set_priv_key, .max_size = virtio_crypto_rsa_max_size, .init = virtio_crypto_rsa_init_tfm, .exit = virtio_crypto_rsa_exit_tfm, .base = { - .cra_name = "pkcs1pad(rsa,sha1)", - .cra_driver_name = "virtio-pkcs1-rsa-with-sha1", + .cra_name = "pkcs1pad(rsa)", + .cra_driver_name = "virtio-pkcs1-rsa", .cra_priority = 150, .cra_module = THIS_MODULE, .cra_ctxsize = sizeof(struct virtio_crypto_akcipher_ctx), diff --git a/include/uapi/linux/virtio_crypto.h b/include/uapi/linux/virtio_crypto.h index 71a54a6849ca9f..2fccb64c9d6bfd 100644 --- a/include/uapi/linux/virtio_crypto.h +++ b/include/uapi/linux/virtio_crypto.h @@ -329,6 +329,7 @@ struct virtio_crypto_op_header { VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x00) #define VIRTIO_CRYPTO_AKCIPHER_DECRYPT \ VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x01) + /* akcipher sign/verify opcodes are deprecated */ #define VIRTIO_CRYPTO_AKCIPHER_SIGN \ VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AKCIPHER, 0x02) #define VIRTIO_CRYPTO_AKCIPHER_VERIFY \ From 70a157db28fd2e4519f94b4756f54eac58a2332c Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 19 Aug 2024 08:25:35 +0200 Subject: [PATCH 11/47] crypto: drivers - Drop sign/verify operations The drivers aspeed-acry.c, hpre_crypto.c and jh7110-rsa.c purport to implement sign/verify operations for raw (unpadded) "rsa". But there is no such thing as message digests generally need to be padded according to a predefined scheme (such as PSS or PKCS#1) to match the size of the usually much larger RSA keys. The bogus sign/verify operations defined by these drivers are never called but block removal of sign/verify from akcipher_alg. Drop them. Signed-off-by: Lukas Wunner --- drivers/crypto/aspeed/aspeed-acry.c | 2 -- drivers/crypto/hisilicon/hpre/hpre_crypto.c | 2 -- drivers/crypto/starfive/jh7110-rsa.c | 2 -- 3 files changed, 6 deletions(-) diff --git a/drivers/crypto/aspeed/aspeed-acry.c b/drivers/crypto/aspeed/aspeed-acry.c index b4613bd4ad9643..7a1e153733e1b1 100644 --- a/drivers/crypto/aspeed/aspeed-acry.c +++ b/drivers/crypto/aspeed/aspeed-acry.c @@ -601,8 +601,6 @@ static struct aspeed_acry_alg aspeed_acry_akcipher_algs[] = { .akcipher.base = { .encrypt = aspeed_acry_rsa_enc, .decrypt = aspeed_acry_rsa_dec, - .sign = aspeed_acry_rsa_dec, - .verify = aspeed_acry_rsa_enc, .set_pub_key = aspeed_acry_rsa_set_pub_key, .set_priv_key = aspeed_acry_rsa_set_priv_key, .max_size = aspeed_acry_rsa_max_size, diff --git a/drivers/crypto/hisilicon/hpre/hpre_crypto.c b/drivers/crypto/hisilicon/hpre/hpre_crypto.c index c167dbd6c7d623..2a29102612108f 100644 --- a/drivers/crypto/hisilicon/hpre/hpre_crypto.c +++ b/drivers/crypto/hisilicon/hpre/hpre_crypto.c @@ -2006,8 +2006,6 @@ static void hpre_curve25519_exit_tfm(struct crypto_kpp *tfm) } static struct akcipher_alg rsa = { - .sign = hpre_rsa_dec, - .verify = hpre_rsa_enc, .encrypt = hpre_rsa_enc, .decrypt = hpre_rsa_dec, .set_pub_key = hpre_rsa_setpubkey, diff --git a/drivers/crypto/starfive/jh7110-rsa.c b/drivers/crypto/starfive/jh7110-rsa.c index a778c48460253f..d109c743f076d6 100644 --- a/drivers/crypto/starfive/jh7110-rsa.c +++ b/drivers/crypto/starfive/jh7110-rsa.c @@ -565,8 +565,6 @@ static void starfive_rsa_exit_tfm(struct crypto_akcipher *tfm) static struct akcipher_alg starfive_rsa = { .encrypt = starfive_rsa_enc, .decrypt = starfive_rsa_dec, - .sign = starfive_rsa_dec, - .verify = starfive_rsa_enc, .set_pub_key = starfive_rsa_set_pub_key, .set_priv_key = starfive_rsa_set_priv_key, .max_size = starfive_rsa_max_size, From e3631c5c11ec6da92234275def482a0487968aef Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 19 Aug 2024 08:45:05 +0200 Subject: [PATCH 12/47] crypto: akcipher - Drop sign/verify operations A sig_alg backend has just been introduced and all asymmetric sign/verify algorithms have been migrated to it. The sign/verify operations can thus be dropped from akcipher_alg. It is now purely for asymmetric encrypt/decrypt. Move struct crypto_akcipher_sync_data from internal.h to akcipher.c and unexport crypto_akcipher_sync_{prep,post}(): They're no longer used by sig.c but only locally in akcipher.c. In crypto_akcipher_sync_{prep,post}(), drop various NULL pointer checks for data->dst as they were only necessary for the verify operation. In the crypto_sig_*() API calls, remove the forks that were necessary while algorithms were converted from crypto_akcipher to crypto_sig one by one. In struct akcipher_testvec, remove the "params", "param_len" and "algo" elements as they were only needed for the ecrdsa verify operation. Remove corresponding dead code from test_akcipher_one() as well. Signed-off-by: Lukas Wunner --- Documentation/crypto/api-akcipher.rst | 2 +- crypto/akcipher.c | 64 ++++---------- crypto/internal.h | 19 ---- crypto/sig.c | 70 --------------- crypto/testmgr.c | 119 ++++++++------------------ crypto/testmgr.h | 4 - include/crypto/akcipher.h | 69 ++------------- include/crypto/internal/akcipher.h | 4 +- 8 files changed, 66 insertions(+), 285 deletions(-) diff --git a/Documentation/crypto/api-akcipher.rst b/Documentation/crypto/api-akcipher.rst index 40aa8746e2a1f2..6f47cc70eca032 100644 --- a/Documentation/crypto/api-akcipher.rst +++ b/Documentation/crypto/api-akcipher.rst @@ -11,7 +11,7 @@ Asymmetric Cipher API :doc: Generic Public Key API .. kernel-doc:: include/crypto/akcipher.h - :functions: crypto_alloc_akcipher crypto_free_akcipher crypto_akcipher_set_pub_key crypto_akcipher_set_priv_key crypto_akcipher_maxsize crypto_akcipher_encrypt crypto_akcipher_decrypt crypto_akcipher_sign crypto_akcipher_verify + :functions: crypto_alloc_akcipher crypto_free_akcipher crypto_akcipher_set_pub_key crypto_akcipher_set_priv_key crypto_akcipher_maxsize crypto_akcipher_encrypt crypto_akcipher_decrypt Asymmetric Cipher Request Handle -------------------------------- diff --git a/crypto/akcipher.c b/crypto/akcipher.c index e0ff5f4dda6d62..72c82d9aa07789 100644 --- a/crypto/akcipher.c +++ b/crypto/akcipher.c @@ -20,6 +20,19 @@ #define CRYPTO_ALG_TYPE_AHASH_MASK 0x0000000e +struct crypto_akcipher_sync_data { + struct crypto_akcipher *tfm; + const void *src; + void *dst; + unsigned int slen; + unsigned int dlen; + + struct akcipher_request *req; + struct crypto_wait cwait; + struct scatterlist sg; + u8 *buf; +}; + static int __maybe_unused crypto_akcipher_report( struct sk_buff *skb, struct crypto_alg *alg) { @@ -126,10 +139,6 @@ int crypto_register_akcipher(struct akcipher_alg *alg) { struct crypto_alg *base = &alg->base; - if (!alg->sign) - alg->sign = akcipher_default_op; - if (!alg->verify) - alg->verify = akcipher_default_op; if (!alg->encrypt) alg->encrypt = akcipher_default_op; if (!alg->decrypt) @@ -158,7 +167,7 @@ int akcipher_register_instance(struct crypto_template *tmpl, } EXPORT_SYMBOL_GPL(akcipher_register_instance); -int crypto_akcipher_sync_prep(struct crypto_akcipher_sync_data *data) +static int crypto_akcipher_sync_prep(struct crypto_akcipher_sync_data *data) { unsigned int reqsize = crypto_akcipher_reqsize(data->tfm); struct akcipher_request *req; @@ -167,10 +176,7 @@ int crypto_akcipher_sync_prep(struct crypto_akcipher_sync_data *data) unsigned int len; u8 *buf; - if (data->dst) - mlen = max(data->slen, data->dlen); - else - mlen = data->slen + data->dlen; + mlen = max(data->slen, data->dlen); len = sizeof(*req) + reqsize + mlen; if (len < mlen) @@ -189,8 +195,7 @@ int crypto_akcipher_sync_prep(struct crypto_akcipher_sync_data *data) sg = &data->sg; sg_init_one(sg, buf, mlen); - akcipher_request_set_crypt(req, sg, data->dst ? sg : NULL, - data->slen, data->dlen); + akcipher_request_set_crypt(req, sg, sg, data->slen, data->dlen); crypto_init_wait(&data->cwait); akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, @@ -198,18 +203,16 @@ int crypto_akcipher_sync_prep(struct crypto_akcipher_sync_data *data) return 0; } -EXPORT_SYMBOL_GPL(crypto_akcipher_sync_prep); -int crypto_akcipher_sync_post(struct crypto_akcipher_sync_data *data, int err) +static int crypto_akcipher_sync_post(struct crypto_akcipher_sync_data *data, + int err) { err = crypto_wait_req(err, &data->cwait); - if (data->dst) - memcpy(data->dst, data->buf, data->dlen); + memcpy(data->dst, data->buf, data->dlen); data->dlen = data->req->dst_len; kfree_sensitive(data->req); return err; } -EXPORT_SYMBOL_GPL(crypto_akcipher_sync_post); int crypto_akcipher_sync_encrypt(struct crypto_akcipher *tfm, const void *src, unsigned int slen, @@ -248,34 +251,5 @@ int crypto_akcipher_sync_decrypt(struct crypto_akcipher *tfm, } EXPORT_SYMBOL_GPL(crypto_akcipher_sync_decrypt); -static void crypto_exit_akcipher_ops_sig(struct crypto_tfm *tfm) -{ - struct crypto_akcipher **ctx = crypto_tfm_ctx(tfm); - - crypto_free_akcipher(*ctx); -} - -int crypto_init_akcipher_ops_sig(struct crypto_tfm *tfm) -{ - struct crypto_akcipher **ctx = crypto_tfm_ctx(tfm); - struct crypto_alg *calg = tfm->__crt_alg; - struct crypto_akcipher *akcipher; - - if (!crypto_mod_get(calg)) - return -EAGAIN; - - akcipher = crypto_create_tfm(calg, &crypto_akcipher_type); - if (IS_ERR(akcipher)) { - crypto_mod_put(calg); - return PTR_ERR(akcipher); - } - - *ctx = akcipher; - tfm->exit = crypto_exit_akcipher_ops_sig; - - return 0; -} -EXPORT_SYMBOL_GPL(crypto_init_akcipher_ops_sig); - MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Generic public key cipher type"); diff --git a/crypto/internal.h b/crypto/internal.h index 711a6a5bfa2b56..46b661be0f90fe 100644 --- a/crypto/internal.h +++ b/crypto/internal.h @@ -22,8 +22,6 @@ #include #include -struct akcipher_request; -struct crypto_akcipher; struct crypto_instance; struct crypto_template; @@ -35,19 +33,6 @@ struct crypto_larval { bool test_started; }; -struct crypto_akcipher_sync_data { - struct crypto_akcipher *tfm; - const void *src; - void *dst; - unsigned int slen; - unsigned int dlen; - - struct akcipher_request *req; - struct crypto_wait cwait; - struct scatterlist sg; - u8 *buf; -}; - enum { CRYPTOA_UNSPEC, CRYPTOA_ALG, @@ -129,10 +114,6 @@ void *crypto_create_tfm_node(struct crypto_alg *alg, void *crypto_clone_tfm(const struct crypto_type *frontend, struct crypto_tfm *otfm); -int crypto_akcipher_sync_prep(struct crypto_akcipher_sync_data *data); -int crypto_akcipher_sync_post(struct crypto_akcipher_sync_data *data, int err); -int crypto_init_akcipher_ops_sig(struct crypto_tfm *tfm); - static inline void *crypto_create_tfm(struct crypto_alg *alg, const struct crypto_type *frontend) { diff --git a/crypto/sig.c b/crypto/sig.c index 4f36ceb7a90be5..1e6b0d6774729b 100644 --- a/crypto/sig.c +++ b/crypto/sig.c @@ -5,12 +5,10 @@ * Copyright (c) 2023 Herbert Xu */ -#include #include #include #include #include -#include #include #include #include @@ -19,8 +17,6 @@ #define CRYPTO_ALG_TYPE_SIG_MASK 0x0000000e -static const struct crypto_type crypto_sig_type; - static void crypto_sig_exit_tfm(struct crypto_tfm *tfm) { struct crypto_sig *sig = __crypto_sig_tfm(tfm); @@ -31,9 +27,6 @@ static void crypto_sig_exit_tfm(struct crypto_tfm *tfm) static int crypto_sig_init_tfm(struct crypto_tfm *tfm) { - if (tfm->__crt_alg->cra_type != &crypto_sig_type) - return crypto_init_akcipher_ops_sig(tfm); - struct crypto_sig *sig = __crypto_sig_tfm(tfm); struct sig_alg *alg = crypto_sig_alg(sig); @@ -93,17 +86,9 @@ EXPORT_SYMBOL_GPL(crypto_alloc_sig); int crypto_sig_maxsize(struct crypto_sig *tfm) { - if (crypto_sig_tfm(tfm)->__crt_alg->cra_type != &crypto_sig_type) - goto akcipher; - struct sig_alg *alg = crypto_sig_alg(tfm); return alg->max_size(tfm); - -akcipher: - struct crypto_akcipher **ctx = crypto_sig_ctx(tfm); - - return crypto_akcipher_maxsize(*ctx); } EXPORT_SYMBOL_GPL(crypto_sig_maxsize); @@ -111,26 +96,9 @@ int crypto_sig_sign(struct crypto_sig *tfm, const void *src, unsigned int slen, void *dst, unsigned int dlen) { - if (crypto_sig_tfm(tfm)->__crt_alg->cra_type != &crypto_sig_type) - goto akcipher; - struct sig_alg *alg = crypto_sig_alg(tfm); return alg->sign(tfm, src, slen, dst, dlen); - -akcipher: - struct crypto_akcipher **ctx = crypto_sig_ctx(tfm); - struct crypto_akcipher_sync_data data = { - .tfm = *ctx, - .src = src, - .dst = dst, - .slen = slen, - .dlen = dlen, - }; - - return crypto_akcipher_sync_prep(&data) ?: - crypto_akcipher_sync_post(&data, - crypto_akcipher_sign(data.req)); } EXPORT_SYMBOL_GPL(crypto_sig_sign); @@ -138,65 +106,27 @@ int crypto_sig_verify(struct crypto_sig *tfm, const void *src, unsigned int slen, const void *digest, unsigned int dlen) { - if (crypto_sig_tfm(tfm)->__crt_alg->cra_type != &crypto_sig_type) - goto akcipher; - struct sig_alg *alg = crypto_sig_alg(tfm); return alg->verify(tfm, src, slen, digest, dlen); - -akcipher: - struct crypto_akcipher **ctx = crypto_sig_ctx(tfm); - struct crypto_akcipher_sync_data data = { - .tfm = *ctx, - .src = src, - .slen = slen, - .dlen = dlen, - }; - int err; - - err = crypto_akcipher_sync_prep(&data); - if (err) - return err; - - memcpy(data.buf + slen, digest, dlen); - - return crypto_akcipher_sync_post(&data, - crypto_akcipher_verify(data.req)); } EXPORT_SYMBOL_GPL(crypto_sig_verify); int crypto_sig_set_pubkey(struct crypto_sig *tfm, const void *key, unsigned int keylen) { - if (crypto_sig_tfm(tfm)->__crt_alg->cra_type != &crypto_sig_type) - goto akcipher; - struct sig_alg *alg = crypto_sig_alg(tfm); return alg->set_pub_key(tfm, key, keylen); - -akcipher: - struct crypto_akcipher **ctx = crypto_sig_ctx(tfm); - - return crypto_akcipher_set_pub_key(*ctx, key, keylen); } EXPORT_SYMBOL_GPL(crypto_sig_set_pubkey); int crypto_sig_set_privkey(struct crypto_sig *tfm, const void *key, unsigned int keylen) { - if (crypto_sig_tfm(tfm)->__crt_alg->cra_type != &crypto_sig_type) - goto akcipher; - struct sig_alg *alg = crypto_sig_alg(tfm); return alg->set_priv_key(tfm, key, keylen); - -akcipher: - struct crypto_akcipher **ctx = crypto_sig_ctx(tfm); - - return crypto_akcipher_set_priv_key(*ctx, key, keylen); } EXPORT_SYMBOL_GPL(crypto_sig_set_privkey); diff --git a/crypto/testmgr.c b/crypto/testmgr.c index 76401b3b634f83..48de2fc1e9659d 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -4131,11 +4131,9 @@ static int test_akcipher_one(struct crypto_akcipher *tfm, struct crypto_wait wait; unsigned int out_len_max, out_len = 0; int err = -ENOMEM; - struct scatterlist src, dst, src_tab[3]; - const char *m, *c; - unsigned int m_size, c_size; - const char *op; - u8 *key, *ptr; + struct scatterlist src, dst, src_tab[2]; + const char *c; + unsigned int c_size; if (testmgr_alloc_buf(xbuf)) return err; @@ -4146,92 +4144,53 @@ static int test_akcipher_one(struct crypto_akcipher *tfm, crypto_init_wait(&wait); - key = kmalloc(vecs->key_len + sizeof(u32) * 2 + vecs->param_len, - GFP_KERNEL); - if (!key) - goto free_req; - memcpy(key, vecs->key, vecs->key_len); - ptr = key + vecs->key_len; - ptr = test_pack_u32(ptr, vecs->algo); - ptr = test_pack_u32(ptr, vecs->param_len); - memcpy(ptr, vecs->params, vecs->param_len); - if (vecs->public_key_vec) - err = crypto_akcipher_set_pub_key(tfm, key, vecs->key_len); + err = crypto_akcipher_set_pub_key(tfm, vecs->key, + vecs->key_len); else - err = crypto_akcipher_set_priv_key(tfm, key, vecs->key_len); + err = crypto_akcipher_set_priv_key(tfm, vecs->key, + vecs->key_len); if (err) - goto free_key; + goto free_req; - /* - * First run test which do not require a private key, such as - * encrypt or verify. - */ + /* First run encrypt test which does not require a private key */ err = -ENOMEM; out_len_max = crypto_akcipher_maxsize(tfm); outbuf_enc = kzalloc(out_len_max, GFP_KERNEL); if (!outbuf_enc) - goto free_key; - - if (!vecs->siggen_sigver_test) { - m = vecs->m; - m_size = vecs->m_size; - c = vecs->c; - c_size = vecs->c_size; - op = "encrypt"; - } else { - /* Swap args so we could keep plaintext (digest) - * in vecs->m, and cooked signature in vecs->c. - */ - m = vecs->c; /* signature */ - m_size = vecs->c_size; - c = vecs->m; /* digest */ - c_size = vecs->m_size; - op = "verify"; - } + goto free_req; + + c = vecs->c; + c_size = vecs->c_size; err = -E2BIG; - if (WARN_ON(m_size > PAGE_SIZE)) + if (WARN_ON(vecs->m_size > PAGE_SIZE)) goto free_all; - memcpy(xbuf[0], m, m_size); + memcpy(xbuf[0], vecs->m, vecs->m_size); - sg_init_table(src_tab, 3); + sg_init_table(src_tab, 2); sg_set_buf(&src_tab[0], xbuf[0], 8); - sg_set_buf(&src_tab[1], xbuf[0] + 8, m_size - 8); - if (vecs->siggen_sigver_test) { - if (WARN_ON(c_size > PAGE_SIZE)) - goto free_all; - memcpy(xbuf[1], c, c_size); - sg_set_buf(&src_tab[2], xbuf[1], c_size); - akcipher_request_set_crypt(req, src_tab, NULL, m_size, c_size); - } else { - sg_init_one(&dst, outbuf_enc, out_len_max); - akcipher_request_set_crypt(req, src_tab, &dst, m_size, - out_len_max); - } + sg_set_buf(&src_tab[1], xbuf[0] + 8, vecs->m_size - 8); + sg_init_one(&dst, outbuf_enc, out_len_max); + akcipher_request_set_crypt(req, src_tab, &dst, vecs->m_size, + out_len_max); akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, crypto_req_done, &wait); - err = crypto_wait_req(vecs->siggen_sigver_test ? - /* Run asymmetric signature verification */ - crypto_akcipher_verify(req) : - /* Run asymmetric encrypt */ - crypto_akcipher_encrypt(req), &wait); + err = crypto_wait_req(crypto_akcipher_encrypt(req), &wait); if (err) { - pr_err("alg: akcipher: %s test failed. err %d\n", op, err); + pr_err("alg: akcipher: encrypt test failed. err %d\n", err); goto free_all; } - if (!vecs->siggen_sigver_test && c) { + if (c) { if (req->dst_len != c_size) { - pr_err("alg: akcipher: %s test failed. Invalid output len\n", - op); + pr_err("alg: akcipher: encrypt test failed. Invalid output len\n"); err = -EINVAL; goto free_all; } /* verify that encrypted message is equal to expected */ if (memcmp(c, outbuf_enc, c_size) != 0) { - pr_err("alg: akcipher: %s test failed. Invalid output\n", - op); + pr_err("alg: akcipher: encrypt test failed. Invalid output\n"); hexdump(outbuf_enc, c_size); err = -EINVAL; goto free_all; @@ -4239,7 +4198,7 @@ static int test_akcipher_one(struct crypto_akcipher *tfm, } /* - * Don't invoke (decrypt or sign) test which require a private key + * Don't invoke decrypt test which requires a private key * for vectors with only a public key. */ if (vecs->public_key_vec) { @@ -4252,13 +4211,12 @@ static int test_akcipher_one(struct crypto_akcipher *tfm, goto free_all; } - if (!vecs->siggen_sigver_test && !c) { + if (!c) { c = outbuf_enc; c_size = req->dst_len; } err = -E2BIG; - op = vecs->siggen_sigver_test ? "sign" : "decrypt"; if (WARN_ON(c_size > PAGE_SIZE)) goto free_all; memcpy(xbuf[0], c, c_size); @@ -4268,34 +4226,29 @@ static int test_akcipher_one(struct crypto_akcipher *tfm, crypto_init_wait(&wait); akcipher_request_set_crypt(req, &src, &dst, c_size, out_len_max); - err = crypto_wait_req(vecs->siggen_sigver_test ? - /* Run asymmetric signature generation */ - crypto_akcipher_sign(req) : - /* Run asymmetric decrypt */ - crypto_akcipher_decrypt(req), &wait); + err = crypto_wait_req(crypto_akcipher_decrypt(req), &wait); if (err) { - pr_err("alg: akcipher: %s test failed. err %d\n", op, err); + pr_err("alg: akcipher: decrypt test failed. err %d\n", err); goto free_all; } out_len = req->dst_len; - if (out_len < m_size) { - pr_err("alg: akcipher: %s test failed. Invalid output len %u\n", - op, out_len); + if (out_len < vecs->m_size) { + pr_err("alg: akcipher: decrypt test failed. Invalid output len %u\n", + out_len); err = -EINVAL; goto free_all; } /* verify that decrypted message is equal to the original msg */ - if (memchr_inv(outbuf_dec, 0, out_len - m_size) || - memcmp(m, outbuf_dec + out_len - m_size, m_size)) { - pr_err("alg: akcipher: %s test failed. Invalid output\n", op); + if (memchr_inv(outbuf_dec, 0, out_len - vecs->m_size) || + memcmp(vecs->m, outbuf_dec + out_len - vecs->m_size, + vecs->m_size)) { + pr_err("alg: akcipher: decrypt test failed. Invalid output\n"); hexdump(outbuf_dec, out_len); err = -EINVAL; } free_all: kfree(outbuf_dec); kfree(outbuf_enc); -free_key: - kfree(key); free_req: akcipher_request_free(req); free_xbuf: diff --git a/crypto/testmgr.h b/crypto/testmgr.h index d29d03fec85222..e10b6d7f2cd9f8 100644 --- a/crypto/testmgr.h +++ b/crypto/testmgr.h @@ -150,16 +150,12 @@ struct drbg_testvec { struct akcipher_testvec { const unsigned char *key; - const unsigned char *params; const unsigned char *m; const unsigned char *c; unsigned int key_len; - unsigned int param_len; unsigned int m_size; unsigned int c_size; bool public_key_vec; - bool siggen_sigver_test; - enum OID algo; }; struct sig_testvec { diff --git a/include/crypto/akcipher.h b/include/crypto/akcipher.h index 18a10cad07aaaf..cdf7da74bf2f0b 100644 --- a/include/crypto/akcipher.h +++ b/include/crypto/akcipher.h @@ -12,24 +12,19 @@ #include /** - * struct akcipher_request - public key request + * struct akcipher_request - public key cipher request * * @base: Common attributes for async crypto requests * @src: Source data - * For verify op this is signature + digest, in that case - * total size of @src is @src_len + @dst_len. - * @dst: Destination data (Should be NULL for verify op) + * @dst: Destination data * @src_len: Size of the input buffer - * For verify op it's size of signature part of @src, this part - * is supposed to be operated by cipher. - * @dst_len: Size of @dst buffer (for all ops except verify). + * @dst_len: Size of @dst buffer * It needs to be at least as big as the expected result * depending on the operation. * After operation it will be updated with the actual size of the * result. * In case of error where the dst sgl size was insufficient, * it will be updated to the size required for the operation. - * For verify op this is size of digest part in @src. * @__ctx: Start of private context data */ struct akcipher_request { @@ -55,15 +50,8 @@ struct crypto_akcipher { }; /** - * struct akcipher_alg - generic public key algorithm + * struct akcipher_alg - generic public key cipher algorithm * - * @sign: Function performs a sign operation as defined by public key - * algorithm. In case of error, where the dst_len was insufficient, - * the req->dst_len will be updated to the size required for the - * operation - * @verify: Function performs a complete verify operation as defined by - * public key algorithm, returning verification status. Requires - * digest value as input parameter. * @encrypt: Function performs an encrypt operation as defined by public key * algorithm. In case of error, where the dst_len was insufficient, * the req->dst_len will be updated to the size required for the @@ -94,8 +82,6 @@ struct crypto_akcipher { * @base: Common crypto API algorithm data structure */ struct akcipher_alg { - int (*sign)(struct akcipher_request *req); - int (*verify)(struct akcipher_request *req); int (*encrypt)(struct akcipher_request *req); int (*decrypt)(struct akcipher_request *req); int (*set_pub_key)(struct crypto_akcipher *tfm, const void *key, @@ -110,9 +96,9 @@ struct akcipher_alg { }; /** - * DOC: Generic Public Key API + * DOC: Generic Public Key Cipher API * - * The Public Key API is used with the algorithms of type + * The Public Key Cipher API is used with the algorithms of type * CRYPTO_ALG_TYPE_AKCIPHER (listed as type "akcipher" in /proc/crypto) */ @@ -243,10 +229,9 @@ static inline void akcipher_request_set_callback(struct akcipher_request *req, * * @req: public key request * @src: ptr to input scatter list - * @dst: ptr to output scatter list or NULL for verify op + * @dst: ptr to output scatter list * @src_len: size of the src input scatter list to be processed - * @dst_len: size of the dst output scatter list or size of signature - * portion in @src for verify op + * @dst_len: size of the dst output scatter list */ static inline void akcipher_request_set_crypt(struct akcipher_request *req, struct scatterlist *src, @@ -347,44 +332,6 @@ int crypto_akcipher_sync_decrypt(struct crypto_akcipher *tfm, const void *src, unsigned int slen, void *dst, unsigned int dlen); -/** - * crypto_akcipher_sign() - Invoke public key sign operation - * - * Function invokes the specific public key sign operation for a given - * public key algorithm - * - * @req: asymmetric key request - * - * Return: zero on success; error code in case of error - */ -static inline int crypto_akcipher_sign(struct akcipher_request *req) -{ - struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req); - - return crypto_akcipher_alg(tfm)->sign(req); -} - -/** - * crypto_akcipher_verify() - Invoke public key signature verification - * - * Function invokes the specific public key signature verification operation - * for a given public key algorithm. - * - * @req: asymmetric key request - * - * Note: req->dst should be NULL, req->src should point to SG of size - * (req->src_size + req->dst_size), containing signature (of req->src_size - * length) with appended digest (of req->dst_size length). - * - * Return: zero on verification success; error code in case of error. - */ -static inline int crypto_akcipher_verify(struct akcipher_request *req) -{ - struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req); - - return crypto_akcipher_alg(tfm)->verify(req); -} - /** * crypto_akcipher_set_pub_key() - Invoke set public key operation * diff --git a/include/crypto/internal/akcipher.h b/include/crypto/internal/akcipher.h index a0fba4b2eccfa0..14ee62bc52b6cd 100644 --- a/include/crypto/internal/akcipher.h +++ b/include/crypto/internal/akcipher.h @@ -124,7 +124,7 @@ static inline struct akcipher_alg *crypto_spawn_akcipher_alg( /** * crypto_register_akcipher() -- Register public key algorithm * - * Function registers an implementation of a public key verify algorithm + * Function registers an implementation of a public key cipher algorithm * * @alg: algorithm definition * @@ -135,7 +135,7 @@ int crypto_register_akcipher(struct akcipher_alg *alg); /** * crypto_unregister_akcipher() -- Unregister public key algorithm * - * Function unregisters an implementation of a public key verify algorithm + * Function unregisters an implementation of a public key cipher algorithm * * @alg: algorithm definition */ From 347ab3a820ab9bf6cdf017f8d5d8f3da8810d642 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Thu, 22 Aug 2024 09:30:48 +0200 Subject: [PATCH 13/47] crypto: sig - Move crypto_sig_*() API calls to include file The crypto_sig_*() API calls lived in sig.c so far because they needed access to struct crypto_sig_type: This was necessary to differentiate between signature algorithms that had already been migrated from crypto_akcipher to crypto_sig and those that hadn't yet. Now that all algorithms have been migrated, the API calls can become static inlines in to mimic what is doing. Signed-off-by: Lukas Wunner Reviewed-by: Stefan Berger --- crypto/sig.c | 46 ------------------------------------------- include/crypto/sig.h | 47 +++++++++++++++++++++++++++++++++----------- 2 files changed, 36 insertions(+), 57 deletions(-) diff --git a/crypto/sig.c b/crypto/sig.c index 1e6b0d6774729b..84d0ea9fd73bb9 100644 --- a/crypto/sig.c +++ b/crypto/sig.c @@ -84,52 +84,6 @@ struct crypto_sig *crypto_alloc_sig(const char *alg_name, u32 type, u32 mask) } EXPORT_SYMBOL_GPL(crypto_alloc_sig); -int crypto_sig_maxsize(struct crypto_sig *tfm) -{ - struct sig_alg *alg = crypto_sig_alg(tfm); - - return alg->max_size(tfm); -} -EXPORT_SYMBOL_GPL(crypto_sig_maxsize); - -int crypto_sig_sign(struct crypto_sig *tfm, - const void *src, unsigned int slen, - void *dst, unsigned int dlen) -{ - struct sig_alg *alg = crypto_sig_alg(tfm); - - return alg->sign(tfm, src, slen, dst, dlen); -} -EXPORT_SYMBOL_GPL(crypto_sig_sign); - -int crypto_sig_verify(struct crypto_sig *tfm, - const void *src, unsigned int slen, - const void *digest, unsigned int dlen) -{ - struct sig_alg *alg = crypto_sig_alg(tfm); - - return alg->verify(tfm, src, slen, digest, dlen); -} -EXPORT_SYMBOL_GPL(crypto_sig_verify); - -int crypto_sig_set_pubkey(struct crypto_sig *tfm, - const void *key, unsigned int keylen) -{ - struct sig_alg *alg = crypto_sig_alg(tfm); - - return alg->set_pub_key(tfm, key, keylen); -} -EXPORT_SYMBOL_GPL(crypto_sig_set_pubkey); - -int crypto_sig_set_privkey(struct crypto_sig *tfm, - const void *key, unsigned int keylen) -{ - struct sig_alg *alg = crypto_sig_alg(tfm); - - return alg->set_priv_key(tfm, key, keylen); -} -EXPORT_SYMBOL_GPL(crypto_sig_set_privkey); - static void sig_prepare_alg(struct sig_alg *alg) { struct crypto_alg *base = &alg->base; diff --git a/include/crypto/sig.h b/include/crypto/sig.h index f0f52a7c5ae759..bbc902642bf5ac 100644 --- a/include/crypto/sig.h +++ b/include/crypto/sig.h @@ -130,7 +130,12 @@ static inline void crypto_free_sig(struct crypto_sig *tfm) * * @tfm: signature tfm handle allocated with crypto_alloc_sig() */ -int crypto_sig_maxsize(struct crypto_sig *tfm); +static inline int crypto_sig_maxsize(struct crypto_sig *tfm) +{ + struct sig_alg *alg = crypto_sig_alg(tfm); + + return alg->max_size(tfm); +} /** * crypto_sig_sign() - Invoke signing operation @@ -145,9 +150,14 @@ int crypto_sig_maxsize(struct crypto_sig *tfm); * * Return: zero on success; error code in case of error */ -int crypto_sig_sign(struct crypto_sig *tfm, - const void *src, unsigned int slen, - void *dst, unsigned int dlen); +static inline int crypto_sig_sign(struct crypto_sig *tfm, + const void *src, unsigned int slen, + void *dst, unsigned int dlen) +{ + struct sig_alg *alg = crypto_sig_alg(tfm); + + return alg->sign(tfm, src, slen, dst, dlen); +} /** * crypto_sig_verify() - Invoke signature verification @@ -163,9 +173,14 @@ int crypto_sig_sign(struct crypto_sig *tfm, * * Return: zero on verification success; error code in case of error. */ -int crypto_sig_verify(struct crypto_sig *tfm, - const void *src, unsigned int slen, - const void *digest, unsigned int dlen); +static inline int crypto_sig_verify(struct crypto_sig *tfm, + const void *src, unsigned int slen, + const void *digest, unsigned int dlen) +{ + struct sig_alg *alg = crypto_sig_alg(tfm); + + return alg->verify(tfm, src, slen, digest, dlen); +} /** * crypto_sig_set_pubkey() - Invoke set public key operation @@ -180,8 +195,13 @@ int crypto_sig_verify(struct crypto_sig *tfm, * * Return: zero on success; error code in case of error */ -int crypto_sig_set_pubkey(struct crypto_sig *tfm, - const void *key, unsigned int keylen); +static inline int crypto_sig_set_pubkey(struct crypto_sig *tfm, + const void *key, unsigned int keylen) +{ + struct sig_alg *alg = crypto_sig_alg(tfm); + + return alg->set_pub_key(tfm, key, keylen); +} /** * crypto_sig_set_privkey() - Invoke set private key operation @@ -196,6 +216,11 @@ int crypto_sig_set_pubkey(struct crypto_sig *tfm, * * Return: zero on success; error code in case of error */ -int crypto_sig_set_privkey(struct crypto_sig *tfm, - const void *key, unsigned int keylen); +static inline int crypto_sig_set_privkey(struct crypto_sig *tfm, + const void *key, unsigned int keylen) +{ + struct sig_alg *alg = crypto_sig_alg(tfm); + + return alg->set_priv_key(tfm, key, keylen); +} #endif From 4a1348513c080f0c6b30ef33f0d2da506696f906 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sat, 27 Jul 2024 12:06:47 +0200 Subject: [PATCH 14/47] ASN.1: Clean up include statements in public headers If is the first header included from a .c file (due to headers being sorted alphabetically), the compiler complains: include/linux/asn1_decoder.h:18:29: error: unknown type name 'size_t' Avoid by including . Jonathan notes that the counterpart already includes , but additionally includes the unnecessary . Drop it. Signed-off-by: Lukas Wunner Reviewed-by: Stefan Berger Reviewed-by: Jonathan Cameron --- include/linux/asn1_decoder.h | 1 + include/linux/asn1_encoder.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/asn1_decoder.h b/include/linux/asn1_decoder.h index 83f9c6e1e5e960..b41bce82a19111 100644 --- a/include/linux/asn1_decoder.h +++ b/include/linux/asn1_decoder.h @@ -9,6 +9,7 @@ #define _LINUX_ASN1_DECODER_H #include +#include struct asn1_decoder; diff --git a/include/linux/asn1_encoder.h b/include/linux/asn1_encoder.h index 08cd0c2ad34f0e..d17484dffb745b 100644 --- a/include/linux/asn1_encoder.h +++ b/include/linux/asn1_encoder.h @@ -6,7 +6,6 @@ #include #include #include -#include #define asn1_oid_len(oid) (sizeof(oid)/sizeof(u32)) unsigned char * From 5f9a30c2df19c7ef99ed1d6aee34407f53a88ad4 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sun, 21 Jul 2024 16:10:21 +0200 Subject: [PATCH 15/47] crypto: ecdsa - Avoid signed integer overflow on signature decoding When extracting a signature component r or s from an ASN.1-encoded integer, ecdsa_get_signature_rs() subtracts the expected length "bufsize" from the ASN.1 length "vlen" (both of unsigned type size_t) and stores the result in "diff" (of signed type ssize_t). This results in a signed integer overflow if vlen > SSIZE_MAX + bufsize. The kernel is compiled with -fno-strict-overflow, which implies -fwrapv, meaning signed integer overflow is not undefined behavior. And the function does check for overflow: if (-diff >= bufsize) return -EINVAL; So the code is fine in principle but not very obvious. In the future it might trigger a false-positive with CONFIG_UBSAN_SIGNED_WRAP=y. Avoid by comparing the two unsigned variables directly and erroring out if "vlen" is too large. Signed-off-by: Lukas Wunner Reviewed-by: Stefan Berger Reviewed-by: Jonathan Cameron --- crypto/ecdsa.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/crypto/ecdsa.c b/crypto/ecdsa.c index 3b9873f56b0a64..4a0ca93c99eaaa 100644 --- a/crypto/ecdsa.c +++ b/crypto/ecdsa.c @@ -35,29 +35,24 @@ static int ecdsa_get_signature_rs(u64 *dest, size_t hdrlen, unsigned char tag, const void *value, size_t vlen, unsigned int ndigits) { size_t bufsize = ndigits * sizeof(u64); - ssize_t diff = vlen - bufsize; const char *d = value; - if (!value || !vlen) + if (!value || !vlen || vlen > bufsize + 1) return -EINVAL; - /* diff = 0: 'value' has exacly the right size - * diff > 0: 'value' has too many bytes; one leading zero is allowed that - * makes the value a positive integer; error on more - * diff < 0: 'value' is missing leading zeros + /* + * vlen may be 1 byte larger than bufsize due to a leading zero byte + * (necessary if the most significant bit of the integer is set). */ - if (diff > 0) { + if (vlen > bufsize) { /* skip over leading zeros that make 'value' a positive int */ if (*d == 0) { vlen -= 1; - diff--; d++; - } - if (diff) + } else { return -EINVAL; + } } - if (-diff >= bufsize) - return -EINVAL; ecc_digits_from_bytes(d, vlen, dest, ndigits); From 1647fefe697666e665551b586abce60cf12a5773 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 22 Jul 2024 16:49:25 +0200 Subject: [PATCH 16/47] crypto: ecdsa - Move X9.62 signature decoding into template Unlike the rsa driver, which separates signature decoding and signature verification into two steps, the ecdsa driver does both in one. This restricts users to the one signature format currently supported (X9.62) and prevents addition of others such as P1363, which is needed by the forthcoming SPDM library (Security Protocol and Data Model) for PCI device authentication. Per Herbert's suggestion, change ecdsa to use a "raw" signature encoding and then implement X9.62 and P1363 as templates which convert their respective encodings to the raw one. One may then specify "x962(ecdsa-nist-XXX)" or "p1363(ecdsa-nist-XXX)" to pick the encoding. The present commit moves X9.62 decoding to a template. A separate commit is going to introduce another template for P1363 decoding. The ecdsa driver internally represents a signature as two u64 arrays of size ECC_MAX_BYTES. This appears to be the most natural choice for the raw format as it can directly be used for verification without having to further decode signature data or copy it around. Repurpose all the existing test vectors for "x962(ecdsa-nist-XXX)" and create a duplicate of them to test the raw encoding. Link: https://lore.kernel.org/all/ZoHXyGwRzVvYkcTP@gondor.apana.org.au/ Tested-by: Stefan Berger Signed-off-by: Lukas Wunner --- crypto/Makefile | 3 +- crypto/asymmetric_keys/public_key.c | 3 + crypto/ecdsa-x962.c | 201 ++++++++ crypto/ecdsa.c | 79 +--- crypto/testmgr.c | 27 ++ crypto/testmgr.h | 699 +++++++++++++++++++++++++++- include/crypto/internal/ecc.h | 13 + 7 files changed, 955 insertions(+), 70 deletions(-) create mode 100644 crypto/ecdsa-x962.c diff --git a/crypto/Makefile b/crypto/Makefile index 7de29bf843e94c..af43a1bd1cfa48 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -52,8 +52,9 @@ rsa_generic-y += rsassa-pkcs1.o obj-$(CONFIG_CRYPTO_RSA) += rsa_generic.o $(obj)/ecdsasignature.asn1.o: $(obj)/ecdsasignature.asn1.c $(obj)/ecdsasignature.asn1.h -$(obj)/ecdsa.o: $(obj)/ecdsasignature.asn1.h +$(obj)/ecdsa-x962.o: $(obj)/ecdsasignature.asn1.h ecdsa_generic-y += ecdsa.o +ecdsa_generic-y += ecdsa-x962.o ecdsa_generic-y += ecdsasignature.asn1.o obj-$(CONFIG_CRYPTO_ECDSA) += ecdsa_generic.o diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c index 3fb27ecd65f660..cc6d48cafa2b3d 100644 --- a/crypto/asymmetric_keys/public_key.c +++ b/crypto/asymmetric_keys/public_key.c @@ -130,6 +130,9 @@ software_key_determine_akcipher(const struct public_key *pkey, strcmp(hash_algo, "sha3-384") != 0 && strcmp(hash_algo, "sha3-512") != 0) return -EINVAL; + n = snprintf(alg_name, CRYPTO_MAX_ALG_NAME, "%s(%s)", + encoding, pkey->pkey_algo); + return n >= CRYPTO_MAX_ALG_NAME ? -EINVAL : 0; } else if (strcmp(pkey->pkey_algo, "ecrdsa") == 0) { if (strcmp(encoding, "raw") != 0) return -EINVAL; diff --git a/crypto/ecdsa-x962.c b/crypto/ecdsa-x962.c new file mode 100644 index 00000000000000..022e654c075d59 --- /dev/null +++ b/crypto/ecdsa-x962.c @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ECDSA X9.62 signature encoding + * + * Copyright (c) 2021 IBM Corporation + * Copyright (c) 2024 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ecdsasignature.asn1.h" + +struct ecdsa_x962_ctx { + struct crypto_sig *child; +}; + +struct ecdsa_x962_signature_ctx { + struct ecdsa_raw_sig sig; + unsigned int ndigits; +}; + +/* Get the r and s components of a signature from the X.509 certificate. */ +static int ecdsa_get_signature_rs(u64 *dest, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen, + unsigned int ndigits) +{ + size_t bufsize = ndigits * sizeof(u64); + const char *d = value; + + if (!value || !vlen || vlen > bufsize + 1) + return -EINVAL; + + /* + * vlen may be 1 byte larger than bufsize due to a leading zero byte + * (necessary if the most significant bit of the integer is set). + */ + if (vlen > bufsize) { + /* skip over leading zeros that make 'value' a positive int */ + if (*d == 0) { + vlen -= 1; + d++; + } else { + return -EINVAL; + } + } + + ecc_digits_from_bytes(d, vlen, dest, ndigits); + + return 0; +} + +int ecdsa_get_signature_r(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct ecdsa_x962_signature_ctx *sig_ctx = context; + + return ecdsa_get_signature_rs(sig_ctx->sig.r, hdrlen, tag, value, vlen, + sig_ctx->ndigits); +} + +int ecdsa_get_signature_s(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct ecdsa_x962_signature_ctx *sig_ctx = context; + + return ecdsa_get_signature_rs(sig_ctx->sig.s, hdrlen, tag, value, vlen, + sig_ctx->ndigits); +} + +static int ecdsa_x962_verify(struct crypto_sig *tfm, + const void *src, unsigned int slen, + const void *digest, unsigned int dlen) +{ + struct ecdsa_x962_ctx *ctx = crypto_sig_ctx(tfm); + struct ecdsa_x962_signature_ctx sig_ctx; + int err; + + sig_ctx.ndigits = DIV_ROUND_UP(crypto_sig_maxsize(ctx->child), + sizeof(u64)); + + err = asn1_ber_decoder(&ecdsasignature_decoder, &sig_ctx, src, slen); + if (err < 0) + return err; + + return crypto_sig_verify(ctx->child, &sig_ctx.sig, sizeof(sig_ctx.sig), + digest, dlen); +} + +static unsigned int ecdsa_x962_max_size(struct crypto_sig *tfm) +{ + struct ecdsa_x962_ctx *ctx = crypto_sig_ctx(tfm); + + return crypto_sig_maxsize(ctx->child); +} + +static int ecdsa_x962_set_pub_key(struct crypto_sig *tfm, + const void *key, unsigned int keylen) +{ + struct ecdsa_x962_ctx *ctx = crypto_sig_ctx(tfm); + + return crypto_sig_set_pubkey(ctx->child, key, keylen); +} + +static int ecdsa_x962_init_tfm(struct crypto_sig *tfm) +{ + struct sig_instance *inst = sig_alg_instance(tfm); + struct crypto_sig_spawn *spawn = sig_instance_ctx(inst); + struct ecdsa_x962_ctx *ctx = crypto_sig_ctx(tfm); + struct crypto_sig *child_tfm; + + child_tfm = crypto_spawn_sig(spawn); + if (IS_ERR(child_tfm)) + return PTR_ERR(child_tfm); + + ctx->child = child_tfm; + + return 0; +} + +static void ecdsa_x962_exit_tfm(struct crypto_sig *tfm) +{ + struct ecdsa_x962_ctx *ctx = crypto_sig_ctx(tfm); + + crypto_free_sig(ctx->child); +} + +static void ecdsa_x962_free(struct sig_instance *inst) +{ + struct crypto_sig_spawn *spawn = sig_instance_ctx(inst); + + crypto_drop_sig(spawn); + kfree(inst); +} + +static int ecdsa_x962_create(struct crypto_template *tmpl, struct rtattr **tb) +{ + struct crypto_sig_spawn *spawn; + struct sig_instance *inst; + struct sig_alg *ecdsa_alg; + u32 mask; + int err; + + err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_SIG, &mask); + if (err) + return err; + + inst = kzalloc(sizeof(*inst) + sizeof(*spawn), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + spawn = sig_instance_ctx(inst); + + err = crypto_grab_sig(spawn, sig_crypto_instance(inst), + crypto_attr_alg_name(tb[1]), 0, mask); + if (err) + goto err_free_inst; + + ecdsa_alg = crypto_spawn_sig_alg(spawn); + + err = -EINVAL; + if (strncmp(ecdsa_alg->base.cra_name, "ecdsa", 5) != 0) + goto err_free_inst; + + err = crypto_inst_setname(sig_crypto_instance(inst), tmpl->name, + &ecdsa_alg->base); + if (err) + goto err_free_inst; + + inst->alg.base.cra_priority = ecdsa_alg->base.cra_priority; + inst->alg.base.cra_ctxsize = sizeof(struct ecdsa_x962_ctx); + + inst->alg.init = ecdsa_x962_init_tfm; + inst->alg.exit = ecdsa_x962_exit_tfm; + + inst->alg.verify = ecdsa_x962_verify; + inst->alg.max_size = ecdsa_x962_max_size; + inst->alg.set_pub_key = ecdsa_x962_set_pub_key; + + inst->free = ecdsa_x962_free; + + err = sig_register_instance(tmpl, inst); + if (err) { +err_free_inst: + ecdsa_x962_free(inst); + } + return err; +} + +struct crypto_template ecdsa_x962_tmpl = { + .name = "x962", + .create = ecdsa_x962_create, + .module = THIS_MODULE, +}; + +MODULE_ALIAS_CRYPTO("x962"); diff --git a/crypto/ecdsa.c b/crypto/ecdsa.c index 4a0ca93c99eaaa..1f7c29468a864e 100644 --- a/crypto/ecdsa.c +++ b/crypto/ecdsa.c @@ -8,9 +8,6 @@ #include #include #include -#include - -#include "ecdsasignature.asn1.h" struct ecc_ctx { unsigned int curve_id; @@ -22,61 +19,6 @@ struct ecc_ctx { struct ecc_point pub_key; }; -struct ecdsa_signature_ctx { - const struct ecc_curve *curve; - u64 r[ECC_MAX_DIGITS]; - u64 s[ECC_MAX_DIGITS]; -}; - -/* - * Get the r and s components of a signature from the X509 certificate. - */ -static int ecdsa_get_signature_rs(u64 *dest, size_t hdrlen, unsigned char tag, - const void *value, size_t vlen, unsigned int ndigits) -{ - size_t bufsize = ndigits * sizeof(u64); - const char *d = value; - - if (!value || !vlen || vlen > bufsize + 1) - return -EINVAL; - - /* - * vlen may be 1 byte larger than bufsize due to a leading zero byte - * (necessary if the most significant bit of the integer is set). - */ - if (vlen > bufsize) { - /* skip over leading zeros that make 'value' a positive int */ - if (*d == 0) { - vlen -= 1; - d++; - } else { - return -EINVAL; - } - } - - ecc_digits_from_bytes(d, vlen, dest, ndigits); - - return 0; -} - -int ecdsa_get_signature_r(void *context, size_t hdrlen, unsigned char tag, - const void *value, size_t vlen) -{ - struct ecdsa_signature_ctx *sig = context; - - return ecdsa_get_signature_rs(sig->r, hdrlen, tag, value, vlen, - sig->curve->g.ndigits); -} - -int ecdsa_get_signature_s(void *context, size_t hdrlen, unsigned char tag, - const void *value, size_t vlen) -{ - struct ecdsa_signature_ctx *sig = context; - - return ecdsa_get_signature_rs(sig->s, hdrlen, tag, value, vlen, - sig->curve->g.ndigits); -} - static int _ecdsa_verify(struct ecc_ctx *ctx, const u64 *hash, const u64 *r, const u64 *s) { const struct ecc_curve *curve = ctx->curve; @@ -126,25 +68,21 @@ static int ecdsa_verify(struct crypto_sig *tfm, { struct ecc_ctx *ctx = crypto_sig_ctx(tfm); size_t bufsize = ctx->curve->g.ndigits * sizeof(u64); - struct ecdsa_signature_ctx sig_ctx = { - .curve = ctx->curve, - }; + const struct ecdsa_raw_sig *sig = src; u64 hash[ECC_MAX_DIGITS]; - int ret; if (unlikely(!ctx->pub_key_set)) return -EINVAL; - ret = asn1_ber_decoder(&ecdsasignature_decoder, &sig_ctx, src, slen); - if (ret < 0) - return ret; + if (slen != sizeof(*sig)) + return -EINVAL; if (bufsize > dlen) bufsize = dlen; ecc_digits_from_bytes(digest, bufsize, hash, ctx->curve->g.ndigits); - return _ecdsa_verify(ctx, hash, sig_ctx.r, sig_ctx.s); + return _ecdsa_verify(ctx, hash, sig->r, sig->s); } static int ecdsa_ecc_ctx_init(struct ecc_ctx *ctx, unsigned int curve_id) @@ -340,8 +278,15 @@ static int __init ecdsa_init(void) if (ret) goto nist_p521_error; + ret = crypto_register_template(&ecdsa_x962_tmpl); + if (ret) + goto x962_tmpl_error; + return 0; +x962_tmpl_error: + crypto_unregister_sig(&ecdsa_nist_p521); + nist_p521_error: crypto_unregister_sig(&ecdsa_nist_p384); @@ -356,6 +301,8 @@ static int __init ecdsa_init(void) static void __exit ecdsa_exit(void) { + crypto_unregister_template(&ecdsa_x962_tmpl); + if (ecdsa_nist_p192_registered) crypto_unregister_sig(&ecdsa_nist_p192); crypto_unregister_sig(&ecdsa_nist_p256); diff --git a/crypto/testmgr.c b/crypto/testmgr.c index 48de2fc1e9659d..5760615e27b1be 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -5750,6 +5750,33 @@ static const struct alg_test_desc alg_test_descs[] = { .suite = { .hash = __VECS(wp512_tv_template) } + }, { + .alg = "x962(ecdsa-nist-p192)", + .test = alg_test_sig, + .suite = { + .sig = __VECS(x962_ecdsa_nist_p192_tv_template) + } + }, { + .alg = "x962(ecdsa-nist-p256)", + .test = alg_test_sig, + .fips_allowed = 1, + .suite = { + .sig = __VECS(x962_ecdsa_nist_p256_tv_template) + } + }, { + .alg = "x962(ecdsa-nist-p384)", + .test = alg_test_sig, + .fips_allowed = 1, + .suite = { + .sig = __VECS(x962_ecdsa_nist_p384_tv_template) + } + }, { + .alg = "x962(ecdsa-nist-p521)", + .test = alg_test_sig, + .fips_allowed = 1, + .suite = { + .sig = __VECS(x962_ecdsa_nist_p521_tv_template) + } }, { .alg = "xcbc(aes)", .test = alg_test_hash, diff --git a/crypto/testmgr.h b/crypto/testmgr.h index e10b6d7f2cd9f8..2bd77eaafdf683 100644 --- a/crypto/testmgr.h +++ b/crypto/testmgr.h @@ -21,6 +21,7 @@ #define _CRYPTO_TESTMGR_H #include +#include #define MAX_IVLEN 32 @@ -656,10 +657,702 @@ static const struct akcipher_testvec rsa_tv_template[] = { } }; +#ifdef CONFIG_CPU_BIG_ENDIAN +#define be64_to_cpua(b1, b2, b3, b4, b5, b6, b7, b8) \ + 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8 +#else +#define be64_to_cpua(b1, b2, b3, b4, b5, b6, b7, b8) \ + 0x##b8, 0x##b7, 0x##b6, 0x##b5, 0x##b4, 0x##b3, 0x##b2, 0x##b1 +#endif + /* * ECDSA test vectors. */ static const struct sig_testvec ecdsa_nist_p192_tv_template[] = { + { + .key = /* secp192r1(sha1) */ + "\x04\xf7\x46\xf8\x2f\x15\xf6\x22\x8e\xd7\x57\x4f\xcc\xe7\xbb\xc1" + "\xd4\x09\x73\xcf\xea\xd0\x15\x07\x3d\xa5\x8a\x8a\x95\x43\xe4\x68" + "\xea\xc6\x25\xc1\xc1\x01\x25\x4c\x7e\xc3\x3c\xa6\x04\x0a\xe7\x08" + "\x98", + .key_len = 49, + .m = + "\xcd\xb9\xd2\x1c\xb7\x6f\xcd\x44\xb3\xfd\x63\xea\xa3\x66\x7f\xae" + "\x63\x85\xe7\x82", + .m_size = 20, + .c = (const unsigned char[]){ + be64_to_cpua(ad, 59, ad, 88, 27, d6, 92, 6b), + be64_to_cpua(a0, 27, 91, c6, f6, 7f, c3, 09), + be64_to_cpua(ba, e5, 93, 83, 6e, b6, 3b, 63), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(86, 80, 6f, a5, 79, 77, da, d0), + be64_to_cpua(ef, 95, 52, 7b, a0, 0f, e4, 18), + be64_to_cpua(10, 68, 01, 9d, ba, ce, 83, 08), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00) }, + .c_size = ECC_MAX_BYTES * 2, + .public_key_vec = true, + }, { + .key = /* secp192r1(sha224) */ + "\x04\xb6\x4b\xb1\xd1\xac\xba\x24\x8f\x65\xb2\x60\x00\x90\xbf\xbd" + "\x78\x05\x73\xe9\x79\x1d\x6f\x7c\x0b\xd2\xc3\x93\xa7\x28\xe1\x75" + "\xf7\xd5\x95\x1d\x28\x10\xc0\x75\x50\x5c\x1a\x4f\x3f\x8f\xa5\xee" + "\xa3", + .key_len = 49, + .m = + "\x8d\xd6\xb8\x3e\xe5\xff\x23\xf6\x25\xa2\x43\x42\x74\x45\xa7\x40" + "\x3a\xff\x2f\xe1\xd3\xf6\x9f\xe8\x33\xcb\x12\x11", + .m_size = 28, + .c = (const unsigned char[]){ + be64_to_cpua(83, 7b, 12, e6, b6, 5b, cb, d4), + be64_to_cpua(14, f8, 11, 2b, 55, dc, ae, 37), + be64_to_cpua(5a, 8b, 82, 69, 7e, 8a, 0a, 09), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(a3, e3, 5c, 99, db, 92, 5b, 36), + be64_to_cpua(eb, c3, 92, 0f, 1e, 72, ee, c4), + be64_to_cpua(6a, 14, 4f, 53, 75, c8, 02, 48), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00) }, + .c_size = ECC_MAX_BYTES * 2, + .public_key_vec = true, + }, { + .key = /* secp192r1(sha256) */ + "\x04\xe2\x51\x24\x9b\xf7\xb6\x32\x82\x39\x66\x3d\x5b\xec\x3b\xae" + "\x0c\xd5\xf2\x67\xd1\xc7\xe1\x02\xe4\xbf\x90\x62\xb8\x55\x75\x56" + "\x69\x20\x5e\xcb\x4e\xca\x33\xd6\xcb\x62\x6b\x94\xa9\xa2\xe9\x58" + "\x91", + .key_len = 49, + .m = + "\x35\xec\xa1\xa0\x9e\x14\xde\x33\x03\xb6\xf6\xbd\x0c\x2f\xb2\xfd" + "\x1f\x27\x82\xa5\xd7\x70\x3f\xef\xa0\x82\x69\x8e\x73\x31\x8e\xd7", + .m_size = 32, + .c = (const unsigned char[]){ + be64_to_cpua(01, 48, fb, 5f, 72, 2a, d4, 8f), + be64_to_cpua(6b, 1a, 58, 56, f1, 8f, f7, fd), + be64_to_cpua(3f, 72, 3f, 1f, 42, d2, 3f, 1d), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(7d, 3a, 97, d9, cd, 1a, 6a, 49), + be64_to_cpua(32, dd, 41, 74, 6a, 51, c7, d9), + be64_to_cpua(b3, 69, 43, fd, 48, 19, 86, cf), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00) }, + .c_size = ECC_MAX_BYTES * 2, + .public_key_vec = true, + }, { + .key = /* secp192r1(sha384) */ + "\x04\x5a\x13\xfe\x68\x86\x4d\xf4\x17\xc7\xa4\xe5\x8c\x65\x57\xb7" + "\x03\x73\x26\x57\xfb\xe5\x58\x40\xd8\xfd\x49\x05\xab\xf1\x66\x1f" + "\xe2\x9d\x93\x9e\xc2\x22\x5a\x8b\x4f\xf3\x77\x22\x59\x7e\xa6\x4e" + "\x8b", + .key_len = 49, + .m = + "\x9d\x2e\x1a\x8f\xed\x6c\x4b\x61\xae\xac\xd5\x19\x79\xce\x67\xf9" + "\xa0\x34\xeb\xb0\x81\xf9\xd9\xdc\x6e\xb3\x5c\xa8\x69\xfc\x8a\x61" + "\x39\x81\xfb\xfd\x5c\x30\x6b\xa8\xee\xed\x89\xaf\xa3\x05\xe4\x78", + .m_size = 48, + .c = (const unsigned char[]){ + be64_to_cpua(dd, 15, bb, d6, 8c, a7, 03, 78), + be64_to_cpua(cf, 7f, 34, b4, b4, e5, c5, 00), + be64_to_cpua(f0, a3, 38, ce, 2b, f8, 9d, 1a), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(93, 12, 3b, 3b, 28, fb, 6d, e1), + be64_to_cpua(d1, 01, 77, 44, 5d, 53, a4, 7c), + be64_to_cpua(64, bc, 5a, 1f, 82, 96, 61, d7), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00) }, + .c_size = ECC_MAX_BYTES * 2, + .public_key_vec = true, + }, { + .key = /* secp192r1(sha512) */ + "\x04\xd5\xf2\x6e\xc3\x94\x5c\x52\xbc\xdf\x86\x6c\x14\xd1\xca\xea" + "\xcc\x72\x3a\x8a\xf6\x7a\x3a\x56\x36\x3b\xca\xc6\x94\x0e\x17\x1d" + "\x9e\xa0\x58\x28\xf9\x4b\xe6\xd1\xa5\x44\x91\x35\x0d\xe7\xf5\x11" + "\x57", + .key_len = 49, + .m = + "\xd5\x4b\xe9\x36\xda\xd8\x6e\xc0\x50\x03\xbe\x00\x43\xff\xf0\x23" + "\xac\xa2\x42\xe7\x37\x77\x79\x52\x8f\x3e\xc0\x16\xc1\xfc\x8c\x67" + "\x16\xbc\x8a\x5d\x3b\xd3\x13\xbb\xb6\xc0\x26\x1b\xeb\x33\xcc\x70" + "\x4a\xf2\x11\x37\xe8\x1b\xba\x55\xac\x69\xe1\x74\x62\x7c\x6e\xb5", + .m_size = 64, + .c = (const unsigned char[]){ + be64_to_cpua(2b, 11, 2d, 1c, b6, 06, c9, 6c), + be64_to_cpua(dd, 3f, 07, 87, 12, a0, d4, ac), + be64_to_cpua(88, 5b, 8f, 59, 43, bf, cf, c6), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(28, 6a, df, 97, fd, 82, 76, 24), + be64_to_cpua(a9, 14, 2a, 5e, f5, e5, fb, 72), + be64_to_cpua(73, b4, 22, 9a, 98, 73, 3c, 83), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00) }, + .c_size = ECC_MAX_BYTES * 2, + .public_key_vec = true, + }, +}; + +static const struct sig_testvec ecdsa_nist_p256_tv_template[] = { + { + .key = /* secp256r1(sha1) */ + "\x04\xb9\x7b\xbb\xd7\x17\x64\xd2\x7e\xfc\x81\x5d\x87\x06\x83\x41" + "\x22\xd6\x9a\xaa\x87\x17\xec\x4f\x63\x55\x2f\x94\xba\xdd\x83\xe9" + "\x34\x4b\xf3\xe9\x91\x13\x50\xb6\xcb\xca\x62\x08\xe7\x3b\x09\xdc" + "\xc3\x63\x4b\x2d\xb9\x73\x53\xe4\x45\xe6\x7c\xad\xe7\x6b\xb0\xe8" + "\xaf", + .key_len = 65, + .m = + "\xc2\x2b\x5f\x91\x78\x34\x26\x09\x42\x8d\x6f\x51\xb2\xc5\xaf\x4c" + "\x0b\xde\x6a\x42", + .m_size = 20, + .c = (const unsigned char[]){ + be64_to_cpua(ee, ca, 6a, 52, 0e, 48, 4d, cc), + be64_to_cpua(f7, d4, ad, 8d, 94, 5a, 69, 89), + be64_to_cpua(cf, d4, e7, b7, f0, 82, 56, 41), + be64_to_cpua(f9, 25, ce, 9f, 3a, a6, 35, 81), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(fb, 9d, 8b, de, d4, 8d, 6f, ad), + be64_to_cpua(f1, 03, 03, f3, 3b, e2, 73, f7), + be64_to_cpua(8a, fa, 54, 93, 29, a7, 70, 86), + be64_to_cpua(d7, e4, ef, 52, 66, d3, 5b, 9d), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00) }, + .c_size = ECC_MAX_BYTES * 2, + .public_key_vec = true, + }, { + .key = /* secp256r1(sha224) */ + "\x04\x8b\x6d\xc0\x33\x8e\x2d\x8b\x67\xf5\xeb\xc4\x7f\xa0\xf5\xd9" + "\x7b\x03\xa5\x78\x9a\xb5\xea\x14\xe4\x23\xd0\xaf\xd7\x0e\x2e\xa0" + "\xc9\x8b\xdb\x95\xf8\xb3\xaf\xac\x00\x2c\x2c\x1f\x7a\xfd\x95\x88" + "\x43\x13\xbf\xf3\x1c\x05\x1a\x14\x18\x09\x3f\xd6\x28\x3e\xc5\xa0" + "\xd4", + .key_len = 65, + .m = + "\x1a\x15\xbc\xa3\xe4\xed\x3a\xb8\x23\x67\xc6\xc4\x34\xf8\x6c\x41" + "\x04\x0b\xda\xc5\x77\xfa\x1c\x2d\xe6\x2c\x3b\xe0", + .m_size = 28, + .c = (const unsigned char[]){ + be64_to_cpua(7d, 25, d8, 25, f5, 81, d2, 1e), + be64_to_cpua(34, 62, 79, cb, 6a, 91, 67, 2e), + be64_to_cpua(ae, ce, 77, 59, 1a, db, 59, d5), + be64_to_cpua(20, 43, fa, c0, 9f, 9d, 7b, e7), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(ce, d5, 2e, 8b, de, 5a, 04, 0e), + be64_to_cpua(bf, 50, 05, 58, 39, 0e, 26, 92), + be64_to_cpua(76, 20, 4a, 77, 22, ec, c8, 66), + be64_to_cpua(5f, f8, 74, f8, 57, d0, 5e, 54), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00) }, + .c_size = ECC_MAX_BYTES * 2, + .public_key_vec = true, + }, { + .key = /* secp256r1(sha256) */ + "\x04\xf1\xea\xc4\x53\xf3\xb9\x0e\x9f\x7e\xad\xe3\xea\xd7\x0e\x0f" + "\xd6\x98\x9a\xca\x92\x4d\x0a\x80\xdb\x2d\x45\xc7\xec\x4b\x97\x00" + "\x2f\xe9\x42\x6c\x29\xdc\x55\x0e\x0b\x53\x12\x9b\x2b\xad\x2c\xe9" + "\x80\xe6\xc5\x43\xc2\x1d\x5e\xbb\x65\x21\x50\xb6\x37\xb0\x03\x8e" + "\xb8", + .key_len = 65, + .m = + "\x8f\x43\x43\x46\x64\x8f\x6b\x96\xdf\x89\xdd\xa9\x01\xc5\x17\x6b" + "\x10\xa6\xd8\x39\x61\xdd\x3c\x1a\xc8\x8b\x59\xb2\xdc\x32\x7a\xa4", + .m_size = 32, + .c = (const unsigned char[]){ + be64_to_cpua(91, dc, 02, 67, dc, 0c, d0, 82), + be64_to_cpua(ac, 44, c3, e8, 24, 11, 2d, a4), + be64_to_cpua(09, dc, 29, 63, a8, 1a, ad, fc), + be64_to_cpua(08, 31, fa, 74, 0d, 1d, 21, 5d), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(4f, 2a, 65, 35, 23, e3, 1d, fa), + be64_to_cpua(0a, 6e, 1b, c4, af, e1, 83, c3), + be64_to_cpua(f9, a9, 81, ac, 4a, 50, d0, 91), + be64_to_cpua(bd, ff, ce, ee, 42, c3, 97, ff), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00) }, + .c_size = ECC_MAX_BYTES * 2, + .public_key_vec = true, + }, { + .key = /* secp256r1(sha384) */ + "\x04\xc5\xc6\xea\x60\xc9\xce\xad\x02\x8d\xf5\x3e\x24\xe3\x52\x1d" + "\x28\x47\x3b\xc3\x6b\xa4\x99\x35\x99\x11\x88\x88\xc8\xf4\xee\x7e" + "\x8c\x33\x8f\x41\x03\x24\x46\x2b\x1a\x82\xf9\x9f\xe1\x97\x1b\x00" + "\xda\x3b\x24\x41\xf7\x66\x33\x58\x3d\x3a\x81\xad\xcf\x16\xe9\xe2" + "\x7c", + .key_len = 65, + .m = + "\x3e\x78\x70\xfb\xcd\x66\xba\x91\xa1\x79\xff\x1e\x1c\x6b\x78\xe6" + "\xc0\x81\x3a\x65\x97\x14\x84\x36\x14\x1a\x9a\xb7\xc5\xab\x84\x94" + "\x5e\xbb\x1b\x34\x71\xcb\x41\xe1\xf6\xfc\x92\x7b\x34\xbb\x86\xbb", + .m_size = 48, + .c = (const unsigned char[]){ + be64_to_cpua(f2, e4, 6c, c7, 94, b1, d5, fe), + be64_to_cpua(08, b2, 6b, 24, 94, 48, 46, 5e), + be64_to_cpua(d0, 2e, 95, 54, d1, 95, 64, 93), + be64_to_cpua(8e, f3, 6f, dc, f8, 69, a6, 2e), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(c0, 60, 11, 92, dc, 17, 89, 12), + be64_to_cpua(69, f4, 3b, 4f, 47, cf, 9b, 16), + be64_to_cpua(19, fb, 5f, 92, f4, c9, 23, 37), + be64_to_cpua(eb, a7, 80, 26, dc, f9, 3a, 44), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00) }, + .c_size = ECC_MAX_BYTES * 2, + .public_key_vec = true, + }, { + .key = /* secp256r1(sha512) */ + "\x04\xd7\x27\x46\x49\xf6\x26\x85\x12\x40\x76\x8e\xe2\xe6\x2a\x7a" + "\x83\xb1\x4e\x7a\xeb\x3b\x5c\x67\x4a\xb5\xa4\x92\x8c\x69\xff\x38" + "\xee\xd9\x4e\x13\x29\x59\xad\xde\x6b\xbb\x45\x31\xee\xfd\xd1\x1b" + "\x64\xd3\xb5\xfc\xaf\x9b\x4b\x88\x3b\x0e\xb7\xd6\xdf\xf1\xd5\x92" + "\xbf", + .key_len = 65, + .m = + "\x57\xb7\x9e\xe9\x05\x0a\x8c\x1b\xc9\x13\xe5\x4a\x24\xc7\xe2\xe9" + "\x43\xc3\xd1\x76\x62\xf4\x98\x1a\x9c\x13\xb0\x20\x1b\xe5\x39\xca" + "\x4f\xd9\x85\x34\x95\xa2\x31\xbc\xbb\xde\xdd\x76\xbb\x61\xe3\xcf" + "\x9d\xc0\x49\x7a\xf3\x7a\xc4\x7d\xa8\x04\x4b\x8d\xb4\x4d\x5b\xd6", + .m_size = 64, + .c = (const unsigned char[]){ + be64_to_cpua(76, f6, 04, 99, 09, 37, 4d, fa), + be64_to_cpua(ed, 8c, 73, 30, 6c, 22, b3, 97), + be64_to_cpua(40, ea, 44, 81, 00, 4e, 29, 08), + be64_to_cpua(b8, 6d, 87, 81, 43, df, fb, 9f), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(76, 31, 79, 4a, e9, 81, 6a, ee), + be64_to_cpua(5c, ad, c3, 78, 1c, c2, c1, 19), + be64_to_cpua(f8, 00, dd, ab, d4, c0, 2b, e6), + be64_to_cpua(1e, b9, 75, 31, f6, 04, a5, 4d), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00) }, + .c_size = ECC_MAX_BYTES * 2, + .public_key_vec = true, + }, +}; + +static const struct sig_testvec ecdsa_nist_p384_tv_template[] = { + { + .key = /* secp384r1(sha1) */ + "\x04\x89\x25\xf3\x97\x88\xcb\xb0\x78\xc5\x72\x9a\x14\x6e\x7a\xb1" + "\x5a\xa5\x24\xf1\x95\x06\x9e\x28\xfb\xc4\xb9\xbe\x5a\x0d\xd9\x9f" + "\xf3\xd1\x4d\x2d\x07\x99\xbd\xda\xa7\x66\xec\xbb\xea\xba\x79\x42" + "\xc9\x34\x89\x6a\xe7\x0b\xc3\xf2\xfe\x32\x30\xbe\xba\xf9\xdf\x7e" + "\x4b\x6a\x07\x8e\x26\x66\x3f\x1d\xec\xa2\x57\x91\x51\xdd\x17\x0e" + "\x0b\x25\xd6\x80\x5c\x3b\xe6\x1a\x98\x48\x91\x45\x7a\x73\xb0\xc3" + "\xf1", + .key_len = 97, + .m = + "\x12\x55\x28\xf0\x77\xd5\xb6\x21\x71\x32\x48\xcd\x28\xa8\x25\x22" + "\x3a\x69\xc1\x93", + .m_size = 20, + .c = (const unsigned char[]){ + be64_to_cpua(ec, 7c, 7e, d0, 87, d7, d7, 6e), + be64_to_cpua(78, f1, 4c, 26, e6, 5b, 86, cf), + be64_to_cpua(3a, c6, f1, 32, 3c, ce, 70, 2b), + be64_to_cpua(8d, 26, 8e, ae, 63, 3f, bc, 20), + be64_to_cpua(57, 55, 07, 20, 43, 30, de, a0), + be64_to_cpua(f5, 0f, 24, 4c, 07, 93, 6f, 21), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(79, 12, 2a, b7, c5, 15, 92, c5), + be64_to_cpua(4a, a1, 59, f1, 1c, a4, 58, 26), + be64_to_cpua(74, a0, 0f, bf, af, c3, 36, 76), + be64_to_cpua(df, 28, 8c, 1b, fa, f9, 95, 88), + be64_to_cpua(5f, 63, b1, be, 5e, 4c, 0e, a1), + be64_to_cpua(cd, bb, 7e, 81, 5d, 8f, 63, c0), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00) }, + .c_size = ECC_MAX_BYTES * 2, + .public_key_vec = true, + }, { + .key = /* secp384r1(sha224) */ + "\x04\x69\x6c\xcf\x62\xee\xd0\x0d\xe5\xb5\x2f\x70\x54\xcf\x26\xa0" + "\xd9\x98\x8d\x92\x2a\xab\x9b\x11\xcb\x48\x18\xa1\xa9\x0d\xd5\x18" + "\x3e\xe8\x29\x6e\xf6\xe4\xb5\x8e\xc7\x4a\xc2\x5f\x37\x13\x99\x05" + "\xb6\xa4\x9d\xf9\xfb\x79\x41\xe7\xd7\x96\x9f\x73\x3b\x39\x43\xdc" + "\xda\xf4\x06\xb9\xa5\x29\x01\x9d\x3b\xe1\xd8\x68\x77\x2a\xf4\x50" + "\x6b\x93\x99\x6c\x66\x4c\x42\x3f\x65\x60\x6c\x1c\x0b\x93\x9b\x9d" + "\xe0", + .key_len = 97, + .m = + "\x12\x80\xb6\xeb\x25\xe2\x3d\xf0\x21\x32\x96\x17\x3a\x38\x39\xfd" + "\x1f\x05\x34\x7b\xb8\xf9\x71\x66\x03\x4f\xd5\xe5", + .m_size = 28, + .c = (const unsigned char[]){ + be64_to_cpua(3f, dd, 15, 1b, 68, 2b, 9d, 8b), + be64_to_cpua(c9, 9c, 11, b8, 10, 01, c5, 41), + be64_to_cpua(c5, da, b4, e3, 93, 07, e0, 99), + be64_to_cpua(97, f1, c8, 72, 26, cf, 5a, 5e), + be64_to_cpua(ec, cb, e4, 89, 47, b2, f7, bc), + be64_to_cpua(8a, 51, 84, ce, 13, 1e, d2, dc), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(88, 2b, 82, 26, 5e, 1c, da, fb), + be64_to_cpua(9f, 19, d0, 42, 8b, 93, c2, 11), + be64_to_cpua(4d, d0, c6, 6e, b0, e9, fc, 14), + be64_to_cpua(df, d8, 68, a2, 64, 42, 65, f3), + be64_to_cpua(4b, 00, 08, 31, 6c, f5, d5, f6), + be64_to_cpua(8b, 03, 2c, fc, 1f, d1, a9, a4), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00) }, + .c_size = ECC_MAX_BYTES * 2, + .public_key_vec = true, + }, { + .key = /* secp384r1(sha256) */ + "\x04\xee\xd6\xda\x3e\x94\x90\x00\x27\xed\xf8\x64\x55\xd6\x51\x9a" + "\x1f\x52\x00\x63\x78\xf1\xa9\xfd\x75\x4c\x9e\xb2\x20\x1a\x91\x5a" + "\xba\x7a\xa3\xe5\x6c\xb6\x25\x68\x4b\xe8\x13\xa6\x54\x87\x2c\x0e" + "\xd0\x83\x95\xbc\xbf\xc5\x28\x4f\x77\x1c\x46\xa6\xf0\xbc\xd4\xa4" + "\x8d\xc2\x8f\xb3\x32\x37\x40\xd6\xca\xf8\xae\x07\x34\x52\x39\x52" + "\x17\xc3\x34\x29\xd6\x40\xea\x5c\xb9\x3f\xfb\x32\x2e\x12\x33\xbc" + "\xab", + .key_len = 97, + .m = + "\xaa\xe7\xfd\x03\x26\xcb\x94\x71\xe4\xce\x0f\xc5\xff\xa6\x29\xa3" + "\xe1\xcc\x4c\x35\x4e\xde\xca\x80\xab\x26\x0c\x25\xe6\x68\x11\xc2", + .m_size = 32, + .c = (const unsigned char[]){ + be64_to_cpua(c8, 8d, 2c, 79, 3a, 8e, 32, c4), + be64_to_cpua(b6, c6, fc, 70, 2e, 66, 3c, 77), + be64_to_cpua(af, 06, 3f, 84, 04, e2, f9, 67), + be64_to_cpua(cc, 47, 53, 87, bc, bd, 83, 3f), + be64_to_cpua(8e, 3f, 7e, ce, 0a, 9b, aa, 59), + be64_to_cpua(08, 09, 12, 9d, 6e, 96, 64, a6), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(10, 0e, f4, 1f, 39, ca, 4d, 43), + be64_to_cpua(4f, 8d, de, 1e, 93, 8d, 95, bb), + be64_to_cpua(15, 68, c0, 75, 3e, 23, 5e, 36), + be64_to_cpua(dd, ce, bc, b2, 97, f4, 9c, f3), + be64_to_cpua(26, a2, b0, 89, 42, 0a, da, d9), + be64_to_cpua(40, 34, b8, 90, a9, 80, ab, 47), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00) }, + .c_size = ECC_MAX_BYTES * 2, + .public_key_vec = true, + }, { + .key = /* secp384r1(sha384) */ + "\x04\x3a\x2f\x62\xe7\x1a\xcf\x24\xd0\x0b\x7c\xe0\xed\x46\x0a\x4f" + "\x74\x16\x43\xe9\x1a\x25\x7c\x55\xff\xf0\x29\x68\x66\x20\x91\xf9" + "\xdb\x2b\xf6\xb3\x6c\x54\x01\xca\xc7\x6a\x5c\x0d\xeb\x68\xd9\x3c" + "\xf1\x01\x74\x1f\xf9\x6c\xe5\x5b\x60\xe9\x7f\x5d\xb3\x12\x80\x2a" + "\xd8\x67\x92\xc9\x0e\x4c\x4c\x6b\xa1\xb2\xa8\x1e\xac\x1c\x97\xd9" + "\x21\x67\xe5\x1b\x5a\x52\x31\x68\xd6\xee\xf0\x19\xb0\x55\xed\x89" + "\x9e", + .key_len = 97, + .m = + "\x8d\xf2\xc0\xe9\xa8\xf3\x8e\x44\xc4\x8c\x1a\xa0\xb8\xd7\x17\xdf" + "\xf2\x37\x1b\xc6\xe3\xf5\x62\xcc\x68\xf5\xd5\x0b\xbf\x73\x2b\xb1" + "\xb0\x4c\x04\x00\x31\xab\xfe\xc8\xd6\x09\xc8\xf2\xea\xd3\x28\xff", + .m_size = 48, + .c = (const unsigned char[]){ + be64_to_cpua(a2, a4, c8, f2, ea, 9d, 11, 1f), + be64_to_cpua(3b, 1f, 07, 8f, 15, 02, fe, 1d), + be64_to_cpua(29, e6, fb, ca, 8c, d6, b6, b4), + be64_to_cpua(2d, 7a, 91, 5f, 49, 2d, 22, 08), + be64_to_cpua(ee, 2e, 62, 35, 46, fa, 00, d8), + be64_to_cpua(9b, 28, 68, c0, a1, ea, 8c, 50), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(ab, 8d, 4e, de, e6, 6d, 9b, 66), + be64_to_cpua(96, 17, 04, c9, 05, 77, f1, 8e), + be64_to_cpua(44, 92, 8c, 86, 99, 65, b3, 97), + be64_to_cpua(71, cd, 8f, 18, 99, f0, 0f, 13), + be64_to_cpua(bf, e3, 75, 24, 49, ac, fb, c8), + be64_to_cpua(fc, 50, f6, 43, bd, 50, 82, 0e), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00) }, + .c_size = ECC_MAX_BYTES * 2, + .public_key_vec = true, + }, { + .key = /* secp384r1(sha512) */ + "\x04\xb4\xe7\xc1\xeb\x64\x25\x22\x46\xc3\x86\x61\x80\xbe\x1e\x46" + "\xcb\xf6\x05\xc2\xee\x73\x83\xbc\xea\x30\x61\x4d\x40\x05\x41\xf4" + "\x8c\xe3\x0e\x5c\xf0\x50\xf2\x07\x19\xe8\x4f\x25\xbe\xee\x0c\x95" + "\x54\x36\x86\xec\xc2\x20\x75\xf3\x89\xb5\x11\xa1\xb7\xf5\xaf\xbe" + "\x81\xe4\xc3\x39\x06\xbd\xe4\xfe\x68\x1c\x6d\x99\x2b\x1b\x63\xfa" + "\xdf\x42\x5c\xc2\x5a\xc7\x0c\xf4\x15\xf7\x1b\xa3\x2e\xd7\x00\xac" + "\xa3", + .key_len = 97, + .m = + "\xe8\xb7\x52\x7d\x1a\x44\x20\x05\x53\x6b\x3a\x68\xf2\xe7\x6c\xa1" + "\xae\x9d\x84\xbb\xba\x52\x43\x3e\x2c\x42\x78\x49\xbf\x78\xb2\x71" + "\xeb\xe1\xe0\xe8\x42\x7b\x11\xad\x2b\x99\x05\x1d\x36\xe6\xac\xfc" + "\x55\x73\xf0\x15\x63\x39\xb8\x6a\x6a\xc5\x91\x5b\xca\x6a\xa8\x0e", + .m_size = 64, + .c = (const unsigned char[]){ + be64_to_cpua(3e, b3, c7, a8, b3, 17, 77, d1), + be64_to_cpua(dc, 2b, 43, 0e, 6a, b3, 53, 6f), + be64_to_cpua(4c, fc, 6f, 80, e3, af, b3, d9), + be64_to_cpua(9a, 02, de, 93, e8, 83, e4, 84), + be64_to_cpua(4d, c6, ef, da, 02, e7, 0f, 52), + be64_to_cpua(00, 1d, 20, 94, 77, fe, 31, fa), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(4e, 45, cf, 3c, 93, ff, 50, 5d), + be64_to_cpua(34, e4, 8b, 80, a5, b6, da, 2c), + be64_to_cpua(c4, 6a, 03, 5f, 8d, 7a, f9, fb), + be64_to_cpua(ec, 63, e3, 0c, ec, 50, dc, cc), + be64_to_cpua(de, 3a, 3d, 16, af, b4, 52, 6a), + be64_to_cpua(63, f6, f0, 3d, 5f, 5f, 99, 3f), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 00) }, + .c_size = ECC_MAX_BYTES * 2, + .public_key_vec = true, + }, +}; + +static const struct sig_testvec ecdsa_nist_p521_tv_template[] = { + { + .key = /* secp521r1(sha224) */ + "\x04\x01\x4f\x43\x18\xb6\xa9\xc9\x5d\x68\xd3\xa9\x42\xf8\x98\xc0" + "\xd2\xd1\xa9\x50\x3b\xe8\xc4\x40\xe6\x11\x78\x88\x4b\xbd\x76\xa7" + "\x9a\xe0\xdd\x31\xa4\x67\x78\x45\x33\x9e\x8c\xd1\xc7\x44\xac\x61" + "\x68\xc8\x04\xe7\x5c\x79\xb1\xf1\x41\x0c\x71\xc0\x53\xa8\xbc\xfb" + "\xf5\xca\xd4\x01\x40\xfd\xa3\x45\xda\x08\xe0\xb4\xcb\x28\x3b\x0a" + "\x02\x35\x5f\x02\x9f\x3f\xcd\xef\x08\x22\x40\x97\x74\x65\xb7\x76" + "\x85\xc7\xc0\x5c\xfb\x81\xe1\xa5\xde\x0c\x4e\x8b\x12\x31\xb6\x47" + "\xed\x37\x0f\x99\x3f\x26\xba\xa3\x8e\xff\x79\x34\x7c\x3a\xfe\x1f" + "\x3b\x83\x82\x2f\x14", + .key_len = 133, + .m = + "\xa2\x3a\x6a\x8c\x7b\x3c\xf2\x51\xf8\xbe\x5f\x4f\x3b\x15\x05\xc4" + "\xb5\xbc\x19\xe7\x21\x85\xe9\x23\x06\x33\x62\xfb", + .m_size = 28, + .c = (const unsigned char[]){ + be64_to_cpua(46, 6b, c7, af, 7a, b9, 19, 0a), + be64_to_cpua(6c, a6, 9b, 89, 8b, 1e, fd, 09), + be64_to_cpua(98, 85, 29, 88, ff, 0b, 94, 94), + be64_to_cpua(18, c6, 37, 8a, cb, a7, d8, 7d), + be64_to_cpua(f8, 3f, 59, 0f, 74, f0, 3f, d8), + be64_to_cpua(e2, ef, 07, 92, ee, 60, 94, 06), + be64_to_cpua(35, f6, dc, 6d, 02, 7b, 22, ac), + be64_to_cpua(d6, 43, e7, ff, 42, b2, ba, 74), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 01), + be64_to_cpua(50, b1, a5, 98, 92, 2a, a5, 52), + be64_to_cpua(1c, ad, 22, da, 82, 00, 35, a3), + be64_to_cpua(0e, 64, cc, c4, e8, 43, d9, 0e), + be64_to_cpua(30, 90, 0f, 1c, 8f, 78, d3, 9f), + be64_to_cpua(26, 0b, 5f, 49, 32, 6b, 91, 99), + be64_to_cpua(0f, f8, 65, 97, 6b, 09, 4d, 22), + be64_to_cpua(5e, f9, 88, f3, d2, 32, 90, 57), + be64_to_cpua(26, 0d, 55, cd, 23, 1e, 7d, a0), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 3a) }, + .c_size = ECC_MAX_BYTES * 2, + .public_key_vec = true, + }, + { + .key = /* secp521r1(sha256) */ + "\x04\x01\x05\x3a\x6b\x3b\x5a\x0f\xa7\xb9\xb7\x32\x53\x4e\xe2\xae" + "\x0a\x52\xc5\xda\xdd\x5a\x79\x1c\x30\x2d\x33\x07\x79\xd5\x70\x14" + "\x61\x0c\xec\x26\x4d\xd8\x35\x57\x04\x1d\x88\x33\x4d\xce\x05\x36" + "\xa5\xaf\x56\x84\xfa\x0b\x9e\xff\x7b\x30\x4b\x92\x1d\x06\xf8\x81" + "\x24\x1e\x51\x00\x09\x21\x51\xf7\x46\x0a\x77\xdb\xb5\x0c\xe7\x9c" + "\xff\x27\x3c\x02\x71\xd7\x85\x36\xf1\xaa\x11\x59\xd8\xb8\xdc\x09" + "\xdc\x6d\x5a\x6f\x63\x07\x6c\xe1\xe5\x4d\x6e\x0f\x6e\xfb\x7c\x05" + "\x8a\xe9\x53\xa8\xcf\xce\x43\x0e\x82\x20\x86\xbc\x88\x9c\xb7\xe3" + "\xe6\x77\x1e\x1f\x8a", + .key_len = 133, + .m = + "\xcc\x97\x73\x0c\x73\xa2\x53\x2b\xfa\xd7\x83\x1d\x0c\x72\x1b\x39" + "\x80\x71\x8d\xdd\xc5\x9b\xff\x55\x32\x98\x25\xa2\x58\x2e\xb7\x73", + .m_size = 32, + .c = (const unsigned char[]){ + be64_to_cpua(de, 7e, d7, 59, 10, e9, d9, d5), + be64_to_cpua(38, 1f, 46, 0b, 04, 64, 34, 79), + be64_to_cpua(ae, ce, 54, 76, 9a, c2, 8f, b8), + be64_to_cpua(95, 35, 6f, 02, 0e, af, e1, 4c), + be64_to_cpua(56, 3c, f6, f0, d8, e1, b7, 5d), + be64_to_cpua(50, 9f, 7d, 1f, ca, 8b, a8, 2d), + be64_to_cpua(06, 0f, fd, 83, fc, 0e, d9, ce), + be64_to_cpua(a5, 5f, 57, 52, 27, 78, 3a, b5), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, cd), + be64_to_cpua(55, 38, b6, f6, 34, 65, c7, bd), + be64_to_cpua(1c, 57, 56, 8f, 12, b7, 1d, 91), + be64_to_cpua(03, 42, 02, 5f, 50, f0, a2, 0d), + be64_to_cpua(fa, 10, dd, 9b, fb, 36, 1a, 31), + be64_to_cpua(e0, 87, 2c, 44, 4b, 5a, ee, af), + be64_to_cpua(a9, 79, 24, b9, 37, 35, dd, a0), + be64_to_cpua(6b, 35, ae, 65, b5, 99, 12, 0a), + be64_to_cpua(50, 85, 38, f9, 15, 83, 18, 04), + be64_to_cpua(00, 00, 00, 00, 00, 00, 01, cf) }, + .c_size = ECC_MAX_BYTES * 2, + .public_key_vec = true, + }, + { + .key = /* secp521r1(sha384) */ + "\x04\x00\x2e\xd6\x21\x04\x75\xc3\xdc\x7d\xff\x0e\xf3\x70\x25\x2b" + "\xad\x72\xfc\x5a\x91\xf1\xd5\x9c\x64\xf3\x1f\x47\x11\x10\x62\x33" + "\xfd\x2e\xe8\x32\xca\x9e\x6f\x0a\x4c\x5b\x35\x9a\x46\xc5\xe7\xd4" + "\x38\xda\xb2\xf0\xf4\x87\xf3\x86\xf4\xea\x70\xad\x1e\xd4\x78\x8c" + "\x36\x18\x17\x00\xa2\xa0\x34\x1b\x2e\x6a\xdf\x06\xd6\x99\x2d\x47" + "\x50\x92\x1a\x8a\x72\x9c\x23\x44\xfa\xa7\xa9\xed\xa6\xef\x26\x14" + "\xb3\x9d\xfe\x5e\xa3\x8c\xd8\x29\xf8\xdf\xad\xa6\xab\xfc\xdd\x46" + "\x22\x6e\xd7\x35\xc7\x23\xb7\x13\xae\xb6\x34\xff\xd7\x80\xe5\x39" + "\xb3\x3b\x5b\x1b\x94", + .key_len = 133, + .m = + "\x36\x98\xd6\x82\xfa\xad\xed\x3c\xb9\x40\xb6\x4d\x9e\xb7\x04\x26" + "\xad\x72\x34\x44\xd2\x81\xb4\x9b\xbe\x01\x04\x7a\xd8\x50\xf8\x59" + "\xba\xad\x23\x85\x6b\x59\xbe\xfb\xf6\x86\xd4\x67\xa8\x43\x28\x76", + .m_size = 48, + .c = (const unsigned char[]){ + be64_to_cpua(b8, 6a, dd, fb, e6, 63, 4e, 28), + be64_to_cpua(84, 59, fd, 1a, c4, 40, dd, 43), + be64_to_cpua(32, 76, 06, d0, f9, c0, e4, e6), + be64_to_cpua(e4, df, 9b, 7d, 9e, 47, ca, 33), + be64_to_cpua(7e, 42, 71, 86, 57, 2d, f1, 7d), + be64_to_cpua(f2, 4b, 64, 98, f7, ec, da, c7), + be64_to_cpua(ec, 51, dc, e8, 35, 5e, ae, 16), + be64_to_cpua(96, 76, 3c, 27, ea, aa, 9c, 26), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, 93), + be64_to_cpua(c6, 4f, ab, 2b, 62, c1, 42, b1), + be64_to_cpua(e5, 5a, 94, 56, cf, 8f, b4, 22), + be64_to_cpua(6a, c3, f3, 7a, d1, fa, e7, a7), + be64_to_cpua(df, c4, c0, db, 54, db, 8a, 0d), + be64_to_cpua(da, a7, cd, 26, 28, 76, 3b, 52), + be64_to_cpua(e4, 3c, bc, 93, 65, 57, 1c, 30), + be64_to_cpua(55, ce, 37, 97, c9, 05, 51, e5), + be64_to_cpua(c3, 6a, 87, 6e, b5, 13, 1f, 20), + be64_to_cpua(00, 00, 00, 00, 00, 00, 00, ff) }, + .c_size = ECC_MAX_BYTES * 2, + .public_key_vec = true, + }, + { + .key = /* secp521r1(sha512) */ + "\x04\x00\xc7\x65\xee\x0b\x86\x7d\x8f\x02\xf1\x74\x5b\xb0\x4c\x3f" + "\xa6\x35\x60\x9f\x55\x23\x11\xcc\xdf\xb8\x42\x99\xee\x6c\x96\x6a" + "\x27\xa2\x56\xb2\x2b\x03\xad\x0f\xe7\x97\xde\x09\x5d\xb4\xc5\x5f" + "\xbd\x87\x37\xbf\x5a\x16\x35\x56\x08\xfd\x6f\x06\x1a\x1c\x84\xee" + "\xc3\x64\xb3\x00\x9e\xbd\x6e\x60\x76\xee\x69\xfd\x3a\xb8\xcd\x7e" + "\x91\x68\x53\x57\x44\x13\x2e\x77\x09\x2a\xbe\x48\xbd\x91\xd8\xf6" + "\x21\x16\x53\x99\xd5\xf0\x40\xad\xa6\xf8\x58\x26\xb6\x9a\xf8\x77" + "\xfe\x3a\x05\x1a\xdb\xa9\x0f\xc0\x6c\x76\x30\x8c\xd8\xde\x44\xae" + "\xd0\x17\xdf\x49\x6a", + .key_len = 133, + .m = + "\x5c\xa6\xbc\x79\xb8\xa0\x1e\x11\x83\xf7\xe9\x05\xdf\xba\xf7\x69" + "\x97\x22\x32\xe4\x94\x7c\x65\xbd\x74\xc6\x9a\x8b\xbd\x0d\xdc\xed" + "\xf5\x9c\xeb\xe1\xc5\x68\x40\xf2\xc7\x04\xde\x9e\x0d\x76\xc5\xa3" + "\xf9\x3c\x6c\x98\x08\x31\xbd\x39\xe8\x42\x7f\x80\x39\x6f\xfe\x68", + .m_size = 64, + .c = (const unsigned char[]){ + be64_to_cpua(28, b5, 04, b0, b6, 33, 1c, 7e), + be64_to_cpua(80, a6, 13, fc, b6, 90, f7, bb), + be64_to_cpua(27, 93, e8, 6c, 49, 7d, 28, fc), + be64_to_cpua(1f, 12, 3e, b7, 7e, 51, ff, 7f), + be64_to_cpua(fb, 62, 1e, 42, 03, 6c, 74, 8a), + be64_to_cpua(63, 0e, 02, cc, 94, a9, 05, b9), + be64_to_cpua(aa, 86, ec, a8, 05, 03, 52, 56), + be64_to_cpua(71, 86, 96, ac, 21, 33, 7e, 4e), + be64_to_cpua(00, 00, 00, 00, 00, 00, 01, 5c), + be64_to_cpua(46, 1e, 77, 44, 78, e0, d1, 04), + be64_to_cpua(72, 74, 13, 63, 39, a6, e5, 25), + be64_to_cpua(00, 55, bb, 6a, b4, 73, 00, d2), + be64_to_cpua(71, d0, e9, ca, a7, c0, cb, aa), + be64_to_cpua(7a, 76, 37, 51, 47, 49, 98, 12), + be64_to_cpua(88, 05, 3e, 43, 39, 01, bd, b7), + be64_to_cpua(95, 35, 89, 4f, 41, 5f, 9e, 19), + be64_to_cpua(43, 52, 1d, e3, c6, bd, 5a, 40), + be64_to_cpua(00, 00, 00, 00, 00, 00, 01, 70) }, + .c_size = ECC_MAX_BYTES * 2, + .public_key_vec = true, + }, +}; + +/* + * ECDSA X9.62 test vectors. + * + * Identical to ECDSA test vectors, except signature in "c" is X9.62 encoded. + */ +static const struct sig_testvec x962_ecdsa_nist_p192_tv_template[] = { { .key = /* secp192r1(sha1) */ "\x04\xf7\x46\xf8\x2f\x15\xf6\x22\x8e\xd7\x57\x4f\xcc\xe7\xbb\xc1" @@ -756,7 +1449,7 @@ static const struct sig_testvec ecdsa_nist_p192_tv_template[] = { }, }; -static const struct sig_testvec ecdsa_nist_p256_tv_template[] = { +static const struct sig_testvec x962_ecdsa_nist_p256_tv_template[] = { { .key = /* secp256r1(sha1) */ "\x04\xb9\x7b\xbb\xd7\x17\x64\xd2\x7e\xfc\x81\x5d\x87\x06\x83\x41" @@ -863,7 +1556,7 @@ static const struct sig_testvec ecdsa_nist_p256_tv_template[] = { }, }; -static const struct sig_testvec ecdsa_nist_p384_tv_template[] = { +static const struct sig_testvec x962_ecdsa_nist_p384_tv_template[] = { { .key = /* secp384r1(sha1) */ "\x04\x89\x25\xf3\x97\x88\xcb\xb0\x78\xc5\x72\x9a\x14\x6e\x7a\xb1" @@ -990,7 +1683,7 @@ static const struct sig_testvec ecdsa_nist_p384_tv_template[] = { }, }; -static const struct sig_testvec ecdsa_nist_p521_tv_template[] = { +static const struct sig_testvec x962_ecdsa_nist_p521_tv_template[] = { { .key = /* secp521r1(sha224) */ "\x04\x01\x4f\x43\x18\xb6\xa9\xc9\x5d\x68\xd3\xa9\x42\xf8\x98\xc0" diff --git a/include/crypto/internal/ecc.h b/include/crypto/internal/ecc.h index 0717a53ae73223..68db975e0963ad 100644 --- a/include/crypto/internal/ecc.h +++ b/include/crypto/internal/ecc.h @@ -42,6 +42,18 @@ #define ECC_POINT_INIT(x, y, ndigits) (struct ecc_point) { x, y, ndigits } +/* + * The integers r and s making up the signature are expected to be + * formatted as two consecutive u64 arrays of size ECC_MAX_BYTES. + * The bytes within each u64 digit are in native endianness, + * but the order of the u64 digits themselves is little endian. + * This format allows direct use by internal vli_*() functions. + */ +struct ecdsa_raw_sig { + u64 r[ECC_MAX_DIGITS]; + u64 s[ECC_MAX_DIGITS]; +}; + /** * ecc_swap_digits() - Copy ndigits from big endian array to native array * @in: Input array @@ -293,4 +305,5 @@ void ecc_point_mult_shamir(const struct ecc_point *result, const u64 *y, const struct ecc_point *q, const struct ecc_curve *curve); +extern struct crypto_template ecdsa_x962_tmpl; #endif From fe0c5bcd2dd89e96315da2932ff5cb7a86f0c7b2 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sun, 8 Sep 2024 08:48:49 +0200 Subject: [PATCH 17/47] crypto: sig - Rename crypto_sig_maxsize() to crypto_sig_keysize() crypto_sig_maxsize() is a bit of a misnomer as it doesn't return the maximum signature size, but rather the key size. Rename it as well as all implementations of the ->max_size callback. A subsequent commit introduces a crypto_sig_maxsize() function which returns the actual maximum signature size. While at it, change the return type of crypto_sig_keysize() from int to unsigned int for consistency with crypto_akcipher_maxsize(). None of the callers checks for a negative return value and an error condition can always be indicated by returning zero. Signed-off-by: Lukas Wunner --- Documentation/crypto/api-sig.rst | 2 +- crypto/asymmetric_keys/public_key.c | 4 ++-- crypto/ecdsa-x962.c | 8 ++++---- crypto/ecdsa.c | 10 +++++----- crypto/ecrdsa.c | 4 ++-- crypto/rsassa-pkcs1.c | 4 ++-- crypto/sig.c | 2 +- crypto/testmgr.c | 2 +- include/crypto/sig.h | 14 +++++++------- 9 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Documentation/crypto/api-sig.rst b/Documentation/crypto/api-sig.rst index e5e87e106884f5..10dabc87df0294 100644 --- a/Documentation/crypto/api-sig.rst +++ b/Documentation/crypto/api-sig.rst @@ -11,5 +11,5 @@ Asymmetric Signature API :doc: Generic Public Key Signature API .. kernel-doc:: include/crypto/sig.h - :functions: crypto_alloc_sig crypto_free_sig crypto_sig_set_pubkey crypto_sig_set_privkey crypto_sig_maxsize crypto_sig_sign crypto_sig_verify + :functions: crypto_alloc_sig crypto_free_sig crypto_sig_set_pubkey crypto_sig_set_privkey crypto_sig_keysize crypto_sig_sign crypto_sig_verify diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c index cc6d48cafa2b3d..8bf5aa329c260f 100644 --- a/crypto/asymmetric_keys/public_key.c +++ b/crypto/asymmetric_keys/public_key.c @@ -201,7 +201,7 @@ static int software_key_query(const struct kernel_pkey_params *params, if (ret < 0) goto error_free_tfm; - len = crypto_sig_maxsize(sig); + len = crypto_sig_keysize(sig); info->supported_ops = KEYCTL_SUPPORTS_VERIFY; if (pkey->key_is_private) @@ -332,7 +332,7 @@ static int software_key_eds_op(struct kernel_pkey_params *params, if (ret) goto error_free_tfm; - ksz = crypto_sig_maxsize(sig); + ksz = crypto_sig_keysize(sig); } else { tfm = crypto_alloc_akcipher(alg_name, 0, 0); if (IS_ERR(tfm)) { diff --git a/crypto/ecdsa-x962.c b/crypto/ecdsa-x962.c index 022e654c075d59..8a15232dfa770d 100644 --- a/crypto/ecdsa-x962.c +++ b/crypto/ecdsa-x962.c @@ -81,7 +81,7 @@ static int ecdsa_x962_verify(struct crypto_sig *tfm, struct ecdsa_x962_signature_ctx sig_ctx; int err; - sig_ctx.ndigits = DIV_ROUND_UP(crypto_sig_maxsize(ctx->child), + sig_ctx.ndigits = DIV_ROUND_UP(crypto_sig_keysize(ctx->child), sizeof(u64)); err = asn1_ber_decoder(&ecdsasignature_decoder, &sig_ctx, src, slen); @@ -92,11 +92,11 @@ static int ecdsa_x962_verify(struct crypto_sig *tfm, digest, dlen); } -static unsigned int ecdsa_x962_max_size(struct crypto_sig *tfm) +static unsigned int ecdsa_x962_key_size(struct crypto_sig *tfm) { struct ecdsa_x962_ctx *ctx = crypto_sig_ctx(tfm); - return crypto_sig_maxsize(ctx->child); + return crypto_sig_keysize(ctx->child); } static int ecdsa_x962_set_pub_key(struct crypto_sig *tfm, @@ -179,7 +179,7 @@ static int ecdsa_x962_create(struct crypto_template *tmpl, struct rtattr **tb) inst->alg.exit = ecdsa_x962_exit_tfm; inst->alg.verify = ecdsa_x962_verify; - inst->alg.max_size = ecdsa_x962_max_size; + inst->alg.key_size = ecdsa_x962_key_size; inst->alg.set_pub_key = ecdsa_x962_set_pub_key; inst->free = ecdsa_x962_free; diff --git a/crypto/ecdsa.c b/crypto/ecdsa.c index 1f7c29468a864e..6cb0a6ce9de199 100644 --- a/crypto/ecdsa.c +++ b/crypto/ecdsa.c @@ -162,7 +162,7 @@ static void ecdsa_exit_tfm(struct crypto_sig *tfm) ecdsa_ecc_ctx_deinit(ctx); } -static unsigned int ecdsa_max_size(struct crypto_sig *tfm) +static unsigned int ecdsa_key_size(struct crypto_sig *tfm) { struct ecc_ctx *ctx = crypto_sig_ctx(tfm); @@ -179,7 +179,7 @@ static int ecdsa_nist_p521_init_tfm(struct crypto_sig *tfm) static struct sig_alg ecdsa_nist_p521 = { .verify = ecdsa_verify, .set_pub_key = ecdsa_set_pub_key, - .max_size = ecdsa_max_size, + .key_size = ecdsa_key_size, .init = ecdsa_nist_p521_init_tfm, .exit = ecdsa_exit_tfm, .base = { @@ -201,7 +201,7 @@ static int ecdsa_nist_p384_init_tfm(struct crypto_sig *tfm) static struct sig_alg ecdsa_nist_p384 = { .verify = ecdsa_verify, .set_pub_key = ecdsa_set_pub_key, - .max_size = ecdsa_max_size, + .key_size = ecdsa_key_size, .init = ecdsa_nist_p384_init_tfm, .exit = ecdsa_exit_tfm, .base = { @@ -223,7 +223,7 @@ static int ecdsa_nist_p256_init_tfm(struct crypto_sig *tfm) static struct sig_alg ecdsa_nist_p256 = { .verify = ecdsa_verify, .set_pub_key = ecdsa_set_pub_key, - .max_size = ecdsa_max_size, + .key_size = ecdsa_key_size, .init = ecdsa_nist_p256_init_tfm, .exit = ecdsa_exit_tfm, .base = { @@ -245,7 +245,7 @@ static int ecdsa_nist_p192_init_tfm(struct crypto_sig *tfm) static struct sig_alg ecdsa_nist_p192 = { .verify = ecdsa_verify, .set_pub_key = ecdsa_set_pub_key, - .max_size = ecdsa_max_size, + .key_size = ecdsa_key_size, .init = ecdsa_nist_p192_init_tfm, .exit = ecdsa_exit_tfm, .base = { diff --git a/crypto/ecrdsa.c b/crypto/ecrdsa.c index 7383dd11089b08..f981b31f424971 100644 --- a/crypto/ecrdsa.c +++ b/crypto/ecrdsa.c @@ -241,7 +241,7 @@ static int ecrdsa_set_pub_key(struct crypto_sig *tfm, const void *key, return 0; } -static unsigned int ecrdsa_max_size(struct crypto_sig *tfm) +static unsigned int ecrdsa_key_size(struct crypto_sig *tfm) { struct ecrdsa_ctx *ctx = crypto_sig_ctx(tfm); @@ -259,7 +259,7 @@ static void ecrdsa_exit_tfm(struct crypto_sig *tfm) static struct sig_alg ecrdsa_alg = { .verify = ecrdsa_verify, .set_pub_key = ecrdsa_set_pub_key, - .max_size = ecrdsa_max_size, + .key_size = ecrdsa_key_size, .exit = ecrdsa_exit_tfm, .base = { .cra_name = "ecrdsa", diff --git a/crypto/rsassa-pkcs1.c b/crypto/rsassa-pkcs1.c index b291ec0944a262..9c28f1c628264a 100644 --- a/crypto/rsassa-pkcs1.c +++ b/crypto/rsassa-pkcs1.c @@ -302,7 +302,7 @@ static int rsassa_pkcs1_verify(struct crypto_sig *tfm, return 0; } -static unsigned int rsassa_pkcs1_max_size(struct crypto_sig *tfm) +static unsigned int rsassa_pkcs1_key_size(struct crypto_sig *tfm) { struct rsassa_pkcs1_ctx *ctx = crypto_sig_ctx(tfm); @@ -419,7 +419,7 @@ static int rsassa_pkcs1_create(struct crypto_template *tmpl, struct rtattr **tb) inst->alg.sign = rsassa_pkcs1_sign; inst->alg.verify = rsassa_pkcs1_verify; - inst->alg.max_size = rsassa_pkcs1_max_size; + inst->alg.key_size = rsassa_pkcs1_key_size; inst->alg.set_pub_key = rsassa_pkcs1_set_pub_key; inst->alg.set_priv_key = rsassa_pkcs1_set_priv_key; diff --git a/crypto/sig.c b/crypto/sig.c index 84d0ea9fd73bb9..7a3521bee29a4c 100644 --- a/crypto/sig.c +++ b/crypto/sig.c @@ -125,7 +125,7 @@ int crypto_register_sig(struct sig_alg *alg) alg->set_priv_key = sig_default_set_key; if (!alg->set_pub_key) return -EINVAL; - if (!alg->max_size) + if (!alg->key_size) return -EINVAL; sig_prepare_alg(alg); diff --git a/crypto/testmgr.c b/crypto/testmgr.c index 5760615e27b1be..9d6774c5a463d4 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -4340,7 +4340,7 @@ static int test_sig_one(struct crypto_sig *tfm, const struct sig_testvec *vecs) if (vecs->public_key_vec) return 0; - sig_size = crypto_sig_maxsize(tfm); + sig_size = crypto_sig_keysize(tfm); if (sig_size < vecs->c_size) { pr_err("alg: sig: invalid maxsize %u\n", sig_size); return -EINVAL; diff --git a/include/crypto/sig.h b/include/crypto/sig.h index bbc902642bf5ac..a3ef17c5f72f66 100644 --- a/include/crypto/sig.h +++ b/include/crypto/sig.h @@ -32,7 +32,7 @@ struct crypto_sig { * @set_priv_key: Function invokes the algorithm specific set private key * function, which knows how to decode and interpret * the BER encoded private key and parameters. Optional. - * @max_size: Function returns key size. Mandatory. + * @key_size: Function returns key size. Mandatory. * @init: Initialize the cryptographic transformation object. * This function is used to initialize the cryptographic * transformation object. This function is called only once at @@ -58,7 +58,7 @@ struct sig_alg { const void *key, unsigned int keylen); int (*set_priv_key)(struct crypto_sig *tfm, const void *key, unsigned int keylen); - unsigned int (*max_size)(struct crypto_sig *tfm); + unsigned int (*key_size)(struct crypto_sig *tfm); int (*init)(struct crypto_sig *tfm); void (*exit)(struct crypto_sig *tfm); @@ -121,20 +121,20 @@ static inline void crypto_free_sig(struct crypto_sig *tfm) } /** - * crypto_sig_maxsize() - Get len for output buffer + * crypto_sig_keysize() - Get key size * - * Function returns the dest buffer size required for a given key. + * Function returns the key size in bytes. * Function assumes that the key is already set in the transformation. If this - * function is called without a setkey or with a failed setkey, you will end up + * function is called without a setkey or with a failed setkey, you may end up * in a NULL dereference. * * @tfm: signature tfm handle allocated with crypto_alloc_sig() */ -static inline int crypto_sig_maxsize(struct crypto_sig *tfm) +static inline unsigned int crypto_sig_keysize(struct crypto_sig *tfm) { struct sig_alg *alg = crypto_sig_alg(tfm); - return alg->max_size(tfm); + return alg->key_size(tfm); } /** From fa9a773a42cb86bad416b1dc19d32295cf5f6e4b Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sun, 8 Sep 2024 10:21:53 +0200 Subject: [PATCH 18/47] crypto: ecdsa - Move X9.62 signature size calculation into template software_key_query() returns the maximum signature and digest size for a given key to user space. When it only supported RSA keys, calculating those sizes was trivial as they were always equivalent to the key size. However when ECDSA was added, the function grew somewhat complicated calculations which take the ASN.1 encoding and curve into account. This doesn't scale well and adjusting the calculations is easily forgotten when adding support for new encodings or curves. In fact, when NIST P521 support was recently added, the function was initially not amended: https://lore.kernel.org/all/b749d5ee-c3b8-4cbd-b252-7773e4536e07@linux.ibm.com/ Introduce a ->max_size() callback to struct sig_alg and take advantage of it to move the signature size calculations to ecdsa-x962.c. Introduce a ->digest_size() callback to struct sig_alg and move the maximum ECDSA digest size to ecdsa.c. It is common across ecdsa-x962.c and the upcoming ecdsa-p1363.c and thus inherited by both of them. For all other algorithms, continue using the key size as maximum signature and digest size. Signed-off-by: Lukas Wunner --- Documentation/crypto/api-sig.rst | 2 +- crypto/asymmetric_keys/public_key.c | 38 +++-------------------------- crypto/ecdsa-x962.c | 36 +++++++++++++++++++++++++++ crypto/ecdsa.c | 16 ++++++++++++ crypto/sig.c | 4 +++ include/crypto/sig.h | 38 +++++++++++++++++++++++++++++ 6 files changed, 99 insertions(+), 35 deletions(-) diff --git a/Documentation/crypto/api-sig.rst b/Documentation/crypto/api-sig.rst index 10dabc87df0294..aaec18e26d545f 100644 --- a/Documentation/crypto/api-sig.rst +++ b/Documentation/crypto/api-sig.rst @@ -11,5 +11,5 @@ Asymmetric Signature API :doc: Generic Public Key Signature API .. kernel-doc:: include/crypto/sig.h - :functions: crypto_alloc_sig crypto_free_sig crypto_sig_set_pubkey crypto_sig_set_privkey crypto_sig_keysize crypto_sig_sign crypto_sig_verify + :functions: crypto_alloc_sig crypto_free_sig crypto_sig_set_pubkey crypto_sig_set_privkey crypto_sig_keysize crypto_sig_maxsize crypto_sig_digestsize crypto_sig_sign crypto_sig_verify diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c index 8bf5aa329c260f..ec2c0e009b4922 100644 --- a/crypto/asymmetric_keys/public_key.c +++ b/crypto/asymmetric_keys/public_key.c @@ -202,6 +202,8 @@ static int software_key_query(const struct kernel_pkey_params *params, goto error_free_tfm; len = crypto_sig_keysize(sig); + info->max_sig_size = crypto_sig_maxsize(sig); + info->max_data_size = crypto_sig_digestsize(sig); info->supported_ops = KEYCTL_SUPPORTS_VERIFY; if (pkey->key_is_private) @@ -227,6 +229,8 @@ static int software_key_query(const struct kernel_pkey_params *params, goto error_free_tfm; len = crypto_akcipher_maxsize(tfm); + info->max_sig_size = len; + info->max_data_size = len; info->supported_ops = KEYCTL_SUPPORTS_ENCRYPT; if (pkey->key_is_private) @@ -234,40 +238,6 @@ static int software_key_query(const struct kernel_pkey_params *params, } info->key_size = len * 8; - - if (strncmp(pkey->pkey_algo, "ecdsa", 5) == 0) { - int slen = len; - /* - * ECDSA key sizes are much smaller than RSA, and thus could - * operate on (hashed) inputs that are larger than key size. - * For example SHA384-hashed input used with secp256r1 - * based keys. Set max_data_size to be at least as large as - * the largest supported hash size (SHA512) - */ - info->max_data_size = 64; - - /* - * Verify takes ECDSA-Sig (described in RFC 5480) as input, - * which is actually 2 'key_size'-bit integers encoded in - * ASN.1. Account for the ASN.1 encoding overhead here. - * - * NIST P192/256/384 may prepend a '0' to a coordinate to - * indicate a positive integer. NIST P521 never needs it. - */ - if (strcmp(pkey->pkey_algo, "ecdsa-nist-p521") != 0) - slen += 1; - /* Length of encoding the x & y coordinates */ - slen = 2 * (slen + 2); - /* - * If coordinate encoding takes at least 128 bytes then an - * additional byte for length encoding is needed. - */ - info->max_sig_size = 1 + (slen >= 128) + 1 + slen; - } else { - info->max_data_size = len; - info->max_sig_size = len; - } - info->max_enc_size = len; info->max_dec_size = len; diff --git a/crypto/ecdsa-x962.c b/crypto/ecdsa-x962.c index 8a15232dfa770d..6a77c13e192b1a 100644 --- a/crypto/ecdsa-x962.c +++ b/crypto/ecdsa-x962.c @@ -99,6 +99,40 @@ static unsigned int ecdsa_x962_key_size(struct crypto_sig *tfm) return crypto_sig_keysize(ctx->child); } +static unsigned int ecdsa_x962_max_size(struct crypto_sig *tfm) +{ + struct ecdsa_x962_ctx *ctx = crypto_sig_ctx(tfm); + struct sig_alg *alg = crypto_sig_alg(ctx->child); + int slen = crypto_sig_keysize(ctx->child); + + /* + * Verify takes ECDSA-Sig-Value (described in RFC 5480) as input, + * which is actually 2 'key_size'-bit integers encoded in ASN.1. + * Account for the ASN.1 encoding overhead here. + * + * NIST P192/256/384 may prepend a '0' to a coordinate to indicate + * a positive integer. NIST P521 never needs it. + */ + if (strcmp(alg->base.cra_name, "ecdsa-nist-p521") != 0) + slen += 1; + + /* Length of encoding the x & y coordinates */ + slen = 2 * (slen + 2); + + /* + * If coordinate encoding takes at least 128 bytes then an + * additional byte for length encoding is needed. + */ + return 1 + (slen >= 128) + 1 + slen; +} + +static unsigned int ecdsa_x962_digest_size(struct crypto_sig *tfm) +{ + struct ecdsa_x962_ctx *ctx = crypto_sig_ctx(tfm); + + return crypto_sig_digestsize(ctx->child); +} + static int ecdsa_x962_set_pub_key(struct crypto_sig *tfm, const void *key, unsigned int keylen) { @@ -180,6 +214,8 @@ static int ecdsa_x962_create(struct crypto_template *tmpl, struct rtattr **tb) inst->alg.verify = ecdsa_x962_verify; inst->alg.key_size = ecdsa_x962_key_size; + inst->alg.max_size = ecdsa_x962_max_size; + inst->alg.digest_size = ecdsa_x962_digest_size; inst->alg.set_pub_key = ecdsa_x962_set_pub_key; inst->free = ecdsa_x962_free; diff --git a/crypto/ecdsa.c b/crypto/ecdsa.c index 6cb0a6ce9de199..cf8e0c5d1dd8bd 100644 --- a/crypto/ecdsa.c +++ b/crypto/ecdsa.c @@ -7,6 +7,7 @@ #include #include #include +#include #include struct ecc_ctx { @@ -169,6 +170,17 @@ static unsigned int ecdsa_key_size(struct crypto_sig *tfm) return DIV_ROUND_UP(ctx->curve->nbits, 8); } +static unsigned int ecdsa_digest_size(struct crypto_sig *tfm) +{ + /* + * ECDSA key sizes are much smaller than RSA, and thus could + * operate on (hashed) inputs that are larger than the key size. + * E.g. SHA384-hashed input used with secp256r1 based keys. + * Return the largest supported hash size (SHA512). + */ + return SHA512_DIGEST_SIZE; +} + static int ecdsa_nist_p521_init_tfm(struct crypto_sig *tfm) { struct ecc_ctx *ctx = crypto_sig_ctx(tfm); @@ -180,6 +192,7 @@ static struct sig_alg ecdsa_nist_p521 = { .verify = ecdsa_verify, .set_pub_key = ecdsa_set_pub_key, .key_size = ecdsa_key_size, + .digest_size = ecdsa_digest_size, .init = ecdsa_nist_p521_init_tfm, .exit = ecdsa_exit_tfm, .base = { @@ -202,6 +215,7 @@ static struct sig_alg ecdsa_nist_p384 = { .verify = ecdsa_verify, .set_pub_key = ecdsa_set_pub_key, .key_size = ecdsa_key_size, + .digest_size = ecdsa_digest_size, .init = ecdsa_nist_p384_init_tfm, .exit = ecdsa_exit_tfm, .base = { @@ -224,6 +238,7 @@ static struct sig_alg ecdsa_nist_p256 = { .verify = ecdsa_verify, .set_pub_key = ecdsa_set_pub_key, .key_size = ecdsa_key_size, + .digest_size = ecdsa_digest_size, .init = ecdsa_nist_p256_init_tfm, .exit = ecdsa_exit_tfm, .base = { @@ -246,6 +261,7 @@ static struct sig_alg ecdsa_nist_p192 = { .verify = ecdsa_verify, .set_pub_key = ecdsa_set_pub_key, .key_size = ecdsa_key_size, + .digest_size = ecdsa_digest_size, .init = ecdsa_nist_p192_init_tfm, .exit = ecdsa_exit_tfm, .base = { diff --git a/crypto/sig.c b/crypto/sig.c index 7a3521bee29a4c..be5ac0e5938421 100644 --- a/crypto/sig.c +++ b/crypto/sig.c @@ -127,6 +127,10 @@ int crypto_register_sig(struct sig_alg *alg) return -EINVAL; if (!alg->key_size) return -EINVAL; + if (!alg->max_size) + alg->max_size = alg->key_size; + if (!alg->digest_size) + alg->digest_size = alg->key_size; sig_prepare_alg(alg); return crypto_register_alg(base); diff --git a/include/crypto/sig.h b/include/crypto/sig.h index a3ef17c5f72f66..cff41ad93824c0 100644 --- a/include/crypto/sig.h +++ b/include/crypto/sig.h @@ -33,6 +33,8 @@ struct crypto_sig { * function, which knows how to decode and interpret * the BER encoded private key and parameters. Optional. * @key_size: Function returns key size. Mandatory. + * @digest_size: Function returns maximum digest size. Optional. + * @max_size: Function returns maximum signature size. Optional. * @init: Initialize the cryptographic transformation object. * This function is used to initialize the cryptographic * transformation object. This function is called only once at @@ -59,6 +61,8 @@ struct sig_alg { int (*set_priv_key)(struct crypto_sig *tfm, const void *key, unsigned int keylen); unsigned int (*key_size)(struct crypto_sig *tfm); + unsigned int (*digest_size)(struct crypto_sig *tfm); + unsigned int (*max_size)(struct crypto_sig *tfm); int (*init)(struct crypto_sig *tfm); void (*exit)(struct crypto_sig *tfm); @@ -137,6 +141,40 @@ static inline unsigned int crypto_sig_keysize(struct crypto_sig *tfm) return alg->key_size(tfm); } +/** + * crypto_sig_digestsize() - Get maximum digest size + * + * Function returns the maximum digest size in bytes. + * Function assumes that the key is already set in the transformation. If this + * function is called without a setkey or with a failed setkey, you may end up + * in a NULL dereference. + * + * @tfm: signature tfm handle allocated with crypto_alloc_sig() + */ +static inline unsigned int crypto_sig_digestsize(struct crypto_sig *tfm) +{ + struct sig_alg *alg = crypto_sig_alg(tfm); + + return alg->digest_size(tfm); +} + +/** + * crypto_sig_maxsize() - Get maximum signature size + * + * Function returns the maximum signature size in bytes. + * Function assumes that the key is already set in the transformation. If this + * function is called without a setkey or with a failed setkey, you may end up + * in a NULL dereference. + * + * @tfm: signature tfm handle allocated with crypto_alloc_sig() + */ +static inline unsigned int crypto_sig_maxsize(struct crypto_sig *tfm) +{ + struct sig_alg *alg = crypto_sig_alg(tfm); + + return alg->max_size(tfm); +} + /** * crypto_sig_sign() - Invoke signing operation * From 8facb0995987cb13dab4053f9f3b909763b7ee14 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sun, 2 Jul 2023 20:32:46 +0200 Subject: [PATCH 19/47] crypto: ecdsa - Support P1363 signature decoding Alternatively to the X9.62 encoding of ecdsa signatures, which uses ASN.1 and is already supported by the kernel, there's another common encoding called P1363. It stores r and s as the concatenation of two big endian, unsigned integers. The name originates from IEEE P1363. Add a P1363 template in support of the forthcoming SPDM library (Security Protocol and Data Model) for PCI device authentication. P1363 is prescribed by SPDM 1.2.1 margin no 44: "For ECDSA signatures, excluding SM2, in SPDM, the signature shall be the concatenation of r and s. The size of r shall be the size of the selected curve. Likewise, the size of s shall be the size of the selected curve. See BaseAsymAlgo in NEGOTIATE_ALGORITHMS for the size of r and s. The byte order for r and s shall be in big endian order. When placing ECDSA signatures into an SPDM signature field, r shall come first followed by s." Link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0274_1.2.1.pdf Signed-off-by: Lukas Wunner Reviewed-by: Stefan Berger Reviewed-by: Jonathan Cameron --- crypto/Makefile | 1 + crypto/asymmetric_keys/public_key.c | 3 +- crypto/ecdsa-p1363.c | 159 ++++++++++++++++++++++++++++ crypto/ecdsa.c | 8 ++ crypto/testmgr.c | 18 ++++ crypto/testmgr.h | 28 +++++ include/crypto/internal/ecc.h | 1 + 7 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 crypto/ecdsa-p1363.c diff --git a/crypto/Makefile b/crypto/Makefile index af43a1bd1cfa48..81be78d39c2d72 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -55,6 +55,7 @@ $(obj)/ecdsasignature.asn1.o: $(obj)/ecdsasignature.asn1.c $(obj)/ecdsasignature $(obj)/ecdsa-x962.o: $(obj)/ecdsasignature.asn1.h ecdsa_generic-y += ecdsa.o ecdsa_generic-y += ecdsa-x962.o +ecdsa_generic-y += ecdsa-p1363.o ecdsa_generic-y += ecdsasignature.asn1.o obj-$(CONFIG_CRYPTO_ECDSA) += ecdsa_generic.o diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c index ec2c0e009b4922..c98c1588802b4f 100644 --- a/crypto/asymmetric_keys/public_key.c +++ b/crypto/asymmetric_keys/public_key.c @@ -110,7 +110,8 @@ software_key_determine_akcipher(const struct public_key *pkey, return -EINVAL; *sig = false; } else if (strncmp(pkey->pkey_algo, "ecdsa", 5) == 0) { - if (strcmp(encoding, "x962") != 0) + if (strcmp(encoding, "x962") != 0 && + strcmp(encoding, "p1363") != 0) return -EINVAL; /* * ECDSA signatures are taken over a raw hash, so they don't diff --git a/crypto/ecdsa-p1363.c b/crypto/ecdsa-p1363.c new file mode 100644 index 00000000000000..eaae7214d69bca --- /dev/null +++ b/crypto/ecdsa-p1363.c @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ECDSA P1363 signature encoding + * + * Copyright (c) 2024 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include + +struct ecdsa_p1363_ctx { + struct crypto_sig *child; +}; + +static int ecdsa_p1363_verify(struct crypto_sig *tfm, + const void *src, unsigned int slen, + const void *digest, unsigned int dlen) +{ + struct ecdsa_p1363_ctx *ctx = crypto_sig_ctx(tfm); + unsigned int keylen = crypto_sig_keysize(ctx->child); + unsigned int ndigits = DIV_ROUND_UP(keylen, sizeof(u64)); + struct ecdsa_raw_sig sig; + + if (slen != 2 * keylen) + return -EINVAL; + + ecc_digits_from_bytes(src, keylen, sig.r, ndigits); + ecc_digits_from_bytes(src + keylen, keylen, sig.s, ndigits); + + return crypto_sig_verify(ctx->child, &sig, sizeof(sig), digest, dlen); +} + +static unsigned int ecdsa_p1363_key_size(struct crypto_sig *tfm) +{ + struct ecdsa_p1363_ctx *ctx = crypto_sig_ctx(tfm); + + return crypto_sig_keysize(ctx->child); +} + +static unsigned int ecdsa_p1363_max_size(struct crypto_sig *tfm) +{ + struct ecdsa_p1363_ctx *ctx = crypto_sig_ctx(tfm); + + return 2 * crypto_sig_keysize(ctx->child); +} + +static unsigned int ecdsa_p1363_digest_size(struct crypto_sig *tfm) +{ + struct ecdsa_p1363_ctx *ctx = crypto_sig_ctx(tfm); + + return crypto_sig_digestsize(ctx->child); +} + +static int ecdsa_p1363_set_pub_key(struct crypto_sig *tfm, + const void *key, unsigned int keylen) +{ + struct ecdsa_p1363_ctx *ctx = crypto_sig_ctx(tfm); + + return crypto_sig_set_pubkey(ctx->child, key, keylen); +} + +static int ecdsa_p1363_init_tfm(struct crypto_sig *tfm) +{ + struct sig_instance *inst = sig_alg_instance(tfm); + struct crypto_sig_spawn *spawn = sig_instance_ctx(inst); + struct ecdsa_p1363_ctx *ctx = crypto_sig_ctx(tfm); + struct crypto_sig *child_tfm; + + child_tfm = crypto_spawn_sig(spawn); + if (IS_ERR(child_tfm)) + return PTR_ERR(child_tfm); + + ctx->child = child_tfm; + + return 0; +} + +static void ecdsa_p1363_exit_tfm(struct crypto_sig *tfm) +{ + struct ecdsa_p1363_ctx *ctx = crypto_sig_ctx(tfm); + + crypto_free_sig(ctx->child); +} + +static void ecdsa_p1363_free(struct sig_instance *inst) +{ + struct crypto_sig_spawn *spawn = sig_instance_ctx(inst); + + crypto_drop_sig(spawn); + kfree(inst); +} + +static int ecdsa_p1363_create(struct crypto_template *tmpl, struct rtattr **tb) +{ + struct crypto_sig_spawn *spawn; + struct sig_instance *inst; + struct sig_alg *ecdsa_alg; + u32 mask; + int err; + + err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_SIG, &mask); + if (err) + return err; + + inst = kzalloc(sizeof(*inst) + sizeof(*spawn), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + spawn = sig_instance_ctx(inst); + + err = crypto_grab_sig(spawn, sig_crypto_instance(inst), + crypto_attr_alg_name(tb[1]), 0, mask); + if (err) + goto err_free_inst; + + ecdsa_alg = crypto_spawn_sig_alg(spawn); + + err = -EINVAL; + if (strncmp(ecdsa_alg->base.cra_name, "ecdsa", 5) != 0) + goto err_free_inst; + + err = crypto_inst_setname(sig_crypto_instance(inst), tmpl->name, + &ecdsa_alg->base); + if (err) + goto err_free_inst; + + inst->alg.base.cra_priority = ecdsa_alg->base.cra_priority; + inst->alg.base.cra_ctxsize = sizeof(struct ecdsa_p1363_ctx); + + inst->alg.init = ecdsa_p1363_init_tfm; + inst->alg.exit = ecdsa_p1363_exit_tfm; + + inst->alg.verify = ecdsa_p1363_verify; + inst->alg.key_size = ecdsa_p1363_key_size; + inst->alg.max_size = ecdsa_p1363_max_size; + inst->alg.digest_size = ecdsa_p1363_digest_size; + inst->alg.set_pub_key = ecdsa_p1363_set_pub_key; + + inst->free = ecdsa_p1363_free; + + err = sig_register_instance(tmpl, inst); + if (err) { +err_free_inst: + ecdsa_p1363_free(inst); + } + return err; +} + +struct crypto_template ecdsa_p1363_tmpl = { + .name = "p1363", + .create = ecdsa_p1363_create, + .module = THIS_MODULE, +}; + +MODULE_ALIAS_CRYPTO("p1363"); diff --git a/crypto/ecdsa.c b/crypto/ecdsa.c index cf8e0c5d1dd8bd..117526d15ddebf 100644 --- a/crypto/ecdsa.c +++ b/crypto/ecdsa.c @@ -298,8 +298,15 @@ static int __init ecdsa_init(void) if (ret) goto x962_tmpl_error; + ret = crypto_register_template(&ecdsa_p1363_tmpl); + if (ret) + goto p1363_tmpl_error; + return 0; +p1363_tmpl_error: + crypto_unregister_template(&ecdsa_x962_tmpl); + x962_tmpl_error: crypto_unregister_sig(&ecdsa_nist_p521); @@ -318,6 +325,7 @@ static int __init ecdsa_init(void) static void __exit ecdsa_exit(void) { crypto_unregister_template(&ecdsa_x962_tmpl); + crypto_unregister_template(&ecdsa_p1363_tmpl); if (ecdsa_nist_p192_registered) crypto_unregister_sig(&ecdsa_nist_p192); diff --git a/crypto/testmgr.c b/crypto/testmgr.c index 9d6774c5a463d4..7d768f0ed81f54 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -5515,6 +5515,24 @@ static const struct alg_test_desc alg_test_descs[] = { .suite = { .hash = __VECS(nhpoly1305_tv_template) } + }, { + .alg = "p1363(ecdsa-nist-p192)", + .test = alg_test_null, + }, { + .alg = "p1363(ecdsa-nist-p256)", + .test = alg_test_sig, + .fips_allowed = 1, + .suite = { + .sig = __VECS(p1363_ecdsa_nist_p256_tv_template) + } + }, { + .alg = "p1363(ecdsa-nist-p384)", + .test = alg_test_null, + .fips_allowed = 1, + }, { + .alg = "p1363(ecdsa-nist-p521)", + .test = alg_test_null, + .fips_allowed = 1, }, { .alg = "pcbc(fcrypt)", .test = alg_test_skcipher, diff --git a/crypto/testmgr.h b/crypto/testmgr.h index 2bd77eaafdf683..55aae1859d2c5d 100644 --- a/crypto/testmgr.h +++ b/crypto/testmgr.h @@ -1805,6 +1805,34 @@ static const struct sig_testvec x962_ecdsa_nist_p521_tv_template[] = { }, }; +/* + * ECDSA P1363 test vectors. + * + * Identical to ECDSA test vectors, except signature in "c" is P1363 encoded. + */ +static const struct sig_testvec p1363_ecdsa_nist_p256_tv_template[] = { + { + .key = /* secp256r1(sha256) */ + "\x04\xf1\xea\xc4\x53\xf3\xb9\x0e\x9f\x7e\xad\xe3\xea\xd7\x0e\x0f" + "\xd6\x98\x9a\xca\x92\x4d\x0a\x80\xdb\x2d\x45\xc7\xec\x4b\x97\x00" + "\x2f\xe9\x42\x6c\x29\xdc\x55\x0e\x0b\x53\x12\x9b\x2b\xad\x2c\xe9" + "\x80\xe6\xc5\x43\xc2\x1d\x5e\xbb\x65\x21\x50\xb6\x37\xb0\x03\x8e" + "\xb8", + .key_len = 65, + .m = + "\x8f\x43\x43\x46\x64\x8f\x6b\x96\xdf\x89\xdd\xa9\x01\xc5\x17\x6b" + "\x10\xa6\xd8\x39\x61\xdd\x3c\x1a\xc8\x8b\x59\xb2\xdc\x32\x7a\xa4", + .m_size = 32, + .c = + "\x08\x31\xfa\x74\x0d\x1d\x21\x5d\x09\xdc\x29\x63\xa8\x1a\xad\xfc" + "\xac\x44\xc3\xe8\x24\x11\x2d\xa4\x91\xdc\x02\x67\xdc\x0c\xd0\x82" + "\xbd\xff\xce\xee\x42\xc3\x97\xff\xf9\xa9\x81\xac\x4a\x50\xd0\x91" + "\x0a\x6e\x1b\xc4\xaf\xe1\x83\xc3\x4f\x2a\x65\x35\x23\xe3\x1d\xfa", + .c_size = 64, + .public_key_vec = true, + }, +}; + /* * EC-RDSA test vectors are generated by gost-engine. */ diff --git a/include/crypto/internal/ecc.h b/include/crypto/internal/ecc.h index 68db975e0963ad..71483e5305e1dc 100644 --- a/include/crypto/internal/ecc.h +++ b/include/crypto/internal/ecc.h @@ -306,4 +306,5 @@ void ecc_point_mult_shamir(const struct ecc_point *result, const struct ecc_curve *curve); extern struct crypto_template ecdsa_x962_tmpl; +extern struct crypto_template ecdsa_p1363_tmpl; #endif From 4c23f8c7676a9049c518510101d664e5309bcaa5 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sun, 8 Sep 2024 11:19:56 +0200 Subject: [PATCH 20/47] crypto: ecrdsa - Fix signature size calculation software_key_query() returns the curve size as maximum signature size for ecrdsa. It should instead return twice as much. It's only the maximum signature size that seems to be off. The maximum digest size is likewise set to the curve size, but that's correct as it matches the checks in ecrdsa_set_pub_key() and ecrdsa_verify(). Signed-off-by: Lukas Wunner --- crypto/ecrdsa.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crypto/ecrdsa.c b/crypto/ecrdsa.c index f981b31f424971..b3dd8a3ddeb796 100644 --- a/crypto/ecrdsa.c +++ b/crypto/ecrdsa.c @@ -252,6 +252,13 @@ static unsigned int ecrdsa_key_size(struct crypto_sig *tfm) return ctx->pub_key.ndigits * sizeof(u64); } +static unsigned int ecrdsa_max_size(struct crypto_sig *tfm) +{ + struct ecrdsa_ctx *ctx = crypto_sig_ctx(tfm); + + return 2 * ctx->pub_key.ndigits * sizeof(u64); +} + static void ecrdsa_exit_tfm(struct crypto_sig *tfm) { } @@ -260,6 +267,7 @@ static struct sig_alg ecrdsa_alg = { .verify = ecrdsa_verify, .set_pub_key = ecrdsa_set_pub_key, .key_size = ecrdsa_key_size, + .max_size = ecrdsa_max_size, .exit = ecrdsa_exit_tfm, .base = { .cra_name = "ecrdsa", From 256496c2142649859a23055302f567e4b67087cb Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Thu, 29 Aug 2024 22:17:36 +0200 Subject: [PATCH 21/47] crypto: virtio - Fix kernel-doc of virtcrypto_dev_stop() It seems the kernel-doc of virtcrypto_dev_start() was copied verbatim to virtcrypto_dev_stop(). Fix it. Signed-off-by: Lukas Wunner --- drivers/crypto/virtio/virtio_crypto_mgr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/crypto/virtio/virtio_crypto_mgr.c b/drivers/crypto/virtio/virtio_crypto_mgr.c index 70e778aac0f2c9..bddbd8ebfebe62 100644 --- a/drivers/crypto/virtio/virtio_crypto_mgr.c +++ b/drivers/crypto/virtio/virtio_crypto_mgr.c @@ -256,7 +256,7 @@ int virtcrypto_dev_start(struct virtio_crypto *vcrypto) * @vcrypto: Pointer to virtio crypto device. * * Function notifies all the registered services that the virtio crypto device - * is ready to be used. + * shall no longer be used. * To be used by virtio crypto device specific drivers. * * Return: void From 18d6fffda66a0a6c74396fee4b1f0c44ee372b0a Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 26 Aug 2024 21:19:33 +0200 Subject: [PATCH 22/47] crypto: virtio - Simplify RSA keysize caching When setting a public or private RSA key, the integer n is cached in the transform context virtio_crypto_akcipher_ctx -- with the sole purpose of calculating the keysize from it in virtio_crypto_rsa_max_size(). It looks like this was copy-pasted from crypto/rsa.c. Cache the keysize directly instead of the integer n, thereby simplifying the code and reducing the memory footprint. Signed-off-by: Lukas Wunner --- .../virtio/virtio_crypto_akcipher_algs.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c index d3fa0fed54ff47..8d5aadd8d62e77 100644 --- a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c +++ b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c @@ -21,7 +21,7 @@ #include "virtio_crypto_common.h" struct virtio_crypto_rsa_ctx { - MPI n; + unsigned int keysize; }; struct virtio_crypto_akcipher_ctx { @@ -352,10 +352,7 @@ static int virtio_crypto_rsa_set_key(struct crypto_akcipher *tfm, int node = virtio_crypto_get_current_node(); uint32_t keytype; int ret; - - /* mpi_free will test n, just free it. */ - mpi_free(rsa_ctx->n); - rsa_ctx->n = NULL; + MPI n; if (private) { keytype = VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE; @@ -368,10 +365,13 @@ static int virtio_crypto_rsa_set_key(struct crypto_akcipher *tfm, if (ret) return ret; - rsa_ctx->n = mpi_read_raw_data(rsa_key.n, rsa_key.n_sz); - if (!rsa_ctx->n) + n = mpi_read_raw_data(rsa_key.n, rsa_key.n_sz); + if (!n) return -ENOMEM; + rsa_ctx->keysize = mpi_get_size(n); + mpi_free(n); + if (!ctx->vcrypto) { vcrypto = virtcrypto_get_dev_node(node, VIRTIO_CRYPTO_SERVICE_AKCIPHER, VIRTIO_CRYPTO_AKCIPHER_RSA); @@ -442,7 +442,7 @@ static unsigned int virtio_crypto_rsa_max_size(struct crypto_akcipher *tfm) struct virtio_crypto_akcipher_ctx *ctx = akcipher_tfm_ctx(tfm); struct virtio_crypto_rsa_ctx *rsa_ctx = &ctx->rsa_ctx; - return mpi_get_size(rsa_ctx->n); + return rsa_ctx->keysize; } static int virtio_crypto_rsa_init_tfm(struct crypto_akcipher *tfm) @@ -460,12 +460,9 @@ static int virtio_crypto_rsa_init_tfm(struct crypto_akcipher *tfm) static void virtio_crypto_rsa_exit_tfm(struct crypto_akcipher *tfm) { struct virtio_crypto_akcipher_ctx *ctx = akcipher_tfm_ctx(tfm); - struct virtio_crypto_rsa_ctx *rsa_ctx = &ctx->rsa_ctx; virtio_crypto_alg_akcipher_close_session(ctx); virtcrypto_dev_put(ctx->vcrypto); - mpi_free(rsa_ctx->n); - rsa_ctx->n = NULL; } static struct virtio_crypto_akcipher_algo virtio_crypto_akcipher_algs[] = { From 7dc50cce21526e87d7af9fb6e1f4f7ca9c0d889b Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Fri, 23 Aug 2024 10:18:27 +0200 Subject: [PATCH 23/47] crypto: virtio - Drop superfluous ctx->tfm backpointer struct virtio_crypto_[as]kcipher_ctx contains a backpointer to struct crypto_[as]kcipher which is superfluous in two ways: First, it's not used anywhere. Second, the context is embedded into struct crypto_tfm, so one could just use container_of() to get from the context to crypto_tfm and from there to crypto_[as]kcipher. Drop the superfluous backpointer. Signed-off-by: Lukas Wunner --- drivers/crypto/virtio/virtio_crypto_akcipher_algs.c | 5 ----- drivers/crypto/virtio/virtio_crypto_skcipher_algs.c | 4 ---- 2 files changed, 9 deletions(-) diff --git a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c index 8d5aadd8d62e77..0a9fd6859ae82d 100644 --- a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c +++ b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c @@ -26,7 +26,6 @@ struct virtio_crypto_rsa_ctx { struct virtio_crypto_akcipher_ctx { struct virtio_crypto *vcrypto; - struct crypto_akcipher *tfm; bool session_valid; __u64 session_id; union { @@ -447,10 +446,6 @@ static unsigned int virtio_crypto_rsa_max_size(struct crypto_akcipher *tfm) static int virtio_crypto_rsa_init_tfm(struct crypto_akcipher *tfm) { - struct virtio_crypto_akcipher_ctx *ctx = akcipher_tfm_ctx(tfm); - - ctx->tfm = tfm; - akcipher_set_reqsize(tfm, sizeof(struct virtio_crypto_akcipher_request)); diff --git a/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c b/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c index 23c41d87d835ff..495fc655a51cdf 100644 --- a/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c +++ b/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c @@ -17,7 +17,6 @@ struct virtio_crypto_skcipher_ctx { struct virtio_crypto *vcrypto; - struct crypto_skcipher *tfm; struct virtio_crypto_sym_session_info enc_sess_info; struct virtio_crypto_sym_session_info dec_sess_info; @@ -515,10 +514,7 @@ static int virtio_crypto_skcipher_decrypt(struct skcipher_request *req) static int virtio_crypto_skcipher_init(struct crypto_skcipher *tfm) { - struct virtio_crypto_skcipher_ctx *ctx = crypto_skcipher_ctx(tfm); - crypto_skcipher_set_reqsize(tfm, sizeof(struct virtio_crypto_sym_request)); - ctx->tfm = tfm; return 0; } From 2acf7790ae403c4849a8c13f37c64f0bedbcfe75 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Fri, 6 Sep 2024 11:33:08 +0200 Subject: [PATCH 24/47] crypto: virtio - Drop superfluous [as]kcipher_ctx pointer The request context virtio_crypto_{akcipher,sym}_request contains a pointer to the transform context virtio_crypto_[as]kcipher_ctx. The pointer is superfluous as it can be calculated with the cheap crypto_akcipher_reqtfm() + akcipher_tfm_ctx() and crypto_skcipher_reqtfm() + crypto_skcipher_ctx() combos. Drop the superfluous pointer. Signed-off-by: Lukas Wunner --- drivers/crypto/virtio/virtio_crypto_akcipher_algs.c | 8 ++++---- drivers/crypto/virtio/virtio_crypto_skcipher_algs.c | 5 +---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c index 0a9fd6859ae82d..0df99886d79529 100644 --- a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c +++ b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c @@ -35,7 +35,6 @@ struct virtio_crypto_akcipher_ctx { struct virtio_crypto_akcipher_request { struct virtio_crypto_request base; - struct virtio_crypto_akcipher_ctx *akcipher_ctx; struct akcipher_request *akcipher_req; void *src_buf; void *dst_buf; @@ -212,7 +211,8 @@ static int virtio_crypto_alg_akcipher_close_session(struct virtio_crypto_akciphe static int __virtio_crypto_akcipher_do_req(struct virtio_crypto_akcipher_request *vc_akcipher_req, struct akcipher_request *req, struct data_queue *data_vq) { - struct virtio_crypto_akcipher_ctx *ctx = vc_akcipher_req->akcipher_ctx; + struct crypto_akcipher *atfm = crypto_akcipher_reqtfm(req); + struct virtio_crypto_akcipher_ctx *ctx = akcipher_tfm_ctx(atfm); struct virtio_crypto_request *vc_req = &vc_akcipher_req->base; struct virtio_crypto *vcrypto = ctx->vcrypto; struct virtio_crypto_op_data_req *req_data = vc_req->req_data; @@ -272,7 +272,8 @@ static int virtio_crypto_rsa_do_req(struct crypto_engine *engine, void *vreq) struct akcipher_request *req = container_of(vreq, struct akcipher_request, base); struct virtio_crypto_akcipher_request *vc_akcipher_req = akcipher_request_ctx(req); struct virtio_crypto_request *vc_req = &vc_akcipher_req->base; - struct virtio_crypto_akcipher_ctx *ctx = vc_akcipher_req->akcipher_ctx; + struct crypto_akcipher *atfm = crypto_akcipher_reqtfm(req); + struct virtio_crypto_akcipher_ctx *ctx = akcipher_tfm_ctx(atfm); struct virtio_crypto *vcrypto = ctx->vcrypto; struct data_queue *data_vq = vc_req->dataq; struct virtio_crypto_op_header *header; @@ -318,7 +319,6 @@ static int virtio_crypto_rsa_req(struct akcipher_request *req, uint32_t opcode) vc_req->dataq = data_vq; vc_req->alg_cb = virtio_crypto_dataq_akcipher_callback; - vc_akcipher_req->akcipher_ctx = ctx; vc_akcipher_req->akcipher_req = req; vc_akcipher_req->opcode = opcode; diff --git a/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c b/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c index 495fc655a51cdf..e2a481a29b7774 100644 --- a/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c +++ b/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c @@ -27,7 +27,6 @@ struct virtio_crypto_sym_request { /* Cipher or aead */ uint32_t type; - struct virtio_crypto_skcipher_ctx *skcipher_ctx; struct skcipher_request *skcipher_req; uint8_t *iv; /* Encryption? */ @@ -324,7 +323,7 @@ __virtio_crypto_skcipher_do_req(struct virtio_crypto_sym_request *vc_sym_req, struct data_queue *data_vq) { struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); - struct virtio_crypto_skcipher_ctx *ctx = vc_sym_req->skcipher_ctx; + struct virtio_crypto_skcipher_ctx *ctx = crypto_skcipher_ctx(tfm); struct virtio_crypto_request *vc_req = &vc_sym_req->base; unsigned int ivsize = crypto_skcipher_ivsize(tfm); struct virtio_crypto *vcrypto = ctx->vcrypto; @@ -480,7 +479,6 @@ static int virtio_crypto_skcipher_encrypt(struct skcipher_request *req) vc_req->dataq = data_vq; vc_req->alg_cb = virtio_crypto_dataq_sym_callback; - vc_sym_req->skcipher_ctx = ctx; vc_sym_req->skcipher_req = req; vc_sym_req->encrypt = true; @@ -505,7 +503,6 @@ static int virtio_crypto_skcipher_decrypt(struct skcipher_request *req) vc_req->dataq = data_vq; vc_req->alg_cb = virtio_crypto_dataq_sym_callback; - vc_sym_req->skcipher_ctx = ctx; vc_sym_req->skcipher_req = req; vc_sym_req->encrypt = false; From 2e4f034040f0f29f81d49f097f9fc03cb6f4b70c Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Fri, 6 Sep 2024 12:05:04 +0200 Subject: [PATCH 25/47] crypto: virtio - Drop superfluous [as]kcipher_req pointer The request context virtio_crypto_{akcipher,sym}_request contains a pointer to the [as]kcipher_request itself. The pointer is superfluous as it can be calculated with container_of(). Drop the superfluous pointer. Signed-off-by: Lukas Wunner --- drivers/crypto/virtio/virtio_crypto_akcipher_algs.c | 7 +++---- drivers/crypto/virtio/virtio_crypto_skcipher_algs.c | 8 +++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c index 0df99886d79529..6edcca0d8eb552 100644 --- a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c +++ b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c @@ -35,7 +35,6 @@ struct virtio_crypto_akcipher_ctx { struct virtio_crypto_akcipher_request { struct virtio_crypto_request base; - struct akcipher_request *akcipher_req; void *src_buf; void *dst_buf; uint32_t opcode; @@ -67,7 +66,9 @@ static void virtio_crypto_dataq_akcipher_callback(struct virtio_crypto_request * { struct virtio_crypto_akcipher_request *vc_akcipher_req = container_of(vc_req, struct virtio_crypto_akcipher_request, base); - struct akcipher_request *akcipher_req; + struct akcipher_request *akcipher_req = + container_of((void *)vc_akcipher_req, struct akcipher_request, + __ctx); int error; switch (vc_req->status) { @@ -86,7 +87,6 @@ static void virtio_crypto_dataq_akcipher_callback(struct virtio_crypto_request * break; } - akcipher_req = vc_akcipher_req->akcipher_req; /* actual length may be less than dst buffer */ akcipher_req->dst_len = len - sizeof(vc_req->status); sg_copy_from_buffer(akcipher_req->dst, sg_nents(akcipher_req->dst), @@ -319,7 +319,6 @@ static int virtio_crypto_rsa_req(struct akcipher_request *req, uint32_t opcode) vc_req->dataq = data_vq; vc_req->alg_cb = virtio_crypto_dataq_akcipher_callback; - vc_akcipher_req->akcipher_req = req; vc_akcipher_req->opcode = opcode; return crypto_transfer_akcipher_request_to_engine(data_vq->engine, req); diff --git a/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c b/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c index e2a481a29b7774..1b3fb21a2a7de2 100644 --- a/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c +++ b/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c @@ -27,7 +27,6 @@ struct virtio_crypto_sym_request { /* Cipher or aead */ uint32_t type; - struct skcipher_request *skcipher_req; uint8_t *iv; /* Encryption? */ bool encrypt; @@ -55,7 +54,9 @@ static void virtio_crypto_dataq_sym_callback { struct virtio_crypto_sym_request *vc_sym_req = container_of(vc_req, struct virtio_crypto_sym_request, base); - struct skcipher_request *ablk_req; + struct skcipher_request *ablk_req = + container_of((void *)vc_sym_req, struct skcipher_request, + __ctx); int error; /* Finish the encrypt or decrypt process */ @@ -75,7 +76,6 @@ static void virtio_crypto_dataq_sym_callback error = -EIO; break; } - ablk_req = vc_sym_req->skcipher_req; virtio_crypto_skcipher_finalize_req(vc_sym_req, ablk_req, error); } @@ -479,7 +479,6 @@ static int virtio_crypto_skcipher_encrypt(struct skcipher_request *req) vc_req->dataq = data_vq; vc_req->alg_cb = virtio_crypto_dataq_sym_callback; - vc_sym_req->skcipher_req = req; vc_sym_req->encrypt = true; return crypto_transfer_skcipher_request_to_engine(data_vq->engine, req); @@ -503,7 +502,6 @@ static int virtio_crypto_skcipher_decrypt(struct skcipher_request *req) vc_req->dataq = data_vq; vc_req->alg_cb = virtio_crypto_dataq_sym_callback; - vc_sym_req->skcipher_req = req; vc_sym_req->encrypt = false; return crypto_transfer_skcipher_request_to_engine(data_vq->engine, req); From 99a6be9dadf09e2f7b26e362f45922350e08342e Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sun, 20 Aug 2023 07:19:58 +0200 Subject: [PATCH 26/47] X.509: Make certificate parser public MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 for the definition of time64_t. That definition was since moved to 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 . No functional change intended. Signed-off-by: Lukas Wunner Reviewed-by: Dan Williams Reviewed-by: Alistair Francis Reviewed-by: Ilpo Järvinen Reviewed-by: Jonathan Cameron --- 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 0688c222806b01..39f1521b773dd2 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 -#include -#include -#include - -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 /* * 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 00000000000000..37436a5c752686 --- /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 +#include +#include +#include + +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 */ From 100b59b024668e32074e169889c467cdcdb85fb9 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sun, 20 Aug 2023 08:42:55 +0200 Subject: [PATCH 27/47] X.509: Parse Subject Alternative Name in certificates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Wilfred Mallawa Reviewed-by: Alistair Francis Reviewed-by: Ilpo Järvinen Reviewed-by: Jonathan Cameron Acked-by: Dan Williams --- 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 ee2fdab42334f1..ff1db59d403772 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 37436a5c752686..8e450befe3b937 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; From c13371cd1c26c12bda30ec6a4e3fe4d99dd8954a Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Tue, 10 Jan 2023 14:57:13 +0100 Subject: [PATCH 28/47] X.509: Move certificate length retrieval into new helper 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 Reviewed-by: Dan Williams Reviewed-by: Alistair Francis Reviewed-by: Jonathan Cameron --- 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 a417413269989f..25ff027fad1d39 100644 --- a/crypto/asymmetric_keys/x509_loader.c +++ b/crypto/asymmetric_keys/x509_loader.c @@ -4,28 +4,42 @@ #include #include +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 69a13e1e5b2e5d..e2af07fec3c6fc 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); From f10e214414097d430bdbf64c6ac48bb823a55d33 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sun, 24 Sep 2023 19:10:12 +0200 Subject: [PATCH 29/47] certs: Create blacklist keyring earlier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Dan Williams Reviewed-by: Wilfred Mallawa Reviewed-by: Alistair Francis Reviewed-by: Ilpo Järvinen Reviewed-by: Jonathan Cameron --- certs/blacklist.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/certs/blacklist.c b/certs/blacklist.c index 675dd7a8f07abc..34185415d45197 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 /* From 5660d8ab98fadc70347f8d9ff61f303029230204 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 6 Sep 2022 12:15:54 +0100 Subject: [PATCH 30/47] spdm: Introduce library to authenticate devices The Security Protocol and Data Model (SPDM) allows for device authentication, measurement, key exchange and encrypted sessions. SPDM was conceived by the Distributed Management Task Force (DMTF). Its specification defines a request/response protocol spoken between host and attached devices over a variety of transports: https://www.dmtf.org/dsp/DSP0274 This implementation supports SPDM 1.0 through 1.3 (the latest version). It is designed to be transport-agnostic as the kernel already supports four different SPDM-capable transports: * PCIe Data Object Exchange, which is a mailbox in PCI config space (PCIe r6.2 sec 6.30, drivers/pci/doe.c) * Management Component Transport Protocol (MCTP, Documentation/networking/mctp.rst) * TCP/IP (in draft stage) https://www.dmtf.org/sites/default/files/standards/documents/DSP0287_1.0.0WIP99.pdf * SCSI and ATA (in draft stage) "SECURITY PROTOCOL IN/OUT" and "TRUSTED SEND/RECEIVE" commands Use cases for SPDM include, but are not limited to: * PCIe Component Measurement and Authentication (PCIe r6.2 sec 6.31) * Compute Express Link (CXL r3.0 sec 14.11.6) * Open Compute Project (Attestation of System Components v1.0) https://www.opencompute.org/documents/attestation-v1-0-20201104-pdf * Open Compute Project (Datacenter NVMe SSD Specification v2.0) https://www.opencompute.org/documents/datacenter-nvme-ssd-specification-v2-0r21-pdf The initial focus of this implementation is enabling PCIe CMA device authentication. As such, only a subset of the SPDM specification is contained herein, namely the request/response sequence GET_VERSION, GET_CAPABILITIES, NEGOTIATE_ALGORITHMS, GET_DIGESTS, GET_CERTIFICATE and CHALLENGE. This sequence first negotiates the SPDM protocol version, capabilities and algorithms with the device. It then retrieves the up to eight certificate chains which may be provisioned on the device. Finally it performs challenge-response authentication with the device using one of those eight certificate chains and the algorithms negotiated before. The challenge-response authentication comprises computing a hash over all exchanged messages to detect modification by a man-in-the-middle or media error. The hash is then signed with the device's private key and the resulting signature is verified by the kernel using the device's public key from the certificate chain. Nonces are included in the message sequence to protect against replay attacks. A simple API is provided for subsystems wishing to authenticate devices: spdm_create(), spdm_authenticate() (can be called repeatedly for reauthentication) and spdm_destroy(). Certificates presented by devices are validated against an in-kernel keyring of trusted root certificates. A pointer to the keyring is passed to spdm_create(). The set of supported cryptographic algorithms is limited to those declared mandatory in PCIe r6.2 sec 6.31.3. Adding more algorithms is straightforward as long as the crypto subsystem supports them. Future commits will extend this implementation with support for measurement, key exchange and encrypted sessions. So far, only the SPDM requester role is implemented. Care was taken to allow for effortless addition of the responder role at a later stage. This could be needed for a PCIe host bridge operating in endpoint mode. The responder role will be able to reuse struct definitions and helpers such as spdm_create_combined_prefix(). Credits: Jonathan wrote a proof-of-concept of this SPDM implementation. Lukas reworked it for upstream. Signed-off-by: Jonathan Cameron Co-developed-by: Lukas Wunner Signed-off-by: Lukas Wunner --- MAINTAINERS | 11 + include/linux/spdm.h | 33 ++ lib/Kconfig | 15 + lib/Makefile | 2 + lib/spdm/Makefile | 10 + lib/spdm/core.c | 425 ++++++++++++++++++++++ lib/spdm/req-authenticate.c | 704 ++++++++++++++++++++++++++++++++++++ lib/spdm/spdm.h | 520 ++++++++++++++++++++++++++ 8 files changed, 1720 insertions(+) create mode 100644 include/linux/spdm.h create mode 100644 lib/spdm/Makefile create mode 100644 lib/spdm/core.c create mode 100644 lib/spdm/req-authenticate.c create mode 100644 lib/spdm/spdm.h diff --git a/MAINTAINERS b/MAINTAINERS index c27f3190737f8b..5e1fb4d4bb2161 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20836,6 +20836,17 @@ M: Security Officers S: Supported F: Documentation/process/security-bugs.rst +SECURITY PROTOCOL AND DATA MODEL (SPDM) +M: Jonathan Cameron +M: Lukas Wunner +L: linux-coco@lists.linux.dev +L: linux-cxl@vger.kernel.org +L: linux-pci@vger.kernel.org +S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/devsec/spdm.git +F: include/linux/spdm.h +F: lib/spdm/ + SECURITY SUBSYSTEM M: Paul Moore M: James Morris diff --git a/include/linux/spdm.h b/include/linux/spdm.h new file mode 100644 index 00000000000000..0da7340020c4e7 --- /dev/null +++ b/include/linux/spdm.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * DMTF Security Protocol and Data Model (SPDM) + * https://www.dmtf.org/dsp/DSP0274 + * + * Copyright (C) 2021-22 Huawei + * Jonathan Cameron + * + * Copyright (C) 2022-24 Intel Corporation + */ + +#ifndef _SPDM_H_ +#define _SPDM_H_ + +#include + +struct key; +struct device; +struct spdm_state; + +typedef ssize_t (spdm_transport)(void *priv, struct device *dev, + const void *request, size_t request_sz, + void *response, size_t response_sz); + +struct spdm_state *spdm_create(struct device *dev, spdm_transport *transport, + void *transport_priv, u32 transport_sz, + struct key *keyring); + +int spdm_authenticate(struct spdm_state *spdm_state); + +void spdm_destroy(struct spdm_state *spdm_state); + +#endif diff --git a/lib/Kconfig b/lib/Kconfig index b38849af6f1302..68f46e4a72a60d 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -777,3 +777,18 @@ config POLYNOMIAL config FIRMWARE_TABLE bool + +config SPDM + tristate + select CRYPTO + select KEYS + select ASYMMETRIC_KEY_TYPE + select ASYMMETRIC_PUBLIC_KEY_SUBTYPE + select X509_CERTIFICATE_PARSER + help + The Security Protocol and Data Model (SPDM) allows for device + authentication, measurement, key exchange and encrypted sessions. + + Crypto algorithms negotiated with SPDM are limited to those enabled + in .config. Drivers selecting SPDM therefore need to also select + any algorithms they deem mandatory. diff --git a/lib/Makefile b/lib/Makefile index 773adf88af4166..5c6a4836599309 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -301,6 +301,8 @@ obj-$(CONFIG_PERCPU_TEST) += percpu_test.o obj-$(CONFIG_ASN1) += asn1_decoder.o obj-$(CONFIG_ASN1_ENCODER) += asn1_encoder.o +obj-$(CONFIG_SPDM) += spdm/ + obj-$(CONFIG_FONT_SUPPORT) += fonts/ hostprogs := gen_crc32table diff --git a/lib/spdm/Makefile b/lib/spdm/Makefile new file mode 100644 index 00000000000000..f579cc898dbcd4 --- /dev/null +++ b/lib/spdm/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# DMTF Security Protocol and Data Model (SPDM) +# https://www.dmtf.org/dsp/DSP0274 +# +# Copyright (C) 2024 Intel Corporation + +obj-$(CONFIG_SPDM) += spdm.o + +spdm-y := core.o req-authenticate.o diff --git a/lib/spdm/core.c b/lib/spdm/core.c new file mode 100644 index 00000000000000..714641935955ce --- /dev/null +++ b/lib/spdm/core.c @@ -0,0 +1,425 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DMTF Security Protocol and Data Model (SPDM) + * https://www.dmtf.org/dsp/DSP0274 + * + * Core routines for message exchange, message transcript, + * signature verification and session state lifecycle + * + * Copyright (C) 2021-22 Huawei + * Jonathan Cameron + * + * Copyright (C) 2022-24 Intel Corporation + */ + +#include "spdm.h" + +#include +#include + +#include +#include + +static int spdm_err(struct device *dev, struct spdm_error_rsp *rsp) +{ + switch (rsp->error_code) { + case SPDM_INVALID_REQUEST: + dev_err(dev, "Invalid request\n"); + return -EINVAL; + case SPDM_INVALID_SESSION: + if (rsp->version == 0x11) { + dev_err(dev, "Invalid session %#x\n", rsp->error_data); + return -EINVAL; + } + break; + case SPDM_BUSY: + dev_err(dev, "Busy\n"); + return -EBUSY; + case SPDM_UNEXPECTED_REQUEST: + dev_err(dev, "Unexpected request\n"); + return -EINVAL; + case SPDM_UNSPECIFIED: + dev_err(dev, "Unspecified error\n"); + return -EINVAL; + case SPDM_DECRYPT_ERROR: + dev_err(dev, "Decrypt error\n"); + return -EIO; + case SPDM_UNSUPPORTED_REQUEST: + dev_err(dev, "Unsupported request %#x\n", rsp->error_data); + return -EINVAL; + case SPDM_REQUEST_IN_FLIGHT: + dev_err(dev, "Request in flight\n"); + return -EINVAL; + case SPDM_INVALID_RESPONSE_CODE: + dev_err(dev, "Invalid response code\n"); + return -EINVAL; + case SPDM_SESSION_LIMIT_EXCEEDED: + dev_err(dev, "Session limit exceeded\n"); + return -EBUSY; + case SPDM_SESSION_REQUIRED: + dev_err(dev, "Session required\n"); + return -EINVAL; + case SPDM_RESET_REQUIRED: + dev_err(dev, "Reset required\n"); + return -ECONNRESET; + case SPDM_RESPONSE_TOO_LARGE: + dev_err(dev, "Response too large\n"); + return -EINVAL; + case SPDM_REQUEST_TOO_LARGE: + dev_err(dev, "Request too large\n"); + return -EINVAL; + case SPDM_LARGE_RESPONSE: + dev_err(dev, "Large response\n"); + return -EMSGSIZE; + case SPDM_MESSAGE_LOST: + dev_err(dev, "Message lost\n"); + return -EIO; + case SPDM_INVALID_POLICY: + dev_err(dev, "Invalid policy\n"); + return -EINVAL; + case SPDM_VERSION_MISMATCH: + dev_err(dev, "Version mismatch\n"); + return -EINVAL; + case SPDM_RESPONSE_NOT_READY: + dev_err(dev, "Response not ready\n"); + return -EINPROGRESS; + case SPDM_REQUEST_RESYNCH: + dev_err(dev, "Request resynchronization\n"); + return -ECONNRESET; + case SPDM_OPERATION_FAILED: + dev_err(dev, "Operation failed\n"); + return -EINVAL; + case SPDM_NO_PENDING_REQUESTS: + return -ENOENT; + case SPDM_VENDOR_DEFINED_ERROR: + dev_err(dev, "Vendor defined error\n"); + return -EINVAL; + } + + dev_err(dev, "Undefined error %#x\n", rsp->error_code); + return -EINVAL; +} + +/** + * spdm_exchange() - Perform SPDM message exchange with device + * + * @spdm_state: SPDM session state + * @req: Request message + * @req_sz: Size of @req + * @rsp: Response message + * @rsp_sz: Size of @rsp + * + * Send the request @req to the device via the @transport in @spdm_state and + * receive the response into @rsp, respecting the maximum buffer size @rsp_sz. + * The request version is automatically populated. + * + * Return response size on success or a negative errno. Response size may be + * less than @rsp_sz and the caller is responsible for checking that. It may + * also be more than expected (though never more than @rsp_sz), e.g. if the + * transport receives only dword-sized chunks. + */ +ssize_t spdm_exchange(struct spdm_state *spdm_state, + void *req, size_t req_sz, void *rsp, size_t rsp_sz) +{ + struct spdm_header *request = req; + struct spdm_header *response = rsp; + ssize_t rc, length; + + if (req_sz < sizeof(struct spdm_header) || + rsp_sz < sizeof(struct spdm_header)) + return -EINVAL; + + request->version = spdm_state->version; + + rc = spdm_state->transport(spdm_state->transport_priv, spdm_state->dev, + req, req_sz, rsp, rsp_sz); + if (rc < 0) + return rc; + + length = rc; + if (length < sizeof(struct spdm_header)) + return length; /* Truncated response is handled by callers */ + + if (response->code == SPDM_ERROR) + return spdm_err(spdm_state->dev, (struct spdm_error_rsp *)rsp); + + if (response->code != (request->code & ~SPDM_REQ)) { + dev_err(spdm_state->dev, + "Response code %#x does not match request code %#x\n", + response->code, request->code); + return -EPROTO; + } + + return length; +} + +/** + * spdm_alloc_transcript() - Allocate transcript buffer + * + * @spdm_state: SPDM session state + * + * Allocate a buffer to accommodate the concatenation of all SPDM messages + * exchanged during an authentication sequence. Used to verify the signature, + * as it is computed over the hashed transcript. + * + * Transcript size is initially one page. It grows by additional pages as + * needed. Minimum size of an authentication sequence is 1k (only one slot + * occupied, only one ECC P256 certificate in chain, SHA 256 hash selected). + * Maximum can be several MBytes. Between 4k and 64k is probably typical. + * + * Return 0 on success or a negative errno. + */ +int spdm_alloc_transcript(struct spdm_state *spdm_state) +{ + spdm_state->transcript = kvmalloc(PAGE_SIZE, GFP_KERNEL); + if (!spdm_state->transcript) + return -ENOMEM; + + spdm_state->transcript_end = spdm_state->transcript; + spdm_state->transcript_max = PAGE_SIZE; + + return 0; +} + +/** + * spdm_free_transcript() - Free transcript buffer + * + * @spdm_state: SPDM session state + * + * Free the transcript buffer after performing authentication. Reset the + * pointer to the current end of transcript as well as the allocation size. + */ +void spdm_free_transcript(struct spdm_state *spdm_state) +{ + kvfree(spdm_state->transcript); + spdm_state->transcript_end = NULL; + spdm_state->transcript_max = 0; +} + +/** + * spdm_append_transcript() - Append a message to transcript buffer + * + * @spdm_state: SPDM session state + * @msg: SPDM message + * @msg_sz: Size of @msg + * + * Append an SPDM message to the transcript after reception or transmission. + * Reallocate a larger transcript buffer if the message exceeds its current + * allocation size. + * + * If the message to be appended is known to fit into the allocation size, + * it may be directly received into or transmitted from the transcript buffer + * instead of calling this function: Simply use the @transcript_end pointer in + * struct spdm_state as the position to store the message, then advance the + * pointer by the message size. + * + * Return 0 on success or a negative errno. + */ +int spdm_append_transcript(struct spdm_state *spdm_state, + const void *msg, size_t msg_sz) +{ + size_t transcript_sz = spdm_state->transcript_end - + spdm_state->transcript; + + if (transcript_sz + msg_sz > spdm_state->transcript_max) { + size_t new_sz = round_up(transcript_sz + msg_sz, PAGE_SIZE); + void *new = kvrealloc(spdm_state->transcript, + new_sz, GFP_KERNEL); + if (!new) + return -ENOMEM; + + spdm_state->transcript = new; + spdm_state->transcript_end = new + transcript_sz; + spdm_state->transcript_max = new_sz; + } + + memcpy(spdm_state->transcript_end, msg, msg_sz); + spdm_state->transcript_end += msg_sz; + + return 0; +} + +/** + * spdm_create_combined_prefix() - Create combined_spdm_prefix for a hash + * + * @version: SPDM version negotiated during GET_VERSION exchange + * @spdm_context: SPDM context of signature generation (or verification) + * @buf: Buffer to receive combined_spdm_prefix (100 bytes) + * + * From SPDM 1.2, a hash is prefixed with the SPDM version and context before + * a signature is generated (or verified) over the resulting concatenation + * (SPDM 1.2.0 section 15). Create that prefix. + */ +void spdm_create_combined_prefix(u8 version, const char *spdm_context, + void *buf) +{ + u8 major = FIELD_GET(0xf0, version); + u8 minor = FIELD_GET(0x0f, version); + size_t len = strlen(spdm_context); + int rc, zero_pad; + + rc = snprintf(buf, SPDM_PREFIX_SZ + 1, + "dmtf-spdm-v%hhx.%hhx.*dmtf-spdm-v%hhx.%hhx.*" + "dmtf-spdm-v%hhx.%hhx.*dmtf-spdm-v%hhx.%hhx.*", + major, minor, major, minor, major, minor, major, minor); + WARN_ON(rc != SPDM_PREFIX_SZ); + + zero_pad = SPDM_COMBINED_PREFIX_SZ - SPDM_PREFIX_SZ - 1 - len; + WARN_ON(zero_pad < 0); + + memset(buf + SPDM_PREFIX_SZ + 1, 0, zero_pad); + memcpy(buf + SPDM_PREFIX_SZ + 1 + zero_pad, spdm_context, len); +} + +/** + * spdm_verify_signature() - Verify signature against leaf key + * + * @spdm_state: SPDM session state + * @spdm_context: SPDM context (used to create combined_spdm_prefix) + * + * Implementation of the abstract SPDMSignatureVerify() function described in + * SPDM 1.2.0 section 16: Compute the hash over @spdm_state->transcript and + * verify that the signature at the end of the transcript was generated by + * @spdm_state->leaf_key. Hashing the entire transcript allows detection + * of message modification by a man-in-the-middle or media error. + * + * Return 0 on success or a negative errno. + */ +int spdm_verify_signature(struct spdm_state *spdm_state, + const char *spdm_context) +{ + struct public_key_signature sig = { + .s = spdm_state->transcript_end - spdm_state->sig_len, + .s_size = spdm_state->sig_len, + .encoding = spdm_state->base_asym_enc, + .hash_algo = spdm_state->base_hash_alg_name, + }; + u8 *mhash __free(kfree) = NULL; + u8 *m __free(kfree); + int rc; + + m = kmalloc(SPDM_COMBINED_PREFIX_SZ + spdm_state->hash_len, GFP_KERNEL); + if (!m) + return -ENOMEM; + + /* Hash the transcript (sans trailing signature) */ + rc = crypto_shash_digest(spdm_state->desc, spdm_state->transcript, + (void *)sig.s - spdm_state->transcript, + m + SPDM_COMBINED_PREFIX_SZ); + if (rc) + return rc; + + if (spdm_state->version <= 0x11) { + /* + * SPDM 1.0 and 1.1 compute the signature only over the hash + * (SPDM 1.0.0 section 4.9.2.7). + */ + sig.digest = m + SPDM_COMBINED_PREFIX_SZ; + sig.digest_size = spdm_state->hash_len; + } else { + /* + * From SPDM 1.2, the hash is prefixed with spdm_context before + * computing the signature over the resulting message M + * (SPDM 1.2.0 sec 15). + */ + spdm_create_combined_prefix(spdm_state->version, spdm_context, + m); + + /* + * RSA and ECDSA algorithms require that M is hashed once more. + * EdDSA and SM2 algorithms omit that step. + * The switch statement prepares for their introduction. + */ + switch (spdm_state->base_asym_alg) { + default: + mhash = kmalloc(spdm_state->hash_len, GFP_KERNEL); + if (!mhash) + return -ENOMEM; + + rc = crypto_shash_digest(spdm_state->desc, m, + SPDM_COMBINED_PREFIX_SZ + spdm_state->hash_len, + mhash); + if (rc) + return rc; + + sig.digest = mhash; + sig.digest_size = spdm_state->hash_len; + break; + } + } + + return public_key_verify_signature(spdm_state->leaf_key, &sig); +} + +/** + * spdm_reset() - Free cryptographic data structures + * + * @spdm_state: SPDM session state + * + * Free cryptographic data structures when an SPDM session is destroyed or + * when the device is reauthenticated. + */ +void spdm_reset(struct spdm_state *spdm_state) +{ + public_key_free(spdm_state->leaf_key); + spdm_state->leaf_key = NULL; + + kfree(spdm_state->desc); + spdm_state->desc = NULL; + + crypto_free_shash(spdm_state->shash); + spdm_state->shash = NULL; +} + +/** + * spdm_create() - Allocate SPDM session + * + * @dev: Responder device + * @transport: Transport function to perform one message exchange + * @transport_priv: Transport private data + * @transport_sz: Maximum message size the transport is capable of (in bytes) + * @keyring: Trusted root certificates + * + * Return a pointer to the allocated SPDM session state or NULL on error. + */ +struct spdm_state *spdm_create(struct device *dev, spdm_transport *transport, + void *transport_priv, u32 transport_sz, + struct key *keyring) +{ + struct spdm_state *spdm_state = kzalloc(sizeof(*spdm_state), GFP_KERNEL); + + if (!spdm_state) + return NULL; + + spdm_state->dev = dev; + spdm_state->transport = transport; + spdm_state->transport_priv = transport_priv; + spdm_state->transport_sz = transport_sz; + spdm_state->root_keyring = keyring; + + mutex_init(&spdm_state->lock); + + return spdm_state; +} +EXPORT_SYMBOL_GPL(spdm_create); + +/** + * spdm_destroy() - Destroy SPDM session + * + * @spdm_state: SPDM session state + */ +void spdm_destroy(struct spdm_state *spdm_state) +{ + u8 slot; + + for_each_set_bit(slot, &spdm_state->provisioned_slots, SPDM_SLOTS) + kvfree(spdm_state->slot[slot]); + + spdm_reset(spdm_state); + mutex_destroy(&spdm_state->lock); + kfree(spdm_state); +} +EXPORT_SYMBOL_GPL(spdm_destroy); + +MODULE_DESCRIPTION("DMTF Security Protocol and Data Model"); +MODULE_LICENSE("GPL"); diff --git a/lib/spdm/req-authenticate.c b/lib/spdm/req-authenticate.c new file mode 100644 index 00000000000000..51fdb88f519bbf --- /dev/null +++ b/lib/spdm/req-authenticate.c @@ -0,0 +1,704 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DMTF Security Protocol and Data Model (SPDM) + * https://www.dmtf.org/dsp/DSP0274 + * + * Requester role: Authenticate a device + * + * Copyright (C) 2021-22 Huawei + * Jonathan Cameron + * + * Copyright (C) 2022-24 Intel Corporation + */ + +#include "spdm.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +/* SPDM 1.2.0 margin no 359 and 803 */ +static const char *spdm_context = "responder-challenge_auth signing"; + +/* + * All SPDM messages exchanged during an authentication sequence up to and + * including GET_DIGESTS fit into a single page, hence are stored in the + * transcript without bounds checking. Only subsequent GET_CERTIFICATE + * and CHALLENGE exchanges may exceed one page. + */ +static_assert(PAGE_SIZE >= + sizeof(struct spdm_get_version_req) + + struct_size_t(struct spdm_get_version_rsp, + version_number_entries, 255) + + sizeof(struct spdm_get_capabilities_req) + + sizeof(struct spdm_get_capabilities_rsp) + + sizeof(struct spdm_negotiate_algs_req) + + sizeof(struct spdm_negotiate_algs_rsp) + + sizeof(struct spdm_req_alg_struct) * 2 * SPDM_MAX_REQ_ALG_STRUCT + + sizeof(struct spdm_get_digests_req) + + struct_size_t(struct spdm_get_digests_rsp, + digests, SPDM_SLOTS * SHA512_DIGEST_SIZE)); + +static int spdm_get_version(struct spdm_state *spdm_state) +{ + struct spdm_get_version_req *req = spdm_state->transcript; + struct spdm_get_version_rsp *rsp; + bool foundver = false; + int rc, length, i; + + spdm_state->version = 0x10; + + *req = (struct spdm_get_version_req) { + .code = SPDM_GET_VERSION, + }; + + rsp = spdm_state->transcript_end += sizeof(*req); + + rc = spdm_exchange(spdm_state, req, sizeof(*req), rsp, + struct_size(rsp, version_number_entries, 255)); + if (rc < 0) + return rc; + + length = rc; + if (length < sizeof(*rsp) || + length < struct_size(rsp, version_number_entries, + rsp->version_number_entry_count)) { + dev_err(spdm_state->dev, "Truncated version response\n"); + return -EIO; + } + + spdm_state->transcript_end += + struct_size(rsp, version_number_entries, + rsp->version_number_entry_count); + + for (i = 0; i < rsp->version_number_entry_count; i++) { + u8 ver = le16_to_cpu(rsp->version_number_entries[i]) >> 8; + + if (ver >= spdm_state->version && ver <= SPDM_MAX_VER) { + spdm_state->version = ver; + foundver = true; + } + } + if (!foundver) { + dev_err(spdm_state->dev, "No common supported version\n"); + return -EPROTO; + } + + return 0; +} + +static int spdm_get_capabilities(struct spdm_state *spdm_state) +{ + struct spdm_get_capabilities_req *req = spdm_state->transcript_end; + struct spdm_get_capabilities_rsp *rsp; + size_t req_sz, rsp_sz; + int rc, length; + + *req = (struct spdm_get_capabilities_req) { + .code = SPDM_GET_CAPABILITIES, + .ctexponent = SPDM_CTEXPONENT, + .flags = cpu_to_le32(SPDM_REQ_CAPS), + }; + + if (spdm_state->version == 0x10) { + req_sz = offsetofend(typeof(*req), param2); + rsp_sz = offsetofend(typeof(*rsp), flags); + } else if (spdm_state->version == 0x11) { + req_sz = offsetofend(typeof(*req), flags); + rsp_sz = offsetofend(typeof(*rsp), flags); + } else { + req_sz = sizeof(*req); + rsp_sz = sizeof(*rsp); + req->data_transfer_size = cpu_to_le32(spdm_state->transport_sz); + req->max_spdm_msg_size = cpu_to_le32(spdm_state->transport_sz); + } + + rsp = spdm_state->transcript_end += req_sz; + + rc = spdm_exchange(spdm_state, req, req_sz, rsp, rsp_sz); + if (rc < 0) + return rc; + + length = rc; + if (length < rsp_sz) { + dev_err(spdm_state->dev, "Truncated capabilities response\n"); + return -EIO; + } + + spdm_state->transcript_end += rsp_sz; + + spdm_state->rsp_caps = le32_to_cpu(rsp->flags); + if ((spdm_state->rsp_caps & SPDM_RSP_MIN_CAPS) != SPDM_RSP_MIN_CAPS) + return -EPROTONOSUPPORT; + + if (spdm_state->version >= 0x12) { + u32 data_transfer_size = le32_to_cpu(rsp->data_transfer_size); + if (data_transfer_size < SPDM_MIN_DATA_TRANSFER_SIZE) { + dev_err(spdm_state->dev, + "Malformed capabilities response\n"); + return -EPROTO; + } + spdm_state->transport_sz = min(spdm_state->transport_sz, + data_transfer_size); + } + + return 0; +} + +static int spdm_parse_algs(struct spdm_state *spdm_state) +{ + switch (spdm_state->base_asym_alg) { + case SPDM_ASYM_RSASSA_2048: + spdm_state->sig_len = 256; + spdm_state->base_asym_enc = "pkcs1"; + break; + case SPDM_ASYM_RSASSA_3072: + spdm_state->sig_len = 384; + spdm_state->base_asym_enc = "pkcs1"; + break; + case SPDM_ASYM_RSASSA_4096: + spdm_state->sig_len = 512; + spdm_state->base_asym_enc = "pkcs1"; + break; + case SPDM_ASYM_ECDSA_ECC_NIST_P256: + spdm_state->sig_len = 64; + spdm_state->base_asym_enc = "p1363"; + break; + case SPDM_ASYM_ECDSA_ECC_NIST_P384: + spdm_state->sig_len = 96; + spdm_state->base_asym_enc = "p1363"; + break; + case SPDM_ASYM_ECDSA_ECC_NIST_P521: + spdm_state->sig_len = 132; + spdm_state->base_asym_enc = "p1363"; + break; + default: + dev_err(spdm_state->dev, "Unknown asym algorithm\n"); + return -EINVAL; + } + + switch (spdm_state->base_hash_alg) { + case SPDM_HASH_SHA_256: + spdm_state->base_hash_alg_name = "sha256"; + break; + case SPDM_HASH_SHA_384: + spdm_state->base_hash_alg_name = "sha384"; + break; + case SPDM_HASH_SHA_512: + spdm_state->base_hash_alg_name = "sha512"; + break; + default: + dev_err(spdm_state->dev, "Unknown hash algorithm\n"); + return -EINVAL; + } + + /* + * shash and desc allocations are reused for subsequent measurement + * retrieval, hence are not freed until spdm_reset(). + */ + spdm_state->shash = crypto_alloc_shash(spdm_state->base_hash_alg_name, + 0, 0); + if (!spdm_state->shash) + return -ENOMEM; + + spdm_state->desc = kzalloc(sizeof(*spdm_state->desc) + + crypto_shash_descsize(spdm_state->shash), + GFP_KERNEL); + if (!spdm_state->desc) + return -ENOMEM; + + spdm_state->desc->tfm = spdm_state->shash; + + /* Used frequently to compute offsets, so cache H */ + spdm_state->hash_len = crypto_shash_digestsize(spdm_state->shash); + + return crypto_shash_init(spdm_state->desc); +} + +static int spdm_negotiate_algs(struct spdm_state *spdm_state) +{ + struct spdm_negotiate_algs_req *req = spdm_state->transcript_end; + struct spdm_negotiate_algs_rsp *rsp; + struct spdm_req_alg_struct *req_alg_struct; + size_t req_sz = sizeof(*req); + size_t rsp_sz = sizeof(*rsp); + int rc, length; + + /* Request length shall be <= 128 bytes (SPDM 1.1.0 margin no 185) */ + BUILD_BUG_ON(req_sz > 128); + + *req = (struct spdm_negotiate_algs_req) { + .code = SPDM_NEGOTIATE_ALGS, + .length = cpu_to_le16(req_sz), + .base_asym_algo = cpu_to_le32(SPDM_ASYM_ALGOS), + .base_hash_algo = cpu_to_le32(SPDM_HASH_ALGOS), + }; + + rsp = spdm_state->transcript_end += req_sz; + + rc = spdm_exchange(spdm_state, req, req_sz, rsp, rsp_sz); + if (rc < 0) + return rc; + + length = rc; + if (length < sizeof(*rsp) || + length < sizeof(*rsp) + rsp->param1 * sizeof(*req_alg_struct)) { + dev_err(spdm_state->dev, "Truncated algorithms response\n"); + return -EIO; + } + + /* + * If request contained a ReqAlgStruct not supported by responder, + * the corresponding RespAlgStruct may be omitted in response. + * Calculate the actual (possibly shorter) response length: + */ + spdm_state->transcript_end += + sizeof(*rsp) + rsp->param1 * sizeof(*req_alg_struct); + + spdm_state->base_asym_alg = le32_to_cpu(rsp->base_asym_sel); + spdm_state->base_hash_alg = le32_to_cpu(rsp->base_hash_sel); + + if ((spdm_state->base_asym_alg & SPDM_ASYM_ALGOS) == 0 || + (spdm_state->base_hash_alg & SPDM_HASH_ALGOS) == 0) { + dev_err(spdm_state->dev, "No common supported algorithms\n"); + return -EPROTO; + } + + /* Responder shall select exactly 1 alg (SPDM 1.0.0 table 14) */ + if (hweight32(spdm_state->base_asym_alg) != 1 || + hweight32(spdm_state->base_hash_alg) != 1 || + rsp->ext_asym_sel_count != 0 || + rsp->ext_hash_sel_count != 0 || + rsp->param1 > req->param1) { + dev_err(spdm_state->dev, "Malformed algorithms response\n"); + return -EPROTO; + } + + return spdm_parse_algs(spdm_state); +} + +static int spdm_get_digests(struct spdm_state *spdm_state) +{ + struct spdm_get_digests_req *req = spdm_state->transcript_end; + struct spdm_get_digests_rsp *rsp; + unsigned long deprovisioned_slots; + int rc, length; + size_t rsp_sz; + u8 slot; + + *req = (struct spdm_get_digests_req) { + .code = SPDM_GET_DIGESTS, + }; + + rsp = spdm_state->transcript_end += sizeof(*req); + + /* + * Assume all 8 slots are populated. We know the hash length (and thus + * the response size) because the responder only returns digests for + * the hash algorithm selected during the NEGOTIATE_ALGORITHMS exchange + * (SPDM 1.1.2 margin no 206). + */ + rsp_sz = sizeof(*rsp) + SPDM_SLOTS * spdm_state->hash_len; + + rc = spdm_exchange(spdm_state, req, sizeof(*req), rsp, rsp_sz); + if (rc < 0) + return rc; + + length = rc; + if (length < sizeof(*rsp) || + length < sizeof(*rsp) + hweight8(rsp->param2) * + spdm_state->hash_len) { + dev_err(spdm_state->dev, "Truncated digests response\n"); + return -EIO; + } + + spdm_state->transcript_end += sizeof(*rsp) + hweight8(rsp->param2) * + spdm_state->hash_len; + + deprovisioned_slots = spdm_state->provisioned_slots & ~rsp->param2; + for_each_set_bit(slot, &deprovisioned_slots, SPDM_SLOTS) { + kvfree(spdm_state->slot[slot]); + spdm_state->slot_sz[slot] = 0; + spdm_state->slot[slot] = NULL; + } + + /* + * Authentication-capable endpoints must carry at least 1 cert chain + * (SPDM 1.0.0 section 4.9.2.1). + */ + spdm_state->provisioned_slots = rsp->param2; + if (!spdm_state->provisioned_slots) { + dev_err(spdm_state->dev, "No certificates provisioned\n"); + return -EPROTO; + } + + return 0; +} + +static int spdm_get_certificate(struct spdm_state *spdm_state, u8 slot) +{ + struct spdm_cert_chain *certs __free(kvfree) = NULL; + struct spdm_get_certificate_rsp *rsp __free(kvfree); + struct spdm_get_certificate_req req = { + .code = SPDM_GET_CERTIFICATE, + .param1 = slot, + }; + size_t rsp_sz, total_length, header_length; + u16 remainder_length = 0xffff; + u16 portion_length; + u16 offset = 0; + int rc, length; + + /* + * It is legal for the responder to send more bytes than requested. + * (Note the "should" in SPDM 1.0.0 table 19.) If we allocate a + * too small buffer, we can't calculate the hash over the (truncated) + * response. Only choice is thus to allocate the maximum possible 64k. + */ + rsp_sz = min_t(u32, sizeof(*rsp) + 0xffff, spdm_state->transport_sz); + rsp = kvmalloc(rsp_sz, GFP_KERNEL); + if (!rsp) + return -ENOMEM; + + do { + /* + * If transport_sz is sufficiently large, first request will be + * for offset 0 and length 0xffff, which means entire cert + * chain (SPDM 1.0.0 table 18). + */ + req.offset = cpu_to_le16(offset); + req.length = cpu_to_le16(min_t(size_t, remainder_length, + rsp_sz - sizeof(*rsp))); + + rc = spdm_exchange(spdm_state, &req, sizeof(req), rsp, rsp_sz); + if (rc < 0) + return rc; + + length = rc; + if (length < sizeof(*rsp) || + length < sizeof(*rsp) + le16_to_cpu(rsp->portion_length)) { + dev_err(spdm_state->dev, + "Truncated certificate response\n"); + return -EIO; + } + + portion_length = le16_to_cpu(rsp->portion_length); + remainder_length = le16_to_cpu(rsp->remainder_length); + + rc = spdm_append_transcript(spdm_state, &req, sizeof(req)); + if (rc) + return rc; + + rc = spdm_append_transcript(spdm_state, rsp, + sizeof(*rsp) + portion_length); + if (rc) + return rc; + + /* + * On first response we learn total length of cert chain. + * Should portion_length + remainder_length exceed 0xffff, + * the min() ensures that the malformed check triggers below. + */ + if (!certs) { + total_length = min(portion_length + remainder_length, + 0xffff); + certs = kvmalloc(total_length, GFP_KERNEL); + if (!certs) + return -ENOMEM; + } + + if (!portion_length || + (rsp->param1 & 0xf) != slot || + offset + portion_length + remainder_length != total_length) + { + dev_err(spdm_state->dev, + "Malformed certificate response\n"); + return -EPROTO; + } + + memcpy((u8 *)certs + offset, rsp->cert_chain, portion_length); + offset += portion_length; + } while (remainder_length > 0); + + header_length = sizeof(struct spdm_cert_chain) + spdm_state->hash_len; + + if (total_length < header_length || + total_length != le16_to_cpu(certs->length)) { + dev_err(spdm_state->dev, + "Malformed certificate chain in slot %u\n", slot); + return -EPROTO; + } + + kvfree(spdm_state->slot[slot]); + spdm_state->slot_sz[slot] = total_length; + spdm_state->slot[slot] = no_free_ptr(certs); + + return 0; +} + +static int spdm_validate_cert_chain(struct spdm_state *spdm_state, u8 slot) +{ + struct x509_certificate *cert __free(x509_free_certificate) = NULL; + struct x509_certificate *prev __free(x509_free_certificate) = NULL; + size_t header_length, total_length; + bool is_leaf_cert; + size_t offset = 0; + struct key *key; + int rc, length; + u8 *certs; + + header_length = sizeof(struct spdm_cert_chain) + spdm_state->hash_len; + total_length = spdm_state->slot_sz[slot] - header_length; + certs = (u8 *)spdm_state->slot[slot] + header_length; + + do { + rc = x509_get_certificate_length(certs + offset, + total_length - offset); + if (rc < 0) { + dev_err(spdm_state->dev, "Invalid certificate length " + "at slot %u offset %zu\n", slot, offset); + return rc; + } + + length = rc; + is_leaf_cert = offset + length == total_length; + + cert = x509_cert_parse(certs + offset, length); + if (IS_ERR(cert)) { + dev_err(spdm_state->dev, "Certificate parse error %pe " + "at slot %u offset %zu\n", cert, slot, offset); + return PTR_ERR(cert); + } + if (cert->unsupported_sig) { + dev_err(spdm_state->dev, "Unsupported signature " + "at slot %u offset %zu\n", slot, offset); + return -EKEYREJECTED; + } + if (cert->blacklisted) + return -EKEYREJECTED; + + /* + * Basic Constraints CA value shall be false for leaf cert, + * true for intermediate and root certs (SPDM 1.3.0 table 42). + * Key Usage bit for digital signature shall be set, except + * for GenericCert in slot > 0 (SPDM 1.3.0 margin no 354). + * KeyCertSign bit must be 0 for non-CA (RFC 5280 sec 4.2.1.9). + */ + if ((is_leaf_cert == + test_bit(KEY_EFLAG_CA, &cert->pub->key_eflags)) || + (is_leaf_cert && slot == 0 && + !test_bit(KEY_EFLAG_DIGITALSIG, &cert->pub->key_eflags)) || + (is_leaf_cert && + test_bit(KEY_EFLAG_KEYCERTSIGN, &cert->pub->key_eflags))) { + dev_err(spdm_state->dev, "Malformed certificate " + "at slot %u offset %zu\n", slot, offset); + return -EKEYREJECTED; + } + + if (!prev) { + /* First cert in chain, check against root_keyring */ + key = find_asymmetric_key(spdm_state->root_keyring, + cert->sig->auth_ids[0], + cert->sig->auth_ids[1], + cert->sig->auth_ids[2], + false); + if (IS_ERR(key)) { + dev_info(spdm_state->dev, "Root certificate " + "of slot %u not found in %s " + "keyring: %s\n", slot, + spdm_state->root_keyring->description, + cert->issuer); + return PTR_ERR(key); + } + + rc = verify_signature(key, cert->sig); + key_put(key); + } else { + /* Subsequent cert in chain, check against previous */ + rc = public_key_verify_signature(prev->pub, cert->sig); + } + + if (rc) { + dev_err(spdm_state->dev, "Signature validation error " + "%d at slot %u offset %zu\n", rc, slot, offset); + return rc; + } + + x509_free_certificate(prev); + prev = cert; + cert = ERR_PTR(-ENOKEY); + + offset += length; + } while (offset < total_length); + + /* Steal pub pointer ahead of x509_free_certificate() */ + spdm_state->leaf_key = prev->pub; + prev->pub = NULL; + + return 0; +} + +/** + * spdm_challenge_rsp_sz() - Calculate CHALLENGE_AUTH response size + * + * @spdm_state: SPDM session state + * @rsp: CHALLENGE_AUTH response (optional) + * + * A CHALLENGE_AUTH response contains multiple variable-length fields + * as well as optional fields. This helper eases calculating its size. + * + * If @rsp is %NULL, assume the maximum OpaqueDataLength of 1024 bytes + * (SPDM 1.0.0 table 21). Otherwise read OpaqueDataLength from @rsp. + * OpaqueDataLength can only be > 0 for SPDM 1.0 and 1.1, as they lack + * the OtherParamsSupport field in the NEGOTIATE_ALGORITHMS request. + * For SPDM 1.2+, we do not offer any Opaque Data Formats in that field, + * which forces OpaqueDataLength to 0 (SPDM 1.2.0 margin no 261). + */ +static size_t spdm_challenge_rsp_sz(struct spdm_state *spdm_state, + struct spdm_challenge_rsp *rsp) +{ + size_t size = sizeof(*rsp) /* Header */ + + spdm_state->hash_len /* CertChainHash */ + + SPDM_NONCE_SZ; /* Nonce */ + + if (rsp) + /* May be unaligned if hash algorithm has odd length */ + size += get_unaligned_le16((u8 *)rsp + size); + else + size += SPDM_MAX_OPAQUE_DATA; /* OpaqueData */ + + size += 2; /* OpaqueDataLength */ + + if (spdm_state->version >= 0x13) + size += 8; /* RequesterContext */ + + return size + spdm_state->sig_len; /* Signature */ +} + +static int spdm_challenge(struct spdm_state *spdm_state, u8 slot) +{ + struct spdm_challenge_rsp *rsp __free(kfree); + struct spdm_challenge_req req = { + .code = SPDM_CHALLENGE, + .param1 = slot, + .param2 = 0, /* No measurement summary hash */ + }; + size_t req_sz, rsp_sz, rsp_sz_max; + int rc, length; + + get_random_bytes(&req.nonce, sizeof(req.nonce)); + + if (spdm_state->version <= 0x12) + req_sz = offsetofend(typeof(req), nonce); + else + req_sz = sizeof(req); + + rsp_sz_max = spdm_challenge_rsp_sz(spdm_state, NULL); + rsp = kzalloc(rsp_sz_max, GFP_KERNEL); + if (!rsp) + return -ENOMEM; + + rc = spdm_exchange(spdm_state, &req, req_sz, rsp, rsp_sz_max); + if (rc < 0) + return rc; + + length = rc; + rsp_sz = spdm_challenge_rsp_sz(spdm_state, rsp); + if (length < rsp_sz) { + dev_err(spdm_state->dev, "Truncated challenge_auth response\n"); + return -EIO; + } + + rc = spdm_append_transcript(spdm_state, &req, req_sz); + if (rc) + return rc; + + rc = spdm_append_transcript(spdm_state, rsp, rsp_sz); + if (rc) + return rc; + + /* Verify signature at end of transcript against leaf key */ + rc = spdm_verify_signature(spdm_state, spdm_context); + if (rc) + dev_err(spdm_state->dev, + "Cannot verify challenge_auth signature: %d\n", rc); + else + dev_info(spdm_state->dev, + "Authenticated with certificate slot %u\n", slot); + + return rc; +} + +/** + * spdm_authenticate() - Authenticate device + * + * @spdm_state: SPDM session state + * + * Authenticate a device through a sequence of GET_VERSION, GET_CAPABILITIES, + * NEGOTIATE_ALGORITHMS, GET_DIGESTS, GET_CERTIFICATE and CHALLENGE exchanges. + * + * Perform internal locking to serialize multiple concurrent invocations. + * Can be called repeatedly for reauthentication. + * + * Return 0 on success or a negative errno. In particular, -EPROTONOSUPPORT + * indicates authentication is not supported by the device. + */ +int spdm_authenticate(struct spdm_state *spdm_state) +{ + u8 slot; + int rc; + + mutex_lock(&spdm_state->lock); + spdm_reset(spdm_state); + + rc = spdm_alloc_transcript(spdm_state); + if (rc) + goto unlock; + + rc = spdm_get_version(spdm_state); + if (rc) + goto unlock; + + rc = spdm_get_capabilities(spdm_state); + if (rc) + goto unlock; + + rc = spdm_negotiate_algs(spdm_state); + if (rc) + goto unlock; + + rc = spdm_get_digests(spdm_state); + if (rc) + goto unlock; + + for_each_set_bit(slot, &spdm_state->provisioned_slots, SPDM_SLOTS) { + rc = spdm_get_certificate(spdm_state, slot); + if (rc) + goto unlock; + } + + for_each_set_bit(slot, &spdm_state->provisioned_slots, SPDM_SLOTS) { + rc = spdm_validate_cert_chain(spdm_state, slot); + if (rc == 0) + break; + } + if (rc) + goto unlock; + + rc = spdm_challenge(spdm_state, slot); + +unlock: + if (rc) + spdm_reset(spdm_state); + spdm_state->authenticated = !rc; + spdm_free_transcript(spdm_state); + mutex_unlock(&spdm_state->lock); + return rc; +} +EXPORT_SYMBOL_GPL(spdm_authenticate); diff --git a/lib/spdm/spdm.h b/lib/spdm/spdm.h new file mode 100644 index 00000000000000..3a104959ad53a9 --- /dev/null +++ b/lib/spdm/spdm.h @@ -0,0 +1,520 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * DMTF Security Protocol and Data Model (SPDM) + * https://www.dmtf.org/dsp/DSP0274 + * + * Copyright (C) 2021-22 Huawei + * Jonathan Cameron + * + * Copyright (C) 2022-24 Intel Corporation + */ + +#ifndef _LIB_SPDM_H_ +#define _LIB_SPDM_H_ + +#undef DEFAULT_SYMBOL_NAMESPACE +#define DEFAULT_SYMBOL_NAMESPACE SPDM + +#define dev_fmt(fmt) "SPDM: " fmt + +#include +#include +#include + +/* SPDM versions supported by this implementation */ +#define SPDM_MIN_VER 0x10 +#define SPDM_MAX_VER 0x13 + +/* SPDM capabilities (SPDM 1.1.0 margin no 177, 178) */ +#define SPDM_CACHE_CAP BIT(0) /* 1.0 resp only */ +#define SPDM_CERT_CAP BIT(1) /* 1.0 */ +#define SPDM_CHAL_CAP BIT(2) /* 1.0 */ +#define SPDM_MEAS_CAP_MASK GENMASK(4, 3) /* 1.0 resp only */ +#define SPDM_MEAS_CAP_NO 0 /* 1.0 resp only */ +#define SPDM_MEAS_CAP_MEAS 1 /* 1.0 resp only */ +#define SPDM_MEAS_CAP_MEAS_SIG 2 /* 1.0 resp only */ +#define SPDM_MEAS_FRESH_CAP BIT(5) /* 1.0 resp only */ +#define SPDM_ENCRYPT_CAP BIT(6) /* 1.1 */ +#define SPDM_MAC_CAP BIT(7) /* 1.1 */ +#define SPDM_MUT_AUTH_CAP BIT(8) /* 1.1 */ +#define SPDM_KEY_EX_CAP BIT(9) /* 1.1 */ +#define SPDM_PSK_CAP_MASK GENMASK(11, 10) /* 1.1 */ +#define SPDM_PSK_CAP_NO 0 /* 1.1 */ +#define SPDM_PSK_CAP_PSK 1 /* 1.1 */ +#define SPDM_PSK_CAP_PSK_CTX 2 /* 1.1 resp only */ +#define SPDM_ENCAP_CAP BIT(12) /* 1.1 */ +#define SPDM_HBEAT_CAP BIT(13) /* 1.1 */ +#define SPDM_KEY_UPD_CAP BIT(14) /* 1.1 */ +#define SPDM_HANDSHAKE_ITC_CAP BIT(15) /* 1.1 */ +#define SPDM_PUB_KEY_ID_CAP BIT(16) /* 1.1 */ +#define SPDM_CHUNK_CAP BIT(17) /* 1.2 */ +#define SPDM_ALIAS_CERT_CAP BIT(18) /* 1.2 resp only */ +#define SPDM_SET_CERT_CAP BIT(19) /* 1.2 resp only */ +#define SPDM_CSR_CAP BIT(20) /* 1.2 resp only */ +#define SPDM_CERT_INST_RESET_CAP BIT(21) /* 1.2 resp only */ +#define SPDM_EP_INFO_CAP_MASK GENMASK(23, 22) /* 1.3 */ +#define SPDM_EP_INFO_CAP_NO 0 /* 1.3 */ +#define SPDM_EP_INFO_CAP_RSP 1 /* 1.3 */ +#define SPDM_EP_INFO_CAP_RSP_SIG 2 /* 1.3 */ +#define SPDM_MEL_CAP BIT(24) /* 1.3 resp only */ +#define SPDM_EVENT_CAP BIT(25) /* 1.3 */ +#define SPDM_MULTI_KEY_CAP_MASK GENMASK(27, 26) /* 1.3 */ +#define SPDM_MULTI_KEY_CAP_NO 0 /* 1.3 */ +#define SPDM_MULTI_KEY_CAP_ONLY 1 /* 1.3 */ +#define SPDM_MULTI_KEY_CAP_SEL 2 /* 1.3 */ +#define SPDM_GET_KEY_PAIR_INFO_CAP BIT(28) /* 1.3 resp only */ +#define SPDM_SET_KEY_PAIR_INFO_CAP BIT(29) /* 1.3 resp only */ + +/* SPDM capabilities supported by this implementation */ +#define SPDM_REQ_CAPS (SPDM_CERT_CAP | SPDM_CHAL_CAP) + +/* SPDM capabilities required from responders */ +#define SPDM_RSP_MIN_CAPS (SPDM_CERT_CAP | SPDM_CHAL_CAP) + +/* + * SPDM cryptographic timeout of this implementation: + * Assume calculations may take up to 1 sec on a busy machine, which equals + * roughly 1 << 20. That's within the limits mandated for responders by CMA + * (1 << 23 usec, PCIe r6.2 sec 6.31.3) and DOE (1 sec, PCIe r6.2 sec 6.30.2). + * Used in GET_CAPABILITIES exchange. + */ +#define SPDM_CTEXPONENT 20 + +/* SPDM asymmetric key signature algorithms (SPDM 1.0.0 table 13) */ +#define SPDM_ASYM_RSASSA_2048 BIT(0) /* 1.0 */ +#define SPDM_ASYM_RSAPSS_2048 BIT(1) /* 1.0 */ +#define SPDM_ASYM_RSASSA_3072 BIT(2) /* 1.0 */ +#define SPDM_ASYM_RSAPSS_3072 BIT(3) /* 1.0 */ +#define SPDM_ASYM_ECDSA_ECC_NIST_P256 BIT(4) /* 1.0 */ +#define SPDM_ASYM_RSASSA_4096 BIT(5) /* 1.0 */ +#define SPDM_ASYM_RSAPSS_4096 BIT(6) /* 1.0 */ +#define SPDM_ASYM_ECDSA_ECC_NIST_P384 BIT(7) /* 1.0 */ +#define SPDM_ASYM_ECDSA_ECC_NIST_P521 BIT(8) /* 1.0 */ +#define SPDM_ASYM_SM2_ECC_SM2_P256 BIT(9) /* 1.2 */ +#define SPDM_ASYM_EDDSA_ED25519 BIT(10) /* 1.2 */ +#define SPDM_ASYM_EDDSA_ED448 BIT(11) /* 1.2 */ + +/* SPDM hash algorithms (SPDM 1.0.0 table 13) */ +#define SPDM_HASH_SHA_256 BIT(0) /* 1.0 */ +#define SPDM_HASH_SHA_384 BIT(1) /* 1.0 */ +#define SPDM_HASH_SHA_512 BIT(2) /* 1.0 */ +#define SPDM_HASH_SHA3_256 BIT(3) /* 1.0 */ +#define SPDM_HASH_SHA3_384 BIT(4) /* 1.0 */ +#define SPDM_HASH_SHA3_512 BIT(5) /* 1.0 */ +#define SPDM_HASH_SM3_256 BIT(6) /* 1.2 */ + +#if IS_ENABLED(CONFIG_CRYPTO_RSA) +#define SPDM_ASYM_RSA SPDM_ASYM_RSASSA_2048 | \ + SPDM_ASYM_RSASSA_3072 | \ + SPDM_ASYM_RSASSA_4096 +#else +#define SPDM_ASYM_RSA 0 +#endif + +#if IS_ENABLED(CONFIG_CRYPTO_ECDSA) +#define SPDM_ASYM_ECDSA SPDM_ASYM_ECDSA_ECC_NIST_P256 | \ + SPDM_ASYM_ECDSA_ECC_NIST_P384 | \ + SPDM_ASYM_ECDSA_ECC_NIST_P521 +#else +#define SPDM_ASYM_ECDSA 0 +#endif + +#if IS_ENABLED(CONFIG_CRYPTO_SHA256) +#define SPDM_HASH_SHA2_256 SPDM_HASH_SHA_256 +#else +#define SPDM_HASH_SHA2_256 0 +#endif + +#if IS_ENABLED(CONFIG_CRYPTO_SHA512) +#define SPDM_HASH_SHA2_384_512 SPDM_HASH_SHA_384 | \ + SPDM_HASH_SHA_512 +#else +#define SPDM_HASH_SHA2_384_512 0 +#endif + +/* SPDM algorithms supported by this implementation */ +#define SPDM_ASYM_ALGOS (SPDM_ASYM_RSA | \ + SPDM_ASYM_ECDSA) + +#define SPDM_HASH_ALGOS (SPDM_HASH_SHA2_256 | \ + SPDM_HASH_SHA2_384_512) + +/* + * Common header shared by all messages. + * Note that the meaning of param1 and param2 is message dependent. + */ +struct spdm_header { + u8 version; + u8 code; /* RequestResponseCode */ + u8 param1; + u8 param2; +} __packed; + +#define SPDM_REQ 0x80 +#define SPDM_GET_VERSION 0x84 + +struct spdm_get_version_req { + u8 version; + u8 code; + u8 param1; + u8 param2; +} __packed; + +struct spdm_get_version_rsp { + u8 version; + u8 code; + u8 param1; + u8 param2; + + u8 reserved; + u8 version_number_entry_count; + __le16 version_number_entries[] __counted_by(version_number_entry_count); +} __packed; + +#define SPDM_GET_CAPABILITIES 0xe1 +#define SPDM_MIN_DATA_TRANSFER_SIZE 42 /* SPDM 1.2.0 margin no 226 */ + +/* + * Newer SPDM versions insert fields at the end of messages (enlarging them) + * or use reserved space for new fields (leaving message size unchanged). + */ +struct spdm_get_capabilities_req { + u8 version; + u8 code; + u8 param1; + u8 param2; + /* End of SPDM 1.0 structure */ + + u8 reserved1; /* 1.1 */ + u8 ctexponent; /* 1.1 */ + u16 reserved2; /* 1.1 */ + __le32 flags; /* 1.1 */ + /* End of SPDM 1.1 structure */ + + __le32 data_transfer_size; /* 1.2 */ + __le32 max_spdm_msg_size; /* 1.2 */ +} __packed; + +struct spdm_get_capabilities_rsp { + u8 version; + u8 code; + u8 param1; + u8 param2; + + u8 reserved1; + u8 ctexponent; + u16 reserved2; + __le32 flags; + /* End of SPDM 1.0 structure */ + + __le32 data_transfer_size; /* 1.2 */ + __le32 max_spdm_msg_size; /* 1.2 */ + /* End of SPDM 1.2 structure */ + + /* + * Additional optional fields at end of this structure: + * - SupportedAlgorithms: variable size * 1.3 * + */ +} __packed; + +#define SPDM_NEGOTIATE_ALGS 0xe3 + +struct spdm_negotiate_algs_req { + u8 version; + u8 code; + u8 param1; /* Number of ReqAlgStruct entries at end */ + u8 param2; + + __le16 length; + u8 measurement_specification; + u8 other_params_support; /* 1.2 */ + + __le32 base_asym_algo; + __le32 base_hash_algo; + + u8 reserved1[12]; + u8 ext_asym_count; + u8 ext_hash_count; + u8 reserved2; + u8 mel_specification; /* 1.3 */ + + /* + * Additional optional fields at end of this structure: + * - ExtAsym: 4 bytes * ext_asym_count + * - ExtHash: 4 bytes * ext_hash_count + * - ReqAlgStruct: variable size * param1 * 1.1 * + */ +} __packed; + +struct spdm_negotiate_algs_rsp { + u8 version; + u8 code; + u8 param1; /* Number of RespAlgStruct entries at end */ + u8 param2; + + __le16 length; + u8 measurement_specification_sel; + u8 other_params_sel; /* 1.2 */ + + __le32 measurement_hash_algo; + __le32 base_asym_sel; + __le32 base_hash_sel; + + u8 reserved1[11]; + u8 mel_specification_sel; /* 1.3 */ + u8 ext_asym_sel_count; /* Either 0 or 1 */ + u8 ext_hash_sel_count; /* Either 0 or 1 */ + u8 reserved2[2]; + + /* + * Additional optional fields at end of this structure: + * - ExtAsym: 4 bytes * ext_asym_count + * - ExtHash: 4 bytes * ext_hash_count + * - RespAlgStruct: variable size * param1 * 1.1 * + */ +} __packed; + +/* Maximum number of ReqAlgStructs sent by this implementation */ +#define SPDM_MAX_REQ_ALG_STRUCT 0 + +struct spdm_req_alg_struct { + u8 alg_type; + u8 alg_count; /* 0x2K where K is number of alg_external entries */ + __le16 alg_supported; /* Size is in alg_count[7:4], always 2 */ + __le32 alg_external[]; +} __packed; + +#define SPDM_GET_DIGESTS 0x81 + +struct spdm_get_digests_req { + u8 version; + u8 code; + u8 param1; /* Reserved */ + u8 param2; /* Reserved */ +} __packed; + +struct spdm_get_digests_rsp { + u8 version; + u8 code; + u8 param1; /* SupportedSlotMask */ /* 1.3 */ + u8 param2; /* ProvisionedSlotMask */ + u8 digests[]; /* Hash of struct spdm_cert_chain for each slot */ + /* End of SPDM 1.2 (and earlier) structure */ + + /* + * Additional optional fields at end of this structure: + * (omitted as long as we do not advertise MULTI_KEY_CAP) + * - KeyPairID: 1 byte for each slot * 1.3 * + * - CertificateInfo: 1 byte for each slot * 1.3 * + * - KeyUsageMask: 2 bytes for each slot * 1.3 * + */ +} __packed; + +#define SPDM_GET_CERTIFICATE 0x82 +#define SPDM_SLOTS 8 /* SPDM 1.0.0 section 4.9.2.1 */ + +struct spdm_get_certificate_req { + u8 version; + u8 code; + u8 param1; /* Slot number 0..7 */ + u8 param2; /* SlotSizeRequested */ /* 1.3 */ + __le16 offset; + __le16 length; +} __packed; + +struct spdm_get_certificate_rsp { + u8 version; + u8 code; + u8 param1; /* Slot number 0..7 */ + u8 param2; /* CertificateInfo */ /* 1.3 */ + __le16 portion_length; + __le16 remainder_length; + u8 cert_chain[]; /* PortionLength long */ +} __packed; + +struct spdm_cert_chain { + __le16 length; + u8 reserved[2]; + /* + * Additional fields at end of this structure: + * - RootHash: Digest of Root Certificate + * - Certificates: Chain of ASN.1 DER-encoded X.509 v3 certificates + */ +} __packed; + +#define SPDM_CHALLENGE 0x83 +#define SPDM_NONCE_SZ 32 /* SPDM 1.0.0 table 20 */ +#define SPDM_PREFIX_SZ 64 /* SPDM 1.2.0 margin no 803 */ +#define SPDM_COMBINED_PREFIX_SZ 100 /* SPDM 1.2.0 margin no 806 */ +#define SPDM_MAX_OPAQUE_DATA 1024 /* SPDM 1.0.0 table 21 */ + +struct spdm_challenge_req { + u8 version; + u8 code; + u8 param1; /* Slot number 0..7 */ + u8 param2; /* MeasurementSummaryHash type */ + u8 nonce[SPDM_NONCE_SZ]; + /* End of SPDM 1.2 (and earlier) structure */ + + u8 context[8]; /* 1.3 */ +} __packed; + +struct spdm_challenge_rsp { + u8 version; + u8 code; + u8 param1; /* Slot number 0..7 */ + u8 param2; /* Slot mask */ + /* + * Additional fields at end of this structure: + * - CertChainHash: Hash of struct spdm_cert_chain for selected slot + * - Nonce: 32 bytes long + * - MeasurementSummaryHash: Optional hash of selected measurements + * - OpaqueDataLength: 2 bytes long + * - OpaqueData: Up to 1024 bytes long + * - RequesterContext: 8 bytes long * 1.3 * + * (inserted, moves Signature field) + * - Signature + */ +} __packed; + +#define SPDM_ERROR 0x7f + +enum spdm_error_code { + SPDM_INVALID_REQUEST = 0x01, /* 1.0 */ + SPDM_INVALID_SESSION = 0x02, /* 1.1 only */ + SPDM_BUSY = 0x03, /* 1.0 */ + SPDM_UNEXPECTED_REQUEST = 0x04, /* 1.0 */ + SPDM_UNSPECIFIED = 0x05, /* 1.0 */ + SPDM_DECRYPT_ERROR = 0x06, /* 1.1 */ + SPDM_UNSUPPORTED_REQUEST = 0x07, /* 1.0 */ + SPDM_REQUEST_IN_FLIGHT = 0x08, /* 1.1 */ + SPDM_INVALID_RESPONSE_CODE = 0x09, /* 1.1 */ + SPDM_SESSION_LIMIT_EXCEEDED = 0x0a, /* 1.1 */ + SPDM_SESSION_REQUIRED = 0x0b, /* 1.2 */ + SPDM_RESET_REQUIRED = 0x0c, /* 1.2 */ + SPDM_RESPONSE_TOO_LARGE = 0x0d, /* 1.2 */ + SPDM_REQUEST_TOO_LARGE = 0x0e, /* 1.2 */ + SPDM_LARGE_RESPONSE = 0x0f, /* 1.2 */ + SPDM_MESSAGE_LOST = 0x10, /* 1.2 */ + SPDM_INVALID_POLICY = 0x11, /* 1.3 */ + SPDM_VERSION_MISMATCH = 0x41, /* 1.0 */ + SPDM_RESPONSE_NOT_READY = 0x42, /* 1.0 */ + SPDM_REQUEST_RESYNCH = 0x43, /* 1.0 */ + SPDM_OPERATION_FAILED = 0x44, /* 1.3 */ + SPDM_NO_PENDING_REQUESTS = 0x45, /* 1.3 */ + SPDM_VENDOR_DEFINED_ERROR = 0xff, /* 1.0 */ +}; + +struct spdm_error_rsp { + u8 version; + u8 code; + enum spdm_error_code error_code:8; + u8 error_data; + + u8 extended_error_data[]; +} __packed; + +/** + * struct spdm_state - SPDM session state + * + * @dev: Responder device. Used for error reporting and passed to @transport. + * @lock: Serializes multiple concurrent spdm_authenticate() calls. + * @authenticated: Whether device was authenticated successfully. + * @dev: Responder device. Used for error reporting and passed to @transport. + * @transport: Transport function to perform one message exchange. + * @transport_priv: Transport private data. + * @transport_sz: Maximum message size the transport is capable of (in bytes). + * Used as DataTransferSize in GET_CAPABILITIES exchange. + * @version: Maximum common supported version of requester and responder. + * Negotiated during GET_VERSION exchange. + * @rsp_caps: Cached capabilities of responder. + * Received during GET_CAPABILITIES exchange. + * @base_asym_alg: Asymmetric key algorithm for signature verification of + * CHALLENGE_AUTH messages. + * Selected by responder during NEGOTIATE_ALGORITHMS exchange. + * @base_hash_alg: Hash algorithm for signature verification of + * CHALLENGE_AUTH messages. + * Selected by responder during NEGOTIATE_ALGORITHMS exchange. + * @provisioned_slots: Bitmask of responder's provisioned certificate slots. + * Received during GET_DIGESTS exchange. + * @base_asym_enc: Human-readable name of @base_asym_alg's signature encoding. + * Passed to crypto subsystem when calling verify_signature(). + * @sig_len: Signature length of @base_asym_alg (in bytes). + * S or SigLen in SPDM specification. + * @base_hash_alg_name: Human-readable name of @base_hash_alg. + * Passed to crypto subsystem when calling crypto_alloc_shash() and + * verify_signature(). + * @shash: Synchronous hash handle for @base_hash_alg computation. + * @desc: Synchronous hash context for @base_hash_alg computation. + * @hash_len: Hash length of @base_hash_alg (in bytes). + * H in SPDM specification. + * @slot: Certificate chain in each of the 8 slots. NULL pointer if a slot is + * not populated. Prefixed by the 4 + H header per SPDM 1.0.0 table 15. + * @slot_sz: Certificate chain size (in bytes). + * @leaf_key: Public key portion of leaf certificate against which to check + * responder's signatures. + * @root_keyring: Keyring against which to check the first certificate in + * responder's certificate chain. + * @transcript: Concatenation of all SPDM messages exchanged during an + * authentication sequence. Used to verify the signature, as it is + * computed over the hashed transcript. + * @transcript_end: Pointer into the @transcript buffer. Marks the current + * end of transcript. If another message is transmitted, it is appended + * at this position. + * @transcript_max: Allocation size of @transcript. Multiple of PAGE_SIZE. + */ +struct spdm_state { + struct device *dev; + struct mutex lock; + unsigned int authenticated:1; + + /* Transport */ + spdm_transport *transport; + void *transport_priv; + u32 transport_sz; + + /* Negotiated state */ + u8 version; + u32 rsp_caps; + u32 base_asym_alg; + u32 base_hash_alg; + unsigned long provisioned_slots; + + /* Signature algorithm */ + const char *base_asym_enc; + size_t sig_len; + + /* Hash algorithm */ + const char *base_hash_alg_name; + struct crypto_shash *shash; + struct shash_desc *desc; + size_t hash_len; + + /* Certificates */ + struct spdm_cert_chain *slot[SPDM_SLOTS]; + size_t slot_sz[SPDM_SLOTS]; + struct public_key *leaf_key; + struct key *root_keyring; + + /* Transcript */ + void *transcript; + void *transcript_end; + size_t transcript_max; +}; + +ssize_t spdm_exchange(struct spdm_state *spdm_state, + void *req, size_t req_sz, void *rsp, size_t rsp_sz); + +int spdm_alloc_transcript(struct spdm_state *spdm_state); +void spdm_free_transcript(struct spdm_state *spdm_state); +int spdm_append_transcript(struct spdm_state *spdm_state, + const void *msg, size_t msg_sz); + +void spdm_create_combined_prefix(u8 version, const char *spdm_context, + void *buf); +int spdm_verify_signature(struct spdm_state *spdm_state, + const char *spdm_context); + +void spdm_reset(struct spdm_state *spdm_state); + +#endif /* _LIB_SPDM_H_ */ From 5ef54f2a66acd62b9ec26e85371c8fce3074917d Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 6 Sep 2022 12:15:55 +0100 Subject: [PATCH 31/47] PCI/CMA: Authenticate devices on enumeration 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 Co-developed-by: Wilfred Mallawa Signed-off-by: Wilfred Mallawa Co-developed-by: Lukas Wunner Signed-off-by: Lukas Wunner --- MAINTAINERS | 1 + drivers/pci/Kconfig | 13 ++++++ drivers/pci/Makefile | 2 + drivers/pci/cma.c | 100 ++++++++++++++++++++++++++++++++++++++++ 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, 134 insertions(+), 3 deletions(-) create mode 100644 drivers/pci/cma.c diff --git a/MAINTAINERS b/MAINTAINERS index 5e1fb4d4bb2161..79bd7695039c9a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20844,6 +20844,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* F: include/linux/spdm.h F: lib/spdm/ diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 0d94e4a967d81d..f1c39a6477a5ed 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 SPDM + 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 374c5c06d92f9d..c557f5a3d53f54 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -37,6 +37,8 @@ obj-$(CONFIG_PCI_DOE) += doe.o obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) += of_property.o obj-$(CONFIG_PCI_NPEM) += npem.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 00000000000000..275338b95640e1 --- /dev/null +++ b/drivers/pci/cma.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Component Measurement and Authentication (CMA-SPDM, PCIe r6.2 sec 6.31) + * + * Copyright (C) 2021 Huawei + * Jonathan Cameron + * + * Copyright (C) 2022-24 Intel Corporation + */ + +#define dev_fmt(fmt) "CMA: " fmt + +#include +#include +#include +#include + +#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); + 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 652d63df9d22e5..34bb8f23279908 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 14d00ce45bfa95..713f894dfc7c1d 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -416,6 +416,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 4f68414c308609..162f72a543ae7e 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2495,6 +2495,7 @@ static void pci_init_capabilities(struct pci_dev *dev) pci_dpc_init(dev); /* Downstream Port Containment */ pci_rcec_init(dev); /* Root Complex Event Collector */ pci_doe_init(dev); /* Data Object Exchange */ + 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 e4ce1145aa3ee0..387db9224cfa39 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -58,6 +58,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 1f14aed4354b8b..0d3d7656c4565f 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 573b4c4c2be61f..4a4583fd92eba3 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -518,6 +519,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 From fa45b9f03250bee7a22abf7d16f92499311fa97f Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sun, 20 Aug 2023 08:45:46 +0200 Subject: [PATCH 32/47] PCI/CMA: Validate Subject Alternative Name in certificates 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 Reviewed-by: Jonathan Cameron # except ASN.1 --- drivers/pci/Makefile | 4 +- drivers/pci/cma.asn1 | 41 ++++++++++++ drivers/pci/cma.c | 124 ++++++++++++++++++++++++++++++++++- include/linux/oid_registry.h | 3 + include/linux/spdm.h | 6 +- lib/spdm/core.c | 5 +- lib/spdm/req-authenticate.c | 6 ++ lib/spdm/spdm.h | 2 + 8 files changed, 187 insertions(+), 4 deletions(-) create mode 100644 drivers/pci/cma.asn1 diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index c557f5a3d53f54..f2ffe7dc7b7212 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -37,7 +37,9 @@ obj-$(CONFIG_PCI_DOE) += doe.o obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) += of_property.o obj-$(CONFIG_PCI_NPEM) += npem.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 00000000000000..da41421d408567 --- /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 275338b95640e1..e974d489c7a2b4 100644 --- a/drivers/pci/cma.c +++ b/drivers/pci/cma.c @@ -10,16 +10,137 @@ #define dev_fmt(fmt) "CMA: " fmt +#include +#include +#include #include #include #include #include +#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, @@ -62,7 +183,8 @@ void pci_cma_init(struct pci_dev *pdev) return; pdev->spdm_state = spdm_create(&pdev->dev, pci_doe_transport, doe, - PCI_DOE_MAX_PAYLOAD, pci_cma_keyring); + PCI_DOE_MAX_PAYLOAD, pci_cma_keyring, + pci_cma_validate); if (!pdev->spdm_state) return; diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h index 6f9242259edc48..44679f0a3fd646 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 }; diff --git a/include/linux/spdm.h b/include/linux/spdm.h index 0da7340020c4e7..568c68b17f1f0e 100644 --- a/include/linux/spdm.h +++ b/include/linux/spdm.h @@ -17,14 +17,18 @@ 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); + struct key *keyring, spdm_validate *validate); int spdm_authenticate(struct spdm_state *spdm_state); diff --git a/lib/spdm/core.c b/lib/spdm/core.c index 714641935955ce..8be4cba29c4b8e 100644 --- a/lib/spdm/core.c +++ b/lib/spdm/core.c @@ -379,12 +379,14 @@ void spdm_reset(struct spdm_state *spdm_state) * @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. */ struct spdm_state *spdm_create(struct device *dev, spdm_transport *transport, void *transport_priv, u32 transport_sz, - struct key *keyring) + struct key *keyring, spdm_validate *validate) { struct spdm_state *spdm_state = kzalloc(sizeof(*spdm_state), GFP_KERNEL); @@ -396,6 +398,7 @@ struct spdm_state *spdm_create(struct device *dev, spdm_transport *transport, spdm_state->transport_priv = transport_priv; spdm_state->transport_sz = transport_sz; spdm_state->root_keyring = keyring; + spdm_state->validate = validate; mutex_init(&spdm_state->lock); diff --git a/lib/spdm/req-authenticate.c b/lib/spdm/req-authenticate.c index 51fdb88f519bbf..90f7a7f2629c62 100644 --- a/lib/spdm/req-authenticate.c +++ b/lib/spdm/req-authenticate.c @@ -537,6 +537,12 @@ static int spdm_validate_cert_chain(struct spdm_state *spdm_state, u8 slot) offset += length; } while (offset < total_length); + if (spdm_state->validate) { + rc = spdm_state->validate(spdm_state->dev, slot, prev); + if (rc) + return rc; + } + /* Steal pub pointer ahead of x509_free_certificate() */ spdm_state->leaf_key = prev->pub; prev->pub = NULL; diff --git a/lib/spdm/spdm.h b/lib/spdm/spdm.h index 3a104959ad53a9..0e3bb6e18d91ff 100644 --- a/lib/spdm/spdm.h +++ b/lib/spdm/spdm.h @@ -455,6 +455,7 @@ struct spdm_error_rsp { * responder's signatures. * @root_keyring: Keyring against which to check the first certificate in * responder's certificate chain. + * @validate: Function to validate additional leaf certificate requirements. * @transcript: Concatenation of all SPDM messages exchanged during an * authentication sequence. Used to verify the signature, as it is * computed over the hashed transcript. @@ -495,6 +496,7 @@ struct spdm_state { size_t slot_sz[SPDM_SLOTS]; struct public_key *leaf_key; struct key *root_keyring; + spdm_validate *validate; /* Transcript */ void *transcript; From a314ae0119821acc640eba60c73f45d1116a8330 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 16 Jan 2023 20:29:04 +0100 Subject: [PATCH 33/47] PCI/CMA: Reauthenticate devices on reset and resume 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 Reviewed-by: Alistair Francis --- 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 e974d489c7a2b4..f2c435b04b926b 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 35270172c83318..107af140907cfc 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 7d85c04fbba2ae..c2c456cd4a2154 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5076,8 +5076,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 713f894dfc7c1d..86d33a8bca3610 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -419,9 +419,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 31090770fffcc9..0028582f05907f 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) From 08fca606e88ada05124912ec174b9ac4d5dc1022 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sun, 28 May 2023 00:56:28 +0200 Subject: [PATCH 34/47] PCI/CMA: Expose in sysfs whether devices are authenticated 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 --- Documentation/ABI/testing/sysfs-devices-spdm | 31 +++++++ MAINTAINERS | 1 + 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 +++ include/linux/spdm.h | 2 + lib/spdm/Makefile | 1 + lib/spdm/req-sysfs.c | 95 ++++++++++++++++++++ lib/spdm/spdm.h | 1 + 11 files changed, 161 insertions(+), 4 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-devices-spdm create mode 100644 lib/spdm/req-sysfs.c diff --git a/Documentation/ABI/testing/sysfs-devices-spdm b/Documentation/ABI/testing/sysfs-devices-spdm new file mode 100644 index 00000000000000..2d6e5d513231a6 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-spdm @@ -0,0 +1,31 @@ +What: /sys/devices/.../authenticated +Date: June 2024 +Contact: Lukas Wunner +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 79bd7695039c9a..4c2b2483f68687 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20844,6 +20844,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: Documentation/ABI/testing/sysfs-devices-spdm F: drivers/pci/cma* F: include/linux/spdm.h F: lib/spdm/ diff --git a/drivers/pci/cma.c b/drivers/pci/cma.c index f2c435b04b926b..59558714f1432b 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 34bb8f23279908..0f94c4ed719eca 100644 --- a/drivers/pci/doe.c +++ b/drivers/pci/doe.c @@ -694,6 +694,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; } @@ -702,6 +703,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 5d0f4db1cab786..d070f7ca725133 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1666,6 +1666,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 86d33a8bca3610..d7e77e6fcf12a9 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -420,10 +420,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 4a4583fd92eba3..2b4bb303c8879d 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2677,6 +2677,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 #define pci_printk(level, pdev, fmt, arg...) \ diff --git a/include/linux/spdm.h b/include/linux/spdm.h index 568c68b17f1f0e..9835a3202a0e86 100644 --- a/include/linux/spdm.h +++ b/include/linux/spdm.h @@ -34,4 +34,6 @@ 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/spdm/Makefile b/lib/spdm/Makefile index f579cc898dbcd4..edd4a3cc3f5c3e 100644 --- a/lib/spdm/Makefile +++ b/lib/spdm/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_SPDM) += spdm.o spdm-y := core.o req-authenticate.o +spdm-$(CONFIG_SYSFS) += req-sysfs.o diff --git a/lib/spdm/req-sysfs.c b/lib/spdm/req-sysfs.c new file mode 100644 index 00000000000000..9bbed7abc15385 --- /dev/null +++ b/lib/spdm/req-sysfs.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DMTF Security Protocol and Data Model (SPDM) + * https://www.dmtf.org/dsp/DSP0274 + * + * Requester role: sysfs interface + * + * Copyright (C) 2023-24 Intel Corporation + */ + +#include "spdm.h" + +#include + +/** + * 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 struct spdm_state *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); + struct spdm_state *spdm_state = dev_to_spdm_state(dev); + + if (!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) +{ + struct spdm_state *spdm_state = dev_to_spdm_state(dev); + int rc; + + if (IS_ERR(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) +{ + struct spdm_state *spdm_state = dev_to_spdm_state(dev); + + if (IS_ERR(spdm_state)) + return PTR_ERR(spdm_state); + + return sysfs_emit(buf, "%u\n", spdm_state->authenticated); +} +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/spdm/spdm.h b/lib/spdm/spdm.h index 0e3bb6e18d91ff..0992b2bc3942fc 100644 --- a/lib/spdm/spdm.h +++ b/lib/spdm/spdm.h @@ -418,6 +418,7 @@ struct spdm_error_rsp { * struct spdm_state - SPDM session state * * @dev: Responder device. Used for error reporting and passed to @transport. + * Attributes in sysfs appear below this device's directory. * @lock: Serializes multiple concurrent spdm_authenticate() calls. * @authenticated: Whether device was authenticated successfully. * @dev: Responder device. Used for error reporting and passed to @transport. From e420d08c760c4c2e0c4b8d120b30ff3cce6e680d Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sat, 8 Jun 2024 21:28:54 +0200 Subject: [PATCH 35/47] PCI/CMA: Expose certificates in sysfs The kernel already caches certificate chains retrieved from a device upon authentication. Expose them in "slot[0-7]" files in sysfs for examination by user space. As noted in the ABI documentation, the "slot[0-7]" files always have a file size of 65535 bytes (the maximum size of a certificate chain per SPDM 1.0.0 table 18), even if the certificate chain in the slot is actually smaller. Although it would be possible to use the certifiate chain's actual size as the file size, doing so would require a separate struct attribute_group for each device, which would occupy additional memory. Slots are visible in sysfs even if they're currently unprovisioned because a future commit will add support for certificate provisioning by writing to the "slot[0-7]" files. Signed-off-by: Lukas Wunner --- Documentation/ABI/testing/sysfs-devices-spdm | 48 ++++++++++++ drivers/pci/pci-sysfs.c | 1 + include/linux/spdm.h | 1 + lib/spdm/req-authenticate.c | 30 +++++++- lib/spdm/req-sysfs.c | 80 ++++++++++++++++++++ lib/spdm/spdm.h | 3 + 6 files changed, 162 insertions(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-devices-spdm b/Documentation/ABI/testing/sysfs-devices-spdm index 2d6e5d513231a6..26b453767ec112 100644 --- a/Documentation/ABI/testing/sysfs-devices-spdm +++ b/Documentation/ABI/testing/sysfs-devices-spdm @@ -29,3 +29,51 @@ Description: 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. + + +What: /sys/devices/.../certificates/ +What: /sys/devices/.../certificates/slot[0-7] +Date: June 2024 +Contact: Lukas Wunner +Description: + The "certificates" directory provides access to the certificate + chains contained in the up to 8 slots of a device. + + A certificate chain is the concatenation of one or more ASN.1 + DER-encoded X.509 v3 certificates (SPDM 1.0.0 sec 4.9.2.1). + It can be examined as follows:: + + # openssl storeutl -text certificates/slot0 + + A common use case is to add the first certificate in a chain + to the keyring of trusted root certificates (".cma" in this + example) after comparing its fingerprint to the one provided + by the device manufacturer:: + + # openssl x509 -in certificates/slot0 -fingerprint -nocert + # keyctl padd asymmetric "" %:.cma < certificates/slot0 + # echo re > authenticated + + The file size of each slot is always 65535 bytes (the maximum + size of a certificate chain per SPDM 1.0.0 table 18), even if + the certificate chain in the slot is actually smaller. + + Unprovisioned slots are represented as empty files. + + Unsupported slots (introduced by SPDM 1.3 margin no 366) are + not visible. If the device only supports SPDM version 1.2 or + earlier, all 8 slots are assumed to be supported and therefore + visible. + + The kernel learns which slots are supported when authenticating + the device for the first time. Hence, no slots are visible + until at least one authentication attempt has been performed. + + SPDM doesn't support on-demand retrieval of certificate chains, + so the kernel caches them when (re-)authenticating the device. + SPDM allows provisioning slots behind the kernel's back by + sending a SET_CERTIFICATE request through a different transport + (e.g. via MCTP from a Baseboard Management Controller). + SPDM does not specify how to notify the kernel of such events, + so unless reauthentication is manually initiated to update the + kernel's cache, the "slot[0-7]" files may contain stale data. diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index d070f7ca725133..1419d8e93655e5 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1669,6 +1669,7 @@ const struct attribute_group *pci_dev_attr_groups[] = { #endif #ifdef CONFIG_PCI_CMA &spdm_attr_group, + &spdm_certificates_group, #endif NULL, }; diff --git a/include/linux/spdm.h b/include/linux/spdm.h index 9835a3202a0e86..97c7d4feab76ea 100644 --- a/include/linux/spdm.h +++ b/include/linux/spdm.h @@ -35,5 +35,6 @@ int spdm_authenticate(struct spdm_state *spdm_state); void spdm_destroy(struct spdm_state *spdm_state); extern const struct attribute_group spdm_attr_group; +extern const struct attribute_group spdm_certificates_group; #endif diff --git a/lib/spdm/req-authenticate.c b/lib/spdm/req-authenticate.c index 90f7a7f2629c62..1f701d07ad46cc 100644 --- a/lib/spdm/req-authenticate.c +++ b/lib/spdm/req-authenticate.c @@ -14,6 +14,7 @@ #include "spdm.h" #include +#include #include #include @@ -288,9 +289,9 @@ static int spdm_get_digests(struct spdm_state *spdm_state) struct spdm_get_digests_req *req = spdm_state->transcript_end; struct spdm_get_digests_rsp *rsp; unsigned long deprovisioned_slots; + u8 slot, supported_slots; int rc, length; size_t rsp_sz; - u8 slot; *req = (struct spdm_get_digests_req) { .code = SPDM_GET_DIGESTS, @@ -338,6 +339,33 @@ static int spdm_get_digests(struct spdm_state *spdm_state) return -EPROTO; } + /* + * If a bit is set in ProvisionedSlotMask, the corresponding bit in + * SupportedSlotMask shall also be set (SPDM 1.3.0 table 35). + */ + if (spdm_state->version >= 0x13 && rsp->param2 & ~rsp->param1) { + dev_err(spdm_state->dev, "Malformed digests response\n"); + return -EPROTO; + } + + if (spdm_state->version >= 0x13) + supported_slots = rsp->param1; + else + supported_slots = GENMASK(7, 0); + + if (spdm_state->supported_slots != supported_slots) { + spdm_state->supported_slots = supported_slots; + + if (device_is_registered(spdm_state->dev)) { + rc = sysfs_update_group(&spdm_state->dev->kobj, + &spdm_certificates_group); + if (rc) + dev_err(spdm_state->dev, + "Cannot update certificates in sysfs: " + "%d\n", rc); + } + } + return 0; } diff --git a/lib/spdm/req-sysfs.c b/lib/spdm/req-sysfs.c index 9bbed7abc15385..afba3c5a2e8f43 100644 --- a/lib/spdm/req-sysfs.c +++ b/lib/spdm/req-sysfs.c @@ -93,3 +93,83 @@ const struct attribute_group spdm_attr_group = { .attrs = spdm_attrs, .is_visible = spdm_attrs_are_visible, }; + +/* certificates attributes */ + +static umode_t spdm_certificates_are_visible(struct kobject *kobj, + struct bin_attribute *a, int n) +{ + struct device *dev = kobj_to_dev(kobj); + struct spdm_state *spdm_state = dev_to_spdm_state(dev); + u8 slot = a->attr.name[4] - '0'; + + if (IS_ERR_OR_NULL(spdm_state)) + return SYSFS_GROUP_INVISIBLE; + + if (!(spdm_state->supported_slots & BIT(slot))) + return 0; + + return a->attr.mode; +} + +static ssize_t spdm_cert_read(struct file *file, struct kobject *kobj, + struct bin_attribute *a, char *buf, loff_t off, + size_t count) +{ + struct device *dev = kobj_to_dev(kobj); + struct spdm_state *spdm_state = dev_to_spdm_state(dev); + u8 slot = a->attr.name[4] - '0'; + size_t header_size, cert_size; + + /* + * Serialize with spdm_authenticate() as it may change hash_len, + * slot_sz[] and slot[] members in struct spdm_state. + */ + guard(mutex)(&spdm_state->lock); + + /* + * slot[] is prefixed by the 4 + H header per SPDM 1.0.0 table 15. + * The header is not exposed to user space, only the certificates are. + */ + header_size = sizeof(struct spdm_cert_chain) + spdm_state->hash_len; + cert_size = spdm_state->slot_sz[slot] - header_size; + + if (!spdm_state->slot[slot]) + return 0; + if (!count) + return 0; + if (off > cert_size) + return 0; + if (off + count > cert_size) + count = cert_size - off; + + memcpy(buf, (u8 *)spdm_state->slot[slot] + header_size + off, count); + return count; +} + +static BIN_ATTR(slot0, 0444, spdm_cert_read, NULL, 0xffff); +static BIN_ATTR(slot1, 0444, spdm_cert_read, NULL, 0xffff); +static BIN_ATTR(slot2, 0444, spdm_cert_read, NULL, 0xffff); +static BIN_ATTR(slot3, 0444, spdm_cert_read, NULL, 0xffff); +static BIN_ATTR(slot4, 0444, spdm_cert_read, NULL, 0xffff); +static BIN_ATTR(slot5, 0444, spdm_cert_read, NULL, 0xffff); +static BIN_ATTR(slot6, 0444, spdm_cert_read, NULL, 0xffff); +static BIN_ATTR(slot7, 0444, spdm_cert_read, NULL, 0xffff); + +static struct bin_attribute *spdm_certificates_bin_attrs[] = { + &bin_attr_slot0, + &bin_attr_slot1, + &bin_attr_slot2, + &bin_attr_slot3, + &bin_attr_slot4, + &bin_attr_slot5, + &bin_attr_slot6, + &bin_attr_slot7, + NULL +}; + +const struct attribute_group spdm_certificates_group = { + .name = "certificates", + .bin_attrs = spdm_certificates_bin_attrs, + .is_bin_visible = spdm_certificates_are_visible, +}; diff --git a/lib/spdm/spdm.h b/lib/spdm/spdm.h index 0992b2bc3942fc..6c426b2be37257 100644 --- a/lib/spdm/spdm.h +++ b/lib/spdm/spdm.h @@ -436,6 +436,8 @@ struct spdm_error_rsp { * @base_hash_alg: Hash algorithm for signature verification of * CHALLENGE_AUTH messages. * 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. @@ -480,6 +482,7 @@ struct spdm_state { u32 rsp_caps; u32 base_asym_alg; u32 base_hash_alg; + unsigned long supported_slots; unsigned long provisioned_slots; /* Signature algorithm */ From 2273ba179e410b398a8014f7adc43ee7545295ec Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sat, 30 Mar 2024 21:23:22 +0100 Subject: [PATCH 36/47] sysfs: Allow bin_attributes to be added to groups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit dfa87c824a9a ("sysfs: allow attributes to be added to groups") introduced dynamic addition of sysfs attributes to groups. Allow the same for bin_attributes, in support of a subsequent commit which adds various bin_attributes every time a PCI device is authenticated. Addition of bin_attributes to groups differs from regular attributes in that different kernfs_ops are selected by sysfs_add_bin_file_mode_ns() vis-à-vis sysfs_add_file_mode_ns(). So call either of those two functions from sysfs_add_file_to_group() based on an additional boolean parameter and add two wrapper functions, one for bin_attributes and another for regular attributes. Removal of bin_attributes from groups does not require a differentiation for bin_attributes and can use the same code path as regular attributes. Signed-off-by: Lukas Wunner Reviewed-by: Alistair Francis Acked-by: Greg Kroah-Hartman Cc: Alan Stern --- fs/sysfs/file.c | 69 ++++++++++++++++++++++++++++++++++++------- include/linux/sysfs.h | 19 ++++++++++++ 2 files changed, 78 insertions(+), 10 deletions(-) diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index d1995e2d6c943a..9268232781b571 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -383,14 +383,14 @@ int sysfs_create_files(struct kobject *kobj, const struct attribute * const *ptr } EXPORT_SYMBOL_GPL(sysfs_create_files); -/** - * sysfs_add_file_to_group - add an attribute file to a pre-existing group. - * @kobj: object we're acting for. - * @attr: attribute descriptor. - * @group: group name. - */ -int sysfs_add_file_to_group(struct kobject *kobj, - const struct attribute *attr, const char *group) +static const struct bin_attribute *to_bin_attr(const struct attribute *attr) +{ + return container_of(attr, struct bin_attribute, attr); +} + +static int __sysfs_add_file_to_group(struct kobject *kobj, + const struct attribute *attr, + const char *group, bool is_bin_attr) { struct kernfs_node *parent; kuid_t uid; @@ -408,14 +408,49 @@ int sysfs_add_file_to_group(struct kobject *kobj, return -ENOENT; kobject_get_ownership(kobj, &uid, &gid); - error = sysfs_add_file_mode_ns(parent, attr, attr->mode, uid, gid, - NULL); + if (is_bin_attr) + error = sysfs_add_bin_file_mode_ns(parent, to_bin_attr(attr), + attr->mode, uid, gid, NULL); + else + error = sysfs_add_file_mode_ns(parent, attr, + attr->mode, uid, gid, NULL); kernfs_put(parent); return error; } + +/** + * sysfs_add_file_to_group - add an attribute file to a pre-existing group. + * @kobj: object we're acting for. + * @attr: attribute descriptor. + * @group: group name. + * + * Returns 0 on success or error code on failure. + */ +int sysfs_add_file_to_group(struct kobject *kobj, + const struct attribute *attr, + const char *group) +{ + return __sysfs_add_file_to_group(kobj, attr, group, false); +} EXPORT_SYMBOL_GPL(sysfs_add_file_to_group); +/** + * sysfs_add_bin_file_to_group - add bin_attribute file to pre-existing group. + * @kobj: object we're acting for. + * @attr: attribute descriptor. + * @group: group name. + * + * Returns 0 on success or error code on failure. + */ +int sysfs_add_bin_file_to_group(struct kobject *kobj, + const struct bin_attribute *attr, + const char *group) +{ + return __sysfs_add_file_to_group(kobj, &attr->attr, group, true); +} +EXPORT_SYMBOL_GPL(sysfs_add_bin_file_to_group); + /** * sysfs_chmod_file - update the modified mode value on an object attribute. * @kobj: object we're acting for. @@ -565,6 +600,20 @@ void sysfs_remove_file_from_group(struct kobject *kobj, } EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group); +/** + * sysfs_remove_bin_file_from_group - remove bin_attribute file from group. + * @kobj: object we're acting for. + * @attr: attribute descriptor. + * @group: group name. + */ +void sysfs_remove_bin_file_from_group(struct kobject *kobj, + const struct bin_attribute *attr, + const char *group) +{ + sysfs_remove_file_from_group(kobj, &attr->attr, group); +} +EXPORT_SYMBOL_GPL(sysfs_remove_bin_file_from_group); + /** * sysfs_create_bin_file - create binary file for object. * @kobj: object. diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index c4e64dc112063f..d41eac7870400f 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -451,6 +451,12 @@ int sysfs_add_file_to_group(struct kobject *kobj, const struct attribute *attr, const char *group); void sysfs_remove_file_from_group(struct kobject *kobj, const struct attribute *attr, const char *group); +int sysfs_add_bin_file_to_group(struct kobject *kobj, + const struct bin_attribute *attr, + const char *group); +void sysfs_remove_bin_file_from_group(struct kobject *kobj, + const struct bin_attribute *attr, + const char *group); int sysfs_merge_group(struct kobject *kobj, const struct attribute_group *grp); void sysfs_unmerge_group(struct kobject *kobj, @@ -660,6 +666,19 @@ static inline void sysfs_remove_file_from_group(struct kobject *kobj, { } +static inline int sysfs_add_bin_file_to_group(struct kobject *kobj, + const struct bin_attribute *attr, + const char *group) +{ + return 0; +} + +static inline void sysfs_remove_bin_file_from_group(struct kobject *kobj, + const struct bin_attribute *attr, + const char *group) +{ +} + static inline int sysfs_merge_group(struct kobject *kobj, const struct attribute_group *grp) { From 960d1dc29ed9dee10111bbd3601dbe40c666c62e Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Tue, 11 Jun 2024 17:35:36 +0200 Subject: [PATCH 37/47] sysfs: Allow symlinks to be added between sibling groups A subsequent commit has the need to create a symlink from an attribute in a first group to an attribute in a second group. Both groups belong to the same kobject. More specifically, each signature received from an authentication- capable device is going to be represented by a file in the first group and shall be accompanied by a symlink pointing to the certificate slot in the second group which was used to generate the signature (a device may have multiple certificate slots and each is represented by a separate file in the second group): /sys/devices/.../signatures/0_certificate_chain -> .../certificates/slot0 There is already a sysfs_add_link_to_group() helper to add a symlink to a group which points to another kobject, but this isn't what's needed here. So add a new function to add a symlink among sibling groups of the same kobject. The existing sysfs_add_link_to_group() helper goes through a locking dance of acquiring sysfs_symlink_target_lock in order to acquire a reference on the target kobject. That's unnecessary for the present use case as the link itself and its target reside below the same kobject. To simplify error handling in the newly introduced function, add a DEFINE_FREE() clause for kernfs_put(). Signed-off-by: Lukas Wunner Acked-by: Greg Kroah-Hartman --- fs/sysfs/group.c | 33 +++++++++++++++++++++++++++++++++ include/linux/kernfs.h | 2 ++ include/linux/sysfs.h | 10 ++++++++++ 3 files changed, 45 insertions(+) diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index d22ad67a0f3291..0cb52c9b9e1902 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -445,6 +445,39 @@ void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name, } EXPORT_SYMBOL_GPL(sysfs_remove_link_from_group); +/** + * sysfs_add_link_to_sibling_group - add a symlink to a sibling attribute group. + * @kobj: The kobject containing the groups. + * @link_grp: The name of the group in which to create the symlink. + * @link: The name of the symlink to create. + * @target_grp: The name of the target group. + * @target: The name of the target attribute. + * + * Returns 0 on success or error code on failure. + */ +int sysfs_add_link_to_sibling_group(struct kobject *kobj, + const char *link_grp, const char *link, + const char *target_grp, const char *target) +{ + struct kernfs_node *target_grp_kn __free(kernfs_put), + *target_kn __free(kernfs_put) = NULL, + *link_grp_kn __free(kernfs_put) = NULL; + + target_grp_kn = kernfs_find_and_get(kobj->sd, target_grp); + if (!target_grp_kn) + return -ENOENT; + + target_kn = kernfs_find_and_get(target_grp_kn, target); + if (!target_kn) + return -ENOENT; + + link_grp_kn = kernfs_find_and_get(kobj->sd, link_grp); + if (!link_grp_kn) + return -ENOENT; + + return PTR_ERR_OR_ZERO(kernfs_create_link(link_grp_kn, link, target_kn)); +} + /** * compat_only_sysfs_link_entry_to_kobj - add a symlink to a kobject pointing * to a group or an attribute diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 87c79d076d6d73..d5726d070dba91 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -407,6 +407,8 @@ struct kernfs_node *kernfs_walk_and_get_ns(struct kernfs_node *parent, void kernfs_get(struct kernfs_node *kn); void kernfs_put(struct kernfs_node *kn); +DEFINE_FREE(kernfs_put, struct kernfs_node *, if (_T) kernfs_put(_T)) + struct kernfs_node *kernfs_node_from_dentry(struct dentry *dentry); struct kernfs_root *kernfs_root_from_sb(struct super_block *sb); struct inode *kernfs_get_inode(struct super_block *sb, struct kernfs_node *kn); diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index d41eac7870400f..703161e6fc8c26 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -465,6 +465,9 @@ int sysfs_add_link_to_group(struct kobject *kobj, const char *group_name, struct kobject *target, const char *link_name); void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name, const char *link_name); +int sysfs_add_link_to_sibling_group(struct kobject *kobj, + const char *link_grp, const char *link, + const char *target_grp, const char *target); int compat_only_sysfs_link_entry_to_kobj(struct kobject *kobj, struct kobject *target_kobj, const char *target_name, @@ -702,6 +705,13 @@ static inline void sysfs_remove_link_from_group(struct kobject *kobj, { } +static inline int sysfs_add_link_to_sibling_group(struct kobject *kobj, + const char *link_grp, const char *link, + const char *target_grp, const char *target) +{ + return 0; +} + static inline int compat_only_sysfs_link_entry_to_kobj(struct kobject *kobj, struct kobject *target_kobj, const char *target_name, From e218643edae820bd1e90ac10a9428c33f239f89b Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Thu, 11 Apr 2024 09:55:56 +0200 Subject: [PATCH 38/47] PCI/CMA: Expose a log of received signatures in sysfs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When authenticating a device with CMA-SPDM, the kernel verifies the challenge-response received from the device, but otherwise keeps it to itself. James Bottomley contends that's not good enough because user space or a remote attestation service may want to re-verify the challenge-response: Either because it mistrusts the kernel or because the kernel is unaware of policy constraints that user space or the remote attestation service want to apply. Facilitate such use cases by exposing a log in sysfs which consists of several files for each challenge-response event. The files are prefixed with a monotonically increasing number, starting at 0: /sys/devices/.../signatures/0_signature /sys/devices/.../signatures/0_transcript /sys/devices/.../signatures/0_requester_nonce /sys/devices/.../signatures/0_responder_nonce /sys/devices/.../signatures/0_hash_algorithm /sys/devices/.../signatures/0_combined_spdm_prefix /sys/devices/.../signatures/0_certificate_chain /sys/devices/.../signatures/0_type The 0_signature is computed over the 0_transcript (a concatenation of all SPDM messages exchanged with the device). To verify the signature, 0_transcript is hashed with 0_hash_algorithm (e.g. "sha384") and prefixed by 0_combined_spdm_prefix. The public key to verify the signature against is the leaf certificate contained in 0_certificate_chain. The nonces chosen by requester and responder are exposed as separate attributes to ease verification of their freshness. They're already contained in the transcript but their offsets within the transcript are variable, so user space would otherwise have to parse the SPDM messages in the transcript to find the nonces. The type attribute contains the event type: Currently it is always "responder-challenge_auth signing". In the future it may also contain "responder-measurements signing". This custom log format was chosen for lack of a better alternative. Although the TCG PFP Specification defines DEVICE_SECURITY_EVENT_DATA structures, those structures do not store the transcript (which can be a few kBytes or up to several MBytes in size). They do store nonces, hence at least allow for verification of nonce freshness. But without the transcript, user space cannot verify the signature: https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/ Exposing the transcript as an attribute of its own has the benefit that it can directly be fed into a protocol dissector for debugging purposes (think Wireshark). Signed-off-by: Lukas Wunner Cc: James Bottomley Cc: Jérôme Glisse Cc: Jason Gunthorpe --- Documentation/ABI/testing/sysfs-devices-spdm | 118 +++++++ drivers/pci/cma.c | 6 + drivers/pci/pci-sysfs.c | 1 + drivers/pci/pci.h | 2 + drivers/pci/probe.c | 1 + include/linux/spdm.h | 6 + lib/spdm/core.c | 2 + lib/spdm/req-authenticate.c | 9 +- lib/spdm/req-sysfs.c | 333 +++++++++++++++++++ lib/spdm/spdm.h | 20 ++ 10 files changed, 497 insertions(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-devices-spdm b/Documentation/ABI/testing/sysfs-devices-spdm index 26b453767ec112..0561eb011c86a0 100644 --- a/Documentation/ABI/testing/sysfs-devices-spdm +++ b/Documentation/ABI/testing/sysfs-devices-spdm @@ -77,3 +77,121 @@ Description: SPDM does not specify how to notify the kernel of such events, so unless reauthentication is manually initiated to update the kernel's cache, the "slot[0-7]" files may contain stale data. + + +What: /sys/devices/.../signatures/ +What: /sys/devices/.../signatures/[0-9]*_signature +What: /sys/devices/.../signatures/[0-9]*_transcript +What: /sys/devices/.../signatures/[0-9]*_hash_algorithm +What: /sys/devices/.../signatures/[0-9]*_combined_spdm_prefix +What: /sys/devices/.../signatures/[0-9]*_certificate_chain +Date: June 2024 +Contact: Lukas Wunner +Description: + The "signatures" directory contains a log of signatures + received from the device to allow for their re-verification. + It is meant for remote attestation services which do not trust + the kernel to have verified the signatures correctly or which + want to apply policy constraints of their own. + + Each signature is exposed as a separate file. The filename + is prefixed with a monotonically increasing, unsigned, 32 bit + number, starting at 0. + + The signature is computed over the "transcript" file, which is + a concatenation of all SPDM messages exchanged with the device. + SPDM 1.2 and newer hash the transcript with "hash_algorithm" + and prepend the "combined_spdm_prefix" before computing the + signature (SPDM 1.2.0 sec 15). For SPDM 1.0 and 1.1, that step + is omitted and "combined_spdm_prefix" is an empty file. + + The signature is verified against the leaf certificate in the + "certificate_chain". To save memory, "certificate_chain" is + a symbolic link to the slot used for signature generation. + If the slot has since been provisioned with a different + certificate chain, verification of the signature will fail. + + In bash syntax, the signature is verified as follows:: + + # number of signature to verify + num=0 + + # split certificate chain into individual certificates + openssl storeutl -text ${num}_certificate_chain | \ + csplit -z -f /tmp/cert - '/^[0-9]*: Certificate$/' '{*}' + + # extract public key from leaf certificate + leaf_cert=$(\ls /tmp/cert?? | tail -1) + openssl x509 -pubkey -in ${leaf_cert} -out ${leaf_cert}.pub + + # verify signature + if [ \! -s ${num}_combined_spdm_prefix ] ; then + # SPDM 1.0 and 1.1 + openssl dgst -$(cat ${num}_hash_algorithm) \ + -signature ${num}_signature -verify ${leaf_cert}.pub \ + ${num}_transcript + else + # SPDM 1.2 and newer + openssl dgst -$(cat ${num}_hash_algorithm) \ + -binary -out /tmp/transcript_hashed ${num}_transcript + openssl dgst -$(cat ${num}_hash_algorithm) \ + -signature ${num}_signature -verify ${leaf_cert}.pub \ + ${num}_combined_spdm_prefix /tmp/transcript_hashed + fi + + Note: The above works for RSA signatures, but not for ECDSA. + SPDM encodes ECDSA signatures in P1363 format (concatenation of + two raw integers), whereas openssl only supports X9.62 format + (ASN.1 DER sequence of two integers). There is no command line + utility to convert between the two formats, but most popular + crypto libraries offer conversion routines: + + | https://github.com/java-crypto/cross_platform_crypto/blob/main/docs/ecdsa_signature_conversion.md + + The "transcript" file can be fed to a protocol dissector to + examine the SPDM messages it contains: + + | https://github.com/th-duvanel/spdm-wid + | https://github.com/jyao1/wireshark-spdm + | https://github.com/DMTF/spdm-dump + + Note: To ease signature verification, the "transcript" file + does not contain the trailing signature. However the signature + is part of the final CHALLENGE_AUTH message, so the protocol + dissector needs to be fed the concatenation of "transcript" + and "signature". + + +What: /sys/devices/.../signatures/[0-9]*_type +Date: June 2024 +Contact: Lukas Wunner +Description: + This file contains the type of event that led to signature + generation. It is one of (sans quotes): + + "responder-challenge_auth signing" + + +What: /sys/devices/.../signatures/[0-9]*_requester_nonce +What: /sys/devices/.../signatures/[0-9]*_responder_nonce +Date: June 2024 +Contact: Lukas Wunner +Description: + These files contain the 32 byte nonce chosen by requester and + responder. They allow remote attestation services to verify + freshness (uniqueness) of the nonces. Nonces used more than + once can be identified with:: + + # hexdump -e '32/1 "%02x" "\n"' [0-9]*_nonce | sort | \ + uniq -c | grep -v '^ 1' + + Remote attestation services may also want to verify that the + entropy of the nonces is acceptable:: + + # ent 0_requester_nonce + + Note: The nonces are also contained in the "transcript", but + their offsets within the transcript are variable. It would be + necessary to parse the SPDM messages in the transcript to find + and extract the nonces, which is cumbersome. That's why they + are exposed as separate files. diff --git a/drivers/pci/cma.c b/drivers/pci/cma.c index 59558714f1432b..e5d9ab5d646e33 100644 --- a/drivers/pci/cma.c +++ b/drivers/pci/cma.c @@ -199,6 +199,12 @@ void pci_cma_init(struct pci_dev *pdev) spdm_authenticate(pdev->spdm_state); } +void pci_cma_publish(struct pci_dev *pdev) +{ + if (!IS_ERR_OR_NULL(pdev->spdm_state)) + spdm_publish_log(pdev->spdm_state); +} + /** * pci_cma_reauthenticate() - Perform CMA-SPDM authentication again * @pdev: Device to reauthenticate diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 1419d8e93655e5..eab317954666c6 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1670,6 +1670,7 @@ const struct attribute_group *pci_dev_attr_groups[] = { #ifdef CONFIG_PCI_CMA &spdm_attr_group, &spdm_certificates_group, + &spdm_signatures_group, #endif NULL, }; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index d7e77e6fcf12a9..54791a87e3dd07 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -419,6 +419,7 @@ 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_publish(struct pci_dev *pdev); void pci_cma_reauthenticate(struct pci_dev *pdev); static inline void pci_cma_disable(struct pci_dev *pdev) { @@ -427,6 +428,7 @@ static inline void pci_cma_disable(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_publish(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 diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 162f72a543ae7e..07244aab9f4d01 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2592,6 +2592,7 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) ret = device_add(&dev->dev); WARN_ON(ret < 0); + pci_cma_publish(dev); pci_npem_create(dev); } diff --git a/include/linux/spdm.h b/include/linux/spdm.h index 97c7d4feab76ea..cc8aa8f773684c 100644 --- a/include/linux/spdm.h +++ b/include/linux/spdm.h @@ -34,7 +34,13 @@ int spdm_authenticate(struct spdm_state *spdm_state); void spdm_destroy(struct spdm_state *spdm_state); +#ifdef CONFIG_SYSFS extern const struct attribute_group spdm_attr_group; extern const struct attribute_group spdm_certificates_group; +extern const struct attribute_group spdm_signatures_group; +void spdm_publish_log(struct spdm_state *spdm_state); +#else +static inline void spdm_publish_log(struct spdm_state *spdm_state) { } +#endif #endif diff --git a/lib/spdm/core.c b/lib/spdm/core.c index 8be4cba29c4b8e..fb12dccdf10339 100644 --- a/lib/spdm/core.c +++ b/lib/spdm/core.c @@ -401,6 +401,7 @@ struct spdm_state *spdm_create(struct device *dev, spdm_transport *transport, spdm_state->validate = validate; mutex_init(&spdm_state->lock); + INIT_LIST_HEAD(&spdm_state->log); return spdm_state; } @@ -419,6 +420,7 @@ void spdm_destroy(struct spdm_state *spdm_state) kvfree(spdm_state->slot[slot]); spdm_reset(spdm_state); + spdm_destroy_log(spdm_state); mutex_destroy(&spdm_state->lock); kfree(spdm_state); } diff --git a/lib/spdm/req-authenticate.c b/lib/spdm/req-authenticate.c index 1f701d07ad46cc..0c74dc0e5cf41c 100644 --- a/lib/spdm/req-authenticate.c +++ b/lib/spdm/req-authenticate.c @@ -617,13 +617,13 @@ static size_t spdm_challenge_rsp_sz(struct spdm_state *spdm_state, static int spdm_challenge(struct spdm_state *spdm_state, u8 slot) { + size_t req_sz, rsp_sz, rsp_sz_max, req_nonce_off, rsp_nonce_off; struct spdm_challenge_rsp *rsp __free(kfree); struct spdm_challenge_req req = { .code = SPDM_CHALLENGE, .param1 = slot, .param2 = 0, /* No measurement summary hash */ }; - size_t req_sz, rsp_sz, rsp_sz_max; int rc, length; get_random_bytes(&req.nonce, sizeof(req.nonce)); @@ -649,10 +649,14 @@ static int spdm_challenge(struct spdm_state *spdm_state, u8 slot) return -EIO; } + req_nonce_off = spdm_state->transcript_end - spdm_state->transcript + + offsetof(typeof(req), nonce); rc = spdm_append_transcript(spdm_state, &req, req_sz); if (rc) return rc; + rsp_nonce_off = spdm_state->transcript_end - spdm_state->transcript + + sizeof(*rsp) + spdm_state->hash_len; rc = spdm_append_transcript(spdm_state, rsp, rsp_sz); if (rc) return rc; @@ -666,6 +670,9 @@ static int spdm_challenge(struct spdm_state *spdm_state, u8 slot) 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); + return rc; } diff --git a/lib/spdm/req-sysfs.c b/lib/spdm/req-sysfs.c index afba3c5a2e8f43..d3c4ca7dbbaaac 100644 --- a/lib/spdm/req-sysfs.c +++ b/lib/spdm/req-sysfs.c @@ -173,3 +173,336 @@ const struct attribute_group spdm_certificates_group = { .bin_attrs = spdm_certificates_bin_attrs, .is_bin_visible = spdm_certificates_are_visible, }; + +/* signatures attributes */ + +static struct bin_attribute *spdm_signatures_bin_attrs[] = { + NULL +}; + +const struct attribute_group spdm_signatures_group = { + .name = "signatures", + .bin_attrs = spdm_signatures_bin_attrs, +}; + +/** + * struct spdm_log_entry - log entry representing one received SPDM signature + * + * @list: List node. Added to the @log list in struct spdm_state. + * @sig: sysfs attribute of received signature (located at end of transcript). + * @req_nonce: sysfs attribute of requester nonce (located within transcript). + * @rsp_nonce: sysfs attribute of responder nonce (located within transcript). + * @transcript: sysfs attribute of transcript (concatenation of all SPDM + * messages exchanged during an authentication sequence) sans trailing + * signature (to simplify signature verification by user space). + * @combined_prefix: sysfs attribute of combined_spdm_prefix + * (SPDM 1.2.0 margin no 806, needed to verify signature). + * @spdm_context: sysfs attribute of spdm_context + * (SPDM 1.2.0 margin no 803, needed to create combined_spdm_prefix). + * @hash_alg: sysfs attribute of hash algorithm (needed to verify signature). + * @sig_name: Name of @sig attribute (with prepended signature counter). + * @req_nonce_name: Name of @req_nonce attribute. + * @rsp_nonce_name: Name of @rsp_nonce attribute. + * @transcript_name: Name of @transcript attribute. + * @combined_prefix_name: Name of @combined_prefix attribute. + * @spdm_context_name: Name of @spdm_context attribute. + * @hash_alg_name: Name of @hash_alg attribute. + * @counter: Signature counter (needed to create certificate_chain symlink). + * @version: Negotiated SPDM version + * (SPDM 1.2.0 margin no 803, needed to create combined_spdm_prefix). + * @slot: Slot which was used to generate the signature + * (needed to create certificate_chain symlink). + */ +struct spdm_log_entry { + struct list_head list; + struct bin_attribute sig; + struct bin_attribute req_nonce; + struct bin_attribute rsp_nonce; + struct bin_attribute transcript; + struct bin_attribute combined_prefix; + struct dev_ext_attribute spdm_context; + struct dev_ext_attribute hash_alg; + char sig_name[sizeof(__stringify(UINT_MAX) "_signature")]; + char req_nonce_name[sizeof(__stringify(UINT_MAX) "_requester_nonce")]; + char rsp_nonce_name[sizeof(__stringify(UINT_MAX) "_responder_nonce")]; + char transcript_name[sizeof(__stringify(UINT_MAX) "_transcript")]; + char combined_prefix_name[sizeof(__stringify(UINT_MAX) "_combined_spdm_prefix")]; + char spdm_context_name[sizeof(__stringify(UINT_MAX) "_type")]; + char hash_alg_name[sizeof(__stringify(UINT_MAX) "_hash_algorithm")]; + u32 counter; + u8 version; + u8 slot; +}; + +static void spdm_unpublish_log_entry(struct kobject *kobj, + struct spdm_log_entry *log) +{ + const char *group = spdm_signatures_group.name; + + sysfs_remove_bin_file_from_group(kobj, &log->sig, group); + sysfs_remove_bin_file_from_group(kobj, &log->req_nonce, group); + sysfs_remove_bin_file_from_group(kobj, &log->rsp_nonce, group); + sysfs_remove_bin_file_from_group(kobj, &log->transcript, group); + sysfs_remove_bin_file_from_group(kobj, &log->combined_prefix, group); + sysfs_remove_file_from_group(kobj, &log->spdm_context.attr.attr, group); + sysfs_remove_file_from_group(kobj, &log->hash_alg.attr.attr, group); + + char cert_chain[sizeof(__stringify(UINT_MAX) "_certificate_chain")]; + snprintf(cert_chain, sizeof(cert_chain), "%u_certificate_chain", + log->counter); + + sysfs_remove_link_from_group(kobj, group, cert_chain); +} + +static void spdm_publish_log_entry(struct kobject *kobj, + struct spdm_log_entry *log) +{ + const char *group = spdm_signatures_group.name; + int rc; + + rc = sysfs_add_bin_file_to_group(kobj, &log->sig, group); + if (rc) + goto err; + + rc = sysfs_add_bin_file_to_group(kobj, &log->req_nonce, group); + if (rc) + goto err; + + rc = sysfs_add_bin_file_to_group(kobj, &log->rsp_nonce, group); + if (rc) + goto err; + + rc = sysfs_add_bin_file_to_group(kobj, &log->transcript, group); + if (rc) + goto err; + + rc = sysfs_add_bin_file_to_group(kobj, &log->combined_prefix, group); + if (rc) + goto err; + + rc = sysfs_add_file_to_group(kobj, &log->spdm_context.attr.attr, group); + if (rc) + goto err; + + rc = sysfs_add_file_to_group(kobj, &log->hash_alg.attr.attr, group); + if (rc) + goto err; + + char cert_chain[sizeof(__stringify(UINT_MAX) "_certificate_chain")]; + snprintf(cert_chain, sizeof(cert_chain), "%u_certificate_chain", + log->counter); + + char slot[sizeof("slot0")]; + snprintf(slot, sizeof(slot), "slot%hhu", log->slot); + + rc = sysfs_add_link_to_sibling_group(kobj, group, cert_chain, + spdm_certificates_group.name, + slot); + if (rc) + goto err; + + return; + +err: + dev_err(kobj_to_dev(kobj), + "Failed to publish signature log entry in sysfs: %d\n", rc); + spdm_unpublish_log_entry(kobj, log); +} + +static ssize_t spdm_read_combined_prefix(struct file *file, + struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct spdm_log_entry *log = attr->private; + + /* + * SPDM 1.0 and 1.1 do not add a combined prefix to the hash + * before computing the signature, so return an empty file. + */ + if (log->version <= 0x11) + return 0; + + void *tmp __free(kfree) = kmalloc(SPDM_COMBINED_PREFIX_SZ, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + spdm_create_combined_prefix(log->version, log->spdm_context.var, tmp); + memcpy(buf, tmp + off, count); + return count; +} + +static void spdm_destroy_log_entry(struct spdm_log_entry *log) +{ + list_del(&log->list); + kvfree(log->transcript.private); + kfree(log); +} + +/** + * spdm_create_log_entry() - Allocate log entry for one received SPDM signature + * + * @spdm_state: SPDM session state + * @spdm_context: SPDM context (needed to create combined_spdm_prefix) + * @slot: Slot which was used to generate the signature + * (needed to create certificate_chain symlink) + * @req_nonce_off: Requester nonce offset within the transcript + * @rsp_nonce_off: Responder nonce offset within the transcript + * + * Allocate and populate a struct spdm_log_entry upon device authentication. + * Publish it in sysfs if the device has already been registered through + * device_add(). + */ +void spdm_create_log_entry(struct spdm_state *spdm_state, + const char *spdm_context, u8 slot, + size_t req_nonce_off, size_t rsp_nonce_off) +{ + struct spdm_log_entry *log = kmalloc(sizeof(*log), GFP_KERNEL); + if (!log) + return; + + *log = (struct spdm_log_entry) { + .slot = slot, + .version = spdm_state->version, + .counter = spdm_state->log_counter, + .list = LIST_HEAD_INIT(log->list), + + .sig = { + .attr.name = log->sig_name, + .attr.mode = 0444, + .read = sysfs_bin_attr_simple_read, + .private = spdm_state->transcript_end - + spdm_state->sig_len, + .size = spdm_state->sig_len }, + + .req_nonce = { + .attr.name = log->req_nonce_name, + .attr.mode = 0444, + .read = sysfs_bin_attr_simple_read, + .private = spdm_state->transcript + req_nonce_off, + .size = SPDM_NONCE_SZ }, + + .rsp_nonce = { + .attr.name = log->rsp_nonce_name, + .attr.mode = 0444, + .read = sysfs_bin_attr_simple_read, + .private = spdm_state->transcript + rsp_nonce_off, + .size = SPDM_NONCE_SZ }, + + .transcript = { + .attr.name = log->transcript_name, + .attr.mode = 0444, + .read = sysfs_bin_attr_simple_read, + .private = spdm_state->transcript, + .size = spdm_state->transcript_end - + spdm_state->transcript - + spdm_state->sig_len }, + + .combined_prefix = { + .attr.name = log->combined_prefix_name, + .attr.mode = 0444, + .read = spdm_read_combined_prefix, + .private = log, + .size = spdm_state->version <= 0x11 ? 0 : + SPDM_COMBINED_PREFIX_SZ }, + + .spdm_context = { + .attr.attr.name = log->spdm_context_name, + .attr.attr.mode = 0444, + .attr.show = device_show_string, + .var = (char *)spdm_context }, + + .hash_alg = { + .attr.attr.name = log->hash_alg_name, + .attr.attr.mode = 0444, + .attr.show = device_show_string, + .var = (char *)spdm_state->base_hash_alg_name }, + }; + + snprintf(log->sig_name, sizeof(log->sig_name), + "%u_signature", spdm_state->log_counter); + snprintf(log->req_nonce_name, sizeof(log->req_nonce_name), + "%u_requester_nonce", spdm_state->log_counter); + snprintf(log->rsp_nonce_name, sizeof(log->rsp_nonce_name), + "%u_responder_nonce", spdm_state->log_counter); + snprintf(log->transcript_name, sizeof(log->transcript_name), + "%u_transcript", spdm_state->log_counter); + snprintf(log->combined_prefix_name, sizeof(log->combined_prefix_name), + "%u_combined_spdm_prefix", spdm_state->log_counter); + snprintf(log->spdm_context_name, sizeof(log->spdm_context_name), + "%u_type", spdm_state->log_counter); + snprintf(log->hash_alg_name, sizeof(log->hash_alg_name), + "%u_hash_algorithm", spdm_state->log_counter); + + sysfs_bin_attr_init(&log->sig); + sysfs_bin_attr_init(&log->req_nonce); + sysfs_bin_attr_init(&log->rsp_nonce); + sysfs_bin_attr_init(&log->transcript); + sysfs_bin_attr_init(&log->combined_prefix); + sysfs_attr_init(&log->spdm_context.attr.attr); + sysfs_attr_init(&log->hash_alg.attr.attr); + + list_add_tail(&log->list, &spdm_state->log); + spdm_state->log_counter++; + + /* Steal transcript pointer ahead of spdm_free_transcript() */ + spdm_state->transcript = NULL; + + if (device_is_registered(spdm_state->dev)) + spdm_publish_log_entry(&spdm_state->dev->kobj, log); +} + +/** + * spdm_publish_log() - Publish log of received SPDM signatures in sysfs + * + * @spdm_state: SPDM session state + * + * sysfs attributes representing received SPDM signatures are not static, + * but created dynamically upon authentication. If a device was authenticated + * before it became visible in sysfs, the attributes could not be created. + * This function retroactively creates those attributes in sysfs after the + * device has become visible through device_add(). + */ +void spdm_publish_log(struct spdm_state *spdm_state) +{ + struct kobject *kobj = &spdm_state->dev->kobj; + struct kernfs_node *grp_kn __free(kernfs_put); + struct spdm_log_entry *log; + + grp_kn = kernfs_find_and_get(kobj->sd, spdm_signatures_group.name); + if (WARN_ON(!grp_kn)) + return; + + mutex_lock(&spdm_state->lock); + list_for_each_entry(log, &spdm_state->log, list) { + struct kernfs_node *sig_kn __free(kernfs_put); + + /* + * Skip over log entries created in-between device_add() and + * spdm_publish_log() as they've already been published. + */ + sig_kn = kernfs_find_and_get(grp_kn, log->sig_name); + if (sig_kn) + continue; + + spdm_publish_log_entry(kobj, log); + } + mutex_unlock(&spdm_state->lock); +} +EXPORT_SYMBOL_GPL(spdm_publish_log); + +/** + * spdm_destroy_log() - Destroy log of received SPDM signatures + * + * @spdm_state: SPDM session state + * + * Be sure to unregister the device through device_del() beforehand, + * which implicitly unpublishes the log in sysfs. + */ +void spdm_destroy_log(struct spdm_state *spdm_state) +{ + struct spdm_log_entry *log, *tmp; + + list_for_each_entry_safe(log, tmp, &spdm_state->log, list) + spdm_destroy_log_entry(log); +} diff --git a/lib/spdm/spdm.h b/lib/spdm/spdm.h index 6c426b2be37257..a63c2922af5d57 100644 --- a/lib/spdm/spdm.h +++ b/lib/spdm/spdm.h @@ -466,6 +466,10 @@ struct spdm_error_rsp { * end of transcript. If another message is transmitted, it is appended * at this position. * @transcript_max: Allocation size of @transcript. Multiple of PAGE_SIZE. + * @log: Linked list of past authentication events. Each list entry is of type + * struct spdm_log_entry and is exposed as several files in sysfs. + * @log_counter: Number of generated log entries so far. Will be prefixed to + * the sysfs files of the next generated log entry. */ struct spdm_state { struct device *dev; @@ -506,6 +510,10 @@ struct spdm_state { void *transcript; void *transcript_end; size_t transcript_max; + + /* Signatures Log */ + struct list_head log; + u32 log_counter; }; ssize_t spdm_exchange(struct spdm_state *spdm_state, @@ -523,4 +531,16 @@ int spdm_verify_signature(struct spdm_state *spdm_state, void spdm_reset(struct spdm_state *spdm_state); +#ifdef CONFIG_SYSFS +void spdm_create_log_entry(struct spdm_state *spdm_state, + const char *spdm_context, u8 slot, + size_t req_nonce_off, size_t rsp_nonce_off); +void spdm_destroy_log(struct spdm_state *spdm_state); +#else +static inline void spdm_create_log_entry(struct spdm_state *spdm_state, + const char *spdm_context, u8 slot, + size_t req_nonce_off, size_t rsp_nonce_off) { } +static inline void spdm_destroy_log(struct spdm_state *spdm_state) { } +#endif + #endif /* _LIB_SPDM_H_ */ From 7fc6ce6512d5c8d222755ac038dd2c113c229092 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sat, 22 Jun 2024 11:58:12 +0200 Subject: [PATCH 39/47] spdm: Limit memory consumed by log of received signatures The SPDM library has just been amended to keep a log of received signatures and expose it in sysfs. Limit the log's memory footprint subject to a sysctl parameter. Purge old signatures when adding a new signature which causes the limit to be exceeded. Likewise purge old signatures when the sysctl parameter is reduced. The latter requires keeping a list of all struct spdm_state and protecting it with a mutex. It will come in handy when further global sysctl parameters are added to the SPDM library. Unfortunately an xarray is not a better option in this case as the xarray-integrated xa_lock() is a spinlock but purging signatures from sysfs may sleep (due to kernfs_rwsem). This functionality is introduced in a separate commit on top of basic signature exposure to split the code into digestible, reviewable chunks. Signed-off-by: Lukas Wunner --- Documentation/ABI/testing/sysfs-devices-spdm | 15 ++++ Documentation/admin-guide/sysctl/index.rst | 2 + Documentation/admin-guide/sysctl/spdm.rst | 33 ++++++++ MAINTAINERS | 1 + lib/spdm/core.c | 11 +++ lib/spdm/req-sysfs.c | 79 +++++++++++++++++++- lib/spdm/spdm.h | 10 +++ 7 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 Documentation/admin-guide/sysctl/spdm.rst diff --git a/Documentation/ABI/testing/sysfs-devices-spdm b/Documentation/ABI/testing/sysfs-devices-spdm index 0561eb011c86a0..4bedd2df04df5e 100644 --- a/Documentation/ABI/testing/sysfs-devices-spdm +++ b/Documentation/ABI/testing/sysfs-devices-spdm @@ -161,6 +161,21 @@ Description: dissector needs to be fed the concatenation of "transcript" and "signature". + 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, + subject to the limit set by "sysctl spdm.max_signatures_size" + (which defaults to 16 MiB). It is advisable to regularly save + backups on non-volatile storage to retain access to signatures + that have been purged (or across reboots):: + + # tar -u -h -f /path/to/signatures.tar signatures/ + + The ctime of each file is the reception time of the signature. + However if the signature was received before the device became + registered in sysfs, the ctime is the registration time of the + device. + What: /sys/devices/.../signatures/[0-9]*_type Date: June 2024 diff --git a/Documentation/admin-guide/sysctl/index.rst b/Documentation/admin-guide/sysctl/index.rst index 03346f98c7b96e..3b48f0039069b9 100644 --- a/Documentation/admin-guide/sysctl/index.rst +++ b/Documentation/admin-guide/sysctl/index.rst @@ -76,6 +76,7 @@ kernel/ global kernel info / tuning net/ networking stuff, for documentation look in: proc/ +spdm/ Security Protocol and Data Model (SPDM) sunrpc/ SUN Remote Procedure Call (NFS) vm/ memory management tuning buffer and cache management @@ -93,6 +94,7 @@ really like to hear about it :-) fs kernel net + spdm sunrpc user vm diff --git a/Documentation/admin-guide/sysctl/spdm.rst b/Documentation/admin-guide/sysctl/spdm.rst new file mode 100644 index 00000000000000..0f3846c83cd490 --- /dev/null +++ b/Documentation/admin-guide/sysctl/spdm.rst @@ -0,0 +1,33 @@ +.. SPDX-License-Identifier: GPL-2.0 + +================================= +Documentation for /proc/sys/spdm/ +================================= + +Copyright (C) 2024 Intel Corporation + +This directory allows tuning Security Protocol and Data Model (SPDM) +parameters. SPDM enables device authentication, measurement, key +exchange and encrypted sessions. + +max_signatures_size +=================== + +Maximum amount of memory occupied by the log of signatures (per device, +in bytes, 16 MiB by default). + +The log is meant for re-verification of signatures by remote attestation +services which do not trust the kernel to have verified the signatures +correctly or which want to apply policy constraints of their own. +A signature is computed over the transcript (a concatenation of all +SPDM messages exchanged with the device during an authentication +sequence). The transcript can be a few kBytes or up to several MBytes +in size, hence this parameter prevents the log from consuming too much +memory. + +The kernel always stores the most recent signature in the log even if it +exceeds ``max_signatures_size``. Additionally as many older signatures +are kept in the log as this limit allows. + +If you reduce the limit, signatures are purged immediately to free up +memory. diff --git a/MAINTAINERS b/MAINTAINERS index 4c2b2483f68687..165d4d246c01f5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20845,6 +20845,7 @@ L: linux-pci@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/devsec/spdm.git F: Documentation/ABI/testing/sysfs-devices-spdm +F: Documentation/admin-guide/sysctl/spdm.rst F: drivers/pci/cma* F: include/linux/spdm.h F: lib/spdm/ diff --git a/lib/spdm/core.c b/lib/spdm/core.c index fb12dccdf10339..c8c759e9d817e1 100644 --- a/lib/spdm/core.c +++ b/lib/spdm/core.c @@ -20,6 +20,9 @@ #include #include +LIST_HEAD(spdm_state_list); /* list of all struct spdm_state */ +DEFINE_MUTEX(spdm_state_mutex); /* protects spdm_state_list */ + static int spdm_err(struct device *dev, struct spdm_error_rsp *rsp) { switch (rsp->error_code) { @@ -403,6 +406,10 @@ struct spdm_state *spdm_create(struct device *dev, spdm_transport *transport, mutex_init(&spdm_state->lock); INIT_LIST_HEAD(&spdm_state->log); + mutex_lock(&spdm_state_mutex); + list_add_tail(&spdm_state->list, &spdm_state_list); + mutex_unlock(&spdm_state_mutex); + return spdm_state; } EXPORT_SYMBOL_GPL(spdm_create); @@ -416,6 +423,10 @@ void spdm_destroy(struct spdm_state *spdm_state) { u8 slot; + mutex_lock(&spdm_state_mutex); + list_del(&spdm_state->list); + mutex_unlock(&spdm_state_mutex); + for_each_set_bit(slot, &spdm_state->provisioned_slots, SPDM_SLOTS) kvfree(spdm_state->slot[slot]); diff --git a/lib/spdm/req-sysfs.c b/lib/spdm/req-sysfs.c index d3c4ca7dbbaaac..48ccc1b4641032 100644 --- a/lib/spdm/req-sysfs.c +++ b/lib/spdm/req-sysfs.c @@ -185,6 +185,8 @@ const struct attribute_group spdm_signatures_group = { .bin_attrs = spdm_signatures_bin_attrs, }; +static unsigned int spdm_max_log_sz = SZ_16M; /* per device */ + /** * struct spdm_log_entry - log entry representing one received SPDM signature * @@ -332,13 +334,31 @@ static ssize_t spdm_read_combined_prefix(struct file *file, return count; } -static void spdm_destroy_log_entry(struct spdm_log_entry *log) +static void spdm_destroy_log_entry(struct spdm_state *spdm_state, + struct spdm_log_entry *log) { + spdm_state->log_sz -= log->transcript.size + log->sig.size + + sizeof(*log); + list_del(&log->list); kvfree(log->transcript.private); kfree(log); } +static void spdm_shrink_log(struct spdm_state *spdm_state) +{ + while (spdm_state->log_sz > spdm_max_log_sz && + !list_is_singular(&spdm_state->log)) { + struct spdm_log_entry *log = + list_first_entry(&spdm_state->log, typeof(*log), list); + + if (device_is_registered(spdm_state->dev)) + spdm_unpublish_log_entry(&spdm_state->dev->kobj, log); + + spdm_destroy_log_entry(spdm_state, log); + } +} + /** * spdm_create_log_entry() - Allocate log entry for one received SPDM signature * @@ -444,6 +464,11 @@ void spdm_create_log_entry(struct spdm_state *spdm_state, list_add_tail(&log->list, &spdm_state->log); spdm_state->log_counter++; + spdm_state->log_sz += log->transcript.size + log->sig.size + + sizeof(*log); + + /* Purge oldest log entries if max log size is exceeded */ + spdm_shrink_log(spdm_state); /* Steal transcript pointer ahead of spdm_free_transcript() */ spdm_state->transcript = NULL; @@ -504,5 +529,55 @@ void spdm_destroy_log(struct spdm_state *spdm_state) struct spdm_log_entry *log, *tmp; list_for_each_entry_safe(log, tmp, &spdm_state->log, list) - spdm_destroy_log_entry(log); + spdm_destroy_log_entry(spdm_state, log); +} + +#ifdef CONFIG_SYSCTL +static int proc_max_log_sz(const struct ctl_table *table, int write, + void *buffer, size_t *lenp, loff_t *ppos) +{ + unsigned int old_max_log_sz = spdm_max_log_sz; + struct spdm_state *spdm_state; + int rc; + + rc = proc_douintvec_minmax(table, write, buffer, lenp, ppos); + if (rc) + return rc; + + /* Purge oldest log entries if max log size has been reduced */ + if (write && spdm_max_log_sz < old_max_log_sz) { + mutex_lock(&spdm_state_mutex); + list_for_each_entry(spdm_state, &spdm_state_list, list) { + mutex_lock(&spdm_state->lock); + spdm_shrink_log(spdm_state); + mutex_unlock(&spdm_state->lock); + } + mutex_unlock(&spdm_state_mutex); + } + + return 0; +} + +static struct ctl_table spdm_ctl_table[] = { + { + .procname = "max_signatures_size", + .data = &spdm_max_log_sz, + .maxlen = sizeof(spdm_max_log_sz), + .mode = 0644, + .proc_handler = proc_max_log_sz, + .extra1 = SYSCTL_ZERO, + /* + * 2 GiB limit avoids filename collision on + * wraparound of unsigned 32-bit log_counter + */ + .extra2 = SYSCTL_INT_MAX, + }, +}; + +static int __init spdm_init(void) +{ + register_sysctl_init("spdm", spdm_ctl_table); + return 0; } +fs_initcall(spdm_init); +#endif /* CONFIG_SYSCTL */ diff --git a/lib/spdm/spdm.h b/lib/spdm/spdm.h index a63c2922af5d57..448107c92db78a 100644 --- a/lib/spdm/spdm.h +++ b/lib/spdm/spdm.h @@ -420,6 +420,8 @@ struct spdm_error_rsp { * @dev: Responder device. Used for error reporting and passed to @transport. * Attributes in sysfs appear below this device's directory. * @lock: Serializes multiple concurrent spdm_authenticate() calls. + * @list: List node. Added to spdm_state_list. Used to iterate over all + * SPDM-capable devices when a global sysctl parameter is changed. * @authenticated: Whether device was authenticated successfully. * @dev: Responder device. Used for error reporting and passed to @transport. * @transport: Transport function to perform one message exchange. @@ -468,12 +470,16 @@ struct spdm_error_rsp { * @transcript_max: Allocation size of @transcript. Multiple of PAGE_SIZE. * @log: Linked list of past authentication events. Each list entry is of type * struct spdm_log_entry and is exposed as several files in sysfs. + * @log_sz: Memory occupied by @log (in bytes) to enforce the limit set by + * spdm_max_log_sz. Includes, for every entry, the struct spdm_log_entry + * itself and the transcript with trailing signature. * @log_counter: Number of generated log entries so far. Will be prefixed to * the sysfs files of the next generated log entry. */ struct spdm_state { struct device *dev; struct mutex lock; + struct list_head list; unsigned int authenticated:1; /* Transport */ @@ -513,9 +519,13 @@ struct spdm_state { /* Signatures Log */ struct list_head log; + size_t log_sz; u32 log_counter; }; +extern struct list_head spdm_state_list; +extern struct mutex spdm_state_mutex; + ssize_t spdm_exchange(struct spdm_state *spdm_state, void *req, size_t req_sz, void *rsp, size_t rsp_sz); From 4cbf47ed00db892432b88c2c3d768d40dd5a6399 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sat, 22 Jun 2024 12:23:09 +0200 Subject: [PATCH 40/47] spdm: Authenticate devices despite invalid certificate chain 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 --- 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 4bedd2df04df5e..808c7428edf9b9 100644 --- a/Documentation/ABI/testing/sysfs-devices-spdm +++ b/Documentation/ABI/testing/sysfs-devices-spdm @@ -161,6 +161,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 0c74dc0e5cf41c..7c977f5835c10f 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) From 9c3e2c16f95a52abd9ef049eb28295a0f9444fbd Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sat, 20 Apr 2024 12:08:47 +0200 Subject: [PATCH 41/47] spdm: Allow control of next requester nonce through sysfs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remote attestation services may mistrust the kernel to always use a fresh nonce for SPDM authentication. So allow user space to set the next requester nonce by writing to a sysfs attribute. Signed-off-by: Lukas Wunner Cc: James Bottomley Cc: Jérôme Glisse Cc: Jason Gunthorpe --- Documentation/ABI/testing/sysfs-devices-spdm | 29 ++++++++++++++++ lib/spdm/core.c | 1 + lib/spdm/req-authenticate.c | 8 ++++- lib/spdm/req-sysfs.c | 35 ++++++++++++++++++++ lib/spdm/spdm.h | 4 +++ 5 files changed, 76 insertions(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-devices-spdm b/Documentation/ABI/testing/sysfs-devices-spdm index 808c7428edf9b9..b35bd1c9b92d15 100644 --- a/Documentation/ABI/testing/sysfs-devices-spdm +++ b/Documentation/ABI/testing/sysfs-devices-spdm @@ -215,3 +215,32 @@ Description: necessary to parse the SPDM messages in the transcript to find and extract the nonces, which is cumbersome. That's why they are exposed as separate files. + + +What: /sys/devices/.../signatures/next_requester_nonce +Date: June 2024 +Contact: Lukas Wunner +Description: + If you do not trust the kernel to always use a fresh nonce, + write 32 bytes to this file to set the requester nonce used + in the next SPDM authentication sequence. + + Meant for remote attestation services. You are responsible + for providing a nonce with sufficient entropy. The kernel + only uses the nonce once, so provide a new one every time + you reauthenticate the device. If you do not provide a + nonce, the kernel generates a random one. + + After the nonce has been consumed, it becomes readable as + the newest [0-9]*_requester_nonce, which proves its usage:: + + # dd if=/dev/random bs=32 count=1 | \ + tee signatures/next_requester_nonce | hexdump + 0000000 e0 77 91 54 bd 56 99 c2 ea 4f 0b 1a 7f ba 6e 59 + 0000010 8f ee f6 b2 26 82 58 34 9e e5 8c 8a 31 58 29 7e + + # echo re > authenticated + + # hexdump $(\ls -t signatures/[0-9]*_requester_nonce | head -1) + 0000000 e0 77 91 54 bd 56 99 c2 ea 4f 0b 1a 7f ba 6e 59 + 0000010 8f ee f6 b2 26 82 58 34 9e e5 8c 8a 31 58 29 7e diff --git a/lib/spdm/core.c b/lib/spdm/core.c index c8c759e9d817e1..54ef0d91cb350e 100644 --- a/lib/spdm/core.c +++ b/lib/spdm/core.c @@ -433,6 +433,7 @@ void spdm_destroy(struct spdm_state *spdm_state) spdm_reset(spdm_state); spdm_destroy_log(spdm_state); mutex_destroy(&spdm_state->lock); + kfree(spdm_state->next_nonce); kfree(spdm_state); } EXPORT_SYMBOL_GPL(spdm_destroy); diff --git a/lib/spdm/req-authenticate.c b/lib/spdm/req-authenticate.c index 7c977f5835c10f..489fc88de74dc3 100644 --- a/lib/spdm/req-authenticate.c +++ b/lib/spdm/req-authenticate.c @@ -626,7 +626,13 @@ static int spdm_challenge(struct spdm_state *spdm_state, u8 slot, bool verify) }; int rc, length; - get_random_bytes(&req.nonce, sizeof(req.nonce)); + if (spdm_state->next_nonce) { + memcpy(&req.nonce, spdm_state->next_nonce, sizeof(req.nonce)); + kfree(spdm_state->next_nonce); + spdm_state->next_nonce = NULL; + } else { + get_random_bytes(&req.nonce, sizeof(req.nonce)); + } if (spdm_state->version <= 0x12) req_sz = offsetofend(typeof(req), nonce); diff --git a/lib/spdm/req-sysfs.c b/lib/spdm/req-sysfs.c index 48ccc1b4641032..5c2edac9dbf466 100644 --- a/lib/spdm/req-sysfs.c +++ b/lib/spdm/req-sysfs.c @@ -176,13 +176,48 @@ const struct attribute_group spdm_certificates_group = { /* signatures attributes */ +static umode_t spdm_signatures_are_visible(struct kobject *kobj, + struct bin_attribute *a, int n) +{ + struct device *dev = kobj_to_dev(kobj); + struct spdm_state *spdm_state = dev_to_spdm_state(dev); + + if (IS_ERR_OR_NULL(spdm_state)) + return SYSFS_GROUP_INVISIBLE; + + return a->attr.mode; +} + +static ssize_t next_requester_nonce_write(struct file *file, + struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = kobj_to_dev(kobj); + struct spdm_state *spdm_state = dev_to_spdm_state(dev); + + guard(mutex)(&spdm_state->lock); + + if (!spdm_state->next_nonce) { + spdm_state->next_nonce = kmalloc(SPDM_NONCE_SZ, GFP_KERNEL); + if (!spdm_state->next_nonce) + return -ENOMEM; + } + + memcpy(spdm_state->next_nonce + off, buf, count); + return count; +} +static BIN_ATTR_WO(next_requester_nonce, SPDM_NONCE_SZ); + static struct bin_attribute *spdm_signatures_bin_attrs[] = { + &bin_attr_next_requester_nonce, NULL }; const struct attribute_group spdm_signatures_group = { .name = "signatures", .bin_attrs = spdm_signatures_bin_attrs, + .is_bin_visible = spdm_signatures_are_visible, }; static unsigned int spdm_max_log_sz = SZ_16M; /* per device */ diff --git a/lib/spdm/spdm.h b/lib/spdm/spdm.h index 448107c92db78a..aa36aa55e718a3 100644 --- a/lib/spdm/spdm.h +++ b/lib/spdm/spdm.h @@ -475,6 +475,9 @@ struct spdm_error_rsp { * itself and the transcript with trailing signature. * @log_counter: Number of generated log entries so far. Will be prefixed to * the sysfs files of the next generated log entry. + * @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. */ struct spdm_state { struct device *dev; @@ -521,6 +524,7 @@ struct spdm_state { struct list_head log; size_t log_sz; u32 log_counter; + u8 *next_nonce; }; extern struct list_head spdm_state_list; From e2e388769aeb86a336495bc67a1866446a1eb6ac Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Thu, 8 Jun 2023 14:31:41 +0200 Subject: [PATCH 42/47] PCI/CMA: Grant guests exclusive control of authentication At any given time, only a single entity in a physical system may have an SPDM connection to a device. That's because the GET_VERSION request (which begins an authentication sequence) resets "the connection and all context associated with that connection" (SPDM 1.3.0 margin no 158). Thus, when a device is passed through to a guest and the guest has authenticated it, a subsequent authentication by the host would reset the device's CMA-SPDM session behind the guest's back. Prevent by letting the guest claim exclusive CMA ownership of the device during passthrough. Refuse CMA reauthentication on the host as long. After passthrough has concluded, reauthenticate the device on the host. Store the flag indicating guest ownership in struct pci_dev's priv_flags to avoid the concurrency issues observed by commit 44bda4b7d26e ("PCI: Fix is_added/is_busmaster race condition"). Side note: The Data Object Exchange r1.1 ECN (published Oct 11 2022) retrofits DOE with Connection IDs. In theory these allow simultaneous CMA-SPDM connections by multiple entities to the same device. But the first hardware generation capable of CMA-SPDM only supports DOE r1.0. The specification also neglects to reserve unique Connection IDs for hosts and guests, which further limits its usefulness. In general, forcing the transport to compensate for SPDM's lack of a connection identifier feels like a questionable layering violation. Signed-off-by: Lukas Wunner Cc: Alex Williamson --- drivers/pci/cma.c | 41 ++++++++++++++++++++++++++++++++ drivers/pci/pci.h | 1 + drivers/vfio/pci/vfio_pci_core.c | 9 +++++-- include/linux/pci.h | 8 +++++++ include/linux/spdm.h | 2 ++ lib/spdm/core.c | 11 +++++++++ 6 files changed, 70 insertions(+), 2 deletions(-) diff --git a/drivers/pci/cma.c b/drivers/pci/cma.c index e5d9ab5d646e33..fc5bccaf32dbdf 100644 --- a/drivers/pci/cma.c +++ b/drivers/pci/cma.c @@ -217,9 +217,50 @@ void pci_cma_reauthenticate(struct pci_dev *pdev) if (IS_ERR_OR_NULL(pdev->spdm_state)) return; + if (test_bit(PCI_CMA_OWNED_BY_GUEST, &pdev->priv_flags)) + return; + spdm_authenticate(pdev->spdm_state); } +#if IS_ENABLED(CONFIG_VFIO_PCI_CORE) +/** + * pci_cma_claim_ownership() - Claim exclusive CMA-SPDM control for guest VM + * @pdev: PCI device + * + * Claim exclusive CMA-SPDM control for a guest virtual machine before + * passthrough of @pdev. The host refrains from performing CMA-SPDM + * authentication of the device until passthrough has concluded. + * + * Necessary because the GET_VERSION request resets the SPDM connection + * and DOE r1.0 allows only a single SPDM connection for the entire system. + * So the host could reset the guest's SPDM connection behind the guest's back. + */ +void pci_cma_claim_ownership(struct pci_dev *pdev) +{ + set_bit(PCI_CMA_OWNED_BY_GUEST, &pdev->priv_flags); + + if (!IS_ERR_OR_NULL(pdev->spdm_state)) + spdm_await(pdev->spdm_state); +} +EXPORT_SYMBOL(pci_cma_claim_ownership); + +/** + * pci_cma_return_ownership() - Relinquish CMA-SPDM control to the host + * @pdev: PCI device + * + * Relinquish CMA-SPDM control to the host after passthrough of @pdev to a + * guest virtual machine has concluded. + */ +void pci_cma_return_ownership(struct pci_dev *pdev) +{ + clear_bit(PCI_CMA_OWNED_BY_GUEST, &pdev->priv_flags); + + pci_cma_reauthenticate(pdev); +} +EXPORT_SYMBOL(pci_cma_return_ownership); +#endif + void pci_cma_destroy(struct pci_dev *pdev) { if (IS_ERR_OR_NULL(pdev->spdm_state)) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 54791a87e3dd07..7b259da45323f0 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -486,6 +486,7 @@ static inline int pci_dev_set_disconnected(struct pci_dev *dev, void *unused) #define PCI_DEV_ADDED 0 #define PCI_DPC_RECOVERED 1 #define PCI_DPC_RECOVERING 2 +#define PCI_CMA_OWNED_BY_GUEST 3 static inline void pci_dev_assign_added(struct pci_dev *dev, bool added) { diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c index 1ab58da9f38a6e..b15366d0a723ac 100644 --- a/drivers/vfio/pci/vfio_pci_core.c +++ b/drivers/vfio/pci/vfio_pci_core.c @@ -483,10 +483,12 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev) if (ret) goto out_power; + pci_cma_claim_ownership(pdev); + /* If reset fails because of the device lock, fail this path entirely */ ret = pci_try_reset_function(pdev); if (ret == -EAGAIN) - goto out_disable_device; + goto out_cma_return; vdev->reset_works = !ret; pci_save_state(pdev); @@ -545,7 +547,8 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev) out_free_state: kfree(vdev->pci_saved_state); vdev->pci_saved_state = NULL; -out_disable_device: +out_cma_return: + pci_cma_return_ownership(pdev); pci_disable_device(pdev); out_power: if (!disable_idle_d3) @@ -674,6 +677,8 @@ void vfio_pci_core_disable(struct vfio_pci_core_device *vdev) vfio_pci_dev_set_try_reset(vdev->vdev.dev_set); + pci_cma_return_ownership(pdev); + /* Put the pm-runtime usage counter acquired during enable */ if (!disable_idle_d3) pm_runtime_put(&pdev->dev); diff --git a/include/linux/pci.h b/include/linux/pci.h index 2b4bb303c8879d..9389a551f43b9a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2430,6 +2430,14 @@ static inline resource_size_t pci_iov_resource_size(struct pci_dev *dev, int res static inline void pci_vf_drivers_autoprobe(struct pci_dev *dev, bool probe) { } #endif +#ifdef CONFIG_PCI_CMA +void pci_cma_claim_ownership(struct pci_dev *pdev); +void pci_cma_return_ownership(struct pci_dev *pdev); +#else +static inline void pci_cma_claim_ownership(struct pci_dev *pdev) { } +static inline void pci_cma_return_ownership(struct pci_dev *pdev) { } +#endif + #if defined(CONFIG_HOTPLUG_PCI) || defined(CONFIG_HOTPLUG_PCI_MODULE) void pci_hp_create_module_link(struct pci_slot *pci_slot); void pci_hp_remove_module_link(struct pci_slot *pci_slot); diff --git a/include/linux/spdm.h b/include/linux/spdm.h index cc8aa8f773684c..27c48ec23d8e5a 100644 --- a/include/linux/spdm.h +++ b/include/linux/spdm.h @@ -32,6 +32,8 @@ struct spdm_state *spdm_create(struct device *dev, spdm_transport *transport, int spdm_authenticate(struct spdm_state *spdm_state); +void spdm_await(struct spdm_state *spdm_state); + void spdm_destroy(struct spdm_state *spdm_state); #ifdef CONFIG_SYSFS diff --git a/lib/spdm/core.c b/lib/spdm/core.c index 54ef0d91cb350e..bbefafc7639da2 100644 --- a/lib/spdm/core.c +++ b/lib/spdm/core.c @@ -414,6 +414,17 @@ struct spdm_state *spdm_create(struct device *dev, spdm_transport *transport, } EXPORT_SYMBOL_GPL(spdm_create); +/** + * spdm_await() - Wait for ongoing spdm_authenticate() to finish + * + * @spdm_state: SPDM session state + */ +void spdm_await(struct spdm_state *spdm_state) +{ + mutex_lock(&spdm_state->lock); + mutex_unlock(&spdm_state->lock); +} + /** * spdm_destroy() - Destroy SPDM session * From 95e2e81b046d4a1ab4fa72c2ad07d2c24d976b52 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Wed, 21 Dec 2022 20:27:38 +0100 Subject: [PATCH 43/47] spdm: Add support for measuring devices Signed-off-by: Jonathan Cameron Signed-off-by: Lukas Wunner --- Documentation/ABI/testing/sysfs-devices-spdm | 13 ++++++----- Documentation/admin-guide/sysctl/spdm.rst | 8 +++---- lib/spdm/core.c | 9 ++++---- lib/spdm/req-authenticate.c | 7 +++++- lib/spdm/req-sysfs.c | 20 ++++++++-------- lib/spdm/spdm.h | 24 ++++++++++++++++---- 6 files changed, 53 insertions(+), 28 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-devices-spdm b/Documentation/ABI/testing/sysfs-devices-spdm index b35bd1c9b92d15..ae645052578fe8 100644 --- a/Documentation/ABI/testing/sysfs-devices-spdm +++ b/Documentation/ABI/testing/sysfs-devices-spdm @@ -157,9 +157,9 @@ Description: Note: To ease signature verification, the "transcript" file does not contain the trailing signature. However the signature - is part of the final CHALLENGE_AUTH message, so the protocol - dissector needs to be fed the concatenation of "transcript" - and "signature". + is part of the final CHALLENGE_AUTH or MEASUREMENT message, + so the protocol 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 @@ -190,6 +190,7 @@ Description: generation. It is one of (sans quotes): "responder-challenge_auth signing" + "responder-measurements signing" What: /sys/devices/.../signatures/[0-9]*_requester_nonce @@ -223,13 +224,13 @@ Contact: Lukas Wunner Description: If you do not trust the kernel to always use a fresh nonce, write 32 bytes to this file to set the requester nonce used - in the next SPDM authentication sequence. + in the next SPDM authentication or measurement sequence. Meant for remote attestation services. You are responsible for providing a nonce with sufficient entropy. The kernel only uses the nonce once, so provide a new one every time - you reauthenticate the device. If you do not provide a - nonce, the kernel generates a random one. + you reauthenticate or measure the device. If you do not + provide a nonce, the kernel generates a random one. After the nonce has been consumed, it becomes readable as the newest [0-9]*_requester_nonce, which proves its usage:: diff --git a/Documentation/admin-guide/sysctl/spdm.rst b/Documentation/admin-guide/sysctl/spdm.rst index 0f3846c83cd490..c6552f0290125d 100644 --- a/Documentation/admin-guide/sysctl/spdm.rst +++ b/Documentation/admin-guide/sysctl/spdm.rst @@ -20,10 +20,10 @@ The log is meant for re-verification of signatures by remote attestation services which do not trust the kernel to have verified the signatures correctly or which want to apply policy constraints of their own. A signature is computed over the transcript (a concatenation of all -SPDM messages exchanged with the device during an authentication -sequence). The transcript can be a few kBytes or up to several MBytes -in size, hence this parameter prevents the log from consuming too much -memory. +SPDM messages exchanged with the device during an authentication or +measurement sequence). The transcript can be a few kBytes or up to +several MBytes in size, hence this parameter prevents the log from +consuming too much memory. The kernel always stores the most recent signature in the log even if it exceeds ``max_signatures_size``. Additionally as many older signatures diff --git a/lib/spdm/core.c b/lib/spdm/core.c index bbefafc7639da2..d547e486e76acd 100644 --- a/lib/spdm/core.c +++ b/lib/spdm/core.c @@ -162,8 +162,8 @@ ssize_t spdm_exchange(struct spdm_state *spdm_state, * @spdm_state: SPDM session state * * Allocate a buffer to accommodate the concatenation of all SPDM messages - * exchanged during an authentication sequence. Used to verify the signature, - * as it is computed over the hashed transcript. + * exchanged during an authentication or measurement sequence. Used to verify + * the signature, as it is computed over the hashed transcript. * * Transcript size is initially one page. It grows by additional pages as * needed. Minimum size of an authentication sequence is 1k (only one slot @@ -189,8 +189,9 @@ int spdm_alloc_transcript(struct spdm_state *spdm_state) * * @spdm_state: SPDM session state * - * Free the transcript buffer after performing authentication. Reset the - * pointer to the current end of transcript as well as the allocation size. + * Free the transcript buffer after performing authentication or measurement. + * Reset the pointer to the current end of transcript as well as the allocation + * size. */ void spdm_free_transcript(struct spdm_state *spdm_state) { diff --git a/lib/spdm/req-authenticate.c b/lib/spdm/req-authenticate.c index 489fc88de74dc3..eb917e7a9765e8 100644 --- a/lib/spdm/req-authenticate.c +++ b/lib/spdm/req-authenticate.c @@ -237,6 +237,7 @@ static int spdm_negotiate_algs(struct spdm_state *spdm_state) *req = (struct spdm_negotiate_algs_req) { .code = SPDM_NEGOTIATE_ALGS, .length = cpu_to_le16(req_sz), + .measurement_specification = SPDM_MEAS_SPEC_DMTF, .base_asym_algo = cpu_to_le32(SPDM_ASYM_ALGOS), .base_hash_algo = cpu_to_le32(SPDM_HASH_ALGOS), }; @@ -264,6 +265,7 @@ static int spdm_negotiate_algs(struct spdm_state *spdm_state) spdm_state->base_asym_alg = le32_to_cpu(rsp->base_asym_sel); spdm_state->base_hash_alg = le32_to_cpu(rsp->base_hash_sel); + spdm_state->meas_hash_alg = le32_to_cpu(rsp->measurement_hash_algo); if ((spdm_state->base_asym_alg & SPDM_ASYM_ALGOS) == 0 || (spdm_state->base_hash_alg & SPDM_HASH_ALGOS) == 0) { @@ -276,7 +278,10 @@ static int spdm_negotiate_algs(struct spdm_state *spdm_state) hweight32(spdm_state->base_hash_alg) != 1 || rsp->ext_asym_sel_count != 0 || rsp->ext_hash_sel_count != 0 || - rsp->param1 > req->param1) { + rsp->param1 > req->param1 || + (spdm_state->rsp_caps & SPDM_MEAS_CAP_MASK && + (hweight32(spdm_state->meas_hash_alg) != 1 || + rsp->measurement_specification_sel != SPDM_MEAS_SPEC_DMTF))) { dev_err(spdm_state->dev, "Malformed algorithms response\n"); return -EPROTO; } diff --git a/lib/spdm/req-sysfs.c b/lib/spdm/req-sysfs.c index 5c2edac9dbf466..0f459f18d220c0 100644 --- a/lib/spdm/req-sysfs.c +++ b/lib/spdm/req-sysfs.c @@ -230,8 +230,9 @@ static unsigned int spdm_max_log_sz = SZ_16M; /* per device */ * @req_nonce: sysfs attribute of requester nonce (located within transcript). * @rsp_nonce: sysfs attribute of responder nonce (located within transcript). * @transcript: sysfs attribute of transcript (concatenation of all SPDM - * messages exchanged during an authentication sequence) sans trailing - * signature (to simplify signature verification by user space). + * messages exchanged during an authentication or measurement sequence) + * sans trailing signature (to simplify signature verification by user + * space). * @combined_prefix: sysfs attribute of combined_spdm_prefix * (SPDM 1.2.0 margin no 806, needed to verify signature). * @spdm_context: sysfs attribute of spdm_context @@ -404,9 +405,9 @@ static void spdm_shrink_log(struct spdm_state *spdm_state) * @req_nonce_off: Requester nonce offset within the transcript * @rsp_nonce_off: Responder nonce offset within the transcript * - * Allocate and populate a struct spdm_log_entry upon device authentication. - * Publish it in sysfs if the device has already been registered through - * device_add(). + * Allocate and populate a struct spdm_log_entry upon device authentication or + * measurement. Publish it in sysfs if the device has already been registered + * through device_add(). */ void spdm_create_log_entry(struct spdm_state *spdm_state, const char *spdm_context, u8 slot, @@ -518,10 +519,11 @@ void spdm_create_log_entry(struct spdm_state *spdm_state, * @spdm_state: SPDM session state * * sysfs attributes representing received SPDM signatures are not static, - * but created dynamically upon authentication. If a device was authenticated - * before it became visible in sysfs, the attributes could not be created. - * This function retroactively creates those attributes in sysfs after the - * device has become visible through device_add(). + * but created dynamically upon authentication or measurement. If a device + * was authenticated or measured before it became visible in sysfs, the + * attributes could not be created. This function retroactively creates + * those attributes in sysfs after the device has become visible through + * device_add(). */ void spdm_publish_log(struct spdm_state *spdm_state) { diff --git a/lib/spdm/spdm.h b/lib/spdm/spdm.h index aa36aa55e718a3..57c2293a2c4ae7 100644 --- a/lib/spdm/spdm.h +++ b/lib/spdm/spdm.h @@ -103,6 +103,19 @@ #define SPDM_HASH_SHA3_512 BIT(5) /* 1.0 */ #define SPDM_HASH_SM3_256 BIT(6) /* 1.2 */ +/* SPDM measurement specifications (SPDM 1.0.0 sec 4.10.1.3) */ +#define SPDM_MEAS_SPEC_DMTF BIT(0) /* 1.0 */ + +/* SPDM measurement hash algorithms (SPDM 1.0.0 table 14) */ +#define SPDM_MEAS_HASH_RAW BIT(0) /* 1.0 */ +#define SPDM_MEAS_HASH_SHA_256 BIT(1) /* 1.0 */ +#define SPDM_MEAS_HASH_SHA_384 BIT(2) /* 1.0 */ +#define SPDM_MEAS_HASH_SHA_512 BIT(3) /* 1.0 */ +#define SPDM_MEAS_HASH_SHA3_256 BIT(4) /* 1.0 */ +#define SPDM_MEAS_HASH_SHA3_384 BIT(5) /* 1.0 */ +#define SPDM_MEAS_HASH_SHA3_512 BIT(6) /* 1.0 */ +#define SPDM_MEAS_HASH_SM3_257 BIT(7) /* 1.2 */ + #if IS_ENABLED(CONFIG_CRYPTO_RSA) #define SPDM_ASYM_RSA SPDM_ASYM_RSASSA_2048 | \ SPDM_ASYM_RSASSA_3072 | \ @@ -433,10 +446,12 @@ struct spdm_error_rsp { * @rsp_caps: Cached capabilities of responder. * Received during GET_CAPABILITIES exchange. * @base_asym_alg: Asymmetric key algorithm for signature verification of - * CHALLENGE_AUTH messages. + * CHALLENGE_AUTH and MEASUREMENTS messages. * Selected by responder during NEGOTIATE_ALGORITHMS exchange. * @base_hash_alg: Hash algorithm for signature verification of - * CHALLENGE_AUTH messages. + * 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. * @supported_slots: Bitmask of responder's supported certificate slots. * Received during GET_DIGESTS exchange (from SPDM 1.3). @@ -462,8 +477,8 @@ struct spdm_error_rsp { * responder's certificate chain. * @validate: Function to validate additional leaf certificate requirements. * @transcript: Concatenation of all SPDM messages exchanged during an - * authentication sequence. Used to verify the signature, as it is - * computed over the hashed transcript. + * authentication or measurement sequence. Used to verify the signature, + * as it is computed over the hashed transcript. * @transcript_end: Pointer into the @transcript buffer. Marks the current * end of transcript. If another message is transmitted, it is appended * at this position. @@ -495,6 +510,7 @@ struct spdm_state { u32 rsp_caps; u32 base_asym_alg; u32 base_hash_alg; + u32 meas_hash_alg; unsigned long supported_slots; unsigned long provisioned_slots; From fb3f4e2498661cad3bfc4ca5874b5f9c3cc2575a Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Wed, 21 Dec 2022 20:53:32 +0100 Subject: [PATCH 44/47] spdm: Add support for key exchange Signed-off-by: Jonathan Cameron Signed-off-by: Lukas Wunner --- lib/spdm/req-authenticate.c | 42 +++++++++++++++++++++++++++++++++---- lib/spdm/spdm.h | 35 ++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 5 deletions(-) diff --git a/lib/spdm/req-authenticate.c b/lib/spdm/req-authenticate.c index eb917e7a9765e8..900ca49f41f342 100644 --- a/lib/spdm/req-authenticate.c +++ b/lib/spdm/req-authenticate.c @@ -227,21 +227,54 @@ static int spdm_negotiate_algs(struct spdm_state *spdm_state) struct spdm_negotiate_algs_req *req = spdm_state->transcript_end; struct spdm_negotiate_algs_rsp *rsp; struct spdm_req_alg_struct *req_alg_struct; - size_t req_sz = sizeof(*req); - size_t rsp_sz = sizeof(*rsp); - int rc, length; + size_t req_sz, rsp_sz; + int rc, length, i = 0; + + req_sz = sizeof(*req) + + sizeof(*req_alg_struct) * SPDM_MAX_REQ_ALG_STRUCT; /* Request length shall be <= 128 bytes (SPDM 1.1.0 margin no 185) */ BUILD_BUG_ON(req_sz > 128); *req = (struct spdm_negotiate_algs_req) { .code = SPDM_NEGOTIATE_ALGS, - .length = cpu_to_le16(req_sz), .measurement_specification = SPDM_MEAS_SPEC_DMTF, .base_asym_algo = cpu_to_le32(SPDM_ASYM_ALGOS), .base_hash_algo = cpu_to_le32(SPDM_HASH_ALGOS), }; + /* + * Only OpaqueDataFmt1 is supported with SPDM 1.2 or later + * (Secured Messages using SPDM Spec 1.1.0 margin no 118) + */ + if (spdm_state->version >= 0x12 && + spdm_state->rsp_caps & SPDM_KEY_EX_CAP) + req->other_params_support = SPDM_OPAQUE_DATA_FMT_GENERAL; + + req_alg_struct = (struct spdm_req_alg_struct *)(req + 1); + if (spdm_state->rsp_caps & SPDM_KEY_EX_CAP) { + req_alg_struct[i++] = (struct spdm_req_alg_struct) { + .alg_type = SPDM_REQ_ALG_STRUCT_DHE, + .alg_count = 0x20, + .alg_supported = cpu_to_le16(SPDM_DHE_ALGOS), + }; + req_alg_struct[i++] = (struct spdm_req_alg_struct) { + .alg_type = SPDM_REQ_ALG_STRUCT_AEAD, + .alg_count = 0x20, + .alg_supported = cpu_to_le16(SPDM_AEAD_ALGOS), + }; + req_alg_struct[i++] = (struct spdm_req_alg_struct) { + .alg_type = SPDM_REQ_ALG_STRUCT_KEY_SCHEDULE, + .alg_count = 0x20, + .alg_supported = cpu_to_le16(SPDM_KEY_SCHEDULE_SPDM), + }; + } + WARN_ON(i > SPDM_MAX_REQ_ALG_STRUCT); + req_sz = sizeof(*req) + i * sizeof(*req_alg_struct); + rsp_sz = sizeof(*rsp) + i * sizeof(*req_alg_struct); + req->length = cpu_to_le16(req_sz); + req->param1 = i; + rsp = spdm_state->transcript_end += req_sz; rc = spdm_exchange(spdm_state, req, req_sz, rsp, rsp_sz); @@ -279,6 +312,7 @@ static int spdm_negotiate_algs(struct spdm_state *spdm_state) rsp->ext_asym_sel_count != 0 || rsp->ext_hash_sel_count != 0 || rsp->param1 > req->param1 || + rsp->other_params_sel != req->other_params_support || (spdm_state->rsp_caps & SPDM_MEAS_CAP_MASK && (hweight32(spdm_state->meas_hash_alg) != 1 || rsp->measurement_specification_sel != SPDM_MEAS_SPEC_DMTF))) { diff --git a/lib/spdm/spdm.h b/lib/spdm/spdm.h index 57c2293a2c4ae7..00fd4e223d46be 100644 --- a/lib/spdm/spdm.h +++ b/lib/spdm/spdm.h @@ -116,6 +116,31 @@ #define SPDM_MEAS_HASH_SHA3_512 BIT(6) /* 1.0 */ #define SPDM_MEAS_HASH_SM3_257 BIT(7) /* 1.2 */ +/* SPDM Diffie-Hellman Ephemeral groups (SPDM 1.1.0 margin no 189) */ +#define SPDM_REQ_ALG_STRUCT_DHE 2 /* 1.1 */ +#define SPDM_DHE_FFDHE_2048 BIT(0) /* 1.1 */ +#define SPDM_DHE_FFDHE_3072 BIT(1) /* 1.1 */ +#define SPDM_DHE_FFDHE_4096 BIT(2) /* 1.1 */ +#define SPDM_DHE_SECP_256R1 BIT(3) /* 1.1 */ +#define SPDM_DHE_SECP_384R1 BIT(4) /* 1.1 */ +#define SPDM_DHE_SECP_521R1 BIT(5) /* 1.1 */ +#define SPDM_DHE_SM2_P256 BIT(6) /* 1.2 */ + +/* SPDM Authenticated Encryption w/ AD algorithms (SPDM 1.1.0 margin no 190) */ +#define SPDM_REQ_ALG_STRUCT_AEAD 3 /* 1.1 */ +#define SPDM_AEAD_AES_128_GCM BIT(0) /* 1.1 */ +#define SPDM_AEAD_AES_256_GCM BIT(1) /* 1.1 */ +#define SPDM_AEAD_CHACHA20_POLY1305 BIT(2) /* 1.1 */ +#define SPDM_AEAD_SM4_GCM BIT(3) /* 1.2 */ + +/* SPDM key schedule algorithms (SPDM 1.1.0 margin no 192) */ +#define SPDM_REQ_ALG_STRUCT_KEY_SCHEDULE 5 /* 1.1 */ +#define SPDM_KEY_SCHEDULE_SPDM BIT(0) /* 1.1 */ + +/* SPDM opaque data formats (SPDM 1.2.0 margin no 261) */ +#define SPDM_OPAQUE_DATA_FMT_VENDOR BIT(0) /* 1.2 */ +#define SPDM_OPAQUE_DATA_FMT_GENERAL BIT(1) /* 1.2 */ + #if IS_ENABLED(CONFIG_CRYPTO_RSA) #define SPDM_ASYM_RSA SPDM_ASYM_RSASSA_2048 | \ SPDM_ASYM_RSASSA_3072 | \ @@ -152,6 +177,14 @@ #define SPDM_HASH_ALGOS (SPDM_HASH_SHA2_256 | \ SPDM_HASH_SHA2_384_512) +#define SPDM_DHE_ALGOS (SPDM_DHE_FFDHE_2048 | \ + SPDM_DHE_FFDHE_3072 | \ + SPDM_DHE_SECP_256R1 | \ + SPDM_DHE_SECP_384R1) + +#define SPDM_AEAD_ALGOS (SPDM_AEAD_AES_256_GCM | \ + SPDM_AEAD_CHACHA20_POLY1305) + /* * Common header shared by all messages. * Note that the meaning of param1 and param2 is message dependent. @@ -288,7 +321,7 @@ struct spdm_negotiate_algs_rsp { } __packed; /* Maximum number of ReqAlgStructs sent by this implementation */ -#define SPDM_MAX_REQ_ALG_STRUCT 0 +#define SPDM_MAX_REQ_ALG_STRUCT 3 struct spdm_req_alg_struct { u8 alg_type; From 4ee99fc154b2542aaf1bbb76d2db7caf3c4c0019 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Wed, 21 Dec 2022 20:57:31 +0100 Subject: [PATCH 45/47] spdm: Add support for mutual authentication Signed-off-by: Jonathan Cameron Signed-off-by: Lukas Wunner --- lib/spdm/req-authenticate.c | 10 +++++++++- lib/spdm/spdm.h | 5 ++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/spdm/req-authenticate.c b/lib/spdm/req-authenticate.c index 900ca49f41f342..16eab10a7f274a 100644 --- a/lib/spdm/req-authenticate.c +++ b/lib/spdm/req-authenticate.c @@ -251,6 +251,7 @@ static int spdm_negotiate_algs(struct spdm_state *spdm_state) spdm_state->rsp_caps & SPDM_KEY_EX_CAP) req->other_params_support = SPDM_OPAQUE_DATA_FMT_GENERAL; + /* ReqAlgStruct order shall be by AlgType (SPDM 1.1.0 margin no 186) */ req_alg_struct = (struct spdm_req_alg_struct *)(req + 1); if (spdm_state->rsp_caps & SPDM_KEY_EX_CAP) { req_alg_struct[i++] = (struct spdm_req_alg_struct) { @@ -263,12 +264,19 @@ static int spdm_negotiate_algs(struct spdm_state *spdm_state) .alg_count = 0x20, .alg_supported = cpu_to_le16(SPDM_AEAD_ALGOS), }; + } + if (spdm_state->rsp_caps & SPDM_MUT_AUTH_CAP) + req_alg_struct[i++] = (struct spdm_req_alg_struct) { + .alg_type = SPDM_REQ_ALG_STRUCT_REQ_BASE_ASYM_ALG, + .alg_count = 0x20, + .alg_supported = cpu_to_le16(SPDM_ASYM_ALGOS), + }; + if (spdm_state->rsp_caps & SPDM_KEY_EX_CAP) req_alg_struct[i++] = (struct spdm_req_alg_struct) { .alg_type = SPDM_REQ_ALG_STRUCT_KEY_SCHEDULE, .alg_count = 0x20, .alg_supported = cpu_to_le16(SPDM_KEY_SCHEDULE_SPDM), }; - } WARN_ON(i > SPDM_MAX_REQ_ALG_STRUCT); req_sz = sizeof(*req) + i * sizeof(*req_alg_struct); rsp_sz = sizeof(*rsp) + i * sizeof(*req_alg_struct); diff --git a/lib/spdm/spdm.h b/lib/spdm/spdm.h index 00fd4e223d46be..16c18ceb43e727 100644 --- a/lib/spdm/spdm.h +++ b/lib/spdm/spdm.h @@ -133,6 +133,9 @@ #define SPDM_AEAD_CHACHA20_POLY1305 BIT(2) /* 1.1 */ #define SPDM_AEAD_SM4_GCM BIT(3) /* 1.2 */ +/* SPDM asymmetric key signature algorithms (SPDM 1.1.0 margin no 191) */ +#define SPDM_REQ_ALG_STRUCT_REQ_BASE_ASYM_ALG 4 /* 1.1 */ + /* SPDM key schedule algorithms (SPDM 1.1.0 margin no 192) */ #define SPDM_REQ_ALG_STRUCT_KEY_SCHEDULE 5 /* 1.1 */ #define SPDM_KEY_SCHEDULE_SPDM BIT(0) /* 1.1 */ @@ -321,7 +324,7 @@ struct spdm_negotiate_algs_rsp { } __packed; /* Maximum number of ReqAlgStructs sent by this implementation */ -#define SPDM_MAX_REQ_ALG_STRUCT 3 +#define SPDM_MAX_REQ_ALG_STRUCT 4 struct spdm_req_alg_struct { u8 alg_type; From 2d75092f0e7898aca2cfcf49e84898bf3afd3f61 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Thu, 31 Aug 2023 14:29:09 +0200 Subject: [PATCH 46/47] spdm: Add support for large SPDM message transfer mechanism Signed-off-by: Lukas Wunner --- lib/spdm/req-authenticate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/spdm/req-authenticate.c b/lib/spdm/req-authenticate.c index 16eab10a7f274a..8f53a77e0ae7a5 100644 --- a/lib/spdm/req-authenticate.c +++ b/lib/spdm/req-authenticate.c @@ -117,7 +117,7 @@ static int spdm_get_capabilities(struct spdm_state *spdm_state) req_sz = sizeof(*req); rsp_sz = sizeof(*rsp); req->data_transfer_size = cpu_to_le32(spdm_state->transport_sz); - req->max_spdm_msg_size = cpu_to_le32(spdm_state->transport_sz); + req->max_spdm_msg_size = cpu_to_le32(UINT_MAX); } rsp = spdm_state->transcript_end += req_sz; From bc9f23e18a00634956897c42ea8d37f26943f7df Mon Sep 17 00:00:00 2001 From: Wilfred Mallawa Date: Fri, 30 Aug 2024 12:14:06 +1000 Subject: [PATCH 47/47] spdm: large-transfer: set chunk capability In the flags field of a GET_CAPABILITIES request, ensure we indicate that large transfers are supported by setting the CHUNK_CAP bit [1]. [1] SPDM Spec 1.3: Table 13 Signed-off-by: Wilfred Mallawa --- lib/spdm/req-authenticate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/spdm/req-authenticate.c b/lib/spdm/req-authenticate.c index 8f53a77e0ae7a5..180a2374e230ca 100644 --- a/lib/spdm/req-authenticate.c +++ b/lib/spdm/req-authenticate.c @@ -118,6 +118,7 @@ static int spdm_get_capabilities(struct spdm_state *spdm_state) rsp_sz = sizeof(*rsp); req->data_transfer_size = cpu_to_le32(spdm_state->transport_sz); req->max_spdm_msg_size = cpu_to_le32(UINT_MAX); + req->flags = cpu_to_le32(req->flags | SPDM_CHUNK_CAP); } rsp = spdm_state->transcript_end += req_sz;