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 <hauke@hauke-m.de>
This commit is contained in:
Hauke Mehrtens 2022-02-12 20:37:12 +01:00
parent 5b13b0b02c
commit 0c0db6e66b
4 changed files with 268 additions and 1 deletions

View File

@ -7,7 +7,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=hostapd
PKG_RELEASE:=38
PKG_RELEASE:=39
PKG_SOURCE_URL:=http://w1.fi/hostap.git
PKG_SOURCE_PROTO:=git

View File

@ -0,0 +1,65 @@
From 2232d3d5f188b65dbb6c823ac62175412739eb16 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <j@w1.fi>
Date: Fri, 7 Jan 2022 13:47:16 +0200
Subject: [PATCH 2/4] dragonfly: Add sqrt() helper function
This is a backport of "SAE: Move sqrt() implementation into a helper
function" to introduce the helper function needed for the following
patches.
Signed-off-by: Jouni Malinen <j@w1.fi>
---
src/common/dragonfly.c | 34 ++++++++++++++++++++++++++++++++++
src/common/dragonfly.h | 2 ++
2 files changed, 36 insertions(+)
--- a/src/common/dragonfly.c
+++ b/src/common/dragonfly.c
@@ -213,3 +213,37 @@ int dragonfly_generate_scalar(const stru
"dragonfly: Unable to get randomness for own scalar");
return -1;
}
+
+
+/* res = sqrt(val) */
+int dragonfly_sqrt(struct crypto_ec *ec, const struct crypto_bignum *val,
+ struct crypto_bignum *res)
+{
+ const struct crypto_bignum *prime;
+ struct crypto_bignum *tmp, *one;
+ int ret = 0;
+ u8 prime_bin[DRAGONFLY_MAX_ECC_PRIME_LEN];
+ size_t prime_len;
+
+ /* For prime p such that p = 3 mod 4, sqrt(w) = w^((p+1)/4) mod p */
+
+ prime = crypto_ec_get_prime(ec);
+ prime_len = crypto_ec_prime_len(ec);
+ tmp = crypto_bignum_init();
+ one = crypto_bignum_init_uint(1);
+
+ if (crypto_bignum_to_bin(prime, prime_bin, sizeof(prime_bin),
+ prime_len) < 0 ||
+ (prime_bin[prime_len - 1] & 0x03) != 3 ||
+ !tmp || !one ||
+ /* tmp = (p+1)/4 */
+ crypto_bignum_add(prime, one, tmp) < 0 ||
+ crypto_bignum_rshift(tmp, 2, tmp) < 0 ||
+ /* res = sqrt(val) */
+ crypto_bignum_exptmod(val, tmp, prime, res) < 0)
+ ret = -1;
+
+ crypto_bignum_deinit(tmp, 0);
+ crypto_bignum_deinit(one, 0);
+ return ret;
+}
--- a/src/common/dragonfly.h
+++ b/src/common/dragonfly.h
@@ -27,5 +27,7 @@ int dragonfly_generate_scalar(const stru
struct crypto_bignum *_rand,
struct crypto_bignum *_mask,
struct crypto_bignum *scalar);
+int dragonfly_sqrt(struct crypto_ec *ec, const struct crypto_bignum *val,
+ struct crypto_bignum *res);
#endif /* DRAGONFLY_H */

View File

