Fix padding side-channels.
This patch tweaks the OAEP padding check to be slightly more constant time and rewrites the PKCS#1 v1.5 padding check to the same end.
This commit is contained in:
parent
f71a27920a
commit
7d0a1d680c
@ -188,10 +188,35 @@ int RSA_padding_add_PKCS1_type_2(uint8_t *to, unsigned tlen,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* constant_time_byte_eq returns 1 if x == y and 0 otherwise. */
|
||||||
|
static int constant_time_byte_eq(unsigned char a, unsigned char b) {
|
||||||
|
unsigned char z = ~(a ^ b);
|
||||||
|
z &= z >> 4;
|
||||||
|
z &= z >> 2;
|
||||||
|
z &= z >> 1;
|
||||||
|
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* constant_time_select returns x if v is 1 and y if v is 0.
|
||||||
|
* Its behavior is undefined if v takes any other value. */
|
||||||
|
static int constant_time_select(int v, int x, int y) {
|
||||||
|
return ((~(v - 1)) & x) | ((v - 1) & y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* constant_time_le returns 1 if x < y and 0 otherwise.
|
||||||
|
* x and y must be positive. */
|
||||||
|
static int constant_time_le(int x, int y) {
|
||||||
|
return ((x - y - 1) >> (sizeof(int) * 8 - 1)) & 1;
|
||||||
|
}
|
||||||
|
|
||||||
int RSA_padding_check_PKCS1_type_2(uint8_t *to, unsigned tlen, const uint8_t *from,
|
int RSA_padding_check_PKCS1_type_2(uint8_t *to, unsigned tlen, const uint8_t *from,
|
||||||
unsigned flen, unsigned num) {
|
unsigned flen, unsigned num) {
|
||||||
unsigned i, j;
|
int i;
|
||||||
const uint8_t *p;
|
unsigned char *em = NULL;
|
||||||
|
int ret = -1;
|
||||||
|
int first_byte_is_zero, second_byte_is_two, looking_for_index;
|
||||||
|
int valid_index, zero_index = 0, msg_index;
|
||||||
|
|
||||||
if (flen == 0) {
|
if (flen == 0) {
|
||||||
OPENSSL_PUT_ERROR(RSA, RSA_padding_check_PKCS1_type_2,
|
OPENSSL_PUT_ERROR(RSA, RSA_padding_check_PKCS1_type_2,
|
||||||
@ -199,42 +224,65 @@ int RSA_padding_check_PKCS1_type_2(uint8_t *to, unsigned tlen, const uint8_t *fr
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
p = from;
|
/* PKCS#1 v1.5 decryption. See "PKCS #1 v2.2: RSA Cryptography
|
||||||
if ((num != (flen + 1)) || (*(p++) != 2)) {
|
* Standard", section 7.2.2. */
|
||||||
|
|
||||||
|
if (flen > num) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num < 11) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
em = OPENSSL_malloc(num);
|
||||||
|
if (em == NULL) {
|
||||||
OPENSSL_PUT_ERROR(RSA, RSA_padding_check_PKCS1_type_2,
|
OPENSSL_PUT_ERROR(RSA, RSA_padding_check_PKCS1_type_2,
|
||||||
RSA_R_BLOCK_TYPE_IS_NOT_02);
|
ERR_R_MALLOC_FAILURE);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* scan over padding data */
|
memset(em, 0, num);
|
||||||
j = flen - 1; /* one for type. */
|
/* This unavoidably leaks timing information about |flen| because we
|
||||||
for (i = 0; i < j; i++) {
|
* cannot have a constant memory access pattern without accessing
|
||||||
if (*(p++) == 0) {
|
* outside the bounds of |from|. */
|
||||||
break;
|
memcpy(em + num - flen, from, flen);
|
||||||
}
|
|
||||||
|
first_byte_is_zero = constant_time_byte_eq(em[0], 0);
|
||||||
|
second_byte_is_two = constant_time_byte_eq(em[1], 2);
|
||||||
|
|
||||||
|
looking_for_index = 1;
|
||||||
|
for (i = 2; i < num; i++) {
|
||||||
|
int equals0 = constant_time_byte_eq(em[i], 0);
|
||||||
|
zero_index =
|
||||||
|
constant_time_select(looking_for_index & equals0, i, zero_index);
|
||||||
|
looking_for_index = constant_time_select(equals0, 0, looking_for_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == j) {
|
/* PS must be at least 8 bytes long, and it starts two bytes into |em|. */
|
||||||
OPENSSL_PUT_ERROR(RSA, RSA_padding_check_PKCS1_type_2,
|
valid_index = constant_time_le(2 + 8, zero_index);
|
||||||
RSA_R_NULL_BEFORE_BLOCK_MISSING);
|
/* Skip the zero byte. */
|
||||||
return -1;
|
msg_index = zero_index + 1;
|
||||||
|
valid_index &= constant_time_le(num - msg_index, tlen);
|
||||||
|
|
||||||
|
if (!(first_byte_is_zero & second_byte_is_two & ~looking_for_index &
|
||||||
|
valid_index)) {
|
||||||
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i < 8) {
|
ret = num - msg_index;
|
||||||
OPENSSL_PUT_ERROR(RSA, RSA_padding_check_PKCS1_type_2,
|
memcpy(to, &em[msg_index], ret);
|
||||||
RSA_R_BAD_PAD_BYTE_COUNT);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
i++; /* Skip over the '\0' */
|
|
||||||
j -= i;
|
|
||||||
if (j > tlen) {
|
|
||||||
OPENSSL_PUT_ERROR(RSA, RSA_padding_check_PKCS1_type_2,
|
|
||||||
RSA_R_DATA_TOO_LARGE);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
memcpy(to, p, (unsigned int)j);
|
|
||||||
|
|
||||||
return j;
|
err:
|
||||||
|
if (em != NULL) {
|
||||||
|
OPENSSL_free(em);
|
||||||
|
}
|
||||||
|
if (ret == -1) {
|
||||||
|
OPENSSL_PUT_ERROR(RSA, RSA_padding_check_PKCS1_type_2,
|
||||||
|
RSA_R_PKCS_DECODING_ERROR);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int RSA_padding_add_none(uint8_t *to, unsigned tlen, const uint8_t *from, unsigned flen) {
|
int RSA_padding_add_none(uint8_t *to, unsigned tlen, const uint8_t *from, unsigned flen) {
|
||||||
|
@ -455,5 +455,6 @@ struct rsa_st {
|
|||||||
#define RSA_R_KEY_SIZE_TOO_SMALL 131
|
#define RSA_R_KEY_SIZE_TOO_SMALL 131
|
||||||
#define RSA_R_BAD_SIGNATURE 132
|
#define RSA_R_BAD_SIGNATURE 132
|
||||||
#define RSA_R_BN_NOT_INITIALIZED 133
|
#define RSA_R_BN_NOT_INITIALIZED 133
|
||||||
|
#define RSA_R_PKCS_DECODING_ERROR 134
|
||||||
|
|
||||||
#endif /* OPENSSL_HEADER_RSA_H */
|
#endif /* OPENSSL_HEADER_RSA_H */
|
||||||
|
@ -69,6 +69,7 @@ const ERR_STRING_DATA RSA_error_string_data[] = {
|
|||||||
{ERR_PACK(ERR_LIB_RSA, 0, RSA_R_OAEP_DECODING_ERROR), "OAEP_DECODING_ERROR"},
|
{ERR_PACK(ERR_LIB_RSA, 0, RSA_R_OAEP_DECODING_ERROR), "OAEP_DECODING_ERROR"},
|
||||||
{ERR_PACK(ERR_LIB_RSA, 0, RSA_R_OUTPUT_BUFFER_TOO_SMALL), "OUTPUT_BUFFER_TOO_SMALL"},
|
{ERR_PACK(ERR_LIB_RSA, 0, RSA_R_OUTPUT_BUFFER_TOO_SMALL), "OUTPUT_BUFFER_TOO_SMALL"},
|
||||||
{ERR_PACK(ERR_LIB_RSA, 0, RSA_R_PADDING_CHECK_FAILED), "PADDING_CHECK_FAILED"},
|
{ERR_PACK(ERR_LIB_RSA, 0, RSA_R_PADDING_CHECK_FAILED), "PADDING_CHECK_FAILED"},
|
||||||
|
{ERR_PACK(ERR_LIB_RSA, 0, RSA_R_PKCS_DECODING_ERROR), "PKCS_DECODING_ERROR"},
|
||||||
{ERR_PACK(ERR_LIB_RSA, 0, RSA_R_SLEN_CHECK_FAILED), "SLEN_CHECK_FAILED"},
|
{ERR_PACK(ERR_LIB_RSA, 0, RSA_R_SLEN_CHECK_FAILED), "SLEN_CHECK_FAILED"},
|
||||||
{ERR_PACK(ERR_LIB_RSA, 0, RSA_R_SLEN_RECOVERY_FAILED), "SLEN_RECOVERY_FAILED"},
|
{ERR_PACK(ERR_LIB_RSA, 0, RSA_R_SLEN_RECOVERY_FAILED), "SLEN_RECOVERY_FAILED"},
|
||||||
{ERR_PACK(ERR_LIB_RSA, 0, RSA_R_SSLV3_ROLLBACK_ATTACK), "SSLV3_ROLLBACK_ATTACK"},
|
{ERR_PACK(ERR_LIB_RSA, 0, RSA_R_SSLV3_ROLLBACK_ATTACK), "SSLV3_ROLLBACK_ATTACK"},
|
||||||
|
Loading…
Reference in New Issue
Block a user