Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate EC/DSA nonces in a way that does not reveal whether top bits are set #24265

Closed
wants to merge 8 commits into from
51 changes: 46 additions & 5 deletions crypto/bn/bn_lib.c
Expand Up @@ -708,14 +708,29 @@ int BN_ucmp(const BIGNUM *a, const BIGNUM *b)
int i;
BN_ULONG t1, t2, *ap, *bp;

ap = a->d;
bp = b->d;

if (BN_get_flags(a, BN_FLG_CONSTTIME)
&& a->top == b->top) {
int res = 0;

for (i = 0; i < b->top; i++) {
res = constant_time_select_int(constant_time_lt_bn(ap[i], bp[i]),
-1, res);
res = constant_time_select_int(constant_time_lt_bn(bp[i], ap[i]),
1, res);
}
return res;
}

bn_check_top(a);
bn_check_top(b);

i = a->top - b->top;
if (i != 0)
return i;
ap = a->d;
bp = b->d;

for (i = a->top - 1; i >= 0; i--) {
t1 = ap[i];
t2 = bp[i];
Expand Down Expand Up @@ -827,11 +842,10 @@ int BN_is_bit_set(const BIGNUM *a, int n)
return (int)(((a->d[i]) >> j) & ((BN_ULONG)1));
}

int BN_mask_bits(BIGNUM *a, int n)
int ossl_bn_mask_bits_fixed_top(BIGNUM *a, int n)
{
int b, w;

bn_check_top(a);
if (n < 0)
return 0;

Expand All @@ -845,10 +859,21 @@ int BN_mask_bits(BIGNUM *a, int n)
a->top = w + 1;
a->d[w] &= ~(BN_MASK2 << b);
}
bn_correct_top(a);
a->flags |= BN_FLG_FIXED_TOP;
return 1;
}

int BN_mask_bits(BIGNUM *a, int n)
{
int ret;

bn_check_top(a);
ret = ossl_bn_mask_bits_fixed_top(a, n);
if (ret)
bn_correct_top(a);
return ret;
}

void BN_set_negative(BIGNUM *a, int b)
{
if (b && !BN_is_zero(a))
Expand Down Expand Up @@ -1022,6 +1047,22 @@ int BN_is_word(const BIGNUM *a, const BN_ULONG w)
return BN_abs_is_word(a, w) && (!w || !a->neg);
}

int ossl_bn_is_word_fixed_top(const BIGNUM *a, const BN_ULONG w)
{
int res, i;
const BN_ULONG *ap = a->d;

if (a->neg || a->top == 0)
return 0;

res = constant_time_select_int(constant_time_eq_bn(ap[0], w), 1, 0);

for (i = 1; i < a->top; i++)
res = constant_time_select_int(constant_time_is_zero_bn(ap[i]),
res, 0);
return res;
}

