From b463a13881d3699c0f2d67ceeda146c76af58ac6 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Fri, 17 May 2019 23:22:02 +0200 Subject: [PATCH] hostapd: fix multiple security problems 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 --- package/network/services/hostapd/Makefile | 2 +- ...group-initialization-to-earlier-step.patch | 104 ++++++ ...AP-pwd-Mask-timing-of-PWE-derivation.patch | 247 ++++++++++++++ ...tant-time-operations-for-private-big.patch | 88 +++++ ...nctions-for-constant-time-operations.patch | 212 ++++++++++++ ...tant-time-selection-for-crypto_bignu.patch | 55 +++ ...timing-differences-in-PWE-derivation.patch | 242 +++++++++++++ ...anches-in-is_quadratic_residue_blind.patch | 139 ++++++++ ...-Mask-timing-of-MODP-groups-22-23-24.patch | 113 ++++++ ...-const_time-selection-for-PWE-in-FFC.patch | 100 ++++++ ...-time-operations-in-sae_test_pwd_see.patch | 133 ++++++++ ...tant-time-and-memory-access-for-find.patch | 319 +++++++++++++++++ ...rm-message-validation-in-error-cases.patch | 52 +++ ...r-Verify-received-scalar-and-element.patch | 53 +++ ...pwd-server-Detect-reflection-attacks.patch | 40 +++ ...t-Verify-received-scalar-and-element.patch | 53 +++ ...k-element-x-y-coordinates-explicitly.patch | 321 ++++++++++++++++++ ...erver-Fix-reassembly-buffer-handling.patch | 40 +++ ...-peer-Fix-reassembly-buffer-handling.patch | 40 +++ 19 files changed, 2352 insertions(+), 1 deletion(-) create mode 100644 package/network/services/hostapd/patches/060-0001-EAP-pwd-Move-EC-group-initialization-to-earlier-step.patch create mode 100644 package/network/services/hostapd/patches/060-0002-EAP-pwd-Mask-timing-of-PWE-derivation.patch create mode 100644 package/network/services/hostapd/patches/061-0001-OpenSSL-Use-constant-time-operations-for-private-big.patch create mode 100644 package/network/services/hostapd/patches/061-0002-Add-helper-functions-for-constant-time-operations.patch create mode 100644 package/network/services/hostapd/patches/061-0003-OpenSSL-Use-constant-time-selection-for-crypto_bignu.patch create mode 100644 package/network/services/hostapd/patches/061-0005-SAE-Minimize-timing-differences-in-PWE-derivation.patch create mode 100644 package/network/services/hostapd/patches/061-0006-SAE-Avoid-branches-in-is_quadratic_residue_blind.patch create mode 100644 package/network/services/hostapd/patches/061-0007-SAE-Mask-timing-of-MODP-groups-22-23-24.patch create mode 100644 package/network/services/hostapd/patches/061-0008-SAE-Use-const_time-selection-for-PWE-in-FFC.patch create mode 100644 package/network/services/hostapd/patches/061-0009-SAE-Use-constant-time-operations-in-sae_test_pwd_see.patch create mode 100644 package/network/services/hostapd/patches/062-0004-EAP-pwd-Use-constant-time-and-memory-access-for-find.patch create mode 100644 package/network/services/hostapd/patches/063-0010-SAE-Fix-confirm-message-validation-in-error-cases.patch create mode 100644 package/network/services/hostapd/patches/064-0011-EAP-pwd-server-Verify-received-scalar-and-element.patch create mode 100644 package/network/services/hostapd/patches/064-0012-EAP-pwd-server-Detect-reflection-attacks.patch create mode 100644 package/network/services/hostapd/patches/064-0013-EAP-pwd-client-Verify-received-scalar-and-element.patch create mode 100644 package/network/services/hostapd/patches/064-0014-EAP-pwd-Check-element-x-y-coordinates-explicitly.patch create mode 100644 package/network/services/hostapd/patches/065-0001-EAP-pwd-server-Fix-reassembly-buffer-handling.patch create mode 100644 package/network/services/hostapd/patches/065-0003-EAP-pwd-peer-Fix-reassembly-buffer-handling.patch diff --git a/package/network/services/hostapd/Makefile b/package/network/services/hostapd/Makefile index dacdf701b9..b548ecdf1b 100644 --- a/package/network/services/hostapd/Makefile +++ b/package/network/services/hostapd/Makefile @@ -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 index 0000000000..6d35769e30 --- /dev/null +++ b/package/network/services/hostapd/patches/060-0001-EAP-pwd-Move-EC-group-initialization-to-earlier-step.patch @@ -0,0 +1,104 @@ +From 2a5c291881fa819325d0287d0763776edfcb1943 Mon Sep 17 00:00:00 2001 +From: Dan Harkins +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 +--- + 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 index 0000000000..0a27abb9e9 --- /dev/null +++ b/package/network/services/hostapd/patches/060-0002-EAP-pwd-Mask-timing-of-PWE-derivation.patch @@ -0,0 +1,247 @@ +From 22ac3dfebf7b25a3aae02f9b4f69025bb4173137 Mon Sep 17 00:00:00 2001 +From: Dan Harkins +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 +--- + 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 index 0000000000..8e6a8cfec3 --- /dev/null +++ b/package/network/services/hostapd/patches/061-0001-OpenSSL-Use-constant-time-operations-for-private-big.patch @@ -0,0 +1,88 @@ +From d42c477cc794163a3757956bbffca5cea000923c Mon Sep 17 00:00:00 2001 +From: Jouni Malinen +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 +--- + 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 index 0000000000..87e41aea87 --- /dev/null +++ b/package/network/services/hostapd/patches/061-0002-Add-helper-functions-for-constant-time-operations.patch @@ -0,0 +1,212 @@ +From 6e34f618d37ddbb5854c42e2ad4fca83492fa7b7 Mon Sep 17 00:00:00 2001 +From: Jouni Malinen +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 +--- + 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 index 0000000000..003985bdd2 --- /dev/null +++ b/package/network/services/hostapd/patches/061-0003-OpenSSL-Use-constant-time-selection-for-crypto_bignu.patch @@ -0,0 +1,55 @@ +From c93461c1d98f52681717a088776ab32fd97872b0 Mon Sep 17 00:00:00 2001 +From: Jouni Malinen +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 +--- + 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 index 0000000000..e72a9cbe5a --- /dev/null +++ b/package/network/services/hostapd/patches/061-0005-SAE-Minimize-timing-differences-in-PWE-derivation.patch @@ -0,0 +1,242 @@ +From 6513db3e96c43c2e36805cf5ead349765d18eaf7 Mon Sep 17 00:00:00 2001 +From: Jouni Malinen +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 +--- + 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 index 0000000000..6d93cb2480 --- /dev/null +++ b/package/network/services/hostapd/patches/061-0006-SAE-Avoid-branches-in-is_quadratic_residue_blind.patch @@ -0,0 +1,139 @@ +From 362704dda04507e7ebb8035122e83d9f0ae7c320 Mon Sep 17 00:00:00 2001 +From: Jouni Malinen +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 +--- + 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 index 0000000000..229d2b1070 --- /dev/null +++ b/package/network/services/hostapd/patches/061-0007-SAE-Mask-timing-of-MODP-groups-22-23-24.patch @@ -0,0 +1,113 @@ +From 90839597cc4016b33f00055b12d59174c62770a3 Mon Sep 17 00:00:00 2001 +From: Jouni Malinen +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 +--- + 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 index 0000000000..47e1b3c68e --- /dev/null +++ b/package/network/services/hostapd/patches/061-0008-SAE-Use-const_time-selection-for-PWE-in-FFC.patch @@ -0,0 +1,100 @@ +From f8f20717f87eff1f025f48ed585c7684debacf72 Mon Sep 17 00:00:00 2001 +From: Jouni Malinen +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 +--- + 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 index 0000000000..150cbeb8ac --- /dev/null +++ b/package/network/services/hostapd/patches/061-0009-SAE-Use-constant-time-operations-in-sae_test_pwd_see.patch @@ -0,0 +1,133 @@ +From cff138b0747fa39765cbc641b66cfa5d7f1735d1 Mon Sep 17 00:00:00 2001 +From: Jouni Malinen +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 +--- + 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 index 0000000000..1870c497c4 --- /dev/null +++ b/package/network/services/hostapd/patches/062-0004-EAP-pwd-Use-constant-time-and-memory-access-for-find.patch @@ -0,0 +1,319 @@ +From aaf65feac67c3993935634eefe5bc76b9fce03aa Mon Sep 17 00:00:00 2001 +From: Jouni Malinen +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 +--- + 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 index 0000000000..3a3658e640 --- /dev/null +++ b/package/network/services/hostapd/patches/063-0010-SAE-Fix-confirm-message-validation-in-error-cases.patch @@ -0,0 +1,52 @@ +From ac8fa9ef198640086cf2ce7c94673be2b6a018a0 Mon Sep 17 00:00:00 2001 +From: Jouni Malinen +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 +--- + 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 index 0000000000..5e0d0d2078 --- /dev/null +++ b/package/network/services/hostapd/patches/064-0011-EAP-pwd-server-Verify-received-scalar-and-element.patch @@ -0,0 +1,53 @@ +From 70ff850e89fbc8bc7da515321b4d15b5eef70581 Mon Sep 17 00:00:00 2001 +From: Mathy Vanhoef +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 +--- + 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 index 0000000000..340b23395f --- /dev/null +++ b/package/network/services/hostapd/patches/064-0012-EAP-pwd-server-Detect-reflection-attacks.patch @@ -0,0 +1,40 @@ +From d63edfa90243e9a7de6ae5c275032f2cc79fef95 Mon Sep 17 00:00:00 2001 +From: Mathy Vanhoef +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 +--- + 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 index 0000000000..f20b491306 --- /dev/null +++ b/package/network/services/hostapd/patches/064-0013-EAP-pwd-client-Verify-received-scalar-and-element.patch @@ -0,0 +1,53 @@ +From 8ad8585f91823ddcc3728155e288e0f9f872e31a Mon Sep 17 00:00:00 2001 +From: Mathy Vanhoef +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 +--- + 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 index 0000000000..17984312ff --- /dev/null +++ b/package/network/services/hostapd/patches/064-0014-EAP-pwd-Check-element-x-y-coordinates-explicitly.patch @@ -0,0 +1,321 @@ +From 16d4f1069118aa19bfce013493e1ac5783f92f1d Mon Sep 17 00:00:00 2001 +From: Jouni Malinen +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 +--- + 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 index 0000000000..9ceb8d7098 --- /dev/null +++ b/package/network/services/hostapd/patches/065-0001-EAP-pwd-server-Fix-reassembly-buffer-handling.patch @@ -0,0 +1,40 @@ +From fe76f487e28bdc61940f304f153a954cf36935ea Mon Sep 17 00:00:00 2001 +From: Jouni Malinen +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 +--- + 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 index 0000000000..b62420da37 --- /dev/null +++ b/package/network/services/hostapd/patches/065-0003-EAP-pwd-peer-Fix-reassembly-buffer-handling.patch @@ -0,0 +1,40 @@ +From d2d1a324ce937628e4d9d9999fe113819b7d4478 Mon Sep 17 00:00:00 2001 +From: Jouni Malinen +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 +--- + 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); -- 2.30.2