Path: blob/main/crypto/libecc/src/examples/sss/sss.c
34907 views
/*1* Copyright (C) 2021 - This file is part of libecc project2*3* Authors:4* Ryad BENADJILA <[email protected]>5* Arnaud EBALARD <[email protected]>6*7* This software is licensed under a dual BSD and GPL v2 license.8* See LICENSE file at the root folder of the project.9*/10#include "sss_private.h"11#include "sss.h"1213/*14* The purpose of this example is to implement the SSS15* (Shamir's Secret Sharing) scheme based on libecc arithmetic16* primitives. The scheme is implemented over a ~256 bit prime17* field.18*19* Secret sharing allows to combine some shares (at least k among n >= k)20* to regenerate a secret. The current code also ensures the integrity21* of the shares using HMAC. A maximum of (2**16 - 1) shares can be22* generated, and beware that the time complexity of generation heavily23* increases with k and n, and the time complexity of shares combination24* increases with k.25*26* Shares regeneration from exisiting ones is also offered although it27* is expensive in CPU cycles (as the Lagrange interpolation polynomials28* have to be evaluated for each existing share before computing new ones).29*30* !! DISCLAIMER !!31* ================32* Some efforts have been put on providing a clean code and constant time33* as well as some SCA (side-channel attacks) resistance (e.g. blinding some34* operations manipulating secrets). However, no absolute guarantee can be claimed:35* use this code knowingly and at your own risks!36*37* Also, as for all other libecc primitives, beware of randomness sources. By default,38* the library uses the OS random sources (e.g. "/dev/urandom"), but the user39* is encouraged to adapt the ../external_deps/rand.c source file to combine40* multiple sources and add entropy there depending on the context where this41* code is integrated. The security level of all the cryptographic primitives42* heavily relies on random sources quality.43*44*/4546#ifndef GET_UINT16_BE47#define GET_UINT16_BE(n, b, i) \48do { \49(n) = (u16)( ((u16) (b)[(i) ]) << 8 ) \50| (u16)( ((u16) (b)[(i) + 1]) ); \51} while( 0 )52#endif5354#ifndef PUT_UINT16_BE55#define PUT_UINT16_BE(n, b, i) \56do { \57(b)[(i) ] = (u8) ( (n) >> 8 ); \58(b)[(i) + 1] = (u8) ( (n) ); \59} while( 0 )60#endif6162/* The prime number we use: it is close to (2**256-1) but still stricly less63* than this value, hence a theoretical security of more than 255 bits but less than64* 256 bits. This prime p is used in the prime field of secp256k1, the "bitcoin"65* curve.66*67* This can be modified with another prime, beware however of the size68* of the prime to be in line with the shared secrets sizes, and also69* that all our shares and secret lie in Fp, and hence are < p,70*71* Although bigger primes could be used, beware that SSS shares recombination72* complexity is quadratic in the number of shares, yielding impractical73* computation time when the prime is too big. Also, some elements related to74* the share generation (_sss_derive_seed) must be adapated to keep proper entropy75* if the prime (size) is modified.76*/77static const u8 prime[] = {780xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,790xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,800xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,810xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f,82};8384ATTRIBUTE_WARN_UNUSED_RET static int _sss_derive_seed(fp_t out, const u8 seed[SSS_SECRET_SIZE], u16 idx)85{86int ret;87u8 hmac_val[SHA512_DIGEST_SIZE];88u8 C[2];89u8 len;90nn nn_val;9192/* Sanity check on sizes to avoid entropy loss through reduction biases */93MUST_HAVE((SHA512_DIGEST_SIZE >= (2 * SSS_SECRET_SIZE)), ret, err);9495/* out must be initialized with a context */96ret = fp_check_initialized(out); EG(ret, err);9798ret = local_memset(hmac_val, 0, sizeof(hmac_val)); EG(ret, err);99ret = local_memset(C, 0, sizeof(C)); EG(ret, err);100101/* Export our idx in big endian representation on two bytes */102PUT_UINT16_BE(idx, C, 0);103104len = sizeof(hmac_val);105ret = hmac(seed, SSS_SECRET_SIZE, SHA512, C, sizeof(C), hmac_val, &len); EG(ret, err);106107ret = nn_init_from_buf(&nn_val, hmac_val, len); EG(ret, err);108/* Since we will put this in Fp, take the modulo */109ret = nn_mod(&nn_val, &nn_val, &(out->ctx->p)); EG(ret, err);110/* Now import our reduced value in Fp as the result of the derivation */111ret = fp_set_nn(out, &nn_val);112113err:114/* Cleanup secret data */115IGNORE_RET_VAL(local_memset(hmac_val, 0, sizeof(hmac_val)));116IGNORE_RET_VAL(local_memset(C, 0, sizeof(C)));117nn_uninit(&nn_val);118119return ret;120}121122/***** Raw versions ***********************/123/* SSS shares and secret generation */124ATTRIBUTE_WARN_UNUSED_RET static int _sss_raw_generate(sss_share *shares, u16 k, u16 n, sss_secret *secret, boolean input_secret)125{126fp_ctx ctx;127nn p;128fp a0, a, s;129fp exp, base, tmp;130fp blind, blind_inv;131u8 secret_seed[SSS_SECRET_SIZE];132u16 idx_shift, num_shares;133int ret;134unsigned int i, j;135p.magic = WORD(0);136exp.magic = base.magic = tmp.magic = s.magic = a.magic = a0.magic = WORD(0);137blind.magic = blind_inv.magic = WORD(0);138139ret = local_memset(secret_seed, 0, sizeof(secret_seed)); EG(ret, err);140141MUST_HAVE((shares != NULL) && (secret != NULL), ret, err);142/* Sanity checks */143MUST_HAVE((n <= (u16)(0xffff - 1)), ret, err);144MUST_HAVE((k <= n), ret, err);145MUST_HAVE((k >= 1), ret, err);146MUST_HAVE((SSS_SECRET_SIZE == sizeof(prime)), ret, err);147148/* Import our prime number and create the Fp context */149ret = nn_init_from_buf(&p, prime, sizeof(prime)); EG(ret, err);150ret = fp_ctx_init_from_p(&ctx, &p); EG(ret, err);151152/* Generate a secret seed of the size of the secret that will be our base to153* generate the plolynomial coefficients.154*/155ret = get_random(secret_seed, sizeof(secret_seed)); EG(ret, err);156/* NOTE: although we could generate all our a[i] coefficients using our randomness157* source, we prefer to derive them from a single secret seed in order to optimize158* the storage space as our share generation algorithm needs to parse these a[i] multiple159* times. This time / memory tradeoff saves a lot of memory space for embedded contexts and160* avoids "malloc" usage (preserving the "no dynamic allocation" philosophy of libecc).161*162* Our secret seed is SSS_SECRET_SIZE long, so on the security side there should be no163* loss of strength/entropy. For each index i, a[i] is computed as follows:164*165* a[i] = HMAC(secret_seed, i)166* where the HMAC is interpreted as a value in Fp (i.e. modulo p), and i is represented167* as a string of 2 elements. The HMAC uses a hash function of at least twice the168* size of the secret to avoid biases in modular reduction.169*/170171/* a0 is either derived from the secret seed or taken from input if172* provided.173*/174ret = fp_init(&a0, &ctx); EG(ret, err);175if(input_secret == SSS_TRUE){176/* Import the secret the user provides177* XXX: NOTE: the user shared secret MUST be in Fp! Since our prime is < (2**256 - 1),178* some 256 bit strings can be rejected here (namely those >= p and <= (2**256 - 1)).179*/180ret = fp_import_from_buf(&a0, secret->secret, SSS_SECRET_SIZE); EG(ret, err);181}182else{183/* Generate the secret from our seed */184ret = _sss_derive_seed(&a0, secret_seed, 0); EG(ret, err);185}186187/* Compute the shares P(x) for x in [idx_shift + 0, ..., idx_shift + n] (or188* [idx_shift + 0, ..., idx_shift + n + 1] to avoid the 0 index),189* with idx_shift a non-zero random index shift to avoid leaking the number of shares.190*/191ret = fp_init(&base, &ctx); EG(ret, err);192ret = fp_init(&exp, &ctx); EG(ret, err);193ret = fp_init(&tmp, &ctx); EG(ret, err);194ret = fp_init(&s, &ctx); EG(ret, err);195ret = fp_init(&a, &ctx); EG(ret, err);196/* Get a random blind mask and invert it */197ret = fp_get_random(&blind, &ctx); EG(ret, err);198ret = fp_init(&blind_inv, &ctx); EG(ret, err);199ret = fp_inv(&blind_inv, &blind); EG(ret, err);200/* Generate a non-zero random index base for x to avoid leaking201* the number of shares. We could use a static sequence from x = 1 to n202* but this would leak some information to the participants about the number203* of shares (e.g. if a participant gets the share with x = 4, she surely knows204* that n >= 4). To avoid the leak we randomize the base value of the index where205* we begin our x.206*/207idx_shift = 0;208while(idx_shift == 0){209ret = get_random((u8*)&idx_shift, sizeof(idx_shift)); EG(ret, err);210}211num_shares = 0;212i = 0;213while(num_shares < n){214_sss_raw_share *cur_share_i = &(shares[num_shares].raw_share);215u16 curr_idx = (u16)(idx_shift + i);216if(curr_idx == 0){217/* Skip the index 0 specific case */218i++;219continue;220}221/* Set s[i] to the a[0] as blinded initial value */222ret = fp_mul(&s, &blind, &a0); EG(ret, err);223/* Get a random base x as u16 for share index */224ret = fp_set_word_value(&base, (word_t)curr_idx); EG(ret, err);225/* Set the exp to 1 */226ret = fp_one(&exp); EG(ret, err);227for(j = 1; j < k; j++){228/* Compute x**j by iterative multiplications */229ret = fp_mul_monty(&exp, &exp, &base); EG(ret, err);230/* Compute our a[j] coefficient */231ret = _sss_derive_seed(&a, secret_seed, (u16)j); EG(ret, err);232/* Blind a[j] */233ret = fp_mul_monty(&a, &a, &blind); EG(ret, err);234/* NOTE1: actually, the real a[j] coefficients are _sss_derive_seed(secret_seed, j)235* multiplied by some power of r^-1 (the Montgomery constant), but this is OK as236* we need any random values (computable from the secret seed) here. We use this "trick"237* to be able to use our more performant redcified versions of Fp multiplication.238*239* NOTE2: this trick makes also this generation not deterministic with the same seed240* on binaries with different WORD sizes (16, 32, 64 bits) as the r Montgomery constant will241* differ depending on this size. However, this is not really an issue per se for our SSS242* as we are in our generation primitive and the a[j] coefficients are expected to be243* random (the only drawback is that deterministic test vectors will not be consistent244* across WORD sizes).245*/246/* Accumulate */247ret = fp_mul_monty(&tmp, &exp, &a); EG(ret, err);248ret = fp_add(&s, &s, &tmp); EG(ret, err);249}250/* Export the computed share */251PUT_UINT16_BE(curr_idx, (u8*)&(cur_share_i->index), 0);252/* Unblind */253ret = fp_mul(&s, &s, &blind_inv); EG(ret, err);254ret = fp_export_to_buf(cur_share_i->share, SSS_SECRET_SIZE, &s); EG(ret, err);255num_shares++;256i++;257}258/* The secret is a[0] */259ret = fp_export_to_buf(secret->secret, SSS_SECRET_SIZE, &a0);260261err:262/* We can throw away our secret seed now that the shares have263* been generated.264*/265IGNORE_RET_VAL(local_memset(secret_seed, 0, sizeof(secret_seed)));266IGNORE_RET_VAL(local_memset(&ctx, 0, sizeof(ctx)));267nn_uninit(&p);268fp_uninit(&a0);269fp_uninit(&a);270fp_uninit(&s);271fp_uninit(&base);272fp_uninit(&exp);273fp_uninit(&tmp);274fp_uninit(&blind);275fp_uninit(&blind_inv);276277return ret;278}279280/* SSS helper to compute Lagrange interpolation on an input value.281* - k is the number of shares pointed by the shares pointer282* - secret is the computed secret283* - val is the 'index' on which the Lagrange interpolation must be computed, i.e.284* the idea is to have using Lagrage formulas the value f(val) where f is our polynomial. Of course285* the proper value can only be computed if enough shares k are provided (the interpolation286* does not hold in other cases and the result will be an incorrect value)287*/288ATTRIBUTE_WARN_UNUSED_RET static int _sss_raw_lagrange(const sss_share *shares, u16 k, sss_secret *secret, u16 val)289{290fp_ctx ctx;291nn p;292fp s, x, y;293fp x_i, x_j, tmp, tmp2;294fp blind, blind_inv, r_inv;295int ret;296unsigned int i, j;297p.magic = WORD(0);298x_i.magic = x_j.magic = tmp.magic = tmp2.magic = s.magic = y.magic = x.magic = WORD(0);299blind.magic = blind_inv.magic = r_inv.magic = WORD(0);300301MUST_HAVE((shares != NULL) && (secret != NULL), ret, err);302/* Sanity checks */303MUST_HAVE((k >= 1), ret, err);304MUST_HAVE((SSS_SECRET_SIZE == sizeof(prime)), ret, err);305306/* Import our prime number and create the Fp context */307ret = nn_init_from_buf(&p, prime, sizeof(prime)); EG(ret, err);308ret = fp_ctx_init_from_p(&ctx, &p); EG(ret, err);309310/* Recombine our shared secrets */311ret = fp_init(&s, &ctx); EG(ret, err);312ret = fp_init(&y, &ctx); EG(ret, err);313ret = fp_init(&x_i, &ctx); EG(ret, err);314ret = fp_init(&x_j, &ctx); EG(ret, err);315ret = fp_init(&tmp, &ctx); EG(ret, err);316ret = fp_init(&tmp2, &ctx); EG(ret, err);317if(val != 0){318/* NOTE: we treat the case 'val = 0' in a specific case for319* optimization. This optimization is of interest since computing320* f(0) (where f(.) is our polynomial) is the formula for getting the321* SSS secret (which happens to be the constant of degree 0 of the322* polynomial).323*/324ret = fp_init(&x, &ctx); EG(ret, err);325ret = fp_set_word_value(&x, (word_t)val); EG(ret, err);326}327/* Get a random blind mask and invert it */328ret = fp_get_random(&blind, &ctx); EG(ret, err);329ret = fp_init(&blind_inv, &ctx); EG(ret, err);330ret = fp_inv(&blind_inv, &blind); EG(ret, err);331/* Perform the computation of r^-1 to optimize our multiplications using Montgomery332* multiplication in the main loop.333*/334ret = fp_init(&r_inv, &ctx); EG(ret, err);335ret = fp_set_nn(&r_inv, &(ctx.r)); EG(ret, err);336ret = fp_inv(&r_inv, &r_inv); EG(ret, err);337/* Proceed with the interpolation */338for(i = 0; i < k; i++){339u16 curr_idx;340const _sss_raw_share *cur_share_i = &(shares[i].raw_share);341/* Import s[i] */342ret = fp_import_from_buf(&s, cur_share_i->share, SSS_SECRET_SIZE); EG(ret, err);343/* Blind s[i] */344ret = fp_mul_monty(&s, &s, &blind); EG(ret, err);345/* Get the index */346GET_UINT16_BE(curr_idx, (const u8*)&(cur_share_i->index), 0);347ret = fp_set_word_value(&x_i, (word_t)(curr_idx)); EG(ret, err);348/* Initialize multiplication with "one" (actually Montgomery r^-1 for multiplication optimization) */349ret = fp_copy(&tmp2, &r_inv); EG(ret, err);350/* Compute the product for all k other than i351* NOTE: we use fp_mul in its redcified version as the multiplication by r^-1 is352* cancelled by the fraction of (x_j - x) * r^-1 / (x_j - x_i) * r^-1 = (x_j - x) / (x_j - x_i)353*/354for(j = 0; j < k; j++){355const _sss_raw_share *cur_share_j = &(shares[j].raw_share);356GET_UINT16_BE(curr_idx, (const u8*)&(cur_share_j->index), 0);357ret = fp_set_word_value(&x_j, (word_t)(curr_idx)); EG(ret, err);358if(j != i){359if(val != 0){360ret = fp_sub(&tmp, &x_j, &x); EG(ret, err);361ret = fp_mul_monty(&s, &s, &tmp); EG(ret, err);362}363else{364/* NOTE: we treat the case 'val = 0' in a specific case for365* optimization. This optimization is of interest since computing366* f(0) (where f(.) is our polynomial) is the formula for getting the367* SSS secret (which happens to be the constant of degree 0 of the368* polynomial).369*/370ret = fp_mul_monty(&s, &s, &x_j); EG(ret, err);371}372ret = fp_sub(&tmp, &x_j, &x_i); EG(ret, err);373ret = fp_mul_monty(&tmp2, &tmp2, &tmp); EG(ret, err);374}375}376/* Invert all the (x_j - x_i) poducts */377ret = fp_inv(&tmp, &tmp2); EG(ret, err);378ret = fp_mul_monty(&s, &s, &tmp); EG(ret, err);379/* Accumulate in secret */380ret = fp_add(&y, &y, &s); EG(ret, err);381}382/* Unblind y */383ret = fp_redcify(&y, &y); EG(ret, err);384ret = fp_mul(&y, &y, &blind_inv); EG(ret, err);385/* We should have our secret in y */386ret = fp_export_to_buf(secret->secret, SSS_SECRET_SIZE, &y);387388err:389IGNORE_RET_VAL(local_memset(&ctx, 0, sizeof(ctx)));390nn_uninit(&p);391fp_uninit(&s);392fp_uninit(&y);393fp_uninit(&x_i);394fp_uninit(&x_j);395fp_uninit(&tmp);396fp_uninit(&tmp2);397fp_uninit(&blind);398fp_uninit(&blind_inv);399fp_uninit(&r_inv);400if(val != 0){401fp_uninit(&x);402}403404return ret;405}406407408/* SSS shares and secret combination */409ATTRIBUTE_WARN_UNUSED_RET static int _sss_raw_combine(const sss_share *shares, u16 k, sss_secret *secret)410{411return _sss_raw_lagrange(shares, k, secret, 0);412}413414/***** Secure versions (public APIs) ***********************/415/* SSS shares and secret generation:416* Inputs:417* - n: is the number of shares to generate418* - k: the quorum of shares to regenerate the secret (of course k <= n)419* - secret: the secret value when input_secret is set to 'true'420* Output:421* - shares: a pointer to the generated n shares422* - secret: the secret value when input_secret is set to 'false', this423* value being randomly generated424*/425int sss_generate(sss_share *shares, unsigned short k, unsigned short n, sss_secret *secret, boolean input_secret)426{427int ret;428unsigned int i;429u8 len;430u8 session_id[SSS_SESSION_ID_SIZE];431432ret = local_memset(session_id, 0, sizeof(session_id)); EG(ret, err);433434/* Generate raw shares */435ret = _sss_raw_generate(shares, k, n, secret, input_secret); EG(ret, err);436437/* Sanity check */438MUST_HAVE((SSS_HMAC_SIZE == sizeof(shares[0].raw_share_hmac)), ret, err);439MUST_HAVE((SHA256_DIGEST_SIZE >= sizeof(shares[0].raw_share_hmac)), ret, err);440441/* Generate a random session ID */442ret = get_random(session_id, sizeof(session_id)); EG(ret, err);443444/* Compute the authenticity seal for each share with HMAC */445for(i = 0; i < n; i++){446_sss_raw_share *cur_share = &(shares[i].raw_share);447u8 *cur_id = (u8*)&(shares[i].session_id);448u8 *cur_share_hmac = (u8*)&(shares[i].raw_share_hmac);449/* NOTE: we 'abuse' casts here for shares[i].raw_share to u8*, but this should be OK since450* our structures are packed.451*/452const u8 *inputs[3] = { (const u8*)cur_share, cur_id, NULL };453const u32 ilens[3] = { sizeof(*cur_share), SSS_SESSION_ID_SIZE, 0 };454455/* Copy the session ID */456ret = local_memcpy(cur_id, session_id, SSS_SESSION_ID_SIZE); EG(ret, err);457458len = SSS_HMAC_SIZE;459ret = hmac_scattered((const u8*)secret, SSS_SECRET_SIZE, SHA256, inputs, ilens, cur_share_hmac, &len); EG(ret, err);460}461462err:463IGNORE_RET_VAL(local_memset(session_id, 0, sizeof(session_id)));464465return ret;466}467468/* SSS shares and secret combination469* Inputs:470* - k: the quorum of shares to regenerate the secret471* - shares: a pointer to the k shares472* Output:473* - secret: the secret value computed from the k shares474*/475int sss_combine(const sss_share *shares, unsigned short k, sss_secret *secret)476{477int ret, cmp;478unsigned int i;479u8 hmac_val[SSS_HMAC_SIZE];480u8 len;481482ret = local_memset(hmac_val, 0, sizeof(hmac_val)); EG(ret, err);483484/* Recombine raw shares */485ret = _sss_raw_combine(shares, k, secret); EG(ret, err);486487/* Compute and check the authenticity seal for each HMAC */488for(i = 0; i < k; i++){489const _sss_raw_share *cur_share = &(shares[i].raw_share);490const u8 *cur_id = (const u8*)&(shares[i].session_id);491const u8 *cur_id0 = (const u8*)&(shares[0].session_id);492const u8 *cur_share_hmac = (const u8*)&(shares[i].raw_share_hmac);493/* NOTE: we 'abuse' casts here for shares[i].raw_share to u8*, but this should be OK since494* our structures are packed.495*/496const u8 *inputs[3] = { (const u8*)cur_share, cur_id, NULL };497const u32 ilens[3] = { sizeof(*cur_share), SSS_SESSION_ID_SIZE, 0 };498499/* Check that all our shares have the same session ID, return an error otherwise */500ret = are_equal(cur_id, cur_id0, SSS_SESSION_ID_SIZE, &cmp); EG(ret, err);501if(!cmp){502#ifdef VERBOSE503ext_printf("[-] sss_combine error for share %d / %d: session ID is not OK!\n", i, k);504#endif505ret = -1;506goto err;507}508509len = sizeof(hmac_val);510ret = hmac_scattered((const u8*)secret, SSS_SECRET_SIZE, SHA256, inputs, ilens, hmac_val, &len); EG(ret, err);511512/* Check the HMAC */513ret = are_equal(hmac_val, cur_share_hmac, len, &cmp); EG(ret, err);514if(!cmp){515#ifdef VERBOSE516ext_printf("[-] sss_combine error for share %d / %d: HMAC is not OK!\n", i, k);517#endif518ret = -1;519goto err;520}521}522523err:524IGNORE_RET_VAL(local_memset(hmac_val, 0, sizeof(hmac_val)));525526return ret;527}528529/* SSS shares regeneration from existing shares530* Inputs:531* - shares: a pointer to the input k shares allowing the regeneration532* - n: is the number of shares to regenerate533* - k: the input shares (of course k <= n)534* Output:535* - shares: a pointer to the generated n shares (among which the k first are536* the ones provided as inputs)537* - secret: the recomputed secret value538*/539int sss_regenerate(sss_share *shares, unsigned short k, unsigned short n, sss_secret *secret)540{541int ret, cmp;542unsigned int i;543u16 max_idx, num_shares;544u8 hmac_val[SSS_HMAC_SIZE];545u8 len;546547/* Sanity check */548MUST_HAVE((n <= (u16)(0xffff - 1)), ret, err);549MUST_HAVE((n >= k), ret, err);550551ret = local_memset(hmac_val, 0, sizeof(hmac_val)); EG(ret, err);552553/* Compute the secret */554ret = _sss_raw_lagrange(shares, k, secret, 0); EG(ret, err);555/* Check the authenticity of our shares */556for(i = 0; i < k; i++){557_sss_raw_share *cur_share = &(shares[i].raw_share);558u8 *cur_id = (u8*)&(shares[i].session_id);559u8 *cur_id0 = (u8*)&(shares[0].session_id);560u8 *cur_share_hmac = (u8*)&(shares[i].raw_share_hmac);561/* NOTE: we 'abuse' casts here for shares[i].raw_share to u8*, but this should be OK since562* our structures are packed.563*/564const u8 *inputs[3] = { (const u8*)cur_share, cur_id, NULL };565const u32 ilens[3] = { sizeof(*cur_share), SSS_SESSION_ID_SIZE, 0 };566567/* Check that all our shares have the same session ID, return an error otherwise */568ret = are_equal(cur_id, cur_id0, SSS_SESSION_ID_SIZE, &cmp); EG(ret, err);569if(!cmp){570#ifdef VERBOSE571ext_printf("[-] sss_regenerate error for share %d / %d: session ID is not OK!\n", i, k);572#endif573ret = -1;574goto err;575}576577len = sizeof(hmac_val);578/* NOTE: we 'abuse' cast here for secret to (const u8*), but this should be OK since our579* structures are packed.580*/581ret = hmac_scattered((const u8*)secret, SSS_SECRET_SIZE, SHA256, inputs, ilens, hmac_val, &len); EG(ret, err);582ret = are_equal(hmac_val, cur_share_hmac, len, &cmp); EG(ret, err);583if(!cmp){584#ifdef VERBOSE585ext_printf("[-] sss_regenerate error for share %d / %d: HMAC is not OK!\n", i, k);586#endif587ret = -1;588goto err;589}590}591592/* Our secret regeneration consists of determining the maximum index, and593* proceed with Lagrange interpolation on new values.594*/595max_idx = 0;596for(i = 0; i < k; i++){597u16 curr_idx;598GET_UINT16_BE(curr_idx, (u8*)&(shares[i].raw_share.index), 0);599if(curr_idx > max_idx){600max_idx = curr_idx;601}602}603/* Now regenerate as many shares as we need */604num_shares = 0;605i = k;606while(num_shares < (n - k)){607_sss_raw_share *cur_share = &(shares[k + num_shares].raw_share);608u8 *cur_id = (u8*)&(shares[k + num_shares].session_id);609u8 *cur_id0 = (u8*)&(shares[0].session_id);610u8 *cur_share_hmac = (u8*)&(shares[k + num_shares].raw_share_hmac);611u16 curr_idx;612/* NOTE: we 'abuse' casts here for shares[i].raw_share.share to sss_secret*, but this should be OK since613* our shares[i].raw_share.share is a SSS_SECRET_SIZE as the sss_secret.secret type encapsulates and our614* structures are packed.615*/616const u8 *inputs[3] = { (const u8*)cur_share, cur_id, NULL };617const u32 ilens[3] = { sizeof(*cur_share), SSS_SESSION_ID_SIZE, 0 };618619/* Skip the index = 0 case */620curr_idx = (u16)(max_idx + (u16)(i - k + 1));621if(curr_idx == 0){622i++;623continue;624}625626/* Copy our session ID */627ret = local_memcpy(cur_id, cur_id0, SSS_SESSION_ID_SIZE); EG(ret, err);628629ret = _sss_raw_lagrange(shares, k, (sss_secret*)(cur_share->share), curr_idx); EG(ret, err);630PUT_UINT16_BE(curr_idx, (u8*)&(cur_share->index), 0);631632/* Compute the HMAC */633len = SSS_HMAC_SIZE;634ret = hmac_scattered((const u8*)secret, SSS_SECRET_SIZE, SHA256, inputs, ilens, cur_share_hmac, &len); EG(ret, err);635num_shares++;636i++;637}638639err:640IGNORE_RET_VAL(local_memset(hmac_val, 0, sizeof(hmac_val)));641642return ret;643}644645646/********* main test program for SSS *************/647#ifdef SSS648#include <libecc/utils/print_buf.h>649650#define K 50651#define N 150652#define MAX_N 200653654int main(int argc, char *argv[])655{656int ret = 0;657unsigned int i;658sss_share shares[MAX_N];659sss_share shares_[MAX_N];660sss_secret secret;661662FORCE_USED_VAR(argc);663FORCE_USED_VAR(argv);664665/* Generate N shares for SSS with at least K shares OK among N */666ext_printf("[+] Generating the secrets %d / %d, call should be OK\n", K, N);667ret = local_memset(&secret, 0x00, sizeof(secret)); EG(ret, err);668/* NOTE: 'false' here means that we let the library generate the secret randomly */669ret = sss_generate(shares, K, N, &secret, SSS_FALSE);670if(ret){671ext_printf(" [X] Error: sss_generate error\n");672goto err;673}674else{675buf_print(" secret", (u8*)&secret, SSS_SECRET_SIZE); EG(ret, err);676}677/* Shuffle shares */678for(i = 0; i < N; i++){679shares_[i] = shares[N - 1 - i];680}681682/* Combine (k-1) shares: this call should trigger an ERROR */683ext_printf("[+] Combining the secrets with less shares: call should trigger an error\n");684ret = local_memset(&secret, 0x00, sizeof(secret)); EG(ret, err);685ret = sss_combine(shares_, K - 1, &secret);686if (ret) {687ext_printf(" [X] Error: sss_combine error\n");688} else{689buf_print(" secret", (u8*)&secret, SSS_SECRET_SIZE);690}691692/* Combine k shares: this call should be OK and recombine the initial693* secret694*/695ext_printf("[+] Combining the secrets with minimum shares: call should be OK\n");696ret = local_memset(&secret, 0x00, sizeof(secret)); EG(ret, err);697ret = sss_combine(shares_, K, &secret);698if (ret) {699ext_printf(" [X] Error: sss_combine error\n");700goto err;701} else {702buf_print(" secret", (u8*)&secret, SSS_SECRET_SIZE);703}704705/* Combine k shares: this call should be OK and recombine the initial706* secret707*/708ext_printf("[+] Combining the secrets with more shares: call should be OK\n");709ret = local_memset(&secret, 0x00, sizeof(secret)); EG(ret, err);710ret = sss_combine(shares_, K + 1, &secret);711if (ret) {712ext_printf(" [X] Error: sss_combine error\n");713goto err;714} else {715buf_print(" secret", (u8*)&secret, SSS_SECRET_SIZE);716}717718/* Combine with a corrupted share: call should trigger an error */719ext_printf("[+] Combining the secrets with more shares but one corrupted: call should trigger an error\n");720ret = local_memset(&secret, 0x00, sizeof(secret)); EG(ret, err);721shares_[K].raw_share.share[0] = 0x00;722ret = sss_combine(shares_, K + 1, &secret);723if (ret) {724ext_printf(" [X] Error: sss_combine error\n");725} else {726buf_print(" secret", (u8*)&secret, SSS_SECRET_SIZE);727}728729/* Regenerate more shares! call should be OK */730ext_printf("[+] Regenerating more shares: call should be OK\n");731ret = local_memset(&secret, 0x00, sizeof(secret)); EG(ret, err);732ret = sss_regenerate(shares, K, MAX_N, &secret); EG(ret, err);733if (ret) {734ext_printf(" [X] Error: sss_regenerate error\n");735goto err;736} else {737buf_print(" secret", (u8*)&secret, SSS_SECRET_SIZE);738}739/* Shuffle shares */740for(i = 0; i < MAX_N; i++){741shares_[i] = shares[MAX_N - 1 - i];742}743744/* Combine newly generated shares: call should be OK */745ext_printf("[+] Combining the secrets with newly generated shares: call should be OK\n");746ret = local_memset(&secret, 0x00, sizeof(secret)); EG(ret, err);747ret = sss_combine(shares_, K, &secret);748if (ret) {749ext_printf(" [X] Error: sss_combine error\n");750goto err;751} else {752buf_print(" secret", (u8*)&secret, SSS_SECRET_SIZE);753}754755/* Modify the session ID of one of the shares: call should trigger an error */756ext_printf("[+] Combining the secrets with newly generated shares and a bad session ID: call should trigger an error\n");757ret = local_memset(&secret, 0x00, sizeof(secret)); EG(ret, err);758shares_[1].session_id[0] = 0x00;759ret = sss_combine(shares_, K, &secret);760if (ret) {761ext_printf(" [X] Error: sss_combine error\n");762} else {763buf_print(" secret", (u8*)&secret, SSS_SECRET_SIZE);764}765766ret = 0;767768err:769return ret;770}771#endif772773774