diff --git a/kernel-open/conftest.sh b/kernel-open/conftest.sh index fdceda72..3bfe39aa 100755 --- a/kernel-open/conftest.sh +++ b/kernel-open/conftest.sh @@ -6721,6 +6721,47 @@ compile_test() { compile_check_conftest "$CODE" "NV_CRYPTO_PRESENT" "" "symbols" ;; + crypto_akcipher_verify) + # + # Determine whether the crypto_akcipher_verify API is still present. + # It was removed by commit 6b34562 ('crypto: akcipher - Drop sign/verify operations') + # in v6.13-rc1 (2024-10-04). + # + # This test is dependent on the crypto conftest to determine whether crypto should be + # enabled at all. That means that if the kernel is old enough such that crypto_akcipher_verify + # + # The test merely checks for the presence of the API, as it assumes that if the API + # is no longer present, the new API to replace it (crypto_sig_verify) must be present. + # If the kernel version is too old to have crypto_akcipher_verify, it will fail the crypto + # conftest above and all crypto code will be compiled out. + # + CODE=" + #include + #include + void conftest_crypto_akcipher_verify(void) { + (void)crypto_akcipher_verify; + }" + + compile_check_conftest "$CODE" "NV_CRYPTO_AKCIPHER_VERIFY_PRESENT" "" "symbols" + ;; + + ecc_digits_from_bytes) + # + # Determine whether ecc_digits_from_bytes is present. + # It was added in commit c6ab5c915da4 ('crypto: ecc - Prevent ecc_digits_from_bytes from + # reading too many bytes') in v6.10. + # + # This functionality is needed when crypto_akcipher_verify is not present. + # + CODE=" + #include + void conftest_ecc_digits_from_bytes(void) { + (void)ecc_digits_from_bytes; + }" + + compile_check_conftest "$CODE" "NV_ECC_DIGITS_FROM_BYTES_PRESENT" "" "symbols" + ;; + mempolicy_has_unified_nodes) # # Determine if the 'mempolicy' structure has diff --git a/kernel-open/nvidia/internal_crypt_lib.h b/kernel-open/nvidia/internal_crypt_lib.h index 2eac7d5e..917acb26 100644 --- a/kernel-open/nvidia/internal_crypt_lib.h +++ b/kernel-open/nvidia/internal_crypt_lib.h @@ -64,7 +64,9 @@ * old or even just user disabled. If we should use LKCA, include headers, else * define stubs to return errors. */ -#if defined(NV_CRYPTO_PRESENT) && defined (NV_CONFIG_CRYPTO_PRESENT) +#if defined(NV_CRYPTO_PRESENT) && defined (NV_CONFIG_CRYPTO_PRESENT) && \ + (defined(NV_CRYPTO_AKCIPHER_VERIFY_PRESENT) || \ + (defined(NV_CRYPTO_SIG_H_PRESENT) && defined(NV_ECC_DIGITS_FROM_BYTES_PRESENT))) #define USE_LKCA 1 #endif diff --git a/kernel-open/nvidia/libspdm_ecc.c b/kernel-open/nvidia/libspdm_ecc.c index 1f8f0100..a9eb4db5 100644 --- a/kernel-open/nvidia/libspdm_ecc.c +++ b/kernel-open/nvidia/libspdm_ecc.c @@ -30,14 +30,26 @@ MODULE_SOFTDEP("pre: ecdh_generic,ecdsa_generic"); #include #include #include +#ifndef NV_CRYPTO_AKCIPHER_VERIFY_PRESENT +#include + +struct signature +{ + u64 r[ECC_MAX_DIGITS]; + u64 s[ECC_MAX_DIGITS]; +}; +#endif // NV_CRYPTO_AKCIPHER_VERIFY_PRESENT + +#define ECDSA_PUBKEY_HEADER_XY_PRESENT (0x4) struct ecc_ctx { unsigned int curve_id; u64 priv_key[ECC_MAX_DIGITS]; // In big endian struct { - // ecdsa wants byte preceding pub_key to be set to '4' - u64 pub_key_prefix; + // ecdsa pubkey has header indicating length of pubkey + u8 padding[7]; + u8 pub_key_prefix; u64 pub_key[2 * ECC_MAX_DIGITS]; }; @@ -221,25 +233,84 @@ bool lkca_ec_compute_key(void *ec_context, const uint8_t *peer_public, #endif } -bool lkca_ecdsa_verify(void *ec_context, size_t hash_nid, - const uint8_t *message_hash, size_t hash_size, - const uint8_t *signature, size_t sig_size) +#ifndef NV_CRYPTO_AKCIPHER_VERIFY_PRESENT +static bool lkca_ecdsa_verify_crypto_sig(void *ec_context, size_t hash_nid, + const uint8_t *message_hash, size_t hash_size, + const uint8_t *signature, size_t sig_size) { #ifndef USE_LKCA return false; #else struct ecc_ctx *ctx = ec_context; + u8 *pub_key; + int err; + DECLARE_CRYPTO_WAIT(wait); + struct crypto_sig * tfm = NULL; + struct signature sig; + + if (sig_size != ctx->size || !ctx->pub_key_set) + { + return false; + } + + tfm = crypto_alloc_sig(ctx->name, CRYPTO_ALG_TYPE_SIG, 0); + if (IS_ERR(tfm)) { + pr_info("crypto_alloc_sig failed in lkca_ecdsa_verify\n"); + return false; + } + + // modify header of pubkey to indicate size + pub_key = (u8 *) &(ctx->pub_key_prefix); + *pub_key = ECDSA_PUBKEY_HEADER_XY_PRESENT; + err = crypto_sig_set_pubkey(tfm, pub_key, ctx->size + 1); + if (err != 0) + { + pr_info("crypto_sig_set_pubkey failed in lkca_ecdsa_verify: %d", -err); + goto failTfm; + } + + // + // Compared to the way we receive the signature, we need to: + // - swap order of all digits + // - swap endianness for each digit + // + memset(&sig, 0, sizeof(sig)); + ecc_digits_from_bytes(signature, ctx->size/2, sig.r, ECC_MAX_DIGITS); + ecc_digits_from_bytes(signature + ctx->size/2, ctx->size/2, sig.s, ECC_MAX_DIGITS); + + err = crypto_sig_verify(tfm, (void *)&sig, sizeof(sig), message_hash, hash_size); + if (err != 0) + { + pr_info("crypto_sig_verify failed in lkca_ecdsa_verify %d\n", -err); + } + +failTfm: + crypto_free_sig(tfm); + + return err == 0; +#endif // USE_LKCA +} + +#else // NV_CRYPTO_AKCIPHER_VERIFY_PRESENT +static bool lkca_ecdsa_verify_akcipher(void *ec_context, size_t hash_nid, + const uint8_t *message_hash, size_t hash_size, + const uint8_t *signature, size_t sig_size) +{ +#ifndef USE_LKCA + return false; +#else // USE_LKCA + struct ecc_ctx *ctx = ec_context; + u8 *pub_key; + int err; + DECLARE_CRYPTO_WAIT(wait); // Roundabout way u64 ber_max_len = 3 + 2 * (4 + (ECC_MAX_BYTES)); u64 ber_len = 0; u8 *ber = NULL; - u8 *pub_key; struct akcipher_request *req = NULL; struct crypto_akcipher *tfm = NULL; struct scatterlist sg; - DECLARE_CRYPTO_WAIT(wait); - int err; if (sig_size != ctx->size) { return false; @@ -251,21 +322,21 @@ bool lkca_ecdsa_verify(void *ec_context, size_t hash_nid, tfm = crypto_alloc_akcipher(ctx->name, CRYPTO_ALG_TYPE_AKCIPHER, 0); if (IS_ERR(tfm)) { - pr_info("ALLOC FAILED\n"); + pr_info("crypto_alloc_akcipher failed in lkca_ecdsa_verify\n"); return false; } - pub_key = (u8 *) ctx->pub_key; - pub_key--; // Go back into byte of pub_key_prefix - *pub_key = 4; // And set it to 4 to placate kernel + // modify header of pubkey to indicate size + pub_key = (u8 *) &(ctx->pub_key_prefix); + *pub_key = ECDSA_PUBKEY_HEADER_XY_PRESENT; if ((err = crypto_akcipher_set_pub_key(tfm, pub_key, ctx->size + 1)) != 0) { - pr_info("SET PUB KEY FAILED: %d\n", -err); + pr_info("crypto_akcipher_set_pub_key failed in lkca_ecdsa_verify: %d\n", -err); goto failTfm; } req = akcipher_request_alloc(tfm, GFP_KERNEL); if (IS_ERR(req)) { - pr_info("REQUEST ALLOC FAILED\n"); + pr_info("akcipher_request_alloc failed in lkca_ecdsa_verify\n"); goto failTfm; } @@ -310,9 +381,8 @@ bool lkca_ecdsa_verify(void *ec_context, size_t hash_nid, CRYPTO_TFM_REQ_MAY_SLEEP, crypto_req_done, &wait); akcipher_request_set_crypt(req, &sg, NULL, ber_len, hash_size); err = crypto_wait_req(crypto_akcipher_verify(req), &wait); - if (err != 0){ - pr_info("Verify FAILED %d\n", -err); + pr_info("crypto_akcipher_verify failed in lkca_ecdsa_verify %d\n", -err); } kfree(ber); @@ -322,5 +392,19 @@ failTfm: crypto_free_akcipher(tfm); return err == 0; -#endif +#endif // USE_LKCA +} +#endif // NV_CRYPTO_AKCIPHER_VERIFY_PRESENT + +bool lkca_ecdsa_verify(void *ec_context, size_t hash_nid, + const uint8_t *message_hash, size_t hash_size, + const uint8_t *signature, size_t sig_size) +{ +#ifndef NV_CRYPTO_AKCIPHER_VERIFY_PRESENT + return lkca_ecdsa_verify_crypto_sig(ec_context, hash_nid, message_hash, hash_size, + signature, sig_size); +#else // NV_CRYPTO_AKCIPHER_VERIFY_PRESENT + return lkca_ecdsa_verify_akcipher(ec_context, hash_nid, message_hash, hash_size, + signature, sig_size); +#endif // NV_CRYPTO_AKCIPHER_VERIFY_PRESENT }