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>
This commit is contained in:
Dan Harkins 2018-05-25 21:40:04 +03:00 committed by Jouni Malinen
parent b8acd50114
commit 22ac3dfebf

View file

@ -112,18 +112,25 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
const u8 *id_peer, size_t id_peer_len, const u8 *id_peer, size_t id_peer_len,
const u8 *token) 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; struct crypto_hash *hash;
unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr; 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; size_t primebytelen, primebitlen;
struct crypto_bignum *x_candidate = NULL, *rnd = NULL, *cofactor = NULL; struct crypto_bignum *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
const struct crypto_bignum *prime;
if (grp->pwe) if (grp->pwe)
return -1; return -1;
prime = crypto_ec_get_prime(grp->group);
cofactor = crypto_bignum_init(); cofactor = crypto_bignum_init();
grp->pwe = crypto_ec_point_init(grp->group); 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"); wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums");
goto fail; goto fail;
} }
@ -140,15 +147,36 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
"buffer"); "buffer");
goto fail; 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); os_memset(prfbuf, 0, primebytelen);
ctr = 0; ctr = 0;
while (1) {
if (ctr > 30) { /*
wpa_printf(MSG_INFO, "EAP-pwd: unable to find random " * Run through the hunting-and-pecking loop 40 times to mask the time
"point on curve for group %d, something's " * necessary to find PWE. The odds of PWE not being found in 40 loops is
"fishy", num); * roughly 1 in 1 trillion.
goto fail; */
} while (ctr < 40) {
ctr++; ctr++;
/* /*
@ -199,58 +227,113 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
x_candidate) < 0) x_candidate) < 0)
goto fail; goto fail;
if (crypto_bignum_cmp(x_candidate, if (crypto_bignum_cmp(x_candidate, prime) >= 0)
crypto_ec_get_prime(grp->group)) >= 0)
continue; continue;
wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate", wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate",
prfbuf, primebytelen); prfbuf, primebytelen);
/* /*
* need to unambiguously identify the solution, if there is * compute y^2 using the equation of the curve
* one... *
* 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 * mask tmp2 so doing legendre won't leak timing info
* don't have a point *
* tmp1 is a random number between 1 and p-1
*/ */
if (crypto_ec_point_solve_y_coord(grp->group, grp->pwe, if (crypto_bignum_rand(tmp1, pm1) < 0 ||
x_candidate, is_odd) != 0) { crypto_bignum_mulmod(tmp2, tmp1, prime, tmp2) < 0 ||
wpa_printf(MSG_INFO, "EAP-pwd: Could not solve for y"); crypto_bignum_mulmod(tmp2, tmp1, prime, tmp2) < 0)
continue; goto fail;
}
/* /*
* If there's a solution to the equation then the point must be * Now tmp2 (y^2) is masked, all values between 1 and p-1
* on the curve so why check again explicitly? OpenSSL code * are equally probable. Multiplying by r^2 does not change
* says this is required by X9.62. We're not X9.62 but it can't * whether or not tmp2 is a quadratic residue, just masks it.
* hurt just to be sure. *
* 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)) { if (crypto_bignum_is_odd(tmp1)) {
wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve"); crypto_bignum_mulmod(tmp2, qr, prime, tmp2);
continue; 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 */ * Now it's safe to do legendre, if check is 1 then it's
if (crypto_ec_point_mul(grp->group, grp->pwe, * a straightforward test (multiplying by qr does not
cofactor, grp->pwe) != 0) { * change result), if check is -1 then it's the opposite test
wpa_printf(MSG_INFO, "EAP-pwd: cannot " * (multiplying a qr by qnr would make a qnr).
"multiply generator by order"); */
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; continue;
} }
if (crypto_ec_point_is_at_infinity(grp->group,
grp->pwe)) { /*
wpa_printf(MSG_INFO, "EAP-pwd: point is at " * If there's a solution to the equation then the point
"infinity"); * 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; 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) { if (0) {
fail: fail:
crypto_ec_point_deinit(grp->pwe, 1); crypto_ec_point_deinit(grp->pwe, 1);
@ -261,6 +344,12 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
crypto_bignum_deinit(cofactor, 1); crypto_bignum_deinit(cofactor, 1);
crypto_bignum_deinit(x_candidate, 1); crypto_bignum_deinit(x_candidate, 1);
crypto_bignum_deinit(rnd, 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); os_free(prfbuf);
return ret; return ret;