e72a9cbe5acf1c623e0f4bac2ea02491e8c2b029
[librecmc/librecmc.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  }