Move RSA (en|de)cryption out of the FIPS module.

Change-Id: I330ac0fa7f0b2c9984d12da831d8f34019ea2c49
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/58526
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: Adam Langley <agl@google.com>
diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt
index 6cc16a8..b45256c 100644
--- a/crypto/CMakeLists.txt
+++ b/crypto/CMakeLists.txt
@@ -204,6 +204,7 @@
   refcount_c11.c
   refcount_lock.c
   rsa_extra/rsa_asn1.c
+  rsa_extra/rsa_crypt.c
   rsa_extra/rsa_print.c
   stack/stack.c
   siphash/siphash.c
diff --git a/crypto/evp/p_rsa.c b/crypto/evp/p_rsa.c
index dc22450..15eb1ef 100644
--- a/crypto/evp/p_rsa.c
+++ b/crypto/evp/p_rsa.c
@@ -67,7 +67,7 @@
 #include <openssl/rsa.h>
 
 #include "../internal.h"
-#include "../fipsmodule/rsa/internal.h"
+#include "../rsa_extra/internal.h"
 #include "internal.h"
 
 
diff --git a/crypto/fipsmodule/rsa/internal.h b/crypto/fipsmodule/rsa/internal.h
index d0b5a4a..12394a4 100644
--- a/crypto/fipsmodule/rsa/internal.h
+++ b/crypto/fipsmodule/rsa/internal.h
@@ -67,6 +67,8 @@
 #endif
 
 
+#define RSA_PKCS1_PADDING_SIZE 11
+
 // Default implementations of RSA operations.
 
 const RSA_METHOD *RSA_default_method(void);
@@ -75,8 +77,6 @@
 int rsa_default_sign_raw(RSA *rsa, size_t *out_len, uint8_t *out,
                          size_t max_out, const uint8_t *in, size_t in_len,
                          int padding);
-int rsa_default_decrypt(RSA *rsa, size_t *out_len, uint8_t *out, size_t max_out,
-                        const uint8_t *in, size_t in_len, int padding);
 int rsa_default_private_transform(RSA *rsa, uint8_t *out, const uint8_t *in,
                                   size_t len);
 
@@ -90,21 +90,13 @@
                        BN_CTX *ctx);
 
 
+int PKCS1_MGF1(uint8_t *out, size_t len, const uint8_t *seed, size_t seed_len,
+               const EVP_MD *md);
 int RSA_padding_add_PKCS1_type_1(uint8_t *to, size_t to_len,
                                  const uint8_t *from, size_t from_len);
 int RSA_padding_check_PKCS1_type_1(uint8_t *out, size_t *out_len,
                                    size_t max_out, const uint8_t *from,
                                    size_t from_len);
-int RSA_padding_add_PKCS1_type_2(uint8_t *to, size_t to_len,
-                                 const uint8_t *from, size_t from_len);
-int RSA_padding_check_PKCS1_type_2(uint8_t *out, size_t *out_len,
-                                   size_t max_out, const uint8_t *from,
-                                   size_t from_len);
-int RSA_padding_check_PKCS1_OAEP_mgf1(uint8_t *out, size_t *out_len,
-                                      size_t max_out, const uint8_t *from,
-                                      size_t from_len, const uint8_t *param,
-                                      size_t param_len, const EVP_MD *md,
-                                      const EVP_MD *mgf1md);
 int RSA_padding_add_none(uint8_t *to, size_t to_len, const uint8_t *from,
                          size_t from_len);
 
@@ -112,10 +104,16 @@
 // within DoS bounds.
 int rsa_check_public_key(const RSA *rsa);
 