@ -0,0 +1,94 @@
From fe534b0baaa8c0e6ddeb24cf529d6e50e33dc501 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <j@w1.fi>
Date: Fri, 7 Jan 2022 13:47:16 +0200
Subject: [PATCH 3/4] SAE: Derive the y coordinate for PWE with own
implementation
The crypto_ec_point_solve_y_coord() wrapper function might not use
constant time operations in the crypto library and as such, could leak
side channel information about the password that is used to generate the
PWE in the hunting and pecking loop. As such, calculate the two possible
y coordinate values and pick the correct one to use with constant time
selection.
Signed-off-by: Jouni Malinen <j@w1.fi>
---
src/common/sae.c | 47 +++++++++++++++++++++++++++++++++--------------
1 file changed, 33 insertions(+), 14 deletions(-)
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -294,14 +294,16 @@ static int sae_derive_pwe_ecc(struct sae
int pwd_seed_odd = 0;
u8 prime[SAE_MAX_ECC_PRIME_LEN];
size_t prime_len;
- struct crypto_bignum *x = NULL, *qr = NULL, *qnr = NULL;
+ struct crypto_bignum *x = NULL, *y = NULL, *qr = NULL, *qnr = NULL;
u8 x_bin[SAE_MAX_ECC_PRIME_LEN];
u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN];
u8 qr_bin[SAE_MAX_ECC_PRIME_LEN];
u8 qnr_bin[SAE_MAX_ECC_PRIME_LEN];
+ u8 x_y[2 * SAE_MAX_ECC_PRIME_LEN];
int res = -1;
u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
* mask */
+ unsigned int is_eq;
os_memset(x_bin, 0, sizeof(x_bin));
@@ -410,25 +412,42 @@ static int sae_derive_pwe_ecc(struct sae
goto fail;
}
- if (!sae->tmp->pwe_ecc)
- sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec);
- if (!sae->tmp->pwe_ecc)
- res = -1;
- else
- res = crypto_ec_point_solve_y_coord(sae->tmp->ec,
- sae->tmp->pwe_ecc, x,
- pwd_seed_odd);
- if (res < 0) {
- /*
- * This should not happen since we already checked that there
- * is a result.
- */
+ /* y = sqrt(x^3 + ax + b) mod p
+ * if LSB(save) == LSB(y): PWE = (x, y)
+ * else: PWE = (x, p - y)
+ *
+ * Calculate y and the two possible values for PWE and after that,
+ * use constant time selection to copy the correct alternative.
+ */
+ y = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x);
+ if (!y ||
+ dragonfly_sqrt(sae->tmp->ec, y, y) < 0 ||
+ crypto_bignum_to_bin(y, x_y, SAE_MAX_ECC_PRIME_LEN,
+ prime_len) < 0 ||
+ crypto_bignum_sub(sae->tmp->prime, y, y) < 0 ||
+ crypto_bignum_to_bin(y, x_y + SAE_MAX_ECC_PRIME_LEN,
+ SAE_MAX_ECC_PRIME_LEN, prime_len) < 0) {
wpa_printf(MSG_DEBUG, "SAE: Could not solve y");
+ goto fail;
+ }
+
+ is_eq = const_time_eq(pwd_seed_odd, x_y[prime_len - 1] & 0x01);
+ const_time_select_bin(is_eq, x_y, x_y + SAE_MAX_ECC_PRIME_LEN,
+ prime_len, x_y + prime_len);
+ os_memcpy(x_y, x_bin, prime_len);
+ wpa_hexdump_key(MSG_DEBUG, "SAE: PWE", x_y, 2 * prime_len);
+ crypto_ec_point_deinit(sae->tmp->pwe_ecc, 1);
+ sae->tmp->pwe_ecc = crypto_ec_point_from_bin(sae->tmp->ec, x_y);
+ if (!sae->tmp->pwe_ecc) {
+ wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
+ res = -1;
}
fail:
+ forced_memzero(x_y, sizeof(x_y));
crypto_bignum_deinit(qr, 0);
crypto_bignum_deinit(qnr, 0);
+ crypto_bignum_deinit(y, 1);
os_free(dummy_password);
bin_clear_free(tmp_password, password_len);
crypto_bignum_deinit(x, 1);

View File

