RT3066: rewrite RSA padding checks to be slightly more constant time.
Also tweak s3_cbc.c to use new constant-time methods. Also fix memory leaks from internal errors in RSA_padding_check_PKCS1_OAEP_mgf1 This patch is based on the original RT submission by Adam Langley <agl@chromium.org>, as well as code from BoringSSL and OpenSSL. Reviewed-by: Kurt Roeckx <kurt@openssl.org>
This commit is contained in:
parent
51b7be8d5f
commit
294d1e36c2
@ -54,7 +54,7 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The following methods return a bitmask of all ones (0xff...f) for true
|
* The boolean methods return a bitmask of all ones (0xff...f) for true
|
||||||
* and 0 for false. This is useful for choosing a value based on the result
|
* and 0 for false. This is useful for choosing a value based on the result
|
||||||
* of a conditional in constant time. For example,
|
* of a conditional in constant time. For example,
|
||||||
*
|
*
|
||||||
@ -67,7 +67,7 @@ extern "C" {
|
|||||||
* can be written as
|
* can be written as
|
||||||
*
|
*
|
||||||
* unsigned int lt = constant_time_lt(a, b);
|
* unsigned int lt = constant_time_lt(a, b);
|
||||||
* c = a & lt | b & ~lt;
|
* c = constant_time_select(lt, a, b);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -107,6 +107,21 @@ static inline unsigned int constant_time_eq(unsigned int a, unsigned int b);
|
|||||||
/* Convenience method for getting an 8-bit mask. */
|
/* Convenience method for getting an 8-bit mask. */
|
||||||
static inline unsigned char constant_time_eq_8(unsigned int a, unsigned int b);
|
static inline unsigned char constant_time_eq_8(unsigned int a, unsigned int b);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns (mask & a) | (~mask & b).
|
||||||
|
*
|
||||||
|
* When |mask| is all 1s or all 0s (as returned by the methods above),
|
||||||
|
* the select methods return either |a| (if |mask| is nonzero) or |b|
|
||||||
|
* (if |mask| is zero).
|
||||||
|
*/
|
||||||
|
static inline unsigned int constant_time_select(unsigned int mask,
|
||||||
|
unsigned int a, unsigned int b);
|
||||||
|
/* Convenience method for unsigned chars. */
|
||||||
|
static inline unsigned char constant_time_select_8(unsigned char mask,
|
||||||
|
unsigned char a, unsigned char b);
|
||||||
|
/* Convenience method for signed integers. */
|
||||||
|
static inline int constant_time_select_int(unsigned int mask, int a, int b);
|
||||||
|
|
||||||
static inline unsigned int constant_time_msb(unsigned int a)
|
static inline unsigned int constant_time_msb(unsigned int a)
|
||||||
{
|
{
|
||||||
return (unsigned int)((int)(a) >> (sizeof(int) * 8 - 1));
|
return (unsigned int)((int)(a) >> (sizeof(int) * 8 - 1));
|
||||||
@ -162,6 +177,23 @@ static inline unsigned char constant_time_eq_8(unsigned int a, unsigned int b)
|
|||||||
return (unsigned char)(constant_time_eq(a, b));
|
return (unsigned char)(constant_time_eq(a, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline unsigned int constant_time_select(unsigned int mask,
|
||||||
|
unsigned int a, unsigned int b)
|
||||||
|
{
|
||||||
|
return (mask & a) | (~mask & b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned char constant_time_select_8(unsigned char mask,
|
||||||
|
unsigned char a, unsigned char b)
|
||||||
|
{
|
||||||
|
return (unsigned char)(constant_time_select(mask, a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int constant_time_select_int(unsigned int mask, int a, int b)
|
||||||
|
{
|
||||||
|
return (int)(constant_time_select(mask, (unsigned)(a), (unsigned)(b)));
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -50,9 +50,9 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
static const unsigned int CONSTTIME_TRUE = ~0;
|
static const unsigned int CONSTTIME_TRUE = (unsigned)(~0);
|
||||||
static const unsigned int CONSTTIME_FALSE = 0;
|
static const unsigned int CONSTTIME_FALSE = 0;
|
||||||
static const unsigned char CONSTTIME_TRUE_8 = ~0;
|
static const unsigned char CONSTTIME_TRUE_8 = 0xff;
|
||||||
static const unsigned char CONSTTIME_FALSE_8 = 0;
|
static const unsigned char CONSTTIME_FALSE_8 = 0;
|
||||||
|
|
||||||
static int test_binary_op(unsigned int (*op)(unsigned int a, unsigned int b),
|
static int test_binary_op(unsigned int (*op)(unsigned int a, unsigned int b),
|
||||||
@ -133,13 +133,86 @@ static int test_is_zero_8(unsigned int a)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int test_select(unsigned int a, unsigned int b)
|
||||||
|
{
|
||||||
|
unsigned int selected = constant_time_select(CONSTTIME_TRUE, a, b);
|
||||||
|
if (selected != a)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Test failed for constant_time_select(%du, %du,"
|
||||||
|
"%du): expected %du(first value), got %du\n",
|
||||||
|
CONSTTIME_TRUE, a, b, a, selected);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
selected = constant_time_select(CONSTTIME_FALSE, a, b);
|
||||||
|
if (selected != b)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Test failed for constant_time_select(%du, %du,"
|
||||||
|
"%du): expected %du(second value), got %du\n",
|
||||||
|
CONSTTIME_FALSE, a, b, b, selected);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_select_8(unsigned char a, unsigned char b)
|
||||||
|
{
|
||||||
|
unsigned char selected = constant_time_select_8(CONSTTIME_TRUE_8, a, b);
|
||||||
|
if (selected != a)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Test failed for constant_time_select(%u, %u,"
|
||||||
|
"%u): expected %u(first value), got %u\n",
|
||||||
|
CONSTTIME_TRUE, a, b, a, selected);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
selected = constant_time_select_8(CONSTTIME_FALSE_8, a, b);
|
||||||
|
if (selected != b)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Test failed for constant_time_select(%u, %u,"
|
||||||
|
"%u): expected %u(second value), got %u\n",
|
||||||
|
CONSTTIME_FALSE, a, b, b, selected);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_select_int(int a, int b)
|
||||||
|
{
|
||||||
|
int selected = constant_time_select_int(CONSTTIME_TRUE, a, b);
|
||||||
|
if (selected != a)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Test failed for constant_time_select(%du, %d,"
|
||||||
|
"%d): expected %d(first value), got %d\n",
|
||||||
|
CONSTTIME_TRUE, a, b, a, selected);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
selected = constant_time_select_int(CONSTTIME_FALSE, a, b);
|
||||||
|
if (selected != b)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Test failed for constant_time_select(%du, %d,"
|
||||||
|
"%d): expected %d(second value), got %d\n",
|
||||||
|
CONSTTIME_FALSE, a, b, b, selected);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static unsigned int test_values[] = {0, 1, 1024, 12345, 32000, UINT_MAX/2-1,
|
static unsigned int test_values[] = {0, 1, 1024, 12345, 32000, UINT_MAX/2-1,
|
||||||
UINT_MAX/2, UINT_MAX/2+1, UINT_MAX-1,
|
UINT_MAX/2, UINT_MAX/2+1, UINT_MAX-1,
|
||||||
UINT_MAX};
|
UINT_MAX};
|
||||||
|
|
||||||
|
static unsigned char test_values_8[] = {0, 1, 2, 20, 32, 127, 128, 129, 255};
|
||||||
|
|
||||||
|
static int signed_test_values[] = {0, 1, -1, 1024, -1024, 12345, -12345,
|
||||||
|
32000, -32000, INT_MAX, INT_MIN, INT_MAX-1,
|
||||||
|
INT_MIN+1};
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
unsigned int a, b, i, j;
|
unsigned int a, b, i, j;
|
||||||
|
int c, d;
|
||||||
|
unsigned char e, f;
|
||||||
int num_failed = 0, num_all = 0;
|
int num_failed = 0, num_all = 0;
|
||||||
fprintf(stdout, "Testing constant time operations...\n");
|
fprintf(stdout, "Testing constant time operations...\n");
|
||||||
|
|
||||||
@ -148,20 +221,8 @@ int main(int argc, char *argv[])
|
|||||||
a = test_values[i];
|
a = test_values[i];
|
||||||
num_failed += test_is_zero(a);
|
num_failed += test_is_zero(a);
|
||||||
num_failed += test_is_zero_8(a);
|
num_failed += test_is_zero_8(a);
|
||||||
num_failed += test_binary_op(&constant_time_lt,
|
num_all += 2;
|
||||||
"constant_time_lt", a, a, 0);
|
for (j = 0; j < sizeof(test_values)/sizeof(int); ++j)
|
||||||
num_failed += test_binary_op_8(&constant_time_lt_8,
|
|
||||||
"constant_time_lt_8", a, a, 0);
|
|
||||||
num_failed += test_binary_op(&constant_time_ge,
|
|
||||||
"constant_time_ge", a, a, 1);
|
|
||||||
num_failed += test_binary_op_8(&constant_time_ge_8,
|
|
||||||
"constant_time_ge_8", a, a, 1);
|
|
||||||
num_failed += test_binary_op(&constant_time_eq,
|
|
||||||
"constant_time_eq", a, a, 1);
|
|
||||||
num_failed += test_binary_op_8(&constant_time_eq_8,
|
|
||||||
"constant_time_eq_8", a, a, 1);
|
|
||||||
num_all += 8;
|
|
||||||
for (j = i + 1; j < sizeof(test_values)/sizeof(int); ++j)
|
|
||||||
{
|
{
|
||||||
b = test_values[j];
|
b = test_values[j];
|
||||||
num_failed += test_binary_op(&constant_time_lt,
|
num_failed += test_binary_op(&constant_time_lt,
|
||||||
@ -188,7 +249,30 @@ int main(int argc, char *argv[])
|
|||||||
"constant_time_eq", b, a, b == a);
|
"constant_time_eq", b, a, b == a);
|
||||||
num_failed += test_binary_op_8(&constant_time_eq_8,
|
num_failed += test_binary_op_8(&constant_time_eq_8,
|
||||||
"constant_time_eq_8", b, a, b == a);
|
"constant_time_eq_8", b, a, b == a);
|
||||||
num_all += 12;
|
num_failed += test_select(a, b);
|
||||||
|
num_all += 13;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(signed_test_values)/sizeof(int); ++i)
|
||||||
|
{
|
||||||
|
c = signed_test_values[i];
|
||||||
|
for (j = 0; j < sizeof(signed_test_values)/sizeof(int); ++j)
|
||||||
|
{
|
||||||
|
d = signed_test_values[j];
|
||||||
|
num_failed += test_select_int(c, d);
|
||||||
|
num_all += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(test_values_8); ++i)
|
||||||
|
{
|
||||||
|
e = test_values_8[i];
|
||||||
|
for (j = 0; j < sizeof(test_values_8); ++j)
|
||||||
|
{
|
||||||
|
f = test_values_8[j];
|
||||||
|
num_failed += test_select_8(e, f);
|
||||||
|
num_all += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +206,7 @@ rsa_oaep.o: ../../include/openssl/opensslv.h ../../include/openssl/ossl_typ.h
|
|||||||
rsa_oaep.o: ../../include/openssl/rand.h ../../include/openssl/rsa.h
|
rsa_oaep.o: ../../include/openssl/rand.h ../../include/openssl/rsa.h
|
||||||
rsa_oaep.o: ../../include/openssl/safestack.h ../../include/openssl/sha.h
|
rsa_oaep.o: ../../include/openssl/safestack.h ../../include/openssl/sha.h
|
||||||
rsa_oaep.o: ../../include/openssl/stack.h ../../include/openssl/symhacks.h
|
rsa_oaep.o: ../../include/openssl/stack.h ../../include/openssl/symhacks.h
|
||||||
rsa_oaep.o: ../cryptlib.h rsa_oaep.c
|
rsa_oaep.o: ../constant_time_locl.h ../cryptlib.h rsa_oaep.c
|
||||||
rsa_pk1.o: ../../e_os.h ../../include/openssl/asn1.h
|
rsa_pk1.o: ../../e_os.h ../../include/openssl/asn1.h
|
||||||
rsa_pk1.o: ../../include/openssl/bio.h ../../include/openssl/bn.h
|
rsa_pk1.o: ../../include/openssl/bio.h ../../include/openssl/bn.h
|
||||||
rsa_pk1.o: ../../include/openssl/buffer.h ../../include/openssl/crypto.h
|
rsa_pk1.o: ../../include/openssl/buffer.h ../../include/openssl/crypto.h
|
||||||
@ -215,7 +215,8 @@ rsa_pk1.o: ../../include/openssl/lhash.h ../../include/openssl/opensslconf.h
|
|||||||
rsa_pk1.o: ../../include/openssl/opensslv.h ../../include/openssl/ossl_typ.h
|
rsa_pk1.o: ../../include/openssl/opensslv.h ../../include/openssl/ossl_typ.h
|
||||||
rsa_pk1.o: ../../include/openssl/rand.h ../../include/openssl/rsa.h
|
rsa_pk1.o: ../../include/openssl/rand.h ../../include/openssl/rsa.h
|
||||||
rsa_pk1.o: ../../include/openssl/safestack.h ../../include/openssl/stack.h
|
rsa_pk1.o: ../../include/openssl/safestack.h ../../include/openssl/stack.h
|
||||||
rsa_pk1.o: ../../include/openssl/symhacks.h ../cryptlib.h rsa_pk1.c
|
rsa_pk1.o: ../../include/openssl/symhacks.h ../constant_time_locl.h
|
||||||
|
rsa_pk1.o: ../cryptlib.h rsa_pk1.c
|
||||||
rsa_pmeth.o: ../../e_os.h ../../include/openssl/asn1.h
|
rsa_pmeth.o: ../../e_os.h ../../include/openssl/asn1.h
|
||||||
rsa_pmeth.o: ../../include/openssl/asn1t.h ../../include/openssl/bio.h
|
rsa_pmeth.o: ../../include/openssl/asn1t.h ../../include/openssl/bio.h
|
||||||
rsa_pmeth.o: ../../include/openssl/bn.h ../../include/openssl/buffer.h
|
rsa_pmeth.o: ../../include/openssl/bn.h ../../include/openssl/buffer.h
|
||||||
|
@ -616,6 +616,7 @@ void ERR_load_RSA_strings(void);
|
|||||||
#define RSA_R_OAEP_DECODING_ERROR 121
|
#define RSA_R_OAEP_DECODING_ERROR 121
|
||||||
#define RSA_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE 148
|
#define RSA_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE 148
|
||||||
#define RSA_R_PADDING_CHECK_FAILED 114
|
#define RSA_R_PADDING_CHECK_FAILED 114
|
||||||
|
#define RSA_R_PKCS_DECODING_ERROR 159
|
||||||
#define RSA_R_P_NOT_PRIME 128
|
#define RSA_R_P_NOT_PRIME 128
|
||||||
#define RSA_R_Q_NOT_PRIME 129
|
#define RSA_R_Q_NOT_PRIME 129
|
||||||
#define RSA_R_RSA_OPERATIONS_NOT_SUPPORTED 130
|
#define RSA_R_RSA_OPERATIONS_NOT_SUPPORTED 130
|
||||||
@ -624,7 +625,7 @@ void ERR_load_RSA_strings(void);
|
|||||||
#define RSA_R_SSLV3_ROLLBACK_ATTACK 115
|
#define RSA_R_SSLV3_ROLLBACK_ATTACK 115
|
||||||
#define RSA_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD 116
|
#define RSA_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD 116
|
||||||
#define RSA_R_UNKNOWN_ALGORITHM_TYPE 117
|
#define RSA_R_UNKNOWN_ALGORITHM_TYPE 117
|
||||||
#define RSA_R_UNKNOWN_DIGEST 159
|
#define RSA_R_UNKNOWN_DIGEST 166
|
||||||
#define RSA_R_UNKNOWN_MASK_DIGEST 151
|
#define RSA_R_UNKNOWN_MASK_DIGEST 151
|
||||||
#define RSA_R_UNKNOWN_PADDING_TYPE 118
|
#define RSA_R_UNKNOWN_PADDING_TYPE 118
|
||||||
#define RSA_R_UNKNOWN_PSS_DIGEST 152
|
#define RSA_R_UNKNOWN_PSS_DIGEST 152
|
||||||
|
@ -181,6 +181,7 @@ static ERR_STRING_DATA RSA_str_reasons[]=
|
|||||||
{ERR_REASON(RSA_R_OAEP_DECODING_ERROR) ,"oaep decoding error"},
|
{ERR_REASON(RSA_R_OAEP_DECODING_ERROR) ,"oaep decoding error"},
|
||||||
{ERR_REASON(RSA_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE),"operation not supported for this keytype"},
|
{ERR_REASON(RSA_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE),"operation not supported for this keytype"},
|
||||||
{ERR_REASON(RSA_R_PADDING_CHECK_FAILED) ,"padding check failed"},
|
{ERR_REASON(RSA_R_PADDING_CHECK_FAILED) ,"padding check failed"},
|
||||||
|
{ERR_REASON(RSA_R_PKCS_DECODING_ERROR) ,"pkcs decoding error"},
|
||||||
{ERR_REASON(RSA_R_P_NOT_PRIME) ,"p not prime"},
|
{ERR_REASON(RSA_R_P_NOT_PRIME) ,"p not prime"},
|
||||||
{ERR_REASON(RSA_R_Q_NOT_PRIME) ,"q not prime"},
|
{ERR_REASON(RSA_R_Q_NOT_PRIME) ,"q not prime"},
|
||||||
{ERR_REASON(RSA_R_RSA_OPERATIONS_NOT_SUPPORTED),"rsa operations not supported"},
|
{ERR_REASON(RSA_R_RSA_OPERATIONS_NOT_SUPPORTED),"rsa operations not supported"},
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#define OPENSSL_FIPSAPI
|
#define OPENSSL_FIPSAPI
|
||||||
|
|
||||||
|
#include "../constant_time_locl.h"
|
||||||
|
|
||||||
#if !defined(OPENSSL_NO_SHA) && !defined(OPENSSL_NO_SHA1)
|
#if !defined(OPENSSL_NO_SHA) && !defined(OPENSSL_NO_SHA1)
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -121,12 +122,13 @@ int RSA_padding_check_PKCS1_OAEP_mgf1(unsigned char *to, int tlen,
|
|||||||
const unsigned char *param, int plen,
|
const unsigned char *param, int plen,
|
||||||
const EVP_MD *md, const EVP_MD *mgf1md)
|
const EVP_MD *md, const EVP_MD *mgf1md)
|
||||||
{
|
{
|
||||||
int i, dblen, mlen = -1;
|
int i, dblen, mlen = -1, one_index = 0, msg_index;
|
||||||
const unsigned char *maskeddb;
|
unsigned int good, found_one_byte;
|
||||||
int lzero;
|
const unsigned char *maskedseed, *maskeddb;
|
||||||
unsigned char *db = NULL, seed[EVP_MAX_MD_SIZE], phash[EVP_MAX_MD_SIZE];
|
/* |em| is the encoded message, zero-padded to exactly |num| bytes:
|
||||||
unsigned char *padded_from;
|
* em = Y || maskedSeed || maskedDB */
|
||||||
int bad = 0;
|
unsigned char *db = NULL, *em = NULL, seed[EVP_MAX_MD_SIZE],
|
||||||
|
phash[EVP_MAX_MD_SIZE];
|
||||||
int mdlen;
|
int mdlen;
|
||||||
|
|
||||||
if (md == NULL)
|
if (md == NULL)
|
||||||
@ -136,85 +138,108 @@ int RSA_padding_check_PKCS1_OAEP_mgf1(unsigned char *to, int tlen,
|
|||||||
|
|
||||||
mdlen = EVP_MD_size(md);
|
mdlen = EVP_MD_size(md);
|
||||||
|
|
||||||
if (--num < 2 * mdlen + 1)
|
if (tlen <= 0 || flen <= 0)
|
||||||
/* 'num' is the length of the modulus, i.e. does not depend on the
|
return -1;
|
||||||
* particular ciphertext. */
|
/*
|
||||||
|
* |num| is the length of the modulus; |flen| is the length of the
|
||||||
|
* encoded message. Therefore, for any |from| that was obtained by
|
||||||
|
* decrypting a ciphertext, we must have |flen| <= |num|. Similarly,
|
||||||
|
* num < 2 * mdlen + 2 must hold for the modulus irrespective of
|
||||||
|
* the ciphertext, see PKCS #1 v2.2, section 7.1.2.
|
||||||
|
* This does not leak any side-channel information.
|
||||||
|
*/
|
||||||
|
if (num < flen || num < 2 * mdlen + 2)
|
||||||
goto decoding_err;
|
goto decoding_err;
|
||||||
|
|
||||||
lzero = num - flen;
|
dblen = num - mdlen - 1;
|
||||||
if (lzero < 0)
|
db = OPENSSL_malloc(dblen);
|
||||||
{
|
em = OPENSSL_malloc(num);
|
||||||
/* signalling this error immediately after detection might allow
|
if (db == NULL || em == NULL)
|
||||||
* for side-channel attacks (e.g. timing if 'plen' is huge
|
|
||||||
* -- cf. James H. Manger, "A Chosen Ciphertext Attack on RSA Optimal
|
|
||||||
* Asymmetric Encryption Padding (OAEP) [...]", CRYPTO 2001),
|
|
||||||
* so we use a 'bad' flag */
|
|
||||||
bad = 1;
|
|
||||||
lzero = 0;
|
|
||||||
flen = num; /* don't overflow the memcpy to padded_from */
|
|
||||||
}
|
|
||||||
|
|
||||||
dblen = num - mdlen;
|
|
||||||
db = OPENSSL_malloc(dblen + num);
|
|
||||||
if (db == NULL)
|
|
||||||
{
|
{
|
||||||
RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP_MGF1, ERR_R_MALLOC_FAILURE);
|
RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP_MGF1, ERR_R_MALLOC_FAILURE);
|
||||||
return -1;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Always do this zero-padding copy (even when lzero == 0)
|
/*
|
||||||
* to avoid leaking timing info about the value of lzero. */
|
* Always do this zero-padding copy (even when num == flen) to avoid
|
||||||
padded_from = db + dblen;
|
* leaking that information. The copy still leaks some side-channel
|
||||||
memset(padded_from, 0, lzero);
|
* information, but it's impossible to have a fixed memory access
|
||||||
memcpy(padded_from + lzero, from, flen);
|
* pattern since we can't read out of the bounds of |from|.
|
||||||
|
*
|
||||||
|
* TODO(emilia): Consider porting BN_bn2bin_padded from BoringSSL.
|
||||||
|
*/
|
||||||
|
memset(em, 0, num);
|
||||||
|
memcpy(em + num - flen, from, flen);
|
||||||
|
|
||||||
maskeddb = padded_from + mdlen;
|
/*
|
||||||
|
* The first byte must be zero, however we must not leak if this is
|
||||||
|
* true. See James H. Manger, "A Chosen Ciphertext Attack on RSA
|
||||||
|
* Optimal Asymmetric Encryption Padding (OAEP) [...]", CRYPTO 2001).
|
||||||
|
*/
|
||||||
|
good = constant_time_is_zero(em[0]);
|
||||||
|
|
||||||
|
maskedseed = em + 1;
|
||||||
|
maskeddb = em + 1 + mdlen;
|
||||||
|
|
||||||
if (PKCS1_MGF1(seed, mdlen, maskeddb, dblen, mgf1md))
|
if (PKCS1_MGF1(seed, mdlen, maskeddb, dblen, mgf1md))
|
||||||
return -1;
|
goto cleanup;
|
||||||
for (i = 0; i < mdlen; i++)
|
for (i = 0; i < mdlen; i++)
|
||||||
seed[i] ^= padded_from[i];
|
seed[i] ^= maskedseed[i];
|
||||||
|
|
||||||
if (PKCS1_MGF1(db, dblen, seed, mdlen, mgf1md))
|
if (PKCS1_MGF1(db, dblen, seed, mdlen, mgf1md))
|
||||||
return -1;
|
goto cleanup;
|
||||||
for (i = 0; i < dblen; i++)
|
for (i = 0; i < dblen; i++)
|
||||||
db[i] ^= maskeddb[i];
|
db[i] ^= maskeddb[i];
|
||||||
|
|
||||||
if (!EVP_Digest((void *)param, plen, phash, NULL, md, NULL))
|
if (!EVP_Digest((void *)param, plen, phash, NULL, md, NULL))
|
||||||
return -1;
|
goto cleanup;
|
||||||
|
|
||||||
if (CRYPTO_memcmp(db, phash, mdlen) != 0 || bad)
|
good &= constant_time_is_zero(CRYPTO_memcmp(db, phash, mdlen));
|
||||||
goto decoding_err;
|
|
||||||
else
|
found_one_byte = 0;
|
||||||
{
|
|
||||||
for (i = mdlen; i < dblen; i++)
|
for (i = mdlen; i < dblen; i++)
|
||||||
if (db[i] != 0x00)
|
|
||||||
break;
|
|
||||||
if (i == dblen || db[i] != 0x01)
|
|
||||||
goto decoding_err;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
/* everything looks OK */
|
/* Padding consists of a number of 0-bytes, followed by a 1. */
|
||||||
|
unsigned int equals1 = constant_time_eq(db[i], 1);
|
||||||
|
unsigned int equals0 = constant_time_is_zero(db[i]);
|
||||||
|
one_index = constant_time_select_int(~found_one_byte & equals1,
|
||||||
|
i, one_index);
|
||||||
|
found_one_byte |= equals1;
|
||||||
|
good &= (found_one_byte | equals0);
|
||||||
|
}
|
||||||
|
|
||||||
|
good &= found_one_byte;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At this point |good| is zero unless the plaintext was valid,
|
||||||
|
* so plaintext-awareness ensures timing side-channels are no longer a
|
||||||
|
* concern.
|
||||||
|
*/
|
||||||
|
if (!good)
|
||||||
|
goto decoding_err;
|
||||||
|
|
||||||
|
msg_index = one_index + 1;
|
||||||
|
mlen = dblen - msg_index;
|
||||||
|
|
||||||
mlen = dblen - ++i;
|
|
||||||
if (tlen < mlen)
|
if (tlen < mlen)
|
||||||
{
|
{
|
||||||
RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP_MGF1, RSA_R_DATA_TOO_LARGE);
|
RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP_MGF1, RSA_R_DATA_TOO_LARGE);
|
||||||
mlen = -1;
|
mlen = -1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
memcpy(to, db + i, mlen);
|
{
|
||||||
|
memcpy(to, db + msg_index, mlen);
|
||||||
|
goto cleanup;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
OPENSSL_free(db);
|
|
||||||
return mlen;
|
|
||||||
|
|
||||||
decoding_err:
|
decoding_err:
|
||||||
/* to avoid chosen ciphertext attacks, the error message should not reveal
|
/* To avoid chosen ciphertext attacks, the error message should not reveal
|
||||||
* which kind of decoding error happened */
|
* which kind of decoding error happened. */
|
||||||
RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP_MGF1, RSA_R_OAEP_DECODING_ERROR);
|
RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP_MGF1, RSA_R_OAEP_DECODING_ERROR);
|
||||||
|
cleanup:
|
||||||
if (db != NULL) OPENSSL_free(db);
|
if (db != NULL) OPENSSL_free(db);
|
||||||
return -1;
|
if (em != NULL) OPENSSL_free(em);
|
||||||
|
return mlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PKCS1_MGF1(unsigned char *mask, long len,
|
int PKCS1_MGF1(unsigned char *mask, long len,
|
||||||
|
@ -58,6 +58,8 @@
|
|||||||
|
|
||||||
#define OPENSSL_FIPSAPI
|
#define OPENSSL_FIPSAPI
|
||||||
|
|
||||||
|
#include "../constant_time_locl.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "cryptlib.h"
|
#include "cryptlib.h"
|
||||||
#include <openssl/bn.h>
|
#include <openssl/bn.h>
|
||||||
@ -183,44 +185,87 @@ int RSA_padding_add_PKCS1_type_2(unsigned char *to, int tlen,
|
|||||||
int RSA_padding_check_PKCS1_type_2(unsigned char *to, int tlen,
|
int RSA_padding_check_PKCS1_type_2(unsigned char *to, int tlen,
|
||||||
const unsigned char *from, int flen, int num)
|
const unsigned char *from, int flen, int num)
|
||||||
{
|
{
|
||||||
int i,j;
|
int i;
|
||||||
const unsigned char *p;
|
/* |em| is the encoded message, zero-padded to exactly |num| bytes */
|
||||||
|
unsigned char *em = NULL;
|
||||||
|
unsigned int good, found_zero_byte;
|
||||||
|
int zero_index = 0, msg_index, mlen = -1;
|
||||||
|
|
||||||
p=from;
|
if (tlen < 0 || flen < 0)
|
||||||
if ((num != (flen+1)) || (*(p++) != 02))
|
return -1;
|
||||||
|
|
||||||
|
/* PKCS#1 v1.5 decryption. See "PKCS #1 v2.2: RSA Cryptography
|
||||||
|
* Standard", section 7.2.2. */
|
||||||
|
|
||||||
|
if (flen > num)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (num < 11)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
em = OPENSSL_malloc(num);
|
||||||
|
if (em == NULL)
|
||||||
{
|
{
|
||||||
RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_TYPE_2,RSA_R_BLOCK_TYPE_IS_NOT_02);
|
RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_TYPE_2, ERR_R_MALLOC_FAILURE);
|
||||||
return(-1);
|
return -1;
|
||||||
}
|
}
|
||||||
#ifdef PKCS1_CHECK
|
memset(em, 0, num);
|
||||||
return(num-11);
|
/*
|
||||||
#endif
|
* Always do this zero-padding copy (even when num == flen) to avoid
|
||||||
|
* leaking that information. The copy still leaks some side-channel
|
||||||
|
* information, but it's impossible to have a fixed memory access
|
||||||
|
* pattern since we can't read out of the bounds of |from|.
|
||||||
|
*
|
||||||
|
* TODO(emilia): Consider porting BN_bn2bin_padded from BoringSSL.
|
||||||
|
*/
|
||||||
|
memcpy(em + num - flen, from, flen);
|
||||||
|
|
||||||
/* scan over padding data */
|
good = constant_time_is_zero(em[0]);
|
||||||
j=flen-1; /* one for type. */
|
good &= constant_time_eq(em[1], 2);
|
||||||
for (i=0; i<j; i++)
|
|
||||||
if (*(p++) == 0) break;
|
|
||||||
|
|
||||||
if (i == j)
|
found_zero_byte = 0;
|
||||||
|
for (i = 2; i < num; i++)
|
||||||
{
|
{
|
||||||
RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_TYPE_2,RSA_R_NULL_BEFORE_BLOCK_MISSING);
|
unsigned int equals0 = constant_time_is_zero(em[i]);
|
||||||
return(-1);
|
zero_index = constant_time_select_int(~found_zero_byte & equals0, i, zero_index);
|
||||||
|
found_zero_byte |= equals0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i < 8)
|
/*
|
||||||
|
* PS must be at least 8 bytes long, and it starts two bytes into |em|.
|
||||||
|
* If we never found a 0-byte, then |zero_index| is 0 and the check
|
||||||
|
* also fails.
|
||||||
|
*/
|
||||||
|
good &= constant_time_ge((unsigned int)(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;
|
||||||
|
mlen = num - msg_index;
|
||||||
|
|
||||||
|
/* For good measure, do this check in constant time as well; it could
|
||||||
|
* leak something if |tlen| was assuming valid padding. */
|
||||||
|
good &= constant_time_ge((unsigned int)(tlen), (unsigned int)(mlen));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We can't continue in constant-time because we need to copy the result
|
||||||
|
* and we cannot fake its length. This unavoidably leaks timing
|
||||||
|
* information at the API boundary.
|
||||||
|
* TODO(emilia): this could be addressed at the call site,
|
||||||
|
* see BoringSSL commit 0aa0767340baf925bda4804882aab0cb974b2d26.
|
||||||
|
*/
|
||||||
|
if (!good)
|
||||||
{
|
{
|
||||||
RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_TYPE_2,RSA_R_BAD_PAD_BYTE_COUNT);
|
mlen = -1;
|
||||||
return(-1);
|
goto err;
|
||||||
}
|
|
||||||
i++; /* Skip over the '\0' */
|
|
||||||
j-=i;
|
|
||||||
if (j > tlen)
|
|
||||||
{
|
|
||||||
RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_TYPE_2,RSA_R_DATA_TOO_LARGE);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
memcpy(to,p,(unsigned int)j);
|
|
||||||
|
|
||||||
return(j);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memcpy(to, em + msg_index, mlen);
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (em != NULL)
|
||||||
|
OPENSSL_free(em);
|
||||||
|
if (mlen == -1)
|
||||||
|
RSAerr(RSA_F_RSA_PADDING_CHECK_PKCS1_TYPE_2, RSA_R_PKCS_DECODING_ERROR);
|
||||||
|
return mlen;
|
||||||
|
}
|
||||||
|
@ -94,7 +94,7 @@ int ssl3_cbc_remove_padding(const SSL* s,
|
|||||||
/* SSLv3 requires that the padding is minimal. */
|
/* SSLv3 requires that the padding is minimal. */
|
||||||
good &= constant_time_ge(block_size, padding_length+1);
|
good &= constant_time_ge(block_size, padding_length+1);
|
||||||
rec->length -= good & (padding_length+1);
|
rec->length -= good & (padding_length+1);
|
||||||
return (int)((good & 1) | (~good & -1));
|
return constant_time_select_int(good, 1, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* tls1_cbc_remove_padding removes the CBC padding from the decrypted, TLS, CBC
|
/* tls1_cbc_remove_padding removes the CBC padding from the decrypted, TLS, CBC
|
||||||
@ -190,7 +190,7 @@ int tls1_cbc_remove_padding(const SSL* s,
|
|||||||
good = constant_time_eq(0xff, good & 0xff);
|
good = constant_time_eq(0xff, good & 0xff);
|
||||||
rec->length -= good & (padding_length+1);
|
rec->length -= good & (padding_length+1);
|
||||||
|
|
||||||
return (int)((good & 1) | (~good & -1));
|
return constant_time_select_int(good, 1, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ssl3_cbc_copy_mac copies |md_size| bytes from the end of |rec| to |out| in
|
/* ssl3_cbc_copy_mac copies |md_size| bytes from the end of |rec| to |out| in
|
||||||
@ -650,7 +650,7 @@ void ssl3_cbc_digest_record(
|
|||||||
/* If this is the block containing the end of the
|
/* If this is the block containing the end of the
|
||||||
* application data, and we are at the offset for the
|
* application data, and we are at the offset for the
|
||||||
* 0x80 value, then overwrite b with 0x80. */
|
* 0x80 value, then overwrite b with 0x80. */
|
||||||
b = (b&~is_past_c) | (0x80&is_past_c);
|
b = constant_time_select_8(is_past_c, 0x80, b);
|
||||||
/* If this the the block containing the end of the
|
/* If this the the block containing the end of the
|
||||||
* application data and we're past the 0x80 value then
|
* application data and we're past the 0x80 value then
|
||||||
* just write zero. */
|
* just write zero. */
|
||||||
@ -666,7 +666,8 @@ void ssl3_cbc_digest_record(
|
|||||||
if (j >= md_block_size - md_length_size)
|
if (j >= md_block_size - md_length_size)
|
||||||
{
|
{
|
||||||
/* If this is index_b, write a length byte. */
|
/* If this is index_b, write a length byte. */
|
||||||
b = (b&~is_block_b) | (is_block_b&length_bytes[j-(md_block_size-md_length_size)]);
|
b = constant_time_select_8(
|
||||||
|
is_block_b, length_bytes[j-(md_block_size-md_length_size)], b);
|
||||||
}
|
}
|
||||||
block[j] = b;
|
block[j] = b;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user