Fix Lucky 13 cache attack on MD/SHA padding

The basis for the Lucky 13 family of attacks is for an attacker to be able to
distinguish between (long) valid TLS-CBC padding and invalid TLS-CBC padding.
Since our code sets padlen = 0 for invalid padding, the length of the input to
the HMAC function gives information about that.

Information about this length (modulo the MD/SHA block size) can be deduced
from how much MD/SHA padding (this is distinct from TLS-CBC padding) is used.
If MD/SHA padding is read from a (static) buffer, a local attacker could get
information about how much is used via a cache attack targeting that buffer.

Let's get rid of this buffer. Now the only buffer used is the internal MD/SHA
one, which is always read fully by the process() function.
This commit is contained in:
Manuel Pégourié-Gonnard 2018-06-28 12:10:27 +02:00
parent 104d85865d
commit 1cc1fb0599
5 changed files with 141 additions and 81 deletions

View File

@ -16,6 +16,13 @@ Security
caused by a miscalculation (for SHA-384) in a countermeasure to the caused by a miscalculation (for SHA-384) in a countermeasure to the
original Lucky 13 attack. Found by Kenny Paterson, Eyal Ronen and Adi original Lucky 13 attack. Found by Kenny Paterson, Eyal Ronen and Adi
Shamir. Shamir.
* Fix a vulnerability in TLS ciphersuites based on CBC, in (D)TLS 1.0 to
1.2, that allowed a local attacker, able to execute code on the local
machine as well as manipulate network packets, to partially recover the
plaintext of messages under some conditions (see previous entry) by using
a cache attack targetting an internal MD/SHA buffer. Connections using
GCM or CCM instead of CBC or using Encrypt-then-Mac (RFC 7366) were not
affected. Found by Kenny Paterson, Eyal Ronen and Adi Shamir.
API Changes API Changes
* Extend the platform module with a util component that contains * Extend the platform module with a util component that contains

View File

