Skip to content

Commit e80c7e4

Browse files
Merge pull request #278 from ARMmbed/dev/yanesca/iotcrypt-767-ecdsa-timing-side-channel
ECDSA timing side channel due to non-constant-time integer comparison
2 parents 90bc6b8 + 3070242 commit e80c7e4

File tree

5 files changed

+240
-3
lines changed

5 files changed

+240
-3
lines changed

include/mbedtls/bignum.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ extern "C" {
185185
*/
186186
typedef struct mbedtls_mpi
187187
{
188-
int s; /*!< integer sign */
188+
int s; /*!< Sign: -1 if the mpi is negative, 1 otherwise */
189189
size_t n; /*!< total # of limbs */
190190
mbedtls_mpi_uint *p; /*!< pointer to limbs */
191191
}
@@ -594,6 +594,24 @@ int mbedtls_mpi_cmp_abs( const mbedtls_mpi *X, const mbedtls_mpi *Y );
594594
*/
595595
int mbedtls_mpi_cmp_mpi( const mbedtls_mpi *X, const mbedtls_mpi *Y );
596596

597+
/**
598+
* \brief Check if an MPI is less than the other in constant time.
599+
*
600+
* \param X The left-hand MPI. This must point to an initialized MPI
601+
* with the same allocated length as Y.
602+
* \param Y The right-hand MPI. This must point to an initialized MPI
603+
* with the same allocated length as X.
604+
* \param ret The result of the comparison:
605+
* \c 1 if \p X is less than \p Y.
606+
* \c 0 if \p X is greater than or equal to \p Y.
607+
*
608+
* \return 0 on success.
609+
* \return MBEDTLS_ERR_MPI_BAD_INPUT_DATA if the allocated length of
610+
* the two input MPIs is not the same.
611+
*/
612+
int mbedtls_mpi_lt_mpi_ct( const mbedtls_mpi *X, const mbedtls_mpi *Y,
613+
unsigned *ret );
614+
597615
/**
598616
* \brief Compare an MPI with an integer.
599617
*

library/bignum.c

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,6 +1148,107 @@ int mbedtls_mpi_cmp_mpi( const mbedtls_mpi *X, const mbedtls_mpi *Y )
11481148
return( 0 );
11491149
}
11501150

1151+
/** Decide if an integer is less than the other, without branches.
1152+
*
1153+
* \param x First integer.
1154+
* \param y Second integer.
1155+
*
1156+
* \return 1 if \p x is less than \p y, 0 otherwise
1157+
*/
1158+
static unsigned ct_lt_mpi_uint( const mbedtls_mpi_uint x,
1159+
const mbedtls_mpi_uint y )
1160+
{
1161+
mbedtls_mpi_uint ret;
1162+
mbedtls_mpi_uint cond;
1163+
1164+
/*
1165+
* Check if the most significant bits (MSB) of the operands are different.
1166+
*/
1167+
cond = ( x ^ y );
1168+
/*
1169+
* If the MSB are the same then the difference x-y will be negative (and
1170+
* have its MSB set to 1 during conversion to unsigned) if and only if x<y.
1171+
*/
1172+
ret = ( x - y ) & ~cond;
1173+
/*
1174+
* If the MSB are different, then the operand with the MSB of 1 is the
1175+
* bigger. (That is if y has MSB of 1, then x<y is true and it is false if
1176+
* the MSB of y is 0.)
1177+
*/
1178+
ret |= y & cond;
1179+
1180+
1181+
ret = ret >> ( biL - 1 );
1182+
1183+
return (unsigned) ret;
1184+
}
1185+
1186+
/*
1187+
* Compare signed values in constant time
1188+
*/
1189+
int mbedtls_mpi_lt_mpi_ct( const mbedtls_mpi *X, const mbedtls_mpi *Y,
1190+
unsigned *ret )
1191+
{
1192+
size_t i;
1193+
/* The value of any of these variables is either 0 or 1 at all times. */
1194+
unsigned cond, done, X_is_negative, Y_is_negative;
1195+
1196+
MPI_VALIDATE_RET( X != NULL );
1197+
MPI_VALIDATE_RET( Y != NULL );
1198+
MPI_VALIDATE_RET( ret != NULL );
1199+
1200+
if( X->n != Y->n )
1201+
return MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
1202+
1203+
/*
1204+
* Set sign_N to 1 if N >= 0, 0 if N < 0.
1205+
* We know that N->s == 1 if N >= 0 and N->s == -1 if N < 0.
1206+
*/
1207+
X_is_negative = ( X->s & 2 ) >> 1;
1208+
Y_is_negative = ( Y->s & 2 ) >> 1;
1209+
1210+
/*
1211+
* If the signs are different, then the positive operand is the bigger.
1212+
* That is if X is negative (X_is_negative == 1), then X < Y is true and it
1213+
* is false if X is positive (X_is_negative == 0).
1214+
*/
1215+
cond = ( X_is_negative ^ Y_is_negative );
1216+
*ret = cond & X_is_negative;
1217+
1218+
/*
1219+
* This is a constant-time function. We might have the result, but we still
1220+
* need to go through the loop. Record if we have the result already.
1221+
*/
1222+
done = cond;
1223+
1224+
for( i = X->n; i > 0; i-- )
1225+
{
1226+
/*
1227+
* If Y->p[i - 1] < X->p[i - 1] then X < Y is true if and only if both
1228+
* X and Y are negative.
1229+
*
1230+
* Again even if we can make a decision, we just mark the result and
1231+
* the fact that we are done and continue looping.
1232+
*/
1233+
cond = ct_lt_mpi_uint( Y->p[i - 1], X->p[i - 1] );
1234+
*ret |= cond & ( 1 - done ) & X_is_negative;
1235+
done |= cond;
1236+
1237+
/*
1238+
* If X->p[i - 1] < Y->p[i - 1] then X < Y is true if and only if both
1239+
* X and Y are positive.
1240+
*
1241+
* Again even if we can make a decision, we just mark the result and
1242+
* the fact that we are done and continue looping.
1243+
*/
1244+
cond = ct_lt_mpi_uint( X->p[i - 1], Y->p[i - 1] );
1245+
*ret |= cond & ( 1 - done ) & ( 1 - X_is_negative );
1246+
done |= cond;
1247+
}
1248+
1249+
return( 0 );
1250+
}
1251+
11511252
/*
11521253
* Compare signed values
11531254
*/

library/ecp.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2803,6 +2803,7 @@ int mbedtls_ecp_gen_privkey( const mbedtls_ecp_group *grp,
28032803
{
28042804
/* SEC1 3.2.1: Generate d such that 1 <= n < N */
28052805
int count = 0;
2806+
unsigned cmp = 0;
28062807

28072808
/*
28082809
* Match the procedure given in RFC 6979 (deterministic ECDSA):
@@ -2827,9 +2828,14 @@ int mbedtls_ecp_gen_privkey( const mbedtls_ecp_group *grp,
28272828
*/
28282829
if( ++count > 30 )
28292830
return( MBEDTLS_ERR_ECP_RANDOM_FAILED );
2831+
2832+
ret = mbedtls_mpi_lt_mpi_ct( d, &grp->N, &cmp );
2833+
if( ret != 0 )
2834+
{
2835+
goto cleanup;
2836+
}
28302837
}
2831-
while( mbedtls_mpi_cmp_int( d, 1 ) < 0 ||
2832-
mbedtls_mpi_cmp_mpi( d, &grp->N ) >= 0 );
2838+
while( mbedtls_mpi_cmp_int( d, 1 ) < 0 || cmp != 1 );
28332839
}
28342840
#endif /* ECP_SHORTWEIERSTRASS */
28352841

tests/suites/test_suite_mpi.data

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,93 @@ mbedtls_mpi_cmp_mpi:10:"2":10:"-3":1
175175
Base test mbedtls_mpi_cmp_mpi (Mixed values) #6
176176
mbedtls_mpi_cmp_mpi:10:"-2":10:"31231231289798":-1
177177

178+
Base test mbedtls_mpi_lt_mpi_ct #1
179+
mbedtls_mpi_lt_mpi_ct:1:"2B5":1:"2B5":0:0
180+
181+
Base test mbedtls_mpi_lt_mpi_ct #2
182+
mbedtls_mpi_lt_mpi_ct:1:"2B5":1:"2B4":0:0
183+
184+
Base test mbedtls_mpi_lt_mpi_ct #3
185+
mbedtls_mpi_lt_mpi_ct:1:"2B5":1:"2B6":1:0
186+
187+
Base test mbedtls_mpi_lt_mpi_ct (Negative values) #1
188+
mbedtls_mpi_lt_mpi_ct:1:"-2":1:"-2":0:0
189+
190+
Base test mbedtls_mpi_lt_mpi_ct (Negative values) #2
191+
mbedtls_mpi_lt_mpi_ct:1:"-2":1:"-3":0:0
192+
193+
Base test mbedtls_mpi_lt_mpi_ct (Negative values) #3
194+
mbedtls_mpi_lt_mpi_ct:1:"-2":1:"-1":1:0
195+
196+
Base test mbedtls_mpi_lt_mpi_ct (Mixed values) #1
197+
mbedtls_mpi_lt_mpi_ct:1:"-3":1:"2":1:0
198+
199+
Base test mbedtls_mpi_lt_mpi_ct (Mixed values) #2
200+
mbedtls_mpi_lt_mpi_ct:1:"2":1:"-3":0:0
201+
202+
Base test mbedtls_mpi_lt_mpi_ct (Mixed values) #3
203+
mbedtls_mpi_lt_mpi_ct:2:"-2":2:"1C67967269C6":1:0
204+
205+
Base test mbedtls_mpi_lt_mpi_ct (X is longer in storage)
206+
mbedtls_mpi_lt_mpi_ct:3:"2B5":2:"2B5":0:MBEDTLS_ERR_MPI_BAD_INPUT_DATA
207+
208+
Base test mbedtls_mpi_lt_mpi_ct (Y is longer in storage)
209+
mbedtls_mpi_lt_mpi_ct:3:"2B5":4:"2B5":0:MBEDTLS_ERR_MPI_BAD_INPUT_DATA
210+
211+
Base test mbedtls_mpi_lt_mpi_ct (corner case - 64 bit) #1
212+
mbedtls_mpi_lt_mpi_ct:2:"7FFFFFFFFFFFFFFF":2:"FF":0:0
213+
214+
Base test mbedtls_mpi_lt_mpi_ct (corner case - 64 bit) #2
215+
mbedtls_mpi_lt_mpi_ct:2:"8000000000000000":2:"7FFFFFFFFFFFFFFF":0:0
216+
217+
Base test mbedtls_mpi_lt_mpi_ct (corner case - 64 bit) #3
218+
mbedtls_mpi_lt_mpi_ct:2:"8000000000000000":2:"1":0:0
219+
220+
Base test mbedtls_mpi_lt_mpi_ct (corner case - 64 bit) #4
221+
mbedtls_mpi_lt_mpi_ct:2:"8000000000000000":2:"0":0:0
222+
223+
Base test mbedtls_mpi_lt_mpi_ct (corner case - 64 bit) #5
224+
mbedtls_mpi_lt_mpi_ct:2:"FFFFFFFFFFFFFFFF":2:"FF":0:0
225+
226+
Base test mbedtls_mpi_lt_mpi_ct (corner case - 32 bit) #1
227+
mbedtls_mpi_lt_mpi_ct:1:"7FFFFFFF":1:"FF":0:0
228+
229+
Base test mbedtls_mpi_lt_mpi_ct (corner case - 32 bit) #2
230+
mbedtls_mpi_lt_mpi_ct:1:"80000000":1:"7FFFFFFF":0:0
231+
232+
Base test mbedtls_mpi_lt_mpi_ct (corner case - 32 bit) #3
233+
mbedtls_mpi_lt_mpi_ct:1:"80000000":1:"1":0:0
234+
235+
Base test mbedtls_mpi_lt_mpi_ct (corner case - 32 bit) #4
236+
mbedtls_mpi_lt_mpi_ct:1:"80000000":1:"0":0:0
237+
238+
Base test mbedtls_mpi_lt_mpi_ct (corner case - 32 bit) #5
239+
mbedtls_mpi_lt_mpi_ct:1:"FFFFFFFF":1:"FF":0:0
240+
241+
Multi-limb mbedtls_mpi_lt_mpi_ct (X<Y, zero vs non-zero MS limb)
242+
mbedtls_mpi_lt_mpi_ct:2:"0FFFFFFFFFFFFFFFF":2:"1FFFFFFFFFFFFFFFF":1:0
243+
244+
Multi-limb mbedtls_mpi_lt_mpi_ct (X>Y, equal MS limbs)
245+
mbedtls_mpi_lt_mpi_ct:2:"-EEFFFFFFFFFFFFFFF1":2:"-EEFFFFFFFFFFFFFFFF":0:0
246+
247+
Multi-limb mbedtls_mpi_lt_mpi_ct (X=Y)
248+
mbedtls_mpi_lt_mpi_ct:2:"EEFFFFFFFFFFFFFFFF":2:"EEFFFFFFFFFFFFFFFF":0:0
249+
250+
Multi-limb mbedtls_mpi_lt_mpi_ct (X=-Y)
251+
mbedtls_mpi_lt_mpi_ct:2:"-EEFFFFFFFFFFFFFFFF":2:"EEFFFFFFFFFFFFFFFF":1:0
252+
253+
Multi-limb mbedtls_mpi_lt_mpi_ct (Alternating limbs) #1
254+
mbedtls_mpi_lt_mpi_ct:2:"11FFFFFFFFFFFFFFFF":2:"FF1111111111111111":1:0
255+
256+
Multi-limb mbedtls_mpi_lt_mpi_ct (Alternating limbs) #2
257+
mbedtls_mpi_lt_mpi_ct:2:"FF1111111111111111":2:"11FFFFFFFFFFFFFFFF":0:0
258+
259+
Multi-limb mbedtls_mpi_lt_mpi_ct (Alternating limbs) #3
260+
mbedtls_mpi_lt_mpi_ct:2:"-11FFFFFFFFFFFFFFFF":2:"-FF1111111111111111":0:0
261+
262+
Multi-limb mbedtls_mpi_lt_mpi_ct (Alternating limbs) #4
263+
mbedtls_mpi_lt_mpi_ct:2:"-FF1111111111111111":2:"-11FFFFFFFFFFFFFFFF":1:0
264+
178265
Base test mbedtls_mpi_cmp_abs #1
179266
mbedtls_mpi_cmp_abs:10:"693":10:"693":0
180267

tests/suites/test_suite_mpi.function

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,31 @@ exit:
587587
}
588588
/* END_CASE */
589589

