/*
 * Shared Dragonfly functionality
 * Copyright (c) 2012-2016, Jouni Malinen <j@w1.fi>
 * 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 (res == -2)
				break;
		}
	}

	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;
}
