Merge pull request #3398 from gilles-peskine-arm/montmul-cmp-branch-development

Remove a secret-dependent branch in Montgomery multiplication
This commit is contained in:
Janos Follath 2020-06-09 12:40:51 +01:00 committed by GitHub
commit 3c4a46c44a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 111 additions and 43 deletions

View file

@ -0,0 +1,6 @@
Security
* Fix a side channel vulnerability in modular exponentiation that could
reveal an RSA private key used in a secure enclave. Noticed by Sangho Lee,
Ming-Wei Shih, Prasun Gera, Taesoo Kim and Hyesoon Kim (Georgia Institute
of Technology); and Marcus Peinado (Microsoft Research). Reported by Raoul
Strackx (Fortanix) in #3394.

View file

@ -243,6 +243,22 @@ void mbedtls_mpi_swap( mbedtls_mpi *X, mbedtls_mpi *Y )
memcpy( Y, &T, sizeof( mbedtls_mpi ) ); memcpy( Y, &T, sizeof( mbedtls_mpi ) );
} }
/*
* Conditionally assign dest = src, without leaking information
* about whether the assignment was made or not.
* dest and src must be arrays of limbs of size n.
* assign must be 0 or 1.
*/
static void mpi_safe_cond_assign( size_t n,
mbedtls_mpi_uint *dest,
const mbedtls_mpi_uint *src,
unsigned char assign )
{
size_t i;
for( i = 0; i < n; i++ )
dest[i] = dest[i] * ( 1 - assign ) + src[i] * assign;
}
/* /*
* Conditionally assign X = Y, without leaking information * Conditionally assign X = Y, without leaking information
* about whether the assignment was made or not. * about whether the assignment was made or not.
@ -262,10 +278,9 @@ int mbedtls_mpi_safe_cond_assign( mbedtls_mpi *X, const mbedtls_mpi *Y, unsigned
X->s = X->s * ( 1 - assign ) + Y->s * assign; X->s = X->s * ( 1 - assign ) + Y->s * assign;
for( i = 0; i < Y->n; i++ ) mpi_safe_cond_assign( Y->n, X->p, Y->p, assign );
X->p[i] = X->p[i] * ( 1 - assign ) + Y->p[i] * assign;
for( ; i < X->n; i++ ) for( i = Y->n; i < X->n; i++ )
X->p[i] *= ( 1 - assign ); X->p[i] *= ( 1 - assign );
cleanup: cleanup:
@ -1327,10 +1342,24 @@ cleanup:
return( ret ); return( ret );
} }
/* /**
* Helper for mbedtls_mpi subtraction * Helper for mbedtls_mpi subtraction.
*
* Calculate d - s where d and s have the same size.
* This function operates modulo (2^ciL)^n and returns the carry
* (1 if there was a wraparound, i.e. if `d < s`, and 0 otherwise).
*
* \param n Number of limbs of \p d and \p s.
* \param[in,out] d On input, the left operand.
* On output, the result of the subtraction:
* \param[in] s The right operand.
*
* \return 1 if `d < s`.
* 0 if `d >= s`.
*/ */
static void mpi_sub_hlp( size_t n, mbedtls_mpi_uint *s, mbedtls_mpi_uint *d ) static mbedtls_mpi_uint mpi_sub_hlp( size_t n,
mbedtls_mpi_uint *d,
const mbedtls_mpi_uint *s )
{ {
size_t i; size_t i;
mbedtls_mpi_uint c, z; mbedtls_mpi_uint c, z;
@ -1341,28 +1370,22 @@ static void mpi_sub_hlp( size_t n, mbedtls_mpi_uint *s, mbedtls_mpi_uint *d )
c = ( *d < *s ) + z; *d -= *s; c = ( *d < *s ) + z; *d -= *s;
} }
while( c != 0 ) return( c );
{
z = ( *d < c ); *d -= c;
c = z; d++;
}
} }
/* /*
* Unsigned subtraction: X = |A| - |B| (HAC 14.9) * Unsigned subtraction: X = |A| - |B| (HAC 14.9, 14.10)
*/ */
int mbedtls_mpi_sub_abs( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B ) int mbedtls_mpi_sub_abs( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B )
{ {
mbedtls_mpi TB; mbedtls_mpi TB;
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
size_t n; size_t n;
mbedtls_mpi_uint carry;
MPI_VALIDATE_RET( X != NULL ); MPI_VALIDATE_RET( X != NULL );
MPI_VALIDATE_RET( A != NULL ); MPI_VALIDATE_RET( A != NULL );
MPI_VALIDATE_RET( B != NULL ); MPI_VALIDATE_RET( B != NULL );
if( mbedtls_mpi_cmp_abs( A, B ) < 0 )
return( MBEDTLS_ERR_MPI_NEGATIVE_VALUE );
mbedtls_mpi_init( &TB ); mbedtls_mpi_init( &TB );
if( X == B ) if( X == B )
@ -1385,7 +1408,18 @@ int mbedtls_mpi_sub_abs( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi
if( B->p[n - 1] != 0 ) if( B->p[n - 1] != 0 )
break; break;
mpi_sub_hlp( n, B->p, X->p ); carry = mpi_sub_hlp( n, X->p, B->p );
if( carry != 0 )
{
/* Propagate the carry to the first nonzero limb of X. */
for( ; n < X->n && X->p[n] == 0; n++ )
--X->p[n];
/* If we ran out of space for the carry, it means that the result
* is negative. */
if( n == X->n )
return( MBEDTLS_ERR_MPI_NEGATIVE_VALUE );
--X->p[n];
}
cleanup: cleanup:
@ -1975,18 +2009,34 @@ static void mpi_montg_init( mbedtls_mpi_uint *mm, const mbedtls_mpi *N )
*mm = ~x + 1; *mm = ~x + 1;
} }
/* /** Montgomery multiplication: A = A * B * R^-1 mod N (HAC 14.36)
* Montgomery multiplication: A = A * B * R^-1 mod N (HAC 14.36) *
* \param[in,out] A One of the numbers to multiply.
* It must have at least as many limbs as N
* (A->n >= N->n), and any limbs beyond n are ignored.
* On successful completion, A contains the result of
* the multiplication A * B * R^-1 mod N where
* R = (2^ciL)^n.
* \param[in] B One of the numbers to multiply.
* It must be nonzero and must not have more limbs than N
* (B->n <= N->n).
* \param[in] N The modulo. N must be odd.
* \param mm The value calculated by `mpi_montg_init(&mm, N)`.
* This is -N^-1 mod 2^ciL.
* \param[in,out] T A bignum for temporary storage.
* It must be at least twice the limb size of N plus 2
* (T->n >= 2 * (N->n + 1)).
* Its initial content is unused and
* its final content is indeterminate.
* Note that unlike the usual convention in the library
* for `const mbedtls_mpi*`, the content of T can change.
*/ */
static int mpi_montmul( mbedtls_mpi *A, const mbedtls_mpi *B, const mbedtls_mpi *N, mbedtls_mpi_uint mm, static void mpi_montmul( mbedtls_mpi *A, const mbedtls_mpi *B, const mbedtls_mpi *N, mbedtls_mpi_uint mm,
const mbedtls_mpi *T ) const mbedtls_mpi *T )
{ {
size_t i, n, m; size_t i, n, m;
mbedtls_mpi_uint u0, u1, *d; mbedtls_mpi_uint u0, u1, *d;
if( T->n < N->n + 1 || T->p == NULL )
return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
memset( T->p, 0, T->n * ciL ); memset( T->p, 0, T->n * ciL );
d = T->p; d = T->p;
@ -2007,21 +2057,33 @@ static int mpi_montmul( mbedtls_mpi *A, const mbedtls_mpi *B, const mbedtls_mpi
*d++ = u0; d[n + 1] = 0; *d++ = u0; d[n + 1] = 0;
} }
memcpy( A->p, d, ( n + 1 ) * ciL ); /* At this point, d is either the desired result or the desired result
* plus N. We now potentially subtract N, avoiding leaking whether the
* subtraction is performed through side channels. */
if( mbedtls_mpi_cmp_abs( A, N ) >= 0 ) /* Copy the n least significant limbs of d to A, so that
mpi_sub_hlp( n, N->p, A->p ); * A = d if d < N (recall that N has n limbs). */
else memcpy( A->p, d, n * ciL );
/* prevent timing attacks */ /* If d >= N then we want to set A to d - N. To prevent timing attacks,
mpi_sub_hlp( n, A->p, T->p ); * do the calculation without using conditional tests. */
/* Set d to d0 + (2^biL)^n - N where d0 is the current value of d. */
return( 0 ); d[n] += 1;
d[n] -= mpi_sub_hlp( n, d, N->p );
/* If d0 < N then d < (2^biL)^n
* so d[n] == 0 and we want to keep A as it is.
* If d0 >= N then d >= (2^biL)^n, and d <= (2^biL)^n + N < 2 * (2^biL)^n
* so d[n] == 1 and we want to set A to the result of the subtraction
* which is d - (2^biL)^n, i.e. the n least significant limbs of d.
* This exactly corresponds to a conditional assignment. */
mpi_safe_cond_assign( n, A->p, d, (unsigned char) d[n] );
} }
/* /*
* Montgomery reduction: A = A * R^-1 mod N * Montgomery reduction: A = A * R^-1 mod N
*
* See mpi_montmul() regarding constraints and guarantees on the parameters.
*/ */
static int mpi_montred( mbedtls_mpi *A, const mbedtls_mpi *N, static void mpi_montred( mbedtls_mpi *A, const mbedtls_mpi *N,
mbedtls_mpi_uint mm, const mbedtls_mpi *T ) mbedtls_mpi_uint mm, const mbedtls_mpi *T )
{ {
mbedtls_mpi_uint z = 1; mbedtls_mpi_uint z = 1;
@ -2030,7 +2092,7 @@ static int mpi_montred( mbedtls_mpi *A, const mbedtls_mpi *N,
U.n = U.s = (int) z; U.n = U.s = (int) z;
U.p = &z; U.p = &z;
return( mpi_montmul( A, &U, N, mm, T ) ); mpi_montmul( A, &U, N, mm, T );
} }
/* /*
@ -2116,13 +2178,13 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi *X, const mbedtls_mpi *A,
else else
MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &W[1], A ) ); MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &W[1], A ) );
MBEDTLS_MPI_CHK( mpi_montmul( &W[1], &RR, N, mm, &T ) ); mpi_montmul( &W[1], &RR, N, mm, &T );
/* /*
* X = R^2 * R^-1 mod N = R mod N * X = R^2 * R^-1 mod N = R mod N
*/ */
MBEDTLS_MPI_CHK( mbedtls_mpi_copy( X, &RR ) ); MBEDTLS_MPI_CHK( mbedtls_mpi_copy( X, &RR ) );
MBEDTLS_MPI_CHK( mpi_montred( X, N, mm, &T ) ); mpi_montred( X, N, mm, &T );
if( wsize > 1 ) if( wsize > 1 )
{ {
@ -2135,7 +2197,7 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi *X, const mbedtls_mpi *A,
MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &W[j], &W[1] ) ); MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &W[j], &W[1] ) );
for( i = 0; i < wsize - 1; i++ ) for( i = 0; i < wsize - 1; i++ )
MBEDTLS_MPI_CHK( mpi_montmul( &W[j], &W[j], N, mm, &T ) ); mpi_montmul( &W[j], &W[j], N, mm, &T );
/* /*
* W[i] = W[i - 1] * W[1] * W[i] = W[i - 1] * W[1]
@ -2145,7 +2207,7 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi *X, const mbedtls_mpi *A,
MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &W[i], N->n + 1 ) ); MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &W[i], N->n + 1 ) );
MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &W[i], &W[i - 1] ) ); MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &W[i], &W[i - 1] ) );
MBEDTLS_MPI_CHK( mpi_montmul( &W[i], &W[1], N, mm, &T ) ); mpi_montmul( &W[i], &W[1], N, mm, &T );
} }
} }
@ -2182,7 +2244,7 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi *X, const mbedtls_mpi *A,
/* /*
* out of window, square X * out of window, square X
*/ */
MBEDTLS_MPI_CHK( mpi_montmul( X, X, N, mm, &T ) ); mpi_montmul( X, X, N, mm, &T );
continue; continue;
} }
@ -2200,12 +2262,12 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi *X, const mbedtls_mpi *A,
* X = X^wsize R^-1 mod N * X = X^wsize R^-1 mod N
*/ */
for( i = 0; i < wsize; i++ ) for( i = 0; i < wsize; i++ )
MBEDTLS_MPI_CHK( mpi_montmul( X, X, N, mm, &T ) ); mpi_montmul( X, X, N, mm, &T );
/* /*
* X = X * W[wbits] R^-1 mod N * X = X * W[wbits] R^-1 mod N
*/ */
MBEDTLS_MPI_CHK( mpi_montmul( X, &W[wbits], N, mm, &T ) ); mpi_montmul( X, &W[wbits], N, mm, &T );
state--; state--;
nbits = 0; nbits = 0;
@ -2218,18 +2280,18 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi *X, const mbedtls_mpi *A,
*/ */
for( i = 0; i < nbits; i++ ) for( i = 0; i < nbits; i++ )
{ {
MBEDTLS_MPI_CHK( mpi_montmul( X, X, N, mm, &T ) ); mpi_montmul( X, X, N, mm, &T );
wbits <<= 1; wbits <<= 1;
if( ( wbits & ( one << wsize ) ) != 0 ) if( ( wbits & ( one << wsize ) ) != 0 )
MBEDTLS_MPI_CHK( mpi_montmul( X, &W[1], N, mm, &T ) ); mpi_montmul( X, &W[1], N, mm, &T );
} }
/* /*
* X = A^E * R * R^-1 mod N = A^E mod N * X = A^E * R * R^-1 mod N = A^E mod N
*/ */
MBEDTLS_MPI_CHK( mpi_montred( X, N, mm, &T ) ); mpi_montred( X, N, mm, &T );
if( neg && E->n != 0 && ( E->p[0] & 1 ) != 0 ) if( neg && E->n != 0 && ( E->p[0] & 1 ) != 0 )
{ {