-// RSA_private_transform calls either the method-specific |private_transform|
-// function (if given) or the generic one. See the comment for
-// |private_transform| in |rsa_meth_st|.
-int RSA_private_transform(RSA *rsa, uint8_t *out, const uint8_t *in,
+// rsa_private_transform_no_self_test calls either the method-specific
+// |private_transform| function (if given) or the generic one. See the comment
+// for |private_transform| in |rsa_meth_st|.
+int rsa_private_transform_no_self_test(RSA *rsa, uint8_t *out,
+                                       const uint8_t *in, size_t len);
+
+// rsa_private_transform acts the same as |rsa_private_transform_no_self_test|
+// but, in FIPS mode, performs an RSA self test before calling the default RSA
+// implementation.
+int rsa_private_transform(RSA *rsa, uint8_t *out, const uint8_t *in,
                           size_t len);
 
 
diff --git a/crypto/fipsmodule/rsa/padding.c b/crypto/fipsmodule/rsa/padding.c
index 85f7835..998e459 100644
--- a/crypto/fipsmodule/rsa/padding.c
+++ b/crypto/fipsmodule/rsa/padding.c
@@ -71,8 +71,6 @@
 #include "../../internal.h"
 
 
-#define RSA_PKCS1_PADDING_SIZE 11
-
 int RSA_padding_add_PKCS1_type_1(uint8_t *to, size_t to_len,
                                  const uint8_t *from, size_t from_len) {
   // See RFC 8017, section 9.2.
@@ -146,109 +144,6 @@
   return 1;
 }
 
-static void rand_nonzero(uint8_t *out, size_t len) {
-  FIPS_service_indicator_lock_state();
-  RAND_bytes(out, len);
-
-  for (size_t i = 0; i < len; i++) {
-    while (out[i] == 0) {
-      RAND_bytes(out + i, 1);
-    }
-  }
-
-  FIPS_service_indicator_unlock_state();
-}
-
-int RSA_padding_add_PKCS1_type_2(uint8_t *to, size_t to_len,
-                                 const uint8_t *from, size_t from_len) {
-  // See RFC 8017, section 7.2.1.
-  if (to_len < RSA_PKCS1_PADDING_SIZE) {
-    OPENSSL_PUT_ERROR(RSA, RSA_R_KEY_SIZE_TOO_SMALL);
-    return 0;
-  }
-
-  if (from_len > to_len - RSA_PKCS1_PADDING_SIZE) {
-    OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
-    return 0;
-  }
-
-  to[0] = 0;
-  to[1] = 2;
-
-  size_t padding_len = to_len - 3 - from_len;
-  rand_nonzero(to + 2, padding_len);
-  to[2 + padding_len] = 0;
-  OPENSSL_memcpy(to + to_len - from_len, from, from_len);
-  return 1;
-}
-
-int RSA_padding_check_PKCS1_type_2(uint8_t *out, size_t *out_len,
-                                   size_t max_out, const uint8_t *from,
-                                   size_t from_len) {
-  if (from_len == 0) {
-    OPENSSL_PUT_ERROR(RSA, RSA_R_EMPTY_PUBLIC_KEY);
-    return 0;
-  }
-
-  // PKCS#1 v1.5 decryption. See "PKCS #1 v2.2: RSA Cryptography
-  // Standard", section 7.2.2.
-  if (from_len < RSA_PKCS1_PADDING_SIZE) {
-    // |from| is zero-padded to the size of the RSA modulus, a public value, so
-    // this can be rejected in non-constant time.
-    OPENSSL_PUT_ERROR(RSA, RSA_R_KEY_SIZE_TOO_SMALL);
-    return 0;
-  }
-
-  crypto_word_t first_byte_is_zero = constant_time_eq_w(from[0], 0);
-  crypto_word_t second_byte_is_two = constant_time_eq_w(from[1], 2);
-
-  crypto_word_t zero_index = 0, looking_for_index = CONSTTIME_TRUE_W;
-  for (size_t i = 2; i < from_len; i++) {
-    crypto_word_t equals0 = constant_time_is_zero_w(from[i]);
-    zero_index =
-        constant_time_select_w(looking_for_index & equals0, i, zero_index);
-    looking_for_index = constant_time_select_w(equals0, 0, looking_for_index);
-  }
-
-  // The input must begin with 00 02.
-  crypto_word_t valid_index = first_byte_is_zero;
-  valid_index &= second_byte_is_two;
-
-  // We must have found the end of PS.
-  valid_index &= ~looking_for_index;
-
-  // PS must be at least 8 bytes long, and it starts two bytes into |from|.
-  valid_index &= constant_time_ge_w(zero_index, 2 + 8);
-
-  // Skip the zero byte.
-  zero_index++;
-
-  // NOTE: Although this logic attempts to be constant time, the API contracts
-  // of this function and |RSA_decrypt| with |RSA_PKCS1_PADDING| make it
-  // impossible to completely avoid Bleichenbacher's attack. Consumers should
-  // use |RSA_PADDING_NONE| and perform the padding check in constant-time
-  // combined with a swap to a random session key or other mitigation.
-  CONSTTIME_DECLASSIFY(&valid_index, sizeof(valid_index));
-  CONSTTIME_DECLASSIFY(&zero_index, sizeof(zero_index));
-
-  if (!valid_index) {
-    OPENSSL_PUT_ERROR(RSA, RSA_R_PKCS_DECODING_ERROR);
-    return 0;
-  }
-
-  const size_t msg_len = from_len - zero_index;
-  if (msg_len > max_out) {
-    // This shouldn't happen because this function is always called with
-    // |max_out| as the key size and |from_len| is bounded by the key size.
-    OPENSSL_PUT_ERROR(RSA, RSA_R_PKCS_DECODING_ERROR);
-    return 0;
-  }
-
-  OPENSSL_memcpy(out, &from[zero_index], msg_len);
-  *out_len = msg_len;
-  return 1;
-}
-
 int RSA_padding_add_none(uint8_t *to, size_t to_len, const uint8_t *from,
                          size_t from_len) {
   if (from_len > to_len) {
@@ -265,8 +160,8 @@
   return 1;
 }
 
-static int PKCS1_MGF1(uint8_t *out, size_t len, const uint8_t *seed,
-                      size_t seed_len, const EVP_MD *md) {
+int PKCS1_MGF1(uint8_t *out, size_t len, const uint8_t *seed, size_t seed_len,
+               const EVP_MD *md) {
   int ret = 0;
   EVP_MD_CTX ctx;
   EVP_MD_CTX_init(&ctx);
@@ -310,184 +205,6 @@
   return ret;
 }
 
-int RSA_padding_add_PKCS1_OAEP_mgf1(uint8_t *to, size_t to_len,
-                                    const uint8_t *from, size_t from_len,
-                                    const uint8_t *param, size_t param_len,
-                                    const EVP_MD *md, const EVP_MD *mgf1md) {
-  if (md == NULL) {
-    md = EVP_sha1();
-  }
-  if (mgf1md == NULL) {
-    mgf1md = md;
-  }
-
-  size_t mdlen = EVP_MD_size(md);
-
-  if (to_len < 2 * mdlen + 2) {
-    OPENSSL_PUT_ERROR(RSA, RSA_R_KEY_SIZE_TOO_SMALL);
-    return 0;
-  }
-
-  size_t emlen = to_len - 1;
-  if (from_len > emlen - 2 * mdlen - 1) {
-    OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
-    return 0;
-  }
-
-  if (emlen < 2 * mdlen + 1) {
-    OPENSSL_PUT_ERROR(RSA, RSA_R_KEY_SIZE_TOO_SMALL);
-    return 0;
-  }
-
-  to[0] = 0;
-  uint8_t *seed = to + 1;
-  uint8_t *db = to + mdlen + 1;
-
-  uint8_t *dbmask = NULL;
-  int ret = 0;
-  FIPS_service_indicator_lock_state();
-  if (!EVP_Digest(param, param_len, db, NULL, md, NULL)) {
-    goto out;
-  }
-  OPENSSL_memset(db + mdlen, 0, emlen - from_len - 2 * mdlen - 1);
-  db[emlen - from_len - mdlen - 1] = 0x01;
-  OPENSSL_memcpy(db + emlen - from_len - mdlen, from, from_len);
-  if (!RAND_bytes(seed, mdlen)) {
-    goto out;
-  }
-
-  dbmask = OPENSSL_malloc(emlen - mdlen);
-  if (dbmask == NULL) {
-    goto out;
-  }
-
-  if (!PKCS1_MGF1(dbmask, emlen - mdlen, seed, mdlen, mgf1md)) {
-    goto out;
-  }
-  for (size_t i = 0; i < emlen - mdlen; i++) {
-    db[i] ^= dbmask[i];
-  }
-
-  uint8_t seedmask[EVP_MAX_MD_SIZE];
-  if (!PKCS1_MGF1(seedmask, mdlen, db, emlen - mdlen, mgf1md)) {
-    goto out;
-  }
-  for (size_t i = 0; i < mdlen; i++) {
-    seed[i] ^= seedmask[i];
-  }
-  ret = 1;
-
-out:
-  OPENSSL_free(dbmask);
-  FIPS_service_indicator_unlock_state();
-  return ret;
-}
-
-int RSA_padding_check_PKCS1_OAEP_mgf1(uint8_t *out, size_t *out_len,
-                                      size_t max_out, const uint8_t *from,
-                                      size_t from_len, const uint8_t *param,
-                                      size_t param_len, const EVP_MD *md,
-                                      const EVP_MD *mgf1md) {
-  uint8_t *db = NULL;
-
-  if (md == NULL) {
-    md = EVP_sha1();
-  }
-  if (mgf1md == NULL) {
-    mgf1md = md;
-  }
-
-  size_t mdlen = EVP_MD_size(md);
-
-  // The encoded message is one byte smaller than the modulus to ensure that it
-  // doesn't end up greater than the modulus. Thus there's an extra "+1" here
-  // compared to https://tools.ietf.org/html/rfc2437#section-9.1.1.2.
-  if (from_len < 1 + 2*mdlen + 1) {
-    // 'from_len' is the length of the modulus, i.e. does not depend on the
-    // particular ciphertext.
-    goto decoding_err;
-  }
-
-  size_t dblen = from_len - mdlen - 1;
-  FIPS_service_indicator_lock_state();
-  db = OPENSSL_malloc(dblen);
-  if (db == NULL) {
-    goto err;
-  }
-
-  const uint8_t *maskedseed = from + 1;
-  const uint8_t *maskeddb = from + 1 + mdlen;
-
-  uint8_t seed[EVP_MAX_MD_SIZE];
-  if (!PKCS1_MGF1(seed, mdlen, maskeddb, dblen, mgf1md)) {
-    goto err;
-  }
-  for (size_t i = 0; i < mdlen; i++) {
-    seed[i] ^= maskedseed[i];
-  }
-
-  if (!PKCS1_MGF1(db, dblen, seed, mdlen, mgf1md)) {
-    goto err;
-  }
-  for (size_t i = 0; i < dblen; i++) {
-    db[i] ^= maskeddb[i];
-  }
-
-  uint8_t phash[EVP_MAX_MD_SIZE];
-  if (!EVP_Digest(param, param_len, phash, NULL, md, NULL)) {
-    goto err;
-  }
-
-  crypto_word_t bad = ~constant_time_is_zero_w(CRYPTO_memcmp(db, phash, mdlen));
-  bad |= ~constant_time_is_zero_w(from[0]);
-
-  crypto_word_t looking_for_one_byte = CONSTTIME_TRUE_W;
-  size_t one_index = 0;
-  for (size_t i = mdlen; i < dblen; i++) {
-    crypto_word_t equals1 = constant_time_eq_w(db[i], 1);
-    crypto_word_t equals0 = constant_time_eq_w(db[i], 0);
-    one_index =
-        constant_time_select_w(looking_for_one_byte & equals1, i, one_index);
-    looking_for_one_byte =
-        constant_time_select_w(equals1, 0, looking_for_one_byte);
-    bad |= looking_for_one_byte & ~equals0;
-  }
-
-  bad |= looking_for_one_byte;
-
-  // Whether the overall padding was valid or not in OAEP is public.
-  if (constant_time_declassify_w(bad)) {
-    goto decoding_err;
-  }
-
-  // Once the padding is known to be valid, the output length is also public.
-  static_assert(sizeof(size_t) <= sizeof(crypto_word_t),
-                "size_t does not fit in crypto_word_t");
-  one_index = constant_time_declassify_w(one_index);
-
-  one_index++;
-  size_t mlen = dblen - one_index;
-  if (max_out < mlen) {
-    OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE);
-    goto err;
-  }
-
-  OPENSSL_memcpy(out, db + one_index, mlen);
-  *out_len = mlen;
-  OPENSSL_free(db);
-  FIPS_service_indicator_unlock_state();
-  return 1;
-
-decoding_err:
-  // To avoid chosen ciphertext attacks, the error message should not reveal
-  // which kind of decoding error happened.
-  OPENSSL_PUT_ERROR(RSA, RSA_R_OAEP_DECODING_ERROR);
- err:
-  OPENSSL_free(db);
-  FIPS_service_indicator_unlock_state();
-  return 0;
-}
-
 static const uint8_t kPSSZeroes[] = {0, 0, 0, 0, 0, 0, 0, 0};
 
 int RSA_verify_PKCS1_PSS_mgf1(const RSA *rsa, const uint8_t *mHash,
@@ -504,9 +221,9 @@
   FIPS_service_indicator_lock_state();
 
   // Negative sLen has special meanings:
-  //	-1	sLen == hLen
-  //	-2	salt length is autorecovered from signature
-  //	-N	reserved
+  //   -1      sLen == hLen
+  //   -2      salt length is autorecovered from signature
+  //   -N      reserved
   size_t hLen = EVP_MD_size(Hash);
   if (sLen == -1) {
     sLen = (int)hLen;
diff --git a/crypto/fipsmodule/rsa/rsa.c b/crypto/fipsmodule/rsa/rsa.c
index 2139275..dffc8aa 100644
--- a/crypto/fipsmodule/rsa/rsa.c
+++ b/crypto/fipsmodule/rsa/rsa.c
@@ -288,21 +288,6 @@
   return 1;
 }
 
-int RSA_public_encrypt(size_t flen, const uint8_t *from, uint8_t *to, RSA *rsa,
-                       int padding) {
-  size_t out_len;
-
-  if (!RSA_encrypt(rsa, &out_len, to, RSA_size(rsa), from, flen, padding)) {
-    return -1;
-  }
-
-  if (out_len > INT_MAX) {
-    OPENSSL_PUT_ERROR(RSA, ERR_R_OVERFLOW);
-    return -1;
-  }
-  return (int)out_len;
-}
-
 static int rsa_sign_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) {
@@ -320,58 +305,6 @@
                                    padding);
 }
 
-int RSA_private_encrypt(size_t flen, const uint8_t *from, uint8_t *to, RSA *rsa,
-                        int padding) {
-  size_t out_len;
-
-  if (!RSA_sign_raw(rsa, &out_len, to, RSA_size(rsa), from, flen, padding)) {
-    return -1;
-  }
-
-  if (out_len > INT_MAX) {
-    OPENSSL_PUT_ERROR(RSA, ERR_R_OVERFLOW);
-    return -1;
-  }
-  return (int)out_len;
-}
-
-int RSA_decrypt(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->meth->decrypt) {
-    return rsa->meth->decrypt(rsa, out_len, out, max_out, in, in_len, padding);
-  }
-
-  return rsa_default_decrypt(rsa, out_len, out, max_out, in, in_len, padding);
-}
-
-int RSA_private_decrypt(size_t flen, const uint8_t *from, uint8_t *to, RSA *rsa,
-                        int padding) {
-  size_t out_len;
-  if (!RSA_decrypt(rsa, &out_len, to, RSA_size(rsa), from, flen, padding)) {
-    return -1;
-  }
-
-  if (out_len > INT_MAX) {
-    OPENSSL_PUT_ERROR(RSA, ERR_R_OVERFLOW);
-    return -1;
-  }
-  return (int)out_len;
-}
-
-int RSA_public_decrypt(size_t flen, const uint8_t *from, uint8_t *to, RSA *rsa,
-                       int padding) {
-  size_t out_len;
-  if (!RSA_verify_raw(rsa, &out_len, to, RSA_size(rsa), from, flen, padding)) {
-    return -1;
-  }
-
-  if (out_len > INT_MAX) {
-    OPENSSL_PUT_ERROR(RSA, ERR_R_OVERFLOW);
-    return -1;
-  }
-  return (int)out_len;
-}
-
 unsigned RSA_size(const RSA *rsa) {
   size_t ret = rsa->meth->size ? rsa->meth->size(rsa) : rsa_default_size(rsa);
   // RSA modulus sizes are bounded by |BIGNUM|, which must fit in |unsigned|.
@@ -962,8 +895,8 @@
   return ret;
 }
 
