Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/external/bsd/wpa/dist/src/common The QR test result can prov...



details:   https://anonhg.NetBSD.org/src/rev/3433a6e15456
branches:  trunk
changeset: 455708:3433a6e15456
user:      christos <christos%NetBSD.org@localhost>
date:      Wed Apr 10 17:59:07 2019 +0000

description:
The QR test result can provide information about the password to an
attacker, so try to minimize differences in how the
sae_test_pwd_seed_ecc() result is used. (CVE-2019-9494)

Use heap memory for the dummy password to allow the same password length
to be used even with long passwords.

Use constant time selection functions to track the real vs. dummy
variables so that the exact same operations can be performed for both QR
test results.

diffstat:

 external/bsd/wpa/dist/src/common/sae.c |  106 +++++++++++++++++---------------
 1 files changed, 57 insertions(+), 49 deletions(-)

diffs (216 lines):

diff -r 3b6210cd31f2 -r 3433a6e15456 external/bsd/wpa/dist/src/common/sae.c
--- a/external/bsd/wpa/dist/src/common/sae.c    Wed Apr 10 17:57:15 2019 +0000
+++ b/external/bsd/wpa/dist/src/common/sae.c    Wed Apr 10 17:59:07 2019 +0000
@@ -9,6 +9,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "utils/const_time.h"
 #include "crypto/crypto.h"
 #include "crypto/sha256.h"
 #include "crypto/random.h"
@@ -269,15 +270,12 @@
                                 const u8 *prime,
                                 const struct crypto_bignum *qr,
                                 const struct crypto_bignum *qnr,
-                                struct crypto_bignum **ret_x_cand)
+                                u8 *pwd_value)
 {
-       u8 pwd_value[SAE_MAX_ECC_PRIME_LEN];
        struct crypto_bignum *y_sqr, *x_cand;
        int res;
        size_t bits;
 
-       *ret_x_cand = NULL;
-
        wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
 
        /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
@@ -286,7 +284,7 @@
                            prime, sae->tmp->prime_len, pwd_value, bits) < 0)
                return -1;
        if (bits % 8)
-               buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8);
+               buf_shift_right(pwd_value, sae->tmp->prime_len, 8 - bits % 8);
        wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
                        pwd_value, sae->tmp->prime_len);
 
@@ -297,20 +295,13 @@
        if (!x_cand)
                return -1;
        y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand);
-       if (!y_sqr) {
-               crypto_bignum_deinit(x_cand, 1);
+       crypto_bignum_deinit(x_cand, 1);
+       if (!y_sqr)
                return -1;
-       }
 
        res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr);
        crypto_bignum_deinit(y_sqr, 1);
-       if (res <= 0) {
-               crypto_bignum_deinit(x_cand, 1);
-               return res;
-       }
-
-       *ret_x_cand = x_cand;
-       return 1;
+       return res;
 }
 
 
@@ -431,25 +422,30 @@
        const u8 *addr[3];
        size_t len[3];
        size_t num_elem;
-       u8 dummy_password[32];
-       size_t dummy_password_len;
+       u8 *dummy_password, *tmp_password;
        int pwd_seed_odd = 0;
        u8 prime[SAE_MAX_ECC_PRIME_LEN];
        size_t prime_len;
-       struct crypto_bignum *x = NULL, *qr, *qnr;
+       struct crypto_bignum *x = NULL, *qr = NULL, *qnr = NULL;
+       u8 x_bin[SAE_MAX_ECC_PRIME_LEN];
+       u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN];
        size_t bits;
-       int res;
+       int res = -1;
+       u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
+                      * mask */
 
-       dummy_password_len = password_len;
-       if (dummy_password_len > sizeof(dummy_password))
-               dummy_password_len = sizeof(dummy_password);
-       if (random_get_bytes(dummy_password, dummy_password_len) < 0)
-               return -1;
+       os_memset(x_bin, 0, sizeof(x_bin));
+
+       dummy_password = os_malloc(password_len);
+       tmp_password = os_malloc(password_len);
+       if (!dummy_password || !tmp_password ||
+           random_get_bytes(dummy_password, password_len) < 0)
+               goto fail;
 
        prime_len = sae->tmp->prime_len;
        if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
                                 prime_len) < 0)
