diff --git a/include/mbedtls/ecp.h b/include/mbedtls/ecp.h index 7d0abc032..e2c605b0d 100644 --- a/include/mbedtls/ecp.h +++ b/include/mbedtls/ecp.h @@ -179,6 +179,13 @@ mbedtls_ecp_keypair; */ typedef struct mbedtls_ecp_restart_mul mbedtls_ecp_restart_mul_ctx; +/** + * \brief Internal restart context for ecp_muladd() + * + * \note Opaque struct + */ +typedef struct mbedtls_ecp_restart_muladd mbedtls_ecp_restart_muladd_ctx; + /** * \brief General context for resuming ECC operations */ @@ -187,6 +194,7 @@ typedef struct unsigned ops_done; /*!< current ops count */ unsigned depth; /*!< call depth (0 = top-level) */ mbedtls_ecp_restart_mul_ctx *rsm; /*!< ecp_mul_comb() sub-context */ + mbedtls_ecp_restart_muladd_ctx *ma; /*!< ecp_muladd() sub-context */ } mbedtls_ecp_restart_ctx; #endif /* MBEDTLS_ECP_EARLY_RETURN */ @@ -654,6 +662,33 @@ int mbedtls_ecp_muladd( mbedtls_ecp_group *grp, mbedtls_ecp_point *R, const mbedtls_mpi *m, const mbedtls_ecp_point *P, const mbedtls_mpi *n, const mbedtls_ecp_point *Q ); +#if defined(MBEDTLS_ECP_EARLY_RETURN) +/** + * \brief Restartable version of \c mbedtls_ecp_muladd() + * + * \note Performs the same job as \c mbedtls_ecp_muladd(), but can + * return early and restart according to the limit set with + * \c mbedtls_ecp_set_max_ops() to reduce blocking. + * + * \param grp ECP group + * \param R Destination point + * \param m Integer by which to multiply P + * \param P Point to multiply by m + * \param n Integer by which to multiply Q + * \param Q Point to be multiplied by n + * \param rs_ctx Restart context + * + * \return See \c mbedtls_ecp_muladd(), or + * MBEDTLS_ERR_ECP_IN_PROGRESS if maximum number of + * operations was reached: see \c mbedtls_ecp_set_max_ops(). + */ +int mbedtls_ecp_muladd_restartable( + mbedtls_ecp_group *grp, mbedtls_ecp_point *R, + const mbedtls_mpi *m, const mbedtls_ecp_point *P, + const mbedtls_mpi *n, const mbedtls_ecp_point *Q, + mbedtls_ecp_restart_ctx *rs_ctx ); +#endif + /** * \brief Check that a point is a valid public key on this curve * diff --git a/library/ecp.c b/library/ecp.c index 4e637d732..493346006 100644 --- a/library/ecp.c +++ b/library/ecp.c @@ -101,9 +101,10 @@ void mbedtls_ecp_set_max_ops( unsigned max_ops ) } /* - * Restart context type for interrupted operations + * Restart sub-context for ecp_mul_comb() */ -struct mbedtls_ecp_restart_mul { +struct mbedtls_ecp_restart_mul +{ mbedtls_ecp_point R; /* current intermediate result */ size_t i; /* current index in various loops, 0 outside */ mbedtls_ecp_point *T; /* table for precomputed points */ @@ -119,7 +120,7 @@ struct mbedtls_ecp_restart_mul { }; /* - * Init restart_mul context + * Init restart_mul sub-context */ static void ecp_restart_mul_init( mbedtls_ecp_restart_mul_ctx *ctx ) { @@ -127,7 +128,7 @@ static void ecp_restart_mul_init( mbedtls_ecp_restart_mul_ctx *ctx ) } /* - * Free the components of a restart_mul context + * Free the components of a restart_mul sub-context */ static void ecp_restart_mul_free( mbedtls_ecp_restart_mul_ctx *ctx ) { @@ -147,6 +148,33 @@ static void ecp_restart_mul_free( mbedtls_ecp_restart_mul_ctx *ctx ) memset( ctx, 0, sizeof( mbedtls_ecp_restart_mul_ctx ) ); } +/* + * Restart context for ecp_muladd() + */ +struct mbedtls_ecp_restart_muladd +{ + int state; /* dummy for now */ +}; + +/* + * Init restart_muladd sub-context + */ +static void ecp_restart_muladd_init( mbedtls_ecp_restart_muladd_ctx *ctx ) +{ + memset( ctx, 0, sizeof( *ctx ) ); +} + +/* + * Free the components of a restart_muladd sub-context + */ +static void ecp_restart_muladd_free( mbedtls_ecp_restart_muladd_ctx *ctx ) +{ + if( ctx == NULL ) + return; + + memset( ctx, 0, sizeof( *ctx ) ); +} + /* * Initialize a restart context */ @@ -1868,9 +1896,9 @@ cleanup: if( ret != 0 ) mbedtls_ecp_point_free( R ); - /* clear restart context when not in progress (done or error) */ + /* clear our sub-context when not in progress (done or error) */ #if defined(MBEDTLS_ECP_EARLY_RETURN) - if( rs_ctx != NULL && rs_ctx->rsm != NULL && ret != MBEDTLS_ERR_ECP_IN_PROGRESS ) { + if( rs_ctx != NULL && ret != MBEDTLS_ERR_ECP_IN_PROGRESS ) { ecp_restart_mul_free( rs_ctx->rsm ); mbedtls_free( rs_ctx->rsm ); rs_ctx->rsm = NULL; @@ -2248,12 +2276,17 @@ cleanup: } /* - * Linear combination + * Restartable linear combination * NOT constant-time */ -int mbedtls_ecp_muladd( mbedtls_ecp_group *grp, mbedtls_ecp_point *R, +#if !defined(MBEDTLS_ECP_EARLY_RETURN) +static +#endif +int mbedtls_ecp_muladd_restartable( + mbedtls_ecp_group *grp, mbedtls_ecp_point *R, const mbedtls_mpi *m, const mbedtls_ecp_point *P, - const mbedtls_mpi *n, const mbedtls_ecp_point *Q ) + const mbedtls_mpi *n, const mbedtls_ecp_point *Q, + mbedtls_ecp_restart_ctx *rs_ctx ) { int ret; mbedtls_ecp_point mP; @@ -2261,9 +2294,29 @@ int mbedtls_ecp_muladd( mbedtls_ecp_group *grp, mbedtls_ecp_point *R, char is_grp_capable = 0; #endif +#if !defined(MBEDTLS_ECP_EARLY_RETURN) + (void) rs_ctx; +#endif + if( ecp_get_type( grp ) != ECP_TYPE_SHORT_WEIERSTRASS ) return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE ); +#if defined(MBEDTLS_ECP_EARLY_RETURN) + /* reset ops count for this call if top-level */ + if( rs_ctx != NULL && rs_ctx->depth++ == 0 ) + rs_ctx->ops_done = 0; + + /* set up our own sub-context if needed */ + if( ecp_max_ops != 0 && rs_ctx != NULL && rs_ctx->ma == NULL ) + { + rs_ctx->ma = mbedtls_calloc( 1, sizeof( mbedtls_ecp_restart_muladd_ctx ) ); + if( rs_ctx->ma == NULL ) + return( MBEDTLS_ERR_ECP_ALLOC_FAILED ); + + ecp_restart_muladd_init( rs_ctx->ma ); + } +#endif /* MBEDTLS_ECP_EARLY_RETURN */ + mbedtls_ecp_point_init( &mP ); MBEDTLS_MPI_CHK( mbedtls_ecp_mul_shortcuts( grp, &mP, m, P ) ); @@ -2290,9 +2343,32 @@ cleanup: #endif /* MBEDTLS_ECP_INTERNAL_ALT */ mbedtls_ecp_point_free( &mP ); +#if defined(MBEDTLS_ECP_EARLY_RETURN) + /* clear our sub-context when not in progress (done or error) */ + if( rs_ctx != NULL && ret != MBEDTLS_ERR_ECP_IN_PROGRESS ) { + ecp_restart_muladd_free( rs_ctx->ma ); + mbedtls_free( rs_ctx->ma ); + rs_ctx->ma = NULL; + } + + + if( rs_ctx != NULL ) + rs_ctx->depth--; +#endif /* MBEDTLS_ECP_EARLY_RETURN */ + return( ret ); } +/* + * Linear combination + * NOT constant-time + */ +int mbedtls_ecp_muladd( mbedtls_ecp_group *grp, mbedtls_ecp_point *R, + const mbedtls_mpi *m, const mbedtls_ecp_point *P, + const mbedtls_mpi *n, const mbedtls_ecp_point *Q ) +{ + return( mbedtls_ecp_muladd_restartable( grp, R, m, P, n, Q, NULL ) ); +} #if defined(ECP_MONTGOMERY) /* diff --git a/tests/suites/test_suite_ecp.data b/tests/suites/test_suite_ecp.data index 82ffec57b..9d25d2261 100644 --- a/tests/suites/test_suite_ecp.data +++ b/tests/suites/test_suite_ecp.data @@ -345,18 +345,22 @@ ecp_test_vect:MBEDTLS_ECP_DP_SECP256K1:"923C6D4756CD940CD1E13A359F6E0F0698791938 ECP selftest ecp_selftest: -ECP early return secp256r1 restart disabled +ECP early return mul secp256r1 restart disabled depends_on:MBEDTLS_ECP_DP_SECP256R1_ENABLED ecp_test_vect_restart:MBEDTLS_ECP_DP_SECP256R1:"814264145F2F56F2E96A8E337A1284993FAF432A5ABCE59E867B7291D507A3AF":"2AF502F3BE8952F2C9B5A8D4160D09E97165BE50BC42AE4A5E8D3B4BA83AEB15":"EB0FAF4CA986C4D38681A0F9872D79D56795BD4BFF6E6DE3C0F5015ECE5EFD85":"2CE1788EC197E096DB95A200CC0AB26A19CE6BCCAD562B8EEE1B593761CF7F41":"DD0F5396219D1EA393310412D19A08F1F5811E9DC8EC8EEA7F80D21C820C2788":"0357DCCD4C804D0D8D33AA42B848834AA5605F9AB0D37239A115BBB647936F50":0:0:0 -ECP early return secp256r1 restart max_ops=1 +ECP early return mul secp256r1 restart max_ops=1 depends_on:MBEDTLS_ECP_DP_SECP256R1_ENABLED ecp_test_vect_restart:MBEDTLS_ECP_DP_SECP256R1:"814264145F2F56F2E96A8E337A1284993FAF432A5ABCE59E867B7291D507A3AF":"2AF502F3BE8952F2C9B5A8D4160D09E97165BE50BC42AE4A5E8D3B4BA83AEB15":"EB0FAF4CA986C4D38681A0F9872D79D56795BD4BFF6E6DE3C0F5015ECE5EFD85":"2CE1788EC197E096DB95A200CC0AB26A19CE6BCCAD562B8EEE1B593761CF7F41":"DD0F5396219D1EA393310412D19A08F1F5811E9DC8EC8EEA7F80D21C820C2788":"0357DCCD4C804D0D8D33AA42B848834AA5605F9AB0D37239A115BBB647936F50":1:1:5000 -ECP early return secp256r1 restart max_ops=10000 +ECP early return mul secp256r1 restart max_ops=10000 depends_on:MBEDTLS_ECP_DP_SECP256R1_ENABLED ecp_test_vect_restart:MBEDTLS_ECP_DP_SECP256R1:"814264145F2F56F2E96A8E337A1284993FAF432A5ABCE59E867B7291D507A3AF":"2AF502F3BE8952F2C9B5A8D4160D09E97165BE50BC42AE4A5E8D3B4BA83AEB15":"EB0FAF4CA986C4D38681A0F9872D79D56795BD4BFF6E6DE3C0F5015ECE5EFD85":"2CE1788EC197E096DB95A200CC0AB26A19CE6BCCAD562B8EEE1B593761CF7F41":"DD0F5396219D1EA393310412D19A08F1F5811E9DC8EC8EEA7F80D21C820C2788":"0357DCCD4C804D0D8D33AA42B848834AA5605F9AB0D37239A115BBB647936F50":10000:0:0 -ECP early return secp256r1 restart max_ops=250 +ECP early return mul secp256r1 restart max_ops=250 depends_on:MBEDTLS_ECP_DP_SECP256R1_ENABLED ecp_test_vect_restart:MBEDTLS_ECP_DP_SECP256R1:"814264145F2F56F2E96A8E337A1284993FAF432A5ABCE59E867B7291D507A3AF":"2AF502F3BE8952F2C9B5A8D4160D09E97165BE50BC42AE4A5E8D3B4BA83AEB15":"EB0FAF4CA986C4D38681A0F9872D79D56795BD4BFF6E6DE3C0F5015ECE5EFD85":"2CE1788EC197E096DB95A200CC0AB26A19CE6BCCAD562B8EEE1B593761CF7F41":"DD0F5396219D1EA393310412D19A08F1F5811E9DC8EC8EEA7F80D21C820C2788":"0357DCCD4C804D0D8D33AA42B848834AA5605F9AB0D37239A115BBB647936F50":250:2:32 + +ECP early return muladd secp256r1 restart disabled +depends_on:MBEDTLS_ECP_DP_SECP256R1_ENABLED +ecp_muladd_restart:MBEDTLS_ECP_DP_SECP256R1:"CB28E0999B9C7715FD0A80D8E47A77079716CBBF917DD72E97566EA1C066957C":"2B57C0235FB7489768D058FF4911C20FDBE71E3699D91339AFBB903EE17255DC":"C3875E57C85038A0D60370A87505200DC8317C8C534948BEA6559C7C18E6D4CE":"3B4E49C4FDBFC006FF993C81A50EAE221149076D6EC09DDD9FB3B787F85B6483":"2442A5CC0ECD015FA3CA31DC8E2BBC70BF42D60CBCA20085E0822CB04235E970":"6FC98BD7E50211A4A27102FA3549DF79EBCB4BF246B80945CDDFE7D509BBFD7D":0:0:0 diff --git a/tests/suites/test_suite_ecp.function b/tests/suites/test_suite_ecp.function index 23905cef9..659830e5d 100644 --- a/tests/suites/test_suite_ecp.function +++ b/tests/suites/test_suite_ecp.function @@ -145,6 +145,77 @@ exit: } /* END_CASE */ +/* BEGIN_CASE depends_on:MBEDTLS_ECP_EARLY_RETURN */ +void ecp_muladd_restart( int id, char *xR_str, char *yR_str, + char *u1_str, char *u2_str, + char *xQ_str, char *yQ_str, + int max_ops, int min_restarts, int max_restarts ) +{ + /* + * Compute R = u1 * G + u2 * Q + * (test vectors mostly taken from ECDSA intermediate results) + * + * See comments at the top of ecp_test_vect_restart() + */ + mbedtls_ecp_restart_ctx ctx; + mbedtls_ecp_group grp; + mbedtls_ecp_point R, Q; + mbedtls_mpi u1, u2, xR, yR; + int cnt_restarts; + int ret; + + mbedtls_ecp_restart_init( &ctx ); + mbedtls_ecp_group_init( &grp ); + mbedtls_ecp_point_init( &R ); + mbedtls_ecp_point_init( &Q ); + mbedtls_mpi_init( &u1 ); mbedtls_mpi_init( &u2 ); + mbedtls_mpi_init( &xR ); mbedtls_mpi_init( &yR ); + + TEST_ASSERT( mbedtls_ecp_group_load( &grp, id ) == 0 ); + + TEST_ASSERT( mbedtls_mpi_read_string( &u1, 16, u1_str ) == 0 ); + TEST_ASSERT( mbedtls_mpi_read_string( &u2, 16, u2_str ) == 0 ); + TEST_ASSERT( mbedtls_mpi_read_string( &xR, 16, xR_str ) == 0 ); + TEST_ASSERT( mbedtls_mpi_read_string( &yR, 16, yR_str ) == 0 ); + + TEST_ASSERT( mbedtls_mpi_read_string( &Q.X, 16, xQ_str ) == 0 ); + TEST_ASSERT( mbedtls_mpi_read_string( &Q.Y, 16, yQ_str ) == 0 ); + TEST_ASSERT( mbedtls_mpi_lset( &Q.Z, 1 ) == 0 ); + + mbedtls_ecp_set_max_ops( (unsigned) max_ops ); + + cnt_restarts = 0; + do { + ret = mbedtls_ecp_muladd_restartable( &grp, &R, + &u1, &grp.G, &u2, &Q, &ctx ); + TEST_ASSERT( ret == 0 || ret == MBEDTLS_ERR_ECP_IN_PROGRESS ); + + if( ret == MBEDTLS_ERR_ECP_IN_PROGRESS ) + cnt_restarts++; + } + while( ret != 0 ); + + TEST_ASSERT( mbedtls_mpi_cmp_mpi( &R.X, &xR ) == 0 ); + TEST_ASSERT( mbedtls_mpi_cmp_mpi( &R.Y, &yR ) == 0 ); + + TEST_ASSERT( cnt_restarts >= min_restarts ); + TEST_ASSERT( cnt_restarts <= max_restarts ); + + /* Do we leak memory when aborting? */ + ret = mbedtls_ecp_muladd_restartable( &grp, &R, + &u1, &grp.G, &u2, &Q, &ctx ); + TEST_ASSERT( ret == 0 || ret == MBEDTLS_ERR_ECP_IN_PROGRESS ); + +exit: + mbedtls_ecp_restart_free( &ctx ); + mbedtls_ecp_group_free( &grp ); + mbedtls_ecp_point_free( &R ); + mbedtls_ecp_point_free( &Q ); + mbedtls_mpi_free( &u1 ); mbedtls_mpi_free( &u2 ); + mbedtls_mpi_free( &xR ); mbedtls_mpi_free( &yR ); +} +/* END_CASE */ + /* BEGIN_CASE */ void ecp_test_vect( int id, char *dA_str, char *xA_str, char *yA_str, char *dB_str, char *xB_str, char *yB_str, char *xZ_str,