diff --git a/components/wpa_supplicant/CMakeLists.txt b/components/wpa_supplicant/CMakeLists.txt index c4e3bccddd..505b89ba42 100644 --- a/components/wpa_supplicant/CMakeLists.txt +++ b/components/wpa_supplicant/CMakeLists.txt @@ -4,6 +4,7 @@ set(srcs "port/os_xtensa.c" "src/ap/wpa_auth.c" "src/ap/wpa_auth_ie.c" "src/common/sae.c" + "src/common/dragonfly.c" "src/common/wpa_common.c" "src/utils/bitfield.c" "src/crypto/aes-siv.c" diff --git a/components/wpa_supplicant/src/common/dragonfly.c b/components/wpa_supplicant/src/common/dragonfly.c new file mode 100644 index 0000000000..1e84271666 --- /dev/null +++ b/components/wpa_supplicant/src/common/dragonfly.c @@ -0,0 +1,249 @@ +/* + * Shared Dragonfly functionality + * Copyright (c) 2012-2016, Jouni Malinen + * Copyright (c) 2019, The Linux Foundation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/const_time.h" +#include "crypto/crypto.h" +#include "dragonfly.h" + + +int dragonfly_suitable_group(int group, int ecc_only) +{ + /* Enforce REVmd rules on which SAE groups are suitable for production + * purposes: FFC groups whose prime is >= 3072 bits and ECC groups + * defined over a prime field whose prime is >= 256 bits. Furthermore, + * ECC groups defined over a characteristic 2 finite field and ECC + * groups with a co-factor greater than 1 are not suitable. Disable + * groups that use Brainpool curves as well for now since they leak more + * timing information due to the prime not being close to a power of + * two. */ + return group == 19 || group == 20 || group == 21 || + (!ecc_only && + (group == 15 || group == 16 || group == 17 || group == 18)); +} + + +unsigned int dragonfly_min_pwe_loop_iter(int group) +{ + if (group == 22 || group == 23 || group == 24) { + /* FFC groups for which pwd-value is likely to be >= p + * frequently */ + return 40; + } + + if (group == 1 || group == 2 || group == 5 || group == 14 || + group == 15 || group == 16 || group == 17 || group == 18) { + /* FFC groups that have prime that is close to a power of two */ + return 1; + } + + /* Default to 40 (this covers most ECC groups) */ + return 40; +} + + +int dragonfly_get_random_qr_qnr(const struct crypto_bignum *prime, + struct crypto_bignum **qr, + struct crypto_bignum **qnr) +{ + *qr = *qnr = NULL; + + while (!(*qr) || !(*qnr)) { + struct crypto_bignum *tmp; + int res; + + tmp = crypto_bignum_init(); + if (!tmp || crypto_bignum_rand(tmp, prime) < 0) { + crypto_bignum_deinit(tmp, 0); + break; + } + + res = crypto_bignum_legendre(tmp, prime); + if (res == 1 && !(*qr)) + *qr = tmp; + else if (res == -1 && !(*qnr)) + *qnr = tmp; + else + crypto_bignum_deinit(tmp, 0); + } + + if (*qr && *qnr) + return 0; + crypto_bignum_deinit(*qr, 0); + crypto_bignum_deinit(*qnr, 0); + *qr = *qnr = NULL; + return -1; +} + + +static struct crypto_bignum * +dragonfly_get_rand_1_to_p_1(const struct crypto_bignum *prime) +{ + struct crypto_bignum *tmp, *pm1, *one; + + tmp = crypto_bignum_init(); + pm1 = crypto_bignum_init(); + one = crypto_bignum_init_set((const u8 *) "\x01", 1); + if (!tmp || !pm1 || !one || + crypto_bignum_sub(prime, one, pm1) < 0 || + crypto_bignum_rand(tmp, pm1) < 0 || + crypto_bignum_add(tmp, one, tmp) < 0) { + crypto_bignum_deinit(tmp, 0); + tmp = NULL; + } + + crypto_bignum_deinit(pm1, 0); + crypto_bignum_deinit(one, 0); + return tmp; +} + + +int dragonfly_is_quadratic_residue_blind(struct crypto_ec *ec, + const u8 *qr, const u8 *qnr, + const struct crypto_bignum *val) +{ + struct crypto_bignum *r, *num, *qr_or_qnr = NULL; + int check, res = -1; + u8 qr_or_qnr_bin[DRAGONFLY_MAX_ECC_PRIME_LEN]; + const struct crypto_bignum *prime; + size_t prime_len; + unsigned int mask; + + prime = crypto_ec_get_prime(ec); + prime_len = crypto_ec_prime_len(ec); + + /* + * Use a blinding technique to mask val while determining whether it is + * a quadratic residue modulo p to avoid leaking timing information + * while determining the Legendre symbol. + * + * v = val + * r = a random number between 1 and p-1, inclusive + * num = (v * r * r) modulo p + */ + r = dragonfly_get_rand_1_to_p_1(prime); + if (!r) + return -1; + + num = crypto_bignum_init(); + if (!num || + crypto_bignum_mulmod(val, r, prime, num) < 0 || + crypto_bignum_mulmod(num, r, prime, num) < 0) + goto fail; + + /* + * Need to minimize differences in handling different cases, so try to + * avoid branches and timing differences. + * + * If r is odd: + * num = (num * qr) module p + * LGR(num, p) = 1 ==> quadratic residue + * else: + * num = (num * qnr) module p + * LGR(num, p) = -1 ==> quadratic residue + * + * mask is set to !odd(r) + */ + mask = const_time_is_zero(crypto_bignum_is_odd(r)); + const_time_select_bin(mask, qnr, qr, prime_len, qr_or_qnr_bin); + qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, prime_len); + if (!qr_or_qnr || + crypto_bignum_mulmod(num, qr_or_qnr, prime, num) < 0) + goto fail; + /* branchless version of check = odd(r) ? 1 : -1, */ + check = const_time_select_int(mask, -1, 1); + + /* Determine the Legendre symbol on the masked value */ + res = crypto_bignum_legendre(num, prime); + if (res == -2) { + res = -1; + goto fail; + } + /* branchless version of res = res == check + * (res is -1, 0, or 1; check is -1 or 1) */ + mask = const_time_eq(res, check); + res = const_time_select_int(mask, 1, 0); +fail: + crypto_bignum_deinit(num, 1); + crypto_bignum_deinit(r, 1); + crypto_bignum_deinit(qr_or_qnr, 1); + return res; +} + + +static int dragonfly_get_rand_2_to_r_1(struct crypto_bignum *val, + const struct crypto_bignum *order) +{ + return crypto_bignum_rand(val, order) == 0 && + !crypto_bignum_is_zero(val) && + !crypto_bignum_is_one(val); +} + + +int dragonfly_generate_scalar(const struct crypto_bignum *order, + struct crypto_bignum *_rand, + struct crypto_bignum *_mask, + struct crypto_bignum *scalar) +{ + int count; + + /* Select two random values rand,mask such that 1 < rand,mask < r and + * rand + mask mod r > 1. */ + for (count = 0; count < 100; count++) { + if (dragonfly_get_rand_2_to_r_1(_rand, order) && + dragonfly_get_rand_2_to_r_1(_mask, order) && + crypto_bignum_add(_rand, _mask, scalar) == 0 && + crypto_bignum_mod(scalar, order, scalar) == 0 && + !crypto_bignum_is_zero(scalar) && + !crypto_bignum_is_one(scalar)) + return 0; + } + + /* This should not be reachable in practice if the random number + * generation is working. */ + wpa_printf(MSG_INFO, + "dragonfly: Unable to get randomness for own scalar"); + return -1; +} + + +/* res = sqrt(val) */ +int dragonfly_sqrt(struct crypto_ec *ec, const struct crypto_bignum *val, + struct crypto_bignum *res) +{ + const struct crypto_bignum *prime; + struct crypto_bignum *tmp, *one; + int ret = 0; + u8 prime_bin[DRAGONFLY_MAX_ECC_PRIME_LEN]; + size_t prime_len; + + /* For prime p such that p = 3 mod 4, sqrt(w) = w^((p+1)/4) mod p */ + + prime = crypto_ec_get_prime(ec); + prime_len = crypto_ec_prime_len(ec); + tmp = crypto_bignum_init(); + one = crypto_bignum_init_uint(1); + + if (crypto_bignum_to_bin(prime, prime_bin, sizeof(prime_bin), + prime_len) < 0 || + (prime_bin[prime_len - 1] & 0x03) != 3 || + !tmp || !one || + /* tmp = (p+1)/4 */ + crypto_bignum_add(prime, one, tmp) < 0 || + crypto_bignum_rshift(tmp, 2, tmp) < 0 || + /* res = sqrt(val) */ + crypto_bignum_exptmod(val, tmp, prime, res) < 0) + ret = -1; + + crypto_bignum_deinit(tmp, 0); + crypto_bignum_deinit(one, 0); + return ret; +} diff --git a/components/wpa_supplicant/src/common/dragonfly.h b/components/wpa_supplicant/src/common/dragonfly.h new file mode 100644 index 0000000000..84d67f575c --- /dev/null +++ b/components/wpa_supplicant/src/common/dragonfly.h @@ -0,0 +1,33 @@ +/* + * Shared Dragonfly functionality + * Copyright (c) 2012-2016, Jouni Malinen + * Copyright (c) 2019, The Linux Foundation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef DRAGONFLY_H +#define DRAGONFLY_H + +#define DRAGONFLY_MAX_ECC_PRIME_LEN 66 + +struct crypto_bignum; +struct crypto_ec; + +int dragonfly_suitable_group(int group, int ecc_only); +unsigned int dragonfly_min_pwe_loop_iter(int group); +int dragonfly_get_random_qr_qnr(const struct crypto_bignum *prime, + struct crypto_bignum **qr, + struct crypto_bignum **qnr); +int dragonfly_is_quadratic_residue_blind(struct crypto_ec *ec, + const u8 *qr, const u8 *qnr, + const struct crypto_bignum *val); +int dragonfly_generate_scalar(const struct crypto_bignum *order, + struct crypto_bignum *_rand, + struct crypto_bignum *_mask, + struct crypto_bignum *scalar); +int dragonfly_sqrt(struct crypto_ec *ec, const struct crypto_bignum *val, + struct crypto_bignum *res); + +#endif /* DRAGONFLY_H */