mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-23 07:22:33 +00:00
b463a13881
This fixes the following security problems: * CVE-2019-9494: cache attack against SAE * CVE-2019-9495: cache attack against EAP-pwd * CVE-2019-9496: SAE confirm missing state validation in hostapd/AP * CVE-2019-9497: EAP-pwd server not checking for reflection attack) * CVE-2019-9498: EAP-pwd server missing commit validation for scalar/element * CVE-2019-9499: EAP-pwd peer missing commit validation for scalar/element * CVE-2019-11555: EAP-pwd message reassembly issue with unexpected fragment Most of these problems are not relevant for normal users, SAE is only used in ieee80211s mesh mode and EAP-pwd is normally not activated. Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
248 lines
7.5 KiB
Diff
248 lines
7.5 KiB
Diff
From 22ac3dfebf7b25a3aae02f9b4f69025bb4173137 Mon Sep 17 00:00:00 2001
|
|
From: Dan Harkins <dharkins@lounge.org>
|
|
Date: Fri, 25 May 2018 21:40:04 +0300
|
|
Subject: [PATCH] EAP-pwd: Mask timing of PWE derivation
|
|
|
|
Run through the hunting-and-pecking loop 40 times to mask the time
|
|
necessary to find PWE. The odds of PWE not being found in 40 loops is
|
|
roughly 1 in 1 trillion.
|
|
|
|
Signed-off-by: Dan Harkins <dharkins@lounge.org>
|
|
---
|
|
src/eap_common/eap_pwd_common.c | 171 ++++++++++++++++++++++++--------
|
|
1 file changed, 130 insertions(+), 41 deletions(-)
|
|
|
|
--- a/src/eap_common/eap_pwd_common.c
|
|
+++ b/src/eap_common/eap_pwd_common.c
|
|
@@ -112,18 +112,25 @@ int compute_password_element(EAP_PWD_gro
|
|
const u8 *id_peer, size_t id_peer_len,
|
|
const u8 *token)
|
|
{
|
|
+ struct crypto_bignum *qr = NULL, *qnr = NULL, *one = NULL;
|
|
+ struct crypto_bignum *tmp1 = NULL, *tmp2 = NULL, *pm1 = NULL;
|
|
struct crypto_hash *hash;
|
|
unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr;
|
|
- int is_odd, ret = 0;
|
|
+ int is_odd, ret = 0, check, found = 0;
|
|
size_t primebytelen, primebitlen;
|
|
struct crypto_bignum *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
|
|
+ const struct crypto_bignum *prime;
|
|
|
|
if (grp->pwe)
|
|
return -1;
|
|
|
|
+ prime = crypto_ec_get_prime(grp->group);
|
|
cofactor = crypto_bignum_init();
|
|
grp->pwe = crypto_ec_point_init(grp->group);
|
|
- if (!cofactor || !grp->pwe) {
|
|
+ tmp1 = crypto_bignum_init();
|
|
+ pm1 = crypto_bignum_init();
|
|
+ one = crypto_bignum_init_set((const u8 *) "\x01", 1);
|
|
+ if (!cofactor || !grp->pwe || !tmp1 || !pm1 || !one) {
|
|
wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums");
|
|
goto fail;
|
|
}
|
|
@@ -140,15 +147,36 @@ int compute_password_element(EAP_PWD_gro
|
|
"buffer");
|
|
goto fail;
|
|
}
|
|
+ if (crypto_bignum_sub(prime, one, pm1) < 0)
|
|
+ goto fail;
|
|
+
|
|
+ /* get a random quadratic residue and nonresidue */
|
|
+ while (!qr || !qnr) {
|
|
+ int res;
|
|
+
|
|
+ if (crypto_bignum_rand(tmp1, prime) < 0)
|
|
+ goto fail;
|
|
+ res = crypto_bignum_legendre(tmp1, prime);
|
|
+ if (!qr && res == 1) {
|
|
+ qr = tmp1;
|
|
+ tmp1 = crypto_bignum_init();
|
|
+ } else if (!qnr && res == -1) {
|
|
+ qnr = tmp1;
|
|
+ tmp1 = crypto_bignum_init();
|
|
+ }
|
|
+ if (!tmp1)
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
os_memset(prfbuf, 0, primebytelen);
|
|
ctr = 0;
|
|
- while (1) {
|
|
- if (ctr > 30) {
|
|
- wpa_printf(MSG_INFO, "EAP-pwd: unable to find random "
|
|
- "point on curve for group %d, something's "
|
|
- "fishy", num);
|
|
- goto fail;
|
|
- }
|
|
+
|
|
+ /*
|
|
+ * Run through the hunting-and-pecking loop 40 times to mask the time
|
|
+ * necessary to find PWE. The odds of PWE not being found in 40 loops is
|
|
+ * roughly 1 in 1 trillion.
|
|
+ */
|
|
+ while (ctr < 40) {
|
|
ctr++;
|
|
|
|
/*
|
|
@@ -199,58 +227,113 @@ int compute_password_element(EAP_PWD_gro
|
|
x_candidate) < 0)
|
|
goto fail;
|
|
|
|
- if (crypto_bignum_cmp(x_candidate,
|
|
- crypto_ec_get_prime(grp->group)) >= 0)
|
|
+ if (crypto_bignum_cmp(x_candidate, prime) >= 0)
|
|
continue;
|
|
|
|
wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate",
|
|
prfbuf, primebytelen);
|
|
|
|
/*
|
|
- * need to unambiguously identify the solution, if there is
|
|
- * one...
|
|
+ * compute y^2 using the equation of the curve
|
|
+ *
|
|
+ * y^2 = x^3 + ax + b
|
|
*/
|
|
- is_odd = crypto_bignum_is_odd(rnd);
|
|
+ tmp2 = crypto_ec_point_compute_y_sqr(grp->group, x_candidate);
|
|
+ if (!tmp2)
|
|
+ goto fail;
|
|
|
|
/*
|
|
- * solve the quadratic equation, if it's not solvable then we
|
|
- * don't have a point
|
|
+ * mask tmp2 so doing legendre won't leak timing info
|
|
+ *
|
|
+ * tmp1 is a random number between 1 and p-1
|
|
*/
|
|
- if (crypto_ec_point_solve_y_coord(grp->group, grp->pwe,
|
|
- x_candidate, is_odd) != 0) {
|
|
- wpa_printf(MSG_INFO, "EAP-pwd: Could not solve for y");
|
|
- continue;
|
|
- }
|
|
+ if (crypto_bignum_rand(tmp1, pm1) < 0 ||
|
|
+ crypto_bignum_mulmod(tmp2, tmp1, prime, tmp2) < 0 ||
|
|
+ crypto_bignum_mulmod(tmp2, tmp1, prime, tmp2) < 0)
|
|
+ goto fail;
|
|
+
|
|
/*
|
|
- * If there's a solution to the equation then the point must be
|
|
- * on the curve so why check again explicitly? OpenSSL code
|
|
- * says this is required by X9.62. We're not X9.62 but it can't
|
|
- * hurt just to be sure.
|
|
+ * Now tmp2 (y^2) is masked, all values between 1 and p-1
|
|
+ * are equally probable. Multiplying by r^2 does not change
|
|
+ * whether or not tmp2 is a quadratic residue, just masks it.
|
|
+ *
|
|
+ * Flip a coin, multiply by the random quadratic residue or the
|
|
+ * random quadratic nonresidue and record heads or tails.
|
|
*/
|
|
- if (!crypto_ec_point_is_on_curve(grp->group, grp->pwe)) {
|
|
- wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve");
|
|
- continue;
|
|
+ if (crypto_bignum_is_odd(tmp1)) {
|
|
+ crypto_bignum_mulmod(tmp2, qr, prime, tmp2);
|
|
+ check = 1;
|
|
+ } else {
|
|
+ crypto_bignum_mulmod(tmp2, qnr, prime, tmp2);
|
|
+ check = -1;
|
|
}
|
|
|
|
- if (!crypto_bignum_is_one(cofactor)) {
|
|
- /* make sure the point is not in a small sub-group */
|
|
- if (crypto_ec_point_mul(grp->group, grp->pwe,
|
|
- cofactor, grp->pwe) != 0) {
|
|
- wpa_printf(MSG_INFO, "EAP-pwd: cannot "
|
|
- "multiply generator by order");
|
|
+ /*
|
|
+ * Now it's safe to do legendre, if check is 1 then it's
|
|
+ * a straightforward test (multiplying by qr does not
|
|
+ * change result), if check is -1 then it's the opposite test
|
|
+ * (multiplying a qr by qnr would make a qnr).
|
|
+ */
|
|
+ if (crypto_bignum_legendre(tmp2, prime) == check) {
|
|
+ if (found == 1)
|
|
+ continue;
|
|
+
|
|
+ /* need to unambiguously identify the solution */
|
|
+ is_odd = crypto_bignum_is_odd(rnd);
|
|
+
|
|
+ /*
|
|
+ * We know x_candidate is a quadratic residue so set
|
|
+ * it here.
|
|
+ */
|
|
+ if (crypto_ec_point_solve_y_coord(grp->group, grp->pwe,
|
|
+ x_candidate,
|
|
+ is_odd) != 0) {
|
|
+ wpa_printf(MSG_INFO,
|
|
+ "EAP-pwd: Could not solve for y");
|
|
continue;
|
|
}
|
|
- if (crypto_ec_point_is_at_infinity(grp->group,
|
|
- grp->pwe)) {
|
|
- wpa_printf(MSG_INFO, "EAP-pwd: point is at "
|
|
- "infinity");
|
|
+
|
|
+ /*
|
|
+ * If there's a solution to the equation then the point
|
|
+ * must be on the curve so why check again explicitly?
|
|
+ * OpenSSL code says this is required by X9.62. We're
|
|
+ * not X9.62 but it can't hurt just to be sure.
|
|
+ */
|
|
+ if (!crypto_ec_point_is_on_curve(grp->group,
|
|
+ grp->pwe)) {
|
|
+ wpa_printf(MSG_INFO,
|
|
+ "EAP-pwd: point is not on curve");
|
|
continue;
|
|
}
|
|
+
|
|
+ if (!crypto_bignum_is_one(cofactor)) {
|
|
+ /* make sure the point is not in a small
|
|
+ * sub-group */
|
|
+ if (crypto_ec_point_mul(grp->group, grp->pwe,
|
|
+ cofactor,
|
|
+ grp->pwe) != 0) {
|
|
+ wpa_printf(MSG_INFO,
|
|
+ "EAP-pwd: cannot multiply generator by order");
|
|
+ continue;
|
|
+ }
|
|
+ if (crypto_ec_point_is_at_infinity(grp->group,
|
|
+ grp->pwe)) {
|
|
+ wpa_printf(MSG_INFO,
|
|
+ "EAP-pwd: point is at infinity");
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+ wpa_printf(MSG_DEBUG,
|
|
+ "EAP-pwd: found a PWE in %d tries", ctr);
|
|
+ found = 1;
|
|
}
|
|
- /* if we got here then we have a new generator. */
|
|
- break;
|
|
}
|
|
- wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %d tries", ctr);
|
|
+ if (found == 0) {
|
|
+ wpa_printf(MSG_INFO,
|
|
+ "EAP-pwd: unable to find random point on curve for group %d, something's fishy",
|
|
+ num);
|
|
+ goto fail;
|
|
+ }
|
|
if (0) {
|
|
fail:
|
|
crypto_ec_point_deinit(grp->pwe, 1);
|
|
@@ -261,6 +344,12 @@ int compute_password_element(EAP_PWD_gro
|
|
crypto_bignum_deinit(cofactor, 1);
|
|
crypto_bignum_deinit(x_candidate, 1);
|
|
crypto_bignum_deinit(rnd, 1);
|
|
+ crypto_bignum_deinit(pm1, 0);
|
|
+ crypto_bignum_deinit(tmp1, 1);
|
|
+ crypto_bignum_deinit(tmp2, 1);
|
|
+ crypto_bignum_deinit(qr, 1);
|
|
+ crypto_bignum_deinit(qnr, 1);
|
|
+ crypto_bignum_deinit(one, 0);
|
|
os_free(prfbuf);
|
|
|
|
return ret;
|