Browse Source

Document the secret module

master
Damien Goutte-Gattat 5 years ago
parent
commit
a3911160a1
  1. 303
      src/secret.c
  2. 34
      src/secret.h

303
src/secret.c

@ -30,6 +30,72 @@
#include "util.h"
/** @file secret.c
* Secret splitting module.
*
* How to use this module to split a secret.
*
* Create a secret object and fills it with the secret data:
* \code
* gfsec_secret_t *secret = gfsec_secret_new();
* gfsec_secret_set_secret(filename);
* \endcode
*
* Create share objects describing the shares to split the
* secret into, and add them to the secret object:
* \code
* gfsec_share_t *share = gfsec_share_new();
* gfsec_share_set_info(share, GFSEC_SHARE_NUMBER_AUTOASSIGN,
* GFSEC_SCHEME_FILE, authority, path, NULL);
* gfsec_secret_add_share(secret, share);
* \endcode
*
* Actually split the secret and write the shares' data:
* \code
* gfsec_secret_split(secret, threshold);
* for ( int i = 0; i < secret->n_shares; i++ ) {
* gfsec_share_t *share = secret->shares[i];
* write_data_somewhere(share->data, share->len);
* }
* \endcode
*
*
* How to use this module to reconstitute a secret.
*
* Create a secret object:
* \code
* gfsec_secret_t *secret = gfsec_secret_new();
* secret->threshold = threshold;
* \endcode
*
* Create share objects describing the shares, and add them
* to the secret object:
* \code
* gfsec_share_t *share = gfsec_share_new();
* gfsec_share_set_info(share, share_number, GFSEC_SCHEME_FILE,
* authority, path, hash);
* \endcode
*
* Fill the shares with data if available:
* \code
* gfsec_share_set_data(share, data, len);
* \endcode
*
* Actually reconstitute the secret:
* \code
* if ( gfsec_secret_can_recombine(secret) ) {
* gfsec_secret_recombine(secret);
* do_something_with_secret(secret->data, secret->len);
* }
* \endcode
*
*/
/**
* Initializes a new share object.
*
* @return The share object, or NULL if memory could not be allocated.
*/
gfsec_share_t *
gfsec_share_new(void)
{
@ -50,6 +116,26 @@ gfsec_share_new(void)
return s;
}
/**
* Fills the fields of a share object.
* Note that all strings are copied to newly allocated buffers.
* The gfsec_share_free function takes care of freeing those buffers.
*
* @param share The share object;
* @param number The share number; should be between 1 and 255
* (inclusive) for a normal share, 0 for a share
* which contains the whole secret, or
* GFSEC_SHARE_NUMBER_AUTOASSIGN to let the
* gfsec_secret_split function assign a share
* number automatically;
* @param scheme The scheme of the URI locating the share;
* @param authority The authority part of the URI (may be NULL);
* @param path The path part of the URI (may be NULL);
* @param hash A buffer containing a SHA-256 hash of the
* share's data (may be NULL).
*
* @return 0 if successful, or one of the GFSEC_ERR_* error codes.
*/
int
gfsec_share_set_info(gfsec_share_t *share,
unsigned char number,
@ -64,9 +150,9 @@ gfsec_share_set_info(gfsec_share_t *share,
share->number = number;
share->scheme = scheme;
if ( ! (share->authority = strdup(authority)) )
if ( authority && ! (share->authority = strdup(authority)) )
return GFSEC_ERR_SYSTEM_ERROR;
if ( ! (share->path = strdup(path)) )
if ( path && ! (share->path = strdup(path)) )
return GFSEC_ERR_SYSTEM_ERROR;
if ( hash )
@ -75,6 +161,20 @@ gfsec_share_set_info(gfsec_share_t *share,
return 0;
}
/**
* Fills a share object with the corresponding data.
* If the share object specifies a hash value, the data are
* checked against this hash, and GFSEC_ERR_INVALID_SHARE is
* returned if the checksum fails.
*
* @param share The share object.
* @param data The buffer containing the data; only the pointer
* is copied, not the buffer itself, and it will be
* freed when the gfsec_share_free function is called;
* @param len The size of the data buffer.
*
* @return 0 if successfull, or one of the GFSEC_ERR_* error codes.
*/
int
gfsec_share_set_data(gfsec_share_t *share,
unsigned char *data,
@ -90,25 +190,29 @@ gfsec_share_set_data(gfsec_share_t *share,
ret = 0;
if ( share->hash ) {
gcry_md_hd_t md;
unsigned char *hash;
unsigned char hash[32];
gcry_md_open(&md, GCRY_MD_SHA256, 0);
gcry_md_write(md, data, len);
hash = gcry_md_read(md, 0);
gcry_md_hash_buffer(GCRY_MD_SHA256, hash, data, len);
if ( memcmp(share->hash, hash, 32) != 0 ) {
ret = GFSEC_ERR_INVALID_SHARE;
share->data = NULL;
share->len = 0;
}
gcry_md_close(md);
}
return ret;
}
/**
* Gets the URI representing the location of a share.
*
* @param share The share object.
* @param buffer The buffer to write the URI into.
* @param len The size of the buffer.
*
* @return The number of characters written into the buffer,
* or GFSEC_ERR_INVALID_CALL if an invalid poitner was passed.
*/
int
gfsec_share_get_uri(gfsec_share_t *share,
char *buffer,
@ -128,6 +232,11 @@ gfsec_share_get_uri(gfsec_share_t *share,
return ret;
}
/**
* Frees a share object and all of its buffers.
*
* @param share The share object.
*/
void
gfsec_share_free(gfsec_share_t *share)
{
@ -147,6 +256,11 @@ gfsec_share_free(gfsec_share_t *share)
free(share);
}
/**
* Initializes a new secret object.
*
* @return The secret object, or NULL if memory could not be allocated.
*/
gfsec_secret_t *
gfsec_secret_new(void)
{
@ -171,6 +285,14 @@ gfsec_secret_new(void)
return s;
}
/**
* Sets the secret to be splitted.
*
* @param secret The secret object.
* @param filename The location of the file to read the secret from.
*
* @return 0 if successful, or one of the GFSEC_ERR_* error codes.
*/
int
gfsec_secret_set_secret_file(gfsec_secret_t *secret,
const char *filename)
@ -183,8 +305,23 @@ gfsec_secret_set_secret_file(gfsec_secret_t *secret,
if ( ! (secret->data = read_file(filename, &(secret->len))) )
return GFSEC_ERR_SYSTEM_ERROR;
return 0;
}
/**
* Adds a share to a secret object.
*
* @param secret The secret object.
* @param share The share to add.
*
* @return
* - 0 if successful;
* - GFSEC_ERR_INVALID_CALL if improperly called;
* - GFSEC_ERR_TOO_MANY_SHARES if the secret object already has the
* maximal number of shares allowed;
* - GFSEC_ERR_SYSTEM_ERROR if a memory allocation error occurs.
*/
int
gfsec_secret_add_share(gfsec_secret_t *secret, gfsec_share_t *share)
{
@ -231,10 +368,22 @@ gfsec_secret_add_share(gfsec_secret_t *secret, gfsec_share_t *share)
return 0;
}
/**
* Checks if the secret can be reconstituted.
* Note that it is mandatory to call this function prior to calling
* the gfsec_secret_combine function.
*
* @param secret The secret object.
*
* @return
* - 0 if the secret can be reconstituted;
* - GFSEC_ERR_INVALID_CALL if improperly called (secret is NULL);
* - GFSEC_ERR_INVALID_LENGTH if not all shares have the same length;
* - GFSEC_ERR_NOT_ENOUGH_SHARES if not enough shares are available.
*/
int
gfsec_secret_can_combine(gfsec_secret_t *secret)
{
int ret;
unsigned u, avail_shares;
gfsec_share_t *share;
@ -265,6 +414,21 @@ gfsec_secret_can_combine(gfsec_secret_t *secret)
return 0;
}
/**
* Reconstitutes the secret.
* The reconstituted secret will be stored in the data field of
* the secret object.
*
* @param secret The secret object.
*
* @return
* - 0 if successful;
* - GFSEC_ERR_INVALID_CALL if called improperly (secret is NULL,
* or gfsec_secret_can_recombine has not been called previously,
* or it has been called but has found the secret could not be
* reconstituted);
* - GFSEC_ERR_SYSTEM_ERROR if a memory allocation error occurs.
*/
int
gfsec_secret_combine(gfsec_secret_t *secret)
{
@ -303,12 +467,16 @@ gfsec_secret_combine(gfsec_secret_t *secret)
}
gfshare_ctx_dec_extract(ctx, secret->data);
gfshare_ctx_free(ctx);
}
return 0;
}
static int
/**
* Gets the number of partial shares in the secret object.
*/
static unsigned
gfsec_secret_get_partial_share_count(gfsec_secret_t *secret)
{
unsigned u, n;
@ -320,33 +488,29 @@ gfsec_secret_get_partial_share_count(gfsec_secret_t *secret)
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);
}
/**
* Splits the secret and fills the shares accordingly.
*
* @param secret The secret object.
* @param threshold The minimal number of shares needed to
* reconstitute the secret.
*
* @return
* - 0 if successful;
* - GFSEC_ERR_INVALID_CALL if called improperly;
* - GFSEC_ERR_NOT_ENOUGH_SHARES if the secret object does not define
* enough shares to satisfy the desired threshold;
* - GFSEC_ERR_SYSTEM_ERROR if a memory allocation error occurs.
*/
int
gfsec_secret_split(gfsec_secret_t *secret, unsigned char threshold)
{
gfshare_ctx *ctx;
gcry_md_hd_t md;
gfsec_share_t *share;
unsigned char sharenrs[255];
unsigned u, v, n;
int rc;
if ( ! secret )
return GFSEC_ERR_INVALID_CALL;
@ -355,34 +519,69 @@ gfsec_secret_split(gfsec_secret_t *secret, unsigned char threshold)
if ( threshold >= n )
return GFSEC_ERR_NOT_ENOUGH_SHARES;
for ( u = v = 0; u < secret->n_shares; u++ ) {
for ( u = v = rc = 0; u < secret->n_shares && rc == 0; u++ ) {
share = secret->shares[u];
if ( share->number != 0 )
if ( ! gfsec_share_is_full(share) )
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;
rc = GFSEC_ERR_SYSTEM_ERROR;
if ( ! (share->data = malloc(32)) )
rc = GFSEC_ERR_SYSTEM_ERROR;
}
if ( ! (ctx = gfshare_ctx_init_enc(sharenrs, n, threshold, secret->len)) )
return GFSEC_ERR_SYSTEM_ERROR;
if ( rc == 0 && ! (ctx = gfshare_ctx_init_enc(sharenrs, n, threshold,
secret->len)) )
rc = GFSEC_ERR_SYSTEM_ERROR;
gfshare_ctx_enc_setsecret(ctx, secret->data);
if ( rc == 0 && gcry_md_open(&md, GCRY_MD_SHA256, 0) != 0 )
rc = GFSEC_ERR_SYSTEM_ERROR;
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);
if ( rc == 0 ) {
gfshare_ctx_enc_setsecret(ctx, secret->data);
for ( u = v = 0; u < secret->n_shares; u++ ) {
share = secret->shares[u];
if ( gfsec_share_is_full(share) )
gfshare_ctx_enc_getshare(ctx, v++, share->data);
else
memcpy(share->data, secret->data, secret->len);
gcry_md_write(md, share->data, share->len);
memcpy(share->hash, gcry_md_read(md, 0), 32);
gcry_md_reset(md);
}
}
else {
for ( u = v = 0; u < secret->n_shares; u++ ) {
share = secret->shares[u];
if ( share->data ) {
free(share->data);
share->data = NULL;
}
if ( share->hash ) {
free(share->hash);
share->hash = NULL;
}
}
}
gfshare_ctx_free(ctx);
if ( ctx )
gfshare_ctx_free(ctx);
if ( md )
gcry_md_close(md);
return 0;
return rc;
}
/**
* Frees a secret object and the associated shares.
*
* @param secret The secret object.
*/
void
gfsec_secret_free(gfsec_secret_t *secret)
{
@ -405,6 +604,14 @@ gfsec_secret_free(gfsec_secret_t *secret)
free(secret);
}
/**
* Translates a GFSEC_ERR_* error code into an error string.
*
* @param err The error code.
*
* @return A statically allocated string containing the error
* message.
*/
const char *
gfsec_error_string(int err)
{

34
src/secret.h

@ -36,33 +36,61 @@ typedef enum gfsec_scheme {
GFSEC_SCHEME_MTP
} gfsec_scheme_t;
/**
* Represents a share of a splitted secret.
*/
typedef struct gfsec_share {
/*
* This must be set to a value between 1 and 255 (inclusive).
* A value of 0 indicates this share actually contains the whole
* secret. A value of GFSEC_SHARE_NUMBER_AUTOASSIGN means the
* share number will be automatically assigned later.
*/
unsigned short number;
/*
* The following fields describe the location where the share
* data is to be found, as a scheme://authority/path triplet.
*/
gfsec_scheme_t scheme;
char *authority;
char *path;
size_t len;
unsigned char *data;
unsigned char *hash;
size_t len; /* Size of the share's data. */
unsigned char *data; /* The actual share's data. */
unsigned char *hash; /* A SHA-256 hash of the data. */
} gfsec_share_t;
#define gfsec_share_is_full(share) (share->number == 0)
/**
* Represents a splitted secret (or a secret to be splitted).
*/
typedef struct gfsec_secret {
/* The location of the reconstituted secret. */
char *filename;
/* The secret's actual data. */
unsigned char *data;
size_t len;
/* The number of shares this secret is splitted into. */
unsigned char n_shares;
/* The current size of the \c shares array. */
unsigned char max_shares;
/* The number of shares needed to reconstitute the secret. */
unsigned char threshold;
/* The shares this secret is splitted into. */
gfsec_share_t **shares;
/* May point to one of the shares in the \c shares array
* containing the whole secret. */
gfsec_share_t *full_share;
/* If non-zero, indicates the secret can be reconstituted. */
unsigned can_combine;
} gfsec_secret_t;

Loading…
Cancel
Save