18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2013, 2014 Kenneth MacKay. All rights reserved.
38c2ecf20Sopenharmony_ci * Copyright (c) 2019 Vitaly Chikunov <vt@altlinux.org>
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
68c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions are
78c2ecf20Sopenharmony_ci * met:
88c2ecf20Sopenharmony_ci *  * Redistributions of source code must retain the above copyright
98c2ecf20Sopenharmony_ci *   notice, this list of conditions and the following disclaimer.
108c2ecf20Sopenharmony_ci *  * Redistributions in binary form must reproduce the above copyright
118c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in the
128c2ecf20Sopenharmony_ci *    documentation and/or other materials provided with the distribution.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
158c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
168c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
178c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
188c2ecf20Sopenharmony_ci * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
198c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
208c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
218c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
228c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
238c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
248c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
258c2ecf20Sopenharmony_ci */
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <linux/module.h>
288c2ecf20Sopenharmony_ci#include <linux/random.h>
298c2ecf20Sopenharmony_ci#include <linux/slab.h>
308c2ecf20Sopenharmony_ci#include <linux/swab.h>
318c2ecf20Sopenharmony_ci#include <linux/fips.h>
328c2ecf20Sopenharmony_ci#include <crypto/ecdh.h>
338c2ecf20Sopenharmony_ci#include <crypto/rng.h>
348c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
358c2ecf20Sopenharmony_ci#include <linux/ratelimit.h>
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#include "ecc.h"
388c2ecf20Sopenharmony_ci#include "ecc_curve_defs.h"
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_citypedef struct {
418c2ecf20Sopenharmony_ci	u64 m_low;
428c2ecf20Sopenharmony_ci	u64 m_high;
438c2ecf20Sopenharmony_ci} uint128_t;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ciconst struct ecc_curve *ecc_get_curve(unsigned int curve_id)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	switch (curve_id) {
488c2ecf20Sopenharmony_ci	/* In FIPS mode only allow P256 and higher */
498c2ecf20Sopenharmony_ci	case ECC_CURVE_NIST_P192:
508c2ecf20Sopenharmony_ci		return fips_enabled ? NULL : &nist_p192;
518c2ecf20Sopenharmony_ci	case ECC_CURVE_NIST_P256:
528c2ecf20Sopenharmony_ci		return &nist_p256;
538c2ecf20Sopenharmony_ci	case ECC_CURVE_NIST_P384:
548c2ecf20Sopenharmony_ci		return &nist_p384;
558c2ecf20Sopenharmony_ci	default:
568c2ecf20Sopenharmony_ci		return NULL;
578c2ecf20Sopenharmony_ci	}
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ecc_get_curve);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic u64 *ecc_alloc_digits_space(unsigned int ndigits)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	size_t len = ndigits * sizeof(u64);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	if (!len)
668c2ecf20Sopenharmony_ci		return NULL;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	return kmalloc(len, GFP_KERNEL);
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic void ecc_free_digits_space(u64 *space)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	kfree_sensitive(space);
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic struct ecc_point *ecc_alloc_point(unsigned int ndigits)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	struct ecc_point *p = kmalloc(sizeof(*p), GFP_KERNEL);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	if (!p)
818c2ecf20Sopenharmony_ci		return NULL;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	p->x = ecc_alloc_digits_space(ndigits);
848c2ecf20Sopenharmony_ci	if (!p->x)
858c2ecf20Sopenharmony_ci		goto err_alloc_x;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	p->y = ecc_alloc_digits_space(ndigits);
888c2ecf20Sopenharmony_ci	if (!p->y)
898c2ecf20Sopenharmony_ci		goto err_alloc_y;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	p->ndigits = ndigits;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	return p;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cierr_alloc_y:
968c2ecf20Sopenharmony_ci	ecc_free_digits_space(p->x);
978c2ecf20Sopenharmony_cierr_alloc_x:
988c2ecf20Sopenharmony_ci	kfree(p);
998c2ecf20Sopenharmony_ci	return NULL;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic void ecc_free_point(struct ecc_point *p)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	if (!p)
1058c2ecf20Sopenharmony_ci		return;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	kfree_sensitive(p->x);
1088c2ecf20Sopenharmony_ci	kfree_sensitive(p->y);
1098c2ecf20Sopenharmony_ci	kfree_sensitive(p);
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic void vli_clear(u64 *vli, unsigned int ndigits)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	int i;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	for (i = 0; i < ndigits; i++)
1178c2ecf20Sopenharmony_ci		vli[i] = 0;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci/* Returns true if vli == 0, false otherwise. */
1218c2ecf20Sopenharmony_cibool vli_is_zero(const u64 *vli, unsigned int ndigits)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	int i;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	for (i = 0; i < ndigits; i++) {
1268c2ecf20Sopenharmony_ci		if (vli[i])
1278c2ecf20Sopenharmony_ci			return false;
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	return true;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vli_is_zero);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci/* Returns nonzero if bit bit of vli is set. */
1358c2ecf20Sopenharmony_cistatic u64 vli_test_bit(const u64 *vli, unsigned int bit)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	return (vli[bit / 64] & ((u64)1 << (bit % 64)));
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic bool vli_is_negative(const u64 *vli, unsigned int ndigits)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	return vli_test_bit(vli, ndigits * 64 - 1);
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci/* Counts the number of 64-bit "digits" in vli. */
1468c2ecf20Sopenharmony_cistatic unsigned int vli_num_digits(const u64 *vli, unsigned int ndigits)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	int i;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	/* Search from the end until we find a non-zero digit.
1518c2ecf20Sopenharmony_ci	 * We do it in reverse because we expect that most digits will
1528c2ecf20Sopenharmony_ci	 * be nonzero.
1538c2ecf20Sopenharmony_ci	 */
1548c2ecf20Sopenharmony_ci	for (i = ndigits - 1; i >= 0 && vli[i] == 0; i--);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	return (i + 1);
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci/* Counts the number of bits required for vli. */
1608c2ecf20Sopenharmony_cistatic unsigned int vli_num_bits(const u64 *vli, unsigned int ndigits)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	unsigned int i, num_digits;
1638c2ecf20Sopenharmony_ci	u64 digit;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	num_digits = vli_num_digits(vli, ndigits);
1668c2ecf20Sopenharmony_ci	if (num_digits == 0)
1678c2ecf20Sopenharmony_ci		return 0;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	digit = vli[num_digits - 1];
1708c2ecf20Sopenharmony_ci	for (i = 0; digit; i++)
1718c2ecf20Sopenharmony_ci		digit >>= 1;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	return ((num_digits - 1) * 64 + i);
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci/* Set dest from unaligned bit string src. */
1778c2ecf20Sopenharmony_civoid vli_from_be64(u64 *dest, const void *src, unsigned int ndigits)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	int i;
1808c2ecf20Sopenharmony_ci	const u64 *from = src;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	for (i = 0; i < ndigits; i++)
1838c2ecf20Sopenharmony_ci		dest[i] = get_unaligned_be64(&from[ndigits - 1 - i]);
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vli_from_be64);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_civoid vli_from_le64(u64 *dest, const void *src, unsigned int ndigits)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	int i;
1908c2ecf20Sopenharmony_ci	const u64 *from = src;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	for (i = 0; i < ndigits; i++)
1938c2ecf20Sopenharmony_ci		dest[i] = get_unaligned_le64(&from[i]);
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vli_from_le64);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci/* Sets dest = src. */
1988c2ecf20Sopenharmony_cistatic void vli_set(u64 *dest, const u64 *src, unsigned int ndigits)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	int i;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	for (i = 0; i < ndigits; i++)
2038c2ecf20Sopenharmony_ci		dest[i] = src[i];
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci/* Returns sign of left - right. */
2078c2ecf20Sopenharmony_ciint vli_cmp(const u64 *left, const u64 *right, unsigned int ndigits)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	int i;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	for (i = ndigits - 1; i >= 0; i--) {
2128c2ecf20Sopenharmony_ci		if (left[i] > right[i])
2138c2ecf20Sopenharmony_ci			return 1;
2148c2ecf20Sopenharmony_ci		else if (left[i] < right[i])
2158c2ecf20Sopenharmony_ci			return -1;
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	return 0;
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vli_cmp);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci/* Computes result = in << c, returning carry. Can modify in place
2238c2ecf20Sopenharmony_ci * (if result == in). 0 < shift < 64.
2248c2ecf20Sopenharmony_ci */
2258c2ecf20Sopenharmony_cistatic u64 vli_lshift(u64 *result, const u64 *in, unsigned int shift,
2268c2ecf20Sopenharmony_ci		      unsigned int ndigits)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	u64 carry = 0;
2298c2ecf20Sopenharmony_ci	int i;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	for (i = 0; i < ndigits; i++) {
2328c2ecf20Sopenharmony_ci		u64 temp = in[i];
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci		result[i] = (temp << shift) | carry;
2358c2ecf20Sopenharmony_ci		carry = temp >> (64 - shift);
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	return carry;
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci/* Computes vli = vli >> 1. */
2428c2ecf20Sopenharmony_cistatic void vli_rshift1(u64 *vli, unsigned int ndigits)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	u64 *end = vli;
2458c2ecf20Sopenharmony_ci	u64 carry = 0;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	vli += ndigits;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	while (vli-- > end) {
2508c2ecf20Sopenharmony_ci		u64 temp = *vli;
2518c2ecf20Sopenharmony_ci		*vli = (temp >> 1) | carry;
2528c2ecf20Sopenharmony_ci		carry = temp << 63;
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci/* Computes result = left + right, returning carry. Can modify in place. */
2578c2ecf20Sopenharmony_cistatic u64 vli_add(u64 *result, const u64 *left, const u64 *right,
2588c2ecf20Sopenharmony_ci		   unsigned int ndigits)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	u64 carry = 0;
2618c2ecf20Sopenharmony_ci	int i;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	for (i = 0; i < ndigits; i++) {
2648c2ecf20Sopenharmony_ci		u64 sum;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci		sum = left[i] + right[i] + carry;
2678c2ecf20Sopenharmony_ci		if (sum != left[i])
2688c2ecf20Sopenharmony_ci			carry = (sum < left[i]);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci		result[i] = sum;
2718c2ecf20Sopenharmony_ci	}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	return carry;
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci/* Computes result = left + right, returning carry. Can modify in place. */
2778c2ecf20Sopenharmony_cistatic u64 vli_uadd(u64 *result, const u64 *left, u64 right,
2788c2ecf20Sopenharmony_ci		    unsigned int ndigits)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	u64 carry = right;
2818c2ecf20Sopenharmony_ci	int i;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	for (i = 0; i < ndigits; i++) {
2848c2ecf20Sopenharmony_ci		u64 sum;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci		sum = left[i] + carry;
2878c2ecf20Sopenharmony_ci		if (sum != left[i])
2888c2ecf20Sopenharmony_ci			carry = (sum < left[i]);
2898c2ecf20Sopenharmony_ci		else
2908c2ecf20Sopenharmony_ci			carry = !!carry;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci		result[i] = sum;
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	return carry;
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci/* Computes result = left - right, returning borrow. Can modify in place. */
2998c2ecf20Sopenharmony_ciu64 vli_sub(u64 *result, const u64 *left, const u64 *right,
3008c2ecf20Sopenharmony_ci		   unsigned int ndigits)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	u64 borrow = 0;
3038c2ecf20Sopenharmony_ci	int i;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	for (i = 0; i < ndigits; i++) {
3068c2ecf20Sopenharmony_ci		u64 diff;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci		diff = left[i] - right[i] - borrow;
3098c2ecf20Sopenharmony_ci		if (diff != left[i])
3108c2ecf20Sopenharmony_ci			borrow = (diff > left[i]);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci		result[i] = diff;
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	return borrow;
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vli_sub);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci/* Computes result = left - right, returning borrow. Can modify in place. */
3208c2ecf20Sopenharmony_cistatic u64 vli_usub(u64 *result, const u64 *left, u64 right,
3218c2ecf20Sopenharmony_ci	     unsigned int ndigits)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	u64 borrow = right;
3248c2ecf20Sopenharmony_ci	int i;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	for (i = 0; i < ndigits; i++) {
3278c2ecf20Sopenharmony_ci		u64 diff;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci		diff = left[i] - borrow;
3308c2ecf20Sopenharmony_ci		if (diff != left[i])
3318c2ecf20Sopenharmony_ci			borrow = (diff > left[i]);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci		result[i] = diff;
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	return borrow;
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_cistatic uint128_t mul_64_64(u64 left, u64 right)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	uint128_t result;
3428c2ecf20Sopenharmony_ci#if defined(CONFIG_ARCH_SUPPORTS_INT128)
3438c2ecf20Sopenharmony_ci	unsigned __int128 m = (unsigned __int128)left * right;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	result.m_low  = m;
3468c2ecf20Sopenharmony_ci	result.m_high = m >> 64;
3478c2ecf20Sopenharmony_ci#else
3488c2ecf20Sopenharmony_ci	u64 a0 = left & 0xffffffffull;
3498c2ecf20Sopenharmony_ci	u64 a1 = left >> 32;
3508c2ecf20Sopenharmony_ci	u64 b0 = right & 0xffffffffull;
3518c2ecf20Sopenharmony_ci	u64 b1 = right >> 32;
3528c2ecf20Sopenharmony_ci	u64 m0 = a0 * b0;
3538c2ecf20Sopenharmony_ci	u64 m1 = a0 * b1;
3548c2ecf20Sopenharmony_ci	u64 m2 = a1 * b0;
3558c2ecf20Sopenharmony_ci	u64 m3 = a1 * b1;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	m2 += (m0 >> 32);
3588c2ecf20Sopenharmony_ci	m2 += m1;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	/* Overflow */
3618c2ecf20Sopenharmony_ci	if (m2 < m1)
3628c2ecf20Sopenharmony_ci		m3 += 0x100000000ull;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	result.m_low = (m0 & 0xffffffffull) | (m2 << 32);
3658c2ecf20Sopenharmony_ci	result.m_high = m3 + (m2 >> 32);
3668c2ecf20Sopenharmony_ci#endif
3678c2ecf20Sopenharmony_ci	return result;
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_cistatic uint128_t add_128_128(uint128_t a, uint128_t b)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	uint128_t result;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	result.m_low = a.m_low + b.m_low;
3758c2ecf20Sopenharmony_ci	result.m_high = a.m_high + b.m_high + (result.m_low < a.m_low);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	return result;
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic void vli_mult(u64 *result, const u64 *left, const u64 *right,
3818c2ecf20Sopenharmony_ci		     unsigned int ndigits)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	uint128_t r01 = { 0, 0 };
3848c2ecf20Sopenharmony_ci	u64 r2 = 0;
3858c2ecf20Sopenharmony_ci	unsigned int i, k;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	/* Compute each digit of result in sequence, maintaining the
3888c2ecf20Sopenharmony_ci	 * carries.
3898c2ecf20Sopenharmony_ci	 */
3908c2ecf20Sopenharmony_ci	for (k = 0; k < ndigits * 2 - 1; k++) {
3918c2ecf20Sopenharmony_ci		unsigned int min;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci		if (k < ndigits)
3948c2ecf20Sopenharmony_ci			min = 0;
3958c2ecf20Sopenharmony_ci		else
3968c2ecf20Sopenharmony_ci			min = (k + 1) - ndigits;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci		for (i = min; i <= k && i < ndigits; i++) {
3998c2ecf20Sopenharmony_ci			uint128_t product;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci			product = mul_64_64(left[i], right[k - i]);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci			r01 = add_128_128(r01, product);
4048c2ecf20Sopenharmony_ci			r2 += (r01.m_high < product.m_high);
4058c2ecf20Sopenharmony_ci		}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci		result[k] = r01.m_low;
4088c2ecf20Sopenharmony_ci		r01.m_low = r01.m_high;
4098c2ecf20Sopenharmony_ci		r01.m_high = r2;
4108c2ecf20Sopenharmony_ci		r2 = 0;
4118c2ecf20Sopenharmony_ci	}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	result[ndigits * 2 - 1] = r01.m_low;
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci/* Compute product = left * right, for a small right value. */
4178c2ecf20Sopenharmony_cistatic void vli_umult(u64 *result, const u64 *left, u32 right,
4188c2ecf20Sopenharmony_ci		      unsigned int ndigits)
4198c2ecf20Sopenharmony_ci{
4208c2ecf20Sopenharmony_ci	uint128_t r01 = { 0 };
4218c2ecf20Sopenharmony_ci	unsigned int k;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	for (k = 0; k < ndigits; k++) {
4248c2ecf20Sopenharmony_ci		uint128_t product;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci		product = mul_64_64(left[k], right);
4278c2ecf20Sopenharmony_ci		r01 = add_128_128(r01, product);
4288c2ecf20Sopenharmony_ci		/* no carry */
4298c2ecf20Sopenharmony_ci		result[k] = r01.m_low;
4308c2ecf20Sopenharmony_ci		r01.m_low = r01.m_high;
4318c2ecf20Sopenharmony_ci		r01.m_high = 0;
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci	result[k] = r01.m_low;
4348c2ecf20Sopenharmony_ci	for (++k; k < ndigits * 2; k++)
4358c2ecf20Sopenharmony_ci		result[k] = 0;
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistatic void vli_square(u64 *result, const u64 *left, unsigned int ndigits)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	uint128_t r01 = { 0, 0 };
4418c2ecf20Sopenharmony_ci	u64 r2 = 0;
4428c2ecf20Sopenharmony_ci	int i, k;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	for (k = 0; k < ndigits * 2 - 1; k++) {
4458c2ecf20Sopenharmony_ci		unsigned int min;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci		if (k < ndigits)
4488c2ecf20Sopenharmony_ci			min = 0;
4498c2ecf20Sopenharmony_ci		else
4508c2ecf20Sopenharmony_ci			min = (k + 1) - ndigits;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci		for (i = min; i <= k && i <= k - i; i++) {
4538c2ecf20Sopenharmony_ci			uint128_t product;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci			product = mul_64_64(left[i], left[k - i]);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci			if (i < k - i) {
4588c2ecf20Sopenharmony_ci				r2 += product.m_high >> 63;
4598c2ecf20Sopenharmony_ci				product.m_high = (product.m_high << 1) |
4608c2ecf20Sopenharmony_ci						 (product.m_low >> 63);
4618c2ecf20Sopenharmony_ci				product.m_low <<= 1;
4628c2ecf20Sopenharmony_ci			}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci			r01 = add_128_128(r01, product);
4658c2ecf20Sopenharmony_ci			r2 += (r01.m_high < product.m_high);
4668c2ecf20Sopenharmony_ci		}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci		result[k] = r01.m_low;
4698c2ecf20Sopenharmony_ci		r01.m_low = r01.m_high;
4708c2ecf20Sopenharmony_ci		r01.m_high = r2;
4718c2ecf20Sopenharmony_ci		r2 = 0;
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	result[ndigits * 2 - 1] = r01.m_low;
4758c2ecf20Sopenharmony_ci}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci/* Computes result = (left + right) % mod.
4788c2ecf20Sopenharmony_ci * Assumes that left < mod and right < mod, result != mod.
4798c2ecf20Sopenharmony_ci */
4808c2ecf20Sopenharmony_cistatic void vli_mod_add(u64 *result, const u64 *left, const u64 *right,
4818c2ecf20Sopenharmony_ci			const u64 *mod, unsigned int ndigits)
4828c2ecf20Sopenharmony_ci{
4838c2ecf20Sopenharmony_ci	u64 carry;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	carry = vli_add(result, left, right, ndigits);
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	/* result > mod (result = mod + remainder), so subtract mod to
4888c2ecf20Sopenharmony_ci	 * get remainder.
4898c2ecf20Sopenharmony_ci	 */
4908c2ecf20Sopenharmony_ci	if (carry || vli_cmp(result, mod, ndigits) >= 0)
4918c2ecf20Sopenharmony_ci		vli_sub(result, result, mod, ndigits);
4928c2ecf20Sopenharmony_ci}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci/* Computes result = (left - right) % mod.
4958c2ecf20Sopenharmony_ci * Assumes that left < mod and right < mod, result != mod.
4968c2ecf20Sopenharmony_ci */
4978c2ecf20Sopenharmony_cistatic void vli_mod_sub(u64 *result, const u64 *left, const u64 *right,
4988c2ecf20Sopenharmony_ci			const u64 *mod, unsigned int ndigits)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	u64 borrow = vli_sub(result, left, right, ndigits);
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	/* In this case, p_result == -diff == (max int) - diff.
5038c2ecf20Sopenharmony_ci	 * Since -x % d == d - x, we can get the correct result from
5048c2ecf20Sopenharmony_ci	 * result + mod (with overflow).
5058c2ecf20Sopenharmony_ci	 */
5068c2ecf20Sopenharmony_ci	if (borrow)
5078c2ecf20Sopenharmony_ci		vli_add(result, result, mod, ndigits);
5088c2ecf20Sopenharmony_ci}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci/*
5118c2ecf20Sopenharmony_ci * Computes result = product % mod
5128c2ecf20Sopenharmony_ci * for special form moduli: p = 2^k-c, for small c (note the minus sign)
5138c2ecf20Sopenharmony_ci *
5148c2ecf20Sopenharmony_ci * References:
5158c2ecf20Sopenharmony_ci * R. Crandall, C. Pomerance. Prime Numbers: A Computational Perspective.
5168c2ecf20Sopenharmony_ci * 9 Fast Algorithms for Large-Integer Arithmetic. 9.2.3 Moduli of special form
5178c2ecf20Sopenharmony_ci * Algorithm 9.2.13 (Fast mod operation for special-form moduli).
5188c2ecf20Sopenharmony_ci */
5198c2ecf20Sopenharmony_cistatic void vli_mmod_special(u64 *result, const u64 *product,
5208c2ecf20Sopenharmony_ci			      const u64 *mod, unsigned int ndigits)
5218c2ecf20Sopenharmony_ci{
5228c2ecf20Sopenharmony_ci	u64 c = -mod[0];
5238c2ecf20Sopenharmony_ci	u64 t[ECC_MAX_DIGITS * 2];
5248c2ecf20Sopenharmony_ci	u64 r[ECC_MAX_DIGITS * 2];
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	vli_set(r, product, ndigits * 2);
5278c2ecf20Sopenharmony_ci	while (!vli_is_zero(r + ndigits, ndigits)) {
5288c2ecf20Sopenharmony_ci		vli_umult(t, r + ndigits, c, ndigits);
5298c2ecf20Sopenharmony_ci		vli_clear(r + ndigits, ndigits);
5308c2ecf20Sopenharmony_ci		vli_add(r, r, t, ndigits * 2);
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci	vli_set(t, mod, ndigits);
5338c2ecf20Sopenharmony_ci	vli_clear(t + ndigits, ndigits);
5348c2ecf20Sopenharmony_ci	while (vli_cmp(r, t, ndigits * 2) >= 0)
5358c2ecf20Sopenharmony_ci		vli_sub(r, r, t, ndigits * 2);
5368c2ecf20Sopenharmony_ci	vli_set(result, r, ndigits);
5378c2ecf20Sopenharmony_ci}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci/*
5408c2ecf20Sopenharmony_ci * Computes result = product % mod
5418c2ecf20Sopenharmony_ci * for special form moduli: p = 2^{k-1}+c, for small c (note the plus sign)
5428c2ecf20Sopenharmony_ci * where k-1 does not fit into qword boundary by -1 bit (such as 255).
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci * References (loosely based on):
5458c2ecf20Sopenharmony_ci * A. Menezes, P. van Oorschot, S. Vanstone. Handbook of Applied Cryptography.
5468c2ecf20Sopenharmony_ci * 14.3.4 Reduction methods for moduli of special form. Algorithm 14.47.
5478c2ecf20Sopenharmony_ci * URL: http://cacr.uwaterloo.ca/hac/about/chap14.pdf
5488c2ecf20Sopenharmony_ci *
5498c2ecf20Sopenharmony_ci * H. Cohen, G. Frey, R. Avanzi, C. Doche, T. Lange, K. Nguyen, F. Vercauteren.
5508c2ecf20Sopenharmony_ci * Handbook of Elliptic and Hyperelliptic Curve Cryptography.
5518c2ecf20Sopenharmony_ci * Algorithm 10.25 Fast reduction for special form moduli
5528c2ecf20Sopenharmony_ci */
5538c2ecf20Sopenharmony_cistatic void vli_mmod_special2(u64 *result, const u64 *product,
5548c2ecf20Sopenharmony_ci			       const u64 *mod, unsigned int ndigits)
5558c2ecf20Sopenharmony_ci{
5568c2ecf20Sopenharmony_ci	u64 c2 = mod[0] * 2;
5578c2ecf20Sopenharmony_ci	u64 q[ECC_MAX_DIGITS];
5588c2ecf20Sopenharmony_ci	u64 r[ECC_MAX_DIGITS * 2];
5598c2ecf20Sopenharmony_ci	u64 m[ECC_MAX_DIGITS * 2]; /* expanded mod */
5608c2ecf20Sopenharmony_ci	int carry; /* last bit that doesn't fit into q */
5618c2ecf20Sopenharmony_ci	int i;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	vli_set(m, mod, ndigits);
5648c2ecf20Sopenharmony_ci	vli_clear(m + ndigits, ndigits);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	vli_set(r, product, ndigits);
5678c2ecf20Sopenharmony_ci	/* q and carry are top bits */
5688c2ecf20Sopenharmony_ci	vli_set(q, product + ndigits, ndigits);
5698c2ecf20Sopenharmony_ci	vli_clear(r + ndigits, ndigits);
5708c2ecf20Sopenharmony_ci	carry = vli_is_negative(r, ndigits);
5718c2ecf20Sopenharmony_ci	if (carry)
5728c2ecf20Sopenharmony_ci		r[ndigits - 1] &= (1ull << 63) - 1;
5738c2ecf20Sopenharmony_ci	for (i = 1; carry || !vli_is_zero(q, ndigits); i++) {
5748c2ecf20Sopenharmony_ci		u64 qc[ECC_MAX_DIGITS * 2];
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci		vli_umult(qc, q, c2, ndigits);
5778c2ecf20Sopenharmony_ci		if (carry)
5788c2ecf20Sopenharmony_ci			vli_uadd(qc, qc, mod[0], ndigits * 2);
5798c2ecf20Sopenharmony_ci		vli_set(q, qc + ndigits, ndigits);
5808c2ecf20Sopenharmony_ci		vli_clear(qc + ndigits, ndigits);
5818c2ecf20Sopenharmony_ci		carry = vli_is_negative(qc, ndigits);
5828c2ecf20Sopenharmony_ci		if (carry)
5838c2ecf20Sopenharmony_ci			qc[ndigits - 1] &= (1ull << 63) - 1;
5848c2ecf20Sopenharmony_ci		if (i & 1)
5858c2ecf20Sopenharmony_ci			vli_sub(r, r, qc, ndigits * 2);
5868c2ecf20Sopenharmony_ci		else
5878c2ecf20Sopenharmony_ci			vli_add(r, r, qc, ndigits * 2);
5888c2ecf20Sopenharmony_ci	}
5898c2ecf20Sopenharmony_ci	while (vli_is_negative(r, ndigits * 2))
5908c2ecf20Sopenharmony_ci		vli_add(r, r, m, ndigits * 2);
5918c2ecf20Sopenharmony_ci	while (vli_cmp(r, m, ndigits * 2) >= 0)
5928c2ecf20Sopenharmony_ci		vli_sub(r, r, m, ndigits * 2);
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	vli_set(result, r, ndigits);
5958c2ecf20Sopenharmony_ci}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci/*
5988c2ecf20Sopenharmony_ci * Computes result = product % mod, where product is 2N words long.
5998c2ecf20Sopenharmony_ci * Reference: Ken MacKay's micro-ecc.
6008c2ecf20Sopenharmony_ci * Currently only designed to work for curve_p or curve_n.
6018c2ecf20Sopenharmony_ci */
6028c2ecf20Sopenharmony_cistatic void vli_mmod_slow(u64 *result, u64 *product, const u64 *mod,
6038c2ecf20Sopenharmony_ci			  unsigned int ndigits)
6048c2ecf20Sopenharmony_ci{
6058c2ecf20Sopenharmony_ci	u64 mod_m[2 * ECC_MAX_DIGITS];
6068c2ecf20Sopenharmony_ci	u64 tmp[2 * ECC_MAX_DIGITS];
6078c2ecf20Sopenharmony_ci	u64 *v[2] = { tmp, product };
6088c2ecf20Sopenharmony_ci	u64 carry = 0;
6098c2ecf20Sopenharmony_ci	unsigned int i;
6108c2ecf20Sopenharmony_ci	/* Shift mod so its highest set bit is at the maximum position. */
6118c2ecf20Sopenharmony_ci	int shift = (ndigits * 2 * 64) - vli_num_bits(mod, ndigits);
6128c2ecf20Sopenharmony_ci	int word_shift = shift / 64;
6138c2ecf20Sopenharmony_ci	int bit_shift = shift % 64;
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	vli_clear(mod_m, word_shift);
6168c2ecf20Sopenharmony_ci	if (bit_shift > 0) {
6178c2ecf20Sopenharmony_ci		for (i = 0; i < ndigits; ++i) {
6188c2ecf20Sopenharmony_ci			mod_m[word_shift + i] = (mod[i] << bit_shift) | carry;
6198c2ecf20Sopenharmony_ci			carry = mod[i] >> (64 - bit_shift);
6208c2ecf20Sopenharmony_ci		}
6218c2ecf20Sopenharmony_ci	} else
6228c2ecf20Sopenharmony_ci		vli_set(mod_m + word_shift, mod, ndigits);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	for (i = 1; shift >= 0; --shift) {
6258c2ecf20Sopenharmony_ci		u64 borrow = 0;
6268c2ecf20Sopenharmony_ci		unsigned int j;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci		for (j = 0; j < ndigits * 2; ++j) {
6298c2ecf20Sopenharmony_ci			u64 diff = v[i][j] - mod_m[j] - borrow;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci			if (diff != v[i][j])
6328c2ecf20Sopenharmony_ci				borrow = (diff > v[i][j]);
6338c2ecf20Sopenharmony_ci			v[1 - i][j] = diff;
6348c2ecf20Sopenharmony_ci		}
6358c2ecf20Sopenharmony_ci		i = !(i ^ borrow); /* Swap the index if there was no borrow */
6368c2ecf20Sopenharmony_ci		vli_rshift1(mod_m, ndigits);
6378c2ecf20Sopenharmony_ci		mod_m[ndigits - 1] |= mod_m[ndigits] << (64 - 1);
6388c2ecf20Sopenharmony_ci		vli_rshift1(mod_m + ndigits, ndigits);
6398c2ecf20Sopenharmony_ci	}
6408c2ecf20Sopenharmony_ci	vli_set(result, v[i], ndigits);
6418c2ecf20Sopenharmony_ci}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci/* Computes result = product % mod using Barrett's reduction with precomputed
6448c2ecf20Sopenharmony_ci * value mu appended to the mod after ndigits, mu = (2^{2w} / mod) and have
6458c2ecf20Sopenharmony_ci * length ndigits + 1, where mu * (2^w - 1) should not overflow ndigits
6468c2ecf20Sopenharmony_ci * boundary.
6478c2ecf20Sopenharmony_ci *
6488c2ecf20Sopenharmony_ci * Reference:
6498c2ecf20Sopenharmony_ci * R. Brent, P. Zimmermann. Modern Computer Arithmetic. 2010.
6508c2ecf20Sopenharmony_ci * 2.4.1 Barrett's algorithm. Algorithm 2.5.
6518c2ecf20Sopenharmony_ci */
6528c2ecf20Sopenharmony_cistatic void vli_mmod_barrett(u64 *result, u64 *product, const u64 *mod,
6538c2ecf20Sopenharmony_ci			     unsigned int ndigits)
6548c2ecf20Sopenharmony_ci{
6558c2ecf20Sopenharmony_ci	u64 q[ECC_MAX_DIGITS * 2];
6568c2ecf20Sopenharmony_ci	u64 r[ECC_MAX_DIGITS * 2];
6578c2ecf20Sopenharmony_ci	const u64 *mu = mod + ndigits;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	vli_mult(q, product + ndigits, mu, ndigits);
6608c2ecf20Sopenharmony_ci	if (mu[ndigits])
6618c2ecf20Sopenharmony_ci		vli_add(q + ndigits, q + ndigits, product + ndigits, ndigits);
6628c2ecf20Sopenharmony_ci	vli_mult(r, mod, q + ndigits, ndigits);
6638c2ecf20Sopenharmony_ci	vli_sub(r, product, r, ndigits * 2);
6648c2ecf20Sopenharmony_ci	while (!vli_is_zero(r + ndigits, ndigits) ||
6658c2ecf20Sopenharmony_ci	       vli_cmp(r, mod, ndigits) != -1) {
6668c2ecf20Sopenharmony_ci		u64 carry;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci		carry = vli_sub(r, r, mod, ndigits);
6698c2ecf20Sopenharmony_ci		vli_usub(r + ndigits, r + ndigits, carry, ndigits);
6708c2ecf20Sopenharmony_ci	}
6718c2ecf20Sopenharmony_ci	vli_set(result, r, ndigits);
6728c2ecf20Sopenharmony_ci}
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci/* Computes p_result = p_product % curve_p.
6758c2ecf20Sopenharmony_ci * See algorithm 5 and 6 from
6768c2ecf20Sopenharmony_ci * http://www.isys.uni-klu.ac.at/PDF/2001-0126-MT.pdf
6778c2ecf20Sopenharmony_ci */
6788c2ecf20Sopenharmony_cistatic void vli_mmod_fast_192(u64 *result, const u64 *product,
6798c2ecf20Sopenharmony_ci			      const u64 *curve_prime, u64 *tmp)
6808c2ecf20Sopenharmony_ci{
6818c2ecf20Sopenharmony_ci	const unsigned int ndigits = 3;
6828c2ecf20Sopenharmony_ci	int carry;
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	vli_set(result, product, ndigits);
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	vli_set(tmp, &product[3], ndigits);
6878c2ecf20Sopenharmony_ci	carry = vli_add(result, result, tmp, ndigits);
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	tmp[0] = 0;
6908c2ecf20Sopenharmony_ci	tmp[1] = product[3];
6918c2ecf20Sopenharmony_ci	tmp[2] = product[4];
6928c2ecf20Sopenharmony_ci	carry += vli_add(result, result, tmp, ndigits);
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	tmp[0] = tmp[1] = product[5];
6958c2ecf20Sopenharmony_ci	tmp[2] = 0;
6968c2ecf20Sopenharmony_ci	carry += vli_add(result, result, tmp, ndigits);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	while (carry || vli_cmp(curve_prime, result, ndigits) != 1)
6998c2ecf20Sopenharmony_ci		carry -= vli_sub(result, result, curve_prime, ndigits);
7008c2ecf20Sopenharmony_ci}
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci/* Computes result = product % curve_prime
7038c2ecf20Sopenharmony_ci * from http://www.nsa.gov/ia/_files/nist-routines.pdf
7048c2ecf20Sopenharmony_ci */
7058c2ecf20Sopenharmony_cistatic void vli_mmod_fast_256(u64 *result, const u64 *product,
7068c2ecf20Sopenharmony_ci			      const u64 *curve_prime, u64 *tmp)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci	int carry;
7098c2ecf20Sopenharmony_ci	const unsigned int ndigits = 4;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	/* t */
7128c2ecf20Sopenharmony_ci	vli_set(result, product, ndigits);
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	/* s1 */
7158c2ecf20Sopenharmony_ci	tmp[0] = 0;
7168c2ecf20Sopenharmony_ci	tmp[1] = product[5] & 0xffffffff00000000ull;
7178c2ecf20Sopenharmony_ci	tmp[2] = product[6];
7188c2ecf20Sopenharmony_ci	tmp[3] = product[7];
7198c2ecf20Sopenharmony_ci	carry = vli_lshift(tmp, tmp, 1, ndigits);
7208c2ecf20Sopenharmony_ci	carry += vli_add(result, result, tmp, ndigits);
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	/* s2 */
7238c2ecf20Sopenharmony_ci	tmp[1] = product[6] << 32;
7248c2ecf20Sopenharmony_ci	tmp[2] = (product[6] >> 32) | (product[7] << 32);
7258c2ecf20Sopenharmony_ci	tmp[3] = product[7] >> 32;
7268c2ecf20Sopenharmony_ci	carry += vli_lshift(tmp, tmp, 1, ndigits);
7278c2ecf20Sopenharmony_ci	carry += vli_add(result, result, tmp, ndigits);
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	/* s3 */
7308c2ecf20Sopenharmony_ci	tmp[0] = product[4];
7318c2ecf20Sopenharmony_ci	tmp[1] = product[5] & 0xffffffff;
7328c2ecf20Sopenharmony_ci	tmp[2] = 0;
7338c2ecf20Sopenharmony_ci	tmp[3] = product[7];
7348c2ecf20Sopenharmony_ci	carry += vli_add(result, result, tmp, ndigits);
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	/* s4 */
7378c2ecf20Sopenharmony_ci	tmp[0] = (product[4] >> 32) | (product[5] << 32);
7388c2ecf20Sopenharmony_ci	tmp[1] = (product[5] >> 32) | (product[6] & 0xffffffff00000000ull);
7398c2ecf20Sopenharmony_ci	tmp[2] = product[7];
7408c2ecf20Sopenharmony_ci	tmp[3] = (product[6] >> 32) | (product[4] << 32);
7418c2ecf20Sopenharmony_ci	carry += vli_add(result, result, tmp, ndigits);
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	/* d1 */
7448c2ecf20Sopenharmony_ci	tmp[0] = (product[5] >> 32) | (product[6] << 32);
7458c2ecf20Sopenharmony_ci	tmp[1] = (product[6] >> 32);
7468c2ecf20Sopenharmony_ci	tmp[2] = 0;
7478c2ecf20Sopenharmony_ci	tmp[3] = (product[4] & 0xffffffff) | (product[5] << 32);
7488c2ecf20Sopenharmony_ci	carry -= vli_sub(result, result, tmp, ndigits);
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	/* d2 */
7518c2ecf20Sopenharmony_ci	tmp[0] = product[6];
7528c2ecf20Sopenharmony_ci	tmp[1] = product[7];
7538c2ecf20Sopenharmony_ci	tmp[2] = 0;
7548c2ecf20Sopenharmony_ci	tmp[3] = (product[4] >> 32) | (product[5] & 0xffffffff00000000ull);
7558c2ecf20Sopenharmony_ci	carry -= vli_sub(result, result, tmp, ndigits);
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	/* d3 */
7588c2ecf20Sopenharmony_ci	tmp[0] = (product[6] >> 32) | (product[7] << 32);
7598c2ecf20Sopenharmony_ci	tmp[1] = (product[7] >> 32) | (product[4] << 32);
7608c2ecf20Sopenharmony_ci	tmp[2] = (product[4] >> 32) | (product[5] << 32);
7618c2ecf20Sopenharmony_ci	tmp[3] = (product[6] << 32);
7628c2ecf20Sopenharmony_ci	carry -= vli_sub(result, result, tmp, ndigits);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	/* d4 */
7658c2ecf20Sopenharmony_ci	tmp[0] = product[7];
7668c2ecf20Sopenharmony_ci	tmp[1] = product[4] & 0xffffffff00000000ull;
7678c2ecf20Sopenharmony_ci	tmp[2] = product[5];
7688c2ecf20Sopenharmony_ci	tmp[3] = product[6] & 0xffffffff00000000ull;
7698c2ecf20Sopenharmony_ci	carry -= vli_sub(result, result, tmp, ndigits);
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	if (carry < 0) {
7728c2ecf20Sopenharmony_ci		do {
7738c2ecf20Sopenharmony_ci			carry += vli_add(result, result, curve_prime, ndigits);
7748c2ecf20Sopenharmony_ci		} while (carry < 0);
7758c2ecf20Sopenharmony_ci	} else {
7768c2ecf20Sopenharmony_ci		while (carry || vli_cmp(curve_prime, result, ndigits) != 1)
7778c2ecf20Sopenharmony_ci			carry -= vli_sub(result, result, curve_prime, ndigits);
7788c2ecf20Sopenharmony_ci	}
7798c2ecf20Sopenharmony_ci}
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci#define SL32OR32(x32, y32) (((u64)x32 << 32) | y32)
7828c2ecf20Sopenharmony_ci#define AND64H(x64)  (x64 & 0xffFFffFF00000000ull)
7838c2ecf20Sopenharmony_ci#define AND64L(x64)  (x64 & 0x00000000ffFFffFFull)
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci/* Computes result = product % curve_prime
7868c2ecf20Sopenharmony_ci * from "Mathematical routines for the NIST prime elliptic curves"
7878c2ecf20Sopenharmony_ci */
7888c2ecf20Sopenharmony_cistatic void vli_mmod_fast_384(u64 *result, const u64 *product,
7898c2ecf20Sopenharmony_ci				const u64 *curve_prime, u64 *tmp)
7908c2ecf20Sopenharmony_ci{
7918c2ecf20Sopenharmony_ci	int carry;
7928c2ecf20Sopenharmony_ci	const unsigned int ndigits = 6;
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	/* t */
7958c2ecf20Sopenharmony_ci	vli_set(result, product, ndigits);
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	/* s1 */
7988c2ecf20Sopenharmony_ci	tmp[0] = 0;		// 0 || 0
7998c2ecf20Sopenharmony_ci	tmp[1] = 0;		// 0 || 0
8008c2ecf20Sopenharmony_ci	tmp[2] = SL32OR32(product[11], (product[10]>>32));	//a22||a21
8018c2ecf20Sopenharmony_ci	tmp[3] = product[11]>>32;	// 0 ||a23
8028c2ecf20Sopenharmony_ci	tmp[4] = 0;		// 0 || 0
8038c2ecf20Sopenharmony_ci	tmp[5] = 0;		// 0 || 0
8048c2ecf20Sopenharmony_ci	carry = vli_lshift(tmp, tmp, 1, ndigits);
8058c2ecf20Sopenharmony_ci	carry += vli_add(result, result, tmp, ndigits);
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	/* s2 */
8088c2ecf20Sopenharmony_ci	tmp[0] = product[6];	//a13||a12
8098c2ecf20Sopenharmony_ci	tmp[1] = product[7];	//a15||a14
8108c2ecf20Sopenharmony_ci	tmp[2] = product[8];	//a17||a16
8118c2ecf20Sopenharmony_ci	tmp[3] = product[9];	//a19||a18
8128c2ecf20Sopenharmony_ci	tmp[4] = product[10];	//a21||a20
8138c2ecf20Sopenharmony_ci	tmp[5] = product[11];	//a23||a22
8148c2ecf20Sopenharmony_ci	carry += vli_add(result, result, tmp, ndigits);
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	/* s3 */
8178c2ecf20Sopenharmony_ci	tmp[0] = SL32OR32(product[11], (product[10]>>32));	//a22||a21
8188c2ecf20Sopenharmony_ci	tmp[1] = SL32OR32(product[6], (product[11]>>32));	//a12||a23
8198c2ecf20Sopenharmony_ci	tmp[2] = SL32OR32(product[7], (product[6])>>32);	//a14||a13
8208c2ecf20Sopenharmony_ci	tmp[3] = SL32OR32(product[8], (product[7]>>32));	//a16||a15
8218c2ecf20Sopenharmony_ci	tmp[4] = SL32OR32(product[9], (product[8]>>32));	//a18||a17
8228c2ecf20Sopenharmony_ci	tmp[5] = SL32OR32(product[10], (product[9]>>32));	//a20||a19
8238c2ecf20Sopenharmony_ci	carry += vli_add(result, result, tmp, ndigits);
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	/* s4 */
8268c2ecf20Sopenharmony_ci	tmp[0] = AND64H(product[11]);	//a23|| 0
8278c2ecf20Sopenharmony_ci	tmp[1] = (product[10]<<32);	//a20|| 0
8288c2ecf20Sopenharmony_ci	tmp[2] = product[6];	//a13||a12
8298c2ecf20Sopenharmony_ci	tmp[3] = product[7];	//a15||a14
8308c2ecf20Sopenharmony_ci	tmp[4] = product[8];	//a17||a16
8318c2ecf20Sopenharmony_ci	tmp[5] = product[9];	//a19||a18
8328c2ecf20Sopenharmony_ci	carry += vli_add(result, result, tmp, ndigits);
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	/* s5 */
8358c2ecf20Sopenharmony_ci	tmp[0] = 0;		//  0|| 0
8368c2ecf20Sopenharmony_ci	tmp[1] = 0;		//  0|| 0
8378c2ecf20Sopenharmony_ci	tmp[2] = product[10];	//a21||a20
8388c2ecf20Sopenharmony_ci	tmp[3] = product[11];	//a23||a22
8398c2ecf20Sopenharmony_ci	tmp[4] = 0;		//  0|| 0
8408c2ecf20Sopenharmony_ci	tmp[5] = 0;		//  0|| 0
8418c2ecf20Sopenharmony_ci	carry += vli_add(result, result, tmp, ndigits);
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	/* s6 */
8448c2ecf20Sopenharmony_ci	tmp[0] = AND64L(product[10]);	// 0 ||a20
8458c2ecf20Sopenharmony_ci	tmp[1] = AND64H(product[10]);	//a21|| 0
8468c2ecf20Sopenharmony_ci	tmp[2] = product[11];	//a23||a22
8478c2ecf20Sopenharmony_ci	tmp[3] = 0;		// 0 || 0
8488c2ecf20Sopenharmony_ci	tmp[4] = 0;		// 0 || 0
8498c2ecf20Sopenharmony_ci	tmp[5] = 0;		// 0 || 0
8508c2ecf20Sopenharmony_ci	carry += vli_add(result, result, tmp, ndigits);
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	/* d1 */
8538c2ecf20Sopenharmony_ci	tmp[0] = SL32OR32(product[6], (product[11]>>32));	//a12||a23
8548c2ecf20Sopenharmony_ci	tmp[1] = SL32OR32(product[7], (product[6]>>32));	//a14||a13
8558c2ecf20Sopenharmony_ci	tmp[2] = SL32OR32(product[8], (product[7]>>32));	//a16||a15
8568c2ecf20Sopenharmony_ci	tmp[3] = SL32OR32(product[9], (product[8]>>32));	//a18||a17
8578c2ecf20Sopenharmony_ci	tmp[4] = SL32OR32(product[10], (product[9]>>32));	//a20||a19
8588c2ecf20Sopenharmony_ci	tmp[5] = SL32OR32(product[11], (product[10]>>32));	//a22||a21
8598c2ecf20Sopenharmony_ci	carry -= vli_sub(result, result, tmp, ndigits);
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	/* d2 */
8628c2ecf20Sopenharmony_ci	tmp[0] = (product[10]<<32);	//a20|| 0
8638c2ecf20Sopenharmony_ci	tmp[1] = SL32OR32(product[11], (product[10]>>32));	//a22||a21
8648c2ecf20Sopenharmony_ci	tmp[2] = (product[11]>>32);	// 0 ||a23
8658c2ecf20Sopenharmony_ci	tmp[3] = 0;		// 0 || 0
8668c2ecf20Sopenharmony_ci	tmp[4] = 0;		// 0 || 0
8678c2ecf20Sopenharmony_ci	tmp[5] = 0;		// 0 || 0
8688c2ecf20Sopenharmony_ci	carry -= vli_sub(result, result, tmp, ndigits);
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	/* d3 */
8718c2ecf20Sopenharmony_ci	tmp[0] = 0;		// 0 || 0
8728c2ecf20Sopenharmony_ci	tmp[1] = AND64H(product[11]);	//a23|| 0
8738c2ecf20Sopenharmony_ci	tmp[2] = product[11]>>32;	// 0 ||a23
8748c2ecf20Sopenharmony_ci	tmp[3] = 0;		// 0 || 0
8758c2ecf20Sopenharmony_ci	tmp[4] = 0;		// 0 || 0
8768c2ecf20Sopenharmony_ci	tmp[5] = 0;		// 0 || 0
8778c2ecf20Sopenharmony_ci	carry -= vli_sub(result, result, tmp, ndigits);
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	if (carry < 0) {
8808c2ecf20Sopenharmony_ci		do {
8818c2ecf20Sopenharmony_ci			carry += vli_add(result, result, curve_prime, ndigits);
8828c2ecf20Sopenharmony_ci		} while (carry < 0);
8838c2ecf20Sopenharmony_ci	} else {
8848c2ecf20Sopenharmony_ci		while (carry || vli_cmp(curve_prime, result, ndigits) != 1)
8858c2ecf20Sopenharmony_ci			carry -= vli_sub(result, result, curve_prime, ndigits);
8868c2ecf20Sopenharmony_ci	}
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci}
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci#undef SL32OR32
8918c2ecf20Sopenharmony_ci#undef AND64H
8928c2ecf20Sopenharmony_ci#undef AND64L
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci/* Computes result = product % curve_prime for different curve_primes.
8958c2ecf20Sopenharmony_ci *
8968c2ecf20Sopenharmony_ci * Note that curve_primes are distinguished just by heuristic check and
8978c2ecf20Sopenharmony_ci * not by complete conformance check.
8988c2ecf20Sopenharmony_ci */
8998c2ecf20Sopenharmony_cistatic bool vli_mmod_fast(u64 *result, u64 *product,
9008c2ecf20Sopenharmony_ci			  const struct ecc_curve *curve)
9018c2ecf20Sopenharmony_ci{
9028c2ecf20Sopenharmony_ci	u64 tmp[2 * ECC_MAX_DIGITS];
9038c2ecf20Sopenharmony_ci	const u64 *curve_prime = curve->p;
9048c2ecf20Sopenharmony_ci	const unsigned int ndigits = curve->g.ndigits;
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	/* All NIST curves have name prefix 'nist_' */
9078c2ecf20Sopenharmony_ci	if (strncmp(curve->name, "nist_", 5) != 0) {
9088c2ecf20Sopenharmony_ci		/* Try to handle Pseudo-Marsenne primes. */
9098c2ecf20Sopenharmony_ci		if (curve_prime[ndigits - 1] == -1ull) {
9108c2ecf20Sopenharmony_ci			vli_mmod_special(result, product, curve_prime,
9118c2ecf20Sopenharmony_ci					 ndigits);
9128c2ecf20Sopenharmony_ci			return true;
9138c2ecf20Sopenharmony_ci		} else if (curve_prime[ndigits - 1] == 1ull << 63 &&
9148c2ecf20Sopenharmony_ci			   curve_prime[ndigits - 2] == 0) {
9158c2ecf20Sopenharmony_ci			vli_mmod_special2(result, product, curve_prime,
9168c2ecf20Sopenharmony_ci					  ndigits);
9178c2ecf20Sopenharmony_ci			return true;
9188c2ecf20Sopenharmony_ci		}
9198c2ecf20Sopenharmony_ci		vli_mmod_barrett(result, product, curve_prime, ndigits);
9208c2ecf20Sopenharmony_ci		return true;
9218c2ecf20Sopenharmony_ci	}
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	switch (ndigits) {
9248c2ecf20Sopenharmony_ci	case 3:
9258c2ecf20Sopenharmony_ci		vli_mmod_fast_192(result, product, curve_prime, tmp);
9268c2ecf20Sopenharmony_ci		break;
9278c2ecf20Sopenharmony_ci	case 4:
9288c2ecf20Sopenharmony_ci		vli_mmod_fast_256(result, product, curve_prime, tmp);
9298c2ecf20Sopenharmony_ci		break;
9308c2ecf20Sopenharmony_ci	case 6:
9318c2ecf20Sopenharmony_ci		vli_mmod_fast_384(result, product, curve_prime, tmp);
9328c2ecf20Sopenharmony_ci		break;
9338c2ecf20Sopenharmony_ci	default:
9348c2ecf20Sopenharmony_ci		pr_err_ratelimited("ecc: unsupported digits size!\n");
9358c2ecf20Sopenharmony_ci		return false;
9368c2ecf20Sopenharmony_ci	}
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	return true;
9398c2ecf20Sopenharmony_ci}
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci/* Computes result = (left * right) % mod.
9428c2ecf20Sopenharmony_ci * Assumes that mod is big enough curve order.
9438c2ecf20Sopenharmony_ci */
9448c2ecf20Sopenharmony_civoid vli_mod_mult_slow(u64 *result, const u64 *left, const u64 *right,
9458c2ecf20Sopenharmony_ci		       const u64 *mod, unsigned int ndigits)
9468c2ecf20Sopenharmony_ci{
9478c2ecf20Sopenharmony_ci	u64 product[ECC_MAX_DIGITS * 2];
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	vli_mult(product, left, right, ndigits);
9508c2ecf20Sopenharmony_ci	vli_mmod_slow(result, product, mod, ndigits);
9518c2ecf20Sopenharmony_ci}
9528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vli_mod_mult_slow);
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci/* Computes result = (left * right) % curve_prime. */
9558c2ecf20Sopenharmony_cistatic void vli_mod_mult_fast(u64 *result, const u64 *left, const u64 *right,
9568c2ecf20Sopenharmony_ci			      const struct ecc_curve *curve)
9578c2ecf20Sopenharmony_ci{
9588c2ecf20Sopenharmony_ci	u64 product[2 * ECC_MAX_DIGITS];
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	vli_mult(product, left, right, curve->g.ndigits);
9618c2ecf20Sopenharmony_ci	vli_mmod_fast(result, product, curve);
9628c2ecf20Sopenharmony_ci}
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci/* Computes result = left^2 % curve_prime. */
9658c2ecf20Sopenharmony_cistatic void vli_mod_square_fast(u64 *result, const u64 *left,
9668c2ecf20Sopenharmony_ci				const struct ecc_curve *curve)
9678c2ecf20Sopenharmony_ci{
9688c2ecf20Sopenharmony_ci	u64 product[2 * ECC_MAX_DIGITS];
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	vli_square(product, left, curve->g.ndigits);
9718c2ecf20Sopenharmony_ci	vli_mmod_fast(result, product, curve);
9728c2ecf20Sopenharmony_ci}
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci#define EVEN(vli) (!(vli[0] & 1))
9758c2ecf20Sopenharmony_ci/* Computes result = (1 / p_input) % mod. All VLIs are the same size.
9768c2ecf20Sopenharmony_ci * See "From Euclid's GCD to Montgomery Multiplication to the Great Divide"
9778c2ecf20Sopenharmony_ci * https://labs.oracle.com/techrep/2001/smli_tr-2001-95.pdf
9788c2ecf20Sopenharmony_ci */
9798c2ecf20Sopenharmony_civoid vli_mod_inv(u64 *result, const u64 *input, const u64 *mod,
9808c2ecf20Sopenharmony_ci			unsigned int ndigits)
9818c2ecf20Sopenharmony_ci{
9828c2ecf20Sopenharmony_ci	u64 a[ECC_MAX_DIGITS], b[ECC_MAX_DIGITS];
9838c2ecf20Sopenharmony_ci	u64 u[ECC_MAX_DIGITS], v[ECC_MAX_DIGITS];
9848c2ecf20Sopenharmony_ci	u64 carry;
9858c2ecf20Sopenharmony_ci	int cmp_result;
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	if (vli_is_zero(input, ndigits)) {
9888c2ecf20Sopenharmony_ci		vli_clear(result, ndigits);
9898c2ecf20Sopenharmony_ci		return;
9908c2ecf20Sopenharmony_ci	}
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci	vli_set(a, input, ndigits);
9938c2ecf20Sopenharmony_ci	vli_set(b, mod, ndigits);
9948c2ecf20Sopenharmony_ci	vli_clear(u, ndigits);
9958c2ecf20Sopenharmony_ci	u[0] = 1;
9968c2ecf20Sopenharmony_ci	vli_clear(v, ndigits);
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	while ((cmp_result = vli_cmp(a, b, ndigits)) != 0) {
9998c2ecf20Sopenharmony_ci		carry = 0;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci		if (EVEN(a)) {
10028c2ecf20Sopenharmony_ci			vli_rshift1(a, ndigits);
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci			if (!EVEN(u))
10058c2ecf20Sopenharmony_ci				carry = vli_add(u, u, mod, ndigits);
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci			vli_rshift1(u, ndigits);
10088c2ecf20Sopenharmony_ci			if (carry)
10098c2ecf20Sopenharmony_ci				u[ndigits - 1] |= 0x8000000000000000ull;
10108c2ecf20Sopenharmony_ci		} else if (EVEN(b)) {
10118c2ecf20Sopenharmony_ci			vli_rshift1(b, ndigits);
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci			if (!EVEN(v))
10148c2ecf20Sopenharmony_ci				carry = vli_add(v, v, mod, ndigits);
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci			vli_rshift1(v, ndigits);
10178c2ecf20Sopenharmony_ci			if (carry)
10188c2ecf20Sopenharmony_ci				v[ndigits - 1] |= 0x8000000000000000ull;
10198c2ecf20Sopenharmony_ci		} else if (cmp_result > 0) {
10208c2ecf20Sopenharmony_ci			vli_sub(a, a, b, ndigits);
10218c2ecf20Sopenharmony_ci			vli_rshift1(a, ndigits);
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci			if (vli_cmp(u, v, ndigits) < 0)
10248c2ecf20Sopenharmony_ci				vli_add(u, u, mod, ndigits);
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci			vli_sub(u, u, v, ndigits);
10278c2ecf20Sopenharmony_ci			if (!EVEN(u))
10288c2ecf20Sopenharmony_ci				carry = vli_add(u, u, mod, ndigits);
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci			vli_rshift1(u, ndigits);
10318c2ecf20Sopenharmony_ci			if (carry)
10328c2ecf20Sopenharmony_ci				u[ndigits - 1] |= 0x8000000000000000ull;
10338c2ecf20Sopenharmony_ci		} else {
10348c2ecf20Sopenharmony_ci			vli_sub(b, b, a, ndigits);
10358c2ecf20Sopenharmony_ci			vli_rshift1(b, ndigits);
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci			if (vli_cmp(v, u, ndigits) < 0)
10388c2ecf20Sopenharmony_ci				vli_add(v, v, mod, ndigits);
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci			vli_sub(v, v, u, ndigits);
10418c2ecf20Sopenharmony_ci			if (!EVEN(v))
10428c2ecf20Sopenharmony_ci				carry = vli_add(v, v, mod, ndigits);
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci			vli_rshift1(v, ndigits);
10458c2ecf20Sopenharmony_ci			if (carry)
10468c2ecf20Sopenharmony_ci				v[ndigits - 1] |= 0x8000000000000000ull;
10478c2ecf20Sopenharmony_ci		}
10488c2ecf20Sopenharmony_ci	}
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	vli_set(result, u, ndigits);
10518c2ecf20Sopenharmony_ci}
10528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vli_mod_inv);
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci/* ------ Point operations ------ */
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci/* Returns true if p_point is the point at infinity, false otherwise. */
10578c2ecf20Sopenharmony_cistatic bool ecc_point_is_zero(const struct ecc_point *point)
10588c2ecf20Sopenharmony_ci{
10598c2ecf20Sopenharmony_ci	return (vli_is_zero(point->x, point->ndigits) &&
10608c2ecf20Sopenharmony_ci		vli_is_zero(point->y, point->ndigits));
10618c2ecf20Sopenharmony_ci}
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci/* Point multiplication algorithm using Montgomery's ladder with co-Z
10648c2ecf20Sopenharmony_ci * coordinates. From https://eprint.iacr.org/2011/338.pdf
10658c2ecf20Sopenharmony_ci */
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci/* Double in place */
10688c2ecf20Sopenharmony_cistatic void ecc_point_double_jacobian(u64 *x1, u64 *y1, u64 *z1,
10698c2ecf20Sopenharmony_ci					const struct ecc_curve *curve)
10708c2ecf20Sopenharmony_ci{
10718c2ecf20Sopenharmony_ci	/* t1 = x, t2 = y, t3 = z */
10728c2ecf20Sopenharmony_ci	u64 t4[ECC_MAX_DIGITS];
10738c2ecf20Sopenharmony_ci	u64 t5[ECC_MAX_DIGITS];
10748c2ecf20Sopenharmony_ci	const u64 *curve_prime = curve->p;
10758c2ecf20Sopenharmony_ci	const unsigned int ndigits = curve->g.ndigits;
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	if (vli_is_zero(z1, ndigits))
10788c2ecf20Sopenharmony_ci		return;
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci	/* t4 = y1^2 */
10818c2ecf20Sopenharmony_ci	vli_mod_square_fast(t4, y1, curve);
10828c2ecf20Sopenharmony_ci	/* t5 = x1*y1^2 = A */
10838c2ecf20Sopenharmony_ci	vli_mod_mult_fast(t5, x1, t4, curve);
10848c2ecf20Sopenharmony_ci	/* t4 = y1^4 */
10858c2ecf20Sopenharmony_ci	vli_mod_square_fast(t4, t4, curve);
10868c2ecf20Sopenharmony_ci	/* t2 = y1*z1 = z3 */
10878c2ecf20Sopenharmony_ci	vli_mod_mult_fast(y1, y1, z1, curve);
10888c2ecf20Sopenharmony_ci	/* t3 = z1^2 */
10898c2ecf20Sopenharmony_ci	vli_mod_square_fast(z1, z1, curve);
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	/* t1 = x1 + z1^2 */
10928c2ecf20Sopenharmony_ci	vli_mod_add(x1, x1, z1, curve_prime, ndigits);
10938c2ecf20Sopenharmony_ci	/* t3 = 2*z1^2 */
10948c2ecf20Sopenharmony_ci	vli_mod_add(z1, z1, z1, curve_prime, ndigits);
10958c2ecf20Sopenharmony_ci	/* t3 = x1 - z1^2 */
10968c2ecf20Sopenharmony_ci	vli_mod_sub(z1, x1, z1, curve_prime, ndigits);
10978c2ecf20Sopenharmony_ci	/* t1 = x1^2 - z1^4 */
10988c2ecf20Sopenharmony_ci	vli_mod_mult_fast(x1, x1, z1, curve);
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci	/* t3 = 2*(x1^2 - z1^4) */
11018c2ecf20Sopenharmony_ci	vli_mod_add(z1, x1, x1, curve_prime, ndigits);
11028c2ecf20Sopenharmony_ci	/* t1 = 3*(x1^2 - z1^4) */
11038c2ecf20Sopenharmony_ci	vli_mod_add(x1, x1, z1, curve_prime, ndigits);
11048c2ecf20Sopenharmony_ci	if (vli_test_bit(x1, 0)) {
11058c2ecf20Sopenharmony_ci		u64 carry = vli_add(x1, x1, curve_prime, ndigits);
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci		vli_rshift1(x1, ndigits);
11088c2ecf20Sopenharmony_ci		x1[ndigits - 1] |= carry << 63;
11098c2ecf20Sopenharmony_ci	} else {
11108c2ecf20Sopenharmony_ci		vli_rshift1(x1, ndigits);
11118c2ecf20Sopenharmony_ci	}
11128c2ecf20Sopenharmony_ci	/* t1 = 3/2*(x1^2 - z1^4) = B */
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	/* t3 = B^2 */
11158c2ecf20Sopenharmony_ci	vli_mod_square_fast(z1, x1, curve);
11168c2ecf20Sopenharmony_ci	/* t3 = B^2 - A */
11178c2ecf20Sopenharmony_ci	vli_mod_sub(z1, z1, t5, curve_prime, ndigits);
11188c2ecf20Sopenharmony_ci	/* t3 = B^2 - 2A = x3 */
11198c2ecf20Sopenharmony_ci	vli_mod_sub(z1, z1, t5, curve_prime, ndigits);
11208c2ecf20Sopenharmony_ci	/* t5 = A - x3 */
11218c2ecf20Sopenharmony_ci	vli_mod_sub(t5, t5, z1, curve_prime, ndigits);
11228c2ecf20Sopenharmony_ci	/* t1 = B * (A - x3) */
11238c2ecf20Sopenharmony_ci	vli_mod_mult_fast(x1, x1, t5, curve);
11248c2ecf20Sopenharmony_ci	/* t4 = B * (A - x3) - y1^4 = y3 */
11258c2ecf20Sopenharmony_ci	vli_mod_sub(t4, x1, t4, curve_prime, ndigits);
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci	vli_set(x1, z1, ndigits);
11288c2ecf20Sopenharmony_ci	vli_set(z1, y1, ndigits);
11298c2ecf20Sopenharmony_ci	vli_set(y1, t4, ndigits);
11308c2ecf20Sopenharmony_ci}
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci/* Modify (x1, y1) => (x1 * z^2, y1 * z^3) */
11338c2ecf20Sopenharmony_cistatic void apply_z(u64 *x1, u64 *y1, u64 *z, const struct ecc_curve *curve)
11348c2ecf20Sopenharmony_ci{
11358c2ecf20Sopenharmony_ci	u64 t1[ECC_MAX_DIGITS];
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	vli_mod_square_fast(t1, z, curve);		/* z^2 */
11388c2ecf20Sopenharmony_ci	vli_mod_mult_fast(x1, x1, t1, curve);	/* x1 * z^2 */
11398c2ecf20Sopenharmony_ci	vli_mod_mult_fast(t1, t1, z, curve);	/* z^3 */
11408c2ecf20Sopenharmony_ci	vli_mod_mult_fast(y1, y1, t1, curve);	/* y1 * z^3 */
11418c2ecf20Sopenharmony_ci}
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci/* P = (x1, y1) => 2P, (x2, y2) => P' */
11448c2ecf20Sopenharmony_cistatic void xycz_initial_double(u64 *x1, u64 *y1, u64 *x2, u64 *y2,
11458c2ecf20Sopenharmony_ci				u64 *p_initial_z, const struct ecc_curve *curve)
11468c2ecf20Sopenharmony_ci{
11478c2ecf20Sopenharmony_ci	u64 z[ECC_MAX_DIGITS];
11488c2ecf20Sopenharmony_ci	const unsigned int ndigits = curve->g.ndigits;
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	vli_set(x2, x1, ndigits);
11518c2ecf20Sopenharmony_ci	vli_set(y2, y1, ndigits);
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	vli_clear(z, ndigits);
11548c2ecf20Sopenharmony_ci	z[0] = 1;
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	if (p_initial_z)
11578c2ecf20Sopenharmony_ci		vli_set(z, p_initial_z, ndigits);
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	apply_z(x1, y1, z, curve);
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	ecc_point_double_jacobian(x1, y1, z, curve);
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	apply_z(x2, y2, z, curve);
11648c2ecf20Sopenharmony_ci}
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci/* Input P = (x1, y1, Z), Q = (x2, y2, Z)
11678c2ecf20Sopenharmony_ci * Output P' = (x1', y1', Z3), P + Q = (x3, y3, Z3)
11688c2ecf20Sopenharmony_ci * or P => P', Q => P + Q
11698c2ecf20Sopenharmony_ci */
11708c2ecf20Sopenharmony_cistatic void xycz_add(u64 *x1, u64 *y1, u64 *x2, u64 *y2,
11718c2ecf20Sopenharmony_ci			const struct ecc_curve *curve)
11728c2ecf20Sopenharmony_ci{
11738c2ecf20Sopenharmony_ci	/* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */
11748c2ecf20Sopenharmony_ci	u64 t5[ECC_MAX_DIGITS];
11758c2ecf20Sopenharmony_ci	const u64 *curve_prime = curve->p;
11768c2ecf20Sopenharmony_ci	const unsigned int ndigits = curve->g.ndigits;
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci	/* t5 = x2 - x1 */
11798c2ecf20Sopenharmony_ci	vli_mod_sub(t5, x2, x1, curve_prime, ndigits);
11808c2ecf20Sopenharmony_ci	/* t5 = (x2 - x1)^2 = A */
11818c2ecf20Sopenharmony_ci	vli_mod_square_fast(t5, t5, curve);
11828c2ecf20Sopenharmony_ci	/* t1 = x1*A = B */
11838c2ecf20Sopenharmony_ci	vli_mod_mult_fast(x1, x1, t5, curve);
11848c2ecf20Sopenharmony_ci	/* t3 = x2*A = C */
11858c2ecf20Sopenharmony_ci	vli_mod_mult_fast(x2, x2, t5, curve);
11868c2ecf20Sopenharmony_ci	/* t4 = y2 - y1 */
11878c2ecf20Sopenharmony_ci	vli_mod_sub(y2, y2, y1, curve_prime, ndigits);
11888c2ecf20Sopenharmony_ci	/* t5 = (y2 - y1)^2 = D */
11898c2ecf20Sopenharmony_ci	vli_mod_square_fast(t5, y2, curve);
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci	/* t5 = D - B */
11928c2ecf20Sopenharmony_ci	vli_mod_sub(t5, t5, x1, curve_prime, ndigits);
11938c2ecf20Sopenharmony_ci	/* t5 = D - B - C = x3 */
11948c2ecf20Sopenharmony_ci	vli_mod_sub(t5, t5, x2, curve_prime, ndigits);
11958c2ecf20Sopenharmony_ci	/* t3 = C - B */
11968c2ecf20Sopenharmony_ci	vli_mod_sub(x2, x2, x1, curve_prime, ndigits);
11978c2ecf20Sopenharmony_ci	/* t2 = y1*(C - B) */
11988c2ecf20Sopenharmony_ci	vli_mod_mult_fast(y1, y1, x2, curve);
11998c2ecf20Sopenharmony_ci	/* t3 = B - x3 */
12008c2ecf20Sopenharmony_ci	vli_mod_sub(x2, x1, t5, curve_prime, ndigits);
12018c2ecf20Sopenharmony_ci	/* t4 = (y2 - y1)*(B - x3) */
12028c2ecf20Sopenharmony_ci	vli_mod_mult_fast(y2, y2, x2, curve);
12038c2ecf20Sopenharmony_ci	/* t4 = y3 */
12048c2ecf20Sopenharmony_ci	vli_mod_sub(y2, y2, y1, curve_prime, ndigits);
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci	vli_set(x2, t5, ndigits);
12078c2ecf20Sopenharmony_ci}
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci/* Input P = (x1, y1, Z), Q = (x2, y2, Z)
12108c2ecf20Sopenharmony_ci * Output P + Q = (x3, y3, Z3), P - Q = (x3', y3', Z3)
12118c2ecf20Sopenharmony_ci * or P => P - Q, Q => P + Q
12128c2ecf20Sopenharmony_ci */
12138c2ecf20Sopenharmony_cistatic void xycz_add_c(u64 *x1, u64 *y1, u64 *x2, u64 *y2,
12148c2ecf20Sopenharmony_ci			const struct ecc_curve *curve)
12158c2ecf20Sopenharmony_ci{
12168c2ecf20Sopenharmony_ci	/* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */
12178c2ecf20Sopenharmony_ci	u64 t5[ECC_MAX_DIGITS];
12188c2ecf20Sopenharmony_ci	u64 t6[ECC_MAX_DIGITS];
12198c2ecf20Sopenharmony_ci	u64 t7[ECC_MAX_DIGITS];
12208c2ecf20Sopenharmony_ci	const u64 *curve_prime = curve->p;
12218c2ecf20Sopenharmony_ci	const unsigned int ndigits = curve->g.ndigits;
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	/* t5 = x2 - x1 */
12248c2ecf20Sopenharmony_ci	vli_mod_sub(t5, x2, x1, curve_prime, ndigits);
12258c2ecf20Sopenharmony_ci	/* t5 = (x2 - x1)^2 = A */
12268c2ecf20Sopenharmony_ci	vli_mod_square_fast(t5, t5, curve);
12278c2ecf20Sopenharmony_ci	/* t1 = x1*A = B */
12288c2ecf20Sopenharmony_ci	vli_mod_mult_fast(x1, x1, t5, curve);
12298c2ecf20Sopenharmony_ci	/* t3 = x2*A = C */
12308c2ecf20Sopenharmony_ci	vli_mod_mult_fast(x2, x2, t5, curve);
12318c2ecf20Sopenharmony_ci	/* t4 = y2 + y1 */
12328c2ecf20Sopenharmony_ci	vli_mod_add(t5, y2, y1, curve_prime, ndigits);
12338c2ecf20Sopenharmony_ci	/* t4 = y2 - y1 */
12348c2ecf20Sopenharmony_ci	vli_mod_sub(y2, y2, y1, curve_prime, ndigits);
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci	/* t6 = C - B */
12378c2ecf20Sopenharmony_ci	vli_mod_sub(t6, x2, x1, curve_prime, ndigits);
12388c2ecf20Sopenharmony_ci	/* t2 = y1 * (C - B) */
12398c2ecf20Sopenharmony_ci	vli_mod_mult_fast(y1, y1, t6, curve);
12408c2ecf20Sopenharmony_ci	/* t6 = B + C */
12418c2ecf20Sopenharmony_ci	vli_mod_add(t6, x1, x2, curve_prime, ndigits);
12428c2ecf20Sopenharmony_ci	/* t3 = (y2 - y1)^2 */
12438c2ecf20Sopenharmony_ci	vli_mod_square_fast(x2, y2, curve);
12448c2ecf20Sopenharmony_ci	/* t3 = x3 */
12458c2ecf20Sopenharmony_ci	vli_mod_sub(x2, x2, t6, curve_prime, ndigits);
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	/* t7 = B - x3 */
12488c2ecf20Sopenharmony_ci	vli_mod_sub(t7, x1, x2, curve_prime, ndigits);
12498c2ecf20Sopenharmony_ci	/* t4 = (y2 - y1)*(B - x3) */
12508c2ecf20Sopenharmony_ci	vli_mod_mult_fast(y2, y2, t7, curve);
12518c2ecf20Sopenharmony_ci	/* t4 = y3 */
12528c2ecf20Sopenharmony_ci	vli_mod_sub(y2, y2, y1, curve_prime, ndigits);
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci	/* t7 = (y2 + y1)^2 = F */
12558c2ecf20Sopenharmony_ci	vli_mod_square_fast(t7, t5, curve);
12568c2ecf20Sopenharmony_ci	/* t7 = x3' */
12578c2ecf20Sopenharmony_ci	vli_mod_sub(t7, t7, t6, curve_prime, ndigits);
12588c2ecf20Sopenharmony_ci	/* t6 = x3' - B */
12598c2ecf20Sopenharmony_ci	vli_mod_sub(t6, t7, x1, curve_prime, ndigits);
12608c2ecf20Sopenharmony_ci	/* t6 = (y2 + y1)*(x3' - B) */
12618c2ecf20Sopenharmony_ci	vli_mod_mult_fast(t6, t6, t5, curve);
12628c2ecf20Sopenharmony_ci	/* t2 = y3' */
12638c2ecf20Sopenharmony_ci	vli_mod_sub(y1, t6, y1, curve_prime, ndigits);
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci	vli_set(x1, t7, ndigits);
12668c2ecf20Sopenharmony_ci}
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_cistatic void ecc_point_mult(struct ecc_point *result,
12698c2ecf20Sopenharmony_ci			   const struct ecc_point *point, const u64 *scalar,
12708c2ecf20Sopenharmony_ci			   u64 *initial_z, const struct ecc_curve *curve,
12718c2ecf20Sopenharmony_ci			   unsigned int ndigits)
12728c2ecf20Sopenharmony_ci{
12738c2ecf20Sopenharmony_ci	/* R0 and R1 */
12748c2ecf20Sopenharmony_ci	u64 rx[2][ECC_MAX_DIGITS];
12758c2ecf20Sopenharmony_ci	u64 ry[2][ECC_MAX_DIGITS];
12768c2ecf20Sopenharmony_ci	u64 z[ECC_MAX_DIGITS];
12778c2ecf20Sopenharmony_ci	u64 sk[2][ECC_MAX_DIGITS];
12788c2ecf20Sopenharmony_ci	u64 *curve_prime = curve->p;
12798c2ecf20Sopenharmony_ci	int i, nb;
12808c2ecf20Sopenharmony_ci	int num_bits;
12818c2ecf20Sopenharmony_ci	int carry;
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	carry = vli_add(sk[0], scalar, curve->n, ndigits);
12848c2ecf20Sopenharmony_ci	vli_add(sk[1], sk[0], curve->n, ndigits);
12858c2ecf20Sopenharmony_ci	scalar = sk[!carry];
12868c2ecf20Sopenharmony_ci	num_bits = sizeof(u64) * ndigits * 8 + 1;
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_ci	vli_set(rx[1], point->x, ndigits);
12898c2ecf20Sopenharmony_ci	vli_set(ry[1], point->y, ndigits);
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	xycz_initial_double(rx[1], ry[1], rx[0], ry[0], initial_z, curve);
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	for (i = num_bits - 2; i > 0; i--) {
12948c2ecf20Sopenharmony_ci		nb = !vli_test_bit(scalar, i);
12958c2ecf20Sopenharmony_ci		xycz_add_c(rx[1 - nb], ry[1 - nb], rx[nb], ry[nb], curve);
12968c2ecf20Sopenharmony_ci		xycz_add(rx[nb], ry[nb], rx[1 - nb], ry[1 - nb], curve);
12978c2ecf20Sopenharmony_ci	}
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci	nb = !vli_test_bit(scalar, 0);
13008c2ecf20Sopenharmony_ci	xycz_add_c(rx[1 - nb], ry[1 - nb], rx[nb], ry[nb], curve);
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ci	/* Find final 1/Z value. */
13038c2ecf20Sopenharmony_ci	/* X1 - X0 */
13048c2ecf20Sopenharmony_ci	vli_mod_sub(z, rx[1], rx[0], curve_prime, ndigits);
13058c2ecf20Sopenharmony_ci	/* Yb * (X1 - X0) */
13068c2ecf20Sopenharmony_ci	vli_mod_mult_fast(z, z, ry[1 - nb], curve);
13078c2ecf20Sopenharmony_ci	/* xP * Yb * (X1 - X0) */
13088c2ecf20Sopenharmony_ci	vli_mod_mult_fast(z, z, point->x, curve);
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci	/* 1 / (xP * Yb * (X1 - X0)) */
13118c2ecf20Sopenharmony_ci	vli_mod_inv(z, z, curve_prime, point->ndigits);
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci	/* yP / (xP * Yb * (X1 - X0)) */
13148c2ecf20Sopenharmony_ci	vli_mod_mult_fast(z, z, point->y, curve);
13158c2ecf20Sopenharmony_ci	/* Xb * yP / (xP * Yb * (X1 - X0)) */
13168c2ecf20Sopenharmony_ci	vli_mod_mult_fast(z, z, rx[1 - nb], curve);
13178c2ecf20Sopenharmony_ci	/* End 1/Z calculation */
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci	xycz_add(rx[nb], ry[nb], rx[1 - nb], ry[1 - nb], curve);
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci	apply_z(rx[0], ry[0], z, curve);
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	vli_set(result->x, rx[0], ndigits);
13248c2ecf20Sopenharmony_ci	vli_set(result->y, ry[0], ndigits);
13258c2ecf20Sopenharmony_ci}
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci/* Computes R = P + Q mod p */
13288c2ecf20Sopenharmony_cistatic void ecc_point_add(const struct ecc_point *result,
13298c2ecf20Sopenharmony_ci		   const struct ecc_point *p, const struct ecc_point *q,
13308c2ecf20Sopenharmony_ci		   const struct ecc_curve *curve)
13318c2ecf20Sopenharmony_ci{
13328c2ecf20Sopenharmony_ci	u64 z[ECC_MAX_DIGITS];
13338c2ecf20Sopenharmony_ci	u64 px[ECC_MAX_DIGITS];
13348c2ecf20Sopenharmony_ci	u64 py[ECC_MAX_DIGITS];
13358c2ecf20Sopenharmony_ci	unsigned int ndigits = curve->g.ndigits;
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	vli_set(result->x, q->x, ndigits);
13388c2ecf20Sopenharmony_ci	vli_set(result->y, q->y, ndigits);
13398c2ecf20Sopenharmony_ci	vli_mod_sub(z, result->x, p->x, curve->p, ndigits);
13408c2ecf20Sopenharmony_ci	vli_set(px, p->x, ndigits);
13418c2ecf20Sopenharmony_ci	vli_set(py, p->y, ndigits);
13428c2ecf20Sopenharmony_ci	xycz_add(px, py, result->x, result->y, curve);
13438c2ecf20Sopenharmony_ci	vli_mod_inv(z, z, curve->p, ndigits);
13448c2ecf20Sopenharmony_ci	apply_z(result->x, result->y, z, curve);
13458c2ecf20Sopenharmony_ci}
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci/* Computes R = u1P + u2Q mod p using Shamir's trick.
13488c2ecf20Sopenharmony_ci * Based on: Kenneth MacKay's micro-ecc (2014).
13498c2ecf20Sopenharmony_ci */
13508c2ecf20Sopenharmony_civoid ecc_point_mult_shamir(const struct ecc_point *result,
13518c2ecf20Sopenharmony_ci			   const u64 *u1, const struct ecc_point *p,
13528c2ecf20Sopenharmony_ci			   const u64 *u2, const struct ecc_point *q,
13538c2ecf20Sopenharmony_ci			   const struct ecc_curve *curve)
13548c2ecf20Sopenharmony_ci{
13558c2ecf20Sopenharmony_ci	u64 z[ECC_MAX_DIGITS];
13568c2ecf20Sopenharmony_ci	u64 sump[2][ECC_MAX_DIGITS];
13578c2ecf20Sopenharmony_ci	u64 *rx = result->x;
13588c2ecf20Sopenharmony_ci	u64 *ry = result->y;
13598c2ecf20Sopenharmony_ci	unsigned int ndigits = curve->g.ndigits;
13608c2ecf20Sopenharmony_ci	unsigned int num_bits;
13618c2ecf20Sopenharmony_ci	struct ecc_point sum = ECC_POINT_INIT(sump[0], sump[1], ndigits);
13628c2ecf20Sopenharmony_ci	const struct ecc_point *points[4];
13638c2ecf20Sopenharmony_ci	const struct ecc_point *point;
13648c2ecf20Sopenharmony_ci	unsigned int idx;
13658c2ecf20Sopenharmony_ci	int i;
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_ci	ecc_point_add(&sum, p, q, curve);
13688c2ecf20Sopenharmony_ci	points[0] = NULL;
13698c2ecf20Sopenharmony_ci	points[1] = p;
13708c2ecf20Sopenharmony_ci	points[2] = q;
13718c2ecf20Sopenharmony_ci	points[3] = &sum;
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_ci	num_bits = max(vli_num_bits(u1, ndigits), vli_num_bits(u2, ndigits));
13748c2ecf20Sopenharmony_ci	i = num_bits - 1;
13758c2ecf20Sopenharmony_ci	idx = (!!vli_test_bit(u1, i)) | ((!!vli_test_bit(u2, i)) << 1);
13768c2ecf20Sopenharmony_ci	point = points[idx];
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci	vli_set(rx, point->x, ndigits);
13798c2ecf20Sopenharmony_ci	vli_set(ry, point->y, ndigits);
13808c2ecf20Sopenharmony_ci	vli_clear(z + 1, ndigits - 1);
13818c2ecf20Sopenharmony_ci	z[0] = 1;
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci	for (--i; i >= 0; i--) {
13848c2ecf20Sopenharmony_ci		ecc_point_double_jacobian(rx, ry, z, curve);
13858c2ecf20Sopenharmony_ci		idx = (!!vli_test_bit(u1, i)) | ((!!vli_test_bit(u2, i)) << 1);
13868c2ecf20Sopenharmony_ci		point = points[idx];
13878c2ecf20Sopenharmony_ci		if (point) {
13888c2ecf20Sopenharmony_ci			u64 tx[ECC_MAX_DIGITS];
13898c2ecf20Sopenharmony_ci			u64 ty[ECC_MAX_DIGITS];
13908c2ecf20Sopenharmony_ci			u64 tz[ECC_MAX_DIGITS];
13918c2ecf20Sopenharmony_ci
13928c2ecf20Sopenharmony_ci			vli_set(tx, point->x, ndigits);
13938c2ecf20Sopenharmony_ci			vli_set(ty, point->y, ndigits);
13948c2ecf20Sopenharmony_ci			apply_z(tx, ty, z, curve);
13958c2ecf20Sopenharmony_ci			vli_mod_sub(tz, rx, tx, curve->p, ndigits);
13968c2ecf20Sopenharmony_ci			xycz_add(tx, ty, rx, ry, curve);
13978c2ecf20Sopenharmony_ci			vli_mod_mult_fast(z, z, tz, curve);
13988c2ecf20Sopenharmony_ci		}
13998c2ecf20Sopenharmony_ci	}
14008c2ecf20Sopenharmony_ci	vli_mod_inv(z, z, curve->p, ndigits);
14018c2ecf20Sopenharmony_ci	apply_z(rx, ry, z, curve);
14028c2ecf20Sopenharmony_ci}
14038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ecc_point_mult_shamir);
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_cistatic int __ecc_is_key_valid(const struct ecc_curve *curve,
14068c2ecf20Sopenharmony_ci			      const u64 *private_key, unsigned int ndigits)
14078c2ecf20Sopenharmony_ci{
14088c2ecf20Sopenharmony_ci	u64 one[ECC_MAX_DIGITS] = { 1, };
14098c2ecf20Sopenharmony_ci	u64 res[ECC_MAX_DIGITS];
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_ci	if (!private_key)
14128c2ecf20Sopenharmony_ci		return -EINVAL;
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci	if (curve->g.ndigits != ndigits)
14158c2ecf20Sopenharmony_ci		return -EINVAL;
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci	/* Make sure the private key is in the range [2, n-3]. */
14188c2ecf20Sopenharmony_ci	if (vli_cmp(one, private_key, ndigits) != -1)
14198c2ecf20Sopenharmony_ci		return -EINVAL;
14208c2ecf20Sopenharmony_ci	vli_sub(res, curve->n, one, ndigits);
14218c2ecf20Sopenharmony_ci	vli_sub(res, res, one, ndigits);
14228c2ecf20Sopenharmony_ci	if (vli_cmp(res, private_key, ndigits) != 1)
14238c2ecf20Sopenharmony_ci		return -EINVAL;
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci	return 0;
14268c2ecf20Sopenharmony_ci}
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ciint ecc_is_key_valid(unsigned int curve_id, unsigned int ndigits,
14298c2ecf20Sopenharmony_ci		     const u64 *private_key, unsigned int private_key_len)
14308c2ecf20Sopenharmony_ci{
14318c2ecf20Sopenharmony_ci	int nbytes;
14328c2ecf20Sopenharmony_ci	const struct ecc_curve *curve = ecc_get_curve(curve_id);
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci	nbytes = ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci	if (private_key_len != nbytes)
14378c2ecf20Sopenharmony_ci		return -EINVAL;
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_ci	return __ecc_is_key_valid(curve, private_key, ndigits);
14408c2ecf20Sopenharmony_ci}
14418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ecc_is_key_valid);
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci/*
14448c2ecf20Sopenharmony_ci * ECC private keys are generated using the method of extra random bits,
14458c2ecf20Sopenharmony_ci * equivalent to that described in FIPS 186-4, Appendix B.4.1.
14468c2ecf20Sopenharmony_ci *
14478c2ecf20Sopenharmony_ci * d = (c mod(n–1)) + 1    where c is a string of random bits, 64 bits longer
14488c2ecf20Sopenharmony_ci *                         than requested
14498c2ecf20Sopenharmony_ci * 0 <= c mod(n-1) <= n-2  and implies that
14508c2ecf20Sopenharmony_ci * 1 <= d <= n-1
14518c2ecf20Sopenharmony_ci *
14528c2ecf20Sopenharmony_ci * This method generates a private key uniformly distributed in the range
14538c2ecf20Sopenharmony_ci * [1, n-1].
14548c2ecf20Sopenharmony_ci */
14558c2ecf20Sopenharmony_ciint ecc_gen_privkey(unsigned int curve_id, unsigned int ndigits, u64 *privkey)
14568c2ecf20Sopenharmony_ci{
14578c2ecf20Sopenharmony_ci	const struct ecc_curve *curve = ecc_get_curve(curve_id);
14588c2ecf20Sopenharmony_ci	u64 priv[ECC_MAX_DIGITS];
14598c2ecf20Sopenharmony_ci	unsigned int nbytes = ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
14608c2ecf20Sopenharmony_ci	unsigned int nbits = vli_num_bits(curve->n, ndigits);
14618c2ecf20Sopenharmony_ci	int err;
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	/* Check that N is included in Table 1 of FIPS 186-4, section 6.1.1 */
14648c2ecf20Sopenharmony_ci	if (nbits < 160 || ndigits > ARRAY_SIZE(priv))
14658c2ecf20Sopenharmony_ci		return -EINVAL;
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_ci	/*
14688c2ecf20Sopenharmony_ci	 * FIPS 186-4 recommends that the private key should be obtained from a
14698c2ecf20Sopenharmony_ci	 * RBG with a security strength equal to or greater than the security
14708c2ecf20Sopenharmony_ci	 * strength associated with N.
14718c2ecf20Sopenharmony_ci	 *
14728c2ecf20Sopenharmony_ci	 * The maximum security strength identified by NIST SP800-57pt1r4 for
14738c2ecf20Sopenharmony_ci	 * ECC is 256 (N >= 512).
14748c2ecf20Sopenharmony_ci	 *
14758c2ecf20Sopenharmony_ci	 * This condition is met by the default RNG because it selects a favored
14768c2ecf20Sopenharmony_ci	 * DRBG with a security strength of 256.
14778c2ecf20Sopenharmony_ci	 */
14788c2ecf20Sopenharmony_ci	if (crypto_get_default_rng())
14798c2ecf20Sopenharmony_ci		return -EFAULT;
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci	err = crypto_rng_get_bytes(crypto_default_rng, (u8 *)priv, nbytes);
14828c2ecf20Sopenharmony_ci	crypto_put_default_rng();
14838c2ecf20Sopenharmony_ci	if (err)
14848c2ecf20Sopenharmony_ci		return err;
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci	/* Make sure the private key is in the valid range. */
14878c2ecf20Sopenharmony_ci	if (__ecc_is_key_valid(curve, priv, ndigits))
14888c2ecf20Sopenharmony_ci		return -EINVAL;
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci	ecc_swap_digits(priv, privkey, ndigits);
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_ci	return 0;
14938c2ecf20Sopenharmony_ci}
14948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ecc_gen_privkey);
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ciint ecc_make_pub_key(unsigned int curve_id, unsigned int ndigits,
14978c2ecf20Sopenharmony_ci		     const u64 *private_key, u64 *public_key)
14988c2ecf20Sopenharmony_ci{
14998c2ecf20Sopenharmony_ci	int ret = 0;
15008c2ecf20Sopenharmony_ci	struct ecc_point *pk;
15018c2ecf20Sopenharmony_ci	u64 priv[ECC_MAX_DIGITS];
15028c2ecf20Sopenharmony_ci	const struct ecc_curve *curve = ecc_get_curve(curve_id);
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_ci	if (!private_key || !curve || ndigits > ARRAY_SIZE(priv)) {
15058c2ecf20Sopenharmony_ci		ret = -EINVAL;
15068c2ecf20Sopenharmony_ci		goto out;
15078c2ecf20Sopenharmony_ci	}
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	ecc_swap_digits(private_key, priv, ndigits);
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ci	pk = ecc_alloc_point(ndigits);
15128c2ecf20Sopenharmony_ci	if (!pk) {
15138c2ecf20Sopenharmony_ci		ret = -ENOMEM;
15148c2ecf20Sopenharmony_ci		goto out;
15158c2ecf20Sopenharmony_ci	}
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_ci	ecc_point_mult(pk, &curve->g, priv, NULL, curve, ndigits);
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci	/* SP800-56A rev 3 5.6.2.1.3 key check */
15208c2ecf20Sopenharmony_ci	if (ecc_is_pubkey_valid_full(curve, pk)) {
15218c2ecf20Sopenharmony_ci		ret = -EAGAIN;
15228c2ecf20Sopenharmony_ci		goto err_free_point;
15238c2ecf20Sopenharmony_ci	}
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci	ecc_swap_digits(pk->x, public_key, ndigits);
15268c2ecf20Sopenharmony_ci	ecc_swap_digits(pk->y, &public_key[ndigits], ndigits);
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_cierr_free_point:
15298c2ecf20Sopenharmony_ci	ecc_free_point(pk);
15308c2ecf20Sopenharmony_ciout:
15318c2ecf20Sopenharmony_ci	return ret;
15328c2ecf20Sopenharmony_ci}
15338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ecc_make_pub_key);
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci/* SP800-56A section 5.6.2.3.4 partial verification: ephemeral keys only */
15368c2ecf20Sopenharmony_ciint ecc_is_pubkey_valid_partial(const struct ecc_curve *curve,
15378c2ecf20Sopenharmony_ci				struct ecc_point *pk)
15388c2ecf20Sopenharmony_ci{
15398c2ecf20Sopenharmony_ci	u64 yy[ECC_MAX_DIGITS], xxx[ECC_MAX_DIGITS], w[ECC_MAX_DIGITS];
15408c2ecf20Sopenharmony_ci
15418c2ecf20Sopenharmony_ci	if (WARN_ON(pk->ndigits != curve->g.ndigits))
15428c2ecf20Sopenharmony_ci		return -EINVAL;
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci	/* Check 1: Verify key is not the zero point. */
15458c2ecf20Sopenharmony_ci	if (ecc_point_is_zero(pk))
15468c2ecf20Sopenharmony_ci		return -EINVAL;
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci	/* Check 2: Verify key is in the range [1, p-1]. */
15498c2ecf20Sopenharmony_ci	if (vli_cmp(curve->p, pk->x, pk->ndigits) != 1)
15508c2ecf20Sopenharmony_ci		return -EINVAL;
15518c2ecf20Sopenharmony_ci	if (vli_cmp(curve->p, pk->y, pk->ndigits) != 1)
15528c2ecf20Sopenharmony_ci		return -EINVAL;
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	/* Check 3: Verify that y^2 == (x^3 + a·x + b) mod p */
15558c2ecf20Sopenharmony_ci	vli_mod_square_fast(yy, pk->y, curve); /* y^2 */
15568c2ecf20Sopenharmony_ci	vli_mod_square_fast(xxx, pk->x, curve); /* x^2 */
15578c2ecf20Sopenharmony_ci	vli_mod_mult_fast(xxx, xxx, pk->x, curve); /* x^3 */
15588c2ecf20Sopenharmony_ci	vli_mod_mult_fast(w, curve->a, pk->x, curve); /* a·x */
15598c2ecf20Sopenharmony_ci	vli_mod_add(w, w, curve->b, curve->p, pk->ndigits); /* a·x + b */
15608c2ecf20Sopenharmony_ci	vli_mod_add(w, w, xxx, curve->p, pk->ndigits); /* x^3 + a·x + b */
15618c2ecf20Sopenharmony_ci	if (vli_cmp(yy, w, pk->ndigits) != 0) /* Equation */
15628c2ecf20Sopenharmony_ci		return -EINVAL;
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci	return 0;
15658c2ecf20Sopenharmony_ci}
15668c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ecc_is_pubkey_valid_partial);
15678c2ecf20Sopenharmony_ci
15688c2ecf20Sopenharmony_ci/* SP800-56A section 5.6.2.3.3 full verification */
15698c2ecf20Sopenharmony_ciint ecc_is_pubkey_valid_full(const struct ecc_curve *curve,
15708c2ecf20Sopenharmony_ci			     struct ecc_point *pk)
15718c2ecf20Sopenharmony_ci{
15728c2ecf20Sopenharmony_ci	struct ecc_point *nQ;
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_ci	/* Checks 1 through 3 */
15758c2ecf20Sopenharmony_ci	int ret = ecc_is_pubkey_valid_partial(curve, pk);
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_ci	if (ret)
15788c2ecf20Sopenharmony_ci		return ret;
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci	/* Check 4: Verify that nQ is the zero point. */
15818c2ecf20Sopenharmony_ci	nQ = ecc_alloc_point(pk->ndigits);
15828c2ecf20Sopenharmony_ci	if (!nQ)
15838c2ecf20Sopenharmony_ci		return -ENOMEM;
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_ci	ecc_point_mult(nQ, pk, curve->n, NULL, curve, pk->ndigits);
15868c2ecf20Sopenharmony_ci	if (!ecc_point_is_zero(nQ))
15878c2ecf20Sopenharmony_ci		ret = -EINVAL;
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_ci	ecc_free_point(nQ);
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci	return ret;
15928c2ecf20Sopenharmony_ci}
15938c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ecc_is_pubkey_valid_full);
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ciint crypto_ecdh_shared_secret(unsigned int curve_id, unsigned int ndigits,
15968c2ecf20Sopenharmony_ci			      const u64 *private_key, const u64 *public_key,
15978c2ecf20Sopenharmony_ci			      u64 *secret)
15988c2ecf20Sopenharmony_ci{
15998c2ecf20Sopenharmony_ci	int ret = 0;
16008c2ecf20Sopenharmony_ci	struct ecc_point *product, *pk;
16018c2ecf20Sopenharmony_ci	u64 priv[ECC_MAX_DIGITS];
16028c2ecf20Sopenharmony_ci	u64 rand_z[ECC_MAX_DIGITS];
16038c2ecf20Sopenharmony_ci	unsigned int nbytes;
16048c2ecf20Sopenharmony_ci	const struct ecc_curve *curve = ecc_get_curve(curve_id);
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci	if (!private_key || !public_key || !curve ||
16078c2ecf20Sopenharmony_ci	    ndigits > ARRAY_SIZE(priv) || ndigits > ARRAY_SIZE(rand_z)) {
16088c2ecf20Sopenharmony_ci		ret = -EINVAL;
16098c2ecf20Sopenharmony_ci		goto out;
16108c2ecf20Sopenharmony_ci	}
16118c2ecf20Sopenharmony_ci
16128c2ecf20Sopenharmony_ci	nbytes = ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci	get_random_bytes(rand_z, nbytes);
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_ci	pk = ecc_alloc_point(ndigits);
16178c2ecf20Sopenharmony_ci	if (!pk) {
16188c2ecf20Sopenharmony_ci		ret = -ENOMEM;
16198c2ecf20Sopenharmony_ci		goto out;
16208c2ecf20Sopenharmony_ci	}
16218c2ecf20Sopenharmony_ci
16228c2ecf20Sopenharmony_ci	ecc_swap_digits(public_key, pk->x, ndigits);
16238c2ecf20Sopenharmony_ci	ecc_swap_digits(&public_key[ndigits], pk->y, ndigits);
16248c2ecf20Sopenharmony_ci	ret = ecc_is_pubkey_valid_partial(curve, pk);
16258c2ecf20Sopenharmony_ci	if (ret)
16268c2ecf20Sopenharmony_ci		goto err_alloc_product;
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci	ecc_swap_digits(private_key, priv, ndigits);
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_ci	product = ecc_alloc_point(ndigits);
16318c2ecf20Sopenharmony_ci	if (!product) {
16328c2ecf20Sopenharmony_ci		ret = -ENOMEM;
16338c2ecf20Sopenharmony_ci		goto err_alloc_product;
16348c2ecf20Sopenharmony_ci	}
16358c2ecf20Sopenharmony_ci
16368c2ecf20Sopenharmony_ci	ecc_point_mult(product, pk, priv, rand_z, curve, ndigits);
16378c2ecf20Sopenharmony_ci
16388c2ecf20Sopenharmony_ci	if (ecc_point_is_zero(product)) {
16398c2ecf20Sopenharmony_ci		ret = -EFAULT;
16408c2ecf20Sopenharmony_ci		goto err_validity;
16418c2ecf20Sopenharmony_ci	}
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_ci	ecc_swap_digits(product->x, secret, ndigits);
16448c2ecf20Sopenharmony_ci
16458c2ecf20Sopenharmony_cierr_validity:
16468c2ecf20Sopenharmony_ci	memzero_explicit(priv, sizeof(priv));
16478c2ecf20Sopenharmony_ci	memzero_explicit(rand_z, sizeof(rand_z));
16488c2ecf20Sopenharmony_ci	ecc_free_point(product);
16498c2ecf20Sopenharmony_cierr_alloc_product:
16508c2ecf20Sopenharmony_ci	ecc_free_point(pk);
16518c2ecf20Sopenharmony_ciout:
16528c2ecf20Sopenharmony_ci	return ret;
16538c2ecf20Sopenharmony_ci}
16548c2ecf20Sopenharmony_ciEXPORT_SYMBOL(crypto_ecdh_shared_secret);
16558c2ecf20Sopenharmony_ci
16568c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
1657