xref: /freebsd/crypto/libecc/src/nn/nn_add.c (revision f0865ec9906d5a18fa2a3b61381f22ce16e606ad)
1*f0865ec9SKyle Evans /*
2*f0865ec9SKyle Evans  *  Copyright (C) 2017 - This file is part of libecc project
3*f0865ec9SKyle Evans  *
4*f0865ec9SKyle Evans  *  Authors:
5*f0865ec9SKyle Evans  *      Ryad BENADJILA <ryadbenadjila@gmail.com>
6*f0865ec9SKyle Evans  *      Arnaud EBALARD <arnaud.ebalard@ssi.gouv.fr>
7*f0865ec9SKyle Evans  *      Jean-Pierre FLORI <jean-pierre.flori@ssi.gouv.fr>
8*f0865ec9SKyle Evans  *
9*f0865ec9SKyle Evans  *  Contributors:
10*f0865ec9SKyle Evans  *      Nicolas VIVET <nicolas.vivet@ssi.gouv.fr>
11*f0865ec9SKyle Evans  *      Karim KHALFALLAH <karim.khalfallah@ssi.gouv.fr>
12*f0865ec9SKyle Evans  *
13*f0865ec9SKyle Evans  *  This software is licensed under a dual BSD and GPL v2 license.
14*f0865ec9SKyle Evans  *  See LICENSE file at the root folder of the project.
15*f0865ec9SKyle Evans  */
16*f0865ec9SKyle Evans #include <libecc/nn/nn_add.h>
17*f0865ec9SKyle Evans #include <libecc/nn/nn.h>
18*f0865ec9SKyle Evans 
19*f0865ec9SKyle Evans /*
20*f0865ec9SKyle Evans  * This module provides conditional addition and subtraction functions between
21*f0865ec9SKyle Evans  * two nn:
22*f0865ec9SKyle Evans  *
23*f0865ec9SKyle Evans  *  o out = in1 +/- in2 if cnd is not zero.
24*f0865ec9SKyle Evans  *  o out = in1 if cnd is zero.
25*f0865ec9SKyle Evans  *
26*f0865ec9SKyle Evans  * The time taken by the operation does not depend on cnd value, i.e. it is
27*f0865ec9SKyle Evans  * constant time for that specific factor, nor on the values of in1 and in2.
28*f0865ec9SKyle Evans  * It still depends on the maximal length of in1 and in2.
29*f0865ec9SKyle Evans  *
30*f0865ec9SKyle Evans  * Common addition and subtraction functions are derived from those conditional
31*f0865ec9SKyle Evans  * versions.
32*f0865ec9SKyle Evans  */
33*f0865ec9SKyle Evans 
34*f0865ec9SKyle Evans /*
35*f0865ec9SKyle Evans  * Conditionally adds 'in2' to 'in1' according to "cnd", storing the result
36*f0865ec9SKyle Evans  * in "out" and returning the carry in 'carry' parameter on success. This
37*f0865ec9SKyle Evans  * is the lowest level function for conditional addition. The function
38*f0865ec9SKyle Evans  * returns 0 on success, -1 on error.
39*f0865ec9SKyle Evans  *
40*f0865ec9SKyle Evans  * Note that unlike "usual" addition, the function is *in general* not
41*f0865ec9SKyle Evans  * commutative, i.e. "_nn_cnd_add(cnd, out, in1, in2)"  is not equivalent
42*f0865ec9SKyle Evans  * to "_nn_cnd_add(cnd, out, in2, in1)". It is commutative though if "cnd"
43*f0865ec9SKyle Evans  * is not zero or 'in1' == 'in2'.
44*f0865ec9SKyle Evans  *
45*f0865ec9SKyle Evans  * Aliasing of inputs and output is possible. "out" is initialized if needed,
46*f0865ec9SKyle Evans  * that is if not aliased to 'in1' or 'in2'. The length of "out" is set to
47*f0865ec9SKyle Evans  * the maximal length of 'in1' and 'in2'. Note that both 'in1' and 'in2' will
48*f0865ec9SKyle Evans  * be read to this maximal length. As our memory managment model assumes that
49*f0865ec9SKyle Evans  * storage arrays only contains zeros past the "wlen" index, correct results
50*f0865ec9SKyle Evans  * will be produced. The length of 'out' is not normalized on return.
51*f0865ec9SKyle Evans  *
52*f0865ec9SKyle Evans  * The runtime of this function should not depend on:
53*f0865ec9SKyle Evans  *  o the value of "cnd",
54*f0865ec9SKyle Evans  *  o the data stored in 'in1' and 'in2'.
55*f0865ec9SKyle Evans  * It depends on:
56*f0865ec9SKyle Evans  *  o the maximal length of 'in1' and 'in2'.
57*f0865ec9SKyle Evans  *
58*f0865ec9SKyle Evans  * This function is for internal use only.
59*f0865ec9SKyle Evans  */
_nn_cnd_add(int cnd,nn_t out,nn_src_t in1,nn_src_t in2,word_t * carry)60*f0865ec9SKyle Evans ATTRIBUTE_WARN_UNUSED_RET static int _nn_cnd_add(int cnd, nn_t out, nn_src_t in1, nn_src_t in2,
61*f0865ec9SKyle Evans 		       word_t *carry)
62*f0865ec9SKyle Evans {
63*f0865ec9SKyle Evans 	word_t tmp, carry1, carry2, _carry = WORD(0);
64*f0865ec9SKyle Evans 	word_t mask = WORD_MASK_IFNOTZERO(cnd);
65*f0865ec9SKyle Evans 	u8 i, loop_wlen;
66*f0865ec9SKyle Evans 	int ret;
67*f0865ec9SKyle Evans 
68*f0865ec9SKyle Evans 	MUST_HAVE((carry != NULL), ret, err);
69*f0865ec9SKyle Evans 	ret = nn_check_initialized(in1); EG(ret, err);
70*f0865ec9SKyle Evans 	ret = nn_check_initialized(in2); EG(ret, err);
71*f0865ec9SKyle Evans 
72*f0865ec9SKyle Evans 	/* Handle aliasing */
73*f0865ec9SKyle Evans 	loop_wlen = LOCAL_MAX(in1->wlen, in2->wlen);
74*f0865ec9SKyle Evans 	if ((out != in1) && (out != in2)) {
75*f0865ec9SKyle Evans 		ret = nn_init(out, (u16)(loop_wlen * WORD_BYTES)); EG(ret, err);
76*f0865ec9SKyle Evans 	} else {
77*f0865ec9SKyle Evans 		ret = nn_set_wlen(out, loop_wlen); EG(ret, err);
78*f0865ec9SKyle Evans 	}
79*f0865ec9SKyle Evans 
80*f0865ec9SKyle Evans 	/* Perform addition one word at a time, propagating the carry. */
81*f0865ec9SKyle Evans 	for (i = 0; i < loop_wlen; i++) {
82*f0865ec9SKyle Evans 		tmp = (word_t)(in1->val[i] + (in2->val[i] & mask));
83*f0865ec9SKyle Evans 		carry1 = (word_t)(tmp < in1->val[i]);
84*f0865ec9SKyle Evans 		out->val[i] = (word_t)(tmp + _carry);
85*f0865ec9SKyle Evans 		carry2 = (word_t)(out->val[i] < tmp);
86*f0865ec9SKyle Evans 		/* There is at most one carry going out. */
87*f0865ec9SKyle Evans 		_carry = (word_t)(carry1 | carry2);
88*f0865ec9SKyle Evans 	}
89*f0865ec9SKyle Evans 
90*f0865ec9SKyle Evans 	(*carry) = _carry;
91*f0865ec9SKyle Evans 
92*f0865ec9SKyle Evans err:
93*f0865ec9SKyle Evans 	return ret;
94*f0865ec9SKyle Evans }
95*f0865ec9SKyle Evans 
96*f0865ec9SKyle Evans /*
97*f0865ec9SKyle Evans  * Conditionally adds 'in2' to 'in1' according to "cnd", storing the result
98*f0865ec9SKyle Evans  * in "out", including the potential carry overflowing past the maximal
99*f0865ec9SKyle Evans  * length of 'in1' and 'in2'. It is user responsibility to ensure that the
100*f0865ec9SKyle Evans  * resulting nn will not be higher than what can be supported. This is
101*f0865ec9SKyle Evans  * for instance guaranteed if both in1->wlen and in2->wlen are less than
102*f0865ec9SKyle Evans  * NN_MAX_WORD_LEN. Otherwise the function will error out which could leak
103*f0865ec9SKyle Evans  * information.
104*f0865ec9SKyle Evans  *
105*f0865ec9SKyle Evans  * Note that the length of the output depends the lengths of the inputs,
106*f0865ec9SKyle Evans  * but also on their values.
107*f0865ec9SKyle Evans  * It is the user responsibility to use this function carefully when
108*f0865ec9SKyle Evans  * constant time of an algorithm using this function is seeked.
109*f0865ec9SKyle Evans  * This choice was preferred above unconditionally increasing
110*f0865ec9SKyle Evans  * the length of the output by one, to ease the management of length
111*f0865ec9SKyle Evans  * explosion when multiple additions are performed.
112*f0865ec9SKyle Evans  * For finer carry propagation and length control the internal "_nn_cnd_add"
113*f0865ec9SKyle Evans  * function can be used.
114*f0865ec9SKyle Evans  *
115*f0865ec9SKyle Evans  * See "_nn_cnd_add" documentation above for further details.
116*f0865ec9SKyle Evans  *
117*f0865ec9SKyle Evans  * The function returns 0 on success, -1 on error.
118*f0865ec9SKyle Evans  */
nn_cnd_add(int cnd,nn_t out,nn_src_t in1,nn_src_t in2)119*f0865ec9SKyle Evans int nn_cnd_add(int cnd, nn_t out, nn_src_t in1, nn_src_t in2)
120*f0865ec9SKyle Evans {
121*f0865ec9SKyle Evans 	word_t carry;
122*f0865ec9SKyle Evans 	int ret;
123*f0865ec9SKyle Evans 
124*f0865ec9SKyle Evans 	ret = _nn_cnd_add(cnd, out, in1, in2, &carry); EG(ret, err);
125*f0865ec9SKyle Evans 
126*f0865ec9SKyle Evans 	/* We cannot allow a non-zero carry if out->wlen is at its limit */
127*f0865ec9SKyle Evans 	MUST_HAVE(((out->wlen != NN_MAX_WORD_LEN) || (!carry)), ret, err);
128*f0865ec9SKyle Evans 
129*f0865ec9SKyle Evans 	if (out->wlen != NN_MAX_WORD_LEN) {
130*f0865ec9SKyle Evans 		/*
131*f0865ec9SKyle Evans 		 * To maintain constant time, we perform carry addition in all
132*f0865ec9SKyle Evans 		 * cases. If carry is 0, no change is performed in practice,
133*f0865ec9SKyle Evans 		 * neither to 'out' value, nor to its length.
134*f0865ec9SKyle Evans 		 * Note that the length of the output can vary and make
135*f0865ec9SKyle Evans 		 * the time taken by further operations on it also vary.
136*f0865ec9SKyle Evans 		 */
137*f0865ec9SKyle Evans 		out->val[out->wlen] = carry;
138*f0865ec9SKyle Evans 		out->wlen = (u8)(out->wlen + carry);
139*f0865ec9SKyle Evans 	}
140*f0865ec9SKyle Evans 
141*f0865ec9SKyle Evans err:
142*f0865ec9SKyle Evans 	return ret;
143*f0865ec9SKyle Evans }
144*f0865ec9SKyle Evans 
145*f0865ec9SKyle Evans /*
146*f0865ec9SKyle Evans  * Unconditionally adds 'in2' to 'in1', storing the result in "out",
147*f0865ec9SKyle Evans  * including the potential carry overflowing past the maximal length of
148*f0865ec9SKyle Evans  * 'in1' and 'in2'. The function returns 0 on success, -1 on error.
149*f0865ec9SKyle Evans  *
150*f0865ec9SKyle Evans  * Note that the length of the output depends the lengths of the inputs,
151*f0865ec9SKyle Evans  * but also on their values.
152*f0865ec9SKyle Evans  * It is the user responsibility to use this function carefully when
153*f0865ec9SKyle Evans  * constant time of an algorithm using this function is seeked.
154*f0865ec9SKyle Evans  *
155*f0865ec9SKyle Evans  * See "_nn_cnd_add" documentation for further details.
156*f0865ec9SKyle Evans  *
157*f0865ec9SKyle Evans  */
nn_add(nn_t out,nn_src_t in1,nn_src_t in2)158*f0865ec9SKyle Evans int nn_add(nn_t out, nn_src_t in1, nn_src_t in2)
159*f0865ec9SKyle Evans {
160*f0865ec9SKyle Evans 	return nn_cnd_add(1, out, in1, in2);
161*f0865ec9SKyle Evans }
162*f0865ec9SKyle Evans 
163*f0865ec9SKyle Evans /*
164*f0865ec9SKyle Evans  * Compute out = in1 + w where 'in1' is an initialized nn and 'w' a word. It is
165*f0865ec9SKyle Evans  * caller responsibility to ensure that the result will fit in a nn (This is
166*f0865ec9SKyle Evans  * for instance guaranteed if 'in1' wlen is less than NN_MAX_WORD_LEN). The
167*f0865ec9SKyle Evans  * function returns 0 on succes, -1 on error.
168*f0865ec9SKyle Evans  *
169*f0865ec9SKyle Evans  * The result is stored in 'out' parameter. 'out' is initialized if needed (i.e.
170*f0865ec9SKyle Evans  * in case aliasing is not used) and is not normalized on return.
171*f0865ec9SKyle Evans  *
172*f0865ec9SKyle Evans  * Note that the length of the output depends the lengths of the inputs,
173*f0865ec9SKyle Evans  * but also on their values.
174*f0865ec9SKyle Evans  * It is the user responsibility to use this function carefully when
175*f0865ec9SKyle Evans  * constant time of an algorithm using this function is seeked.
176*f0865ec9SKyle Evans  *
177*f0865ec9SKyle Evans  * This function is for internal use only.
178*f0865ec9SKyle Evans  */
nn_add_word(nn_t out,nn_src_t in1,word_t w)179*f0865ec9SKyle Evans ATTRIBUTE_WARN_UNUSED_RET static int nn_add_word(nn_t out, nn_src_t in1, word_t w)
180*f0865ec9SKyle Evans {
181*f0865ec9SKyle Evans 	word_t carry, tmp;
182*f0865ec9SKyle Evans 	u8 i, n_wlen;
183*f0865ec9SKyle Evans 	int ret;
184*f0865ec9SKyle Evans 
185*f0865ec9SKyle Evans 	ret = nn_check_initialized(in1); EG(ret, err);
186*f0865ec9SKyle Evans 
187*f0865ec9SKyle Evans 	/* Handle aliasing */
188*f0865ec9SKyle Evans 	n_wlen = in1->wlen;
189*f0865ec9SKyle Evans 	if (out != in1) {
190*f0865ec9SKyle Evans 		ret = nn_init(out, (u16)(n_wlen * WORD_BYTES)); EG(ret, err);
191*f0865ec9SKyle Evans 	} else {
192*f0865ec9SKyle Evans 		ret = nn_set_wlen(out, n_wlen); EG(ret, err);
193*f0865ec9SKyle Evans 	}
194*f0865ec9SKyle Evans 
195*f0865ec9SKyle Evans 	/* No matter its value, propagate the carry. */
196*f0865ec9SKyle Evans 	carry = w;
197*f0865ec9SKyle Evans 	for (i = 0; i < n_wlen; i++) {
198*f0865ec9SKyle Evans 		tmp = (word_t)(in1->val[i] + carry);
199*f0865ec9SKyle Evans 		carry = (word_t)(tmp < in1->val[i]);
200*f0865ec9SKyle Evans 		out->val[i] = tmp;
201*f0865ec9SKyle Evans 	}
202*f0865ec9SKyle Evans 
203*f0865ec9SKyle Evans 	MUST_HAVE(((out->wlen != NN_MAX_WORD_LEN) || (!carry)), ret, err);
204*f0865ec9SKyle Evans 	if (out->wlen != NN_MAX_WORD_LEN) {
205*f0865ec9SKyle Evans 		/*
206*f0865ec9SKyle Evans 		 * To maintain constant time, we perform carry addition in all
207*f0865ec9SKyle Evans 		 * cases. If carry is 0, no change is performed in practice,
208*f0865ec9SKyle Evans 		 * neither to 'out' value, nor to its length.
209*f0865ec9SKyle Evans 		 * Note that the length of the output can vary and make
210*f0865ec9SKyle Evans 		 * the time taken by further operations on it will vary.
211*f0865ec9SKyle Evans 		 */
212*f0865ec9SKyle Evans 		out->val[out->wlen] = carry;
213*f0865ec9SKyle Evans 		out->wlen = (u8)(out->wlen + carry);
214*f0865ec9SKyle Evans 	}
215*f0865ec9SKyle Evans 
216*f0865ec9SKyle Evans err:
217*f0865ec9SKyle Evans 	return ret;
218*f0865ec9SKyle Evans }
219*f0865ec9SKyle Evans 
220*f0865ec9SKyle Evans /*
221*f0865ec9SKyle Evans  * Compute out = in1 + 1. Aliasing is supported i.e. nn_inc(in1, in1) works as
222*f0865ec9SKyle Evans  * expected and provides in1++. It is caller responsibility to ensure that the
223*f0865ec9SKyle Evans  * result will fit in a nn (This is for instance guaranteed if 'in1' wlen is
224*f0865ec9SKyle Evans  * less than NN_MAX_WORD_LEN). The function returns 0 on success, -1 on error.
225*f0865ec9SKyle Evans  *
226*f0865ec9SKyle Evans  * Note that the length of the output depends the lengths of the inputs,
227*f0865ec9SKyle Evans  * but also on their values.
228*f0865ec9SKyle Evans  * It is the user responsibility to use this function carefully when
229*f0865ec9SKyle Evans  * constant time of an algorithm using this function is seeked.
230*f0865ec9SKyle Evans  */
nn_inc(nn_t out,nn_src_t in1)231*f0865ec9SKyle Evans int nn_inc(nn_t out, nn_src_t in1)
232*f0865ec9SKyle Evans {
233*f0865ec9SKyle Evans 	return nn_add_word(out, in1, WORD(1));
234*f0865ec9SKyle Evans }
235*f0865ec9SKyle Evans 
236*f0865ec9SKyle Evans /*
237*f0865ec9SKyle Evans  * Conditionally subtracts 'in2' from 'in1' according to "cnd",
238*f0865ec9SKyle Evans  * storing the result in "out":
239*f0865ec9SKyle Evans  *  o out = in1 - in2 if cnd is not zero.
240*f0865ec9SKyle Evans  *  o out = in1 if cnd is zero.
241*f0865ec9SKyle Evans  *
242*f0865ec9SKyle Evans  * 'in1' and 'in2' must point to initialized nn, such that the value of 'in1'
243*f0865ec9SKyle Evans  * is larger than 'in2'. Aliasing is supported, i.e. 'out' can point to the
244*f0865ec9SKyle Evans  * same nn as 'in1' or 'in2'. If aliasing is not used, 'out' is initialized by
245*f0865ec9SKyle Evans  * the function. The length of 'out' is set to the length of 'in1'
246*f0865ec9SKyle Evans  * and is not normalized on return.
247*f0865ec9SKyle Evans  *
248*f0865ec9SKyle Evans  * The function returns 0 on success, -1 on error.
249*f0865ec9SKyle Evans  */
nn_cnd_sub(int cnd,nn_t out,nn_src_t in1,nn_src_t in2)250*f0865ec9SKyle Evans int nn_cnd_sub(int cnd, nn_t out, nn_src_t in1, nn_src_t in2)
251*f0865ec9SKyle Evans {
252*f0865ec9SKyle Evans 	word_t tmp, borrow1, borrow2, borrow = WORD(0);
253*f0865ec9SKyle Evans 	word_t mask = WORD_MASK_IFNOTZERO(cnd);
254*f0865ec9SKyle Evans 	u8 loop_wlen, i;
255*f0865ec9SKyle Evans 	int ret;
256*f0865ec9SKyle Evans 
257*f0865ec9SKyle Evans 	ret = nn_check_initialized(in1); EG(ret, err);
258*f0865ec9SKyle Evans 	ret = nn_check_initialized(in2); EG(ret, err);
259*f0865ec9SKyle Evans 
260*f0865ec9SKyle Evans 	/* Handle aliasing */
261*f0865ec9SKyle Evans 	loop_wlen = LOCAL_MAX(in1->wlen, in2->wlen);
262*f0865ec9SKyle Evans 	if ((out != in1) && (out != in2)) {
263*f0865ec9SKyle Evans 		ret = nn_init(out, (u16)(loop_wlen * WORD_BYTES)); EG(ret, err);
264*f0865ec9SKyle Evans 	} else {
265*f0865ec9SKyle Evans 		ret = nn_set_wlen(out, in1->wlen); EG(ret, err);
266*f0865ec9SKyle Evans 	}
267*f0865ec9SKyle Evans 
268*f0865ec9SKyle Evans 	/* Perform subtraction one word at a time, propagating the borrow. */
269*f0865ec9SKyle Evans 	for (i = 0; i < loop_wlen; i++) {
270*f0865ec9SKyle Evans 		tmp = (word_t)(in1->val[i] - (in2->val[i] & mask));
271*f0865ec9SKyle Evans 		borrow1 = (word_t)(tmp > in1->val[i]);
272*f0865ec9SKyle Evans 		out->val[i] = (word_t)(tmp - borrow);
273*f0865ec9SKyle Evans 		borrow2 = (word_t)(out->val[i] > tmp);
274*f0865ec9SKyle Evans 		/* There is at most one borrow going out. */
275*f0865ec9SKyle Evans 		borrow = (word_t)(borrow1 | borrow2);
276*f0865ec9SKyle Evans 	}
277*f0865ec9SKyle Evans 
278*f0865ec9SKyle Evans 	/* We only support the in1 >= in2 case */
279*f0865ec9SKyle Evans 	ret = (borrow != WORD(0)) ? -1 : 0;
280*f0865ec9SKyle Evans 
281*f0865ec9SKyle Evans err:
282*f0865ec9SKyle Evans 	return ret;
283*f0865ec9SKyle Evans }
284*f0865ec9SKyle Evans 
285*f0865ec9SKyle Evans /* Same as the one above, but the subtraction is performed unconditionally. */
nn_sub(nn_t out,nn_src_t in1,nn_src_t in2)286*f0865ec9SKyle Evans int nn_sub(nn_t out, nn_src_t in1, nn_src_t in2)
287*f0865ec9SKyle Evans {
288*f0865ec9SKyle Evans 	return nn_cnd_sub(1, out, in1, in2);
289*f0865ec9SKyle Evans }
290*f0865ec9SKyle Evans 
291*f0865ec9SKyle Evans /*
292*f0865ec9SKyle Evans  * Compute out = in1 - 1 where in1 is a *positive* integer. Aliasing is
293*f0865ec9SKyle Evans  * supported i.e. nn_dec(A, A) works as expected and provides A -= 1.
294*f0865ec9SKyle Evans  * The function returns 0 on success, -1 on error.
295*f0865ec9SKyle Evans  */
nn_dec(nn_t out,nn_src_t in1)296*f0865ec9SKyle Evans int nn_dec(nn_t out, nn_src_t in1)
297*f0865ec9SKyle Evans {
298*f0865ec9SKyle Evans 	const word_t w = WORD(1);
299*f0865ec9SKyle Evans 	word_t tmp, borrow;
300*f0865ec9SKyle Evans 	u8 n_wlen, i;
301*f0865ec9SKyle Evans 	int ret;
302*f0865ec9SKyle Evans 
303*f0865ec9SKyle Evans 	ret = nn_check_initialized(in1); EG(ret, err);
304*f0865ec9SKyle Evans 	n_wlen = in1->wlen;
305*f0865ec9SKyle Evans 	ret = nn_set_wlen(out, n_wlen); EG(ret, err);
306*f0865ec9SKyle Evans 
307*f0865ec9SKyle Evans 	/* Perform subtraction w/ provided word and propagate the borrow */
308*f0865ec9SKyle Evans 	borrow = w;
309*f0865ec9SKyle Evans 	for (i = 0; i < n_wlen; i++) {
310*f0865ec9SKyle Evans 		tmp = (word_t)(in1->val[i] - borrow);
311*f0865ec9SKyle Evans 		borrow = (word_t)(tmp > in1->val[i]);
312*f0865ec9SKyle Evans 		out->val[i] = tmp;
313*f0865ec9SKyle Evans 	}
314*f0865ec9SKyle Evans 
315*f0865ec9SKyle Evans 	ret = (borrow != WORD(0)) ? -1 : 0;
316*f0865ec9SKyle Evans 
317*f0865ec9SKyle Evans err:
318*f0865ec9SKyle Evans 	return ret;
319*f0865ec9SKyle Evans }
320*f0865ec9SKyle Evans 
321*f0865ec9SKyle Evans /*
322*f0865ec9SKyle Evans  * The following functions handle modular arithmetic. Our outputs sizes do not
323*f0865ec9SKyle Evans  * need a "normalization" since everything will be bounded by the modular number
324*f0865ec9SKyle Evans  * size.
325*f0865ec9SKyle Evans  *
326*f0865ec9SKyle Evans  * Warning: the following functions are only useful when the inputs are < p,
327*f0865ec9SKyle Evans  * i.e. we suppose that the input are already reduced modulo p. These primitives
328*f0865ec9SKyle Evans  * are mostly useful for the Fp layer. Even though they give results when
329*f0865ec9SKyle Evans  * applied to inputs >= p, there is no guarantee that the result is indeed < p
330*f0865ec9SKyle Evans  * or correct whatsoever.
331*f0865ec9SKyle Evans  */
332*f0865ec9SKyle Evans 
333*f0865ec9SKyle Evans /*
334*f0865ec9SKyle Evans  * Compute out = in1 + in2 mod p. The function returns 0 on success, -1 on
335*f0865ec9SKyle Evans  * error.
336*f0865ec9SKyle Evans  *
337*f0865ec9SKyle Evans  * Aliasing not fully supported, for internal use only.
338*f0865ec9SKyle Evans  */
_nn_mod_add(nn_t out,nn_src_t in1,nn_src_t in2,nn_src_t p)339*f0865ec9SKyle Evans static int _nn_mod_add(nn_t out, nn_src_t in1, nn_src_t in2, nn_src_t p)
340*f0865ec9SKyle Evans {
341*f0865ec9SKyle Evans 	int ret, larger, cmp;
342*f0865ec9SKyle Evans 
343*f0865ec9SKyle Evans 	ret = nn_check_initialized(in1); EG(ret, err);
344*f0865ec9SKyle Evans 	ret = nn_check_initialized(in2); EG(ret, err);
345*f0865ec9SKyle Evans 	ret = nn_check_initialized(p); EG(ret, err);
346*f0865ec9SKyle Evans 	MUST_HAVE((p->wlen < NN_MAX_WORD_LEN), ret, err); /* otherwise carry could overflow */
347*f0865ec9SKyle Evans 	SHOULD_HAVE((!nn_cmp(in1, p, &cmp)) && (cmp < 0), ret, err); /* a SHOULD_HAVE as documented above */
348*f0865ec9SKyle Evans 	SHOULD_HAVE((!nn_cmp(in2, p, &cmp)) && (cmp < 0), ret, err); /* a SHOULD_HAVE as documented above */
349*f0865ec9SKyle Evans 
350*f0865ec9SKyle Evans 	ret = nn_add(out, in1, in2); EG(ret, err);
351*f0865ec9SKyle Evans 	/*
352*f0865ec9SKyle Evans 	 * If previous addition extends out->wlen, this may have an effect on
353*f0865ec9SKyle Evans 	 * computation time of functions below. For that reason, we always
354*f0865ec9SKyle Evans 	 * normalize out->wlen to p->wlen + 1. Its length is set to that of
355*f0865ec9SKyle Evans 	 * p after the computations.
356*f0865ec9SKyle Evans 	 *
357*f0865ec9SKyle Evans 	 * We could also use _nn_cnd_add to catch the carry and deal
358*f0865ec9SKyle Evans 	 * with p's of size NN_MAX_WORD_LEN.
359*f0865ec9SKyle Evans 	 * It is still painful because we have no constraint on the lengths
360*f0865ec9SKyle Evans 	 * of in1 and in2 so getting a carry out does not necessarily mean
361*f0865ec9SKyle Evans 	 * that the sum is larger than p...
362*f0865ec9SKyle Evans 	 */
363*f0865ec9SKyle Evans 	ret = nn_set_wlen(out, (u8)(p->wlen + 1)); EG(ret, err);
364*f0865ec9SKyle Evans 	ret = nn_cmp(out, p, &cmp); EG(ret, err);
365*f0865ec9SKyle Evans 	larger = (cmp >= 0);
366*f0865ec9SKyle Evans 	ret = nn_cnd_sub(larger, out, out, p); EG(ret, err);
367*f0865ec9SKyle Evans 	ret = nn_set_wlen(out, p->wlen);
368*f0865ec9SKyle Evans 
369*f0865ec9SKyle Evans err:
370*f0865ec9SKyle Evans 	return ret;
371*f0865ec9SKyle Evans }
372*f0865ec9SKyle Evans 
373*f0865ec9SKyle Evans /*
374*f0865ec9SKyle Evans  * Compute out = in1 + in2 mod p. The function returns 0 on success, -1 on
375*f0865ec9SKyle Evans  * error.
376*f0865ec9SKyle Evans  *
377*f0865ec9SKyle Evans  * Aliasing is supported.
378*f0865ec9SKyle Evans  */
nn_mod_add(nn_t out,nn_src_t in1,nn_src_t in2,nn_src_t p)379*f0865ec9SKyle Evans int nn_mod_add(nn_t out, nn_src_t in1, nn_src_t in2, nn_src_t p)
380*f0865ec9SKyle Evans {
381*f0865ec9SKyle Evans 	int ret;
382*f0865ec9SKyle Evans 
383*f0865ec9SKyle Evans 	if(out == p){
384*f0865ec9SKyle Evans 		nn p_cpy;
385*f0865ec9SKyle Evans 		p_cpy.magic = WORD(0);
386*f0865ec9SKyle Evans 
387*f0865ec9SKyle Evans 		ret = nn_copy(&p_cpy, p); EG(ret, err1);
388*f0865ec9SKyle Evans 		ret = _nn_mod_add(out, in1, in2, &p_cpy);
389*f0865ec9SKyle Evans 
390*f0865ec9SKyle Evans err1:
391*f0865ec9SKyle Evans 		nn_uninit(&p_cpy);
392*f0865ec9SKyle Evans 		EG(ret, err);
393*f0865ec9SKyle Evans 	}
394*f0865ec9SKyle Evans 	else{
395*f0865ec9SKyle Evans 		ret = _nn_mod_add(out, in1, in2, p);
396*f0865ec9SKyle Evans 	}
397*f0865ec9SKyle Evans 
398*f0865ec9SKyle Evans err:
399*f0865ec9SKyle Evans 	return ret;
400*f0865ec9SKyle Evans }
401*f0865ec9SKyle Evans 
402*f0865ec9SKyle Evans /*
403*f0865ec9SKyle Evans  * Compute out = in1 + 1 mod p. The function returns 0 on success, -1 on error.
404*f0865ec9SKyle Evans  *
405*f0865ec9SKyle Evans  * Aliasing not fully supported, for internal use only.
406*f0865ec9SKyle Evans  */
_nn_mod_inc(nn_t out,nn_src_t in1,nn_src_t p)407*f0865ec9SKyle Evans static int _nn_mod_inc(nn_t out, nn_src_t in1, nn_src_t p)
408*f0865ec9SKyle Evans {
409*f0865ec9SKyle Evans 	int larger, ret, cmp;
410*f0865ec9SKyle Evans 
411*f0865ec9SKyle Evans 	ret = nn_check_initialized(in1); EG(ret, err);
412*f0865ec9SKyle Evans 	ret = nn_check_initialized(p); EG(ret, err);
413*f0865ec9SKyle Evans 	MUST_HAVE((p->wlen < NN_MAX_WORD_LEN), ret, err); /* otherwise carry could overflow */
414*f0865ec9SKyle Evans 	SHOULD_HAVE((!nn_cmp(in1, p, &cmp)) && (cmp < 0), ret, err); /* a SHOULD_HAVE as documented above */
415*f0865ec9SKyle Evans 
416*f0865ec9SKyle Evans 	ret = nn_inc(out, in1); EG(ret, err);
417*f0865ec9SKyle Evans 	ret = nn_set_wlen(out, (u8)(p->wlen + 1)); EG(ret, err); /* see comment in nn_mod_add() */
418*f0865ec9SKyle Evans 	ret = nn_cmp(out, p, &cmp); EG(ret, err);
419*f0865ec9SKyle Evans 	larger = (cmp >= 0);
420*f0865ec9SKyle Evans 	ret = nn_cnd_sub(larger, out, out, p); EG(ret, err);
421*f0865ec9SKyle Evans 	ret = nn_set_wlen(out, p->wlen);
422*f0865ec9SKyle Evans 
423*f0865ec9SKyle Evans err:
424*f0865ec9SKyle Evans 	return ret;
425*f0865ec9SKyle Evans }
426*f0865ec9SKyle Evans 
427*f0865ec9SKyle Evans /*
428*f0865ec9SKyle Evans  * Compute out = in1 + 1 mod p. The function returns 0 on success, -1 on error.
429*f0865ec9SKyle Evans  *
430*f0865ec9SKyle Evans  * Aliasing supported.
431*f0865ec9SKyle Evans  */
nn_mod_inc(nn_t out,nn_src_t in1,nn_src_t p)432*f0865ec9SKyle Evans int nn_mod_inc(nn_t out, nn_src_t in1, nn_src_t p)
433*f0865ec9SKyle Evans {
434*f0865ec9SKyle Evans 	int ret;
435*f0865ec9SKyle Evans 
436*f0865ec9SKyle Evans 	if(out == p){
437*f0865ec9SKyle Evans 		nn p_cpy;
438*f0865ec9SKyle Evans 		p_cpy.magic = WORD(0);
439*f0865ec9SKyle Evans 
440*f0865ec9SKyle Evans 		ret = nn_copy(&p_cpy, p); EG(ret, err1);
441*f0865ec9SKyle Evans 		ret = _nn_mod_inc(out, in1, &p_cpy);
442*f0865ec9SKyle Evans 
443*f0865ec9SKyle Evans err1:
444*f0865ec9SKyle Evans 		nn_uninit(&p_cpy);
445*f0865ec9SKyle Evans 		EG(ret, err);
446*f0865ec9SKyle Evans 	}
447*f0865ec9SKyle Evans 	else{
448*f0865ec9SKyle Evans 		ret = _nn_mod_inc(out, in1, p);
449*f0865ec9SKyle Evans 	}
450*f0865ec9SKyle Evans 
451*f0865ec9SKyle Evans err:
452*f0865ec9SKyle Evans 	return ret;
453*f0865ec9SKyle Evans 
454*f0865ec9SKyle Evans }
455*f0865ec9SKyle Evans 
456*f0865ec9SKyle Evans /*
457*f0865ec9SKyle Evans  * Compute out = in1 - in2 mod p. The function returns 0 on success, -1 on
458*f0865ec9SKyle Evans  * error.
459*f0865ec9SKyle Evans  *
460*f0865ec9SKyle Evans  * Aliasing not supported, for internal use only.
461*f0865ec9SKyle Evans  */
_nn_mod_sub(nn_t out,nn_src_t in1,nn_src_t in2,nn_src_t p)462*f0865ec9SKyle Evans static int _nn_mod_sub(nn_t out, nn_src_t in1, nn_src_t in2, nn_src_t p)
463*f0865ec9SKyle Evans {
464*f0865ec9SKyle Evans 	int smaller, ret, cmp;
465*f0865ec9SKyle Evans 	nn_src_t in2_;
466*f0865ec9SKyle Evans 	nn in2_cpy;
467*f0865ec9SKyle Evans 	in2_cpy.magic = WORD(0);
468*f0865ec9SKyle Evans 
469*f0865ec9SKyle Evans 	ret = nn_check_initialized(in1); EG(ret, err);
470*f0865ec9SKyle Evans 	ret = nn_check_initialized(in2); EG(ret, err);
471*f0865ec9SKyle Evans 	ret = nn_check_initialized(p); EG(ret, err);
472*f0865ec9SKyle Evans 	MUST_HAVE((p->wlen < NN_MAX_WORD_LEN), ret, err); /* otherwise carry could overflow */
473*f0865ec9SKyle Evans 	SHOULD_HAVE((!nn_cmp(in1, p, &cmp)) && (cmp < 0), ret, err); /* a SHOULD_HAVE as documented above */
474*f0865ec9SKyle Evans 	SHOULD_HAVE((!nn_cmp(in2, p, &cmp)) && (cmp < 0), ret, err); /* a SHOULD_HAVE as documented above */
475*f0865ec9SKyle Evans 
476*f0865ec9SKyle Evans 	/* Handle the case where in2 and out are aliased */
477*f0865ec9SKyle Evans 	if (in2 == out) {
478*f0865ec9SKyle Evans 		ret = nn_copy(&in2_cpy, in2); EG(ret, err);
479*f0865ec9SKyle Evans 		in2_ = &in2_cpy;
480*f0865ec9SKyle Evans 	} else {
481*f0865ec9SKyle Evans 		ret = nn_init(&in2_cpy, 0); EG(ret, err);
482*f0865ec9SKyle Evans 		in2_ = in2;
483*f0865ec9SKyle Evans 	}
484*f0865ec9SKyle Evans 
485*f0865ec9SKyle Evans 	/* The below trick is used to avoid handling of "negative" numbers. */
486*f0865ec9SKyle Evans 	ret = nn_cmp(in1, in2_, &cmp); EG(ret, err);
487*f0865ec9SKyle Evans 	smaller = (cmp < 0);
488*f0865ec9SKyle Evans 	ret = nn_cnd_add(smaller, out, in1, p); EG(ret, err);
489*f0865ec9SKyle Evans 	ret = nn_set_wlen(out, (u8)(p->wlen + 1)); EG(ret, err);/* See Comment in nn_mod_add() */
490*f0865ec9SKyle Evans 	ret = nn_sub(out, out, in2_); EG(ret, err);
491*f0865ec9SKyle Evans 	ret = nn_set_wlen(out, p->wlen);
492*f0865ec9SKyle Evans 
493*f0865ec9SKyle Evans err:
494*f0865ec9SKyle Evans 	nn_uninit(&in2_cpy);
495*f0865ec9SKyle Evans 
496*f0865ec9SKyle Evans 	return ret;
497*f0865ec9SKyle Evans }
498*f0865ec9SKyle Evans 
499*f0865ec9SKyle Evans /*
500*f0865ec9SKyle Evans  * Compute out = in1 - in2 mod p. The function returns 0 on success, -1 on
501*f0865ec9SKyle Evans  * error.
502*f0865ec9SKyle Evans  *
503*f0865ec9SKyle Evans  * Aliasing supported.
504*f0865ec9SKyle Evans  */
nn_mod_sub(nn_t out,nn_src_t in1,nn_src_t in2,nn_src_t p)505*f0865ec9SKyle Evans int nn_mod_sub(nn_t out, nn_src_t in1, nn_src_t in2, nn_src_t p)
506*f0865ec9SKyle Evans {
507*f0865ec9SKyle Evans 	int ret;
508*f0865ec9SKyle Evans 
509*f0865ec9SKyle Evans 	if(out == p){
510*f0865ec9SKyle Evans 		nn p_cpy;
511*f0865ec9SKyle Evans 		p_cpy.magic = WORD(0);
512*f0865ec9SKyle Evans 
513*f0865ec9SKyle Evans 		ret = nn_copy(&p_cpy, p); EG(ret, err1);
514*f0865ec9SKyle Evans 		ret = _nn_mod_sub(out, in1, in2, &p_cpy);
515*f0865ec9SKyle Evans 
516*f0865ec9SKyle Evans err1:
517*f0865ec9SKyle Evans 		nn_uninit(&p_cpy);
518*f0865ec9SKyle Evans 		EG(ret, err);
519*f0865ec9SKyle Evans 	}
520*f0865ec9SKyle Evans 	else{
521*f0865ec9SKyle Evans 		ret = _nn_mod_sub(out, in1, in2, p);
522*f0865ec9SKyle Evans 	}
523*f0865ec9SKyle Evans 
524*f0865ec9SKyle Evans err:
525*f0865ec9SKyle Evans 	return ret;
526*f0865ec9SKyle Evans }
527*f0865ec9SKyle Evans 
528*f0865ec9SKyle Evans /*
529*f0865ec9SKyle Evans  * Compute out = in1 - 1 mod p. The function returns 0 on success, -1 on error
530*f0865ec9SKyle Evans  *
531*f0865ec9SKyle Evans  * Aliasing not supported, for internal use only.
532*f0865ec9SKyle Evans  */
_nn_mod_dec(nn_t out,nn_src_t in1,nn_src_t p)533*f0865ec9SKyle Evans static int _nn_mod_dec(nn_t out, nn_src_t in1, nn_src_t p)
534*f0865ec9SKyle Evans {
535*f0865ec9SKyle Evans 	int ret, iszero, cmp;
536*f0865ec9SKyle Evans 
537*f0865ec9SKyle Evans 	ret = nn_check_initialized(in1); EG(ret, err);
538*f0865ec9SKyle Evans 	ret = nn_check_initialized(p); EG(ret, err);
539*f0865ec9SKyle Evans 	MUST_HAVE((p->wlen < NN_MAX_WORD_LEN), ret, err); /* otherwise carry could overflow */
540*f0865ec9SKyle Evans 	FORCE_USED_VAR(cmp); /* nop to silence possible warning with macro below */
541*f0865ec9SKyle Evans 	SHOULD_HAVE((!nn_cmp(in1, p, &cmp)) && (cmp < 0), ret, err);  /* a SHOULD_HAVE; Documented above */
542*f0865ec9SKyle Evans 
543*f0865ec9SKyle Evans 	/* The below trick is used to avoid handling of "negative" numbers. */
544*f0865ec9SKyle Evans 	ret = nn_iszero(in1, &iszero); EG(ret, err);
545*f0865ec9SKyle Evans 	ret = nn_cnd_add(iszero, out, in1, p); EG(ret, err);
546*f0865ec9SKyle Evans 	ret = nn_set_wlen(out, (u8)(p->wlen + 1)); EG(ret, err); /* See Comment in nn_mod_add() */
547*f0865ec9SKyle Evans 	ret = nn_dec(out, out); EG(ret, err);
548*f0865ec9SKyle Evans 	ret = nn_set_wlen(out, p->wlen);
549*f0865ec9SKyle Evans 
550*f0865ec9SKyle Evans err:
551*f0865ec9SKyle Evans 	return ret;
552*f0865ec9SKyle Evans }
553*f0865ec9SKyle Evans 
554*f0865ec9SKyle Evans /*
555*f0865ec9SKyle Evans  * Compute out = in1 - 1 mod p. The function returns 0 on success, -1 on error
556*f0865ec9SKyle Evans  *
557*f0865ec9SKyle Evans  * Aliasing supported.
558*f0865ec9SKyle Evans  */
nn_mod_dec(nn_t out,nn_src_t in1,nn_src_t p)559*f0865ec9SKyle Evans int nn_mod_dec(nn_t out, nn_src_t in1, nn_src_t p)
560*f0865ec9SKyle Evans {
561*f0865ec9SKyle Evans 	int ret;
562*f0865ec9SKyle Evans 
563*f0865ec9SKyle Evans 	if(out == p){
564*f0865ec9SKyle Evans 		nn p_cpy;
565*f0865ec9SKyle Evans 		p_cpy.magic = WORD(0);
566*f0865ec9SKyle Evans 
567*f0865ec9SKyle Evans 		ret = nn_copy(&p_cpy, p); EG(ret, err1);
568*f0865ec9SKyle Evans 		ret = _nn_mod_dec(out, in1, &p_cpy);
569*f0865ec9SKyle Evans 
570*f0865ec9SKyle Evans err1:
571*f0865ec9SKyle Evans 		nn_uninit(&p_cpy);
572*f0865ec9SKyle Evans 		EG(ret, err);
573*f0865ec9SKyle Evans 	}
574*f0865ec9SKyle Evans 	else{
575*f0865ec9SKyle Evans 		ret = _nn_mod_dec(out, in1, p);
576*f0865ec9SKyle Evans 	}
577*f0865ec9SKyle Evans 
578*f0865ec9SKyle Evans err:
579*f0865ec9SKyle Evans 	return ret;
580*f0865ec9SKyle Evans }
581*f0865ec9SKyle Evans 
582*f0865ec9SKyle Evans /*
583*f0865ec9SKyle Evans  * Compute out = -in mod p. The function returns 0 on success, -1 on error.
584*f0865ec9SKyle Evans  * Because we only support positive integers, we compute
585*f0865ec9SKyle Evans  * out = p - in (except when value is 0).
586*f0865ec9SKyle Evans  *
587*f0865ec9SKyle Evans  * We suppose that in is already reduced modulo p.
588*f0865ec9SKyle Evans  *
589*f0865ec9SKyle Evans  * Aliasing is supported.
590*f0865ec9SKyle Evans  *
591*f0865ec9SKyle Evans  */
nn_mod_neg(nn_t out,nn_src_t in,nn_src_t p)592*f0865ec9SKyle Evans int nn_mod_neg(nn_t out, nn_src_t in, nn_src_t p)
593*f0865ec9SKyle Evans {
594*f0865ec9SKyle Evans 	int ret, cmp, iszero;
595*f0865ec9SKyle Evans 
596*f0865ec9SKyle Evans 	FORCE_USED_VAR(cmp);
597*f0865ec9SKyle Evans 
598*f0865ec9SKyle Evans 	ret = nn_check_initialized(in); EG(ret, err);
599*f0865ec9SKyle Evans 	ret = nn_check_initialized(p); EG(ret, err);
600*f0865ec9SKyle Evans 
601*f0865ec9SKyle Evans 	SHOULD_HAVE((!nn_cmp(in, p, &cmp)) && (cmp < 0), ret, err);  /* a SHOULD_HAVE; Documented above */
602*f0865ec9SKyle Evans 
603*f0865ec9SKyle Evans 	ret = nn_iszero(in, &iszero); EG(ret, err);
604*f0865ec9SKyle Evans 	if (iszero) {
605*f0865ec9SKyle Evans 		ret = nn_init(out, 0); EG(ret, err);
606*f0865ec9SKyle Evans 		ret = nn_zero(out); EG(ret, err);
607*f0865ec9SKyle Evans 	} else {
608*f0865ec9SKyle Evans 		ret = nn_sub(out, p, in); EG(ret, err);
609*f0865ec9SKyle Evans 	}
610*f0865ec9SKyle Evans 
611*f0865ec9SKyle Evans err:
612*f0865ec9SKyle Evans 	return ret;
613*f0865ec9SKyle Evans }
614