hostapd: Apply SAE/EAP-pwd side-channel attack update 2
authorHauke Mehrtens <hauke@hauke-m.de>
Sat, 12 Feb 2022 19:37:12 +0000 (20:37 +0100)
committerHauke Mehrtens <hauke@hauke-m.de>
Sat, 12 Feb 2022 23:23:30 +0000 (00:23 +0100)
This fixes some recent security problems in hostapd.
See here for details: https://w1.fi/security/2022-1
* CVE-2022-23303
* CVE-2022-23304

Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
package/network/services/hostapd/Makefile
package/network/services/hostapd/patches/072-dragonfly-Add-sqrt-helper-function.patch [new file with mode: 0644]
package/network/services/hostapd/patches/073-SAE-Derive-the-y-coordinate-for-PWE-with-own-impleme.patch [new file with mode: 0644]
package/network/services/hostapd/patches/074-EAP-pwd-Derive-the-y-coordinate-for-PWE-with-own-imp.patch [new file with mode: 0644]

index 5377c940a3c2384171b152e448c2b6faece98ece..d1ca7ba8b69b5b58576b972c72258f5a2bbd861b 100644 (file)
@@ -7,7 +7,7 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=hostapd
-PKG_RELEASE:=38
+PKG_RELEASE:=39
 
 PKG_SOURCE_URL:=http://w1.fi/hostap.git
 PKG_SOURCE_PROTO:=git
