#include "sss_private.h"
#include "sss.h"
#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
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;
MUST_HAVE((SHA512_DIGEST_SIZE >= (2 * SSS_SECRET_SIZE)), ret, err);
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);
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);
ret = nn_mod(&nn_val, &nn_val, &(out->ctx->p)); EG(ret, err);
ret = fp_set_nn(out, &nn_val);
err:
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;
}
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);
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);
ret = nn_init_from_buf(&p, prime, sizeof(prime)); EG(ret, err);
ret = fp_ctx_init_from_p(&ctx, &p); EG(ret, err);
ret = get_random(secret_seed, sizeof(secret_seed)); EG(ret, err);
ret = fp_init(&a0, &ctx); EG(ret, err);
if(input_secret == SSS_TRUE){
ret = fp_import_from_buf(&a0, secret->secret, SSS_SECRET_SIZE); EG(ret, err);
}
else{
ret = _sss_derive_seed(&a0, secret_seed, 0); EG(ret, err);
}
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);
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);
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){
i++;
continue;
}
ret = fp_mul(&s, &blind, &a0); EG(ret, err);
ret = fp_set_word_value(&base, (word_t)curr_idx); EG(ret, err);
ret = fp_one(&exp); EG(ret, err);
for(j = 1; j < k; j++){
ret = fp_mul_monty(&exp, &exp, &base); EG(ret, err);
ret = _sss_derive_seed(&a, secret_seed, (u16)j); EG(ret, err);
ret = fp_mul_monty(&a, &a, &blind); EG(ret, err);
ret = fp_mul_monty(&tmp, &exp, &a); EG(ret, err);
ret = fp_add(&s, &s, &tmp); EG(ret, err);
}
PUT_UINT16_BE(curr_idx, (u8*)&(cur_share_i->index), 0);
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++;
}
ret = fp_export_to_buf(secret->secret, SSS_SECRET_SIZE, &a0);
err:
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;
}
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);
MUST_HAVE((k >= 1), ret, err);
MUST_HAVE((SSS_SECRET_SIZE == sizeof(prime)), ret, err);
ret = nn_init_from_buf(&p, prime, sizeof(prime)); EG(ret, err);
ret = fp_ctx_init_from_p(&ctx, &p); EG(ret, err);
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){
ret = fp_init(&x, &ctx); EG(ret, err);
ret = fp_set_word_value(&x, (word_t)val); EG(ret, err);
}
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);
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);
for(i = 0; i < k; i++){
u16 curr_idx;
const _sss_raw_share *cur_share_i = &(shares[i].raw_share);
ret = fp_import_from_buf(&s, cur_share_i->share, SSS_SECRET_SIZE); EG(ret, err);
ret = fp_mul_monty(&s, &s, &blind); EG(ret, err);
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);
ret = fp_copy(&tmp2, &r_inv); EG(ret, err);
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{
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);
}
}
ret = fp_inv(&tmp, &tmp2); EG(ret, err);
ret = fp_mul_monty(&s, &s, &tmp); EG(ret, err);
ret = fp_add(&y, &y, &s); EG(ret, err);
}
ret = fp_redcify(&y, &y); EG(ret, err);
ret = fp_mul(&y, &y, &blind_inv); EG(ret, err);
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;
}
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);
}
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);
ret = _sss_raw_generate(shares, k, n, secret, input_secret); EG(ret, err);
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);
ret = get_random(session_id, sizeof(session_id)); EG(ret, err);
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);
const u8 *inputs[3] = { (const u8*)cur_share, cur_id, NULL };
const u32 ilens[3] = { sizeof(*cur_share), SSS_SESSION_ID_SIZE, 0 };
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;
}
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);
ret = _sss_raw_combine(shares, k, secret); EG(ret, err);
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);
const u8 *inputs[3] = { (const u8*)cur_share, cur_id, NULL };
const u32 ilens[3] = { sizeof(*cur_share), SSS_SESSION_ID_SIZE, 0 };
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);
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;
}
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;
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);
ret = _sss_raw_lagrange(shares, k, secret, 0); EG(ret, err);
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);
const u8 *inputs[3] = { (const u8*)cur_share, cur_id, NULL };
const u32 ilens[3] = { sizeof(*cur_share), SSS_SESSION_ID_SIZE, 0 };
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);
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;
}
}
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;
}
}
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;
const u8 *inputs[3] = { (const u8*)cur_share, cur_id, NULL };
const u32 ilens[3] = { sizeof(*cur_share), SSS_SESSION_ID_SIZE, 0 };
curr_idx = (u16)(max_idx + (u16)(i - k + 1));
if(curr_idx == 0){
i++;
continue;
}
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);
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;
}
#ifdef SSS
#include <libecc/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);
ext_printf("[+] Generating the secrets %d / %d, call should be OK\n", K, N);
ret = local_memset(&secret, 0x00, sizeof(secret)); EG(ret, err);
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);
}
for(i = 0; i < N; i++){
shares_[i] = shares[N - 1 - i];
}
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);
}
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);
}
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);
}
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);
}
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);
}
for(i = 0; i < MAX_N; i++){
shares_[i] = shares[MAX_N - 1 - i];
}
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);
}
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