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