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