1/* $OpenBSD: ec_mult.c,v 1.31 2023/06/24 17:49:44 jsing Exp $ */
2/*
3 * Originally written by Bodo Moeller and Nils Larsch for the OpenSSL project.
4 */
5/* ====================================================================
6 * Copyright (c) 1998-2007 The OpenSSL Project.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in
17 *    the documentation and/or other materials provided with the
18 *    distribution.
19 *
20 * 3. All advertising materials mentioning features or use of this
21 *    software must display the following acknowledgment:
22 *    "This product includes software developed by the OpenSSL Project
23 *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
24 *
25 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
26 *    endorse or promote products derived from this software without
27 *    prior written permission. For written permission, please contact
28 *    openssl-core@openssl.org.
29 *
30 * 5. Products derived from this software may not be called "OpenSSL"
31 *    nor may "OpenSSL" appear in their names without prior written
32 *    permission of the OpenSSL Project.
33 *
34 * 6. Redistributions of any form whatsoever must retain the following
35 *    acknowledgment:
36 *    "This product includes software developed by the OpenSSL Project
37 *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
38 *
39 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
40 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
43 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
45 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
48 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
50 * OF THE POSSIBILITY OF SUCH DAMAGE.
51 * ====================================================================
52 *
53 * This product includes cryptographic software written by Eric Young
54 * (eay@cryptsoft.com).  This product includes software written by Tim
55 * Hudson (tjh@cryptsoft.com).
56 *
57 */
58/* ====================================================================
59 * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
60 * Portions of this software developed by SUN MICROSYSTEMS, INC.,
61 * and contributed to the OpenSSL project.
62 */
63
64#include <string.h>
65
66#include <openssl/err.h>
67
68#include "ec_local.h"
69
70/*
71 * This file implements the wNAF-based interleaving multi-exponentation method
72 * (<URL:http://www.informatik.tu-darmstadt.de/TI/Mitarbeiter/moeller.html#multiexp>);
73 * for multiplication with precomputation, we use wNAF splitting
74 * (<URL:http://www.informatik.tu-darmstadt.de/TI/Mitarbeiter/moeller.html#fastexp>).
75 */
76
77/* Determine the modified width-(w+1) Non-Adjacent Form (wNAF) of 'scalar'.
78 * This is an array  r[]  of values that are either zero or odd with an
79 * absolute value less than  2^w  satisfying
80 *     scalar = \sum_j r[j]*2^j
81 * where at most one of any  w+1  consecutive digits is non-zero
82 * with the exception that the most significant digit may be only
83 * w-1 zeros away from that next non-zero digit.
84 */
85static signed char *
86compute_wNAF(const BIGNUM *scalar, int w, size_t *ret_len)
87{
88	int window_val;
89	int ok = 0;
90	signed char *r = NULL;
91	int sign = 1;
92	int bit, next_bit, mask;
93	size_t len = 0, j;
94
95	if (BN_is_zero(scalar)) {
96		r = malloc(1);
97		if (!r) {
98			ECerror(ERR_R_MALLOC_FAILURE);
99			goto err;
100		}
101		r[0] = 0;
102		*ret_len = 1;
103		return r;
104	}
105	if (w <= 0 || w > 7) {
106		/* 'signed char' can represent integers with
107		 * absolute values less than 2^7 */
108		ECerror(ERR_R_INTERNAL_ERROR);
109		goto err;
110	}
111	bit = 1 << w;		/* at most 128 */
112	next_bit = bit << 1;	/* at most 256 */
113	mask = next_bit - 1;	/* at most 255 */
114
115	if (BN_is_negative(scalar)) {
116		sign = -1;
117	}
118	if (scalar->d == NULL || scalar->top == 0) {
119		ECerror(ERR_R_INTERNAL_ERROR);
120		goto err;
121	}
122	len = BN_num_bits(scalar);
123	r = malloc(len + 1);	/* modified wNAF may be one digit longer than
124				 * binary representation (*ret_len will be
125				 * set to the actual length, i.e. at most
126				 * BN_num_bits(scalar) + 1) */
127	if (r == NULL) {
128		ECerror(ERR_R_MALLOC_FAILURE);
129		goto err;
130	}
131	window_val = scalar->d[0] & mask;
132	j = 0;
133	while ((window_val != 0) || (j + w + 1 < len)) {
134		/* if j+w+1 >= len, window_val will not increase */
135		int digit = 0;
136
137		/* 0 <= window_val <= 2^(w+1) */
138		if (window_val & 1) {
139			/* 0 < window_val < 2^(w+1) */
140			if (window_val & bit) {
141				digit = window_val - next_bit;	/* -2^w < digit < 0 */
142
143#if 1				/* modified wNAF */
144				if (j + w + 1 >= len) {
145					/*
146					 * special case for generating
147					 * modified wNAFs: no new bits will
148					 * be added into window_val, so using
149					 * a positive digit here will
150					 * decrease the total length of the
151					 * representation
152					 */
153
154					digit = window_val & (mask >> 1);	/* 0 < digit < 2^w */
155				}
156#endif
157			} else {
158				digit = window_val;	/* 0 < digit < 2^w */
159			}
160
161			if (digit <= -bit || digit >= bit || !(digit & 1)) {
162				ECerror(ERR_R_INTERNAL_ERROR);
163				goto err;
164			}
165			window_val -= digit;
166
167			/*
168			 * now window_val is 0 or 2^(w+1) in standard wNAF
169			 * generation; for modified window NAFs, it may also
170			 * be 2^w
171			 */
172			if (window_val != 0 && window_val != next_bit && window_val != bit) {
173				ECerror(ERR_R_INTERNAL_ERROR);
174				goto err;
175			}
176		}
177		r[j++] = sign * digit;
178
179		window_val >>= 1;
180		window_val += bit * BN_is_bit_set(scalar, j + w);
181
182		if (window_val > next_bit) {
183			ECerror(ERR_R_INTERNAL_ERROR);
184			goto err;
185		}
186	}
187
188	if (j > len + 1) {
189		ECerror(ERR_R_INTERNAL_ERROR);
190		goto err;
191	}
192	len = j;
193	ok = 1;
194
195 err:
196	if (!ok) {
197		free(r);
198		r = NULL;
199	}
200	if (ok)
201		*ret_len = len;
202	return r;
203}
204
205
206/* TODO: table should be optimised for the wNAF-based implementation,
207 *       sometimes smaller windows will give better performance
208 *       (thus the boundaries should be increased)
209 */
210#define EC_window_bits_for_scalar_size(b) \
211		((size_t) \
212		 ((b) >= 2000 ? 6 : \
213		  (b) >=  800 ? 5 : \
214		  (b) >=  300 ? 4 : \
215		  (b) >=   70 ? 3 : \
216		  (b) >=   20 ? 2 : \
217		  1))
218
219/* Compute
220 *      \sum scalars[i]*points[i],
221 * also including
222 *      scalar*generator
223 * in the addition if scalar != NULL
224 */
225int
226ec_wNAF_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *scalar,
227    size_t num, const EC_POINT *points[], const BIGNUM *scalars[], BN_CTX *ctx)
228{
229	const EC_POINT *generator = NULL;
230	EC_POINT *tmp = NULL;
231	size_t totalnum;
232	size_t numblocks = 0;	/* for wNAF splitting */
233	size_t i, j;
234	int k;
235	int r_is_inverted = 0;
236	int r_is_at_infinity = 1;
237	size_t *wsize = NULL;	/* individual window sizes */
238	signed char **wNAF = NULL;	/* individual wNAFs */
239	signed char *tmp_wNAF = NULL;
240	size_t *wNAF_len = NULL;
241	size_t max_len = 0;
242	size_t num_val;
243	EC_POINT **val = NULL;	/* precomputation */
244	EC_POINT **v;
245	EC_POINT ***val_sub = NULL;	/* pointers to sub-arrays of 'val' or
246					 * 'pre_comp->points' */
247	int num_scalar = 0;	/* flag: will be set to 1 if 'scalar' must be
248				 * treated like other scalars, i.e.
249				 * precomputation is not available */
250	int ret = 0;
251
252	if (group->meth != r->meth) {
253		ECerror(EC_R_INCOMPATIBLE_OBJECTS);
254		return 0;
255	}
256	if ((scalar == NULL) && (num == 0)) {
257		return EC_POINT_set_to_infinity(group, r);
258	}
259	for (i = 0; i < num; i++) {
260		if (group->meth != points[i]->meth) {
261			ECerror(EC_R_INCOMPATIBLE_OBJECTS);
262			return 0;
263		}
264	}
265
266	if (scalar != NULL) {
267		generator = EC_GROUP_get0_generator(group);
268		if (generator == NULL) {
269			ECerror(EC_R_UNDEFINED_GENERATOR);
270			goto err;
271		}
272
273		numblocks = 1;
274		num_scalar = 1;	/* treat 'scalar' like 'num'-th
275				 * element of 'scalars' */
276	}
277	totalnum = num + numblocks;
278
279	/* includes space for pivot */
280	wNAF = reallocarray(NULL, (totalnum + 1), sizeof wNAF[0]);
281	if (wNAF == NULL) {
282		ECerror(ERR_R_MALLOC_FAILURE);
283		goto err;
284	}
285
286	wNAF[0] = NULL;		/* preliminary pivot */
287
288	wsize = reallocarray(NULL, totalnum, sizeof wsize[0]);
289	wNAF_len = reallocarray(NULL, totalnum, sizeof wNAF_len[0]);
290	val_sub = reallocarray(NULL, totalnum, sizeof val_sub[0]);
291
292	if (wsize == NULL || wNAF_len == NULL || val_sub == NULL) {
293		ECerror(ERR_R_MALLOC_FAILURE);
294		goto err;
295	}
296
297	/* num_val will be the total number of temporarily precomputed points */
298	num_val = 0;
299
300	for (i = 0; i < num + num_scalar; i++) {
301		size_t bits;
302
303		bits = i < num ? BN_num_bits(scalars[i]) : BN_num_bits(scalar);
304		wsize[i] = EC_window_bits_for_scalar_size(bits);
305		num_val += (size_t) 1 << (wsize[i] - 1);
306		wNAF[i + 1] = NULL;	/* make sure we always have a pivot */
307		wNAF[i] = compute_wNAF((i < num ? scalars[i] : scalar), wsize[i], &wNAF_len[i]);
308		if (wNAF[i] == NULL)
309			goto err;
310		if (wNAF_len[i] > max_len)
311			max_len = wNAF_len[i];
312	}
313
314	if (numblocks) {
315		/* we go here iff scalar != NULL */
316
317		if (num_scalar != 1) {
318			ECerror(ERR_R_INTERNAL_ERROR);
319			goto err;
320		}
321	}
322	/*
323	 * All points we precompute now go into a single array 'val'.
324	 * 'val_sub[i]' is a pointer to the subarray for the i-th point, or
325	 * to a subarray of 'pre_comp->points' if we already have
326	 * precomputation.
327	 */
328	val = reallocarray(NULL, (num_val + 1), sizeof val[0]);
329	if (val == NULL) {
330		ECerror(ERR_R_MALLOC_FAILURE);
331		goto err;
332	}
333	val[num_val] = NULL;	/* pivot element */
334
335	/* allocate points for precomputation */
336	v = val;
337	for (i = 0; i < num + num_scalar; i++) {
338		val_sub[i] = v;
339		for (j = 0; j < ((size_t) 1 << (wsize[i] - 1)); j++) {
340			*v = EC_POINT_new(group);
341			if (*v == NULL)
342				goto err;
343			v++;
344		}
345	}
346	if (!(v == val + num_val)) {
347		ECerror(ERR_R_INTERNAL_ERROR);
348		goto err;
349	}
350	if (!(tmp = EC_POINT_new(group)))
351		goto err;
352
353	/*
354	 * prepare precomputed values: val_sub[i][0] :=     points[i]
355	 * val_sub[i][1] := 3 * points[i] val_sub[i][2] := 5 * points[i] ...
356	 */
357	for (i = 0; i < num + num_scalar; i++) {
358		if (i < num) {
359			if (!EC_POINT_copy(val_sub[i][0], points[i]))
360				goto err;
361		} else {
362			if (!EC_POINT_copy(val_sub[i][0], generator))
363				goto err;
364		}
365
366		if (wsize[i] > 1) {
367			if (!EC_POINT_dbl(group, tmp, val_sub[i][0], ctx))
368				goto err;
369			for (j = 1; j < ((size_t) 1 << (wsize[i] - 1)); j++) {
370				if (!EC_POINT_add(group, val_sub[i][j], val_sub[i][j - 1], tmp, ctx))
371					goto err;
372			}
373		}
374	}
375
376	if (!EC_POINTs_make_affine(group, num_val, val, ctx))
377		goto err;
378
379	r_is_at_infinity = 1;
380
381	for (k = max_len - 1; k >= 0; k--) {
382		if (!r_is_at_infinity) {
383			if (!EC_POINT_dbl(group, r, r, ctx))
384				goto err;
385		}
386		for (i = 0; i < totalnum; i++) {
387			if (wNAF_len[i] > (size_t) k) {
388				int digit = wNAF[i][k];
389				int is_neg;
390
391				if (digit) {
392					is_neg = digit < 0;
393
394					if (is_neg)
395						digit = -digit;
396
397					if (is_neg != r_is_inverted) {
398						if (!r_is_at_infinity) {
399							if (!EC_POINT_invert(group, r, ctx))
400								goto err;
401						}
402						r_is_inverted = !r_is_inverted;
403					}
404					/* digit > 0 */
405
406					if (r_is_at_infinity) {
407						if (!EC_POINT_copy(r, val_sub[i][digit >> 1]))
408							goto err;
409						r_is_at_infinity = 0;
410					} else {
411						if (!EC_POINT_add(group, r, r, val_sub[i][digit >> 1], ctx))
412							goto err;
413					}
414				}
415			}
416		}
417	}
418
419	if (r_is_at_infinity) {
420		if (!EC_POINT_set_to_infinity(group, r))
421			goto err;
422	} else {
423		if (r_is_inverted)
424			if (!EC_POINT_invert(group, r, ctx))
425				goto err;
426	}
427
428	ret = 1;
429
430 err:
431	EC_POINT_free(tmp);
432	free(wsize);
433	free(wNAF_len);
434	free(tmp_wNAF);
435	if (wNAF != NULL) {
436		signed char **w;
437
438		for (w = wNAF; *w != NULL; w++)
439			free(*w);
440
441		free(wNAF);
442	}
443	if (val != NULL) {
444		for (v = val; *v != NULL; v++)
445			EC_POINT_free(*v);
446		free(val);
447	}
448	free(val_sub);
449	return ret;
450}
451