diff --git a/src/wp_ecx_kmgmt.c b/src/wp_ecx_kmgmt.c index 2cbaa83..5c7504e 100644 --- a/src/wp_ecx_kmgmt.c +++ b/src/wp_ecx_kmgmt.c @@ -560,6 +560,15 @@ static int wp_ecx_get_params_priv_key(wp_Ecx* ecx, OSSL_PARAM params[]) WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "exportPriv", rc); ok = 0; } + if (ok && ecx->clamped) { + if ((outLen < 2) || (p->data_size < outLen)) { + ok = 0; + } + else { + ((unsigned char*)p->data)[0 ] = ecx->unclamped[0]; + ((unsigned char*)p->data)[outLen - 1] = ecx->unclamped[1]; + } + } } p->return_size = outLen; } diff --git a/src/wp_seed_src.c b/src/wp_seed_src.c index fd4b3da..0918609 100644 --- a/src/wp_seed_src.c +++ b/src/wp_seed_src.c @@ -689,10 +689,8 @@ static size_t wp_seed_src_get_seed(wp_SeedSrcCtx* ctx, unsigned char** pSeed, static void wp_seed_src_clear_seed(wp_SeedSrcCtx* ctx, unsigned char* seed, size_t seedLen) { + (void)ctx; OPENSSL_secure_clear_free(seed, seedLen); - if (ctx != NULL) { - ctx->state = EVP_RAND_STATE_UNINITIALISED; - } } /** diff --git a/test/test_aestag.c b/test/test_aestag.c index bc21c4e..1280c1f 100644 --- a/test/test_aestag.c +++ b/test/test_aestag.c @@ -1196,6 +1196,116 @@ int test_aes128_gcm_set_iv_inv(void *data) EVP_GCM_TLS_FIXED_IV_LEN, 12); } +/******************************************************************************/ + +/* + * GCM streaming decryption with a tampered authentication tag. + * Verifies that DecryptFinal correctly rejects a forged tag. + */ +static int test_aes_gcm_bad_tag_helper(OSSL_LIB_CTX *libCtx, + const char *cipherName, int keyLen) +{ + int err = 0; + EVP_CIPHER *cipher = NULL; + EVP_CIPHER_CTX *ctx = NULL; + unsigned char key[32]; + unsigned char iv[12]; + unsigned char aad[] = "additional data"; + unsigned char pt[] = "GCM plaintext for tag test"; + int ptLen = (int)(sizeof(pt) - 1); + unsigned char ct[64]; + unsigned char tag[16]; + unsigned char dec[64]; + int outLen = 0, fLen = 0; + + memset(key, 0xAA, keyLen); + memset(iv, 0xBB, sizeof(iv)); + + cipher = EVP_CIPHER_fetch(libCtx, cipherName, ""); + if (cipher == NULL) { + err = 1; + } + + /* Encrypt */ + if (err == 0) { + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) + err = 1; + } + if (err == 0) { + err = EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv) != 1; + } + if (err == 0) { + err = EVP_EncryptUpdate(ctx, NULL, &outLen, aad, + (int)(sizeof(aad) - 1)) != 1; + } + if (err == 0) { + err = EVP_EncryptUpdate(ctx, ct, &outLen, pt, ptLen) != 1; + } + if (err == 0) { + err = EVP_EncryptFinal_ex(ctx, ct + outLen, &fLen) != 1; + outLen += fLen; + } + if (err == 0) { + err = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, tag) != 1; + } + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + + /* Tamper with the tag */ + if (err == 0) { + tag[0] ^= 0x01; + } + + /* Decrypt with tampered tag -- must fail at DecryptFinal */ + if (err == 0) { + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) + err = 1; + } + if (err == 0) { + err = EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv) != 1; + } + if (err == 0) { + err = EVP_DecryptUpdate(ctx, NULL, &fLen, aad, + (int)(sizeof(aad) - 1)) != 1; + } + if (err == 0) { + err = EVP_DecryptUpdate(ctx, dec, &fLen, ct, outLen) != 1; + } + if (err == 0) { + err = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, tag) != 1; + } + if (err == 0) { + int ret = EVP_DecryptFinal_ex(ctx, dec + fLen, &fLen); + if (ret == 1) { + PRINT_ERR_MSG("%s bad-tag: DecryptFinal should have failed", + cipherName); + err = 1; + } + } + + EVP_CIPHER_CTX_free(ctx); + EVP_CIPHER_free(cipher); + return err; +} + +int test_aes_gcm_bad_tag(void *data) +{ + int err = 0; + + (void)data; + + PRINT_MSG("AES-128-GCM streaming decryption with tampered tag"); + err = test_aes_gcm_bad_tag_helper(wpLibCtx, "AES-128-GCM", 16); + if (err == 0) { + PRINT_MSG("AES-256-GCM streaming decryption with tampered tag"); + err = test_aes_gcm_bad_tag_helper(wpLibCtx, "AES-256-GCM", 32); + } + + return err; +} + #endif /* WP_HAVE_AESGCM */ /******************************************************************************/ @@ -1245,5 +1355,141 @@ int test_aes128_ccm_tls(void *data) EVP_CCM_TLS_FIXED_IV_LEN, 1); } +/******************************************************************************/ + +/* + * CCM streaming decryption with a tampered authentication tag. + * Verifies that DecryptFinal correctly rejects a forged tag. + */ +static int test_aes_ccm_bad_tag_helper(OSSL_LIB_CTX *libCtx, + const char *cipherName, int keyLen) +{ + int err = 0; + EVP_CIPHER *cipher = NULL; + EVP_CIPHER_CTX *ctx = NULL; + unsigned char key[32]; + unsigned char iv[13]; + unsigned char aad[] = "additional data"; + unsigned char pt[] = "CCM plaintext for tag test"; + int ptLen = (int)(sizeof(pt) - 1); + unsigned char ct[64]; + unsigned char tag[16]; + unsigned char dec[64]; + int outLen = 0, fLen = 0; + + memset(key, 0xAA, keyLen); + memset(iv, 0xBB, sizeof(iv)); + + cipher = EVP_CIPHER_fetch(libCtx, cipherName, ""); + if (cipher == NULL) { + err = 1; + } + + /* Encrypt */ + if (err == 0) { + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) + err = 1; + } + if (err == 0) { + err = EVP_EncryptInit(ctx, cipher, NULL, NULL) != 1; + } + if (err == 0) { + err = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, + (int)sizeof(iv), NULL) != 1; + } + if (err == 0) { + err = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, NULL) != 1; + } + if (err == 0) { + err = EVP_EncryptInit(ctx, NULL, key, iv) != 1; + } + if (err == 0) { + err = EVP_EncryptUpdate(ctx, NULL, &outLen, NULL, ptLen) != 1; + } + if (err == 0) { + err = EVP_EncryptUpdate(ctx, NULL, &outLen, aad, + (int)(sizeof(aad) - 1)) != 1; + } + if (err == 0) { + err = EVP_EncryptUpdate(ctx, ct, &outLen, pt, ptLen) != 1; + } + if (err == 0) { + err = EVP_EncryptFinal_ex(ctx, ct + outLen, &fLen) != 1; + outLen += fLen; + } + if (err == 0) { + err = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, tag) != 1; + } + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + + /* Tamper with the tag */ + if (err == 0) { + tag[0] ^= 0x01; + } + + /* Decrypt with tampered tag -- must fail */ + if (err == 0) { + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) + err = 1; + } + if (err == 0) { + err = EVP_DecryptInit(ctx, cipher, NULL, NULL) != 1; + } + if (err == 0) { + err = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, + (int)sizeof(iv), NULL) != 1; + } + if (err == 0) { + err = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, tag) != 1; + } + if (err == 0) { + err = EVP_DecryptInit(ctx, NULL, key, iv) != 1; + } + if (err == 0) { + err = EVP_DecryptUpdate(ctx, NULL, &fLen, NULL, outLen) != 1; + } + if (err == 0) { + err = EVP_DecryptUpdate(ctx, NULL, &fLen, aad, + (int)(sizeof(aad) - 1)) != 1; + } + if (err == 0) { + /* CCM DecryptUpdate should fail with bad tag */ + int ret = EVP_DecryptUpdate(ctx, dec, &fLen, ct, outLen); + if (ret == 1) { + /* If Update succeeded, Final must fail */ + ret = EVP_DecryptFinal_ex(ctx, dec + fLen, &fLen); + if (ret == 1) { + PRINT_ERR_MSG("%s bad-tag: decryption should have failed", + cipherName); + err = 1; + } + } + /* else: Update failed, which is also correct for CCM bad tag */ + } + + EVP_CIPHER_CTX_free(ctx); + EVP_CIPHER_free(cipher); + return err; +} + +int test_aes_ccm_bad_tag(void *data) +{ + int err = 0; + + (void)data; + + PRINT_MSG("AES-128-CCM streaming decryption with tampered tag"); + err = test_aes_ccm_bad_tag_helper(wpLibCtx, "AES-128-CCM", 16); + if (err == 0) { + PRINT_MSG("AES-256-CCM streaming decryption with tampered tag"); + err = test_aes_ccm_bad_tag_helper(wpLibCtx, "AES-256-CCM", 32); + } + + return err; +} + #endif /* WP_HAVE_AESCCM */ diff --git a/test/test_ecc.c b/test/test_ecc.c index 3d23547..a3e3fdc 100644 --- a/test/test_ecc.c +++ b/test/test_ecc.c @@ -970,6 +970,159 @@ int test_ecdh_p521(void *data) } #endif /* WP_HAVE_EC_P521 */ +#ifdef WP_HAVE_X25519 +/* RFC 7748 Section 6.1 X25519 test vectors. */ +static const unsigned char x25519_alice_priv[] = { + 0x77, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 0x7d, + 0x3c, 0x16, 0xc1, 0x72, 0x51, 0xb2, 0x66, 0x45, + 0xdf, 0x4c, 0x2f, 0x87, 0xeb, 0xc0, 0x99, 0x2a, + 0xb1, 0x77, 0xfb, 0xa5, 0x1d, 0xb9, 0x2c, 0x2a +}; +static const unsigned char x25519_bob_priv[] = { + 0x5d, 0xab, 0x08, 0x7e, 0x62, 0x4a, 0x8a, 0x4b, + 0x79, 0xe1, 0x7f, 0x8b, 0x83, 0x80, 0x0e, 0xe6, + 0x6f, 0x3b, 0xb1, 0x29, 0x26, 0x18, 0xb6, 0xfd, + 0x1c, 0x2f, 0x8b, 0x27, 0xff, 0x88, 0xe0, 0xeb +}; +static const unsigned char x25519_shared_secret[] = { + 0x4a, 0x5d, 0x9d, 0x5b, 0xa4, 0xce, 0x2d, 0xe1, + 0x72, 0x8e, 0x3b, 0xf4, 0x80, 0x35, 0x0f, 0x25, + 0xe0, 0x7e, 0x21, 0xc9, 0x47, 0xd1, 0x9e, 0x33, + 0x76, 0xf0, 0x9b, 0x3c, 0x1e, 0x16, 0x17, 0x42 +}; + +/* + * X25519 known-answer ECDH test using RFC 7748 Section 6.1 vectors. + * Validates order-reduction and endian-swap logic in wp_x25519_derive by + * comparing the derived shared secret against the expected RFC output. + */ +int test_ecdh_x25519_vector(void *data) +{ + int err = 0; + EVP_PKEY *keyA = NULL; + EVP_PKEY *keyB = NULL; + unsigned char *secret = NULL; + + (void)data; + + PRINT_MSG("X25519 ECDH known-answer test (RFC 7748 Sec 6.1)"); + + keyA = EVP_PKEY_new_raw_private_key_ex(wpLibCtx, "X25519", NULL, + x25519_alice_priv, sizeof(x25519_alice_priv)); + if (keyA == NULL) { + PRINT_ERR_MSG("Failed to import X25519 Alice private key"); + err = 1; + } + if (err == 0) { + keyB = EVP_PKEY_new_raw_private_key_ex(wpLibCtx, "X25519", NULL, + x25519_bob_priv, sizeof(x25519_bob_priv)); + if (keyB == NULL) { + PRINT_ERR_MSG("Failed to import X25519 Bob private key"); + err = 1; + } + } + if (err == 0) { + err = test_ecdh_derive(keyA, keyB, &secret, 32); + } + if (err == 0) { + if (memcmp(secret, x25519_shared_secret, 32) != 0) { + PRINT_ERR_MSG("X25519 shared secret does not match expected"); + PRINT_BUFFER("Got", secret, 32); + PRINT_BUFFER("Expected", + (unsigned char *)x25519_shared_secret, 32); + err = 1; + } + } + + OPENSSL_free(secret); + EVP_PKEY_free(keyB); + EVP_PKEY_free(keyA); + + return err; +} +#endif /* WP_HAVE_X25519 */ + +#ifdef WP_HAVE_X448 +/* RFC 7748 Section 6.2 X448 test vectors (from OpenSSL evppkey_ecx.txt). */ +static const unsigned char x448_alice_priv[] = { + 0x9a, 0x8f, 0x49, 0x25, 0xd1, 0x51, 0x9f, 0x57, + 0x75, 0xcf, 0x46, 0xb0, 0x4b, 0x58, 0x00, 0xd4, + 0xee, 0x9e, 0xe8, 0xba, 0xe8, 0xbc, 0x55, 0x65, + 0xd4, 0x98, 0xc2, 0x8d, 0xd9, 0xc9, 0xba, 0xf5, + 0x74, 0xa9, 0x41, 0x97, 0x44, 0x89, 0x73, 0x91, + 0x00, 0x63, 0x82, 0xa6, 0xf1, 0x27, 0xab, 0x1d, + 0x9a, 0xc2, 0xd8, 0xc0, 0xa5, 0x98, 0x72, 0x6b +}; +static const unsigned char x448_bob_priv[] = { + 0x1c, 0x30, 0x6a, 0x7a, 0xc2, 0xa0, 0xe2, 0xe0, + 0x99, 0x0b, 0x29, 0x44, 0x70, 0xcb, 0xa3, 0x39, + 0xe6, 0x45, 0x37, 0x72, 0xb0, 0x75, 0x81, 0x1d, + 0x8f, 0xad, 0x0d, 0x1d, 0x69, 0x27, 0xc1, 0x20, + 0xbb, 0x5e, 0xe8, 0x97, 0x2b, 0x0d, 0x3e, 0x21, + 0x37, 0x4c, 0x9c, 0x92, 0x1b, 0x09, 0xd1, 0xb0, + 0x36, 0x6f, 0x10, 0xb6, 0x51, 0x73, 0x99, 0x2d +}; +static const unsigned char x448_shared_secret[] = { + 0x07, 0xff, 0xf4, 0x18, 0x1a, 0xc6, 0xcc, 0x95, + 0xec, 0x1c, 0x16, 0xa9, 0x4a, 0x0f, 0x74, 0xd1, + 0x2d, 0xa2, 0x32, 0xce, 0x40, 0xa7, 0x75, 0x52, + 0x28, 0x1d, 0x28, 0x2b, 0xb6, 0x0c, 0x0b, 0x56, + 0xfd, 0x24, 0x64, 0xc3, 0x35, 0x54, 0x39, 0x36, + 0x52, 0x1c, 0x24, 0x40, 0x30, 0x85, 0xd5, 0x9a, + 0x44, 0x9a, 0x50, 0x37, 0x51, 0x4a, 0x87, 0x9d +}; + +/* + * X448 known-answer ECDH test using RFC 7748 Section 6.2 vectors. + * Validates endian-swap logic in wp_x448_derive by comparing the + * derived shared secret against the expected RFC output. + */ +int test_ecdh_x448_vector(void *data) +{ + int err = 0; + EVP_PKEY *keyA = NULL; + EVP_PKEY *keyB = NULL; + unsigned char *secret = NULL; + + (void)data; + + PRINT_MSG("X448 ECDH known-answer test (RFC 7748 Sec 6.2)"); + + keyA = EVP_PKEY_new_raw_private_key_ex(wpLibCtx, "X448", NULL, + x448_alice_priv, sizeof(x448_alice_priv)); + if (keyA == NULL) { + PRINT_ERR_MSG("Failed to import X448 Alice private key"); + err = 1; + } + if (err == 0) { + keyB = EVP_PKEY_new_raw_private_key_ex(wpLibCtx, "X448", NULL, + x448_bob_priv, sizeof(x448_bob_priv)); + if (keyB == NULL) { + PRINT_ERR_MSG("Failed to import X448 Bob private key"); + err = 1; + } + } + if (err == 0) { + err = test_ecdh_derive(keyA, keyB, &secret, 56); + } + if (err == 0) { + if (memcmp(secret, x448_shared_secret, 56) != 0) { + PRINT_ERR_MSG("X448 shared secret does not match expected"); + PRINT_BUFFER("Got", secret, 56); + PRINT_BUFFER("Expected", + (unsigned char *)x448_shared_secret, 56); + err = 1; + } + } + + OPENSSL_free(secret); + EVP_PKEY_free(keyB); + EVP_PKEY_free(keyA); + + return err; +} +#endif /* WP_HAVE_X448 */ + #endif /* WP_HAVE_ECDH */ #ifdef WP_HAVE_ECDSA diff --git a/test/test_ecx.c b/test/test_ecx.c index 99304fc..325ed33 100644 --- a/test/test_ecx.c +++ b/test/test_ecx.c @@ -693,4 +693,71 @@ int test_ecx_null_init(void* data) return err; } +#ifdef WP_HAVE_X25519 +/* + * Test that importing an X25519 private key and reading it back via + * EVP_PKEY_get_params returns the original unclamped bytes. + * X25519 clamping modifies the first and last byte internally, but the + * get_params readback must return the original imported bytes. + */ +int test_ecx_x25519_raw_priv_roundtrip(void *data) +{ + int err = 0; + EVP_PKEY *pkey = NULL; + /* RFC 7748 Section 6.1 Alice private key -- first byte 0x77 clamps to + * 0x70, last byte 0x2a clamps to 0x6a. */ + static const unsigned char privKey[] = { + 0x77, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 0x7d, + 0x3c, 0x16, 0xc1, 0x72, 0x51, 0xb2, 0x66, 0x45, + 0xdf, 0x4c, 0x2f, 0x87, 0xeb, 0xc0, 0x99, 0x2a, + 0xb1, 0x77, 0xfb, 0xa5, 0x1d, 0xb9, 0x2c, 0x2a + }; + unsigned char readback[32]; + OSSL_PARAM params[2]; + + (void)data; + + PRINT_MSG("X25519 raw private key get_params roundtrip"); + + pkey = EVP_PKEY_new_raw_private_key_ex(wpLibCtx, "X25519", NULL, + privKey, sizeof(privKey)); + if (pkey == NULL) { + PRINT_ERR_MSG("Failed to import X25519 private key"); + err = 1; + } + + /* Use EVP_PKEY_get_params to exercise the get_params code path + * (EVP_PKEY_get_raw_private_key uses the export path instead). */ + if (err == 0) { + params[0] = OSSL_PARAM_construct_octet_string( + OSSL_PKEY_PARAM_PRIV_KEY, readback, sizeof(readback)); + params[1] = OSSL_PARAM_construct_end(); + if (EVP_PKEY_get_params(pkey, params) != 1) { + PRINT_ERR_MSG("EVP_PKEY_get_params for priv key failed"); + err = 1; + } + } + if (err == 0) { + if (params[0].return_size != sizeof(privKey)) { + PRINT_ERR_MSG("Readback length mismatch: %zu vs %zu", + params[0].return_size, sizeof(privKey)); + err = 1; + } + } + if (err == 0) { + if (memcmp(readback, privKey, sizeof(privKey)) != 0) { + PRINT_ERR_MSG("X25519 private key get_params does not match " + "original (unclamped bytes not restored)"); + PRINT_BUFFER("Got", readback, (int)sizeof(privKey)); + PRINT_BUFFER("Expected", (unsigned char *)privKey, + (int)sizeof(privKey)); + err = 1; + } + } + + EVP_PKEY_free(pkey); + return err; +} +#endif /* WP_HAVE_X25519 */ + #endif /* defined(WP_HAVE_ED25519) || defined(WP_HAVE_ECD444) */ diff --git a/test/test_hkdf.c b/test/test_hkdf.c index 9a09814..d8d940f 100644 --- a/test/test_hkdf.c +++ b/test/test_hkdf.c @@ -535,6 +535,82 @@ static int test_hkdf_fail(void) return err; } +/* + * Test that HKDF Extract-Only mode rejects output lengths that do not match + * the hash digest size. + */ +static int test_hkdf_extract_only_bad_len(OSSL_LIB_CTX *libCtx) +{ + int err = 0; + EVP_PKEY_CTX *ctx = NULL; + unsigned char inKey[32] = { 0, }; + unsigned char salt[32] = { 0, }; + unsigned char out[64]; + size_t len; + int mdSize; + + PRINT_MSG("HKDF Extract-Only with wrong output length"); + + mdSize = EVP_MD_get_size(EVP_sha256()); + if (mdSize <= 0) { + PRINT_ERR_MSG("EVP_MD_get_size(EVP_sha256()) failed: %d", mdSize); + err = 1; + } + if (err == 0) { + ctx = EVP_PKEY_CTX_new_from_name(libCtx, "HKDF", NULL); + if (ctx == NULL) { + err = 1; + } + } + if (err == 0) { + err = EVP_PKEY_derive_init(ctx) != 1; + } + if (err == 0) { + err = EVP_PKEY_CTX_hkdf_mode(ctx, EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY) != 1; + } + if (err == 0) { + err = EVP_PKEY_CTX_set_hkdf_md(ctx, EVP_sha256()) != 1; + } + if (err == 0) { + err = EVP_PKEY_CTX_set1_hkdf_key(ctx, inKey, sizeof(inKey)) != 1; + } + if (err == 0) { + err = EVP_PKEY_CTX_set1_hkdf_salt(ctx, salt, sizeof(salt)) != 1; + } + + /* Request wrong length (too short) -- must fail */ + if (err == 0) { + len = (size_t)(mdSize - 1); + if (EVP_PKEY_derive(ctx, out, &len) == 1) { + PRINT_ERR_MSG("Extract-Only should reject len %zu (md=%d)", + len, mdSize); + err = 1; + } + } + + /* Request wrong length (too long) -- must fail */ + if (err == 0) { + len = (size_t)(mdSize + 1); + if (EVP_PKEY_derive(ctx, out, &len) == 1) { + PRINT_ERR_MSG("Extract-Only should reject len %zu (md=%d)", + len, mdSize); + err = 1; + } + } + + /* Request correct length -- must succeed */ + if (err == 0) { + len = (size_t)mdSize; + if (EVP_PKEY_derive(ctx, out, &len) != 1) { + PRINT_ERR_MSG("Extract-Only should accept len %zu", len); + err = 1; + } + } + + EVP_PKEY_CTX_free(ctx); + return err; +} + #define NUM_MODES 3 int test_hkdf(void *data) @@ -574,6 +650,9 @@ int test_hkdf(void *data) if (err == 0) { err = test_hkdf_fail(); } + if (err == 0) { + err = test_hkdf_extract_only_bad_len(wpLibCtx); + } return err; } diff --git a/test/test_rand_seed.c b/test/test_rand_seed.c index 1692c89..4d08aa2 100644 --- a/test/test_rand_seed.c +++ b/test/test_rand_seed.c @@ -421,6 +421,110 @@ static int test_seed_src_three_level(OSSL_LIB_CTX *libCtx, const char *propq) return err; } +/** + * Test that SEED-SRC remains usable after a child DRBG instantiates from it. + * Instantiates two child DRBGs from the same SEED-SRC parent and verifies + * both can generate random bytes. + */ +static int test_seed_src_multi_child(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 *child1_ctx = NULL; + EVP_RAND_CTX *child2_ctx = NULL; + unsigned char buf[32]; + OSSL_PARAM params[2]; + + PRINT_MSG("Testing SEED-SRC survives multiple child DRBG instantiations"); + + seed_src = EVP_RAND_fetch(libCtx, "SEED-SRC", propq); + ctr_drbg = EVP_RAND_fetch(libCtx, "CTR-DRBG", propq); + if (seed_src == NULL || ctr_drbg == NULL) { + PRINT_ERR_MSG("Failed to fetch RAND algorithms"); + 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; + } + + /* First child DRBG: instantiate from SEED-SRC (calls get_seed + clear_seed) */ + child1_ctx = EVP_RAND_CTX_new(ctr_drbg, seed_ctx); + if (child1_ctx == NULL) { + PRINT_ERR_MSG("Failed to create first child DRBG"); + err = 1; + goto cleanup; + } + 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(child1_ctx, params) != 1) { + PRINT_ERR_MSG("Failed to set first child DRBG params"); + err = 1; + goto cleanup; + } + if (EVP_RAND_instantiate(child1_ctx, 256, 0, NULL, 0, NULL) != 1) { + PRINT_ERR_MSG("Failed to instantiate first child DRBG"); + err = 1; + goto cleanup; + } + PRINT_MSG("First child DRBG instantiated OK"); + + /* Second child DRBG: instantiate from same SEED-SRC. + * Before the fix, this fails because clear_seed set SEED-SRC to + * UNINITIALISED and get_seed returns 0. */ + child2_ctx = EVP_RAND_CTX_new(ctr_drbg, seed_ctx); + if (child2_ctx == NULL) { + PRINT_ERR_MSG("Failed to create second child DRBG"); + err = 1; + goto cleanup; + } + if (EVP_RAND_CTX_set_params(child2_ctx, params) != 1) { + PRINT_ERR_MSG("Failed to set second child DRBG params"); + err = 1; + goto cleanup; + } + if (EVP_RAND_instantiate(child2_ctx, 256, 0, NULL, 0, NULL) != 1) { + PRINT_ERR_MSG("Failed to instantiate second child DRBG from same " + "SEED-SRC"); + err = 1; + goto cleanup; + } + PRINT_MSG("Second child DRBG instantiated OK"); + + /* Verify both children can generate */ + if (EVP_RAND_generate(child1_ctx, buf, sizeof(buf), 256, 0, NULL, 0) != 1) { + PRINT_ERR_MSG("Failed to generate from first child DRBG"); + err = 1; + goto cleanup; + } + if (EVP_RAND_generate(child2_ctx, buf, sizeof(buf), 256, 0, NULL, 0) != 1) { + PRINT_ERR_MSG("Failed to generate from second child DRBG"); + err = 1; + goto cleanup; + } + PRINT_MSG("Both child DRBGs generate OK after shared SEED-SRC parent"); + +cleanup: + EVP_RAND_CTX_free(child2_ctx); + EVP_RAND_CTX_free(child1_ctx); + EVP_RAND_CTX_free(seed_ctx); + EVP_RAND_free(ctr_drbg); + EVP_RAND_free(seed_src); + return err; +} + /** * Main test entry point - runs tests with OpenSSL default provider and wolfProvider. */ @@ -462,6 +566,9 @@ int test_rand_seed(void *data) if (err == 0) { err = test_seed_src_three_level(wpLibCtx, NULL); } + if (err == 0) { + err = test_seed_src_multi_child(wpLibCtx, NULL); + } if (err == 0) { PRINT_MSG("=== All DRBG SEED-SRC hierarchy tests passed ==="); diff --git a/test/unit.c b/test/unit.c index 564b05a..6fd2940 100644 --- a/test/unit.c +++ b/test/unit.c @@ -271,6 +271,7 @@ TEST_CASE test_case[] = { TEST_DECL(test_aes128_gcm_fixed, NULL), TEST_DECL(test_aes128_gcm_tls, NULL), TEST_DECL(test_aes128_gcm_set_iv_inv, NULL), + TEST_DECL(test_aes_gcm_bad_tag, NULL), #endif #ifdef WP_HAVE_AESCCM TEST_DECL(test_aes128_ccm, NULL), @@ -278,6 +279,7 @@ TEST_CASE test_case[] = { TEST_DECL(test_aes256_ccm, NULL), #if OPENSSL_VERSION_NUMBER >= 0x10100000L TEST_DECL(test_aes128_ccm_tls, NULL), + TEST_DECL(test_aes_ccm_bad_tag, NULL), #endif #endif #ifdef WP_HAVE_RANDOM @@ -407,6 +409,7 @@ TEST_CASE test_case[] = { #ifdef WP_HAVE_ECKEYGEN TEST_DECL(test_ecdh_x25519_keygen, NULL), #endif + TEST_DECL(test_ecdh_x25519_vector, NULL), #endif #endif #ifdef WP_HAVE_X448 @@ -417,6 +420,7 @@ TEST_CASE test_case[] = { #ifdef WP_HAVE_ECKEYGEN TEST_DECL(test_ecdh_x448_keygen, NULL), #endif + TEST_DECL(test_ecdh_x448_vector, NULL), #endif #endif #ifdef WP_HAVE_ECKEYGEN @@ -440,6 +444,9 @@ 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), +#ifdef WP_HAVE_X25519 + TEST_DECL(test_ecx_x25519_raw_priv_roundtrip, NULL), +#endif #endif TEST_DECL(test_pkcs7_x509_sign_verify, NULL), diff --git a/test/unit.h b/test/unit.h index 41557eb..3278e42 100644 --- a/test/unit.h +++ b/test/unit.h @@ -201,6 +201,7 @@ int test_aes256_gcm(void *data); int test_aes128_gcm_fixed(void *data); int test_aes128_gcm_tls(void *data); int test_aes128_gcm_set_iv_inv(void *data); +int test_aes_gcm_bad_tag(void *data); #endif /* WP_HAVE_AESGCM */ @@ -210,6 +211,7 @@ int test_aes128_ccm(void *data); int test_aes192_ccm(void *data); int test_aes256_ccm(void *data); int test_aes128_ccm_tls(void *data); +int test_aes_ccm_bad_tag(void *data); #endif /* WP_HAVE_AESCCM */ @@ -358,6 +360,13 @@ int test_ecdh_x448_keygen(void *data); #endif /* WP_HAVE_ECKEYGEN */ +#ifdef WP_HAVE_X25519 +int test_ecdh_x25519_vector(void *data); +#endif /* WP_HAVE_X25519 */ +#ifdef WP_HAVE_X448 +int test_ecdh_x448_vector(void *data); +#endif /* WP_HAVE_X448 */ + #ifdef WP_HAVE_EC_P192 int test_ecdh_p192(void *data); #endif /* WP_HAVE_EC_P192 */ @@ -431,6 +440,9 @@ 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); +#ifdef WP_HAVE_X25519 +int test_ecx_x25519_raw_priv_roundtrip(void *data); +#endif /* WP_HAVE_X25519 */ #endif int test_pkcs7_x509_sign_verify(void *data);