]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
EAP-pwd: Derive the y coordinate for PWE with own implementation
authorJouni Malinen <j@w1.fi>
Fri, 7 Jan 2022 16:52:27 +0000 (18:52 +0200)
committerJouni Malinen <j@w1.fi>
Tue, 11 Jan 2022 18:15:36 +0000 (20:15 +0200)
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

index 2b2b8efdbd016ba64b40bd5b429f5a79712b31f2..ff22b29b087a9d1f31093baf053072e5f7d679d0 100644 (file)
@@ -127,7 +127,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 *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_group *grp, u16 num,
        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_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);
-       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_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;
        }
 
@@ -289,6 +313,7 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
        /* 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_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;
 }