int main(void) {
	if (sodium_init() == -1) {
		return -1;
	}

	return_status status = return_status_init();

	//create buffers
	buffer_t *master_key = buffer_create_on_heap(50, 50);
	buffer_t *subkey1 = buffer_create_on_heap(60, 60);
	buffer_t *subkey2 = buffer_create_on_heap(60, 60);
	buffer_t *subkey1_copy = buffer_create_on_heap(60, 60);

	int status_int = 0;
	status_int = buffer_fill_random(master_key, master_key->buffer_length);
	if (status_int != 0) {
		throw(KEYDERIVATION_FAILED, "Failed to generate master key.");
	}
	printf("Master key:\n");
	print_hex(master_key);
	putchar('\n');

	status = derive_key(subkey1, subkey1->buffer_length, master_key, 0);
	throw_on_error(KEYDERIVATION_FAILED, "Failed to derive first subkey.");
	printf("First subkey:\n");
	print_hex(subkey1);
	putchar('\n');

	status = derive_key(subkey2, subkey2->buffer_length, master_key, 1);
	throw_on_error(KEYDERIVATION_FAILED, "Failed to derive the second subkey.");
	printf("Second subkey:\n");
	print_hex(subkey2);
	putchar('\n');

	if (buffer_compare(subkey1, subkey2) == 0) {
		throw(KEYGENERATION_FAILED, "Both subkeys are the same.");
	}

	status = derive_key(subkey1_copy, subkey1_copy->buffer_length, master_key, 0);
	throw_on_error(KEYDERIVATION_FAILED, "Failed to derive copy of the first subkey.");

	if (buffer_compare(subkey1, subkey1_copy) != 0) {
		throw(INCORRECT_DATA, "Failed to reproduce subkey.");
	}

cleanup:
	buffer_destroy_from_heap_and_null_if_valid(master_key);
	buffer_destroy_from_heap_and_null_if_valid(subkey1);
	buffer_destroy_from_heap_and_null_if_valid(subkey2);
	buffer_destroy_from_heap_and_null_if_valid(subkey1_copy);

	on_error {
		print_errors(&status);
	}
	return_status_destroy_errors(&status);

	return status.status;
}
int main(void) {
	sodium_init();

	int status;
	//create random chain key
	buffer_t *chain_key = buffer_create(crypto_auth_BYTES, crypto_auth_BYTES);
	status = buffer_fill_random(chain_key, chain_key->buffer_length);
	if (status != 0) {
		fprintf(stderr, "ERROR: Failed to create chain key. (%i)\n", status);
		buffer_clear(chain_key);
		return status;
	}

	//print first chain key
	printf("Chain key (%zi Bytes):\n", chain_key->content_length);
	print_hex(chain_key);
	putchar('\n');

	//derive message key from chain key
	buffer_t *message_key = buffer_create(crypto_auth_BYTES, crypto_auth_BYTES);
	status = derive_message_key(message_key, chain_key);
	buffer_clear(chain_key);
	if (status != 0) {
		fprintf(stderr, "ERROR: Failed to derive message key. (%i)\n", status);
		buffer_clear(message_key);
		return status;
	}

	//print message key
	printf("Message key (%zi Bytes):\n", message_key->content_length);
	print_hex(message_key);
	putchar('\n');

	buffer_clear(message_key);
	return EXIT_SUCCESS;
}
int main(void) {
	return_status status = return_status_init();

	//generate keys
	buffer_t *header_key = buffer_create_on_heap(crypto_aead_chacha20poly1305_KEYBYTES, crypto_aead_chacha20poly1305_KEYBYTES);
	buffer_t *message_key = buffer_create_on_heap(crypto_secretbox_KEYBYTES, crypto_secretbox_KEYBYTES);
	buffer_t *public_identity_key = buffer_create_on_heap(PUBLIC_KEY_SIZE, PUBLIC_KEY_SIZE);
	buffer_t *public_ephemeral_key = buffer_create_on_heap(PUBLIC_KEY_SIZE, PUBLIC_KEY_SIZE);
	buffer_t *public_prekey = buffer_create_on_heap(PUBLIC_KEY_SIZE, PUBLIC_KEY_SIZE);

	buffer_t *header = buffer_create_on_heap(4, 4);
	buffer_create_from_string(message, "Hello world!\n");

	buffer_t *packet = NULL;
	buffer_t *decrypted_header = NULL;


	if(sodium_init() == -1) {
		throw(INIT_ERROR, "Failed to initialize libsodium.");
	}

	//generate message
	header->content[0] = 0x01;
	header->content[1] = 0x02;
	header->content[2] = 0x03;
	header->content[3] = 0x04;
	molch_message_type packet_type = 1;
	printf("Packet type: %02x\n", packet_type);
	putchar('\n');

	//NORMAL MESSAGE
	printf("NORMAL MESSAGE\n");
	int status_int = 0;
	status = create_and_print_message(
			&packet,
			header_key,
			message_key,
			packet_type,
			header,
			message,
			NULL,
			NULL,
			NULL);
	throw_on_error(GENERIC_ERROR, "Failed to create and print message.");

	//now decrypt the header
	status = packet_decrypt_header(
			&decrypted_header,
			packet,
			header_key);
	throw_on_error(DECRYPT_ERROR, "Failed to decrypt the header.");


	if (decrypted_header->content_length != header->content_length) {
		throw(INVALID_VALUE, "Decrypted header isn't of the same length.");
	}
	printf("Decrypted header has the same length.\n\n");

	//compare headers
	if (buffer_compare(header, decrypted_header) != 0) {
		throw(INVALID_VALUE, "Decrypted header doesn't match.");
	}
	printf("Decrypted header matches.\n\n");

	//check if it decrypts manipulated packets (manipulated metadata)
	printf("Manipulating header length.\n");
	packet->content[2]++;
	status = packet_decrypt_header(
			&decrypted_header,
			packet,
			header_key);
	if (status.status == SUCCESS) {
		throw(GENERIC_ERROR, "Manipulated packet was accepted.");
	} else {
		return_status_destroy_errors(&status);
	}

	printf("Header manipulation detected.\n\n");

	//repair manipulation
	packet->content[2]--;
	//check if it decrypts manipulated packets (manipulated header)
	printf("Manipulate header.\n");
	packet->content[3 + crypto_aead_chacha20poly1305_NPUBBYTES + 1] ^= 0x12;
	status = packet_decrypt_header(
			&decrypted_header,
			packet,
			header_key);
	if (status.status == SUCCESS) {
		throw(GENERIC_ERROR, "Manipulated packet was accepted.");
	} else {
		return_status_destroy_errors(&status);
	}

	printf("Header manipulation detected!\n\n");

	//undo header manipulation
	packet->content[3 + crypto_aead_chacha20poly1305_NPUBBYTES + 1] ^= 0x12;

	//PREKEY MESSAGE
	printf("PREKEY_MESSAGE\n");
	//create the public keys
	status_int = buffer_fill_random(public_identity_key, PUBLIC_KEY_SIZE);
	if (status_int != 0) {
		throw(KEYGENERATION_FAILED, "Failed to generate public identity key.");
	}
	status_int = buffer_fill_random(public_ephemeral_key, PUBLIC_KEY_SIZE);
	if (status_int != 0) {
		throw(KEYGENERATION_FAILED, "Failed to generate public ephemeral key.");
	}
	status_int = buffer_fill_random(public_prekey, PUBLIC_KEY_SIZE);
	if (status_int != 0) {
		throw(KEYGENERATION_FAILED, "Failed to generate public prekey.");
	}

	buffer_destroy_from_heap_and_null_if_valid(packet);

	packet_type = PREKEY_MESSAGE;
	status = create_and_print_message(
			&packet,
			header_key,
			message_key,
			packet_type,
			header,
			message,
			public_identity_key,
			public_ephemeral_key,
			public_prekey);
	throw_on_error(GENERIC_ERROR, "Failed to crate and print message.");

	//now decrypt the header
	status = packet_decrypt_header(
			&decrypted_header,
			packet,
			header_key);
	throw_on_error(DECRYPT_ERROR, "Failed to decrypt the header.");

	if (decrypted_header->content_length != header->content_length) {
		throw(INVALID_VALUE, "Decrypted header isn't of the same length.");
	}
	printf("Decrypted header has the same length.\n\n");

	//compare headers
	if (buffer_compare(header, decrypted_header) != 0) {
		throw(INVALID_VALUE, "Decrypted header doesn't match.");
	}
	printf("Decrypted header matches.\n");

cleanup:
	buffer_destroy_from_heap_and_null_if_valid(header_key);
	buffer_destroy_from_heap_and_null_if_valid(message_key);
	buffer_destroy_from_heap_and_null_if_valid(header);
	buffer_destroy_from_heap_and_null_if_valid(public_identity_key);
	buffer_destroy_from_heap_and_null_if_valid(public_ephemeral_key);
	buffer_destroy_from_heap_and_null_if_valid(public_prekey);
	buffer_destroy_from_heap_and_null_if_valid(packet);
	buffer_destroy_from_heap_and_null_if_valid(decrypted_header);

	on_error {
		print_errors(&status);
	}
	return_status_destroy_errors(&status);

	return status.status;
}
Exemple #4
0
/*
 * Create message and header keys, encrypt header and message
 * and print them.
 */
