From 0d39e4120bc5ece53c86c5802c546259b8ca286a Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Fri, 12 Jan 2024 17:56:58 +0900 Subject: [PATCH] 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 | 71 +++++++++----------------- lib/nettle/int/ecdsa-compute-k.h | 8 +-- lib/nettle/pk.c | 79 ++++++++++++++++++++--------- tests/sign-verify-deterministic.c | 2 +- 6 files changed, 158 insertions(+), 117 deletions(-) diff --git a/lib/nettle/int/dsa-compute-k.c b/lib/nettle/int/dsa-compute-k.c index 17d63318c4..ddeb6f6d1e 100644 --- a/lib/nettle/int/dsa-compute-k.c +++ b/lib/nettle/int/dsa-compute-k.c @@ -31,33 +31,37 @@ #include "mpn-base256.h" #include -#define BITS_TO_LIMBS(bits) (((bits) + GMP_NUMB_BITS - 1) / GMP_NUMB_BITS) - -/* The maximum size of q, choosen 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) +/* 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]; + + 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; @@ -66,6 +70,7 @@ _gnutls_dsa_compute_k(mpz_t k, 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); @@ -73,7 +78,7 @@ _gnutls_dsa_compute_k(mpz_t k, 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); @@ -97,12 +102,12 @@ _gnutls_dsa_compute_k(mpz_t k, 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); @@ -178,12 +183,8 @@ _gnutls_dsa_compute_k(mpz_t k, 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) @@ -207,3 +208,24 @@ _gnutls_dsa_compute_k(mpz_t k, 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); +} diff --git a/lib/nettle/int/dsa-compute-k.h b/lib/nettle/int/dsa-compute-k.h index 64e90e0ca2..e88fce0a6d 100644 --- a/lib/nettle/int/dsa-compute-k.h +++ b/lib/nettle/int/dsa-compute-k.h @@ -26,12 +26,29 @@ #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 */ diff --git a/lib/nettle/int/ecdsa-compute-k.c b/lib/nettle/int/ecdsa-compute-k.c index 94914ebdfa..819302c1c7 100644 --- a/lib/nettle/int/ecdsa-compute-k.c +++ b/lib/nettle/int/ecdsa-compute-k.c @@ -29,67 +29,46 @@ #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, - "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836" - "146BC9B1B4D22831", - 16); + mpz_set_str(q, + "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836" + "146BC9B1B4D22831", + 16); return 0; case GNUTLS_ECC_CURVE_SECP224R1: - mpz_init_set_str(*q, - "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2" - "E0B8F03E13DD29455C5C2A3D", - 16); + mpz_set_str(q, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2" + "E0B8F03E13DD29455C5C2A3D", + 16); return 0; #endif case GNUTLS_ECC_CURVE_SECP256R1: - mpz_init_set_str(*q, - "FFFFFFFF00000000FFFFFFFFFFFFFFFF" - "BCE6FAADA7179E84F3B9CAC2FC632551", - 16); + mpz_set_str(q, + "FFFFFFFF00000000FFFFFFFFFFFFFFFF" + "BCE6FAADA7179E84F3B9CAC2FC632551", + 16); return 0; case GNUTLS_ECC_CURVE_SECP384R1: - mpz_init_set_str(*q, - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - "FFFFFFFFFFFFFFFFC7634D81F4372DDF" - "581A0DB248B0A77AECEC196ACCC52973", - 16); + mpz_set_str(q, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFC7634D81F4372DDF" + "581A0DB248B0A77AECEC196ACCC52973", + 16); return 0; case GNUTLS_ECC_CURVE_SECP521R1: - mpz_init_set_str(*q, - "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - "FFA51868783BF2F966B7FCC0148F709A" - "5D03BB5C9B8899C47AEBB6FB71E91386" - "409", - 16); + mpz_set_str(q, + "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFA51868783BF2F966B7FCC0148F709A" + "5D03BB5C9B8899C47AEBB6FB71E91386" + "409", + 16); 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; -} diff --git a/lib/nettle/int/ecdsa-compute-k.h b/lib/nettle/int/ecdsa-compute-k.h index 7ca401d6e4..a7e612bcab 100644 --- a/lib/nettle/int/ecdsa-compute-k.h +++ b/lib/nettle/int/ecdsa-compute-k.h @@ -26,12 +26,6 @@ #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 */ diff --git a/lib/nettle/pk.c b/lib/nettle/pk.c index 588e9df502..b19fe3804a 100644 --- a/lib/nettle/pk.c +++ b/lib/nettle/pk.c @@ -102,10 +102,16 @@ static void rnd_nonce_func(void *_ctx, size_t length, uint8_t * data) } } -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) @@ -976,7 +982,10 @@ _wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo, 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; @@ -1005,19 +1014,32 @@ _wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo, hash_len = vdata->size; } - 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; @@ -1038,7 +1060,7 @@ _wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo, ecdsa_cleanup: dsa_signature_clear(&sig); ecc_scalar_zclear(&priv); - mpz_clear(k); + mpz_clear(q); if (ret < 0) { gnutls_assert(); @@ -1051,7 +1073,9 @@ _wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo, 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; @@ -1074,21 +1098,27 @@ _wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo, 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; @@ -1108,7 +1138,6 @@ _wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo, dsa_fail: dsa_signature_clear(&sig); - mpz_clear(k); if (ret < 0) { gnutls_assert(); diff --git a/tests/sign-verify-deterministic.c b/tests/sign-verify-deterministic.c index 6e907288ee..25aa553a59 100644 --- a/tests/sign-verify-deterministic.c +++ b/tests/sign-verify-deterministic.c @@ -197,7 +197,7 @@ void doit(void) &signature); if (ret < 0) testfail("gnutls_pubkey_verify_data2\n"); - success(" - pass"); + success(" - pass\n"); next: gnutls_free(signature.data); -- 2.44.0