-               return -1;
+               goto fail;
        bits = crypto_ec_prime_len_bits(sae->tmp->ec);
 
        /*
@@ -458,7 +454,7 @@
         */
        if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits,
                              &qr, &qnr) < 0)
-               return -1;
+               goto fail;
 
        wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
                              password, password_len);
@@ -474,7 +470,7 @@
         */
        sae_pwd_seed_key(addr1, addr2, addrs);
 
-       addr[0] = password;
+       addr[0] = tmp_password;
        len[0] = password_len;
        num_elem = 1;
        if (identifier) {
@@ -491,9 +487,8 @@
         * attacks that attempt to determine the number of iterations required
         * in the loop.
         */
-       for (counter = 1; counter <= k || !x; counter++) {
+       for (counter = 1; counter <= k || !found; counter++) {
                u8 pwd_seed[SHA256_MAC_LEN];
-               struct crypto_bignum *x_cand;
 
                if (counter > 200) {
                        /* This should not happen in practice */
@@ -501,36 +496,45 @@
                        break;
                }
 
-               wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
+               wpa_printf(MSG_DEBUG, "SAE: counter = %03u", counter);
+               const_time_select_bin(found, dummy_password, password,
+                                     password_len, tmp_password);
                if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
                                       addr, len, pwd_seed) < 0)
                        break;
 
                res = sae_test_pwd_seed_ecc(sae, pwd_seed,
-                                           prime, qr, qnr, &x_cand);
+                                           prime, qr, qnr, x_cand_bin);
+               const_time_select_bin(found, x_bin, x_cand_bin, prime_len,
+                                     x_bin);
+               pwd_seed_odd = const_time_select_u8(
+                       found, pwd_seed_odd,
+                       pwd_seed[SHA256_MAC_LEN - 1] & 0x01);
+               os_memset(pwd_seed, 0, sizeof(pwd_seed));
                if (res < 0)
                        goto fail;
-               if (res > 0 && !x) {
-                       wpa_printf(MSG_DEBUG,
-                                  "SAE: Selected pwd-seed with counter %u",
-                                  counter);
-                       x = x_cand;
-                       pwd_seed_odd = pwd_seed[SHA256_MAC_LEN - 1] & 0x01;
-                       os_memset(pwd_seed, 0, sizeof(pwd_seed));
+               /* Need to minimize differences in handling res == 0 and 1 here
+                * to avoid differences in timing and instruction cache access,
+                * so use const_time_select_*() to make local copies of the
+                * values based on whether this loop iteration was the one that
+                * found the pwd-seed/x. */
 
-                       /*
-                        * Use a dummy password for the following rounds, if
-                        * any.
-                        */
-                       addr[0] = dummy_password;
-                       len[0] = dummy_password_len;
-               } else if (res > 0) {
-                       crypto_bignum_deinit(x_cand, 1);
-               }
+               /* found is 0 or 0xff here and res is 0 or 1. Bitwise OR of them
+                * (with res converted to 0/0xff) handles this in constant time.
+                */
+               found |= res * 0xff;
+               wpa_printf(MSG_DEBUG, "SAE: pwd-seed result %d found=0x%02x",
+                          res, found);
        }
 
+       if (!found) {
+               wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
+               res = -1;
+               goto fail;
+       }
+
+       x = crypto_bignum_init_set(x_bin, prime_len);
        if (!x) {
-               wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
                res = -1;
                goto fail;
        }
@@ -543,7 +547,6 @@
                res = crypto_ec_point_solve_y_coord(sae->tmp->ec,
                                                    sae->tmp->pwe_ecc, x,
                                                    pwd_seed_odd);
-       crypto_bignum_deinit(x, 1);
        if (res < 0) {
                /*
                 * This should not happen since we already checked that there
@@ -555,6 +558,11 @@
 fail:
        crypto_bignum_deinit(qr, 0);
        crypto_bignum_deinit(qnr, 0);
+       os_free(dummy_password);
+       bin_clear_free(tmp_password, password_len);
+       crypto_bignum_deinit(x, 1);
+       os_memset(x_bin, 0, sizeof(x_bin));
+       os_memset(x_cand_bin, 0, sizeof(x_cand_bin));
 
        return res;
 }



Home | Main Index | Thread Index | Old Index