diff --git a/src/wp_aes_aead.c b/src/wp_aes_aead.c index 318497dc..c7f44277 100644 --- a/src/wp_aes_aead.c +++ b/src/wp_aes_aead.c @@ -1688,6 +1688,10 @@ static void *wp_aes_gcm_newctx(WOLFPROV_CTX* provCtx, size_t keyBits) */ static void wp_aes_gcm_freectx(wp_AeadCtx* ctx) { + OPENSSL_free(ctx->aad); +#if defined(WP_HAVE_AESGCM) && !defined(WOLFSSL_AESGCM_STREAM) + OPENSSL_free(ctx->in); +#endif wc_AesFree(&ctx->aes); OPENSSL_free(ctx); } diff --git a/src/wp_aes_block.c b/src/wp_aes_block.c index 46a27242..fbf5aa6a 100644 --- a/src/wp_aes_block.c +++ b/src/wp_aes_block.c @@ -866,21 +866,22 @@ static int wp_aes_block_final_dec(wp_AesBlockCtx* ctx, unsigned char *out, if (ok && ctx->pad) { unsigned char pad; + unsigned char invalid; + unsigned char i; pad = ctx->buf[AES_BLOCK_SIZE - 1]; - if ((pad == 0) || (pad > AES_BLOCK_SIZE)) { + invalid = wp_ct_byte_mask_eq(pad, 0) | + ~wp_ct_int_mask_gte(AES_BLOCK_SIZE, (int)pad); + for (i = 0; i < AES_BLOCK_SIZE; i++) { + unsigned char mask = wp_ct_int_mask_gte((int)i, + AES_BLOCK_SIZE - (int)pad); + invalid |= mask & wp_ct_byte_mask_ne(ctx->buf[i], pad); + } + if (invalid) { ok = 0; } - if (ok) { - unsigned char len = AES_BLOCK_SIZE; - unsigned char i; - - for (i = 0; i < pad; i++) { - if (ctx->buf[--len] != pad) { - return 0; - } - } - ctx->bufSz = len; + else { + ctx->bufSz = AES_BLOCK_SIZE - pad; } } diff --git a/src/wp_cmac.c b/src/wp_cmac.c index f8a01c47..a04a8664 100644 --- a/src/wp_cmac.c +++ b/src/wp_cmac.c @@ -162,11 +162,16 @@ static wp_CmacCtx* wp_cmac_dup(wp_CmacCtx* src) dst = wp_cmac_new(NULL); } if (dst != NULL) { - *dst = *src; - dst->keyLen = 0; - - if ((src->keyLen != 0) && - (!wp_cmac_set_key(dst, src->key, src->keyLen, 0))) { + /* Copy the entire context to preserve in-progress CMAC state. */ + XMEMCPY(&dst->cmac, &src->cmac, sizeof(Cmac)); + dst->type = src->type; + dst->size = src->size; + dst->expKeySize = src->expKeySize; + if (src->keyLen <= sizeof(dst->key)) { + XMEMCPY(dst->key, src->key, src->keyLen); + dst->keyLen = src->keyLen; + } + else { wp_cmac_free(dst); dst = NULL; } diff --git a/src/wp_des.c b/src/wp_des.c index 561f1dea..1b6096dc 100644 --- a/src/wp_des.c +++ b/src/wp_des.c @@ -418,10 +418,15 @@ static int wp_des3_block_update(wp_Des3BlockCtx *ctx, unsigned char *out, int i; unsigned char off = inLen % DES_BLOCK_SIZE; unsigned char pad = DES_BLOCK_SIZE - off - 1; - for (i = off; i < DES_BLOCK_SIZE; i++) { - out[inLen - off + i] = pad; + if (outSize < inLen + pad + 1) { + ok = 0; + } + if (ok) { + for (i = off; i < DES_BLOCK_SIZE; i++) { + out[inLen - off + i] = pad; + } + inLen += pad + 1; } - inLen += pad + 1; } if (ctx->bufSz != 0) { size_t len = DES_BLOCK_SIZE - ctx->bufSz; @@ -578,21 +583,22 @@ static int wp_des3_block_final_dec(wp_Des3BlockCtx* ctx, unsigned char *out, if (ok && ctx->pad) { unsigned char pad; + unsigned char invalid; + unsigned char i; pad = ctx->buf[DES_BLOCK_SIZE - 1]; - if ((pad == 0) || (pad > DES_BLOCK_SIZE)) { + invalid = wp_ct_byte_mask_eq(pad, 0) | + ~wp_ct_int_mask_gte(DES_BLOCK_SIZE, (int)pad); + for (i = 0; i < DES_BLOCK_SIZE; i++) { + unsigned char mask = wp_ct_int_mask_gte((int)i, + DES_BLOCK_SIZE - (int)pad); + invalid |= mask & wp_ct_byte_mask_ne(ctx->buf[i], pad); + } + if (invalid) { ok = 0; } - if (ok) { - unsigned char len = DES_BLOCK_SIZE; - unsigned char i; - - for (i = 0; i < pad; i++) { - if (ctx->buf[--len] != pad) { - return 0; - } - } - ctx->bufSz = len; + else { + ctx->bufSz = DES_BLOCK_SIZE - pad; } } diff --git a/src/wp_dh_kmgmt.c b/src/wp_dh_kmgmt.c index 16173f3e..3956e1fd 100644 --- a/src/wp_dh_kmgmt.c +++ b/src/wp_dh_kmgmt.c @@ -453,7 +453,7 @@ void wp_dh_free(wp_Dh* dh) if (cnt == 0) { /* No more references to this object. */ OPENSSL_free(dh->pub); - OPENSSL_free(dh->priv); + OPENSSL_clear_free(dh->priv, dh->privSz); #ifndef WP_SINGLE_THREADED wc_FreeMutex(&dh->mutex); #endif @@ -730,6 +730,9 @@ static int wp_dh_get_params_encoded_public_key(wp_Dh* dh, OSSL_PARAM params[]) if (p->data_size < outLen) { ok = 0; } + if (ok && (dh->pubSz > outLen)) { + ok = 0; + } if (ok) { unsigned char* data = p->data; size_t padSz = outLen - dh->pubSz; @@ -863,16 +866,6 @@ static int wp_dh_get_params(wp_Dh* dh, OSSL_PARAM params[]) } } } - if (ok) { - /* Only call if we haven't already handled OSSL_PKEY_PARAM_PRIV_KEY */ - p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PRIV_KEY); - if (p == NULL || p->data != NULL) { - if (!wp_params_set_octet_string_be(params, OSSL_PKEY_PARAM_PRIV_KEY, - dh->priv, dh->privSz)) { - ok = 0; - } - } - } if (ok && (!wp_dh_get_params_encoded_public_key(dh, params))) { ok = 0; } diff --git a/src/wp_drbg.c b/src/wp_drbg.c index daf4e565..311f8a38 100644 --- a/src/wp_drbg.c +++ b/src/wp_drbg.c @@ -334,29 +334,53 @@ static int wp_drbg_reseed(wp_DrbgCtx* ctx, int predResist, const unsigned char* addIn, size_t addInLen) { int ok = 1; + int rc; + unsigned char *seed = NULL; + size_t seedLen = 0; WOLFPROV_ENTER(WP_LOG_COMP_RNG, "wp_drbg_reseed"); -#if 0 - /* Calling Hash_DRBG_Instantiate would be better. */ - int rc; - rc = wc_RNG_DRBG_Reseed(ctx->rng, entropy, entropyLen); - if (rc != 0) { - ok = 0; + /* If no entropy provided, get fresh entropy from the OS source. */ + if (entropy == NULL || entropyLen == 0) { + seedLen = 48; + seed = OPENSSL_malloc(seedLen); + if (seed == NULL) { + ok = 0; + } + if (ok) { + OS_Seed osSeed; + rc = wc_GenerateSeed(&osSeed, seed, (word32)seedLen); + if (rc != 0) { + ok = 0; + } + else { + entropy = seed; + entropyLen = seedLen; + } + } } - if (ok && (addInLen > 0)) { - rc = wc_RNG_DRBG_Reseed(ctx->rng, addIn, addInLen); + + if (ok && entropy != NULL && entropyLen > 0) { + rc = wc_RNG_DRBG_Reseed(ctx->rng, entropy, (word32)entropyLen); if (rc != 0) { + WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_COMP_RNG, + "wc_RNG_DRBG_Reseed", rc); + ok = 0; + } + } + if (ok && (addInLen > 0) && (addIn != NULL)) { + rc = wc_RNG_DRBG_Reseed(ctx->rng, addIn, (word32)addInLen); + if (rc != 0) { + WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_COMP_RNG, + "wc_RNG_DRBG_Reseed", rc); ok = 0; } } -#else - (void)ctx; - (void)entropy; - (void)entropyLen; - (void)addIn; - (void)addInLen; -#endif + + /* Securely clear and free locally allocated seed buffer. */ + if (seed != NULL) { + OPENSSL_clear_free(seed, seedLen); + } (void)predResist; @@ -388,6 +412,7 @@ static int wp_drbg_enable_locking(wp_DrbgCtx* ctx) if (rc != 0) { WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_COMP_RNG, "wc_InitMutex", rc); OPENSSL_free(ctx->mutex); + ctx->mutex = NULL; ok = 0; } } @@ -547,11 +572,16 @@ static int wp_drbg_set_ctx_params(wp_DrbgCtx* ctx, const OSSL_PARAM params[]) */ static int wp_drbg_verify_zeroization(wp_DrbgCtx* ctx) { + int ok; + WOLFPROV_ENTER(WP_LOG_COMP_RNG, "wp_drbg_verify_zeroization"); - (void)ctx; - WOLFPROV_LEAVE(WP_LOG_COMP_RNG, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), 1); - return 1; + /* After uninstantiate, ctx->rng is freed (with internal state zeroized + * by wolfSSL) and set to NULL. Verify that cleanup occurred. */ + ok = (ctx->rng == NULL); + + WOLFPROV_LEAVE(WP_LOG_COMP_RNG, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok); + return ok; } /** diff --git a/src/wp_ecx_kmgmt.c b/src/wp_ecx_kmgmt.c index 2cbaa839..811a0a33 100644 --- a/src/wp_ecx_kmgmt.c +++ b/src/wp_ecx_kmgmt.c @@ -356,16 +356,46 @@ static wp_Ecx* wp_ecx_dup(const wp_Ecx* src, int selection) { wp_Ecx* dst = NULL; - (void)selection; if (wolfssl_prov_is_running()) { /* Create a new ecx object. */ dst = wp_ecx_new(src->provCtx, src->data); } if (dst != NULL) { - XMEMCPY(&dst->key, &src->key, sizeof(src->key)); dst->includePublic = src->includePublic; - dst->hasPub = src->hasPub; - dst->hasPriv = src->hasPriv; + + /* Copy the full key union to preserve internal wolfSSL state. + * Private material is zeroized below if not selected. */ + XMEMCPY(&dst->key, &src->key, sizeof(src->key)); + + /* Set public key flag if available and requested. */ + if (src->hasPub && + ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)) { + dst->hasPub = 1; + } + /* Set private key flag if available and requested. */ + if (src->hasPriv && + ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)) { + dst->hasPriv = 1; + dst->clamped = src->clamped; + XMEMCPY(dst->unclamped, src->unclamped, sizeof(src->unclamped)); + } + else { + /* Private key not selected — re-import only public key to + * ensure no private material remains in the dst key object. */ + if (dst->hasPub) { + byte buf[64]; + word32 len = (word32)sizeof(buf); + int rc = (*src->data->exportPub)((void*)&src->key, buf, &len, + ECX_LITTLE_ENDIAN); + if (rc == 0) { + /* Re-init key and import only public part. */ + (*dst->data->freeKey)((void*)&dst->key); + (*dst->data->initKey)((void*)&dst->key); + (*dst->data->importPub)(buf, len, (void*)&dst->key, + ECX_LITTLE_ENDIAN); + } + } + } } return dst; diff --git a/src/wp_hkdf.c b/src/wp_hkdf.c index 2536a6e7..6dabe2f5 100644 --- a/src/wp_hkdf.c +++ b/src/wp_hkdf.c @@ -716,6 +716,9 @@ static int wp_kdf_tls1_3_derive(wp_HkdfCtx* ctx, unsigned char* key, ok = 0; } } + else { + ok = 0; + } } WOLFPROV_LEAVE(WP_LOG_COMP_HKDF, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok); diff --git a/src/wp_hmac.c b/src/wp_hmac.c index 5ff78ae9..5534c79b 100644 --- a/src/wp_hmac.c +++ b/src/wp_hmac.c @@ -187,12 +187,22 @@ static wp_HmacCtx* wp_hmac_dup(wp_HmacCtx* src) dst = wp_hmac_new(src->provCtx); } if (dst != NULL) { - *dst = *src; - dst->key = NULL; - dst->keyLen = 0; + int ok = 1; - if ((src->key != NULL) && + dst->type = src->type; + dst->size = src->size; + dst->provCtx = src->provCtx; + + /* Copy the Hmac struct directly to preserve in-progress state. + * wc_HmacCopy is not available in all wolfSSL versions. */ + XMEMCPY(&dst->hmac, &src->hmac, sizeof(Hmac)); + + if (ok && (src->key != NULL) && (!wp_hmac_set_key(dst, src->key, src->keyLen, 0))) { + ok = 0; + } + + if (!ok) { wp_hmac_free(dst); dst = NULL; } diff --git a/src/wp_mac_kmgmt.c b/src/wp_mac_kmgmt.c index e942d1b5..92893fac 100644 --- a/src/wp_mac_kmgmt.c +++ b/src/wp_mac_kmgmt.c @@ -223,10 +223,11 @@ void wp_mac_free(wp_Mac* mac) int rc; rc = wc_LockMutex(&mac->mutex); - cnt = --mac->refCnt; - if (rc == 0) { - wc_UnLockMutex(&mac->mutex); + if (rc != 0) { + return; } + cnt = --mac->refCnt; + wc_UnLockMutex(&mac->mutex); #else cnt = --mac->refCnt; #endif @@ -318,6 +319,10 @@ static int wp_mac_has(const wp_Mac* mac, int selection) if (mac == NULL) { ok = 0; } + if (ok && ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)) { + /* MAC keys do not have a public key component. */ + ok = 0; + } if (ok && ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)) { ok &= mac->key != NULL; } @@ -344,11 +349,13 @@ static int wp_mac_match(const wp_Mac* mac1, const wp_Mac* mac2, int selection) if (!wolfssl_prov_is_running()) { ok = 0; } - if (ok && ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) && - (mac1->keyLen != MAX_SIZE_T) && ((mac1->keyLen != mac2->keyLen) || - (XMEMCMP(mac1->key, mac2->key, mac1->keyLen) != 0) || - (XMEMCMP(mac1->cipher, mac2->cipher, WP_MAX_CIPH_NAME_SIZE) != 0))) { - ok = 0; + if (ok && ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)) { + if ((mac1->keyLen == MAX_SIZE_T) || (mac2->keyLen == MAX_SIZE_T) || + (mac1->keyLen != mac2->keyLen) || + (CRYPTO_memcmp(mac1->key, mac2->key, mac1->keyLen) != 0) || + (XMEMCMP(mac1->cipher, mac2->cipher, WP_MAX_CIPH_NAME_SIZE) != 0)) { + ok = 0; + } } WOLFPROV_LEAVE(WP_LOG_COMP_MAC, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok); diff --git a/src/wp_mac_sig.c b/src/wp_mac_sig.c index c1ef17d9..685ff47a 100644 --- a/src/wp_mac_sig.c +++ b/src/wp_mac_sig.c @@ -128,6 +128,7 @@ static void wp_mac_ctx_free(wp_MacSigCtx* ctx) { if (ctx != NULL) { EVP_MAC_CTX_free(ctx->macCtx); + wp_mac_free(ctx->mac); OPENSSL_free(ctx->propQuery); OPENSSL_free(ctx); } @@ -160,6 +161,9 @@ static wp_MacSigCtx* wp_mac_ctx_dup(wp_MacSigCtx* srcCtx) ok = 0; } } + if (ok && !wp_mac_up_ref(srcCtx->mac)) { + ok = 0; + } if (ok) { dstCtx->mac = srcCtx->mac; } @@ -199,8 +203,14 @@ static int wp_mac_digest_sign_init(wp_MacSigCtx *ctx, const char *mdName, if (!wolfssl_prov_is_running()) { ok = 0; } + if (ok && mac != NULL && !wp_mac_up_ref(mac)) { + ok = 0; + } if (ok) { - ctx->mac = mac; + if (mac != NULL) { + wp_mac_free(ctx->mac); + ctx->mac = mac; + } if (!wp_mac_get_private_key(ctx->mac, &priv, &privLen)) { ok = 0; diff --git a/src/wp_params.c b/src/wp_params.c index b52d745c..761123a4 100644 --- a/src/wp_params.c +++ b/src/wp_params.c @@ -208,6 +208,9 @@ void wp_param_set_mp_buf(OSSL_PARAM* p, const char* key, unsigned char* num, for (i = 0; i < nLen; i++) { data[i] = num[nLen - 1 - i]; } +#else + XMEMCPY(data, num, nLen); + (void)i; #endif } @@ -393,6 +396,7 @@ int wp_params_get_bn_be(const OSSL_PARAM* params, const char* key, } #else XMEMCPY(*data, p->data, p->data_size); + *len = p->data_size; #endif } diff --git a/src/wp_rsa_asym.c b/src/wp_rsa_asym.c index 66b20012..78167355 100644 --- a/src/wp_rsa_asym.c +++ b/src/wp_rsa_asym.c @@ -263,7 +263,7 @@ static int wp_rsaa_encrypt_init(wp_RsaAsymCtx* ctx, wp_Rsa* rsa, ok = 0; } else { - ok = wp_rsaa_init(ctx, rsa, params, EVP_PKEY_OP_SIGN); + ok = wp_rsaa_init(ctx, rsa, params, EVP_PKEY_OP_ENCRYPT); } WOLFPROV_LEAVE(WP_LOG_COMP_RSA, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok); diff --git a/src/wp_rsa_kem.c b/src/wp_rsa_kem.c index 424503a2..7ff08507 100644 --- a/src/wp_rsa_kem.c +++ b/src/wp_rsa_kem.c @@ -204,11 +204,27 @@ static int wp_rsakem_decapsulate_init(wp_RsaKemCtx* ctx, wp_Rsa* rsa, static int wp_mp_rand(mp_int* a, int digits, WC_RNG* rng) { - int cnt = digits * sizeof(mp_digit); + int rc; + size_t cnt; + unsigned char* buf; + + if (digits <= 0) { + return BAD_FUNC_ARG; + } - a->used = digits; + cnt = (size_t)digits * sizeof(mp_digit); + buf = (unsigned char*)OPENSSL_malloc(cnt); + if (buf == NULL) { + return MEMORY_E; + } - return wc_RNG_GenerateBlock(rng, (byte*)a->dp, cnt); + rc = wc_RNG_GenerateBlock(rng, buf, (word32)cnt); + if (rc == 0) { + rc = mp_read_unsigned_bin(a, buf, (int)cnt); + } + OPENSSL_clear_free(buf, cnt); + + return rc; } /** @@ -274,6 +290,9 @@ static int wp_rsasve_gen_rand_bytes(wp_RsaKemCtx* ctx, unsigned char* out) } } + mp_clear(&r); + mp_clear(&mod); + WOLFPROV_LEAVE(WP_LOG_COMP_RSA, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok); return ok; } diff --git a/src/wp_rsa_kmgmt.c b/src/wp_rsa_kmgmt.c index 372d99a0..5b766b1c 100644 --- a/src/wp_rsa_kmgmt.c +++ b/src/wp_rsa_kmgmt.c @@ -998,12 +998,38 @@ static int wp_rsa_get_params_pss(wp_RsaPssParams* pss, OSSL_PARAM params[]) ok = 0; } } - /* MGF is default so don't set. */ - if (ok && (pss->mgf != WP_RSA_PSS_MGF_DEF)) { + /* Always export MGF1 digest when requested. Translate wolfSSL-style + * digest names to OpenSSL-style names for interoperability. */ + if (ok) { p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_RSA_MGF1_DIGEST); - if ((p != NULL) && wp_digest_to_ossl_digest(pss->hashType, &osslDigest) - && !OSSL_PARAM_set_utf8_string(p, osslDigest)) { - ok = 0; + if (p != NULL) { + const char* mgfName = NULL; + /* Convert mgf type to OpenSSL name via wp_digest_to_ossl_digest. */ + if (pss->mgf != WP_RSA_PSS_MGF_DEF) { + enum wc_HashType mgfHash = WC_HASH_TYPE_NONE; + switch (pss->mgf) { + case WC_MGF1SHA256: mgfHash = WC_HASH_TYPE_SHA256; break; + case WC_MGF1SHA384: mgfHash = WC_HASH_TYPE_SHA384; break; + case WC_MGF1SHA512: mgfHash = WC_HASH_TYPE_SHA512; break; + default: break; + } + if (mgfHash != WC_HASH_TYPE_NONE) { + if (!wp_digest_to_ossl_digest(mgfHash, &mgfName)) { + ok = 0; + } + } + } + /* Fall back to signing digest if MGF1 not explicitly set. */ + if (ok && mgfName == NULL) { + if (!wp_digest_to_ossl_digest(pss->hashType, &mgfName)) { + ok = 0; + } + } + if (ok && mgfName != NULL) { + if (!OSSL_PARAM_set_utf8_string(p, mgfName)) { + ok = 0; + } + } } } if (ok) { @@ -1607,6 +1633,7 @@ static wp_Rsa* wp_rsa_gen(wp_RsaGenCtx* ctx, OSSL_CALLBACK* cb, void* cbArg) rsa->hasPub = 1; rsa->hasPriv = 1; rsa->pssParams = ctx->pssParams; + rsa->pssDefSet = ctx->pssDefSet; break; } } diff --git a/src/wp_seed_src.c b/src/wp_seed_src.c index c6704c2d..e0028f68 100644 --- a/src/wp_seed_src.c +++ b/src/wp_seed_src.c @@ -479,7 +479,7 @@ static int wp_seed_src_generate(wp_SeedSrcCtx* ctx, unsigned char* out, memcpy(out, buf, outLen); } if (buf != NULL) { - OPENSSL_free(buf); + OPENSSL_clear_free(buf, outLen); } WOLFPROV_LEAVE(WP_LOG_COMP_RNG, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), diff --git a/test/test_cmac.c b/test/test_cmac.c index f4ab49ed..41ae3c89 100644 --- a/test/test_cmac.c +++ b/test/test_cmac.c @@ -257,5 +257,114 @@ int test_cmac_create(void *data) return ret; } +int test_cmac_dup(void *data) +{ + int ret = 0; + EVP_MAC* emac = NULL; + EVP_MAC_CTX* src = NULL; + EVP_MAC_CTX* dup = NULL; + OSSL_PARAM params[3]; + char cipher[] = "AES-256-CBC"; + unsigned char key[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 + }; + unsigned char prefix[] = "dup-prefix"; + unsigned char tailA[] = "-tail-a"; + unsigned char tailB[] = "-tail-b"; + unsigned char msgA[sizeof(prefix) + sizeof(tailA)]; + unsigned char msgB[sizeof(prefix) + sizeof(tailB)]; + unsigned char macA[16]; + unsigned char macB[16]; + unsigned char expA[16]; + unsigned char expB[16]; + size_t macASz = sizeof(macA); + size_t macBSz = sizeof(macB); + int expASz = sizeof(expA); + int expBSz = sizeof(expB); + + (void)data; + + PRINT_MSG("Testing CMAC context dup"); + + /* Build full messages for one-shot expected MAC calculations. */ + memcpy(msgA, prefix, sizeof(prefix)); + memcpy(msgA + sizeof(prefix), tailA, sizeof(tailA)); + memcpy(msgB, prefix, sizeof(prefix)); + memcpy(msgB + sizeof(prefix), tailB, sizeof(tailB)); + + /* Compute expected MACs. */ + ret = test_cmac_gen_mac(wpLibCtx, cipher, key, (int)sizeof(key), + msgA, (int)sizeof(msgA), expA, &expASz); + if (ret != 0) { + PRINT_MSG("Generate expected MAC A failed"); + } + if (ret == 0) { + ret = test_cmac_gen_mac(wpLibCtx, cipher, key, (int)sizeof(key), + msgB, (int)sizeof(msgB), expB, &expBSz); + if (ret != 0) { + PRINT_MSG("Generate expected MAC B failed"); + } + } + + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_CIPHER, + cipher, 0); + params[1] = OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_KEY, + (void*)key, sizeof(key)); + params[2] = OSSL_PARAM_construct_end(); + + if (ret == 0) { + ret = (emac = EVP_MAC_fetch(wpLibCtx, "CMAC", NULL)) == NULL; + } + if (ret == 0) { + ret = (src = EVP_MAC_CTX_new(emac)) == NULL; + } + if (ret == 0) { + ret = EVP_MAC_CTX_set_params(src, params) != 1; + } + if (ret == 0) { + ret = EVP_MAC_init(src, NULL, 0, NULL) != 1; + } + if (ret == 0) { + ret = EVP_MAC_update(src, prefix, sizeof(prefix)) != 1; + } + /* Duplicate after partial update. */ + if (ret == 0) { + ret = (dup = EVP_MAC_CTX_dup(src)) == NULL; + } + if (ret == 0) { + ret = EVP_MAC_update(src, tailA, sizeof(tailA)) != 1; + } + if (ret == 0) { + ret = EVP_MAC_update(dup, tailB, sizeof(tailB)) != 1; + } + if (ret == 0) { + ret = EVP_MAC_final(src, macA, &macASz, sizeof(macA)) != 1; + } + if (ret == 0) { + ret = EVP_MAC_final(dup, macB, &macBSz, sizeof(macB)) != 1; + } + if (ret == 0) { + if ((macASz != (size_t)expASz) || (memcmp(macA, expA, macASz) != 0)) { + PRINT_MSG("Duplicated source context MAC mismatch"); + ret = -1; + } + } + if (ret == 0) { + if ((macBSz != (size_t)expBSz) || (memcmp(macB, expB, macBSz) != 0)) { + PRINT_MSG("Duplicated destination context MAC mismatch"); + ret = -1; + } + } + + EVP_MAC_CTX_free(dup); + EVP_MAC_CTX_free(src); + EVP_MAC_free(emac); + + return ret; +} + #endif /* WP_HAVE_CMAC */ diff --git a/test/test_ecx.c b/test/test_ecx.c index 99304fcf..edefcc97 100644 --- a/test/test_ecx.c +++ b/test/test_ecx.c @@ -693,4 +693,139 @@ int test_ecx_null_init(void* data) return err; } +int test_ecx_dup(void *data) +{ + int err = 0; + EVP_PKEY *pkey = NULL; + EVP_PKEY *dupKey = NULL; + const unsigned char *p = NULL; + + (void)data; + +#ifdef WP_HAVE_ED25519 + PRINT_MSG("Testing ECX dup with Ed25519"); + + /* Load key from DER. */ + p = ed25519_key_der; + pkey = d2i_PrivateKey_ex(EVP_PKEY_ED25519, NULL, &p, + sizeof(ed25519_key_der), wpLibCtx, NULL); + if (pkey == NULL) { + PRINT_MSG("Failed to load Ed25519 key"); + err = 1; + } + + /* Duplicate the key. */ + if (err == 0) { + dupKey = EVP_PKEY_dup(pkey); + if (dupKey == NULL) { + PRINT_MSG("EVP_PKEY_dup failed"); + err = 1; + } + } + + /* Verify the keys are equal. */ + if (err == 0) { + if (EVP_PKEY_eq(pkey, dupKey) != 1) { + PRINT_MSG("EVP_PKEY_eq failed after dup"); + err = 1; + } + } + + /* Sign with dup, verify with original. */ + if (err == 0) { + unsigned char sig[ED25519_SIG_SIZE]; + size_t sigLen = sizeof(sig); + unsigned char msg[32]; + + err = RAND_bytes(msg, sizeof(msg)) == 0; + + if (err == 0) { + PRINT_MSG("Sign with dup key"); + err = test_digest_sign(dupKey, wpLibCtx, msg, sizeof(msg), + NULL, NULL, sig, &sigLen, 0, 0); + } + if (err == 0) { + PRINT_MSG("Verify with original key"); + err = test_digest_verify(pkey, wpLibCtx, msg, sizeof(msg), + NULL, NULL, sig, sigLen, 0, 0); + } + } + + /* Sign with original, verify with dup. */ + if (err == 0) { + unsigned char sig[ED25519_SIG_SIZE]; + size_t sigLen = sizeof(sig); + unsigned char msg[32]; + + err = RAND_bytes(msg, sizeof(msg)) == 0; + + if (err == 0) { + PRINT_MSG("Sign with original key"); + err = test_digest_sign(pkey, wpLibCtx, msg, sizeof(msg), + NULL, NULL, sig, &sigLen, 0, 0); + } + if (err == 0) { + PRINT_MSG("Verify with dup key"); + err = test_digest_verify(dupKey, wpLibCtx, msg, sizeof(msg), + NULL, NULL, sig, sigLen, 0, 0); + } + } + + EVP_PKEY_free(dupKey); + EVP_PKEY_free(pkey); + dupKey = NULL; + pkey = NULL; +#endif /* WP_HAVE_ED25519 */ + +#ifdef WP_HAVE_ED448 + if (err == 0) { + PRINT_MSG("Testing ECX dup with Ed448"); + + p = ed448_key_der; + pkey = d2i_PrivateKey_ex(EVP_PKEY_ED448, NULL, &p, + sizeof(ed448_key_der), wpLibCtx, NULL); + if (pkey == NULL) { + PRINT_MSG("Failed to load Ed448 key"); + err = 1; + } + + if (err == 0) { + dupKey = EVP_PKEY_dup(pkey); + if (dupKey == NULL) { + PRINT_MSG("EVP_PKEY_dup failed for Ed448"); + err = 1; + } + } + + if (err == 0) { + if (EVP_PKEY_eq(pkey, dupKey) != 1) { + PRINT_MSG("EVP_PKEY_eq failed after Ed448 dup"); + err = 1; + } + } + + if (err == 0) { + unsigned char sig[ED448_SIG_SIZE]; + size_t sigLen = sizeof(sig); + unsigned char msg[32]; + + err = RAND_bytes(msg, sizeof(msg)) == 0; + if (err == 0) { + err = test_digest_sign(dupKey, wpLibCtx, msg, sizeof(msg), + NULL, NULL, sig, &sigLen, 0, 0); + } + if (err == 0) { + err = test_digest_verify(pkey, wpLibCtx, msg, sizeof(msg), + NULL, NULL, sig, sigLen, 0, 0); + } + } + + EVP_PKEY_free(dupKey); + EVP_PKEY_free(pkey); + } +#endif /* WP_HAVE_ED448 */ + + return err; +} + #endif /* defined(WP_HAVE_ED25519) || defined(WP_HAVE_ECD444) */ diff --git a/test/test_hmac.c b/test/test_hmac.c index 2d5a8aca..4a5b651e 100644 --- a/test/test_hmac.c +++ b/test/test_hmac.c @@ -295,6 +295,280 @@ int test_hmac_create(void *data) return ret; } -#endif /* WP_HAVE_HMAC */ +int test_hmac_dup(void *data) +{ + int ret = 0; + EVP_MAC* emac = NULL; + EVP_MAC_CTX* src = NULL; + EVP_MAC_CTX* dup = NULL; + OSSL_PARAM params[3]; + char digest[] = "SHA-256"; + unsigned char key[] = "My empire of dirt"; + unsigned char prefix[] = "dup-prefix"; + unsigned char tailA[] = "-tail-a"; + unsigned char tailB[] = "-tail-b"; + unsigned char msgA[sizeof(prefix) + sizeof(tailA)]; + unsigned char msgB[sizeof(prefix) + sizeof(tailB)]; + unsigned char macA[32]; + unsigned char macB[32]; + unsigned char expA[32]; + unsigned char expB[32]; + size_t macASz = sizeof(macA); + size_t macBSz = sizeof(macB); + int expASz = sizeof(expA); + int expBSz = sizeof(expB); + + (void)data; + + PRINT_MSG("Testing HMAC context dup"); + + /* Build full messages for one-shot expected MAC calculations. */ + memcpy(msgA, prefix, sizeof(prefix)); + memcpy(msgA + sizeof(prefix), tailA, sizeof(tailA)); + memcpy(msgB, prefix, sizeof(prefix)); + memcpy(msgB + sizeof(prefix), tailB, sizeof(tailB)); + + /* Compute expected MACs. */ + ret = test_mac_gen_mac(wpLibCtx, "SHA-256", "HMAC", key, sizeof(key), + msgA, (int)sizeof(msgA), expA, &expASz); + if (ret != 0) { + PRINT_MSG("Generate expected MAC A failed"); + } + if (ret == 0) { + ret = test_mac_gen_mac(wpLibCtx, "SHA-256", "HMAC", key, sizeof(key), + msgB, (int)sizeof(msgB), expB, &expBSz); + if (ret != 0) { + PRINT_MSG("Generate expected MAC B failed"); + } + } + + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, + digest, 0); + params[1] = OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_KEY, + (void*)key, sizeof(key)); + params[2] = OSSL_PARAM_construct_end(); + + if (ret == 0) { + ret = (emac = EVP_MAC_fetch(wpLibCtx, "HMAC", NULL)) == NULL; + } + if (ret == 0) { + ret = (src = EVP_MAC_CTX_new(emac)) == NULL; + } + if (ret == 0) { + ret = EVP_MAC_CTX_set_params(src, params) != 1; + } + if (ret == 0) { + ret = EVP_MAC_init(src, NULL, 0, NULL) != 1; + } + if (ret == 0) { + ret = EVP_MAC_update(src, prefix, sizeof(prefix)) != 1; + } + /* Duplicate after partial update. */ + if (ret == 0) { + ret = (dup = EVP_MAC_CTX_dup(src)) == NULL; + } + if (ret == 0) { + ret = EVP_MAC_update(src, tailA, sizeof(tailA)) != 1; + } + if (ret == 0) { + ret = EVP_MAC_update(dup, tailB, sizeof(tailB)) != 1; + } + if (ret == 0) { + ret = EVP_MAC_final(src, macA, &macASz, sizeof(macA)) != 1; + } + if (ret == 0) { + ret = EVP_MAC_final(dup, macB, &macBSz, sizeof(macB)) != 1; + } + if (ret == 0) { + if ((macASz != (size_t)expASz) || (memcmp(macA, expA, macASz) != 0)) { + PRINT_MSG("Duplicated source context MAC mismatch"); + ret = -1; + } + } + if (ret == 0) { + if ((macBSz != (size_t)expBSz) || (memcmp(macB, expB, macBSz) != 0)) { + PRINT_MSG("Duplicated destination context MAC mismatch"); + ret = -1; + } + } + + EVP_MAC_CTX_free(dup); + EVP_MAC_CTX_free(src); + EVP_MAC_free(emac); + + return ret; +} + +int test_mac_key_match(void *data) +{ + int ret = 0; + EVP_PKEY *pkey1 = NULL; + EVP_PKEY *pkey2 = NULL; + EVP_PKEY *pkey3 = NULL; + unsigned char key1[] = "matching-key-value-1234"; + unsigned char key2[] = "different-key-value-567"; + + (void)data; + + PRINT_MSG("Testing MAC key match with CRYPTO_memcmp"); + + /* Create two keys with the same key material. */ + pkey1 = EVP_PKEY_new_raw_private_key_ex(wpLibCtx, "HMAC", NULL, + key1, sizeof(key1)); + if (pkey1 == NULL) { + PRINT_MSG("Failed to create pkey1"); + ret = 1; + } + if (ret == 0) { + pkey2 = EVP_PKEY_new_raw_private_key_ex(wpLibCtx, "HMAC", NULL, + key1, sizeof(key1)); + if (pkey2 == NULL) { + PRINT_MSG("Failed to create pkey2"); + ret = 1; + } + } + + /* Verify same keys match. */ + if (ret == 0) { + if (EVP_PKEY_eq(pkey1, pkey2) != 1) { + PRINT_MSG("Same keys should match but don't"); + ret = -1; + } + } + + /* Create a third key with different material. */ + if (ret == 0) { + pkey3 = EVP_PKEY_new_raw_private_key_ex(wpLibCtx, "HMAC", NULL, + key2, sizeof(key2)); + if (pkey3 == NULL) { + PRINT_MSG("Failed to create pkey3"); + ret = 1; + } + } + + /* Verify different keys don't match. */ + if (ret == 0) { + if (EVP_PKEY_eq(pkey1, pkey3) == 1) { + PRINT_MSG("Different keys should not match but do"); + ret = -1; + } + } + + EVP_PKEY_free(pkey3); + EVP_PKEY_free(pkey2); + EVP_PKEY_free(pkey1); + + return ret; +} + +int test_mac_sig_dup(void *data) +{ + int ret = 0; + EVP_MD_CTX *ctx = NULL; + EVP_MD_CTX *dupCtx = NULL; + EVP_PKEY_CTX *pctx = NULL; + EVP_PKEY *pkey = NULL; + unsigned char key[] = "My empire of dirt"; + unsigned char prefix[] = "dup-prefix"; + unsigned char tailA[] = "-tail-a"; + unsigned char tailB[] = "-tail-b"; + unsigned char msgA[sizeof(prefix) + sizeof(tailA)]; + unsigned char msgB[sizeof(prefix) + sizeof(tailB)]; + unsigned char macA[64]; + unsigned char macB[64]; + unsigned char expA[64]; + unsigned char expB[64]; + size_t macASz = sizeof(macA); + size_t macBSz = sizeof(macB); + int expASz = sizeof(expA); + int expBSz = sizeof(expB); + (void)data; + + PRINT_MSG("Testing MAC sig context dup (ref counting)"); + + /* Build full messages for expected MAC computation. */ + memcpy(msgA, prefix, sizeof(prefix)); + memcpy(msgA + sizeof(prefix), tailA, sizeof(tailA)); + memcpy(msgB, prefix, sizeof(prefix)); + memcpy(msgB + sizeof(prefix), tailB, sizeof(tailB)); + + /* Compute expected MACs via one-shot HMAC. */ + ret = test_mac_gen_mac(wpLibCtx, "SHA-256", "HMAC", key, sizeof(key), + msgA, (int)sizeof(msgA), expA, &expASz); + if (ret == 0) { + ret = test_mac_gen_mac(wpLibCtx, "SHA-256", "HMAC", key, sizeof(key), + msgB, (int)sizeof(msgB), expB, &expBSz); + } + + if (ret == 0) { + pkey = EVP_PKEY_new_raw_private_key_ex(wpLibCtx, "HMAC", NULL, + key, sizeof(key)); + if (pkey == NULL) { + PRINT_MSG("Failed to create HMAC pkey"); + ret = 1; + } + } + + if (ret == 0) { + ctx = EVP_MD_CTX_new(); + if (ctx == NULL) { + ret = 1; + } + } + if (ret == 0) { + ret = EVP_DigestSignInit_ex(ctx, &pctx, "SHA-256", wpLibCtx, NULL, + pkey, NULL) != 1; + } + if (ret == 0) { + ret = EVP_DigestSignUpdate(ctx, prefix, sizeof(prefix)) != 1; + } + + /* Duplicate the signing context mid-stream. */ + if (ret == 0) { + dupCtx = EVP_MD_CTX_new(); + if (dupCtx == NULL) { + ret = 1; + } + } + if (ret == 0) { + ret = EVP_MD_CTX_copy_ex(dupCtx, ctx) != 1; + } + + /* Feed different tails and finalize. */ + if (ret == 0) { + ret = EVP_DigestSignUpdate(ctx, tailA, sizeof(tailA)) != 1; + } + if (ret == 0) { + ret = EVP_DigestSignFinal(ctx, macA, &macASz) != 1; + } + if (ret == 0) { + ret = EVP_DigestSignUpdate(dupCtx, tailB, sizeof(tailB)) != 1; + } + if (ret == 0) { + ret = EVP_DigestSignFinal(dupCtx, macB, &macBSz) != 1; + } + + /* Verify each branch matches its expected MAC. */ + if (ret == 0) { + if ((macASz != (size_t)expASz) || (memcmp(macA, expA, macASz) != 0)) { + PRINT_MSG("Source sig context MAC mismatch after dup"); + ret = -1; + } + } + if (ret == 0) { + if ((macBSz != (size_t)expBSz) || (memcmp(macB, expB, macBSz) != 0)) { + PRINT_MSG("Duplicated sig context MAC mismatch"); + ret = -1; + } + } + + EVP_MD_CTX_free(dupCtx); + EVP_MD_CTX_free(ctx); + EVP_PKEY_free(pkey); + + return ret; +} + +#endif /* WP_HAVE_HMAC */ diff --git a/test/test_rand_seed.c b/test/test_rand_seed.c index 1692c897..18566303 100644 --- a/test/test_rand_seed.c +++ b/test/test_rand_seed.c @@ -470,6 +470,131 @@ int test_rand_seed(void *data) return err; } +/** + * Test DRBG reseed and verify_zeroization - Validates #169, #170. + * + * Creates SEED-SRC -> CTR-DRBG hierarchy, generates bytes, reseeds, + * generates more bytes (verifying they differ), then uninstantiates + * and calls verify_zeroization. + */ +static int test_drbg_reseed_helper(OSSL_LIB_CTX *libCtx, const char *propq) +{ + int err = 0; + EVP_RAND *seed_src = NULL; + EVP_RAND *ctr_drbg = NULL; + EVP_RAND_CTX *seed_ctx = NULL; + EVP_RAND_CTX *drbg_ctx = NULL; + unsigned char buf1[32]; + unsigned char buf2[32]; + + PRINT_MSG("Testing DRBG reseed and verify_zeroization"); + + seed_src = EVP_RAND_fetch(libCtx, "SEED-SRC", propq); + if (seed_src == NULL) { + PRINT_ERR_MSG("Failed to fetch SEED-SRC"); + err = 1; + goto cleanup; + } + + seed_ctx = EVP_RAND_CTX_new(seed_src, NULL); + if (seed_ctx == NULL) { + PRINT_ERR_MSG("Failed to create SEED-SRC context"); + err = 1; + goto cleanup; + } + + if (EVP_RAND_instantiate(seed_ctx, 0, 0, NULL, 0, NULL) != 1) { + PRINT_ERR_MSG("Failed to instantiate SEED-SRC"); + err = 1; + goto cleanup; + } + + ctr_drbg = EVP_RAND_fetch(libCtx, "CTR-DRBG", propq); + if (ctr_drbg == NULL) { + PRINT_ERR_MSG("Failed to fetch CTR-DRBG"); + err = 1; + goto cleanup; + } + + drbg_ctx = EVP_RAND_CTX_new(ctr_drbg, seed_ctx); + if (drbg_ctx == NULL) { + PRINT_ERR_MSG("Failed to create CTR-DRBG context"); + err = 1; + goto cleanup; + } + + { + OSSL_PARAM params[2]; + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_DRBG_PARAM_CIPHER, + (char*)"AES-256-CTR", 0); + params[1] = OSSL_PARAM_construct_end(); + if (EVP_RAND_CTX_set_params(drbg_ctx, params) != 1) { + PRINT_ERR_MSG("Failed to set CTR-DRBG cipher param"); + err = 1; + goto cleanup; + } + } + + if (EVP_RAND_instantiate(drbg_ctx, 256, 0, NULL, 0, NULL) != 1) { + PRINT_ERR_MSG("Failed to instantiate CTR-DRBG"); + err = 1; + goto cleanup; + } + + /* Generate first block. */ + if (EVP_RAND_generate(drbg_ctx, buf1, sizeof(buf1), 256, 0, + NULL, 0) != 1) { + PRINT_ERR_MSG("Failed first generate"); + err = 1; + goto cleanup; + } + + /* Reseed (exercises fix #169). */ + if (EVP_RAND_reseed(drbg_ctx, 0, NULL, 0, NULL, 0) != 1) { + PRINT_ERR_MSG("EVP_RAND_reseed failed"); + err = 1; + goto cleanup; + } + PRINT_MSG("DRBG reseed succeeded"); + + /* Generate second block after reseed. */ + if (EVP_RAND_generate(drbg_ctx, buf2, sizeof(buf2), 256, 0, + NULL, 0) != 1) { + PRINT_ERR_MSG("Failed second generate after reseed"); + err = 1; + goto cleanup; + } + + /* Buffers should differ (extremely high probability). */ + if (memcmp(buf1, buf2, sizeof(buf1)) == 0) { + PRINT_ERR_MSG("Pre/post-reseed outputs are identical"); + err = 1; + goto cleanup; + } + PRINT_MSG("Pre/post-reseed outputs differ as expected"); + + /* Uninstantiate and verify zeroization (exercises fix #170). */ + if (EVP_RAND_uninstantiate(drbg_ctx) != 1) { + PRINT_ERR_MSG("EVP_RAND_uninstantiate failed"); + err = 1; + goto cleanup; + } + + if (EVP_RAND_verify_zeroization(drbg_ctx) != 1) { + PRINT_ERR_MSG("EVP_RAND_verify_zeroization failed"); + err = 1; + goto cleanup; + } + PRINT_MSG("DRBG verify_zeroization succeeded"); + +cleanup: + EVP_RAND_CTX_free(drbg_ctx); + EVP_RAND_CTX_free(seed_ctx); + EVP_RAND_free(ctr_drbg); + EVP_RAND_free(seed_src); + return err; +} + #else /* !(WP_HAVE_SEED_SRC && WP_HAVE_RANDOM) */ int test_rand_seed(void *data) @@ -482,3 +607,23 @@ int test_rand_seed(void *data) #endif /* WP_HAVE_SEED_SRC && WP_HAVE_RANDOM */ +int test_drbg_reseed(void *data) +{ + int err = 0; + + (void)data; + +#if defined(WP_HAVE_SEED_SRC) && defined(WP_HAVE_RANDOM) + PRINT_MSG("Test OpenSSL DRBG reseed/zeroization"); + err = test_drbg_reseed_helper(osslLibCtx, NULL); + if (err == 0) { + PRINT_MSG("Test wolfProvider DRBG reseed/zeroization"); + err = test_drbg_reseed_helper(wpLibCtx, NULL); + } +#else + PRINT_MSG("DRBG reseed test skipped - SEED-SRC not enabled"); +#endif + + return err; +} + diff --git a/test/test_rsa.c b/test/test_rsa.c index 5cf07b35..c0eaeb48 100644 --- a/test/test_rsa.c +++ b/test/test_rsa.c @@ -1952,7 +1952,11 @@ static int test_rsa_decode_evp_pkey(EVP_PKEY* pkey1, EVP_PKEY* pkey2) PRINT_MSG("Comparing key %s (%d %d)", str_keys[i], err1, err2); if (err == 0 && err1 == 1) { - err = OPENSSL_strcasecmp(str1, str2) != 0; + if (OPENSSL_strcasecmp(str1, str2) != 0) { + PRINT_ERR_MSG("String mismatch for %s: '%s' vs '%s'", + str_keys[i], str1, str2); + err = 1; + } } OPENSSL_free(str1); @@ -2134,4 +2138,216 @@ int test_rsa_null_init(void* data) return err; } +static int test_rsa_pss_mgf1_get_params_helper(OSSL_LIB_CTX *libCtx) +{ + int err = 0; + EVP_PKEY *pkey = NULL; + EVP_PKEY_CTX *pkeyCtx = NULL; + OSSL_PARAM params[2]; + char mgfMdName[64] = ""; + char *pmgfMdName = mgfMdName; + + /* Generate RSA-PSS key with SHA-256 for signing, SHA-384 for MGF1. */ + pkeyCtx = EVP_PKEY_CTX_new_from_name(libCtx, "RSA-PSS", NULL); + if (pkeyCtx == NULL) { + PRINT_ERR_MSG("Failed to create RSA-PSS context"); + err = 1; + } + if (err == 0) { + err = EVP_PKEY_keygen_init(pkeyCtx) <= 0; + } + if (err == 0) { + err = EVP_PKEY_CTX_set_rsa_keygen_bits(pkeyCtx, 2048) <= 0; + } + if (err == 0) { + err = EVP_PKEY_CTX_set_rsa_pss_keygen_md(pkeyCtx, EVP_sha256()) <= 0; + } + if (err == 0) { + err = EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md(pkeyCtx, + EVP_sha384()) <= 0; + } + if (err == 0) { + err = EVP_PKEY_keygen(pkeyCtx, &pkey) <= 0; + } + EVP_PKEY_CTX_free(pkeyCtx); + pkeyCtx = NULL; + + /* Now retrieve the MGF1 digest param and verify it's SHA-384 not SHA-256. */ + if (err == 0) { + params[0] = OSSL_PARAM_construct_utf8_string( + OSSL_PKEY_PARAM_RSA_MGF1_DIGEST, pmgfMdName, sizeof(mgfMdName)); + params[1] = OSSL_PARAM_construct_end(); + err = EVP_PKEY_get_params(pkey, params) != 1; + } + if (err == 0) { + /* The fix ensures MGF1 digest (SHA-384) is returned, not the + * signing digest (SHA-256). Verify it contains "384" and not "256". */ + if (strstr(mgfMdName, "384") == NULL) { + PRINT_ERR_MSG("MGF1 digest should contain '384' but got: %s", + mgfMdName); + err = 1; + } + else if (strstr(mgfMdName, "256") != NULL) { + PRINT_ERR_MSG("MGF1 digest should not contain '256' but got: %s", + mgfMdName); + err = 1; + } + else { + PRINT_MSG("MGF1 digest correctly returned: %s", mgfMdName); + } + } + + EVP_PKEY_free(pkey); + return err; +} + +int test_rsa_pss_mgf1_get_params(void *data) +{ + int err = 0; + + (void)data; + + PRINT_MSG("Test OpenSSL RSA-PSS MGF1 digest get_params"); + err = test_rsa_pss_mgf1_get_params_helper(osslLibCtx); + if (err == 0) { + PRINT_MSG("Test wolfProvider RSA-PSS MGF1 digest get_params"); + err = test_rsa_pss_mgf1_get_params_helper(wpLibCtx); + } + + return err; +} + +static int test_rsa_kem_helper(OSSL_LIB_CTX *libCtx) +{ + int err = 0; + EVP_PKEY *pkey = NULL; + EVP_PKEY_CTX *genCtx = NULL; + EVP_PKEY_CTX *encCtx = NULL; + EVP_PKEY_CTX *decCtx = NULL; + unsigned char *ct = NULL; + unsigned char *secret = NULL; + unsigned char *recovered = NULL; + size_t ctLen = 0; + size_t secretLen = 0; + size_t recoveredLen = 0; + + /* Generate RSA-2048 key. */ + genCtx = EVP_PKEY_CTX_new_from_name(libCtx, "RSA", NULL); + if (genCtx == NULL) { + err = 1; + } + if (err == 0) { + err = EVP_PKEY_keygen_init(genCtx) <= 0; + } + if (err == 0) { + err = EVP_PKEY_CTX_set_rsa_keygen_bits(genCtx, 2048) <= 0; + } + if (err == 0) { + err = EVP_PKEY_keygen(genCtx, &pkey) <= 0; + } + EVP_PKEY_CTX_free(genCtx); + genCtx = NULL; + + /* Encapsulate. */ + if (err == 0) { + encCtx = EVP_PKEY_CTX_new_from_pkey(libCtx, pkey, NULL); + if (encCtx == NULL) { + err = 1; + } + } + if (err == 0) { + err = EVP_PKEY_encapsulate_init(encCtx, NULL) <= 0; + } + if (err == 0) { + OSSL_PARAM params[2]; + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_KEM_PARAM_OPERATION, + (char*)"RSASVE", 0); + params[1] = OSSL_PARAM_construct_end(); + err = EVP_PKEY_CTX_set_params(encCtx, params) != 1; + } + /* Query sizes. */ + if (err == 0) { + err = EVP_PKEY_encapsulate(encCtx, NULL, &ctLen, NULL, + &secretLen) <= 0; + } + if (err == 0) { + ct = OPENSSL_malloc(ctLen); + secret = OPENSSL_malloc(secretLen); + if (ct == NULL || secret == NULL) { + err = 1; + } + } + if (err == 0) { + err = EVP_PKEY_encapsulate(encCtx, ct, &ctLen, secret, + &secretLen) <= 0; + } + + /* Decapsulate. */ + if (err == 0) { + decCtx = EVP_PKEY_CTX_new_from_pkey(libCtx, pkey, NULL); + if (decCtx == NULL) { + err = 1; + } + } + if (err == 0) { + err = EVP_PKEY_decapsulate_init(decCtx, NULL) <= 0; + } + if (err == 0) { + OSSL_PARAM params[2]; + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_KEM_PARAM_OPERATION, + (char*)"RSASVE", 0); + params[1] = OSSL_PARAM_construct_end(); + err = EVP_PKEY_CTX_set_params(decCtx, params) != 1; + } + /* Query size. */ + if (err == 0) { + err = EVP_PKEY_decapsulate(decCtx, NULL, &recoveredLen, ct, + ctLen) <= 0; + } + if (err == 0) { + recovered = OPENSSL_malloc(recoveredLen); + if (recovered == NULL) { + err = 1; + } + } + if (err == 0) { + err = EVP_PKEY_decapsulate(decCtx, recovered, &recoveredLen, ct, + ctLen) <= 0; + } + + /* Verify secret matches. */ + if (err == 0) { + if (secretLen != recoveredLen || + memcmp(secret, recovered, secretLen) != 0) { + PRINT_ERR_MSG("KEM: recovered secret doesn't match original"); + err = 1; + } + } + + OPENSSL_free(recovered); + OPENSSL_free(secret); + OPENSSL_free(ct); + EVP_PKEY_CTX_free(decCtx); + EVP_PKEY_CTX_free(encCtx); + EVP_PKEY_free(pkey); + + return err; +} + +int test_rsa_kem(void *data) +{ + int err = 0; + + (void)data; + + PRINT_MSG("Test OpenSSL RSA KEM encapsulate/decapsulate"); + err = test_rsa_kem_helper(osslLibCtx); + if (err == 0) { + PRINT_MSG("Test wolfProvider RSA KEM encapsulate/decapsulate"); + err = test_rsa_kem_helper(wpLibCtx); + } + + return err; +} + #endif /* WP_HAVE_RSA */ diff --git a/test/test_tls_cbc.c b/test/test_tls_cbc.c index 4e514951..76cc24d3 100644 --- a/test/test_tls_cbc.c +++ b/test/test_tls_cbc.c @@ -238,3 +238,114 @@ int test_tls12_cbc_ossl(void *data) } #endif /* WP_HAVE_AESCBC && WP_HAVE_RSA && WP_HAVE_ECDH && WP_HAVE_SHA384 */ + +#ifdef WP_HAVE_DES3CBC +#if !defined(HAVE_FIPS) || defined(WP_ALLOW_NON_FIPS) + +/* + * Test DES3 CBC padding validation (exercises fix #838 constant-time padding). + * Encrypts data of various sizes and verifies decrypt roundtrip works, + * exercising all padding byte values (1-8 for DES block size). + */ +static int test_des3_cbc_pad_roundtrip(OSSL_LIB_CTX *encCtx, + OSSL_LIB_CTX *decCtx) +{ + int err = 0; + EVP_CIPHER *encCipher = NULL; + EVP_CIPHER *decCipher = NULL; + unsigned char key[24]; + unsigned char iv[8]; + unsigned char pt[64]; + unsigned char ct[128]; + unsigned char dec[128]; + int ctLen, decLen, finalLen; + int i; + + encCipher = EVP_CIPHER_fetch(encCtx, "DES-EDE3-CBC", ""); + decCipher = EVP_CIPHER_fetch(decCtx, "DES-EDE3-CBC", ""); + if (encCipher == NULL || decCipher == NULL) { + err = 1; + } + + memset(key, 0xAA, sizeof(key)); + memset(iv, 0xBB, sizeof(iv)); + if (RAND_bytes(pt, sizeof(pt)) != 1) { + err = 1; + } + + /* Test various plaintext sizes to exercise all padding values (1-8). */ + for (i = 1; i <= 8 && err == 0; i++) { + int ptLen = 8 + i; /* 9..16 bytes, padding will be 7..0+8 */ + EVP_CIPHER_CTX *ctx; + + /* Encrypt */ + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) { err = 1; break; } + if (EVP_EncryptInit_ex(ctx, encCipher, NULL, key, iv) != 1) { + err = 1; + } + ctLen = 0; + if (err == 0 && EVP_EncryptUpdate(ctx, ct, &ctLen, pt, ptLen) != 1) { + err = 1; + } + finalLen = 0; + if (err == 0 && EVP_EncryptFinal_ex(ctx, ct + ctLen, &finalLen) != 1) { + err = 1; + } + ctLen += finalLen; + EVP_CIPHER_CTX_free(ctx); + + if (err != 0) break; + + /* Decrypt */ + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) { err = 1; break; } + if (EVP_DecryptInit_ex(ctx, decCipher, NULL, key, iv) != 1) { + err = 1; + } + decLen = 0; + if (err == 0 && EVP_DecryptUpdate(ctx, dec, &decLen, ct, ctLen) != 1) { + err = 1; + } + finalLen = 0; + if (err == 0 && EVP_DecryptFinal_ex(ctx, dec + decLen, &finalLen) != 1) { + PRINT_ERR_MSG("DES3 DecryptFinal failed for ptLen=%d", ptLen); + err = 1; + } + decLen += finalLen; + EVP_CIPHER_CTX_free(ctx); + + if (err == 0 && (decLen != ptLen || + memcmp(dec, pt, ptLen) != 0)) { + PRINT_ERR_MSG("DES3 roundtrip mismatch for ptLen=%d", ptLen); + err = 1; + } + } + + EVP_CIPHER_free(encCipher); + EVP_CIPHER_free(decCipher); + return err; +} + +int test_des3_tls_cbc(void *data) +{ + int err = 0; + + (void)data; + + PRINT_MSG("DES3 CBC padding roundtrip (OpenSSL -> wolfProvider)"); + err = test_des3_cbc_pad_roundtrip(osslLibCtx, wpLibCtx); + if (err == 0) { + PRINT_MSG("DES3 CBC padding roundtrip (wolfProvider -> OpenSSL)"); + err = test_des3_cbc_pad_roundtrip(wpLibCtx, osslLibCtx); + } + if (err == 0) { + PRINT_MSG("DES3 CBC padding roundtrip (wolfProvider -> wolfProvider)"); + err = test_des3_cbc_pad_roundtrip(wpLibCtx, wpLibCtx); + } + + return err; +} + +#endif /* !HAVE_FIPS || WP_ALLOW_NON_FIPS */ +#endif /* WP_HAVE_DES3CBC */ diff --git a/test/unit.c b/test/unit.c index 0e03de10..88ad34eb 100644 --- a/test/unit.c +++ b/test/unit.c @@ -203,9 +203,13 @@ TEST_CASE test_case[] = { #endif #ifdef WP_HAVE_HMAC TEST_DECL(test_hmac_create, NULL), + TEST_DECL(test_hmac_dup, NULL), + TEST_DECL(test_mac_key_match, NULL), + TEST_DECL(test_mac_sig_dup, NULL), #endif #ifdef WP_HAVE_CMAC TEST_DECL(test_cmac_create, &flags), + TEST_DECL(test_cmac_dup, &flags), #endif #ifdef WP_HAVE_GMAC TEST_DECL(test_gmac_create, &flags), @@ -227,6 +231,7 @@ TEST_CASE test_case[] = { #if !defined(HAVE_FIPS) || defined(WP_ALLOW_NON_FIPS) TEST_DECL(test_des3_cbc, NULL), TEST_DECL(test_des3_cbc_stream, NULL), + TEST_DECL(test_des3_tls_cbc, NULL), #endif #endif #ifdef WP_HAVE_AESECB @@ -281,6 +286,7 @@ TEST_CASE test_case[] = { TEST_DECL(test_random, NULL), #endif TEST_DECL(test_rand_seed, NULL), + TEST_DECL(test_drbg_reseed, NULL), TEST_DECL(test_seccomp_sandbox, NULL), #ifdef WP_HAVE_DH TEST_DECL(test_dh_pgen_pkey, NULL), @@ -313,6 +319,8 @@ TEST_CASE test_case[] = { TEST_DECL(test_rsa_decode_pkcs8, NULL), TEST_DECL(test_rsa_encode_pkcs8, NULL), TEST_DECL(test_rsa_null_init, NULL), + TEST_DECL(test_rsa_pss_mgf1_get_params, NULL), + TEST_DECL(test_rsa_kem, NULL), #endif /* WP_HAVE_RSA */ #ifdef WP_HAVE_EC_P192 #ifdef WP_HAVE_ECKEYGEN @@ -435,6 +443,7 @@ TEST_CASE test_case[] = { TEST_DECL(test_ecx_sign_verify_raw_pub, NULL), TEST_DECL(test_ecx_misc, NULL), TEST_DECL(test_ecx_null_init, NULL), + TEST_DECL(test_ecx_dup, NULL), #endif TEST_DECL(test_pkcs7_x509_sign_verify, NULL), diff --git a/test/unit.h b/test/unit.h index 6c87f91d..9574e19b 100644 --- a/test/unit.h +++ b/test/unit.h @@ -118,11 +118,15 @@ int test_shake_256(void *data); #ifdef WP_HAVE_HMAC int test_hmac_create(void *data); +int test_hmac_dup(void *data); +int test_mac_key_match(void *data); +int test_mac_sig_dup(void *data); #endif /* WP_HAVE_HMAC */ #ifdef WP_HAVE_CMAC int test_cmac_create(void *data); -#endif /* WP_HAVE_HMAC */ +int test_cmac_dup(void *data); +#endif /* WP_HAVE_CMAC */ #ifdef WP_HAVE_GMAC int test_gmac_create(void *data); @@ -147,6 +151,7 @@ int test_krb5kdf(void *data); #ifdef WP_HAVE_DES3CBC int test_des3_cbc(void *data); int test_des3_cbc_stream(void *data); +int test_des3_tls_cbc(void *data); #endif #ifdef WP_HAVE_AESECB @@ -225,6 +230,7 @@ int test_random(void *data); /* DRBG SEED-SRC hierarchy tests */ int test_rand_seed(void *data); +int test_drbg_reseed(void *data); /* Seccomp sandbox test - mimics OpenSSH fork+sandbox behavior */ int test_seccomp_sandbox(void *data); @@ -280,6 +286,8 @@ int test_rsa_fromdata(void* data); int test_rsa_decode_pkcs8(void* data); int test_rsa_encode_pkcs8(void* data); int test_rsa_null_init(void* data); +int test_rsa_pss_mgf1_get_params(void *data); +int test_rsa_kem(void *data); #endif /* WP_HAVE_RSA */ #ifdef WP_HAVE_DH @@ -426,6 +434,7 @@ int test_ecx_sign_verify_raw_priv(void *data); int test_ecx_sign_verify_raw_pub(void *data); int test_ecx_misc(void *data); int test_ecx_null_init(void *data); +int test_ecx_dup(void *data); #endif int test_pkcs7_x509_sign_verify(void *data);