diff options
Diffstat (limited to 'package/network/services/hostapd/patches/061-0005-SAE-Minimize-timing-differences-in-PWE-derivation.patch')
-rw-r--r-- | package/network/services/hostapd/patches/061-0005-SAE-Minimize-timing-differences-in-PWE-derivation.patch | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/package/network/services/hostapd/patches/061-0005-SAE-Minimize-timing-differences-in-PWE-derivation.patch b/package/network/services/hostapd/patches/061-0005-SAE-Minimize-timing-differences-in-PWE-derivation.patch new file mode 100644 index 0000000000..e72a9cbe5a --- /dev/null +++ b/package/network/services/hostapd/patches/061-0005-SAE-Minimize-timing-differences-in-PWE-derivation.patch @@ -0,0 +1,242 @@ +From 6513db3e96c43c2e36805cf5ead349765d18eaf7 Mon Sep 17 00:00:00 2001 +From: Jouni Malinen <jouni@codeaurora.org> +Date: Tue, 26 Feb 2019 13:05:09 +0200 +Subject: [PATCH 05/14] SAE: Minimize timing differences in PWE derivation + +The QR test result can provide information about the password to an +attacker, so try to minimize differences in how the +sae_test_pwd_seed_ecc() result is used. (CVE-2019-9494) + +Use heap memory for the dummy password to allow the same password length +to be used even with long passwords. + +Use constant time selection functions to track the real vs. dummy +variables so that the exact same operations can be performed for both QR +test results. + +Signed-off-by: Jouni Malinen <jouni@codeaurora.org> +--- + src/common/sae.c | 106 ++++++++++++++++++++++++++++++------------------------- + 1 file changed, 57 insertions(+), 49 deletions(-) + +--- a/src/common/sae.c ++++ b/src/common/sae.c +@@ -9,6 +9,7 @@ + #include "includes.h" + + #include "common.h" ++#include "utils/const_time.h" + #include "crypto/crypto.h" + #include "crypto/sha256.h" + #include "crypto/random.h" +@@ -269,15 +270,12 @@ static int sae_test_pwd_seed_ecc(struct + const u8 *prime, + const struct crypto_bignum *qr, + const struct crypto_bignum *qnr, +- struct crypto_bignum **ret_x_cand) ++ u8 *pwd_value) + { +- u8 pwd_value[SAE_MAX_ECC_PRIME_LEN]; + struct crypto_bignum *y_sqr, *x_cand; + int res; + size_t bits; + +- *ret_x_cand = NULL; +- + wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); + + /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ +@@ -286,7 +284,7 @@ static int sae_test_pwd_seed_ecc(struct + prime, sae->tmp->prime_len, pwd_value, bits) < 0) + return -1; + if (bits % 8) +- buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8); ++ buf_shift_right(pwd_value, sae->tmp->prime_len, 8 - bits % 8); + wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", + pwd_value, sae->tmp->prime_len); + +@@ -297,20 +295,13 @@ static int sae_test_pwd_seed_ecc(struct + if (!x_cand) + return -1; + y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand); +- if (!y_sqr) { +- crypto_bignum_deinit(x_cand, 1); ++ crypto_bignum_deinit(x_cand, 1); ++ if (!y_sqr) + return -1; +- } + + res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr); + crypto_bignum_deinit(y_sqr, 1); +- if (res <= 0) { +- crypto_bignum_deinit(x_cand, 1); +- return res; +- } +- +- *ret_x_cand = x_cand; +- return 1; ++ return res; + } + + +@@ -431,25 +422,30 @@ static int sae_derive_pwe_ecc(struct sae + const u8 *addr[3]; + size_t len[3]; + size_t num_elem; +- u8 dummy_password[32]; +- size_t dummy_password_len; ++ u8 *dummy_password, *tmp_password; + int pwd_seed_odd = 0; + u8 prime[SAE_MAX_ECC_PRIME_LEN]; + size_t prime_len; +- struct crypto_bignum *x = NULL, *qr, *qnr; ++ struct crypto_bignum *x = NULL, *qr = NULL, *qnr = NULL; ++ u8 x_bin[SAE_MAX_ECC_PRIME_LEN]; ++ u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN]; + size_t bits; +- int res; +- +- dummy_password_len = password_len; +- if (dummy_password_len > sizeof(dummy_password)) +- dummy_password_len = sizeof(dummy_password); +- if (random_get_bytes(dummy_password, dummy_password_len) < 0) +- return -1; ++ int res = -1; ++ u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_* ++ * mask */ ++ ++ os_memset(x_bin, 0, sizeof(x_bin)); ++ ++ dummy_password = os_malloc(password_len); ++ tmp_password = os_malloc(password_len); ++ if (!dummy_password || !tmp_password || ++ random_get_bytes(dummy_password, password_len) < 0) ++ goto fail; + + prime_len = sae->tmp->prime_len; + if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), + prime_len) < 0) +- return -1; ++ goto fail; + bits = crypto_ec_prime_len_bits(sae->tmp->ec); + + /* +@@ -458,7 +454,7 @@ static int sae_derive_pwe_ecc(struct sae + */ + if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits, + &qr, &qnr) < 0) +- return -1; ++ goto fail; + + wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", + password, password_len); +@@ -474,7 +470,7 @@ static int sae_derive_pwe_ecc(struct sae + */ + sae_pwd_seed_key(addr1, addr2, addrs); + +- addr[0] = password; ++ addr[0] = tmp_password; + len[0] = password_len; + num_elem = 1; + if (identifier) { +@@ -491,9 +487,8 @@ static int sae_derive_pwe_ecc(struct sae + * attacks that attempt to determine the number of iterations required + * in the loop. + */ +- for (counter = 1; counter <= k || !x; counter++) { ++ for (counter = 1; counter <= k || !found; counter++) { + u8 pwd_seed[SHA256_MAC_LEN]; +- struct crypto_bignum *x_cand; + + if (counter > 200) { + /* This should not happen in practice */ +@@ -501,40 +496,49 @@ static int sae_derive_pwe_ecc(struct sae + break; + } + +- wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); ++ wpa_printf(MSG_DEBUG, "SAE: counter = %03u", counter); ++ const_time_select_bin(found, dummy_password, password, ++ password_len, tmp_password); + if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem, + addr, len, pwd_seed) < 0) + break; + + res = sae_test_pwd_seed_ecc(sae, pwd_seed, +- prime, qr, qnr, &x_cand); ++ prime, qr, qnr, x_cand_bin); ++ const_time_select_bin(found, x_bin, x_cand_bin, prime_len, ++ x_bin); ++ pwd_seed_odd = const_time_select_u8( ++ found, pwd_seed_odd, ++ pwd_seed[SHA256_MAC_LEN - 1] & 0x01); ++ os_memset(pwd_seed, 0, sizeof(pwd_seed)); + if (res < 0) + goto fail; +- if (res > 0 && !x) { +- wpa_printf(MSG_DEBUG, +- "SAE: Selected pwd-seed with counter %u", +- counter); +- x = x_cand; +- pwd_seed_odd = pwd_seed[SHA256_MAC_LEN - 1] & 0x01; +- os_memset(pwd_seed, 0, sizeof(pwd_seed)); +- +- /* +- * Use a dummy password for the following rounds, if +- * any. +- */ +- addr[0] = dummy_password; +- len[0] = dummy_password_len; +- } else if (res > 0) { +- crypto_bignum_deinit(x_cand, 1); +- } ++ /* Need to minimize differences in handling res == 0 and 1 here ++ * to avoid differences in timing and instruction cache access, ++ * so use const_time_select_*() to make local copies of the ++ * values based on whether this loop iteration was the one that ++ * found the pwd-seed/x. */ ++ ++ /* found is 0 or 0xff here and res is 0 or 1. Bitwise OR of them ++ * (with res converted to 0/0xff) handles this in constant time. ++ */ ++ found |= res * 0xff; ++ wpa_printf(MSG_DEBUG, "SAE: pwd-seed result %d found=0x%02x", ++ res, found); + } + +- if (!x) { ++ if (!found) { + wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE"); + res = -1; + goto fail; + } + ++ x = crypto_bignum_init_set(x_bin, prime_len); ++ if (!x) { ++ res = -1; ++ goto fail; ++ } ++ + if (!sae->tmp->pwe_ecc) + sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec); + if (!sae->tmp->pwe_ecc) +@@ -543,7 +547,6 @@ static int sae_derive_pwe_ecc(struct sae + res = crypto_ec_point_solve_y_coord(sae->tmp->ec, + sae->tmp->pwe_ecc, x, + pwd_seed_odd); +- crypto_bignum_deinit(x, 1); + if (res < 0) { + /* + * This should not happen since we already checked that there +@@ -555,6 +558,11 @@ static int sae_derive_pwe_ecc(struct sae + fail: + crypto_bignum_deinit(qr, 0); + crypto_bignum_deinit(qnr, 0); ++ os_free(dummy_password); ++ bin_clear_free(tmp_password, password_len); ++ crypto_bignum_deinit(x, 1); ++ os_memset(x_bin, 0, sizeof(x_bin)); ++ os_memset(x_cand_bin, 0, sizeof(x_cand_bin)); + + return res; + } |