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()
7 Try to avoid showing externally visible timing or memory access
8 differences regardless of whether the derived pwd-value is smaller than
11 This is related to CVE-2019-9494.
13 Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
15 src/common/sae.c | 75 ++++++++++++++++++++++++++++++++++----------------------
16 1 file changed, 46 insertions(+), 29 deletions(-)
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
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)
29 u8 pwd_value[SAE_MAX_PRIME_LEN];
30 size_t bits = sae->tmp->prime_len * 8;
32 - struct crypto_bignum *a, *b;
34 + struct crypto_bignum *a, *b = NULL;
38 wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
40 @@ -330,16 +333,29 @@ static int sae_test_pwd_seed_ffc(struct
41 wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value,
44 - if (os_memcmp(pwd_value, sae->tmp->dh->prime, sae->tmp->prime_len) >= 0)
46 - wpa_printf(MSG_DEBUG, "SAE: pwd-value >= p");
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
55 + pwd_value_valid = const_time_fill_msb(res);
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);
62 /* PWE = pwd-value^((p-1)/r) modulo p */
65 a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
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) {
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));
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);
83 + crypto_bignum_div(b, sae->tmp->order, b) < 0)
87 - if (a == NULL || b == NULL)
90 - res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
92 - crypto_bignum_deinit(a, 0);
93 - crypto_bignum_deinit(b, 0);
96 - wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE");
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");
108 - wpa_printf(MSG_DEBUG, "SAE: PWE found");
110 + res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
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.
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);
128 + crypto_bignum_deinit(a, 1);
129 + crypto_bignum_deinit(b, 1);