From 6f6c8db008e943967a5d0f706e2884c23baa0aa7 Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Fri, 12 Jan 2024 17:56:58 +0900 Subject: [PATCH 2/5] nettle: avoid normalization of mpz_t in deterministic ECDSA This removes function calls that potentially leak bit-length of a private key used to calculate a nonce in deterministic ECDSA. Namely: - _gnutls_dsa_compute_k has been rewritten to work on always zero-padded mp_limb_t arrays instead of mpz_t - rnd_mpz_func has been replaced with rnd_datum_func, which is backed by a byte array instead of an mpz_t value Signed-off-by: Daiki Ueno --- lib/nettle/int/dsa-compute-k.c | 84 +++++++++++++++++++------------ lib/nettle/int/dsa-compute-k.h | 31 +++++++++--- lib/nettle/int/ecdsa-compute-k.c | 33 +++--------- lib/nettle/int/ecdsa-compute-k.h | 8 +-- lib/nettle/pk.c | 79 ++++++++++++++++++++--------- tests/sign-verify-deterministic.c | 2 +- 6 files changed, 139 insertions(+), 98 deletions(-) --- a/lib/nettle/int/dsa-compute-k.c +++ b/lib/nettle/int/dsa-compute-k.c @@ -29,53 +29,58 @@ #include "gnutls_int.h" #include "mem.h" #include "mpn-base256.h" #include -#define BITS_TO_LIMBS(bits) (((bits) + GMP_NUMB_BITS - 1) / GMP_NUMB_BITS) +/* For mini-gmp */ +#ifndef GMP_LIMB_BITS +#define GMP_LIMB_BITS GMP_NUMB_BITS +#endif + +static inline int is_zero_limb(mp_limb_t x) +{ + x |= (x << 1); + return ((x >> 1) - 1) >> (GMP_LIMB_BITS - 1); +} + +static int sec_zero_p(const mp_limb_t *ap, mp_size_t n) +{ + volatile mp_limb_t w; + mp_size_t i; + + for (i = 0, w = 0; i < n; i++) + w |= ap[i]; -/* The maximum size of q, chosen from the fact that we support - * 521-bit elliptic curve generator and 512-bit DSA subgroup at - * maximum. */ -#define MAX_Q_BITS 521 -#define MAX_Q_SIZE ((MAX_Q_BITS + 7) / 8) -#define MAX_Q_LIMBS BITS_TO_LIMBS(MAX_Q_BITS) - -#define MAX_HASH_BITS (MAX_HASH_SIZE * 8) -#define MAX_HASH_LIMBS BITS_TO_LIMBS(MAX_HASH_BITS) - -int -_gnutls_dsa_compute_k(mpz_t k, - const mpz_t q, - const mpz_t x, - gnutls_mac_algorithm_t mac, - const uint8_t *digest, - size_t length) + return is_zero_limb(w); +} + +int _gnutls_dsa_compute_k(mp_limb_t *h, const mp_limb_t *q, const mp_limb_t *x, + mp_size_t qn, mp_bitcnt_t q_bits, + gnutls_mac_algorithm_t mac, const uint8_t *digest, + size_t length) { uint8_t V[MAX_HASH_SIZE]; uint8_t K[MAX_HASH_SIZE]; uint8_t xp[MAX_Q_SIZE]; uint8_t tp[MAX_Q_SIZE]; - mp_limb_t h[MAX(MAX_Q_LIMBS, MAX_HASH_LIMBS)]; - mp_bitcnt_t q_bits = mpz_sizeinbase (q, 2); - mp_size_t qn = mpz_size(q); mp_bitcnt_t h_bits = length * 8; mp_size_t hn = BITS_TO_LIMBS(h_bits); size_t nbytes = (q_bits + 7) / 8; const uint8_t c0 = 0x00; const uint8_t c1 = 0x01; mp_limb_t cy; gnutls_hmac_hd_t hd; int ret = 0; + mp_limb_t scratch[MAX_Q_LIMBS]; if (unlikely(q_bits > MAX_Q_BITS)) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); if (unlikely(length > MAX_HASH_SIZE)) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); /* int2octets(x) */ - mpn_get_base256(xp, nbytes, mpz_limbs_read(x), qn); + mpn_get_base256(xp, nbytes, x, qn); /* bits2octets(h) */ mpn_set_base256(h, hn, digest, length); if (hn < qn) @@ -95,16 +100,16 @@ _gnutls_dsa_compute_k(mpz_t k, if (shift % GMP_NUMB_BITS > 0) mpn_rshift(h, h, hn, shift % GMP_NUMB_BITS); } - cy = mpn_sub_n(h, h, mpz_limbs_read(q), qn); + cy = mpn_sub_n(h, h, q, qn); /* Fall back to addmul_1, if nettle is linked with mini-gmp. */ #ifdef mpn_cnd_add_n - mpn_cnd_add_n(cy, h, h, mpz_limbs_read(q), qn); + mpn_cnd_add_n(cy, h, h, q, qn); #else - mpn_addmul_1(h, mpz_limbs_read(q), qn, cy != 0); + mpn_addmul_1(h, q, qn, cy != 0); #endif mpn_get_base256(tp, nbytes, h, qn); /* Step b */ memset(V, c1, length); @@ -176,16 +181,12 @@ _gnutls_dsa_compute_k(mpz_t k, /* Step 3 */ mpn_set_base256 (h, qn, tp, tlen); if (tlen * 8 > q_bits) mpn_rshift (h, h, qn, tlen * 8 - q_bits); /* Check if k is in [1,q-1] */ - if (!mpn_zero_p (h, qn) && - mpn_cmp (h, mpz_limbs_read(q), qn) < 0) { - mpn_copyi(mpz_limbs_write(k, qn), h, qn); - mpz_limbs_finish(k, qn); + if (!sec_zero_p(h, qn) && mpn_sub_n(scratch, h, q, qn)) break; - } ret = gnutls_hmac_init(&hd, mac, K, length); if (ret < 0) goto out; ret = gnutls_hmac(hd, V, length); @@ -205,5 +206,26 @@ _gnutls_dsa_compute_k(mpz_t k, zeroize_key(xp, sizeof(xp)); zeroize_key(tp, sizeof(tp)); return ret; } + +/* cancel-out dsa_sign's addition of 1 to random data */ +void _gnutls_dsa_compute_k_finish(uint8_t *k, size_t nbytes, mp_limb_t *h, + mp_size_t n) +{ + /* Fall back to sub_1, if nettle is linked with mini-gmp. */ +#ifdef mpn_sec_sub_1 + mp_limb_t t[MAX_Q_LIMBS]; + + mpn_sec_sub_1(h, h, n, 1, t); +#else + mpn_sub_1(h, h, n, 1); +#endif + mpn_get_base256(k, nbytes, h, n); +} + +void _gnutls_ecdsa_compute_k_finish(uint8_t *k, size_t nbytes, mp_limb_t *h, + mp_size_t n) +{ + mpn_get_base256(k, nbytes, h, n); +} --- a/lib/nettle/int/dsa-compute-k.h +++ b/lib/nettle/int/dsa-compute-k.h @@ -24,14 +24,31 @@ #define GNUTLS_LIB_NETTLE_INT_DSA_COMPUTE_K_H #include #include /* includes gmp.h */ -int -_gnutls_dsa_compute_k(mpz_t k, - const mpz_t q, - const mpz_t x, - gnutls_mac_algorithm_t mac, - const uint8_t *digest, - size_t length); +#define BITS_TO_LIMBS(bits) (((bits) + GMP_NUMB_BITS - 1) / GMP_NUMB_BITS) + +/* The maximum size of q, chosen from the fact that we support + * 521-bit elliptic curve generator and 512-bit DSA subgroup at + * maximum. */ +#define MAX_Q_BITS 521 +#define MAX_Q_SIZE ((MAX_Q_BITS + 7) / 8) +#define MAX_Q_LIMBS BITS_TO_LIMBS(MAX_Q_BITS) + +#define MAX_HASH_BITS (MAX_HASH_SIZE * 8) +#define MAX_HASH_LIMBS BITS_TO_LIMBS(MAX_HASH_BITS) + +#define DSA_COMPUTE_K_ITCH MAX(MAX_Q_LIMBS, MAX_HASH_LIMBS) + +int _gnutls_dsa_compute_k(mp_limb_t *h, const mp_limb_t *q, const mp_limb_t *x, + mp_size_t qn, mp_bitcnt_t q_bits, + gnutls_mac_algorithm_t mac, const uint8_t *digest, + size_t length); + +void _gnutls_dsa_compute_k_finish(uint8_t *k, size_t nbytes, mp_limb_t *h, + mp_size_t n); + +void _gnutls_ecdsa_compute_k_finish(uint8_t *k, size_t nbytes, mp_limb_t *h, + mp_size_t n); #endif /* GNUTLS_LIB_NETTLE_INT_DSA_COMPUTE_K_H */ --- a/lib/nettle/int/ecdsa-compute-k.c +++ b/lib/nettle/int/ecdsa-compute-k.c @@ -27,43 +27,42 @@ #include "ecdsa-compute-k.h" #include "dsa-compute-k.h" #include "gnutls_int.h" -static inline int -_gnutls_ecc_curve_to_dsa_q(mpz_t *q, gnutls_ecc_curve_t curve) +int _gnutls_ecc_curve_to_dsa_q(mpz_t q, gnutls_ecc_curve_t curve) { switch (curve) { #ifdef ENABLE_NON_SUITEB_CURVES case GNUTLS_ECC_CURVE_SECP192R1: - mpz_init_set_str(*q, + mpz_init_set_str(q, "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836" "146BC9B1B4D22831", 16); return 0; case GNUTLS_ECC_CURVE_SECP224R1: - mpz_init_set_str(*q, + mpz_init_set_str(q, "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2" "E0B8F03E13DD29455C5C2A3D", 16); return 0; #endif case GNUTLS_ECC_CURVE_SECP256R1: - mpz_init_set_str(*q, + mpz_init_set_str(q, "FFFFFFFF00000000FFFFFFFFFFFFFFFF" "BCE6FAADA7179E84F3B9CAC2FC632551", 16); return 0; case GNUTLS_ECC_CURVE_SECP384R1: - mpz_init_set_str(*q, + mpz_init_set_str(q, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFC7634D81F4372DDF" "581A0DB248B0A77AECEC196ACCC52973", 16); return 0; case GNUTLS_ECC_CURVE_SECP521R1: - mpz_init_set_str(*q, + mpz_init_set_str(q, "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFA51868783BF2F966B7FCC0148F709A" "5D03BB5C9B8899C47AEBB6FB71E91386" "409", @@ -71,25 +70,5 @@ _gnutls_ecc_curve_to_dsa_q(mpz_t *q, gnu return 0; default: return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM); } } - -int -_gnutls_ecdsa_compute_k (mpz_t k, - gnutls_ecc_curve_t curve, - const mpz_t x, - gnutls_mac_algorithm_t mac, - const uint8_t *digest, - size_t length) -{ - mpz_t q; - int ret; - - ret = _gnutls_ecc_curve_to_dsa_q(&q, curve); - if (ret < 0) - return gnutls_assert_val(ret); - - ret = _gnutls_dsa_compute_k (k, q, x, mac, digest, length); - mpz_clear(q); - return ret; -} --- a/lib/nettle/int/ecdsa-compute-k.h +++ b/lib/nettle/int/ecdsa-compute-k.h @@ -24,14 +24,8 @@ #define GNUTLS_LIB_NETTLE_INT_ECDSA_COMPUTE_K_H #include #include /* includes gmp.h */ -int -_gnutls_ecdsa_compute_k (mpz_t k, - gnutls_ecc_curve_t curve, - const mpz_t x, - gnutls_mac_algorithm_t mac, - const uint8_t *digest, - size_t length); +int _gnutls_ecc_curve_to_dsa_q(mpz_t q, gnutls_ecc_curve_t curve); #endif /* GNUTLS_LIB_NETTLE_INT_ECDSA_COMPUTE_K_H */ --- a/lib/nettle/pk.c +++ b/lib/nettle/pk.c @@ -95,14 +95,20 @@ static void rnd_nonce_func(void *_ctx, s if (gnutls_rnd(GNUTLS_RND_NONCE, data, length) < 0) { _gnutls_switch_lib_state(LIB_STATE_ERROR); } } -static void rnd_mpz_func(void *_ctx, size_t length, uint8_t * data) +static void rnd_datum_func(void *ctx, size_t length, uint8_t *data) { - mpz_t *k = _ctx; - nettle_mpz_get_str_256 (length, data, *k); + gnutls_datum_t *d = ctx; + + if (length > d->size) { + memset(data, 0, length - d->size); + memcpy(data + (length - d->size), d->data, d->size); + } else { + memcpy(data, d->data, length); + } } static void rnd_nonce_func_fallback(void *_ctx, size_t length, uint8_t * data) { if (unlikely(_gnutls_get_lib_state() != LIB_STATE_SELFTEST)) { @@ -1074,11 +1080,14 @@ _wrap_nettle_pk_sign(gnutls_pk_algorithm { struct ecc_scalar priv; struct dsa_signature sig; int curve_id = pk_params->curve; const struct ecc_curve *curve; - mpz_t k; + mpz_t q; + /* 521-bit elliptic curve generator at maximum */ + uint8_t buf[(521 + 7) / 8]; + gnutls_datum_t k = { NULL, 0 }; void *random_ctx; nettle_random_func *random_func; curve = get_supported_nist_curve(curve_id); if (curve == NULL) { @@ -1121,23 +1130,36 @@ _wrap_nettle_pk_sign(gnutls_pk_algorithm break; default: not_approved = true; } - mpz_init(k); + mpz_init(q); + if (_gnutls_get_lib_state() == LIB_STATE_SELFTEST || (sign_params->flags & GNUTLS_PK_FLAG_REPRODUCIBLE)) { - ret = _gnutls_ecdsa_compute_k(k, - curve_id, - pk_params->params[ECC_K], - DIG_TO_MAC(sign_params->dsa_dig), - vdata->data, - vdata->size); + mp_limb_t h[DSA_COMPUTE_K_ITCH]; + + ret = _gnutls_ecc_curve_to_dsa_q(q, curve_id); + if (ret < 0) + goto ecdsa_cleanup; + + ret = _gnutls_dsa_compute_k( + h, mpz_limbs_read(q), priv.p, + ecc_size(priv.ecc), ecc_bit_size(priv.ecc), + DIG_TO_MAC(sign_params->dsa_dig), vdata->data, + vdata->size); if (ret < 0) goto ecdsa_cleanup; + + k.data = buf; + k.size = (ecc_bit_size(priv.ecc) + 7) / 8; + + _gnutls_ecdsa_compute_k_finish(k.data, k.size, h, + ecc_size(priv.ecc)); + random_ctx = &k; - random_func = rnd_mpz_func; + random_func = rnd_datum_func; } else { random_ctx = NULL; random_func = rnd_nonce_func; } ecdsa_sign(&priv, random_ctx, random_func, hash_len, @@ -1154,11 +1176,11 @@ _wrap_nettle_pk_sign(gnutls_pk_algorithm &sig.s); ecdsa_cleanup: dsa_signature_clear(&sig); ecc_scalar_zclear(&priv); - mpz_clear(k); + mpz_clear(q); if (ret < 0) { gnutls_assert(); goto cleanup; } @@ -1167,11 +1189,13 @@ _wrap_nettle_pk_sign(gnutls_pk_algorithm case GNUTLS_PK_DSA: { struct dsa_params pub; bigint_t priv; struct dsa_signature sig; - mpz_t k; + /* 512-bit DSA subgroup at maximum */ + uint8_t buf[(512 + 7) / 8]; + gnutls_datum_t k = { NULL, 0 }; void *random_ctx; nettle_random_func *random_func; /* DSA is currently being defined as sunset with the * current draft of FIPS 186-5 */ @@ -1194,25 +1218,31 @@ _wrap_nettle_pk_sign(gnutls_pk_algorithm ("Security level of algorithm requires hash %s(%d) or better (have: %d)\n", _gnutls_mac_get_name(me), hash_len, (int)vdata->size); hash_len = vdata->size; } - mpz_init(k); if (_gnutls_get_lib_state() == LIB_STATE_SELFTEST || (sign_params->flags & GNUTLS_PK_FLAG_REPRODUCIBLE)) { - ret = _gnutls_dsa_compute_k(k, - pub.q, - TOMPZ(priv), - DIG_TO_MAC(sign_params->dsa_dig), - vdata->data, - vdata->size); + mp_limb_t h[DSA_COMPUTE_K_ITCH]; + + ret = _gnutls_dsa_compute_k( + h, mpz_limbs_read(pub.q), + mpz_limbs_read(TOMPZ(priv)), mpz_size(pub.q), + mpz_sizeinbase(pub.q, 2), + DIG_TO_MAC(sign_params->dsa_dig), vdata->data, + vdata->size); if (ret < 0) goto dsa_fail; - /* cancel-out dsa_sign's addition of 1 to random data */ - mpz_sub_ui (k, k, 1); + + k.data = buf; + k.size = (mpz_sizeinbase(pub.q, 2) + 7) / 8; + + _gnutls_dsa_compute_k_finish(k.data, k.size, h, + mpz_size(pub.q)); + random_ctx = &k; - random_func = rnd_mpz_func; + random_func = rnd_datum_func; } else { random_ctx = NULL; random_func = rnd_nonce_func; } ret = @@ -1228,11 +1258,10 @@ _wrap_nettle_pk_sign(gnutls_pk_algorithm _gnutls_encode_ber_rs(signature, &sig.r, &sig.s); dsa_fail: dsa_signature_clear(&sig); - mpz_clear(k); if (ret < 0) { gnutls_assert(); goto cleanup; } --- a/tests/sign-verify-deterministic.c +++ b/tests/sign-verify-deterministic.c @@ -195,11 +195,11 @@ void doit(void) ret = gnutls_pubkey_verify_data2(pubkey, tests[i].sigalgo, 0, &tests[i].msg, &signature); if (ret < 0) testfail("gnutls_pubkey_verify_data2\n"); - success(" - pass"); + success(" - pass\n"); next: gnutls_free(signature.data); gnutls_privkey_deinit(privkey); gnutls_pubkey_deinit(pubkey);