Add blinding to a DSA signature

This extends the recently added ECDSA signature blinding to blind DSA too.

This is based on side channel attacks demonstrated by Keegan Ryan (NCC
Group) for ECDSA which are likely to be able to be applied to DSA.

Normally, as in ECDSA, during signing the signer calculates:

s:= k^-1 * (m + r * priv_key) mod order

In ECDSA, the addition operation above provides a sufficient signal for a
flush+reload attack to derive the private key given sufficient signature
operations.

As a mitigation (based on a suggestion from Keegan) we add blinding to
the operation so that:

s := k^-1 * blind^-1 (blind * m + blind * r * priv_key) mod order

Since this attack is a localhost side channel only no CVE is assigned.

This commit also tweaks the previous ECDSA blinding so that blinding is
only removed at the last possible step.

Reviewed-by: Rich Salz <rsalz@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/6522)
This commit is contained in:
Matt Caswell 2018-06-19 15:07:02 +01:00
parent 95aec8641f
commit 7f9822a482
3 changed files with 58 additions and 26 deletions

View File

@ -13,8 +13,8 @@
chosen point SCA attacks. chosen point SCA attacks.
[Sohaib ul Hassan, Nicola Tuveri, Billy Bob Brumley] [Sohaib ul Hassan, Nicola Tuveri, Billy Bob Brumley]
*) Add blinding to an ECDSA signature to protect against side channel attacks *) Add blinding to ECDSA and DSA signatures to protect against side channel
discovered by Keegan Ryan (NCC Group). attacks discovered by Keegan Ryan (NCC Group).
[Matt Caswell] [Matt Caswell]
*) Enforce checking in the pkeyutl command line app to ensure that the input *) Enforce checking in the pkeyutl command line app to ensure that the input

View File

