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
164 changes: 125 additions & 39 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,17 +238,63 @@ 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);
#ifdef BN_DEBUG
/* With BN_DEBUG on a fixed top number cannot be returned */
bn_correct_top(r);
#endif
}

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
* the generation so that an RNG failure isn't fatal as long as |priv|
* ossl_bn_gen_dsa_nonce_fixed_top generates a random number 0 <= out < range.
* Unlike BN_rand_range, it also includes the contents of |priv| and |message|
* in the generation so that an RNG failure isn't fatal as long as |priv|
* remains secret. This is intended for use in DSA and ECDSA where an RNG
* weakness leads directly to private key exposure unless this function is
* used.
*/
int BN_generate_dsa_nonce(BIGNUM *out, const BIGNUM *range,
const BIGNUM *priv, const unsigned char *message,
size_t message_len, BN_CTX *ctx)
int ossl_bn_gen_dsa_nonce_fixed_top(BIGNUM *out, const BIGNUM *range,
const BIGNUM *priv,
const unsigned char *message,
size_t message_len, BN_CTX *ctx)
{
EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
/*
Expand All @@ -258,20 +304,24 @@ 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;
/* Ensure top byte is set to avoid non-constant time in bin2bn */
k_bytes[0] = 0xff;

/* 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 +331,60 @@ 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++) {
unsigned char i = 0;

for (done = 1; done < num_k_bytes;) {
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, &i, sizeof(i))
|| !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;
++i;
}

if (!BN_bin2bn(k_bytes, num_k_bytes, out))
goto err;
if (BN_mod(out, out, range, ctx) != 1)
goto err;
ret = 1;
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
#ifdef BN_DEBUG
/* With BN_DEBUG on a fixed top number cannot be returned */
bn_correct_top(out);
#endif
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 All @@ -324,3 +393,20 @@ int BN_generate_dsa_nonce(BIGNUM *out, const BIGNUM *range,
OPENSSL_cleanse(private_bytes, sizeof(private_bytes));
return ret;
}

int BN_generate_dsa_nonce(BIGNUM *out, const BIGNUM *range,
const BIGNUM *priv, const unsigned char *message,
size_t message_len, BN_CTX *ctx)
{
int ret;

ret = ossl_bn_gen_dsa_nonce_fixed_top(out, range, priv, message,
message_len, ctx);
/*
* This call makes the BN_generate_dsa_nonce non-const-time, thus we
* do not use it internally. But fixed_top BNs currently cannot be returned
* from public API calls.
*/
bn_correct_top(out);
return ret;
}
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