FileBase::FileBase(int data_fd, int meta_fd, const key_type& key_, const id_type& id_, bool check) : m_lock() , m_refcount(1) , m_header() , m_id(id_) , m_data_fd(data_fd) , m_meta_fd(meta_fd) , m_dirty(false) , m_check(check) , m_stream() , m_removed(false) { auto data_stream = std::make_shared<POSIXFileStream>(data_fd); auto meta_stream = std::make_shared<POSIXFileStream>(meta_fd); key_type data_key, meta_key; byte generated_keys[KEY_LENGTH * 3]; hkdf(key_.data(), key_.size(), nullptr, 0, id_.data(), id_.size(), generated_keys, sizeof(generated_keys)); memcpy(data_key.data(), generated_keys, KEY_LENGTH); memcpy(meta_key.data(), generated_keys + KEY_LENGTH, KEY_LENGTH); memcpy(m_key.data(), generated_keys + 2 * KEY_LENGTH, KEY_LENGTH); auto crypt = make_cryptstream_aes_gcm( std::move(data_stream), std::move(meta_stream), data_key, meta_key, id_, check); m_stream = crypt.first; m_header = crypt.second; read_header(); }
int main(void) { sodium_init(); printf("HKDF as described in RFC 5869 based on HMAC-SHA512256!\n\n"); unsigned char output_key[200]; size_t output_key_length = sizeof(output_key); //create random salt unsigned char salt[crypto_auth_KEYBYTES]; randombytes_buf(salt, crypto_auth_KEYBYTES); printf("Salt (%i Bytes):\n", crypto_auth_KEYBYTES); print_hex(salt, crypto_auth_KEYBYTES, 30); putchar('\n'); //create key to derive from unsigned char input_key[100]; size_t input_key_length = sizeof(input_key); randombytes_buf(input_key, input_key_length); printf("Input key (%zu Bytes):\n", input_key_length); print_hex(input_key, input_key_length, 30); putchar('\n'); //info unsigned char* info = (unsigned char*) "This is some info!"; size_t info_length = sizeof(info); printf("Info (%zu Bytes):\n", info_length); //this could also be binary data printf("%s\n\n", info); int status; status = hkdf(output_key, output_key_length, salt, input_key, input_key_length, info, info_length); if (status != 0) { fprintf(stderr, "ERROR: Failed to derive key. %i\n", status); return EXIT_FAILURE; } printf("Derived key (%zu Bytes):\n", output_key_length); print_hex(output_key, output_key_length, 30); putchar('\n'); return EXIT_SUCCESS; }
/* * Derive a root and initial chain key for a new ratchet. * * The chain and root key have to be crypto_secretbox_KEYBYTES long. * * RK, CK, HK = HKDF( RK, DH(DHRr, DHRs) ) */ int derive_root_chain_and_header_keys( buffer_t * const root_key, //crypto_secretbox_KEYBYTES buffer_t * const chain_key, //crypto_secretbox_KEYBYTES buffer_t * const header_key, //crypto_aead_chacha20poly1305_KEYBYTES const buffer_t * const our_private_ephemeral, const buffer_t * const our_public_ephemeral, const buffer_t * const their_public_ephemeral, const buffer_t * const previous_root_key, bool am_i_alice) { assert(crypto_secretbox_KEYBYTES == crypto_auth_KEYBYTES); //check size of the buffers if ((root_key->buffer_length < crypto_secretbox_KEYBYTES) || (chain_key->buffer_length < crypto_secretbox_KEYBYTES) || (header_key->buffer_length < crypto_aead_chacha20poly1305_KEYBYTES) || (our_private_ephemeral->content_length != crypto_box_SECRETKEYBYTES) || (our_public_ephemeral->content_length != crypto_box_PUBLICKEYBYTES) || (their_public_ephemeral->content_length != crypto_box_PUBLICKEYBYTES) || (previous_root_key->content_length != crypto_secretbox_KEYBYTES)) { return -6; } int status; //input key for HKDF (root key and chain key derivation) //input_key = DH(our_private_ephemeral, their_public_ephemeral) buffer_t *input_key = buffer_create(crypto_secretbox_KEYBYTES, crypto_secretbox_KEYBYTES); status = diffie_hellman( input_key, our_private_ephemeral, our_public_ephemeral, their_public_ephemeral, am_i_alice); if (status != 0) { buffer_clear(input_key); return status; } //now create root and chain key in temporary buffer //RK, CK = HKDF(previous_root_key, input_key) buffer_t *info = buffer_create_from_string(INFO); buffer_t *hkdf_buffer = buffer_create(2 * crypto_secretbox_KEYBYTES + crypto_aead_chacha20poly1305_KEYBYTES, 2 * crypto_secretbox_KEYBYTES + crypto_aead_chacha20poly1305_KEYBYTES); status = hkdf( hkdf_buffer, hkdf_buffer->content_length, previous_root_key, //salt input_key, info); buffer_clear(input_key); if (status != 0) { buffer_clear(hkdf_buffer); return status; } //copy keys from hkdf buffer status = buffer_copy(root_key, 0, hkdf_buffer, 0, crypto_secretbox_KEYBYTES); if (status != 0) { buffer_clear(hkdf_buffer); buffer_clear(root_key); return status; } status = buffer_copy(chain_key, 0, hkdf_buffer, crypto_secretbox_KEYBYTES, crypto_secretbox_KEYBYTES); if (status != 0) { buffer_clear(hkdf_buffer); buffer_clear(root_key); buffer_clear(chain_key); return status; } status = buffer_copy(header_key, 0, hkdf_buffer, 2 * crypto_secretbox_KEYBYTES, crypto_secretbox_KEYBYTES); if (status != 0) { buffer_clear(hkdf_buffer); buffer_clear(root_key); buffer_clear(chain_key); buffer_clear(header_key); return status; } buffer_clear(hkdf_buffer); return 0; }
/* * Derive initial root, chain and header keys. * * RK, CKs/r, HKs/r, NHKs/r = HKDF(HASH(DH(A,B0) || DH(A0,B) || DH(A0,B0))) */ int derive_initial_root_chain_and_header_keys( buffer_t * const root_key, //crypto_secretbox_KEYBYTES buffer_t * const send_chain_key, //crypto_secretbox_KEYBYTES buffer_t * const receive_chain_key, //crypto_secretbox_KEYBYTES buffer_t * const send_header_key, //crypto_aead_chacha20poly1305_KEYBYTES buffer_t * const receive_header_key, //crypto_aead_chacha20poly1305_KEYBYTES buffer_t * const next_send_header_key, //crypto_aead_chacha20poly1305_KEYBYTES buffer_t * const next_receive_header_key, //crypto_aead_chacha20poly1305_KEYBYTES const buffer_t * const our_private_identity, const buffer_t * const our_public_identity, const buffer_t * const their_public_identity, const buffer_t * const our_private_ephemeral, const buffer_t * const our_public_ephemeral, const buffer_t * const their_public_ephemeral, bool am_i_alice) { //check buffer sizes if ((root_key->buffer_length < crypto_secretbox_KEYBYTES) || (send_chain_key->buffer_length < crypto_secretbox_KEYBYTES) || (receive_chain_key->buffer_length < crypto_secretbox_KEYBYTES) || (send_header_key->buffer_length < crypto_aead_chacha20poly1305_KEYBYTES) || (receive_header_key->buffer_length < crypto_aead_chacha20poly1305_KEYBYTES) || (next_send_header_key->buffer_length < crypto_aead_chacha20poly1305_KEYBYTES) || (next_receive_header_key->buffer_length < crypto_aead_chacha20poly1305_KEYBYTES) || (our_private_identity->content_length != crypto_box_SECRETKEYBYTES) || (our_public_identity->content_length != crypto_box_PUBLICKEYBYTES) || (their_public_identity->content_length != crypto_box_PUBLICKEYBYTES) || (our_private_ephemeral->content_length != crypto_box_SECRETKEYBYTES) || (our_public_ephemeral->content_length != crypto_box_PUBLICKEYBYTES) || (their_public_ephemeral->content_length != crypto_box_PUBLICKEYBYTES)) { return -6; } int status; //derive pre_root_key to later derive the initial root key, //header keys and chain keys from //pre_root_key = HASH( DH(A,B0) || DH(A0,B) || DH(A0,B0) ) assert(crypto_secretbox_KEYBYTES == crypto_auth_BYTES); buffer_t *pre_root_key = buffer_create(crypto_secretbox_KEYBYTES, crypto_secretbox_KEYBYTES); status = triple_diffie_hellman( pre_root_key, our_private_identity, our_public_identity, our_private_ephemeral, our_public_ephemeral, their_public_identity, their_public_ephemeral, am_i_alice); if (status != 0) { buffer_clear(pre_root_key); return status; } //derive chain, root and header keys from pre_root_key via HKDF //RK, CK, HK, NHK1, NHK2 = HKDF(salt, pre_root_key) buffer_t *hkdf_buffer = buffer_create(2 * crypto_secretbox_KEYBYTES + 3 * crypto_aead_chacha20poly1305_KEYBYTES, 2 * crypto_secretbox_KEYBYTES + 3 * crypto_aead_chacha20poly1305_KEYBYTES); buffer_t *salt = buffer_create_from_string("molch--libsodium-crypto-library"); assert(salt->content_length == crypto_auth_KEYBYTES); buffer_t *info = buffer_create_from_string(INFO); status = hkdf( hkdf_buffer, hkdf_buffer->content_length, salt, pre_root_key, info); buffer_clear(pre_root_key); if (status != 0) { buffer_clear(hkdf_buffer); return status; } //now copy the keys //root key: status = buffer_copy(root_key, 0, hkdf_buffer, 0, crypto_secretbox_KEYBYTES); if (status != 0) { buffer_clear(hkdf_buffer); buffer_clear(root_key); return status; } //chain keys and header keys if (am_i_alice) { //Alice: CKs=<none>, CKr=HKDF //TODO <none> will be an empty buffer in the future send_chain_key->content_length = crypto_secretbox_KEYBYTES; memset(send_chain_key->content, 0, send_chain_key->content_length); status = buffer_copy(receive_chain_key, 0, hkdf_buffer, crypto_secretbox_KEYBYTES, crypto_secretbox_KEYBYTES); if (status != 0) { buffer_clear(hkdf_buffer); buffer_clear(root_key); buffer_clear(receive_chain_key); return status; } //HKs=<none>, HKr=HKDF //TODO <none> will be an empty buffer in the future send_header_key->content_length = crypto_aead_chacha20poly1305_KEYBYTES; memset(send_header_key->content, 0, send_header_key->content_length); status = buffer_copy(receive_header_key, 0, hkdf_buffer, 2 * crypto_secretbox_KEYBYTES, crypto_aead_chacha20poly1305_KEYBYTES); if (status != 0) { buffer_clear(hkdf_buffer); buffer_clear(root_key); buffer_clear(receive_chain_key); buffer_clear(receive_header_key); return status; } //NHKs, NHKr status = buffer_copy(next_send_header_key, 0, hkdf_buffer, 2 * crypto_secretbox_KEYBYTES + crypto_aead_chacha20poly1305_KEYBYTES, crypto_aead_chacha20poly1305_KEYBYTES); if (status != 0) { buffer_clear(hkdf_buffer); buffer_clear(root_key); buffer_clear(receive_chain_key); buffer_clear(receive_header_key); buffer_clear(next_send_header_key); return status; } status = buffer_copy(next_receive_header_key, 0, hkdf_buffer, 2 * crypto_secretbox_KEYBYTES + 2 * crypto_aead_chacha20poly1305_KEYBYTES, crypto_aead_chacha20poly1305_KEYBYTES); if (status != 0) { buffer_clear(hkdf_buffer); buffer_clear(root_key); buffer_clear(receive_chain_key); buffer_clear(receive_header_key); buffer_clear(next_send_header_key); buffer_clear(next_receive_header_key); return status; } } else { //Bob: CKs=HKDF, CKr=<none> receive_chain_key->content_length = crypto_secretbox_KEYBYTES; memset(receive_chain_key->content, 0, receive_chain_key->content_length); status = buffer_copy(send_chain_key, 0, hkdf_buffer, crypto_secretbox_KEYBYTES, crypto_secretbox_KEYBYTES); if (status != 0) { buffer_clear(hkdf_buffer); buffer_clear(root_key); buffer_clear(send_chain_key); return status; } //HKs=HKDF, HKr=<none> receive_header_key->content_length = crypto_aead_chacha20poly1305_KEYBYTES; memset(receive_header_key->content, 0, receive_header_key->content_length); status = buffer_copy(send_header_key, 0, hkdf_buffer, 2 * crypto_secretbox_KEYBYTES, crypto_aead_chacha20poly1305_KEYBYTES); if (status != 0) { buffer_clear(hkdf_buffer); buffer_clear(root_key); buffer_clear(send_chain_key); buffer_clear(send_header_key); return status; } //NHKr, NHKs status = buffer_copy(next_receive_header_key, 0, hkdf_buffer, 2 * crypto_secretbox_KEYBYTES + crypto_aead_chacha20poly1305_KEYBYTES, crypto_aead_chacha20poly1305_KEYBYTES); if (status != 0) { buffer_clear(hkdf_buffer); buffer_clear(root_key); buffer_clear(send_chain_key); buffer_clear(send_header_key); buffer_clear(next_receive_header_key); return status; } status = buffer_copy(next_send_header_key, 0, hkdf_buffer, 2 * crypto_secretbox_KEYBYTES + 2 * crypto_aead_chacha20poly1305_KEYBYTES, crypto_aead_chacha20poly1305_KEYBYTES); if (status != 0) { buffer_clear(hkdf_buffer); buffer_clear(root_key); buffer_clear(send_chain_key); buffer_clear(send_header_key); buffer_clear(next_receive_header_key); buffer_clear(next_send_header_key); return status; } } buffer_clear(hkdf_buffer); return 0; }