Skip to content

Commit

Permalink
Update Allowed RSA KeySize Generation to FIPS 186-5 specification (#1823
Browse files Browse the repository at this point in the history
)

### Description of changes: 
Updates RSA key size generation to support FIPS 186-5 specification that
allows for RSA key sizes >= 2048 and even modulus length.

**Note**: The even modulous length check is satisfied by the `bits % 128
== 0` check on the `RSA_generate_key_fips` and `EVP_PKEY_keygen`
indicator check paths. 128 is chosen here as the underlying RSA key
generation implementation will only generate RSA keys that are evenly
divisible by 128, otherwise it will round down to the nearest value.
Enforcing this on the key generation path will ensure that the request
bit length is always returned on the FIPS path. Signing/Verification
paths continue to enforce the divisible by 2 check in the event the key
was not generated by our module.

Also cleans up the entire ACVP RSA capabilities registration (looks like
a lot of whitespace changes, but I also collapsed some of the
registration attributes that didn't need to be duplicated). Also added
back the 1024 bit key testing with other algorithms in addition to
SHA-1.

Note ACVP only supports testing up to 8192 key size due to
infrastructure limitations, and SigGen and SigVer only supports keys up
to 4096.

ACVP KeyGen tests the following key sizes: 2048, 3072, 4096, 6144, or
8192

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.
  • Loading branch information
skmcgrail authored Sep 11, 2024
1 parent af0ecc7 commit 3f4d2f6
Show file tree
Hide file tree
Showing 10 changed files with 548 additions and 492 deletions.
11 changes: 6 additions & 5 deletions crypto/fipsmodule/rsa/rsa_impl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1269,11 +1269,12 @@ int RSA_generate_key_ex(RSA *rsa, int bits, const BIGNUM *e_value,
}

int RSA_generate_key_fips(RSA *rsa, int bits, BN_GENCB *cb) {
// FIPS 186-4 allows 2048-bit and 3072-bit RSA keys (1024-bit and 1536-bit
// primes, respectively) with the prime generation method we use.
// Subsequently, IG A.14 stated that larger modulus sizes can be used and ACVP
// testing supports 4096 bits.
if (bits != 2048 && bits != 3072 && bits != 4096) {
// FIPS 186-5 Section 5.1:
// This standard specifies the use of a modulus whose bit length is an even
// integer and greater than or equal to 2048 bits. Furthermore, this standard
// specifies that p and q be of the same bit length – namely, half the bit
// length of n
if (bits < 2048 || bits % 128 != 0) {
OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_RSA_PARAMETERS);
return 0;
}
Expand Down
26 changes: 9 additions & 17 deletions crypto/fipsmodule/service_indicator/service_indicator.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,14 +267,12 @@ static void evp_md_ctx_verify_service_indicator(const EVP_MD_CTX *ctx,
}
}

// The approved RSA key sizes for signing are 2048, 3072 and 4096 bits.
// Note: |EVP_PKEY_size| returns the size in bytes.
size_t pkey_size = EVP_PKEY_size(ctx->pctx->pkey);
// The approved RSA key sizes for signing are key sizes >= 2048 bits and bits % 2 == 0.
size_t n_bits = RSA_bits(ctx->pctx->pkey->pkey.rsa);

// Check if the MD type and the RSA key size are approved.
if (md_ok(md_type, pkey_type) &&
((rsa_1024_ok && pkey_size == 128) || pkey_size == 256 ||
pkey_size == 384 || pkey_size == 512)) {
((rsa_1024_ok && n_bits == 1024) || (n_bits >= 2048 && n_bits % 2 == 0))) {
FIPS_service_indicator_update_state();
}
} else if (pkey_type == EVP_PKEY_EC) {
Expand Down Expand Up @@ -306,18 +304,12 @@ void ECDH_verify_service_indicator(const EC_KEY *ec_key) {

void EVP_PKEY_keygen_verify_service_indicator(const EVP_PKEY *pkey) {
if (pkey->type == EVP_PKEY_RSA || pkey->type == EVP_PKEY_RSA_PSS) {
// 2048, 3072 and 4096 bit keys are approved for RSA key generation.
// EVP_PKEY_size returns the size of the key in bytes.
// Note: |EVP_PKEY_size| returns the length in bytes.
size_t key_size = EVP_PKEY_size(pkey);
switch (key_size) {
case 256:
case 384:
case 512:
FIPS_service_indicator_update_state();
break;
default:
break;
// The approved RSA key sizes for signing are key sizes >= 2048 bits and
// bits % 2 == 0, though we check bits % 128 == 0 for consistency with
// our RSA key generation.
size_t n_bits = RSA_bits(pkey->pkey.rsa);
if (n_bits >= 2048 && n_bits % 128 == 0) {
FIPS_service_indicator_update_state();
}
} else if (pkey->type == EVP_PKEY_EC) {
// Note: even though the function is called |EC_GROUP_get_curve_name|
Expand Down
92 changes: 67 additions & 25 deletions crypto/fipsmodule/service_indicator/service_indicator_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2013,9 +2013,8 @@ TEST(ServiceIndicatorTest, RSAKeyGen) {
bssl::UniquePtr<RSA> rsa(RSA_new());
ASSERT_TRUE(rsa);

// |RSA_generate_key_fips| may only be used for 2048-, 3072-, and 4096-bit
// keys.
for (const size_t bits : {512, 1024, 3071, 4095}) {
// |RSA_generate_key_fips| may only be used for bits >= 2048 && bits % 128 == 0
for (const size_t bits : {512, 1024, 2520, 3071}) {
SCOPED_TRACE(bits);

rsa.reset(RSA_new());
Expand All @@ -2024,8 +2023,9 @@ TEST(ServiceIndicatorTest, RSAKeyGen) {
EXPECT_EQ(approved, AWSLC_NOT_APPROVED);
}

// Test that we can generate keys of the supported lengths:
for (const size_t bits : {2048, 3072, 4096}) {
// Test that we can generate keys with supported lengths,
// larger key sizes are supported but are omitted for time.
for (const size_t bits : {2048, 3072, 4096, 6144, 8192}) {
SCOPED_TRACE(bits);

rsa.reset(RSA_new());
Expand All @@ -2045,7 +2045,7 @@ TEST(ServiceIndicatorTest, RSAKeyGen) {
ASSERT_TRUE(ctx);

if (kEVPKeyGenShouldCallFIPSFunctions) {
// Test unapproved key sizes of RSA.
// Test various unapproved key sizes of RSA.
for (const size_t bits : {512, 1024, 3071, 4095}) {
SCOPED_TRACE(bits);
CALL_SERVICE_AND_CHECK_APPROVED(
Expand All @@ -2059,8 +2059,8 @@ TEST(ServiceIndicatorTest, RSAKeyGen) {
raw = nullptr;
}

// Test approved key sizes of RSA.
for (const size_t bits : {2048, 3072, 4096}) {
// Test various approved key sizes of RSA.
for (const size_t bits : {2048, 3072, 4096, 6144, 8192}) {
SCOPED_TRACE(bits);
CALL_SERVICE_AND_CHECK_APPROVED(
approved, ASSERT_TRUE(EVP_PKEY_keygen_init(ctx.get())));
Expand Down Expand Up @@ -2095,9 +2095,6 @@ struct RSATestVector kRSATestVectors[] = {
{ 1536, &EVP_sha256, false, AWSLC_NOT_APPROVED, AWSLC_NOT_APPROVED },
{ 1536, &EVP_sha512, true, AWSLC_NOT_APPROVED, AWSLC_NOT_APPROVED },
{ 2048, &EVP_md5, false, AWSLC_NOT_APPROVED, AWSLC_NOT_APPROVED },
{ 3071, &EVP_md5, true, AWSLC_NOT_APPROVED, AWSLC_NOT_APPROVED },
{ 3071, &EVP_sha256, false, AWSLC_NOT_APPROVED, AWSLC_NOT_APPROVED },
{ 3071, &EVP_sha512, true, AWSLC_NOT_APPROVED, AWSLC_NOT_APPROVED },
{ 4096, &EVP_md5, false, AWSLC_NOT_APPROVED, AWSLC_NOT_APPROVED },

// RSA test cases that are approved.
Expand Down Expand Up @@ -2195,6 +2192,54 @@ struct RSATestVector kRSATestVectors[] = {
{ 4096, &EVP_sha3_256, true, AWSLC_APPROVED, AWSLC_APPROVED },
{ 4096, &EVP_sha3_384, true, AWSLC_APPROVED, AWSLC_APPROVED },
{ 4096, &EVP_sha3_512, true, AWSLC_APPROVED, AWSLC_APPROVED },

{ 6144, &EVP_sha1, false, AWSLC_NOT_APPROVED, AWSLC_APPROVED },
{ 6144, &EVP_sha224, false, AWSLC_APPROVED, AWSLC_APPROVED },
{ 6144, &EVP_sha256, false, AWSLC_APPROVED, AWSLC_APPROVED },
{ 6144, &EVP_sha384, false, AWSLC_APPROVED, AWSLC_APPROVED },
{ 6144, &EVP_sha512, false, AWSLC_APPROVED, AWSLC_APPROVED },
{ 6144, &EVP_sha512_224, false, AWSLC_APPROVED, AWSLC_APPROVED },
{ 6144, &EVP_sha512_256, false, AWSLC_APPROVED, AWSLC_APPROVED },
{ 6144, &EVP_sha3_224, false, AWSLC_APPROVED, AWSLC_APPROVED },
{ 6144, &EVP_sha3_256, false, AWSLC_APPROVED, AWSLC_APPROVED },
{ 6144, &EVP_sha3_384, false, AWSLC_APPROVED, AWSLC_APPROVED },
{ 6144, &EVP_sha3_512, false, AWSLC_APPROVED, AWSLC_APPROVED },

{ 6144, &EVP_sha1, true, AWSLC_NOT_APPROVED, AWSLC_APPROVED },
{ 6144, &EVP_sha224, true, AWSLC_APPROVED, AWSLC_APPROVED },
{ 6144, &EVP_sha256, true, AWSLC_APPROVED, AWSLC_APPROVED },
{ 6144, &EVP_sha384, true, AWSLC_APPROVED, AWSLC_APPROVED },
{ 6144, &EVP_sha512, true, AWSLC_APPROVED, AWSLC_APPROVED },
{ 6144, &EVP_sha512_224, true, AWSLC_APPROVED, AWSLC_APPROVED },
{ 6144, &EVP_sha512_256, true, AWSLC_APPROVED, AWSLC_APPROVED },
{ 6144, &EVP_sha3_224, true, AWSLC_APPROVED, AWSLC_APPROVED },
{ 6144, &EVP_sha3_256, true, AWSLC_APPROVED, AWSLC_APPROVED },
{ 6144, &EVP_sha3_384, true, AWSLC_APPROVED, AWSLC_APPROVED },
{ 6144, &EVP_sha3_512, true, AWSLC_APPROVED, AWSLC_APPROVED },

{ 8192, &EVP_sha1, false, AWSLC_NOT_APPROVED, AWSLC_APPROVED },
{ 8192, &EVP_sha224, false, AWSLC_APPROVED, AWSLC_APPROVED },
{ 8192, &EVP_sha256, false, AWSLC_APPROVED, AWSLC_APPROVED },
{ 8192, &EVP_sha384, false, AWSLC_APPROVED, AWSLC_APPROVED },
{ 8192, &EVP_sha512, false, AWSLC_APPROVED, AWSLC_APPROVED },
{ 8192, &EVP_sha512_224, false, AWSLC_APPROVED, AWSLC_APPROVED },
{ 8192, &EVP_sha512_256, false, AWSLC_APPROVED, AWSLC_APPROVED },
{ 8192, &EVP_sha3_224, false, AWSLC_APPROVED, AWSLC_APPROVED },
{ 8192, &EVP_sha3_256, false, AWSLC_APPROVED, AWSLC_APPROVED },
{ 8192, &EVP_sha3_384, false, AWSLC_APPROVED, AWSLC_APPROVED },
{ 8192, &EVP_sha3_512, false, AWSLC_APPROVED, AWSLC_APPROVED },

{ 8192, &EVP_sha1, true, AWSLC_NOT_APPROVED, AWSLC_APPROVED },
{ 8192, &EVP_sha224, true, AWSLC_APPROVED, AWSLC_APPROVED },
{ 8192, &EVP_sha256, true, AWSLC_APPROVED, AWSLC_APPROVED },
{ 8192, &EVP_sha384, true, AWSLC_APPROVED, AWSLC_APPROVED },
{ 8192, &EVP_sha512, true, AWSLC_APPROVED, AWSLC_APPROVED },
{ 8192, &EVP_sha512_224, true, AWSLC_APPROVED, AWSLC_APPROVED },
{ 8192, &EVP_sha512_256, true, AWSLC_APPROVED, AWSLC_APPROVED },
{ 8192, &EVP_sha3_224, true, AWSLC_APPROVED, AWSLC_APPROVED },
{ 8192, &EVP_sha3_256, true, AWSLC_APPROVED, AWSLC_APPROVED },
{ 8192, &EVP_sha3_384, true, AWSLC_APPROVED, AWSLC_APPROVED },
{ 8192, &EVP_sha3_512, true, AWSLC_APPROVED, AWSLC_APPROVED },
};

class RSAServiceIndicatorTest : public TestWithNoErrors<RSATestVector> {};
Expand Down Expand Up @@ -2229,20 +2274,20 @@ static RSA *GetRSAKey(unsigned bits) {
return ret;
}

// When using |EVP_PKEY_assign| to assign |RSA| to |EVP_PKEY|, the pointer will
// get assigned to |EVP_PKEY| and get freed along with it.
static RSA *GetRSAPSSKey(unsigned bits) {
bssl::UniquePtr<BIGNUM> e(BN_new());
if (!e || !BN_set_word(e.get(), RSA_F4)) {
static void AssignRSAPSSKey(EVP_PKEY *pkey, unsigned bits) {
RSA *rsa = GetRSAKey(bits);
if (rsa == NULL || pkey == NULL) {
abort();
}

RSA *key = RSA_new();
if (!key || !RSA_generate_key_ex(key, bits, e.get(), nullptr)) {
// When using |EVP_PKEY_assign| to assign |RSA| to |EVP_PKEY|, the pointer
// will get assigned to |EVP_PKEY| and get freed along with it. This will not
// up the reference to RSA unlike |EVP_PKEY_assign_RSA|! So we do that after.
if (!EVP_PKEY_assign(pkey, EVP_PKEY_RSA_PSS, rsa)) {
abort();
}

return key;
RSA_up_ref(rsa);
}

TEST_P(RSAServiceIndicatorTest, RSASigGen) {
Expand All @@ -2253,8 +2298,7 @@ TEST_P(RSAServiceIndicatorTest, RSASigGen) {
ASSERT_TRUE(pkey);
RSA *rsa = nullptr;
if(test.use_pss) {
rsa = GetRSAPSSKey(test.key_size);
ASSERT_TRUE(EVP_PKEY_assign(pkey.get(), EVP_PKEY_RSA_PSS, rsa));
AssignRSAPSSKey(pkey.get(), test.key_size);
} else {
rsa = GetRSAKey(test.key_size);
ASSERT_TRUE(EVP_PKEY_set1_RSA(pkey.get(), rsa));
Expand Down Expand Up @@ -2351,8 +2395,7 @@ TEST_P(RSAServiceIndicatorTest, RSASigVer) {

RSA *rsa = nullptr;
if(test.use_pss) {
rsa = GetRSAPSSKey(test.key_size);
ASSERT_TRUE(EVP_PKEY_assign(pkey.get(), EVP_PKEY_RSA_PSS, rsa));
AssignRSAPSSKey(pkey.get(), test.key_size);
} else {
rsa = GetRSAKey(test.key_size);
ASSERT_TRUE(EVP_PKEY_set1_RSA(pkey.get(), rsa));
Expand Down Expand Up @@ -2433,8 +2476,7 @@ TEST_P(RSAServiceIndicatorTest, ManualRSASignVerify) {

RSA *rsa = nullptr;
if(test.use_pss) {
rsa = GetRSAPSSKey(test.key_size);
ASSERT_TRUE(EVP_PKEY_assign(pkey.get(), EVP_PKEY_RSA_PSS, rsa));
AssignRSAPSSKey(pkey.get(), test.key_size);
} else {
rsa = GetRSAKey(test.key_size);
ASSERT_TRUE(EVP_PKEY_set1_RSA(pkey.get(), rsa));
Expand Down
5 changes: 5 additions & 0 deletions include/openssl/rsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,11 @@ OPENSSL_EXPORT int RSA_meth_set_sign(RSA_METHOD *meth,
// is called with event=2 when the n'th prime is rejected as unsuitable and
// with event=3 when a suitable value for |p| is found.
//
// Note: |bits| is expected to be divisible by 128, and if not will be rounded
// down to the nearest valid value. For example, requesting 3071 bits will
// provide a key that is 2944 bits. |RSA_bits| can be used to verify the
// RSA modulus size of the returned key.
//
// It returns one on success or zero on error.
OPENSSL_EXPORT int RSA_generate_key_ex(RSA *rsa, int bits, const BIGNUM *e,
BN_GENCB *cb);
Expand Down
Binary file modified util/fipstools/acvp/acvptool/test/expected/RSA.bz2
Binary file not shown.
1 change: 1 addition & 0 deletions util/fipstools/acvp/acvptool/test/tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
{"Wrapper": "modulewrapper", "In": "vectors/kdf-components.bz2", "Out": "expected/kdf-components.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/RSA.bz2", "Out": "expected/RSA.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/RSA-SigGen.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/RSA-KeyGen.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/TLS-1.2-KDF.bz2", "Out": "expected/TLS-1.2-KDF.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/PBKDF.bz2", "Out": "expected/PBKDF.bz2"},
{"Wrapper": "modulewrapper", "In": "vectors/KDA-HKDF.bz2", "Out": "expected/KDA-HKDF.bz2"},
Expand Down
Binary file not shown.
Binary file modified util/fipstools/acvp/acvptool/test/vectors/RSA-SigGen.bz2
Binary file not shown.
Binary file modified util/fipstools/acvp/acvptool/test/vectors/RSA.bz2
Binary file not shown.
Loading

0 comments on commit 3f4d2f6

Please sign in to comment.