diff --git a/ChangeLog b/ChangeLog index 2bdaba0b5..3454268c9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,6 +6,9 @@ Security * Fix possible client-side NULL pointer dereference (read) when the client tries to continue the handshake after it failed (a misuse of the API). (Found by GDS Labs using afl-fuzz.) + * Add countermeasure against Lenstra's RSA-CRT attack for PKCS#1 v1.5 + signatures. (Found by Florian Weimer, Red Hat.) + https://securityblog.redhat.com/2015/09/02/factoring-rsa-keys-with-tls-perfect-forward-secrecy/ Bugfix * Fix unused function warning when using MBEDTLS_MDx_ALT or diff --git a/library/rsa.c b/library/rsa.c index 6297250ae..9fe62d103 100644 --- a/library/rsa.c +++ b/library/rsa.c @@ -919,6 +919,11 @@ int rsa_rsassa_pkcs1_v15_sign( rsa_context *ctx, { size_t nb_pad, olen; unsigned char *p = sig; + unsigned char *sig_try = NULL, *verif = NULL; + size_t i; + unsigned char diff; + volatile unsigned char diff_no_optimize; + int ret; if( ctx->padding != RSA_PKCS_V15 ) return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); @@ -1021,9 +1026,39 @@ int rsa_rsassa_pkcs1_v15_sign( rsa_context *ctx, return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); } - return( ( mode == RSA_PUBLIC ) - ? rsa_public( ctx, sig, sig ) - : rsa_private( ctx, f_rng, p_rng, sig, sig ) ); + if( mode == RSA_PUBLIC ) + return( rsa_public( ctx, sig, sig ) ); + + /* + * In order to prevent Lenstra's attack, make the signature in a + * temporary buffer and check it before returning it. + */ + sig_try = malloc( ctx->len ); + verif = malloc( ctx->len ); + if( sig_try == NULL || verif == NULL ) + return( POLARSSL_ERR_MPI_MALLOC_FAILED ); + + MPI_CHK( rsa_private( ctx, f_rng, p_rng, sig, sig_try ) ); + MPI_CHK( rsa_public( ctx, sig_try, verif ) ); + + /* Compare in constant time just in case */ + for( diff = 0, i = 0; i < ctx->len; i++ ) + diff |= verif[i] ^ sig[i]; + diff_no_optimize = diff; + + if( diff_no_optimize != 0 ) + { + ret = POLARSSL_ERR_RSA_PRIVATE_FAILED; + goto cleanup; + } + + memcpy( sig, sig_try, ctx->len ); + +cleanup: + free( sig_try ); + free( verif ); + + return( ret ); } /*