@ -0,0 +1,108 @@
From 603cd880e7f90595482658a7136fa6a7be5cb485 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <j@w1.fi>
Date: Fri, 7 Jan 2022 18:52:27 +0200
Subject: [PATCH 4/4] EAP-pwd: Derive the y coordinate for PWE with own
implementation
The crypto_ec_point_solve_y_coord() wrapper function might not use
constant time operations in the crypto library and as such, could leak
side channel information about the password that is used to generate the
PWE in the hunting and pecking loop. As such, calculate the two possible
y coordinate values and pick the correct one to use with constant time
selection.
Signed-off-by: Jouni Malinen <j@w1.fi>
---
src/eap_common/eap_pwd_common.c | 46 ++++++++++++++++++++++++++-------
1 file changed, 36 insertions(+), 10 deletions(-)
--- a/src/eap_common/eap_pwd_common.c
+++ b/src/eap_common/eap_pwd_common.c
@@ -127,7 +127,8 @@ int compute_password_element(EAP_PWD_gro
u8 qr_or_qnr_bin[MAX_ECC_PRIME_LEN];
u8 x_bin[MAX_ECC_PRIME_LEN];
u8 prime_bin[MAX_ECC_PRIME_LEN];
- struct crypto_bignum *tmp2 = NULL;
+ u8 x_y[2 * MAX_ECC_PRIME_LEN];
+ struct crypto_bignum *tmp2 = NULL, *y = NULL;
struct crypto_hash *hash;
unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr;
int ret = 0, res;
@@ -139,6 +140,7 @@ int compute_password_element(EAP_PWD_gro
u8 found_ctr = 0, is_odd = 0;
int cmp_prime;
unsigned int in_range;
+ unsigned int is_eq;
if (grp->pwe)
return -1;
@@ -151,11 +153,6 @@ int compute_password_element(EAP_PWD_gro
if (crypto_bignum_to_bin(prime, prime_bin, sizeof(prime_bin),
primebytelen) < 0)
return -1;
- grp->pwe = crypto_ec_point_init(grp->group);
- if (!grp->pwe) {
- wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums");
- goto fail;
- }
if ((prfbuf = os_malloc(primebytelen)) == NULL) {
wpa_printf(MSG_INFO, "EAP-pwd: unable to malloc space for prf "
@@ -261,10 +258,37 @@ int compute_password_element(EAP_PWD_gro
*/
crypto_bignum_deinit(x_candidate, 1);
x_candidate = crypto_bignum_init_set(x_bin, primebytelen);
- if (!x_candidate ||
- crypto_ec_point_solve_y_coord(grp->group, grp->pwe, x_candidate,
- is_odd) != 0) {
- wpa_printf(MSG_INFO, "EAP-pwd: Could not solve for y");
+ if (!x_candidate)
+ goto fail;
+
+ /* y = sqrt(x^3 + ax + b) mod p
+ * if LSB(y) == LSB(pwd-seed): PWE = (x, y)
+ * else: PWE = (x, p - y)
+ *
+ * Calculate y and the two possible values for PWE and after that,
+ * use constant time selection to copy the correct alternative.
+ */
+ y = crypto_ec_point_compute_y_sqr(grp->group, x_candidate);
+ if (!y ||
+ dragonfly_sqrt(grp->group, y, y) < 0 ||
+ crypto_bignum_to_bin(y, x_y, MAX_ECC_PRIME_LEN, primebytelen) < 0 ||
+ crypto_bignum_sub(prime, y, y) < 0 ||
+ crypto_bignum_to_bin(y, x_y + MAX_ECC_PRIME_LEN,
+ MAX_ECC_PRIME_LEN, primebytelen) < 0) {
+ wpa_printf(MSG_DEBUG, "SAE: Could not solve y");
+ goto fail;
+ }
+
+ /* Constant time selection of the y coordinate from the two
+ * options */
+ is_eq = const_time_eq(is_odd, x_y[primebytelen - 1] & 0x01);
+ const_time_select_bin(is_eq, x_y, x_y + MAX_ECC_PRIME_LEN,
+ primebytelen, x_y + primebytelen);
+ os_memcpy(x_y, x_bin, primebytelen);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: PWE", x_y, 2 * primebytelen);
+ grp->pwe = crypto_ec_point_from_bin(grp->group, x_y);
+ if (!grp->pwe) {
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Could not generate PWE");
goto fail;
}
@@ -289,6 +313,7 @@ int compute_password_element(EAP_PWD_gro
/* cleanliness and order.... */
crypto_bignum_deinit(x_candidate, 1);
crypto_bignum_deinit(tmp2, 1);
+ crypto_bignum_deinit(y, 1);
crypto_bignum_deinit(qr, 1);
crypto_bignum_deinit(qnr, 1);
bin_clear_free(prfbuf, primebytelen);
@@ -296,6 +321,7 @@ int compute_password_element(EAP_PWD_gro
os_memset(qnr_bin, 0, sizeof(qnr_bin));
os_memset(qr_or_qnr_bin, 0, sizeof(qr_or_qnr_bin));
os_memset(pwe_digest, 0, sizeof(pwe_digest));
+ forced_memzero(x_y, sizeof(x_y));
return ret;
}