@ -59,19 +59,13 @@ const DSA_METHOD *DSA_OpenSSL(void)
static DSA_SIG *dsa_do_sign(const unsigned char *dgst, int dlen, DSA *dsa) static DSA_SIG *dsa_do_sign(const unsigned char *dgst, int dlen, DSA *dsa)
{ {
BIGNUM *kinv = NULL; BIGNUM *kinv = NULL;
BIGNUM *m; BIGNUM *m, *blind, *blindm, *tmp;
BIGNUM *xr;
BN_CTX *ctx = NULL; BN_CTX *ctx = NULL;
int reason = ERR_R_BN_LIB; int reason = ERR_R_BN_LIB;
DSA_SIG *ret = NULL; DSA_SIG *ret = NULL;
int rv = 0; int rv = 0;
m = BN_new(); if (dsa->p == NULL || dsa->q == NULL || dsa->g == NULL) {
xr = BN_new();
if (m == NULL || xr == NULL)
goto err;
if (!dsa->p || !dsa->q || !dsa->g) {
reason = DSA_R_MISSING_PARAMETERS; reason = DSA_R_MISSING_PARAMETERS;
goto err; goto err;
} }
@ -87,6 +81,13 @@ static DSA_SIG *dsa_do_sign(const unsigned char *dgst, int dlen, DSA *dsa)
ctx = BN_CTX_new(); ctx = BN_CTX_new();
if (ctx == NULL) if (ctx == NULL)
goto err; goto err;
m = BN_CTX_get(ctx);
blind = BN_CTX_get(ctx);
blindm = BN_CTX_get(ctx);
tmp = BN_CTX_get(ctx);
if (tmp == NULL)
goto err;
redo: redo:
if (!dsa_sign_setup(dsa, ctx, &kinv, &ret->r, dgst, dlen)) if (!dsa_sign_setup(dsa, ctx, &kinv, &ret->r, dgst, dlen))
goto err; goto err;
@ -101,17 +102,50 @@ static DSA_SIG *dsa_do_sign(const unsigned char *dgst, int dlen, DSA *dsa)
if (BN_bin2bn(dgst, dlen, m) == NULL) if (BN_bin2bn(dgst, dlen, m) == NULL)
goto err; goto err;
/* Compute s = inv(k) (m + xr) mod q */ /*
if (!BN_mod_mul(xr, dsa->priv_key, ret->r, dsa->q, ctx)) * The normal signature calculation is:
goto err; /* s = xr */ *
if (!BN_add(ret->s, xr, m)) * s := k^-1 * (m + r * priv_key) mod q
goto err; /* s = m + xr */ *
if (BN_cmp(ret->s, dsa->q) > 0) * We will blind this to protect against side channel attacks
if (!BN_sub(ret->s, ret->s, dsa->q)) *
* s := blind^-1 * k^-1 * (blind * m + blind * r * priv_key) mod q
*/
/* Generate a blinding value */
do {
if (!BN_priv_rand(blind, BN_num_bits(dsa->q) - 1,
BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY))
goto err; goto err;
} while (BN_is_zero(blind));
BN_set_flags(blind, BN_FLG_CONSTTIME);
BN_set_flags(blindm, BN_FLG_CONSTTIME);
BN_set_flags(tmp, BN_FLG_CONSTTIME);
/* tmp := blind * priv_key * r mod q */
if (!BN_mod_mul(tmp, blind, dsa->priv_key, dsa->q, ctx))
goto err;
if (!BN_mod_mul(tmp, tmp, ret->r, dsa->q, ctx))
goto err;
/* blindm := blind * m mod q */
if (!BN_mod_mul(blindm, blind, m, dsa->q, ctx))
goto err;
/* s : = (blind * priv_key * r) + (blind * m) mod q */
if (!BN_mod_add_quick(ret->s, tmp, blindm, dsa->q))
goto err;
/* s := s * k^-1 mod q */
if (!BN_mod_mul(ret->s, ret->s, kinv, dsa->q, ctx)) if (!BN_mod_mul(ret->s, ret->s, kinv, dsa->q, ctx))
goto err; goto err;
/* s:= s * blind^-1 mod q */
if (BN_mod_inverse(blind, blind, dsa->q, ctx) == NULL)
goto err;
if (!BN_mod_mul(ret->s, ret->s, blind, dsa->q, ctx))
goto err;
/* /*
* Redo if r or s is zero as required by FIPS 186-3: this is very * Redo if r or s is zero as required by FIPS 186-3: this is very
* unlikely. * unlikely.
@ -128,8 +162,6 @@ static DSA_SIG *dsa_do_sign(const unsigned char *dgst, int dlen, DSA *dsa)
ret = NULL; ret = NULL;
} }
BN_CTX_free(ctx); BN_CTX_free(ctx);
BN_clear_free(m);
BN_clear_free(xr);
BN_clear_free(kinv); BN_clear_free(kinv);
return ret; return ret;
} }

View File

@ -288,7 +288,7 @@ ECDSA_SIG *ossl_ecdsa_sign_sig(const unsigned char *dgst, int dgst_len,
* *
* We will blind this to protect against side channel attacks * We will blind this to protect against side channel attacks
* *
* s := k^-1 * blind^-1 * (blind * m + blind * r * priv_key) mod order * s := blind^-1 * k^-1 * (blind * m + blind * r * priv_key) mod order
*/ */
/* Generate a blinding value */ /* Generate a blinding value */
@ -323,6 +323,12 @@ ECDSA_SIG *ossl_ecdsa_sign_sig(const unsigned char *dgst, int dgst_len,
goto err; goto err;
} }
/* s := s * k^-1 mod order */
if (!BN_mod_mul(s, s, ckinv, order, ctx)) {
ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_BN_LIB);
goto err;
}
/* s:= s * blind^-1 mod order */ /* s:= s * blind^-1 mod order */
if (BN_mod_inverse(blind, blind, order, ctx) == NULL) { if (BN_mod_inverse(blind, blind, order, ctx) == NULL) {
ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_BN_LIB); ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_BN_LIB);
@ -333,12 +339,6 @@ ECDSA_SIG *ossl_ecdsa_sign_sig(const unsigned char *dgst, int dgst_len,
goto err; goto err;
} }
/* s := s * k^-1 mod order */
if (!BN_mod_mul(s, s, ckinv, order, ctx)) {
ECerr(EC_F_OSSL_ECDSA_SIGN_SIG, ERR_R_BN_LIB);
goto err;
}
if (BN_is_zero(s)) { if (BN_is_zero(s)) {
/* /*
* if kinv and r have been supplied by the caller, don't * if kinv and r have been supplied by the caller, don't