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;
+}