-int RSA_private_transform(RSA *rsa, uint8_t *out, const uint8_t *in,
-                          size_t len) {
+int rsa_private_transform_no_self_test(RSA *rsa, uint8_t *out,
+                                       const uint8_t *in, size_t len) {
   if (rsa->meth->private_transform) {
     return rsa->meth->private_transform(rsa, out, in, len);
   }
@@ -971,6 +904,12 @@
   return rsa_default_private_transform(rsa, out, in, len);
 }
 
+int rsa_private_transform(RSA *rsa, uint8_t *out, const uint8_t *in,
+                          size_t len) {
+  boringssl_ensure_rsa_self_test();
+  return rsa_private_transform_no_self_test(rsa, out, in, len);
+}
+
 int RSA_flags(const RSA *rsa) { return rsa->flags; }
 
 int RSA_test_flags(const RSA *rsa, int flags) { return rsa->flags & flags; }
diff --git a/crypto/fipsmodule/rsa/rsa_impl.c b/crypto/fipsmodule/rsa/rsa_impl.c
index 4500b1a..dabcd2f 100644
--- a/crypto/fipsmodule/rsa/rsa_impl.c
+++ b/crypto/fipsmodule/rsa/rsa_impl.c
@@ -266,94 +266,6 @@
   return BN_num_bytes(rsa->n);
 }
 
