|
@@ -1,6 +1,6 @@
|
|
|
/*
|
|
|
* Simultaneous authentication of equals
|
|
|
- * Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
|
|
|
+ * Copyright (c) 2012-2015, Jouni Malinen <j@w1.fi>
|
|
|
*
|
|
|
* This software may be distributed under the terms of the BSD license.
|
|
|
* See README for more details.
|
|
@@ -169,17 +169,107 @@ static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key)
|
|
|
}
|
|
|
|
|
|
|
|
|
+static struct crypto_bignum *
|
|
|
+get_rand_1_to_p_1(const u8 *prime, size_t prime_len, size_t prime_bits,
|
|
|
+ int *r_odd)
|
|
|
+{
|
|
|
+ for (;;) {
|
|
|
+ struct crypto_bignum *r;
|
|
|
+ u8 tmp[SAE_MAX_ECC_PRIME_LEN];
|
|
|
+
|
|
|
+ if (random_get_bytes(tmp, prime_len) < 0)
|
|
|
+ break;
|
|
|
+ if (prime_bits % 8)
|
|
|
+ buf_shift_right(tmp, prime_len, 8 - prime_bits % 8);
|
|
|
+ if (os_memcmp(tmp, prime, prime_len) >= 0)
|
|
|
+ continue;
|
|
|
+ r = crypto_bignum_init_set(tmp, prime_len);
|
|
|
+ if (!r)
|
|
|
+ break;
|
|
|
+ if (crypto_bignum_is_zero(r)) {
|
|
|
+ crypto_bignum_deinit(r, 0);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ *r_odd = tmp[prime_len - 1] & 0x01;
|
|
|
+ return r;
|
|
|
+ }
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static int is_quadratic_residue_blind(struct sae_data *sae,
|
|
|
+ const u8 *prime, size_t bits,
|
|
|
+ const struct crypto_bignum *qr,
|
|
|
+ const struct crypto_bignum *qnr,
|
|
|
+ const struct crypto_bignum *y_sqr)
|
|
|
+{
|
|
|
+ struct crypto_bignum *r, *num;
|
|
|
+ int r_odd, check, res = -1;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Use the blinding technique to mask y_sqr while determining
|
|
|
+ * whether it is a quadratic residue modulo p to avoid leaking
|
|
|
+ * timing information while determining the Legendre symbol.
|
|
|
+ *
|
|
|
+ * v = y_sqr
|
|
|
+ * r = a random number between 1 and p-1, inclusive
|
|
|
+ * num = (v * r * r) modulo p
|
|
|
+ */
|
|
|
+ r = get_rand_1_to_p_1(prime, sae->tmp->prime_len, bits, &r_odd);
|
|
|
+ if (!r)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ num = crypto_bignum_init();
|
|
|
+ if (!num ||
|
|
|
+ crypto_bignum_mulmod(y_sqr, r, sae->tmp->prime, num) < 0 ||
|
|
|
+ crypto_bignum_mulmod(num, r, sae->tmp->prime, num) < 0)
|
|
|
+ goto fail;
|
|
|
+
|
|
|
+ if (r_odd) {
|
|
|
+ /*
|
|
|
+ * num = (num * qr) module p
|
|
|
+ * LGR(num, p) = 1 ==> quadratic residue
|
|
|
+ */
|
|
|
+ if (crypto_bignum_mulmod(num, qr, sae->tmp->prime, num) < 0)
|
|
|
+ goto fail;
|
|
|
+ check = 1;
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * num = (num * qnr) module p
|
|
|
+ * LGR(num, p) = -1 ==> quadratic residue
|
|
|
+ */
|
|
|
+ if (crypto_bignum_mulmod(num, qnr, sae->tmp->prime, num) < 0)
|
|
|
+ goto fail;
|
|
|
+ check = -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ res = crypto_bignum_legendre(num, sae->tmp->prime);
|
|
|
+ if (res == -2) {
|
|
|
+ res = -1;
|
|
|
+ goto fail;
|
|
|
+ }
|
|
|
+ res = res == check;
|
|
|
+fail:
|
|
|
+ crypto_bignum_deinit(num, 1);
|
|
|
+ crypto_bignum_deinit(r, 1);
|
|
|
+ return res;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
|
|
|
- struct crypto_ec_point *pwe)
|
|
|
+ const u8 *prime,
|
|
|
+ const struct crypto_bignum *qr,
|
|
|
+ const struct crypto_bignum *qnr,
|
|
|
+ struct crypto_bignum **ret_x_cand)
|
|
|
{
|
|
|
- u8 pwd_value[SAE_MAX_ECC_PRIME_LEN], prime[SAE_MAX_ECC_PRIME_LEN];
|
|
|
- struct crypto_bignum *x;
|
|
|
- int y_bit;
|
|
|
+ u8 pwd_value[SAE_MAX_ECC_PRIME_LEN];
|
|
|
+ struct crypto_bignum *y_sqr, *x_cand;
|
|
|
+ int res;
|
|
|
size_t bits;
|
|
|
|
|
|
- if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
|
|
|
- sae->tmp->prime_len) < 0)
|
|
|
- return -1;
|
|
|
+ *ret_x_cand = NULL;
|
|
|
|
|
|
wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
|
|
|
|
|
@@ -195,20 +285,23 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
|
|
|
if (os_memcmp(pwd_value, prime, sae->tmp->prime_len) >= 0)
|
|
|
return 0;
|
|
|
|
|
|
- y_bit = pwd_seed[SHA256_MAC_LEN - 1] & 0x01;
|
|
|
-
|
|
|
- x = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
|
|
|
- if (x == NULL)
|
|
|
+ x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
|
|
|
+ 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);
|
|
|
return -1;
|
|
|
- if (crypto_ec_point_solve_y_coord(sae->tmp->ec, pwe, x, y_bit) < 0) {
|
|
|
- crypto_bignum_deinit(x, 0);
|
|
|
- wpa_printf(MSG_DEBUG, "SAE: No solution found");
|
|
|
- return 0;
|
|
|
}
|
|
|
- crypto_bignum_deinit(x, 0);
|
|
|
|
|
|
- wpa_printf(MSG_DEBUG, "SAE: PWE found");
|
|
|
+ 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;
|
|
|
}
|
|
|
|
|
@@ -286,6 +379,42 @@ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
|
|
|
}
|
|
|
|
|
|
|
|
|
+static int get_random_qr_qnr(const u8 *prime, size_t prime_len,
|
|
|
+ const struct crypto_bignum *prime_bn,
|
|
|
+ size_t prime_bits, struct crypto_bignum **qr,
|
|
|
+ struct crypto_bignum **qnr)
|
|
|
+{
|
|
|
+ *qr = NULL;
|
|
|
+ *qnr = NULL;
|
|
|
+
|
|
|
+ while (!(*qr) || !(*qnr)) {
|
|
|
+ u8 tmp[SAE_MAX_ECC_PRIME_LEN];
|
|
|
+ struct crypto_bignum *q;
|
|
|
+ int res;
|
|
|
+
|
|
|
+ if (random_get_bytes(tmp, prime_len) < 0)
|
|
|
+ break;
|
|
|
+ if (prime_bits % 8)
|
|
|
+ buf_shift_right(tmp, prime_len, 8 - prime_bits % 8);
|
|
|
+ if (os_memcmp(tmp, prime, prime_len) >= 0)
|
|
|
+ continue;
|
|
|
+ q = crypto_bignum_init_set(tmp, prime_len);
|
|
|
+ if (!q)
|
|
|
+ break;
|
|
|
+ res = crypto_bignum_legendre(q, prime_bn);
|
|
|
+
|
|
|
+ if (res == 1 && !(*qr))
|
|
|
+ *qr = q;
|
|
|
+ else if (res == -1 && !(*qnr))
|
|
|
+ *qnr = q;
|
|
|
+ else
|
|
|
+ crypto_bignum_deinit(q, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ return (*qr && *qnr) ? 0 : -1;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
|
|
|
const u8 *addr2, const u8 *password,
|
|
|
size_t password_len)
|
|
@@ -294,16 +423,25 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
|
|
|
u8 addrs[2 * ETH_ALEN];
|
|
|
const u8 *addr[2];
|
|
|
size_t len[2];
|
|
|
- int found = 0;
|
|
|
- struct crypto_ec_point *pwe_tmp;
|
|
|
+ int pwd_seed_odd = 0;
|
|
|
+ u8 prime[SAE_MAX_ECC_PRIME_LEN];
|
|
|
+ size_t prime_len;
|
|
|
+ struct crypto_bignum *x = NULL, *qr, *qnr;
|
|
|
+ size_t bits;
|
|
|
+ int res;
|
|
|
|
|
|
- if (sae->tmp->pwe_ecc == NULL) {
|
|
|
- sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec);
|
|
|
- if (sae->tmp->pwe_ecc == NULL)
|
|
|
- return -1;
|
|
|
- }
|
|
|
- pwe_tmp = crypto_ec_point_init(sae->tmp->ec);
|
|
|
- if (pwe_tmp == NULL)
|
|
|
+ prime_len = sae->tmp->prime_len;
|
|
|
+ if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
|
|
|
+ prime_len) < 0)
|
|
|
+ return -1;
|
|
|
+ bits = crypto_ec_prime_len_bits(sae->tmp->ec);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Create a random quadratic residue (qr) and quadratic non-residue
|
|
|
+ * (qnr) modulo p for blinding purposes during the loop.
|
|
|
+ */
|
|
|
+ if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits,
|
|
|
+ &qr, &qnr) < 0)
|
|
|
return -1;
|
|
|
|
|
|
wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
|
|
@@ -326,9 +464,9 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
|
|
|
* attacks that attempt to determine the number of iterations required
|
|
|
* in the loop.
|
|
|
*/
|
|
|
- for (counter = 1; counter <= k || !found; counter++) {
|
|
|
+ for (counter = 1; counter <= k || !x; counter++) {
|
|
|
u8 pwd_seed[SHA256_MAC_LEN];
|
|
|
- int res;
|
|
|
+ struct crypto_bignum *x_cand;
|
|
|
|
|
|
if (counter > 200) {
|
|
|
/* This should not happen in practice */
|
|
@@ -340,25 +478,51 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
|
|
|
if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len,
|
|
|
pwd_seed) < 0)
|
|
|
break;
|
|
|
+
|
|
|
res = sae_test_pwd_seed_ecc(sae, pwd_seed,
|
|
|
- found ? pwe_tmp :
|
|
|
- sae->tmp->pwe_ecc);
|
|
|
+ prime, qr, qnr, &x_cand);
|
|
|
if (res < 0)
|
|
|
- break;
|
|
|
- if (res == 0)
|
|
|
- continue;
|
|
|
- if (found) {
|
|
|
- wpa_printf(MSG_DEBUG, "SAE: Ignore this PWE (one was "
|
|
|
- "already selected)");
|
|
|
- } else {
|
|
|
- wpa_printf(MSG_DEBUG, "SAE: Use this PWE");
|
|
|
- found = 1;
|
|
|
+ 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));
|
|
|
+ } else if (res > 0) {
|
|
|
+ crypto_bignum_deinit(x_cand, 1);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- crypto_ec_point_deinit(pwe_tmp, 1);
|
|
|
+ if (!x) {
|
|
|
+ wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
|
|
|
+ res = -1;
|
|
|
+ goto fail;
|
|
|
+ }
|
|
|
|
|
|
- return found ? 0 : -1;
|
|
|
+ if (!sae->tmp->pwe_ecc)
|
|
|
+ sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec);
|
|
|
+ if (!sae->tmp->pwe_ecc)
|
|
|
+ res = -1;
|
|
|
+ else
|
|
|
+ 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
|
|
|
+ * is a result.
|
|
|
+ */
|
|
|
+ wpa_printf(MSG_DEBUG, "SAE: Could not solve y");
|
|
|
+ }
|
|
|
+
|
|
|
+fail:
|
|
|
+ crypto_bignum_deinit(qr, 0);
|
|
|
+ crypto_bignum_deinit(qnr, 0);
|
|
|
+
|
|
|
+ return res;
|
|
|
}
|
|
|
|
|
|
|