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 | 42 +++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c index 755aafb..ab8eb92 100644 --- a/src/eap_common/eap_pwd_common.c +++ b/src/eap_common/eap_pwd_common.c @@ -137,7 +137,8 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, 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 *tmp1 = NULL, *tmp2 = NULL, *pm1 = NULL; + u8 x_y[2 * MAX_ECC_PRIME_LEN]; + struct crypto_bignum *tmp1 = NULL, *tmp2 = NULL, *pm1 = NULL, *y = NULL; struct crypto_hash *hash; unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr; int ret = 0, check, res; @@ -149,6 +150,7 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, u8 mask, found_ctr = 0, is_odd = 0; int cmp_prime; unsigned int in_range; + unsigned int is_eq; if (grp->pwe) return -1; @@ -161,7 +163,6 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, if (crypto_bignum_to_bin(prime, prime_bin, sizeof(prime_bin), primebytelen) < 0) return -1; - grp->pwe = crypto_ec_point_init(grp->group); tmp1 = crypto_bignum_init(); pm1 = crypto_bignum_init(); one = crypto_bignum_init_set((const u8 *) "\x01", 1); @@ -323,10 +324,37 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, */ 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; } @@ -353,6 +381,7 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, crypto_bignum_deinit(pm1, 0); crypto_bignum_deinit(tmp1, 1); crypto_bignum_deinit(tmp2, 1); + crypto_bignum_deinit(y, 1); crypto_bignum_deinit(qr, 1); crypto_bignum_deinit(qnr, 1); crypto_bignum_deinit(qr_or_qnr, 1); @@ -362,6 +391,7 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, 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; } -- 1.8.3.1