1 From 6513db3e96c43c2e36805cf5ead349765d18eaf7 Mon Sep 17 00:00:00 2001
2 From: Jouni Malinen <jouni@codeaurora.org>
3 Date: Tue, 26 Feb 2019 13:05:09 +0200
4 Subject: [PATCH 05/14] SAE: Minimize timing differences in PWE derivation
6 The QR test result can provide information about the password to an
7 attacker, so try to minimize differences in how the
8 sae_test_pwd_seed_ecc() result is used. (CVE-2019-9494)
10 Use heap memory for the dummy password to allow the same password length
11 to be used even with long passwords.
13 Use constant time selection functions to track the real vs. dummy
14 variables so that the exact same operations can be performed for both QR
17 Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
19 src/common/sae.c | 106 ++++++++++++++++++++++++++++++-------------------------
20 1 file changed, 57 insertions(+), 49 deletions(-)
22 --- a/src/common/sae.c
23 +++ b/src/common/sae.c
28 +#include "utils/const_time.h"
29 #include "crypto/crypto.h"
30 #include "crypto/sha256.h"
31 #include "crypto/random.h"
32 @@ -269,15 +270,12 @@ static int sae_test_pwd_seed_ecc(struct
34 const struct crypto_bignum *qr,
35 const struct crypto_bignum *qnr,
36 - struct crypto_bignum **ret_x_cand)
39 - u8 pwd_value[SAE_MAX_ECC_PRIME_LEN];
40 struct crypto_bignum *y_sqr, *x_cand;
46 wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
48 /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
49 @@ -286,7 +284,7 @@ static int sae_test_pwd_seed_ecc(struct
50 prime, sae->tmp->prime_len, pwd_value, bits) < 0)
53 - buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8);
54 + buf_shift_right(pwd_value, sae->tmp->prime_len, 8 - bits % 8);
55 wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
56 pwd_value, sae->tmp->prime_len);
58 @@ -297,20 +295,13 @@ static int sae_test_pwd_seed_ecc(struct
61 y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand);
63 - crypto_bignum_deinit(x_cand, 1);
64 + crypto_bignum_deinit(x_cand, 1);
69 res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr);
70 crypto_bignum_deinit(y_sqr, 1);
72 - crypto_bignum_deinit(x_cand, 1);
76 - *ret_x_cand = x_cand;
82 @@ -431,25 +422,30 @@ static int sae_derive_pwe_ecc(struct sae
86 - u8 dummy_password[32];
87 - size_t dummy_password_len;
88 + u8 *dummy_password, *tmp_password;
90 u8 prime[SAE_MAX_ECC_PRIME_LEN];
92 - struct crypto_bignum *x = NULL, *qr, *qnr;
93 + struct crypto_bignum *x = NULL, *qr = NULL, *qnr = NULL;
94 + u8 x_bin[SAE_MAX_ECC_PRIME_LEN];
95 + u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN];
99 - dummy_password_len = password_len;
100 - if (dummy_password_len > sizeof(dummy_password))
101 - dummy_password_len = sizeof(dummy_password);
102 - if (random_get_bytes(dummy_password, dummy_password_len) < 0)
105 + u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
108 + os_memset(x_bin, 0, sizeof(x_bin));
110 + dummy_password = os_malloc(password_len);
111 + tmp_password = os_malloc(password_len);
112 + if (!dummy_password || !tmp_password ||
113 + random_get_bytes(dummy_password, password_len) < 0)
116 prime_len = sae->tmp->prime_len;
117 if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
121 bits = crypto_ec_prime_len_bits(sae->tmp->ec);
124 @@ -458,7 +454,7 @@ static int sae_derive_pwe_ecc(struct sae
126 if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits,
131 wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
132 password, password_len);
133 @@ -474,7 +470,7 @@ static int sae_derive_pwe_ecc(struct sae
135 sae_pwd_seed_key(addr1, addr2, addrs);
137 - addr[0] = password;
138 + addr[0] = tmp_password;
139 len[0] = password_len;
142 @@ -491,9 +487,8 @@ static int sae_derive_pwe_ecc(struct sae
143 * attacks that attempt to determine the number of iterations required
146 - for (counter = 1; counter <= k || !x; counter++) {
147 + for (counter = 1; counter <= k || !found; counter++) {
148 u8 pwd_seed[SHA256_MAC_LEN];
149 - struct crypto_bignum *x_cand;
152 /* This should not happen in practice */
153 @@ -501,40 +496,49 @@ static int sae_derive_pwe_ecc(struct sae
157 - wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
158 + wpa_printf(MSG_DEBUG, "SAE: counter = %03u", counter);
159 + const_time_select_bin(found, dummy_password, password,
160 + password_len, tmp_password);
161 if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
162 addr, len, pwd_seed) < 0)
165 res = sae_test_pwd_seed_ecc(sae, pwd_seed,
166 - prime, qr, qnr, &x_cand);
167 + prime, qr, qnr, x_cand_bin);
168 + const_time_select_bin(found, x_bin, x_cand_bin, prime_len,
170 + pwd_seed_odd = const_time_select_u8(
171 + found, pwd_seed_odd,
172 + pwd_seed[SHA256_MAC_LEN - 1] & 0x01);
173 + os_memset(pwd_seed, 0, sizeof(pwd_seed));
176 - if (res > 0 && !x) {
177 - wpa_printf(MSG_DEBUG,
178 - "SAE: Selected pwd-seed with counter %u",
181 - pwd_seed_odd = pwd_seed[SHA256_MAC_LEN - 1] & 0x01;
182 - os_memset(pwd_seed, 0, sizeof(pwd_seed));
185 - * Use a dummy password for the following rounds, if
188 - addr[0] = dummy_password;
189 - len[0] = dummy_password_len;
190 - } else if (res > 0) {
191 - crypto_bignum_deinit(x_cand, 1);
193 + /* Need to minimize differences in handling res == 0 and 1 here
194 + * to avoid differences in timing and instruction cache access,
195 + * so use const_time_select_*() to make local copies of the
196 + * values based on whether this loop iteration was the one that
197 + * found the pwd-seed/x. */
199 + /* found is 0 or 0xff here and res is 0 or 1. Bitwise OR of them
200 + * (with res converted to 0/0xff) handles this in constant time.
202 + found |= res * 0xff;
203 + wpa_printf(MSG_DEBUG, "SAE: pwd-seed result %d found=0x%02x",
209 wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
214 + x = crypto_bignum_init_set(x_bin, prime_len);
220 if (!sae->tmp->pwe_ecc)
221 sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec);
222 if (!sae->tmp->pwe_ecc)
223 @@ -543,7 +547,6 @@ static int sae_derive_pwe_ecc(struct sae
224 res = crypto_ec_point_solve_y_coord(sae->tmp->ec,
225 sae->tmp->pwe_ecc, x,
227 - crypto_bignum_deinit(x, 1);
230 * This should not happen since we already checked that there
231 @@ -555,6 +558,11 @@ static int sae_derive_pwe_ecc(struct sae
233 crypto_bignum_deinit(qr, 0);
234 crypto_bignum_deinit(qnr, 0);
235 + os_free(dummy_password);
236 + bin_clear_free(tmp_password, password_len);
237 + crypto_bignum_deinit(x, 1);
238 + os_memset(x_bin, 0, sizeof(x_bin));
239 + os_memset(x_cand_bin, 0, sizeof(x_cand_bin));