150cbeb8acb856bf74919d7a975c2cdcbf64bb57
[openwrt/openwrt.git] /
1 From cff138b0747fa39765cbc641b66cfa5d7f1735d1 Mon Sep 17 00:00:00 2001
2 From: Jouni Malinen <jouni@codeaurora.org>
3 Date: Sat, 2 Mar 2019 16:05:56 +0200
4 Subject: [PATCH 09/14] SAE: Use constant time operations in
5 sae_test_pwd_seed_ffc()
6
7 Try to avoid showing externally visible timing or memory access
8 differences regardless of whether the derived pwd-value is smaller than
9 the group prime.
10
11 This is related to CVE-2019-9494.
12
13 Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
14 ---
15 src/common/sae.c | 75 ++++++++++++++++++++++++++++++++++----------------------
16 1 file changed, 46 insertions(+), 29 deletions(-)
17
18 --- a/src/common/sae.c
19 +++ b/src/common/sae.c
20 @@ -311,14 +311,17 @@ static int sae_test_pwd_seed_ecc(struct
21 }
22
23
24 +/* Returns -1 on fatal failure, 0 if PWE cannot be derived from the provided
25 + * pwd-seed, or 1 if a valid PWE was derived from pwd-seed. */
26 static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
27 struct crypto_bignum *pwe)
28 {
29 u8 pwd_value[SAE_MAX_PRIME_LEN];
30 size_t bits = sae->tmp->prime_len * 8;
31 u8 exp[1];
32 - struct crypto_bignum *a, *b;
33 - int res;
34 + struct crypto_bignum *a, *b = NULL;
35 + int res, is_val;
36 + u8 pwd_value_valid;
37
38 wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
39
40 @@ -330,16 +333,29 @@ static int sae_test_pwd_seed_ffc(struct
41 wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value,
42 sae->tmp->prime_len);
43
44 - if (os_memcmp(pwd_value, sae->tmp->dh->prime, sae->tmp->prime_len) >= 0)
45 - {
46 - wpa_printf(MSG_DEBUG, "SAE: pwd-value >= p");
47 - return 0;
48 - }
49 + /* Check whether pwd-value < p */
50 + res = const_time_memcmp(pwd_value, sae->tmp->dh->prime,
51 + sae->tmp->prime_len);
52 + /* pwd-value >= p is invalid, so res is < 0 for the valid cases and
53 + * the negative sign can be used to fill the mask for constant time
54 + * selection */
55 + pwd_value_valid = const_time_fill_msb(res);
56 +
57 + /* If pwd-value >= p, force pwd-value to be < p and perform the
58 + * calculations anyway to hide timing difference. The derived PWE will
59 + * be ignored in that case. */
60 + pwd_value[0] = const_time_select_u8(pwd_value_valid, pwd_value[0], 0);
61
62 /* PWE = pwd-value^((p-1)/r) modulo p */
63
64 + res = -1;
65 a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
66 + if (!a)
67 + goto fail;
68
69 + /* This is an optimization based on the used group that does not depend
70 + * on the password in any way, so it is fine to use separate branches
71 + * for this step without constant time operations. */
72 if (sae->tmp->dh->safe_prime) {
73 /*
74 * r = (p-1)/2 for the group used here, so this becomes:
75 @@ -353,33 +369,34 @@ static int sae_test_pwd_seed_ffc(struct
76 b = crypto_bignum_init_set(exp, sizeof(exp));
77 if (b == NULL ||
78 crypto_bignum_sub(sae->tmp->prime, b, b) < 0 ||
79 - crypto_bignum_div(b, sae->tmp->order, b) < 0) {
80 - crypto_bignum_deinit(b, 0);
81 - b = NULL;
82 - }
83 + crypto_bignum_div(b, sae->tmp->order, b) < 0)
84 + goto fail;
85 }
86
87 - if (a == NULL || b == NULL)
88 - res = -1;
89 - else
90 - res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
91 -
92 - crypto_bignum_deinit(a, 0);
93 - crypto_bignum_deinit(b, 0);
94 -
95 - if (res < 0) {
96 - wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE");
97 - return -1;
98 - }
99 -
100 - /* if (PWE > 1) --> found */
101 - if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) {
102 - wpa_printf(MSG_DEBUG, "SAE: PWE <= 1");
103 - return 0;
104 - }
105 + if (!b)
106 + goto fail;
107
108 - wpa_printf(MSG_DEBUG, "SAE: PWE found");
109 - return 1;
110 + res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
111 + if (res < 0)
112 + goto fail;
113 +
114 + /* There were no fatal errors in calculations, so determine the return
115 + * value using constant time operations. We get here for number of
116 + * invalid cases which are cleared here after having performed all the
117 + * computation. PWE is valid if pwd-value was less than prime and
118 + * PWE > 1. Start with pwd-value check first and then use constant time
119 + * operations to clear res to 0 if PWE is 0 or 1.
120 + */
121 + res = const_time_select_u8(pwd_value_valid, 1, 0);
122 + is_val = crypto_bignum_is_zero(pwe);
123 + res = const_time_select_u8(const_time_is_zero(is_val), res, 0);
124 + is_val = crypto_bignum_is_one(pwe);
125 + res = const_time_select_u8(const_time_is_zero(is_val), res, 0);
126 +
127 +fail:
128 + crypto_bignum_deinit(a, 1);
129 + crypto_bignum_deinit(b, 1);
130 + return res;
131 }
132
133