hostapd: fix CVE-2019-9494
[openwrt/openwrt.git] / package / network / services / hostapd / patches / 061-0009-SAE-Use-constant-time-operations-in-sae_test_pwd_see.patch
diff --git a/package/network/services/hostapd/patches/061-0009-SAE-Use-constant-time-operations-in-sae_test_pwd_see.patch b/package/network/services/hostapd/patches/061-0009-SAE-Use-constant-time-operations-in-sae_test_pwd_see.patch
new file mode 100644 (file)
index 0000000..150cbeb
--- /dev/null
@@ -0,0 +1,133 @@
+From cff138b0747fa39765cbc641b66cfa5d7f1735d1 Mon Sep 17 00:00:00 2001
+From: Jouni Malinen <jouni@codeaurora.org>
+Date: Sat, 2 Mar 2019 16:05:56 +0200
+Subject: [PATCH 09/14] SAE: Use constant time operations in
+ sae_test_pwd_seed_ffc()
+
+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 | 75 ++++++++++++++++++++++++++++++++++----------------------
+ 1 file changed, 46 insertions(+), 29 deletions(-)
+
+--- a/src/common/sae.c
++++ b/src/common/sae.c
+@@ -311,14 +311,17 @@ static int sae_test_pwd_seed_ecc(struct
+ }
++/* 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);
+@@ -330,16 +333,29 @@ static int sae_test_pwd_seed_ffc(struct
+       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:
+@@ -353,33 +369,34 @@ static int sae_test_pwd_seed_ffc(struct
+               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 (res < 0) {
+-              wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE");
+-              return -1;
+-      }
+-
+-      /* if (PWE > 1) --> found */
+-      if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) {
+-              wpa_printf(MSG_DEBUG, "SAE: PWE <= 1");
+-              return 0;
+-      }
++      if (!b)
++              goto fail;
+-      wpa_printf(MSG_DEBUG, "SAE: PWE found");
+-      return 1;
++      res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
++      if (res < 0)
++              goto fail;
++
++      /* 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);
++
++fail:
++      crypto_bignum_deinit(a, 1);
++      crypto_bignum_deinit(b, 1);
++      return res;
+ }