Add new APIs for creating RSA keys

This adds APIs for constructing RSA keys given all the parameters. This
is much less tedious than the set0 functions, and also avoids issues
caused by deferred initialization.

It doesn't quite finish initializing the keys on construction, as that
is tied up in the rest of this mess. But later work, probably after
Conscrypt is moved to these APIs, will do this.

As part of this, add APIs that explicitly create RSA keys with no e.
There is actually no way to do this right now without reaching into
library internals, because RSA_set0_key refuses to accept an e-less
private key. Handle this by adding a flag.

I also had to add a path for Conscrypt to make an RSA_METHOD-backed key
with n but no e. (They need n to compute RSA-PSS padding.) I think that
all wants to be rethought but, for now, just add an API for it.

This bumps BORINGSSL_API_VERSION so Conscrypt can have an easier time
switching to the new APIs.

Bug: 316
Change-Id: I81498a7d0690886842016c3680ea27d1ee0fa490
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/59386
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/fipsmodule/rsa/rsa.c b/crypto/fipsmodule/rsa/rsa.c
index 8f5cb5f..5b687b7 100644
--- a/crypto/fipsmodule/rsa/rsa.c
+++ b/crypto/fipsmodule/rsa/rsa.c
@@ -83,6 +83,83 @@
 
 DEFINE_STATIC_EX_DATA_CLASS(g_rsa_ex_data_class)
 
+static int bn_dup_into(BIGNUM **dst, const BIGNUM *src) {
+  if (src == NULL) {
+    OPENSSL_PUT_ERROR(RSA, ERR_R_PASSED_NULL_PARAMETER);
+    return 0;
+  }
+
+  BN_free(*dst);
+  *dst = BN_dup(src);
+  return *dst != NULL;
+}
+
+RSA *RSA_new_public_key(const BIGNUM *n, const BIGNUM *e) {
+  RSA *rsa = RSA_new();
+  if (rsa == NULL ||               //
+      !bn_dup_into(&rsa->n, n) ||  //
+      !bn_dup_into(&rsa->e, e) ||  //
+      !RSA_check_key(rsa)) {
+    RSA_free(rsa);
+    return NULL;
+  }
+
+  return rsa;
+}
+
+RSA *RSA_new_private_key(const BIGNUM *n, const BIGNUM *e, const BIGNUM *d,
+                         const BIGNUM *p, const BIGNUM *q, const BIGNUM *dmp1,
+                         const BIGNUM *dmq1, const BIGNUM *iqmp) {
+  RSA *rsa = RSA_new();
+  if (rsa == NULL ||                     //
+      !bn_dup_into(&rsa->n, n) ||        //
+      !bn_dup_into(&rsa->e, e) ||        //
+      !bn_dup_into(&rsa->d, d) ||        //
+      !bn_dup_into(&rsa->p, p) ||        //
+      !bn_dup_into(&rsa->q, q) ||        //
+      !bn_dup_into(&rsa->dmp1, dmp1) ||  //
+      !bn_dup_into(&rsa->dmq1, dmq1) ||  //
+      !bn_dup_into(&rsa->iqmp, iqmp) ||  //
+      !RSA_check_key(rsa)) {
+    RSA_free(rsa);
+    return NULL;
+  }
+
+  return rsa;
+}
+
+RSA *RSA_new_private_key_no_crt(const BIGNUM *n, const BIGNUM *e,
+                                const BIGNUM *d) {
+  RSA *rsa = RSA_new();
+  if (rsa == NULL ||               //
+      !bn_dup_into(&rsa->n, n) ||  //
+      !bn_dup_into(&rsa->e, e) ||  //
+      !bn_dup_into(&rsa->d, d) ||  //
+      !RSA_check_key(rsa)) {
+    RSA_free(rsa);
+    return NULL;
+  }
+
+  return rsa;
+}
+
+RSA *RSA_new_private_key_no_e(const BIGNUM *n, const BIGNUM *d) {
+  RSA *rsa = RSA_new();
+  if (rsa == NULL) {
+    return NULL;
+  }
+
+  rsa->flags |= RSA_FLAG_NO_PUBLIC_EXPONENT;
+  if (!bn_dup_into(&rsa->n, n) ||  //
+      !bn_dup_into(&rsa->d, d) ||  //
+      !RSA_check_key(rsa)) {
+    RSA_free(rsa);
+    return NULL;
+  }
+
+  return rsa;
+}
+
 RSA *RSA_new(void) { return RSA_new_method(NULL); }
 
 RSA *RSA_new_method(const ENGINE *engine) {
@@ -118,6 +195,17 @@
   return rsa;
 }
 
