Add a safer deterministic ECDSA function

`mbedtls_ecdsa_sign_det` reuses the internal HMAC-DRBG instance to
implement blinding. The advantage of this is that the algorithm is
deterministic too, not just the resulting signature. The drawback is
that the blinding is always the same for the same key and message.
This diminishes the efficiency of blinding and leaks information about
the private key.

A function that takes external randomness fixes this weakness.
This commit is contained in:
Janos Follath 2019-01-04 14:32:30 +00:00 committed by Darryl Green
parent c04305f036
commit dca667ac80
2 changed files with 103 additions and 15 deletions

View file

@ -215,6 +215,51 @@ int mbedtls_ecdsa_sign_det( mbedtls_ecp_group *grp, mbedtls_mpi *r,
mbedtls_mpi *s, const mbedtls_mpi *d, mbedtls_mpi *s, const mbedtls_mpi *d,
const unsigned char *buf, size_t blen, const unsigned char *buf, size_t blen,
mbedtls_md_type_t md_alg ); mbedtls_md_type_t md_alg );
/**
* \brief This function computes the ECDSA signature of a
* previously-hashed message, deterministic version.
*
* For more information, see <em>RFC-6979: Deterministic
* Usage of the Digital Signature Algorithm (DSA) and Elliptic
* Curve Digital Signature Algorithm (ECDSA)</em>.
*
* \note If the bitlength of the message hash is larger than the
* bitlength of the group order, then the hash is truncated as
* defined in <em>Standards for Efficient Cryptography Group
* (SECG): SEC1 Elliptic Curve Cryptography</em>, section
* 4.1.3, step 5.
*
* \see ecp.h
*
* \param grp The context for the elliptic curve to use.
* This must be initialized and have group parameters
* set, for example through mbedtls_ecp_group_load().
* \param r The MPI context in which to store the first part
* the signature. This must be initialized.
* \param s The MPI context in which to store the second part
* the signature. This must be initialized.
* \param d The private signing key. This must be initialized
* and setup, for example through mbedtls_ecp_gen_privkey().
* \param buf The hashed content to be signed. This must be a readable
* buffer of length \p blen Bytes. It may be \c NULL if
* \p blen is zero.
* \param blen The length of \p buf in Bytes.
* \param md_alg The hash algorithm used to hash the original data.
* \param f_rng_blind The RNG function used for blinding. This must not be
* \c NULL.
* \param p_rng_blind The RNG context to be passed to \p f_rng. This may be
* \c NULL if \p f_rng doesn't need a context parameter.
*
* \return \c 0 on success.
* \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX
* error code on failure.
*/
int mbedtls_ecdsa_sign_det_ext( mbedtls_ecp_group *grp, mbedtls_mpi *r,
mbedtls_mpi *s, const mbedtls_mpi *d,
const unsigned char *buf, size_t blen,
mbedtls_md_type_t md_alg,
int (*f_rng_blind)(void *, unsigned char *, size_t),
void *p_rng_blind );
#endif /* MBEDTLS_ECDSA_DETERMINISTIC */ #endif /* MBEDTLS_ECDSA_DETERMINISTIC */
/** /**

View file

@ -254,6 +254,8 @@ static int ecdsa_sign_restartable( mbedtls_ecp_group *grp,
mbedtls_mpi *r, mbedtls_mpi *s, mbedtls_mpi *r, mbedtls_mpi *s,
const mbedtls_mpi *d, const unsigned char *buf, size_t blen, const mbedtls_mpi *d, const unsigned char *buf, size_t blen,
int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, int (*f_rng)(void *, unsigned char *, size_t), void *p_rng,
int (*f_rng_blind)(void *, unsigned char *, size_t),
void *p_rng_blind,
mbedtls_ecdsa_restart_ctx *rs_ctx ) mbedtls_ecdsa_restart_ctx *rs_ctx )
{ {
int ret, key_tries, sign_tries; int ret, key_tries, sign_tries;
@ -323,7 +325,9 @@ static int ecdsa_sign_restartable( mbedtls_ecp_group *grp,
mul: mul:
#endif #endif
MBEDTLS_MPI_CHK( mbedtls_ecp_mul_restartable( grp, &R, pk, &grp->G, MBEDTLS_MPI_CHK( mbedtls_ecp_mul_restartable( grp, &R, pk, &grp->G,
f_rng, p_rng, ECDSA_RS_ECP ) ); f_rng_blind,
p_rng_blind,
ECDSA_RS_ECP ) );
MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( pr, &R.X, &grp->N ) ); MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( pr, &R.X, &grp->N ) );
} }
while( mbedtls_mpi_cmp_int( pr, 0 ) == 0 ); while( mbedtls_mpi_cmp_int( pr, 0 ) == 0 );
@ -349,7 +353,8 @@ modn:
* Generate a random value to blind inv_mod in next step, * Generate a random value to blind inv_mod in next step,
* avoiding a potential timing leak. * avoiding a potential timing leak.
*/ */
MBEDTLS_MPI_CHK( mbedtls_ecp_gen_privkey( grp, &t, f_rng, p_rng ) ); MBEDTLS_MPI_CHK( mbedtls_ecp_gen_privkey( grp, &t, f_rng_blind,
p_rng_blind ) );
/* /*
* Step 6: compute s = (e + r * d) / k = t (e + rd) / (kt) mod n * Step 6: compute s = (e + r * d) / k = t (e + rd) / (kt) mod n
@ -406,8 +411,9 @@ int mbedtls_ecdsa_sign( mbedtls_ecp_group *grp, mbedtls_mpi *r, mbedtls_mpi *s,
ECDSA_VALIDATE_RET( f_rng != NULL ); ECDSA_VALIDATE_RET( f_rng != NULL );
ECDSA_VALIDATE_RET( buf != NULL || blen == 0 ); ECDSA_VALIDATE_RET( buf != NULL || blen == 0 );
/* Use the same RNG for both blinding and ephemeral key generation */
return( ecdsa_sign_restartable( grp, r, s, d, buf, blen, return( ecdsa_sign_restartable( grp, r, s, d, buf, blen,
f_rng, p_rng, NULL ) ); f_rng, p_rng, f_rng, p_rng, NULL ) );
} }
#endif /* !MBEDTLS_ECDSA_SIGN_ALT */ #endif /* !MBEDTLS_ECDSA_SIGN_ALT */
@ -419,6 +425,8 @@ static int ecdsa_sign_det_restartable( mbedtls_ecp_group *grp,
mbedtls_mpi *r, mbedtls_mpi *s, mbedtls_mpi *r, mbedtls_mpi *s,
const mbedtls_mpi *d, const unsigned char *buf, size_t blen, const mbedtls_mpi *d, const unsigned char *buf, size_t blen,
mbedtls_md_type_t md_alg, mbedtls_md_type_t md_alg,
int (*f_rng_blind)(void *, unsigned char *, size_t),
void *p_rng_blind,
mbedtls_ecdsa_restart_ctx *rs_ctx ) mbedtls_ecdsa_restart_ctx *rs_ctx )
{ {
int ret; int ret;
@ -465,8 +473,22 @@ sign:
ret = mbedtls_ecdsa_sign( grp, r, s, d, buf, blen, ret = mbedtls_ecdsa_sign( grp, r, s, d, buf, blen,
mbedtls_hmac_drbg_random, p_rng ); mbedtls_hmac_drbg_random, p_rng );
#else #else
ret = ecdsa_sign_restartable( grp, r, s, d, buf, blen, if( f_rng_blind != NULL )
mbedtls_hmac_drbg_random, p_rng, rs_ctx ); ret = ecdsa_sign_restartable( grp, r, s, d, buf, blen,
mbedtls_hmac_drbg_random, p_rng,
f_rng_blind, p_rng_blind, rs_ctx );
else
/*
* Use the same RNG for both blinding and ephemeral key generation.
* Since the RNG output is always the same for the same key and message,
* this limits the efficiency of blinding and leaks information through
* side channels. After mbedtls_ecdsa_sign_det() is removed NULL won't
* be a valid value for f_rng_blind anymore. Therefore it should be
* checked by the caller and this branch and check can be removed.
*/
ret = ecdsa_sign_restartable( grp, r, s, d, buf, blen,
mbedtls_hmac_drbg_random, p_rng,
mbedtls_hmac_drbg_random, p_rng, rs_ctx );
#endif /* MBEDTLS_ECDSA_SIGN_ALT */ #endif /* MBEDTLS_ECDSA_SIGN_ALT */
cleanup: cleanup:
@ -479,11 +501,12 @@ cleanup:
} }
/* /*
* Deterministic signature wrapper * Deterministic signature wrappers
*/ */
int mbedtls_ecdsa_sign_det( mbedtls_ecp_group *grp, mbedtls_mpi *r, mbedtls_mpi *s, int mbedtls_ecdsa_sign_det( mbedtls_ecp_group *grp, mbedtls_mpi *r,
const mbedtls_mpi *d, const unsigned char *buf, size_t blen, mbedtls_mpi *s, const mbedtls_mpi *d,
mbedtls_md_type_t md_alg ) const unsigned char *buf, size_t blen,
mbedtls_md_type_t md_alg )
{ {
ECDSA_VALIDATE_RET( grp != NULL ); ECDSA_VALIDATE_RET( grp != NULL );
ECDSA_VALIDATE_RET( r != NULL ); ECDSA_VALIDATE_RET( r != NULL );
@ -491,7 +514,27 @@ int mbedtls_ecdsa_sign_det( mbedtls_ecp_group *grp, mbedtls_mpi *r, mbedtls_mpi
ECDSA_VALIDATE_RET( d != NULL ); ECDSA_VALIDATE_RET( d != NULL );
ECDSA_VALIDATE_RET( buf != NULL || blen == 0 ); ECDSA_VALIDATE_RET( buf != NULL || blen == 0 );
return( ecdsa_sign_det_restartable( grp, r, s, d, buf, blen, md_alg, NULL ) ); return( ecdsa_sign_det_restartable( grp, r, s, d, buf, blen, md_alg,
NULL, NULL, NULL ) );
}
int mbedtls_ecdsa_sign_det_ext( mbedtls_ecp_group *grp, mbedtls_mpi *r,
mbedtls_mpi *s, const mbedtls_mpi *d,
const unsigned char *buf, size_t blen,
mbedtls_md_type_t md_alg,
int (*f_rng_blind)(void *, unsigned char *,
size_t),
void *p_rng_blind )
{
ECDSA_VALIDATE_RET( grp != NULL );
ECDSA_VALIDATE_RET( r != NULL );
ECDSA_VALIDATE_RET( s != NULL );
ECDSA_VALIDATE_RET( d != NULL );
ECDSA_VALIDATE_RET( buf != NULL || blen == 0 );
ECDSA_VALIDATE_RET( f_rng_blind != NULL );
return( ecdsa_sign_det_restartable( grp, r, s, d, buf, blen, md_alg,
f_rng_blind, p_rng_blind, NULL ) );
} }
#endif /* MBEDTLS_ECDSA_DETERMINISTIC */ #endif /* MBEDTLS_ECDSA_DETERMINISTIC */
@ -670,11 +713,9 @@ int mbedtls_ecdsa_write_signature_restartable( mbedtls_ecdsa_context *ctx,
mbedtls_mpi_init( &s ); mbedtls_mpi_init( &s );
#if defined(MBEDTLS_ECDSA_DETERMINISTIC) #if defined(MBEDTLS_ECDSA_DETERMINISTIC)
(void) f_rng;
(void) p_rng;
MBEDTLS_MPI_CHK( ecdsa_sign_det_restartable( &ctx->grp, &r, &s, &ctx->d, MBEDTLS_MPI_CHK( ecdsa_sign_det_restartable( &ctx->grp, &r, &s, &ctx->d,
hash, hlen, md_alg, rs_ctx ) ); hash, hlen, md_alg, f_rng,
p_rng, rs_ctx ) );
#else #else
(void) md_alg; (void) md_alg;
@ -682,8 +723,10 @@ int mbedtls_ecdsa_write_signature_restartable( mbedtls_ecdsa_context *ctx,
MBEDTLS_MPI_CHK( mbedtls_ecdsa_sign( &ctx->grp, &r, &s, &ctx->d, MBEDTLS_MPI_CHK( mbedtls_ecdsa_sign( &ctx->grp, &r, &s, &ctx->d,
hash, hlen, f_rng, p_rng ) ); hash, hlen, f_rng, p_rng ) );
#else #else
/* Use the same RNG for both blinding and ephemeral key generation */
MBEDTLS_MPI_CHK( ecdsa_sign_restartable( &ctx->grp, &r, &s, &ctx->d, MBEDTLS_MPI_CHK( ecdsa_sign_restartable( &ctx->grp, &r, &s, &ctx->d,
hash, hlen, f_rng, p_rng, rs_ctx ) ); hash, hlen, f_rng, p_rng, f_rng,
p_rng, rs_ctx ) );
#endif /* MBEDTLS_ECDSA_SIGN_ALT */ #endif /* MBEDTLS_ECDSA_SIGN_ALT */
#endif /* MBEDTLS_ECDSA_DETERMINISTIC */ #endif /* MBEDTLS_ECDSA_DETERMINISTIC */