Tools to make secret sharing easier.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

406 lines
9.3 KiB

/*
* gfsecret - Secret sharing tools
* Copyright (C) 2016 Damien Goutte-Gattat
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "secret.h"
#include <stdlib.h>
#include <string.h>
#include <gcrypt.h>
#include <libgfshare.h>
#include "util.h"
gfsec_share_t *
gfsec_share_new(void)
{
gfsec_share_t *s = NULL;
if ( (s = malloc(sizeof(gfsec_share_t))) ) {
s->number = GFSEC_SHARE_NUMBER_AUTOASSIGN;
s->scheme = GFSEC_SCHEME_FILE;
s->authority = NULL;
s->path = NULL;
s->len = 0;
s->data = NULL;
s->hash = NULL;
}
return s;
}
int
gfsec_share_set_info(gfsec_share_t *share,
unsigned char number,
gfsec_scheme_t scheme,
const char *authority,
const char *path,
const unsigned char *hash)
{
if ( ! share )
return GFSEC_ERR_INVALID_CALL;
share->number = number;
share->scheme = scheme;
if ( ! (share->authority = strdup(authority)) )
return GFSEC_ERR_SYSTEM_ERROR;
if ( ! (share->path = strdup(path)) )
return GFSEC_ERR_SYSTEM_ERROR;
if ( hash )
memcpy(share->hash, hash, 32);
return 0;
}
int
gfsec_share_set_data(gfsec_share_t *share,
unsigned char *data,
size_t len)
{
int ret;
if ( ! share || ! data )
return GFSEC_ERR_INVALID_CALL;
share->data = data;
share->len = len;
ret = 0;
if ( share->hash ) {
gcry_md_hd_t md;
unsigned char *hash;
gcry_md_open(&md, GCRY_MD_SHA256, 0);
gcry_md_write(md, data, len);
hash = gcry_md_read(md, 0);
if ( memcmp(share->hash, hash, 32) != 0 ) {
ret = GFSEC_ERR_INVALID_SHARE;
share->data = NULL;
share->len = 0;
}
gcry_md_close(md);
}
return ret;
}
int
gfsec_share_get_uri(gfsec_share_t *share,
char *buffer,
size_t len)
{
static char *schemes[] = { "file://", "uuid://", "label://", "mtp://" };
int ret;
if ( ! share || ! buffer )
return GFSEC_ERR_INVALID_CALL;
ret = snprintf(buffer, len, "%s%s%s",
schemes[share->scheme],
share->authority ? share->authority : "",
share->path);
return ret;
}
void
gfsec_share_free(gfsec_share_t *share)
{
if ( ! share )
return;
if ( share->authority )
free(share->authority);
if ( share->path )
free(share->path);
if ( share->data )
free(share->data);
if ( share->hash )
free(share->hash);
free(share);
}
gfsec_secret_t *
gfsec_secret_new(void)
{
gfsec_secret_t *s = NULL;
if ( (s = malloc(sizeof(*s))) ) {
s->filename = NULL;
s->data = NULL;
s->len = 0;
s->n_shares = 0;
s->max_shares = 0;
s->threshold = 0;
s->shares = NULL;
s->full_share = NULL;
s->can_combine = 0;
}
return s;
}
int
gfsec_secret_set_secret_file(gfsec_secret_t *secret,
const char *filename)
{
if ( ! secret || ! filename )
return GFSEC_ERR_INVALID_CALL;
if ( (secret->filename = strdup(filename)) )
return GFSEC_ERR_SYSTEM_ERROR;
if ( ! (secret->data = read_file(filename, &(secret->len))) )
return GFSEC_ERR_SYSTEM_ERROR;
}
int
gfsec_secret_add_share(gfsec_secret_t *secret, gfsec_share_t *share)
{
if ( ! secret || ! share )
return GFSEC_ERR_INVALID_CALL;
if ( secret->n_shares >= secret->max_shares ) {
size_t new_max;
void *tmp;
new_max = secret->max_shares + 10;
if ( new_max > 255 ) {
new_max = 255;
if ( secret->n_shares >= new_max )
return GFSEC_ERR_TOO_MANY_SHARES;
}
tmp = realloc(secret->shares, new_max * sizeof(gfsec_share_t *));
if ( ! tmp )
return GFSEC_ERR_SYSTEM_ERROR;
secret->shares = tmp;
secret->max_shares = new_max;
}
if ( share->number == GFSEC_SHARE_NUMBER_AUTOASSIGN ) {
unsigned nr, u;
do {
nr = (random() & 0xFF00) >> 8;
if ( nr == 0 )
nr = 1;
for ( u = 0; u < secret->n_shares; u++ )
if ( secret->shares[u]->number == nr )
nr = 0;
} while ( nr == 0 );
share->number = nr;
}
secret->shares[secret->n_shares++] = share;
return 0;
}
int
gfsec_secret_can_combine(gfsec_secret_t *secret)
{
int ret;
unsigned u, avail_shares;
gfsec_share_t *share;
if ( ! secret )
return GFSEC_ERR_INVALID_CALL;
for ( u = 0, avail_shares = 0; u < secret->n_shares; u++ ) {
share = secret->shares[u];
if ( share->data ) {
if ( secret->len == 0 )
secret->len = share->len;
else if ( secret->len != share->len )
return GFSEC_ERR_INVALID_LENGTH;
if ( gfsec_share_is_full(share) )
secret->full_share = share;
else
avail_shares += 1;
}
}
if ( ! secret->full_share && avail_shares < secret->threshold )
return GFSEC_ERR_NOT_ENOUGH_SHARES;
secret->can_combine = 1;
return 0;
}
int
gfsec_secret_combine(gfsec_secret_t *secret)
{
gfsec_share_t *share;
unsigned u;
if ( ! secret )
return GFSEC_ERR_INVALID_CALL;
if ( ! secret->can_combine )
return GFSEC_ERR_INVALID_CALL;
if ( ! (secret->data = malloc(secret->len)) )
return GFSEC_ERR_SYSTEM_ERROR;
if ( secret->full_share )
memcpy(secret->data, secret->full_share->data, secret->len);
else {
gfshare_ctx *ctx;
unsigned char sharenrs[255];
unsigned v;
for ( u = v = 0; u < secret->n_shares; u++ ) {
share = secret->shares[u];
if ( share->data && ! gfsec_share_is_full(share) )
sharenrs[v++] = share->number;
}
if ( ! (ctx= gfshare_ctx_init_dec(sharenrs, v, secret->len)) )
return GFSEC_ERR_SYSTEM_ERROR;
for ( u = v = 0; u < secret->n_shares; u++ ) {
share = secret->shares[u];
if ( share->data && ! gfsec_share_is_full(share) )
gfshare_ctx_dec_giveshare(ctx, v++, share->data);
}
gfshare_ctx_dec_extract(ctx, secret->data);
}
return 0;
}
static int
gfsec_secret_get_partial_share_count(gfsec_secret_t *secret)
{
unsigned u, n;
for ( u = n = 0; u < secret->n_shares; u++ )
if ( gfsec_share_is_full(secret->shares[u]) )
n += 1;
return n;
}
static void
gfsec_secret_hash_shares(gfsec_secret_t *secret)
{
gcry_md_hd_t md;
gfsec_share_t *share;
unsigned u;
gcry_md_open(&md, GCRY_MD_SHA256, 0);
for ( u = 0; u < secret->n_shares; u++ ) {
share = secret->shares[u];
gcry_md_write(md, share->data, share->len);
memcpy(share->hash, gcry_md_read(md, 0), 32);
gcry_md_reset(md);
}
gcry_md_close(md);
}
int
gfsec_secret_split(gfsec_secret_t *secret, unsigned char threshold)
{
gfshare_ctx *ctx;
gfsec_share_t *share;
unsigned char sharenrs[255];
unsigned u, v, n;
if ( ! secret )
return GFSEC_ERR_INVALID_CALL;
n = gfsec_secret_get_partial_share_count(secret);
if ( threshold >= n )
return GFSEC_ERR_NOT_ENOUGH_SHARES;
for ( u = v = 0; u < secret->n_shares; u++ ) {
share = secret->shares[u];
if ( share->number != 0 )
sharenrs[v++] = share->number;
if ( ! (share->data = malloc(secret->len)) )
return GFSEC_ERR_SYSTEM_ERROR;
if ( ! (share->hash = malloc(32)) )
return GFSEC_ERR_SYSTEM_ERROR;
}
if ( ! (ctx = gfshare_ctx_init_enc(sharenrs, n, threshold, secret->len)) )
return GFSEC_ERR_SYSTEM_ERROR;
gfshare_ctx_enc_setsecret(ctx, secret->data);
for ( u = v = 0; u < secret->n_shares; u++ ) {
share = secret->shares[u];
if ( share->number != 0 )
gfshare_ctx_enc_getshare(ctx, v++, share->data);
else
memcpy(share->data, secret->data, secret->len);
}
gfshare_ctx_free(ctx);
return 0;
}
void
gfsec_secret_free(gfsec_secret_t *secret)
{
unsigned u;
if ( ! secret )
return;
if ( secret->filename )
free(secret->filename);
if ( secret->data )
free(secret->data);
for ( u = 0; u < secret->n_shares; u++ )
gfsec_share_free(secret->shares[u]);
if ( secret->shares )
free(secret->shares);
free(secret);
}