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] = ∑ 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