-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) {
-  boringssl_ensure_rsa_self_test();
-
-  if (!rsa_check_public_key(rsa)) {
-    return 0;
-  }
-
-  const unsigned rsa_size = RSA_size(rsa);
-  BIGNUM *f, *result;
-  uint8_t *buf = NULL;
-  BN_CTX *ctx = NULL;
-  int i, ret = 0;
-
-  if (max_out < rsa_size) {
-    OPENSSL_PUT_ERROR(RSA, RSA_R_OUTPUT_BUFFER_TOO_SMALL);
-    return 0;
-  }
-
-  ctx = BN_CTX_new();
-  if (ctx == NULL) {
-    goto err;
-  }
-
-  BN_CTX_start(ctx);
-  f = BN_CTX_get(ctx);
-  result = BN_CTX_get(ctx);
-  buf = OPENSSL_malloc(rsa_size);
-  if (!f || !result || !buf) {
-    goto err;
-  }
-
-  switch (padding) {
-    case RSA_PKCS1_PADDING:
-      i = RSA_padding_add_PKCS1_type_2(buf, rsa_size, in, in_len);
-      break;
-    case RSA_PKCS1_OAEP_PADDING:
-      // Use the default parameters: SHA-1 for both hashes and no label.
-      i = RSA_padding_add_PKCS1_OAEP_mgf1(buf, rsa_size, in, in_len,
-                                          NULL, 0, NULL, NULL);
-      break;
-    case RSA_NO_PADDING:
-      i = RSA_padding_add_none(buf, rsa_size, in, in_len);
-      break;
-    default:
-      OPENSSL_PUT_ERROR(RSA, RSA_R_UNKNOWN_PADDING_TYPE);
-      goto err;
-  }
-
-  if (i <= 0) {
-    goto err;
-  }
-
-  if (BN_bin2bn(buf, rsa_size, f) == NULL) {
-    goto err;
-  }
-
-  if (BN_ucmp(f, rsa->n) >= 0) {
-    // usually the padding functions would catch this
-    OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE_FOR_MODULUS);
-    goto err;
-  }
-
-  if (!BN_MONT_CTX_set_locked(&rsa->mont_n, &rsa->lock, rsa->n, ctx) ||
-      !BN_mod_exp_mont(result, f, rsa->e, &rsa->mont_n->N, ctx, rsa->mont_n)) {
-    goto err;
-  }
-
-  // put in leading 0 bytes if the number is less than the length of the
-  // modulus
-  if (!BN_bn2bin_padded(out, rsa_size, result)) {
-    OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR);
-    goto err;
-  }
-
-  *out_len = rsa_size;
-  ret = 1;
-
-err:
-  if (ctx != NULL) {
-    BN_CTX_end(ctx);
-    BN_CTX_free(ctx);
-  }
-  OPENSSL_free(buf);
-
-  return ret;
-}
-
 // MAX_BLINDINGS_PER_RSA defines the maximum number of cached BN_BLINDINGs per
 // RSA*. Then this limit is exceeded, BN_BLINDING objects will be created and
 // destroyed as needed.
