/* * AEAD wrapper around the Secret shared data * * Author: Daan Sprenkels * * This module implements a AEAD wrapper around some secret shared data, * allowing the data to be in any format. (Directly secret-sharing requires the * message to be picked uniformly in the message space.) * * The NaCl cryptographic library is used for the encryption. The encryption * scheme that is used for wrapping the message is salsa20/poly1305. Because * we are using an ephemeral key, we are using a zero'd nonce. */ #include "randombytes.h" #include "tweetnacl.h" #include "sss.h" #include "tweetnacl.h" #include #include /* * These assertions may be considered overkill, but would if the tweetnacl API * ever change we *really* want to prevent buffer overflow vulnerabilities. */ #if crypto_secretbox_KEYBYTES != 32 # error "crypto_secretbox_KEYBYTES size is invalid" #endif /* * Nonce for the `crypto_secretbox` authenticated encryption. * The nonce is constant (zero), because we are using an ephemeral key. */ static const unsigned char nonce[crypto_secretbox_NONCEBYTES] = { 0 }; /* * Return a mutable pointer to the ciphertext part of this Share */ static uint8_t* get_ciphertext(sss_Share *share) { return &((uint8_t*) share)[sss_KEYSHARE_LEN]; } /* * Return a mutable pointer to the Keyshare part of this Share */ static sss_Keyshare* get_keyshare(sss_Share *share) { return (sss_Keyshare*) &share[0]; } /* * Return a const pointer to the ciphertext part of this Share */ static const uint8_t* get_ciphertext_const(const sss_Share *share) { return &((const uint8_t*) share)[sss_KEYSHARE_LEN]; } /* * Return a const pointer to the Keyshare part of this Share */ static const sss_Keyshare* get_keyshare_const(const sss_Share *share) { return (const sss_Keyshare*) &share[0]; } /* * Create `n` shares with theshold `k` and write them to `out` */ void sss_create_shares(sss_Share *out, const unsigned char *data, uint8_t n, uint8_t k) { unsigned char key[32]; unsigned char m[crypto_secretbox_ZEROBYTES + sss_MLEN] = { 0 }; unsigned long long mlen = sizeof(m); /* length includes zero-bytes */ unsigned char c[mlen]; int tmp; sss_Keyshare keyshares[n]; size_t idx; /* Generate a random encryption key */ randombytes(key, sizeof(key)); /* AEAD encrypt the data with the key */ memcpy(&m[crypto_secretbox_ZEROBYTES], data, sss_MLEN); tmp = crypto_secretbox(c, m, mlen, nonce, key); assert(tmp == 0); /* should always happen */ /* Generate KeyShares */ sss_create_keyshares(keyshares, key, n, k); /* Build regular shares */ for (idx = 0; idx < n; idx++) { memcpy(get_keyshare((sss_Share*) &out[idx]), &keyshares[idx][0], sss_KEYSHARE_LEN); memcpy(get_ciphertext((sss_Share*) &out[idx]), &c[crypto_secretbox_BOXZEROBYTES], sss_CLEN); } } /* * Combine `k` shares pointed to by `shares` and write the result to `data` * * This function returns -1 if any of the shares were corrupted or if the number * of shares was too low. It is not possible to detect which of these errors * did occur. */ int sss_combine_shares(uint8_t *data, const sss_Share *shares, uint8_t k) { unsigned char key[crypto_secretbox_KEYBYTES]; unsigned char c[crypto_secretbox_BOXZEROBYTES + sss_CLEN] = { 0 }; unsigned long long clen = sizeof(c); unsigned char m[clen]; sss_Keyshare keyshares[k]; size_t idx; int ret = 0; /* Check if all ciphertexts are the same */ if (k < 1) return -1; for (idx = 1; idx < k; idx++) { if (memcmp(get_ciphertext_const(&shares[0]), get_ciphertext_const(&shares[idx]), sss_CLEN) != 0) { return -1; } } /* Restore the key */ for (idx = 0; idx < k; idx++) { memcpy(&keyshares[idx], get_keyshare_const(&shares[idx]), sss_KEYSHARE_LEN); } sss_combine_keyshares(key, (const sss_Keyshare*) keyshares, k); /* Decrypt the ciphertext */ memcpy(&c[crypto_secretbox_BOXZEROBYTES], &shares[0][sss_KEYSHARE_LEN], sss_CLEN); ret |= crypto_secretbox_open(m, c, clen, nonce, key); memcpy(data, &m[crypto_secretbox_ZEROBYTES], sss_MLEN); return ret; }