xref: /openbsd-src/lib/libcrypto/ec/ec_mult.c (revision 9581610f4a7d5c243444308e52cfaf2239da948c)
1*9581610fStb /* $OpenBSD: ec_mult.c,v 1.57 2025/01/11 13:58:31 tb Exp $ */
24fcf65c5Sdjm /*
34fcf65c5Sdjm  * Originally written by Bodo Moeller and Nils Larsch for the OpenSSL project.
44fcf65c5Sdjm  */
5da347917Sbeck /* ====================================================================
64fcf65c5Sdjm  * Copyright (c) 1998-2007 The OpenSSL Project.  All rights reserved.
7da347917Sbeck  *
8da347917Sbeck  * Redistribution and use in source and binary forms, with or without
9da347917Sbeck  * modification, are permitted provided that the following conditions
10da347917Sbeck  * are met:
11da347917Sbeck  *
12da347917Sbeck  * 1. Redistributions of source code must retain the above copyright
13da347917Sbeck  *    notice, this list of conditions and the following disclaimer.
14da347917Sbeck  *
15da347917Sbeck  * 2. Redistributions in binary form must reproduce the above copyright
16da347917Sbeck  *    notice, this list of conditions and the following disclaimer in
17da347917Sbeck  *    the documentation and/or other materials provided with the
18da347917Sbeck  *    distribution.
19da347917Sbeck  *
20da347917Sbeck  * 3. All advertising materials mentioning features or use of this
21da347917Sbeck  *    software must display the following acknowledgment:
22da347917Sbeck  *    "This product includes software developed by the OpenSSL Project
23da347917Sbeck  *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
24da347917Sbeck  *
25da347917Sbeck  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
26da347917Sbeck  *    endorse or promote products derived from this software without
27da347917Sbeck  *    prior written permission. For written permission, please contact
28da347917Sbeck  *    openssl-core@openssl.org.
29da347917Sbeck  *
30da347917Sbeck  * 5. Products derived from this software may not be called "OpenSSL"
31da347917Sbeck  *    nor may "OpenSSL" appear in their names without prior written
32da347917Sbeck  *    permission of the OpenSSL Project.
33da347917Sbeck  *
34da347917Sbeck  * 6. Redistributions of any form whatsoever must retain the following
35da347917Sbeck  *    acknowledgment:
36da347917Sbeck  *    "This product includes software developed by the OpenSSL Project
37da347917Sbeck  *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
38da347917Sbeck  *
39da347917Sbeck  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
40da347917Sbeck  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41da347917Sbeck  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42da347917Sbeck  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
43da347917Sbeck  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44da347917Sbeck  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
45da347917Sbeck  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46da347917Sbeck  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47da347917Sbeck  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
48da347917Sbeck  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49da347917Sbeck  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
50da347917Sbeck  * OF THE POSSIBILITY OF SUCH DAMAGE.
51da347917Sbeck  * ====================================================================
52da347917Sbeck  *
53da347917Sbeck  * This product includes cryptographic software written by Eric Young
54da347917Sbeck  * (eay@cryptsoft.com).  This product includes software written by Tim
55da347917Sbeck  * Hudson (tjh@cryptsoft.com).
56da347917Sbeck  *
57da347917Sbeck  */
584fcf65c5Sdjm /* ====================================================================
594fcf65c5Sdjm  * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
604fcf65c5Sdjm  * Portions of this software developed by SUN MICROSYSTEMS, INC.,
614fcf65c5Sdjm  * and contributed to the OpenSSL project.
624fcf65c5Sdjm  */
634fcf65c5Sdjm 
64846d7a9aStb #include <stdint.h>
658e0dcea1Stb #include <stdlib.h>
66846d7a9aStb #include <string.h>
67da347917Sbeck 
688e0dcea1Stb #include <openssl/bn.h>
698e0dcea1Stb #include <openssl/ec.h>
70da347917Sbeck #include <openssl/err.h>
71da347917Sbeck 
72c9675a23Stb #include "ec_local.h"
73da347917Sbeck 
742380456eStb /* Holds the wNAF digits of bn and the corresponding odd multiples of point. */
752380456eStb struct ec_wnaf {
762380456eStb 	signed char *digits;
772380456eStb 	size_t num_digits;
782380456eStb 	EC_POINT **multiples;
792380456eStb 	size_t num_multiples;
802380456eStb };
812380456eStb 
829a8e114fStb static int
839a8e114fStb ec_window_bits(const BIGNUM *bn)
84da347917Sbeck {
859a8e114fStb 	int bits = BN_num_bits(bn);
86da347917Sbeck 
879a8e114fStb 	if (bits >= 2000)
889a8e114fStb 		return 6;
899a8e114fStb 	if (bits >= 800)
909a8e114fStb 		return 5;
919a8e114fStb 	if (bits >= 300)
929a8e114fStb 		return 4;
939a8e114fStb 	if (bits >= 70)
949a8e114fStb 		return 3;
959a8e114fStb 	if (bits >= 20)
969a8e114fStb 		return 2;
979a8e114fStb 
989a8e114fStb 	return 1;
999a8e114fStb }
1009a8e114fStb 
101007a51dcStb /*
102ff030d81Stb  * Width-(w+1) non-adjacent form of bn = \sum_j n_j 2^j, with odd n_j,
103ff030d81Stb  * where at most one of any (w+1) consecutive digits is non-zero.
104007a51dcStb  */
105007a51dcStb 
1069a8e114fStb static int
1072380456eStb ec_compute_wnaf(const BIGNUM *bn, signed char *digits, size_t num_digits)
1089a8e114fStb {
109ff030d81Stb 	int digit, bit, next, sign, wbits, window;
1102380456eStb 	size_t i;
1119a8e114fStb 	int ret = 0;
1129a8e114fStb 
1132380456eStb 	if (num_digits != BN_num_bits(bn) + 1) {
1142380456eStb 		ECerror(ERR_R_INTERNAL_ERROR);
1154fcf65c5Sdjm 		goto err;
1164fcf65c5Sdjm 	}
1174fcf65c5Sdjm 
118360ab434Stb 	sign = BN_is_negative(bn) ? -1 : 1;
119360ab434Stb 
12008f8d319Stb 	wbits = ec_window_bits(bn);
12108f8d319Stb 
1229a8e114fStb 	bit = 1 << wbits;
1239a8e114fStb 	next = bit << 1;
1249a8e114fStb 
12557573e5dStb 	/* Extract the wbits + 1 lowest bits from bn into window. */
126007a51dcStb 	window = 0;
127007a51dcStb 	for (i = 0; i < wbits + 1; i++) {
128007a51dcStb 		if (BN_is_bit_set(bn, i))
129007a51dcStb 			window |= (1 << i);
130007a51dcStb 	}
131fb282802Stb 
132007a51dcStb 	/* Instead of bn >>= 1 in each iteration, slide window to the left. */
1332380456eStb 	for (i = 0; i < num_digits; i++) {
1349a8e114fStb 		digit = 0;
1359a8e114fStb 
136007a51dcStb 		/*
137007a51dcStb 		 * If window is odd, the i-th wNAF digit is window (mods 2^w),
138360ab434Stb 		 * where mods is the signed modulo in (-2^w-1, 2^w-1]. Subtract
139360ab434Stb 		 * the digit from window, so window is 0 or next, and add the
140360ab434Stb 		 * digit to the wNAF digits.
141007a51dcStb 		 */
1429a8e114fStb 		if ((window & 1) != 0) {
1439a8e114fStb 			digit = window;
144ff030d81Stb 			if ((window & bit) != 0)
1459a8e114fStb 				digit = window - next;
1469a8e114fStb 			window -= digit;
147da347917Sbeck 		}
148da347917Sbeck 
1492380456eStb 		digits[i] = sign * digit;
150007a51dcStb 
151007a51dcStb 		/* Slide the window to the left. */
1529a8e114fStb 		window >>= 1;
153fb282802Stb 		window += bit * BN_is_bit_set(bn, i + wbits + 1);
154da347917Sbeck 	}
155da347917Sbeck 
1569a8e114fStb 	ret = 1;
157da347917Sbeck 
158da347917Sbeck  err:
1599a8e114fStb 	return ret;
1609a8e114fStb }
161da347917Sbeck 
1625186b6f3Stb static int
1635186b6f3Stb ec_compute_odd_multiples(const EC_GROUP *group, const EC_POINT *point,
1642380456eStb     EC_POINT **multiples, size_t num_multiples, BN_CTX *ctx)
1655186b6f3Stb {
1665186b6f3Stb 	EC_POINT *doubled = NULL;
1675186b6f3Stb 	size_t i;
1685186b6f3Stb 	int ret = 0;
1695186b6f3Stb 
1702380456eStb 	if (num_multiples < 1)
171846d7a9aStb 		goto err;
172846d7a9aStb 
1732380456eStb 	if ((multiples[0] = EC_POINT_dup(point, group)) == NULL)
1745186b6f3Stb 		goto err;
1755186b6f3Stb 
1765186b6f3Stb 	if ((doubled = EC_POINT_new(group)) == NULL)
1775186b6f3Stb 		goto err;
1785186b6f3Stb 	if (!EC_POINT_dbl(group, doubled, point, ctx))
1795186b6f3Stb 		goto err;
1802380456eStb 	for (i = 1; i < num_multiples; i++) {
1812380456eStb 		if ((multiples[i] = EC_POINT_new(group)) == NULL)
1825186b6f3Stb 			goto err;
1832380456eStb 		if (!EC_POINT_add(group, multiples[i], multiples[i - 1], doubled,
1842380456eStb 		    ctx))
1855186b6f3Stb 			goto err;
1865186b6f3Stb 	}
1875186b6f3Stb 
1885186b6f3Stb 	ret = 1;
1895186b6f3Stb 
1905186b6f3Stb  err:
1915186b6f3Stb 	EC_POINT_free(doubled);
1925186b6f3Stb 
1935186b6f3Stb 	return ret;
1945186b6f3Stb }
1955186b6f3Stb 
1965186b6f3Stb /*
1972380456eStb  * Bring multiples held in wnaf0 and wnaf1 simultaneously into affine form
1989d4c47a8Stb  * so that the operations in the loop in ec_wnaf_mul() can take fast paths.
1995186b6f3Stb  */
2005186b6f3Stb 
2015186b6f3Stb static int
2022380456eStb ec_normalize_points(const EC_GROUP *group, struct ec_wnaf *wnaf0,
2032380456eStb     struct ec_wnaf *wnaf1, BN_CTX *ctx)
2045186b6f3Stb {
2052380456eStb 	EC_POINT **points0 = wnaf0->multiples, **points1 = wnaf1->multiples;
2062380456eStb 	size_t len0 = wnaf0->num_multiples, len1 = wnaf1->num_multiples;
207846d7a9aStb 	EC_POINT **val = NULL;
208846d7a9aStb 	size_t len = 0;
209846d7a9aStb 	int ret = 0;
2105186b6f3Stb 
211846d7a9aStb 	if (len1 > SIZE_MAX - len0)
212846d7a9aStb 		goto err;
213846d7a9aStb 	len = len0 + len1;
214846d7a9aStb 
215846d7a9aStb 	if ((val = calloc(len, sizeof(*val))) == NULL) {
2165186b6f3Stb 		ECerror(ERR_R_MALLOC_FAILURE);
2175186b6f3Stb 		goto err;
2185186b6f3Stb 	}
2192380456eStb 	memcpy(&val[0], points0, sizeof(*val) * len0);
2202380456eStb 	memcpy(&val[len0], points1, sizeof(*val) * len1);
2215186b6f3Stb 
222*9581610fStb 	if (!group->meth->points_make_affine(group, len, val, ctx))
2235186b6f3Stb 		goto err;
2245186b6f3Stb 
2255186b6f3Stb 	ret = 1;
2265186b6f3Stb 
2275186b6f3Stb  err:
2285186b6f3Stb 	free(val);
2295186b6f3Stb 
2305186b6f3Stb 	return ret;
2315186b6f3Stb }
2325186b6f3Stb 
2332380456eStb static void
2342380456eStb ec_points_free(EC_POINT **points, size_t num_points)
2352380456eStb {
2362380456eStb 	size_t i;
2372380456eStb 
2382380456eStb 	if (points == NULL)
2392380456eStb 		return;
2402380456eStb 
2412380456eStb 	for (i = 0; i < num_points; i++)
2422380456eStb 		EC_POINT_free(points[i]);
2432380456eStb 	free(points);
2442380456eStb }
2452380456eStb 
2462380456eStb static void
2472380456eStb ec_wnaf_free(struct ec_wnaf *wnaf)
2482380456eStb {
2492380456eStb 	if (wnaf == NULL)
2502380456eStb 		return;
2512380456eStb 
2522380456eStb 	free(wnaf->digits);
2532380456eStb 	ec_points_free(wnaf->multiples, wnaf->num_multiples);
2542380456eStb 	free(wnaf);
2552380456eStb }
2562380456eStb 
2572380456eStb /*
2582380456eStb  * Calculate wNAF splitting of bn and the corresponding odd multiples of point.
2592380456eStb  */
2602380456eStb 
2612380456eStb static struct ec_wnaf *
2622380456eStb ec_wnaf_new(const EC_GROUP *group, const EC_POINT *point, const BIGNUM *bn,
2632380456eStb     BN_CTX *ctx)
2642380456eStb {
2652380456eStb 	struct ec_wnaf *wnaf;
2662380456eStb 
2672380456eStb 	if ((wnaf = calloc(1, sizeof(*wnaf))) == NULL)
2682380456eStb 		goto err;
2692380456eStb 
2702380456eStb 	wnaf->num_digits = BN_num_bits(bn) + 1;
2712380456eStb 	if ((wnaf->digits = calloc(wnaf->num_digits,
2722380456eStb 	    sizeof(*wnaf->digits))) == NULL)
2732380456eStb 		goto err;
2742380456eStb 
2752380456eStb 	if (!ec_compute_wnaf(bn, wnaf->digits, wnaf->num_digits))
2762380456eStb 		goto err;
2772380456eStb 
2783d8e7e8cStb 	wnaf->num_multiples = 1ULL << (ec_window_bits(bn) - 1);
2792380456eStb 	if ((wnaf->multiples = calloc(wnaf->num_multiples,
2802380456eStb 	    sizeof(*wnaf->multiples))) == NULL)
2812380456eStb 		goto err;
2822380456eStb 
2832380456eStb 	if (!ec_compute_odd_multiples(group, point, wnaf->multiples,
2842380456eStb 	    wnaf->num_multiples, ctx))
2852380456eStb 		goto err;
2862380456eStb 
2872380456eStb 	return wnaf;
2882380456eStb 
2892380456eStb  err:
2902380456eStb 	ec_wnaf_free(wnaf);
2912380456eStb 
2922380456eStb 	return NULL;
2932380456eStb }
2942380456eStb 
2952380456eStb static signed char
2962380456eStb ec_wnaf_digit(struct ec_wnaf *wnaf, size_t idx)
2972380456eStb {
2982380456eStb 	if (idx >= wnaf->num_digits)
2992380456eStb 		return 0;
3002380456eStb 
3012380456eStb 	return wnaf->digits[idx];
3022380456eStb }
3032380456eStb 
3044ccb4605Stb static const EC_POINT *
3052380456eStb ec_wnaf_multiple(struct ec_wnaf *wnaf, signed char digit)
3062380456eStb {
3072380456eStb 	if (digit < 0)
3082380456eStb 		return NULL;
3092380456eStb 	if (digit >= 2 * wnaf->num_multiples)
3102380456eStb 		return NULL;
3112380456eStb 
3122380456eStb 	return wnaf->multiples[digit >> 1];
3132380456eStb }
3142380456eStb 
3152e917874Stb /*
3162e917874Stb  * Compute r = generator * m + point * n in non-constant time.
317da347917Sbeck  */
3182e917874Stb 
319f67ac449Stedu int
3209d4c47a8Stb ec_wnaf_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *m,
3212e917874Stb     const EC_POINT *point, const BIGNUM *n, BN_CTX *ctx)
322da347917Sbeck {
3232380456eStb 	struct ec_wnaf *wnaf[2] = { NULL, NULL };
324846d7a9aStb 	const EC_POINT *generator;
3255186b6f3Stb 	size_t i;
326da347917Sbeck 	int k;
327da347917Sbeck 	int r_is_inverted = 0;
3282380456eStb 	size_t num_digits;
329da347917Sbeck 	int ret = 0;
330da347917Sbeck 
3312e917874Stb 	if (m == NULL || n == NULL) {
3322e917874Stb 		ECerror(ERR_R_PASSED_NULL_PARAMETER);
3332e917874Stb 		goto err;
3342e917874Stb 	}
3352e917874Stb 	if (group->meth != r->meth || group->meth != point->meth) {
3365067ae9fSbeck 		ECerror(EC_R_INCOMPATIBLE_OBJECTS);
3372e917874Stb 		goto err;
3384fcf65c5Sdjm 	}
3394fcf65c5Sdjm 
340846d7a9aStb 	if ((generator = EC_GROUP_get0_generator(group)) == NULL) {
341846d7a9aStb 		ECerror(EC_R_UNDEFINED_GENERATOR);
342846d7a9aStb 		goto err;
343846d7a9aStb 	}
344846d7a9aStb 
3452380456eStb 	if ((wnaf[0] = ec_wnaf_new(group, generator, m, ctx)) == NULL)
346846d7a9aStb 		goto err;
3472380456eStb 	if ((wnaf[1] = ec_wnaf_new(group, point, n, ctx)) == NULL)
348da347917Sbeck 		goto err;
349da347917Sbeck 
3502380456eStb 	if (!ec_normalize_points(group, wnaf[0], wnaf[1], ctx))
3512380456eStb 		goto err;
3522380456eStb 
3532380456eStb 	num_digits = wnaf[0]->num_digits;
3542380456eStb 	if (wnaf[1]->num_digits > num_digits)
3552380456eStb 		num_digits = wnaf[1]->num_digits;
356da347917Sbeck 
35732080735Stb 	/*
35832080735Stb 	 * Set r to the neutral element. Scan through the wNAF representations
35932080735Stb 	 * of m and n, starting at the most significant digit. Double r and for
3600716b503Stb 	 * each wNAF digit of m add the digit times the generator, and for each
3610716b503Stb 	 * wNAF digit of n add the digit times the point, adjusting the signs
3620716b503Stb 	 * as appropriate.
36332080735Stb 	 */
36432080735Stb 
36532080735Stb 	if (!EC_POINT_set_to_infinity(group, r))
36632080735Stb 		goto err;
367da347917Sbeck 
3682380456eStb 	for (k = num_digits - 1; k >= 0; k--) {
369f67ac449Stedu 		if (!EC_POINT_dbl(group, r, r, ctx))
370f67ac449Stedu 			goto err;
37132080735Stb 
3724b70f32aStb 		for (i = 0; i < 2; i++) {
3732380456eStb 			const EC_POINT *multiple;
3742380456eStb 			signed char digit;
375b6d9506bStb 			int is_neg = 0;
376da347917Sbeck 
3772380456eStb 			if ((digit = ec_wnaf_digit(wnaf[i], k)) == 0)
378b6d9506bStb 				continue;
379b6d9506bStb 
380b6d9506bStb 			if (digit < 0) {
381b6d9506bStb 				is_neg = 1;
38223230adbStb 				digit = -digit;
383b6d9506bStb 			}
384da347917Sbeck 
385f67ac449Stedu 			if (is_neg != r_is_inverted) {
386f67ac449Stedu 				if (!EC_POINT_invert(group, r, ctx))
387f67ac449Stedu 					goto err;
388da347917Sbeck 				r_is_inverted = !r_is_inverted;
389da347917Sbeck 			}
390da347917Sbeck 
3912380456eStb 			if ((multiple = ec_wnaf_multiple(wnaf[i], digit)) == NULL)
3922380456eStb 				goto err;
3932380456eStb 
3942380456eStb 			if (!EC_POINT_add(group, r, r, multiple, ctx))
395f67ac449Stedu 				goto err;
396da347917Sbeck 		}
397da347917Sbeck 	}
398da347917Sbeck 
39932080735Stb 	if (r_is_inverted) {
400f67ac449Stedu 		if (!EC_POINT_invert(group, r, ctx))
401f67ac449Stedu 			goto err;
402da347917Sbeck 	}
403da347917Sbeck 
404da347917Sbeck 	ret = 1;
405da347917Sbeck 
406da347917Sbeck  err:
4072380456eStb 	ec_wnaf_free(wnaf[0]);
4082380456eStb 	ec_wnaf_free(wnaf[1]);
40971532873Stb 
410da347917Sbeck 	return ret;
411da347917Sbeck }
412