This repository has been archived by the owner on Jan 24, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 86
/
sss.c
772 lines (695 loc) · 27.5 KB
/
sss.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
/*
* Copyright (C) 2021 - This file is part of libecc project
*
* Authors:
* Ryad BENADJILA <[email protected]>
* Arnaud EBALARD <[email protected]>
*
* This software is licensed under a dual BSD and GPL v2 license.
* See LICENSE file at the root folder of the project.
*/
#include "sss_private.h"
#include "sss.h"
/*
* The purpose of this example is to implement the SSS
* (Shamir's Secret Sharing) scheme based on libecc arithmetic
* primitives. The scheme is implemented over a ~256 bit prime
* field.
*
* Secret sharing allows to combine some shares (at least k among n >= k)
* to regenerate a secret. The current code also ensures the integrity
* of the shares using HMAC. A maximum of (2**16 - 1) shares can be
* generated, and beware that the time complexity of generation heavily
* increases with k and n, and the time complexity of shares combination
* increases with k.
*
* Shares regeneration from exisiting ones is also offered although it
* is expensive in CPU cycles (as the Lagrange interpolation polynomials
* have to be evaluated for each existing share before computing new ones).
*
* !! DISCLAIMER !!
* ================
* Some efforts have been put on providing a clean code and constant time
* as well as some SCA (side-channel attacks) resistance (e.g. blinding some
* operations manipulating secrets). However, no absolute guarantee can be claimed:
* use this code knowingly and at your own risks!
*
* Also, as for all other libecc primitives, beware of randomness sources. By default,
* the library uses the OS random sources (e.g. "/dev/urandom"), but the user
* is encouraged to adapt the ../external_deps/rand.c source file to combine
* multiple sources and add entropy there depending on the context where this
* code is integrated. The security level of all the cryptographic primitives
* heavily relies on random sources quality.
*
*/
#ifndef GET_UINT16_BE
#define GET_UINT16_BE(n, b, i) \
do { \
(n) = (u16)( ((u16) (b)[(i) ]) << 8 ) \
| (u16)( ((u16) (b)[(i) + 1]) ); \
} while( 0 )
#endif
#ifndef PUT_UINT16_BE
#define PUT_UINT16_BE(n, b, i) \
do { \
(b)[(i) ] = (u8) ( (n) >> 8 ); \
(b)[(i) + 1] = (u8) ( (n) ); \
} while( 0 )
#endif
/* The prime number we use: it is close to (2**256-1) but still stricly less
* than this value, hence a theoretical security of more than 255 bits but less than
* 256 bits. This prime p is used in the prime field of secp256k1, the "bitcoin"
* curve.
*
* This can be modified with another prime, beware however of the size
* of the prime to be in line with the shared secrets sizes, and also
* that all our shares and secret lie in Fp, and hence are < p,
*
* Although bigger primes could be used, beware that SSS shares recombination
* complexity is quadratic in the number of shares, yielding impractical
* computation time when the prime is too big. Also, some elements related to
* the share generation (_sss_derive_seed) must be adapated to keep proper entropy
* if the prime (size) is modified.
*/
static const u8 prime[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f,
};
ATTRIBUTE_WARN_UNUSED_RET static int _sss_derive_seed(fp_t out, const u8 seed[SSS_SECRET_SIZE], u16 idx)
{
int ret;
u8 hmac_val[SHA512_DIGEST_SIZE];
u8 C[2];
u8 len;
nn nn_val;
/* Sanity check on sizes to avoid entropy loss through reduction biases */
MUST_HAVE((SHA512_DIGEST_SIZE >= (2 * SSS_SECRET_SIZE)), ret, err);
/* out must be initialized with a context */
ret = fp_check_initialized(out); EG(ret, err);
ret = local_memset(hmac_val, 0, sizeof(hmac_val)); EG(ret, err);
ret = local_memset(C, 0, sizeof(C)); EG(ret, err);
/* Export our idx in big endian representation on two bytes */
PUT_UINT16_BE(idx, C, 0);
len = sizeof(hmac_val);
ret = hmac(seed, SSS_SECRET_SIZE, SHA512, C, sizeof(C), hmac_val, &len); EG(ret, err);
ret = nn_init_from_buf(&nn_val, hmac_val, len); EG(ret, err);
/* Since we will put this in Fp, take the modulo */
ret = nn_mod(&nn_val, &nn_val, &(out->ctx->p)); EG(ret, err);
/* Now import our reduced value in Fp as the result of the derivation */
ret = fp_set_nn(out, &nn_val);
err:
/* Cleanup secret data */
IGNORE_RET_VAL(local_memset(hmac_val, 0, sizeof(hmac_val)));
IGNORE_RET_VAL(local_memset(C, 0, sizeof(C)));
nn_uninit(&nn_val);
return ret;
}
/***** Raw versions ***********************/
/* SSS shares and secret generation */
ATTRIBUTE_WARN_UNUSED_RET static int _sss_raw_generate(sss_share *shares, u16 k, u16 n, sss_secret *secret, boolean input_secret)
{
fp_ctx ctx;
nn p;
fp a0, a, s;
fp exp, base, tmp;
fp blind, blind_inv;
u8 secret_seed[SSS_SECRET_SIZE];
u16 idx_shift, num_shares;
int ret;
unsigned int i, j;
p.magic = WORD(0);
exp.magic = base.magic = tmp.magic = s.magic = a.magic = a0.magic = WORD(0);
blind.magic = blind_inv.magic = WORD(0);
ret = local_memset(secret_seed, 0, sizeof(secret_seed)); EG(ret, err);
MUST_HAVE((shares != NULL) && (secret != NULL), ret, err);
/* Sanity checks */
MUST_HAVE((n <= (u16)(0xffff - 1)), ret, err);
MUST_HAVE((k <= n), ret, err);
MUST_HAVE((k >= 1), ret, err);
MUST_HAVE((SSS_SECRET_SIZE == sizeof(prime)), ret, err);
/* Import our prime number and create the Fp context */
ret = nn_init_from_buf(&p, prime, sizeof(prime)); EG(ret, err);
ret = fp_ctx_init_from_p(&ctx, &p); EG(ret, err);
/* Generate a secret seed of the size of the secret that will be our base to
* generate the plolynomial coefficients.
*/
ret = get_random(secret_seed, sizeof(secret_seed)); EG(ret, err);
/* NOTE: although we could generate all our a[i] coefficients using our randomness
* source, we prefer to derive them from a single secret seed in order to optimize
* the storage space as our share generation algorithm needs to parse these a[i] multiple
* times. This time / memory tradeoff saves a lot of memory space for embedded contexts and
* avoids "malloc" usage (preserving the "no dynamic allocation" philosophy of libecc).
*
* Our secret seed is SSS_SECRET_SIZE long, so on the security side there should be no
* loss of strength/entropy. For each index i, a[i] is computed as follows:
*
* a[i] = HMAC(secret_seed, i)
* where the HMAC is interpreted as a value in Fp (i.e. modulo p), and i is represented
* as a string of 2 elements. The HMAC uses a hash function of at least twice the
* size of the secret to avoid biases in modular reduction.
*/
/* a0 is either derived from the secret seed or taken from input if
* provided.
*/
ret = fp_init(&a0, &ctx); EG(ret, err);
if(input_secret == SSS_TRUE){
/* Import the secret the user provides
* XXX: NOTE: the user shared secret MUST be in Fp! Since our prime is < (2**256 - 1),
* some 256 bit strings can be rejected here (namely those >= p and <= (2**256 - 1)).
*/
ret = fp_import_from_buf(&a0, secret->secret, SSS_SECRET_SIZE); EG(ret, err);
}
else{
/* Generate the secret from our seed */
ret = _sss_derive_seed(&a0, secret_seed, 0); EG(ret, err);
}
/* Compute the shares P(x) for x in [idx_shift + 0, ..., idx_shift + n] (or
* [idx_shift + 0, ..., idx_shift + n + 1] to avoid the 0 index),
* with idx_shift a non-zero random index shift to avoid leaking the number of shares.
*/
ret = fp_init(&base, &ctx); EG(ret, err);
ret = fp_init(&exp, &ctx); EG(ret, err);
ret = fp_init(&tmp, &ctx); EG(ret, err);
ret = fp_init(&s, &ctx); EG(ret, err);
ret = fp_init(&a, &ctx); EG(ret, err);
/* Get a random blind mask and invert it */
ret = fp_get_random(&blind, &ctx); EG(ret, err);
ret = fp_init(&blind_inv, &ctx); EG(ret, err);
ret = fp_inv(&blind_inv, &blind); EG(ret, err);
/* Generate a non-zero random index base for x to avoid leaking
* the number of shares. We could use a static sequence from x = 1 to n
* but this would leak some information to the participants about the number
* of shares (e.g. if a participant gets the share with x = 4, she surely knows
* that n >= 4). To avoid the leak we randomize the base value of the index where
* we begin our x.
*/
idx_shift = 0;
while(idx_shift == 0){
ret = get_random((u8*)&idx_shift, sizeof(idx_shift)); EG(ret, err);
}
num_shares = 0;
i = 0;
while(num_shares < n){
_sss_raw_share *cur_share_i = &(shares[num_shares].raw_share);
u16 curr_idx = (u16)(idx_shift + i);
if(curr_idx == 0){
/* Skip the index 0 specific case */
i++;
continue;
}
/* Set s[i] to the a[0] as blinded initial value */
ret = fp_mul(&s, &blind, &a0); EG(ret, err);
/* Get a random base x as u16 for share index */
ret = fp_set_word_value(&base, (word_t)curr_idx); EG(ret, err);
/* Set the exp to 1 */
ret = fp_one(&exp); EG(ret, err);
for(j = 1; j < k; j++){
/* Compute x**j by iterative multiplications */
ret = fp_mul_monty(&exp, &exp, &base); EG(ret, err);
/* Compute our a[j] coefficient */
ret = _sss_derive_seed(&a, secret_seed, (u16)j); EG(ret, err);
/* Blind a[j] */
ret = fp_mul_monty(&a, &a, &blind); EG(ret, err);
/* NOTE1: actually, the real a[j] coefficients are _sss_derive_seed(secret_seed, j)
* multiplied by some power of r^-1 (the Montgomery constant), but this is OK as
* we need any random values (computable from the secret seed) here. We use this "trick"
* to be able to use our more performant redcified versions of Fp multiplication.
*
* NOTE2: this trick makes also this generation not deterministic with the same seed
* on binaries with different WORD sizes (16, 32, 64 bits) as the r Montgomery constant will
* differ depending on this size. However, this is not really an issue per se for our SSS
* as we are in our generation primitive and the a[j] coefficients are expected to be
* random (the only drawback is that deterministic test vectors will not be consistent
* across WORD sizes).
*/
/* Accumulate */
ret = fp_mul_monty(&tmp, &exp, &a); EG(ret, err);
ret = fp_add(&s, &s, &tmp); EG(ret, err);
}
/* Export the computed share */
PUT_UINT16_BE(curr_idx, (u8*)&(cur_share_i->index), 0);
/* Unblind */
ret = fp_mul(&s, &s, &blind_inv); EG(ret, err);
ret = fp_export_to_buf(cur_share_i->share, SSS_SECRET_SIZE, &s); EG(ret, err);
num_shares++;
i++;
}
/* The secret is a[0] */
ret = fp_export_to_buf(secret->secret, SSS_SECRET_SIZE, &a0);
err:
/* We can throw away our secret seed now that the shares have
* been generated.
*/
IGNORE_RET_VAL(local_memset(secret_seed, 0, sizeof(secret_seed)));
IGNORE_RET_VAL(local_memset(&ctx, 0, sizeof(ctx)));
nn_uninit(&p);
fp_uninit(&a0);
fp_uninit(&a);
fp_uninit(&s);
fp_uninit(&base);
fp_uninit(&exp);
fp_uninit(&tmp);
fp_uninit(&blind);
fp_uninit(&blind_inv);
return ret;
}
/* SSS helper to compute Lagrange interpolation on an input value.
* - k is the number of shares pointed by the shares pointer
* - secret is the computed secret
* - val is the 'index' on which the Lagrange interpolation must be computed, i.e.
* the idea is to have using Lagrage formulas the value f(val) where f is our polynomial. Of course
* the proper value can only be computed if enough shares k are provided (the interpolation
* does not hold in other cases and the result will be an incorrect value)
*/
ATTRIBUTE_WARN_UNUSED_RET static int _sss_raw_lagrange(const sss_share *shares, u16 k, sss_secret *secret, u16 val)
{
fp_ctx ctx;
nn p;
fp s, x, y;
fp x_i, x_j, tmp, tmp2;
fp blind, blind_inv, r_inv;
int ret;
unsigned int i, j;
p.magic = WORD(0);
x_i.magic = x_j.magic = tmp.magic = tmp2.magic = s.magic = y.magic = x.magic = WORD(0);
blind.magic = blind_inv.magic = r_inv.magic = WORD(0);
MUST_HAVE((shares != NULL) && (secret != NULL), ret, err);
/* Sanity checks */
MUST_HAVE((k >= 1), ret, err);
MUST_HAVE((SSS_SECRET_SIZE == sizeof(prime)), ret, err);
/* Import our prime number and create the Fp context */
ret = nn_init_from_buf(&p, prime, sizeof(prime)); EG(ret, err);
ret = fp_ctx_init_from_p(&ctx, &p); EG(ret, err);
/* Recombine our shared secrets */
ret = fp_init(&s, &ctx); EG(ret, err);
ret = fp_init(&y, &ctx); EG(ret, err);
ret = fp_init(&x_i, &ctx); EG(ret, err);
ret = fp_init(&x_j, &ctx); EG(ret, err);
ret = fp_init(&tmp, &ctx); EG(ret, err);
ret = fp_init(&tmp2, &ctx); EG(ret, err);
if(val != 0){
/* NOTE: we treat the case 'val = 0' in a specific case for
* optimization. This optimization is of interest since computing
* f(0) (where f(.) is our polynomial) is the formula for getting the
* SSS secret (which happens to be the constant of degree 0 of the
* polynomial).
*/
ret = fp_init(&x, &ctx); EG(ret, err);
ret = fp_set_word_value(&x, (word_t)val); EG(ret, err);
}
/* Get a random blind mask and invert it */
ret = fp_get_random(&blind, &ctx); EG(ret, err);
ret = fp_init(&blind_inv, &ctx); EG(ret, err);
ret = fp_inv(&blind_inv, &blind); EG(ret, err);
/* Perform the computation of r^-1 to optimize our multiplications using Montgomery
* multiplication in the main loop.
*/
ret = fp_init(&r_inv, &ctx); EG(ret, err);
ret = fp_set_nn(&r_inv, &(ctx.r)); EG(ret, err);
ret = fp_inv(&r_inv, &r_inv); EG(ret, err);
/* Proceed with the interpolation */
for(i = 0; i < k; i++){
u16 curr_idx;
const _sss_raw_share *cur_share_i = &(shares[i].raw_share);
/* Import s[i] */
ret = fp_import_from_buf(&s, cur_share_i->share, SSS_SECRET_SIZE); EG(ret, err);
/* Blind s[i] */
ret = fp_mul_monty(&s, &s, &blind); EG(ret, err);
/* Get the index */
GET_UINT16_BE(curr_idx, (const u8*)&(cur_share_i->index), 0);
ret = fp_set_word_value(&x_i, (word_t)(curr_idx)); EG(ret, err);
/* Initialize multiplication with "one" (actually Montgomery r^-1 for multiplication optimization) */
ret = fp_copy(&tmp2, &r_inv); EG(ret, err);
/* Compute the product for all k other than i
* NOTE: we use fp_mul in its redcified version as the multiplication by r^-1 is
* cancelled by the fraction of (x_j - x) * r^-1 / (x_j - x_i) * r^-1 = (x_j - x) / (x_j - x_i)
*/
for(j = 0; j < k; j++){
const _sss_raw_share *cur_share_j = &(shares[j].raw_share);
GET_UINT16_BE(curr_idx, (const u8*)&(cur_share_j->index), 0);
ret = fp_set_word_value(&x_j, (word_t)(curr_idx)); EG(ret, err);
if(j != i){
if(val != 0){
ret = fp_sub(&tmp, &x_j, &x); EG(ret, err);
ret = fp_mul_monty(&s, &s, &tmp); EG(ret, err);
}
else{
/* NOTE: we treat the case 'val = 0' in a specific case for
* optimization. This optimization is of interest since computing
* f(0) (where f(.) is our polynomial) is the formula for getting the
* SSS secret (which happens to be the constant of degree 0 of the
* polynomial).
*/
ret = fp_mul_monty(&s, &s, &x_j); EG(ret, err);
}
ret = fp_sub(&tmp, &x_j, &x_i); EG(ret, err);
ret = fp_mul_monty(&tmp2, &tmp2, &tmp); EG(ret, err);
}
}
/* Invert all the (x_j - x_i) poducts */
ret = fp_inv(&tmp, &tmp2); EG(ret, err);
ret = fp_mul_monty(&s, &s, &tmp); EG(ret, err);
/* Accumulate in secret */
ret = fp_add(&y, &y, &s); EG(ret, err);
}
/* Unblind y */
ret = fp_redcify(&y, &y); EG(ret, err);
ret = fp_mul(&y, &y, &blind_inv); EG(ret, err);
/* We should have our secret in y */
ret = fp_export_to_buf(secret->secret, SSS_SECRET_SIZE, &y);
err:
IGNORE_RET_VAL(local_memset(&ctx, 0, sizeof(ctx)));
nn_uninit(&p);
fp_uninit(&s);
fp_uninit(&y);
fp_uninit(&x_i);
fp_uninit(&x_j);
fp_uninit(&tmp);
fp_uninit(&tmp2);
fp_uninit(&blind);
fp_uninit(&blind_inv);
fp_uninit(&r_inv);
if(val != 0){
fp_uninit(&x);
}
return ret;
}
/* SSS shares and secret combination */
ATTRIBUTE_WARN_UNUSED_RET static int _sss_raw_combine(const sss_share *shares, u16 k, sss_secret *secret)
{
return _sss_raw_lagrange(shares, k, secret, 0);
}
/***** Secure versions (public APIs) ***********************/
/* SSS shares and secret generation:
* Inputs:
* - n: is the number of shares to generate
* - k: the quorum of shares to regenerate the secret (of course k <= n)
* - secret: the secret value when input_secret is set to 'true'
* Output:
* - shares: a pointer to the generated n shares
* - secret: the secret value when input_secret is set to 'false', this
* value being randomly generated
*/
int sss_generate(sss_share *shares, unsigned short k, unsigned short n, sss_secret *secret, boolean input_secret)
{
int ret;
unsigned int i;
u8 len;
u8 session_id[SSS_SESSION_ID_SIZE];
ret = local_memset(session_id, 0, sizeof(session_id)); EG(ret, err);
/* Generate raw shares */
ret = _sss_raw_generate(shares, k, n, secret, input_secret); EG(ret, err);
/* Sanity check */
MUST_HAVE((SSS_HMAC_SIZE == sizeof(shares[0].raw_share_hmac)), ret, err);
MUST_HAVE((SHA256_DIGEST_SIZE >= sizeof(shares[0].raw_share_hmac)), ret, err);
/* Generate a random session ID */
ret = get_random(session_id, sizeof(session_id)); EG(ret, err);
/* Compute the authenticity seal for each share with HMAC */
for(i = 0; i < n; i++){
_sss_raw_share *cur_share = &(shares[i].raw_share);
u8 *cur_id = (u8*)&(shares[i].session_id);
u8 *cur_share_hmac = (u8*)&(shares[i].raw_share_hmac);
/* NOTE: we 'abuse' casts here for shares[i].raw_share to u8*, but this should be OK since
* our structures are packed.
*/
const u8 *inputs[3] = { (const u8*)cur_share, cur_id, NULL };
const u32 ilens[3] = { sizeof(*cur_share), SSS_SESSION_ID_SIZE, 0 };
/* Copy the session ID */
ret = local_memcpy(cur_id, session_id, SSS_SESSION_ID_SIZE); EG(ret, err);
len = SSS_HMAC_SIZE;
ret = hmac_scattered((const u8*)secret, SSS_SECRET_SIZE, SHA256, inputs, ilens, cur_share_hmac, &len); EG(ret, err);
}
err:
IGNORE_RET_VAL(local_memset(session_id, 0, sizeof(session_id)));
return ret;
}
/* SSS shares and secret combination
* Inputs:
* - k: the quorum of shares to regenerate the secret
* - shares: a pointer to the k shares
* Output:
* - secret: the secret value computed from the k shares
*/
int sss_combine(const sss_share *shares, unsigned short k, sss_secret *secret)
{
int ret, cmp;
unsigned int i;
u8 hmac_val[SSS_HMAC_SIZE];
u8 len;
ret = local_memset(hmac_val, 0, sizeof(hmac_val)); EG(ret, err);
/* Recombine raw shares */
ret = _sss_raw_combine(shares, k, secret); EG(ret, err);
/* Compute and check the authenticity seal for each HMAC */
for(i = 0; i < k; i++){
const _sss_raw_share *cur_share = &(shares[i].raw_share);
const u8 *cur_id = (const u8*)&(shares[i].session_id);
const u8 *cur_id0 = (const u8*)&(shares[0].session_id);
const u8 *cur_share_hmac = (const u8*)&(shares[i].raw_share_hmac);
/* NOTE: we 'abuse' casts here for shares[i].raw_share to u8*, but this should be OK since
* our structures are packed.
*/
const u8 *inputs[3] = { (const u8*)cur_share, cur_id, NULL };
const u32 ilens[3] = { sizeof(*cur_share), SSS_SESSION_ID_SIZE, 0 };
/* Check that all our shares have the same session ID, return an error otherwise */
ret = are_equal(cur_id, cur_id0, SSS_SESSION_ID_SIZE, &cmp); EG(ret, err);
if(!cmp){
#ifdef VERBOSE
ext_printf("[-] sss_combine error for share %d / %d: session ID is not OK!\n", i, k);
#endif
ret = -1;
goto err;
}
len = sizeof(hmac_val);
ret = hmac_scattered((const u8*)secret, SSS_SECRET_SIZE, SHA256, inputs, ilens, hmac_val, &len); EG(ret, err);
/* Check the HMAC */
ret = are_equal(hmac_val, cur_share_hmac, len, &cmp); EG(ret, err);
if(!cmp){
#ifdef VERBOSE
ext_printf("[-] sss_combine error for share %d / %d: HMAC is not OK!\n", i, k);
#endif
ret = -1;
goto err;
}
}
err:
IGNORE_RET_VAL(local_memset(hmac_val, 0, sizeof(hmac_val)));
return ret;
}
/* SSS shares regeneration from existing shares
* Inputs:
* - shares: a pointer to the input k shares allowing the regeneration
* - n: is the number of shares to regenerate
* - k: the input shares (of course k <= n)
* Output:
* - shares: a pointer to the generated n shares (among which the k first are
* the ones provided as inputs)
* - secret: the recomputed secret value
*/
int sss_regenerate(sss_share *shares, unsigned short k, unsigned short n, sss_secret *secret)
{
int ret, cmp;
unsigned int i;
u16 max_idx, num_shares;
u8 hmac_val[SSS_HMAC_SIZE];
u8 len;
/* Sanity check */
MUST_HAVE((n <= (u16)(0xffff - 1)), ret, err);
MUST_HAVE((n >= k), ret, err);
ret = local_memset(hmac_val, 0, sizeof(hmac_val)); EG(ret, err);
/* Compute the secret */
ret = _sss_raw_lagrange(shares, k, secret, 0); EG(ret, err);
/* Check the authenticity of our shares */
for(i = 0; i < k; i++){
_sss_raw_share *cur_share = &(shares[i].raw_share);
u8 *cur_id = (u8*)&(shares[i].session_id);
u8 *cur_id0 = (u8*)&(shares[0].session_id);
u8 *cur_share_hmac = (u8*)&(shares[i].raw_share_hmac);
/* NOTE: we 'abuse' casts here for shares[i].raw_share to u8*, but this should be OK since
* our structures are packed.
*/
const u8 *inputs[3] = { (const u8*)cur_share, cur_id, NULL };
const u32 ilens[3] = { sizeof(*cur_share), SSS_SESSION_ID_SIZE, 0 };
/* Check that all our shares have the same session ID, return an error otherwise */
ret = are_equal(cur_id, cur_id0, SSS_SESSION_ID_SIZE, &cmp); EG(ret, err);
if(!cmp){
#ifdef VERBOSE
ext_printf("[-] sss_regenerate error for share %d / %d: session ID is not OK!\n", i, k);
#endif
ret = -1;
goto err;
}
len = sizeof(hmac_val);
/* NOTE: we 'abuse' cast here for secret to (const u8*), but this should be OK since our
* structures are packed.
*/
ret = hmac_scattered((const u8*)secret, SSS_SECRET_SIZE, SHA256, inputs, ilens, hmac_val, &len); EG(ret, err);
ret = are_equal(hmac_val, cur_share_hmac, len, &cmp); EG(ret, err);
if(!cmp){
#ifdef VERBOSE
ext_printf("[-] sss_regenerate error for share %d / %d: HMAC is not OK!\n", i, k);
#endif
ret = -1;
goto err;
}
}
/* Our secret regeneration consists of determining the maximum index, and
* proceed with Lagrange interpolation on new values.
*/
max_idx = 0;
for(i = 0; i < k; i++){
u16 curr_idx;
GET_UINT16_BE(curr_idx, (u8*)&(shares[i].raw_share.index), 0);
if(curr_idx > max_idx){
max_idx = curr_idx;
}
}
/* Now regenerate as many shares as we need */
num_shares = 0;
i = k;
while(num_shares < (n - k)){
_sss_raw_share *cur_share = &(shares[k + num_shares].raw_share);
u8 *cur_id = (u8*)&(shares[k + num_shares].session_id);
u8 *cur_id0 = (u8*)&(shares[0].session_id);
u8 *cur_share_hmac = (u8*)&(shares[k + num_shares].raw_share_hmac);
u16 curr_idx;
/* NOTE: we 'abuse' casts here for shares[i].raw_share.share to sss_secret*, but this should be OK since
* our shares[i].raw_share.share is a SSS_SECRET_SIZE as the sss_secret.secret type encapsulates and our
* structures are packed.
*/
const u8 *inputs[3] = { (const u8*)cur_share, cur_id, NULL };
const u32 ilens[3] = { sizeof(*cur_share), SSS_SESSION_ID_SIZE, 0 };
/* Skip the index = 0 case */
curr_idx = (u16)(max_idx + (u16)(i - k + 1));
if(curr_idx == 0){
i++;
continue;
}
/* Copy our session ID */
ret = local_memcpy(cur_id, cur_id0, SSS_SESSION_ID_SIZE); EG(ret, err);
ret = _sss_raw_lagrange(shares, k, (sss_secret*)(cur_share->share), curr_idx); EG(ret, err);
PUT_UINT16_BE(curr_idx, (u8*)&(cur_share->index), 0);
/* Compute the HMAC */
len = SSS_HMAC_SIZE;
ret = hmac_scattered((const u8*)secret, SSS_SECRET_SIZE, SHA256, inputs, ilens, cur_share_hmac, &len); EG(ret, err);
num_shares++;
i++;
}
err:
IGNORE_RET_VAL(local_memset(hmac_val, 0, sizeof(hmac_val)));
return ret;
}
/********* main test program for SSS *************/
#ifdef SSS
#include "utils/print_buf.h"
#define K 50
#define N 150
#define MAX_N 200
int main(int argc, char *argv[])
{
int ret = 0;
unsigned int i;
sss_share shares[MAX_N];
sss_share shares_[MAX_N];
sss_secret secret;
FORCE_USED_VAR(argc);
FORCE_USED_VAR(argv);
/* Generate N shares for SSS with at least K shares OK among N */
ext_printf("[+] Generating the secrets %d / %d, call should be OK\n", K, N);
ret = local_memset(&secret, 0x00, sizeof(secret)); EG(ret, err);
/* NOTE: 'false' here means that we let the library generate the secret randomly */
ret = sss_generate(shares, K, N, &secret, SSS_FALSE);
if(ret){
ext_printf(" [X] Error: sss_generate error\n");
goto err;
}
else{
buf_print(" secret", (u8*)&secret, SSS_SECRET_SIZE); EG(ret, err);
}
/* Shuffle shares */
for(i = 0; i < N; i++){
shares_[i] = shares[N - 1 - i];
}
/* Combine (k-1) shares: this call should trigger an ERROR */
ext_printf("[+] Combining the secrets with less shares: call should trigger an error\n");
ret = local_memset(&secret, 0x00, sizeof(secret)); EG(ret, err);
ret = sss_combine(shares_, K - 1, &secret);
if (ret) {
ext_printf(" [X] Error: sss_combine error\n");
} else{
buf_print(" secret", (u8*)&secret, SSS_SECRET_SIZE);
}
/* Combine k shares: this call should be OK and recombine the initial
* secret
*/
ext_printf("[+] Combining the secrets with minimum shares: call should be OK\n");
ret = local_memset(&secret, 0x00, sizeof(secret)); EG(ret, err);
ret = sss_combine(shares_, K, &secret);
if (ret) {
ext_printf(" [X] Error: sss_combine error\n");
goto err;
} else {
buf_print(" secret", (u8*)&secret, SSS_SECRET_SIZE);
}
/* Combine k shares: this call should be OK and recombine the initial
* secret
*/
ext_printf("[+] Combining the secrets with more shares: call should be OK\n");
ret = local_memset(&secret, 0x00, sizeof(secret)); EG(ret, err);
ret = sss_combine(shares_, K + 1, &secret);
if (ret) {
ext_printf(" [X] Error: sss_combine error\n");
goto err;
} else {
buf_print(" secret", (u8*)&secret, SSS_SECRET_SIZE);
}
/* Combine with a corrupted share: call should trigger an error */
ext_printf("[+] Combining the secrets with more shares but one corrupted: call should trigger an error\n");
ret = local_memset(&secret, 0x00, sizeof(secret)); EG(ret, err);
shares_[K].raw_share.share[0] = 0x00;
ret = sss_combine(shares_, K + 1, &secret);
if (ret) {
ext_printf(" [X] Error: sss_combine error\n");
} else {
buf_print(" secret", (u8*)&secret, SSS_SECRET_SIZE);
}
/* Regenerate more shares! call should be OK */
ext_printf("[+] Regenerating more shares: call should be OK\n");
ret = local_memset(&secret, 0x00, sizeof(secret)); EG(ret, err);
ret = sss_regenerate(shares, K, MAX_N, &secret); EG(ret, err);
if (ret) {
ext_printf(" [X] Error: sss_regenerate error\n");
goto err;
} else {
buf_print(" secret", (u8*)&secret, SSS_SECRET_SIZE);
}
/* Shuffle shares */
for(i = 0; i < MAX_N; i++){
shares_[i] = shares[MAX_N - 1 - i];
}
/* Combine newly generated shares: call should be OK */
ext_printf("[+] Combining the secrets with newly generated shares: call should be OK\n");
ret = local_memset(&secret, 0x00, sizeof(secret)); EG(ret, err);
ret = sss_combine(shares_, K, &secret);
if (ret) {
ext_printf(" [X] Error: sss_combine error\n");
goto err;
} else {
buf_print(" secret", (u8*)&secret, SSS_SECRET_SIZE);
}
/* Modify the session ID of one of the shares: call should trigger an error */
ext_printf("[+] Combining the secrets with newly generated shares and a bad session ID: call should trigger an error\n");
ret = local_memset(&secret, 0x00, sizeof(secret)); EG(ret, err);
shares_[1].session_id[0] = 0x00;
ret = sss_combine(shares_, K, &secret);
if (ret) {
ext_printf(" [X] Error: sss_combine error\n");
} else {
buf_print(" secret", (u8*)&secret, SSS_SECRET_SIZE);
}
ret = 0;
err:
return ret;
}
#endif