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
#include "internal/constant_time.h"
#include <stdio.h>
-#include "internal/cryptlib.h"
#include <openssl/bn.h>
#include <openssl/rsa.h>
#include <openssl/rand.h>
+/* Just for the SSL_MAX_MASTER_KEY_LENGTH value */
+#include <openssl/ssl.h>
+#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)
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;
+}
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
#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
# 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
# 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
{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),
#include <openssl/rsa.h>
#include <openssl/params.h>
#include <openssl/err.h>
+/* Just for SSL_MAX_MASTER_KEY_LENGTH */
+#include <openssl/ssl.h>
#include "internal/constant_time.h"
+#include "crypto/rsa.h"
#include "prov/providercommonerr.h"
#include "prov/provider_ctx.h"
#include "prov/implementations.h"
/* 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)
{
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 (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 (len == 0) {
- ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY);
+ 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,
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;
}
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
};
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;
}
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
};