+RSA *RSA_new_method_no_e(const ENGINE *engine, const BIGNUM *n) {
+  RSA *rsa = RSA_new_method(engine);
+  if (rsa == NULL ||
+      !bn_dup_into(&rsa->n, n)) {
+    RSA_free(rsa);
+    return NULL;
+  }
+  rsa->flags |= RSA_FLAG_NO_PUBLIC_EXPONENT;
+  return rsa;
+}
+
 void RSA_free(RSA *rsa) {
   if (rsa == NULL) {
     return;
diff --git a/crypto/fipsmodule/rsa/rsa_impl.c b/crypto/fipsmodule/rsa/rsa_impl.c
index 6eb4438..e9379d9 100644
--- a/crypto/fipsmodule/rsa/rsa_impl.c
+++ b/crypto/fipsmodule/rsa/rsa_impl.c
@@ -74,7 +74,7 @@
 
 
 int rsa_check_public_key(const RSA *rsa) {
-  if (rsa->n == NULL || rsa->e == NULL) {
+  if (rsa->n == NULL) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_VALUE_MISSING);
     return 0;
   }
@@ -102,13 +102,17 @@
   // [2] https://www.imperialviolet.org/2012/03/17/rsados.html
   // [3] https://msdn.microsoft.com/en-us/library/aa387685(VS.85).aspx
   static const unsigned kMaxExponentBits = 33;
-  unsigned e_bits = BN_num_bits(rsa->e);
-  if (e_bits > kMaxExponentBits ||
-      // Additionally reject e = 1 or even e. e must be odd to be relatively
-      // prime with phi(n).
-      e_bits < 2 ||
-      !BN_is_odd(rsa->e)) {
-    OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_E_VALUE);
+  if (rsa->e != NULL) {
+    unsigned e_bits = BN_num_bits(rsa->e);
+    if (e_bits > kMaxExponentBits ||
+        // Additionally reject e = 1 or even e. e must be odd to be relatively
+        // prime with phi(n).
+        e_bits < 2 || !BN_is_odd(rsa->e)) {
+      OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_E_VALUE);
+      return 0;
+    }
+  } else if (!(rsa->flags & RSA_FLAG_NO_PUBLIC_EXPONENT)) {
+    OPENSSL_PUT_ERROR(RSA, RSA_R_VALUE_MISSING);
     return 0;
   }
 
@@ -120,7 +124,7 @@
     OPENSSL_PUT_ERROR(RSA, RSA_R_KEY_SIZE_TOO_SMALL);
     return 0;
   }
-  assert(BN_ucmp(rsa->n, rsa->e) > 0);
+  assert(rsa->e == NULL || BN_ucmp(rsa->n, rsa->e) > 0);
 
   return 1;
 }
@@ -183,7 +187,7 @@
     goto err;
   }
 
