diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index e81c32fe4f..4baed5c48e 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -2684,9 +2684,11 @@ PROV_R_AES_KEY_SETUP_FAILED:101:aes key setup failed PROV_R_BAD_DECRYPT:100:bad decrypt PROV_R_BAD_ENCODING:141:bad encoding PROV_R_BAD_LENGTH:142:bad length +PROV_R_BAD_TLS_CLIENT_VERSION:161:bad tls client version PROV_R_BN_ERROR:160:bn error PROV_R_BOTH_MODE_AND_MODE_INT:127:both mode and mode int PROV_R_CIPHER_OPERATION_FAILED:102:cipher operation failed +PROV_R_FAILED_TO_DECRYPT:162:failed to decrypt PROV_R_FAILED_TO_GENERATE_KEY:121:failed to generate key PROV_R_FAILED_TO_GET_PARAMETER:103:failed to get parameter PROV_R_FAILED_TO_SET_PARAMETER:104:failed to set parameter diff --git a/crypto/rsa/rsa_pk1.c b/crypto/rsa/rsa_pk1.c index 0c77422404..007e9b8cd5 100644 --- a/crypto/rsa/rsa_pk1.c +++ b/crypto/rsa/rsa_pk1.c @@ -10,10 +10,13 @@ #include "internal/constant_time.h" #include -#include "internal/cryptlib.h" #include #include #include +/* Just for the SSL_MAX_MASTER_KEY_LENGTH value */ +#include +#include "internal/cryptlib.h" +#include "crypto/rsa.h" int RSA_padding_add_PKCS1_type_1(unsigned char *to, int tlen, const unsigned char *from, int flen) @@ -253,3 +256,123 @@ int RSA_padding_check_PKCS1_type_2(unsigned char *to, int tlen, return constant_time_select_int(good, mlen, -1); } + +/* + * 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 + * in the buffer pointed to by |to| which should be |tlen| bytes long. |tlen| + * must be at least SSL_MAX_MASTER_KEY_LENGTH. The original decrypted message + * should be stored in |from| which must be |flen| bytes in length and padded + * such that |flen == RSA_size()|. The TLS protocol version that the client + * originally requested should be passed in |client_version|. Some buggy clients + * can exist which use the negotiated version instead of the originally + * requested protocol version. If it is necessary to work around this bug then + * the negotiated protocol version can be passed in |alt_version|, otherwise 0 + * should be passed. + * + * If the passed message is publicly invalid or some other error that can be + * treated in non-constant time occurs then -1 is returned. On success the + * length of the decrypted data is returned. This will always be + * SSL_MAX_MASTER_KEY_LENGTH. If an error occurs that should be treated in + * constant time then this function will appear to return successfully, but the + * decrypted data will be randomly generated (as per + * https://tools.ietf.org/html/rfc5246#section-7.4.7.1). + */ +int rsa_padding_check_PKCS1_type_2_TLS(unsigned char *to, size_t tlen, + const unsigned char *from, size_t flen, + int client_version, int alt_version) +{ + unsigned int i, good, version_good; + unsigned char rand_premaster_secret[SSL_MAX_MASTER_KEY_LENGTH]; + + /* + * If these checks fail then either the message in publicly invalid, or + * we've been called incorrectly. We can fail immediately. + */ + if (flen < RSA_PKCS1_PADDING_SIZE + SSL_MAX_MASTER_KEY_LENGTH + || tlen < SSL_MAX_MASTER_KEY_LENGTH) { + ERR_raise(ERR_LIB_RSA, RSA_R_PKCS_DECODING_ERROR); + return -1; + } + + /* + * Generate a random premaster secret to use in the event that we fail + * to decrypt. + */ + if (RAND_priv_bytes(rand_premaster_secret, + sizeof(rand_premaster_secret)) <= 0) { + ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR); + return -1; + } + + good = constant_time_is_zero(from[0]); + good &= constant_time_eq(from[1], 2); + + /* Check we have the expected padding data */ + for (i = 2; i < flen - SSL_MAX_MASTER_KEY_LENGTH - 1; i++) + good &= ~constant_time_is_zero_8(from[i]); + good &= constant_time_is_zero_8(from[flen - SSL_MAX_MASTER_KEY_LENGTH - 1]); + + + /* + * If the version in the decrypted pre-master secret is correct then + * version_good will be 0xff, otherwise it'll be zero. The + * Klima-Pokorny-Rosa extension of Bleichenbacher's attack + * (http://eprint.iacr.org/2003/052/) exploits the version number + * check as a "bad version oracle". Thus version checks are done in + * constant time and are treated like any other decryption error. + */ + version_good = + constant_time_eq(from[flen - SSL_MAX_MASTER_KEY_LENGTH], + (client_version >> 8) & 0xff); + version_good &= + constant_time_eq(from[flen - SSL_MAX_MASTER_KEY_LENGTH + 1], + client_version & 0xff); + + /* + * The premaster secret must contain the same version number as the + * ClientHello to detect version rollback attacks (strangely, the + * protocol does not offer such protection for DH ciphersuites). + * However, buggy clients exist that send the negotiated protocol + * version instead if the server does not support the requested + * protocol version. If SSL_OP_TLS_ROLLBACK_BUG is set then we tolerate + * such clients. In that case alt_version will be non-zero and set to + * the negotiated version. + */ + if (alt_version > 0) { + unsigned int workaround_good; + + workaround_good = + constant_time_eq(from[flen - SSL_MAX_MASTER_KEY_LENGTH], + (alt_version >> 8) & 0xff); + workaround_good &= + constant_time_eq(from[flen - SSL_MAX_MASTER_KEY_LENGTH + 1], + alt_version & 0xff); + version_good |= workaround_good; + } + + good &= version_good; + + + /* + * Now copy the result over to the to buffer if good, or random data if + * not good. + */ + for (i = 0; i < SSL_MAX_MASTER_KEY_LENGTH; i++) { + to[i] = + constant_time_select_8(good, + from[flen - SSL_MAX_MASTER_KEY_LENGTH + i], + rand_premaster_secret[i]); + } + + /* + * We must not leak whether a decryption failure occurs because of + * Bleichenbacher's attack on PKCS #1 v1.5 RSA padding (see RFC 2246, + * section 7.4.7.1). The code follows that advice of the TLS RFC and + * generates a random premaster secret for the case that the decrypt + * fails. See https://tools.ietf.org/html/rfc5246#section-7.4.7.1 + * So, whether we actually succeeded or not, return success. + */ + + return SSL_MAX_MASTER_KEY_LENGTH; +} diff --git a/include/crypto/rsa.h b/include/crypto/rsa.h index 6d2e7ffb53..29256371f6 100644 --- a/include/crypto/rsa.h +++ b/include/crypto/rsa.h @@ -18,4 +18,8 @@ int rsa_set0_all_params(RSA *r, const STACK_OF(BIGNUM) *primes, int rsa_get0_all_params(RSA *r, STACK_OF(BIGNUM_const) *primes, STACK_OF(BIGNUM_const) *exps, STACK_OF(BIGNUM_const) *coeffs); + +int rsa_padding_check_PKCS1_type_2_TLS(unsigned char *to, size_t tlen, + const unsigned char *from, size_t flen, + int client_version, int alt_version); #endif diff --git a/include/openssl/core_names.h b/include/openssl/core_names.h index 053432e0f0..e441ddf6c8 100644 --- a/include/openssl/core_names.h +++ b/include/openssl/core_names.h @@ -194,13 +194,15 @@ extern "C" { #define OSSL_SIGNATURE_PARAM_DIGEST_SIZE "digest-size" /* Asym cipher parameters */ -#define OSSL_ASYM_CIPHER_PARAM_PAD_MODE "pad-mode" -#define OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST OSSL_ALG_PARAM_DIGEST -#define OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST_PROPS "digest-props" -#define OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST "mgf1-digest" -#define OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST_PROPS "mgf1-digest-props" -#define OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL "oaep-label" -#define OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL_LEN "oaep-label-len" +#define OSSL_ASYM_CIPHER_PARAM_PAD_MODE "pad-mode" +#define OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST OSSL_ALG_PARAM_DIGEST +#define OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST_PROPS "digest-props" +#define OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST "mgf1-digest" +#define OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST_PROPS "mgf1-digest-props" +#define OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL "oaep-label" +#define OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL_LEN "oaep-label-len" +#define OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION "tls-client-version" +#define OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION "tls-negotiated-version" /* * Serializer parameters diff --git a/include/openssl/rsa.h b/include/openssl/rsa.h index 02a02160b3..9753c22c2c 100644 --- a/include/openssl/rsa.h +++ b/include/openssl/rsa.h @@ -182,13 +182,15 @@ 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 RSA_PKCS1_PADDING 1 -# define RSA_SSLV23_PADDING 2 -# define RSA_NO_PADDING 3 -# define RSA_PKCS1_OAEP_PADDING 4 -# define RSA_X931_PADDING 5 +# define RSA_PKCS1_PADDING 1 +# define RSA_SSLV23_PADDING 2 +# define RSA_NO_PADDING 3 +# define RSA_PKCS1_OAEP_PADDING 4 +# define RSA_X931_PADDING 5 + /* EVP_PKEY_ only */ -# define RSA_PKCS1_PSS_PADDING 6 +# define RSA_PKCS1_PSS_PADDING 6 +# define RSA_PKCS1_WITH_TLS_PADDING 7 # define RSA_PKCS1_PADDING_SIZE 11 diff --git a/providers/common/include/prov/providercommonerr.h b/providers/common/include/prov/providercommonerr.h index 85e5856d30..d97b735ca9 100644 --- a/providers/common/include/prov/providercommonerr.h +++ b/providers/common/include/prov/providercommonerr.h @@ -53,9 +53,11 @@ int ERR_load_PROV_strings(void); # define PROV_R_BAD_DECRYPT 100 # define PROV_R_BAD_ENCODING 141 # define PROV_R_BAD_LENGTH 142 +# define PROV_R_BAD_TLS_CLIENT_VERSION 161 # define PROV_R_BN_ERROR 160 # define PROV_R_BOTH_MODE_AND_MODE_INT 127 # define PROV_R_CIPHER_OPERATION_FAILED 102 +# define PROV_R_FAILED_TO_DECRYPT 162 # define PROV_R_FAILED_TO_GENERATE_KEY 121 # define PROV_R_FAILED_TO_GET_PARAMETER 103 # define PROV_R_FAILED_TO_SET_PARAMETER 104 diff --git a/providers/common/provider_err.c b/providers/common/provider_err.c index 7cae969c79..eba3dbef5f 100644 --- a/providers/common/provider_err.c +++ b/providers/common/provider_err.c @@ -19,11 +19,14 @@ static const ERR_STRING_DATA PROV_str_reasons[] = { {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_BAD_DECRYPT), "bad decrypt"}, {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_BAD_ENCODING), "bad encoding"}, {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_BAD_LENGTH), "bad length"}, + {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_BAD_TLS_CLIENT_VERSION), + "bad tls client version"}, {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_BN_ERROR), "bn error"}, {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_BOTH_MODE_AND_MODE_INT), "both mode and mode int"}, {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_CIPHER_OPERATION_FAILED), "cipher operation failed"}, + {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_FAILED_TO_DECRYPT), "failed to decrypt"}, {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_FAILED_TO_GENERATE_KEY), "failed to generate key"}, {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_FAILED_TO_GET_PARAMETER), diff --git a/providers/implementations/asymciphers/rsa_enc.c b/providers/implementations/asymciphers/rsa_enc.c index 9b17377783..53fc6de265 100644 --- a/providers/implementations/asymciphers/rsa_enc.c +++ b/providers/implementations/asymciphers/rsa_enc.c @@ -14,7 +14,10 @@ #include #include #include +/* Just for SSL_MAX_MASTER_KEY_LENGTH */ +#include #include "internal/constant_time.h" +#include "crypto/rsa.h" #include "prov/providercommonerr.h" #include "prov/provider_ctx.h" #include "prov/implementations.h" @@ -51,6 +54,9 @@ typedef struct { /* OAEP label */ unsigned char *oaep_label; size_t oaep_labellen; + /* TLS padding */ + unsigned int client_version; + unsigned int alt_version; } PROV_RSA_CTX; static void *rsa_newctx(void *provctx) @@ -130,38 +136,70 @@ static int rsa_decrypt(void *vprsactx, unsigned char *out, size_t *outlen, { PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; int ret; + size_t len = RSA_size(prsactx->rsa); - if (out == NULL) { - size_t len = RSA_size(prsactx->rsa); - - if (len == 0) { - ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY); + if (prsactx->pad_mode == RSA_PKCS1_WITH_TLS_PADDING) { + if (out == NULL) { + *outlen = SSL_MAX_MASTER_KEY_LENGTH; + return 1; + } + if (outsize < SSL_MAX_MASTER_KEY_LENGTH) { + ERR_raise(ERR_LIB_PROV, PROV_R_BAD_LENGTH); + return 0; + } + } else { + if (out == NULL) { + if (len == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY); + return 0; + } + *outlen = len; + return 1; + } + + if (outsize < len) { + ERR_raise(ERR_LIB_PROV, PROV_R_BAD_LENGTH); return 0; } - *outlen = len; - return 1; } - if (prsactx->pad_mode == RSA_PKCS1_OAEP_PADDING) { - int rsasize = RSA_size(prsactx->rsa); + if (prsactx->pad_mode == RSA_PKCS1_OAEP_PADDING + || prsactx->pad_mode == RSA_PKCS1_WITH_TLS_PADDING) { unsigned char *tbuf; - if ((tbuf = OPENSSL_malloc(rsasize)) == NULL) { + if ((tbuf = OPENSSL_malloc(len)) == NULL) { PROVerr(0, ERR_R_MALLOC_FAILURE); return 0; } ret = RSA_private_decrypt(inlen, in, tbuf, prsactx->rsa, RSA_NO_PADDING); - if (ret <= 0) { + /* + * With no padding then, on success ret should be len, otherwise an + * error occurred (non-constant time) + */ + if (ret != (int)len) { OPENSSL_free(tbuf); + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_DECRYPT); return 0; } - ret = RSA_padding_check_PKCS1_OAEP_mgf1(out, ret, tbuf, - ret, ret, - prsactx->oaep_label, - prsactx->oaep_labellen, - prsactx->oaep_md, - prsactx->mgf1_md); + if (prsactx->pad_mode == RSA_PKCS1_OAEP_PADDING) { + ret = RSA_padding_check_PKCS1_OAEP_mgf1(out, outsize, tbuf, + len, len, + prsactx->oaep_label, + prsactx->oaep_labellen, + prsactx->oaep_md, + prsactx->mgf1_md); + } else { + /* RSA_PKCS1_WITH_TLS_PADDING */ + if (prsactx->client_version <= 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_BAD_TLS_CLIENT_VERSION); + return 0; + } + ret = rsa_padding_check_PKCS1_type_2_TLS(out, outsize, + tbuf, len, + prsactx->client_version, + prsactx->alt_version); + } OPENSSL_free(tbuf); } else { ret = RSA_private_decrypt(inlen, in, out, prsactx->rsa, @@ -252,6 +290,14 @@ static int rsa_get_ctx_params(void *vprsactx, OSSL_PARAM *params) if (p != NULL && !OSSL_PARAM_set_size_t(p, prsactx->oaep_labellen)) return 0; + p = OSSL_PARAM_locate(params, OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION); + if (p != NULL && !OSSL_PARAM_set_uint(p, prsactx->client_version)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION); + if (p != NULL && !OSSL_PARAM_set_uint(p, prsactx->alt_version)) + return 0; + return 1; } @@ -262,6 +308,8 @@ static const OSSL_PARAM known_gettable_ctx_params[] = { OSSL_PARAM_DEFN(OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL, OSSL_PARAM_OCTET_PTR, NULL, 0), OSSL_PARAM_size_t(OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL_LEN, NULL), + OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION, NULL), + OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION, NULL), OSSL_PARAM_END }; @@ -354,6 +402,24 @@ static int rsa_set_ctx_params(void *vprsactx, const OSSL_PARAM params[]) prsactx->oaep_labellen = tmp_labellen; } + p = OSSL_PARAM_locate_const(params, OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION); + if (p != NULL) { + unsigned int client_version; + + if (!OSSL_PARAM_get_uint(p, &client_version)) + return 0; + prsactx->client_version = client_version; + } + + p = OSSL_PARAM_locate_const(params, OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION); + if (p != NULL) { + unsigned int alt_version; + + if (!OSSL_PARAM_get_uint(p, &alt_version)) + return 0; + prsactx->alt_version = alt_version; + } + return 1; } @@ -363,6 +429,8 @@ static const OSSL_PARAM known_settable_ctx_params[] = { OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST, NULL, 0), OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST_PROPS, NULL, 0), 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_END };