@@ -516,7 +428,7 @@
     goto err;
   }
 
-  if (!RSA_private_transform(rsa, out, buf, rsa_size)) {
+  if (!rsa_private_transform_no_self_test(rsa, out, buf, rsa_size)) {
     goto err;
   }
 
@@ -530,71 +442,6 @@
   return ret;
 }
 
-int rsa_default_decrypt(RSA *rsa, size_t *out_len, uint8_t *out, size_t max_out,
-                        const uint8_t *in, size_t in_len, int padding) {
-  boringssl_ensure_rsa_self_test();
-
-  const unsigned rsa_size = RSA_size(rsa);
-  uint8_t *buf = NULL;
-  int ret = 0;
-
-  if (max_out < rsa_size) {
-    OPENSSL_PUT_ERROR(RSA, RSA_R_OUTPUT_BUFFER_TOO_SMALL);
-    return 0;
-  }
-
-  if (padding == RSA_NO_PADDING) {
-    buf = out;
-  } else {
-    // Allocate a temporary buffer to hold the padded plaintext.
-    buf = OPENSSL_malloc(rsa_size);
-    if (buf == NULL) {
-      goto err;
-    }
-  }
-
-  if (in_len != rsa_size) {
-    OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_LEN_NOT_EQUAL_TO_MOD_LEN);
-    goto err;
-  }
-
-  if (!RSA_private_transform(rsa, buf, in, rsa_size)) {
-    goto err;
-  }
-
-  switch (padding) {
-    case RSA_PKCS1_PADDING:
-      ret =
-          RSA_padding_check_PKCS1_type_2(out, out_len, rsa_size, buf, rsa_size);
-      break;
-    case RSA_PKCS1_OAEP_PADDING:
-      // Use the default parameters: SHA-1 for both hashes and no label.
-      ret = RSA_padding_check_PKCS1_OAEP_mgf1(out, out_len, rsa_size, buf,
-                                              rsa_size, NULL, 0, NULL, NULL);
-      break;
-    case RSA_NO_PADDING:
-      *out_len = rsa_size;
-      ret = 1;
-      break;
-    default:
-      OPENSSL_PUT_ERROR(RSA, RSA_R_UNKNOWN_PADDING_TYPE);
-      goto err;
-  }
-
-  CONSTTIME_DECLASSIFY(&ret, sizeof(ret));
-  if (!ret) {
-    OPENSSL_PUT_ERROR(RSA, RSA_R_PADDING_CHECK_FAILED);
-  } else {
-    CONSTTIME_DECLASSIFY(out, *out_len);
-  }
-
-err:
-  if (padding != RSA_NO_PADDING) {
-    OPENSSL_free(buf);
-  }
-
-  return ret;
-}
 
 static int mod_exp(BIGNUM *r0, const BIGNUM *I, RSA *rsa, BN_CTX *ctx);
 
