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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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