diff --git a/include/wolfprovider/internal.h b/include/wolfprovider/internal.h index e0660520..6f8e7745 100644 --- a/include/wolfprovider/internal.h +++ b/include/wolfprovider/internal.h @@ -48,6 +48,9 @@ /** Maximum value of a size_t. */ #define MAX_SIZE_T ((size_t)-1) +/** True when a length fits in a wolfCrypt word32; guards 32-bit truncation. */ +#define WP_FITS_WORD32(len) ((uint64_t)(len) <= (uint64_t)UINT32_MAX) + /** Maximum supported digest name size. */ #define WP_MAX_MD_NAME_SIZE 15 /** Maximum supported cipher name size. */ diff --git a/scripts/resolve-ref.sh b/scripts/resolve-ref.sh index 7c2a86ac..05170f2b 100755 --- a/scripts/resolve-ref.sh +++ b/scripts/resolve-ref.sh @@ -38,7 +38,12 @@ else exit 1 fi - sha=$(echo "$body" | jq -r .sha) + # OSP container images (e.g. sssd) ship without jq; fall back to grep. + if command -v jq >/dev/null 2>&1; then + sha=$(printf '%s' "$body" | jq -r .sha) + else + sha=$(printf '%s' "$body" | grep -o '"sha"[[:space:]]*:[[:space:]]*"[0-9a-f]\{40\}"' | head -n1 | grep -o '[0-9a-f]\{40\}') + fi if [[ -z "$sha" || "$sha" == "null" ]]; then echo "resolve-ref: no sha for $REF in $REPO" >&2 exit 1 diff --git a/src/wp_aes_aead.c b/src/wp_aes_aead.c index 2cb70795..4a5e39c4 100644 --- a/src/wp_aes_aead.c +++ b/src/wp_aes_aead.c @@ -290,10 +290,15 @@ static int wp_aead_cache_aad(wp_AeadCtx *ctx, const unsigned char *in, WOLFPROV_ENTER(WP_LOG_COMP_AES, "wp_aead_cache_aad"); if (inLen > 0) { - p = (unsigned char*)OPENSSL_realloc(ctx->aad, ctx->aadLen + inLen); - if (p == NULL) { + if (inLen > 0xFFFFFFFFU - ctx->aadLen) { ok = 0; } + if (ok) { + p = (unsigned char*)OPENSSL_realloc(ctx->aad, ctx->aadLen + inLen); + if (p == NULL) { + ok = 0; + } + } if (ok) { ctx->aad = p; XMEMCPY(ctx->aad + ctx->aadLen, in, inLen); @@ -1460,6 +1465,10 @@ static int wp_aesgcm_encdec(wp_AeadCtx *ctx, unsigned char *out, size_t* outLen, WOLFPROV_ENTER(WP_LOG_COMP_AES, "wp_aesgcm_encdec"); + if ((!WP_FITS_WORD32(ctx->inLen)) || (!WP_FITS_WORD32(ctx->aadLen))) { + return 0; + } + if (ctx->tagLen == UNINITIALISED_SIZET) { ctx->tagLen = EVP_GCM_TLS_TAG_LEN; } @@ -1540,7 +1549,7 @@ static int wp_aesgcm_encdec(wp_AeadCtx *ctx, unsigned char *out, size_t* outLen, XMEMCPY(out, tmp + offset, (ctx->inLen - offset)); *outLen = (ctx->inLen - offset); } - OPENSSL_free(tmp); + OPENSSL_clear_free(tmp, ctx->inLen); } else { *outLen = 0; @@ -1550,7 +1559,7 @@ static int wp_aesgcm_encdec(wp_AeadCtx *ctx, unsigned char *out, size_t* outLen, ctx->aad = NULL; ctx->aadLen = 0; ctx->aadSet = 0; - OPENSSL_free(ctx->in); + OPENSSL_clear_free(ctx->in, ctx->inLen); ctx->bufSize = 0; ctx->in = NULL; ctx->inLen = 0; @@ -1984,6 +1993,10 @@ static int wp_aesccm_encdec(wp_AeadCtx *ctx, unsigned char *out, WOLFPROV_ENTER(WP_LOG_COMP_AES, "wp_aesccm_encdec"); + if ((!WP_FITS_WORD32(inLen)) || (!WP_FITS_WORD32(ctx->aadLen))) { + return 0; + } + if (ctx->tagLen == UNINITIALISED_SIZET) { ctx->tagLen = EVP_CCM_TLS_TAG_LEN; } diff --git a/src/wp_aes_block.c b/src/wp_aes_block.c index 4e623fd3..592662ad 100644 --- a/src/wp_aes_block.c +++ b/src/wp_aes_block.c @@ -117,7 +117,7 @@ static void *wp_aes_block_dupctx(wp_AesBlockCtx *src) if (src->tlsmacAlloced && src->tlsmac != NULL) { dst->tlsmac = OPENSSL_malloc(src->tlsmacsize); if (dst->tlsmac == NULL) { - OPENSSL_free(dst); + OPENSSL_clear_free(dst, sizeof(*dst)); return NULL; } XMEMCPY(dst->tlsmac, src->tlsmac, src->tlsmacsize); diff --git a/src/wp_aes_stream.c b/src/wp_aes_stream.c index 38c9f29a..de0dcbfd 100644 --- a/src/wp_aes_stream.c +++ b/src/wp_aes_stream.c @@ -438,6 +438,8 @@ static int wp_aes_cts_encrypt(wp_AesStreamCtx *ctx, unsigned char *out, XMEMCPY(out + AES_BLOCK_SIZE, ctsBlock, inLen - AES_BLOCK_SIZE); } + OPENSSL_cleanse(ctsBlock, sizeof(ctsBlock)); + return ok; } @@ -505,6 +507,9 @@ static int wp_aes_cts_decrypt(wp_AesStreamCtx *ctx, unsigned char *out, } } + OPENSSL_cleanse(ctsBlock, sizeof(ctsBlock)); + OPENSSL_cleanse(tmp, sizeof(tmp)); + return ok; } diff --git a/src/wp_aes_wrap.c b/src/wp_aes_wrap.c index e8f30346..26a8e5ea 100644 --- a/src/wp_aes_wrap.c +++ b/src/wp_aes_wrap.c @@ -345,12 +345,10 @@ static int wp_aes_wrap_update(wp_AesWrapCtx *ctx, unsigned char *out, WOLFPROV_ENTER(WP_LOG_COMP_AES, "wp_aes_wrap_update"); - if (!wolfssl_prov_is_running()) { + if ((!WP_FITS_WORD32(inLen)) || (!WP_FITS_WORD32(outSize))) { ok = 0; } - - if (ok && (((uint64_t)inLen > (uint64_t)UINT32_MAX) || - ((uint64_t)outSize > (uint64_t)UINT32_MAX))) { + if (ok && !wolfssl_prov_is_running()) { ok = 0; } diff --git a/src/wp_cmac.c b/src/wp_cmac.c index 9971edba..292597ef 100644 --- a/src/wp_cmac.c +++ b/src/wp_cmac.c @@ -120,6 +120,9 @@ static int wp_cmac_set_key(wp_CmacCtx* macCtx, const unsigned char* key, if (keyLen > AES_256_KEY_SIZE) { ok = 0; } + if (ok && (macCtx->expKeySize != 0) && (keyLen != macCtx->expKeySize)) { + ok = 0; + } if (ok) { if (macCtx->keyLen > 0) { OPENSSL_cleanse(macCtx->key, macCtx->keyLen); @@ -234,7 +237,8 @@ static int wp_cmac_update(wp_CmacCtx* macCtx, const unsigned char* data, WOLFPROV_ENTER(WP_LOG_COMP_MAC, "wp_cmac_update"); while (ok && (dataLen > 0)) { - word32 chunk = (dataLen > 0xFFFFFFFFU) ? 0xFFFFFFFFU : (word32)dataLen; + word32 chunk = (!WP_FITS_WORD32(dataLen)) ? + 0xFFFFFFFFU : (word32)dataLen; int rc = wc_CmacUpdate(&macCtx->cmac, data, chunk); if (rc != 0) { WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_CmacUpdate", @@ -419,8 +423,9 @@ static int wp_cmac_setup_cipher(wp_CmacCtx* macCtx, const OSSL_PARAM params[]) macCtx->size = AES_BLOCK_SIZE; for (i = 0; i < WP_CMAC_CIPHER_NAMES_LEN; i++) { - if (XSTRNCMP(p->data, wp_cmac_cipher_names[i].name, - p->data_size) == 0) { + if ((XSTRLEN(wp_cmac_cipher_names[i].name) == p->data_size) && + (XSTRNCMP(p->data, wp_cmac_cipher_names[i].name, + p->data_size) == 0)) { macCtx->expKeySize = wp_cmac_cipher_names[i].keySize; break; } diff --git a/src/wp_dec_epki2pki.c b/src/wp_dec_epki2pki.c index 29d61567..c31b98b1 100644 --- a/src/wp_dec_epki2pki.c +++ b/src/wp_dec_epki2pki.c @@ -151,7 +151,8 @@ static int wp_DecryptPKCS8Key(byte* input, word32 sz, const char* password, ret = 0; } if (ret == 0) { - ret = wc_AllocDer(&pkcs8Der, pkcs8Sz, DYNAMIC_TYPE_KEY, NULL); + /* PRIVATEKEY_TYPE so wc_FreeDer zeroizes the decrypted key. */ + ret = wc_AllocDer(&pkcs8Der, pkcs8Sz, PRIVATEKEY_TYPE, NULL); } if (ret == 0) { ret = wc_CreatePKCS8Key(pkcs8Der->buffer, &pkcs8Der->length, diff --git a/src/wp_des.c b/src/wp_des.c index e4eb4f73..896dbb80 100644 --- a/src/wp_des.c +++ b/src/wp_des.c @@ -296,6 +296,9 @@ static int wp_des3_block_init(wp_Des3BlockCtx *ctx, const unsigned char *key, } if (ok && (iv == NULL) && ctx->ivSet && (ctx->mode == EVP_CIPH_CBC_MODE)) { XMEMCPY(ctx->iv, ctx->oiv, ctx->ivLen); + if (wc_Des3_SetIV(&ctx->des3, ctx->iv) != 0) { + ok = 0; + } } if (ok && (key != NULL)) { diff --git a/src/wp_dh_exch.c b/src/wp_dh_exch.c index 3154e297..9b657b2b 100644 --- a/src/wp_dh_exch.c +++ b/src/wp_dh_exch.c @@ -152,6 +152,7 @@ static wp_DhCtx* wp_dh_dupctx(wp_DhCtx* src) dst->pad = src->pad; dst->kdfType = src->kdfType; dst->kdfMd = src->kdfMd; + XMEMCPY(dst->kdfMdName, src->kdfMdName, sizeof(dst->kdfMdName)); dst->ukmLen = src->ukmLen; dst->keyLen = src->keyLen; } @@ -276,8 +277,11 @@ static int wp_dh_derive_secret(wp_DhCtx* ctx, unsigned char* secret, WOLFPROV_ENTER(WP_LOG_COMP_DH, "wp_dh_derive_secret"); + if (!WP_FITS_WORD32(*secLen)) { + ok = 0; + } /* Get our private key data. */ - if (!wp_dh_get_priv(ctx->key, &priv, &privSz)) { + if (ok && (!wp_dh_get_priv(ctx->key, &priv, &privSz))) { ok = 0; } /* Get peer's public key data. */ diff --git a/src/wp_dh_kmgmt.c b/src/wp_dh_kmgmt.c index c3dba0c1..7e322027 100644 --- a/src/wp_dh_kmgmt.c +++ b/src/wp_dh_kmgmt.c @@ -450,13 +450,15 @@ void wp_dh_free(wp_Dh* dh) int rc; rc = wc_LockMutex(&dh->mutex); - if (rc < 0) { - WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_LockMutex", rc); - } - cnt = --dh->refCnt; if (rc == 0) { + cnt = --dh->refCnt; wc_UnLockMutex(&dh->mutex); } + else { + WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_LockMutex", rc); + /* Cannot safely decrement without the lock; keep the object. */ + cnt = dh->refCnt; + } #else cnt = --dh->refCnt; #endif @@ -847,7 +849,25 @@ static int wp_dh_get_params(wp_Dh* dh, OSSL_PARAM params[]) if (p->data == NULL) { p->return_size = dh->pubSz; } - else { + else if (p->data_type == OSSL_PARAM_UNSIGNED_INTEGER) { + mp_int pub; + + if (mp_init(&pub) != 0) { + ok = 0; + } + else { + if (mp_read_unsigned_bin(&pub, dh->pub, + (int)dh->pubSz) != 0) { + ok = 0; + } + if (ok) { + ok = wp_params_set_mp(params, OSSL_PKEY_PARAM_PUB_KEY, + &pub, 1); + } + mp_clear(&pub); + } + } + else { /* return_size is set within this function */ ok = wp_params_set_octet_string_be(params, OSSL_PKEY_PARAM_PUB_KEY, dh->pub, dh->pubSz); @@ -861,13 +881,21 @@ static int wp_dh_get_params(wp_Dh* dh, OSSL_PARAM params[]) p->return_size = dh->privSz; } else if (p->data_type == OSSL_PARAM_UNSIGNED_INTEGER) { - if (p->data_size < dh->privSz) { + mp_int priv; + + if (mp_init(&priv) != 0) { ok = 0; } else { - /* OSSL returns a BIGNUM, but we copy raw bytes*/ - XMEMCPY(p->data, dh->priv, dh->privSz); - p->return_size = dh->privSz; + if (mp_read_unsigned_bin(&priv, dh->priv, + (int)dh->privSz) != 0) { + ok = 0; + } + if (ok) { + ok = wp_params_set_mp(params, OSSL_PKEY_PARAM_PRIV_KEY, + &priv, 1); + } + mp_forcezero(&priv); } } else { @@ -1225,7 +1253,7 @@ static int wp_dh_import(wp_Dh* dh, int selection, const OSSL_PARAM params[]) OSSL_PARAM_BN(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0) /** DH public key parameters. */ #define WP_DH_PUBLIC_KEY_PARAMS \ - OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0) + OSSL_PARAM_BN(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0) /** DH domain parameters - with FFC_Q. */ #define WP_DH_DOMAIN_PARAMS \ OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_P, NULL, 0), \ @@ -1665,6 +1693,9 @@ static int wp_dh_gen_parameters(wp_DhGenCtx *ctx, wp_Dh* dh) WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_DhGenerateParams", rc); ok = 0; } + if (ok) { + dh->bits = mp_count_bits(&dh->key.p); + } WOLFPROV_LEAVE(WP_LOG_COMP_DH, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok); return ok; diff --git a/src/wp_digests.c b/src/wp_digests.c index 0c777eeb..7bd3f1a7 100644 --- a/src/wp_digests.c +++ b/src/wp_digests.c @@ -154,7 +154,7 @@ static int name##_update(void* ctx, const unsigned char* in, size_t inLen) \ int ok = 1; \ WOLFPROV_ENTER(WP_LOG_COMP_DIGEST, #name "_update"); \ while (ok && (inLen > 0)) { \ - word32 chunk = (inLen > 0xFFFFFFFFU) ? 0xFFFFFFFFU : (word32)inLen; \ + word32 chunk = (!WP_FITS_WORD32(inLen)) ? 0xFFFFFFFFU : (word32)inLen; \ int rc = upd(ctx, in, chunk); \ if (rc != 0) { \ WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, #upd, rc); \ @@ -640,7 +640,7 @@ static int name##_final(CTX* ctx, unsigned char* out, size_t* outLen, \ if (!wolfssl_prov_is_running()) { \ ok = 0; \ } \ - if (ok && (outSize < ctx->outLen)) { \ + if (ok && ((outSize < ctx->outLen) || (!WP_FITS_WORD32(ctx->outLen)))) { \ ok = 0; \ } \ if (ok) { \ diff --git a/src/wp_drbg.c b/src/wp_drbg.c index a92188f8..cf2cf2a4 100644 --- a/src/wp_drbg.c +++ b/src/wp_drbg.c @@ -138,12 +138,8 @@ static void wp_drbg_free(wp_DrbgCtx* ctx) OPENSSL_free(ctx->mutex); } #endif - #if LIBWOLFSSL_VERSION_HEX >= 0x05000000 - (void)wc_rng_free(ctx->rng); - #else wc_FreeRng(ctx->rng); OPENSSL_clear_free(ctx->rng, sizeof(*ctx->rng)); - #endif OPENSSL_free(ctx); } } @@ -235,28 +231,20 @@ static int wp_drbg_instantiate(wp_DrbgCtx* ctx, unsigned int strength, WOLFPROV_MSG_DEBUG(WP_LOG_COMP_RNG, "No parent DRBG, using direct seeding"); - #if LIBWOLFSSL_VERSION_HEX >= 0x05000000 - ctx->rng = wc_rng_new((byte*)pStr, (word32)pStrLen, NULL); - if (ctx->rng == NULL) { - ok = 0; - } - #else - (void)pStr; - (void)pStrLen; - + /* wc_InitRngNonce (not wc_rng_new) keeps rng on the OPENSSL allocator to match the parent path's free. */ ctx->rng = OPENSSL_zalloc(sizeof(*ctx->rng)); if (ctx->rng == NULL) { ok = 0; } if (ok) { - int rc = wc_InitRng(ctx->rng); + int rc = wc_InitRngNonce(ctx->rng, (byte*)pStr, (word32)pStrLen); if (rc != 0) { - WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_COMP_RNG, "wc_InitRng", rc); + WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_COMP_RNG, "wc_InitRngNonce", rc); OPENSSL_clear_free(ctx->rng, sizeof(*ctx->rng)); + ctx->rng = NULL; ok = 0; } } - #endif } #ifndef WP_HAVE_DRBG_RESEED @@ -279,12 +267,8 @@ static int wp_drbg_uninstantiate(wp_DrbgCtx* ctx) { WOLFPROV_ENTER(WP_LOG_COMP_RNG, "wp_drbg_uninstantiate"); -#if LIBWOLFSSL_VERSION_HEX >= 0x05000000 - (void)wc_rng_free(ctx->rng); -#else wc_FreeRng(ctx->rng); OPENSSL_clear_free(ctx->rng, sizeof(*ctx->rng)); -#endif ctx->rng = NULL; #ifndef WP_HAVE_DRBG_RESEED ctx->rngError = 0; @@ -336,7 +320,7 @@ static int wp_drbg_generate(wp_DrbgCtx* ctx, unsigned char* out, } #endif - if (ok && (outLen > 0xFFFFFFFFU)) { + if (ok && (!WP_FITS_WORD32(outLen))) { WOLFPROV_MSG_DEBUG(WP_LOG_COMP_RNG, "Request length is too big"); ok = 0; } @@ -413,10 +397,10 @@ static int wp_drbg_reseed(wp_DrbgCtx* ctx, int predResist, } /* wolfCrypt RNG APIs take word32 lengths; reject oversized inputs. */ - if (ok && entropy != NULL && entropyLen > 0xFFFFFFFFU) { + if (ok && entropy != NULL && !WP_FITS_WORD32(entropyLen)) { ok = 0; } - if (ok && addIn != NULL && addInLen > 0xFFFFFFFFU) { + if (ok && addIn != NULL && !WP_FITS_WORD32(addInLen)) { ok = 0; } @@ -627,7 +611,7 @@ static const OSSL_PARAM* wp_drbg_gettable_ctx_params(wp_DrbgCtx* ctx, */ static const OSSL_PARAM wp_supported_gettable_drbg_ctx_params[] = { OSSL_PARAM_size_t(OSSL_RAND_PARAM_MAX_REQUEST, NULL), - OSSL_PARAM_size_t(OSSL_RAND_PARAM_STATE, NULL), + OSSL_PARAM_int(OSSL_RAND_PARAM_STATE, NULL), OSSL_PARAM_END }; (void)ctx; @@ -783,7 +767,7 @@ static size_t wp_drbg_get_seed(wp_DrbgCtx* ctx, unsigned char** pSeed, goto end; } - if (minLen > 0xFFFFFFFFU) { + if (!WP_FITS_WORD32(minLen)) { OPENSSL_secure_free(buffer); goto end; } diff --git a/src/wp_ecc_kmgmt.c b/src/wp_ecc_kmgmt.c index 0560c41a..4b5f31e2 100644 --- a/src/wp_ecc_kmgmt.c +++ b/src/wp_ecc_kmgmt.c @@ -406,13 +406,15 @@ void wp_ecc_free(wp_Ecc* ecc) int rc; rc = wc_LockMutex(&ecc->mutex); - if (rc < 0) { - WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_LockMutex", rc); - } - cnt = --ecc->refCnt; if (rc == 0) { + cnt = --ecc->refCnt; wc_UnLockMutex(&ecc->mutex); } + else { + WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_LockMutex", rc); + /* Cannot safely decrement without the lock; keep the object. */ + cnt = ecc->refCnt; + } #else cnt = --ecc->refCnt; #endif @@ -688,6 +690,8 @@ static const OSSL_PARAM *wp_ecc_gettable_params(WOLFPROV_CTX* provCtx) OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_EC_PUB_Y, NULL, 0), OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0), + OSSL_PARAM_int(OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, NULL), OSSL_PARAM_END }; (void)provCtx; @@ -1054,17 +1058,25 @@ static int wp_ecc_validate(const wp_Ecc* ecc, int selection, int checkType) (void)checkType; #endif { - /* We may have a private key inside that does not match the public - * key that has been set, which is OK. Override the internal type - * to force a public key only check */ - origType = ecc->key.type; - ((wp_Ecc*)ecc)->key.type = ECC_PUBLICKEY; - rc = wc_ecc_check_key((ecc_key*)&ecc->key); - ((wp_Ecc*)ecc)->key.type = origType; - if (rc != 0) { - WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_ecc_check_key", rc); + /* Fail closed if the key mutex can't be held for the check. */ + if (wp_lock(wp_ecc_get_mutex((wp_Ecc*)ecc)) != 1) { ok = 0; } + if (ok) { + /* We may have a private key inside that does not match the + * public key that has been set, which is OK. Override the + * internal type to force a public key only check */ + origType = ecc->key.type; + ((wp_Ecc*)ecc)->key.type = ECC_PUBLICKEY; + rc = wc_ecc_check_key((ecc_key*)&ecc->key); + ((wp_Ecc*)ecc)->key.type = origType; + wp_unlock(wp_ecc_get_mutex((wp_Ecc*)ecc)); + if (rc != 0) { + WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, + "wc_ecc_check_key", rc); + ok = 0; + } + } } } if (ok && ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) && diff --git a/src/wp_ecdh_exch.c b/src/wp_ecdh_exch.c index f9ba02d5..2255b0e2 100644 --- a/src/wp_ecdh_exch.c +++ b/src/wp_ecdh_exch.c @@ -154,6 +154,7 @@ static wp_EcdhCtx* wp_ecdh_dup(wp_EcdhCtx* src) dst->cofactor = src->cofactor; dst->kdfType = src->kdfType; dst->kdfMd = src->kdfMd; + XMEMCPY(dst->kdfMdName, src->kdfMdName, sizeof(dst->kdfMdName)); dst->ukmLen = src->ukmLen; dst->keyLen = src->keyLen; } @@ -228,6 +229,11 @@ static int wp_ecdh_kdf_derive(wp_EcdhCtx* ctx, unsigned char* key, WOLFPROV_ENTER(WP_LOG_COMP_ECDH, "wp_ecdh_kdf_derive"); + if ((!WP_FITS_WORD32(secLen)) || (!WP_FITS_WORD32(ctx->ukmLen)) || + (!WP_FITS_WORD32(ctx->keyLen))) { + return 0; + } + if (keySize < ctx->keyLen) { ok = 0; } diff --git a/src/wp_ecdsa_sig.c b/src/wp_ecdsa_sig.c index e4ae0383..624d7612 100644 --- a/src/wp_ecdsa_sig.c +++ b/src/wp_ecdsa_sig.c @@ -301,8 +301,11 @@ static int wp_ecdsa_sign(wp_EcdsaSigCtx *ctx, unsigned char *sig, if (sigSize == (size_t)-1) { sigSize = *sigLen; } + if ((!WP_FITS_WORD32(sigSize)) || (!WP_FITS_WORD32(tbsLen))) { + ok = 0; + } len = (word32)sigSize; - if (wp_lock(wp_ecc_get_mutex(ctx->ecc)) != 1) { + if (ok && (wp_lock(wp_ecc_get_mutex(ctx->ecc)) != 1)) { ok = 0; } if (ok) { @@ -391,6 +394,9 @@ static int wp_ecdsa_verify(wp_EcdsaSigCtx *ctx, const unsigned char *sig, (tbsLen < WC_MIN_DIGEST_SIZE)) { ok = 0; } + else if ((!WP_FITS_WORD32(sigLen)) || (!WP_FITS_WORD32(tbsLen))) { + ok = 0; + } else { int res; int rc = wc_ecc_verify_hash(sig, (word32)sigLen, tbs, (word32)tbsLen, @@ -568,6 +574,10 @@ static int wp_ecdsa_digest_signverify_update(wp_EcdsaSigCtx *ctx, WOLFPROV_ENTER(WP_LOG_COMP_ECDSA, "wp_ecdsa_digest_signverify_update"); + if (!WP_FITS_WORD32(dataLen)) { + return 0; + } + int rc = wc_HashUpdate(&ctx->hash, #if LIBWOLFSSL_VERSION_HEX >= 0x05007004 ctx->hash.type, @@ -761,14 +771,14 @@ static int wp_ecdsa_digest_verify_final(wp_EcdsaSigCtx *ctx, unsigned char *sig, }; ok = OSSL_PARAM_set_octet_string(p, ecdsa_sha256, sizeof(ecdsa_sha256)); } - if ((XMEMCMP(ctx->mdName, "SHA384", 7) == 0) || + else if ((XMEMCMP(ctx->mdName, "SHA384", 7) == 0) || (XMEMCMP(ctx->mdName, "sha384", 7) == 0)) { static const unsigned char ecdsa_sha384[] = { 0x30, 0x0a, 0x06, 0x08, 42, 134, 72, 206, 61, 4, 3, 3 }; ok = OSSL_PARAM_set_octet_string(p, ecdsa_sha384, sizeof(ecdsa_sha384)); } - if ((XMEMCMP(ctx->mdName, "SHA512", 7) == 0) || + else if ((XMEMCMP(ctx->mdName, "SHA512", 7) == 0) || (XMEMCMP(ctx->mdName, "sha512", 7) == 0)) { static const unsigned char ecdsa_sha512[] = { 0x30, 0x0a, 0x06, 0x08, 42, 134, 72, 206, 61, 4, 3, 4 diff --git a/src/wp_ecx_exch.c b/src/wp_ecx_exch.c index c900fe42..548683cf 100644 --- a/src/wp_ecx_exch.c +++ b/src/wp_ecx_exch.c @@ -230,7 +230,10 @@ static int wp_x25519_derive(wp_EcxCtx* ctx, unsigned char* secret, WOLFPROV_ENTER(WP_LOG_COMP_X25519, "wp_x25519_derive"); - if (!wolfssl_prov_is_running()) { + if (!WP_FITS_WORD32(secSize)) { + ok = 0; + } + if (ok && !wolfssl_prov_is_running()) { ok = 0; } @@ -330,7 +333,10 @@ static int wp_x448_derive(wp_EcxCtx* ctx, unsigned char* secret, WOLFPROV_ENTER(WP_LOG_COMP_X448, "wp_x448_derive"); - if (!wolfssl_prov_is_running()) { + if (!WP_FITS_WORD32(secSize)) { + ok = 0; + } + if (ok && !wolfssl_prov_is_running()) { ok = 0; } diff --git a/src/wp_ecx_kmgmt.c b/src/wp_ecx_kmgmt.c index 52e9c115..b26df4ec 100644 --- a/src/wp_ecx_kmgmt.c +++ b/src/wp_ecx_kmgmt.c @@ -323,13 +323,15 @@ void wp_ecx_free(wp_Ecx* ecx) int rc; rc = wc_LockMutex(&ecx->mutex); - if (rc < 0) { - WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_LockMutex", rc); - } - cnt = --ecx->refCnt; if (rc == 0) { + cnt = --ecx->refCnt; wc_UnLockMutex(&ecx->mutex); } + else { + WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_LockMutex", rc); + /* Cannot safely decrement without the lock; keep the object. */ + cnt = ecx->refCnt; + } #else cnt = --ecx->refCnt; #endif @@ -739,7 +741,7 @@ static int wp_ecx_match_priv_key(const wp_Ecx* ecx1, const wp_Ecx* ecx2) if (ok && (len1 != len2)) { ok = 0; } - if (ok && (XMEMCMP(key1, key2, len1) != 0)) { + if (ok && (CRYPTO_memcmp(key1, key2, len1) != 0)) { ok = 0; } @@ -1027,7 +1029,7 @@ static int wp_ecx_import(wp_Ecx* ecx, int selection, const OSSL_PARAM params[]) /** ECX private key parameters. */ #define WP_ECX_PRIVATE_KEY_PARAMS \ - OSSL_PARAM_BN(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0) + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0) /** ECX public key parameters. */ #define WP_ECX_PUBLIC_KEY_PARAMS \ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0) @@ -1157,6 +1159,7 @@ static int wp_ecx_export_keypair(wp_Ecx* ecx, OSSL_PARAM* params, int* pIdx, } wp_param_set_octet_string_ptr(¶ms[i++], OSSL_PKEY_PARAM_PRIV_KEY, data + *idx, outLen); + *idx += outLen; } } @@ -2242,6 +2245,7 @@ static int wp_ecx_encode(wp_EcxEncDecCtx* ctx, OSSL_CORE_BIO *cBio, OPENSSL_free(pemData); } OPENSSL_free(cipherInfo); + BIO_free(out); WOLFPROV_LEAVE(WP_LOG_COMP_KE, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok); return ok; } diff --git a/src/wp_ecx_sig.c b/src/wp_ecx_sig.c index e5b6a0a0..dad4977f 100644 --- a/src/wp_ecx_sig.c +++ b/src/wp_ecx_sig.c @@ -399,6 +399,9 @@ static int wp_ed25519_digest_sign(wp_EcxSigCtx *ctx, unsigned char *sig, } } } + if (ok && (!WP_FITS_WORD32(tbsLen))) { + ok = 0; + } if (ok) { if (wp_lock(wp_ecx_get_mutex(ctx->ecx)) != 1) { ok = 0; @@ -462,8 +465,11 @@ static int wp_ed25519_digest_verify(wp_EcxSigCtx *ctx, unsigned char *sig, } } } + if (ok && ((!WP_FITS_WORD32(sigLen)) || (!WP_FITS_WORD32(tbsLen)))) { + ok = 0; + } if (ok) { - int res; + int res = 0; int rc = wc_ed25519_verify_msg(sig, (word32)sigLen, tbs, (word32)tbsLen, &res, wp_ecx_get_key(ctx->ecx)); if (rc != 0) { @@ -606,6 +612,9 @@ static int wp_ed448_digest_sign(wp_EcxSigCtx *ctx, unsigned char *sig, } } } + if (ok && (!WP_FITS_WORD32(tbsLen))) { + ok = 0; + } if (ok) { if (wp_lock(wp_ecx_get_mutex(ctx->ecx)) != 1) { ok = 0; @@ -675,8 +684,11 @@ static int wp_ed448_digest_verify(wp_EcxSigCtx *ctx, unsigned char *sig, } } } + if (ok && ((!WP_FITS_WORD32(sigLen)) || (!WP_FITS_WORD32(tbsLen)))) { + ok = 0; + } if (ok) { - int res; + int res = 0; int rc = wc_ed448_verify_msg(sig, (word32)sigLen, tbs, (word32)tbsLen, &res, wp_ecx_get_key(ctx->ecx), NULL, 0); if (rc != 0) { diff --git a/src/wp_gmac.c b/src/wp_gmac.c index 50bff4d1..8e2f5abc 100644 --- a/src/wp_gmac.c +++ b/src/wp_gmac.c @@ -123,6 +123,9 @@ static int wp_gmac_set_key(wp_GmacCtx* macCtx, const unsigned char *key, if (keyLen > AES_256_KEY_SIZE) { ok = 0; } + if (ok && (macCtx->expKeySize != 0) && (keyLen != macCtx->expKeySize)) { + ok = 0; + } if (ok) { if (macCtx->keyLen > 0) { OPENSSL_cleanse(macCtx->key, macCtx->keyLen); @@ -244,6 +247,9 @@ static int wp_gmac_update(wp_GmacCtx* macCtx, const unsigned char* data, WOLFPROV_ENTER(WP_LOG_COMP_MAC, "wp_gmac_update"); /* Data cached as wolfSSL doesn't have a streaming API. */ + if (dataLen > 0xFFFFFFFFU - macCtx->dataLen) { + return 0; + } p = OPENSSL_realloc(macCtx->data, macCtx->dataLen + dataLen); if (p == NULL) { ok = 0; @@ -276,7 +282,10 @@ static int wp_gmac_final(wp_GmacCtx* macCtx, unsigned char* out, size_t* outl, WOLFPROV_ENTER(WP_LOG_COMP_MAC, "wp_gmac_final"); - if (!wolfssl_prov_is_running()) { + if ((!WP_FITS_WORD32(macCtx->dataLen)) || (!WP_FITS_WORD32(outSize))) { + ok = 0; + } + if (ok && !wolfssl_prov_is_running()) { ok = 0; } if (ok && (outSize < AES_BLOCK_SIZE)) { @@ -414,8 +423,9 @@ static int wp_gmac_setup_cipher(wp_GmacCtx* macCtx, const OSSL_PARAM params[]) size_t i; for (i = 0; i < WP_GMAC_CIPHER_NAMES_LEN; i++) { - if (XSTRNCMP(p->data, wp_gmac_cipher_names[i].name, - p->data_size) == 0) { + if ((XSTRLEN(wp_gmac_cipher_names[i].name) == p->data_size) && + (XSTRNCMP(p->data, wp_gmac_cipher_names[i].name, + p->data_size) == 0)) { macCtx->expKeySize = wp_gmac_cipher_names[i].keySize; break; } diff --git a/src/wp_hkdf.c b/src/wp_hkdf.c index 43a53d5e..12c9f6df 100644 --- a/src/wp_hkdf.c +++ b/src/wp_hkdf.c @@ -158,7 +158,7 @@ static void wp_kdf_hkdf_clear(wp_HkdfCtx* ctx) } OPENSSL_free(ctx->label); OPENSSL_free(ctx->prefix); - OPENSSL_free(ctx->salt); + OPENSSL_clear_free(ctx->salt, ctx->saltSz); OPENSSL_cleanse(ctx->info, ctx->infoSz); } @@ -209,6 +209,11 @@ static int wp_kdf_hkdf_derive(wp_HkdfCtx* ctx, unsigned char* key, int ok = 1; WOLFPROV_ENTER(WP_LOG_COMP_HKDF, "wp_kdf_hkdf_derive"); + + if ((!WP_FITS_WORD32(keyLen)) || (!WP_FITS_WORD32(ctx->keySz)) || + (!WP_FITS_WORD32(ctx->saltSz)) || (!WP_FITS_WORD32(ctx->infoSz))) { + return 0; + } WOLFPROV_MSG_DEBUG(WP_LOG_COMP_HKDF, "HKDF derive: keyLen=%zu, mode=%d", keyLen, ctx->mode); WOLFPROV_MSG_DEBUG(WP_LOG_COMP_HKDF, "HKDF derive: keySz=%zu, saltSz=%zu, infoSz=%zu", ctx->keySz, ctx->saltSz, ctx->infoSz); @@ -368,7 +373,7 @@ static int wp_hkdf_base_set_ctx_params(wp_HkdfCtx* ctx, #else if (p != NULL) { #endif - OPENSSL_free(ctx->salt); + OPENSSL_clear_free(ctx->salt, ctx->saltSz); ctx->salt = NULL; if (!OSSL_PARAM_get_octet_string( p, (void**)&ctx->salt, 0, &ctx->saltSz)) { @@ -575,6 +580,12 @@ static int wp_tls13_hkdf_expand(wp_HkdfCtx* ctx, unsigned char* inKey, int rc; WOLFPROV_ENTER(WP_LOG_COMP_HKDF, "wp_tls13_hkdf_expand"); + + if ((!WP_FITS_WORD32(keyLen)) || (!WP_FITS_WORD32(inKeyLen)) || + (!WP_FITS_WORD32(dataLen)) || (!WP_FITS_WORD32(ctx->prefixLen)) || + (!WP_FITS_WORD32(ctx->labelLen))) { + return 0; + } WOLFPROV_MSG_DEBUG(WP_LOG_COMP_HKDF, "TLS1.3 HKDF expand: inKeyLen=%zu, dataLen=%zu, keyLen=%zu", inKeyLen, dataLen, keyLen); diff --git a/src/wp_hmac.c b/src/wp_hmac.c index 54ac20d7..c3c44ff4 100644 --- a/src/wp_hmac.c +++ b/src/wp_hmac.c @@ -137,6 +137,10 @@ static int wp_hmac_set_key(wp_HmacCtx* macCtx, const unsigned char* key, blockSize = (word32)blockSizeRet; } + if (ok && !WP_FITS_WORD32(keyLen)) { + ok = 0; + } + if (ok && (macCtx->keyLen > 0)) { OPENSSL_secure_clear_free(macCtx->key, macCtx->keyLen); } @@ -271,7 +275,8 @@ static int wp_hmac_update(wp_HmacCtx* macCtx, const unsigned char* data, WOLFPROV_ENTER(WP_LOG_COMP_MAC, "wp_hmac_update"); while (ok && (dataLen > 0)) { - word32 chunk = (dataLen > 0xFFFFFFFFU) ? 0xFFFFFFFFU : (word32)dataLen; + word32 chunk = (!WP_FITS_WORD32(dataLen)) ? + 0xFFFFFFFFU : (word32)dataLen; int rc = wc_HmacUpdate(&macCtx->hmac, data, chunk); if (rc != 0) { WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_HmacUpdate", diff --git a/src/wp_internal.c b/src/wp_internal.c index f392b00c..4b47e095 100644 --- a/src/wp_internal.c +++ b/src/wp_internal.c @@ -900,8 +900,9 @@ int wp_cipher_from_params(const OSSL_PARAM params[], int* cipher, size_t i; for (i = 0; i < WP_CIPHER_NAMES_LEN; i++) { - if (XSTRNCMP(p->data, wp_cipher_names[i].name, - p->data_size) == 0) { + if ((XSTRLEN(wp_cipher_names[i].name) == p->data_size) && + (XSTRNCMP(p->data, wp_cipher_names[i].name, + p->data_size) == 0)) { *cipher = wp_cipher_names[i].id; if (cipherName != NULL) { *cipherName = wp_cipher_names[i].canonName; diff --git a/src/wp_kbkdf.c b/src/wp_kbkdf.c index 7b595cb6..01d4588e 100644 --- a/src/wp_kbkdf.c +++ b/src/wp_kbkdf.c @@ -254,6 +254,18 @@ static int wp_kdf_kbkdf_set_ctx_params(wp_KbkdfCtx* ctx, } } + if (ok) { + p = OSSL_PARAM_locate((OSSL_PARAM*)params, OSSL_KDF_PARAM_LABEL); + if ((p != NULL) && (p->data != NULL)) { + OPENSSL_free(ctx->label); + ctx->label = NULL; + if (!OSSL_PARAM_get_octet_string(p, (void**)&ctx->label, 0, + &ctx->labelLen)) { + ok = 0; + } + } + } + if (ok) { p = OSSL_PARAM_locate((OSSL_PARAM*)params, OSSL_KDF_PARAM_INFO); if ((p != NULL) && (p->data != NULL)) { @@ -419,6 +431,10 @@ static int wp_kbkdf_init_mac(wp_KbkdfCtx* ctx, unsigned char* key, WOLFPROV_ENTER(WP_LOG_COMP_KDF, "wp_kbkdf_init_mac"); + if (!WP_FITS_WORD32(keyLen)) { + return 0; + } + switch(ctx->mac) { #ifdef WP_HAVE_HMAC case WP_MAC_TYPE_HMAC: @@ -479,6 +495,10 @@ static int wp_kbkdf_mac_update(wp_KbkdfCtx* ctx, const unsigned char *data, WOLFPROV_ENTER(WP_LOG_COMP_KDF, "wp_kbkdf_mac_update"); + if (!WP_FITS_WORD32(dataLen)) { + return 0; + } + switch(ctx->mac) { #ifdef WP_HAVE_HMAC case WP_MAC_TYPE_HMAC: @@ -595,6 +615,11 @@ static int wp_kdf_kbkdf_derive(wp_KbkdfCtx* ctx, unsigned char* key, WOLFPROV_ENTER(WP_LOG_COMP_KDF, "wp_kdf_kbkdf_derive"); + /* SP800-108 L is a 32-bit bit-length, so cap output at what it can hold. */ + if (keyLen > 0x1FFFFFFFU) { + return 0; + } + if (!wolfssl_prov_is_running()) { ok = 0; } diff --git a/src/wp_kdf_kmgmt.c b/src/wp_kdf_kmgmt.c index 69a2420a..c208a6a1 100644 --- a/src/wp_kdf_kmgmt.c +++ b/src/wp_kdf_kmgmt.c @@ -126,10 +126,14 @@ void wp_kdf_free(wp_Kdf* kdf) int rc; rc = wc_LockMutex(&kdf->mutex); - cnt = --kdf->refCnt; if (rc == 0) { + cnt = --kdf->refCnt; wc_UnLockMutex(&kdf->mutex); } + else { + /* Cannot safely decrement without the lock; keep the object. */ + cnt = kdf->refCnt; + } #else cnt = --kdf->refCnt; #endif diff --git a/src/wp_logging.c b/src/wp_logging.c index cad93e44..74fc0155 100644 --- a/src/wp_logging.c +++ b/src/wp_logging.c @@ -454,7 +454,7 @@ void WOLFPROV_BUFFER(int component, const unsigned char* buffer, } } - wolfprovider_msg(component, WP_LOG_LEVEL_VERBOSE, line); + wolfprovider_msg(component, WP_LOG_LEVEL_VERBOSE, "%s", line); buffer += WOLFPROV_LINE_LEN; buflen -= WOLFPROV_LINE_LEN; } diff --git a/src/wp_mldsa_kmgmt.c b/src/wp_mldsa_kmgmt.c index dea599a7..14eba6c4 100644 --- a/src/wp_mldsa_kmgmt.c +++ b/src/wp_mldsa_kmgmt.c @@ -284,10 +284,14 @@ void wp_mldsa_free(wp_MlDsa* mldsa) int rc; rc = wc_LockMutex(&mldsa->mutex); - cnt = --mldsa->refCnt; if (rc == 0) { + cnt = --mldsa->refCnt; wc_UnLockMutex(&mldsa->mutex); } + else { + /* Cannot safely decrement without the lock; keep the object. */ + cnt = mldsa->refCnt; + } #else cnt = --mldsa->refCnt; #endif @@ -456,6 +460,7 @@ static int wp_mldsa_match(const wp_MlDsa* a, const wp_MlDsa* b, int selection) word32 lenB; word32 allocA = 0; word32 allocB = 0; + int checked = 0; WOLFPROV_ENTER(WP_LOG_COMP_PQC, "wp_mldsa_match"); @@ -467,7 +472,14 @@ static int wp_mldsa_match(const wp_MlDsa* a, const wp_MlDsa* b, int selection) WOLFPROV_LEAVE(WP_LOG_COMP_PQC, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), 0); return 0; } - if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) { + /* Presence mismatch fails; both-present compares; neither-present skips. */ + if (((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) && + (a->hasPub != b->hasPub)) { + ok = 0; + } + if (ok && ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) && + a->hasPub && b->hasPub) { + checked = 1; lenA = a->data->pubKeySize; lenB = b->data->pubKeySize; bufA = (unsigned char*)OPENSSL_malloc(lenA); @@ -495,7 +507,13 @@ static int wp_mldsa_match(const wp_MlDsa* a, const wp_MlDsa* b, int selection) bufA = NULL; bufB = NULL; } - if (ok && ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)) { + if (ok && ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) && + (a->hasPriv != b->hasPriv)) { + ok = 0; + } + if (ok && ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) && + a->hasPriv && b->hasPriv) { + checked = 1; allocA = a->data->privKeySize; allocB = b->data->privKeySize; lenA = allocA; @@ -517,13 +535,19 @@ static int wp_mldsa_match(const wp_MlDsa* a, const wp_MlDsa* b, int selection) ok = 0; } } - if (ok && ((lenA != lenB) || (XMEMCMP(bufA, bufB, lenA) != 0))) { + if (ok && ((lenA != lenB) || (CRYPTO_memcmp(bufA, bufB, lenA) != 0))) { ok = 0; } /* Zero full allocations even if export truncated the out lengths. */ OPENSSL_clear_free(bufA, allocA); OPENSSL_clear_free(bufB, allocB); } + /* A public/private selection with no component present in both is not a match. */ + if (ok && !checked && + ((selection & (OSSL_KEYMGMT_SELECT_PUBLIC_KEY | + OSSL_KEYMGMT_SELECT_PRIVATE_KEY)) != 0)) { + ok = 0; + } WOLFPROV_LEAVE(WP_LOG_COMP_PQC, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok); return ok; } @@ -1554,6 +1578,7 @@ static int wp_mldsa_encode(wp_MlDsaEncDecCtx* ctx, OSSL_CORE_BIO* cBio, OPENSSL_free(pemData); } OPENSSL_free(cipherInfo); + BIO_free(out); WOLFPROV_LEAVE(WP_LOG_COMP_PQC, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok); return ok; } diff --git a/src/wp_mldsa_sig.c b/src/wp_mldsa_sig.c index 5b01912b..59380a1f 100644 --- a/src/wp_mldsa_sig.c +++ b/src/wp_mldsa_sig.c @@ -442,7 +442,7 @@ static int wp_mldsa_sign(wp_MlDsaSigCtx* ctx, unsigned char* sig, } /* wolfSSL's ML-DSA API takes a 32-bit message length. Reject >4 GiB * messages explicitly rather than silently truncating. */ - if (ok && (msgLen > 0xFFFFFFFFU)) { + if (ok && (!WP_FITS_WORD32(msgLen))) { ok = 0; } if (ok) { @@ -530,7 +530,7 @@ static int wp_mldsa_verify(wp_MlDsaSigCtx* ctx, const unsigned char* sig, } /* wolfSSL's ML-DSA API takes 32-bit lengths. Reject oversize inputs * explicitly rather than silently truncating. */ - if ((sigLen > 0xFFFFFFFFU) || (msgLen > 0xFFFFFFFFU)) { + if ((!WP_FITS_WORD32(sigLen)) || (!WP_FITS_WORD32(msgLen))) { WOLFPROV_LEAVE(WP_LOG_COMP_PQC, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), 0); return 0; } @@ -744,7 +744,7 @@ static int wp_mldsa_stream_verify_final(wp_MlDsaSigCtx* ctx, int res = 0; unsigned char mu[MLDSA_MU_SZ]; - if ((ctx->mldsa == NULL) || (sig == NULL) || (sigLen > 0xFFFFFFFFU)) { + if ((ctx->mldsa == NULL) || (sig == NULL) || (!WP_FITS_WORD32(sigLen))) { return 0; } if (!wp_mldsa_mu_ensure(ctx)) { @@ -888,7 +888,7 @@ static int wp_mldsa_verify_prehash(wp_MlDsaSigCtx* ctx, int digestLen; unsigned char digest[WC_MAX_DIGEST_SIZE]; - if ((ctx->mldsa == NULL) || (sig == NULL) || (sigLen > 0xFFFFFFFFU)) { + if ((ctx->mldsa == NULL) || (sig == NULL) || (!WP_FITS_WORD32(sigLen))) { return 0; } digestLen = wc_HashGetDigestSize(ctx->hashType); @@ -985,7 +985,7 @@ static int wp_mldsa_digest_signverify_update(wp_MlDsaSigCtx* ctx, WOLFPROV_LEAVE(WP_LOG_COMP_PQC, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), 0); return 0; } - if (dataLen > 0xFFFFFFFFU) { + if (!WP_FITS_WORD32(dataLen)) { ok = 0; } else if (ctx->hashType != WC_HASH_TYPE_NONE) { diff --git a/src/wp_params.c b/src/wp_params.c index 59506a63..9ac19417 100644 --- a/src/wp_params.c +++ b/src/wp_params.c @@ -247,7 +247,13 @@ int wp_params_get_digest(const OSSL_PARAM* params, char* name, const char* mdProps = NULL; if (name != NULL) { - XMEMCPY(name, mdName, XSTRLEN(mdName) + 1); + size_t nameLen = XSTRLEN(mdName); + + if (nameLen >= WP_MAX_MD_NAME_SIZE) { + nameLen = WP_MAX_MD_NAME_SIZE - 1; + } + XMEMCPY(name, mdName, nameLen); + name[nameLen] = '\0'; } if (ok && (type != NULL) && (!wp_params_get_utf8_string_ptr(params, OSSL_ALG_PARAM_PROPERTIES, &mdProps))) { diff --git a/src/wp_pbkdf2.c b/src/wp_pbkdf2.c index 2458e645..8b6a37ab 100644 --- a/src/wp_pbkdf2.c +++ b/src/wp_pbkdf2.c @@ -298,10 +298,14 @@ static int wp_kdf_pbkdf2_derive(wp_Pbkdf2Ctx* ctx, unsigned char* key, } /* wc_PBKDF2_ex fails when ctx->iterations is 0. */ - /* Reject iteration counts that would truncate in the int cast below. */ + /* Reject values that would truncate in the int casts below. */ if (ok && (ctx->iterations > (uint64_t)INT_MAX)) { ok = 0; } + if (ok && ((ctx->passwordSz > (size_t)INT_MAX) || + (ctx->saltSz > (size_t)INT_MAX) || (keyLen > (size_t)INT_MAX))) { + ok = 0; + } if (ok) { int rc; diff --git a/src/wp_rsa_asym.c b/src/wp_rsa_asym.c index 364c1494..9fd1a035 100644 --- a/src/wp_rsa_asym.c +++ b/src/wp_rsa_asym.c @@ -293,7 +293,14 @@ static int wp_rsaa_encrypt(wp_RsaAsymCtx* ctx, unsigned char* out, WOLFPROV_ENTER(WP_LOG_COMP_RSA, "wp_rsaa_encrypt"); - if (!wolfssl_prov_is_running()) { + if ((!WP_FITS_WORD32(inLen)) || + ((outSize != (size_t)-1) && (!WP_FITS_WORD32(outSize)))) { + ok = 0; + } + if (!ok) { + /* Length guard already failed; skip the operation. */ + } + else if (!wolfssl_prov_is_running()) { ok = 0; } else if (!wp_rsa_check_key_size(ctx->rsa, 1)) { @@ -403,7 +410,14 @@ static int wp_rsaa_decrypt(wp_RsaAsymCtx* ctx, unsigned char* out, WOLFPROV_ENTER(WP_LOG_COMP_RSA, "wp_rsaa_decrypt"); - if (!wolfssl_prov_is_running()) { + if ((!WP_FITS_WORD32(inLen)) || + ((outSize != (size_t)-1) && (!WP_FITS_WORD32(outSize)))) { + ok = 0; + } + if (!ok) { + /* Length guard already failed; skip the operation. */ + } + else if (!wolfssl_prov_is_running()) { ok = 0; } else if (!wp_rsa_check_key_size(ctx->rsa, 1)) { @@ -419,17 +433,26 @@ static int wp_rsaa_decrypt(wp_RsaAsymCtx* ctx, unsigned char* out, } else { int rc = 0; +#ifdef WC_RSA_BLINDING + int locked = 0; +#endif if (outSize == (size_t)-1) { outSize = *outLen; } #ifdef WC_RSA_BLINDING - /* TODO: not thread safe */ - rc = wc_RsaSetRNG(wp_rsa_get_key(ctx->rsa), &ctx->rng); - if (rc != 0) { - WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_RsaSetRNG", rc); + /* Fail closed if the key mutex can't be held for the shared-key RNG. */ + if (wp_lock(wp_rsa_get_mutex(ctx->rsa)) != 1) { ok = 0; } + else { + locked = 1; + rc = wc_RsaSetRNG(wp_rsa_get_key(ctx->rsa), &ctx->rng); + if (rc != 0) { + WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_RsaSetRNG", rc); + ok = 0; + } + } if (!ok) { } else @@ -524,7 +547,10 @@ static int wp_rsaa_decrypt(wp_RsaAsymCtx* ctx, unsigned char* out, ok = 0; } #ifdef WC_RSA_BLINDING - wc_RsaSetRNG(wp_rsa_get_key(ctx->rsa), NULL); + if (locked) { + wc_RsaSetRNG(wp_rsa_get_key(ctx->rsa), NULL); + wp_unlock(wp_rsa_get_mutex(ctx->rsa)); + } #endif if (ok) { *outLen = rc; diff --git a/src/wp_rsa_kem.c b/src/wp_rsa_kem.c index 10f81eef..31dbfc8b 100644 --- a/src/wp_rsa_kem.c +++ b/src/wp_rsa_kem.c @@ -51,6 +51,9 @@ typedef struct wp_RsaKemCtx { /** Type of pperation being performed. */ int op; + + /** EVP operation (encapsulate/decapsulate) this ctx was initialized for. */ + int operation; } wp_RsaKemCtx; @@ -127,6 +130,7 @@ static wp_RsaKemCtx* wp_rsakem_ctx_dup(wp_RsaKemCtx* srcCtx) if (ok) { dstCtx->rsa = srcCtx->rsa; dstCtx->op = srcCtx->op; + dstCtx->operation = srcCtx->operation; } if (!ok) { @@ -153,8 +157,7 @@ static int wp_rsakem_init(wp_RsaKemCtx* ctx, wp_Rsa* rsa, { int ok = 1; - /* TODO: check key type and size with operation. */ - (void)operation; + ctx->operation = operation; WP_CHECK_FIPS_ALGO(WP_CAST_ALGO_RSA); @@ -391,6 +394,10 @@ static int wp_rsakem_encapsulate(wp_RsaKemCtx* ctx, unsigned char* out, WOLFPROV_ENTER(WP_LOG_COMP_RSA, "wp_rsakem_encapsulate"); + if (ctx->operation != EVP_PKEY_OP_ENCAPSULATE) { + return 0; + } + switch (ctx->op) { case WP_RSA_KEM_OP_RSASVE: ok = wp_rsasve_generate(ctx, out, outlen, secret, secretlen); @@ -486,6 +493,10 @@ static int wp_rsakem_decapsulate(wp_RsaKemCtx* ctx, unsigned char* out, WOLFPROV_ENTER(WP_LOG_COMP_RSA, "wp_rsakem_decapsulate"); + if (ctx->operation != EVP_PKEY_OP_DECAPSULATE) { + return 0; + } + switch (ctx->op) { case WP_RSA_KEM_OP_RSASVE: ok = wp_rsasve_recover(ctx, out, outlen, in, inlen); diff --git a/src/wp_rsa_kmgmt.c b/src/wp_rsa_kmgmt.c index c7146f65..c87c56bc 100644 --- a/src/wp_rsa_kmgmt.c +++ b/src/wp_rsa_kmgmt.c @@ -530,10 +530,14 @@ void wp_rsa_free(wp_Rsa* rsa) int rc; rc = wc_LockMutex(&rsa->mutex); - cnt = --rsa->refCnt; if (rc == 0) { + cnt = --rsa->refCnt; wc_UnLockMutex(&rsa->mutex); } + else { + /* Cannot safely decrement without the lock; keep the object. */ + cnt = rsa->refCnt; + } #else cnt = --rsa->refCnt; #endif @@ -915,7 +919,7 @@ static int wp_rsa_get_params_key_data(wp_Rsa* rsa, OSSL_PARAM params[]) size_t oLen; mp_int* mp = (mp_int*)(((byte*)&rsa->key) + wp_rsa_offset[i]); oLen = mp_unsigned_bin_size(mp); - if (oLen > p->data_size) { + if ((p->data != NULL) && (oLen > p->data_size)) { ok = 0; } if (ok && (p->data != NULL) && @@ -1605,7 +1609,7 @@ static int wp_rsa_import(wp_Rsa* rsa, int selection, const OSSL_PARAM params[]) if (ok) { rsa->bits = mp_count_bits(&rsa->key.n); rsa->hasPub = importPub; - rsa->hasPriv = importPriv; + rsa->hasPriv = importPriv && !mp_iszero((mp_int*)&rsa->key.d); } WOLFPROV_LEAVE(WP_LOG_COMP_RSA, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok); diff --git a/src/wp_rsa_sig.c b/src/wp_rsa_sig.c index 962c50ac..4568eb92 100644 --- a/src/wp_rsa_sig.c +++ b/src/wp_rsa_sig.c @@ -647,7 +647,10 @@ static int wp_rsa_sign_pkcs1(wp_RsaSigCtx* ctx, unsigned char* sig, } } if (ok) { - if (wp_lock(wp_rsa_get_mutex(ctx->rsa)) != 1) { + if ((!WP_FITS_WORD32(tbsLen)) || (!WP_FITS_WORD32(sigSize))) { + ok = 0; + } + if (ok && (wp_lock(wp_rsa_get_mutex(ctx->rsa)) != 1)) { ok = 0; } if (ok) { @@ -703,7 +706,10 @@ static int wp_rsa_sign_pss(wp_RsaSigCtx* ctx, unsigned char* sig, WOLFPROV_ENTER(WP_LOG_COMP_RSA, "wp_rsa_sign_pss"); if (ok) { - if (wp_lock(wp_rsa_get_mutex(ctx->rsa)) != 1) { + if ((!WP_FITS_WORD32(tbsLen)) || (!WP_FITS_WORD32(sigSize))) { + ok = 0; + } + if (ok && (wp_lock(wp_rsa_get_mutex(ctx->rsa)) != 1)) { ok = 0; } if (ok) { @@ -880,6 +886,9 @@ static int wp_rsa_sign_x931(wp_RsaSigCtx* ctx, unsigned char* sig, if (paddedSz <= 0) { ok = 0; } + if (ok && (tbsLen >= (size_t)paddedSz)) { + ok = 0; + } if (ok) { tbuf = (unsigned char *)OPENSSL_malloc(paddedSz); if (tbuf == NULL) { @@ -907,6 +916,9 @@ static int wp_rsa_sign_x931(wp_RsaSigCtx* ctx, unsigned char* sig, ok = 0; } } + if (ok && (!WP_FITS_WORD32(sigSize))) { + ok = 0; + } if (ok) { word32 len = (word32)sigSize; if (wp_lock(wp_rsa_get_mutex(ctx->rsa)) != 1) { @@ -1078,6 +1090,10 @@ static int wp_rsa_verify_pkcs1(wp_RsaSigCtx* ctx, const unsigned char* sig, WOLFPROV_ENTER(WP_LOG_COMP_RSA, "wp_rsa_verify_pkcs1"); + if (!WP_FITS_WORD32(sigLen)) { + return 0; + } + rc = wc_RsaSSL_Verify(sig, (word32)sigLen, decryptedSig, (word32)sigLen, wp_rsa_get_key(ctx->rsa)); if (rc < 0) { @@ -1141,10 +1157,14 @@ static int wp_rsa_verify_pss(wp_RsaSigCtx* ctx, const unsigned char* sig, WOLFPROV_ENTER(WP_LOG_COMP_RSA, "wp_rsa_verify_pss"); + if (!WP_FITS_WORD32(sigLen)) { + ok = 0; + } + #if LIBWOLFSSL_VERSION_HEX >= 0x05007004 - if (ctx->hash.type == WC_HASH_TYPE_NONE) + if (ok && (ctx->hash.type == WC_HASH_TYPE_NONE)) #else - if (ctx->hashType == WC_HASH_TYPE_NONE) + if (ok && (ctx->hashType == WC_HASH_TYPE_NONE)) #endif { ok = wp_rsa_setup_md(ctx, WP_RSA_DEFAULT_MD, NULL, EVP_PKEY_OP_VERIFY); @@ -1512,6 +1532,9 @@ static int wp_rsa_verify_recover(wp_RsaSigCtx* ctx, unsigned char* rout, ok = 0; } + if (ok && ((!WP_FITS_WORD32(sigLen)) || (!WP_FITS_WORD32(routsize)))) { + ok = 0; + } if (ok) { rc = wc_RsaSSL_Verify(sig, (word32)sigLen, rout, (word32)routsize, wp_rsa_get_key(ctx->rsa)); @@ -1571,6 +1594,10 @@ static int wp_rsa_digest_signverify_update(wp_RsaSigCtx* ctx, WOLFPROV_ENTER(WP_LOG_COMP_RSA, "wp_rsa_digest_signverify_update"); + if (!WP_FITS_WORD32(dataLen)) { + return 0; + } + int rc = wc_HashUpdate(&ctx->hash, #if LIBWOLFSSL_VERSION_HEX >= 0x05007004 ctx->hash.type, @@ -1949,7 +1976,10 @@ static int wp_rsa_get_ctx_params(wp_RsaSigCtx* ctx, OSSL_PARAM* params) if (ok) { p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_ALGORITHM_ID); if (p != NULL) { - ok = wp_rsa_get_alg_id(ctx, p); + /* An unencodable digest must not fail the whole get; mark it empty. */ + if (!wp_rsa_get_alg_id(ctx, p)) { + p->return_size = 0; + } } } diff --git a/src/wp_tls1_prf.c b/src/wp_tls1_prf.c index 6d99db77..9c3b23aa 100644 --- a/src/wp_tls1_prf.c +++ b/src/wp_tls1_prf.c @@ -166,6 +166,9 @@ static int wp_kdf_tls1_prf_derive(wp_Tls1Prf_Ctx* ctx, unsigned char* key, if (ok && (keyLen == 0)) { ok = 0; } + if (ok && (!WP_FITS_WORD32(keyLen))) { + ok = 0; + } if (ok) { int rc; diff --git a/src/wp_wolfprov.c b/src/wp_wolfprov.c index e207ce17..675ab62f 100644 --- a/src/wp_wolfprov.c +++ b/src/wp_wolfprov.c @@ -641,11 +641,11 @@ static const OSSL_ALGORITHM wolfprov_keymgmt[] = { #ifdef WP_HAVE_ED25519 { WP_NAMES_ED25519, WOLFPROV_PROPERTIES, wp_ed25519_keymgmt_functions, - "X25519" }, + "ED25519" }, #endif #ifdef WP_HAVE_ED448 { WP_NAMES_ED448, WOLFPROV_PROPERTIES, wp_ed448_keymgmt_functions, - "X448" }, + "ED448" }, #endif #ifdef WP_HAVE_DH diff --git a/test/test_rsa.c b/test/test_rsa.c index b64bb20b..d85edaf7 100644 --- a/test/test_rsa.c +++ b/test/test_rsa.c @@ -3089,4 +3089,93 @@ int test_rsa_key_integrity(void* data) return err; } +static int test_rsa_alg_id_get(EVP_PKEY* pkey, OSSL_LIB_CTX* libCtx, + const char* mdName, unsigned char* aid, size_t aidSize, size_t* aidLen, + int* initRet, int* getRet) +{ + int err; + EVP_MD_CTX* mdCtx = NULL; + EVP_PKEY_CTX* pkeyCtx = NULL; + OSSL_PARAM params[2]; + + *aidLen = 0; + *initRet = 0; + *getRet = 0; + + err = (mdCtx = EVP_MD_CTX_new()) == NULL; + if (err == 0) { + *initRet = EVP_DigestSignInit_ex(mdCtx, &pkeyCtx, mdName, libCtx, NULL, + pkey, NULL); + } + if ((err == 0) && (*initRet == 1)) { + params[0] = OSSL_PARAM_construct_octet_string( + OSSL_SIGNATURE_PARAM_ALGORITHM_ID, aid, aidSize); + params[1] = OSSL_PARAM_construct_end(); + *getRet = EVP_PKEY_CTX_get_params(pkeyCtx, params); + if (*getRet == 1) { + *aidLen = params[0].return_size; + } + } + + EVP_MD_CTX_free(mdCtx); + return err; +} + +int test_rsa_sig_alg_id(void *data) +{ + int err = 0; + EVP_PKEY *pkey = NULL; + const unsigned char *p = rsa_key_der_2048; + const char* mds[] = { "SHA256", "SHA384", "SHA512" }; + unsigned char wpAid[64]; + unsigned char osslAid[64]; + size_t wpLen = 0; + size_t osslLen = 0; + int wpInit = 0; + int osslInit = 0; + int wpGet = 0; + int osslGet = 0; + size_t i; + + (void)data; + + pkey = d2i_PrivateKey(EVP_PKEY_RSA, NULL, &p, sizeof(rsa_key_der_2048)); + err = pkey == NULL; + + for (i = 0; (err == 0) && (i < sizeof(mds) / sizeof(mds[0])); i++) { + PRINT_MSG("RSA PKCS#1 alg-id A/B against OpenSSL"); + err = test_rsa_alg_id_get(pkey, wpLibCtx, mds[i], wpAid, sizeof(wpAid), + &wpLen, &wpInit, &wpGet); + if (err == 0) { + err = test_rsa_alg_id_get(pkey, osslLibCtx, mds[i], osslAid, + sizeof(osslAid), &osslLen, &osslInit, &osslGet); + } + if ((err == 0) && ((wpInit != 1) || (osslInit != 1) || + (wpGet != 1) || (osslGet != 1))) { + err = 1; + } + if ((err == 0) && ((wpLen == 0) || (wpLen != osslLen) || + (memcmp(wpAid, osslAid, wpLen) != 0))) { + err = 1; + } + } + + /* An unencodable digest must leave the alg-id empty without failing the + * whole get_ctx_params call. Skip when the digest is unavailable. */ + if (err == 0) { + err = test_rsa_alg_id_get(pkey, wpLibCtx, "SHA-1", wpAid, + sizeof(wpAid), &wpLen, &wpInit, &wpGet); + } + if ((err == 0) && (wpInit == 1)) { + PRINT_MSG("RSA PKCS#1 unencodable alg-id stays empty"); + if ((wpGet != 1) || (wpLen != 0)) { + err = 1; + } + } + + EVP_PKEY_free(pkey); + + return err; +} + #endif /* WP_HAVE_RSA */ diff --git a/test/unit.c b/test/unit.c index 84aab20c..90391a15 100644 --- a/test/unit.c +++ b/test/unit.c @@ -339,6 +339,7 @@ TEST_CASE test_case[] = { #ifndef WOLFPROV_QUICKTEST TEST_DECL(test_rsa_get_params, NULL), #endif + TEST_DECL(test_rsa_sig_alg_id, NULL), TEST_DECL(test_rsa_pss_salt, NULL), TEST_DECL(test_rsa_pss_restrictions, NULL), TEST_DECL(test_rsa_load_key, NULL), diff --git a/test/unit.h b/test/unit.h index 9e89c1f0..314a8a7b 100644 --- a/test/unit.h +++ b/test/unit.h @@ -300,6 +300,7 @@ int test_rsa_enc_dec_nopad(void *data); int test_rsa_pkey_keygen(void *data); int test_rsa_pkey_invalid_key_size(void *data); int test_rsa_get_params(void *data); +int test_rsa_sig_alg_id(void *data); int test_rsa_pss_salt(void *date); int test_rsa_pss_restrictions(void *data);