e72a9cbe5acf1c623e0f4bac2ea02491e8c2b029
[openwrt/staging/blocktrron.git] /
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
5
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)
9
10 Use heap memory for the dummy password to allow the same password length
11 to be used even with long passwords.
12
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
15 test results.
16
17 Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
18 ---
19 src/common/sae.c | 106 ++++++++++++++++++++++++++++++-------------------------
20 1 file changed, 57 insertions(+), 49 deletions(-)
21
22 --- a/src/common/sae.c
23 +++ b/src/common/sae.c
24 @@ -9,6 +9,7 @@
25 #include "includes.h"
26
27 #include "common.h"
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
33 const u8 *prime,
34 const struct crypto_bignum *qr,
35 const struct crypto_bignum *qnr,
36 - struct crypto_bignum **ret_x_cand)
37 + u8 *pwd_value)
38 {
39 - u8 pwd_value[SAE_MAX_ECC_PRIME_LEN];
40 struct crypto_bignum *y_sqr, *x_cand;
41 int res;
42 size_t bits;
43
44 - *ret_x_cand = NULL;
45 -
46 wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
47
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)
51 return -1;
52 if (bits % 8)
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);
57
58 @@ -297,20 +295,13 @@ static int sae_test_pwd_seed_ecc(struct
59 if (!x_cand)
60 return -1;
61 y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand);
62 - if (!y_sqr) {
63 - crypto_bignum_deinit(x_cand, 1);
64 + crypto_bignum_deinit(x_cand, 1);
65 + if (!y_sqr)
66 return -1;
67 - }
68
69 res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr);
70 crypto_bignum_deinit(y_sqr, 1);
71 - if (res <= 0) {
72 - crypto_bignum_deinit(x_cand, 1);
73 - return res;
74 - }
75 -
76 - *ret_x_cand = x_cand;
77 - return 1;
78 + return res;
79 }
80
81
82 @@ -431,25 +422,30 @@ static int sae_derive_pwe_ecc(struct sae
83 const u8 *addr[3];
84 size_t len[3];
85 size_t num_elem;
86 - u8 dummy_password[32];
87 - size_t dummy_password_len;
88 + u8 *dummy_password, *tmp_password;
89 int pwd_seed_odd = 0;
90 u8 prime[SAE_MAX_ECC_PRIME_LEN];
91 size_t 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];
96 size_t bits;
97 - int res;
98 -
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)
103 - return -1;
104 + int res = -1;
105 + u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
106 + * mask */
107 +
108 + os_memset(x_bin, 0, sizeof(x_bin));
109 +
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)
114 + goto fail;
115
116 prime_len = sae->tmp->prime_len;
117 if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
118 prime_len) < 0)
119 - return -1;
120 + goto fail;
121 bits = crypto_ec_prime_len_bits(sae->tmp->ec);
122
123 /*
124 @@ -458,7 +454,7 @@ static int sae_derive_pwe_ecc(struct sae
125 */
126 if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits,
127 &qr, &qnr) < 0)
128 - return -1;
129 + goto fail;
130
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
134 */
135 sae_pwd_seed_key(addr1, addr2, addrs);
136
137 - addr[0] = password;
138 + addr[0] = tmp_password;
139 len[0] = password_len;
140 num_elem = 1;
141 if (identifier) {
142 @@ -491,9 +487,8 @@ static int sae_derive_pwe_ecc(struct sae
143 * attacks that attempt to determine the number of iterations required
144 * in the loop.
145 */
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;
150
151 if (counter > 200) {
152 /* This should not happen in practice */
153 @@ -501,40 +496,49 @@ static int sae_derive_pwe_ecc(struct sae
154 break;
155 }
156
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)
163 break;
164
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,
169 + x_bin);
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));
174 if (res < 0)
175 goto fail;
176 - if (res > 0 && !x) {
177 - wpa_printf(MSG_DEBUG,
178 - "SAE: Selected pwd-seed with counter %u",
179 - counter);
180 - x = x_cand;
181 - pwd_seed_odd = pwd_seed[SHA256_MAC_LEN - 1] & 0x01;
182 - os_memset(pwd_seed, 0, sizeof(pwd_seed));
183 -
184 - /*
185 - * Use a dummy password for the following rounds, if
186 - * any.
187 - */
188 - addr[0] = dummy_password;
189 - len[0] = dummy_password_len;
190 - } else if (res > 0) {
191 - crypto_bignum_deinit(x_cand, 1);
192 - }
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. */
198 +
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.
201 + */
202 + found |= res * 0xff;
203 + wpa_printf(MSG_DEBUG, "SAE: pwd-seed result %d found=0x%02x",
204 + res, found);
205 }
206
207 - if (!x) {
208 + if (!found) {
209 wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
210 res = -1;
211 goto fail;
212 }
213
214 + x = crypto_bignum_init_set(x_bin, prime_len);
215 + if (!x) {
216 + res = -1;
217 + goto fail;
218 + }
219 +
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,
226 pwd_seed_odd);
227 - crypto_bignum_deinit(x, 1);
228 if (res < 0) {
229 /*
230 * This should not happen since we already checked that there
231 @@ -555,6 +558,11 @@ static int sae_derive_pwe_ecc(struct sae
232 fail:
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));
240
241 return res;
242 }