diff --git a/linuxkm/lkcapi_ecdsa_glue.c b/linuxkm/lkcapi_ecdsa_glue.c index c9daf0b000..74cc6eeb7f 100644 --- a/linuxkm/lkcapi_ecdsa_glue.c +++ b/linuxkm/lkcapi_ecdsa_glue.c @@ -48,26 +48,17 @@ #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 13, 0) /* - * notes: - * - ecdsa supported with linux 6.12 and earlier for now, only. - * - pkcs1pad rsa supported both before and after linux 6.13, but - * without sign/verify after linux 6.13. - * - * In linux 6.13 the sign/verify callbacks were removed from + * note: In linux 6.13 the sign/verify callbacks were removed from * akcipher_alg, and ecdsa changed from a struct akcipher_alg type to * struct sig_alg type. * - * pkcs1pad rsa remained a struct akcipher_alg, but without sign/verify - * functionality. + * The 6.13+ base "ecdsa-nist-pN" sig_alg is verify-only, and takes the + * signature in the kernel's raw format (struct ecdsa_raw_sig -- r and s + * as little endian u64 digit arrays). The X9.62 (ASN.1 DER) and IEEE + * P1363 signature encodings are handled by the kernel's "x962(...)" and + * "p1363(...)" wrapping templates, instantiated around the base alg. */ - #if defined (LINUXKM_LKCAPI_REGISTER_ECDSA) - #undef LINUXKM_LKCAPI_REGISTER_ECDSA - #endif /* LINUXKM_LKCAPI_REGISTER_ECDSA */ - - #if defined(LINUXKM_LKCAPI_REGISTER_ALL_KCONFIG) && defined(CONFIG_CRYPTO_ECDSA) && \ - !defined(LINUXKM_LKCAPI_DONT_REGISTER_ECDSA) - #error Config conflict: missing implementation forces off LINUXKM_LKCAPI_REGISTER_ECDSA. - #endif + #define LINUXKM_ECDSA_SIG_ALG #endif #if defined(LINUXKM_LKCAPI_REGISTER_ALL_KCONFIG) && \ @@ -82,6 +73,27 @@ #include #include +#ifdef LINUXKM_ECDSA_SIG_ALG + #define ecdsa_tfm_type crypto_sig + #define ecdsa_tfm_ctx_cb crypto_sig_ctx + + /* Mirror of struct ecdsa_raw_sig from the kernel's + * include/crypto/internal/ecc.h, which can't be included here because its + * struct ecc_point collides with wolfCrypt's. The kernel dimensions the + * digit arrays with ECC_MAX_DIGITS = DIV_ROUND_UP(521, 64) (NIST P521). + * The u64 digits are ordered little endian, with native endianness within + * each digit. + */ + #define KM_ECDSA_MAX_DIGITS ((521 + 63) / 64) + struct km_ecdsa_raw_sig { + u64 r[KM_ECDSA_MAX_DIGITS]; + u64 s[KM_ECDSA_MAX_DIGITS]; + }; +#else + #define ecdsa_tfm_type crypto_akcipher + #define ecdsa_tfm_ctx_cb akcipher_tfm_ctx +#endif /* !LINUXKM_ECDSA_SIG_ALG */ + #if defined(WOLFSSL_SP_X86_64_ASM) && !defined(NO_AVX2_SUPPORT) #define WOLFKM_ECDSA_DRIVER_ISA_EXT "-avx2" #else @@ -123,26 +135,52 @@ struct km_ecdsa_ctx { ecc_key * key; int curve_id; word32 curve_len; +#ifdef LINUXKM_ECDSA_SIG_ALG + word32 curve_nbits; +#endif /* LINUXKM_ECDSA_SIG_ALG */ }; /* shared ecdsa callbacks */ -static void km_ecdsa_exit(struct crypto_akcipher *tfm); -static int km_ecdsa_set_pub(struct crypto_akcipher *tfm, +static void km_ecdsa_exit(struct ecdsa_tfm_type *tfm); +static int km_ecdsa_set_pub(struct ecdsa_tfm_type *tfm, const void *key, unsigned int keylen); +#ifdef LINUXKM_ECDSA_SIG_ALG +static unsigned int km_ecdsa_key_size(struct crypto_sig *tfm); +static unsigned int km_ecdsa_digest_size(struct crypto_sig *tfm); +static int km_ecdsa_verify(struct crypto_sig *tfm, + const void *src, unsigned int slen, + const void *digest, unsigned int dlen); +#else static unsigned int km_ecdsa_max_size(struct crypto_akcipher *tfm); static int km_ecdsa_verify(struct akcipher_request *req); +#endif /* !LINUXKM_ECDSA_SIG_ALG */ /* ecdsa_nist_pN callbacks */ #if defined(LINUXKM_ECC192) -static int km_ecdsa_nist_p192_init(struct crypto_akcipher *tfm); +static int km_ecdsa_nist_p192_init(struct ecdsa_tfm_type *tfm); #endif /* LINUXKM_ECC192 */ -static int km_ecdsa_nist_p256_init(struct crypto_akcipher *tfm); -static int km_ecdsa_nist_p384_init(struct crypto_akcipher *tfm); +static int km_ecdsa_nist_p256_init(struct ecdsa_tfm_type *tfm); +static int km_ecdsa_nist_p384_init(struct ecdsa_tfm_type *tfm); #if defined(HAVE_ECC521) -static int km_ecdsa_nist_p521_init(struct crypto_akcipher *tfm); +static int km_ecdsa_nist_p521_init(struct ecdsa_tfm_type *tfm); #endif /* HAVE_ECC521 */ #if defined(LINUXKM_ECC192) +#ifdef LINUXKM_ECDSA_SIG_ALG +static struct sig_alg ecdsa_nist_p192 = { + .base.cra_name = WOLFKM_ECDSA_P192_NAME, + .base.cra_driver_name = WOLFKM_ECDSA_P192_DRIVER, + .base.cra_priority = WOLFSSL_LINUXKM_LKCAPI_PRIORITY, + .base.cra_module = THIS_MODULE, + .base.cra_ctxsize = sizeof(struct km_ecdsa_ctx), + .verify = km_ecdsa_verify, + .set_pub_key = km_ecdsa_set_pub, + .key_size = km_ecdsa_key_size, + .digest_size = km_ecdsa_digest_size, + .init = km_ecdsa_nist_p192_init, + .exit = km_ecdsa_exit, +}; +#else /* !LINUXKM_ECDSA_SIG_ALG */ static struct akcipher_alg ecdsa_nist_p192 = { .base.cra_name = WOLFKM_ECDSA_P192_NAME, .base.cra_driver_name = WOLFKM_ECDSA_P192_DRIVER, @@ -155,8 +193,24 @@ static struct akcipher_alg ecdsa_nist_p192 = { .init = km_ecdsa_nist_p192_init, .exit = km_ecdsa_exit, }; +#endif /* !LINUXKM_ECDSA_SIG_ALG */ #endif /* LINUXKM_ECC192 */ +#ifdef LINUXKM_ECDSA_SIG_ALG +static struct sig_alg ecdsa_nist_p256 = { + .base.cra_name = WOLFKM_ECDSA_P256_NAME, + .base.cra_driver_name = WOLFKM_ECDSA_P256_DRIVER, + .base.cra_priority = WOLFSSL_LINUXKM_LKCAPI_PRIORITY, + .base.cra_module = THIS_MODULE, + .base.cra_ctxsize = sizeof(struct km_ecdsa_ctx), + .verify = km_ecdsa_verify, + .set_pub_key = km_ecdsa_set_pub, + .key_size = km_ecdsa_key_size, + .digest_size = km_ecdsa_digest_size, + .init = km_ecdsa_nist_p256_init, + .exit = km_ecdsa_exit, +}; +#else /* !LINUXKM_ECDSA_SIG_ALG */ static struct akcipher_alg ecdsa_nist_p256 = { .base.cra_name = WOLFKM_ECDSA_P256_NAME, .base.cra_driver_name = WOLFKM_ECDSA_P256_DRIVER, @@ -169,7 +223,23 @@ static struct akcipher_alg ecdsa_nist_p256 = { .init = km_ecdsa_nist_p256_init, .exit = km_ecdsa_exit, }; +#endif /* !LINUXKM_ECDSA_SIG_ALG */ +#ifdef LINUXKM_ECDSA_SIG_ALG +static struct sig_alg ecdsa_nist_p384 = { + .base.cra_name = WOLFKM_ECDSA_P384_NAME, + .base.cra_driver_name = WOLFKM_ECDSA_P384_DRIVER, + .base.cra_priority = WOLFSSL_LINUXKM_LKCAPI_PRIORITY, + .base.cra_module = THIS_MODULE, + .base.cra_ctxsize = sizeof(struct km_ecdsa_ctx), + .verify = km_ecdsa_verify, + .set_pub_key = km_ecdsa_set_pub, + .key_size = km_ecdsa_key_size, + .digest_size = km_ecdsa_digest_size, + .init = km_ecdsa_nist_p384_init, + .exit = km_ecdsa_exit, +}; +#else /* !LINUXKM_ECDSA_SIG_ALG */ static struct akcipher_alg ecdsa_nist_p384 = { .base.cra_name = WOLFKM_ECDSA_P384_NAME, .base.cra_driver_name = WOLFKM_ECDSA_P384_DRIVER, @@ -182,8 +252,24 @@ static struct akcipher_alg ecdsa_nist_p384 = { .init = km_ecdsa_nist_p384_init, .exit = km_ecdsa_exit, }; +#endif /* !LINUXKM_ECDSA_SIG_ALG */ #if defined(HAVE_ECC521) +#ifdef LINUXKM_ECDSA_SIG_ALG +static struct sig_alg ecdsa_nist_p521 = { + .base.cra_name = WOLFKM_ECDSA_P521_NAME, + .base.cra_driver_name = WOLFKM_ECDSA_P521_DRIVER, + .base.cra_priority = WOLFSSL_LINUXKM_LKCAPI_PRIORITY, + .base.cra_module = THIS_MODULE, + .base.cra_ctxsize = sizeof(struct km_ecdsa_ctx), + .verify = km_ecdsa_verify, + .set_pub_key = km_ecdsa_set_pub, + .key_size = km_ecdsa_key_size, + .digest_size = km_ecdsa_digest_size, + .init = km_ecdsa_nist_p521_init, + .exit = km_ecdsa_exit, +}; +#else /* !LINUXKM_ECDSA_SIG_ALG */ static struct akcipher_alg ecdsa_nist_p521 = { .base.cra_name = WOLFKM_ECDSA_P521_NAME, .base.cra_driver_name = WOLFKM_ECDSA_P521_DRIVER, @@ -196,6 +282,7 @@ static struct akcipher_alg ecdsa_nist_p521 = { .init = km_ecdsa_nist_p521_init, .exit = km_ecdsa_exit, }; +#endif /* !LINUXKM_ECDSA_SIG_ALG */ #endif /* HAVE_ECC521 */ /** @@ -204,18 +291,18 @@ static struct akcipher_alg ecdsa_nist_p521 = { * Kernel crypto ECDSA api expects raw uncompressed format with concatenated * x and y points, with leading 0x04 on pub key. * - * param tfm the crypto_akcipher transform + * param tfm the crypto_akcipher (crypto_sig on linux 6.13+) transform * param key raw uncompressed x, y points, with leading 0x04 * param keylen key length * */ -static int km_ecdsa_set_pub(struct crypto_akcipher *tfm, const void *key, +static int km_ecdsa_set_pub(struct ecdsa_tfm_type *tfm, const void *key, unsigned int keylen) { int err = 0; struct km_ecdsa_ctx * ctx = NULL; const byte * pub = key; - ctx = akcipher_tfm_ctx(tfm); + ctx = ecdsa_tfm_ctx_cb(tfm); switch (ctx->curve_len) { #if defined(LINUXKM_ECC192) @@ -279,23 +366,45 @@ static int km_ecdsa_set_pub(struct crypto_akcipher *tfm, const void *key, return err; } -static unsigned int km_ecdsa_max_size(struct crypto_akcipher *tfm) +#ifdef LINUXKM_ECDSA_SIG_ALG +/* Returns the ECDSA key size (mirrors the kernel's ecdsa_key_size()): + * linux kernel version < 6.15.3: key size in bytes. + * linux kernel version >= 6.15.3: key size in bits. + * */ +static unsigned int km_ecdsa_key_size(struct crypto_sig *tfm) { - struct km_ecdsa_ctx * ctx = NULL; - - ctx = akcipher_tfm_ctx(tfm); - - #ifdef WOLFKM_DEBUG_ECDSA - pr_info("info: exiting km_ecdsa_max_size\n"); - #endif /* WOLFKM_DEBUG_ECDSA */ - return (unsigned int) ctx->curve_len; + struct km_ecdsa_ctx *ctx = crypto_sig_ctx(tfm); + #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 15, 3) + return ctx->curve_nbits; + #else + return ctx->curve_len; + #endif } -static void km_ecdsa_exit(struct crypto_akcipher *tfm) +/* Mirrors the kernel's ecdsa_digest_size(): ECDSA keys are much smaller than + * RSA keys, and can operate on (hashed) inputs that are larger than the key + * size, e.g. a SHA-384 digest verified with a P-256 key, so advertise the + * largest digest size km_ecdsa_verify() accepts rather than the key size + * that crypto/sig.c would default to. + * */ +static unsigned int km_ecdsa_digest_size(struct crypto_sig *tfm) +{ + (void)tfm; + return WC_MAX_DIGEST_SIZE; +} +#else /* !LINUXKM_ECDSA_SIG_ALG */ +static unsigned int km_ecdsa_max_size(struct crypto_akcipher *tfm) +{ + struct km_ecdsa_ctx *ctx = akcipher_tfm_ctx(tfm); + return ctx->curve_len; +} +#endif /* !LINUXKM_ECDSA_SIG_ALG */ + +static void km_ecdsa_exit(struct ecdsa_tfm_type *tfm) { struct km_ecdsa_ctx * ctx = NULL; - ctx = akcipher_tfm_ctx(tfm); + ctx = ecdsa_tfm_ctx_cb(tfm); if (ctx->key) { wc_ecc_free(ctx->key); @@ -309,12 +418,11 @@ static void km_ecdsa_exit(struct crypto_akcipher *tfm) return; } -static int km_ecdsa_init(struct crypto_akcipher *tfm, int curve_id) +static int km_ecdsa_init(struct ecdsa_tfm_type *tfm, int curve_id) { - struct km_ecdsa_ctx * ctx = NULL; - int ret = 0; + struct km_ecdsa_ctx *ctx = ecdsa_tfm_ctx_cb(tfm); + int ret = 0; - ctx = akcipher_tfm_ctx(tfm); XMEMSET(ctx, 0, sizeof(struct km_ecdsa_ctx)); ctx->curve_id = curve_id; ctx->curve_len = 0; @@ -330,10 +438,17 @@ static int km_ecdsa_init(struct crypto_akcipher *tfm, int curve_id) ctx->curve_len = (word32) ret; + #ifdef LINUXKM_ECDSA_SIG_ALG + /* NIST P-521 is the only supported curve whose bit length isn't a + * multiple of 8 (the kernel's ecdsa_key_size() returns + * curve->nbits). */ + ctx->curve_nbits = (curve_id == ECC_SECP521R1) ? 521 : + (ctx->curve_len * WOLFSSL_BIT_SIZE); + #endif /* LINUXKM_ECDSA_SIG_ALG */ + ctx->key = (ecc_key *)malloc(sizeof(ecc_key)); - if (!ctx->key) { + if (!ctx->key) return -ENOMEM; - } ret = wc_ecc_init(ctx->key); if (ret < 0) { @@ -350,29 +465,182 @@ static int km_ecdsa_init(struct crypto_akcipher *tfm, int curve_id) } #if defined(LINUXKM_ECC192) -static int km_ecdsa_nist_p192_init(struct crypto_akcipher *tfm) +static int km_ecdsa_nist_p192_init(struct ecdsa_tfm_type *tfm) { return km_ecdsa_init(tfm, ECC_SECP192R1); } #endif /* LINUXKM_ECC192 */ -static int km_ecdsa_nist_p256_init(struct crypto_akcipher *tfm) +static int km_ecdsa_nist_p256_init(struct ecdsa_tfm_type *tfm) { return km_ecdsa_init(tfm, ECC_SECP256R1); } -static int km_ecdsa_nist_p384_init(struct crypto_akcipher *tfm) +static int km_ecdsa_nist_p384_init(struct ecdsa_tfm_type *tfm) { return km_ecdsa_init(tfm, ECC_SECP384R1); } #if defined(HAVE_ECC521) -static int km_ecdsa_nist_p521_init(struct crypto_akcipher *tfm) +static int km_ecdsa_nist_p521_init(struct ecdsa_tfm_type *tfm) { return km_ecdsa_init(tfm, ECC_SECP521R1); } #endif /* HAVE_ECC521 */ +#ifdef LINUXKM_ECDSA_SIG_ALG + +/* Convert a little endian u64 digit array (the kernel's raw r/s format) to a + * big endian byte array of out_len bytes. Only the low 8*out_len bits of the + * digit array are read -- digits past DIV_ROUND_UP(out_len, 8) may be + * uninitialized in signatures decoded by the kernel's x962 template, and must + * not be read. + */ +static void km_ecdsa_raw_digits_to_bytes(const u64 *digits, byte *out, + word32 out_len) +{ + word32 i; + + for (i = 0; i < out_len; i++) + out[out_len - 1U - i] = (byte)(digits[i >> 3] >> ((i & 7U) << 3)); +} + +/* + * Verify an ecdsa_nist signature (linux 6.13+ struct sig_alg edition). + * + * src: + * - the signature, formatted as the kernel's struct ecdsa_raw_sig: r then + * s, each an array of ECC_MAX_DIGITS little endian u64 digits. + * - slen must == sizeof(struct ecdsa_raw_sig) (curve-independent). + * + * The kernel's x962 and p1363 templates decode DER and IEEE P1363 + * signature encodings (respectively) to this raw format, zero-filling + * the unused high bytes of the first DIV_ROUND_UP(curve_len, 8) digits + * only. + * + * digest: + * - the hash to be verified. wolfCrypt truncates oversized digests to + * the group order bit length internally (FIPS 186-4 style), matching + * the in-tree implementation. + * + * See kernel (6.13 or later): + * - include/crypto/sig.h + * - crypto/ecdsa.c + */ +static int km_ecdsa_verify(struct crypto_sig *tfm, + const void *src, unsigned int slen, + const void *digest, unsigned int dlen) +{ + struct km_ecdsa_ctx *ctx = crypto_sig_ctx(tfm); + const struct km_ecdsa_raw_sig + *raw_sig = (const struct km_ecdsa_raw_sig *)src; + byte *work_buffer = NULL; + byte *r_buf = NULL; + byte *s_buf = NULL; + byte *der_sig = NULL; + word32 der_sig_len = 0; + int result = -1; + int err = -1; + + if (src == NULL || digest == NULL) + return -EINVAL; + + if ((ctx->key == NULL) || (ctx->key->type != ECC_PUBLICKEY)) + return -EINVAL; + + if (slen != sizeof(struct km_ecdsa_raw_sig)) + return -EINVAL; + + /* 6 ECDSA (struct sig_testvec) test vectors in crypto/testmgr.h use SHA-1 + * (m_size 20). + */ +#ifdef WC_LINUXKM_HAVE_SELFTEST + wc_static_assert2(WC_MIN_DIGEST_SIZE_FOR_VERIFY <= 20, + "WC_MIN_DIGEST_SIZE_FOR_VERIFY must allow SHA-1-sized digests when " + "native kernel self-test is enabled."); +#endif + + if ((dlen > WC_MAX_DIGEST_SIZE) || + (dlen < WC_MIN_DIGEST_SIZE_FOR_VERIFY)) + { + return -EINVAL; + } + + /* Reject set bits between the curve length and the top of the last + * meaningful digit -- below, only the low 8*curve_len bits of each of r + * and s are converted, and such bits would otherwise be silently masked + * off. (the in-tree implementation rejects them via its r < n, s < n + * checks.) + */ + if (ctx->curve_len & 7U) { + word32 top_i = (ctx->curve_len - 1U) >> 3; + word32 top_shift = (ctx->curve_len & 7U) << 3; + if ((raw_sig->r[top_i] >> top_shift) || + (raw_sig->s[top_i] >> top_shift)) + { + return -EBADMSG; + } + } + + /* work_buffer holds the big endian r and s, followed by the + * DER-encoded signature to be passed to wc_ecc_verify_hash(). */ + work_buffer = (byte *)malloc((2U * ctx->curve_len) + ECC_MAX_SIG_SIZE); + if (unlikely(work_buffer == NULL)) + return -ENOMEM; + + r_buf = work_buffer; + s_buf = work_buffer + ctx->curve_len; + der_sig = work_buffer + (2U * ctx->curve_len); + + km_ecdsa_raw_digits_to_bytes(raw_sig->r, r_buf, ctx->curve_len); + km_ecdsa_raw_digits_to_bytes(raw_sig->s, s_buf, ctx->curve_len); + + der_sig_len = ECC_MAX_SIG_SIZE; + err = wc_ecc_rs_raw_to_sig(r_buf, ctx->curve_len, s_buf, ctx->curve_len, + der_sig, &der_sig_len); + if (err) { + #ifdef WOLFKM_DEBUG_ECDSA + pr_err("error: %s: ecdsa verify: wc_ecc_rs_raw_to_sig returned: %d\n", + WOLFKM_ECDSA_DRIVER, err); + #endif /* WOLFKM_DEBUG_ECDSA */ + err = -EBADMSG; + goto ecdsa_verify_end; + } + + err = wc_ecc_verify_hash(der_sig, der_sig_len, digest, dlen, &result, + ctx->key); + + if (err) { + #ifdef WOLFKM_DEBUG_ECDSA + pr_err("error: %s: ecdsa verify: verify_hash returned: %d\n", + WOLFKM_ECDSA_DRIVER, err); + #endif /* WOLFKM_DEBUG_ECDSA */ + err = -EBADMSG; + goto ecdsa_verify_end; + } + + if (result != 1) { + #ifdef WOLFKM_DEBUG_ECDSA + pr_err("info: %s: ecdsa verify: verify fail: %d\n", + WOLFKM_ECDSA_DRIVER, result); + #endif /* WOLFKM_DEBUG_ECDSA */ + err = -EKEYREJECTED; + goto ecdsa_verify_end; + } + +ecdsa_verify_end: + + free(work_buffer); + + #ifdef WOLFKM_DEBUG_ECDSA + pr_info("info: exiting km_ecdsa_verify dlen %d, slen %d, " + "err %d, result %d\n", dlen, slen, err, result); + #endif /* WOLFKM_DEBUG_ECDSA */ + return err; +} + +#else /* !LINUXKM_ECDSA_SIG_ALG */ + /* * Verify an ecdsa_nist signature. * @@ -405,8 +673,17 @@ static int km_ecdsa_verify(struct akcipher_request *req) sig_len = req->src_len; hash_len = req->dst_len; + /* 6 ECDSA (struct sig_testvec) test vectors in crypto/testmgr.h use SHA-1 + * (m_size 20). + */ +#ifdef WC_LINUXKM_HAVE_SELFTEST + wc_static_assert2(WC_MIN_DIGEST_SIZE_FOR_VERIFY <= 20, + "WC_MIN_DIGEST_SIZE_FOR_VERIFY must allow SHA-1-sized digests when " + "native kernel self-test is enabled."); +#endif + if ((hash_len > WC_MAX_DIGEST_SIZE) || - (hash_len < WC_MIN_DIGEST_SIZE)) + (hash_len < WC_MIN_DIGEST_SIZE_FOR_VERIFY)) { err = -EINVAL; goto ecdsa_verify_end; @@ -456,7 +733,8 @@ static int km_ecdsa_verify(struct akcipher_request *req) } ecdsa_verify_end: - if (sig != NULL) { free(sig); sig = NULL; } + + free(sig); #ifdef WOLFKM_DEBUG_ECDSA pr_info("info: exiting km_ecdsa_verify hash_len %d, sig_len %d, " @@ -465,6 +743,8 @@ ecdsa_verify_end: return err; } +#endif /* !LINUXKM_ECDSA_SIG_ALG */ + #if defined(LINUXKM_ECC192) static int linuxkm_test_ecdsa_nist_p192(void) { @@ -709,6 +989,161 @@ static int linuxkm_test_ecdsa_nist_p521(void) } #endif /* HAVE_ECC521 */ +#ifdef LINUXKM_ECDSA_SIG_ALG + +/* Convert a big endian byte array to the kernel's little endian u64 raw + * digit array format, zero-filling the KM_ECDSA_MAX_DIGITS digits. Inverse + * of km_ecdsa_raw_digits_to_bytes(). + */ +static void km_ecdsa_bytes_to_raw_digits(const byte *in, word32 in_len, + u64 *out) +{ + word32 i; + + XMEMSET(out, 0, KM_ECDSA_MAX_DIGITS * sizeof(u64)); + for (i = 0; i < in_len; i++) + out[i >> 3] |= ((u64)in[in_len - 1U - i]) << ((i & 7U) << 3); +} + +static int linuxkm_test_ecdsa_nist_driver(const char * driver, + const byte * pub, word32 pub_len, + const byte * sig, word32 sig_len, + const byte * hash, word32 hash_len) +{ + int test_rc = WC_NO_ERR_TRACE(WC_FAILURE); + int ret = 0; + struct crypto_sig * tfm = NULL; + struct km_ecdsa_raw_sig * raw_sig = NULL; + byte * r_buf = NULL; + byte * s_buf = NULL; + word32 r_len = 0; + word32 s_len = 0; + word32 curve_len = 0; + + /* infer the curve length from the raw uncompressed pub key: + * 0x04 || x || y. */ + if ((pub_len < 1) || (((pub_len - 1) & 1) != 0)) { + pr_err("error: %s: test pub key has invalid length %u\n", + driver, pub_len); + return BAD_FUNC_ARG; + } + curve_len = (pub_len - 1) >> 1; + + /* allocate the kernel-raw-format signature, followed by buffers for the + * big endian r and s decoded from the DER test signature. */ + raw_sig = (struct km_ecdsa_raw_sig *)malloc( + sizeof(struct km_ecdsa_raw_sig) + (2 * curve_len)); + if (! raw_sig) { + pr_err("error: allocating raw_sig buffer failed.\n"); + test_rc = MEMORY_E; + goto test_ecdsa_nist_end; + } + r_buf = (byte *)raw_sig + sizeof(struct km_ecdsa_raw_sig); + s_buf = r_buf + curve_len; + + /* + * Allocate the sig transform. + */ + tfm = crypto_alloc_sig(driver, 0, 0); + if (IS_ERR(tfm)) { + pr_err("error: allocating sig algorithm %s failed: %d\n", + driver, (int)PTR_ERR(tfm)); + if (PTR_ERR(tfm) == -ENOMEM) + test_rc = MEMORY_E; + else + test_rc = BAD_FUNC_ARG; + tfm = NULL; + goto test_ecdsa_nist_end; + } + + /* now set pub key for verify test. */ + ret = crypto_sig_set_pubkey(tfm, pub, pub_len); + if (ret) { + pr_err("error: crypto_sig_set_pubkey returned: %d\n", ret); + test_rc = BAD_FUNC_ARG; + goto test_ecdsa_nist_end; + } + + { + /* The behavior of crypto_sig_Xsize (X= max, key, digest) changed + * at linux kernel v6.15.3: + * < 6.15.3: keysize is in bytes. + * >= 6.15.3: keysize is in bits, maxsize and digestsize in + * bytes. */ + unsigned int maxsize = crypto_sig_maxsize(tfm); + unsigned int keysize = crypto_sig_keysize(tfm); + unsigned int digestsize = crypto_sig_digestsize(tfm); + + #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 15, 3) + keysize = ((keysize + WOLFSSL_BIT_SIZE - 1) / WOLFSSL_BIT_SIZE); + #endif /* linux >= 6.15.3 */ + + #ifdef WOLFKM_DEBUG_ECDSA + pr_info("info: crypto_sig_{max, key, digest}size: " + "{%d, %d, %d}\n", + maxsize, keysize, digestsize); + #endif /* WOLFKM_DEBUG_ECDSA */ + + if ((keysize != curve_len) || + (maxsize != curve_len) || + (digestsize != (unsigned int)WC_MAX_DIGEST_SIZE)) + { + pr_err("error: crypto_sig_{max, key, digest}size " + "returned {%u, %u, %u}, expected {%u, %u, %u}\n", + maxsize, keysize, digestsize, + curve_len, curve_len, (unsigned int)WC_MAX_DIGEST_SIZE); + test_rc = BAD_FUNC_ARG; + goto test_ecdsa_nist_end; + } + } + + /* convert the DER test signature to the kernel's raw format. */ + r_len = curve_len; + s_len = curve_len; + ret = wc_ecc_sig_to_rs(sig, sig_len, r_buf, &r_len, s_buf, &s_len); + if (ret) { + pr_err("error: wc_ecc_sig_to_rs returned: %d\n", ret); + test_rc = ret; + goto test_ecdsa_nist_end; + } + + km_ecdsa_bytes_to_raw_digits(r_buf, r_len, raw_sig->r); + km_ecdsa_bytes_to_raw_digits(s_buf, s_len, raw_sig->s); + + ret = crypto_sig_verify(tfm, raw_sig, (unsigned int)sizeof(*raw_sig), + hash, hash_len); + if (ret) { + pr_err("error: crypto_sig_verify returned: %d\n", ret); + test_rc = BAD_FUNC_ARG; + goto test_ecdsa_nist_end; + } + + /* corrupt the signature -- verify should now fail. */ + raw_sig->r[0] ^= 1U; + + ret = crypto_sig_verify(tfm, raw_sig, (unsigned int)sizeof(*raw_sig), + hash, hash_len); + if ((ret != -EBADMSG) && (ret != -EKEYREJECTED)) { + pr_err("error: crypto_sig_verify returned %d, expected %d or %d\n", + ret, -EBADMSG, -EKEYREJECTED); + test_rc = BAD_FUNC_ARG; + goto test_ecdsa_nist_end; + } + + test_rc = 0; +test_ecdsa_nist_end: + if (tfm) + crypto_free_sig(tfm); + free(raw_sig); + + #ifdef WOLFKM_DEBUG_ECDSA + pr_info("info: %s: self test returned: %d\n", driver, test_rc); + #endif /* WOLFKM_DEBUG_ECDSA */ + return test_rc; +} + +#else /* !LINUXKM_ECDSA_SIG_ALG */ + static int linuxkm_test_ecdsa_nist_driver(const char * driver, const byte * pub, word32 pub_len, const byte * sig, word32 sig_len, @@ -854,6 +1289,8 @@ test_ecdsa_nist_end: return test_rc; } +#endif /* !LINUXKM_ECDSA_SIG_ALG */ + #endif /* LINUXKM_LKCAPI_REGISTER_ECDSA */ #endif /* !WC_SKIP_INCLUDED_C_FILES */