150cbeb8acb856bf74919d7a975c2cdcbf64bb57
[librecmc/librecmc.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