hostapd: fix multiple security problems
authorHauke Mehrtens <hauke@hauke-m.de>
Fri, 17 May 2019 21:22:02 +0000 (23:22 +0200)
committerHauke Mehrtens <hauke@hauke-m.de>
Fri, 21 Jun 2019 08:29:23 +0000 (10:29 +0200)
This fixes the following security problems:
* CVE-2019-9494:  cache attack against SAE
* CVE-2019-9495:  cache attack against EAP-pwd
* CVE-2019-9496:  SAE confirm missing state validation in hostapd/AP
* CVE-2019-9497:  EAP-pwd server not checking for reflection attack)
* CVE-2019-9498:  EAP-pwd server missing commit validation for scalar/element
* CVE-2019-9499:  EAP-pwd peer missing commit validation for scalar/element
* CVE-2019-11555: EAP-pwd message reassembly issue with unexpected fragment

Most of these problems are not relevant for normal users, SAE is only
used in ieee80211s mesh mode and EAP-pwd is normally not activated.

Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
19 files changed:
package/network/services/hostapd/Makefile
package/network/services/hostapd/patches/060-0001-EAP-pwd-Move-EC-group-initialization-to-earlier-step.patch [new file with mode: 0644]
package/network/services/hostapd/patches/060-0002-EAP-pwd-Mask-timing-of-PWE-derivation.patch [new file with mode: 0644]
package/network/services/hostapd/patches/061-0001-OpenSSL-Use-constant-time-operations-for-private-big.patch [new file with mode: 0644]
package/network/services/hostapd/patches/061-0002-Add-helper-functions-for-constant-time-operations.patch [new file with mode: 0644]
package/network/services/hostapd/patches/061-0003-OpenSSL-Use-constant-time-selection-for-crypto_bignu.patch [new file with mode: 0644]
package/network/services/hostapd/patches/061-0005-SAE-Minimize-timing-differences-in-PWE-derivation.patch [new file with mode: 0644]
package/network/services/hostapd/patches/061-0006-SAE-Avoid-branches-in-is_quadratic_residue_blind.patch [new file with mode: 0644]
package/network/services/hostapd/patches/061-0007-SAE-Mask-timing-of-MODP-groups-22-23-24.patch [new file with mode: 0644]
package/network/services/hostapd/patches/061-0008-SAE-Use-const_time-selection-for-PWE-in-FFC.patch [new file with mode: 0644]
package/network/services/hostapd/patches/061-0009-SAE-Use-constant-time-operations-in-sae_test_pwd_see.patch [new file with mode: 0644]
package/network/services/hostapd/patches/062-0004-EAP-pwd-Use-constant-time-and-memory-access-for-find.patch [new file with mode: 0644]
package/network/services/hostapd/patches/063-0010-SAE-Fix-confirm-message-validation-in-error-cases.patch [new file with mode: 0644]
package/network/services/hostapd/patches/064-0011-EAP-pwd-server-Verify-received-scalar-and-element.patch [new file with mode: 0644]
package/network/services/hostapd/patches/064-0012-EAP-pwd-server-Detect-reflection-attacks.patch [new file with mode: 0644]
package/network/services/hostapd/patches/064-0013-EAP-pwd-client-Verify-received-scalar-and-element.patch [new file with mode: 0644]
package/network/services/hostapd/patches/064-0014-EAP-pwd-Check-element-x-y-coordinates-explicitly.patch [new file with mode: 0644]
package/network/services/hostapd/patches/065-0001-EAP-pwd-server-Fix-reassembly-buffer-handling.patch [new file with mode: 0644]
package/network/services/hostapd/patches/065-0003-EAP-pwd-peer-Fix-reassembly-buffer-handling.patch [new file with mode: 0644]

index dacdf70..b548ecd 100644 (file)
@@ -7,7 +7,7 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=hostapd
-PKG_RELEASE:=5
+PKG_RELEASE:=6
 
 PKG_SOURCE_URL:=http://w1.fi/hostap.git
 PKG_SOURCE_PROTO:=git
