Skip to content

Commit

Permalink
rsa: Add option to disable implicit rejection
Browse files Browse the repository at this point in the history
Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
Reviewed-by: Tim Hudson <tjh@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from #13817)
  • Loading branch information
tomato42 authored and t8m committed Dec 12, 2022
1 parent 8ae4f0e commit 5ab3ec1
Show file tree
Hide file tree
Showing 10 changed files with 95 additions and 8 deletions.
7 changes: 7 additions & 0 deletions crypto/cms/cms_env.c
Expand Up @@ -608,6 +608,13 @@ static int cms_RecipientInfo_ktri_decrypt(CMS_ContentInfo *cms,
if (!ossl_cms_env_asn1_ctrl(ri, 1))
goto err;

if (EVP_PKEY_is_a(pkey, "RSA"))
/* upper layer CMS code incorrectly assumes that a successful RSA
* decryption means that the key matches ciphertext (which never
* was the case, implicit rejection or not), so to make it work
* disable implicit rejection for RSA keys */
EVP_PKEY_CTX_ctrl_str(ktri->pctx, "rsa_pkcs1_implicit_rejection", "0");

if (EVP_PKEY_decrypt(ktri->pctx, NULL, &eklen,
ktri->encryptedKey->data,
ktri->encryptedKey->length) <= 0)
Expand Down
6 changes: 6 additions & 0 deletions crypto/evp/ctrl_params_translate.c
Expand Up @@ -2201,6 +2201,12 @@ static const struct translation_st evp_pkey_ctx_translations[] = {
EVP_PKEY_CTRL_GET_RSA_OAEP_LABEL, NULL, NULL,
OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL, OSSL_PARAM_OCTET_STRING, NULL },

{ SET, EVP_PKEY_RSA, 0, EVP_PKEY_OP_TYPE_CRYPT,
EVP_PKEY_CTRL_RSA_IMPLICIT_REJECTION, NULL,
"rsa_pkcs1_implicit_rejection",
OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION, OSSL_PARAM_UNSIGNED_INTEGER,
NULL },

{ SET, EVP_PKEY_RSA_PSS, 0, EVP_PKEY_OP_TYPE_GEN,
EVP_PKEY_CTRL_MD, "rsa_pss_keygen_md", NULL,
OSSL_ALG_PARAM_DIGEST, OSSL_PARAM_UTF8_STRING, fix_md },
Expand Down
16 changes: 11 additions & 5 deletions crypto/rsa/rsa_ossl.c
Expand Up @@ -390,6 +390,12 @@ static int rsa_ossl_private_decrypt(int flen, const unsigned char *from,
BIGNUM *unblind = NULL;
BN_BLINDING *blinding = NULL;

/*
* we need the value of the private exponent to perform implicit rejection
*/
if ((rsa->flags & RSA_FLAG_EXT_PKEY) && (padding == RSA_PKCS1_PADDING))
padding = RSA_PKCS1_NO_IMPLICIT_REJECT_PADDING;

if ((ctx = BN_CTX_new_ex(rsa->libctx)) == NULL)
goto err;
BN_CTX_start(ctx);
Expand Down Expand Up @@ -488,7 +494,7 @@ static int rsa_ossl_private_decrypt(int flen, const unsigned char *from,
* derive the Key Derivation Key from private exponent and public
* ciphertext
*/
if (!(rsa->flags & RSA_FLAG_EXT_PKEY)) {
if (padding == RSA_PKCS1_PADDING) {
/*
* 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
Expand Down Expand Up @@ -564,11 +570,11 @@ static int rsa_ossl_private_decrypt(int flen, const unsigned char *from,
goto err;

switch (padding) {
case RSA_PKCS1_NO_IMPLICIT_REJECT_PADDING:
r = RSA_padding_check_PKCS1_type_2(to, num, buf, j, num);
break;
case RSA_PKCS1_PADDING:
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);
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);
Expand Down
20 changes: 19 additions & 1 deletion crypto/rsa/rsa_pmeth.c
Expand Up @@ -52,6 +52,8 @@ typedef struct {
/* OAEP label */
unsigned char *oaep_label;
size_t oaep_labellen;
/* if to use implicit rejection in PKCS#1 v1.5 decryption */
int implicit_rejection;
} RSA_PKEY_CTX;

/* True if PSS parameters are restricted */
Expand All @@ -72,6 +74,7 @@ static int pkey_rsa_init(EVP_PKEY_CTX *ctx)
/* Maximum for sign, auto for verify */
rctx->saltlen = RSA_PSS_SALTLEN_AUTO;
rctx->min_saltlen = -1;
rctx->implicit_rejection = 1;
ctx->data = rctx;
ctx->keygen_info = rctx->gentmp;
ctx->keygen_info_count = 2;
Expand All @@ -97,6 +100,7 @@ static int pkey_rsa_copy(EVP_PKEY_CTX *dst, const EVP_PKEY_CTX *src)
dctx->md = sctx->md;
dctx->mgf1md = sctx->mgf1md;
dctx->saltlen = sctx->saltlen;
dctx->implicit_rejection = sctx->implicit_rejection;
if (sctx->oaep_label) {
OPENSSL_free(dctx->oaep_label);
dctx->oaep_label = OPENSSL_memdup(sctx->oaep_label, sctx->oaep_labellen);
Expand Down Expand Up @@ -345,6 +349,7 @@ static int pkey_rsa_decrypt(EVP_PKEY_CTX *ctx,
const unsigned char *in, size_t inlen)
{
int ret;
int pad_mode;
RSA_PKEY_CTX *rctx = ctx->data;
/*
* Discard const. Its marked as const because this may be a cached copy of
Expand All @@ -365,7 +370,12 @@ static int pkey_rsa_decrypt(EVP_PKEY_CTX *ctx,
rctx->oaep_labellen,
rctx->md, rctx->mgf1md);
} else {
ret = RSA_private_decrypt(inlen, in, out, rsa, rctx->pad_mode);
if (rctx->pad_mode == RSA_PKCS1_PADDING &&
rctx->implicit_rejection == 0)
pad_mode = RSA_PKCS1_NO_IMPLICIT_REJECT_PADDING;
else
pad_mode = rctx->pad_mode;
ret = RSA_private_decrypt(inlen, in, out, rsa, pad_mode);
}
*outlen = constant_time_select_s(constant_time_msb_s(ret), *outlen, ret);
ret = constant_time_select_int(constant_time_msb(ret), ret, 1);
Expand Down Expand Up @@ -585,6 +595,14 @@ static int pkey_rsa_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
*(unsigned char **)p2 = rctx->oaep_label;
return rctx->oaep_labellen;

case EVP_PKEY_CTRL_RSA_IMPLICIT_REJECTION:
if (rctx->pad_mode != RSA_PKCS1_PADDING) {
ERR_raise(ERR_LIB_RSA, RSA_R_INVALID_PADDING_MODE);
return -2;
}
rctx->implicit_rejection = p1;
return 1;

case EVP_PKEY_CTRL_DIGESTINIT:
case EVP_PKEY_CTRL_PKCS7_SIGN:
#ifndef OPENSSL_NO_CMS
Expand Down
10 changes: 10 additions & 0 deletions doc/man1/openssl-pkeyutl.pod.in
Expand Up @@ -272,6 +272,16 @@ explicitly set in PSS mode then the signing digest is used.
Sets the digest used for the OAEP hash function. If not explicitly set then
SHA1 is used.

=item B<rsa_pkcs1_implicit_rejection:>I<flag>

Disables (when set to 0) or enables (when set to 1) the use of implicit
rejection with PKCS#1 v1.5 decryption. When enabled (the default), as a
protection against Bleichenbacher attack, the library will generate a
deterministic random plaintext that it will return to the caller in case
of padding check failure.
When disabled, it's the callers' responsibility to handle the returned
errors in a side-channel free manner.

=back

=head1 RSA-PSS ALGORITHM
Expand Down
2 changes: 2 additions & 0 deletions doc/man3/EVP_PKEY_CTX_ctrl.pod
Expand Up @@ -399,6 +399,8 @@ instead of padding errors in case padding checks fail. Applications that
want to remain secure while using earlier versions of OpenSSL, still need to
handle both the error code from the RSA decryption operation and the
returned message in a side channel secure manner.
This protection against Bleichenbacher attacks can be disabled by setting
the OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION (an unsigned integer) to 0.

=head2 DSA parameters

Expand Down
9 changes: 9 additions & 0 deletions doc/man7/provider-asym_cipher.pod
Expand Up @@ -235,6 +235,15 @@ The TLS protocol version first requested by the client.

The negotiated TLS protocol version.

=item "implicit-rejection" (B<OSSL_PKEY_PARAM_IMPLICIT_REJECTION>) <unsigned integer>

Gets of sets the use of the implicit rejection mechanism for RSA PKCS#1 v1.5
decryption. When set (non zero value), the decryption API will return
a deterministically random value if the PKCS#1 v1.5 padding check fails.
This makes explotation of the Bleichenbacher significantly harder, even
if the code using the RSA decryption API is not implemented in side-channel
free manner. Set by default.

=back

OSSL_FUNC_asym_cipher_gettable_ctx_params() and OSSL_FUNC_asym_cipher_settable_ctx_params()
Expand Down
2 changes: 2 additions & 0 deletions include/openssl/core_names.h
Expand Up @@ -302,6 +302,7 @@ extern "C" {
#define OSSL_PKEY_PARAM_DIST_ID "distid"
#define OSSL_PKEY_PARAM_PUB_KEY "pub"
#define OSSL_PKEY_PARAM_PRIV_KEY "priv"
#define OSSL_PKEY_PARAM_IMPLICIT_REJECTION "implicit-rejection"

/* Diffie-Hellman/DSA Parameters */
#define OSSL_PKEY_PARAM_FFC_P "p"
Expand Down Expand Up @@ -482,6 +483,7 @@ extern "C" {
#define OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL "oaep-label"
#define OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION "tls-client-version"
#define OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION "tls-negotiated-version"
#define OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION "implicit-rejection"

/*
* Encoder / decoder parameters
Expand Down
5 changes: 5 additions & 0 deletions include/openssl/rsa.h
Expand Up @@ -189,6 +189,8 @@ int EVP_PKEY_CTX_get0_rsa_oaep_label(EVP_PKEY_CTX *ctx, unsigned char **label);

# define EVP_PKEY_CTRL_RSA_KEYGEN_PRIMES (EVP_PKEY_ALG_CTRL + 13)

# define EVP_PKEY_CTRL_RSA_IMPLICIT_REJECTION (EVP_PKEY_ALG_CTRL + 14)

# define RSA_PKCS1_PADDING 1
# define RSA_NO_PADDING 3
# define RSA_PKCS1_OAEP_PADDING 4
Expand All @@ -198,6 +200,9 @@ int EVP_PKEY_CTX_get0_rsa_oaep_label(EVP_PKEY_CTX *ctx, unsigned char **label);
# define RSA_PKCS1_PSS_PADDING 6
# define RSA_PKCS1_WITH_TLS_PADDING 7

/* internal RSA_ only */
# define RSA_PKCS1_NO_IMPLICIT_REJECT_PADDING 8

# define RSA_PKCS1_PADDING_SIZE 11

# define RSA_set_app_data(s,arg) RSA_set_ex_data(s,0,arg)
Expand Down
26 changes: 24 additions & 2 deletions providers/implementations/asymciphers/rsa_enc.c
Expand Up @@ -75,6 +75,8 @@ typedef struct {
/* TLS padding */
unsigned int client_version;
unsigned int alt_version;
/* PKCS#1 v1.5 decryption mode */
unsigned int implicit_rejection;
} PROV_RSA_CTX;

static void *rsa_newctx(void *provctx)
Expand Down Expand Up @@ -107,6 +109,7 @@ static int rsa_init(void *vprsactx, void *vrsa, const OSSL_PARAM params[],
RSA_free(prsactx->rsa);
prsactx->rsa = vrsa;
prsactx->operation = operation;
prsactx->implicit_rejection = 1;

switch (RSA_test_flags(prsactx->rsa, RSA_FLAG_TYPE_MASK)) {
case RSA_FLAG_TYPE_RSA:
Expand Down Expand Up @@ -195,6 +198,7 @@ static int rsa_decrypt(void *vprsactx, unsigned char *out, size_t *outlen,
{
PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
int ret;
int pad_mode;
size_t len = RSA_size(prsactx->rsa);

if (!ossl_prov_is_running())
Expand Down Expand Up @@ -270,8 +274,12 @@ static int rsa_decrypt(void *vprsactx, unsigned char *out, size_t *outlen,
}
OPENSSL_free(tbuf);
} else {
ret = RSA_private_decrypt(inlen, in, out, prsactx->rsa,
prsactx->pad_mode);
if ((prsactx->implicit_rejection == 0) &&
(prsactx->pad_mode == RSA_PKCS1_PADDING))
pad_mode = RSA_PKCS1_NO_IMPLICIT_REJECT_PADDING;
else
pad_mode = prsactx->pad_mode;
ret = RSA_private_decrypt(inlen, in, out, prsactx->rsa, pad_mode);
}
*outlen = constant_time_select_s(constant_time_msb_s(ret), *outlen, ret);
ret = constant_time_select_int(constant_time_msb(ret), 0, 1);
Expand Down Expand Up @@ -395,6 +403,10 @@ static int rsa_get_ctx_params(void *vprsactx, OSSL_PARAM *params)
if (p != NULL && !OSSL_PARAM_set_uint(p, prsactx->alt_version))
return 0;

p = OSSL_PARAM_locate(params, OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION);
if (p != NULL && !OSSL_PARAM_set_uint(p, prsactx->implicit_rejection))
return 0;

return 1;
}

Expand All @@ -406,6 +418,7 @@ static const OSSL_PARAM known_gettable_ctx_params[] = {
NULL, 0),
OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION, NULL),
OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION, NULL),
OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION, NULL),
OSSL_PARAM_END
};

Expand Down Expand Up @@ -543,6 +556,14 @@ static int rsa_set_ctx_params(void *vprsactx, const OSSL_PARAM params[])
return 0;
prsactx->alt_version = alt_version;
}
p = OSSL_PARAM_locate_const(params, OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION);
if (p != NULL) {
unsigned int implicit_rejection;

if (!OSSL_PARAM_get_uint(p, &implicit_rejection))
return 0;
prsactx->implicit_rejection = implicit_rejection;
}

return 1;
}
Expand All @@ -555,6 +576,7 @@ static const OSSL_PARAM known_settable_ctx_params[] = {
OSSL_PARAM_octet_string(OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL, NULL, 0),
OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION, NULL),
OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION, NULL),
OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION, NULL),
OSSL_PARAM_END
};

Expand Down

0 comments on commit 5ab3ec1

Please sign in to comment.