int BN_is_odd(const BIGNUM *a)
{
return (a->top > 0) && (a->d[0] & 1);
Expand Down
123 changes: 90 additions & 33 deletions crypto/bn/bn_rand.c
Expand Up @@ -184,8 +184,8 @@ static int bnrand_range(BNRAND_FLAG flag, BIGNUM *r, const BIGNUM *range,
} else {
do {
/* range = 11..._2 or range = 101..._2 */
if (!bnrand(flag, r, n, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY, 0,
ctx))
if (!bnrand(flag, r, n, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY,
strength, ctx))
return 0;

if (!--count) {
Expand Down Expand Up @@ -238,6 +238,47 @@ int BN_pseudo_rand_range(BIGNUM *r, const BIGNUM *range)
# endif
#endif

int ossl_bn_priv_rand_range_fixed_top(BIGNUM *r, const BIGNUM *range,
unsigned int strength, BN_CTX *ctx)
{
int n;
int count = 100;

if (r == NULL) {
ERR_raise(ERR_LIB_BN, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}

if (range->neg || BN_is_zero(range)) {
ERR_raise(ERR_LIB_BN, BN_R_INVALID_RANGE);
return 0;
}

n = BN_num_bits(range); /* n > 0 */

/* BN_is_bit_set(range, n - 1) always holds */

if (n == 1) {
BN_zero(r);
} else {
BN_set_flags(r, BN_FLG_CONSTTIME);
do {
if (!bnrand(PRIVATE, r, n + 1, BN_RAND_TOP_ONE, BN_RAND_BOTTOM_ANY,
strength, ctx))
return 0;

if (!--count) {
ERR_raise(ERR_LIB_BN, BN_R_TOO_MANY_ITERATIONS);
return 0;
}
ossl_bn_mask_bits_fixed_top(r, n);
}
while (BN_ucmp(r, range) >= 0);
}

return 1;
}

/*
* BN_generate_dsa_nonce generates a random number 0 <= out < range. Unlike
* BN_rand_range, it also includes the contents of |priv| and |message| in
Expand All @@ -258,20 +299,22 @@ int BN_generate_dsa_nonce(BIGNUM *out, const BIGNUM *range,
unsigned char random_bytes[64];
unsigned char digest[SHA512_DIGEST_LENGTH];
unsigned done, todo;
/* We generate |range|+8 bytes of random output. */
const unsigned num_k_bytes = BN_num_bytes(range) + 8;
/* We generate |range|+1 bytes of random output. */
const unsigned num_k_bytes = BN_num_bytes(range) + 1;
unsigned char private_bytes[96];
unsigned char *k_bytes = NULL;
const int max_n = 64; /* Pr(failure to generate) < 2^max_n */
int n;
int ret = 0;
EVP_MD *md = NULL;
OSSL_LIB_CTX *libctx = ossl_bn_get_libctx(ctx);

if (mdctx == NULL)
goto err;
goto end;

k_bytes = OPENSSL_malloc(num_k_bytes);
if (k_bytes == NULL)
goto err;
goto end;

/* We copy |priv| into a local buffer to avoid exposing its length. */
if (BN_bn2binpad(priv, private_bytes, sizeof(private_bytes)) < 0) {
Expand All @@ -281,41 +324,55 @@ int BN_generate_dsa_nonce(BIGNUM *out, const BIGNUM *range,
* length of the private key.
*/
ERR_raise(ERR_LIB_BN, BN_R_PRIVATE_KEY_TOO_LARGE);
goto err;
goto end;
}

md = EVP_MD_fetch(libctx, "SHA512", NULL);
if (md == NULL) {
ERR_raise(ERR_LIB_BN, BN_R_NO_SUITABLE_DIGEST);
goto err;
}
for (done = 0; done < num_k_bytes;) {
if (RAND_priv_bytes_ex(libctx, random_bytes, sizeof(random_bytes), 0) <= 0)
goto err;

if (!EVP_DigestInit_ex(mdctx, md, NULL)
|| !EVP_DigestUpdate(mdctx, &done, sizeof(done))
|| !EVP_DigestUpdate(mdctx, private_bytes,
sizeof(private_bytes))
|| !EVP_DigestUpdate(mdctx, message, message_len)
|| !EVP_DigestUpdate(mdctx, random_bytes, sizeof(random_bytes))
|| !EVP_DigestFinal_ex(mdctx, digest, NULL))
goto err;

todo = num_k_bytes - done;
if (todo > SHA512_DIGEST_LENGTH)
todo = SHA512_DIGEST_LENGTH;
memcpy(k_bytes + done, digest, todo);
done += todo;
goto end;
}
for (n = 0; n < max_n; n++) {
for (done = 0; done < num_k_bytes;) {
t8m marked this conversation as resolved.
Show resolved Hide resolved
if (RAND_priv_bytes_ex(libctx, random_bytes, sizeof(random_bytes),
0) <= 0)
goto end;

if (!EVP_DigestInit_ex(mdctx, md, NULL)
|| !EVP_DigestUpdate(mdctx, &done, sizeof(done))
|| !EVP_DigestUpdate(mdctx, private_bytes,
sizeof(private_bytes))
|| !EVP_DigestUpdate(mdctx, message, message_len)
|| !EVP_DigestUpdate(mdctx, random_bytes,
sizeof(random_bytes))
|| !EVP_DigestFinal_ex(mdctx, digest, NULL))
goto end;

todo = num_k_bytes - done;
if (todo > SHA512_DIGEST_LENGTH)
todo = SHA512_DIGEST_LENGTH;
memcpy(k_bytes + done, digest, todo);
done += todo;
}

if (!BN_bin2bn(k_bytes, num_k_bytes, out))
goto err;
if (BN_mod(out, out, range, ctx) != 1)
goto err;
ret = 1;
/* Ensure top byte is set to avoid non-constant time in bin2bn */
k_bytes[0] = 0x80;
if (!BN_bin2bn(k_bytes, num_k_bytes, out))
goto end;

err:
/* Clear out the top bits and rejection filter into range */
BN_set_flags(out, BN_FLG_CONSTTIME);
ossl_bn_mask_bits_fixed_top(out, BN_num_bits(range));

if (BN_ucmp(out, range) < 0) {
ret = 1;
t8m marked this conversation as resolved.
Show resolved Hide resolved
goto end;
}
}
/* Failed to generate anything */
ERR_raise(ERR_LIB_BN, ERR_R_INTERNAL_ERROR);

end:
EVP_MD_CTX_free(mdctx);
EVP_MD_free(md);
OPENSSL_clear_free(k_bytes, num_k_bytes);
Expand Down
6 changes: 3 additions & 3 deletions crypto/bn/bn_shift.c
Expand Up @@ -156,6 +156,9 @@ int BN_rshift(BIGNUM *r, const BIGNUM *a, int n)
return 0;
}

bn_check_top(r);
bn_check_top(a);

ret = bn_rshift_fixed_top(r, a, n);

bn_correct_top(r);
Expand All @@ -177,9 +180,6 @@ int bn_rshift_fixed_top(BIGNUM *r, const BIGNUM *a, int n)
BN_ULONG *t, *f;
BN_ULONG l, m, mask;

bn_check_top(r);
bn_check_top(a);

assert(n >= 0);

nw = n / BN_BITS2;
Expand Down
50 changes: 44 additions & 6 deletions crypto/deterministic_nonce.c
Expand Up @@ -7,11 +7,13 @@
* https://www.openssl.org/source/license.html
*/

#include <string.h>
#include <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/core_names.h>
#include <openssl/kdf.h>
#include "internal/deterministic_nonce.h"
#include "crypto/bn.h"

/*
* Convert a Bit String to an Integer (See RFC 6979 Section 2.3.2)
Expand All @@ -38,6 +40,36 @@ static int bits2int(BIGNUM *out, int qlen_bits,
return 1;
}

/*
* Convert as above a Bit String in const time to an Integer w fixed top
*
* Params:
* out The returned Integer as a BIGNUM
* qlen_bits The maximum size of the returned integer in bits. The returned
* Integer is shifted right if inlen is larger than qlen_bits..
* in, inlen The input Bit String (in bytes). It has sizeof(BN_ULONG) bytes
* prefix with all bits set that needs to be cleared out after
* the conversion.
* Returns: 1 if successful, or 0 otherwise.
*/
static int bits2int_consttime(BIGNUM *out, int qlen_bits,
const unsigned char *in, size_t inlen)
{
int blen_bits = (inlen - sizeof(BN_ULONG)) * 8;
int shift;

if (BN_bin2bn(in, (int)inlen, out) == NULL)
return 0;

BN_set_flags(out, BN_FLG_CONSTTIME);
ossl_bn_mask_bits_fixed_top(out, blen_bits);

shift = blen_bits - qlen_bits;
if (shift > 0)
return bn_rshift_fixed_top(out, out, shift);
return 1;
}

/*
* Convert an Integer to an Octet String (See RFC 6979 2.3.3).
* The value is zero padded if required.
Expand Down Expand Up @@ -155,8 +187,9 @@ int ossl_gen_deterministic_nonce_rfc6979(BIGNUM *out, const BIGNUM *q,
{
EVP_KDF_CTX *kdfctx = NULL;
int ret = 0, rlen = 0, qlen_bits = 0;
unsigned char *entropyx = NULL, *nonceh = NULL, *T = NULL;
unsigned char *entropyx = NULL, *nonceh = NULL, *rbits = NULL, *T = NULL;
size_t allocsz = 0;
const size_t prefsz = sizeof(BN_ULONG);

if (out == NULL)
return 0;
Expand All @@ -167,15 +200,18 @@ int ossl_gen_deterministic_nonce_rfc6979(BIGNUM *out, const BIGNUM *q,

/* Note rlen used here is in bytes since the input values are byte arrays */
rlen = (qlen_bits + 7) / 8;
allocsz = 3 * rlen;
allocsz = prefsz + 3 * rlen;

/* Use a single alloc for the buffers T, nonceh and entropyx */
T = (unsigned char *)OPENSSL_zalloc(allocsz);
if (T == NULL)
return 0;
nonceh = T + rlen;
rbits = T + prefsz;
nonceh = rbits + rlen;
entropyx = nonceh + rlen;

memset(T, 0xff, prefsz);
t8m marked this conversation as resolved.
Show resolved Hide resolved

if (!int2octets(entropyx, priv, rlen)
|| !bits2octets(nonceh, q, qlen_bits, rlen, hm, hmlen))
goto end;
Expand All @@ -185,10 +221,12 @@ int ossl_gen_deterministic_nonce_rfc6979(BIGNUM *out, const BIGNUM *q,
goto end;

do {
if (!EVP_KDF_derive(kdfctx, T, rlen, NULL)
|| !bits2int(out, qlen_bits, T, rlen))
if (!EVP_KDF_derive(kdfctx, rbits, rlen, NULL)
|| !bits2int_consttime(out, qlen_bits, T, rlen + prefsz))
goto end;
} while (BN_is_zero(out) || BN_is_one(out) || BN_cmp(out, q) >= 0);
} while (ossl_bn_is_word_fixed_top(out, 0)
|| ossl_bn_is_word_fixed_top(out, 1)
|| BN_ucmp(out, q) >= 0);
ret = 1;

end:
Expand Down
4 changes: 2 additions & 2 deletions crypto/dsa/dsa_ossl.c
Expand Up @@ -286,9 +286,9 @@ static int dsa_sign_setup(DSA *dsa, BN_CTX *ctx_in,
dlen, ctx))
goto err;
}
} else if (!BN_priv_rand_range_ex(k, dsa->params.q, 0, ctx))
} else if (!ossl_bn_priv_rand_range_fixed_top(k, dsa->params.q, 0, ctx))
goto err;
} while (BN_is_zero(k));
} while (ossl_bn_is_word_fixed_top(k, 0));

BN_set_flags(k, BN_FLG_CONSTTIME);
BN_set_flags(l, BN_FLG_CONSTTIME);
Expand Down