123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- 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(-)
- diff --git a/src/common/sae.c b/src/common/sae.c
- index 8129a7c..d55323b 100644
- --- 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"
- @@ -292,15 +293,12 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
- 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) */
- @@ -309,7 +307,7 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
- 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);
-
- @@ -320,20 +318,13 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
- 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;
- }
-
-
- @@ -454,25 +445,30 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
- 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;
- + int res = -1;
- + u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
- + * mask */
-
- - 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;
- + 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);
-
- /*
- @@ -481,7 +477,7 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
- */
- 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);
- @@ -497,7 +493,7 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
- */
- sae_pwd_seed_key(addr1, addr2, addrs);
-
- - addr[0] = password;
- + addr[0] = tmp_password;
- len[0] = password_len;
- num_elem = 1;
- if (identifier) {
- @@ -514,9 +510,8 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
- * 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 */
- @@ -524,36 +519,45 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
- 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));
- + /* 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);
- + }
-
- - /*
- - * 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);
- - }
- + 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) {
- - wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
- res = -1;
- goto fail;
- }
- @@ -566,7 +570,6 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
- 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
- @@ -578,6 +581,11 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
- 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;
- }
- --
- 2.7.4
|