diff --git a/src/common/sae.c b/src/common/sae.c index 8129a7c15..d55323bcd 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,40 +519,49 @@ 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. */ - /* - * 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); - } + /* 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) @@ -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; }