Example #1
0
int main(void) {
	sodium_init();

	//create a user_store
	user_store *store = user_store_create();

	//check the content
	buffer_t *list = user_store_list(store);
	if (list->content_length != 0) {
		fprintf(stderr, "ERROR: List of users is not empty.\n");
		user_store_destroy(store);
		buffer_destroy_from_heap(list);

		return EXIT_FAILURE;
	}
	buffer_destroy_from_heap(list);

	int status;
	//create three users with prekeys and identity keys
	//first alice
	//alice identity key
	buffer_t *alice_private_identity = buffer_create(crypto_box_SECRETKEYBYTES, crypto_box_SECRETKEYBYTES);
	buffer_t *alice_public_identity = buffer_create(crypto_box_PUBLICKEYBYTES, crypto_box_PUBLICKEYBYTES);
	status = generate_and_print_keypair(
			alice_public_identity->content,
			alice_private_identity->content,
			"Alice",
			"identity");
	if (status != 0) {
		fprintf(stderr, "ERROR: Failed to generate Alice's identity keypair.\n");
		buffer_clear(alice_private_identity);
		return status;
	}

	//alice prekeys
	buffer_t *alice_private_prekeys = buffer_create(PREKEY_AMOUNT * crypto_box_SECRETKEYBYTES, PREKEY_AMOUNT * crypto_box_SECRETKEYBYTES);
	buffer_t *alice_public_prekeys = buffer_create(PREKEY_AMOUNT * crypto_box_PUBLICKEYBYTES, PREKEY_AMOUNT * crypto_box_PUBLICKEYBYTES);
	status = generate_prekeys(alice_private_prekeys, alice_public_prekeys);
	if (status != 0) {
		fprintf(stderr, "ERROR: Failed to generate Alice's prekeys.\n");
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_prekeys);
		return status;
	}

	//make illegal access to the user store
	printf("User store length: %zi\n", store->length); //The program should crash here!

	user_store_destroy(store);
	return EXIT_SUCCESS;
}
int main(void) {
	sodium_init();

	int status;

	//create Alice's keypair
	unsigned char alice_public_key[crypto_box_PUBLICKEYBYTES];
	unsigned char alice_private_key[crypto_box_SECRETKEYBYTES];
	status = generate_and_print_keypair(
			alice_public_key,
			alice_private_key,
			"Alice",
			"");
	if (status != 0) {
		sodium_memzero(alice_private_key, crypto_box_SECRETKEYBYTES);
		return status;
	}

	//create Bob's keypair
	unsigned char bob_public_key[crypto_box_PUBLICKEYBYTES];
	unsigned char bob_private_key[crypto_box_SECRETKEYBYTES];
	status = generate_and_print_keypair(
			bob_public_key,
			bob_private_key,
			"Bob",
			"");
	if (status != 0) {
		sodium_memzero(alice_private_key, crypto_box_SECRETKEYBYTES);
		sodium_memzero(bob_private_key, crypto_box_SECRETKEYBYTES);
		return status;
	}

	//Diffie Hellman on Alice's side
	unsigned char alice_shared_secret[crypto_generichash_BYTES];
	status = diffie_hellman(
			alice_shared_secret,
			alice_private_key,
			alice_public_key,
			bob_public_key,
			true);
	sodium_memzero(alice_private_key, crypto_box_SECRETKEYBYTES);
	if (status != 0) {
		fprintf(stderr, "ERROR: Diffie Hellman with Alice's private key failed. (%i)\n", status);
		sodium_memzero(bob_private_key, crypto_box_SECRETKEYBYTES);
		sodium_memzero(alice_shared_secret, crypto_generichash_BYTES);
		return status;
	}

	//print Alice's shared secret
	printf("Alice's shared secret ECDH(A_priv, B_pub) (%i Bytes):\n", crypto_generichash_BYTES);
	print_hex(alice_shared_secret, crypto_generichash_BYTES, 30);
	putchar('\n');

	//Diffie Hellman on Bob's side
	unsigned char bob_shared_secret[crypto_generichash_BYTES];
	status = diffie_hellman(
			bob_shared_secret,
			bob_private_key,
			bob_public_key,
			alice_public_key,
			false);
	sodium_memzero(bob_private_key, crypto_box_SECRETKEYBYTES);
	if (status != 0) {
		fprintf(stderr, "ERROR: Diffie Hellman with Bob's private key failed. (%i)\n", status);
		sodium_memzero(alice_shared_secret, crypto_generichash_BYTES);
		sodium_memzero(bob_shared_secret, crypto_generichash_BYTES);
		return status;
	}

	//print Bob's shared secret
	printf("Bob's shared secret ECDH(B_priv, A_pub) (%i Bytes):\n", crypto_generichash_BYTES);
	print_hex(bob_shared_secret, crypto_generichash_BYTES, 30);
	putchar('\n');

	//compare both shared secrets
	status = sodium_memcmp(alice_shared_secret, bob_shared_secret, crypto_generichash_BYTES);
	sodium_memzero(alice_shared_secret, crypto_generichash_BYTES);
	sodium_memzero(bob_shared_secret, crypto_generichash_BYTES);
	if (status != 0) {
		fprintf(stderr, "ERROR: Diffie Hellman didn't produce the same shared secret. (%i)\n", status);
		return status;
	}

	printf("Both shared secrets match!\n");
	return EXIT_SUCCESS;
}
int main(void) {
	sodium_init();

	int status;

	//create Alice's identity keypair
	buffer_t *alice_public_identity = buffer_create(crypto_box_PUBLICKEYBYTES, crypto_box_PUBLICKEYBYTES);
	buffer_t *alice_private_identity = buffer_create(crypto_box_SECRETKEYBYTES, crypto_box_SECRETKEYBYTES);
	status = generate_and_print_keypair(
			alice_public_identity,
			alice_private_identity,
			buffer_create_from_string("Alice"),
			buffer_create_from_string("identity"));
	if (status != 0) {
		buffer_clear(alice_private_identity);
		return status;
	}

	//create Alice's ephemeral keypair
	buffer_t *alice_public_ephemeral = buffer_create(crypto_box_PUBLICKEYBYTES, crypto_box_PUBLICKEYBYTES);
	buffer_t *alice_private_ephemeral = buffer_create(crypto_box_SECRETKEYBYTES, crypto_box_SECRETKEYBYTES);
	status = generate_and_print_keypair(
			alice_public_ephemeral,
			alice_private_ephemeral,
			buffer_create_from_string("Alice"),
			buffer_create_from_string("ephemeral"));
	if (status != 0) {
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_ephemeral);
		return status;
	}

	//create Bob's identity keypair
	buffer_t *bob_public_identity = buffer_create(crypto_box_PUBLICKEYBYTES, crypto_box_PUBLICKEYBYTES);
	buffer_t *bob_private_identity = buffer_create(crypto_box_SECRETKEYBYTES, crypto_box_SECRETKEYBYTES);
	status = generate_and_print_keypair(
			bob_public_identity,
			bob_private_identity,
			buffer_create_from_string("Bob"),
			buffer_create_from_string("identity"));
	if (status != 0) {
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_ephemeral);
		buffer_clear(bob_private_identity);
		return status;
	}

	//create Bob's ephemeral keypair
	buffer_t *bob_public_ephemeral = buffer_create(crypto_box_PUBLICKEYBYTES, crypto_box_PUBLICKEYBYTES);
	buffer_t *bob_private_ephemeral = buffer_create(crypto_box_SECRETKEYBYTES, crypto_box_SECRETKEYBYTES);
	status = generate_and_print_keypair(
			bob_public_ephemeral,
			bob_private_ephemeral,
			buffer_create_from_string("Bob"),
			buffer_create_from_string("ephemeral"));
	if (status != 0) {
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_ephemeral);
		buffer_clear(bob_private_identity);
		buffer_clear(bob_private_ephemeral);
		return status;
	}

	//derive Alice's initial root and chain key
	buffer_t *alice_root_key = buffer_create(crypto_secretbox_KEYBYTES, crypto_secretbox_KEYBYTES);
	buffer_t *alice_send_chain_key = buffer_create(crypto_secretbox_KEYBYTES, crypto_secretbox_KEYBYTES);
	buffer_t *alice_receive_chain_key = buffer_create(crypto_secretbox_KEYBYTES, crypto_secretbox_KEYBYTES);
	buffer_t *alice_send_header_key = buffer_create(crypto_aead_chacha20poly1305_KEYBYTES, crypto_aead_chacha20poly1305_KEYBYTES);
	buffer_t *alice_receive_header_key = buffer_create(crypto_aead_chacha20poly1305_KEYBYTES, crypto_aead_chacha20poly1305_KEYBYTES);
	buffer_t *alice_next_send_header_key = buffer_create(crypto_aead_chacha20poly1305_KEYBYTES, crypto_aead_chacha20poly1305_KEYBYTES);
	buffer_t *alice_next_receive_header_key = buffer_create(crypto_aead_chacha20poly1305_KEYBYTES, crypto_aead_chacha20poly1305_KEYBYTES);
	status = derive_initial_root_chain_and_header_keys(
			alice_root_key,
			alice_send_chain_key,
			alice_receive_chain_key,
			alice_send_header_key,
			alice_receive_header_key,
			alice_next_send_header_key,
			alice_next_receive_header_key,
			alice_private_identity,
			alice_public_identity,
			bob_public_identity,
			alice_private_ephemeral,
			alice_public_ephemeral,
			bob_public_ephemeral,
			true);
	buffer_clear(alice_private_identity);
	buffer_clear(alice_private_ephemeral);
	if (status != 0) {
		fprintf(stderr, "ERROR: Failed to derive Alice's initial root and chain key. (%i)\n", status);
		buffer_clear(alice_root_key);
		buffer_clear(alice_send_chain_key);
		buffer_clear(alice_receive_chain_key);
		buffer_clear(alice_send_header_key);
		buffer_clear(alice_receive_header_key);
		buffer_clear(alice_next_send_header_key);
		buffer_clear(alice_next_receive_header_key);

		buffer_clear(bob_private_identity);
		buffer_clear(bob_private_ephemeral);

		return status;
	}

	//print Alice's initial root and chain key
	printf("Alice's initial root key (%zi Bytes):\n", alice_root_key->content_length);
	print_hex(alice_root_key);
	putchar('\n');
	printf("Alice's initial send chain key (%zi Bytes):\n", alice_send_chain_key->content_length);
	print_hex(alice_send_chain_key);
	putchar('\n');
	printf("Alice's initial receive chain key (%zi Bytes):\n", alice_receive_chain_key->content_length);
	print_hex(alice_receive_chain_key);
	putchar('\n');
	printf("Alice's initial send header key (%zi Bytes):\n", alice_send_header_key->content_length);
	print_hex(alice_send_header_key);
	putchar('\n');
	printf("Alice's initial receive header key (%zi Bytes):\n", alice_receive_header_key->content_length);
	print_hex(alice_receive_header_key);
	printf("Alice's initial next send header key (%zi Bytes):\n", alice_next_send_header_key->content_length);
	print_hex(alice_next_send_header_key);
	putchar('\n');
	printf("Alice's initial next receive header key (%zi Bytes):\n", alice_next_receive_header_key->content_length);
	print_hex(alice_next_receive_header_key);
	putchar('\n');

	//derive Bob's initial root and chain key
	buffer_t *bob_root_key = buffer_create(crypto_secretbox_KEYBYTES, crypto_secretbox_KEYBYTES);
	buffer_t *bob_send_chain_key = buffer_create(crypto_secretbox_KEYBYTES, crypto_secretbox_KEYBYTES);
	buffer_t *bob_receive_chain_key = buffer_create(crypto_secretbox_KEYBYTES, crypto_secretbox_KEYBYTES);
	buffer_t *bob_send_header_key = buffer_create(crypto_aead_chacha20poly1305_KEYBYTES, crypto_aead_chacha20poly1305_KEYBYTES);
	buffer_t *bob_receive_header_key = buffer_create(crypto_aead_chacha20poly1305_KEYBYTES, crypto_aead_chacha20poly1305_KEYBYTES);
	buffer_t *bob_next_send_header_key = buffer_create(crypto_aead_chacha20poly1305_KEYBYTES, crypto_aead_chacha20poly1305_KEYBYTES);
	buffer_t *bob_next_receive_header_key = buffer_create(crypto_aead_chacha20poly1305_KEYBYTES, crypto_aead_chacha20poly1305_KEYBYTES);
	status = derive_initial_root_chain_and_header_keys(
			bob_root_key,
			bob_send_chain_key,
			bob_receive_chain_key,
			bob_send_header_key,
			bob_receive_header_key,
			bob_next_send_header_key,
			bob_next_receive_header_key,
			bob_private_identity,
			bob_public_identity,
			alice_public_identity,
			bob_private_ephemeral,
			bob_public_ephemeral,
			alice_public_ephemeral,
			false);
	buffer_clear(bob_private_identity);
	buffer_clear(bob_private_ephemeral);
	if (status != 0) {
		fprintf(stderr, "ERROR: Failed to derive Bob's initial root and chain key. (%i)", status);
		buffer_clear(alice_root_key);
		buffer_clear(alice_send_chain_key);
		buffer_clear(alice_receive_chain_key);
		buffer_clear(alice_send_header_key);
		buffer_clear(alice_receive_header_key);
		buffer_clear(alice_next_send_header_key);
		buffer_clear(alice_next_receive_header_key);
		buffer_clear(bob_root_key);
		buffer_clear(bob_send_chain_key);
		buffer_clear(bob_receive_chain_key);
		buffer_clear(bob_send_header_key);
		buffer_clear(bob_receive_header_key);
		buffer_clear(bob_next_send_header_key);
		buffer_clear(bob_next_receive_header_key);
		return status;
	}

	//print Bob's initial root and chain key
	printf("Bob's initial root key (%zi Bytes):\n", bob_root_key->content_length);
	print_hex(bob_root_key);
	putchar('\n');
	printf("Bob's initial send chain key (%zi Bytes):\n", bob_send_chain_key->content_length);
	print_hex(bob_send_chain_key);
	putchar('\n');
	printf("Bob's initial receive chain key (%zi Bytes):\n", bob_receive_chain_key->content_length);
	print_hex(bob_receive_chain_key);
	putchar('\n');
	printf("Bob's initial send header key (%zi Bytes):\n", bob_send_header_key->content_length);
	print_hex(bob_send_header_key);
	putchar('\n');
	printf("Bob's initial receive header key (%zi Bytes):\n", bob_receive_header_key->content_length);
	print_hex(bob_receive_header_key);
	printf("Bob's initial next send header key (%zi Bytes):\n", bob_next_send_header_key->content_length);
	print_hex(bob_next_send_header_key);
	putchar('\n');
	printf("Bob's initial next receive header key (%zi Bytes):\n", bob_next_receive_header_key->content_length);
	print_hex(bob_next_receive_header_key);
	putchar('\n');

	//compare Alice's and Bob's initial root key
	if (buffer_compare(alice_root_key, bob_root_key) != 0) {
		fprintf(stderr, "ERROR: Alice's and Bob's initial root keys don't match.\n");
		buffer_clear(alice_root_key);
		buffer_clear(alice_send_chain_key);
		buffer_clear(alice_receive_chain_key);
		buffer_clear(alice_send_header_key);
		buffer_clear(alice_receive_header_key);
		buffer_clear(alice_next_send_header_key);
		buffer_clear(alice_next_receive_header_key);
		buffer_clear(bob_root_key);
		buffer_clear(bob_send_chain_key);
		buffer_clear(bob_receive_chain_key);
		buffer_clear(bob_send_header_key);
		buffer_clear(bob_receive_header_key);
		buffer_clear(bob_next_send_header_key);
		buffer_clear(bob_next_receive_header_key);
		return -10;
	}
	printf("Alice's and Bob's initial root keys match.\n");

	buffer_clear(alice_root_key);
	buffer_clear(bob_root_key);

	//compare Alice's and Bob's initial chain keys
	if (buffer_compare(alice_send_chain_key, bob_receive_chain_key) != 0) {
		fprintf(stderr, "ERROR: Alice's and Bob's initial chain keys don't match.\n");
		buffer_clear(alice_send_chain_key);
		buffer_clear(alice_receive_chain_key);
		buffer_clear(alice_send_header_key);
		buffer_clear(alice_receive_header_key);
		buffer_clear(alice_next_send_header_key);
		buffer_clear(alice_next_receive_header_key);
		buffer_clear(bob_send_chain_key);
		buffer_clear(bob_receive_chain_key);
		buffer_clear(bob_send_header_key);
		buffer_clear(bob_receive_header_key);
		buffer_clear(bob_next_send_header_key);
		buffer_clear(bob_next_receive_header_key);
		return -10;
	}
	printf("Alice's and Bob's initial chain keys match.\n");

	buffer_clear(alice_send_chain_key);
	buffer_clear(bob_receive_chain_key);

	if (buffer_compare(alice_receive_chain_key, bob_send_chain_key) != 0) {
		fprintf(stderr, "ERROR: Alice's and Bob's initial chain keys don't match.\n");
		buffer_clear(alice_receive_chain_key);
		buffer_clear(alice_send_header_key);
		buffer_clear(alice_receive_header_key);
		buffer_clear(alice_next_send_header_key);
		buffer_clear(alice_next_receive_header_key);
		buffer_clear(bob_send_chain_key);
		buffer_clear(bob_send_header_key);
		buffer_clear(bob_receive_header_key);
		buffer_clear(bob_next_send_header_key);
		buffer_clear(bob_next_receive_header_key);
		return -10;
	}
	printf("Alice's and Bob's initial chain keys match.\n");

	//compare Alice's and Bob's initial header keys 1/2
	if (buffer_compare(alice_send_header_key, bob_receive_header_key) != 0) {
		fprintf(stderr, "ERROR: Alice's initial send and Bob's initial receive header keys don't match.\n");
		buffer_clear(alice_send_header_key);
		buffer_clear(alice_receive_header_key);
		buffer_clear(alice_next_send_header_key);
		buffer_clear(alice_next_receive_header_key);
		buffer_clear(bob_send_header_key);
		buffer_clear(bob_receive_header_key);
		buffer_clear(bob_next_send_header_key);
		buffer_clear(bob_next_receive_header_key);
		return -10;
	}
	printf("Alice's initial send and Bob's initial receive header keys match.\n");

	buffer_clear(alice_send_header_key);
	buffer_clear(bob_receive_header_key);

	//compare Alice's and Bob's initial header keys 2/2
	if (buffer_compare(alice_receive_header_key, bob_send_header_key) != 0) {
		fprintf(stderr, "ERROR: Alice's initial receive and Bob's initial send header keys don't match.\n");
		buffer_clear(alice_receive_header_key);
		buffer_clear(alice_next_receive_header_key);
		buffer_clear(alice_next_send_header_key);
		buffer_clear(bob_send_header_key);
		buffer_clear(bob_next_send_header_key);
		buffer_clear(bob_next_receive_header_key);
		return -10;
	}
	printf("Alice's initial receive and Bob's initial send header keys match.\n");

	buffer_clear(alice_receive_header_key);
	buffer_clear(bob_send_header_key);

	//compare Alice's and Bob's initial next header keys 1/2
	if (buffer_compare(alice_next_send_header_key, bob_next_receive_header_key) != 0) {
		fprintf(stderr, "ERROR: Alice's initial next send and Bob's initial next receive header keys don't match.\n");
		buffer_clear(alice_next_receive_header_key);
		buffer_clear(alice_next_send_header_key);
		buffer_clear(bob_next_send_header_key);
		buffer_clear(bob_next_receive_header_key);
		return -10;
	}
	printf("Alice's initial next send and Bob's initial next receive header keys match.\n");
	buffer_clear(alice_next_send_header_key);
	buffer_clear(bob_next_receive_header_key);

	//compare Alice's and Bob's initial next header keys 2/2
	if (buffer_compare(alice_next_receive_header_key, bob_next_send_header_key) != 0) {
		fprintf(stderr, "ERROR: Alice's initial next receive and Bob's initial next send header keys don't match.\n");
		buffer_clear(alice_next_receive_header_key);
		buffer_clear(bob_next_send_header_key);
		return -10;
	}
	printf("Alice's initial next receive and Bob's initial next send header keys match.\n");

	buffer_clear(alice_next_receive_header_key);
	buffer_clear(bob_next_send_header_key);

	return EXIT_SUCCESS;
}
Example #4
0
int main(void) {
	if (sodium_init() == -1) {
		return -1;
	}

	return_status status = return_status_init();

	//create buffers
	buffer_t *alice_public_key = buffer_create_on_heap(crypto_box_PUBLICKEYBYTES, crypto_box_PUBLICKEYBYTES);
	buffer_t *alice_private_key = buffer_create_on_heap(crypto_box_SECRETKEYBYTES, crypto_box_SECRETKEYBYTES);
	buffer_t *alice_shared_secret = buffer_create_on_heap(crypto_generichash_BYTES, crypto_generichash_BYTES);
	buffer_t *bob_public_key = buffer_create_on_heap(crypto_box_PUBLICKEYBYTES, crypto_box_PUBLICKEYBYTES);
	buffer_t *bob_private_key = buffer_create_on_heap(crypto_box_SECRETKEYBYTES, crypto_box_SECRETKEYBYTES);
	buffer_t *bob_shared_secret = buffer_create_on_heap(crypto_generichash_BYTES, crypto_generichash_BYTES);

	int status_int = 0;
	//create Alice's keypair
	buffer_create_from_string(alice_string, "Alice");
	buffer_create_from_string(empty_string, "");
	status = generate_and_print_keypair(
			alice_public_key,
			alice_private_key,
			alice_string,
			empty_string);
	throw_on_error(KEYGENERATION_FAILED, "Failed to generate and print Alice's keypair.");

	//create Bob's keypair
	buffer_create_from_string(bob_string, "Bob");
	status = generate_and_print_keypair(
			bob_public_key,
			bob_private_key,
			bob_string,
			empty_string);
	throw_on_error(KEYGENERATION_FAILED, "Failed to generate and print Bob's keypair.");

	//Diffie Hellman on Alice's side
	status = diffie_hellman(
			alice_shared_secret,
			alice_private_key,
			alice_public_key,
			bob_public_key,
			true);
	buffer_clear(alice_private_key);
	throw_on_error(KEYGENERATION_FAILED, "Diffie Hellman with Alice's private key failed.");

	//print Alice's shared secret
	printf("Alice's shared secret ECDH(A_priv, B_pub) (%zu Bytes):\n", alice_shared_secret->content_length);
	print_hex(alice_shared_secret);
	putchar('\n');

	//Diffie Hellman on Bob's side
	status = diffie_hellman(
			bob_shared_secret,
			bob_private_key,
			bob_public_key,
			alice_public_key,
			false);
	buffer_clear(bob_private_key);
	throw_on_error(KEYGENERATION_FAILED, "Diffie Hellman with Bob's private key failed.");

	//print Bob's shared secret
	printf("Bob's shared secret ECDH(B_priv, A_pub) (%zu Bytes):\n", bob_shared_secret->content_length);
	print_hex(bob_shared_secret);
	putchar('\n');

	//compare both shared secrets
	status_int = buffer_compare(alice_shared_secret, bob_shared_secret);
	buffer_clear(alice_shared_secret);
	buffer_clear(bob_shared_secret);
	if (status_int != 0) {
		throw(INCORRECT_DATA, "Diffie Hellman didn't produce the same shared secret.");
	}

	printf("Both shared secrets match!\n");

cleanup:
	buffer_destroy_from_heap_and_null_if_valid(alice_public_key);
	buffer_destroy_from_heap_and_null_if_valid(alice_private_key);
	buffer_destroy_from_heap_and_null_if_valid(alice_shared_secret);
	buffer_destroy_from_heap_and_null_if_valid(bob_public_key);
	buffer_destroy_from_heap_and_null_if_valid(bob_private_key);
	buffer_destroy_from_heap_and_null_if_valid(bob_shared_secret);

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

	return status.status;
}
Example #5
0
int main(void) {
	sodium_init();

	//create a user_store
	user_store *store = user_store_create();

	//check the content
	buffer_t *list = user_store_list(store);
	if (list->content_length != 0) {
		fprintf(stderr, "ERROR: List of users is not empty.\n");
		user_store_destroy(store);
		buffer_destroy_from_heap(list);

		return EXIT_FAILURE;
	}
	buffer_destroy_from_heap(list);

	int status;
	//create three users with prekeys and identity keys
	//first alice
	//alice identity key
	buffer_t *alice_private_identity = buffer_create(crypto_box_SECRETKEYBYTES, crypto_box_SECRETKEYBYTES);
	buffer_t *alice_public_identity = buffer_create(crypto_box_PUBLICKEYBYTES, crypto_box_PUBLICKEYBYTES);
	status = generate_and_print_keypair(
			alice_public_identity,
			alice_private_identity,
			buffer_create_from_string("Alice"),
			buffer_create_from_string("identity"));
	if (status != 0) {
		fprintf(stderr, "ERROR: Failed to generate Alice's identity keypair.\n");
		buffer_clear(alice_private_identity);
		return status;
	}

	//alice prekeys
	buffer_t *alice_private_prekeys = buffer_create(PREKEY_AMOUNT * crypto_box_SECRETKEYBYTES, PREKEY_AMOUNT * crypto_box_SECRETKEYBYTES);
	buffer_t *alice_public_prekeys = buffer_create(PREKEY_AMOUNT * crypto_box_PUBLICKEYBYTES, PREKEY_AMOUNT * crypto_box_PUBLICKEYBYTES);
	status = generate_prekeys(alice_private_prekeys, alice_public_prekeys);
	if (status != 0) {
		fprintf(stderr, "ERROR: Failed to generate Alice's prekeys.\n");
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_prekeys);
		return status;
	}

	//then bob
	//bob's identity key
	buffer_t *bob_private_identity = buffer_create(crypto_box_SECRETKEYBYTES, crypto_box_SECRETKEYBYTES);
	buffer_t *bob_public_identity = buffer_create(crypto_box_PUBLICKEYBYTES, crypto_box_PUBLICKEYBYTES);
	status = generate_and_print_keypair(
			bob_public_identity,
			bob_private_identity,
			buffer_create_from_string("Bob"),
			buffer_create_from_string("identity"));
	if (status != 0) {
		fprintf(stderr, "ERROR: Failed to generate Bob's identity keypair.\n");
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_prekeys);
		buffer_clear(bob_private_identity);
		return status;
	}

	//bob's prekeys
	buffer_t *bob_private_prekeys = buffer_create(PREKEY_AMOUNT * crypto_box_SECRETKEYBYTES, PREKEY_AMOUNT * crypto_box_SECRETKEYBYTES);
	buffer_t *bob_public_prekeys = buffer_create(PREKEY_AMOUNT * crypto_box_PUBLICKEYBYTES, PREKEY_AMOUNT * crypto_box_PUBLICKEYBYTES);
	status = generate_prekeys(bob_private_prekeys, bob_public_prekeys);
	if (status != 0) {
		fprintf(stderr, "ERROR: Failed to generate Bob's prekeys.\n");
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_prekeys);
		buffer_clear(bob_private_identity);
		buffer_clear(bob_private_prekeys);
		return status;
	}

	//then charlie
	//charlie's identity key
	buffer_t *charlie_private_identity = buffer_create(crypto_box_SECRETKEYBYTES, crypto_box_SECRETKEYBYTES);
	buffer_t *charlie_public_identity = buffer_create(crypto_box_PUBLICKEYBYTES, crypto_box_PUBLICKEYBYTES);
	status = generate_and_print_keypair(
			charlie_public_identity,
			charlie_private_identity,
			buffer_create_from_string("Charlie"),
			buffer_create_from_string("identity"));
	if (status != 0) {
		fprintf(stderr, "ERROR: Failed to generate Charlie's identity keypair.\n");
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_prekeys);
		buffer_clear(bob_private_identity);
		buffer_clear(bob_private_prekeys);
		buffer_clear(charlie_private_identity);
		return status;
	}

	//charlie's prekeys
	buffer_t *charlie_private_prekeys = buffer_create(PREKEY_AMOUNT * crypto_box_SECRETKEYBYTES, PREKEY_AMOUNT * crypto_box_SECRETKEYBYTES);
	buffer_t *charlie_public_prekeys = buffer_create(PREKEY_AMOUNT * crypto_box_PUBLICKEYBYTES, PREKEY_AMOUNT * crypto_box_PUBLICKEYBYTES);
	status = generate_prekeys(charlie_private_prekeys, charlie_public_prekeys);
	if (status != 0) {
		fprintf(stderr, "ERROR: Failed to generate Charlie's prekeys.\n");
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_prekeys);
		buffer_clear(bob_private_identity);
		buffer_clear(bob_private_prekeys);
		buffer_clear(charlie_private_identity);
		buffer_clear(charlie_private_prekeys);
		return status;
	}

	//add alice to the user store
	status = user_store_add(
			store,
			alice_public_identity,
			alice_private_identity,
			alice_public_prekeys,
			alice_private_prekeys);
	if (status != 0) {
		fprintf(stderr, "ERROR: Failed to add Alice to the user store.\n");
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_prekeys);
		buffer_clear(bob_private_identity);
		buffer_clear(bob_private_prekeys);
		buffer_clear(charlie_private_identity);
		buffer_clear(charlie_private_prekeys);
		user_store_destroy(store);
		return status;
	}
	printf("Successfully added Alice to the user store.\n");

	//check length of the user store
	sodium_mprotect_readonly(store);
	if (store->length != 1) {
		fprintf(stderr, "ERROR: User store has incorrect length.\n");
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_prekeys);
		buffer_clear(bob_private_identity);
		buffer_clear(bob_private_prekeys);
		buffer_clear(charlie_private_identity);
		buffer_clear(charlie_private_prekeys);
		user_store_destroy(store);
		return EXIT_FAILURE;
	}
	sodium_mprotect_noaccess(store);
	printf("Length of the user store matches.");

	//list user store
	list = user_store_list(store);
	if (buffer_compare(list, alice_public_identity) != 0) {
		fprintf(stderr, "ERROR: Failed to list users.\n");
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_prekeys);
		buffer_clear(bob_private_identity);
		buffer_clear(bob_private_prekeys);
		buffer_clear(charlie_private_identity);
		buffer_clear(charlie_private_prekeys);
		buffer_destroy_from_heap(list);
		user_store_destroy(store);
		return EXIT_FAILURE;
	}
	buffer_destroy_from_heap(list);
	printf("Successfully listed users.\n");

	//add bob to the user store
	status = user_store_add(
			store,
			bob_public_identity,
			bob_private_identity,
			bob_public_prekeys,
			bob_private_prekeys);
	if (status != 0) {
		fprintf(stderr, "ERROR: Failed to add Bob to the user store.\n");
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_prekeys);
		buffer_clear(bob_private_identity);
		buffer_clear(bob_private_prekeys);
		buffer_clear(charlie_private_identity);
		buffer_clear(charlie_private_prekeys);
		user_store_destroy(store);
		return status;
	}
	printf("Successfully added Bob to the user store.\n");

	//check length of the user store
	sodium_mprotect_readonly(store);
	if (store->length != 2) {
		fprintf(stderr, "ERROR: User store has incorrect length.\n");
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_prekeys);
		buffer_clear(bob_private_identity);
		buffer_clear(bob_private_prekeys);
		buffer_clear(charlie_private_identity);
		buffer_clear(charlie_private_prekeys);
		user_store_destroy(store);
		return EXIT_FAILURE;
	}
	sodium_mprotect_noaccess(store);
	printf("Length of the user store matches.");

	//list user store
	list = user_store_list(store);
	if ((buffer_compare_partial(list, 0, alice_public_identity, 0, crypto_box_PUBLICKEYBYTES) != 0)
			|| (buffer_compare_partial(list, crypto_box_PUBLICKEYBYTES, bob_public_identity, 0, crypto_box_PUBLICKEYBYTES) != 0)) {
		fprintf(stderr, "ERROR: Failed to list users.\n");
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_prekeys);
		buffer_clear(bob_private_identity);
		buffer_clear(bob_private_prekeys);
		buffer_clear(charlie_private_identity);
		buffer_clear(charlie_private_prekeys);
		buffer_destroy_from_heap(list);
		user_store_destroy(store);
		return EXIT_FAILURE;
	}
	buffer_destroy_from_heap(list);
	printf("Successfully listed users.\n");

	//add charlie to the user store
	status = user_store_add(
			store,
			charlie_public_identity,
			charlie_private_identity,
			charlie_public_prekeys,
			charlie_private_prekeys);
	if (status != 0) {
		fprintf(stderr, "ERROR: Failed to add Charlie to the user store.\n");
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_prekeys);
		buffer_clear(bob_private_identity);
		buffer_clear(bob_private_prekeys);
		buffer_clear(charlie_private_identity);
		buffer_clear(charlie_private_prekeys);
		user_store_destroy(store);
		return status;
	}
	printf("Successfully added Charlie to the user store.\n");

	//check length of the user store
	sodium_mprotect_readonly(store);
	if (store->length != 3) {
		fprintf(stderr, "ERROR: User store has incorrect length.\n");
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_prekeys);
		buffer_clear(bob_private_identity);
		buffer_clear(bob_private_prekeys);
		buffer_clear(charlie_private_identity);
		buffer_clear(charlie_private_prekeys);
		user_store_destroy(store);
		return EXIT_FAILURE;
	}
	sodium_mprotect_noaccess(store);
	printf("Length of the user store matches.");

	//list user store
	list = user_store_list(store);
	if ((buffer_compare_partial(list, 0, alice_public_identity, 0, crypto_box_PUBLICKEYBYTES) != 0)
			|| (buffer_compare_partial(list, crypto_box_PUBLICKEYBYTES, bob_public_identity, 0, crypto_box_PUBLICKEYBYTES) != 0)
			|| (buffer_compare_partial(list, 2 * crypto_box_PUBLICKEYBYTES, charlie_public_identity, 0, crypto_box_PUBLICKEYBYTES) != 0)) {
		fprintf(stderr, "ERROR: Failed to list users.\n");
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_prekeys);
		buffer_clear(bob_private_identity);
		buffer_clear(bob_private_prekeys);
		buffer_clear(charlie_private_identity);
		buffer_clear(charlie_private_prekeys);
		buffer_destroy_from_heap(list);
		user_store_destroy(store);
		return EXIT_FAILURE;
	}
	buffer_destroy_from_heap(list);
	printf("Successfully listed users.\n");

	//find node
	user_store_node *bob_node = user_store_find_node(store, bob_public_identity);
	if (bob_node == NULL) {
		fprintf(stderr, "ERROR: Failed to find Bob's node.\n");
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_prekeys);
		buffer_clear(bob_private_identity);
		buffer_clear(bob_private_prekeys);
		buffer_clear(charlie_private_identity);
		buffer_clear(charlie_private_prekeys);
		user_store_destroy(store);
		return EXIT_FAILURE;
	}
	printf("Node found.\n");

	sodium_mprotect_readonly(bob_node);
	if ((buffer_compare(&(bob_node->public_identity_key), bob_public_identity) != 0)
			|| (buffer_compare(&(bob_node->private_identity_key), bob_private_identity) != 0)
			|| (buffer_compare(&(bob_node->public_prekeys), bob_public_prekeys) != 0)
			|| (buffer_compare(&(bob_node->private_prekeys), bob_private_prekeys) != 0)) {
		fprintf(stderr, "ERROR: Bob's data from the user store doesn't match.\n");
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_prekeys);
		buffer_clear(bob_private_identity);
		buffer_clear(bob_private_prekeys);
		buffer_clear(charlie_private_identity);
		buffer_clear(charlie_private_prekeys);
		user_store_destroy(store);
		return EXIT_FAILURE;
	}
	sodium_mprotect_noaccess(bob_node);
	printf("Data from the node matches.\n");

	//remove a user identified by it's key
	user_store_remove_by_key(store, bob_public_identity);
	//check the length
	sodium_mprotect_readonly(store);
	if (store->length != 2) {
		fprintf(stderr, "ERROR: User store has incorrect length.\n");
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_prekeys);
		buffer_clear(bob_private_identity);
		buffer_clear(bob_private_prekeys);
		buffer_clear(charlie_private_identity);
		buffer_clear(charlie_private_prekeys);
		user_store_destroy(store);
		return EXIT_FAILURE;
	}
	sodium_mprotect_noaccess(store);
	printf("Length of the user store matches.");
	//check the user list
	list = user_store_list(store);
	if ((buffer_compare_partial(list, 0, alice_public_identity, 0, crypto_box_PUBLICKEYBYTES) != 0)
			|| (buffer_compare_partial(list, crypto_box_PUBLICKEYBYTES, charlie_public_identity, 0, crypto_box_PUBLICKEYBYTES) != 0)) {
		fprintf(stderr, "ERROR: Removing user failed.\n");
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_prekeys);
		buffer_clear(bob_private_identity);
		buffer_clear(bob_private_prekeys);
		buffer_clear(charlie_private_identity);
		buffer_clear(charlie_private_prekeys);
		user_store_destroy(store);
		buffer_destroy_from_heap(list);
		return EXIT_FAILURE;
	}
	buffer_destroy_from_heap(list);
	printf("Successfully removed user.\n");

	//readd bob
	status = user_store_add(
			store,
			bob_public_identity,
			bob_private_identity,
			bob_public_prekeys,
			bob_private_prekeys);
	if (status != 0) {
		fprintf(stderr, "ERROR: Failed to readd Bob to the user store.\n");
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_prekeys);
		buffer_clear(bob_private_identity);
		buffer_clear(bob_private_prekeys);
		buffer_clear(charlie_private_identity);
		buffer_clear(charlie_private_prekeys);
		user_store_destroy(store);
		return status;
	}
	printf("Successfully readded Bob to the user store.\n");

	//now find bob again
	bob_node = user_store_find_node(store, bob_public_identity);
	if (bob_node == NULL) {
		fprintf(stderr, "ERROR: Failed to find Bob's node.\n");
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_prekeys);
		buffer_clear(bob_private_identity);
		buffer_clear(bob_private_prekeys);
		buffer_clear(charlie_private_identity);
		buffer_clear(charlie_private_prekeys);
		user_store_destroy(store);
		return EXIT_FAILURE;
	}
	printf("Bob's node found again.\n");

	//remove bob by it's node
	user_store_remove(store, bob_node);
	//check the length
	sodium_mprotect_readonly(store);
	if (store->length != 2) {
		fprintf(stderr, "ERROR: User store has incorrect length.\n");
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_prekeys);
		buffer_clear(bob_private_identity);
		buffer_clear(bob_private_prekeys);
		buffer_clear(charlie_private_identity);
		buffer_clear(charlie_private_prekeys);
		user_store_destroy(store);
		return EXIT_FAILURE;
	}
	sodium_mprotect_noaccess(store);
	printf("Length of the user store matches.");
	//check the user list
	list = user_store_list(store);
	if ((buffer_compare_partial(list, 0, alice_public_identity, 0, crypto_box_PUBLICKEYBYTES) != 0)
			|| (buffer_compare_partial(list, crypto_box_PUBLICKEYBYTES, charlie_public_identity, 0, crypto_box_PUBLICKEYBYTES) != 0)) {
		fprintf(stderr, "ERROR: Removing user failed.\n");
		buffer_clear(alice_private_identity);
		buffer_clear(alice_private_prekeys);
		buffer_clear(bob_private_identity);
		buffer_clear(bob_private_prekeys);
		buffer_clear(charlie_private_identity);
		buffer_clear(charlie_private_prekeys);
		user_store_destroy(store);
		buffer_destroy_from_heap(list);
		return EXIT_FAILURE;
	}
	buffer_destroy_from_heap(list);
	printf("Successfully removed user.\n");

	buffer_clear(alice_private_identity);
	buffer_clear(alice_private_prekeys);
	buffer_clear(bob_private_identity);
	buffer_clear(bob_private_prekeys);
	buffer_clear(charlie_private_identity);
	buffer_clear(charlie_private_prekeys);

	//clear the user store
	user_store_clear(store);
	//check the length
	sodium_mprotect_readonly(store);
	if (store->length != 0) {
		fprintf(stderr, "ERROR: User store has incorrect length.\n");
		user_store_destroy(store);
		return EXIT_FAILURE;
	}
	printf("Successfully cleared user store.\n");

	sodium_mprotect_noaccess(store);

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

	return_status status = return_status_init();

	//create buffers
	//alice keys
	buffer_t * const alice_public_identity = buffer_create_on_heap(crypto_box_PUBLICKEYBYTES, crypto_box_PUBLICKEYBYTES);
	buffer_t * const alice_private_identity = buffer_create_on_heap(crypto_box_SECRETKEYBYTES, crypto_box_SECRETKEYBYTES);
	buffer_t * const alice_public_ephemeral = buffer_create_on_heap(crypto_box_PUBLICKEYBYTES, crypto_box_PUBLICKEYBYTES);
	buffer_t * const alice_private_ephemeral = buffer_create_on_heap(crypto_box_SECRETKEYBYTES, crypto_box_SECRETKEYBYTES);
	buffer_t * const alice_shared_secret = buffer_create_on_heap(crypto_generichash_BYTES, crypto_generichash_BYTES);
	//bobs keys
	buffer_t * const bob_public_identity = buffer_create_on_heap(crypto_box_PUBLICKEYBYTES, crypto_box_PUBLICKEYBYTES);
	buffer_t * const bob_private_identity = buffer_create_on_heap(crypto_box_SECRETKEYBYTES, crypto_box_SECRETKEYBYTES);
	buffer_t * const bob_public_ephemeral = buffer_create_on_heap(crypto_box_PUBLICKEYBYTES, crypto_box_PUBLICKEYBYTES);
	buffer_t * const bob_private_ephemeral = buffer_create_on_heap(crypto_box_SECRETKEYBYTES, crypto_box_SECRETKEYBYTES);
	buffer_t * const bob_shared_secret = buffer_create_on_heap(crypto_generichash_BYTES, crypto_generichash_BYTES);

	printf("Generate Alice's keys -------------------------------------------------------\n\n");

	int status_int = 0;
	//create Alice's identity keypair
	buffer_create_from_string(alice_string, "Alice");
	buffer_create_from_string(identity_string, "identity");
	status = generate_and_print_keypair(
			alice_public_identity,
			alice_private_identity,
			alice_string,
			identity_string);
	throw_on_error(KEYGENERATION_FAILED, "Failed to generate and print Alice' identity keypair.");

	//create Alice's ephemeral keypair
	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' ephemeral keypair.");

	printf("Generate Bob's keys ---------------------------------------------------------\n\n");

	//create Bob's identity keypair
	buffer_create_from_string(bob_string, "Bob");
	status = generate_and_print_keypair(
			bob_public_identity,
			bob_private_identity,
			bob_string,
			identity_string);
	throw_on_error(KEYGENERATION_FAILED, "Failed to generate and print Bob's identity keypair.");

	//create Bob's ephemeral keypair
	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.");

	printf("Calculate shared secret via Triple Diffie Hellman ---------------------------\n\n");

	//Triple Diffie Hellman on Alice's side
	status = triple_diffie_hellman(
			alice_shared_secret,
			alice_private_identity,
			alice_public_identity,
			alice_private_ephemeral,
			alice_public_ephemeral,
			bob_public_identity,
			bob_public_ephemeral,
			true);
	buffer_clear(alice_private_identity);
	buffer_clear(alice_private_ephemeral);
	throw_on_error(KEYGENERATION_FAILED, "Triple Diffie Hellman for Alice failed.");
	//print Alice's shared secret
	printf("Alice's shared secret H(DH(A_priv,B0_pub)||DH(A0_priv,B_pub)||DH(A0_priv,B0_pub)):\n");
	print_hex(alice_shared_secret);
	putchar('\n');

	//Triple Diffie Hellman on Bob's side
	status = triple_diffie_hellman(
			bob_shared_secret,
			bob_private_identity,
			bob_public_identity,
			bob_private_ephemeral,
			bob_public_ephemeral,
			alice_public_identity,
			alice_public_ephemeral,
			false);
	buffer_clear(bob_private_identity);
	buffer_clear(bob_private_ephemeral);
	throw_on_error(KEYGENERATION_FAILED, "Triple Diffie Hellnan for Bob failed.");
	//print Bob's shared secret
	printf("Bob's shared secret H(DH(B0_priv, A_pub)||DH(B_priv, A0_pub)||DH(B0_priv, A0_pub)):\n");
	print_hex(bob_shared_secret);
	putchar('\n');

	//compare both shared secrets
	status_int = buffer_compare(alice_shared_secret, bob_shared_secret);
	buffer_clear(alice_shared_secret);
	buffer_clear(bob_shared_secret);
	if (status_int != 0) {
		throw(INCORRECT_DATA, "Triple Diffie Hellman didn't produce the same shared secret.");
	}

	printf("Both shared secrets match!\n");

