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