#include "crypto/bn.h"
#include "rsa_local.h"
#include "internal/constant_time.h"
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+#include <openssl/hmac.h>
static int rsa_ossl_public_encrypt(int flen, const unsigned char *from,
unsigned char *to, RSA *rsa, int padding);
BIGNUM *f, *ret;
int j, num = 0, r = -1;
unsigned char *buf = NULL;
+ unsigned char d_hash[SHA256_DIGEST_LENGTH] = {0};
+ HMAC_CTX *hmac = NULL;
+ unsigned int md_len = SHA256_DIGEST_LENGTH;
+ unsigned char kdk[SHA256_DIGEST_LENGTH] = {0};
BN_CTX *ctx = NULL;
int local_blinding = 0;
+ EVP_MD *md = NULL;
/*
* Used only if the blinding structure is shared. A non-NULL unblind
* instructs rsa_blinding_convert() and rsa_blinding_invert() to store
goto err;
}
+ if (flen < 1) {
+ ERR_raise(ERR_LIB_RSA, RSA_R_DATA_TOO_SMALL);
+ goto err;
+ }
+
/* make data into a big number */
if (BN_bin2bn(from, (int)flen, f) == NULL)
goto err;
if (!rsa_blinding_invert(blinding, ret, unblind, ctx))
goto err;
+ /*
+ * derive the Key Derivation Key from private exponent and public
+ * ciphertext
+ */
+ if (!(rsa->flags & RSA_FLAG_EXT_PKEY)) {
+ /*
+ * because we use d as a handle to rsa->d we need to keep it local and
+ * free before any further use of rsa->d
+ */
+ BIGNUM *d = BN_new();
+ if (d == NULL) {
+ ERR_raise(ERR_LIB_RSA, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+ if (rsa->d == NULL) {
+ ERR_raise(ERR_LIB_RSA, RSA_R_MISSING_PRIVATE_KEY);
+ BN_free(d);
+ goto err;
+ }
+ BN_with_flags(d, rsa->d, BN_FLG_CONSTTIME);
+ if (BN_bn2binpad(d, buf, num) < 0) {
+ ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
+ BN_free(d);
+ goto err;
+ }
+ BN_free(d);
+
+ /*
+ * we use hardcoded hash so that migrating between versions that use
+ * different hash doesn't provide a Bleichenbacher oracle:
+ * if the attacker can see that different versions return different
+ * messages for the same ciphertext, they'll know that the message is
+ * syntethically generated, which means that the padding check failed
+ */
+ md = EVP_MD_fetch(rsa->libctx, "sha256", NULL);
+ if (md == NULL) {
+ ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+
+ if (EVP_Digest(buf, num, d_hash, NULL, md, NULL) <= 0) {
+ ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+
+ hmac = HMAC_CTX_new();
+ if (hmac == NULL) {
+ ERR_raise(ERR_LIB_RSA, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ if (HMAC_Init_ex(hmac, d_hash, sizeof(d_hash), md, NULL) <= 0) {
+ ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+
+ if (flen < num) {
+ memset(buf, 0, num - flen);
+ if (HMAC_Update(hmac, buf, num - flen) <= 0) {
+ ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ }
+ if (HMAC_Update(hmac, from, flen) <= 0) {
+ ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+
+ md_len = SHA256_DIGEST_LENGTH;
+ if (HMAC_Final(hmac, kdk, &md_len) <= 0) {
+ ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ }
+
j = BN_bn2binpad(ret, buf, num);
if (j < 0)
goto err;
switch (padding) {
case RSA_PKCS1_PADDING:
- r = RSA_padding_check_PKCS1_type_2(to, num, buf, j, num);
+ if (rsa->flags & RSA_FLAG_EXT_PKEY)
+ r = RSA_padding_check_PKCS1_type_2(to, num, buf, j, num);
+ else
+ r = ossl_rsa_padding_check_PKCS1_type_2(rsa->libctx, to, num, buf, j, num, kdk);
break;
case RSA_PKCS1_OAEP_PADDING:
r = RSA_padding_check_PKCS1_OAEP(to, num, buf, j, num, NULL, 0);
#endif
err:
+ HMAC_CTX_free(hmac);
+ EVP_MD_free(md);
BN_CTX_end(ctx);
BN_CTX_free(ctx);
OPENSSL_clear_free(buf, num);
#include <openssl/rand.h>
/* Just for the SSL_MAX_MASTER_KEY_LENGTH value */
#include <openssl/prov_ssl.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+#include <openssl/hmac.h>
#include "internal/cryptlib.h"
#include "crypto/rsa.h"
#include "rsa_local.h"
+
int RSA_padding_add_PKCS1_type_1(unsigned char *to, int tlen,
const unsigned char *from, int flen)
{
return constant_time_select_int(good, mlen, -1);
}
+
+static int ossl_rsa_prf(OSSL_LIB_CTX *ctx,
+ unsigned char *to, int tlen,
+ const char *label, int llen,
+ const unsigned char *kdk,
+ uint16_t bitlen)
+{
+ int pos;
+ int ret = -1;
+ uint16_t iter = 0;
+ unsigned char be_iter[sizeof(iter)];
+ unsigned char be_bitlen[sizeof(bitlen)];
+ HMAC_CTX *hmac = NULL;
+ EVP_MD *md = NULL;
+ unsigned char hmac_out[SHA256_DIGEST_LENGTH];
+ unsigned int md_len;
+
+ if (tlen * 8 != bitlen) {
+ ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
+ return ret;
+ }
+
+ be_bitlen[0] = (bitlen >> 8) & 0xff;
+ be_bitlen[1] = bitlen & 0xff;
+
+ hmac = HMAC_CTX_new();
+ if (hmac == NULL) {
+ ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+
+ /*
+ * we use hardcoded hash so that migrating between versions that use
+ * different hash doesn't provide a Bleichenbacher oracle:
+ * if the attacker can see that different versions return different
+ * messages for the same ciphertext, they'll know that the message is
+ * syntethically generated, which means that the padding check failed
+ */
+ md = EVP_MD_fetch(ctx, "sha256", NULL);
+ if (md == NULL) {
+ ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+
+ if (HMAC_Init_ex(hmac, kdk, SHA256_DIGEST_LENGTH, md, NULL) <= 0) {
+ ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+
+ for (pos = 0; pos < tlen; pos += SHA256_DIGEST_LENGTH, iter++) {
+ if (HMAC_Init_ex(hmac, NULL, 0, NULL, NULL) <= 0) {
+ ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+
+ be_iter[0] = (iter >> 8) & 0xff;
+ be_iter[1] = iter & 0xff;
+
+ if (HMAC_Update(hmac, be_iter, sizeof(be_iter)) <= 0) {
+ ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ if (HMAC_Update(hmac, (unsigned char *)label, llen) <= 0) {
+ ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ if (HMAC_Update(hmac, be_bitlen, sizeof(be_bitlen)) <= 0) {
+ ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+
+ /*
+ * HMAC_Final requires the output buffer to fit the whole MAC
+ * value, so we need to use the intermediate buffer for the last
+ * unaligned block
+ */
+ md_len = SHA256_DIGEST_LENGTH;
+ if (pos + SHA256_DIGEST_LENGTH > tlen) {
+ if (HMAC_Final(hmac, hmac_out, &md_len) <= 0) {
+ ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ memcpy(to + pos, hmac_out, tlen - pos);
+ } else {
+ if (HMAC_Final(hmac, to + pos, &md_len) <= 0) {
+ ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ }
+ }
+
+ ret = 0;
+
+err:
+ HMAC_CTX_free(hmac);
+ EVP_MD_free(md);
+ return ret;
+}
+
+/*
+ * ossl_rsa_padding_check_PKCS1_type_2() checks and removes the PKCS#1 type 2
+ * padding from a decrypted RSA message. Unlike the
+ * RSA_padding_check_PKCS1_type_2() it will not return an error in case it
+ * detects a padding error, rather it will return a deterministically generated
+ * random message. In other words it will perform an implicit rejection
+ * of an invalid padding. This means that the returned value does not indicate
+ * if the padding of the encrypted message was correct or not, making
+ * side channel attacks like the ones described by Bleichenbacher impossible
+ * without access to the full decrypted value and a brute-force search of
+ * remaining padding bytes
+ */
+int ossl_rsa_padding_check_PKCS1_type_2(OSSL_LIB_CTX *ctx,
+ unsigned char *to, int tlen,
+ const unsigned char *from, int flen,
+ int num, unsigned char *kdk)
+{
+/*
+ * We need to generate a random length for the synthethic message, to avoid
+ * bias towards zero and avoid non-constant timeness of DIV, we prepare
+ * 128 values to check if they are not too large for the used key size,
+ * and use 0 in case none of them are small enough, as 2^-128 is a good enough
+ * safety margin
+ */
+#define MAX_LEN_GEN_TRIES 128
+ unsigned char *synthetic = NULL;
+ int synthethic_length;
+ uint16_t len_candidate;
+ unsigned char candidate_lengths[MAX_LEN_GEN_TRIES * sizeof(len_candidate)];
+ uint16_t len_mask;
+ uint16_t max_sep_offset;
+ int synth_msg_index = 0;
+ int ret = -1;
+ int i, j;
+ unsigned int good, found_zero_byte;
+ int zero_index = 0, msg_index;
+
+ /*
+ * If these checks fail then either the message in publicly invalid, or
+ * we've been called incorrectly. We can fail immediately.
+ * Since this code is called only internally by openssl, those are just
+ * sanity checks
+ */
+ if (num != flen || tlen <= 0 || flen <= 0) {
+ ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
+ return -1;
+ }
+
+ /* Generate a random message to return in case the padding checks fail */
+ synthetic = OPENSSL_malloc(flen);
+ if (synthetic == NULL) {
+ ERR_raise(ERR_LIB_RSA, ERR_R_MALLOC_FAILURE);
+ return -1;
+ }
+
+ if (ossl_rsa_prf(ctx, synthetic, flen, "message", 7, kdk, flen * 8) < 0)
+ goto err;
+
+ /* decide how long the random message should be */
+ if (ossl_rsa_prf(ctx, candidate_lengths, sizeof(candidate_lengths),
+ "length", 6, kdk,
+ MAX_LEN_GEN_TRIES * sizeof(len_candidate) * 8) < 0)
+ goto err;
+
+ /*
+ * max message size is the size of the modulus size less 2 bytes for
+ * version and padding type and a minimum of 8 bytes padding
+ */
+ len_mask = max_sep_offset = flen - 2 - 8;
+ /*
+ * we want a mask so lets propagate the high bit to all positions less
+ * significant than it
+ */
+ len_mask |= len_mask >> 1;
+ len_mask |= len_mask >> 2;
+ len_mask |= len_mask >> 4;
+ len_mask |= len_mask >> 8;
+
+ synthethic_length = 0;
+ for (i = 0; i < MAX_LEN_GEN_TRIES * (int)sizeof(len_candidate);
+ i += sizeof(len_candidate)) {
+ len_candidate = (candidate_lengths[i] << 8) | candidate_lengths[i + 1];
+ len_candidate &= len_mask;
+
+ synthethic_length = constant_time_select_int(
+ constant_time_lt(len_candidate, max_sep_offset),
+ len_candidate, synthethic_length);
+ }
+
+ synth_msg_index = flen - synthethic_length;
+
+ /* we have alternative message ready, check the real one */
+ good = constant_time_is_zero(from[0]);
+ good &= constant_time_eq(from[1], 2);
+
+ /* then look for the padding|message separator (the first zero byte) */
+ found_zero_byte = 0;
+ for (i = 2; i < flen; i++) {
+ unsigned int equals0 = constant_time_is_zero(from[i]);
+ zero_index = constant_time_select_int(~found_zero_byte & equals0,
+ i, zero_index);
+ found_zero_byte |= equals0;
+ }
+
+ /*
+ * padding must be at least 8 bytes long, and it starts two bytes into
+ * |from|. If we never found a 0-byte, then |zero_index| is 0 and the check
+ * also fails.
+ */
+ good &= constant_time_ge(zero_index, 2 + 8);
+
+ /*
+ * Skip the zero byte. This is incorrect if we never found a zero-byte
+ * but in this case we also do not copy the message out.
+ */
+ msg_index = zero_index + 1;
+
+ /*
+ * old code returned an error in case the decrypted message wouldn't fit
+ * into the |to|, since that would leak information, return the synthethic
+ * message instead
+ */
+ good &= constant_time_ge(tlen, num - msg_index);
+
+ msg_index = constant_time_select_int(good, msg_index, synth_msg_index);
+
+ /*
+ * since at this point the |msg_index| does not provide the signal
+ * indicating if the padding check failed or not, we don't have to worry
+ * about leaking the length of returned message, we still need to ensure
+ * that we read contents of both buffers so that cache accesses don't leak
+ * the value of |good|
+ */
+ for (i = msg_index, j = 0; i < flen && j < tlen; i++, j++)
+ to[j] = constant_time_select_8(good, from[i], synthetic[i]);
+ ret = j;
+
+err:
+ /*
+ * the only time ret < 0 is when the ciphertext is publicly invalid
+ * or we were called with invalid parameters, so we don't have to perform
+ * a side-channel secure raising of the error
+ */
+ if (ret < 0)
+ ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
+ OPENSSL_free(synthetic);
+ return ret;
+}
+
/*
* ossl_rsa_padding_check_PKCS1_type_2_TLS() checks and removes the PKCS1 type 2
* padding from a decrypted RSA message in a TLS signature. The result is stored