int create_and_print_message(
		buffer_t * const packet, //needs to be 3 + crypto_aead_chacha20poly1305_NPUBBYTES + crypto_aead_chacha20poly1305_ABYTES + crypto_secretbox_NONCEBYTES + message_length + header_length + crypto_secretbox_MACBYTES + 255
		const unsigned char packet_type,
		const unsigned char current_protocol_version,
		const unsigned char highest_supported_protocol_version,
		const buffer_t * const message,
		buffer_t * const message_key, //output, crypto_secretbox_KEYBYTES
		const buffer_t * const header,
		buffer_t * const header_key) { //output, crypto_aead_chacha20poly1305_KEYBYTES
	int status;
	//create header key
	status = buffer_fill_random(header_key, crypto_aead_chacha20poly1305_KEYBYTES);
	if (status != 0) {
		buffer_clear(header_key);
		return status;
	}
	printf("Header key (%zi Bytes):\n", header_key->content_length);
	print_hex(header_key);
	putchar('\n');

	//create message key
	status = buffer_fill_random(message_key, crypto_secretbox_KEYBYTES);
	if (status != 0) {
		buffer_clear(header_key);
		buffer_clear(message_key);
		return status;
	}
	printf("Message key (%zi Bytes):\n", message_key->content_length);
	print_hex(message_key);
	putchar('\n');

	//print the header (as hex):
	printf("Header (%zi Bytes):\n", header->content_length);
	print_hex(header);
	putchar('\n');

	//print the message (as string):
	printf("Message (%zi Bytes):\n%.*s\n\n", message->content_length, (int)message->content_length, message->content);

	//now encrypt the message
	status = packet_encrypt(
			packet,
			packet_type,
			current_protocol_version,
			highest_supported_protocol_version,
			header,
			header_key,
			message,
			message_key);
	if (status != 0) {
		fprintf(stderr, "ERROR: Failed to encrypt message and header. (%i)\n", status);
		return status;
	}

	//print header nonce
	buffer_t *header_nonce = buffer_create_with_existing_array(packet->content + 3, crypto_aead_chacha20poly1305_NPUBBYTES);
	printf("Header Nonce (%zi Bytes):\n", header_nonce->content_length);
	print_hex(header_nonce);
	putchar('\n');

	//print encrypted packet
	printf("Encrypted Packet (%zi Bytes):\n", packet->content_length);
	print_hex(packet);
	putchar('\n');

	return 0;
}
int main(void) {
	if (sodium_init() == -1) {
		return -1;
	}

	return_status status = return_status_init();

	buffer_t *public_prekey = buffer_create_on_heap(PUBLIC_KEY_SIZE, PUBLIC_KEY_SIZE);
	buffer_t *private_prekey1 = buffer_create_on_heap(PRIVATE_KEY_SIZE, PRIVATE_KEY_SIZE);
	buffer_t *private_prekey2 = buffer_create_on_heap(PRIVATE_KEY_SIZE, PRIVATE_KEY_SIZE);
	buffer_t *prekey_list = buffer_create_on_heap(PREKEY_AMOUNT * PUBLIC_KEY_SIZE, PREKEY_AMOUNT * PUBLIC_KEY_SIZE);

	Prekey **protobuf_export_prekeys = NULL;
	buffer_t **protobuf_export_prekeys_buffers = NULL;
	size_t protobuf_export_prekeys_size = 0;
	Prekey **protobuf_export_deprecated_prekeys = NULL;
	buffer_t **protobuf_export_deprecated_prekeys_buffers = NULL;
	size_t protobuf_export_deprecated_prekeys_size = 0;

	Prekey **protobuf_second_export_prekeys = NULL;
	buffer_t **protobuf_second_export_prekeys_buffers = NULL;
	size_t protobuf_second_export_prekeys_size = 0;
	Prekey **protobuf_second_export_deprecated_prekeys = NULL;
	buffer_t **protobuf_second_export_deprecated_prekeys_buffers = NULL;
	size_t protobuf_second_export_deprecated_prekeys_size = 0;

	prekey_store *store = NULL;
	status = prekey_store_create(&store);
	throw_on_error(CREATION_ERROR, "Failed to create a prekey store.");

	status = prekey_store_list(store, prekey_list);
	throw_on_error(DATA_FETCH_ERROR, "Failed to list prekeys.");
	printf("Prekey list:\n");
	print_hex(prekey_list);
	putchar('\n');

	//compare the public keys with the ones in the prekey store
	for (size_t i = 0; i < PREKEY_AMOUNT; i++) {
		if (buffer_compare_partial(prekey_list, PUBLIC_KEY_SIZE * i, store->prekeys[i].public_key, 0, PUBLIC_KEY_SIZE) != 0) {
			throw(INCORRECT_DATA, "Key list doesn't match the prekey store.");
		}
	}
	printf("Prekey list matches the prekey store!\n");

	//get a private key
	const size_t prekey_index = 10;
	if (buffer_clone(public_prekey, store->prekeys[prekey_index].public_key) != 0) {
		throw(BUFFER_ERROR, "Failed to clone public key.");
	}

	status = prekey_store_get_prekey(store, public_prekey, private_prekey1);
	throw_on_error(DATA_FETCH_ERROR, "Failed to get prekey.")
	printf("Get a Prekey:\n");
	printf("Public key:\n");
	print_hex(public_prekey);
	printf("Private key:\n");
	print_hex(private_prekey1);
	putchar('\n');

	if (store->deprecated_prekeys == NULL) {
		throw(GENERIC_ERROR, "Failed to deprecate requested key.");
	}

	if ((buffer_compare(public_prekey, store->deprecated_prekeys->public_key) != 0)
			|| (buffer_compare(private_prekey1, store->deprecated_prekeys->private_key) != 0)) {
		throw(INCORRECT_DATA, "Deprecated key is incorrect.");
	}

	if (buffer_compare(store->prekeys[prekey_index].public_key, public_prekey) == 0) {
		throw(KEYGENERATION_FAILED, "Failed to generate new key for deprecated one.");
	}
	printf("Successfully deprecated requested key!\n");

	//check if the prekey can be obtained from the deprecated keys
	status = prekey_store_get_prekey(store, public_prekey, private_prekey2);
	throw_on_error(DATA_FETCH_ERROR, "Failed to get key from the deprecated area.");

	if (buffer_compare(private_prekey1, private_prekey2) != 0) {
		throw(INCORRECT_DATA, "Prekey from the deprecated area didn't match.");
	}
	printf("Successfully got prekey from the deprecated area!\n");

	//try to get a nonexistent key
	if (buffer_fill_random(public_prekey, PUBLIC_KEY_SIZE) != 0) {
		throw(KEYGENERATION_FAILED, "Failed to generate invalid public prekey.");
	}
	status = prekey_store_get_prekey(store, public_prekey, private_prekey1);
	if (status.status == SUCCESS) {
		throw(GENERIC_ERROR, "Didn't complain about invalid public key.");
	}
	printf("Detected invalid public prekey!\n");
	//reset return status
	return_status_destroy_errors(&status);
	status.status = SUCCESS;

	//Protobuf-C export
	printf("Protobuf-C export\n");
	status = protobuf_export(
		store,
		&protobuf_export_prekeys,
		&protobuf_export_prekeys_size,
		&protobuf_export_prekeys_buffers,
		&protobuf_export_deprecated_prekeys,
		&protobuf_export_deprecated_prekeys_size,
		&protobuf_export_deprecated_prekeys_buffers);
	throw_on_error(EXPORT_ERROR, "Failed to export prekey store to protobuf.");

	printf("Prekeys:\n");
	puts("[\n");
	for (size_t i = 0; i < protobuf_export_prekeys_size; i++) {
		print_hex(protobuf_export_prekeys_buffers[i]);
		puts(",\n");
	}
	puts("]\n\n");

	printf("Deprecated Prekeys:\n");
	puts("[\n");
	for (size_t i = 0; i < protobuf_export_deprecated_prekeys_size; i++) {
		print_hex(protobuf_export_deprecated_prekeys_buffers[i]);
		puts(",\n");
	}
	puts("]\n\n");

	prekey_store_destroy(store);
	store = NULL;

	printf("Import from Protobuf-C\n");
	status = protobuf_import(
		&store,
		protobuf_export_prekeys_buffers,
		protobuf_export_prekeys_size,
		protobuf_export_deprecated_prekeys_buffers,
		protobuf_export_deprecated_prekeys_size);
	throw_on_error(IMPORT_ERROR, "Failed to import from protobuf.");

	printf("Protobuf-C export again\n");
	status = protobuf_export(
		store,
		&protobuf_second_export_prekeys,
		&protobuf_second_export_prekeys_size,
		&protobuf_second_export_prekeys_buffers,
		&protobuf_second_export_deprecated_prekeys,
		&protobuf_second_export_deprecated_prekeys_size,
		&protobuf_second_export_deprecated_prekeys_buffers);
	throw_on_error(EXPORT_ERROR, "Failed to export prekey store to protobuf.");

	//compare both prekey lists
	printf("Compare normal prekeys\n");
	if (protobuf_export_prekeys_size != protobuf_second_export_prekeys_size) {
		throw(INCORRECT_DATA, "Both prekey exports contain different amounts of keys.");
	}
	for (size_t i = 0; i < protobuf_export_prekeys_size; i++) {
		if (buffer_compare(protobuf_export_prekeys_buffers[i], protobuf_second_export_prekeys_buffers[i]) != 0) {
			throw(INCORRECT_DATA, "First and second prekey export are not identical.");
		}
	}

	//compare both deprecated prekey lists
	printf("Compare deprecated prekeys\n");
	if (protobuf_export_deprecated_prekeys_size != protobuf_second_export_deprecated_prekeys_size) {
		throw(INCORRECT_DATA, "Both depcated prekey exports contain different amounts of keys.");
	}
	for (size_t i = 0; i < protobuf_export_deprecated_prekeys_size; i++) {
		if (buffer_compare(protobuf_export_deprecated_prekeys_buffers[i], protobuf_second_export_deprecated_prekeys_buffers[i]) != 0) {
			throw(INCORRECT_DATA, "First and second deprecated prekey export are not identical.");
		}
	}

	//test the automatic deprecation of old keys
	if (buffer_clone(public_prekey, store->prekeys[PREKEY_AMOUNT-1].public_key) != 0) {
		throw(BUFFER_ERROR, "Failed to clone public key.");
	}

	store->prekeys[PREKEY_AMOUNT-1].expiration_date -= 365 * 24 * 3600; //one year
	store->oldest_expiration_date = store->prekeys[PREKEY_AMOUNT - 1].expiration_date;

	status = prekey_store_rotate(store);
	throw_on_error(GENERIC_ERROR, "Failed to rotate the prekeys.");

	if (buffer_compare(store->deprecated_prekeys->public_key, public_prekey) != 0) {
		throw(GENERIC_ERROR, "Failed to deprecate outdated key.");
	}
	printf("Successfully deprecated outdated key!\n");

	//test the automatic removal of old deprecated keys!
	if (buffer_clone(public_prekey, store->deprecated_prekeys->next->public_key) != 0) {
		throw(BUFFER_ERROR, "Failed to clone public key.");
	}

	store->deprecated_prekeys->next->expiration_date -= 24 * 3600;
	store->oldest_deprecated_expiration_date = store->deprecated_prekeys->next->expiration_date;

	status = prekey_store_rotate(store);
	throw_on_error(GENERIC_ERROR, "Failed to rotate the prekeys.");

	if (store->deprecated_prekeys->next != NULL) {
		throw(GENERIC_ERROR, "Failed to remove outdated key.");
	}
	printf("Successfully removed outdated deprecated key!\n");

	status = protobuf_no_deprecated_keys();
	throw_on_error(GENERIC_ERROR, "Failed to im-/export a prekey store without deprecated prekeys.");

cleanup:
	buffer_destroy_from_heap_and_null_if_valid(public_prekey);
	buffer_destroy_from_heap_and_null_if_valid(private_prekey1);
	buffer_destroy_from_heap_and_null_if_valid(private_prekey2);
	buffer_destroy_from_heap_and_null_if_valid(prekey_list);
	prekey_store_destroy(store);

	if (protobuf_export_prekeys != NULL) {
		for (size_t i = 0; i < protobuf_export_prekeys_size; i++) {
			if (protobuf_export_prekeys[i] != NULL) {
				prekey__free_unpacked(protobuf_export_prekeys[i], &protobuf_c_allocators);
				protobuf_export_prekeys[i] = NULL;
			}

		}
		zeroed_free_and_null_if_valid(protobuf_export_prekeys);
	}

	if (protobuf_export_deprecated_prekeys != NULL) {
		for (size_t i = 0; i < protobuf_export_deprecated_prekeys_size; i++) {
			if (protobuf_export_deprecated_prekeys[i] != NULL) {
				prekey__free_unpacked(protobuf_export_deprecated_prekeys[i], &protobuf_c_allocators);
				protobuf_export_deprecated_prekeys[i] = NULL;
			}
		}

		zeroed_free_and_null_if_valid(protobuf_export_deprecated_prekeys);
	}

	if (protobuf_export_prekeys_buffers != NULL) {
		for (size_t i = 0; i < protobuf_export_prekeys_size; i++) {
			buffer_destroy_from_heap_and_null_if_valid(protobuf_export_prekeys_buffers[i]);
		}

		zeroed_free_and_null_if_valid(protobuf_export_prekeys_buffers);
	}

	if (protobuf_export_deprecated_prekeys_buffers != NULL) {
		for (size_t i = 0; i < protobuf_export_deprecated_prekeys_size; i++) {
			buffer_destroy_from_heap_and_null_if_valid(protobuf_export_deprecated_prekeys_buffers[i]);
		}

		zeroed_free_and_null_if_valid(protobuf_export_deprecated_prekeys_buffers);
	}

	if (protobuf_second_export_prekeys != NULL) {
		for (size_t i = 0; i < protobuf_second_export_prekeys_size; i++) {
			if (protobuf_second_export_prekeys[i] != NULL) {
				prekey__free_unpacked(protobuf_second_export_prekeys[i], &protobuf_c_allocators);
				protobuf_second_export_prekeys[i] = NULL;
			}

		}
		zeroed_free_and_null_if_valid(protobuf_second_export_prekeys);
	}

	if (protobuf_second_export_deprecated_prekeys != NULL) {
		for (size_t i = 0; i < protobuf_second_export_deprecated_prekeys_size; i++) {
			if (protobuf_second_export_deprecated_prekeys[i] != NULL) {
				prekey__free_unpacked(protobuf_second_export_deprecated_prekeys[i], &protobuf_c_allocators);
				protobuf_second_export_deprecated_prekeys[i] = NULL;
			}
		}

		zeroed_free_and_null_if_valid(protobuf_second_export_deprecated_prekeys);
	}

	if (protobuf_second_export_prekeys_buffers != NULL) {
		for (size_t i = 0; i < protobuf_second_export_prekeys_size; i++) {
			buffer_destroy_from_heap_and_null_if_valid(protobuf_second_export_prekeys_buffers[i]);
		}

		zeroed_free_and_null_if_valid(protobuf_second_export_prekeys_buffers);
	}

	if (protobuf_second_export_deprecated_prekeys_buffers != NULL) {
		for (size_t i = 0; i < protobuf_second_export_deprecated_prekeys_size; i++) {
			buffer_destroy_from_heap_and_null_if_valid(protobuf_second_export_deprecated_prekeys_buffers[i]);
		}

		zeroed_free_and_null_if_valid(protobuf_second_export_deprecated_prekeys_buffers);
	}

	on_error {
		print_errors(&status);
	}
	return_status_destroy_errors(&status);

	return status.status;
}
int main(void) {
	if (sodium_init() == -1) {
		return -1;
	}

	return_status status = return_status_init();

	//create key buffers
	buffer_t *alice_public_ephemeral = buffer_create_on_heap(crypto_box_PUBLICKEYBYTES, crypto_box_PUBLICKEYBYTES);
	buffer_t *alice_private_ephemeral = buffer_create_on_heap(crypto_box_SECRETKEYBYTES, crypto_box_SECRETKEYBYTES);
	buffer_t *bob_public_ephemeral = buffer_create_on_heap(crypto_box_PUBLICKEYBYTES, crypto_box_PUBLICKEYBYTES);
	buffer_t *bob_private_ephemeral = buffer_create_on_heap(crypto_box_SECRETKEYBYTES, crypto_box_SECRETKEYBYTES);
	buffer_t *previous_root_key = buffer_create_on_heap(crypto_secretbox_KEYBYTES, crypto_secretbox_KEYBYTES);
	buffer_t *alice_root_key = buffer_create_on_heap(crypto_secretbox_KEYBYTES, crypto_secretbox_KEYBYTES);
	buffer_t *alice_chain_key = buffer_create_on_heap(crypto_secretbox_KEYBYTES, crypto_secretbox_KEYBYTES);
	buffer_t *alice_header_key = buffer_create_on_heap(HEADER_KEY_SIZE, HEADER_KEY_SIZE);
	buffer_t *bob_root_key = buffer_create_on_heap(crypto_secretbox_KEYBYTES, crypto_secretbox_KEYBYTES);
	buffer_t *bob_chain_key = buffer_create_on_heap(crypto_secretbox_KEYBYTES, crypto_secretbox_KEYBYTES);
	buffer_t *bob_header_key = buffer_create_on_heap(HEADER_KEY_SIZE, HEADER_KEY_SIZE);

	//create Alice's keypair
	buffer_create_from_string(alice_string, "Alice");
	buffer_create_from_string(ephemeral_string, "ephemeral");
	status = generate_and_print_keypair(
			alice_public_ephemeral,
			alice_private_ephemeral,
			alice_string,
			ephemeral_string);
	throw_on_error(KEYGENERATION_FAILED, "Failed to generate and print Alice's ephemeral keypair.");

	//create Bob's keypair
	buffer_create_from_string(bob_string, "Bob");
	status = generate_and_print_keypair(
			bob_public_ephemeral,
			bob_private_ephemeral,
			bob_string,
			ephemeral_string);
	throw_on_error(KEYGENERATION_FAILED, "Failed to generate and print Bob's ephemeral keypair.");

	//create previous root key
	if (buffer_fill_random(previous_root_key, crypto_secretbox_KEYBYTES) != 0) {
		throw(KEYGENERATION_FAILED, "Failed to generate previous root key.");
	}

	//print previous root key
	printf("Previous root key (%zu Bytes):\n", previous_root_key->content_length);
	print_hex(previous_root_key);
	putchar('\n');

	//derive root and chain key for Alice
	status = derive_root_next_header_and_chain_keys(
			alice_root_key,
			alice_header_key,
			alice_chain_key,
			alice_private_ephemeral,
			alice_public_ephemeral,
			bob_public_ephemeral,
			previous_root_key,
			true);
	throw_on_error(KEYDERIVATION_FAILED, "Failed to derive root, next header and chain key for Alice.");

	//print Alice's root and chain key
	printf("Alice's root key (%zu Bytes):\n", alice_root_key->content_length);
	print_hex(alice_root_key);
	printf("Alice's chain key (%zu Bytes):\n", alice_chain_key->content_length);
	print_hex(alice_chain_key);
	printf("Alice's header key (%zu Bytes):\n", alice_header_key->content_length);
	print_hex(alice_header_key);
	putchar('\n');

	//derive root and chain key for Bob
	status = derive_root_next_header_and_chain_keys(
			bob_root_key,
			bob_header_key,
			bob_chain_key,
			bob_private_ephemeral,
			bob_public_ephemeral,
			alice_public_ephemeral,
			previous_root_key,
			false);
	throw_on_error(KEYDERIVATION_FAILED, "Failed to derive root, next header and chain key for Bob.");

	//print Bob's root and chain key
	printf("Bob's root key (%zu Bytes):\n", bob_root_key->content_length);
	print_hex(bob_root_key);
	printf("Bob's chain key (%zu Bytes):\n", bob_chain_key->content_length);
	print_hex(bob_chain_key);
	printf("Bob's header key (%zu Bytes):\n", bob_header_key->content_length);
	print_hex(bob_header_key);
	putchar('\n');

	//compare Alice's and Bob's root keys
	if (buffer_compare(alice_root_key, bob_root_key) == 0) {
		printf("Alice's and Bob's root keys match.\n");
	} else {
		throw(INCORRECT_DATA, "Alice's and Bob's root keys don't match.");
	}
	buffer_clear(alice_root_key);
	buffer_clear(bob_root_key);

	//compare Alice's and Bob's chain keys
	if (buffer_compare(alice_chain_key, bob_chain_key) == 0) {
		printf("Alice's and Bob's chain keys match.\n");
	} else {
		throw(INCORRECT_DATA, "Alice's and Bob's chain keys don't match.");
	}

	//compare Alice's and Bob's header keys
	if (buffer_compare(alice_header_key, bob_header_key) == 0) {
		printf("Alice's and Bob's header keys match.\n");
	} else {
		throw(INCORRECT_DATA, "Alice's and Bob's header keys don't match.");
	}

cleanup:
	buffer_destroy_from_heap_and_null_if_valid(alice_public_ephemeral);
	buffer_destroy_from_heap_and_null_if_valid(alice_private_ephemeral);
	buffer_destroy_from_heap_and_null_if_valid(bob_public_ephemeral);
	buffer_destroy_from_heap_and_null_if_valid(bob_private_ephemeral);
	buffer_destroy_from_heap_and_null_if_valid(previous_root_key);
	buffer_destroy_from_heap_and_null_if_valid(alice_root_key);
	buffer_destroy_from_heap_and_null_if_valid(alice_chain_key);
	buffer_destroy_from_heap_and_null_if_valid(alice_header_key);
	buffer_destroy_from_heap_and_null_if_valid(bob_root_key);
	buffer_destroy_from_heap_and_null_if_valid(bob_chain_key);
	buffer_destroy_from_heap_and_null_if_valid(bob_header_key);

	on_error {
		print_errors(&status);
	}
	return_status_destroy_errors(&status);

	return status.status;
}