diff --git a/crypto/rsa_extra/internal.h b/crypto/rsa_extra/internal.h
new file mode 100644
index 0000000..6317cfc
--- /dev/null
+++ b/crypto/rsa_extra/internal.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+
+#ifndef OPENSSL_HEADER_RSA_EXTRA_INTERNAL_H
+#define OPENSSL_HEADER_RSA_EXTRA_INTERNAL_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+int RSA_padding_check_PKCS1_OAEP_mgf1(uint8_t *out, size_t *out_len,
+                                      size_t max_out, const uint8_t *from,
+                                      size_t from_len, const uint8_t *param,
+                                      size_t param_len, const EVP_MD *md,
+                                      const EVP_MD *mgf1md);
+
+
+#if defined(__cplusplus)
+}  // extern C
+#endif
+
+#endif  // OPENSSL_HEADER_RSA_EXTRA_INTERNAL_H
diff --git a/crypto/rsa_extra/rsa_crypt.c b/crypto/rsa_extra/rsa_crypt.c
new file mode 100644
index 0000000..97afa3d
--- /dev/null
+++ b/crypto/rsa_extra/rsa_crypt.c
@@ -0,0 +1,563 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/base.h>
+
+#include <limits.h>
+
+#include <openssl/err.h>
+#include <openssl/rsa.h>
+#include <openssl/bn.h>
+#include <openssl/rand.h>
+#include <openssl/mem.h>
+#include <openssl/evp.h>
+
+#include "../fipsmodule/bn/internal.h"
+#include "../fipsmodule/rsa/internal.h"
+#include "../internal.h"
+#include "internal.h"
+
+
+static void rand_nonzero(uint8_t *out, size_t len) {
+  RAND_bytes(out, len);
+
+  for (size_t i = 0; i < len; i++) {
+    while (out[i] == 0) {
+      RAND_bytes(out + i, 1);
+    }
+  }
+}
+
+int RSA_padding_add_PKCS1_OAEP_mgf1(uint8_t *to, size_t to_len,
+                                    const uint8_t *from, size_t from_len,
+                                    const uint8_t *param, size_t param_len,
+                                    const EVP_MD *md, const EVP_MD *mgf1md) {
+  if (md == NULL) {
+    md = EVP_sha1();
+  }
+  if (mgf1md == NULL) {
+    mgf1md = md;
+  }
+
+  size_t mdlen = EVP_MD_size(md);
+
+  if (to_len < 2 * mdlen + 2) {
+    OPENSSL_PUT_ERROR(RSA, RSA_R_KEY_SIZE_TOO_SMALL);
+    return 0;
+  }
+
+  size_t emlen = to_len - 1;
+  if (from_len > emlen - 2 * mdlen - 1) {
+    OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
+    return 0;
+  }
+
+  if (emlen < 2 * mdlen + 1) {
+    OPENSSL_PUT_ERROR(RSA, RSA_R_KEY_SIZE_TOO_SMALL);
+    return 0;
+  }
+
+  to[0] = 0;
+  uint8_t *seed = to + 1;
+  uint8_t *db = to + mdlen + 1;
+
+  uint8_t *dbmask = NULL;
+  int ret = 0;
+  if (!EVP_Digest(param, param_len, db, NULL, md, NULL)) {
+    goto out;
+  }
+  OPENSSL_memset(db + mdlen, 0, emlen - from_len - 2 * mdlen - 1);
+  db[emlen - from_len - mdlen - 1] = 0x01;
+  OPENSSL_memcpy(db + emlen - from_len - mdlen, from, from_len);
+  if (!RAND_bytes(seed, mdlen)) {
+    goto out;
+  }
+
+  dbmask = OPENSSL_malloc(emlen - mdlen);
+  if (dbmask == NULL) {
+    goto out;
+  }
+
+  if (!PKCS1_MGF1(dbmask, emlen - mdlen, seed, mdlen, mgf1md)) {
+    goto out;
+  }
+  for (size_t i = 0; i < emlen - mdlen; i++) {
+    db[i] ^= dbmask[i];
+  }
+
+  uint8_t seedmask[EVP_MAX_MD_SIZE];
+  if (!PKCS1_MGF1(seedmask, mdlen, db, emlen - mdlen, mgf1md)) {
+    goto out;
+  }
+  for (size_t i = 0; i < mdlen; i++) {
+    seed[i] ^= seedmask[i];
+  }
+  ret = 1;
+
+out:
+  OPENSSL_free(dbmask);
+  return ret;
+}
+
+int RSA_padding_check_PKCS1_OAEP_mgf1(uint8_t *out, size_t *out_len,
+                                      size_t max_out, const uint8_t *from,
+                                      size_t from_len, const uint8_t *param,
+                                      size_t param_len, const EVP_MD *md,
+                                      const EVP_MD *mgf1md) {
+  uint8_t *db = NULL;
+
+  if (md == NULL) {
+    md = EVP_sha1();
+  }
+  if (mgf1md == NULL) {
+    mgf1md = md;
+  }
+
+  size_t mdlen = EVP_MD_size(md);
+
+  // The encoded message is one byte smaller than the modulus to ensure that it
+  // doesn't end up greater than the modulus. Thus there's an extra "+1" here
+  // compared to https://tools.ietf.org/html/rfc2437#section-9.1.1.2.
+  if (from_len < 1 + 2 * mdlen + 1) {
+    // 'from_len' is the length of the modulus, i.e. does not depend on the
+    // particular ciphertext.
+    goto decoding_err;
+  }
+
+  size_t dblen = from_len - mdlen - 1;
+  db = OPENSSL_malloc(dblen);
+  if (db == NULL) {
+    goto err;
+  }
+
+  const uint8_t *maskedseed = from + 1;
+  const uint8_t *maskeddb = from + 1 + mdlen;
+
+  uint8_t seed[EVP_MAX_MD_SIZE];
+  if (!PKCS1_MGF1(seed, mdlen, maskeddb, dblen, mgf1md)) {
+    goto err;
+  }
+  for (size_t i = 0; i < mdlen; i++) {
+    seed[i] ^= maskedseed[i];
+  }
+
+  if (!PKCS1_MGF1(db, dblen, seed, mdlen, mgf1md)) {
+    goto err;
+  }
+  for (size_t i = 0; i < dblen; i++) {
+    db[i] ^= maskeddb[i];
+  }
+
+  uint8_t phash[EVP_MAX_MD_SIZE];
+  if (!EVP_Digest(param, param_len, phash, NULL, md, NULL)) {
+    goto err;
+  }
+
+  crypto_word_t bad = ~constant_time_is_zero_w(CRYPTO_memcmp(db, phash, mdlen));
+  bad |= ~constant_time_is_zero_w(from[0]);
+
+  crypto_word_t looking_for_one_byte = CONSTTIME_TRUE_W;
+  size_t one_index = 0;
+  for (size_t i = mdlen; i < dblen; i++) {
+    crypto_word_t equals1 = constant_time_eq_w(db[i], 1);
+    crypto_word_t equals0 = constant_time_eq_w(db[i], 0);
+    one_index =
+        constant_time_select_w(looking_for_one_byte & equals1, i, one_index);
+    looking_for_one_byte =
+        constant_time_select_w(equals1, 0, looking_for_one_byte);
+    bad |= looking_for_one_byte & ~equals0;
+  }
+
+  bad |= looking_for_one_byte;
+
+  // Whether the overall padding was valid or not in OAEP is public.
+  if (constant_time_declassify_w(bad)) {
+    goto decoding_err;
+  }
+
+  // Once the padding is known to be valid, the output length is also public.
+  static_assert(sizeof(size_t) <= sizeof(crypto_word_t),
+                "size_t does not fit in crypto_word_t");
+  one_index = constant_time_declassify_w(one_index);
+
+  one_index++;
+  size_t mlen = dblen - one_index;
+  if (max_out < mlen) {
+    OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE);
+    goto err;
+  }
+
+  OPENSSL_memcpy(out, db + one_index, mlen);
+  *out_len = mlen;
+  OPENSSL_free(db);
+  return 1;
+
+decoding_err:
+  // To avoid chosen ciphertext attacks, the error message should not reveal
+  // which kind of decoding error happened.
+  OPENSSL_PUT_ERROR(RSA, RSA_R_OAEP_DECODING_ERROR);
+err:
+  OPENSSL_free(db);
+  return 0;
+}
+
+static int rsa_padding_add_PKCS1_type_2(uint8_t *to, size_t to_len,
+                                        const uint8_t *from, size_t from_len) {
+  // See RFC 8017, section 7.2.1.
+  if (to_len < RSA_PKCS1_PADDING_SIZE) {
+    OPENSSL_PUT_ERROR(RSA, RSA_R_KEY_SIZE_TOO_SMALL);
+    return 0;
+  }
+
+  if (from_len > to_len - RSA_PKCS1_PADDING_SIZE) {
+    OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
+    return 0;
+  }
+
+  to[0] = 0;
+  to[1] = 2;
+
+  size_t padding_len = to_len - 3 - from_len;
+  rand_nonzero(to + 2, padding_len);
+  to[2 + padding_len] = 0;
+  OPENSSL_memcpy(to + to_len - from_len, from, from_len);
+  return 1;
+}
+
+static int rsa_padding_check_PKCS1_type_2(uint8_t *out, size_t *out_len,
+                                          size_t max_out, const uint8_t *from,
+                                          size_t from_len) {
+  if (from_len == 0) {
+    OPENSSL_PUT_ERROR(RSA, RSA_R_EMPTY_PUBLIC_KEY);
+    return 0;
+  }
+
+  // PKCS#1 v1.5 decryption. See "PKCS #1 v2.2: RSA Cryptography
+  // Standard", section 7.2.2.
+  if (from_len < RSA_PKCS1_PADDING_SIZE) {
+    // |from| is zero-padded to the size of the RSA modulus, a public value, so
+    // this can be rejected in non-constant time.
+    OPENSSL_PUT_ERROR(RSA, RSA_R_KEY_SIZE_TOO_SMALL);
+    return 0;
+  }
+
+  crypto_word_t first_byte_is_zero = constant_time_eq_w(from[0], 0);
+  crypto_word_t second_byte_is_two = constant_time_eq_w(from[1], 2);
+
+  crypto_word_t zero_index = 0, looking_for_index = CONSTTIME_TRUE_W;
+  for (size_t i = 2; i < from_len; i++) {
+    crypto_word_t equals0 = constant_time_is_zero_w(from[i]);
+    zero_index =
+        constant_time_select_w(looking_for_index & equals0, i, zero_index);
+    looking_for_index = constant_time_select_w(equals0, 0, looking_for_index);
+  }
+
+  // The input must begin with 00 02.
+  crypto_word_t valid_index = first_byte_is_zero;
+  valid_index &= second_byte_is_two;
+
+  // We must have found the end of PS.
+  valid_index &= ~looking_for_index;
+
+  // PS must be at least 8 bytes long, and it starts two bytes into |from|.
+  valid_index &= constant_time_ge_w(zero_index, 2 + 8);
+
+  // Skip the zero byte.
+  zero_index++;
+
+  // NOTE: Although this logic attempts to be constant time, the API contracts
+  // of this function and |RSA_decrypt| with |RSA_PKCS1_PADDING| make it
+  // impossible to completely avoid Bleichenbacher's attack. Consumers should
+  // use |RSA_PADDING_NONE| and perform the padding check in constant-time
+  // combined with a swap to a random session key or other mitigation.
+  CONSTTIME_DECLASSIFY(&valid_index, sizeof(valid_index));
+  CONSTTIME_DECLASSIFY(&zero_index, sizeof(zero_index));
+
+  if (!valid_index) {
+    OPENSSL_PUT_ERROR(RSA, RSA_R_PKCS_DECODING_ERROR);
+    return 0;
+  }
+
+  const size_t msg_len = from_len - zero_index;
+  if (msg_len > max_out) {
+    // This shouldn't happen because this function is always called with
+    // |max_out| as the key size and |from_len| is bounded by the key size.
+    OPENSSL_PUT_ERROR(RSA, RSA_R_PKCS_DECODING_ERROR);
+    return 0;
+  }
+
+  OPENSSL_memcpy(out, &from[zero_index], msg_len);
+  *out_len = msg_len;
+  return 1;
+}
+
+int RSA_public_encrypt(size_t flen, const uint8_t *from, uint8_t *to, RSA *rsa,
+                       int padding) {
+  size_t out_len;
+
+  if (!RSA_encrypt(rsa, &out_len, to, RSA_size(rsa), from, flen, padding)) {
+    return -1;
+  }
+
+  if (out_len > INT_MAX) {
+    OPENSSL_PUT_ERROR(RSA, ERR_R_OVERFLOW);
+    return -1;
+  }
+  return (int)out_len;
+}
+
+int RSA_private_encrypt(size_t flen, const uint8_t *from, uint8_t *to, RSA *rsa,
+                        int padding) {
+  size_t out_len;
+
+  if (!RSA_sign_raw(rsa, &out_len, to, RSA_size(rsa), from, flen, padding)) {
+    return -1;
+  }
+
+  if (out_len > INT_MAX) {
+    OPENSSL_PUT_ERROR(RSA, ERR_R_OVERFLOW);
+    return -1;
+  }
+  return (int)out_len;
+}
+
+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_check_public_key(rsa)) {
+    return 0;
+  }
+
+  const unsigned rsa_size = RSA_size(rsa);
+  BIGNUM *f, *result;
+  uint8_t *buf = NULL;
+  BN_CTX *ctx = NULL;
+  int i, ret = 0;
+
+  if (max_out < rsa_size) {
+    OPENSSL_PUT_ERROR(RSA, RSA_R_OUTPUT_BUFFER_TOO_SMALL);
+    return 0;
+  }
+
+  ctx = BN_CTX_new();
+  if (ctx == NULL) {
+    goto err;
+  }
+
+  BN_CTX_start(ctx);
+  f = BN_CTX_get(ctx);
+  result = BN_CTX_get(ctx);
+  buf = OPENSSL_malloc(rsa_size);
+  if (!f || !result || !buf) {
+    goto err;
+  }
+
+  switch (padding) {
+    case RSA_PKCS1_PADDING:
+      i = rsa_padding_add_PKCS1_type_2(buf, rsa_size, in, in_len);
+      break;
+    case RSA_PKCS1_OAEP_PADDING:
+      // Use the default parameters: SHA-1 for both hashes and no label.
+      i = RSA_padding_add_PKCS1_OAEP_mgf1(buf, rsa_size, in, in_len, NULL, 0,
+                                          NULL, NULL);
+      break;
+    case RSA_NO_PADDING:
+      i = RSA_padding_add_none(buf, rsa_size, in, in_len);
+      break;
+    default:
+      OPENSSL_PUT_ERROR(RSA, RSA_R_UNKNOWN_PADDING_TYPE);
+      goto err;
+  }
+
+  if (i <= 0) {
+    goto err;
+  }
+
+  if (BN_bin2bn(buf, rsa_size, f) == NULL) {
+    goto err;
+  }
+
+  if (BN_ucmp(f, rsa->n) >= 0) {
+    // usually the padding functions would catch this
+    OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE_FOR_MODULUS);
+    goto err;
+  }
+
+  if (!BN_MONT_CTX_set_locked(&rsa->mont_n, &rsa->lock, rsa->n, ctx) ||
+      !BN_mod_exp_mont(result, f, rsa->e, &rsa->mont_n->N, ctx, rsa->mont_n)) {
+    goto err;
+  }
+
+  // put in leading 0 bytes if the number is less than the length of the
+  // modulus
+  if (!BN_bn2bin_padded(out, rsa_size, result)) {
+    OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR);
+    goto err;
+  }
+
+  *out_len = rsa_size;
+  ret = 1;
+
+err:
+  if (ctx != NULL) {
+    BN_CTX_end(ctx);
+    BN_CTX_free(ctx);
+  }
+  OPENSSL_free(buf);
+
+  return ret;
+}
+
+static int rsa_default_decrypt(RSA *rsa, size_t *out_len, uint8_t *out,
+                               size_t max_out, const uint8_t *in, size_t in_len,
+                               int padding) {
+  const unsigned rsa_size = RSA_size(rsa);
+  uint8_t *buf = NULL;
+  int ret = 0;
+
+  if (max_out < rsa_size) {
+    OPENSSL_PUT_ERROR(RSA, RSA_R_OUTPUT_BUFFER_TOO_SMALL);
+    return 0;
+  }
+
+  if (padding == RSA_NO_PADDING) {
+    buf = out;
+  } else {
+    // Allocate a temporary buffer to hold the padded plaintext.
+    buf = OPENSSL_malloc(rsa_size);
+    if (buf == NULL) {
+      goto err;
+    }
+  }
+
+  if (in_len != rsa_size) {
+    OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_LEN_NOT_EQUAL_TO_MOD_LEN);
+    goto err;
+  }
+
+  if (!rsa_private_transform(rsa, buf, in, rsa_size)) {
+    goto err;
+  }
+
+  switch (padding) {
+    case RSA_PKCS1_PADDING:
+      ret =
+          rsa_padding_check_PKCS1_type_2(out, out_len, rsa_size, buf, rsa_size);
+      break;
+    case RSA_PKCS1_OAEP_PADDING:
+      // Use the default parameters: SHA-1 for both hashes and no label.
+      ret = RSA_padding_check_PKCS1_OAEP_mgf1(out, out_len, rsa_size, buf,
+                                              rsa_size, NULL, 0, NULL, NULL);
+      break;
+    case RSA_NO_PADDING:
+      *out_len = rsa_size;
+      ret = 1;
+      break;
+    default:
+      OPENSSL_PUT_ERROR(RSA, RSA_R_UNKNOWN_PADDING_TYPE);
+      goto err;
+  }
+
+  CONSTTIME_DECLASSIFY(&ret, sizeof(ret));
+  if (!ret) {
+    OPENSSL_PUT_ERROR(RSA, RSA_R_PADDING_CHECK_FAILED);
+  } else {
+    CONSTTIME_DECLASSIFY(out, *out_len);
+  }
+
+err:
+  if (padding != RSA_NO_PADDING) {
+    OPENSSL_free(buf);
+  }
+
+  return ret;
+}
+
+int RSA_decrypt(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->meth->decrypt) {
+    return rsa->meth->decrypt(rsa, out_len, out, max_out, in, in_len, padding);
+  }
+
+  return rsa_default_decrypt(rsa, out_len, out, max_out, in, in_len, padding);
+}
+
+int RSA_private_decrypt(size_t flen, const uint8_t *from, uint8_t *to, RSA *rsa,
+                        int padding) {
+  size_t out_len;
+  if (!RSA_decrypt(rsa, &out_len, to, RSA_size(rsa), from, flen, padding)) {
+    return -1;
+  }
+
+  if (out_len > INT_MAX) {
+    OPENSSL_PUT_ERROR(RSA, ERR_R_OVERFLOW);
+    return -1;
+  }
+  return (int)out_len;
+}
+
+int RSA_public_decrypt(size_t flen, const uint8_t *from, uint8_t *to, RSA *rsa,
+                       int padding) {
+  size_t out_len;
+  if (!RSA_verify_raw(rsa, &out_len, to, RSA_size(rsa), from, flen, padding)) {
+    return -1;
+  }
+
+  if (out_len > INT_MAX) {
+    OPENSSL_PUT_ERROR(RSA, ERR_R_OVERFLOW);
+    return -1;
+  }
+  return (int)out_len;
+}