rsa: Add option to disable implicit rejection
authorHubert Kario <hkario@redhat.com>
Thu, 27 Oct 2022 17:16:58 +0000 (19:16 +0200)
committerTomas Mraz <tomas@openssl.org>
Mon, 12 Dec 2022 10:30:52 +0000 (11:30 +0100)
Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
Reviewed-by: Tim Hudson <tjh@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/13817)

crypto/cms/cms_env.c
crypto/evp/ctrl_params_translate.c
crypto/rsa/rsa_ossl.c
crypto/rsa/rsa_pmeth.c
doc/man1/openssl-pkeyutl.pod.in
doc/man3/EVP_PKEY_CTX_ctrl.pod
doc/man7/provider-asym_cipher.pod
include/openssl/core_names.h
include/openssl/rsa.h
providers/implementations/asymciphers/rsa_enc.c

index d25504a03f74785b6e64fe9c5c296325d3b2b3b9..c55511011f6aefc708b32b84de0a768511fcc803 100644 (file)
@@ -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)
index 56ed5ea6d68c0e2a165f6b32ad747b437767191e..f64c1fcb2acaccb9d01d02c54e4202cb36874cf6 100644 (file)
@@ -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 },
index 2b25dad89350949e343fa92cbc1b5228d9312dfc..094a6632b66734588be3b80bfad81ba1bdf9e707 100644 (file)
@@ -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);
@@ -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
@@ -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);
index 8b35e5c3c6d6506c6f71f4f92e02f0ce0053be27..c67b20baf5672c4b8afe4e40cf86814ae8f612f7 100644 (file)
@@ -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 */
@@ -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;
@@ -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);
@@ -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
@@ -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);
@@ -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
index b7c45caa23fda7b32fd27cb694ef5e14189fc6e0..dd878297987220c00879426b73e9c2e87d17f5c8 100644 (file)
@@ -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
index 12026174a586d72002429291a84f2bbb2b09d096..f7957e95f7fa11ccd6426e6ecbbdc83ada469e2a 100644 (file)
@@ -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
 
index ac3f6271969deeff403e386e29878f18ef73a951..cb770c9e857c0d39a78b7e2d03f0651d70cb61ac 100644 (file)
@@ -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()
index e6c4758a33e22aa9629214432dac6588b656a9ac..6e4a4f8539da2d6465a8d3bfe51be091bd7dadbf 100644 (file)
@@ -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"
@@ -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
index bce2125822763c514011c72e5c44f7a66b0fe776..167427d3c486634f89e0611d601471ec522013c6 100644 (file)
@@ -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
@@ -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)
index 3d331ea8dfdc0e2f535bd04a05b46f48145f0a73..fbafb84f8cb6cb11bb06b2b107d2b24d1b40222d 100644 (file)
@@ -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)
@@ -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:
@@ -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())
@@ -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);
@@ -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;
 }
 
@@ -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
 };
 
@@ -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;
 }
@@ -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
 };