Add FIPS-compliant key generation that calls check_fips for RSA and EC.

Change-Id: Ie466b7b55bdd679c5baf2127bd8de4a5058fc3b7
Reviewed-on: https://boringssl-review.googlesource.com/16346
Commit-Queue: Steven Valdez <svaldez@google.com>
Reviewed-by: Steven Valdez <svaldez@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/crypto/fipsmodule/ec/ec_key.c b/crypto/fipsmodule/ec/ec_key.c
index 0ba7f6e..b58af68 100644
--- a/crypto/fipsmodule/ec/ec_key.c
+++ b/crypto/fipsmodule/ec/ec_key.c
@@ -494,6 +494,10 @@
   return ok;
 }
 
+int EC_KEY_generate_key_fips(EC_KEY *eckey) {
+  return EC_KEY_generate_key(eckey) && EC_KEY_check_fips(eckey);
+}
+
 int EC_KEY_get_ex_new_index(long argl, void *argp, CRYPTO_EX_unused *unused,
                             CRYPTO_EX_dup *dup_func,
                             CRYPTO_EX_free *free_func) {
diff --git a/crypto/fipsmodule/ec/ec_test.cc b/crypto/fipsmodule/ec/ec_test.cc
index da42b06..71f9fd8 100644
--- a/crypto/fipsmodule/ec/ec_test.cc
+++ b/crypto/fipsmodule/ec/ec_test.cc
@@ -314,13 +314,11 @@
                                                    x.get(), y.get(), nullptr));
 }
 