590+
/* BEGIN_CASE */
591+
void mbedtls_mpi_lt_mpi_ct( int size_X, char * input_X,
592+
int size_Y, char * input_Y,
593+
int input_ret, int input_err )
594+
{
595+
unsigned ret;
596+
unsigned input_uret = input_ret;
597+
mbedtls_mpi X, Y;
598+
mbedtls_mpi_init( &X ); mbedtls_mpi_init( &Y );
599+
600+
TEST_ASSERT( mbedtls_mpi_read_string( &X, 16, input_X ) == 0 );
601+
TEST_ASSERT( mbedtls_mpi_read_string( &Y, 16, input_Y ) == 0 );
602+
603+
mbedtls_mpi_grow( &X, size_X );
604+
mbedtls_mpi_grow( &Y, size_Y );
605+
606+
TEST_ASSERT( mbedtls_mpi_lt_mpi_ct( &X, &Y, &ret ) == input_err );
607+
if( input_err == 0 )
608+
TEST_ASSERT( ret == input_uret );
609+
610+
exit:
611+
mbedtls_mpi_free( &X ); mbedtls_mpi_free( &Y );
612+
}
613+
/* END_CASE */
614+
590615
/* BEGIN_CASE */
591616
void mbedtls_mpi_cmp_abs( int radix_X, char * input_X, int radix_Y,
592617
char * input_Y, int input_A )

0 commit comments

Comments
 (0)