diff --git a/fips_algorithms.h b/fips_algorithms.h index 625cb66..c94c9a2 100644 --- a/fips_algorithms.h +++ b/fips_algorithms.h @@ -69,6 +69,7 @@ SFTKFIPSAlgorithmList sftk_fips_mechs[] = { #define CKF_ECW (CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP) #define CKF_WRP (CKF_WRAP | CKF_UNWRAP) #define CKF_KEK (CKF_WRAP | CKF_UNWRAP) +#define CKF_KEM (CKF_ENCAPSULATE | CKF_DECAPSULATE ) #define CKF_KEA CKF_DERIVE #define CKF_KDF CKF_DERIVE #define CKF_HSH CKF_DIGEST @@ -116,11 +117,12 @@ SFTKFIPSAlgorithmList sftk_fips_mechs[] = { { CKM_ECDSA_SHA384, { EC_FB_KEY, CKF_SGN }, EC_FB_STEP, SFTKFIPSECC }, { CKM_ECDSA_SHA512, { EC_FB_KEY, CKF_SGN }, EC_FB_STEP, SFTKFIPSECC }, /* only allowed keys are implented for ML_DSA */ - { CKM_ML_DSA_KEY_PAIR_GEN, { CK_ALL_KEY, CKF_SGN }, CK_ALL_STEP, SFTKFIPSNone }, + { CKM_ML_DSA_KEY_PAIR_GEN, { CK_ALL_KEY, CKF_KPG }, CK_ALL_STEP, SFTKFIPSNone }, { CKM_ML_DSA, { CK_ALL_KEY, CKF_SGN }, CK_ALL_STEP, SFTKFIPSNone }, /* only allowed keys are implented for ML_KEM */ - { CKM_ML_KEM_KEY_PAIR_GEN, { CK_ALL_KEY, CKF_SGN }, CK_ALL_STEP, SFTKFIPSMLKEM }, - { CKM_ML_KEM, { CK_ALL_KEY, CKF_SGN }, CK_ALL_STEP, SFTKFIPSMLKEM }, + { CKM_ML_KEM_KEY_PAIR_GEN, { CK_ALL_KEY, CKF_KPG }, CK_ALL_STEP, SFTKFIPSMLKEM }, + + { CKM_ML_KEM, { CK_ALL_KEY, CKF_KEM }, CK_ALL_STEP, SFTKFIPSMLKEM }, /* ------------------------- RC2 Operations --------------------------- */ /* ------------------------- AES Operations --------------------------- */ { CKM_AES_KEY_GEN, { AES_FB_KEY, CKF_GEN }, AES_FB_STEP, SFTKFIPSNone }, @@ -185,7 +187,7 @@ SFTKFIPSAlgorithmList sftk_fips_mechs[] = { * to set the FIPS indicators on these (sigh) */ /* NOTE: CKM_NSS_ML_KEM_KEY_GEN and the KYBER equivalent does not do * pairwise consistency checks on key gen, so are not FIPS */ - { CKM_NSS_ML_KEM, { CK_ALL_KEY, CKF_SGN }, CK_ALL_STEP, SFTKFIPSNone }, + { CKM_NSS_ML_KEM, { CK_ALL_KEY, CKF_KEM }, CK_ALL_STEP, SFTKFIPSMLKEM }, { CKM_NSS_AES_KEY_WRAP, { AES_FB_KEY, CKF_ECW }, AES_FB_STEP, SFTKFIPSNone }, { CKM_NSS_AES_KEY_WRAP_PAD, { AES_FB_KEY, CKF_ECW }, AES_FB_STEP, SFTKFIPSNone }, { CKM_NSS_TLS_KEY_AND_MAC_DERIVE_SHA256, { 384, 384, CKF_DERIVE }, 1, SFTKFIPSTlsKeyCheck }, @@ -206,6 +208,5 @@ SFTKFIPSAlgorithmList sftk_fips_mechs[] = { * resulting key will also be FIPS and the resulting operations will be * FIPS approved. */ { CKM_CONCATENATE_BASE_AND_KEY, { 112, CK_MAX, CKF_DERIVE }, 1, SFTKFIPSNone }, - { CKM_CONCATENATE_BASE_AND_DATA, { 112, CK_MAX, CKF_DERIVE }, 1, SFTKFIPSNone }, }; const int SFTK_NUMBER_FIPS_ALGORITHMS = PR_ARRAY_SIZE(sftk_fips_mechs); diff --git a/nss-3.112-mlkem-fips-update.patch b/nss-3.112-mlkem-fips-update.patch new file mode 100644 index 0000000..15a798a --- /dev/null +++ b/nss-3.112-mlkem-fips-update.patch @@ -0,0 +1,685 @@ +# HG changeset patch +# User Robert Relyea +# Date 1761684967 25200 +# Tue Oct 28 13:56:07 2025 -0700 +# Branch RHEL10 +# Node ID 418f1657f74f96af94aa91b1fcf39d7c4740b3fa +# Parent a2ec761c754f728b669dc32ea94e260362b43b5b +nss-3.112-mlkem-fips-update.patch + +diff --git a/lib/freebl/fipsfreebl.c b/lib/freebl/fipsfreebl.c +--- a/lib/freebl/fipsfreebl.c ++++ b/lib/freebl/fipsfreebl.c +@@ -1797,29 +1797,34 @@ loser: + + static SECStatus + freebl_ML_KEM_Test(KyberParams param_set, + const unsigned char *seed, size_t seed_len, + const unsigned char *enc_seed, size_t enc_seed_len, + const unsigned char *pub_key, size_t pub_key_len, + const unsigned char *priv_key, size_t priv_key_len, + const unsigned char *cipher_text, size_t cipher_text_len, +- const unsigned char *key, size_t key_len) ++ const unsigned char *key, size_t key_len, ++ const unsigned char *rej_key, size_t rej_key_len) + { + SECStatus rv; + unsigned char cipher_text_buf[MAX_ML_KEM_CIPHER_LENGTH]; ++ unsigned char zero_text_buf[MAX_ML_KEM_CIPHER_LENGTH] = { 0 }; + unsigned char priv_key_buf[MAX_ML_KEM_PRIVATE_KEY_LENGTH]; + unsigned char pub_key_buf[MAX_ML_KEM_PUBLIC_KEY_LENGTH]; + unsigned char key_buf[KYBER_SHARED_SECRET_BYTES]; + unsigned char key2_buf[KYBER_SHARED_SECRET_BYTES]; ++ unsigned char key3_buf[KYBER_SHARED_SECRET_BYTES]; + SECItem ct_item = { siBuffer, cipher_text_buf, cipher_text_len }; ++ SECItem zt_item = { siBuffer, zero_text_buf, cipher_text_len }; + SECItem priv_key_item = { siBuffer, priv_key_buf, priv_key_len }; + SECItem pub_key_item = { siBuffer, pub_key_buf, pub_key_len }; + SECItem key_item = { siBuffer, key_buf, sizeof(key_buf) }; + SECItem key2_item = { siBuffer, key2_buf, sizeof(key2_buf) }; ++ SECItem key3_item = { siBuffer, key3_buf, sizeof(key3_buf) }; + SECItem seed_item = { siBuffer, (unsigned char *)seed, seed_len }; + SECItem eseed_item = { siBuffer, (unsigned char *)enc_seed, enc_seed_len }; + + PORT_Assert(pub_key_len <= sizeof(pub_key_buf)); + PORT_Assert(priv_key_len <= sizeof(priv_key_buf)); + PORT_Assert(cipher_text_len <= sizeof(cipher_text_buf)); + + rv = Kyber_NewKey(param_set, &seed_item, &priv_key_item, &pub_key_item); +@@ -1828,43 +1833,53 @@ freebl_ML_KEM_Test(KyberParams param_set + return rv; + } + + if ((priv_key_item.len != priv_key_len) || + (pub_key_item.len != pub_key_len) || + (PORT_Memcmp(priv_key_item.data, priv_key, priv_key_len) != 0) || + (PORT_Memcmp(pub_key_item.data, pub_key, pub_key_len) != 0)) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); +- return rv; ++ return SECFailure; + } + + rv = Kyber_Encapsulate(param_set, &eseed_item, &pub_key_item, + &ct_item, &key_item); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return rv; + } + + if ((ct_item.len != cipher_text_len) || + (key_item.len != key_len) || + (PORT_Memcmp(ct_item.data, cipher_text, cipher_text_len) != 0) || + (PORT_Memcmp(key_item.data, key, key_len) != 0)) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); +- return rv; ++ return SECFailure; + } + + rv = Kyber_Decapsulate(param_set, &priv_key_item, &ct_item, &key2_item); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return rv; + } + if (SECITEM_CompareItem(&key2_item, &key_item) != 0) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); ++ return SECFailure; ++ } ++ rv = Kyber_Decapsulate(param_set, &priv_key_item, &zt_item, &key3_item); ++ if (rv != SECSuccess) { ++ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return rv; + } ++ if ((key3_item.len != rej_key_len) || ++ (PORT_Memcmp(key3_item.data, rej_key, rej_key_len) != 0)) { ++ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); ++ return SECFailure; ++ } + return SECSuccess; + } + + static SECStatus + freebl_fips_ML_KEM_PowerUpSelfTest() + { + SECStatus rv; + +@@ -1881,27 +1896,29 @@ freebl_fips_ML_KEM_PowerUpSelfTest() + PORT_Assert(sizeof (ml_kem1024_key) == KYBER_SHARED_SECRET_BYTES); + + rv = freebl_ML_KEM_Test(params_ml_kem768_test_mode, + ml_kem_key_seed, KYBER_KEYPAIR_COIN_BYTES, + ml_kem_enc_seed, KYBER_SHARED_SECRET_BYTES, + ml_kem768_pub_key, KYBER768_PUBLIC_KEY_BYTES, + ml_kem768_priv_key, KYBER768_PRIVATE_KEY_BYTES, + ml_kem768_cipher_text, KYBER768_CIPHERTEXT_BYTES, +- ml_kem768_key, KYBER_SHARED_SECRET_BYTES); ++ ml_kem768_key, KYBER_SHARED_SECRET_BYTES, ++ ml_kem768_rej_key, KYBER_SHARED_SECRET_BYTES); + if (rv != SECSuccess) { + return SECFailure; + } + rv = freebl_ML_KEM_Test(params_ml_kem1024_test_mode, + ml_kem_key_seed, KYBER_KEYPAIR_COIN_BYTES, + ml_kem_enc_seed, KYBER_SHARED_SECRET_BYTES, + ml_kem1024_pub_key, MLKEM1024_PUBLIC_KEY_BYTES, + ml_kem1024_priv_key, MLKEM1024_PRIVATE_KEY_BYTES, + ml_kem1024_cipher_text, MLKEM1024_CIPHERTEXT_BYTES, +- ml_kem1024_key, KYBER_SHARED_SECRET_BYTES); ++ ml_kem1024_key, KYBER_SHARED_SECRET_BYTES, ++ ml_kem1024_rej_key, KYBER_SHARED_SECRET_BYTES); + if (rv != SECSuccess) { + return SECFailure; + } + + return SECSuccess; + } + + #ifdef NSS_ENABLE_ML_DSA +diff --git a/lib/freebl/kyber.c b/lib/freebl/kyber.c +--- a/lib/freebl/kyber.c ++++ b/lib/freebl/kyber.c +@@ -4,16 +4,17 @@ + + #ifdef FREEBL_NO_DEPEND + #include "stubs.h" + #endif + + #include + + #include "blapi.h" ++#include "blapii.h" + #include "secerr.h" + #include "secitem.h" + + #include "kyber-pqcrystals-ref.h" + #include "kyber.h" + #include "verified/internal/libcrux_core.h" + #include "verified/libcrux_mlkem768_portable.h" + #include "verified/libcrux_mlkem768.h" +@@ -198,29 +199,39 @@ Kyber_NewKey(KyberParams params, const S + } + coins = randbuf; + } + NSS_CLASSIFY(coins, KYBER_KEYPAIR_COIN_BYTES); + if (params == params_ml_kem768 || params == params_ml_kem768_test_mode) { + libcrux_ml_kem_mlkem768_MlKem768KeyPair keys = libcrux_ml_kem_mlkem768_portable_generate_key_pair(coins); + memcpy(pubkey->data, keys.pk.value, KYBER768_PUBLIC_KEY_BYTES); + memcpy(privkey->data, keys.sk.value, KYBER768_PRIVATE_KEY_BYTES); ++ PORT_SafeZero(&keys, sizeof(keys)); + } else if (params == params_ml_kem1024 || params == params_ml_kem1024_test_mode) { + libcrux_ml_kem_mlkem1024_MlKem1024KeyPair keys = libcrux_ml_kem_mlkem1024_portable_generate_key_pair(coins); + memcpy(pubkey->data, keys.pk.value, MLKEM1024_PUBLIC_KEY_BYTES); + memcpy(privkey->data, keys.sk.value, MLKEM1024_PRIVATE_KEY_BYTES); ++ PORT_SafeZero(&keys, sizeof(keys)); + #ifndef NSS_DISABLE_KYBER + } else if (params == params_kyber768_round3 || params == params_kyber768_round3_test_mode) { + pqcrystals_kyber768_ref_keypair_derand(pubkey->data, privkey->data, coins); + #endif + } else { + /* unreachable */ ++ if (coins == randbuf) { ++ PORT_SafeZero(randbuf, sizeof(randbuf)); ++ } + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } ++ /* manual max stack count 68000 */ ++ BLAPI_CLEAR_STACK(70000); ++ if (coins == randbuf) { ++ PORT_SafeZero(randbuf, sizeof(randbuf)); ++ } + NSS_DECLASSIFY(pubkey->data, pubkey->len); + return SECSuccess; + } + + SECStatus + Kyber_Encapsulate(KyberParams params, const SECItem *enc_seed, const SECItem *pubkey, SECItem *ciphertext, SECItem *secret) + { + if (!valid_params(params)) { +@@ -239,103 +250,133 @@ Kyber_Encapsulate(KyberParams params, co + coins = enc_seed->data; + } else { + if (RNG_GenerateGlobalRandomBytes(randbuf, sizeof randbuf) != SECSuccess) { + PORT_SetError(SEC_ERROR_NEED_RANDOM); + return SECFailure; + } + coins = randbuf; + } ++ SECStatus rv = SECSuccess; + NSS_CLASSIFY(coins, KYBER_ENC_COIN_BYTES); + if (params == params_ml_kem768 || params == params_ml_kem768_test_mode) { + /* shouldn't this just use the typedef im libcrux_mlkem768.h? */ + libcrux_ml_kem_types_MlKemPublicKey_15 pk_value; + memcpy(pk_value.value, pubkey->data, KYBER768_PUBLIC_KEY_BYTES); + + bool valid_pk = libcrux_ml_kem_mlkem768_portable_validate_public_key(&pk_value); + if (!valid_pk) { ++ PORT_SafeZero(&pk_value, sizeof(pk_value)); + PORT_SetError(SEC_ERROR_INVALID_ARGS); +- return SECFailure; ++ rv = SECFailure; ++ goto loser; + } + + tuple_3c encap = libcrux_ml_kem_mlkem768_portable_encapsulate(&pk_value, coins); + memcpy(ciphertext->data, encap.fst.value, KYBER768_CIPHERTEXT_BYTES); + memcpy(secret->data, encap.snd, KYBER_SHARED_SECRET_BYTES); ++ PORT_SafeZero(&pk_value, sizeof(pk_value)); ++ PORT_SafeZero(&encap, sizeof(encap)); + } else if (params == params_ml_kem1024 || params == params_ml_kem1024_test_mode) { + /* shouldn't this just use the typedef im libcrux_mlkem1024.h? */ + libcrux_ml_kem_types_MlKemPublicKey_1f pk_value; + memcpy(pk_value.value, pubkey->data, MLKEM1024_PUBLIC_KEY_BYTES); + + bool valid_pk = libcrux_ml_kem_mlkem1024_portable_validate_public_key(&pk_value); + if (!valid_pk) { ++ PORT_SafeZero(&pk_value, sizeof(pk_value)); + PORT_SetError(SEC_ERROR_INVALID_ARGS); +- return SECFailure; ++ rv = SECFailure; ++ goto loser; + } + + tuple_21 encap = libcrux_ml_kem_mlkem1024_portable_encapsulate(&pk_value, coins); + memcpy(ciphertext->data, encap.fst.value, MLKEM1024_CIPHERTEXT_BYTES); + memcpy(secret->data, encap.snd, KYBER_SHARED_SECRET_BYTES); ++ PORT_SafeZero(&pk_value, sizeof(pk_value)); ++ PORT_SafeZero(&encap, sizeof(encap)); + #ifndef NSS_DISABLE_KYBER + } else if (params == params_kyber768_round3 || params == params_kyber768_round3_test_mode) { + pqcrystals_kyber768_ref_enc_derand(ciphertext->data, secret->data, pubkey->data, coins); + #endif + } else { + /* unreachable */ + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); +- return SECFailure; ++ rv = SECFailure; ++ goto loser; + } ++loser: ++ if (coins == randbuf) { ++ PORT_SafeZero(randbuf, sizeof(randbuf)); ++ } ++ /* manual max stack count 6700 */ ++ BLAPI_CLEAR_STACK(10000); + +- return SECSuccess; ++ return rv; + } + + SECStatus + Kyber_Decapsulate(KyberParams params, const SECItem *privkey, const SECItem *ciphertext, SECItem *secret) + { + if (!valid_params(params)) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return SECFailure; + } + + if (!(valid_privkey(params, privkey) && valid_ciphertext(params, ciphertext) && valid_secret(params, secret))) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + ++ SECStatus rv = SECSuccess; + if (params == params_ml_kem768 || params == params_ml_kem768_test_mode) { + libcrux_ml_kem_types_MlKemPrivateKey_55 private_key; + memcpy(private_key.value, privkey->data, KYBER768_PRIVATE_KEY_BYTES); + + libcrux_ml_kem_mlkem768_MlKem768Ciphertext cipher_text; + memcpy(cipher_text.value, ciphertext->data, KYBER768_CIPHERTEXT_BYTES); + + bool valid = libcrux_ml_kem_mlkem768_portable_validate_private_key(&private_key, &cipher_text); + if (!valid) { ++ PORT_SafeZero(&private_key, sizeof(private_key)); ++ PORT_SafeZero(&cipher_text, sizeof(cipher_text)); + PORT_SetError(SEC_ERROR_INVALID_ARGS); +- return SECFailure; ++ rv = SECFailure; ++ goto loser; + } + + libcrux_ml_kem_mlkem768_portable_decapsulate(&private_key, &cipher_text, secret->data); ++ PORT_SafeZero(&private_key, sizeof(private_key)); ++ PORT_SafeZero(&cipher_text, sizeof(cipher_text)); + } else if (params == params_ml_kem1024 || params == params_ml_kem1024_test_mode) { + libcrux_ml_kem_types_MlKemPrivateKey_95 private_key; + memcpy(private_key.value, privkey->data, MLKEM1024_PRIVATE_KEY_BYTES); + + libcrux_ml_kem_mlkem1024_MlKem1024Ciphertext cipher_text; + memcpy(cipher_text.value, ciphertext->data, MLKEM1024_CIPHERTEXT_BYTES); + + bool valid = libcrux_ml_kem_mlkem1024_portable_validate_private_key(&private_key, &cipher_text); + if (!valid) { ++ PORT_SafeZero(&private_key, sizeof(private_key)); ++ PORT_SafeZero(&cipher_text, sizeof(cipher_text)); + PORT_SetError(SEC_ERROR_INVALID_ARGS); +- return SECFailure; ++ rv = SECFailure; ++ goto loser; + } + + libcrux_ml_kem_mlkem1024_portable_decapsulate(&private_key, &cipher_text, secret->data); ++ PORT_SafeZero(&private_key, sizeof(private_key)); ++ PORT_SafeZero(&cipher_text, sizeof(cipher_text)); + #ifndef NSS_DISABLE_KYBER + } else if (params == params_kyber768_round3 || params == params_kyber768_round3_test_mode) { + pqcrystals_kyber768_ref_dec(secret->data, ciphertext->data, privkey->data); + #endif + } else { + // unreachable + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } ++loser: ++ /* manual max stack count 8200 */ ++ BLAPI_CLEAR_STACK(10000); + +- return SECSuccess; ++ return rv; + } +diff --git a/lib/freebl/ml_kem_fips_vectors.h b/lib/freebl/ml_kem_fips_vectors.h +--- a/lib/freebl/ml_kem_fips_vectors.h ++++ b/lib/freebl/ml_kem_fips_vectors.h +@@ -415,16 +415,21 @@ + 0xd6, 0x18, 0xf6, 0x5a, 0x70, 0x0b, 0x41, 0xf0, 0x17, 0x08, 0x67, 0x98, + 0x2f, 0x5a, 0x68, 0xc7, 0x5c, 0x65, 0x15, 0x0e, + }; + static const unsigned char ml_kem768_key[] = { + 0x42, 0xf5, 0x58, 0xb0, 0xbc, 0x5d, 0x70, 0x0a, 0x91, 0x1b, 0x0f, 0xc6, + 0x7f, 0x62, 0x37, 0x6f, 0x7a, 0xee, 0x46, 0x67, 0xf1, 0x96, 0x9e, 0x03, + 0xf1, 0x8b, 0xdf, 0xdf, 0x3c, 0x59, 0xfb, 0xdc, + }; ++ static const unsigned char ml_kem768_rej_key[] = { ++ 0x6b, 0x24, 0x6e, 0xb1, 0x8b, 0x55, 0x3a, 0xb4, 0xcc, 0x4a, 0x57, 0x04, ++ 0x4d, 0x8d, 0x15, 0x34, 0xa8, 0x85, 0xd1, 0xd3, 0x42, 0x14, 0xc9, 0x87, ++ 0x78, 0x96, 0x19, 0xcb, 0x73, 0xe6, 0x12, 0x9a, ++ }; + static const unsigned char ml_kem1024_pub_key[] = { + 0x4b, 0x94, 0xc2, 0x94, 0x50, 0x11, 0x11, 0x91, 0x82, 0x3b, 0x35, 0x14, + 0xc9, 0xac, 0x1e, 0xa3, 0xd9, 0x82, 0x5c, 0xcb, 0x86, 0x39, 0x3a, 0x2d, + 0xfb, 0x04, 0x65, 0x4f, 0xa2, 0x19, 0x2d, 0x37, 0xbf, 0xad, 0x1c, 0x49, + 0x7c, 0x65, 0x02, 0xee, 0xe5, 0xca, 0x80, 0xa7, 0x3b, 0xfc, 0xe0, 0xba, + 0xf5, 0xa5, 0x4a, 0x88, 0x58, 0x5a, 0x40, 0x13, 0x97, 0xa3, 0xd2, 0x32, + 0xf4, 0x26, 0xa7, 0xaf, 0xb0, 0x82, 0xbc, 0x21, 0xa4, 0x43, 0x17, 0x09, + 0x0e, 0xaa, 0xc7, 0x59, 0x2c, 0x2e, 0xa8, 0x8a, 0x65, 0x3c, 0x44, 0x91, +@@ -952,8 +957,13 @@ + 0x3b, 0xaf, 0x03, 0xad, 0xf1, 0xab, 0x8f, 0x86, 0xea, 0x46, 0x28, 0xf8, + 0x53, 0x80, 0xee, 0x89, 0x53, 0xd4, 0xa1, 0xad, + }; + static const unsigned char ml_kem1024_key[] = { + 0x73, 0x8a, 0x88, 0xcd, 0x9a, 0x25, 0x86, 0x1e, 0x9a, 0x37, 0xc6, 0x5a, + 0x67, 0xec, 0x1b, 0x39, 0xf0, 0x69, 0xae, 0xd9, 0x64, 0x6e, 0xde, 0x89, + 0x41, 0x0a, 0xce, 0x47, 0xa9, 0x6a, 0x9b, 0x57, + }; ++ static const unsigned char ml_kem1024_rej_key[] = { ++ 0x6a, 0x58, 0xe1, 0xc5, 0x96, 0x86, 0xc9, 0xfc, 0x32, 0x2e, 0xc2, 0x6b, ++ 0x6e, 0xa0, 0xd5, 0x27, 0x22, 0x67, 0x29, 0x2c, 0xe9, 0xa4, 0x0f, 0x12, ++ 0x75, 0x13, 0x96, 0xbc, 0x53, 0x22, 0x72, 0x46, ++ }; +diff --git a/lib/softoken/kem.c b/lib/softoken/kem.c +--- a/lib/softoken/kem.c ++++ b/lib/softoken/kem.c +@@ -198,16 +198,18 @@ NSC_EncapsulateKey(CK_SESSION_HANDLE hSe + SFTKAttribute *encapsulationKey = NULL; + + CK_RV crv; + SFTKFreeStatus status; + CK_ULONG paramSet = 0; /* use a generic U_LONG so we can handle + * different param set types based on the + * Mechanism value */ + KyberParams kyberParams; ++ CK_OBJECT_CLASS ckclass = CKO_SECRET_KEY; ++ CK_KEY_TYPE ckkeyType = CKK_GENERIC_SECRET; + + CHECK_FORK(); + + if (!pMechanism || !phKey || !pulCiphertextLen) { + return CKR_ARGUMENTS_BAD; + } + + if (!sftk_kem_ValidateMechanism(pMechanism)) { +@@ -232,16 +234,25 @@ NSC_EncapsulateKey(CK_SESSION_HANDLE hSe + } + for (unsigned long int i = 0; i < ulAttributeCount; i++) { + crv = sftk_AddAttributeType(key, sftk_attr_expand(&pTemplate[i])); + if (crv != CKR_OK) { + goto cleanup; + } + } + ++ crv = sftk_defaultAttribute(key, CKA_CLASS, &ckclass, sizeof(ckclass)); ++ if (crv != CKR_OK) { ++ goto cleanup; ++ } ++ crv = sftk_defaultAttribute(key, CKA_KEY_TYPE, &ckkeyType, sizeof(ckkeyType)); ++ if (crv != CKR_OK) { ++ goto cleanup; ++ } ++ + encapsulationKeyObject = sftk_ObjectFromHandle(hPublicKey, session); + if (encapsulationKeyObject == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + goto cleanup; + } + encapsulationKey = sftk_FindAttribute(encapsulationKeyObject, CKA_VALUE); + if (encapsulationKey == NULL) { + crv = CKR_KEY_HANDLE_INVALID; +@@ -264,17 +275,17 @@ NSC_EncapsulateKey(CK_SESSION_HANDLE hSe + SECItem pubKey = { siBuffer, encapsulationKey->attrib.pValue, encapsulationKey->attrib.ulValueLen }; + + /* The length of secretBuf can be increased if we ever support other KEMs + * by changing the define at the top of this file */ + uint8_t secretBuf[MAX_KEM_SHARED_SECRET_BYTES] = { 0 }; + SECItem secret = { siBuffer, secretBuf, sizeof secretBuf }; + + key->isFIPS = sftk_operationIsFIPS(slot, pMechanism, CKA_ENCAPSULATE, +- key, 0); ++ encapsulationKeyObject, 0); + key->source = SFTK_SOURCE_KEA; + switch (pMechanism->mechanism) { + #ifndef NSS_DISABLE_KYBER + case CKM_NSS_KYBER: + #endif + case CKM_NSS_ML_KEM: + case CKM_ML_KEM: + PORT_Assert(secret.len >= KYBER_SHARED_SECRET_BYTES); +@@ -347,16 +358,18 @@ NSC_DecapsulateKey(CK_SESSION_HANDLE hSe + SFTKAttribute *decapsulationKey = NULL; + CK_ULONG paramSet = 0; /* use a generic U_LONG so we can handle + * different param set types based on the + * Mechanism value */ + + CK_RV crv; + SFTKFreeStatus status; + KyberParams kyberParams; ++ CK_OBJECT_CLASS ckclass = CKO_SECRET_KEY; ++ CK_KEY_TYPE ckkeyType = CKK_GENERIC_SECRET; + + CHECK_FORK(); + + if (!pMechanism || !pCiphertext || !pTemplate || !phKey) { + return CKR_ARGUMENTS_BAD; + } + + if (!sftk_kem_ValidateMechanism(pMechanism)) { +@@ -381,16 +394,25 @@ NSC_DecapsulateKey(CK_SESSION_HANDLE hSe + } + for (unsigned long int i = 0; i < ulAttributeCount; i++) { + crv = sftk_AddAttributeType(key, sftk_attr_expand(&pTemplate[i])); + if (crv != CKR_OK) { + goto cleanup; + } + } + ++ crv = sftk_defaultAttribute(key, CKA_CLASS, &ckclass, sizeof(ckclass)); ++ if (crv != CKR_OK) { ++ goto cleanup; ++ } ++ crv = sftk_defaultAttribute(key, CKA_KEY_TYPE, &ckkeyType, sizeof(ckkeyType)); ++ if (crv != CKR_OK) { ++ goto cleanup; ++ } ++ + decapsulationKeyObject = sftk_ObjectFromHandle(hPrivateKey, session); + if (decapsulationKeyObject == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + goto cleanup; + } + decapsulationKey = sftk_FindAttribute(decapsulationKeyObject, CKA_VALUE); + if (decapsulationKey == NULL) { + crv = CKR_KEY_HANDLE_INVALID; +@@ -412,17 +434,17 @@ NSC_DecapsulateKey(CK_SESSION_HANDLE hSe + decapsulationKey->attrib.ulValueLen }; + SECItem ciphertext = { siBuffer, pCiphertext, ulCiphertextLen }; + + /* The length of secretBuf can be increased if we ever support other KEMs + * by changing the define at the top of this file */ + uint8_t secretBuf[MAX_KEM_SHARED_SECRET_BYTES] = { 0 }; + SECItem secret = { siBuffer, secretBuf, sizeof secretBuf }; + key->isFIPS = sftk_operationIsFIPS(slot, pMechanism, CKA_DECAPSULATE, +- key, 0); ++ decapsulationKeyObject, 0); + key->source = SFTK_SOURCE_KEA; + + switch (pMechanism->mechanism) { + #ifndef NSS_DISABLE_KYBER + case CKM_NSS_KYBER: + #endif + case CKM_NSS_ML_KEM: + case CKM_ML_KEM: +diff --git a/lib/softoken/pkcs11c.c b/lib/softoken/pkcs11c.c +--- a/lib/softoken/pkcs11c.c ++++ b/lib/softoken/pkcs11c.c +@@ -5236,17 +5236,17 @@ sftk_compareKeysEqual(CK_SESSION_HANDLE + SFTKObject *key1obj = NULL; + SFTKObject *key2obj = NULL; + SFTKAttribute *att1 = NULL; + SFTKAttribute *att2 = NULL; + + /* fetch the pkcs11 objects from the handles */ + session = sftk_SessionFromHandle(hSession); + if (session == NULL) { +- return CKR_SESSION_HANDLE_INVALID; ++ return PR_FALSE; + } + + key1obj = sftk_ObjectFromHandle(key1, session); + key2obj = sftk_ObjectFromHandle(key2, session); + sftk_FreeSession(session); + if ((key1obj == NULL) || (key2obj == NULL)) { + goto loser; + } +@@ -5268,17 +5268,17 @@ sftk_compareKeysEqual(CK_SESSION_HANDLE + goto loser; + } + result = PR_TRUE; + loser: + if (key1obj) { + sftk_FreeObject(key1obj); + } + if (key2obj) { +- sftk_FreeObject(key1obj); ++ sftk_FreeObject(key2obj); + } + if (att1) { + sftk_FreeAttribute(att1); + } + if (att2) { + sftk_FreeAttribute(att2); + } + return result; +@@ -5758,24 +5758,24 @@ sftk_PairwiseConsistencyCheck(CK_SESSION + SECITEM_ZfreeItem(&prime, PR_FALSE); + } + /* clean up before we return */ + sftk_FreeAttribute(pubAttribute); + if (crv != CKR_OK) { + return crv; + } + } +- isKEM = sftk_isTrue(privateKey, CKA_ENCAPSULATE); ++ isKEM = sftk_isTrue(privateKey, CKA_DECAPSULATE); + if (isKEM) { + unsigned char *cipher_text = NULL; + CK_ULONG cipher_text_length = 0; + CK_OBJECT_HANDLE key1 = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE key2 = CK_INVALID_HANDLE; + CK_KEY_TYPE genType = CKO_SECRET_KEY; +- CK_ATTRIBUTE template = { CKA_KEY_TYPE, NULL, 0 }; ++ CK_ATTRIBUTE template = { CKA_CLASS, NULL, 0 }; + + template.pValue = &genType; + template.ulValueLen = sizeof(genType); + crv = CKR_OK; + switch (keyType) { + case CKK_ML_KEM: + cipher_text_length = MAX_ML_KEM_CIPHER_LENGTH; + mech.mechanism = CKM_ML_KEM; +@@ -7905,16 +7905,17 @@ sftk_DeriveEncrypt(SFTKCipher encrypt, v + return crv; + } + + crv = sftk_forceAttribute(key, CKA_VALUE, tmpdata, keySize); + PORT_Memset(tmpdata, 0, sizeof tmpdata); + return crv; + } + ++ + CK_RV + sftk_HKDF(CK_HKDF_PARAMS_PTR params, CK_SESSION_HANDLE hSession, + SFTKObject *sourceKey, const unsigned char *sourceKeyBytes, + int sourceKeyLen, SFTKObject *key, unsigned char *outKeyBytes, + int keySize, PRBool canBeData, PRBool isFIPS) + { + SFTKSession *session; + SFTKAttribute *saltKey_att = NULL; +@@ -7958,16 +7959,21 @@ sftk_HKDF(CK_HKDF_PARAMS_PTR params, CK_ + } + + /* sourceKey is NULL if we are called from the POST, skip the + * sensitiveCheck */ + if (sourceKey != NULL) { + crv = sftk_DeriveSensitiveCheck(sourceKey, key, canBeData); + if (crv != CKR_OK) + return crv; ++ /* if the source key is data, clear the FIPS flag ++ * and only get the FIPS state from the salt */ ++ if (sourceKey->objclass == CKO_DATA) { ++ key->isFIPS = PR_FALSE; ++ } + } + + /* HKDF-Extract(salt, base key value) */ + if (params->bExtract) { + CK_BYTE *salt; + CK_ULONG saltLen; + HMACContext *hmac; + unsigned int bufLen; +@@ -7988,16 +7994,17 @@ sftk_HKDF(CK_HKDF_PARAMS_PTR params, CK_ + break; + case CKF_HKDF_SALT_KEY: + /* lookup key */ + session = sftk_SessionFromHandle(hSession); + if (session == NULL) { + return CKR_SESSION_HANDLE_INVALID; + } + ++ + saltKey = sftk_ObjectFromHandle(params->hSaltKey, session); + sftk_FreeSession(session); + if (saltKey == NULL) { + return CKR_KEY_HANDLE_INVALID; + } + /* if the base key is not fips, but the salt key is, the + * resulting key can be fips */ + if (isFIPS && (key->isFIPS == 0) && (saltKey->isFIPS == 1)) { +@@ -9190,17 +9197,23 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession + + PORT_Memcpy(buf, att->attrib.pValue, att->attrib.ulValueLen); + PORT_Memcpy(buf + att->attrib.ulValueLen, + att2->attrib.pValue, att2->attrib.ulValueLen); + + crv = sftk_forceAttribute(key, CKA_VALUE, buf, keySize); + PORT_ZFree(buf, tmpKeySize); + /* preserve the source of the original base key */ +- /* key->source = sourceKey->source; */ ++ key->source = sourceKey->source; ++ ++ /* make sure this is fully fips approved, and mark it ++ * unapproved if not */ ++ if (key->isFIPS) { ++ key->isFIPS = paramKey->isFIPS; ++ } + sftk_FreeAttribute(att2); + sftk_FreeObject(paramKey); + break; + } + + case CKM_CONCATENATE_BASE_AND_DATA: + crv = sftk_DeriveSensitiveCheck(sourceKey, key, PR_FALSE); + if (crv != CKR_OK) +diff --git a/lib/softoken/pkcs11u.c b/lib/softoken/pkcs11u.c +--- a/lib/softoken/pkcs11u.c ++++ b/lib/softoken/pkcs11u.c +@@ -2285,16 +2285,22 @@ sftk_AttributeToFlags(CK_ATTRIBUTE_TYPE + flags = CKF_VERIFY; + break; + case CKA_VERIFY_RECOVER: + flags = CKF_VERIFY_RECOVER; + break; + case CKA_DERIVE: + flags = CKF_DERIVE; + break; ++ case CKA_ENCAPSULATE: ++ flags = CKF_ENCAPSULATE; ++ break; ++ case CKA_DECAPSULATE: ++ flags = CKF_DECAPSULATE; ++ break; + /* fake attribute to select digesting */ + case CKA_DIGEST: + flags = CKF_DIGEST; + break; + /* fake attribute to select key gen */ + case CKA_NSS_GENERATE: + flags = CKF_GENERATE; + break; diff --git a/nss-3.112-partial-pub-key-validate.patch b/nss-3.112-partial-pub-key-validate.patch new file mode 100644 index 0000000..ab36f1a --- /dev/null +++ b/nss-3.112-partial-pub-key-validate.patch @@ -0,0 +1,1501 @@ +# HG changeset patch +# User Robert Relyea +# Date 1763508464 28800 +# Tue Nov 18 15:27:44 2025 -0800 +# Branch RHEL10 +# Node ID d7226ed21acc227df5dfb0a73b99a31640fddcba +# Parent 6949dbcd6631ec0db3f53b3cf8957fac3a80da25 +nss-3.112-partial-pub-key-validate.patch + +diff --git a/cmd/bltest/blapitest.c b/cmd/bltest/blapitest.c +--- a/cmd/bltest/blapitest.c ++++ b/cmd/bltest/blapitest.c +@@ -27,28 +27,30 @@ + + SECStatus EC_DecodeParams(const SECItem *encodedParams, + ECParams **ecparams); + SECStatus EC_CopyParams(PLArenaPool *arena, ECParams *dstParams, + const ECParams *srcParams); + + char *progName; + char *testdir = NULL; ++PRBool strictOAEP = PR_FALSE; + + #define BLTEST_DEFAULT_CHUNKSIZE 4096 + + #define WORDSIZE sizeof(unsigned long) + +-#define CHECKERROR(rv, ln) \ +- if (rv) { \ ++#define CHECKERROR(rv, erv, ln) \ ++ if (rv != erv) { \ + PRErrorCode prerror = PR_GetError(); \ + PR_fprintf(PR_STDERR, "%s: ERR %d (%s) at line %d.\n", progName, \ + prerror, PORT_ErrorToString(prerror), ln); \ + exit(-1); \ +- } ++ } \ ++ rv = SECSuccess + + /* Macros for performance timing. */ + #define TIMESTART() \ + time1 = PR_IntervalNow(); + + #define TIMEFINISH(time, reps) \ + time2 = (PRIntervalTime)(PR_IntervalNow() - time1); \ + time1 = PR_IntervalToMilliseconds(time2); \ +@@ -112,16 +114,19 @@ Usage() + PRINTUSAGE("", "-m", "cipher mode to use"); + PRINTUSAGE("", "-i", "file which contains input buffer"); + PRINTUSAGE("", "-o", "file for output buffer"); + PRINTUSAGE("", "-k", "file which contains key"); + PRINTUSAGE("", "-v", "file which contains initialization vector"); + PRINTUSAGE("", "-p", "do performance test"); + PRINTUSAGE("", "-4", "run test in multithread mode. th_num number of parallel threads"); + PRINTUSAGE("", "-5", "run test for specified time interval(in seconds)"); ++ PRINTUSAGE("", "-6", "hash algorithm for RSA PSS or OAEP"); ++ PRINTUSAGE("", "-7", "mask hash algorithm for RSA PSS or OAEP"); ++ PRINTUSAGE("", "-8", "validate the public key for RSAOAEP Encrypt"); + PRINTUSAGE("", "--aad", "File with contains additional auth data"); + fprintf(stderr, "\n"); + PRINTUSAGE(progName, "-H -m mode", "Hash a buffer"); + PRINTUSAGE("", "", "[-i plaintext] [-o hash]"); + PRINTUSAGE("", "", "[-b bufsize]"); + PRINTUSAGE("", "", "[-p repetitions | -5 time_interval] [-4 th_num]"); + PRINTUSAGE("", "-m", "cipher mode to use"); + PRINTUSAGE("", "-i", "file which contains input buffer"); +@@ -370,23 +375,23 @@ eckey_from_filedata(PLArenaPool *arena, + ECPrivateKey *key; + SECStatus rv; + ECParams *tmpECParams = NULL; + key = (ECPrivateKey *)PORT_ArenaZAlloc(arena, sizeof(ECPrivateKey)); + /* read and convert params */ + key->ecParams.arena = arena; + key_from_filedata(arena, &key->ecParams.DEREncoding, 0, 1, filedata); + rv = SECOID_Init(); +- CHECKERROR(rv, __LINE__); ++ CHECKERROR(rv, SECSuccess, __LINE__); + rv = EC_DecodeParams(&key->ecParams.DEREncoding, &tmpECParams); +- CHECKERROR(rv, __LINE__); ++ CHECKERROR(rv, SECSuccess, __LINE__); + rv = EC_CopyParams(key->ecParams.arena, &key->ecParams, tmpECParams); +- CHECKERROR(rv, __LINE__); ++ CHECKERROR(rv, SECSuccess, __LINE__); + rv = SECOID_Shutdown(); +- CHECKERROR(rv, __LINE__); ++ CHECKERROR(rv, SECSuccess, __LINE__); + PORT_FreeArena(tmpECParams->arena, PR_TRUE); + /* read key */ + key_from_filedata(arena, &key->publicValue, 1, 3, filedata); + return key; + } + + typedef struct curveNameTagPairStr { + char *curveName; +@@ -799,16 +804,17 @@ struct bltestCipherInfoStr { + } cipher; + /* performance testing */ + int repetitionsToPerfom; + int seconds; + int repetitions; + int cxreps; + double cxtime; + double optime; ++ SECStatus expect; + }; + + PRBool + is_symmkeyCipher(bltestCipherMode mode) + { + /* change as needed! */ + if (mode >= bltestDES_ECB && mode <= bltestCHACHA20_CTR) + return PR_TRUE; +@@ -836,16 +842,29 @@ is_authCipher(bltestCipherMode mode) + case bltestCHACHA20: + return PR_TRUE; + default: + return PR_FALSE; + } + } + + PRBool ++is_rsaV2(bltestCipherMode mode) ++{ ++ /* change as needed! */ ++ switch (mode) { ++ case bltestRSA_OAEP: ++ case bltestRSA_PSS: ++ return PR_TRUE; ++ default: ++ return PR_FALSE; ++ } ++} ++ ++PRBool + is_singleShotCipher(bltestCipherMode mode) + { + /* change as needed! */ + switch (mode) { + case bltestAES_GCM: + case bltestAES_CTS: + case bltestCHACHA20_CTR: + case bltestCHACHA20: +@@ -1748,16 +1767,18 @@ bltest_rsa_init(bltestCipherInfo *cipher + /* Have to convert private key to public key. Memory + * is freed with private key's arena */ + pubKey = (RSAPublicKey *)PORT_ArenaAlloc(privKey->arena, + sizeof(RSAPublicKey)); + pubKey->modulus.len = privKey->modulus.len; + pubKey->modulus.data = privKey->modulus.data; + pubKey->publicExponent.len = privKey->publicExponent.len; + pubKey->publicExponent.data = privKey->publicExponent.data; ++ /* if structOAEP is set, exercise the oaep verify tests */ ++ pubKey->needVerify = strictOAEP; + asymk->pubKey = (void *)pubKey; + } + switch (cipherInfo->mode) { + case bltestRSA: + cipherInfo->cipher.pubkeyCipher = encrypt ? rsa_PublicKeyOp + : rsa_PrivateKeyOp; + break; + case bltestRSA_PSS: +@@ -1785,20 +1806,20 @@ blapi_pqg_param_gen(unsigned int keysize + } + + SECStatus + bltest_pqg_init(bltestDSAParams *dsap) + { + SECStatus rv, res; + PQGVerify *vfy = NULL; + rv = blapi_pqg_param_gen(dsap->keysize, &dsap->pqg, &vfy); +- CHECKERROR(rv, __LINE__); ++ CHECKERROR(rv, SECSuccess, __LINE__); + rv = PQG_VerifyParams(dsap->pqg, vfy, &res); +- CHECKERROR(res, __LINE__); +- CHECKERROR(rv, __LINE__); ++ CHECKERROR(res, SECSuccess, __LINE__); ++ CHECKERROR(rv, SECSuccess, __LINE__); + return rv; + } + + SECStatus + bltest_dsa_init(bltestCipherInfo *cipherInfo, PRBool encrypt) + { + int i; + DSAPrivateKey **dummyKey; +@@ -2247,48 +2268,48 @@ pubkeyInitKey(bltestCipherInfo *cipherIn + case bltestDSA: + dsap = &asymk->cipherParams.dsa; + dsaKey = (DSAPrivateKey **)&asymk->privKey; + if (keysize > 0) { + dsap->keysize = keysize * 8; + if (!dsap->pqg) + bltest_pqg_init(dsap); + rv = DSA_NewKey(dsap->pqg, dsaKey); +- CHECKERROR(rv, __LINE__); ++ CHECKERROR(rv, cipherInfo->expect, __LINE__); + serialize_key(&(*dsaKey)->params.prime, 5, file); + } else { + setupIO(cipherInfo->arena, &asymk->key, file, NULL, 0); + *dsaKey = dsakey_from_filedata(cipherInfo->arena, &asymk->key.buf); + dsap->keysize = (*dsaKey)->params.prime.len * 8; + } + break; + case bltestECDSA: + ecKey = (ECPrivateKey **)&asymk->privKey; + if (curveName != NULL) { + tmpECParamsDER = getECParams(curveName); + rv = SECOID_Init(); +- CHECKERROR(rv, __LINE__); ++ CHECKERROR(rv, SECSuccess, __LINE__); + rv = EC_DecodeParams(tmpECParamsDER, &tmpECParams) == SECFailure; +- CHECKERROR(rv, __LINE__); ++ CHECKERROR(rv, SECSuccess, __LINE__); + rv = EC_NewKey(tmpECParams, ecKey); +- CHECKERROR(rv, __LINE__); ++ CHECKERROR(rv, cipherInfo->expect, __LINE__); + ecSerialize[0].type = tmpECParamsDER->type; + ecSerialize[0].data = tmpECParamsDER->data; + ecSerialize[0].len = tmpECParamsDER->len; + ecSerialize[1].type = (*ecKey)->publicValue.type; + ecSerialize[1].data = (*ecKey)->publicValue.data; + ecSerialize[1].len = (*ecKey)->publicValue.len; + ecSerialize[2].type = (*ecKey)->privateValue.type; + ecSerialize[2].data = (*ecKey)->privateValue.data; + ecSerialize[2].len = (*ecKey)->privateValue.len; + serialize_key(&(ecSerialize[0]), 3, file); + SECITEM_FreeItem(tmpECParamsDER, PR_TRUE); + PORT_FreeArena(tmpECParams->arena, PR_TRUE); + rv = SECOID_Shutdown(); +- CHECKERROR(rv, __LINE__); ++ CHECKERROR(rv, SECSuccess, __LINE__); + } else { + setupIO(cipherInfo->arena, &asymk->key, file, NULL, 0); + *ecKey = eckey_from_filedata(cipherInfo->arena, &asymk->key.buf); + } + break; + default: + return SECFailure; + } +@@ -2494,42 +2515,42 @@ cipherDoOp(bltestCipherInfo *cipherInfo) + : PR_MIN(cipherInfo->input.pBuf.len, 16); + unsigned char *output = cipherInfo->output.pBuf.data; + unsigned int outputLen = maxLen; + unsigned int totalOutputLen = 0; + TIMESTART(); + rv = (*cipherInfo->cipher.symmkeyCipher)(cipherInfo->cx, + output, &len, outputLen, + input, inputLen); +- CHECKERROR(rv, __LINE__); ++ CHECKERROR(rv, cipherInfo->expect, __LINE__); + totalOutputLen += len; + if (cipherInfo->input.pBuf.len > inputLen) { + input += inputLen; + inputLen = cipherInfo->input.pBuf.len - inputLen; + output += len; + outputLen -= len; + rv = (*cipherInfo->cipher.symmkeyCipher)(cipherInfo->cx, + output, &len, outputLen, + input, inputLen); +- CHECKERROR(rv, __LINE__); ++ CHECKERROR(rv, cipherInfo->expect, __LINE__); + totalOutputLen += len; + } + cipherInfo->output.pBuf.len = totalOutputLen; + TIMEFINISH(cipherInfo->optime, 1.0); + cipherInfo->repetitions = 0; + if (cipherInfo->repetitionsToPerfom != 0) { + TIMESTART(); + for (i = 0; i < cipherInfo->repetitionsToPerfom; i++, + cipherInfo->repetitions++) { + (*cipherInfo->cipher.symmkeyCipher)(cipherInfo->cx, dummyOut, + &len, maxLen, + cipherInfo->input.pBuf.data, + cipherInfo->input.pBuf.len); + +- CHECKERROR(rv, __LINE__); ++ CHECKERROR(rv, cipherInfo->expect, __LINE__); + } + } else { + int opsBetweenChecks = 0; + TIMEMARK(cipherInfo->seconds); + while (!(TIMETOFINISH())) { + int j = 0; + for (; j < opsBetweenChecks; j++) { + (*cipherInfo->cipher.symmkeyCipher)( +@@ -2551,32 +2572,32 @@ cipherDoOp(bltestCipherInfo *cipherInfo) + + TIMESTART(); + rv = (*cipherInfo->cipher.aeadCipher)( + cipherInfo->cx, + output, &outputLen, maxLen, + input, inputLen, + sk->iv.buf.data, sk->iv.buf.len, + ask->aad.buf.data, ask->aad.buf.len); +- CHECKERROR(rv, __LINE__); ++ CHECKERROR(rv, cipherInfo->expect, __LINE__); + cipherInfo->output.pBuf.len = outputLen; + TIMEFINISH(cipherInfo->optime, 1.0); + + cipherInfo->repetitions = 0; + if (cipherInfo->repetitionsToPerfom != 0) { + TIMESTART(); + for (i = 0; i < cipherInfo->repetitionsToPerfom; i++, + cipherInfo->repetitions++) { + rv = (*cipherInfo->cipher.aeadCipher)( + cipherInfo->cx, + output, &outputLen, maxLen, + input, inputLen, + sk->iv.buf.data, sk->iv.buf.len, + ask->aad.buf.data, ask->aad.buf.len); +- CHECKERROR(rv, __LINE__); ++ CHECKERROR(rv, cipherInfo->expect, __LINE__); + } + } else { + int opsBetweenChecks = 0; + TIMEMARK(cipherInfo->seconds); + while (!(TIMETOFINISH())) { + int j = 0; + for (; j < opsBetweenChecks; j++) { + (*cipherInfo->cipher.aeadCipher)( +@@ -2591,74 +2612,74 @@ cipherDoOp(bltestCipherInfo *cipherInfo) + } + TIMEFINISH(cipherInfo->optime, 1.0); + } else if (is_pubkeyCipher(cipherInfo->mode)) { + TIMESTART(); + rv = (*cipherInfo->cipher.pubkeyCipher)(cipherInfo->cx, + &cipherInfo->output.pBuf, + &cipherInfo->input.pBuf); + TIMEFINISH(cipherInfo->optime, 1.0); +- CHECKERROR(rv, __LINE__); ++ CHECKERROR(rv, cipherInfo->expect, __LINE__); + cipherInfo->repetitions = 0; + if (cipherInfo->repetitionsToPerfom != 0) { + TIMESTART(); + for (i = 0; i < cipherInfo->repetitionsToPerfom; + i++, cipherInfo->repetitions++) { + SECItem dummy; + dummy.data = dummyOut; + dummy.len = maxLen; +- (*cipherInfo->cipher.pubkeyCipher)(cipherInfo->cx, &dummy, +- &cipherInfo->input.pBuf); +- CHECKERROR(rv, __LINE__); ++ rv = (*cipherInfo->cipher.pubkeyCipher)(cipherInfo->cx, &dummy, ++ &cipherInfo->input.pBuf); ++ CHECKERROR(rv, cipherInfo->expect, __LINE__); + } + } else { + int opsBetweenChecks = 0; + TIMEMARK(cipherInfo->seconds); + while (!(TIMETOFINISH())) { + int j = 0; + for (; j < opsBetweenChecks; j++) { + SECItem dummy; + dummy.data = dummyOut; + dummy.len = maxLen; +- (*cipherInfo->cipher.pubkeyCipher)(cipherInfo->cx, &dummy, +- &cipherInfo->input.pBuf); +- CHECKERROR(rv, __LINE__); ++ rv = (*cipherInfo->cipher.pubkeyCipher)(cipherInfo->cx, &dummy, ++ &cipherInfo->input.pBuf); ++ CHECKERROR(rv, cipherInfo->expect, __LINE__); + } + cipherInfo->repetitions += j; + } + } + TIMEFINISH(cipherInfo->optime, 1.0); + } else if (is_hashCipher(cipherInfo->mode)) { + TIMESTART(); + rv = (*cipherInfo->cipher.hashCipher)(cipherInfo->output.pBuf.data, + cipherInfo->input.pBuf.data, + cipherInfo->input.pBuf.len); + TIMEFINISH(cipherInfo->optime, 1.0); +- CHECKERROR(rv, __LINE__); ++ CHECKERROR(rv, cipherInfo->expect, __LINE__); + cipherInfo->repetitions = 0; + if (cipherInfo->repetitionsToPerfom != 0) { + TIMESTART(); + for (i = 0; i < cipherInfo->repetitionsToPerfom; + i++, cipherInfo->repetitions++) { +- (*cipherInfo->cipher.hashCipher)(dummyOut, +- cipherInfo->input.pBuf.data, +- cipherInfo->input.pBuf.len); +- CHECKERROR(rv, __LINE__); ++ rv = (*cipherInfo->cipher.hashCipher)(dummyOut, ++ cipherInfo->input.pBuf.data, ++ cipherInfo->input.pBuf.len); ++ CHECKERROR(rv, cipherInfo->expect, __LINE__); + } + } else { + int opsBetweenChecks = 0; + TIMEMARK(cipherInfo->seconds); + while (!(TIMETOFINISH())) { + int j = 0; + for (; j < opsBetweenChecks; j++) { + bltestIO *input = &cipherInfo->input; +- (*cipherInfo->cipher.hashCipher)(dummyOut, +- input->pBuf.data, +- input->pBuf.len); +- CHECKERROR(rv, __LINE__); ++ rv = (*cipherInfo->cipher.hashCipher)(dummyOut, ++ input->pBuf.data, ++ input->pBuf.len); ++ CHECKERROR(rv, cipherInfo->expect, __LINE__); + } + cipherInfo->repetitions += j; + } + } + TIMEFINISH(cipherInfo->optime, 1.0); + } + PORT_Free(dummyOut); + return rv; +@@ -2996,29 +3017,20 @@ load_file_data(PLArenaPool *arena, bltes + file = PR_Open(fn, PR_RDONLY, 00660); + if (file) { + setupIO(arena, data, file, NULL, 0); + PR_Close(file); + } + } + + HASH_HashType +-mode_str_to_hash_alg(const SECItem *modeStr) ++mode_string_to_hash_alg(const char *modeString) + { + bltestCipherMode mode; +- char *tempModeStr = NULL; +- if (!modeStr || modeStr->len == 0) +- return HASH_AlgNULL; +- tempModeStr = PORT_Alloc(modeStr->len + 1); +- if (!tempModeStr) +- return HASH_AlgNULL; +- memcpy(tempModeStr, modeStr->data, modeStr->len); +- tempModeStr[modeStr->len] = '\0'; +- mode = get_mode(tempModeStr); +- PORT_Free(tempModeStr); ++ mode = get_mode(modeString); + switch (mode) { + case bltestMD2: + return HASH_AlgMD2; + case bltestMD5: + return HASH_AlgMD5; + case bltestSHA1: + return HASH_AlgSHA1; + case bltestSHA224: +@@ -3029,16 +3041,33 @@ mode_str_to_hash_alg(const SECItem *mode + return HASH_AlgSHA384; + case bltestSHA512: + return HASH_AlgSHA512; + default: + return HASH_AlgNULL; + } + } + ++HASH_HashType ++mode_str_to_hash_alg(const SECItem *modeStr) ++{ ++ HASH_HashType hashType; ++ char *tempModeStr = NULL; ++ if (!modeStr || modeStr->len == 0) ++ return HASH_AlgNULL; ++ tempModeStr = PORT_Alloc(modeStr->len + 1); ++ if (!tempModeStr) ++ return HASH_AlgNULL; ++ memcpy(tempModeStr, modeStr->data, modeStr->len); ++ tempModeStr[modeStr->len] = '\0'; ++ hashType = mode_string_to_hash_alg(tempModeStr); ++ PORT_Free(tempModeStr); ++ return hashType; ++} ++ + void + get_params(PLArenaPool *arena, bltestParams *params, + bltestCipherMode mode, int j) + { + char filename[256]; + char *modestr = mode_strings[mode]; + bltestIO tempIO; + +@@ -3259,16 +3288,17 @@ blapi_selftest(bltestCipherMode *modes, + char filename[256]; + PLArenaPool *arena; + SECItem item; + SECStatus rv = SECSuccess, srv; + + PORT_Memset(&cipherInfo, 0, sizeof(cipherInfo)); + arena = PORT_NewArena(BLTEST_DEFAULT_CHUNKSIZE); + cipherInfo.arena = arena; ++ cipherInfo.expect = SECSuccess; + + nummodes = (numModes == 0) ? NUMMODES : numModes; + for (i = 0; i < nummodes; i++) { + if (numModes > 0) + mode = modes[i]; + else + mode = i; + if (mode == bltestINVALID) { +@@ -3299,31 +3329,41 @@ blapi_selftest(bltestCipherMode *modes, + "plaintext", j); + load_file_data(arena, &pt, filename, + is_sigCipher(mode) ? bltestBase64Encoded + : bltestBinary); + snprintf(filename, sizeof(filename), "%s/tests/%s/%s%d", testdir, modestr, + "ciphertext", j); + load_file_data(arena, &ct, filename, bltestBase64Encoded); + ++ snprintf(filename, sizeof(filename), "%s/tests/%s/%s%s%s%d", testdir, modestr, ++ "result", encrypt? "-encrypt" : "-decrypt", strictOAEP ? "-strict":"", j); ++ ++ cipherInfo.expect = SECSuccess; ++ if (PR_Access(filename, PR_ACCESS_EXISTS) == PR_SUCCESS) { ++ cipherInfo.expect = SECFailure; ++ } ++ + get_params(arena, params, mode, j); + /* Forward Operation (Encrypt/Sign/Hash) + ** Align the input buffer (plaintext) according to request + ** then perform operation and compare to ciphertext + */ + if (encrypt) { + rv |= bltestCopyIO(arena, &cipherInfo.input, &pt); + misalignBuffer(arena, &cipherInfo.input, inoff); + memset(&cipherInfo.output.buf, 0, sizeof cipherInfo.output.buf); + rv |= cipherInit(&cipherInfo, PR_TRUE); + misalignBuffer(arena, &cipherInfo.output, outoff); + rv |= cipherDoOp(&cipherInfo); + rv |= cipherFinish(&cipherInfo); +- rv |= verify_self_test(&cipherInfo.output, +- &ct, mode, PR_TRUE, SECSuccess); ++ if (cipherInfo.expect == SECSuccess) { ++ rv |= verify_self_test(&cipherInfo.output, ++ &ct, mode, PR_TRUE, SECSuccess); ++ } + /* If testing hash, only one op to test */ + if (is_hashCipher(mode)) + continue; + if (is_sigCipher(mode)) { + /* Verify operations support detached signature files. For + ** consistency between tests that run Sign/Verify back to + ** back (eg: self-tests) and tests that are only running + ** verify operations, copy the output into the sig buf, +@@ -3332,16 +3372,22 @@ blapi_selftest(bltestCipherMode *modes, + ** verify-only operations, this ensures that the output + ** buffer is properly configured + */ + rv |= bltestCopyIO(arena, ¶ms->asymk.sig, &cipherInfo.output); + } + } + if (!decrypt) + continue; ++ ++ /* If we expect the encrypt to fail, then don't do the decrypt */ ++ if (cipherInfo.expect == SECFailure) { ++ continue; ++ } ++ + /* Reverse Operation (Decrypt/Verify) + ** Align the input buffer (ciphertext) according to request + ** then perform operation and compare to plaintext + */ + if (is_sigCipher(mode)) { + rv |= bltestCopyIO(arena, &cipherInfo.input, &pt); + rv |= bltestCopyIO(arena, &cipherInfo.output, ¶ms->asymk.sig); + } else { +@@ -3692,16 +3738,19 @@ enum { + opt_UseSigSeed, + opt_SeedFile, + opt_AAD, + opt_InputOffset, + opt_OutputOffset, + opt_MonteCarlo, + opt_ThreadNum, + opt_SecondsToRun, ++ opt_RSAHash, ++ opt_MaskHash, ++ opt_OAEPStrict, + opt_CmdLine + }; + + static secuCommandFlag bltest_commands[] = { + { /* cmd_Decrypt */ 'D', PR_FALSE, 0, PR_FALSE }, + { /* cmd_Encrypt */ 'E', PR_FALSE, 0, PR_FALSE }, + { /* cmd_FIPS */ 'F', PR_FALSE, 0, PR_FALSE }, + { /* cmd_Hash */ 'H', PR_FALSE, 0, PR_FALSE }, +@@ -3742,19 +3791,29 @@ static secuCommandFlag bltest_options[] + { /* opt_UseSigSeed */ 'y', PR_FALSE, 0, PR_FALSE }, + { /* opt_SeedFile */ 'z', PR_FALSE, 0, PR_FALSE }, + { /* opt_AAD */ 0, PR_TRUE, 0, PR_FALSE, "aad" }, + { /* opt_InputOffset */ '1', PR_TRUE, 0, PR_FALSE }, + { /* opt_OutputOffset */ '2', PR_TRUE, 0, PR_FALSE }, + { /* opt_MonteCarlo */ '3', PR_FALSE, 0, PR_FALSE }, + { /* opt_ThreadNum */ '4', PR_TRUE, 0, PR_FALSE }, + { /* opt_SecondsToRun */ '5', PR_TRUE, 0, PR_FALSE }, ++ { /* opt_RSAHash */ '6', PR_TRUE, 0, PR_FALSE }, ++ { /* opt_MaskHash */ '7', PR_TRUE, 0, PR_FALSE }, ++ { /* opt_OAEPStrict */ '8', PR_FALSE, 0, PR_FALSE }, + { /* opt_CmdLine */ '-', PR_FALSE, 0, PR_FALSE } + }; + ++unsigned int ++HashLen(HASH_HashType type) ++{ ++ const SECHashObject *hashObj = HASH_GetRawHashObject(type); ++ return hashObj->length; ++} ++ + int + main(int argc, char **argv) + { + SECStatus rv = SECFailure; + + double totalTime = 0.0; + PRIntervalTime time1, time2; + PRFileDesc *outfile = NULL; +@@ -3832,16 +3891,17 @@ main(int argc, char **argv) + if (bltest.options[opt_InputOffset].activated) + inoff = PORT_Atoi(bltest.options[opt_InputOffset].arg); + if (bltest.options[opt_OutputOffset].activated) + outoff = PORT_Atoi(bltest.options[opt_OutputOffset].arg); + + testdir = (bltest.options[opt_SelfTestDir].activated) ? strdup(bltest.options[opt_SelfTestDir].arg) + : "."; + ++ strictOAEP= bltest.options[opt_OAEPStrict].activated; + /* + * Handle three simple cases first + */ + + /* test the RSA_PopulatePrivateKey function with known vectors */ + if (bltest.commands[cmd_RSAPopulateKV].activated) { + PORT_Free(cipherInfo); + return doRSAPopulateTestKV(); +@@ -4145,16 +4205,84 @@ main(int argc, char **argv) + } + memset(&askp->aad, 0, sizeof askp->aad); + askp->aad.mode = ioMode; + setupIO(cipherInfo->arena, &askp->aad, file, aadstr, 0); + if (file) { + PR_Close(file); + } + } ++ /* set up rsaoaep and pss values */ ++ if (is_rsaV2(cipherInfo->mode)) { ++ char *seedstr = NULL; ++ bltestRSAParams *rsakp; ++ bltestIO tempIO; ++ rsakp = ¶ms->asymk.cipherParams.rsa; ++ ++ /* hash */ ++ if (bltest.options[opt_RSAHash].activated) { ++ if (bltest.options[opt_CmdLine].activated) { ++ rsakp->hashAlg = mode_string_to_hash_alg( ++ bltest.options[opt_RSAHash].arg); ++ } else { ++ load_file_data(arena, &tempIO, ++ bltest.options[opt_RSAHash].arg, ++ bltestBinary); ++ rsakp->hashAlg = mode_str_to_hash_alg(&tempIO.buf); ++ } ++ } else { ++ /* save the random iv for reference */ ++ file = PR_Open("tmp.hash", PR_WRONLY | PR_CREATE_FILE, 00660); ++ PR_Write(file, "sha256\n", sizeof("sha256\n")-1); ++ rsakp->hashAlg = HASH_AlgSHA256; ++ PR_Close(file); ++ ++ } ++ /* mask */ ++ if (bltest.options[opt_RSAHash].activated) { ++ if (bltest.options[opt_CmdLine].activated) { ++ rsakp->maskHashAlg = mode_string_to_hash_alg( ++ bltest.options[opt_MaskHash].arg); ++ } else { ++ load_file_data(arena, &tempIO, ++ bltest.options[opt_MaskHash].arg, ++ bltestBinary); ++ rsakp->maskHashAlg = mode_str_to_hash_alg(&tempIO.buf); ++ } ++ } else { ++ /* save the random iv for reference */ ++ file = PR_Open("tmp.maskhash", PR_WRONLY | PR_CREATE_FILE, ++ 00660); ++ PR_Write(file, "sha256\n", sizeof("sha256\n")-1); ++ rsakp->maskHashAlg = HASH_AlgSHA256; ++ PR_Close(file); ++ ++ } ++ /* seed salt */ ++ file = NULL; ++ if (bltest.options[opt_Seed].activated) { ++ if (bltest.options[opt_CmdLine].activated) { ++ seedstr = bltest.options[opt_Seed].arg; ++ } else { ++ file = PR_Open(bltest.options[opt_Seed].arg, ++ PR_RDONLY, 00660); ++ } ++ } else { ++ /* save the random seed for reference */ ++ file = PR_Open("tmp.seed", PR_WRONLY | PR_CREATE_FILE, 00660); ++ } ++ memset(&rsakp->seed, 0, sizeof rsakp->seed); ++ rsakp->seed.mode = ioMode; ++ setupIO(cipherInfo->arena, &rsakp->seed, file, seedstr, ++ HashLen(rsakp->hashAlg)); ++ if (file) { ++ PR_Close(file); ++ } ++ file = NULL; ++ } + + if (bltest.commands[cmd_Verify].activated) { + file = PR_Open(bltest.options[opt_SigFile].arg, PR_RDONLY, 00660); + if (is_sigCipher(cipherInfo->mode)) { + memset(¶ms->asymk.sig, 0, sizeof(bltestIO)); + params->asymk.sig.mode = ioMode; + setupIO(cipherInfo->arena, ¶ms->asymk.sig, file, NULL, 0); + } +diff --git a/cmd/bltest/tests/rsa_oaep/ciphertext18 b/cmd/bltest/tests/rsa_oaep/ciphertext18 +new file mode 100644 +--- /dev/null ++++ b/cmd/bltest/tests/rsa_oaep/ciphertext18 +@@ -0,0 +1,6 @@ ++m8ZlOZEwDf3oapXT/tuBWS5Boa9rS08M/pFz1N2Y1GU0sYC4zcv79zxmjk2RjYLe ++VjCAM8gAGoVdpV7P53NvAwEZcEpXd/ewhCz1sopc19o96pEYqC01Ik9TW0H/41Rh ++O/KvWvVEoJ8s0TS6z+ViCK5iBjdxLhF7SKxwJXfcwZvOua3lr9yjgkljVzIYf7XA ++1ZkkuxB0PDPw6ud022rn/xKhjhL9GJObY9rNYZwyS6hSpUEM+ZV106X+7nISVgOr ++vP93jbTwg0ndqn3XJKOduJVZVTR/dEebqSuBnWW4TIGE3Tq84Xf+ySGpMtD3vRbG ++C5P6A7EyqXJOOwUvvZzAcw== +diff --git a/cmd/bltest/tests/rsa_oaep/ciphertext19 b/cmd/bltest/tests/rsa_oaep/ciphertext19 +new file mode 100644 +--- /dev/null ++++ b/cmd/bltest/tests/rsa_oaep/ciphertext19 +@@ -0,0 +1,6 @@ ++eNIFlfXIih5XqUTcmFUeW3WQdlrO8yeLkGBgYZcHkFCNXeEze26osWLzIpuRc+vY ++kdHVVxDWhcp4Q/3AWL8Ib67BYfZk/AQn+IJCDsd2tdVxg3/ifCtYYm+09KY8oNy/ ++uWudjMEiS1FR7l+wb2QNHcQH6UjJqt5+z19nCcwyBfHH6E3u+U+Pq6uDdhSODu3e ++s0UxYjiDl0UUW5m7uJVq7MCrqAHgTkH3h+wWOMJaORZ7tk9tfCnvUt9AiAToyv0Y ++G9Ylbkll5bM+UO7R6JsXqm25cM6pshP5jy+aXbw+KqXLjxbgrDdZQZ1MXup7QT/z ++znUkc0USufg5BmymMoPP/A== +diff --git a/cmd/bltest/tests/rsa_oaep/ciphertext20 b/cmd/bltest/tests/rsa_oaep/ciphertext20 +new file mode 100644 +--- /dev/null ++++ b/cmd/bltest/tests/rsa_oaep/ciphertext20 +@@ -0,0 +1,6 @@ ++Ll4/kyqhXiKtf+rgK8vNrVpfqV+YOiARW1vCX5Mo28OEbUQHNeetPdpbQ8gnlkFa ++xWdVrUmeVSY4A3Nkv8f3ZvC2m08A1OCvSxhgTKUJvO8eW5DErFdj0irT4FKt4nsO ++G86+cOiEr5OuWNfZv+UI36TJaadgGXHNAD/keCl/tq65tiiw6EwWYSm3kypA5MN+ ++2e5eBtdwZfFM+kouQZf75AUitcYYGCbnZLWIyZc8OfTyEgtcR8ERTz+BrT0Ub8va ++ML0MEc3+nIMApdXU9SWbyr/WA8Sz/wh5kyA+Yr6Xww0j36chjRgSh3Dn3ykZkYZX ++gwWtxqWTY6mWaatQXCfnHA== +diff --git a/cmd/bltest/tests/rsa_oaep/ciphertext21 b/cmd/bltest/tests/rsa_oaep/ciphertext21 +new file mode 100644 +--- /dev/null ++++ b/cmd/bltest/tests/rsa_oaep/ciphertext21 +@@ -0,0 +1,6 @@ ++gsKckbXuYtoxoGMVpkVsd1Nb2BMTqMuThlUXbzQB1qzJ1/PTbdpRWFtcDKsW202f ++tO4NPt7sCIHdiqdqwXVQxcTabrbwaUZICtwphbLUQDKMU/pwhicKHSH8hD0/FbwU ++4iYUgYKxkjsQUlW4Eq4Yh+2Vfk2+ae6PFAvdQE0EHEWsNUkbbbv5tNweLH0JmQGp ++W4k2MKm2LUu4+v+n8aQ4FwkuQKS2ZqddTjR+QRjM+hzsSVNPpvvCu4kK4xU3G0ru ++JWLCtmdmVGXpLlwiLUn0FomRUa1fqQOhp3W4/WvJf1kdcj2pPLzgHPKgivb+03o+ ++4pHctkSM4dgC9vhMOLc8Tg== +diff --git a/cmd/bltest/tests/rsa_oaep/ciphertext22 b/cmd/bltest/tests/rsa_oaep/ciphertext22 +new file mode 100644 +--- /dev/null ++++ b/cmd/bltest/tests/rsa_oaep/ciphertext22 +@@ -0,0 +1,6 @@ ++agf+F8PWGOKXgZYUpqPZ2NT17bkElsyzUNXbmcuZKd1YHSo6TZZj+Jdd48QhlDHK ++fUIch1+QIgRnTWZ9Jpw6d7Agk1jz0yjg2ywrEmdT1aRwBonVn1r1rK3q2giCZLEd ++YhQ2fz/Iz89b084w7yjLDI2mbOi0Pbizn6IB6whkJYIKPjMLgekvwQ6EM4VvrsM6 ++gHT9BGmICIpO+ipHYYwNhZiePpnZSck5p7lxmHLa/WWFeGrFJzNzab+8ToRw12hR ++oL4DBLfob5wvychGSkjGeWWRbFhbujalHOI8fOg92bUxSeaks3XDhRhv8eZmX1c6 ++hpKbNFNI99e20O5riigz7Q== +diff --git a/cmd/bltest/tests/rsa_oaep/hash18 b/cmd/bltest/tests/rsa_oaep/hash18 +new file mode 100644 +--- /dev/null ++++ b/cmd/bltest/tests/rsa_oaep/hash18 +@@ -0,0 +1,1 @@ ++sha256 +diff --git a/cmd/bltest/tests/rsa_oaep/hash19 b/cmd/bltest/tests/rsa_oaep/hash19 +new file mode 100644 +--- /dev/null ++++ b/cmd/bltest/tests/rsa_oaep/hash19 +@@ -0,0 +1,1 @@ ++sha256 +diff --git a/cmd/bltest/tests/rsa_oaep/hash20 b/cmd/bltest/tests/rsa_oaep/hash20 +new file mode 100644 +--- /dev/null ++++ b/cmd/bltest/tests/rsa_oaep/hash20 +@@ -0,0 +1,1 @@ ++sha256 +diff --git a/cmd/bltest/tests/rsa_oaep/hash21 b/cmd/bltest/tests/rsa_oaep/hash21 +new file mode 100644 +--- /dev/null ++++ b/cmd/bltest/tests/rsa_oaep/hash21 +@@ -0,0 +1,1 @@ ++sha256 +diff --git a/cmd/bltest/tests/rsa_oaep/hash22 b/cmd/bltest/tests/rsa_oaep/hash22 +new file mode 100644 +--- /dev/null ++++ b/cmd/bltest/tests/rsa_oaep/hash22 +@@ -0,0 +1,1 @@ ++sha256 +diff --git a/cmd/bltest/tests/rsa_oaep/key18 b/cmd/bltest/tests/rsa_oaep/key18 +new file mode 100644 +--- /dev/null ++++ b/cmd/bltest/tests/rsa_oaep/key18 +@@ -0,0 +1,1 @@ ++AAAAAQAAAAEA5UUoEM5A1qttdY2GGJiJ5ypWHjr6o+CISs8S+zs2npypQiFLjSaf37nacs1Vz7J5mSHwJhTRXm2uzUYPckEuN1TA/88v4zl3O20RY2IBvI+T9Gqb/ir8KmH+/xQivsBaN2qIu0u/ZNzHyDpQlB3rJu4iqJhTQC/CdKN19o3Ex9QRdSC4xAITKnMooImWbLlBcqx0M6G0DMraD9DRaKntHhYabmtI8ly3wbldjHXlLc4YFEm+/Mk5kzNZAUCQ/gZrU/C6ue45cHhiqJpJ2Wx4X+3EQveTh9+emJMtOz4sogvy67ow+0/0f3svgbiycwe4HL5gu4yOCsH6l6asfqIYdwAAAAMBAAEAAAEAdDQ+H256vJN80kUd/19pHgp+Rao9GUyXp9sW0tFrWK8stsDrayp1g87OYruiT7g1G6Fw98w7ZjIPlCl7cLyKsS66961Kp0+cGDmqw4WMPcdEBXD3iba9wrmSpjTNe4NA9q9/h/bXlka0185+wuP7KOswE+XI5FKQq8+h3Ypzf5kEFCq2m1o13qEpqVyzKn4nJvkzmGvQN1+lcLi1OuhNr58iXG7yNS07Uewi9ytWMoYv/DB4peVT0kEHTrQsCSS1x4DPE6gj4taRosU4wwPQa0a23/5d/1MfLlQCa/GSa7vEryfa5bXbxbVpGcXEZLs33RG9M7IE2H0DB1RKP5Dm9wAAAQDlRSgQzkDWq211jYYYmInnKlYeOvqj4IhKzxL7OzaenKlCIUuNJp/fudpyzVXPsnmZIfAmFNFeba7NRg9yQS43VMD/zy/jOXc7bRFjYgG8j5P0apv+KvwqYf7/FCK+wFo3aoi7S79k3MfIOlCUHesm7iKomFNAL8J0o3X2jcTH1BF1ILjEAhMqcyigiZZsuUFyrHQzobQMytoP0NFoqe0eFhpua0jyXLfBuV2MdeUtzhgUSb78yTmTM1kBQJD+BmtT8Lq57jlweGKomknZbHhf7cRC95OH356Yky07PiyiC/LrujD7T/R/ey+BuLJzB7gcvmC7jI4KwfqXpqx+ohh3AAAAAQEAAAEAdDQ+H256vJN80kUd/19pHgp+Rao9GUyXp9sW0tFrWK8stsDrayp1g87OYruiT7g1G6Fw98w7ZjIPlCl7cLyKsS66961Kp0+cGDmqw4WMPcdEBXD3iba9wrmSpjTNe4NA9q9/h/bXlka0185+wuP7KOswE+XI5FKQq8+h3Ypzf5kEFCq2m1o13qEpqVyzKn4nJvkzmGvQN1+lcLi1OuhNr58iXG7yNS07Uewi9ytWMoYv/DB4peVT0kEHTrQsCSS1x4DPE6gj4taRosU4wwPQa0a23/5d/1MfLlQCa/GSa7vEryfa5bXbxbVpGcXEZLs33RG9M7IE2H0DB1RKP5Dm9wAAAAEBAAAAAQE= +\ No newline at end of file +diff --git a/cmd/bltest/tests/rsa_oaep/key19 b/cmd/bltest/tests/rsa_oaep/key19 +new file mode 100644 +--- /dev/null ++++ b/cmd/bltest/tests/rsa_oaep/key19 +@@ -0,0 +1,1 @@ ++AAAAAQAAAAEAiEv3eeAtf4akF4xEo9sRDLbrulalIBrTmzTYl7Twlk18eUU2CvamKx03Ls8YRdJsJ+73F+03e5gXQuqaq3IArYzy3P09rghqgPW52/Sux49ajDLfWsOUbWa4bQSAHxLHi45ZipyPnWgqHqzE7tq5OL4XpN0JVEg/wvm4c8FBIt91Qq5xQFdFkr5WpsnCyLTvvpDtaFCzIAoN2LY2ijH13V3dMuN1dKhCj2L9INEHgxRIqq5iKmBrRqZMhXdvZAh83lOc8aBgAkZ7Puvjz2fSSHlVNb9xfl+JUxOoHy6uUw66ZA5P1yacOlqwra/9gbe1GjuU0GJJQS8sVtt0MgHcjQAAAAMBAAEAAAEAYYSKMAbXKeVK5YXVTz3hYM+w7zbmkWqCN+Gnn8wjUHDx5HUlJtxXV2sDRxxtv42rbRAm+67DhWugnOF/oVM2I/jMz1xCUTFp0VZwIMnSeIgl4AjgM3Fad6E9M+rWfaQnAAKekyT8okGLHGjRqJWC5xYenjSNq4LLCDOToz00RbYQu0GaHXawG26AJbarYRaZLeG1zWUSSVIWnL+F4mMVh0huiSlS9TWTe2YQrkKqFzslyJ3DCqXQDz1gyuOm7E/3zeNAmGFZXrzCwLJuTpFQY66Ohmt25rDQdTvPR/V+Lnev3acLdTvwuFyT4MYsZeqCJ6b5ocUy5sW6WzwntsGg0QAAAAAAAAAAAAAAAAAAAAAAAAAA +\ No newline at end of file +diff --git a/cmd/bltest/tests/rsa_oaep/key20 b/cmd/bltest/tests/rsa_oaep/key20 +new file mode 100644 +--- /dev/null ++++ b/cmd/bltest/tests/rsa_oaep/key20 +@@ -0,0 +1,1 @@ ++AAAAAQAAAAEAxpqCGNigv6qOWXo2iOvLLM35bPnp5JgaB3XnnbWOUqF0vbvvRP85giGkYYDj4poPGT0NRshTkz7fKbAVCHCs7HBFzzaxXisQsFd9mno+x+62nERyJ8Jx6XASVxN9IoyhE7xXS1eDPjccG9XlXktwaRsvC3HCxlqEPxNCE+l9RM8re859xMesjtML2Ww6U5tKci//84f/dKjAD5Hbdoj2JIwRhRl+2ACUml2rWrglSjKH19ZzJkbWFFJhecrt1FD/CFsx+hFAtZQtuqs7PFQvZSwU1OoVTyf/jMEXwhsDoRyf3+QQp8wAm4GztJxXTclXk2jNA+g9/UJ91ZxUwrIYoQAAAAMBAAEAAAEAi59wOvuA9352mR08XcI3koY0BpMkBhuQoRIZBVzwc9MieYk4S6+KxJvWLt4oV9SrLLkpLkwDxY774RjzkH12dCYsOzNs9nL23raqQ9xX1EZnJfdIlyoGJmeUJMYPTtH6ANYnlPoxozyJStPtr1wZGs/3X30XieVrd68yrMGedhZ6tej11vJS/a22DWG57NjZC7/enzBkYHtzVCFnDRk08Qxzcss85IF8khG8NpLz1tl4+9vKKkwa08d++SC3EAs2/d9y9RCxiYTcXEgaWTmINT6JpWBXwdJAUIjOr1ujequixnapB351UZODHGKBieU1hpc52NuKz/SCLuCQmxkM0QAAAAAAAAAAAAAAAAAAAAAAAAAA +\ No newline at end of file +diff --git a/cmd/bltest/tests/rsa_oaep/key21 b/cmd/bltest/tests/rsa_oaep/key21 +new file mode 100644 +--- /dev/null ++++ b/cmd/bltest/tests/rsa_oaep/key21 +@@ -0,0 +1,1 @@ ++AAAAAQAAAAEAhxOfLwr3NUmZUE50aKLO+KHw8Y4yOLd9wZKJq/9WcuHnOJJJw0uV35ugsUvaXAcRwszCOJTtu+cZ1zMvRoEB/EQGcltmULHF9ViHkK5b7gcaSP2ALIrZyF9/IN5vnUGqsg1719WmgAOee6Zo1nytdbq5+cJGmAC8a6CorCM68eR1UsdoB7RfVQpqzc9fjG+HZ2wv58mk/OEhJAHUJRf7xjbrbF9jZ3DeiMd8f/dxK+bEKCXt2XsYZoOquylOdNVxCBylGSelzJqFwUvHUeJzSsz3PvXHB3YBH0cABVufoMOjwRYaNJvryhRICmbnH0Gw9ai90A/lotbprv2bzuGKUQAAAAMBAAEAAAEAGLqQ4+3gABNvS/JxAU79ESukjrR3oUaGcU/AgIqIaxPMXuD5PBW0ZHF7Y7n9SefJap5N2En7k/DlHC60svWfRVCy955YhJ4NVYHwVwcWsOGFtFkVb/fLERuydu6t3m7ucrxk/tEuxgCEZSuO/kAHBD3ltlCDLQv2KYzP7kfzJEllSDVplfcSfIxi5VQTcHuLZFScYQSeeWdjprWZ1Bv/fyFzqioqyNb/3SyFEMnXubDR0KH5yAWfeRRNLCje+DDUa41ZGiyBzlVf8eRtoh8CUNfDq6MtbfBvzjyswVV4M/h2RCO7nGAaJ8OkpCjzW04czzR/hwA2SbzP49OPD+fj1QAAAAAAAAAAAAAAAAAAAAAAAAAA +\ No newline at end of file +diff --git a/cmd/bltest/tests/rsa_oaep/key22 b/cmd/bltest/tests/rsa_oaep/key22 +new file mode 100644 +--- /dev/null ++++ b/cmd/bltest/tests/rsa_oaep/key22 +@@ -0,0 +1,1 @@ ++AAAAAQAAAAEAgu9i5HnjEXoGUpZ1SMf6auSlz7+X6N4hQ6Yiicq4BC66QIyjY3IjEyhlJKHsX3//GzWd+6NCerSWb23j54IrFcLGNH1oWylxZXJtCIn2+gVDsw4D+RwbG/cY6B3U4Qs1HnlSqgxKVVYAQmQyeIKshXldD86aC5OnfDlguZBN3Qe641oqoAshOzAPzBQU0fo4dey5SVXftIKm4YE2+rVK7UmbQsNBCPYonT3obsGqspnx5MghuP1MMV2HkTyL4GJk/MwTFKlsd8Nq9PrrPfpbEtpITV57lTvRcKMqW/8WaaY7YfNKtIE/TppFyw5WcxP+iPffXA+s3/kZNn4/zEGowQAAAAMBAAEAAAEAVjyMiaatoykXWPW3sYPkrtqBSt8Jkloa1o9pWA+oOzj7euAmDK1z5H5Kf2IWaJ/IyYb60m2or2CZZDKgVC4rafexMGNlvguk9Ku6ETfjA09lizPg3NQEgn06oVNYy5xurEnXNGQEcys8Pkos+GOxHBv+eMd5msV0d4lSTsi2CkJHnKuwFphjYdCTQTBZK8zlKDkY6El6Dyfmy9lrg/T5NC6kE2eeCss2/hkWzIGMbDcE3UVZGhJJhrca9SUkUHbGwbicGYUOnEGlQho9IBCpHt7CP1XWSNwZFJkRcmBFKomghAaLSXM/bfm8tghsnqI+zAL6bTdAaZkDgw5Pc0Cr+QAAAAAAAAAAAAAAAAAAAAAAAAAA +\ No newline at end of file +diff --git a/cmd/bltest/tests/rsa_oaep/maskhash18 b/cmd/bltest/tests/rsa_oaep/maskhash18 +new file mode 100644 +--- /dev/null ++++ b/cmd/bltest/tests/rsa_oaep/maskhash18 +@@ -0,0 +1,1 @@ ++sha256 +diff --git a/cmd/bltest/tests/rsa_oaep/maskhash19 b/cmd/bltest/tests/rsa_oaep/maskhash19 +new file mode 100644 +--- /dev/null ++++ b/cmd/bltest/tests/rsa_oaep/maskhash19 +@@ -0,0 +1,1 @@ ++sha256 +diff --git a/cmd/bltest/tests/rsa_oaep/maskhash20 b/cmd/bltest/tests/rsa_oaep/maskhash20 +new file mode 100644 +--- /dev/null ++++ b/cmd/bltest/tests/rsa_oaep/maskhash20 +@@ -0,0 +1,1 @@ ++sha256 +diff --git a/cmd/bltest/tests/rsa_oaep/maskhash21 b/cmd/bltest/tests/rsa_oaep/maskhash21 +new file mode 100644 +--- /dev/null ++++ b/cmd/bltest/tests/rsa_oaep/maskhash21 +@@ -0,0 +1,1 @@ ++sha256 +diff --git a/cmd/bltest/tests/rsa_oaep/maskhash22 b/cmd/bltest/tests/rsa_oaep/maskhash22 +new file mode 100644 +--- /dev/null ++++ b/cmd/bltest/tests/rsa_oaep/maskhash22 +@@ -0,0 +1,1 @@ ++sha256 +diff --git a/cmd/bltest/tests/rsa_oaep/numtests b/cmd/bltest/tests/rsa_oaep/numtests +--- a/cmd/bltest/tests/rsa_oaep/numtests ++++ b/cmd/bltest/tests/rsa_oaep/numtests +@@ -1,1 +1,1 @@ +-18 ++23 +diff --git a/cmd/bltest/tests/rsa_oaep/plaintext18 b/cmd/bltest/tests/rsa_oaep/plaintext18 +new file mode 100644 +--- /dev/null ++++ b/cmd/bltest/tests/rsa_oaep/plaintext18 +@@ -0,0 +1,1 @@ ++if2H&:?Lط8(֪ +\ No newline at end of file +diff --git a/cmd/bltest/tests/rsa_oaep/plaintext19 b/cmd/bltest/tests/rsa_oaep/plaintext19 +new file mode 100644 +--- /dev/null ++++ b/cmd/bltest/tests/rsa_oaep/plaintext19 +@@ -0,0 +1,1 @@ ++ ^=. F-T/4 1 , copy g-> factor, return MP_COMPOSITE */ ++ if (mp_cmp_d(&g, 1) > 0) { ++ if (factor) { ++ mp_copy(&g, factor); ++ } ++ res = MP_COMPOSITE; ++ goto CLEANUP; ++ } ++ ++ /* Compute z = (x ** m) mod a */ ++ MP_CHECKOK(mp_exptmod(&x, &m, a, &z)); ++ ++ if (mp_cmp_d(&z, 1) == 0 || mp_cmp(&z, &amo) == 0) { ++ res = MP_PRIME; ++ continue; ++ } ++ ++ res = MP_NOT_POWER; /* just in case the following for loop never executes. */ ++ for (jx = 1; jx < b; jx++) { ++ /* xp = z */ ++ MP_CHECKOK(mp_copy(&z, &xp)); ++ /* z = z^2 (mod a) */ ++ MP_CHECKOK(mp_sqrmod(&z, a, &z)); ++ res = MP_NOT_POWER; /* previous line set res to MP_PRIME */ ++ ++ if (mp_cmp_d(&z, 1) == 0) { ++ res = MP_COMPOSITE; ++ break; ++ } ++ if (mp_cmp(&z, &amo) == 0) { ++ res = MP_PRIME; ++ break; ++ } ++ } /* end testing loop */ ++ ++ /* If the test passes, we will continue iterating, but a failed ++ test means the candidate is definitely NOT prime, so we will ++ immediately break out of this loop ++ */ ++ if (res != MP_PRIME) ++ break; ++ } /* end iterations loop */ ++ ++ if (res == MP_PRIME) { ++ goto CLEANUP; ++ } ++ ++ if (res == MP_NOT_POWER) { ++ /* xp = z */ ++ MP_CHECKOK(mp_copy(&z, &xp)); ++ /* z = z^2 (mod a) */ ++ MP_CHECKOK(mp_sqrmod(&z, a, &z)); ++ /* if z != 1 xp=z */ ++ if (mp_cmp_d(&z, 1) != 0) { ++ MP_CHECKOK(mp_copy(&z, &xp)); ++ } ++ } ++ res = MP_NOT_POWER; ++ ++ /* g = GCD(xp-1,a) */ ++ MP_CHECKOK(mp_sub_d(&xp, 1, &xp)); ++ MP_CHECKOK(mp_gcd(&xp, a, &g)); ++ /* if g > 1 , copy g-> factor, return MP_COMPOSITE */ ++ if (mp_cmp_d(&g, 1) > 0) { ++ if (factor) { ++ mp_copy(&g, factor); ++ } ++ res = MP_COMPOSITE; ++ goto CLEANUP; ++ } ++ res = MP_NOT_POWER; ++ ++CLEANUP: ++ mp_clear(&m); ++ mp_clear(&z); ++ mp_clear(&g); ++ mp_clear(&xp); ++ mp_clear(&x); ++ mp_clear(&amo); ++ return res; ++ ++} /* end mpp_pprime() */ ++ + /* }}} */ + + /* Produce table of composites from list of primes and trial value. + ** trial must be odd. List of primes must not include 2. + ** sieve should have dimension >= MAXPRIME/2, where MAXPRIME is largest + ** prime in list of primes. After this function is finished, + ** if sieve[i] is non-zero, then (trial + 2*i) is composite. + ** Each prime used in the sieve costs one division of trial, and eliminates +diff --git a/lib/freebl/mpi/mpprime.h b/lib/freebl/mpi/mpprime.h +--- a/lib/freebl/mpi/mpprime.h ++++ b/lib/freebl/mpi/mpprime.h +@@ -30,19 +30,21 @@ mp_err mpp_random_size(mp_int *a, mp_siz + typedef mp_err (*mpp_random_fn)(mp_int *); + + /* Pseudo-primality testing */ + mp_err mpp_divis_vector(mp_int *a, const mp_digit *vec, int size, int *which); + mp_err mpp_divis_primes(mp_int *a, mp_digit *np); + mp_err mpp_fermat(mp_int *a, mp_digit w); + mp_err mpp_fermat_list(mp_int *a, const mp_digit *primes, mp_size nPrimes); + mp_err mpp_pprime(mp_int *a, int nt); ++mp_err mpp_pprime_or_power(mp_int *a, mp_int *factor, int nt); + mp_err mpp_sieve(mp_int *trial, const mp_digit *primes, mp_size nPrimes, + unsigned char *sieve, mp_size nSieve); + mp_err mpp_make_prime(mp_int *start, mp_size nBits, mp_size strong); + + /* Pseudo-primality tests using a user-provided mpp_random implementation */ + mp_err mpp_pprime_ext_random(mp_int *a, int nt, mpp_random_fn random); ++mp_err mpp_pprime_or_power_ext_random(mp_int *a, mp_int *f, int nt, mpp_random_fn random); + mp_err mpp_make_prime_ext_random(mp_int *start, mp_size nBits, mp_size strong, mpp_random_fn random); + + SEC_END_PROTOS + + #endif /* end _H_MP_PRIME_ */ +diff --git a/lib/freebl/rsapkcs.c b/lib/freebl/rsapkcs.c +--- a/lib/freebl/rsapkcs.c ++++ b/lib/freebl/rsapkcs.c +@@ -10,16 +10,17 @@ + #include "stubs.h" + #endif + + #include "secerr.h" + + #include "blapi.h" + #include "secitem.h" + #include "blapii.h" ++#include "secmpi.h" + + #define RSA_BLOCK_MIN_PAD_LEN 8 + #define RSA_BLOCK_FIRST_OCTET 0x00 + #define RSA_BLOCK_PRIVATE_PAD_OCTET 0xff + #define RSA_BLOCK_AFTER_PAD_OCTET 0x00 + + /* + * RSA block types +@@ -795,16 +796,86 @@ eme_oaep_encode(unsigned char *em, + for (i = 0; i < hash->length; ++i) + em[1 + i] ^= mask[i]; + + PORT_ZFree(mask, dbMaskLen); + return SECSuccess; + } + + SECStatus ++RSA_PartialVerify(RSAPublicKey *key) ++{ ++ mp_int n, e, fact; ++ mp_err err; ++ SECStatus rv = SECSuccess; ++ mp_int small_primes_product; ++ /* this string is the NIST string *751, since the requirement is to detect up to prime 751 ++ * inclusive */ ++ const char *primes_product_decimal_string ="1090367704589007566035202161494385722019383400119651874476478760664145697976533387343668263403266024900638505861980470638958322454581550203448421495842767449796261170069732921897400657944793163960886418313690808872680411646815934535605444102983629208422567461571568587963766123806881456648026733413053968363611105"; ++ ++ /* modulus length check done by the FIPS indicator code */ ++ /* validate public exponent this test only succeeds if the exponent e is ++ * 2^16 <= e < 2^256. e = 2^16 will be rejected below byte the iseven check */ ++ if (!key->publicExponent.data[0] || (key->publicExponent.len < 3) || (key->publicExponent.len > 32)) { ++ PORT_SetError(SEC_ERROR_INVALID_KEY); ++ return SECFailure; ++ } ++ /* convert to mpi for more detailed tests */ ++ MP_DIGITS(&n) = 0; ++ MP_DIGITS(&e) = 0; ++ MP_DIGITS(&fact) = 0; ++ MP_DIGITS(&small_primes_product) = 0; ++ CHECK_MPI_OK(mp_init(&n)); ++ CHECK_MPI_OK(mp_init(&e)); ++ CHECK_MPI_OK(mp_init(&small_primes_product)); ++ CHECK_MPI_OK(mp_init(&fact)); ++ SECITEM_TO_MPINT(key->modulus, &n); ++ SECITEM_TO_MPINT(key->publicExponent, &e); ++ ++ /* reject even exponents and moduluses */ ++ if (mp_iseven(&e) || mp_iseven(&n)) { ++ err = MP_BADARG; ++ goto cleanup; ++ } ++ /* check for componsite or power */ ++ err = mpp_pprime_or_power_secure(&n, NULL, 4); ++ if (err != MP_NOT_POWER) { ++ /* prime check succeeded, therefore modulus is not composite */ ++ /* or we found a factor, indicating that the modulus is probably ++ * a power of a prime */ ++ err = MP_BADARG; ++ goto cleanup; ++ } ++ /* check for small factors using gcd */ ++ CHECK_MPI_OK(mp_read_radix(&small_primes_product, ++ primes_product_decimal_string, 10)); ++ CHECK_MPI_OK(mp_gcd(&n,&small_primes_product, &fact)); ++ if (mp_cmp_d(&fact, 1) != 0) { ++ /* factor found, not a good modulus */ ++ err = MP_BADARG; ++ goto cleanup; ++ } ++ ++cleanup: ++ mp_clear(&n); ++ mp_clear(&e); ++ mp_clear(&small_primes_product); ++ mp_clear(&fact); ++ if (err) { ++ PORT_SetError(SEC_ERROR_INVALID_KEY); ++ rv = SECFailure; ++ } else { ++ /* if we are called again with this key, no need to verify ++ * again */ ++ key->needVerify = PR_FALSE; ++ } ++ return rv; ++} ++ ++SECStatus + RSA_EncryptOAEP(RSAPublicKey *key, + HASH_HashType hashAlg, + HASH_HashType maskHashAlg, + const unsigned char *label, + unsigned int labelLen, + const unsigned char *seed, + unsigned int seedLen, + unsigned char *output, +@@ -827,16 +898,25 @@ RSA_EncryptOAEP(RSAPublicKey *key, + return SECFailure; + } + + if ((labelLen == 0 && label != NULL) || + (labelLen > 0 && label == NULL)) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return SECFailure; + } ++ /* we only need to verify public keys once, and only in FIPS mode ++ */ ++ if (key->needVerify) { ++ rv = RSA_PartialVerify(key); ++ if (rv != SECSuccess) { ++ /* error code set by RSA_PartialVerify() */ ++ return rv; ++ } ++ } + + oaepEncoded = (unsigned char *)PORT_Alloc(modulusLen); + if (oaepEncoded == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + rv = eme_oaep_encode(oaepEncoded, modulusLen, input, inputLen, + hashAlg, maskHashAlg, label, labelLen, seed, seedLen); +diff --git a/lib/freebl/secmpi.c b/lib/freebl/secmpi.c +--- a/lib/freebl/secmpi.c ++++ b/lib/freebl/secmpi.c +@@ -17,12 +17,18 @@ mpp_random_secure(mp_int *a) + + mp_err + mpp_pprime_secure(mp_int *a, int nt) + { + return mpp_pprime_ext_random(a, nt, &mpp_random_secure); + } + + mp_err ++mpp_pprime_or_power_secure(mp_int *a, mp_int *fact, int nt) ++{ ++ return mpp_pprime_or_power_ext_random(a, fact, nt, &mpp_random_secure); ++} ++ ++mp_err + mpp_make_prime_secure(mp_int *start, mp_size nBits, mp_size strong) + { + return mpp_make_prime_ext_random(start, nBits, strong, &mpp_random_secure); + } +diff --git a/lib/freebl/secmpi.h b/lib/freebl/secmpi.h +--- a/lib/freebl/secmpi.h ++++ b/lib/freebl/secmpi.h +@@ -54,10 +54,13 @@ + } + + /* Fill the `used` digits of an mp_int with random bits */ + mp_err mpp_random_secure(mp_int *a); + + /* Pseudo-primality testing using `mpp_random_secure` to choose Miller-Rabin base */ + mp_err mpp_pprime_secure(mp_int *a, int nt); + ++/* Pseudo-primality testing using `mpp_random_secure` to choose extended Miller-Rabin base */ ++mp_err mpp_pprime_or_power_secure(mp_int *a, mp_int *fact, int nt); ++ + /* Variant of `mpp_make_prime` using `mpp_random_secure` to choose Miller-Rabin base */ + mp_err mpp_make_prime_secure(mp_int *start, mp_size nBits, mp_size strong); +diff --git a/lib/softoken/fipstest.c b/lib/softoken/fipstest.c +--- a/lib/softoken/fipstest.c ++++ b/lib/softoken/fipstest.c +@@ -470,17 +470,18 @@ sftk_fips_RSA_PowerUpSelfTest(void) + 0x66, 0xa6, 0x5e, 0x30, 0x0c, 0x82, 0xd5, 0x81 + }; + + static const RSAPublicKey bl_public_key = { + NULL, + { FIPS_RSA_TYPE, (unsigned char *)rsa_modulus, + FIPS_RSA_MODULUS_LENGTH }, + { FIPS_RSA_TYPE, (unsigned char *)rsa_public_exponent, +- FIPS_RSA_PUBLIC_EXPONENT_LENGTH } ++ FIPS_RSA_PUBLIC_EXPONENT_LENGTH }, ++ PR_FALSE, + }; + static const RSAPrivateKey bl_private_key = { + NULL, + { FIPS_RSA_TYPE, (unsigned char *)rsa_version, + FIPS_RSA_PRIVATE_VERSION_LENGTH }, + { FIPS_RSA_TYPE, (unsigned char *)rsa_modulus, + FIPS_RSA_MODULUS_LENGTH }, + { FIPS_RSA_TYPE, (unsigned char *)rsa_public_exponent, +diff --git a/lib/softoken/lowkey.c b/lib/softoken/lowkey.c +--- a/lib/softoken/lowkey.c ++++ b/lib/softoken/lowkey.c +@@ -309,16 +309,18 @@ nsslowkey_ConvertToPublicKey(NSSLOWKEYPr + rv = SECITEM_CopyItem(arena, &pubk->u.rsa.modulus, + &privk->u.rsa.modulus); + if (rv == SECSuccess) { + rv = SECITEM_CopyItem(arena, &pubk->u.rsa.publicExponent, + &privk->u.rsa.publicExponent); + if (rv == SECSuccess) + return pubk; + } ++ /* this key was already verified fully as a private key */ ++ pubk->u.rsa.needVerify = PR_FALSE; + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + break; + case NSSLOWKEYDSAKey: + pubk = (NSSLOWKEYPublicKey *)PORT_ArenaZAlloc(arena, + sizeof(NSSLOWKEYPublicKey)); + if (pubk != NULL) { +diff --git a/lib/softoken/pkcs11.c b/lib/softoken/pkcs11.c +--- a/lib/softoken/pkcs11.c ++++ b/lib/softoken/pkcs11.c +@@ -2030,16 +2030,19 @@ sftk_GetPubKey(SFTKObject *object, CK_KE + return NULL; + } + + /* fill in the structure */ + pubKey->arena = arena; + switch (key_type) { + case CKK_RSA: + pubKey->keyType = NSSLOWKEYRSAKey; ++ /* if we claim the object is fix, then make sure it's verified ++ * before we do an OAEP operation */ ++ pubKey->u.rsa.needVerify = object->isFIPS; + crv = sftk_Attribute2SSecItem(arena, &pubKey->u.rsa.modulus, + object, CKA_MODULUS); + if (crv != CKR_OK) + break; + crv = sftk_Attribute2SSecItem(arena, &pubKey->u.rsa.publicExponent, + object, CKA_PUBLIC_EXPONENT); + break; + case CKK_DSA: +diff --git a/lib/softoken/pkcs11c.c b/lib/softoken/pkcs11c.c +--- a/lib/softoken/pkcs11c.c ++++ b/lib/softoken/pkcs11c.c +@@ -7370,16 +7370,18 @@ mldsa_next: + crv = CKR_HOST_MEMORY; + goto loser; + } + rv = SECITEM_CopyItem(arena, &pubk.u.rsa.publicExponent, &lpk->u.rsa.publicExponent); + if (rv != SECSuccess) { + crv = CKR_HOST_MEMORY; + goto loser; + } ++ pubk.u.rsa.needVerify = PR_FALSE; /* We're just encoding the key from the ++ * private key */ + + if (SEC_ASN1EncodeItem(arena, &spki.subjectPublicKey, + &pubk, nsslowkey_RSAPublicKeyTemplate) == NULL) { + crv = CKR_HOST_MEMORY; + goto loser; + } + + publicKeyInfo = SEC_ASN1EncodeItem(arena, NULL, +diff --git a/tests/cipher/cipher.sh b/tests/cipher/cipher.sh +--- a/tests/cipher/cipher.sh ++++ b/tests/cipher/cipher.sh +@@ -87,18 +87,18 @@ cipher_main() + cipher_without_offset + else + inOff=0 + while [ $inOff -lt 8 ] + do + outOff=0 + while [ $outOff -lt 8 ] + do +- echo "bltest -T -m $PARAM -d $CIPHERTESTDIR -1 $inOff -2 $outOff" +- ${PROFTOOL} ${BINDIR}/bltest${PROG_SUFFIX} -T -m $PARAM -d $CIPHERTESTDIR -1 $inOff -2 $outOff ++ echo "bltest -T -m $PARAM -d $CIPHERTESTDIR -1 $inOff -2 $outOff -8" ++ ${PROFTOOL} ${BINDIR}/bltest${PROG_SUFFIX} -T -m $PARAM -d $CIPHERTESTDIR -1 $inOff -2 $outOff -8 + if [ $? -ne 0 ]; then + failedStr="$failedStr[$inOff:$outOff]" + fi + outOff=`expr $outOff + 1` + done + inOff=`expr $inOff + 1` + done + if [ -n "$failedStr" ]; then diff --git a/nss-3.112-update-fixes.patch b/nss-3.112-update-fixes.patch new file mode 100644 index 0000000..46f1e55 --- /dev/null +++ b/nss-3.112-update-fixes.patch @@ -0,0 +1,799 @@ +# HG changeset patch +# User Robert Relyea +# Date 1762542092 28800 +# Fri Nov 07 11:01:32 2025 -0800 +# Branch RHEL10 +# Node ID 6949dbcd6631ec0db3f53b3cf8957fac3a80da25 +# Parent 3e2ea63bf062fef47334b69723db7eac95daaa2a +nss-3.112-update-fixes.patch + +diff --git a/lib/cryptohi/keythi.h b/lib/cryptohi/keythi.h +--- a/lib/cryptohi/keythi.h ++++ b/lib/cryptohi/keythi.h +@@ -240,16 +240,17 @@ typedef struct SECKEYPublicKeyStr SECKEY + struct SECKEYPrivateKeyStr { + PLArenaPool *arena; + KeyType keyType; + PK11SlotInfo *pkcs11Slot; /* pkcs11 slot this key lives in */ + CK_OBJECT_HANDLE pkcs11ID; /* ID of pkcs11 object */ + PRBool pkcs11IsTemp; /* temp pkcs11 object, delete it when done */ + void *wincx; /* context for errors and pw prompts */ + PRUint32 staticflags; /* bit flag of cached PKCS#11 attributes */ ++ PRBool pkcs11IsOwner; /* the pkcs11ID is owned by this SECKEY */ + }; + typedef struct SECKEYPrivateKeyStr SECKEYPrivateKey; + + typedef struct { + PRCList links; + SECKEYPrivateKey *key; + } SECKEYPrivateKeyListNode; + +diff --git a/lib/cryptohi/seckey.c b/lib/cryptohi/seckey.c +--- a/lib/cryptohi/seckey.c ++++ b/lib/cryptohi/seckey.c +@@ -416,17 +416,17 @@ SECKEY_CreateEDPrivateKey(SECKEYECParams + return (privk); + } + + void + SECKEY_DestroyPrivateKey(SECKEYPrivateKey *privk) + { + if (privk) { + if (privk->pkcs11Slot) { +- if (privk->pkcs11IsTemp) { ++ if (privk->pkcs11IsOwner) { + PK11_DestroyObject(privk->pkcs11Slot, privk->pkcs11ID); + } + PK11_FreeSlot(privk->pkcs11Slot); + } + if (privk->arena) { + PORT_FreeArena(privk->arena, PR_TRUE); + } + } +@@ -1429,16 +1429,17 @@ SECKEY_CopyPrivateKey(const SECKEYPrivat + copyk->pkcs11ID = + PK11_CopyKey(privk->pkcs11Slot, privk->pkcs11ID); + if (copyk->pkcs11ID == CK_INVALID_HANDLE) + goto fail; + } else { + copyk->pkcs11ID = privk->pkcs11ID; + } + copyk->pkcs11IsTemp = privk->pkcs11IsTemp; ++ copyk->pkcs11IsOwner = privk->pkcs11IsOwner; + copyk->wincx = privk->wincx; + copyk->staticflags = privk->staticflags; + return copyk; + } else { + PORT_SetError(SEC_ERROR_NO_MEMORY); + } + + fail: +diff --git a/lib/cryptohi/secvfy.c b/lib/cryptohi/secvfy.c +--- a/lib/cryptohi/secvfy.c ++++ b/lib/cryptohi/secvfy.c +@@ -436,17 +436,17 @@ sec_DecodeSigAlg(const SECKEYPublicKey * + const SECItem *param, SECOidTag *encalgp, SECOidTag *hashalg, + CK_MECHANISM_TYPE *mechp, SECItem *mechparamsp) + { + unsigned int len; + PLArenaPool *arena; + SECStatus rv; + SECItem oid; + SECOidTag encalg; +- PRBool comboRequired = PR_TRUE; ++ PRBool comboRequired = PR_FALSE; + char *evp; + + PR_ASSERT(hashalg != NULL); + PR_ASSERT(encalgp != NULL); + PR_ASSERT(mechp != NULL); + /* Get the expected combined mechanism from the signature OID + * We'll override it in the table below if necessary */ + *mechp = PK11_AlgtagToMechanism(sigAlg); +diff --git a/lib/freebl/leancrypto/lc_sha3.h b/lib/freebl/leancrypto/lc_sha3.h +--- a/lib/freebl/leancrypto/lc_sha3.h ++++ b/lib/freebl/leancrypto/lc_sha3.h +@@ -20,17 +20,20 @@ + struct lc_hash_ctx _##name ; \ + _##name.hash= type_; \ + _##name.buf= NULL; \ + _##name.stream = false; \ + _##name.u.ctx_ptr = NULL; \ + struct lc_hash_ctx *name= &_##name; + + #define LC_HASH_SET_CTX(name, type_) \ +- name->hash= type_; ++ name->hash= type_; \ ++ name->stream = true; \ ++ name->u.ctx_ptr = NULL; \ ++ name->buf = NULL; + + #define LC_SHAKE_256_CTX(name) \ + LC_HASH_SET_CTX(name, lc_shake256); + + typedef enum { + lc_shake128, + lc_shake256, + } sha3Type; +diff --git a/lib/pk11wrap/pk11akey.c b/lib/pk11wrap/pk11akey.c +--- a/lib/pk11wrap/pk11akey.c ++++ b/lib/pk11wrap/pk11akey.c +@@ -1002,22 +1002,23 @@ PK11_ExtractPublicKey(PK11SlotInfo *slot + return pubKey; + } + + /* + * Build a Private Key structure from raw PKCS #11 information. + */ + SECKEYPrivateKey * + PK11_MakePrivKey(PK11SlotInfo *slot, KeyType keyType, +- PRBool isTemp, CK_OBJECT_HANDLE privID, void *wincx) ++ PRBool isOwner, CK_OBJECT_HANDLE privID, void *wincx) + { + PLArenaPool *arena; + SECKEYPrivateKey *privKey; + PRBool isPrivate; + SECStatus rv; ++ PRBool isTemp = isOwner; + + /* don't know? look it up */ + if (keyType == nullKey) { + CK_KEY_TYPE pk11Type = CKK_RSA; + + pk11Type = PK11_ReadULongAttribute(slot, privID, CKA_KEY_TYPE); + isTemp = (PRBool)!PK11_HasAttributeSet(slot, privID, CKA_TOKEN, PR_FALSE); + keyType = pk11_GetKeyTypeFromPKCS11KeyType(pk11Type); +@@ -1048,16 +1049,17 @@ PK11_MakePrivKey(PK11SlotInfo *slot, Key + return NULL; + } + + privKey->arena = arena; + privKey->keyType = keyType; + privKey->pkcs11Slot = PK11_ReferenceSlot(slot); + privKey->pkcs11ID = privID; + privKey->pkcs11IsTemp = isTemp; ++ privKey->pkcs11IsOwner = isOwner; + privKey->wincx = wincx; + + return privKey; + } + + PK11SlotInfo * + PK11_GetSlotFromPrivateKey(SECKEYPrivateKey *key) + { +@@ -2635,17 +2637,17 @@ PK11_CopyTokenPrivKeyToSessionPrivKey(PK + 1, &newKeyID); + PK11_ExitSlotMonitor(destSlot); + + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + return NULL; + } + +- return PK11_MakePrivKey(destSlot, privKey->keyType, PR_TRUE /*isTemp*/, ++ return PK11_MakePrivKey(destSlot, privKey->keyType, PR_TRUE /*isOwner*/, + newKeyID, privKey->wincx); + } + + SECKEYPrivateKey * + PK11_ConvertSessionPrivKeyToTokenPrivKey(SECKEYPrivateKey *privk, void *wincx) + { + PK11SlotInfo *slot = privk->pkcs11Slot; + CK_ATTRIBUTE template[1]; +@@ -2668,17 +2670,17 @@ PK11_ConvertSessionPrivKeyToTokenPrivKey + template, 1, &newKeyID); + PK11_RestoreROSession(slot, rwsession); + + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + return NULL; + } + +- return PK11_MakePrivKey(slot, nullKey /*KeyType*/, PR_FALSE /*isTemp*/, ++ return PK11_MakePrivKey(slot, nullKey /*KeyType*/, PR_FALSE /*isOwner*/, + newKeyID, NULL /*wincx*/); + } + + /* + * destroy a private key if there are no matching certs. + * this function also frees the privKey structure. + */ + SECStatus +@@ -2731,17 +2733,17 @@ pk11_DoKeys(PK11SlotInfo *slot, CK_OBJEC + { + SECStatus rv = SECSuccess; + SECKEYPrivateKey *privKey; + pk11KeyCallback *keycb = (pk11KeyCallback *)arg; + if (!arg) { + return SECFailure; + } + +- privKey = PK11_MakePrivKey(slot, nullKey, PR_TRUE, keyHandle, keycb->wincx); ++ privKey = PK11_MakePrivKey(slot, nullKey, PR_FALSE, keyHandle, keycb->wincx); + + if (privKey == NULL) { + return SECFailure; + } + + if (keycb->callback) { + rv = (*keycb->callback)(privKey, keycb->callbackArg); + } +@@ -2823,17 +2825,17 @@ PK11_FindKeyByKeyID(PK11SlotInfo *slot, + { + CK_OBJECT_HANDLE keyHandle; + SECKEYPrivateKey *privKey; + + keyHandle = pk11_FindPrivateKeyFromCertID(slot, keyID); + if (keyHandle == CK_INVALID_HANDLE) { + return NULL; + } +- privKey = PK11_MakePrivKey(slot, nullKey, PR_TRUE, keyHandle, wincx); ++ privKey = PK11_MakePrivKey(slot, nullKey, PR_FALSE, keyHandle, wincx); + return privKey; + } + + /* + * Generate a CKA_ID from the relevant public key data. The CKA_ID is generated + * from the pubKeyData by SHA1_Hashing it to produce a smaller CKA_ID (to make + * smart cards happy. + */ +@@ -3011,15 +3013,15 @@ PK11_ListPrivKeysInSlot(PK11SlotInfo *sl + keys = SECKEY_NewPrivateKeyList(); + if (keys == NULL) { + PORT_Free(key_ids); + return NULL; + } + + for (i = 0; i < objCount; i++) { + SECKEYPrivateKey *privKey = +- PK11_MakePrivKey(slot, nullKey, PR_TRUE, key_ids[i], wincx); ++ PK11_MakePrivKey(slot, nullKey, PR_FALSE, key_ids[i], wincx); + SECKEY_AddPrivateKeyToListTail(keys, privKey); + } + + PORT_Free(key_ids); + return keys; + } +diff --git a/lib/pk11wrap/pk11skey.c b/lib/pk11wrap/pk11skey.c +--- a/lib/pk11wrap/pk11skey.c ++++ b/lib/pk11wrap/pk11skey.c +@@ -433,17 +433,17 @@ PK11_VerifyKeyOK(PK11SymKey *key) + return PR_FALSE; + } + return (PRBool)(key->series == key->slot->series); + } + + static PK11SymKey * + pk11_ImportSymKeyWithTempl(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, + PK11Origin origin, PRBool isToken, CK_ATTRIBUTE *keyTemplate, +- unsigned int templateCount, SECItem *key, void *wincx) ++ unsigned int templateCount, SECItem *key, PRBool force, void *wincx) + { + PK11SymKey *symKey; + SECStatus rv; + + symKey = pk11_CreateSymKey(slot, type, !isToken, PR_TRUE, wincx); + if (symKey == NULL) { + return NULL; + } +@@ -458,22 +458,71 @@ pk11_ImportSymKeyWithTempl(PK11SlotInfo + return NULL; + } + + symKey->origin = origin; + + /* import the keys */ + rv = PK11_CreateNewObject(slot, symKey->session, keyTemplate, + templateCount, isToken, &symKey->objectID); +- if (rv != SECSuccess) { ++ if (rv == SECSuccess) { ++ return symKey; ++ } ++ /* we failed to create the key, if force isn't set, we just fail now */ ++ if (!force) { ++ PK11_FreeSymKey(symKey); ++ return NULL; ++ } ++ /* if force is set, we are simulating an unwrap, probably from another token, we ++ * are probably here because we are trying to import into a FIPS token. Normally ++ * we would want this to fail, but the application got here because they are using ++ * unwrap, which is the correct way to do this, so try to import the key into ++ * the FIPS token my hand */ ++ /* first generate a seed key */ ++ PK11SymKey *seedKey = PK11_KeyGen(slot, CKM_SHA256_HMAC, NULL, 256, wincx); ++ ++ if (seedKey == NULL) { + PK11_FreeSymKey(symKey); + return NULL; + } + +- return symKey; ++ /* now append our key data to the seed key and truncate the seed key */ ++ CK_KEY_DERIVATION_STRING_DATA params = { 0 }; ++ CK_MECHANISM mechanism = { 0, NULL, 0 }; ++ params.pData = key->data; ++ params.ulLen = key->len; ++ mechanism.mechanism = CKM_CONCATENATE_DATA_AND_BASE; ++ mechanism.pParameter = ¶ms; ++ mechanism.ulParameterLen = sizeof(params); ++ ++ /* derive removed the CKA_VALUE_LEN before it called us, but now we need ++ * it back. We know there is space in the template because derive leaves ++ * space for the CKA_VALUE_LEN attribute. Any other callers that set force ++ * should also make sure there is space for the CKA_VALUE_LEN */ ++ CK_ULONG valueLen; /* Don't define this in the 'if' statement, because it ++ * would go out of scope before we use it */ ++ if (!pk11_FindAttrInTemplate(keyTemplate, templateCount, CKA_VALUE_LEN)) { ++ valueLen = (CK_ULONG)key->len; ++ keyTemplate[templateCount].type = CKA_VALUE_LEN; ++ keyTemplate[templateCount].pValue = (void *)&valueLen; ++ keyTemplate[templateCount].ulValueLen = sizeof(valueLen); ++ templateCount++; ++ } ++ ++ CK_RV crv = PK11_GETTAB(slot)->C_DeriveKey(symKey->session, &mechanism, ++ seedKey->objectID, keyTemplate, ++ templateCount, ++ &symKey->objectID); ++ PK11_FreeSymKey(seedKey); ++ if (crv == CKR_OK) { ++ return symKey; ++ } ++ ++ PK11_FreeSymKey(symKey); ++ return NULL; + } + + /* + * turn key bits into an appropriate key object + */ + PK11SymKey * + PK11_ImportSymKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, + PK11Origin origin, CK_ATTRIBUTE_TYPE operation, SECItem *key, void *wincx) +@@ -501,17 +550,17 @@ PK11_ImportSymKey(PK11SlotInfo *slot, CK + attrs++; + PK11_SETATTRS(attrs, operation, &cktrue, 1); + attrs++; + templateCount = attrs - keyTemplate; + PR_ASSERT(templateCount + 1 <= sizeof(keyTemplate) / sizeof(CK_ATTRIBUTE)); + + keyType = PK11_GetKeyType(type, key->len); + symKey = pk11_ImportSymKeyWithTempl(slot, type, origin, PR_FALSE, +- keyTemplate, templateCount, key, wincx); ++ keyTemplate, templateCount, key, PR_FALSE, wincx); + return symKey; + } + /* Import a PKCS #11 data object and return it as a key. This key is + * only useful in a limited number of mechanisms, such as HKDF. */ + PK11SymKey * + PK11_ImportDataKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, PK11Origin origin, + CK_ATTRIBUTE_TYPE operation, SECItem *key, void *wincx) + { +@@ -580,17 +629,17 @@ PK11_ImportSymKeyWithFlags(PK11SlotInfo + PK11_SETATTRS(attrs, operation, &cktrue, sizeof(cktrue)); + attrs++; + } + templateCount = attrs - keyTemplate; + PR_ASSERT(templateCount + 1 <= sizeof(keyTemplate) / sizeof(CK_ATTRIBUTE)); + + keyType = PK11_GetKeyType(type, key->len); + symKey = pk11_ImportSymKeyWithTempl(slot, type, origin, isPerm, +- keyTemplate, templateCount, key, wincx); ++ keyTemplate, templateCount, key, PR_FALSE, wincx); + if (symKey && isPerm) { + symKey->owner = PR_FALSE; + } + return symKey; + } + + PK11SymKey * + PK11_FindFixedKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, SECItem *keyID, +@@ -2670,29 +2719,29 @@ pk11_HandUnwrap(PK11SlotInfo *slot, CK_O + } + + outKey.len = (key_size == 0) ? len : key_size; + outKey.type = siBuffer; + + if (PK11_DoesMechanism(slot, target)) { + symKey = pk11_ImportSymKeyWithTempl(slot, target, PK11_OriginUnwrap, + isPerm, keyTemplate, +- templateCount, &outKey, wincx); ++ templateCount, &outKey, PR_TRUE, wincx); + } else { + slot = PK11_GetBestSlot(target, wincx); + if (slot == NULL) { + PORT_SetError(SEC_ERROR_NO_MODULE); + PORT_Free(outKey.data); + if (crvp) + *crvp = CKR_DEVICE_ERROR; + return NULL; + } + symKey = pk11_ImportSymKeyWithTempl(slot, target, PK11_OriginUnwrap, + isPerm, keyTemplate, +- templateCount, &outKey, wincx); ++ templateCount, &outKey, PR_TRUE, wincx); + PK11_FreeSlot(slot); + } + PORT_Free(outKey.data); + + if (crvp) + *crvp = symKey ? CKR_OK : CKR_DEVICE_ERROR; + return symKey; + } +diff --git a/lib/softoken/pkcs11.c b/lib/softoken/pkcs11.c +--- a/lib/softoken/pkcs11.c ++++ b/lib/softoken/pkcs11.c +@@ -1033,36 +1033,40 @@ sftk_handleCrlObject(SFTKSession *sessio + + /* + * check the consistancy and initialize a Public Key Object + */ + static CK_RV + sftk_handlePublicKeyObject(SFTKSession *session, SFTKObject *object, + CK_KEY_TYPE key_type) + { +- CK_BBOOL encrypt = CK_TRUE; +- CK_BBOOL recover = CK_TRUE; +- CK_BBOOL wrap = CK_TRUE; ++ CK_BBOOL encrypt = CK_FALSE; ++ CK_BBOOL recover = CK_FALSE; ++ CK_BBOOL wrap = CK_FALSE; + CK_BBOOL derive = CK_FALSE; +- CK_BBOOL verify = CK_TRUE; ++ CK_BBOOL verify = CK_FALSE; + CK_BBOOL encapsulate = CK_FALSE; + CK_ULONG paramSet = 0; + CK_RV crv; + + switch (key_type) { + case CKK_RSA: + crv = sftk_ConstrainAttribute(object, CKA_MODULUS, + RSA_MIN_MODULUS_BITS, 0, 0); + if (crv != CKR_OK) { + return crv; + } + crv = sftk_ConstrainAttribute(object, CKA_PUBLIC_EXPONENT, 2, 0, 0); + if (crv != CKR_OK) { + return crv; + } ++ encrypt = CK_TRUE; ++ recover = CK_TRUE; ++ wrap = CK_TRUE; ++ verify = CK_TRUE; + break; + case CKK_DSA: + crv = sftk_ConstrainAttribute(object, CKA_SUBPRIME, + DSA_MIN_Q_BITS, DSA_MAX_Q_BITS, 0); + if (crv != CKR_OK) { + return crv; + } + crv = sftk_ConstrainAttribute(object, CKA_PRIME, +@@ -1073,92 +1077,73 @@ sftk_handlePublicKeyObject(SFTKSession * + crv = sftk_ConstrainAttribute(object, CKA_BASE, 2, DSA_MAX_P_BITS, 0); + if (crv != CKR_OK) { + return crv; + } + crv = sftk_ConstrainAttribute(object, CKA_VALUE, 2, DSA_MAX_P_BITS, 0); + if (crv != CKR_OK) { + return crv; + } +- encrypt = CK_FALSE; +- recover = CK_FALSE; +- wrap = CK_FALSE; ++ verify = CK_TRUE; + break; + case CKK_DH: + crv = sftk_ConstrainAttribute(object, CKA_PRIME, + DH_MIN_P_BITS, DH_MAX_P_BITS, 0); + if (crv != CKR_OK) { + return crv; + } + crv = sftk_ConstrainAttribute(object, CKA_BASE, 2, DH_MAX_P_BITS, 0); + if (crv != CKR_OK) { + return crv; + } + crv = sftk_ConstrainAttribute(object, CKA_VALUE, 2, DH_MAX_P_BITS, 0); + if (crv != CKR_OK) { + return crv; + } +- verify = CK_FALSE; + derive = CK_TRUE; +- encrypt = CK_FALSE; +- recover = CK_FALSE; +- wrap = CK_FALSE; + break; + case CKK_EC_MONTGOMERY: + case CKK_EC_EDWARDS: + case CKK_EC: + if (!sftk_hasAttribute(object, CKA_EC_PARAMS)) { + return CKR_TEMPLATE_INCOMPLETE; + } + if (!sftk_hasAttribute(object, CKA_EC_POINT)) { + return CKR_TEMPLATE_INCOMPLETE; + } + /* for ECDSA and EDDSA. Change if the structure of any of them is modified. */ + derive = (key_type == CKK_EC_EDWARDS) ? CK_FALSE : CK_TRUE; /* CK_TRUE for ECDH */ + verify = (key_type == CKK_EC_MONTGOMERY) ? CK_FALSE : CK_TRUE; /* for ECDSA and EDDSA */ +- encrypt = CK_FALSE; +- recover = CK_FALSE; +- wrap = CK_FALSE; + break; + #ifndef NSS_DISABLE_KYBER + case CKK_NSS_KYBER: + #endif + case CKK_NSS_ML_KEM: + case CKK_ML_KEM: + if (!sftk_hasAttribute(object, CKA_PARAMETER_SET)) { + if (!sftk_hasAttribute(object, CKA_NSS_PARAMETER_SET)) { + return CKR_TEMPLATE_INCOMPLETE; + } + } +- derive = CK_FALSE; +- verify = CK_FALSE; +- encrypt = CK_FALSE; +- recover = CK_FALSE; +- wrap = CK_FALSE; + encapsulate = CK_TRUE; + break; + #ifdef NSS_ENABLE_ML_DSA + case CKK_ML_DSA: + if (!sftk_hasAttribute(object, CKA_PARAMETER_SET)) { + return CKR_TEMPLATE_INCOMPLETE; + } + crv = sftk_GetULongAttribute (object, CKA_PARAMETER_SET, + ¶mSet); + if (crv != CKR_OK) { + return crv; + } + if (sftk_MLDSAGetSigLen(paramSet) == 0) { + return CKR_ATTRIBUTE_VALUE_INVALID; + } +- derive = CK_FALSE; + verify = CK_TRUE; +- encrypt = CK_FALSE; +- recover = CK_FALSE; +- wrap = CK_FALSE; +- encapsulate = CK_FALSE; + break; + #endif + default: + return CKR_ATTRIBUTE_VALUE_INVALID; + } + + /* make sure the required fields exist */ + crv = sftk_defaultAttribute(object, CKA_SUBJECT, NULL, 0); +@@ -1226,21 +1211,21 @@ sftk_verifyRSAPrivateKey(SFTKObject *obj + + /* + * check the consistancy and initialize a Private Key Object + */ + static CK_RV + sftk_handlePrivateKeyObject(SFTKSession *session, SFTKObject *object, CK_KEY_TYPE key_type) + { + CK_BBOOL cktrue = CK_TRUE; +- CK_BBOOL encrypt = CK_TRUE; ++ CK_BBOOL encrypt = CK_FALSE; + CK_BBOOL sign = CK_FALSE; +- CK_BBOOL recover = CK_TRUE; +- CK_BBOOL wrap = CK_TRUE; +- CK_BBOOL derive = CK_TRUE; ++ CK_BBOOL recover = CK_FALSE; ++ CK_BBOOL wrap = CK_FALSE; ++ CK_BBOOL derive = CK_FALSE; + CK_BBOOL decapsulate = CK_FALSE; + CK_BBOOL ckfalse = CK_FALSE; + PRBool createObjectInfo = PR_TRUE; + PRBool fillPrivateKey = PR_FALSE; + CK_ULONG paramSet = 0; + int missing_rsa_mod_component = 0; + int missing_rsa_exp_component = 0; + int missing_rsa_crt_component = 0; +@@ -1302,71 +1287,74 @@ sftk_handlePrivateKeyObject(SFTKSession + crv = sftk_forceAttribute(object, CKA_NSS_DB, + sftk_item_expand(&mod)); + if (mod.data) + SECITEM_ZfreeItem(&mod, PR_FALSE); + if (crv != CKR_OK) + return crv; + + sign = CK_TRUE; +- derive = CK_FALSE; ++ wrap = CK_TRUE; ++ encrypt = CK_TRUE; + break; + case CKK_DSA: + if (!sftk_hasAttribute(object, CKA_SUBPRIME)) { + return CKR_TEMPLATE_INCOMPLETE; + } ++ if (!sftk_hasAttribute(object, CKA_PRIME)) { ++ return CKR_TEMPLATE_INCOMPLETE; ++ } ++ if (!sftk_hasAttribute(object, CKA_BASE)) { ++ return CKR_TEMPLATE_INCOMPLETE; ++ } ++ if (!sftk_hasAttribute(object, CKA_VALUE)) { ++ return CKR_TEMPLATE_INCOMPLETE; ++ } + sign = CK_TRUE; +- derive = CK_FALSE; +- /* fall through */ ++ break; + case CKK_DH: + if (!sftk_hasAttribute(object, CKA_PRIME)) { + return CKR_TEMPLATE_INCOMPLETE; + } + if (!sftk_hasAttribute(object, CKA_BASE)) { + return CKR_TEMPLATE_INCOMPLETE; + } + if (!sftk_hasAttribute(object, CKA_VALUE)) { + return CKR_TEMPLATE_INCOMPLETE; + } + /* allow subprime to be set after the fact */ + crv = sftk_defaultAttribute(object, CKA_SUBPRIME, NULL, 0); + if (crv != CKR_OK) { + return crv; + } +- encrypt = CK_FALSE; +- recover = CK_FALSE; +- wrap = CK_FALSE; ++ derive = CK_TRUE; + break; + case CKK_EC: + case CKK_EC_EDWARDS: + case CKK_EC_MONTGOMERY: + if (!sftk_hasAttribute(object, CKA_EC_PARAMS)) { + return CKR_TEMPLATE_INCOMPLETE; + } + if (!sftk_hasAttribute(object, CKA_VALUE)) { + return CKR_TEMPLATE_INCOMPLETE; + } + /* for ECDSA and EDDSA. Change if the structure of any of them is modified. */ + derive = (key_type == CKK_EC_EDWARDS) ? CK_FALSE : CK_TRUE; /* CK_TRUE for ECDH */ + sign = (key_type == CKK_EC_MONTGOMERY) ? CK_FALSE : CK_TRUE; /* for ECDSA and EDDSA */ +- encrypt = CK_FALSE; +- recover = CK_FALSE; +- wrap = CK_FALSE; + break; + case CKK_NSS_JPAKE_ROUND1: + if (!sftk_hasAttribute(object, CKA_PRIME) || + !sftk_hasAttribute(object, CKA_SUBPRIME) || + !sftk_hasAttribute(object, CKA_BASE)) { + return CKR_TEMPLATE_INCOMPLETE; + } + /* fall through */ + case CKK_NSS_JPAKE_ROUND2: + /* CKA_NSS_JPAKE_SIGNERID and CKA_NSS_JPAKE_PEERID are checked in + the J-PAKE code. */ +- encrypt = sign = recover = wrap = CK_FALSE; + derive = CK_TRUE; + createObjectInfo = PR_FALSE; + break; + #ifndef NSS_DISABLE_KYBER + case CKK_NSS_KYBER: + #endif + case CKK_NSS_ML_KEM: + case CKK_ML_KEM: +@@ -1376,17 +1364,16 @@ sftk_handlePrivateKeyObject(SFTKSession + if (!sftk_hasAttribute(object, CKA_VALUE)) { + return CKR_TEMPLATE_INCOMPLETE; + } + if (!sftk_hasAttribute(object, CKA_PARAMETER_SET)) { + if (!sftk_hasAttribute(object, CKA_NSS_PARAMETER_SET)) { + return CKR_TEMPLATE_INCOMPLETE; + } + } +- encrypt = sign = recover = wrap = CK_FALSE; + decapsulate = CK_TRUE; + break; + #ifdef NSS_ENABLE_ML_DSA + case CKK_ML_DSA: + if (!sftk_hasAttribute(object, CKA_KEY_TYPE)) { + return CKR_TEMPLATE_INCOMPLETE; + } + /* make sure we have a CKA_PARAMETER_SET */ +@@ -1454,17 +1441,16 @@ sftk_handlePrivateKeyObject(SFTKSession + } + } + sftk_DeleteAttributeType(object, CKA_NSS_SEED_OK); + /* if we got this far, we should have a CKA_VALUE, either but + * one given to us, or by it being generated above */ + if (!sftk_hasAttribute(object, CKA_VALUE)) { + return CKR_TEMPLATE_INCOMPLETE; + } +- encrypt = decapsulate = recover = wrap = CK_FALSE; + sign = CK_TRUE; + break; + #endif + default: + return CKR_ATTRIBUTE_VALUE_INVALID; + } + crv = sftk_defaultAttribute(object, CKA_SUBJECT, NULL, 0); + if (crv != CKR_OK) +diff --git a/lib/softoken/sftkdb.c b/lib/softoken/sftkdb.c +--- a/lib/softoken/sftkdb.c ++++ b/lib/softoken/sftkdb.c +@@ -54,16 +54,17 @@ sftkdb_isULONGAttribute(CK_ATTRIBUTE_TYP + case CKA_KEY_GEN_MECHANISM: + case CKA_KEY_TYPE: + case CKA_MECHANISM_TYPE: + case CKA_MODULUS_BITS: + case CKA_PRIME_BITS: + case CKA_SUBPRIME_BITS: + case CKA_VALUE_BITS: + case CKA_VALUE_LEN: ++ case CKA_PARAMETER_SET: + + case CKA_TRUST_DIGITAL_SIGNATURE: + case CKA_TRUST_NON_REPUDIATION: + case CKA_TRUST_KEY_ENCIPHERMENT: + case CKA_TRUST_DATA_ENCIPHERMENT: + case CKA_TRUST_KEY_AGREEMENT: + case CKA_TRUST_KEY_CERT_SIGN: + case CKA_TRUST_CRL_SIGN: +@@ -386,17 +387,25 @@ sftkdb_fixupTemplateOut(CK_ATTRIBUTE *te + CK_ULONG value; + + value = sftk_SDBULong2ULong(ntemplate[i].pValue); + if (length < sizeof(CK_ULONG)) { + template[i].ulValueLen = -1; + crv = CKR_BUFFER_TOO_SMALL; + continue; + } +- PORT_Memcpy(template[i].pValue, &value, sizeof(CK_ULONG)); ++ /* handle the case where the CKA_PARAMETER_SET was ++ * incorrectly encoded */ ++ if ((value > 0xff) && ++ (template[i].type == CKA_PARAMETER_SET)) { ++ PORT_Memcpy(template[i].pValue, ntemplate[i].pValue, ++ ntemplate[i].ulValueLen); ++ } else { ++ PORT_Memcpy(template[i].pValue, &value, sizeof(CK_ULONG)); ++ } + } + template[i].ulValueLen = sizeof(CK_ULONG); + } + } + + /* if no data was retrieved, no need to process encrypted or signed + * attributes */ + if ((template[i].pValue == NULL) || (template[i].ulValueLen == -1)) { +@@ -1580,16 +1589,18 @@ sftkdb_DestroyObject(SFTKDBHandle *handl + (void)sftkdb_DestroyAttributeSignature(handle, keydb, objectID, + CKA_PRIME_2); + (void)sftkdb_DestroyAttributeSignature(handle, keydb, objectID, + CKA_EXPONENT_1); + (void)sftkdb_DestroyAttributeSignature(handle, keydb, objectID, + CKA_EXPONENT_2); + (void)sftkdb_DestroyAttributeSignature(handle, keydb, objectID, + CKA_COEFFICIENT); ++ (void)sftkdb_DestroyAttributeSignature(handle, keydb, objectID, ++ CKA_SEED); + } else { + keydb = SFTK_GET_SDB(handle->peerDB); + } + /* now destroy any authenticated attributes that may exist */ + (void)sftkdb_DestroyAttributeSignature(handle, keydb, objectID, + CKA_MODULUS); + (void)sftkdb_DestroyAttributeSignature(handle, keydb, objectID, + CKA_PUBLIC_EXPONENT); +diff --git a/lib/softoken/sftkpwd.c b/lib/softoken/sftkpwd.c +--- a/lib/softoken/sftkpwd.c ++++ b/lib/softoken/sftkpwd.c +@@ -1205,16 +1205,17 @@ sftk_updateEncrypted(PLArenaPool *arena, + CK_ATTRIBUTE_TYPE privAttrTypes[] = { + CKA_VALUE, + CKA_PRIVATE_EXPONENT, + CKA_PRIME_1, + CKA_PRIME_2, + CKA_EXPONENT_1, + CKA_EXPONENT_2, + CKA_COEFFICIENT, ++ CKA_SEED, + }; + const CK_ULONG privAttrCount = sizeof(privAttrTypes) / sizeof(privAttrTypes[0]); + + // We don't know what attributes this object has, so we update them one at a + // time. + unsigned int i; + for (i = 0; i < privAttrCount; i++) { + // Read the old attribute in the clear. diff --git a/nss.spec b/nss.spec index 9f2f9a8..04ffd68 100644 --- a/nss.spec +++ b/nss.spec @@ -3,7 +3,7 @@ # NOTE: To avoid NVR clashes of nspr* packages: # - reset %%{nspr_release} to 1, when updating %%{nspr_version} # - increment %%{nspr_version}, when updating the NSS part only -%global baserelease 4 +%global baserelease 5 %global nss_release %baserelease # use "%%global nspr_release %%[%%baserelease+n]" to handle offsets when # release number between nss and nspr are different. @@ -168,19 +168,22 @@ Patch53: nss-3.101-skip-ocsp-if-not-connected.patch Patch74: nss-3.90-dh-test-update.patch Patch75: nss-3.90-ppc_no_init.patch # https://bugzilla.mozilla.org/show_bug.cgi?id=676100 -Patch85: nss-3.101-fix-cms-abi-break.patch -Patch88: nss-3.101-fix-shlibsign-fips.patch +Patch78: nss-3.101-fix-cms-abi-break.patch +Patch79: nss-3.101-fix-shlibsign-fips.patch # Post Quantum specific -Patch90: nss-3.112-disable-dsa.patch -Patch91: nss-3.112-replace-xyber-with-mlkem-256.patch -Patch92: nss-3.112-add-sec384r1-mlkem-1024.patch -Patch93: nss-3.112-add-ml-dsa-base.patch -Patch94: nss-3.112-add-ml-dsa-gtests.patch -Patch95: nss-3.112-add-ml-dsa-ssl-support.patch -Patch96: nss-3.112-fips-and-fixes-el10.patch -Patch97: nss-3.112-big-endian-compression-fix.patch -Patch98: nss-3.112-fix-get-interface.patch +Patch80: nss-3.112-disable-dsa.patch +Patch81: nss-3.112-replace-xyber-with-mlkem-256.patch +Patch82: nss-3.112-add-sec384r1-mlkem-1024.patch +Patch83: nss-3.112-add-ml-dsa-base.patch +Patch84: nss-3.112-add-ml-dsa-gtests.patch +Patch85: nss-3.112-add-ml-dsa-ssl-support.patch +Patch86: nss-3.112-fips-and-fixes-el10.patch +Patch87: nss-3.112-big-endian-compression-fix.patch +Patch88: nss-3.112-fix-get-interface.patch +Patch89: nss-3.112-mlkem-fips-update.patch +Patch90: nss-3.112-update-fixes.patch +Patch91: nss-3.112-partial-pub-key-validate.patch # NSS reverse patches Patch300: nss-3.79-distrusted-certs.patch @@ -728,6 +731,10 @@ rm -rf \ $RPM_BUILD_ROOT/%{_datadir}/aclocal/nspr.m4 \ $RPM_BUILD_ROOT/%{_includedir}/nspr4/md +#cp win.h to old name for compatibility +cp $RPM_BUILD_ROOT/%{_includedir}/nspr4/prwin.h \ + $RPM_BUILD_ROOT/%{_includedir}/nspr4/prwin16.h + for f in nspr-config; do install -c -m 644 ${f}.1 $RPM_BUILD_ROOT%{_mandir}/man1/${f}.1 done @@ -1164,11 +1171,22 @@ fi %changelog +* Mon Nov 3 2025 Bob Relyea - 3.112.0-5 +- fips update +- Fix indicators for the new post-quantum algorithms +- Fix the ML-KEM Self-tests +- Fix the ML-KEM zeroizaiton +- Add partial public validation before OAEP +- bug fixes +- add CKA_SEED to private attributes so they are updated on password change. +- mark CKA_PARAMETER_SET as CK_ULONG when storing into the database +- fix unrefrence read in leancrypto. + * Thu Aug 7 2025 Bob Relyea - 3.112.0-4 - fix interface issue when pulling 3.0 pkcs#11 interfaces explicitly * Fri Aug 1 2025 Bob Relyea - 3.112.0-3 -- restore CONCATENATE functions accidentally remvoed in the last patch +- restore CONCATENATE functions accidentally removed in the last patch - fix big endian issue in tstclnt and selfserv in certificate compression * Wed Jul 30 2025 Bob Relyea - 3.112.0-2