@ -309,14 +309,6 @@ void mbedtls_md5_update( mbedtls_md5_context *ctx,
} }
#endif #endif
static const unsigned char md5_padding[64] =
{
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/* /*
* MD5 final digest * MD5 final digest
*/ */
@ -324,26 +316,48 @@ int mbedtls_md5_finish_ret( mbedtls_md5_context *ctx,
unsigned char output[16] ) unsigned char output[16] )
{ {
int ret; int ret;
uint32_t last, padn; uint32_t used;
uint32_t high, low; uint32_t high, low;
unsigned char msglen[8];
/*
* Add padding: 0x80 then 0x00 until 8 bytes remain for the length
*/
used = ctx->total[0] & 0x3F;
ctx->buffer[used++] = 0x80;
if( used <= 56 )
{
/* Enough room for padding + length in current block */
memset( ctx->buffer + used, 0, 56 - used );
}
else
{
/* We'll need an extra block */
memset( ctx->buffer + used, 0, 64 - used );
if( ( ret = mbedtls_internal_md5_process( ctx, ctx->buffer ) ) != 0 )
return( ret );
memset( ctx->buffer, 0, 56 );
}
/*
* Add message length
*/
high = ( ctx->total[0] >> 29 ) high = ( ctx->total[0] >> 29 )
| ( ctx->total[1] << 3 ); | ( ctx->total[1] << 3 );
low = ( ctx->total[0] << 3 ); low = ( ctx->total[0] << 3 );
PUT_UINT32_LE( low, msglen, 0 ); PUT_UINT32_LE( low, ctx->buffer, 56 );
PUT_UINT32_LE( high, msglen, 4 ); PUT_UINT32_LE( high, ctx->buffer, 60 );
last = ctx->total[0] & 0x3F; if( ( ret = mbedtls_internal_md5_process( ctx, ctx->buffer ) ) != 0 )
padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); return( ret );
if( ( ret = mbedtls_md5_update_ret( ctx, md5_padding, padn ) ) != 0 )
return( ret );
if( ( ret = mbedtls_md5_update_ret( ctx, msglen, 8 ) ) != 0 )
return( ret );
/*
* Output final state
*/
PUT_UINT32_LE( ctx->state[0], output, 0 ); PUT_UINT32_LE( ctx->state[0], output, 0 );
PUT_UINT32_LE( ctx->state[1], output, 4 ); PUT_UINT32_LE( ctx->state[1], output, 4 );
PUT_UINT32_LE( ctx->state[2], output, 8 ); PUT_UINT32_LE( ctx->state[2], output, 8 );

View File

@ -342,14 +342,6 @@ void mbedtls_sha1_update( mbedtls_sha1_context *ctx,
} }
#endif #endif
static const unsigned char sha1_padding[64] =
{
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/* /*
* SHA-1 final digest * SHA-1 final digest
*/ */
@ -357,25 +349,48 @@ int mbedtls_sha1_finish_ret( mbedtls_sha1_context *ctx,
unsigned char output[20] ) unsigned char output[20] )
{ {
int ret; int ret;
uint32_t last, padn; uint32_t used;
uint32_t high, low; uint32_t high, low;
unsigned char msglen[8];
/*
* Add padding: 0x80 then 0x00 until 8 bytes remain for the length
*/
used = ctx->total[0] & 0x3F;
ctx->buffer[used++] = 0x80;
if( used <= 56 )
{
/* Enough room for padding + length in current block */
memset( ctx->buffer + used, 0, 56 - used );
}
else
{
/* We'll need an extra block */
memset( ctx->buffer + used, 0, 64 - used );
if( ( ret = mbedtls_internal_sha1_process( ctx, ctx->buffer ) ) != 0 )
return( ret );
memset( ctx->buffer, 0, 56 );
}
/*
* Add message length
*/
high = ( ctx->total[0] >> 29 ) high = ( ctx->total[0] >> 29 )
| ( ctx->total[1] << 3 ); | ( ctx->total[1] << 3 );
low = ( ctx->total[0] << 3 ); low = ( ctx->total[0] << 3 );
PUT_UINT32_BE( high, msglen, 0 ); PUT_UINT32_BE( high, ctx->buffer, 56 );
PUT_UINT32_BE( low, msglen, 4 ); PUT_UINT32_BE( low, ctx->buffer, 60 );
last = ctx->total[0] & 0x3F; if( ( ret = mbedtls_internal_sha1_process( ctx, ctx->buffer ) ) != 0 )
padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
if( ( ret = mbedtls_sha1_update_ret( ctx, sha1_padding, padn ) ) != 0 )
return( ret );
if( ( ret = mbedtls_sha1_update_ret( ctx, msglen, 8 ) ) != 0 )
return( ret ); return( ret );
/*
* Output final state
*/
PUT_UINT32_BE( ctx->state[0], output, 0 ); PUT_UINT32_BE( ctx->state[0], output, 0 );
PUT_UINT32_BE( ctx->state[1], output, 4 ); PUT_UINT32_BE( ctx->state[1], output, 4 );
PUT_UINT32_BE( ctx->state[2], output, 8 ); PUT_UINT32_BE( ctx->state[2], output, 8 );

View File

@ -311,14 +311,6 @@ void mbedtls_sha256_update( mbedtls_sha256_context *ctx,
} }
#endif #endif
static const unsigned char sha256_padding[64] =
{
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/* /*
* SHA-256 final digest * SHA-256 final digest
*/ */
@ -326,26 +318,48 @@ int mbedtls_sha256_finish_ret( mbedtls_sha256_context *ctx,
unsigned char output[32] ) unsigned char output[32] )
{ {
int ret; int ret;
uint32_t last, padn; uint32_t used;
uint32_t high, low; uint32_t high, low;
unsigned char msglen[8];
/*
* Add padding: 0x80 then 0x00 until 8 bytes remain for the length
*/
used = ctx->total[0] & 0x3F;
ctx->buffer[used++] = 0x80;
if( used <= 56 )
{
/* Enough room for padding + length in current block */
memset( ctx->buffer + used, 0, 56 - used );
}
else
{
/* We'll need an extra block */
memset( ctx->buffer + used, 0, 64 - used );
if( ( ret = mbedtls_internal_sha256_process( ctx, ctx->buffer ) ) != 0 )
return( ret );
memset( ctx->buffer, 0, 56 );
}
/*
* Add message length
*/
high = ( ctx->total[0] >> 29 ) high = ( ctx->total[0] >> 29 )
| ( ctx->total[1] << 3 ); | ( ctx->total[1] << 3 );
low = ( ctx->total[0] << 3 ); low = ( ctx->total[0] << 3 );
PUT_UINT32_BE( high, msglen, 0 ); PUT_UINT32_BE( high, ctx->buffer, 56 );
PUT_UINT32_BE( low, msglen, 4 ); PUT_UINT32_BE( low, ctx->buffer, 60 );
last = ctx->total[0] & 0x3F; if( ( ret = mbedtls_internal_sha256_process( ctx, ctx->buffer ) ) != 0 )
padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
if( ( ret = mbedtls_sha256_update_ret( ctx, sha256_padding, padn ) ) != 0 )
return( ret );
if( ( ret = mbedtls_sha256_update_ret( ctx, msglen, 8 ) ) != 0 )
return( ret ); return( ret );
/*
* Output final state
*/
PUT_UINT32_BE( ctx->state[0], output, 0 ); PUT_UINT32_BE( ctx->state[0], output, 0 );
PUT_UINT32_BE( ctx->state[1], output, 4 ); PUT_UINT32_BE( ctx->state[1], output, 4 );
PUT_UINT32_BE( ctx->state[2], output, 8 ); PUT_UINT32_BE( ctx->state[2], output, 8 );

View File

@ -341,18 +341,6 @@ void mbedtls_sha512_update( mbedtls_sha512_context *ctx,
} }
#endif #endif
static const unsigned char sha512_padding[128] =
{
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/* /*
* SHA-512 final digest * SHA-512 final digest
*/ */
@ -360,26 +348,48 @@ int mbedtls_sha512_finish_ret( mbedtls_sha512_context *ctx,
unsigned char output[64] ) unsigned char output[64] )
{ {
int ret; int ret;
size_t last, padn; unsigned used;
uint64_t high, low; uint64_t high, low;
unsigned char msglen[16];
/*
* Add padding: 0x80 then 0x00 until 16 bytes remain for the length
*/
used = ctx->total[0] & 0x7F;
ctx->buffer[used++] = 0x80;
if( used <= 112 )
{
/* Enough room for padding + length in current block */
memset( ctx->buffer + used, 0, 112 - used );
}
else
{
/* We'll need an extra block */
memset( ctx->buffer + used, 0, 128 - used );
if( ( ret = mbedtls_internal_sha512_process( ctx, ctx->buffer ) ) != 0 )
return( ret );
memset( ctx->buffer, 0, 112 );
}
/*
* Add message length
*/
high = ( ctx->total[0] >> 61 ) high = ( ctx->total[0] >> 61 )
| ( ctx->total[1] << 3 ); | ( ctx->total[1] << 3 );
low = ( ctx->total[0] << 3 ); low = ( ctx->total[0] << 3 );
PUT_UINT64_BE( high, msglen, 0 ); PUT_UINT64_BE( high, ctx->buffer, 112 );
PUT_UINT64_BE( low, msglen, 8 ); PUT_UINT64_BE( low, ctx->buffer, 120 );
last = (size_t)( ctx->total[0] & 0x7F ); if( ( ret = mbedtls_internal_sha512_process( ctx, ctx->buffer ) ) != 0 )
padn = ( last < 112 ) ? ( 112 - last ) : ( 240 - last ); return( ret );
if( ( ret = mbedtls_sha512_update_ret( ctx, sha512_padding, padn ) ) != 0 )
return( ret );
if( ( ret = mbedtls_sha512_update_ret( ctx, msglen, 16 ) ) != 0 )
return( ret );
/*
* Output final state
*/
PUT_UINT64_BE( ctx->state[0], output, 0 ); PUT_UINT64_BE( ctx->state[0], output, 0 );
PUT_UINT64_BE( ctx->state[1], output, 8 ); PUT_UINT64_BE( ctx->state[1], output, 8 );
PUT_UINT64_BE( ctx->state[2], output, 16 ); PUT_UINT64_BE( ctx->state[2], output, 16 );