/* threshcrypt shares.c
* Copyright 2012 Ryan Castellucci
* This software is published under the terms of the Simplified BSD License.
* Please see the 'COPYING' file for details.
* Portions of this file are derived from libgfshare examples which are
* Copyright Daniel Silverstone 2006-2011
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include "common.h"
#include "util.h"
#include "shares.h"
#include "crypt.h"
/* put n random non-zero uchars into *sharenrs with no duplicates */
void gen_sharenrs(unsigned char *sharenrs, unsigned char n) {
int i;
unsigned int r;
unsigned char t;
unsigned char buf[255];
for (i = 0; i < 255; i++) buf[i] = i + 1;
while(i > 0) {
do {
fill_prng((unsigned char *)&r, sizeof(r));
} while (r > (UINT_MAX - (UINT_MAX % i) - 1)); /* avoids bias */
r %= i--;
t = buf[i];
buf[i] = buf[r];
buf[r] = t;
}
assert(i == 0);
while (i < n) {
sharenrs[i] = buf[i];
i++;
}
}
int tc_gfsplit(header_data_t *header) {
int err;
unsigned int i;
share_data_t *share;
gfshare_ctx *G = NULL;
assert(header->nshares > 0);
assert(header->thresh > 0);
assert(header->thresh <= header->nshares);
unsigned char *sharenrs = safe_malloc(header->nshares);
/* master key setup */
fill_rand(header->master_key, header->key_size);
fill_prng(header->master_salt, SALT_SIZE);
/* Add (hopefully) some extra protection against potentially weak RNG
* TODO use a hash for this... */
for (i = 0; i < header->nshares; i++ ) {
share = &(header->shares[i]);
memxor(header->master_key, share->key, header->key_size);
}
size_t hmac_size = header->hmac_size;
if ((err = pbkdf2(header->master_key, header->key_size,
header->master_salt, SALT_SIZE, SUBKEY_ITER,
find_hash("sha256"), header->master_hmac, &hmac_size)) != CRYPT_OK) {
fprintf(stderr, "PBKDF2 failed: %d\n", err);
}
/* end master key setup */
#ifdef CRYPTODEBUG
fprintf(stderr, "MasterSlt: ");
for (i = 0;i < SALT_SIZE;i++) {
fprintf(stderr, "%02x", header->master_salt[i]);
}
fprintf(stderr, "\n");
fprintf(stderr, "MasterKey: ");
for (i = 0;i < header->key_size;i++) {
fprintf(stderr, "%02x", header->master_key[i]);
}
fprintf(stderr, "\n");
fprintf(stderr, "MasterMAC: ");
for (i = 0;i < header->hmac_size;i++) {
fprintf(stderr, "%02x", header->master_hmac[i]);
}
fprintf(stderr, "\n\n");
#endif
if (header->thresh > 1) {
gen_sharenrs(sharenrs, header->nshares);
/* TODO figure out how to mlock G's memory */
G = gfshare_ctx_init_enc(sharenrs, header->nshares, header->thresh, header->key_size);
/* G->buffer could be free'd and reinitialized with size G->buffersize - wrapper? */
if (G == NULL) {
perror("gfshare_ctx_init_enc");
return -1;
}
gfshare_ctx_enc_setsecret(G, header->master_key);
}
for (i = 0; i < header->nshares; i++ ) {
share = &(header->shares[i]);
if (share->ptxt == NULL)
share->ptxt = keymem_alloc(header->keymem, header->share_size);
share->ctxt = safe_malloc(header->share_size);
share->hmac = safe_malloc(header->hmac_size);
share->ptxt[0] = sharenrs[i];
if (header->thresh == 1) {
memcpy(share->ptxt + 1, header->master_key, header->key_size);
} else {
gfshare_ctx_enc_getshare(G, i, share->ptxt + 1);
}
#ifdef CRYPTODEBUG
fprintf(stderr, "Salt [%02x]: ", i);
for (j = 0;j < SALT_SIZE;j++) {
fprintf(stderr, "%02x", share->salt[j]);
}
fprintf(stderr, "\n");
fprintf(stderr, "Key [%02x]: ", i);
for (j = 0;j < header->key_size;j++) {
fprintf(stderr, "%02x", share->key[j]);
}
fprintf(stderr, "\n");
fprintf(stderr, "Share[%02x]: ", i);
for (j = 0;j < header->share_size;j++) {
fprintf(stderr, "%02x", share->ptxt[j]);
}
fprintf(stderr, "\n");
#endif /* CRYPTODEBUG */
if ((err = encrypt_data(share->ptxt, share->ctxt, header->share_size,
share->key, header->key_size,
share->hmac, header->hmac_size)) != 0) {
fprintf(stderr, "Encrypt failed: %d\n", err);
}
#ifdef CRYPTODEBUG
fprintf(stderr, "Crypt[%02x]: ", i);
for (j = 0;j < header->share_size;j++) {
fprintf(stderr, "%02x", share->ctxt[j]);
}
fprintf(stderr, "\n");
#endif /* CRYPTODEBUG */
#ifdef TESTDECRYPT
if ((err = decrypt_data(share->ctxt, share->ptxt, header->share_size,
share->key, header->key_size,
share->hmac, header->hmac_size)) != 0) {
fprintf(stderr, "Decrypt failed: %d\n", err);
}
#ifdef CRYPTODEBUG
fprintf(stderr, "Plain[%02x]: ", i);
for (j = 0;j < header->share_size;j++) {
fprintf(stderr, "%02x", share->ptxt[j]);
}
fprintf(stderr, "\n");
#endif /* CRYPTODEBUG */
#endif /* TESTDECRYPT */
MEMWIPE(share->ptxt, header->share_size);
MEMWIPE(share->key, header->key_size);
#ifdef CRYPTODEBUG
fprintf(stderr, "MAC [%02x]: ", i);
for (j = 0;j < header->hmac_size;j++) {
fprintf(stderr, "%02x", share->hmac[j]);
}
fprintf(stderr, "\n\n");
#endif /* CRYPTODEBUG */
}
/* wipe sensitive data and free memory */
if (header->thresh > 1) {
gfshare_ctx_free(G);
}
wipe_free(sharenrs, header->nshares);
/* header->master_key stays so that it can be used for file encryption */
return 0;
}
int unlock_shares(const unsigned char *pass, size_t pass_len, header_data_t *header) {
int i, err, ret;
ret = 0;
if (header->tmp_share_key == NULL)
header->tmp_share_key = keymem_alloc(header->keymem, header->key_size);
if (header->tmp_share_ptxt == NULL)
header->tmp_share_ptxt = keymem_alloc(header->keymem, header->share_size);
for (i = 0; i < header->nshares; i++) {
share_data_t *share;
share = &(header->shares[i]);
assert(share->key == NULL);
if (share->ptxt == NULL) {
share->key = header->tmp_share_key;
share->ptxt = header->tmp_share_ptxt;
unsigned long key_size;
key_size = header->key_size;
fprintf(stderr, "\033[0G\033[2KChecking share %d", i);
if ((err = pbkdf2(pass, pass_len, share->salt, SALT_SIZE, share->iter,
find_hash("sha256"), share->key, &key_size)) != CRYPT_OK) {
/* on an hmac failure (wrong password) */
MEMWIPE(share->key, header->key_size);
continue;
}
if ((err = decrypt_data(share->ctxt, share->ptxt, header->share_size,
share->key, header->key_size,
share->hmac, header->hmac_size)) == 0) {
fprintf(stderr, "\033[0G\033[2KUnlocked share %d\n", i);
/* new ptxt region for the next share */
header->tmp_share_ptxt = keymem_alloc(header->keymem, header->share_size);
ret++;
} else {
MEMWIPE(share->ptxt, header->share_size);
share->ptxt = NULL;
}
MEMWIPE(share->key, header->key_size);
share->key = NULL;
}
}
fprintf(stderr, "\033[0G\033[2K");
return ret;
}
int tc_gfcombine(header_data_t *header) {
int i, loaded, err, ret;
err = ret = loaded = 0;
assert(header->master_key == NULL);
header->master_key = keymem_alloc(header->keymem, header->key_size);
if (header->thresh == 1) {
for (i = 0; i < header->nshares; i++) {
share_data_t *share;
share = &(header->shares[i]);
if (share->ptxt != NULL) {
memcpy(header->master_key, share->ptxt + 1, header->key_size);
break;
}
}
} else {
gfshare_ctx *G;
unsigned char sharenrs[256];
MEMZERO(sharenrs, sizeof(sharenrs));
/* Initialize a gfshare context for decoding */
G = gfshare_ctx_init_dec(sharenrs, header->thresh, header->key_size);
for (i = 0; i < header->nshares; i++) {
share_data_t *share;
share = &(header->shares[i]);
if (share->ptxt != NULL) {
sharenrs[loaded] = share->ptxt[0];
gfshare_ctx_dec_newshares(G, sharenrs);
gfshare_ctx_dec_giveshare(G, loaded, share->ptxt + 1);
if (++loaded == header->thresh)
break;
}
}
gfshare_ctx_dec_extract(G, header->master_key);
gfshare_ctx_free(G);
}
return ret;
}
/* vim: set ts=2 sw=2 et ai si: */