diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 49cfb622b69..fa8beef47c1 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -1042,6 +1042,7 @@ WOLFSSL_KYBER_NO_DECAPSULATE WOLFSSL_KYBER_NO_ENCAPSULATE WOLFSSL_KYBER_NO_MAKE_KEY WOLFSSL_LIB +WOLFSSL_LINUXKM_LKCAPI_NO_STDRNG WOLFSSL_LINUXKM_USE_GET_RANDOM_KPROBES WOLFSSL_LINUXKM_USE_GET_RANDOM_USER_KRETPROBE WOLFSSL_LINUXKM_USE_MUTEXES diff --git a/doc/dox_comments/header_files/asn_public.h b/doc/dox_comments/header_files/asn_public.h index e6af89d4dd9..76891997e28 100644 --- a/doc/dox_comments/header_files/asn_public.h +++ b/doc/dox_comments/header_files/asn_public.h @@ -3251,10 +3251,13 @@ int wc_Curve448PublicKeyToDer(curve448_key* key, byte* output, \return Success On successfully writing the encoded signature to output, returns the length written to the buffer + \return 0 Returned on error, including when digSz is larger than + WC_MAX_DIGEST_SIZE \param out pointer to the buffer where the encoded signature will be written \param digest pointer to the digest to use to encode the signature - \param digSz the length of the buffer containing the digest + \param digSz the length of the buffer containing the digest; at most + WC_MAX_DIGEST_SIZE \param hashOID OID identifying the hash type used to generate the signature. Valid options, depending on build configurations, are: SHAh, SHA256h, SHA384h, SHA512h, MD2h, MD5h, DESb, DES3b, CTC_MD5wRSA, diff --git a/linuxkm/linuxkm_wc_port.h b/linuxkm/linuxkm_wc_port.h index cd69dd18d2b..1ed8862f4dd 100644 --- a/linuxkm/linuxkm_wc_port.h +++ b/linuxkm/linuxkm_wc_port.h @@ -803,7 +803,12 @@ #include #include #include - #include + #if LINUX_VERSION_CODE < KERNEL_VERSION(7, 5, 0) && !defined(WOLFSSL_LINUXKM_LKCAPI_NO_STDRNG) + #define WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG + #include + #else + #undef WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG + #endif #include #include #include @@ -2328,9 +2333,11 @@ #endif /* !WC_CONTAINERIZE_THIS */ #endif - #ifdef LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT + #ifdef WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG struct crypto_rng; WOLFSSL_API int wc_linux_kernel_rng_is_wolfcrypt(struct crypto_rng *rng); + #else + #define wc_linux_kernel_rng_is_wolfcrypt(rng) 0 #endif /* Undo copied defines from wc_port.h, to avoid redefinition warnings. */ diff --git a/linuxkm/lkcapi_rsa_glue.c b/linuxkm/lkcapi_rsa_glue.c index 23abc0faa77..e359554b817 100644 --- a/linuxkm/lkcapi_rsa_glue.c +++ b/linuxkm/lkcapi_rsa_glue.c @@ -270,6 +270,7 @@ static unsigned int km_rsa_max_size(struct crypto_akcipher *tfm); const void *key, unsigned int keylen); static unsigned int km_pkcs1_key_size(struct crypto_sig *tfm); + static unsigned int km_pkcs1_digest_size(struct crypto_sig *tfm); static int km_pkcs1_sign(struct crypto_sig *tfm, const void *src, unsigned int slen, void *dst, unsigned int dlen); @@ -280,6 +281,8 @@ static unsigned int km_rsa_max_size(struct crypto_akcipher *tfm); #endif /* !LINUXKM_AKCIPHER_NO_SIGNVERIFY */ /* misc */ static int get_hash_enc_len(int hash_oid); + static int km_pkcs1_check_digest_len(const struct km_rsa_ctx * ctx, + unsigned int digest_len); #if defined(LINUXKM_DIRECT_RSA) static struct akcipher_alg direct_rsa = { @@ -456,6 +459,7 @@ static struct akcipher_alg direct_rsa = { .set_priv_key = km_pkcs1_set_priv, .set_pub_key = km_pkcs1_set_pub, .key_size = km_pkcs1_key_size, + .digest_size = km_pkcs1_digest_size, .init = km_pkcs1_sha224_init, .exit = km_pkcs1_exit, }; @@ -473,6 +477,7 @@ static struct akcipher_alg direct_rsa = { .set_priv_key = km_pkcs1_set_priv, .set_pub_key = km_pkcs1_set_pub, .key_size = km_pkcs1_key_size, + .digest_size = km_pkcs1_digest_size, .init = km_pkcs1_sha256_init, .exit = km_pkcs1_exit, }; @@ -490,6 +495,7 @@ static struct akcipher_alg direct_rsa = { .set_priv_key = km_pkcs1_set_priv, .set_pub_key = km_pkcs1_set_pub, .key_size = km_pkcs1_key_size, + .digest_size = km_pkcs1_digest_size, .init = km_pkcs1_sha384_init, .exit = km_pkcs1_exit, }; @@ -507,6 +513,7 @@ static struct akcipher_alg direct_rsa = { .set_priv_key = km_pkcs1_set_priv, .set_pub_key = km_pkcs1_set_pub, .key_size = km_pkcs1_key_size, + .digest_size = km_pkcs1_digest_size, .init = km_pkcs1_sha512_init, .exit = km_pkcs1_exit, }; @@ -524,6 +531,7 @@ static struct akcipher_alg direct_rsa = { .set_priv_key = km_pkcs1_set_priv, .set_pub_key = km_pkcs1_set_pub, .key_size = km_pkcs1_key_size, + .digest_size = km_pkcs1_digest_size, .init = km_pkcs1_sha3_256_init, .exit = km_pkcs1_exit, }; @@ -539,6 +547,7 @@ static struct akcipher_alg direct_rsa = { .set_priv_key = km_pkcs1_set_priv, .set_pub_key = km_pkcs1_set_pub, .key_size = km_pkcs1_key_size, + .digest_size = km_pkcs1_digest_size, .init = km_pkcs1_sha3_384_init, .exit = km_pkcs1_exit, }; @@ -554,6 +563,7 @@ static struct akcipher_alg direct_rsa = { .set_priv_key = km_pkcs1_set_priv, .set_pub_key = km_pkcs1_set_pub, .key_size = km_pkcs1_key_size, + .digest_size = km_pkcs1_digest_size, .init = km_pkcs1_sha3_512_init, .exit = km_pkcs1_exit, }; @@ -1138,13 +1148,64 @@ static int km_pkcs1_sha3_512_init(struct tfm_type *tfm) } #endif /* WOLFSSL_SHA3 */ +/* + * Argument validation common to the pkcs1 sign and verify callbacks, in + * both the akcipher and sig_alg editions: the tfm must be keyed, + * digest_len must be exactly the digest length of the tfm's hash, and + * the encoded, minimally padded digest must fit in the key. + * + * The callbacks encode the digest in place with wc_EncodeSignature(), and + * nothing above them holds the caller to the hash's digest length (the + * kernel bounds e.g. a KEYCTL_PKEY_SIGN request only by the size the + * driver advertises), so the exact match is required on every path. + * + * Returns 0 on success, -EINVAL or -EOVERFLOW on error. + */ +static int km_pkcs1_check_digest_len(const struct km_rsa_ctx * ctx, + unsigned int digest_len) +{ + int hash_enc_len = 0; + + if (ctx->key_len <= 0 || ctx->digest_len <= 0) { + /* invalid key state */ + return -EINVAL; + } + + hash_enc_len = get_hash_enc_len(ctx->hash_oid); + if (hash_enc_len <= 0) { + #ifdef WOLFKM_DEBUG_RSA + pr_err("error: %s: bad hash enc len %d\n", + WOLFKM_RSA_DRIVER, hash_enc_len); + #endif /* WOLFKM_DEBUG_RSA */ + return -EINVAL; + } + + if (digest_len != ctx->digest_len) { + #ifdef WOLFKM_DEBUG_RSA + pr_err("error: %s: got digest_len %u, expected %u\n", + WOLFKM_RSA_DRIVER, digest_len, ctx->digest_len); + #endif /* WOLFKM_DEBUG_RSA */ + return -EINVAL; + } + + /* bail if the padded ASN.1-prefixed digest won't fit in the given RSA + * key. */ + if ((word64)ctx->digest_len + (word64)hash_enc_len + RSA_MIN_PAD_SZ > + ctx->key_len) + { + return -EOVERFLOW; + } + + return 0; +} + #if !defined(LINUXKM_AKCIPHER_NO_SIGNVERIFY) /* * Generates a pkcs1 encoded signature. * * src: * - req->src scatterlist is the digest to be encoded, padded, and signed. - * - req->src_len + encoding + min padding must be <= key_size. + * - req->src_len must be the correct digest len. * * dst: * - req->dst scatterlist is destination signature. @@ -1161,7 +1222,6 @@ static int km_pkcs1pad_sign(struct akcipher_request *req) int err = 0; int sig_len = 0; int enc_len = 0; - int hash_enc_len = 0; byte * msg = NULL; byte * sig = NULL; byte * work_buffer = NULL; @@ -1174,22 +1234,9 @@ static int km_pkcs1pad_sign(struct akcipher_request *req) tfm = crypto_akcipher_reqtfm(req); ctx = akcipher_tfm_ctx(tfm); - if (ctx->key_len <= 0 || ctx->digest_len <= 0) { - /* invalid key state */ - err = -EINVAL; - goto pkcs1pad_sign_out; - } - - hash_enc_len = get_hash_enc_len(ctx->hash_oid); - if (hash_enc_len <= 0) { - err = -EINVAL; - goto pkcs1pad_sign_out; - } - - if ((word64)req->src_len + (word64)hash_enc_len + RSA_MIN_PAD_SZ > ctx->key_len) { - err = -EOVERFLOW; + err = km_pkcs1_check_digest_len(ctx, req->src_len); + if (err) goto pkcs1pad_sign_out; - } if (req->dst_len < ctx->key_len) { err = -EOVERFLOW; @@ -1278,7 +1325,6 @@ static int km_pkcs1pad_verify(struct akcipher_request *req) int dec_len = 0; word32 msg_len = 0; int enc_msg_len = 0; - int hash_enc_len = 0; int n_diff = 0; byte * sig = NULL; byte * msg = NULL; @@ -1295,38 +1341,16 @@ static int km_pkcs1pad_verify(struct akcipher_request *req) msg_len = req->dst_len; sig_len = req->src_len; - if (ctx->key_len <= 0 || ctx->digest_len <= 0) { - /* invalid key state */ - err = -EINVAL; + err = km_pkcs1_check_digest_len(ctx, msg_len); + if (err) goto pkcs1pad_verify_out; - } - hash_enc_len = get_hash_enc_len(ctx->hash_oid); - if (hash_enc_len <= 0) { - #ifdef WOLFKM_DEBUG_RSA - pr_err("error: %s: bad hash enc len %d\n", - WOLFKM_RSA_DRIVER, hash_enc_len); - #endif /* WOLFKM_DEBUG_RSA */ + if (sig_len != ctx->key_len) { + /* invalid src args */ err = -EINVAL; goto pkcs1pad_verify_out; } - if (msg_len != ctx->digest_len || sig_len != ctx->key_len) { - /* invalid src or dst args */ - #ifdef WOLFKM_DEBUG_RSA - pr_err("error: %s: got msg_len %d, expected %d\n", - WOLFKM_RSA_DRIVER, msg_len, ctx->digest_len); - #endif /* WOLFKM_DEBUG_RSA */ - err = -EINVAL; - goto pkcs1pad_verify_out; - } - - /* bail if the padded ASN.1-prefixed digest won't fit in the given RSA key. */ - if ((word64)ctx->digest_len + (word64)hash_enc_len + RSA_MIN_PAD_SZ > ctx->key_len) { - err = -EOVERFLOW; - goto pkcs1pad_verify_out; - } - work_buffer = malloc(2 * ctx->key_len); if (unlikely(work_buffer == NULL)) { err = -ENOMEM; @@ -1409,12 +1433,24 @@ static unsigned int km_pkcs1_key_size(struct crypto_sig *tfm) #endif /* linux >= 6.15.3 */ } +/* Returns the digest size in bytes. The kernel uses it to bound the data + * it will pass to km_pkcs1_sign() (see software_key_query()); without it, + * the sig core substitutes the key size. */ +static unsigned int km_pkcs1_digest_size(struct crypto_sig *tfm) +{ + struct km_rsa_ctx * ctx = NULL; + + ctx = crypto_sig_ctx(tfm); + + return ctx->digest_len; +} + /* * Generates a pkcs1 encoded signature. * * src: * - src contains the digest to be encoded, padded, and signed. - * - slen + encoding + min padding must be <= key_size. + * - slen must be the correct digest len. * * dst: * - dst is destination signature buffer. @@ -1435,7 +1471,6 @@ static int km_pkcs1_sign(struct crypto_sig *tfm, int err = 0; int sig_len = 0; int enc_msg_len = 0; - int hash_enc_len = 0; byte * msg = NULL; byte * sig = dst; /* reuse dst buffer. we will check * if it is large enough. */ @@ -1447,22 +1482,9 @@ static int km_pkcs1_sign(struct crypto_sig *tfm, ctx = crypto_sig_ctx(tfm); - if (ctx->key_len <= 0 || ctx->digest_len <= 0) { - /* invalid key state */ - err = -EINVAL; - goto pkcs1_sign_out; - } - - hash_enc_len = get_hash_enc_len(ctx->hash_oid); - if (hash_enc_len <= 0) { - err = -EINVAL; - goto pkcs1_sign_out; - } - - if ((word64)slen + (word64)hash_enc_len + RSA_MIN_PAD_SZ > ctx->key_len) { - err = -EOVERFLOW; + err = km_pkcs1_check_digest_len(ctx, slen); + if (err) goto pkcs1_sign_out; - } if (dlen < ctx->key_len) { err = -EOVERFLOW; @@ -1556,7 +1578,6 @@ static int km_pkcs1_verify(struct crypto_sig *tfm, int dec_len = 0; word32 msg_len = 0; int enc_msg_len = 0; - int hash_enc_len = 0; int n_diff = 0; byte * enc_digest = NULL; byte * msg = NULL; @@ -1572,38 +1593,16 @@ static int km_pkcs1_verify(struct crypto_sig *tfm, msg_len = dlen; sig_len = slen; - if (ctx->key_len <= 0 || ctx->digest_len <= 0) { - /* invalid key state */ - err = -EINVAL; - goto pkcs1_verify_out; - } - - hash_enc_len = get_hash_enc_len(ctx->hash_oid); - if (hash_enc_len <= 0) { - #ifdef WOLFKM_DEBUG_RSA - pr_err("error: %s: bad hash enc len %d\n", - WOLFKM_RSA_DRIVER, hash_enc_len); - #endif /* WOLFKM_DEBUG_RSA */ - err = -EINVAL; + err = km_pkcs1_check_digest_len(ctx, msg_len); + if (err) goto pkcs1_verify_out; - } - if (msg_len != ctx->digest_len || sig_len != ctx->key_len) { - /* invalid src or dst args */ - #ifdef WOLFKM_DEBUG_RSA - pr_err("error: %s: got msg_len %d, expected %d\n", - WOLFKM_RSA_DRIVER, msg_len, ctx->digest_len); - #endif /* WOLFKM_DEBUG_RSA */ + if (sig_len != ctx->key_len) { + /* invalid src args */ err = -EINVAL; goto pkcs1_verify_out; } - /* bail if the padded ASN.1-prefixed digest won't fit in the given RSA key. */ - if ((word64)ctx->digest_len + (word64)hash_enc_len + RSA_MIN_PAD_SZ > ctx->key_len) { - err = -EOVERFLOW; - goto pkcs1_verify_out; - } - work_buffer = malloc(2 * ctx->key_len); if (unlikely(work_buffer == NULL)) { err = -ENOMEM; @@ -2859,6 +2858,18 @@ static int linuxkm_test_pkcs1pad_driver(const char * driver, int nbits, goto test_pkcs1_end; } + /* a digest length not matching the hash must be rejected, with nothing + * written to dst. sig serves as a long enough source. */ + sg_init_one(&src, sig, hash_len + 1); + akcipher_request_set_crypt(req, &src, &dst, hash_len + 1, key_len); + ret = crypto_akcipher_sign(req); + if (ret != -WC_NO_ERR_TRACE(EINVAL)) { + pr_err("error: crypto_akcipher_sign (oversize digest) returned %d, " + "expected %d\n", ret, -WC_NO_ERR_TRACE(EINVAL)); + test_rc = BAD_FUNC_ARG; + goto test_pkcs1_end; + } + /* now set pub key for verify test. */ ret = crypto_akcipher_set_pub_key(tfm, pub + 24, pub_len - 24); if (ret) { @@ -3367,8 +3378,10 @@ static int linuxkm_test_pkcs1_driver(const char * driver, int nbits, { /* The behavior of crypto_sig_Xsize (X= max, key, digest) changed * at linux kernel v6.15.3: - * < 6.15.3: all three should return the same value (in bytes). - * >= 6.15.3: keysize is in bits, maxsize and digestsize in bytes. */ + * < 6.15.3: maxsize and keysize should return the same value (in + * bytes). + * >= 6.15.3: keysize is in bits, maxsize in bytes. + * digestsize is the hash's digest size in bytes throughout. */ unsigned int maxsize = crypto_sig_maxsize(tfm); unsigned int keysize = crypto_sig_keysize(tfm); unsigned int digestsize = crypto_sig_digestsize(tfm); @@ -3385,15 +3398,26 @@ static int linuxkm_test_pkcs1_driver(const char * driver, int nbits, if ((int)maxsize != key_len || (int)keysize != key_len || - (int)digestsize != key_len) { + digestsize != hash_len) { pr_err("error: crypto_sig_{max, key, digest}size " - "returned {%d, %d, %d}, expected %d\n", - maxsize, keysize, digestsize, key_len); + "returned {%d, %d, %d}, expected {%d, %d, %d}\n", + maxsize, keysize, digestsize, key_len, key_len, + (int)hash_len); test_rc = BAD_FUNC_ARG; goto test_pkcs1_end; } } + /* a digest length not matching the hash must be rejected. sig serves as a + * long enough source. */ + ret = crypto_sig_sign(tfm, sig, hash_len + 1, km_sig, key_len); + if (ret != -WC_NO_ERR_TRACE(EINVAL)) { + pr_err("error: crypto_sig_sign (oversize digest) returned %d, " + "expected %d\n", ret, -WC_NO_ERR_TRACE(EINVAL)); + test_rc = BAD_FUNC_ARG; + goto test_pkcs1_end; + } + ret = crypto_sig_sign(tfm, hash, hash_len, km_sig, key_len); if (ret < 0) { pr_err("error: crypto_sig_sign returned: %d\n", ret); diff --git a/linuxkm/lkcapi_sha_glue.c b/linuxkm/lkcapi_sha_glue.c index 71056189df8..807b0677995 100644 --- a/linuxkm/lkcapi_sha_glue.c +++ b/linuxkm/lkcapi_sha_glue.c @@ -4218,6 +4218,7 @@ static int wc_linuxkm_rng_bank_fini(struct wc_rng_bank *ctx) { return ret; } +#ifdef WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG static int wc_linuxkm_drbg_init_tfm(struct crypto_tfm *tfm) { return wc_linuxkm_rng_bank_init((struct wc_rng_bank *)crypto_tfm_ctx(tfm)); @@ -4231,6 +4232,7 @@ static void wc_linuxkm_drbg_exit_tfm(struct crypto_tfm *tfm) } static int wc_linuxkm_drbg_default_instance_registered = 0; +#endif /* WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG */ static struct wc_rng_bank_inst *linuxkm_get_drbg(struct wc_rng_bank *ctx) { int err; @@ -4311,18 +4313,6 @@ static int linuxkm_put_drbg(struct wc_rng_bank_inst **drbg) { #if defined(LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT) && defined(HAVE_HASHDRBG) -int wc_linux_kernel_rng_is_wolfcrypt(struct crypto_rng *rng) { - if (rng && - wc_linuxkm_drbg_default_instance_registered && - (rng->base.__crt_alg->cra_init == wc_linuxkm_drbg_init_tfm)) - { - return 1; - } - else { - return 0; - } -} - #ifdef WC_RNG_HAVE_RBGC WC_MAYBE_UNUSED static int linuxkm_InitRng_DefaultRBGC(WC_RNG* rng) { @@ -4805,6 +4795,7 @@ static int wc_linuxkm_drbg_generate(struct wc_rng_bank *ctx, return ret; } +#ifdef WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG static int wc_linuxkm_drbg_generate_tfm(struct crypto_rng *tfm, const u8 *src, unsigned int slen, u8 *dst, unsigned int dlen) @@ -4896,6 +4887,12 @@ static struct rng_alg wc_linuxkm_drbg = { .cra_module = THIS_MODULE } }; +#endif /* WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG */ + +/* if WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG, wc_linuxkm_drbg_loaded follows the + * results of crypto_register_rng(), otherwise it follows the results of + * wc_linuxkm_rng_bank_init(). + */ static int wc_linuxkm_drbg_loaded = 0; #ifdef LINUXKM_DRBG_GET_RANDOM_BYTES @@ -5391,6 +5388,10 @@ static int wc_get_random_bytes_callbacks_installed = 0; #elif defined(WOLFSSL_LINUXKM_USE_GET_RANDOM_KPROBES) +#ifndef WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG + #error WOLFSSL_LINUXKM_USE_GET_RANDOM_KPROBES requires WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG. +#endif + #ifndef WOLFSSL_EXPERIMENTAL_SETTINGS #error WOLFSSL_LINUXKM_USE_GET_RANDOM_KPROBES requires WOLFSSL_EXPERIMENTAL_SETTINGS. #endif @@ -5609,12 +5610,18 @@ static int wc_linuxkm_drbg_startup(void) ret = random_test(); if (ret) { - pr_err("ERROR: self-test for %s failed " - "with return code %d.\n", - wc_linuxkm_drbg.base.cra_driver_name, ret); + pr_err("ERROR: self-test for RNG failed " + "with return code %d.\n", ret); return -EINVAL; } +/* return early if configuration has no persistent or registered RNG. */ +#if !defined(LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT) && !defined(WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG) + wc_linuxkm_drbg_loaded = 1; + return 0; +#endif + +#ifdef WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG ret = crypto_register_rng(&wc_linuxkm_drbg); if (ret != 0) { pr_err("ERROR: crypto_register_rng: %d\n", ret); @@ -5704,6 +5711,7 @@ static int wc_linuxkm_drbg_startup(void) wc_linuxkm_drbg_loaded = 1; WOLFKM_INSTALL_NOTICE(wc_linuxkm_drbg); +#endif /* WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG */ #ifdef LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT /* for the default RNG, make sure we don't cache an underlying SHA256 @@ -5711,7 +5719,7 @@ static int wc_linuxkm_drbg_startup(void) */ wc_linuxkm_rng_initing_default_bank_flag = 1; -#if LINUX_VERSION_CODE < KERNEL_VERSION(7, 1, 0) +#if LINUX_VERSION_CODE < KERNEL_VERSION(7, 1, 0) && defined(WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG) ret = crypto_del_default_rng(); if (ret) { @@ -5753,9 +5761,9 @@ static int wc_linuxkm_drbg_startup(void) crypto_put_default_rng(); -#else /* >= 7.1.0 */ +#else /* >= 7.1.0 || !WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG */ -#ifdef CONFIG_CRYPTO_FIPS +#if defined(CONFIG_CRYPTO_FIPS) && defined(WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG) if (fips_enabled) { char buf[16]; @@ -5776,7 +5784,7 @@ static int wc_linuxkm_drbg_startup(void) } } else -#endif /* CONFIG_CRYPTO_FIPS */ +#endif /* CONFIG_CRYPTO_FIPS && WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG */ { static struct wc_rng_bank local_default_bank; ret = wc_linuxkm_rng_bank_init(&local_default_bank); @@ -5785,6 +5793,9 @@ static int wc_linuxkm_drbg_startup(void) pr_err("ERROR: wc_linuxkm_rng_bank_init returned %d\n", ret); return ret; } +#ifndef WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG + wc_linuxkm_drbg_loaded = 1; +#endif } #endif /* >= 7.1.0 */ @@ -5810,9 +5821,11 @@ static int wc_linuxkm_drbg_startup(void) } } +#ifdef WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG wc_linuxkm_drbg_default_instance_registered = 1; pr_info("%s registered as systemwide default stdrng.\n", wc_linuxkm_drbg.base.cra_driver_name); pr_info("libwolfssl: to unload module, first echo 1 > /sys/module/libwolfssl/deinstall_algs\n"); +#endif #ifdef WC_LINUXKM_HAVE_RNG_REGISTRY /* stock-notifier invalidation coverage rides with the registered @@ -5895,19 +5908,24 @@ static int wc_linuxkm_drbg_startup(void) #endif /* LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT */ +#if defined(LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT) || defined(WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG) return 0; +#else + /* not reachable */ +#endif } static int wc_linuxkm_drbg_cleanup(void) { - int cur_refcnt; - if (! wc_linuxkm_drbg_loaded) { pr_err("ERROR: wc_linuxkm_drbg_cleanup called with ! wc_linuxkm_drbg_loaded\n"); return -EINVAL; } #ifdef LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT - if (wc_linuxkm_drbg_default_instance_registered) { + #ifdef WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG + if (wc_linuxkm_drbg_default_instance_registered) + #endif + { /* These deinstallations are racey, but the kernel doesn't provide any other * way. It's written to be retryable. */ @@ -5962,15 +5980,15 @@ static int wc_linuxkm_drbg_cleanup(void) { #endif /* LINUXKM_DRBG_GET_RANDOM_BYTES */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(7, 1, 0) +#if LINUX_VERSION_CODE < KERNEL_VERSION(7, 1, 0) && defined(WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG) ret = crypto_del_default_rng(); if (ret) { pr_err("ERROR: crypto_del_default_rng failed: %d\n", ret); return ret; } -#else /* >= 7.1.0 */ +#else /* >= 7.1.0 || !WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG */ -#ifdef CONFIG_CRYPTO_FIPS +#if defined(CONFIG_CRYPTO_FIPS) && defined(WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG) if (fips_enabled) { ret = crypto_del_default_rng(); if (ret) { @@ -5984,26 +6002,34 @@ static int wc_linuxkm_drbg_cleanup(void) { ret = wc_linuxkm_rng_bank_fini(default_bank); if (ret) pr_err("ERROR: wc_linuxkm_rng_bank_fini in wc_linuxkm_drbg_cleanup failed: %d\n", ret); + else + default_bank = NULL; } #endif /* >= 7.1.0 */ +#ifdef WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG wc_linuxkm_drbg_default_instance_registered = 0; +#endif } #endif /* LINUXKM_LKCAPI_REGISTER_HASH_DRBG_DEFAULT */ - cur_refcnt = WC_LKM_REFCOUNT_TO_INT(wc_linuxkm_drbg.base.cra_refcnt); +#ifdef WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG + { + int cur_refcnt = WC_LKM_REFCOUNT_TO_INT(wc_linuxkm_drbg.base.cra_refcnt); - if (cur_refcnt != 1) { - pr_err("ERROR: wc_linuxkm_drbg_cleanup called with refcnt = %d\n", cur_refcnt); - return -EBUSY; - } + if (cur_refcnt != 1) { + pr_err("ERROR: wc_linuxkm_drbg_cleanup called with refcnt = %d\n", cur_refcnt); + return -EBUSY; + } - crypto_unregister_rng(&wc_linuxkm_drbg); + crypto_unregister_rng(&wc_linuxkm_drbg); - if (! (wc_linuxkm_drbg.base.cra_flags & CRYPTO_ALG_DEAD)) { - pr_warn("WARNING: wc_linuxkm_drbg_cleanup: after crypto_unregister_rng, wc_linuxkm_drbg isn't dead.\n"); - return -EBUSY; + if (! (wc_linuxkm_drbg.base.cra_flags & CRYPTO_ALG_DEAD)) { + pr_warn("WARNING: wc_linuxkm_drbg_cleanup: after crypto_unregister_rng, wc_linuxkm_drbg isn't dead.\n"); + return -EBUSY; + } } +#endif /* WOLFSSL_LINUXKM_LKCAPI_HAVE_STDRNG */ wc_linuxkm_drbg_loaded = 0; @@ -6012,6 +6038,25 @@ static int wc_linuxkm_drbg_cleanup(void) { #endif /* LINUXKM_LKCAPI_REGISTER_HASH_DRBG */ +#ifndef wc_linux_kernel_rng_is_wolfcrypt +int wc_linux_kernel_rng_is_wolfcrypt(struct crypto_rng *rng) { + #ifdef LINUXKM_LKCAPI_REGISTER_HASH_DRBG + if (rng && + wc_linuxkm_drbg_default_instance_registered && + (rng->base.__crt_alg->cra_init == wc_linuxkm_drbg_init_tfm)) + { + return 1; + } + else { + return 0; + } + #else + (void)rng; + return 0; + #endif +} +#endif /* !wc_linux_kernel_rng_is_wolfcrypt */ + #ifndef LKCAPI_INITRNG #define LKCAPI_INITRNG(rng) wc_InitRng(rng) #endif diff --git a/linuxkm/simd_hammer/simd_hammer.c b/linuxkm/simd_hammer/simd_hammer.c index 5c040564974..ff4c66200b3 100644 --- a/linuxkm/simd_hammer/simd_hammer.c +++ b/linuxkm/simd_hammer/simd_hammer.c @@ -94,6 +94,10 @@ #define wc_simd_timer_container_of(var, t, field) from_timer(var, t, field) #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(7, 5, 0) + #define HAVE_STDRNG +#endif + /* sidestep "flush-left function calls" warnings from check-source-text: */ #define MODULE_PARAM module_param @@ -130,10 +134,16 @@ MODULE_PARM_DESC(probe_alg, "sync shash driver name for the probe"); static char *probe_kind = "shash"; MODULE_PARAM(probe_kind, charp, 0444); +#ifdef HAVE_STDRNG MODULE_PARM_DESC(probe_kind, "\"shash\" (probe_alg digest), \"rng\" (crypto_rng_get_bytes " "on probe_alg, e.g. probe_alg=stdrng), or \"grb\" " "(get_random_bytes, the kernel randomness path)"); +#else +MODULE_PARM_DESC(probe_kind, + "\"shash\" (probe_alg digest) or \"grb\" " + "(get_random_bytes, the kernel randomness path)"); +#endif static unsigned int probe_bytes = 4096; MODULE_PARAM(probe_bytes, uint, 0444); @@ -179,14 +189,18 @@ struct wc_simd_pcpu { static struct wc_simd_pcpu *wc_simd_pc; static struct crypto_shash *wc_simd_probe_tfm; +#ifdef HAVE_STDRNG static struct crypto_rng *wc_simd_probe_rng; +#endif static struct task_struct *wc_simd_report_task; static bool wc_simd_raw_mode; static bool wc_simd_hardirq_probe; enum wc_simd_probe_kind { WC_SIMD_PROBE_SHASH = 0, +#ifdef HAVE_STDRNG WC_SIMD_PROBE_RNG, +#endif WC_SIMD_PROBE_GRB }; static enum wc_simd_probe_kind wc_simd_probe_kind; @@ -212,11 +226,13 @@ static void wc_simd_probe_once(struct wc_simd_pcpu *c, u64 late_us) u64 t0 = ktime_get_ns(), dur; switch (wc_simd_probe_kind) { +#ifdef HAVE_STDRNG case WC_SIMD_PROBE_RNG: /* Exercises the registered stdrng, i.e. wolfCrypt's DRBG when * the module is loaded at higher cra_priority. */ ret = crypto_rng_get_bytes(wc_simd_probe_rng, c->pbuf, probe_bytes); break; +#endif case WC_SIMD_PROBE_GRB: /* Exercises the kernel randomness path itself, including any * get_random_bytes hook the module under test installs. */ @@ -536,10 +552,12 @@ static void wc_simd_teardown(void) wc_simd_probe_tfm = NULL; } +#ifdef HAVE_STDRNG if (wc_simd_probe_rng) { crypto_free_rng(wc_simd_probe_rng); wc_simd_probe_rng = NULL; } +#endif } static int __init wc_simd_init(void) @@ -578,13 +596,20 @@ static int __init wc_simd_init(void) if (!strcmp(probe_kind, "shash")) wc_simd_probe_kind = WC_SIMD_PROBE_SHASH; +#ifdef HAVE_STDRNG else if (!strcmp(probe_kind, "rng")) wc_simd_probe_kind = WC_SIMD_PROBE_RNG; +#endif else if (!strcmp(probe_kind, "grb")) wc_simd_probe_kind = WC_SIMD_PROBE_GRB; else { +#ifdef HAVE_STDRNG pr_err("ERROR: %s: probe_kind must be \"shash\", \"rng\" or \"grb\"\n", __func__); +#else + pr_err("ERROR: %s: probe_kind must be \"shash\" or \"grb\"\n", + __func__); +#endif return -EINVAL; } @@ -602,7 +627,9 @@ static int __init wc_simd_init(void) ret = -EINVAL; goto err; } - } else if (wc_simd_probe_kind == WC_SIMD_PROBE_RNG) { + } +#ifdef HAVE_STDRNG + else if (wc_simd_probe_kind == WC_SIMD_PROBE_RNG) { /* ONE shared tfm for every CPU, deliberately. A tfm per CPU would * hand each probe its own private DRBG and measure nothing about * the shared path under test. */ @@ -615,7 +642,18 @@ static int __init wc_simd_init(void) __func__, probe_alg, ret); return ret; } + { + const char *drv = crypto_tfm_alg_driver_name( + crypto_rng_tfm(wc_simd_probe_rng)); + pr_info("%s: probe rng \"%s\" bound to driver \"%s\".\n", + __func__, probe_alg, drv); + if (! strstr(drv, "wolfcrypt")) + pr_warn("%s: driver \"%s\" is not wolfCrypt -- the rng " + "probe will measure the stock DRBG.\n", + __func__, drv); + } } +#endif wc_simd_pc = kcalloc(nr_cpu_ids, sizeof(*wc_simd_pc), GFP_KERNEL); if (!wc_simd_pc) { diff --git a/tests/api/test_tls.c b/tests/api/test_tls.c index 61e1c86e62c..a867aae05bc 100644 --- a/tests/api/test_tls.c +++ b/tests/api/test_tls.c @@ -9229,7 +9229,7 @@ int test_tls_unset_fd_no_stdin_io(void) EXPECT_DECLS; #if !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) && \ defined(USE_WOLFSSL_IO) && (defined(__unix__) || defined(__APPLE__)) && \ - !defined(WOLFSSL_LEANPSK) + !defined(WOLFSSL_LEANPSK) && !defined(HAVE_VALGRIND) WOLFSSL_CTX* ctx = NULL; WOLFSSL* ssl = NULL; int sv[2] = { -1, -1 }; diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 74eae07c332..18bf3143de9 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -17832,7 +17832,8 @@ enum { * @param [in] digest Buffer holding digest. * @param [in] digSz Length of digest in bytes. * @return Encoded data size on success. - * @return 0 when dynamic memory allocation fails. + * @return 0 when digSz is larger than WC_MAX_DIGEST_SIZE or dynamic memory + * allocation fails. */ #ifdef WOLFSSL_ASN_TEMPLATE word32 wc_EncodeSignature(byte* out, const byte* digest, word32 digSz, @@ -17843,6 +17844,12 @@ word32 wc_EncodeSignature(byte* out, const byte* digest, word32 digSz, word32 sz = 0; unsigned char dgst[WC_MAX_DIGEST_SIZE]; + /* A DigestInfo holds no more than the largest supported digest. This + * also bounds the copy into dgst when encoding in place. */ + if (digSz > (word32)sizeof(dgst)) { + ret = BUFFER_E; + } + CALLOC_ASNSETDATA(dataASN, digestInfoASN_Length, ret, NULL); if (ret == 0) { diff --git a/wolfcrypt/src/asn_orig.c b/wolfcrypt/src/asn_orig.c index 41620c70e28..db50c035799 100644 --- a/wolfcrypt/src/asn_orig.c +++ b/wolfcrypt/src/asn_orig.c @@ -2983,6 +2983,14 @@ word32 wc_EncodeSignature(byte* out, const byte* digest, word32 digSz, byte seqArray[MAX_SEQ_SZ]; word32 encDigSz, algoSz, seqSz; + /* A DigestInfo holds no more than the largest supported digest. This + * also bounds the copy into digArray. */ + if ((digSz > WC_MAX_DIGEST_SIZE) || + (digSz > (word32)sizeof(digArray) - MAX_ENCODED_DIG_ASN_SZ)) + { + return 0; + } + encDigSz = SetDigest(digest, digSz, digArray); algoSz = SetAlgoID(hashOID, algoArray, oidHashType, 0); seqSz = SetSequence(encDigSz + algoSz, seqArray); diff --git a/wolfcrypt/src/signature.c b/wolfcrypt/src/signature.c index 6edd43fd3fb..cb8671b9914 100644 --- a/wolfcrypt/src/signature.c +++ b/wolfcrypt/src/signature.c @@ -84,6 +84,11 @@ static int wc_SignatureDerEncode(enum wc_HashType hash_type, byte* hash_data, *hash_enc_len = (word32)ret; ret = 0; } + else { + /* wc_EncodeSignature() returns 0 on failure. hash_len is a valid + * digest size here, leaving allocation failure as the cause. */ + ret = MEMORY_E; + } return ret; } diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index c9b3a45d3ff..6c7e984818e 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -41104,6 +41104,19 @@ static wc_test_ret_t rsa_sig_test(RsaKey* key, word32 keyLen, int modLen, WC_RNG #endif /* Parameter Validation testing. */ +#ifndef NO_ASN + /* A digest longer than WC_MAX_DIGEST_SIZE must be rejected, whether or + * not it is encoded in place. */ + XMEMSET(out, 0, RSA_TEST_BYTES); + if (wc_EncodeSignature(out, out, WC_MAX_DIGEST_SIZE + 1, + wc_HashGetOID(WC_HASH_TYPE_SHA256)) != 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, exit_rsa_sig); + if (wc_EncodeSignature(out + (RSA_TEST_BYTES / 2), out, + WC_MAX_DIGEST_SIZE + 1, + wc_HashGetOID(WC_HASH_TYPE_SHA256)) != 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, exit_rsa_sig); +#endif /* !NO_ASN */ + ret = wc_SignatureGetSize(WC_SIGNATURE_TYPE_NONE, key, keyLen); if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), exit_rsa_sig);