diff --git a/package/network/services/hostapd/patches/072-dragonfly-Add-sqrt-helper-function.patch b/package/network/services/hostapd/patches/072-dragonfly-Add-sqrt-helper-function.patch
new file mode 100644 (file)
index 0000000..b8b1e07
--- /dev/null
@@ -0,0 +1,65 @@
+From 2232d3d5f188b65dbb6c823ac62175412739eb16 Mon Sep 17 00:00:00 2001
+From: Jouni Malinen <j@w1.fi>
+Date: Fri, 7 Jan 2022 13:47:16 +0200
+Subject: [PATCH 2/4] dragonfly: Add sqrt() helper function
+
+This is a backport of "SAE: Move sqrt() implementation into a helper
+function" to introduce the helper function needed for the following
+patches.
+
+Signed-off-by: Jouni Malinen <j@w1.fi>
+---
+ src/common/dragonfly.c | 34 ++++++++++++++++++++++++++++++++++
+ src/common/dragonfly.h |  2 ++
+ 2 files changed, 36 insertions(+)
+
+--- a/src/common/dragonfly.c
++++ b/src/common/dragonfly.c
+@@ -213,3 +213,37 @@ int dragonfly_generate_scalar(const stru
+                  "dragonfly: Unable to get randomness for own scalar");
+       return -1;
+ }
++
++
++/* res = sqrt(val) */
++int dragonfly_sqrt(struct crypto_ec *ec, const struct crypto_bignum *val,
++                 struct crypto_bignum *res)
++{
++      const struct crypto_bignum *prime;
++      struct crypto_bignum *tmp, *one;
++      int ret = 0;
++      u8 prime_bin[DRAGONFLY_MAX_ECC_PRIME_LEN];
++      size_t prime_len;
++
++      /* For prime p such that p = 3 mod 4, sqrt(w) = w^((p+1)/4) mod p */
++
++      prime = crypto_ec_get_prime(ec);
++      prime_len = crypto_ec_prime_len(ec);
++      tmp = crypto_bignum_init();
++      one = crypto_bignum_init_uint(1);
++
++      if (crypto_bignum_to_bin(prime, prime_bin, sizeof(prime_bin),
++                               prime_len) < 0 ||
++          (prime_bin[prime_len - 1] & 0x03) != 3 ||
++          !tmp || !one ||
++          /* tmp = (p+1)/4 */
++          crypto_bignum_add(prime, one, tmp) < 0 ||
++          crypto_bignum_rshift(tmp, 2, tmp) < 0 ||
++          /* res = sqrt(val) */
++          crypto_bignum_exptmod(val, tmp, prime, res) < 0)
++              ret = -1;
++
++      crypto_bignum_deinit(tmp, 0);
++      crypto_bignum_deinit(one, 0);
++      return ret;
++}
+--- a/src/common/dragonfly.h
++++ b/src/common/dragonfly.h
+@@ -27,5 +27,7 @@ int dragonfly_generate_scalar(const stru
+                             struct crypto_bignum *_rand,
+                             struct crypto_bignum *_mask,
+                             struct crypto_bignum *scalar);
++int dragonfly_sqrt(struct crypto_ec *ec, const struct crypto_bignum *val,
++                 struct crypto_bignum *res);
+ #endif /* DRAGONFLY_H */
diff --git a/package/network/services/hostapd/patches/073-SAE-Derive-the-y-coordinate-for-PWE-with-own-impleme.patch b/package/network/services/hostapd/patches/073-SAE-Derive-the-y-coordinate-for-PWE-with-own-impleme.patch
new file mode 100644 (file)
index 0000000..f0db451
--- /dev/null
@@ -0,0 +1,94 @@
+From fe534b0baaa8c0e6ddeb24cf529d6e50e33dc501 Mon Sep 17 00:00:00 2001
+From: Jouni Malinen <j@w1.fi>
+Date: Fri, 7 Jan 2022 13:47:16 +0200
+Subject: [PATCH 3/4] SAE: Derive the y coordinate for PWE with own
+ implementation
+
+The crypto_ec_point_solve_y_coord() wrapper function might not use
+constant time operations in the crypto library and as such, could leak
+side channel information about the password that is used to generate the
+PWE in the hunting and pecking loop. As such, calculate the two possible
+y coordinate values and pick the correct one to use with constant time
+selection.
+
+Signed-off-by: Jouni Malinen <j@w1.fi>
+---
+ src/common/sae.c | 47 +++++++++++++++++++++++++++++++++--------------
+ 1 file changed, 33 insertions(+), 14 deletions(-)
+
+--- a/src/common/sae.c
++++ b/src/common/sae.c
+@@ -294,14 +294,16 @@ static int sae_derive_pwe_ecc(struct sae
+       int pwd_seed_odd = 0;
+       u8 prime[SAE_MAX_ECC_PRIME_LEN];
+       size_t prime_len;
+-      struct crypto_bignum *x = NULL, *qr = NULL, *qnr = NULL;
++      struct crypto_bignum *x = NULL, *y = NULL, *qr = NULL, *qnr = NULL;
+       u8 x_bin[SAE_MAX_ECC_PRIME_LEN];
+       u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN];
+       u8 qr_bin[SAE_MAX_ECC_PRIME_LEN];
+       u8 qnr_bin[SAE_MAX_ECC_PRIME_LEN];
++      u8 x_y[2 * SAE_MAX_ECC_PRIME_LEN];
+       int res = -1;
+       u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
+                      * mask */
++      unsigned int is_eq;
+       os_memset(x_bin, 0, sizeof(x_bin));
+@@ -410,25 +412,42 @@ static int sae_derive_pwe_ecc(struct sae
+               goto fail;
+       }
+-      if (!sae->tmp->pwe_ecc)
+-              sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec);
+-      if (!sae->tmp->pwe_ecc)
+-              res = -1;
+-      else
+-              res = crypto_ec_point_solve_y_coord(sae->tmp->ec,
+-                                                  sae->tmp->pwe_ecc, x,
+-                                                  pwd_seed_odd);
+-      if (res < 0) {
+-              /*
+-               * This should not happen since we already checked that there
+-               * is a result.
+-               */
++      /* y = sqrt(x^3 + ax + b) mod p
++       * if LSB(save) == LSB(y): PWE = (x, y)
++       * else: PWE = (x, p - y)
++       *
++       * Calculate y and the two possible values for PWE and after that,
++       * use constant time selection to copy the correct alternative.
++       */
++      y = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x);
++      if (!y ||
++          dragonfly_sqrt(sae->tmp->ec, y, y) < 0 ||
++          crypto_bignum_to_bin(y, x_y, SAE_MAX_ECC_PRIME_LEN,
++                               prime_len) < 0 ||
++          crypto_bignum_sub(sae->tmp->prime, y, y) < 0 ||
++          crypto_bignum_to_bin(y, x_y + SAE_MAX_ECC_PRIME_LEN,
++                               SAE_MAX_ECC_PRIME_LEN, prime_len) < 0) {
+               wpa_printf(MSG_DEBUG, "SAE: Could not solve y");
++              goto fail;
++      }
++
++      is_eq = const_time_eq(pwd_seed_odd, x_y[prime_len - 1] & 0x01);
++      const_time_select_bin(is_eq, x_y, x_y + SAE_MAX_ECC_PRIME_LEN,
++                            prime_len, x_y + prime_len);
++      os_memcpy(x_y, x_bin, prime_len);
++      wpa_hexdump_key(MSG_DEBUG, "SAE: PWE", x_y, 2 * prime_len);
++      crypto_ec_point_deinit(sae->tmp->pwe_ecc, 1);
++      sae->tmp->pwe_ecc = crypto_ec_point_from_bin(sae->tmp->ec, x_y);
++      if (!sae->tmp->pwe_ecc) {
++              wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
++              res = -1;
+       }
+ fail:
++      forced_memzero(x_y, sizeof(x_y));
+       crypto_bignum_deinit(qr, 0);
+       crypto_bignum_deinit(qnr, 0);
++      crypto_bignum_deinit(y, 1);
+       os_free(dummy_password);
+       bin_clear_free(tmp_password, password_len);
+       crypto_bignum_deinit(x, 1);
diff --git a/package/network/services/hostapd/patches/074-EAP-pwd-Derive-the-y-coordinate-for-PWE-with-own-imp.patch b/package/network/services/hostapd/patches/074-EAP-pwd-Derive-the-y-coordinate-for-PWE-with-own-imp.patch
new file mode 100644 (file)
index 0000000..c5ddddc
--- /dev/null
@@ -0,0 +1,108 @@
+From 603cd880e7f90595482658a7136fa6a7be5cb485 Mon Sep 17 00:00:00 2001
+From: Jouni Malinen <j@w1.fi>
+Date: Fri, 7 Jan 2022 18:52:27 +0200
+Subject: [PATCH 4/4] EAP-pwd: Derive the y coordinate for PWE with own
+ implementation
+
+The crypto_ec_point_solve_y_coord() wrapper function might not use
+constant time operations in the crypto library and as such, could leak
+side channel information about the password that is used to generate the
+PWE in the hunting and pecking loop. As such, calculate the two possible
+y coordinate values and pick the correct one to use with constant time
+selection.
+
+Signed-off-by: Jouni Malinen <j@w1.fi>
+---
+ src/eap_common/eap_pwd_common.c | 46 ++++++++++++++++++++++++++-------
+ 1 file changed, 36 insertions(+), 10 deletions(-)
+
+--- a/src/eap_common/eap_pwd_common.c
++++ b/src/eap_common/eap_pwd_common.c
+@@ -127,7 +127,8 @@ int compute_password_element(EAP_PWD_gro
+       u8 qr_or_qnr_bin[MAX_ECC_PRIME_LEN];
+       u8 x_bin[MAX_ECC_PRIME_LEN];
+       u8 prime_bin[MAX_ECC_PRIME_LEN];
+-      struct crypto_bignum *tmp2 = NULL;
++      u8 x_y[2 * MAX_ECC_PRIME_LEN];
++      struct crypto_bignum *tmp2 = NULL, *y = NULL;
+       struct crypto_hash *hash;
+       unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr;
+       int ret = 0, res;
+@@ -139,6 +140,7 @@ int compute_password_element(EAP_PWD_gro
+       u8 found_ctr = 0, is_odd = 0;
+       int cmp_prime;
+       unsigned int in_range;
++      unsigned int is_eq;
+       if (grp->pwe)
+               return -1;
+@@ -151,11 +153,6 @@ int compute_password_element(EAP_PWD_gro
+       if (crypto_bignum_to_bin(prime, prime_bin, sizeof(prime_bin),
+                                primebytelen) < 0)
+               return -1;
+-      grp->pwe = crypto_ec_point_init(grp->group);
+-      if (!grp->pwe) {
+-              wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums");
+-              goto fail;
+-      }
+       if ((prfbuf = os_malloc(primebytelen)) == NULL) {
+               wpa_printf(MSG_INFO, "EAP-pwd: unable to malloc space for prf "
+@@ -261,10 +258,37 @@ int compute_password_element(EAP_PWD_gro
+        */
+       crypto_bignum_deinit(x_candidate, 1);
+       x_candidate = crypto_bignum_init_set(x_bin, primebytelen);
+-      if (!x_candidate ||
+-          crypto_ec_point_solve_y_coord(grp->group, grp->pwe, x_candidate,
+-                                        is_odd) != 0) {
+-              wpa_printf(MSG_INFO, "EAP-pwd: Could not solve for y");
++      if (!x_candidate)
++              goto fail;
++
++      /* y = sqrt(x^3 + ax + b) mod p
++       * if LSB(y) == LSB(pwd-seed): PWE = (x, y)
++       * else: PWE = (x, p - y)
++       *
++       * Calculate y and the two possible values for PWE and after that,
++       * use constant time selection to copy the correct alternative.
++       */
++      y = crypto_ec_point_compute_y_sqr(grp->group, x_candidate);
++      if (!y ||
++          dragonfly_sqrt(grp->group, y, y) < 0 ||
++          crypto_bignum_to_bin(y, x_y, MAX_ECC_PRIME_LEN, primebytelen) < 0 ||
++          crypto_bignum_sub(prime, y, y) < 0 ||
++          crypto_bignum_to_bin(y, x_y + MAX_ECC_PRIME_LEN,
++                               MAX_ECC_PRIME_LEN, primebytelen) < 0) {
++              wpa_printf(MSG_DEBUG, "SAE: Could not solve y");
++              goto fail;
++      }
++
++      /* Constant time selection of the y coordinate from the two
++       * options */
++      is_eq = const_time_eq(is_odd, x_y[primebytelen - 1] & 0x01);
++      const_time_select_bin(is_eq, x_y, x_y + MAX_ECC_PRIME_LEN,
++                            primebytelen, x_y + primebytelen);
++      os_memcpy(x_y, x_bin, primebytelen);
++      wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: PWE", x_y, 2 * primebytelen);
++      grp->pwe = crypto_ec_point_from_bin(grp->group, x_y);
++      if (!grp->pwe) {
++              wpa_printf(MSG_DEBUG, "EAP-pwd: Could not generate PWE");
+               goto fail;
+       }
+@@ -289,6 +313,7 @@ int compute_password_element(EAP_PWD_gro
+       /* cleanliness and order.... */
+       crypto_bignum_deinit(x_candidate, 1);
+       crypto_bignum_deinit(tmp2, 1);
++      crypto_bignum_deinit(y, 1);
+       crypto_bignum_deinit(qr, 1);
+       crypto_bignum_deinit(qnr, 1);
+       bin_clear_free(prfbuf, primebytelen);
+@@ -296,6 +321,7 @@ int compute_password_element(EAP_PWD_gro
+       os_memset(qnr_bin, 0, sizeof(qnr_bin));
+       os_memset(qr_or_qnr_bin, 0, sizeof(qr_or_qnr_bin));
+       os_memset(pwe_digest, 0, sizeof(pwe_digest));
++      forced_memzero(x_y, sizeof(x_y));
+       return ret;
+ }