Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/libecc/src/nn/nn.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
#define NN_CONSISTENCY_CHECK
17
#include <libecc/nn/nn.h>
18
19
/*
20
* Used for the conditional swap algorithm SCA
21
* resistance, see below in the implementation of
22
* nn_cnd_swap.
23
*/
24
#include <libecc/utils/utils_rand.h>
25
26
/*
27
* Except otherwise specified, all functions accept *initialized* nn.
28
* The WORD(NN_MAX_WORD_LEN + WORDSIZE) magic is here to detect modules
29
* compiled with different WORDSIZE or NN_MAX_WORD_LEN and are binary
30
* incompatible.
31
*/
32
33
#define NN_MAGIC ((word_t)((0xb4cf5d56e2023316ULL ^ (WORD(NN_MAX_WORD_LEN + WORDSIZE)))))
34
35
/*
36
* Local helper internally used to check that the storage space
37
* above wlen is made of zero words. The function does NOT check
38
* if given nn has been initialized. This must have been done
39
* by the caller.
40
*
41
* Due to its performance cost, this consistency check is used
42
* in SHOULD_HAVE macros, meaning that it will only be present
43
* in DEBUG mode. Hence the ATTRIBUTE_UNUSED so that no warning
44
* (error in -Werror) is triggered at compilation time.
45
*
46
*/
47
ATTRIBUTE_WARN_UNUSED_RET static int ATTRIBUTE_UNUSED __nn_is_wlen_consistent(nn_src_t A)
48
{
49
word_t val = 0;
50
u8 i;
51
52
for (i = A->wlen; i < NN_MAX_WORD_LEN; i++) {
53
val |= (A)->val[i];
54
}
55
56
return (val == 0);
57
}
58
59
/*
60
* Verify that pointed nn has already been initialized. This function
61
* should be used as a safety net in all function before using a nn
62
* received as parameter. Returns 0 on success, -1 on error.
63
*/
64
int nn_check_initialized(nn_src_t A)
65
{
66
int ret;
67
68
MUST_HAVE((A != NULL), ret, err);
69
MUST_HAVE((A->magic == NN_MAGIC), ret, err);
70
MUST_HAVE((A->wlen <= NN_MAX_WORD_LEN), ret, err);
71
SHOULD_HAVE(__nn_is_wlen_consistent(A), ret, err);
72
73
ret = 0;
74
75
err:
76
return ret;
77
}
78
79
/*
80
* Initialize nn from expected initial byte length 'len', setting its wlen
81
* to associated (ceil) value and clearing whole storage space. Return 0
82
* on success, -1 on error.
83
*/
84
int nn_init(nn_t A, u16 len)
85
{
86
int ret;
87
u8 i;
88
89
MUST_HAVE(((A != NULL) && (len <= NN_MAX_BYTE_LEN)), ret, err);
90
91
A->wlen = (u8)BYTE_LEN_WORDS(len);
92
A->magic = NN_MAGIC;
93
94
for (i = 0; i < NN_MAX_WORD_LEN; i++) {
95
A->val[i] = WORD(0);
96
}
97
98
ret = 0;
99
100
err:
101
return ret;
102
}
103
104
/*
105
* Uninitialize the pointed nn to prevent further use (magic field in the
106
* structure is zeroized). The associated storage space is also zeroized. If
107
* given pointer is NULL or does not point to an initialized nn, the function
108
* does nothing.
109
*/
110
void nn_uninit(nn_t A)
111
{
112
if ((A != NULL) && (A->magic == NN_MAGIC)) {
113
int i;
114
115
for (i = 0; i < NN_MAX_WORD_LEN; i++) {
116
A->val[i] = WORD(0);
117
}
118
119
A->wlen = 0;
120
A->magic = WORD(0);
121
}
122
123
return;
124
}
125
126
/*
127
* Set current value of pointed initialized nn to 0. Returns 0 on success, -1
128
* on error.
129
*/
130
int nn_zero(nn_t A)
131
{
132
int ret;
133
134
ret = nn_check_initialized(A); EG(ret, err);
135
ret = nn_init(A, 0);
136
137
err:
138
return ret;
139
}
140
141
/*
142
* Set current value of pointed initialized nn to given word value. Returns 0
143
* on success, -1 on error.
144
*/
145
int nn_set_word_value(nn_t A, word_t val)
146
{
147
int ret;
148
149
ret = nn_zero(A); EG(ret, err);
150
151
A->val[0] = val;
152
A->wlen = 1;
153
154
err:
155
return ret;
156
}
157
158
/*
159
* Set current value of pointed initialized nn to 1. Returns 0 on success, -1
160
* on error.
161
*/
162
int nn_one(nn_t A)
163
{
164
return nn_set_word_value(A, WORD(1));
165
}
166
167
/*
168
* Conditionally swap two nn's content *in constant time*. Swapping is done
169
* if 'cnd' is not zero. Nothing is done otherwise. Returns 0 on success, -1
170
* on error.
171
*
172
* Aliasing of inputs is supported.
173
*/
174
int nn_cnd_swap(int cnd, nn_t in1, nn_t in2)
175
{
176
word_t mask = WORD_MASK_IFNOTZERO(cnd);
177
u8 len, i;
178
word_t t, r;
179
volatile word_t r_mask;
180
int ret;
181
182
ret = nn_check_initialized(in1); EG(ret, err);
183
ret = nn_check_initialized(in2); EG(ret, err);
184
185
MUST_HAVE((in1->wlen <= NN_MAX_WORD_LEN), ret, err);
186
MUST_HAVE((in2->wlen <= NN_MAX_WORD_LEN), ret, err);
187
188
len = (in1->wlen >= in2->wlen) ? in1->wlen : in2->wlen;
189
190
/* Use a random word for randomly masking the delta value hamming
191
* weight as proposed in Algorithm 4 of "Nonce@once: A Single-Trace
192
* EM Side Channel Attack on Several Constant-Time Elliptic
193
* Curve Implementations in Mobile Platforms" by Alam et al.
194
*/
195
ret = get_unsafe_random((u8*)&r, sizeof(r)); EG(ret, err);
196
r_mask = r;
197
198
for (i = 0; i < NN_MAX_WORD_LEN; i++) {
199
word_t local_mask = WORD_MASK_IFNOTZERO((i < len));
200
t = ((in1->val[i] ^ in2->val[i]) & mask) ^ r_mask;
201
in1->val[i] ^= ((t & local_mask) ^ (r_mask & local_mask));
202
in2->val[i] ^= ((t & local_mask) ^ (r_mask & local_mask));
203
}
204
205
t = (word_t)(((in1->wlen ^ in2->wlen) & mask) ^ r_mask);
206
in1->wlen ^= (u8)(t ^ r_mask);
207
in2->wlen ^= (u8)(t ^ r_mask);
208
209
err:
210
return ret;
211
}
212
213
/*
214
* Adjust internal wlen attribute of given nn to new_wlen. If internal wlen
215
* attribute value is reduced, words above that limit in A are zeroized.
216
* new_wlen must be in [0, NN_MAX_WORD_LEN].
217
* The trimming is performed in constant time wrt to the length of the
218
* input to avoid leaking it.
219
* Returns 0 on success, -1 on error.
220
*/
221
int nn_set_wlen(nn_t A, u8 new_wlen)
222
{
223
int ret;
224
u8 i;
225
226
ret = nn_check_initialized(A); EG(ret, err);
227
MUST_HAVE((new_wlen <= NN_MAX_WORD_LEN), ret, err);
228
MUST_HAVE((A->wlen <= NN_MAX_WORD_LEN), ret, err);
229
230
/* Trimming performed in constant time */
231
for (i = 0; i < NN_MAX_WORD_LEN; i++) {
232
A->val[i] = (word_t)(A->val[i] & WORD_MASK_IFZERO((i >= new_wlen)));
233
}
234
235
A->wlen = new_wlen;
236
237
err:
238
return ret;
239
}
240
241
/*
242
* The function tests if given nn value is zero. The result of the test is given
243
* using 'iszero' out parameter (1 if nn is zero, 0 if it is not). The function
244
* returns 0 on success, -1 on error. 'iszero' is not meaningfull on error.
245
* When A is valid, check is done *in constant time*.
246
*/
247
int nn_iszero(nn_src_t A, int *iszero)
248
{
249
int ret, notzero;
250
u8 i;
251
252
ret = nn_check_initialized(A); EG(ret, err);
253
MUST_HAVE((A->wlen <= NN_MAX_WORD_LEN), ret, err);
254
MUST_HAVE((iszero != NULL), ret, err);
255
256
notzero = 0;
257
for (i = 0; i < NN_MAX_WORD_LEN; i++) {
258
int mask = ((i < A->wlen) ? 1 : 0);
259
notzero |= ((A->val[i] != 0) & mask);
260
}
261
262
*iszero = !notzero;
263
264
err:
265
return ret;
266
}
267
268
/*
269
* The function tests if given nn value is one. The result of the test is given
270
* using 'isone' out parameter (1 if nn is one, 0 if it is not). The function
271
* returns 0 on success, -1 on error. 'isone' is not meaningfull on error.
272
* When A is valid, check is done *in constant time*.
273
*/
274
int nn_isone(nn_src_t A, int *isone)
275
{
276
int ret, notone;
277
u8 i;
278
279
ret = nn_check_initialized(A); EG(ret, err);
280
MUST_HAVE(!(A->wlen > NN_MAX_WORD_LEN), ret, err);
281
MUST_HAVE((isone != NULL), ret, err);
282
283
/* val[0] access is ok no matter wlen value */
284
notone = (A->val[0] != 1);
285
for (i = 1; i < NN_MAX_WORD_LEN; i++) {
286
int mask = ((i < A->wlen) ? 1 : 0);
287
notone |= ((A->val[i] != 0) & mask);
288
}
289
290
*isone = !notone;
291
292
err:
293
return ret;
294
}
295
296
/*
297
* The function tests if given nn value is odd. The result of the test is given
298
* using 'isodd' out parameter (1 if nn is odd, 0 if it is not). The function
299
* returns 0 on success, -1 on error. 'isodd' is not meaningfull on error.
300
*/
301
int nn_isodd(nn_src_t A, int *isodd)
302
{
303
int ret;
304
305
ret = nn_check_initialized(A); EG(ret, err);
306
MUST_HAVE((isodd != NULL), ret, err);
307
308
*isodd = (A->wlen != 0) && (A->val[0] & 1);
309
310
err:
311
return ret;
312
}
313
314
/*
315
* Compare given nn against given word value. This is done *in constant time*
316
* (only depending on the input length, not on its value or on the word value)
317
* when provided nn is valid. The function returns 0 on success and provides
318
* the comparison value in 'cmp' parameter. -1 is returned on error, in which
319
* case 'cmp' is not meaningful.
320
*/
321
int nn_cmp_word(nn_src_t in, word_t w, int *cmp)
322
{
323
int ret, tmp = 0;
324
word_t mask;
325
u8 i;
326
327
ret = nn_check_initialized(in); EG(ret, err);
328
MUST_HAVE((cmp != NULL), ret, err);
329
330
/* No need to read, we can conclude */
331
if (in->wlen == 0) {
332
*cmp = -(w != 0);
333
ret = 0;
334
goto err;
335
}
336
337
/*
338
* Let's loop on all words above first one to see if one
339
* of those is non-zero.
340
*/
341
for (i = (u8)(in->wlen - 1); i > 0; i--) {
342
tmp |= (in->val[i] != 0);
343
}
344
345
/*
346
* Compare first word of nn w/ w if needed. This
347
* is done w/ masking to avoid doing or not doing
348
* it based on 'tmp' (i.e. fact that a high word
349
* of nn is not zero).
350
*/
351
mask = WORD_MASK_IFZERO(tmp);
352
tmp += (int)(((word_t)(in->val[i] > w)) & (mask));
353
tmp -= (int)(((word_t)(in->val[i] < w)) & (mask));
354
*cmp = tmp;
355
356
err:
357
return ret;
358
}
359
360
/*
361
* Compare given two nn 'A' and '. This is done *in constant time* (only
362
* depending on the largest length of the inputs, not on their values). The
363
* function returns 0 on success and provides the comparison value in
364
* 'cmp' parameter (0 if A == B, -1 if A < B, +1 if A > B). -1 is returned
365
* on error, in which case 'cmp' is not meaningful.
366
*
367
* Aliasing of inputs is supported.
368
*/
369
int nn_cmp(nn_src_t A, nn_src_t B, int *cmp)
370
{
371
int tmp, mask, ret, i;
372
u8 cmp_len;
373
374
ret = nn_check_initialized(A); EG(ret, err);
375
ret = nn_check_initialized(B); EG(ret, err);
376
MUST_HAVE((cmp != NULL), ret, err);
377
378
cmp_len = (A->wlen >= B->wlen) ? A->wlen : B->wlen;
379
380
tmp = 0;
381
for (i = (cmp_len - 1); i >= 0; i--) { /* ok even if cmp_len is 0 */
382
mask = !(tmp & 0x1);
383
tmp += ((A->val[i] > B->val[i]) & mask);
384
tmp -= ((A->val[i] < B->val[i]) & mask);
385
}
386
(*cmp) = tmp;
387
388
err:
389
return ret;
390
}
391
392
/*
393
* Copy given nn 'src_nn' value into 'dst_nn'. This is done *in constant time*.
394
* 'dst_nn' must point to a declared nn, but *need not be initialized*; it will
395
* be (manually) initialized by the function. 'src_nn' must have been
396
* initialized prior to the call. The function returns 0 on success, -1 on error.
397
*
398
* Alising of input and output is supported.
399
*/
400
int nn_copy(nn_t dst_nn, nn_src_t src_nn)
401
{
402
int ret;
403
u8 i;
404
405
MUST_HAVE((dst_nn != NULL), ret, err);
406
ret = nn_check_initialized(src_nn); EG(ret, err);
407
408
for (i = 0; i < NN_MAX_WORD_LEN; i++) {
409
dst_nn->val[i] = src_nn->val[i];
410
}
411
412
dst_nn->wlen = src_nn->wlen;
413
dst_nn->magic = NN_MAGIC;
414
415
err:
416
return ret;
417
}
418
419
/*
420
* Update wlen value of given nn if a set of words below wlen value are zero.
421
* The function is *not constant time*, i.e. it depends on the input value.
422
* The function returns 0 on sucess, -1 on error.
423
*/
424
int nn_normalize(nn_t in1)
425
{
426
int ret;
427
428
ret = nn_check_initialized(in1); EG(ret, err);
429
430
while ((in1->wlen > 0) && (in1->val[in1->wlen - 1] == 0)) {
431
in1->wlen--;
432
}
433
434
err:
435
return ret;
436
}
437
438
/*
439
* Convert given consecutive WORD_BYTES bytes pointed by 'val' from network (big
440
* endian) order to host order. 'val' needs not point to a word-aligned region.
441
* The function returns 0 on success, -1 on error. On success, the result is
442
* provided in 'out'. 'out' is not meaningful on error.
443
*/
444
ATTRIBUTE_WARN_UNUSED_RET static int _ntohw(const u8 *val, word_t *out)
445
{
446
word_t res = 0;
447
u8 *res_buf = (u8 *)(&res);
448
int i, ret;
449
450
MUST_HAVE(((val != NULL) && (out != NULL)), ret, err);
451
452
if (arch_is_big_endian()) {
453
/* copy bytes, one by one to avoid alignement issues */
454
for (i = 0; i < WORD_BYTES; i++) {
455
res_buf[i] = val[i];
456
}
457
} else {
458
u8 tmp;
459
460
for (i = 0; i < (WORD_BYTES / 2); i++) {
461
tmp = val[i];
462
res_buf[i] = val[WORD_BYTES - i - 1];
463
res_buf[WORD_BYTES - i - 1] = tmp;
464
}
465
466
VAR_ZEROIFY(tmp);
467
}
468
469
*out = res;
470
ret = 0;
471
472
err:
473
return ret;
474
}
475
476
/* Same as previous function but from host to network byte order. */
477
ATTRIBUTE_WARN_UNUSED_RET static inline int _htonw(const u8 *val, word_t *out)
478
{
479
return _ntohw(val, out);
480
}
481
482
/*
483
* 'out_nn' is expected to point to the storage location of a declared nn,
484
* which will be initialized by the function (i.e. given nn need not be
485
* initialized). The function then imports value (expected to be in big
486
* endian) from given buffer 'buf' of length 'buflen' into it. The function
487
* expects (and enforces) that buflen is less than or equal to NN_MAX_BYTE_LEN.
488
* The function returns 0 on success, -1 on error.
489
*/
490
int nn_init_from_buf(nn_t out_nn, const u8 *buf, u16 buflen)
491
{
492
u8 tmp[NN_MAX_BYTE_LEN];
493
u16 wpos;
494
int ret;
495
496
MUST_HAVE(((out_nn != NULL) && (buf != NULL) &&
497
(buflen <= NN_MAX_BYTE_LEN)), ret, err);
498
499
ret = local_memset(tmp, 0, (u32)(NN_MAX_BYTE_LEN - buflen)); EG(ret, err);
500
ret = local_memcpy(tmp + NN_MAX_BYTE_LEN - buflen, buf, buflen); EG(ret, err);
501
502
ret = nn_init(out_nn, buflen); EG(ret, err);
503
504
for (wpos = 0; wpos < NN_MAX_WORD_LEN; wpos++) {
505
u16 buf_pos = (u16)((NN_MAX_WORD_LEN - wpos - 1) * WORD_BYTES);
506
ret = _ntohw(tmp + buf_pos, &(out_nn->val[wpos])); EG(ret, err);
507
}
508
509
ret = local_memset(tmp, 0, NN_MAX_BYTE_LEN);
510
511
err:
512
return ret;
513
}
514
515
/*
516
* Export 'buflen' LSB bytes of given nn as a big endian buffer. If buffer
517
* length is larger than effective size of input nn, padding w/ zero is
518
* performed. If buffer size is smaller than input nn effective size,
519
* MSB bytes are simply lost in exported buffer. The function returns 0
520
* on success, -1 on error.
521
*/
522
int nn_export_to_buf(u8 *buf, u16 buflen, nn_src_t in_nn)
523
{
524
u8 *src_word_ptr, *dst_word_ptr;
525
const u8 wb = WORD_BYTES;
526
u16 remain = buflen;
527
int ret;
528
u8 i;
529
530
MUST_HAVE((buf != NULL), ret, err);
531
ret = nn_check_initialized(in_nn); EG(ret, err);
532
533
ret = local_memset(buf, 0, buflen); EG(ret, err);
534
535
/*
536
* We consider each word in input nn one at a time and convert
537
* it to big endian in a temporary word. Based on remaining
538
* length of output buffer, we copy the LSB bytes of temporary
539
* word into it at current position. That way, filling of the
540
* buffer is performed from its end to its beginning, word by
541
* word, except for the last one, which may be shorten if
542
* given buffer length is not a multiple of word length.
543
*/
544
for (i = 0; remain && (i < in_nn->wlen); i++) {
545
u16 copylen = (remain > wb) ? wb : remain;
546
word_t val;
547
548
ret = _htonw((const u8 *)&in_nn->val[i], &val); EG(ret, err);
549
550
dst_word_ptr = (buf + buflen - (i * wb) - copylen);
551
src_word_ptr = (u8 *)(&val) + wb - copylen;
552
553
ret = local_memcpy(dst_word_ptr, src_word_ptr, copylen); EG(ret, err);
554
src_word_ptr = NULL;
555
556
remain = (u16)(remain - copylen);
557
}
558
559
err:
560
return ret;
561
}
562
563
/*
564
* Given a table 'tab' pointing to a set of 'tabsize' NN elements, the
565
* function copies the value of element at position idx (idx < tabsize)
566
* in 'out' parameters. Masking is used to avoid leaking which element
567
* was copied.
568
*
569
* Note that the main copying loop is done on the maximum bits for all
570
* NN elements and not based on the specific effective size of each
571
* NN elements in 'tab'
572
*
573
* Returns 0 on success, -1 on error.
574
*
575
* Aliasing of out and the selected element inside the tab is NOT supported.
576
*/
577
int nn_tabselect(nn_t out, u8 idx, nn_src_t *tab, u8 tabsize)
578
{
579
u8 i, k;
580
word_t mask;
581
int ret;
582
583
/* Basic sanity checks */
584
MUST_HAVE(((tab != NULL) && (idx < tabsize)), ret, err);
585
586
ret = nn_check_initialized(out); EG(ret, err);
587
588
/* Zeroize out and enforce its size. */
589
ret = nn_zero(out); EG(ret, err);
590
591
out->wlen = 0;
592
593
for (k = 0; k < tabsize; k++) {
594
/* Check current element is initialized */
595
ret = nn_check_initialized(tab[k]); EG(ret, err);
596
597
mask = WORD_MASK_IFNOTZERO(idx == k);
598
599
out->wlen = (u8)(out->wlen | ((tab[k]->wlen) & mask));
600
601
for (i = 0; i < NN_MAX_WORD_LEN; i++) {
602
out->val[i] |= (tab[k]->val[i] & mask);
603
}
604
}
605
606
err:
607
return ret;
608
}
609
610