-TEST_P(ECCurveTest, CheckFIPS) {
+TEST_P(ECCurveTest, GenerateFIPS) {
   // Generate an EC_KEY.
   bssl::UniquePtr<EC_KEY> key(EC_KEY_new_by_curve_name(GetParam().nid));
   ASSERT_TRUE(key);
-  ASSERT_TRUE(EC_KEY_generate_key(key.get()));
-
-  EXPECT_TRUE(EC_KEY_check_fips(key.get()));
+  ASSERT_TRUE(EC_KEY_generate_key_fips(key.get()));
 }
 
 TEST_P(ECCurveTest, AddingEqualPoints) {
diff --git a/crypto/fipsmodule/rsa/rsa_impl.c b/crypto/fipsmodule/rsa/rsa_impl.c
index 784b81c..e09a37c 100644
--- a/crypto/fipsmodule/rsa/rsa_impl.c
+++ b/crypto/fipsmodule/rsa/rsa_impl.c
@@ -909,8 +909,8 @@
 
 int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e_value, BN_GENCB *cb) {
   /* See FIPS 186-4 appendix B.3. This function implements a generalized version
-   * of the FIPS algorithm. For FIPS compliance, the caller is responsible for
-   * passing in 2048 or 3072 to |bits| and 65537 for |e_value|. */
+   * of the FIPS algorithm. |RSA_generate_key_fips| performs additional checks
+   * for FIPS-compliant key generation. */
 
   /* Always generate RSA keys which are a multiple of 128 bits. Round |bits|
    * down as needed. */
@@ -1037,6 +1037,23 @@
   return ret;
 }
 
+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. */
+  if (bits != 2048 && bits != 3072) {
+    OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_RSA_PARAMETERS);
+    return 0;
+  }
+
+  BIGNUM *e = BN_new();
+  int ret = e != NULL &&
+            BN_set_word(e, RSA_F4) &&
+            RSA_generate_key_ex(rsa, bits, e, cb) &&
+            RSA_check_fips(rsa);
+  BN_free(e);
+  return ret;
+}
+
 DEFINE_METHOD_FUNCTION(RSA_METHOD, RSA_default_method) {
   /* All of the methods are NULL to make it easier for the compiler/linker to
    * drop unused functions. The wrapper functions will select the appropriate
diff --git a/crypto/rsa_extra/rsa_test.cc b/crypto/rsa_extra/rsa_test.cc
index 3fb834d..a53d04d 100644
--- a/crypto/rsa_extra/rsa_test.cc
+++ b/crypto/rsa_extra/rsa_test.cc
@@ -486,6 +486,30 @@
   EXPECT_TRUE(RSA_check_fips(pub.get()));
 }
 
+TEST(RSATest, GenerateFIPS) {
+  bssl::UniquePtr<RSA> rsa(RSA_new());
+  ASSERT_TRUE(rsa);
+
+  // RSA_generate_key_fips may only be used for 2048-bit and 3072-bit keys.
+  EXPECT_FALSE(RSA_generate_key_fips(rsa.get(), 512, nullptr));
+  EXPECT_FALSE(RSA_generate_key_fips(rsa.get(), 1024, nullptr));
+  EXPECT_FALSE(RSA_generate_key_fips(rsa.get(), 2047, nullptr));
+  EXPECT_FALSE(RSA_generate_key_fips(rsa.get(), 2049, nullptr));
+  EXPECT_FALSE(RSA_generate_key_fips(rsa.get(), 3071, nullptr));
+  EXPECT_FALSE(RSA_generate_key_fips(rsa.get(), 3073, nullptr));
+  EXPECT_FALSE(RSA_generate_key_fips(rsa.get(), 4096, nullptr));
+  ERR_clear_error();
+
+  // Test that we can generate 2048-bit and 3072-bit RSA keys.
+  EXPECT_TRUE(RSA_generate_key_fips(rsa.get(), 2048, nullptr));
+  EXPECT_EQ(2048u, BN_num_bits(rsa->n));
+
+  rsa.reset(RSA_new());
+  ASSERT_TRUE(rsa);
+  EXPECT_TRUE(RSA_generate_key_fips(rsa.get(), 3072, nullptr));
+  EXPECT_EQ(3072u, BN_num_bits(rsa->n));
+}
+
 TEST(RSATest, BadKey) {
   bssl::UniquePtr<RSA> key(RSA_new());
   bssl::UniquePtr<BIGNUM> e(BN_new());
diff --git a/fipsoracle/cavp_ecdsa2_keypair_test.cc b/fipsoracle/cavp_ecdsa2_keypair_test.cc
index bc8b6c1..5cb0f5b 100644
--- a/fipsoracle/cavp_ecdsa2_keypair_test.cc
+++ b/fipsoracle/cavp_ecdsa2_keypair_test.cc
@@ -48,7 +48,7 @@
     bssl::UniquePtr<BIGNUM> qx(BN_new()), qy(BN_new());
     bssl::UniquePtr<EC_KEY> key(EC_KEY_new_by_curve_name(nid));
     if (!key ||
-        !EC_KEY_generate_key(key.get()) ||
+        !EC_KEY_generate_key_fips(key.get()) ||
         !EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(key.get()),
                                              EC_KEY_get0_public_key(key.get()),
                                              qx.get(), qy.get(), nullptr)) {
diff --git a/fipsoracle/cavp_ecdsa2_siggen_test.cc b/fipsoracle/cavp_ecdsa2_siggen_test.cc
index e97d161..2d6c79e 100644
--- a/fipsoracle/cavp_ecdsa2_siggen_test.cc
+++ b/fipsoracle/cavp_ecdsa2_siggen_test.cc
@@ -41,7 +41,7 @@
   bssl::UniquePtr<EC_KEY> key(EC_KEY_new_by_curve_name(nid));
   std::vector<uint8_t> msg;
   if (!qx || !qy || !key ||
-      !EC_KEY_generate_key(key.get()) ||
+      !EC_KEY_generate_key_fips(key.get()) ||
       !EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(key.get()),
                                            EC_KEY_get0_public_key(key.get()),
                                            qx.get(), qy.get(), nullptr) ||
diff --git a/fipsoracle/cavp_rsa2_keygen_test.cc b/fipsoracle/cavp_rsa2_keygen_test.cc
index d831cfb..a96404d 100644
--- a/fipsoracle/cavp_rsa2_keygen_test.cc
+++ b/fipsoracle/cavp_rsa2_keygen_test.cc
@@ -41,12 +41,10 @@
   size_t bits = strtoul(mod_str.c_str(), nullptr, 0);
   size_t count = strtoul(count_str.c_str(), nullptr, 0);
   for (size_t i = 0; i < count; i++) {
-    bssl::UniquePtr<BIGNUM> gen_e(BN_new());
     bssl::UniquePtr<RSA> key(RSA_new());
     if (key == nullptr ||
         bits == 0 ||
-        !BN_set_word(gen_e.get(), RSA_F4) ||
-        !RSA_generate_key_ex(key.get(), bits, gen_e.get(), nullptr)) {
+        !RSA_generate_key_fips(key.get(), bits, nullptr)) {
       return 0;
     }
 
diff --git a/fipsoracle/cavp_rsa2_siggen_test.cc b/fipsoracle/cavp_rsa2_siggen_test.cc
index 069adad..ad32c68 100644
--- a/fipsoracle/cavp_rsa2_siggen_test.cc
+++ b/fipsoracle/cavp_rsa2_siggen_test.cc
@@ -50,21 +50,19 @@
   if (t->IsAtNewInstructionBlock()) {
     int mod_bits = strtoul(mod_str.c_str(), nullptr, 0);
     ctx->key = bssl::UniquePtr<RSA>(RSA_new());
-    bssl::UniquePtr<BIGNUM> e(BN_new());
     if (ctx->key == nullptr ||
         mod_bits == 0 ||
-        !BN_set_word(e.get(), RSA_F4) ||
-        !RSA_generate_key_ex(ctx->key.get(), mod_bits, e.get(), nullptr)) {
+        !RSA_generate_key_fips(ctx->key.get(), mod_bits, nullptr)) {
       return false;
     }
 
-    const BIGNUM *n;
-    RSA_get0_key(ctx->key.get(), &n, nullptr, nullptr);
+    const BIGNUM *n, *e;
+    RSA_get0_key(ctx->key.get(), &n, &e, nullptr);
 
     std::vector<uint8_t> n_bytes(BN_num_bytes(n));
-    std::vector<uint8_t> e_bytes(BN_num_bytes(e.get()));
+    std::vector<uint8_t> e_bytes(BN_num_bytes(e));
     if (!BN_bn2bin_padded(n_bytes.data(), n_bytes.size(), n) ||
-        !BN_bn2bin_padded(e_bytes.data(), e_bytes.size(), e.get())) {
+        !BN_bn2bin_padded(e_bytes.data(), e_bytes.size(), e)) {
       return false;
     }
 
diff --git a/include/openssl/ec_key.h b/include/openssl/ec_key.h
index cb7ef10..2186f02 100644
--- a/include/openssl/ec_key.h
+++ b/include/openssl/ec_key.h
@@ -177,6 +177,10 @@
  * or zero otherwise. */
 OPENSSL_EXPORT int EC_KEY_generate_key(EC_KEY *key);
 
+/* EC_KEY_generate_key_fips behaves like |EC_KEY_generate_key| but performs
+ * additional checks for FIPS compliance. */
+OPENSSL_EXPORT int EC_KEY_generate_key_fips(EC_KEY *key);
+
 
 /* Serialisation. */
 
diff --git a/include/openssl/rsa.h b/include/openssl/rsa.h
index 6ce75f6..c4d7f53 100644
--- a/include/openssl/rsa.h
+++ b/include/openssl/rsa.h
@@ -122,6 +122,11 @@
 OPENSSL_EXPORT int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e,
                                        BN_GENCB *cb);
 
+/* RSA_generate_key_fips behaves like |RSA_generate_key_ex| but performs
+ * additional checks for FIPS compliance. The public exponent is always 65537
+ * and |bits| must be either 2048 or 3072. */
+OPENSSL_EXPORT int RSA_generate_key_fips(RSA *rsa, int bits, BN_GENCB *cb);
+
 
 /* Encryption / Decryption */