xref: /openbsd-src/lib/libcrypto/ec/ec_mult.c (revision 1ad61ae0a79a724d2d3ec69e69c8e1d1ff6b53a0)
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  */
85 static signed char *
86 compute_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  */
225 int
226 ec_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