-  if (rsa->p != NULL && rsa->q != NULL) {
+  if (rsa->e != NULL && rsa->p != NULL && rsa->q != NULL) {
     // TODO: p and q are also CONSTTIME_SECRET but not yet marked as such
     // because the Montgomery code does things like test whether or not values
     // are zero. So the secret marking probably needs to happen inside that
@@ -478,6 +482,11 @@
 int rsa_verify_raw_no_self_test(RSA *rsa, size_t *out_len, uint8_t *out,
                                 size_t max_out, const uint8_t *in,
                                 size_t in_len, int padding) {
+  if (rsa->n == NULL || rsa->e == NULL) {
+    OPENSSL_PUT_ERROR(RSA, RSA_R_VALUE_MISSING);
+    return 0;
+  }
+
   if (!rsa_check_public_key(rsa)) {
     return 0;
   }
@@ -617,13 +626,18 @@
     goto err;
   }
 
-  const int do_blinding = (rsa->flags & RSA_FLAG_NO_BLINDING) == 0;
+  const int do_blinding =
+      (rsa->flags & (RSA_FLAG_NO_BLINDING | RSA_FLAG_NO_PUBLIC_EXPONENT)) == 0;
 
   if (rsa->e == NULL && do_blinding) {
     // We cannot do blinding or verification without |e|, and continuing without
     // those countermeasures is dangerous. However, the Java/Android RSA API
     // requires support for keys where only |d| and |n| (and not |e|) are known.
-    // The callers that require that bad behavior set |RSA_FLAG_NO_BLINDING|.
+    // The callers that require that bad behavior must set
+    // |RSA_FLAG_NO_BLINDING| or use |RSA_new_private_key_no_e|.
+    //
+    // TODO(davidben): Update this comment when Conscrypt is updated to use
+    // |RSA_new_private_key_no_e|.
     OPENSSL_PUT_ERROR(RSA, RSA_R_NO_PUBLIC_EXPONENT);
     goto err;
   }
diff --git a/crypto/rsa_extra/rsa_crypt.c b/crypto/rsa_extra/rsa_crypt.c
index 97afa3d..c9d29fc 100644
--- a/crypto/rsa_extra/rsa_crypt.c
+++ b/crypto/rsa_extra/rsa_crypt.c
@@ -376,6 +376,11 @@
 
 int RSA_encrypt(RSA *rsa, size_t *out_len, uint8_t *out, size_t max_out,
                 const uint8_t *in, size_t in_len, int padding) {
+  if (rsa->n == NULL || rsa->e == NULL) {
+    OPENSSL_PUT_ERROR(RSA, RSA_R_VALUE_MISSING);
+    return 0;
+  }
+
   if (!rsa_check_public_key(rsa)) {
     return 0;
   }
diff --git a/crypto/rsa_extra/rsa_test.cc b/crypto/rsa_extra/rsa_test.cc
index b6ab5b8..4a450e5 100644
--- a/crypto/rsa_extra/rsa_test.cc
+++ b/crypto/rsa_extra/rsa_test.cc
@@ -396,68 +396,108 @@
 class RSAEncryptTest : public testing::TestWithParam<RSAEncryptParam> {};
 
 TEST_P(RSAEncryptTest, TestKey) {
+  // Construct an RSA key in different ways.
   const auto &param = GetParam();
-  bssl::UniquePtr<RSA> key(
+  bssl::UniquePtr<RSA> parsed(
       RSA_private_key_from_bytes(param.der, param.der_len));
-  ASSERT_TRUE(key);
+  ASSERT_TRUE(parsed);
+  EXPECT_TRUE(RSA_get0_e(parsed.get()));
+  EXPECT_TRUE(RSA_get0_d(parsed.get()));
 
-  EXPECT_TRUE(RSA_check_key(key.get()));
+  bssl::UniquePtr<RSA> constructed(RSA_new_private_key(
+      RSA_get0_n(parsed.get()), RSA_get0_e(parsed.get()),
+      RSA_get0_d(parsed.get()), RSA_get0_p(parsed.get()),
+      RSA_get0_q(parsed.get()), RSA_get0_dmp1(parsed.get()),
+      RSA_get0_dmq1(parsed.get()), RSA_get0_iqmp(parsed.get())));
+  ASSERT_TRUE(constructed);
+  EXPECT_TRUE(RSA_get0_e(constructed.get()));
+  EXPECT_TRUE(RSA_get0_d(constructed.get()));
 
-  uint8_t ciphertext[256];
+  bssl::UniquePtr<RSA> no_crt(RSA_new_private_key_no_crt(
+      RSA_get0_n(parsed.get()), RSA_get0_e(parsed.get()),
+      RSA_get0_d(parsed.get())));
+  ASSERT_TRUE(no_crt);
+  EXPECT_TRUE(RSA_get0_e(no_crt.get()));
+  EXPECT_TRUE(RSA_get0_d(no_crt.get()));
 
-  // Test that PKCS#1 v1.5 encryption round-trips.
-  size_t ciphertext_len = 0;
-  ASSERT_TRUE(RSA_encrypt(key.get(), &ciphertext_len, ciphertext,
-                          sizeof(ciphertext), kPlaintext, kPlaintextLen,
-                          RSA_PKCS1_PADDING));
-  EXPECT_EQ(RSA_size(key.get()), ciphertext_len);
+  bssl::UniquePtr<RSA> no_e(RSA_new_private_key_no_e(RSA_get0_n(parsed.get()),
+                                                     RSA_get0_d(parsed.get())));
+  ASSERT_TRUE(no_e);
+  EXPECT_FALSE(RSA_get0_e(no_e.get()));
+  EXPECT_TRUE(RSA_get0_d(no_e.get()));
 
-  uint8_t plaintext[256];
-  size_t plaintext_len = 0;
-  ASSERT_TRUE(RSA_decrypt(key.get(), &plaintext_len, plaintext,
-                          sizeof(plaintext), ciphertext, ciphertext_len,
-                          RSA_PKCS1_PADDING));
-  EXPECT_EQ(Bytes(kPlaintext, kPlaintextLen), Bytes(plaintext, plaintext_len));
+  bssl::UniquePtr<RSA> pub(
+      RSA_new_public_key(RSA_get0_n(parsed.get()), RSA_get0_e(parsed.get())));
+  ASSERT_TRUE(pub);
+  EXPECT_TRUE(RSA_get0_e(pub.get()));
+  EXPECT_FALSE(RSA_get0_d(pub.get()));
 
-  // Test that OAEP encryption round-trips.
-  ciphertext_len = 0;
-  ASSERT_TRUE(RSA_encrypt(key.get(), &ciphertext_len, ciphertext,
-                          sizeof(ciphertext), kPlaintext, kPlaintextLen,
-                          RSA_PKCS1_OAEP_PADDING));
-  EXPECT_EQ(RSA_size(key.get()), ciphertext_len);
+  for (RSA *key :
+       {parsed.get(), constructed.get(), no_crt.get(), no_e.get(), pub.get()}) {
+    EXPECT_TRUE(RSA_check_key(key));
 
-  plaintext_len = 0;
-  ASSERT_TRUE(RSA_decrypt(key.get(), &plaintext_len, plaintext,
-                          sizeof(plaintext), ciphertext, ciphertext_len,
-                          RSA_PKCS1_OAEP_PADDING));
-  EXPECT_EQ(Bytes(kPlaintext, kPlaintextLen), Bytes(plaintext, plaintext_len));
+    uint8_t ciphertext[256], plaintext[256];
+    size_t ciphertext_len = 0, plaintext_len = 0;
 
-  // |oaep_ciphertext| should decrypt to |kPlaintext|.
-  plaintext_len = 0;
-  ASSERT_TRUE(RSA_decrypt(key.get(), &plaintext_len, plaintext,
-                          sizeof(plaintext), param.oaep_ciphertext,
-                          param.oaep_ciphertext_len, RSA_PKCS1_OAEP_PADDING));
-  EXPECT_EQ(Bytes(kPlaintext, kPlaintextLen), Bytes(plaintext, plaintext_len));
+    if (RSA_get0_e(key) != nullptr) {
+      // Test that PKCS#1 v1.5 encryption round-trips.
+      ASSERT_TRUE(RSA_encrypt(key, &ciphertext_len, ciphertext,
+                              sizeof(ciphertext), kPlaintext, kPlaintextLen,
+                              RSA_PKCS1_PADDING));
+      EXPECT_EQ(RSA_size(key), ciphertext_len);
 
-  // Try decrypting corrupted ciphertexts.
-  OPENSSL_memcpy(ciphertext, param.oaep_ciphertext, param.oaep_ciphertext_len);
-  for (size_t i = 0; i < param.oaep_ciphertext_len; i++) {
-    SCOPED_TRACE(i);
-    ciphertext[i] ^= 1;
-    EXPECT_FALSE(RSA_decrypt(
-        key.get(), &plaintext_len, plaintext, sizeof(plaintext), ciphertext,
-        param.oaep_ciphertext_len, RSA_PKCS1_OAEP_PADDING));
-    ERR_clear_error();
-    ciphertext[i] ^= 1;
-  }
+      ASSERT_TRUE(RSA_decrypt(parsed.get(), &plaintext_len, plaintext,
+                              sizeof(plaintext), ciphertext, ciphertext_len,
+                              RSA_PKCS1_PADDING));
+      EXPECT_EQ(Bytes(kPlaintext, kPlaintextLen),
+                Bytes(plaintext, plaintext_len));
 
-  // Test truncated ciphertexts.
-  for (size_t len = 0; len < param.oaep_ciphertext_len; len++) {
-    SCOPED_TRACE(len);
-    EXPECT_FALSE(RSA_decrypt(key.get(), &plaintext_len, plaintext,
-                             sizeof(plaintext), ciphertext, len,
-                             RSA_PKCS1_OAEP_PADDING));
-    ERR_clear_error();
+      // Test that OAEP encryption round-trips.
+      ciphertext_len = 0;
+      ASSERT_TRUE(RSA_encrypt(key, &ciphertext_len, ciphertext,
+                              sizeof(ciphertext), kPlaintext, kPlaintextLen,
+                              RSA_PKCS1_OAEP_PADDING));
+      EXPECT_EQ(RSA_size(key), ciphertext_len);
+
+      plaintext_len = 0;
+      ASSERT_TRUE(RSA_decrypt(parsed.get(), &plaintext_len, plaintext,
+                              sizeof(plaintext), ciphertext, ciphertext_len,
+                              RSA_PKCS1_OAEP_PADDING));
+      EXPECT_EQ(Bytes(kPlaintext, kPlaintextLen),
+                Bytes(plaintext, plaintext_len));
+    }
+
+    if (RSA_get0_d(key) != nullptr) {
+      // |oaep_ciphertext| should decrypt to |kPlaintext|.
+      plaintext_len = 0;
+      ASSERT_TRUE(RSA_decrypt(key, &plaintext_len, plaintext, sizeof(plaintext),
+                              param.oaep_ciphertext, param.oaep_ciphertext_len,
+                              RSA_PKCS1_OAEP_PADDING));
+      EXPECT_EQ(Bytes(kPlaintext, kPlaintextLen),
+                Bytes(plaintext, plaintext_len));
+
+      // Try decrypting corrupted ciphertexts.
+      OPENSSL_memcpy(ciphertext, param.oaep_ciphertext,
+                     param.oaep_ciphertext_len);
+      for (size_t i = 0; i < param.oaep_ciphertext_len; i++) {
+        SCOPED_TRACE(i);
+        ciphertext[i] ^= 1;
+        EXPECT_FALSE(RSA_decrypt(
+            key, &plaintext_len, plaintext, sizeof(plaintext), ciphertext,
+            param.oaep_ciphertext_len, RSA_PKCS1_OAEP_PADDING));
+        ERR_clear_error();
+        ciphertext[i] ^= 1;
+      }
+
+      // Test truncated ciphertexts.
+      for (size_t len = 0; len < param.oaep_ciphertext_len; len++) {
+        SCOPED_TRACE(len);
+        EXPECT_FALSE(RSA_decrypt(key, &plaintext_len, plaintext,
+                                 sizeof(plaintext), ciphertext, len,
+                                 RSA_PKCS1_OAEP_PADDING));
+        ERR_clear_error();
+      }
+    }
   }
 }
 
@@ -1111,6 +1151,42 @@
       check_rsa_compatible(/*enc=*/key2.get(), /*dec=*/key1.get()));
 }
 
+// Test that RSA keys do not support operations will cleanly fail them.
+TEST(RSATest, MissingParameters) {
+  bssl::UniquePtr<RSA> sample(
+      RSA_private_key_from_bytes(kKey1, sizeof(kKey1) - 1));
+  ASSERT_TRUE(sample);
+
+  // Make a sample signature.
+  const uint8_t kZeros[32] = {0};
+  std::vector<uint8_t> sig(RSA_size(sample.get()));
+  unsigned len_u;
+  ASSERT_TRUE(RSA_sign(NID_sha256, kZeros, sizeof(kZeros), sig.data(), &len_u,
+                       sample.get()));
+  sig.resize(len_u);
+
+  // A public key cannot perform private key operations.
+  bssl::UniquePtr<RSA> rsa(
+      RSA_new_public_key(RSA_get0_n(sample.get()), RSA_get0_e(sample.get())));
+  ASSERT_TRUE(rsa);
+  std::vector<uint8_t> out(RSA_size(sample.get()));
+  EXPECT_FALSE(RSA_sign(NID_sha256, kZeros, sizeof(kZeros), out.data(), &len_u,
+                        rsa.get()));
+  size_t len;
+  EXPECT_FALSE(RSA_decrypt(rsa.get(), &len, out.data(), out.size(),
+                           kOAEPCiphertext1, sizeof(kOAEPCiphertext1) - 1,
+                           RSA_PKCS1_OAEP_PADDING));
+
+  // A private key without e cannot perform public key operations.
+  rsa.reset(RSA_new_private_key_no_e(RSA_get0_n(sample.get()),
+                                     RSA_get0_d(sample.get())));
+  ASSERT_TRUE(rsa);
+  EXPECT_FALSE(RSA_verify(NID_sha256, kZeros, sizeof(kZeros), sig.data(),
+                          sig.size(), rsa.get()));
+  EXPECT_FALSE(RSA_encrypt(rsa.get(), &len, out.data(), out.size(), kPlaintext,
+                           kPlaintextLen, RSA_PKCS1_OAEP_PADDING));
+}
+
 #if !defined(BORINGSSL_SHARED_LIBRARY)
 TEST(RSATest, SqrtTwo) {
   bssl::UniquePtr<BIGNUM> sqrt(BN_new()), pow2(BN_new());
diff --git a/include/openssl/base.h b/include/openssl/base.h
index 80d18a7..ee26627 100644
--- a/include/openssl/base.h
+++ b/include/openssl/base.h
@@ -193,7 +193,7 @@
 // A consumer may use this symbol in the preprocessor to temporarily build
 // against multiple revisions of BoringSSL at the same time. It is not
 // recommended to do so for longer than is necessary.
-#define BORINGSSL_API_VERSION 19
+#define BORINGSSL_API_VERSION 20
 
 #if defined(BORINGSSL_SHARED_LIBRARY)
 
diff --git a/include/openssl/rsa.h b/include/openssl/rsa.h
index a1c03cd..49a6aa6 100644
--- a/include/openssl/rsa.h
+++ b/include/openssl/rsa.h
@@ -79,7 +79,22 @@
 // documented, functions which take a |const| pointer are non-mutating and
 // functions which take a non-|const| pointer are mutating.
 
-// RSA_new returns a new, empty |RSA| object or NULL on error.
+// RSA_new_public_key returns a new |RSA| object containing a public key with
+// the specified parameters, or NULL on error or invalid input.
+OPENSSL_EXPORT RSA *RSA_new_public_key(const BIGNUM *n, const BIGNUM *e);
+
+// RSA_new_private_key returns a new |RSA| object containing a private key with
+// the specified parameters, or NULL on error or invalid input. All parameters
+// are mandatory and may not be NULL.
+//
+// This function creates standard RSA private keys with CRT parameters.
+OPENSSL_EXPORT RSA *RSA_new_private_key(const BIGNUM *n, const BIGNUM *e,
+                                        const BIGNUM *d, const BIGNUM *p,
+                                        const BIGNUM *q, const BIGNUM *dmp1,
+                                        const BIGNUM *dmq1, const BIGNUM *iqmp);
+
+// RSA_new returns a new, empty |RSA| object or NULL on error. Prefer using
+// |RSA_new_public_key| or |RSA_new_private_key| to import an RSA key.
 OPENSSL_EXPORT RSA *RSA_new(void);
 
 // RSA_new_method acts the same as |RSA_new| but takes an explicit |ENGINE|.
@@ -148,6 +163,20 @@
                                         const BIGNUM **out_dmq1,
                                         const BIGNUM **out_iqmp);
 
+
+// Setting individual properties.
+//
+// These functions allow setting individual properties of an |RSA| object. This
+// is typically used with |RSA_new| to construct an RSA key field by field.
+// Prefer instead to use |RSA_new_public_key| and |RSA_new_private_key|. These
+// functions defer some initialization to the first use of an |RSA| object. This
+// means invalid inputs may be caught late.
+//
+// TODO(crbug.com/boringssl/316): This deferred initialization also causes
+// performance problems in multi-threaded applications. The preferred APIs
+// currently have the same issues, but they will initialize eagerly in the
+// future.
+
 // RSA_set0_key sets |rsa|'s modulus, public exponent, and private exponent to
 // |n|, |e|, and |d| respectively, if non-NULL. On success, it takes ownership
 // of each argument and returns one. Otherwise, it returns zero.
@@ -570,6 +599,28 @@
                                             size_t *out_len, const RSA *rsa);
 
 
+// Obscure RSA variants.
+//
+// These functions allow creating RSA keys with obscure combinations of
+// parameters.
+
+// RSA_new_private_key_no_crt behaves like |RSA_new_private_key| but constructs
+// an RSA key without CRT coefficients.
+//
+// Keys created by this function will be less performant and cannot be
+// serialized.
+OPENSSL_EXPORT RSA *RSA_new_private_key_no_crt(const BIGNUM *n, const BIGNUM *e,
+                                               const BIGNUM *d);
+
+// RSA_new_private_key_no_e behaves like |RSA_new_private_key| but constructs an
+// RSA key without CRT parameters or public exponent.
+//
+// Keys created by this function will be less performant, cannot be serialized,
+// and lack hardening measures that protect against side channels and fault
+// attacks.
+OPENSSL_EXPORT RSA *RSA_new_private_key_no_e(const BIGNUM *n, const BIGNUM *d);
+
+
 // ex_data functions.
 //
 // See |ex_data.h| for details.
@@ -600,6 +651,11 @@
 // RSA_FLAG_EXT_PKEY is deprecated and ignored.
 #define RSA_FLAG_EXT_PKEY 0x20
 
+// RSA_FLAG_NO_PUBLIC_EXPONENT indicates that private keys without a public
+// exponent are allowed. This is an internal constant. Use
+// |RSA_new_private_key_no_e| to construct such keys.
+#define RSA_FLAG_NO_PUBLIC_EXPONENT 0x40
+
 
 // RSA public exponent values.
 
@@ -688,6 +744,14 @@
 // the id-RSASSA-PSS key encoding.
 OPENSSL_EXPORT const RSA_PSS_PARAMS *RSA_get0_pss_params(const RSA *rsa);
 
+// RSA_new_method_no_e returns a newly-allocated |RSA| object backed by
+// |engine|, with a public modulus of |n| and no known public exponent.
+//
+// Do not use this function. It exists only to support Conscrypt, whose use
+// should be replaced with a more sound mechanism. See
+// https://crbug.com/boringssl/602.
+OPENSSL_EXPORT RSA *RSA_new_method_no_e(const ENGINE *engine, const BIGNUM *n);
+
 
 struct rsa_meth_st {
   struct openssl_method_common_st common;