xref: /freebsd/crypto/libecc/src/nn/nn_mod_pow.c (revision f0865ec9906d5a18fa2a3b61381f22ce16e606ad)
1*f0865ec9SKyle Evans /*
2*f0865ec9SKyle Evans  *  Copyright (C) 2021 - 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_mul_redc1.h>
17*f0865ec9SKyle Evans #include <libecc/nn/nn_div_public.h>
18*f0865ec9SKyle Evans #include <libecc/nn/nn_logical.h>
19*f0865ec9SKyle Evans #include <libecc/nn/nn_mod_pow.h>
20*f0865ec9SKyle Evans #include <libecc/nn/nn_rand.h>
21*f0865ec9SKyle Evans #include <libecc/nn/nn.h>
22*f0865ec9SKyle Evans 
23*f0865ec9SKyle Evans /*
24*f0865ec9SKyle Evans  * NOT constant time with regard to the bitlength of exp.
25*f0865ec9SKyle Evans  *
26*f0865ec9SKyle Evans  * Implements a left to right Montgomery Ladder for modular exponentiation.
27*f0865ec9SKyle Evans  * This is an internal common helper and assumes that all the initialization
28*f0865ec9SKyle Evans  * and aliasing of inputs/outputs are handled by the callers. Depending on
29*f0865ec9SKyle Evans  * the inputs, redcification is optionally used.
30*f0865ec9SKyle Evans  * The base is reduced if necessary.
31*f0865ec9SKyle Evans  *
32*f0865ec9SKyle Evans  * Montgomery Ladder is masked using Itoh et al. anti-ADPA
33*f0865ec9SKyle Evans  * (Address-bit DPA) countermeasure.
34*f0865ec9SKyle Evans  * See "A Practical Countermeasure against Address-Bit Differential Power Analysis"
35*f0865ec9SKyle Evans  * by Itoh, Izu and Takenaka for more information.
36*f0865ec9SKyle Evans 
37*f0865ec9SKyle Evans  * Returns 0 on success, -1 on error.
38*f0865ec9SKyle Evans  */
_nn_exp_monty_ladder_ltr(nn_t out,nn_src_t base,nn_src_t exp,nn_src_t mod,nn_src_t r,nn_src_t r_square,word_t mpinv)39*f0865ec9SKyle Evans ATTRIBUTE_WARN_UNUSED_RET static int _nn_exp_monty_ladder_ltr(nn_t out, nn_src_t base, nn_src_t exp, nn_src_t mod, nn_src_t r, nn_src_t r_square, word_t mpinv)
40*f0865ec9SKyle Evans {
41*f0865ec9SKyle Evans 	nn T[3];
42*f0865ec9SKyle Evans 	nn mask;
43*f0865ec9SKyle Evans 	bitcnt_t explen, oldexplen;
44*f0865ec9SKyle Evans  	u8 expbit, rbit;
45*f0865ec9SKyle Evans 	int ret, cmp;
46*f0865ec9SKyle Evans 	T[0].magic = T[1].magic = T[2].magic = mask.magic = WORD(0);
47*f0865ec9SKyle Evans 
48*f0865ec9SKyle Evans 	/* Initialize out */
49*f0865ec9SKyle Evans 	ret = nn_init(out, 0); EG(ret, err);
50*f0865ec9SKyle Evans 
51*f0865ec9SKyle Evans 	ret = nn_init(&T[0], 0); EG(ret, err);
52*f0865ec9SKyle Evans 	ret = nn_init(&T[1], 0); EG(ret, err);
53*f0865ec9SKyle Evans 	ret = nn_init(&T[2], 0); EG(ret, err);
54*f0865ec9SKyle Evans 
55*f0865ec9SKyle Evans 	/* Generate our Itoh random mask */
56*f0865ec9SKyle Evans 	ret = nn_get_random_len(&mask, NN_MAX_BYTE_LEN); EG(ret, err);
57*f0865ec9SKyle Evans 
58*f0865ec9SKyle Evans 	ret = nn_bitlen(exp, &explen); EG(ret, err);
59*f0865ec9SKyle Evans 
60*f0865ec9SKyle Evans 
61*f0865ec9SKyle Evans 	/* From now on, since we deal with Itoh's countermeasure, we must have at
62*f0865ec9SKyle Evans 	 * least 2 bits in the exponent. We will deal with the particular cases of 0 and 1
63*f0865ec9SKyle Evans 	 * bit exponents later.
64*f0865ec9SKyle Evans 	 */
65*f0865ec9SKyle Evans 	oldexplen = explen;
66*f0865ec9SKyle Evans 	explen = (explen < 2) ? 2 : explen;
67*f0865ec9SKyle Evans 
68*f0865ec9SKyle Evans 	ret = nn_getbit(&mask, (bitcnt_t)(explen - 1), &rbit); EG(ret, err);
69*f0865ec9SKyle Evans 
70*f0865ec9SKyle Evans 	/* Reduce the base if necessary */
71*f0865ec9SKyle Evans 	ret = nn_cmp(base, mod, &cmp); EG(ret, err);
72*f0865ec9SKyle Evans 	if(cmp >= 0){
73*f0865ec9SKyle Evans 		/* Modular reduction */
74*f0865ec9SKyle Evans 		ret = nn_mod(&T[rbit], base, mod); EG(ret, err);
75*f0865ec9SKyle Evans 		if(r != NULL){
76*f0865ec9SKyle Evans 			/* Redcify the base if necessary */
77*f0865ec9SKyle Evans 			ret = nn_mul_redc1(&T[rbit], &T[rbit], r_square, mod, mpinv); EG(ret, err);
78*f0865ec9SKyle Evans 		}
79*f0865ec9SKyle Evans 	}
80*f0865ec9SKyle Evans 	else{
81*f0865ec9SKyle Evans 		if(r != NULL){
82*f0865ec9SKyle Evans 			/* Redcify the base if necessary */
83*f0865ec9SKyle Evans 			ret = nn_mul_redc1(&T[rbit], base, r_square, mod, mpinv); EG(ret, err);
84*f0865ec9SKyle Evans 		}
85*f0865ec9SKyle Evans 		else{
86*f0865ec9SKyle Evans 			ret = nn_copy(&T[rbit], base); EG(ret, err);
87*f0865ec9SKyle Evans 		}
88*f0865ec9SKyle Evans 	}
89*f0865ec9SKyle Evans 
90*f0865ec9SKyle Evans 	/* We implement the Montgomery ladder exponentiation with Itoh masking using three
91*f0865ec9SKyle Evans 	 * registers T[0], T[1] and T[2]. The random mask is in 'mask'.
92*f0865ec9SKyle Evans 	 */
93*f0865ec9SKyle Evans 	if(r != NULL){
94*f0865ec9SKyle Evans 		ret = nn_mul_redc1(&T[1-rbit], &T[rbit], &T[rbit], mod, mpinv); EG(ret, err);
95*f0865ec9SKyle Evans 	}
96*f0865ec9SKyle Evans 	else{
97*f0865ec9SKyle Evans 		ret = nn_mod_mul(&T[1-rbit], &T[rbit], &T[rbit], mod); EG(ret, err);
98*f0865ec9SKyle Evans 	}
99*f0865ec9SKyle Evans 
100*f0865ec9SKyle Evans 	/* Now proceed with the Montgomery Ladder algorithm.
101*f0865ec9SKyle Evans 	 */
102*f0865ec9SKyle Evans 	explen = (bitcnt_t)(explen - 1);
103*f0865ec9SKyle Evans 	while (explen > 0) {
104*f0865ec9SKyle Evans 		u8 rbit_next;
105*f0865ec9SKyle Evans 		explen = (bitcnt_t)(explen - 1);
106*f0865ec9SKyle Evans 
107*f0865ec9SKyle Evans 		/* rbit is r[i+1], and rbit_next is r[i] */
108*f0865ec9SKyle Evans 		ret = nn_getbit(&mask, explen, &rbit_next); EG(ret, err);
109*f0865ec9SKyle Evans 		/* Get the exponent bit */
110*f0865ec9SKyle Evans 		ret = nn_getbit(exp, explen, &expbit); EG(ret, err);
111*f0865ec9SKyle Evans 
112*f0865ec9SKyle Evans 		/* Square */
113*f0865ec9SKyle Evans 		if(r != NULL){
114*f0865ec9SKyle Evans 			ret = nn_mul_redc1(&T[2], &T[expbit ^ rbit], &T[expbit ^ rbit], mod, mpinv); EG(ret, err);
115*f0865ec9SKyle Evans 		}
116*f0865ec9SKyle Evans 		else{
117*f0865ec9SKyle Evans 			ret = nn_mod_mul(&T[2], &T[expbit ^ rbit], &T[expbit ^ rbit], mod); EG(ret, err);
118*f0865ec9SKyle Evans 		}
119*f0865ec9SKyle Evans 		/* Multiply */
120*f0865ec9SKyle Evans 		if(r != NULL){
121*f0865ec9SKyle Evans 			ret = nn_mul_redc1(&T[1], &T[0], &T[1], mod, mpinv); EG(ret, err);
122*f0865ec9SKyle Evans 		}
123*f0865ec9SKyle Evans 		else{
124*f0865ec9SKyle Evans 			ret = nn_mod_mul(&T[1], &T[0], &T[1], mod); EG(ret, err);
125*f0865ec9SKyle Evans 		}
126*f0865ec9SKyle Evans 		/* Copy */
127*f0865ec9SKyle Evans 		ret = nn_copy(&T[0], &T[2 - (expbit ^ rbit_next)]); EG(ret, err);
128*f0865ec9SKyle Evans 		ret = nn_copy(&T[1], &T[1 + (expbit ^ rbit_next)]); EG(ret, err);
129*f0865ec9SKyle Evans 		/* Update rbit */
130*f0865ec9SKyle Evans 		rbit = rbit_next;
131*f0865ec9SKyle Evans 	}
132*f0865ec9SKyle Evans 	ret = nn_one(&T[1 - rbit]);
133*f0865ec9SKyle Evans 	if(r != NULL){
134*f0865ec9SKyle Evans 		/* Unredcify in out */
135*f0865ec9SKyle Evans 		ret = nn_mul_redc1(&T[rbit], &T[rbit], &T[1 - rbit], mod, mpinv); EG(ret, err);
136*f0865ec9SKyle Evans 	}
137*f0865ec9SKyle Evans 
138*f0865ec9SKyle Evans 	/* Deal with the particular cases of 0 and 1 bit exponents */
139*f0865ec9SKyle Evans 	/* Case with 0 bit exponent: T[1 - rbit] contains 1 modulo mod */
140*f0865ec9SKyle Evans 	ret = nn_mod(&T[1 - rbit], &T[1 - rbit], mod); EG(ret, err);
141*f0865ec9SKyle Evans 	/* Case with 1 bit exponent */
142*f0865ec9SKyle Evans 	ret = nn_mod(&T[2], base, mod); EG(ret, err);
143*f0865ec9SKyle Evans 	/* Proceed with the output */
144*f0865ec9SKyle Evans 	ret = nn_cnd_swap((oldexplen == 0), out, &T[1 - rbit]);
145*f0865ec9SKyle Evans 	ret = nn_cnd_swap((oldexplen == 1), out, &T[2]);
146*f0865ec9SKyle Evans 	ret = nn_cnd_swap(((oldexplen != 0) && (oldexplen != 1)), out, &T[rbit]);
147*f0865ec9SKyle Evans 
148*f0865ec9SKyle Evans err:
149*f0865ec9SKyle Evans 	nn_uninit(&T[0]);
150*f0865ec9SKyle Evans 	nn_uninit(&T[1]);
151*f0865ec9SKyle Evans 	nn_uninit(&T[2]);
152*f0865ec9SKyle Evans 	nn_uninit(&mask);
153*f0865ec9SKyle Evans 
154*f0865ec9SKyle Evans 	return ret;
155*f0865ec9SKyle Evans }
156*f0865ec9SKyle Evans 
157*f0865ec9SKyle Evans /*
158*f0865ec9SKyle Evans  * NOT constant time with regard to the bitlength of exp.
159*f0865ec9SKyle Evans  *
160*f0865ec9SKyle Evans  * Reduces the base modulo mod if it is not already reduced,
161*f0865ec9SKyle Evans  * which is also a small divergence wrt constant time leaking
162*f0865ec9SKyle Evans  * the information that base <= mod or not: please use with care
163*f0865ec9SKyle Evans  * in the callers if this information is sensitive.
164*f0865ec9SKyle Evans  *
165*f0865ec9SKyle Evans  * Aliasing not supported. Expects caller to check parameters
166*f0865ec9SKyle Evans  * have been initialized. This is an internal helper.
167*f0865ec9SKyle Evans  *
168*f0865ec9SKyle Evans  * Compute (base ** exp) mod (mod) using a Montgomery Ladder algorithm
169*f0865ec9SKyle Evans  * with Montgomery redcification, hence the Montgomery coefficients as input.
170*f0865ec9SKyle Evans  * The module "mod" is expected to be odd for redcification to be used.
171*f0865ec9SKyle Evans  *
172*f0865ec9SKyle Evans  * Returns 0 on success, -1 on error.
173*f0865ec9SKyle Evans  */
_nn_mod_pow_redc(nn_t out,nn_src_t base,nn_src_t exp,nn_src_t mod,nn_src_t r,nn_src_t r_square,word_t mpinv)174*f0865ec9SKyle Evans ATTRIBUTE_WARN_UNUSED_RET static int _nn_mod_pow_redc(nn_t out, nn_src_t base, nn_src_t exp, nn_src_t mod, nn_src_t r, nn_src_t r_square, word_t mpinv)
175*f0865ec9SKyle Evans {
176*f0865ec9SKyle Evans 	return _nn_exp_monty_ladder_ltr(out, base, exp, mod, r, r_square, mpinv);
177*f0865ec9SKyle Evans }
178*f0865ec9SKyle Evans 
179*f0865ec9SKyle Evans /*
180*f0865ec9SKyle Evans  * NOT constant time with regard to the bitlength of exp.
181*f0865ec9SKyle Evans  *
182*f0865ec9SKyle Evans  * Reduces the base modulo mod if it is not already reduced,
183*f0865ec9SKyle Evans  * which is also a small divergence wrt constant time leaking
184*f0865ec9SKyle Evans  * the information that base <= mod or not: please use with care
185*f0865ec9SKyle Evans  * in the callers if this information is sensitive.
186*f0865ec9SKyle Evans  *
187*f0865ec9SKyle Evans  * Aliasing is supported. Expects caller to check parameters
188*f0865ec9SKyle Evans  * have been initialized. This is an internal helper.
189*f0865ec9SKyle Evans  *
190*f0865ec9SKyle Evans  * Compute (base ** exp) mod (mod) using a Montgomery Ladder algorithm.
191*f0865ec9SKyle Evans  * This function works for all values of "mod", but is slower that the one
192*f0865ec9SKyle Evans  * using Montgomery multiplication (which only works for odd "mod"). Hence,
193*f0865ec9SKyle Evans  * it is only used on even "mod" by upper layers.
194*f0865ec9SKyle Evans  *
195*f0865ec9SKyle Evans  * Returns 0 on success, -1 on error.
196*f0865ec9SKyle Evans  */
_nn_mod_pow(nn_t out,nn_src_t base,nn_src_t exp,nn_src_t mod)197*f0865ec9SKyle Evans ATTRIBUTE_WARN_UNUSED_RET static int _nn_mod_pow(nn_t out, nn_src_t base, nn_src_t exp, nn_src_t mod)
198*f0865ec9SKyle Evans {
199*f0865ec9SKyle Evans 	int ret;
200*f0865ec9SKyle Evans 
201*f0865ec9SKyle Evans 	if ((out == base) || (out == exp) || (out == mod)) {
202*f0865ec9SKyle Evans 		nn _out;
203*f0865ec9SKyle Evans 		_out.magic = WORD(0);
204*f0865ec9SKyle Evans 
205*f0865ec9SKyle Evans 		ret = nn_init(&_out, 0); EG(ret, err);
206*f0865ec9SKyle Evans 		ret = _nn_exp_monty_ladder_ltr(&_out, base, exp, mod, NULL, NULL, WORD(0)); EG(ret, err);
207*f0865ec9SKyle Evans 		ret = nn_copy(out, &_out);
208*f0865ec9SKyle Evans 	}
209*f0865ec9SKyle Evans 	else{
210*f0865ec9SKyle Evans 		ret = _nn_exp_monty_ladder_ltr(out, base, exp, mod, NULL, NULL, WORD(0));
211*f0865ec9SKyle Evans 	}
212*f0865ec9SKyle Evans 
213*f0865ec9SKyle Evans err:
214*f0865ec9SKyle Evans 	return ret;
215*f0865ec9SKyle Evans }
216*f0865ec9SKyle Evans 
217*f0865ec9SKyle Evans /*
218*f0865ec9SKyle Evans  * Same purpose as above but handles aliasing.
219*f0865ec9SKyle Evans  * Expects caller to check parameters
220*f0865ec9SKyle Evans  * have been initialized. This is an internal helper.
221*f0865ec9SKyle Evans  */
_nn_mod_pow_redc_aliased(nn_t out,nn_src_t base,nn_src_t exp,nn_src_t mod,nn_src_t r,nn_src_t r_square,word_t mpinv)222*f0865ec9SKyle Evans ATTRIBUTE_WARN_UNUSED_RET static int _nn_mod_pow_redc_aliased(nn_t out, nn_src_t base, nn_src_t exp, nn_src_t mod, nn_src_t r, nn_src_t r_square, word_t mpinv)
223*f0865ec9SKyle Evans {
224*f0865ec9SKyle Evans 	nn _out;
225*f0865ec9SKyle Evans 	int ret;
226*f0865ec9SKyle Evans 	_out.magic = WORD(0);
227*f0865ec9SKyle Evans 
228*f0865ec9SKyle Evans 	ret = nn_init(&_out, 0); EG(ret, err);
229*f0865ec9SKyle Evans 	ret = _nn_mod_pow_redc(&_out, base, exp, mod, r, r_square, mpinv); EG(ret, err);
230*f0865ec9SKyle Evans 	ret = nn_copy(out, &_out);
231*f0865ec9SKyle Evans 
232*f0865ec9SKyle Evans err:
233*f0865ec9SKyle Evans 	nn_uninit(&_out);
234*f0865ec9SKyle Evans 
235*f0865ec9SKyle Evans 	return ret;
236*f0865ec9SKyle Evans }
237*f0865ec9SKyle Evans 
238*f0865ec9SKyle Evans /* Aliased version of previous one.
239*f0865ec9SKyle Evans  * NOTE: our nn_mod_pow_redc primitives suppose that the modulo is odd for Montgomery multiplication
240*f0865ec9SKyle Evans  * primitives to provide consistent results.
241*f0865ec9SKyle Evans  *
242*f0865ec9SKyle Evans  * Aliasing is supported.
243*f0865ec9SKyle Evans  */
nn_mod_pow_redc(nn_t out,nn_src_t base,nn_src_t exp,nn_src_t mod,nn_src_t r,nn_src_t r_square,word_t mpinv)244*f0865ec9SKyle Evans int nn_mod_pow_redc(nn_t out, nn_src_t base, nn_src_t exp, nn_src_t mod, nn_src_t r, nn_src_t r_square, word_t mpinv)
245*f0865ec9SKyle Evans {
246*f0865ec9SKyle Evans 	int ret, isodd;
247*f0865ec9SKyle Evans 
248*f0865ec9SKyle Evans 	ret = nn_check_initialized(base); EG(ret, err);
249*f0865ec9SKyle Evans 	ret = nn_check_initialized(exp); EG(ret, err);
250*f0865ec9SKyle Evans 	ret = nn_check_initialized(mod); EG(ret, err);
251*f0865ec9SKyle Evans 	ret = nn_check_initialized(r); EG(ret, err);
252*f0865ec9SKyle Evans 	ret = nn_check_initialized(r_square); EG(ret, err);
253*f0865ec9SKyle Evans 
254*f0865ec9SKyle Evans 	/* Check that we have an odd number for our modulo */
255*f0865ec9SKyle Evans 	ret = nn_isodd(mod, &isodd); EG(ret, err);
256*f0865ec9SKyle Evans 	MUST_HAVE(isodd, ret, err);
257*f0865ec9SKyle Evans 
258*f0865ec9SKyle Evans 	/* Handle the case where our prime is less than two words size.
259*f0865ec9SKyle Evans 	 * We need it to be >= 2 words size for the Montgomery multiplication to be
260*f0865ec9SKyle Evans 	 * usable.
261*f0865ec9SKyle Evans 	 */
262*f0865ec9SKyle Evans 	if(mod->wlen < 2){
263*f0865ec9SKyle Evans 		/* Local copy our modulo */
264*f0865ec9SKyle Evans 		nn _mod;
265*f0865ec9SKyle Evans 		_mod.magic = WORD(0);
266*f0865ec9SKyle Evans 
267*f0865ec9SKyle Evans 		/* And set its length accordingly */
268*f0865ec9SKyle Evans 		ret = nn_copy(&_mod, mod); EG(ret, err1);
269*f0865ec9SKyle Evans 		ret = nn_set_wlen(&_mod, 2); EG(ret, err1);
270*f0865ec9SKyle Evans 		/* Handle output aliasing */
271*f0865ec9SKyle Evans 		if ((out == base) || (out == exp) || (out == mod) || (out == r) || (out == r_square)) {
272*f0865ec9SKyle Evans 			ret = _nn_mod_pow_redc_aliased(out, base, exp, &_mod, r, r_square, mpinv); EG(ret, err1);
273*f0865ec9SKyle Evans 		} else {
274*f0865ec9SKyle Evans 			ret = _nn_mod_pow_redc(out, base, exp, &_mod, r, r_square, mpinv); EG(ret, err1);
275*f0865ec9SKyle Evans 		}
276*f0865ec9SKyle Evans err1:
277*f0865ec9SKyle Evans 		nn_uninit(&_mod);
278*f0865ec9SKyle Evans 		EG(ret, err);
279*f0865ec9SKyle Evans 	}
280*f0865ec9SKyle Evans 	else{
281*f0865ec9SKyle Evans 		/* Handle output aliasing */
282*f0865ec9SKyle Evans 		if ((out == base) || (out == exp) || (out == mod) || (out == r) || (out == r_square)) {
283*f0865ec9SKyle Evans 			ret = _nn_mod_pow_redc_aliased(out, base, exp, mod, r, r_square, mpinv);
284*f0865ec9SKyle Evans 		} else {
285*f0865ec9SKyle Evans 			ret = _nn_mod_pow_redc(out, base, exp, mod, r, r_square, mpinv);
286*f0865ec9SKyle Evans 		}
287*f0865ec9SKyle Evans 	}
288*f0865ec9SKyle Evans 
289*f0865ec9SKyle Evans err:
290*f0865ec9SKyle Evans 	return ret;
291*f0865ec9SKyle Evans }
292*f0865ec9SKyle Evans 
293*f0865ec9SKyle Evans 
294*f0865ec9SKyle Evans /*
295*f0865ec9SKyle Evans  * NOT constant time with regard to the bitlength of exp.
296*f0865ec9SKyle Evans  * Aliasing is supported.
297*f0865ec9SKyle Evans  *
298*f0865ec9SKyle Evans  * Compute (base ** exp) mod (mod) using a Montgomery Ladder algorithm.
299*f0865ec9SKyle Evans  * Internally, this computes Montgomery coefficients and uses the redc
300*f0865ec9SKyle Evans  * function.
301*f0865ec9SKyle Evans  *
302*f0865ec9SKyle Evans  * Returns 0 on success, -1 on error.
303*f0865ec9SKyle Evans  */
nn_mod_pow(nn_t out,nn_src_t base,nn_src_t exp,nn_src_t mod)304*f0865ec9SKyle Evans int nn_mod_pow(nn_t out, nn_src_t base, nn_src_t exp, nn_src_t mod)
305*f0865ec9SKyle Evans {
306*f0865ec9SKyle Evans 	nn r, r_square;
307*f0865ec9SKyle Evans 	word_t mpinv;
308*f0865ec9SKyle Evans 	int ret, isodd;
309*f0865ec9SKyle Evans 	r.magic = r_square.magic = WORD(0);
310*f0865ec9SKyle Evans 
311*f0865ec9SKyle Evans 	/* Handle the case where our modulo is even: in this case, we cannot
312*f0865ec9SKyle Evans 	 * use our Montgomery multiplication primitives as they are only suitable
313*f0865ec9SKyle Evans 	 * for odd numbers. We fallback to less efficient regular modular exponentiation.
314*f0865ec9SKyle Evans 	 */
315*f0865ec9SKyle Evans 	ret = nn_isodd(mod, &isodd); EG(ret, err);
316*f0865ec9SKyle Evans 	if(!isodd){
317*f0865ec9SKyle Evans 		/* mod is even: use the regular unoptimized modular exponentiation */
318*f0865ec9SKyle Evans 		ret = _nn_mod_pow(out, base, exp, mod);
319*f0865ec9SKyle Evans 	}
320*f0865ec9SKyle Evans 	else{
321*f0865ec9SKyle Evans 		/* mod is odd */
322*f0865ec9SKyle Evans 		/* Compute the Montgomery coefficients */
323*f0865ec9SKyle Evans 		ret = nn_compute_redc1_coefs(&r, &r_square, mod, &mpinv); EG(ret, err);
324*f0865ec9SKyle Evans 
325*f0865ec9SKyle Evans 		/* Now use the redc version */
326*f0865ec9SKyle Evans 		ret = nn_mod_pow_redc(out, base, exp, mod, &r, &r_square, mpinv);
327*f0865ec9SKyle Evans 	}
328*f0865ec9SKyle Evans 
329*f0865ec9SKyle Evans err:
330*f0865ec9SKyle Evans 	nn_uninit(&r);
331*f0865ec9SKyle Evans 	nn_uninit(&r_square);
332*f0865ec9SKyle Evans 
333*f0865ec9SKyle Evans 	return ret;
334*f0865ec9SKyle Evans }
335