diff --git a/package/network/services/hostapd/patches/060-0001-EAP-pwd-Move-EC-group-initialization-to-earlier-step.patch b/package/network/services/hostapd/patches/060-0001-EAP-pwd-Move-EC-group-initialization-to-earlier-step.patch
new file mode 100644 (file)
index 0000000..6d35769
--- /dev/null
@@ -0,0 +1,104 @@
+From 2a5c291881fa819325d0287d0763776edfcb1943 Mon Sep 17 00:00:00 2001
+From: Dan Harkins <dharkins@lounge.org>
+Date: Fri, 25 May 2018 21:40:04 +0300
+Subject: [PATCH] EAP-pwd: Move EC group initialization to earlier step
+
+This is needed for adding support for salted passwords.
+
+Signed-off-by: Dan Harkins <dharkins@lounge.org>
+---
+ src/eap_common/eap_pwd_common.c | 32 +++++++++++++++++++++++---------
+ src/eap_common/eap_pwd_common.h |  1 +
+ src/eap_peer/eap_pwd.c          |  2 +-
+ src/eap_server/eap_server_pwd.c |  2 +-
+ 4 files changed, 26 insertions(+), 11 deletions(-)
+
+--- a/src/eap_common/eap_pwd_common.c
++++ b/src/eap_common/eap_pwd_common.c
+@@ -81,6 +81,27 @@ static int eap_pwd_kdf(const u8 *key, si
+ }
++EAP_PWD_group * get_eap_pwd_group(u16 num)
++{
++      EAP_PWD_group *grp;
++
++      grp = os_zalloc(sizeof(EAP_PWD_group));
++      if (!grp)
++              return NULL;
++      grp->group = crypto_ec_init(num);
++      if (!grp->group) {
++              wpa_printf(MSG_INFO, "EAP-pwd: unable to create EC group");
++              os_free(grp);
++              return NULL;
++      }
++
++      grp->group_num = num;
++      wpa_printf(MSG_INFO, "EAP-pwd: provisioned group %d", num);
++
++      return grp;
++}
++
++
+ /*
+  * compute a "random" secret point on an elliptic curve based
+  * on the password and identities.
+@@ -97,12 +118,8 @@ int compute_password_element(EAP_PWD_gro
+       size_t primebytelen, primebitlen;
+       struct crypto_bignum *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
+-      grp->pwe = NULL;
+-      grp->group = crypto_ec_init(num);
+-      if (!grp->group) {
+-              wpa_printf(MSG_INFO, "EAP-pwd: unable to create EC group");
+-              goto fail;
+-      }
++      if (grp->pwe)
++              return -1;
+       cofactor = crypto_bignum_init();
+       grp->pwe = crypto_ec_point_init(grp->group);
+@@ -234,11 +251,8 @@ int compute_password_element(EAP_PWD_gro
+               break;
+       }
+       wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %d tries", ctr);
+-      grp->group_num = num;
+       if (0) {
+  fail:
+-              crypto_ec_deinit(grp->group);
+-              grp->group = NULL;
+               crypto_ec_point_deinit(grp->pwe, 1);
+               grp->pwe = NULL;
+               ret = 1;
+--- a/src/eap_common/eap_pwd_common.h
++++ b/src/eap_common/eap_pwd_common.h
+@@ -50,6 +50,7 @@ struct eap_pwd_id {
+ } STRUCT_PACKED;
+ /* common routines */
++EAP_PWD_group * get_eap_pwd_group(u16 num);
+ int compute_password_element(EAP_PWD_group *grp, u16 num,
+                            const u8 *password, size_t password_len,
+                            const u8 *id_server, size_t id_server_len,
+--- a/src/eap_peer/eap_pwd.c
++++ b/src/eap_peer/eap_pwd.c
+@@ -265,7 +265,7 @@ eap_pwd_perform_id_exchange(struct eap_s
+       wpa_hexdump_ascii(MSG_INFO, "EAP-PWD (peer): server sent id of",
+                         data->id_server, data->id_server_len);
+-      data->grp = os_zalloc(sizeof(EAP_PWD_group));
++      data->grp = get_eap_pwd_group(data->group_num);
+       if (data->grp == NULL) {
+               wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for "
+                          "group");
+--- a/src/eap_server/eap_server_pwd.c
++++ b/src/eap_server/eap_server_pwd.c
+@@ -562,7 +562,7 @@ static void eap_pwd_process_id_resp(stru
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-PWD (server): peer sent id of",
+                         data->id_peer, data->id_peer_len);
+-      data->grp = os_zalloc(sizeof(EAP_PWD_group));
++      data->grp = get_eap_pwd_group(data->group_num);
+       if (data->grp == NULL) {
+               wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for "
+                          "group");
diff --git a/package/network/services/hostapd/patches/060-0002-EAP-pwd-Mask-timing-of-PWE-derivation.patch b/package/network/services/hostapd/patches/060-0002-EAP-pwd-Mask-timing-of-PWE-derivation.patch
new file mode 100644 (file)
index 0000000..0a27abb
--- /dev/null
@@ -0,0 +1,247 @@
+From 22ac3dfebf7b25a3aae02f9b4f69025bb4173137 Mon Sep 17 00:00:00 2001
+From: Dan Harkins <dharkins@lounge.org>
+Date: Fri, 25 May 2018 21:40:04 +0300
+Subject: [PATCH] EAP-pwd: Mask timing of PWE derivation
+
+Run through the hunting-and-pecking loop 40 times to mask the time
+necessary to find PWE. The odds of PWE not being found in 40 loops is
+roughly 1 in 1 trillion.
+
+Signed-off-by: Dan Harkins <dharkins@lounge.org>
+---
+ src/eap_common/eap_pwd_common.c | 171 ++++++++++++++++++++++++--------
+ 1 file changed, 130 insertions(+), 41 deletions(-)
+
+--- a/src/eap_common/eap_pwd_common.c
++++ b/src/eap_common/eap_pwd_common.c
+@@ -112,18 +112,25 @@ int compute_password_element(EAP_PWD_gro
+                            const u8 *id_peer, size_t id_peer_len,
+                            const u8 *token)
+ {
++      struct crypto_bignum *qr = NULL, *qnr = NULL, *one = NULL;
++      struct crypto_bignum *tmp1 = NULL, *tmp2 = NULL, *pm1 = NULL;
+       struct crypto_hash *hash;
+       unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr;
+-      int is_odd, ret = 0;
++      int is_odd, ret = 0, check, found = 0;
+       size_t primebytelen, primebitlen;
+       struct crypto_bignum *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
++      const struct crypto_bignum *prime;
+       if (grp->pwe)
+               return -1;
++      prime = crypto_ec_get_prime(grp->group);
+       cofactor = crypto_bignum_init();
+       grp->pwe = crypto_ec_point_init(grp->group);
+-      if (!cofactor || !grp->pwe) {
++      tmp1 = crypto_bignum_init();
++      pm1 = crypto_bignum_init();
++      one = crypto_bignum_init_set((const u8 *) "\x01", 1);
++      if (!cofactor || !grp->pwe || !tmp1 || !pm1 || !one) {
+               wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums");
+               goto fail;
+       }
+@@ -140,15 +147,36 @@ int compute_password_element(EAP_PWD_gro
+                          "buffer");
+               goto fail;
+       }
++      if (crypto_bignum_sub(prime, one, pm1) < 0)
++              goto fail;
++
++      /* get a random quadratic residue and nonresidue */
++      while (!qr || !qnr) {
++              int res;
++
++              if (crypto_bignum_rand(tmp1, prime) < 0)
++                      goto fail;
++              res = crypto_bignum_legendre(tmp1, prime);
++              if (!qr && res == 1) {
++                      qr = tmp1;
++                      tmp1 = crypto_bignum_init();
++              } else if (!qnr && res == -1) {
++                      qnr = tmp1;
++                      tmp1 = crypto_bignum_init();
++              }
++              if (!tmp1)
++                      goto fail;
++      }
++
+       os_memset(prfbuf, 0, primebytelen);
+       ctr = 0;
+-      while (1) {
+-              if (ctr > 30) {
+-                      wpa_printf(MSG_INFO, "EAP-pwd: unable to find random "
+-                                 "point on curve for group %d, something's "
+-                                 "fishy", num);
+-                      goto fail;
+-              }
++
++      /*
++       * Run through the hunting-and-pecking loop 40 times to mask the time
++       * necessary to find PWE. The odds of PWE not being found in 40 loops is
++       * roughly 1 in 1 trillion.
++       */
++      while (ctr < 40) {
+               ctr++;
+               /*
+@@ -199,58 +227,113 @@ int compute_password_element(EAP_PWD_gro
+                                        x_candidate) < 0)
+                       goto fail;
+-              if (crypto_bignum_cmp(x_candidate,
+-                                    crypto_ec_get_prime(grp->group)) >= 0)
++              if (crypto_bignum_cmp(x_candidate, prime) >= 0)
+                       continue;
+               wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate",
+                           prfbuf, primebytelen);
+               /*
+-               * need to unambiguously identify the solution, if there is
+-               * one...
++               * compute y^2 using the equation of the curve
++               *
++               *      y^2 = x^3 + ax + b
+                */
+-              is_odd = crypto_bignum_is_odd(rnd);
++              tmp2 = crypto_ec_point_compute_y_sqr(grp->group, x_candidate);
++              if (!tmp2)
++                      goto fail;
+               /*
+-               * solve the quadratic equation, if it's not solvable then we
+-               * don't have a point
++               * mask tmp2 so doing legendre won't leak timing info
++               *
++               * tmp1 is a random number between 1 and p-1
+                */
+-              if (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");
+-                      continue;
+-              }
++              if (crypto_bignum_rand(tmp1, pm1) < 0 ||
++                  crypto_bignum_mulmod(tmp2, tmp1, prime, tmp2) < 0 ||
++                  crypto_bignum_mulmod(tmp2, tmp1, prime, tmp2) < 0)
++                      goto fail;
++
+               /*
+-               * If there's a solution to the equation then the point must be
+-               * on the curve so why check again explicitly? OpenSSL code
+-               * says this is required by X9.62. We're not X9.62 but it can't
+-               * hurt just to be sure.
++               * Now tmp2 (y^2) is masked, all values between 1 and p-1
++               * are equally probable. Multiplying by r^2 does not change
++               * whether or not tmp2 is a quadratic residue, just masks it.
++               *
++               * Flip a coin, multiply by the random quadratic residue or the
++               * random quadratic nonresidue and record heads or tails.
+                */
+-              if (!crypto_ec_point_is_on_curve(grp->group, grp->pwe)) {
+-                      wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve");
+-                      continue;
++              if (crypto_bignum_is_odd(tmp1)) {
++                      crypto_bignum_mulmod(tmp2, qr, prime, tmp2);
++                      check = 1;
++              } else {
++                      crypto_bignum_mulmod(tmp2, qnr, prime, tmp2);
++                      check = -1;
+               }
+-              if (!crypto_bignum_is_one(cofactor)) {
+-                      /* make sure the point is not in a small sub-group */
+-                      if (crypto_ec_point_mul(grp->group, grp->pwe,
+-                                              cofactor, grp->pwe) != 0) {
+-                              wpa_printf(MSG_INFO, "EAP-pwd: cannot "
+-                                         "multiply generator by order");
++              /*
++               * Now it's safe to do legendre, if check is 1 then it's
++               * a straightforward test (multiplying by qr does not
++               * change result), if check is -1 then it's the opposite test
++               * (multiplying a qr by qnr would make a qnr).
++               */
++              if (crypto_bignum_legendre(tmp2, prime) == check) {
++                      if (found == 1)
++                              continue;
++
++                      /* need to unambiguously identify the solution */
++                      is_odd = crypto_bignum_is_odd(rnd);
++
++                      /*
++                       * We know x_candidate is a quadratic residue so set
++                       * it here.
++                       */
++                      if (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");
+                               continue;
+                       }
+-                      if (crypto_ec_point_is_at_infinity(grp->group,
+-                                                         grp->pwe)) {
+-                              wpa_printf(MSG_INFO, "EAP-pwd: point is at "
+-                                         "infinity");
++
++                      /*
++                       * If there's a solution to the equation then the point
++                       * must be on the curve so why check again explicitly?
++                       * OpenSSL code says this is required by X9.62. We're
++                       * not X9.62 but it can't hurt just to be sure.
++                       */
++                      if (!crypto_ec_point_is_on_curve(grp->group,
++                                                       grp->pwe)) {
++                              wpa_printf(MSG_INFO,
++                                         "EAP-pwd: point is not on curve");
+                               continue;
+                       }
++
++                      if (!crypto_bignum_is_one(cofactor)) {
++                              /* make sure the point is not in a small
++                               * sub-group */
++                              if (crypto_ec_point_mul(grp->group, grp->pwe,
++                                                      cofactor,
++                                                      grp->pwe) != 0) {
++                                      wpa_printf(MSG_INFO,
++                                                 "EAP-pwd: cannot multiply generator by order");
++                                      continue;
++                              }
++                              if (crypto_ec_point_is_at_infinity(grp->group,
++                                                                 grp->pwe)) {
++                                      wpa_printf(MSG_INFO,
++                                                 "EAP-pwd: point is at infinity");
++                                      continue;
++                              }
++                      }
++                      wpa_printf(MSG_DEBUG,
++                                 "EAP-pwd: found a PWE in %d tries", ctr);
++                      found = 1;
+               }
+-              /* if we got here then we have a new generator. */
+-              break;
+       }
+-      wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %d tries", ctr);
++      if (found == 0) {
++              wpa_printf(MSG_INFO,
++                         "EAP-pwd: unable to find random point on curve for group %d, something's fishy",
++                         num);
++              goto fail;
++      }
+       if (0) {
+  fail:
+               crypto_ec_point_deinit(grp->pwe, 1);
+@@ -261,6 +344,12 @@ int compute_password_element(EAP_PWD_gro
+       crypto_bignum_deinit(cofactor, 1);
+       crypto_bignum_deinit(x_candidate, 1);
+       crypto_bignum_deinit(rnd, 1);
++      crypto_bignum_deinit(pm1, 0);
++      crypto_bignum_deinit(tmp1, 1);
++      crypto_bignum_deinit(tmp2, 1);
++      crypto_bignum_deinit(qr, 1);
++      crypto_bignum_deinit(qnr, 1);
++      crypto_bignum_deinit(one, 0);
+       os_free(prfbuf);
+       return ret;
diff --git a/package/network/services/hostapd/patches/061-0001-OpenSSL-Use-constant-time-operations-for-private-big.patch b/package/network/services/hostapd/patches/061-0001-OpenSSL-Use-constant-time-operations-for-private-big.patch
new file mode 100644 (file)
index 0000000..8e6a8cf
--- /dev/null
@@ -0,0 +1,88 @@
+From d42c477cc794163a3757956bbffca5cea000923c Mon Sep 17 00:00:00 2001
+From: Jouni Malinen <jouni@codeaurora.org>
+Date: Tue, 26 Feb 2019 11:43:03 +0200
+Subject: [PATCH 01/14] OpenSSL: Use constant time operations for private
+ bignums
+
+This helps in reducing measurable timing differences in operations
+involving private information. BoringSSL has removed BN_FLG_CONSTTIME
+and expects specific constant time functions to be called instead, so a
+bit different approach is needed depending on which library is used.
+
+The main operation that needs protection against side channel attacks is
+BN_mod_exp() that depends on private keys (the public key validation
+step in crypto_dh_derive_secret() is an exception that can use the
+faster version since it does not depend on private keys).
+
+crypto_bignum_div() is currently used only in SAE FFC case with not
+safe-prime groups and only with values that do not depend on private
+keys, so it is not critical to protect it.
+
+crypto_bignum_inverse() is currently used only in SAE FFC PWE
+derivation. The additional protection here is targeting only OpenSSL.
+BoringSSL may need conversion to using BN_mod_inverse_blinded().
+
+This is related to CVE-2019-9494 and CVE-2019-9495.
+
+Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
+---
+ src/crypto/crypto_openssl.c | 20 +++++++++++++++-----
+ 1 file changed, 15 insertions(+), 5 deletions(-)
+
+--- a/src/crypto/crypto_openssl.c
++++ b/src/crypto/crypto_openssl.c
+@@ -548,7 +548,8 @@ int crypto_mod_exp(const u8 *base, size_
+           bn_result == NULL)
+               goto error;
+-      if (BN_mod_exp(bn_result, bn_base, bn_exp, bn_modulus, ctx) != 1)
++      if (BN_mod_exp_mont_consttime(bn_result, bn_base, bn_exp, bn_modulus,
++                                    ctx, NULL) != 1)
+               goto error;
+       *result_len = BN_bn2bin(bn_result, result);
+@@ -1294,8 +1295,9 @@ int crypto_bignum_exptmod(const struct c
+       bnctx = BN_CTX_new();
+       if (bnctx == NULL)
+               return -1;
+-      res = BN_mod_exp((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b,
+-                       (const BIGNUM *) c, bnctx);
++      res = BN_mod_exp_mont_consttime((BIGNUM *) d, (const BIGNUM *) a,
++                                      (const BIGNUM *) b, (const BIGNUM *) c,
++                                      bnctx, NULL);
+       BN_CTX_free(bnctx);
+       return res ? 0 : -1;
+@@ -1314,6 +1316,11 @@ int crypto_bignum_inverse(const struct c
+       bnctx = BN_CTX_new();
+       if (bnctx == NULL)
+               return -1;
++#ifdef OPENSSL_IS_BORINGSSL
++      /* TODO: use BN_mod_inverse_blinded() ? */
++#else /* OPENSSL_IS_BORINGSSL */
++      BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME);
++#endif /* OPENSSL_IS_BORINGSSL */
+       res = BN_mod_inverse((BIGNUM *) c, (const BIGNUM *) a,
+                            (const BIGNUM *) b, bnctx);
+       BN_CTX_free(bnctx);
+@@ -1347,6 +1354,9 @@ int crypto_bignum_div(const struct crypt
+       bnctx = BN_CTX_new();
+       if (bnctx == NULL)
+               return -1;
++#ifndef OPENSSL_IS_BORINGSSL
++      BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME);
++#endif /* OPENSSL_IS_BORINGSSL */
+       res = BN_div((BIGNUM *) c, NULL, (const BIGNUM *) a,
+                    (const BIGNUM *) b, bnctx);
+       BN_CTX_free(bnctx);
+@@ -1438,8 +1448,8 @@ int crypto_bignum_legendre(const struct
+           /* exp = (p-1) / 2 */
+           !BN_sub(exp, (const BIGNUM *) p, BN_value_one()) ||
+           !BN_rshift1(exp, exp) ||
+-          !BN_mod_exp(tmp, (const BIGNUM *) a, exp, (const BIGNUM *) p,
+-                      bnctx))
++          !BN_mod_exp_mont_consttime(tmp, (const BIGNUM *) a, exp,
++                                     (const BIGNUM *) p, bnctx, NULL))
+               goto fail;
+       if (BN_is_word(tmp, 1))
diff --git a/package/network/services/hostapd/patches/061-0002-Add-helper-functions-for-constant-time-operations.patch b/package/network/services/hostapd/patches/061-0002-Add-helper-functions-for-constant-time-operations.patch
new file mode 100644 (file)
index 0000000..87e41ae
--- /dev/null
@@ -0,0 +1,212 @@
+From 6e34f618d37ddbb5854c42e2ad4fca83492fa7b7 Mon Sep 17 00:00:00 2001
+From: Jouni Malinen <jouni@codeaurora.org>
+Date: Wed, 27 Feb 2019 18:38:30 +0200
+Subject: [PATCH 02/14] Add helper functions for constant time operations
+
+These functions can be used to help implement constant time operations
+for various cryptographic operations that must minimize externally
+observable differences in processing (both in timing and also in
+internal cache use, etc.).
+
+This is related to CVE-2019-9494 and CVE-2019-9495.
+
+Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
+---
+ src/utils/const_time.h | 191 +++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 191 insertions(+)
+ create mode 100644 src/utils/const_time.h
+
+--- /dev/null
++++ b/src/utils/const_time.h
+@@ -0,0 +1,191 @@
++/*
++ * Helper functions for constant time operations
++ * Copyright (c) 2019, The Linux Foundation
++ *
++ * This software may be distributed under the terms of the BSD license.
++ * See README for more details.
++ *
++ * These helper functions can be used to implement logic that needs to minimize
++ * externally visible differences in execution path by avoiding use of branches,
++ * avoiding early termination or other time differences, and forcing same memory
++ * access pattern regardless of values.
++ */
++
++#ifndef CONST_TIME_H
++#define CONST_TIME_H
++
++
++#if defined(__clang__)
++#define NO_UBSAN_UINT_OVERFLOW \
++      __attribute__((no_sanitize("unsigned-integer-overflow")))
++#else
++#define NO_UBSAN_UINT_OVERFLOW
++#endif
++
++
++/**
++ * const_time_fill_msb - Fill all bits with MSB value
++ * @val: Input value
++ * Returns: Value with all the bits set to the MSB of the input val
++ */
++static inline unsigned int const_time_fill_msb(unsigned int val)
++{
++      /* Move the MSB to LSB and multiple by -1 to fill in all bits. */
++      return (val >> (sizeof(val) * 8 - 1)) * ~0U;
++}
++
++
++/* Returns: -1 if val is zero; 0 if val is not zero */
++static inline unsigned int const_time_is_zero(unsigned int val)
++      NO_UBSAN_UINT_OVERFLOW
++{
++      /* Set MSB to 1 for 0 and fill rest of bits with the MSB value */
++      return const_time_fill_msb(~val & (val - 1));
++}
++
++
++/* Returns: -1 if a == b; 0 if a != b */
++static inline unsigned int const_time_eq(unsigned int a, unsigned int b)
++{
++      return const_time_is_zero(a ^ b);
++}
++
++
++/* Returns: -1 if a == b; 0 if a != b */
++static inline u8 const_time_eq_u8(unsigned int a, unsigned int b)
++{
++      return (u8) const_time_eq(a, b);
++}
++
++
++/**
++ * const_time_eq_bin - Constant time memory comparison
++ * @a: First buffer to compare
++ * @b: Second buffer to compare
++ * @len: Number of octets to compare
++ * Returns: -1 if buffers are equal, 0 if not
++ *
++ * This function is meant for comparing passwords or hash values where
++ * difference in execution time or memory access pattern could provide external
++ * observer information about the location of the difference in the memory
++ * buffers. The return value does not behave like memcmp(), i.e.,
++ * const_time_eq_bin() cannot be used to sort items into a defined order. Unlike
++ * memcmp(), the execution time of const_time_eq_bin() does not depend on the
++ * contents of the compared memory buffers, but only on the total compared
++ * length.
++ */
++static inline unsigned int const_time_eq_bin(const void *a, const void *b,
++                                           size_t len)
++{
++      const u8 *aa = a;
++      const u8 *bb = b;
++      size_t i;
++      u8 res = 0;
++
++      for (i = 0; i < len; i++)
++              res |= aa[i] ^ bb[i];
++
++      return const_time_is_zero(res);
++}
++
++
++/**
++ * const_time_select - Constant time unsigned int selection
++ * @mask: 0 (false) or -1 (true) to identify which value to select
++ * @true_val: Value to select for the true case
++ * @false_val: Value to select for the false case
++ * Returns: true_val if mask == -1, false_val if mask == 0
++ */
++static inline unsigned int const_time_select(unsigned int mask,
++                                           unsigned int true_val,
++                                           unsigned int false_val)
++{
++      return (mask & true_val) | (~mask & false_val);
++}
++
++
++/**
++ * const_time_select_int - Constant time int selection
++ * @mask: 0 (false) or -1 (true) to identify which value to select
++ * @true_val: Value to select for the true case
++ * @false_val: Value to select for the false case
++ * Returns: true_val if mask == -1, false_val if mask == 0
++ */
++static inline int const_time_select_int(unsigned int mask, int true_val,
++                                      int false_val)
++{
++      return (int) const_time_select(mask, (unsigned int) true_val,
++                                     (unsigned int) false_val);
++}
++
++
++/**
++ * const_time_select_u8 - Constant time u8 selection
++ * @mask: 0 (false) or -1 (true) to identify which value to select
++ * @true_val: Value to select for the true case
++ * @false_val: Value to select for the false case
++ * Returns: true_val if mask == -1, false_val if mask == 0
++ */
++static inline u8 const_time_select_u8(u8 mask, u8 true_val, u8 false_val)
++{
++      return (u8) const_time_select(mask, true_val, false_val);
++}
++
++
++/**
++ * const_time_select_s8 - Constant time s8 selection
++ * @mask: 0 (false) or -1 (true) to identify which value to select
++ * @true_val: Value to select for the true case
++ * @false_val: Value to select for the false case
++ * Returns: true_val if mask == -1, false_val if mask == 0
++ */
++static inline s8 const_time_select_s8(u8 mask, s8 true_val, s8 false_val)
++{
++      return (s8) const_time_select(mask, (unsigned int) true_val,
++                                    (unsigned int) false_val);
++}
++
++
++/**
++ * const_time_select_bin - Constant time binary buffer selection copy
++ * @mask: 0 (false) or -1 (true) to identify which value to copy
++ * @true_val: Buffer to copy for the true case
++ * @false_val: Buffer to copy for the false case
++ * @len: Number of octets to copy
++ * @dst: Destination buffer for the copy
++ *
++ * This function copies the specified buffer into the destination buffer using
++ * operations with identical memory access pattern regardless of which buffer
++ * is being copied.
++ */
++static inline void const_time_select_bin(u8 mask, const u8 *true_val,
++                                       const u8 *false_val, size_t len,
++                                       u8 *dst)
++{
++      size_t i;
++
++      for (i = 0; i < len; i++)
++              dst[i] = const_time_select_u8(mask, true_val[i], false_val[i]);
++}
++
++
++static inline int const_time_memcmp(const void *a, const void *b, size_t len)
++{
++      const u8 *aa = a;
++      const u8 *bb = b;
++      int diff, res = 0;
++      unsigned int mask;
++
++      if (len == 0)
++              return 0;
++      do {
++              len--;
++              diff = (int) aa[len] - (int) bb[len];
++              mask = const_time_is_zero((unsigned int) diff);
++              res = const_time_select_int(mask, res, diff);
++      } while (len);
++
++      return res;
++}
++
++#endif /* CONST_TIME_H */
diff --git a/package/network/services/hostapd/patches/061-0003-OpenSSL-Use-constant-time-selection-for-crypto_bignu.patch b/package/network/services/hostapd/patches/061-0003-OpenSSL-Use-constant-time-selection-for-crypto_bignu.patch
new file mode 100644 (file)
index 0000000..003985b
--- /dev/null
@@ -0,0 +1,55 @@
+From c93461c1d98f52681717a088776ab32fd97872b0 Mon Sep 17 00:00:00 2001
+From: Jouni Malinen <jouni@codeaurora.org>
+Date: Fri, 8 Mar 2019 00:24:12 +0200
+Subject: [PATCH 03/14] OpenSSL: Use constant time selection for
+ crypto_bignum_legendre()
+
+Get rid of the branches that depend on the result of the Legendre
+operation. This is needed to avoid leaking information about different
+temporary results in blinding mechanisms.
+
+This is related to CVE-2019-9494 and CVE-2019-9495.
+
+Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
+---
+ src/crypto/crypto_openssl.c | 15 +++++++++------
+ 1 file changed, 9 insertions(+), 6 deletions(-)
+
+--- a/src/crypto/crypto_openssl.c
++++ b/src/crypto/crypto_openssl.c
+@@ -24,6 +24,7 @@
+ #endif /* CONFIG_ECC */
+ #include "common.h"
++#include "utils/const_time.h"
+ #include "wpabuf.h"
+ #include "dh_group5.h"
+ #include "sha1.h"
+@@ -1434,6 +1435,7 @@ int crypto_bignum_legendre(const struct
+       BN_CTX *bnctx;
+       BIGNUM *exp = NULL, *tmp = NULL;
+       int res = -2;
++      unsigned int mask;
+       if (TEST_FAIL())
+               return -2;
+@@ -1452,12 +1454,13 @@ int crypto_bignum_legendre(const struct
+                                      (const BIGNUM *) p, bnctx, NULL))
+               goto fail;
+-      if (BN_is_word(tmp, 1))
+-              res = 1;
+-      else if (BN_is_zero(tmp))
+-              res = 0;
+-      else
+-              res = -1;
++      /* Return 1 if tmp == 1, 0 if tmp == 0, or -1 otherwise. Need to use
++       * constant time selection to avoid branches here. */
++      res = -1;
++      mask = const_time_eq(BN_is_word(tmp, 1), 1);
++      res = const_time_select_int(mask, 1, res);
++      mask = const_time_eq(BN_is_zero(tmp), 1);
++      res = const_time_select_int(mask, 0, res);
+ fail:
+       BN_clear_free(tmp);
diff --git a/package/network/services/hostapd/patches/061-0005-SAE-Minimize-timing-differences-in-PWE-derivation.patch b/package/network/services/hostapd/patches/061-0005-SAE-Minimize-timing-differences-in-PWE-derivation.patch
new file mode 100644 (file)
index 0000000..e72a9cb
--- /dev/null
@@ -0,0 +1,242 @@
+From 6513db3e96c43c2e36805cf5ead349765d18eaf7 Mon Sep 17 00:00:00 2001
+From: Jouni Malinen <jouni@codeaurora.org>
+Date: Tue, 26 Feb 2019 13:05:09 +0200
+Subject: [PATCH 05/14] SAE: Minimize timing differences in PWE derivation
+
+The QR test result can provide information about the password to an
+attacker, so try to minimize differences in how the
+sae_test_pwd_seed_ecc() result is used. (CVE-2019-9494)
+
+Use heap memory for the dummy password to allow the same password length
+to be used even with long passwords.
+
+Use constant time selection functions to track the real vs. dummy
+variables so that the exact same operations can be performed for both QR
+test results.
+
+Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
+---
+ src/common/sae.c | 106 ++++++++++++++++++++++++++++++-------------------------
+ 1 file changed, 57 insertions(+), 49 deletions(-)
+
+--- a/src/common/sae.c
++++ b/src/common/sae.c
+@@ -9,6 +9,7 @@
+ #include "includes.h"
+ #include "common.h"
++#include "utils/const_time.h"
+ #include "crypto/crypto.h"
+ #include "crypto/sha256.h"
+ #include "crypto/random.h"
+@@ -269,15 +270,12 @@ static int sae_test_pwd_seed_ecc(struct
+                                const u8 *prime,
+                                const struct crypto_bignum *qr,
+                                const struct crypto_bignum *qnr,
+-                               struct crypto_bignum **ret_x_cand)
++                               u8 *pwd_value)
+ {
+-      u8 pwd_value[SAE_MAX_ECC_PRIME_LEN];
+       struct crypto_bignum *y_sqr, *x_cand;
+       int res;
+       size_t bits;
+-      *ret_x_cand = NULL;
+-
+       wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
+       /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
+@@ -286,7 +284,7 @@ static int sae_test_pwd_seed_ecc(struct
+                           prime, sae->tmp->prime_len, pwd_value, bits) < 0)
+               return -1;
+       if (bits % 8)
+-              buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8);
++              buf_shift_right(pwd_value, sae->tmp->prime_len, 8 - bits % 8);
+       wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
+                       pwd_value, sae->tmp->prime_len);
+@@ -297,20 +295,13 @@ static int sae_test_pwd_seed_ecc(struct
+       if (!x_cand)
+               return -1;
+       y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand);
+-      if (!y_sqr) {
+-              crypto_bignum_deinit(x_cand, 1);
++      crypto_bignum_deinit(x_cand, 1);
++      if (!y_sqr)
+               return -1;
+-      }
+       res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr);
+       crypto_bignum_deinit(y_sqr, 1);
+-      if (res <= 0) {
+-              crypto_bignum_deinit(x_cand, 1);
+-              return res;
+-      }
+-
+-      *ret_x_cand = x_cand;
+-      return 1;
++      return res;
+ }
+@@ -431,25 +422,30 @@ static int sae_derive_pwe_ecc(struct sae
+       const u8 *addr[3];
+       size_t len[3];
+       size_t num_elem;
+-      u8 dummy_password[32];
+-      size_t dummy_password_len;
++      u8 *dummy_password, *tmp_password;
+       int pwd_seed_odd = 0;
+       u8 prime[SAE_MAX_ECC_PRIME_LEN];
+       size_t prime_len;
+-      struct crypto_bignum *x = NULL, *qr, *qnr;
++      struct crypto_bignum *x = NULL, *qr = NULL, *qnr = NULL;
++      u8 x_bin[SAE_MAX_ECC_PRIME_LEN];
++      u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN];
+       size_t bits;
+-      int res;
+-
+-      dummy_password_len = password_len;
+-      if (dummy_password_len > sizeof(dummy_password))
+-              dummy_password_len = sizeof(dummy_password);
+-      if (random_get_bytes(dummy_password, dummy_password_len) < 0)
+-              return -1;
++      int res = -1;
++      u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
++                     * mask */
++
++      os_memset(x_bin, 0, sizeof(x_bin));
++
++      dummy_password = os_malloc(password_len);
++      tmp_password = os_malloc(password_len);
++      if (!dummy_password || !tmp_password ||
++          random_get_bytes(dummy_password, password_len) < 0)
++              goto fail;
+       prime_len = sae->tmp->prime_len;
+       if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
+                                prime_len) < 0)
+-              return -1;
++              goto fail;
+       bits = crypto_ec_prime_len_bits(sae->tmp->ec);
+       /*
+@@ -458,7 +454,7 @@ static int sae_derive_pwe_ecc(struct sae
+        */
+       if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits,
+                             &qr, &qnr) < 0)
+-              return -1;
++              goto fail;
+       wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
+                             password, password_len);
+@@ -474,7 +470,7 @@ static int sae_derive_pwe_ecc(struct sae
+        */
+       sae_pwd_seed_key(addr1, addr2, addrs);
+-      addr[0] = password;
++      addr[0] = tmp_password;
+       len[0] = password_len;
+       num_elem = 1;
+       if (identifier) {
+@@ -491,9 +487,8 @@ static int sae_derive_pwe_ecc(struct sae
+        * attacks that attempt to determine the number of iterations required
+        * in the loop.
+        */
+-      for (counter = 1; counter <= k || !x; counter++) {
++      for (counter = 1; counter <= k || !found; counter++) {
+               u8 pwd_seed[SHA256_MAC_LEN];
+-              struct crypto_bignum *x_cand;
+               if (counter > 200) {
+                       /* This should not happen in practice */
+@@ -501,40 +496,49 @@ static int sae_derive_pwe_ecc(struct sae
+                       break;
+               }
+-              wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
++              wpa_printf(MSG_DEBUG, "SAE: counter = %03u", counter);
++              const_time_select_bin(found, dummy_password, password,
++                                    password_len, tmp_password);
+               if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
+                                      addr, len, pwd_seed) < 0)
+                       break;
+               res = sae_test_pwd_seed_ecc(sae, pwd_seed,
+-                                          prime, qr, qnr, &x_cand);
++                                          prime, qr, qnr, x_cand_bin);
++              const_time_select_bin(found, x_bin, x_cand_bin, prime_len,
++                                    x_bin);
++              pwd_seed_odd = const_time_select_u8(
++                      found, pwd_seed_odd,
++                      pwd_seed[SHA256_MAC_LEN - 1] & 0x01);
++              os_memset(pwd_seed, 0, sizeof(pwd_seed));
+               if (res < 0)
+                       goto fail;
+-              if (res > 0 && !x) {
+-                      wpa_printf(MSG_DEBUG,
+-                                 "SAE: Selected pwd-seed with counter %u",
+-                                 counter);
+-                      x = x_cand;
+-                      pwd_seed_odd = pwd_seed[SHA256_MAC_LEN - 1] & 0x01;
+-                      os_memset(pwd_seed, 0, sizeof(pwd_seed));
+-
+-                      /*
+-                       * Use a dummy password for the following rounds, if
+-                       * any.
+-                       */
+-                      addr[0] = dummy_password;
+-                      len[0] = dummy_password_len;
+-              } else if (res > 0) {
+-                      crypto_bignum_deinit(x_cand, 1);
+-              }
++              /* Need to minimize differences in handling res == 0 and 1 here
++               * to avoid differences in timing and instruction cache access,
++               * so use const_time_select_*() to make local copies of the
++               * values based on whether this loop iteration was the one that
++               * found the pwd-seed/x. */
++
++              /* found is 0 or 0xff here and res is 0 or 1. Bitwise OR of them
++               * (with res converted to 0/0xff) handles this in constant time.
++               */
++              found |= res * 0xff;
++              wpa_printf(MSG_DEBUG, "SAE: pwd-seed result %d found=0x%02x",
++                         res, found);
+       }
+-      if (!x) {
++      if (!found) {
+               wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
+               res = -1;
+               goto fail;
+       }
++      x = crypto_bignum_init_set(x_bin, prime_len);
++      if (!x) {
++              res = -1;
++              goto fail;
++      }
++
+       if (!sae->tmp->pwe_ecc)
+               sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec);
+       if (!sae->tmp->pwe_ecc)
+@@ -543,7 +547,6 @@ static int sae_derive_pwe_ecc(struct sae
+               res = crypto_ec_point_solve_y_coord(sae->tmp->ec,
+                                                   sae->tmp->pwe_ecc, x,
+                                                   pwd_seed_odd);
+-      crypto_bignum_deinit(x, 1);
+       if (res < 0) {
+               /*
+                * This should not happen since we already checked that there
+@@ -555,6 +558,11 @@ static int sae_derive_pwe_ecc(struct sae
+ fail:
+       crypto_bignum_deinit(qr, 0);
+       crypto_bignum_deinit(qnr, 0);
++      os_free(dummy_password);
++      bin_clear_free(tmp_password, password_len);
++      crypto_bignum_deinit(x, 1);
++      os_memset(x_bin, 0, sizeof(x_bin));
++      os_memset(x_cand_bin, 0, sizeof(x_cand_bin));
+       return res;
+ }
diff --git a/package/network/services/hostapd/patches/061-0006-SAE-Avoid-branches-in-is_quadratic_residue_blind.patch b/package/network/services/hostapd/patches/061-0006-SAE-Avoid-branches-in-is_quadratic_residue_blind.patch
new file mode 100644 (file)
index 0000000..6d93cb2
--- /dev/null
@@ -0,0 +1,139 @@
+From 362704dda04507e7ebb8035122e83d9f0ae7c320 Mon Sep 17 00:00:00 2001
+From: Jouni Malinen <jouni@codeaurora.org>
+Date: Tue, 26 Feb 2019 19:34:38 +0200
+Subject: [PATCH 06/14] SAE: Avoid branches in is_quadratic_residue_blind()
+
+Make the non-failure path in the function proceed without branches based
+on r_odd and in constant time to minimize risk of observable differences
+in timing or cache use. (CVE-2019-9494)
+
+Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
+---
+ src/common/sae.c | 64 ++++++++++++++++++++++++++++++++------------------------
+ 1 file changed, 37 insertions(+), 27 deletions(-)
+
+--- a/src/common/sae.c
++++ b/src/common/sae.c
+@@ -209,12 +209,14 @@ get_rand_1_to_p_1(const u8 *prime, size_
+ static int is_quadratic_residue_blind(struct sae_data *sae,
+                                     const u8 *prime, size_t bits,
+-                                    const struct crypto_bignum *qr,
+-                                    const struct crypto_bignum *qnr,
++                                    const u8 *qr, const u8 *qnr,
+                                     const struct crypto_bignum *y_sqr)
+ {
+-      struct crypto_bignum *r, *num;
++      struct crypto_bignum *r, *num, *qr_or_qnr = NULL;
+       int r_odd, check, res = -1;
++      u8 qr_or_qnr_bin[SAE_MAX_ECC_PRIME_LEN];
++      size_t prime_len = sae->tmp->prime_len;
++      unsigned int mask;
+       /*
+        * Use the blinding technique to mask y_sqr while determining
+@@ -225,7 +227,7 @@ static int is_quadratic_residue_blind(st
+        * r = a random number between 1 and p-1, inclusive
+        * num = (v * r * r) modulo p
+        */
+-      r = get_rand_1_to_p_1(prime, sae->tmp->prime_len, bits, &r_odd);
++      r = get_rand_1_to_p_1(prime, prime_len, bits, &r_odd);
+       if (!r)
+               return -1;
+@@ -235,41 +237,45 @@ static int is_quadratic_residue_blind(st
+           crypto_bignum_mulmod(num, r, sae->tmp->prime, num) < 0)
+               goto fail;
+-      if (r_odd) {
+-              /*
+-               * num = (num * qr) module p
+-               * LGR(num, p) = 1 ==> quadratic residue
+-               */
+-              if (crypto_bignum_mulmod(num, qr, sae->tmp->prime, num) < 0)
+-                      goto fail;
+-              check = 1;
+-      } else {
+-              /*
+-               * num = (num * qnr) module p
+-               * LGR(num, p) = -1 ==> quadratic residue
+-               */
+-              if (crypto_bignum_mulmod(num, qnr, sae->tmp->prime, num) < 0)
+-                      goto fail;
+-              check = -1;
+-      }
++      /*
++       * Need to minimize differences in handling different cases, so try to
++       * avoid branches and timing differences.
++       *
++       * If r_odd:
++       * num = (num * qr) module p
++       * LGR(num, p) = 1 ==> quadratic residue
++       * else:
++       * num = (num * qnr) module p
++       * LGR(num, p) = -1 ==> quadratic residue
++       */
++      mask = const_time_is_zero(r_odd);
++      const_time_select_bin(mask, qnr, qr, prime_len, qr_or_qnr_bin);
++      qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, prime_len);
++      if (!qr_or_qnr ||
++          crypto_bignum_mulmod(num, qr_or_qnr, sae->tmp->prime, num) < 0)
++              goto fail;
++      /* r_odd is 0 or 1; branchless version of check = r_odd ? 1 : -1, */
++      check = const_time_select_int(mask, -1, 1);
+       res = crypto_bignum_legendre(num, sae->tmp->prime);
+       if (res == -2) {
+               res = -1;
+               goto fail;
+       }
+-      res = res == check;
++      /* branchless version of res = res == check
++       * (res is -1, 0, or 1; check is -1 or 1) */
++      mask = const_time_eq(res, check);
++      res = const_time_select_int(mask, 1, 0);
+ fail:
+       crypto_bignum_deinit(num, 1);
+       crypto_bignum_deinit(r, 1);
++      crypto_bignum_deinit(qr_or_qnr, 1);
+       return res;
+ }
+ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
+-                               const u8 *prime,
+-                               const struct crypto_bignum *qr,
+-                               const struct crypto_bignum *qnr,
++                               const u8 *prime, const u8 *qr, const u8 *qnr,
+                                u8 *pwd_value)
+ {
+       struct crypto_bignum *y_sqr, *x_cand;
+@@ -429,6 +435,8 @@ static int sae_derive_pwe_ecc(struct sae
+       struct crypto_bignum *x = 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];
+       size_t bits;
+       int res = -1;
+       u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
+@@ -453,7 +461,9 @@ static int sae_derive_pwe_ecc(struct sae
+        * (qnr) modulo p for blinding purposes during the loop.
+        */
+       if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits,
+-                            &qr, &qnr) < 0)
++                            &qr, &qnr) < 0 ||
++          crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin), prime_len) < 0 ||
++          crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin), prime_len) < 0)
+               goto fail;
+       wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
+@@ -504,7 +514,7 @@ static int sae_derive_pwe_ecc(struct sae
+                       break;
+               res = sae_test_pwd_seed_ecc(sae, pwd_seed,
+-                                          prime, qr, qnr, x_cand_bin);
++                                          prime, qr_bin, qnr_bin, x_cand_bin);
+               const_time_select_bin(found, x_bin, x_cand_bin, prime_len,
+                                     x_bin);
+               pwd_seed_odd = const_time_select_u8(
diff --git a/package/network/services/hostapd/patches/061-0007-SAE-Mask-timing-of-MODP-groups-22-23-24.patch b/package/network/services/hostapd/patches/061-0007-SAE-Mask-timing-of-MODP-groups-22-23-24.patch
new file mode 100644 (file)
index 0000000..229d2b1
--- /dev/null
@@ -0,0 +1,113 @@
+From 90839597cc4016b33f00055b12d59174c62770a3 Mon Sep 17 00:00:00 2001
+From: Jouni Malinen <jouni@codeaurora.org>
+Date: Sat, 2 Mar 2019 12:24:09 +0200
+Subject: [PATCH 07/14] SAE: Mask timing of MODP groups 22, 23, 24
+
+These groups have significant probability of coming up with pwd-value
+that is equal or greater than the prime and as such, need for going
+through the PWE derivation loop multiple times. This can result in
+sufficient timing different to allow an external observer to determine
+how many rounds are needed and that can leak information about the used
+password.
+
+Force at least 40 loop rounds for these MODP groups similarly to the ECC
+group design to mask timing. This behavior is not described in IEEE Std
+802.11-2016 for SAE, but it does not result in different values (i.e.,
+only different timing), so such implementation specific countermeasures
+can be done without breaking interoperability with other implementation.
+
+Note: These MODP groups 22, 23, and 24 are not considered sufficiently
+strong to be used with SAE (or more or less anything else). As such,
+they should never be enabled in runtime configuration for any production
+use cases. These changes to introduce additional protection to mask
+timing is only for completeness of implementation and not an indication
+that these groups should be used.
+
+This is related to CVE-2019-9494.
+
+Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
+---
+ src/common/sae.c | 38 ++++++++++++++++++++++++++++----------
+ 1 file changed, 28 insertions(+), 10 deletions(-)
+
+--- a/src/common/sae.c
++++ b/src/common/sae.c
+@@ -578,22 +578,27 @@ fail:
+ }
++static int sae_modp_group_require_masking(int group)
++{
++      /* Groups for which pwd-value is likely to be >= p frequently */
++      return group == 22 || group == 23 || group == 24;
++}
++
++
+ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
+                             const u8 *addr2, const u8 *password,
+                             size_t password_len, const char *identifier)
+ {
+-      u8 counter;
++      u8 counter, k;
+       u8 addrs[2 * ETH_ALEN];
+       const u8 *addr[3];
+       size_t len[3];
+       size_t num_elem;
+       int found = 0;
++      struct crypto_bignum *pwe = NULL;
+-      if (sae->tmp->pwe_ffc == NULL) {
+-              sae->tmp->pwe_ffc = crypto_bignum_init();
+-              if (sae->tmp->pwe_ffc == NULL)
+-                      return -1;
+-      }
++      crypto_bignum_deinit(sae->tmp->pwe_ffc, 1);
++      sae->tmp->pwe_ffc = NULL;
+       wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
+                             password, password_len);
+@@ -617,7 +622,9 @@ static int sae_derive_pwe_ffc(struct sae
+       len[num_elem] = sizeof(counter);
+       num_elem++;
+-      for (counter = 1; !found; counter++) {
++      k = sae_modp_group_require_masking(sae->group) ? 40 : 1;
++
++      for (counter = 1; counter <= k || !found; counter++) {
+               u8 pwd_seed[SHA256_MAC_LEN];
+               int res;
+@@ -627,19 +634,30 @@ static int sae_derive_pwe_ffc(struct sae
+                       break;
+               }
+-              wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
++              wpa_printf(MSG_DEBUG, "SAE: counter = %02u", counter);
+               if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
+                                      addr, len, pwd_seed) < 0)
+                       break;
+-              res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc);
++              if (!pwe) {
++                      pwe = crypto_bignum_init();
++                      if (!pwe)
++                              break;
++              }
++              res = sae_test_pwd_seed_ffc(sae, pwd_seed, pwe);
+               if (res < 0)
+                       break;
+               if (res > 0) {
+-                      wpa_printf(MSG_DEBUG, "SAE: Use this PWE");
+                       found = 1;
++                      if (!sae->tmp->pwe_ffc) {
++                              wpa_printf(MSG_DEBUG, "SAE: Use this PWE");
++                              sae->tmp->pwe_ffc = pwe;
++                              pwe = NULL;
++                      }
+               }
+       }
++      crypto_bignum_deinit(pwe, 1);
++
+       return found ? 0 : -1;
+ }
diff --git a/package/network/services/hostapd/patches/061-0008-SAE-Use-const_time-selection-for-PWE-in-FFC.patch b/package/network/services/hostapd/patches/061-0008-SAE-Use-const_time-selection-for-PWE-in-FFC.patch
new file mode 100644 (file)
index 0000000..47e1b3c
--- /dev/null
@@ -0,0 +1,100 @@
+From f8f20717f87eff1f025f48ed585c7684debacf72 Mon Sep 17 00:00:00 2001
+From: Jouni Malinen <jouni@codeaurora.org>
+Date: Sat, 2 Mar 2019 12:45:33 +0200
+Subject: [PATCH 08/14] SAE: Use const_time selection for PWE in FFC
+
+This is an initial step towards making the FFC case use strictly
+constant time operations similarly to the ECC case.
+sae_test_pwd_seed_ffc() does not yet have constant time behavior,
+though.
+
+This is related to CVE-2019-9494.
+
+Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
+---
+ src/common/sae.c | 53 +++++++++++++++++++++++++++++++++++------------------
+ 1 file changed, 35 insertions(+), 18 deletions(-)
+
+--- a/src/common/sae.c
++++ b/src/common/sae.c
+@@ -589,17 +589,28 @@ static int sae_derive_pwe_ffc(struct sae
+                             const u8 *addr2, const u8 *password,
+                             size_t password_len, const char *identifier)
+ {
+-      u8 counter, k;
++      u8 counter, k, sel_counter = 0;
+       u8 addrs[2 * ETH_ALEN];
+       const u8 *addr[3];
+       size_t len[3];
+       size_t num_elem;
+-      int found = 0;
+-      struct crypto_bignum *pwe = NULL;
++      u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
++                     * mask */
++      u8 mask;
++      struct crypto_bignum *pwe;
++      size_t prime_len = sae->tmp->prime_len * 8;
++      u8 *pwe_buf;
+       crypto_bignum_deinit(sae->tmp->pwe_ffc, 1);
+       sae->tmp->pwe_ffc = NULL;
++      /* Allocate a buffer to maintain selected and candidate PWE for constant
++       * time selection. */
++      pwe_buf = os_zalloc(prime_len * 2);
++      pwe = crypto_bignum_init();
++      if (!pwe_buf || !pwe)
++              goto fail;
++
+       wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
+                             password, password_len);
+@@ -638,27 +649,33 @@ static int sae_derive_pwe_ffc(struct sae
+               if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
+                                      addr, len, pwd_seed) < 0)
+                       break;
+-              if (!pwe) {
+-                      pwe = crypto_bignum_init();
+-                      if (!pwe)
+-                              break;
+-              }
+               res = sae_test_pwd_seed_ffc(sae, pwd_seed, pwe);
++              /* res is -1 for fatal failure, 0 if a valid PWE was not found,
++               * or 1 if a valid PWE was found. */
+               if (res < 0)
+                       break;
+-              if (res > 0) {
+-                      found = 1;
+-                      if (!sae->tmp->pwe_ffc) {
+-                              wpa_printf(MSG_DEBUG, "SAE: Use this PWE");
+-                              sae->tmp->pwe_ffc = pwe;
+-                              pwe = NULL;
+-                      }
+-              }
++              /* Store the candidate PWE into the second half of pwe_buf and
++               * the selected PWE in the beginning of pwe_buf using constant
++               * time selection. */
++              if (crypto_bignum_to_bin(pwe, pwe_buf + prime_len, prime_len,
++                                       prime_len) < 0)
++                      break;
++              const_time_select_bin(found, pwe_buf, pwe_buf + prime_len,
++                                    prime_len, pwe_buf);
++              sel_counter = const_time_select_u8(found, sel_counter, counter);
++              mask = const_time_eq_u8(res, 1);
++              found = const_time_select_u8(found, found, mask);
+       }
+-      crypto_bignum_deinit(pwe, 1);
++      if (!found)
++              goto fail;
+-      return found ? 0 : -1;
++      wpa_printf(MSG_DEBUG, "SAE: Use PWE from counter = %02u", sel_counter);
++      sae->tmp->pwe_ffc = crypto_bignum_init_set(pwe_buf, prime_len);
++fail:
++      crypto_bignum_deinit(pwe, 1);
++      bin_clear_free(pwe_buf, prime_len * 2);
++      return sae->tmp->pwe_ffc ? 0 : -1;
+ }
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;
+ }
diff --git a/package/network/services/hostapd/patches/062-0004-EAP-pwd-Use-constant-time-and-memory-access-for-find.patch b/package/network/services/hostapd/patches/062-0004-EAP-pwd-Use-constant-time-and-memory-access-for-find.patch
new file mode 100644 (file)
index 0000000..1870c49
--- /dev/null
@@ -0,0 +1,319 @@
+From aaf65feac67c3993935634eefe5bc76b9fce03aa Mon Sep 17 00:00:00 2001
+From: Jouni Malinen <jouni@codeaurora.org>
+Date: Tue, 26 Feb 2019 11:59:45 +0200
+Subject: [PATCH 04/14] EAP-pwd: Use constant time and memory access for
+ finding the PWE
+
+This algorithm could leak information to external observers in form of
+timing differences or memory access patterns (cache use). While the
+previous implementation had protection against the most visible timing
+differences (looping 40 rounds and masking the legendre operation), it
+did not protect against memory access patterns between the two possible
+code paths in the masking operations. That might be sufficient to allow
+an unprivileged process running on the same device to be able to
+determine which path is being executed through a cache attack and based
+on that, determine information about the used password.
+
+Convert the PWE finding loop to use constant time functions and
+identical memory access path without different branches for the QR/QNR
+cases to minimize possible side-channel information similarly to the
+changes done for SAE authentication. (CVE-2019-9495)
+
+Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
+---
+ src/eap_common/eap_pwd_common.c | 187 +++++++++++++++++++++-------------------
+ 1 file changed, 99 insertions(+), 88 deletions(-)
+
+--- a/src/eap_common/eap_pwd_common.c
++++ b/src/eap_common/eap_pwd_common.c
+@@ -8,11 +8,15 @@
+ #include "includes.h"
+ #include "common.h"
++#include "utils/const_time.h"
+ #include "crypto/sha256.h"
+ #include "crypto/crypto.h"
+ #include "eap_defs.h"
+ #include "eap_pwd_common.h"
++#define MAX_ECC_PRIME_LEN 66
++
++
+ /* The random function H(x) = HMAC-SHA256(0^32, x) */
+ struct crypto_hash * eap_pwd_h_init(void)
+ {
+@@ -102,6 +106,15 @@ EAP_PWD_group * get_eap_pwd_group(u16 nu
+ }
++static void buf_shift_right(u8 *buf, size_t len, size_t bits)
++{
++      size_t i;
++      for (i = len - 1; i > 0; i--)
++              buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits);
++      buf[0] >>= bits;
++}
++
++
+ /*
+  * compute a "random" secret point on an elliptic curve based
+  * on the password and identities.
+@@ -113,17 +126,27 @@ int compute_password_element(EAP_PWD_gro
+                            const u8 *token)
+ {
+       struct crypto_bignum *qr = NULL, *qnr = NULL, *one = NULL;
++      struct crypto_bignum *qr_or_qnr = NULL;
++      u8 qr_bin[MAX_ECC_PRIME_LEN];
++      u8 qnr_bin[MAX_ECC_PRIME_LEN];
++      u8 qr_or_qnr_bin[MAX_ECC_PRIME_LEN];
++      u8 x_bin[MAX_ECC_PRIME_LEN];
+       struct crypto_bignum *tmp1 = NULL, *tmp2 = NULL, *pm1 = NULL;
+       struct crypto_hash *hash;
+       unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr;
+-      int is_odd, ret = 0, check, found = 0;
+-      size_t primebytelen, primebitlen;
+-      struct crypto_bignum *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
++      int ret = 0, check, res;
++      u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
++                     * mask */
++      size_t primebytelen = 0, primebitlen;
++      struct crypto_bignum *x_candidate = NULL, *cofactor = NULL;
+       const struct crypto_bignum *prime;
++      u8 mask, found_ctr = 0, is_odd = 0;
+       if (grp->pwe)
+               return -1;
++      os_memset(x_bin, 0, sizeof(x_bin));
++
+       prime = crypto_ec_get_prime(grp->group);
+       cofactor = crypto_bignum_init();
+       grp->pwe = crypto_ec_point_init(grp->group);
+@@ -152,8 +175,6 @@ int compute_password_element(EAP_PWD_gro
+       /* get a random quadratic residue and nonresidue */
+       while (!qr || !qnr) {
+-              int res;
+-
+               if (crypto_bignum_rand(tmp1, prime) < 0)
+                       goto fail;
+               res = crypto_bignum_legendre(tmp1, prime);
+@@ -167,6 +188,11 @@ int compute_password_element(EAP_PWD_gro
+               if (!tmp1)
+                       goto fail;
+       }
++      if (crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin),
++                               primebytelen) < 0 ||
++          crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin),
++                               primebytelen) < 0)
++              goto fail;
+       os_memset(prfbuf, 0, primebytelen);
+       ctr = 0;
+@@ -194,17 +220,16 @@ int compute_password_element(EAP_PWD_gro
+               eap_pwd_h_update(hash, &ctr, sizeof(ctr));
+               eap_pwd_h_final(hash, pwe_digest);
+-              crypto_bignum_deinit(rnd, 1);
+-              rnd = crypto_bignum_init_set(pwe_digest, SHA256_MAC_LEN);
+-              if (!rnd) {
+-                      wpa_printf(MSG_INFO, "EAP-pwd: unable to create rnd");
+-                      goto fail;
+-              }
++              is_odd = const_time_select_u8(
++                      found, is_odd, pwe_digest[SHA256_MAC_LEN - 1] & 0x01);
+               if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN,
+                               (u8 *) "EAP-pwd Hunting And Pecking",
+                               os_strlen("EAP-pwd Hunting And Pecking"),
+                               prfbuf, primebitlen) < 0)
+                       goto fail;
++              if (primebitlen % 8)
++                      buf_shift_right(prfbuf, primebytelen,
++                                      8 - primebitlen % 8);
+               crypto_bignum_deinit(x_candidate, 1);
+               x_candidate = crypto_bignum_init_set(prfbuf, primebytelen);
+@@ -214,24 +239,13 @@ int compute_password_element(EAP_PWD_gro
+                       goto fail;
+               }
+-              /*
+-               * eap_pwd_kdf() returns a string of bits 0..primebitlen but
+-               * BN_bin2bn will treat that string of bits as a big endian
+-               * number. If the primebitlen is not an even multiple of 8
+-               * then excessive bits-- those _after_ primebitlen-- so now
+-               * we have to shift right the amount we masked off.
+-               */
+-              if ((primebitlen % 8) &&
+-                  crypto_bignum_rshift(x_candidate,
+-                                       (8 - (primebitlen % 8)),
+-                                       x_candidate) < 0)
+-                      goto fail;
+-
+               if (crypto_bignum_cmp(x_candidate, prime) >= 0)
+                       continue;
+-              wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate",
+-                          prfbuf, primebytelen);
++              wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: x_candidate",
++                              prfbuf, primebytelen);
++              const_time_select_bin(found, x_bin, prfbuf, primebytelen,
++                                    x_bin);
+               /*
+                * compute y^2 using the equation of the curve
+@@ -260,13 +274,15 @@ int compute_password_element(EAP_PWD_gro
+                * Flip a coin, multiply by the random quadratic residue or the
+                * random quadratic nonresidue and record heads or tails.
+                */
+-              if (crypto_bignum_is_odd(tmp1)) {
+-                      crypto_bignum_mulmod(tmp2, qr, prime, tmp2);
+-                      check = 1;
+-              } else {
+-                      crypto_bignum_mulmod(tmp2, qnr, prime, tmp2);
+-                      check = -1;
+-              }
++              mask = const_time_eq_u8(crypto_bignum_is_odd(tmp1), 1);
++              check = const_time_select_s8(mask, 1, -1);
++              const_time_select_bin(mask, qr_bin, qnr_bin, primebytelen,
++                                    qr_or_qnr_bin);
++              crypto_bignum_deinit(qr_or_qnr, 1);
++              qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, primebytelen);
++              if (!qr_or_qnr ||
++                  crypto_bignum_mulmod(tmp2, qr_or_qnr, prime, tmp2) < 0)
++                      goto fail;
+               /*
+                * Now it's safe to do legendre, if check is 1 then it's
+@@ -274,59 +290,12 @@ int compute_password_element(EAP_PWD_gro
+                * change result), if check is -1 then it's the opposite test
+                * (multiplying a qr by qnr would make a qnr).
+                */
+-              if (crypto_bignum_legendre(tmp2, prime) == check) {
+-                      if (found == 1)
+-                              continue;
+-
+-                      /* need to unambiguously identify the solution */
+-                      is_odd = crypto_bignum_is_odd(rnd);
+-
+-                      /*
+-                       * We know x_candidate is a quadratic residue so set
+-                       * it here.
+-                       */
+-                      if (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");
+-                              continue;
+-                      }
+-
+-                      /*
+-                       * If there's a solution to the equation then the point
+-                       * must be on the curve so why check again explicitly?
+-                       * OpenSSL code says this is required by X9.62. We're
+-                       * not X9.62 but it can't hurt just to be sure.
+-                       */
+-                      if (!crypto_ec_point_is_on_curve(grp->group,
+-                                                       grp->pwe)) {
+-                              wpa_printf(MSG_INFO,
+-                                         "EAP-pwd: point is not on curve");
+-                              continue;
+-                      }
+-
+-                      if (!crypto_bignum_is_one(cofactor)) {
+-                              /* make sure the point is not in a small
+-                               * sub-group */
+-                              if (crypto_ec_point_mul(grp->group, grp->pwe,
+-                                                      cofactor,
+-                                                      grp->pwe) != 0) {
+-                                      wpa_printf(MSG_INFO,
+-                                                 "EAP-pwd: cannot multiply generator by order");
+-                                      continue;
+-                              }
+-                              if (crypto_ec_point_is_at_infinity(grp->group,
+-                                                                 grp->pwe)) {
+-                                      wpa_printf(MSG_INFO,
+-                                                 "EAP-pwd: point is at infinity");
+-                                      continue;
+-                              }
+-                      }
+-                      wpa_printf(MSG_DEBUG,
+-                                 "EAP-pwd: found a PWE in %d tries", ctr);
+-                      found = 1;
+-              }
++              res = crypto_bignum_legendre(tmp2, prime);
++              if (res == -2)
++                      goto fail;
++              mask = const_time_eq(res, check);
++              found_ctr = const_time_select_u8(found, found_ctr, ctr);
++              found |= mask;
+       }
+       if (found == 0) {
+               wpa_printf(MSG_INFO,
+@@ -334,6 +303,44 @@ int compute_password_element(EAP_PWD_gro
+                          num);
+               goto fail;
+       }
++
++      /*
++       * We know x_candidate is a quadratic residue so set it here.
++       */
++      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");
++              goto fail;
++      }
++
++      /*
++       * If there's a solution to the equation then the point must be on the
++       * curve so why check again explicitly? OpenSSL code says this is
++       * required by X9.62. We're not X9.62 but it can't hurt just to be sure.
++       */
++      if (!crypto_ec_point_is_on_curve(grp->group, grp->pwe)) {
++              wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve");
++              goto fail;
++      }
++
++      if (!crypto_bignum_is_one(cofactor)) {
++              /* make sure the point is not in a small sub-group */
++              if (crypto_ec_point_mul(grp->group, grp->pwe, cofactor,
++                                      grp->pwe) != 0) {
++                      wpa_printf(MSG_INFO,
++                                 "EAP-pwd: cannot multiply generator by order");
++                      goto fail;
++              }
++              if (crypto_ec_point_is_at_infinity(grp->group, grp->pwe)) {
++                      wpa_printf(MSG_INFO, "EAP-pwd: point is at infinity");
++                      goto fail;
++              }
++      }
++      wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %02d tries", found_ctr);
++
+       if (0) {
+  fail:
+               crypto_ec_point_deinit(grp->pwe, 1);
+@@ -343,14 +350,18 @@ int compute_password_element(EAP_PWD_gro
+       /* cleanliness and order.... */
+       crypto_bignum_deinit(cofactor, 1);
+       crypto_bignum_deinit(x_candidate, 1);
+-      crypto_bignum_deinit(rnd, 1);
+       crypto_bignum_deinit(pm1, 0);
+       crypto_bignum_deinit(tmp1, 1);
+       crypto_bignum_deinit(tmp2, 1);
+       crypto_bignum_deinit(qr, 1);
+       crypto_bignum_deinit(qnr, 1);
++      crypto_bignum_deinit(qr_or_qnr, 1);
+       crypto_bignum_deinit(one, 0);
+-      os_free(prfbuf);
++      bin_clear_free(prfbuf, primebytelen);
++      os_memset(qr_bin, 0, sizeof(qr_bin));
++      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));
+       return ret;
+ }
diff --git a/package/network/services/hostapd/patches/063-0010-SAE-Fix-confirm-message-validation-in-error-cases.patch b/package/network/services/hostapd/patches/063-0010-SAE-Fix-confirm-message-validation-in-error-cases.patch
new file mode 100644 (file)
index 0000000..3a3658e
--- /dev/null
@@ -0,0 +1,52 @@
+From ac8fa9ef198640086cf2ce7c94673be2b6a018a0 Mon Sep 17 00:00:00 2001
+From: Jouni Malinen <jouni@codeaurora.org>
+Date: Tue, 5 Mar 2019 23:43:25 +0200
+Subject: [PATCH 10/14] SAE: Fix confirm message validation in error cases
+
+Explicitly verify that own and peer commit scalar/element are available
+when trying to check SAE confirm message. It could have been possible to
+hit a NULL pointer dereference if the peer element could not have been
+parsed. (CVE-2019-9496)
+
+Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
+---
+ src/common/sae.c | 14 +++++++++++---
+ 1 file changed, 11 insertions(+), 3 deletions(-)
+
+--- a/src/common/sae.c
++++ b/src/common/sae.c
+@@ -1464,23 +1464,31 @@ int sae_check_confirm(struct sae_data *s
+       wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data));
+-      if (sae->tmp == NULL) {
++      if (!sae->tmp || !sae->peer_commit_scalar ||
++          !sae->tmp->own_commit_scalar) {
+               wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available");
+               return -1;
+       }
+-      if (sae->tmp->ec)
++      if (sae->tmp->ec) {
++              if (!sae->tmp->peer_commit_element_ecc ||
++                  !sae->tmp->own_commit_element_ecc)
++                      return -1;
+               sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar,
+                                  sae->tmp->peer_commit_element_ecc,
+                                  sae->tmp->own_commit_scalar,
+                                  sae->tmp->own_commit_element_ecc,
+                                  verifier);
+-      else
++      } else {
++              if (!sae->tmp->peer_commit_element_ffc ||
++                  !sae->tmp->own_commit_element_ffc)
++                      return -1;
+               sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar,
+                                  sae->tmp->peer_commit_element_ffc,
+                                  sae->tmp->own_commit_scalar,
+                                  sae->tmp->own_commit_element_ffc,
+                                  verifier);
++      }
+       if (os_memcmp_const(verifier, data + 2, SHA256_MAC_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch");
diff --git a/package/network/services/hostapd/patches/064-0011-EAP-pwd-server-Verify-received-scalar-and-element.patch b/package/network/services/hostapd/patches/064-0011-EAP-pwd-server-Verify-received-scalar-and-element.patch
new file mode 100644 (file)
index 0000000..5e0d0d2
--- /dev/null
@@ -0,0 +1,53 @@
+From 70ff850e89fbc8bc7da515321b4d15b5eef70581 Mon Sep 17 00:00:00 2001
+From: Mathy Vanhoef <mathy.vanhoef@nyu.edu>
+Date: Sun, 31 Mar 2019 17:13:06 +0200
+Subject: [PATCH 11/14] EAP-pwd server: Verify received scalar and element
+
+When processing an EAP-pwd Commit frame, the peer's scalar and element
+(elliptic curve point) were not validated. This allowed an adversary to
+bypass authentication, and impersonate any user if the crypto
+implementation did not verify the validity of the EC point.
+
+Fix this vulnerability by assuring the received scalar lies within the
+valid range, and by checking that the received element is not the point
+at infinity and lies on the elliptic curve being used. (CVE-2019-9498)
+
+The vulnerability is only exploitable if OpenSSL version 1.0.2 or lower
+is used, or if LibreSSL or wolfssl is used. Newer versions of OpenSSL
+(and also BoringSSL) implicitly validate the elliptic curve point in
+EC_POINT_set_affine_coordinates_GFp(), preventing the attack.
+
+Signed-off-by: Mathy Vanhoef <mathy.vanhoef@nyu.edu>
+---
+ src/eap_server/eap_server_pwd.c | 20 ++++++++++++++++++++
+ 1 file changed, 20 insertions(+)
+
+--- a/src/eap_server/eap_server_pwd.c
++++ b/src/eap_server/eap_server_pwd.c
+@@ -653,6 +653,26 @@ eap_pwd_process_commit_resp(struct eap_s
+               goto fin;
+       }
++      /* verify received scalar */
++      if (crypto_bignum_is_zero(data->peer_scalar) ||
++          crypto_bignum_is_one(data->peer_scalar) ||
++          crypto_bignum_cmp(data->peer_scalar,
++                            crypto_ec_get_order(data->grp->group)) >= 0) {
++              wpa_printf(MSG_INFO,
++                         "EAP-PWD (server): received scalar is invalid");
++              goto fin;
++      }
++
++      /* verify received element */
++      if (!crypto_ec_point_is_on_curve(data->grp->group,
++                                       data->peer_element) ||
++          crypto_ec_point_is_at_infinity(data->grp->group,
++                                         data->peer_element)) {
++              wpa_printf(MSG_INFO,
++                         "EAP-PWD (server): received element is invalid");
++              goto fin;
++      }
++
+       /* check to ensure peer's element is not in a small sub-group */
+       if (!crypto_bignum_is_one(cofactor)) {
+               if (crypto_ec_point_mul(data->grp->group, data->peer_element,
diff --git a/package/network/services/hostapd/patches/064-0012-EAP-pwd-server-Detect-reflection-attacks.patch b/package/network/services/hostapd/patches/064-0012-EAP-pwd-server-Detect-reflection-attacks.patch
new file mode 100644 (file)
index 0000000..340b233
--- /dev/null
@@ -0,0 +1,40 @@
+From d63edfa90243e9a7de6ae5c275032f2cc79fef95 Mon Sep 17 00:00:00 2001
+From: Mathy Vanhoef <mathy.vanhoef@nyu.edu>
+Date: Sun, 31 Mar 2019 17:26:01 +0200
+Subject: [PATCH 12/14] EAP-pwd server: Detect reflection attacks
+
+When processing an EAP-pwd Commit frame, verify that the peer's scalar
+and elliptic curve element differ from the one sent by the server. This
+prevents reflection attacks where the adversary reflects the scalar and
+element sent by the server. (CVE-2019-9497)
+
+The vulnerability allows an adversary to complete the EAP-pwd handshake
+as any user. However, the adversary does not learn the negotiated
+session key, meaning the subsequent 4-way handshake would fail. As a
+result, this cannot be abused to bypass authentication unless EAP-pwd is
+used in non-WLAN cases without any following key exchange that would
+require the attacker to learn the MSK.
+
+Signed-off-by: Mathy Vanhoef <mathy.vanhoef@nyu.edu>
+---
+ src/eap_server/eap_server_pwd.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+--- a/src/eap_server/eap_server_pwd.c
++++ b/src/eap_server/eap_server_pwd.c
+@@ -688,6 +688,15 @@ eap_pwd_process_commit_resp(struct eap_s
+               }
+       }
++      /* detect reflection attacks */
++      if (crypto_bignum_cmp(data->my_scalar, data->peer_scalar) == 0 ||
++          crypto_ec_point_cmp(data->grp->group, data->my_element,
++                              data->peer_element) == 0) {
++              wpa_printf(MSG_INFO,
++                         "EAP-PWD (server): detected reflection attack!");
++              goto fin;
++      }
++
+       /* compute the shared key, k */
+       if ((crypto_ec_point_mul(data->grp->group, data->grp->pwe,
+                                data->peer_scalar, K) < 0) ||
diff --git a/package/network/services/hostapd/patches/064-0013-EAP-pwd-client-Verify-received-scalar-and-element.patch b/package/network/services/hostapd/patches/064-0013-EAP-pwd-client-Verify-received-scalar-and-element.patch
new file mode 100644 (file)
index 0000000..f20b491
--- /dev/null
@@ -0,0 +1,53 @@
+From 8ad8585f91823ddcc3728155e288e0f9f872e31a Mon Sep 17 00:00:00 2001
+From: Mathy Vanhoef <mathy.vanhoef@nyu.edu>
+Date: Sun, 31 Mar 2019 17:43:44 +0200
+Subject: [PATCH 13/14] EAP-pwd client: Verify received scalar and element
+
+When processing an EAP-pwd Commit frame, the server's scalar and element
+(elliptic curve point) were not validated. This allowed an adversary to
+bypass authentication, and act as a rogue Access Point (AP) if the
+crypto implementation did not verify the validity of the EC point.
+
+Fix this vulnerability by assuring the received scalar lies within the
+valid range, and by checking that the received element is not the point
+at infinity and lies on the elliptic curve being used. (CVE-2019-9499)
+
+The vulnerability is only exploitable if OpenSSL version 1.0.2 or lower
+is used, or if LibreSSL or wolfssl is used. Newer versions of OpenSSL
+(and also BoringSSL) implicitly validate the elliptic curve point in
+EC_POINT_set_affine_coordinates_GFp(), preventing the attack.
+
+Signed-off-by: Mathy Vanhoef <mathy.vanhoef@nyu.edu>
+---
+ src/eap_peer/eap_pwd.c | 20 ++++++++++++++++++++
+ 1 file changed, 20 insertions(+)
+
+--- a/src/eap_peer/eap_pwd.c
++++ b/src/eap_peer/eap_pwd.c
+@@ -436,6 +436,26 @@ eap_pwd_perform_commit_exchange(struct e
+               goto fin;
+       }
++      /* verify received scalar */
++      if (crypto_bignum_is_zero(data->server_scalar) ||
++          crypto_bignum_is_one(data->server_scalar) ||
++          crypto_bignum_cmp(data->server_scalar,
++                            crypto_ec_get_order(data->grp->group)) >= 0) {
++              wpa_printf(MSG_INFO,
++                         "EAP-PWD (peer): received scalar is invalid");
++              goto fin;
++      }
++
++      /* verify received element */
++      if (!crypto_ec_point_is_on_curve(data->grp->group,
++                                       data->server_element) ||
++          crypto_ec_point_is_at_infinity(data->grp->group,
++                                         data->server_element)) {
++              wpa_printf(MSG_INFO,
++                         "EAP-PWD (peer): received element is invalid");
++              goto fin;
++      }
++
+       /* check to ensure server's element is not in a small sub-group */
+       if (!crypto_bignum_is_one(cofactor)) {
+               if (crypto_ec_point_mul(data->grp->group, data->server_element,
diff --git a/package/network/services/hostapd/patches/064-0014-EAP-pwd-Check-element-x-y-coordinates-explicitly.patch b/package/network/services/hostapd/patches/064-0014-EAP-pwd-Check-element-x-y-coordinates-explicitly.patch
new file mode 100644 (file)
index 0000000..1798431
--- /dev/null
@@ -0,0 +1,321 @@
+From 16d4f1069118aa19bfce013493e1ac5783f92f1d Mon Sep 17 00:00:00 2001
+From: Jouni Malinen <jouni@codeaurora.org>
+Date: Fri, 5 Apr 2019 02:12:50 +0300
+Subject: [PATCH 14/14] EAP-pwd: Check element x,y coordinates explicitly
+
+This adds an explicit check for 0 < x,y < prime based on RFC 5931,
+2.8.5.2.2 requirement. The earlier checks might have covered this
+implicitly, but it is safer to avoid any dependency on implicit checks
+and specific crypto library behavior. (CVE-2019-9498 and CVE-2019-9499)
+
+Furthermore, this moves the EAP-pwd element and scalar parsing and
+validation steps into shared helper functions so that there is no need
+to maintain two separate copies of this common functionality between the
+server and peer implementations.
+
+Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
+---
+ src/eap_common/eap_pwd_common.c | 106 ++++++++++++++++++++++++++++++++++++++++
+ src/eap_common/eap_pwd_common.h |   3 ++
+ src/eap_peer/eap_pwd.c          |  45 ++---------------
+ src/eap_server/eap_server_pwd.c |  45 ++---------------
+ 4 files changed, 117 insertions(+), 82 deletions(-)
+
+--- a/src/eap_common/eap_pwd_common.c
++++ b/src/eap_common/eap_pwd_common.c
+@@ -427,3 +427,109 @@ int compute_keys(EAP_PWD_group *grp, con
+       return 1;
+ }
++
++
++static int eap_pwd_element_coord_ok(const struct crypto_bignum *prime,
++                                  const u8 *buf, size_t len)
++{
++      struct crypto_bignum *val;
++      int ok = 1;
++
++      val = crypto_bignum_init_set(buf, len);
++      if (!val || crypto_bignum_is_zero(val) ||
++          crypto_bignum_cmp(val, prime) >= 0)
++              ok = 0;
++      crypto_bignum_deinit(val, 0);
++      return ok;
++}
++
++
++struct crypto_ec_point * eap_pwd_get_element(EAP_PWD_group *group,
++                                           const u8 *buf)
++{
++      struct crypto_ec_point *element;
++      const struct crypto_bignum *prime;
++      size_t prime_len;
++      struct crypto_bignum *cofactor = NULL;
++
++      prime = crypto_ec_get_prime(group->group);
++      prime_len = crypto_ec_prime_len(group->group);
++
++      /* RFC 5931, 2.8.5.2.2: 0 < x,y < p */
++      if (!eap_pwd_element_coord_ok(prime, buf, prime_len) ||
++          !eap_pwd_element_coord_ok(prime, buf + prime_len, prime_len)) {
++              wpa_printf(MSG_INFO, "EAP-pwd: Invalid coordinate in element");
++              return NULL;
++      }
++
++      element = crypto_ec_point_from_bin(group->group, buf);
++      if (!element) {
++              wpa_printf(MSG_INFO, "EAP-pwd: EC point from element failed");
++              return NULL;
++      }
++
++      /* RFC 5931, 2.8.5.2.2: on curve and not the point at infinity */
++      if (!crypto_ec_point_is_on_curve(group->group, element) ||
++          crypto_ec_point_is_at_infinity(group->group, element)) {
++              wpa_printf(MSG_INFO, "EAP-pwd: Invalid element");
++              goto fail;
++      }
++
++      cofactor = crypto_bignum_init();
++      if (!cofactor || crypto_ec_cofactor(group->group, cofactor) < 0) {
++              wpa_printf(MSG_INFO,
++                         "EAP-pwd: Unable to get cofactor for curve");
++              goto fail;
++      }
++
++      if (!crypto_bignum_is_one(cofactor)) {
++              struct crypto_ec_point *point;
++              int ok = 1;
++
++              /* check to ensure peer's element is not in a small sub-group */
++              point = crypto_ec_point_init(group->group);
++              if (!point ||
++                  crypto_ec_point_mul(group->group, element,
++                                      cofactor, point) != 0 ||
++                  crypto_ec_point_is_at_infinity(group->group, point))
++                      ok = 0;
++              crypto_ec_point_deinit(point, 0);
++
++              if (!ok) {
++                      wpa_printf(MSG_INFO,
++                                 "EAP-pwd: Small sub-group check on peer element failed");
++                      goto fail;
++              }
++      }
++
++out:
++      crypto_bignum_deinit(cofactor, 0);
++      return element;
++fail:
++      crypto_ec_point_deinit(element, 0);
++      element = NULL;
++      goto out;
++}
++
++
++struct crypto_bignum * eap_pwd_get_scalar(EAP_PWD_group *group, const u8 *buf)
++{
++      struct crypto_bignum *scalar;
++      const struct crypto_bignum *order;
++      size_t order_len;
++
++      order = crypto_ec_get_order(group->group);
++      order_len = crypto_ec_order_len(group->group);
++
++      /* RFC 5931, 2.8.5.2: 1 < scalar < r */
++      scalar = crypto_bignum_init_set(buf, order_len);
++      if (!scalar || crypto_bignum_is_zero(scalar) ||
++          crypto_bignum_is_one(scalar) ||
++          crypto_bignum_cmp(scalar, order) >= 0) {
++              wpa_printf(MSG_INFO, "EAP-pwd: received scalar is invalid");
++              crypto_bignum_deinit(scalar, 0);
++              scalar = NULL;
++      }
++
++      return scalar;
++}
+--- a/src/eap_common/eap_pwd_common.h
++++ b/src/eap_common/eap_pwd_common.h
+@@ -64,5 +64,8 @@ int compute_keys(EAP_PWD_group *grp, con
+ struct crypto_hash * eap_pwd_h_init(void);
+ void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len);
+ void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest);
++struct crypto_ec_point * eap_pwd_get_element(EAP_PWD_group *group,
++                                           const u8 *buf);
++struct crypto_bignum * eap_pwd_get_scalar(EAP_PWD_group *group, const u8 *buf);
+ #endif  /* EAP_PWD_COMMON_H */
+--- a/src/eap_peer/eap_pwd.c
++++ b/src/eap_peer/eap_pwd.c
+@@ -344,7 +344,7 @@ eap_pwd_perform_commit_exchange(struct e
+                               const struct wpabuf *reqData,
+                               const u8 *payload, size_t payload_len)
+ {
+-      struct crypto_ec_point *K = NULL, *point = NULL;
++      struct crypto_ec_point *K = NULL;
+       struct crypto_bignum *mask = NULL, *cofactor = NULL;
+       const u8 *ptr;
+       u8 *scalar = NULL, *element = NULL;
+@@ -413,8 +413,7 @@ eap_pwd_perform_commit_exchange(struct e
+       /* process the request */
+       data->k = crypto_bignum_init();
+       K = crypto_ec_point_init(data->grp->group);
+-      point = crypto_ec_point_init(data->grp->group);
+-      if (!data->k || !K || !point) {
++      if (!data->k || !K) {
+               wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation "
+                          "fail");
+               goto fin;
+@@ -422,55 +421,20 @@ eap_pwd_perform_commit_exchange(struct e
+       /* element, x then y, followed by scalar */
+       ptr = payload;
+-      data->server_element = crypto_ec_point_from_bin(data->grp->group, ptr);
++      data->server_element = eap_pwd_get_element(data->grp, ptr);
+       if (!data->server_element) {
+               wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element "
+                          "fail");
+               goto fin;
+       }
+       ptr += prime_len * 2;
+-      data->server_scalar = crypto_bignum_init_set(ptr, order_len);
++      data->server_scalar = eap_pwd_get_scalar(data->grp, ptr);
+       if (!data->server_scalar) {
+               wpa_printf(MSG_INFO,
+                          "EAP-PWD (peer): setting peer scalar fail");
+               goto fin;
+       }
+-      /* verify received scalar */
+-      if (crypto_bignum_is_zero(data->server_scalar) ||
+-          crypto_bignum_is_one(data->server_scalar) ||
+-          crypto_bignum_cmp(data->server_scalar,
+-                            crypto_ec_get_order(data->grp->group)) >= 0) {
+-              wpa_printf(MSG_INFO,
+-                         "EAP-PWD (peer): received scalar is invalid");
+-              goto fin;
+-      }
+-
+-      /* verify received element */
+-      if (!crypto_ec_point_is_on_curve(data->grp->group,
+-                                       data->server_element) ||
+-          crypto_ec_point_is_at_infinity(data->grp->group,
+-                                         data->server_element)) {
+-              wpa_printf(MSG_INFO,
+-                         "EAP-PWD (peer): received element is invalid");
+-              goto fin;
+-      }
+-
+-      /* check to ensure server's element is not in a small sub-group */
+-      if (!crypto_bignum_is_one(cofactor)) {
+-              if (crypto_ec_point_mul(data->grp->group, data->server_element,
+-                                      cofactor, point) < 0) {
+-                      wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
+-                                 "server element by order!\n");
+-                      goto fin;
+-              }
+-              if (crypto_ec_point_is_at_infinity(data->grp->group, point)) {
+-                      wpa_printf(MSG_INFO, "EAP-PWD (peer): server element "
+-                                 "is at infinity!\n");
+-                      goto fin;
+-              }
+-      }
+-
+       /* compute the shared key, k */
+       if (crypto_ec_point_mul(data->grp->group, data->grp->pwe,
+                               data->server_scalar, K) < 0 ||
+@@ -544,7 +508,6 @@ fin:
+       crypto_bignum_deinit(mask, 1);
+       crypto_bignum_deinit(cofactor, 1);
+       crypto_ec_point_deinit(K, 1);
+-      crypto_ec_point_deinit(point, 1);
+       if (data->outbuf == NULL)
+               eap_pwd_state(data, FAILURE);
+       else
+--- a/src/eap_server/eap_server_pwd.c
++++ b/src/eap_server/eap_server_pwd.c
+@@ -604,7 +604,7 @@ eap_pwd_process_commit_resp(struct eap_s
+ {
+       const u8 *ptr;
+       struct crypto_bignum *cofactor = NULL;
+-      struct crypto_ec_point *K = NULL, *point = NULL;
++      struct crypto_ec_point *K = NULL;
+       int res = 0;
+       size_t prime_len, order_len;
+@@ -623,9 +623,8 @@ eap_pwd_process_commit_resp(struct eap_s
+       data->k = crypto_bignum_init();
+       cofactor = crypto_bignum_init();
+-      point = crypto_ec_point_init(data->grp->group);
+       K = crypto_ec_point_init(data->grp->group);
+-      if (!data->k || !cofactor || !point || !K) {
++      if (!data->k || !cofactor || !K) {
+               wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation "
+                          "fail");
+               goto fin;
+@@ -639,55 +638,20 @@ eap_pwd_process_commit_resp(struct eap_s
+       /* element, x then y, followed by scalar */
+       ptr = payload;
+-      data->peer_element = crypto_ec_point_from_bin(data->grp->group, ptr);
++      data->peer_element = eap_pwd_get_element(data->grp, ptr);
+       if (!data->peer_element) {
+               wpa_printf(MSG_INFO, "EAP-PWD (server): setting peer element "
+                          "fail");
+               goto fin;
+       }
+       ptr += prime_len * 2;
+-      data->peer_scalar = crypto_bignum_init_set(ptr, order_len);
++      data->peer_scalar = eap_pwd_get_scalar(data->grp, ptr);
+       if (!data->peer_scalar) {
+               wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation "
+                          "fail");
+               goto fin;
+       }
+-      /* verify received scalar */
+-      if (crypto_bignum_is_zero(data->peer_scalar) ||
+-          crypto_bignum_is_one(data->peer_scalar) ||
+-          crypto_bignum_cmp(data->peer_scalar,
+-                            crypto_ec_get_order(data->grp->group)) >= 0) {
+-              wpa_printf(MSG_INFO,
+-                         "EAP-PWD (server): received scalar is invalid");
+-              goto fin;
+-      }
+-
+-      /* verify received element */
+-      if (!crypto_ec_point_is_on_curve(data->grp->group,
+-                                       data->peer_element) ||
+-          crypto_ec_point_is_at_infinity(data->grp->group,
+-                                         data->peer_element)) {
+-              wpa_printf(MSG_INFO,
+-                         "EAP-PWD (server): received element is invalid");
+-              goto fin;
+-      }
+-
+-      /* check to ensure peer's element is not in a small sub-group */
+-      if (!crypto_bignum_is_one(cofactor)) {
+-              if (crypto_ec_point_mul(data->grp->group, data->peer_element,
+-                                      cofactor, point) != 0) {
+-                      wpa_printf(MSG_INFO, "EAP-PWD (server): cannot "
+-                                 "multiply peer element by order");
+-                      goto fin;
+-              }
+-              if (crypto_ec_point_is_at_infinity(data->grp->group, point)) {
+-                      wpa_printf(MSG_INFO, "EAP-PWD (server): peer element "
+-                                 "is at infinity!\n");
+-                      goto fin;
+-              }
+-      }
+-
+       /* detect reflection attacks */
+       if (crypto_bignum_cmp(data->my_scalar, data->peer_scalar) == 0 ||
+           crypto_ec_point_cmp(data->grp->group, data->my_element,
+@@ -739,7 +703,6 @@ eap_pwd_process_commit_resp(struct eap_s
+ fin:
+       crypto_ec_point_deinit(K, 1);
+-      crypto_ec_point_deinit(point, 1);
+       crypto_bignum_deinit(cofactor, 1);
+       if (res)
diff --git a/package/network/services/hostapd/patches/065-0001-EAP-pwd-server-Fix-reassembly-buffer-handling.patch b/package/network/services/hostapd/patches/065-0001-EAP-pwd-server-Fix-reassembly-buffer-handling.patch
new file mode 100644 (file)
index 0000000..9ceb8d7
--- /dev/null
@@ -0,0 +1,40 @@
+From fe76f487e28bdc61940f304f153a954cf36935ea Mon Sep 17 00:00:00 2001
+From: Jouni Malinen <jouni@codeaurora.org>
+Date: Wed, 17 Apr 2019 01:55:32 +0300
+Subject: [PATCH 1/3] EAP-pwd server: Fix reassembly buffer handling
+
+data->inbuf allocation might fail and if that were to happen, the next
+fragment in the exchange could have resulted in NULL pointer
+dereference. Unexpected fragment with more bit might also be able to
+trigger this. Fix that by explicitly checking for data->inbuf to be
+available before using it.
+
+Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
+---
+ src/eap_server/eap_server_pwd.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+--- a/src/eap_server/eap_server_pwd.c
++++ b/src/eap_server/eap_server_pwd.c
+@@ -882,6 +882,12 @@ static void eap_pwd_process(struct eap_s
+        * the first and all intermediate fragments have the M bit set
+        */
+       if (EAP_PWD_GET_MORE_BIT(lm_exch) || data->in_frag_pos) {
++              if (!data->inbuf) {
++                      wpa_printf(MSG_DEBUG,
++                                 "EAP-pwd: No buffer for reassembly");
++                      eap_pwd_state(data, FAILURE);
++                      return;
++              }
+               if ((data->in_frag_pos + len) > wpabuf_size(data->inbuf)) {
+                       wpa_printf(MSG_DEBUG, "EAP-pwd: Buffer overflow "
+                                  "attack detected! (%d+%d > %d)",
+@@ -902,7 +908,7 @@ static void eap_pwd_process(struct eap_s
+        * last fragment won't have the M bit set (but we're obviously
+        * buffering fragments so that's how we know it's the last)
+        */
+-      if (data->in_frag_pos) {
++      if (data->in_frag_pos && data->inbuf) {
+               pos = wpabuf_head_u8(data->inbuf);
+               len = data->in_frag_pos;
+               wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes",
diff --git a/package/network/services/hostapd/patches/065-0003-EAP-pwd-peer-Fix-reassembly-buffer-handling.patch b/package/network/services/hostapd/patches/065-0003-EAP-pwd-peer-Fix-reassembly-buffer-handling.patch
new file mode 100644 (file)
index 0000000..b62420d
--- /dev/null
@@ -0,0 +1,40 @@
+From d2d1a324ce937628e4d9d9999fe113819b7d4478 Mon Sep 17 00:00:00 2001
+From: Jouni Malinen <jouni@codeaurora.org>
+Date: Wed, 17 Apr 2019 02:21:20 +0300
+Subject: [PATCH 3/3] EAP-pwd peer: Fix reassembly buffer handling
+
+Unexpected fragment might result in data->inbuf not being allocated
+before processing and that could have resulted in NULL pointer
+dereference. Fix that by explicitly checking for data->inbuf to be
+available before using it.
+
+Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
+---
+ src/eap_peer/eap_pwd.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+--- a/src/eap_peer/eap_pwd.c
++++ b/src/eap_peer/eap_pwd.c
+@@ -805,6 +805,13 @@ eap_pwd_process(struct eap_sm *sm, void
+        * buffer and ACK the fragment
+        */
+       if (EAP_PWD_GET_MORE_BIT(lm_exch) || data->in_frag_pos) {
++              if (!data->inbuf) {
++                      wpa_printf(MSG_DEBUG,
++                                 "EAP-pwd: No buffer for reassembly");
++                      ret->methodState = METHOD_DONE;
++                      ret->decision = DECISION_FAIL;
++                      return NULL;
++              }
+               data->in_frag_pos += len;
+               if (data->in_frag_pos > wpabuf_size(data->inbuf)) {
+                       wpa_printf(MSG_INFO, "EAP-pwd: Buffer overflow attack "
+@@ -831,7 +838,7 @@ eap_pwd_process(struct eap_sm *sm, void
+       /*
+        * we're buffering and this is the last fragment
+        */
+-      if (data->in_frag_pos) {
++      if (data->in_frag_pos && data->inbuf) {
+               wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes",
+                          (int) len);
+               pos = wpabuf_head_u8(data->inbuf);