cleanup:
	//alice keys
	buffer_destroy_from_heap(alice_public_identity);
	buffer_destroy_from_heap(alice_private_identity);
	buffer_destroy_from_heap(alice_public_ephemeral);
	buffer_destroy_from_heap(alice_private_ephemeral);
	buffer_destroy_from_heap(alice_shared_secret);
	//bobs keys
	buffer_destroy_from_heap(bob_public_identity);
	buffer_destroy_from_heap(bob_private_identity);
	buffer_destroy_from_heap(bob_public_ephemeral);
	buffer_destroy_from_heap(bob_private_ephemeral);
	buffer_destroy_from_heap(bob_shared_secret);

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

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

	int status;

	//create Alice's identity keypair
	unsigned char alice_public_identity[crypto_box_PUBLICKEYBYTES];
	unsigned char alice_private_identity[crypto_box_SECRETKEYBYTES];
	status = generate_and_print_keypair(
			alice_public_identity,
			alice_private_identity,
			"Alice",
			"identity");
	if (status != 0) {
		sodium_memzero(alice_private_identity, sizeof(alice_private_identity));
		return status;
	}

	//create Alice's ephemeral keypair
	unsigned char alice_public_ephemeral[crypto_box_PUBLICKEYBYTES];
	unsigned char alice_private_ephemeral[crypto_box_SECRETKEYBYTES];
	status = generate_and_print_keypair(
			alice_public_ephemeral,
			alice_private_ephemeral,
			"Alice",
			"ephemeral");
	if (status != 0) {
		sodium_memzero(alice_private_identity, sizeof(alice_private_identity));
		sodium_memzero(alice_private_ephemeral, sizeof(alice_private_ephemeral));
		return status;
	}

	//create Bob's identity keypair
	unsigned char bob_public_identity[crypto_box_PUBLICKEYBYTES];
	unsigned char bob_private_identity[crypto_box_SECRETKEYBYTES];
	status = generate_and_print_keypair(
			bob_public_identity,
			bob_private_identity,
			"Bob",
			"identity");
	if (status != 0) {
		sodium_memzero(alice_private_identity, sizeof(alice_private_identity));
		sodium_memzero(alice_private_ephemeral, sizeof(alice_private_ephemeral));
		sodium_memzero(bob_private_identity, sizeof(bob_private_identity));
		return status;
	}

	//create Bob's ephemeral keypair
	unsigned char bob_public_ephemeral[crypto_box_PUBLICKEYBYTES];
	unsigned char bob_private_ephemeral[crypto_box_SECRETKEYBYTES];
	status = generate_and_print_keypair(
			bob_public_ephemeral,
			bob_private_ephemeral,
			"Bob",
			"ephemeral");
	if (status != 0) {
		sodium_memzero(alice_private_identity, sizeof(alice_private_identity));
		sodium_memzero(alice_private_ephemeral, sizeof(alice_private_ephemeral));
		sodium_memzero(bob_private_identity, sizeof(bob_private_identity));
		sodium_memzero(bob_private_ephemeral, sizeof(bob_private_ephemeral));
		return status;
	}

	//derive Alice's initial root and chain key
	unsigned char alice_root_key[crypto_secretbox_KEYBYTES];
	unsigned char alice_send_chain_key[crypto_secretbox_KEYBYTES];
	unsigned char alice_receive_chain_key[crypto_secretbox_KEYBYTES];
	unsigned char alice_send_header_key[crypto_aead_chacha20poly1305_KEYBYTES];
	unsigned char alice_receive_header_key[crypto_aead_chacha20poly1305_KEYBYTES];
	unsigned char alice_next_send_header_key[crypto_aead_chacha20poly1305_KEYBYTES];
	unsigned char alice_next_receive_header_key[crypto_aead_chacha20poly1305_KEYBYTES];
	status = derive_initial_root_chain_and_header_keys(
			alice_root_key,
			alice_send_chain_key,
			alice_receive_chain_key,
			alice_send_header_key,
			alice_receive_header_key,
			alice_next_send_header_key,
			alice_next_receive_header_key,
			alice_private_identity,
			alice_public_identity,
			bob_public_identity,
			alice_private_ephemeral,
			alice_public_ephemeral,
			bob_public_ephemeral,
			true);
	sodium_memzero(alice_private_identity, sizeof(alice_private_identity));
	sodium_memzero(alice_private_ephemeral, sizeof(alice_private_ephemeral));
	if (status != 0) {
		fprintf(stderr, "ERROR: Failed to derive Alice's initial root and chain key. (%i)\n", status);
		sodium_memzero(alice_root_key, sizeof(alice_root_key));
		sodium_memzero(alice_send_chain_key, sizeof(alice_send_chain_key));
		sodium_memzero(alice_receive_chain_key, sizeof(alice_receive_chain_key));
		sodium_memzero(alice_send_header_key, sizeof(alice_send_header_key));
		sodium_memzero(alice_receive_header_key, sizeof(alice_receive_header_key));
		sodium_memzero(alice_next_send_header_key, sizeof(alice_next_send_header_key));
		sodium_memzero(alice_next_receive_header_key, sizeof(alice_next_receive_header_key));

		sodium_memzero(bob_private_identity, sizeof(bob_private_identity));
		sodium_memzero(bob_private_ephemeral, sizeof(bob_private_ephemeral));

		return status;
	}

	//print Alice's initial root and chain key
	printf("Alice's initial root key (%zi Bytes):\n", sizeof(alice_root_key));
	print_hex(alice_root_key, sizeof(alice_root_key), 30);
	putchar('\n');
	printf("Alice's initial send chain key (%zi Bytes):\n", sizeof(alice_send_chain_key));
	print_hex(alice_send_chain_key, sizeof(alice_send_chain_key), 30);
	putchar('\n');
	printf("Alice's initial receive chain key (%zi Bytes):\n", sizeof(alice_receive_chain_key));
	print_hex(alice_receive_chain_key, sizeof(alice_receive_chain_key), 30);
	putchar('\n');
	printf("Alice's initial send header key (%zi Bytes):\n", sizeof(alice_send_header_key));
	print_hex(alice_send_header_key, sizeof(alice_send_header_key), 30);
	putchar('\n');
	printf("Alice's initial receive header key (%zi Bytes):\n", sizeof(alice_receive_header_key));
	print_hex(alice_receive_header_key, sizeof(alice_receive_header_key), 30);
	printf("Alice's initial next send header key (%zi Bytes):\n", sizeof(alice_next_send_header_key));
	print_hex(alice_next_send_header_key, sizeof(alice_next_send_header_key), 30);
	putchar('\n');
	printf("Alice's initial next receive header key (%zi Bytes):\n", sizeof(alice_next_receive_header_key));
	print_hex(alice_next_receive_header_key, sizeof(alice_next_receive_header_key), 30);
	putchar('\n');

	//derive Bob's initial root and chain key
	unsigned char bob_root_key[crypto_secretbox_KEYBYTES];
	unsigned char bob_send_chain_key[crypto_secretbox_KEYBYTES];
	unsigned char bob_receive_chain_key[crypto_secretbox_KEYBYTES];
	unsigned char bob_send_header_key[crypto_aead_chacha20poly1305_KEYBYTES];
	unsigned char bob_receive_header_key[crypto_aead_chacha20poly1305_KEYBYTES];
	unsigned char bob_next_send_header_key[crypto_aead_chacha20poly1305_KEYBYTES];
	unsigned char bob_next_receive_header_key[crypto_aead_chacha20poly1305_KEYBYTES];
	status = derive_initial_root_chain_and_header_keys(
			bob_root_key,
			bob_send_chain_key,
			bob_receive_chain_key,
			bob_send_header_key,
			bob_receive_header_key,
			bob_next_send_header_key,
			bob_next_receive_header_key,
			bob_private_identity,
			bob_public_identity,
			alice_public_identity,
			bob_private_ephemeral,
			bob_public_ephemeral,
			alice_public_ephemeral,
			false);
	sodium_memzero(bob_private_identity, sizeof(bob_private_identity));
	sodium_memzero(bob_private_ephemeral, sizeof(bob_private_ephemeral));
	if (status != 0) {
		fprintf(stderr, "ERROR: Failed to derive Bob's initial root and chain key. (%i)", status);
		sodium_memzero(alice_root_key, sizeof(alice_root_key));
		sodium_memzero(alice_send_chain_key, sizeof(alice_send_chain_key));
		sodium_memzero(alice_receive_chain_key, sizeof(alice_receive_chain_key));
		sodium_memzero(alice_send_header_key, sizeof(alice_send_header_key));
		sodium_memzero(alice_receive_header_key, sizeof(alice_receive_header_key));
		sodium_memzero(alice_next_send_header_key, sizeof(alice_next_send_header_key));
		sodium_memzero(alice_next_receive_header_key, sizeof(alice_next_receive_header_key));
		sodium_memzero(bob_root_key, sizeof(bob_root_key));
		sodium_memzero(bob_send_chain_key, sizeof(bob_send_chain_key));
		sodium_memzero(bob_receive_chain_key, sizeof(bob_receive_chain_key));
		sodium_memzero(bob_send_header_key, sizeof(bob_send_header_key));
		sodium_memzero(bob_receive_header_key, sizeof(bob_receive_header_key));
		sodium_memzero(bob_next_send_header_key, sizeof(bob_next_send_header_key));
		sodium_memzero(bob_next_receive_header_key, sizeof(bob_next_receive_header_key));
		return status;
	}

	//print Bob's initial root and chain key
	printf("Bob's initial root key (%zi Bytes):\n", sizeof(bob_root_key));
	print_hex(bob_root_key, sizeof(bob_root_key), 30);
	putchar('\n');
	printf("Bob's initial send chain key (%zi Bytes):\n", sizeof(bob_send_chain_key));
	print_hex(bob_send_chain_key, sizeof(bob_send_chain_key), 30);
	putchar('\n');
	printf("Bob's initial receive chain key (%zi Bytes):\n", sizeof(bob_receive_chain_key));
	print_hex(bob_receive_chain_key, sizeof(bob_receive_chain_key), 30);
	putchar('\n');
	printf("Bob's initial send header key (%zi Bytes):\n", sizeof(bob_send_header_key));
	print_hex(bob_send_header_key, sizeof(bob_send_header_key), 30);
	putchar('\n');
	printf("Bob's initial receive header key (%zi Bytes):\n", sizeof(bob_receive_header_key));
	print_hex(bob_receive_header_key, sizeof(bob_receive_header_key), 30);
	printf("Bob's initial next send header key (%zi Bytes):\n", sizeof(bob_next_send_header_key));
	print_hex(bob_next_send_header_key, sizeof(bob_next_send_header_key), 30);
	putchar('\n');
	printf("Bob's initial next receive header key (%zi Bytes):\n", sizeof(bob_next_receive_header_key));
	print_hex(bob_next_receive_header_key, sizeof(bob_next_receive_header_key), 30);
	putchar('\n');

	//compare Alice's and Bob's initial root key
	if (sodium_memcmp(alice_root_key, bob_root_key, sizeof(alice_root_key)) != 0) {
		fprintf(stderr, "ERROR: Alice's and Bob's initial root keys don't match.\n");
		sodium_memzero(alice_root_key, sizeof(alice_root_key));
		sodium_memzero(alice_send_chain_key, sizeof(alice_send_chain_key));
		sodium_memzero(alice_receive_chain_key, sizeof(alice_receive_chain_key));
		sodium_memzero(alice_send_header_key, sizeof(alice_send_header_key));
		sodium_memzero(alice_receive_header_key, sizeof(alice_receive_header_key));
		sodium_memzero(alice_next_send_header_key, sizeof(alice_next_send_header_key));
		sodium_memzero(alice_next_receive_header_key, sizeof(alice_next_receive_header_key));
		sodium_memzero(bob_root_key, sizeof(bob_root_key));
		sodium_memzero(bob_send_chain_key, sizeof(bob_send_chain_key));
		sodium_memzero(bob_receive_chain_key, sizeof(bob_receive_chain_key));
		sodium_memzero(bob_send_header_key, sizeof(bob_send_header_key));
		sodium_memzero(bob_receive_header_key, sizeof(bob_receive_header_key));
		sodium_memzero(bob_next_send_header_key, sizeof(bob_next_send_header_key));
		sodium_memzero(bob_next_receive_header_key, sizeof(bob_next_receive_header_key));
		return -10;
	}
	printf("Alice's and Bob's initial root keys match.\n");

	sodium_memzero(alice_root_key, sizeof(alice_root_key));
	sodium_memzero(bob_root_key, sizeof(bob_root_key));

	//compare Alice's and Bob's initial chain keys
	if (sodium_memcmp(alice_send_chain_key, bob_receive_chain_key, sizeof(alice_send_chain_key)) != 0) {
		fprintf(stderr, "ERROR: Alice's and Bob's initial chain keys don't match.\n");
		sodium_memzero(alice_send_chain_key, sizeof(alice_send_chain_key));
		sodium_memzero(alice_receive_chain_key, sizeof(alice_receive_chain_key));
		sodium_memzero(alice_send_header_key, sizeof(alice_send_header_key));
		sodium_memzero(alice_receive_header_key, sizeof(alice_receive_header_key));
		sodium_memzero(alice_next_send_header_key, sizeof(alice_next_send_header_key));
		sodium_memzero(alice_next_receive_header_key, sizeof(alice_next_receive_header_key));
		sodium_memzero(bob_send_chain_key, sizeof(bob_send_chain_key));
		sodium_memzero(bob_receive_chain_key, sizeof(bob_receive_chain_key));
		sodium_memzero(bob_send_header_key, sizeof(bob_send_header_key));
		sodium_memzero(bob_receive_header_key, sizeof(bob_receive_header_key));
		sodium_memzero(bob_next_send_header_key, sizeof(bob_next_send_header_key));
		sodium_memzero(bob_next_receive_header_key, sizeof(bob_next_receive_header_key));
		return -10;
	}
	printf("Alice's and Bob's initial chain keys match.\n");

	sodium_memzero(alice_send_chain_key, sizeof(alice_send_chain_key));
	sodium_memzero(bob_receive_chain_key, sizeof(bob_receive_chain_key));

	if (sodium_memcmp(alice_receive_chain_key, bob_send_chain_key, sizeof(alice_receive_chain_key)) != 0) {
		fprintf(stderr, "ERROR: Alice's and Bob's initial chain keys don't match.\n");
		sodium_memzero(alice_receive_chain_key, sizeof(alice_receive_chain_key));
		sodium_memzero(alice_send_header_key, sizeof(alice_send_header_key));
		sodium_memzero(alice_receive_header_key, sizeof(alice_receive_header_key));
		sodium_memzero(alice_next_send_header_key, sizeof(alice_next_send_header_key));
		sodium_memzero(alice_next_receive_header_key, sizeof(alice_next_receive_header_key));
		sodium_memzero(bob_send_chain_key, sizeof(bob_send_chain_key));
		sodium_memzero(bob_send_header_key, sizeof(bob_send_header_key));
		sodium_memzero(bob_receive_header_key, sizeof(bob_receive_header_key));
		sodium_memzero(bob_next_send_header_key, sizeof(bob_next_send_header_key));
		sodium_memzero(bob_next_receive_header_key, sizeof(bob_next_receive_header_key));
		return -10;
	}
	printf("Alice's and Bob's initial chain keys match.\n");

	//compare Alice's and Bob's initial header keys 1/2
	if (sodium_memcmp(alice_send_header_key, bob_receive_header_key, sizeof(alice_send_header_key)) != 0) {
		fprintf(stderr, "ERROR: Alice's initial send and Bob's initial receive header keys don't match.\n");
		sodium_memzero(alice_send_header_key, sizeof(alice_send_header_key));
		sodium_memzero(alice_receive_header_key, sizeof(alice_receive_header_key));
		sodium_memzero(alice_next_send_header_key, sizeof(alice_next_send_header_key));
		sodium_memzero(alice_next_receive_header_key, sizeof(alice_next_receive_header_key));
		sodium_memzero(bob_send_header_key, sizeof(bob_send_header_key));
		sodium_memzero(bob_receive_header_key, sizeof(bob_receive_header_key));
		sodium_memzero(bob_next_send_header_key, sizeof(bob_next_send_header_key));
		sodium_memzero(bob_next_receive_header_key, sizeof(bob_next_receive_header_key));
		return -10;
	}
	printf("Alice's initial send and Bob's initial receive header keys match.\n");

	sodium_memzero(alice_send_header_key, sizeof(alice_send_header_key));
	sodium_memzero(bob_receive_header_key, sizeof(bob_receive_header_key));

	//compare Alice's and Bob's initial header keys 2/2
	if (sodium_memcmp(alice_receive_header_key, bob_send_header_key, sizeof(alice_receive_header_key)) != 0) {
		fprintf(stderr, "ERROR: Alice's initial receive and Bob's initial send header keys don't match.\n");
		sodium_memzero(alice_receive_header_key, sizeof(alice_receive_header_key));
		sodium_memzero(alice_next_receive_header_key, sizeof(alice_next_receive_header_key));
		sodium_memzero(alice_next_send_header_key, sizeof(alice_next_send_header_key));
		sodium_memzero(bob_send_header_key, sizeof(bob_send_header_key));
		sodium_memzero(bob_next_send_header_key, sizeof(bob_next_send_header_key));
		sodium_memzero(bob_next_receive_header_key, sizeof(bob_next_receive_header_key));
		return -10;
	}
	printf("Alice's initial receive and Bob's initial send header keys match.\n");

	sodium_memzero(alice_receive_header_key, sizeof(alice_receive_header_key));
	sodium_memzero(bob_send_header_key, sizeof(bob_send_header_key));

	//compare Alice's and Bob's initial next header keys 1/2
	if (sodium_memcmp(alice_next_send_header_key, bob_next_receive_header_key, sizeof(alice_next_send_header_key)) != 0) {
		fprintf(stderr, "ERROR: Alice's initial next send and Bob's initial next receive header keys don't match.\n");
		sodium_memzero(alice_next_receive_header_key, sizeof(alice_next_receive_header_key));
		sodium_memzero(alice_next_send_header_key, sizeof(alice_next_send_header_key));
		sodium_memzero(bob_next_send_header_key, sizeof(bob_next_send_header_key));
		sodium_memzero(bob_next_receive_header_key, sizeof(bob_next_receive_header_key));
		return -10;
	}
	printf("Alice's initial next send and Bob's initial next receive header keys match.\n");
	sodium_memzero(alice_next_send_header_key, sizeof(alice_next_send_header_key));
	sodium_memzero(bob_next_receive_header_key, sizeof(bob_next_receive_header_key));

	//compare Alice's and Bob's initial next header keys 2/2
	if (sodium_memcmp(alice_next_receive_header_key, bob_next_send_header_key, sizeof(alice_next_receive_header_key)) != 0) {
		fprintf(stderr, "ERROR: Alice's initial next receive and Bob's initial next send header keys don't match.\n");
		sodium_memzero(alice_next_receive_header_key, sizeof(alice_next_receive_header_key));
		sodium_memzero(bob_next_send_header_key, sizeof(bob_next_send_header_key));
		return -10;
	}
	printf("Alice's initial next receive and Bob's initial next send header keys match.\n");

	sodium_memzero(alice_next_receive_header_key, sizeof(alice_next_receive_header_key));
	sodium_memzero(bob_next_send_header_key, sizeof(bob_next_send_header_key));

	return EXIT_SUCCESS;
}
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;
}