]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
SAE: Use constant time operations in sae_test_pwd_seed_ffc()
authorJouni Malinen <jouni@codeaurora.org>
Sat, 2 Mar 2019 14:05:56 +0000 (16:05 +0200)
committerJouni Malinen <j@w1.fi>
Tue, 9 Apr 2019 14:11:15 +0000 (17:11 +0300)
Try to avoid showing externally visible timing or memory access
differences regardless of whether the derived pwd-value is smaller than
the group prime.

This is related to CVE-2019-9494.

Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
src/common/sae.c

index fa9a145e340907d0a4db10f89cc9bd71eb3edd49..eaf825d19ff364774a191d3f7e63d5c17dc02e1f 100644 (file)
@@ -334,14 +334,17 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
 }
 
 
+/* Returns -1 on fatal failure, 0 if PWE cannot be derived from the provided
+ * pwd-seed, or 1 if a valid PWE was derived from pwd-seed. */
 static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
                                 struct crypto_bignum *pwe)
 {
        u8 pwd_value[SAE_MAX_PRIME_LEN];
        size_t bits = sae->tmp->prime_len * 8;
        u8 exp[1];
-       struct crypto_bignum *a, *b;
-       int res;
+       struct crypto_bignum *a, *b = NULL;
+       int res, is_val;
+       u8 pwd_value_valid;
 
        wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
 
@@ -353,16 +356,29 @@ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
        wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value,
                        sae->tmp->prime_len);
 
-       if (os_memcmp(pwd_value, sae->tmp->dh->prime, sae->tmp->prime_len) >= 0)
-       {
-               wpa_printf(MSG_DEBUG, "SAE: pwd-value >= p");
-               return 0;
-       }
+       /* Check whether pwd-value < p */
+       res = const_time_memcmp(pwd_value, sae->tmp->dh->prime,
+                               sae->tmp->prime_len);
+       /* pwd-value >= p is invalid, so res is < 0 for the valid cases and
+        * the negative sign can be used to fill the mask for constant time
+        * selection */
+       pwd_value_valid = const_time_fill_msb(res);
+
+       /* If pwd-value >= p, force pwd-value to be < p and perform the
+        * calculations anyway to hide timing difference. The derived PWE will
+        * be ignored in that case. */
+       pwd_value[0] = const_time_select_u8(pwd_value_valid, pwd_value[0], 0);
 
        /* PWE = pwd-value^((p-1)/r) modulo p */
 
+       res = -1;
        a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
+       if (!a)
+               goto fail;
 
+       /* This is an optimization based on the used group that does not depend
+        * on the password in any way, so it is fine to use separate branches
+        * for this step without constant time operations. */
        if (sae->tmp->dh->safe_prime) {
                /*
                 * r = (p-1)/2 for the group used here, so this becomes:
@@ -376,33 +392,34 @@ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
                b = crypto_bignum_init_set(exp, sizeof(exp));
                if (b == NULL ||
                    crypto_bignum_sub(sae->tmp->prime, b, b) < 0 ||
-                   crypto_bignum_div(b, sae->tmp->order, b) < 0) {
-                       crypto_bignum_deinit(b, 0);
-                       b = NULL;
-               }
+                   crypto_bignum_div(b, sae->tmp->order, b) < 0)
+                       goto fail;
        }
 
-       if (a == NULL || b == NULL)
-               res = -1;
-       else
-               res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
-
-       crypto_bignum_deinit(a, 0);
-       crypto_bignum_deinit(b, 0);
+       if (!b)
+               goto fail;
 
-       if (res < 0) {
-               wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE");
-               return -1;
-       }
+       res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
+       if (res < 0)
+               goto fail;
 
-       /* if (PWE > 1) --> found */
-       if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) {
-               wpa_printf(MSG_DEBUG, "SAE: PWE <= 1");
-               return 0;
-       }
+       /* There were no fatal errors in calculations, so determine the return
+        * value using constant time operations. We get here for number of
+        * invalid cases which are cleared here after having performed all the
+        * computation. PWE is valid if pwd-value was less than prime and
+        * PWE > 1. Start with pwd-value check first and then use constant time
+        * operations to clear res to 0 if PWE is 0 or 1.
+        */
+       res = const_time_select_u8(pwd_value_valid, 1, 0);
+       is_val = crypto_bignum_is_zero(pwe);
+       res = const_time_select_u8(const_time_is_zero(is_val), res, 0);
+       is_val = crypto_bignum_is_one(pwe);
+       res = const_time_select_u8(const_time_is_zero(is_val), res, 0);
 
-       wpa_printf(MSG_DEBUG, "SAE: PWE found");
-       return 1;
+fail:
+       crypto_bignum_deinit(a, 1);
+       crypto_bignum_deinit(b, 1);
+       return res;
 }