diff --git a/SOURCES/gnutls-3.6.16-deterministic-ecdsa-fixes.patch b/SOURCES/gnutls-3.6.16-deterministic-ecdsa-fixes.patch new file mode 100644 index 0000000..a4289ba --- /dev/null +++ b/SOURCES/gnutls-3.6.16-deterministic-ecdsa-fixes.patch @@ -0,0 +1,474 @@ +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 + diff --git a/SPECS/gnutls.spec b/SPECS/gnutls.spec index 6da8117..e0b6ef2 100644 --- a/SPECS/gnutls.spec +++ b/SPECS/gnutls.spec @@ -1,5 +1,5 @@ Version: 3.6.16 -Release: 8%{?dist}.1 +Release: 8%{?dist}.3 Patch1: gnutls-3.2.7-rpath.patch Patch2: gnutls-3.6.4-no-now-guile.patch Patch3: gnutls-3.6.13-enable-intel-cet.patch @@ -14,6 +14,7 @@ Patch17: gnutls-3.7.8-rsa-kx-timing.patch Patch18: gnutls-3.6.16-rehandshake-tickets.patch Patch19: gnutls-3.6.16-rsa-psk-timing.patch Patch20: gnutls-3.6.16-rsa-psk-timing-followup.patch +Patch21: gnutls-3.6.16-deterministic-ecdsa-fixes.patch %bcond_without dane %if 0%{?rhel} %bcond_with guile @@ -298,6 +299,12 @@ fi %endif %changelog +* Tue Mar 26 2024 Daiki Ueno - 3.6.16-8.3 +- Fix memleak with older GMP (RHEL-28957) + +* Mon Mar 25 2024 Daiki Ueno - 3.6.16-8.2 +- Fix timing side-channel in deterministic ECDSA (RHEL-28957) + * Thu Jan 18 2024 Daiki Ueno - 3.6.16-8.1 - auth/rsa-psk: minimize branching after decryption (RHEL-21586)