/*
 * Copyright (c) 2016-2020 Belledonne Communications SARL.
 *
 * This file is part of bctoolbox.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <cmath>
#include "bctoolbox_tester.h"
#include "bctoolbox/crypto.h"
#include "bctoolbox/crypto.hh"
#include "bctoolbox/exception.hh"
#ifdef HAVE_MBEDTLS
/* used to cross test ECDH25519 */
#include "mbedtls/ecdh.h"
#endif /* HAVE_MBEDTLS */
#include <array>

using namespace bctoolbox;

static void DHM(void) {

	int i;
	bctbx_DHMContext_t *alice,*bob;
	bctbx_rng_context_t *RNG;
	uint8_t availableAlgos[2]={BCTBX_DHM_2048, BCTBX_DHM_3072};
	uint8_t availableAlgosNb=2;

	/* Init the RNG */
	RNG = bctbx_rng_context_new();

	/* Get all the available DH algos */
	//availableAlgosNb=bctbx_getDHMAvailableAlgos(availableAlgos);

	for (i=0; i<availableAlgosNb; i++) {
		 uint8_t secretSize=0;
		 uint32_t keySize=0;

		 switch (availableAlgos[i]) {
			 case BCTBX_DHM_2048:
				secretSize = 32;
				keySize = 256;
				break;

			 case BCTBX_DHM_3072:
				secretSize = 32;
				keySize = 384;
				break;

		 }

		/* Create Alice and Bob contexts */
		alice = bctbx_CreateDHMContext(availableAlgos[i], secretSize);
		bob = bctbx_CreateDHMContext(availableAlgos[i], secretSize);

		/* Generate keys pairs */
		bctbx_DHMCreatePublic(alice, (int (*)(void *, uint8_t *, size_t))bctbx_rng_get, RNG);
		bctbx_DHMCreatePublic(bob, (int (*)(void *, uint8_t *, size_t))bctbx_rng_get, RNG);

		/* exchange public keys */
		alice->peer = (uint8_t *)malloc(keySize*sizeof(uint8_t));
		memcpy(alice->peer,bob->self,keySize);
		bob->peer = (uint8_t *)malloc(keySize*sizeof(uint8_t));
		memcpy(bob->peer,alice->self,keySize);

		/* compute shared secrets */
		bctbx_DHMComputeSecret(alice, (int (*)(void *, uint8_t *, size_t))bctbx_rng_get, RNG);
		bctbx_DHMComputeSecret(bob, (int (*)(void *, uint8_t *, size_t))bctbx_rng_get, RNG);

		/* compare the secrets */
		BC_ASSERT_TRUE(memcmp(alice->key,bob->key,keySize)==0);

		/* clear contexts */
		bctbx_DestroyDHMContext(alice);
		bctbx_DestroyDHMContext(bob);
	}

	/* clear contexts */
	bctbx_rng_context_free(RNG);
}

static void ECDH_exchange(bctbx_ECDHContext_t *alice, bctbx_ECDHContext_t *bob) {
	/* exchange public keys */
	bctbx_ECDHSetPeerPublicKey(alice, bob->selfPublic, alice->pointCoordinateLength);
	bctbx_ECDHSetPeerPublicKey(bob, alice->selfPublic, bob->pointCoordinateLength);

	/* compute shared secrets */
	bctbx_ECDHComputeSecret(alice, NULL, NULL);
	bctbx_ECDHComputeSecret(bob, NULL, NULL);

	/* compare the secrets */
	BC_ASSERT_TRUE(memcmp(alice->sharedSecret, bob->sharedSecret, alice->pointCoordinateLength)==0);
}

static void ECDH(void) {
	if (!bctbx_crypto_have_ecc()) {
		bctbx_warning("test skipped as we don't have Elliptic Curve Cryptography in bctoolbox");
		return;
	}

	/* Patterns */
	uint8_t ECDHpattern_X25519_alicePrivate[] = {0x77, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 0x7d, 0x3c, 0x16, 0xc1, 0x72, 0x51, 0xb2, 0x66, 0x45, 0xdf, 0x4c, 0x2f, 0x87, 0xeb, 0xc0, 0x99, 0x2a, 0xb1, 0x77, 0xfb, 0xa5, 0x1d, 0xb9, 0x2c, 0x2a};
	uint8_t ECDHpattern_X25519_alicePublic[] = {0x85, 0x20, 0xf0, 0x09, 0x89, 0x30, 0xa7, 0x54, 0x74, 0x8b, 0x7d, 0xdc, 0xb4, 0x3e, 0xf7, 0x5a, 0x0d, 0xbf, 0x3a, 0x0d, 0x26, 0x38, 0x1a, 0xf4, 0xeb, 0xa4, 0xa9, 0x8e, 0xaa, 0x9b, 0x4e, 0x6a};
	uint8_t ECDHpattern_X25519_bobPrivate[] = {0x5d, 0xab, 0x08, 0x7e, 0x62, 0x4a, 0x8a, 0x4b, 0x79, 0xe1, 0x7f, 0x8b, 0x83, 0x80, 0x0e, 0xe6, 0x6f, 0x3b, 0xb1, 0x29, 0x26, 0x18, 0xb6, 0xfd, 0x1c, 0x2f, 0x8b, 0x27, 0xff, 0x88, 0xe0, 0xeb};
	uint8_t ECDHpattern_X25519_bobPublic[] = {0xde, 0x9e, 0xdb, 0x7d, 0x7b, 0x7d, 0xc1, 0xb4, 0xd3, 0x5b, 0x61, 0xc2, 0xec, 0xe4, 0x35, 0x37, 0x3f, 0x83, 0x43, 0xc8, 0x5b, 0x78, 0x67, 0x4d, 0xad, 0xfc, 0x7e, 0x14, 0x6f, 0x88, 0x2b, 0x4f};
	uint8_t ECDHpattern_X25519_sharedSecret[] = {0x4a, 0x5d, 0x9d, 0x5b, 0xa4, 0xce, 0x2d, 0xe1, 0x72, 0x8e, 0x3b, 0xf4, 0x80, 0x35, 0x0f, 0x25, 0xe0, 0x7e, 0x21, 0xc9, 0x47, 0xd1, 0x9e, 0x33, 0x76, 0xf0, 0x9b, 0x3c, 0x1e, 0x16, 0x17, 0x42};
	uint8_t alicePublic_libSignalPattern[] = { 0x1b, 0xb7, 0x59, 0x66, 0xf2, 0xe9, 0x3a, 0x36, 0x91, 0xdf, 0xff, 0x94, 0x2b, 0xb2, 0xa4, 0x66, 0xa1, 0xc0, 0x8b, 0x8d, 0x78, 0xca, 0x3f, 0x4d, 0x6d, 0xf8, 0xb8, 0xbf, 0xa2, 0xe4, 0xee, 0x28};
	uint8_t alicePrivate_libSignalPattern[] = { 0xc8, 0x06, 0x43, 0x9d, 0xc9, 0xd2, 0xc4, 0x76, 0xff, 0xed, 0x8f, 0x25, 0x80, 0xc0, 0x88, 0x8d, 0x58, 0xab, 0x40, 0x6b, 0xf7, 0xae, 0x36, 0x98, 0x87, 0x90, 0x21, 0xb9, 0x6b, 0xb4, 0xbf, 0x59};
	uint8_t bobPublic_libSignalPattern[] = { 0x65, 0x36, 0x14, 0x99, 0x3d, 0x2b, 0x15, 0xee, 0x9e, 0x5f, 0xd3, 0xd8, 0x6c, 0xe7, 0x19, 0xef, 0x4e, 0xc1, 0xda, 0xae, 0x18, 0x86, 0xa8, 0x7b, 0x3f, 0x5f, 0xa9, 0x56, 0x5a, 0x27, 0xa2, 0x2f};
	uint8_t bobPrivate_libSignalPattern[] = { 0xb0, 0x3b, 0x34, 0xc3, 0x3a, 0x1c, 0x44, 0xf2, 0x25, 0xb6, 0x62, 0xd2, 0xbf, 0x48, 0x59, 0xb8, 0x13, 0x54, 0x11, 0xfa, 0x7b, 0x03, 0x86, 0xd4, 0x5f, 0xb7, 0x5d, 0xc5, 0xb9, 0x1b, 0x44, 0x66};
	uint8_t shared_libSignalPattern[] = { 0x32, 0x5f, 0x23, 0x93, 0x28, 0x94, 0x1c, 0xed, 0x6e, 0x67, 0x3b, 0x86, 0xba, 0x41, 0x01, 0x74, 0x48, 0xe9, 0x9b, 0x64, 0x9a, 0x9c, 0x38, 0x06, 0xc1, 0xdd, 0x7c, 0xa4, 0xc4, 0x77, 0xe6, 0x29};
	uint8_t ECDHpattern_X448_alicePrivate[] = {0x9a, 0x8f, 0x49, 0x25, 0xd1, 0x51, 0x9f, 0x57, 0x75, 0xcf, 0x46, 0xb0, 0x4b, 0x58, 0x00, 0xd4, 0xee, 0x9e, 0xe8, 0xba, 0xe8, 0xbc, 0x55, 0x65, 0xd4, 0x98, 0xc2, 0x8d, 0xd9, 0xc9, 0xba, 0xf5, 0x74, 0xa9, 0x41, 0x97, 0x44, 0x89, 0x73, 0x91, 0x00, 0x63, 0x82, 0xa6, 0xf1, 0x27, 0xab, 0x1d, 0x9a, 0xc2, 0xd8, 0xc0, 0xa5, 0x98, 0x72, 0x6b};
	uint8_t ECDHpattern_X448_alicePublic[] = {0x9b, 0x08, 0xf7, 0xcc, 0x31, 0xb7, 0xe3, 0xe6, 0x7d, 0x22, 0xd5, 0xae, 0xa1, 0x21, 0x07, 0x4a, 0x27, 0x3b, 0xd2, 0xb8, 0x3d, 0xe0, 0x9c, 0x63, 0xfa, 0xa7, 0x3d, 0x2c, 0x22, 0xc5, 0xd9, 0xbb, 0xc8, 0x36, 0x64, 0x72, 0x41, 0xd9, 0x53, 0xd4, 0x0c, 0x5b, 0x12, 0xda, 0x88, 0x12, 0x0d, 0x53, 0x17, 0x7f, 0x80, 0xe5, 0x32, 0xc4, 0x1f, 0xa0};
	uint8_t ECDHpattern_X448_bobPrivate[] = {0x1c, 0x30, 0x6a, 0x7a, 0xc2, 0xa0, 0xe2, 0xe0, 0x99, 0x0b, 0x29, 0x44, 0x70, 0xcb, 0xa3, 0x39, 0xe6, 0x45, 0x37, 0x72, 0xb0, 0x75, 0x81, 0x1d, 0x8f, 0xad, 0x0d, 0x1d, 0x69, 0x27, 0xc1, 0x20, 0xbb, 0x5e, 0xe8, 0x97, 0x2b, 0x0d, 0x3e, 0x21, 0x37, 0x4c, 0x9c, 0x92, 0x1b, 0x09, 0xd1, 0xb0, 0x36, 0x6f, 0x10, 0xb6, 0x51, 0x73, 0x99, 0x2d};
	uint8_t ECDHpattern_X448_bobPublic[] = {0x3e, 0xb7, 0xa8, 0x29, 0xb0, 0xcd, 0x20, 0xf5, 0xbc, 0xfc, 0x0b, 0x59, 0x9b, 0x6f, 0xec, 0xcf, 0x6d, 0xa4, 0x62, 0x71, 0x07, 0xbd, 0xb0, 0xd4, 0xf3, 0x45, 0xb4, 0x30, 0x27, 0xd8, 0xb9, 0x72, 0xfc, 0x3e, 0x34, 0xfb, 0x42, 0x32, 0xa1, 0x3c, 0xa7, 0x06, 0xdc, 0xb5, 0x7a, 0xec, 0x3d, 0xae, 0x07, 0xbd, 0xc1, 0xc6, 0x7b, 0xf3, 0x36, 0x09};
	uint8_t ECDHpattern_X448_sharedSecret[] = {0x07, 0xff, 0xf4, 0x18, 0x1a, 0xc6, 0xcc, 0x95, 0xec, 0x1c, 0x16, 0xa9, 0x4a, 0x0f, 0x74, 0xd1, 0x2d, 0xa2, 0x32, 0xce, 0x40, 0xa7, 0x75, 0x52, 0x28, 0x1d, 0x28, 0x2b, 0xb6, 0x0c, 0x0b, 0x56, 0xfd, 0x24, 0x64, 0xc3, 0x35, 0x54, 0x39, 0x36, 0x52, 0x1c, 0x24, 0x40, 0x30, 0x85, 0xd5, 0x9a, 0x44, 0x9a, 0x50, 0x37, 0x51, 0x4a, 0x87, 0x9d};

	int i;
	bctbx_ECDHContext_t *alice,*bob;
	bctbx_rng_context_t *RNG;
	uint8_t availableAlgos[2]={BCTBX_ECDH_X25519, BCTBX_ECDH_X448};
	uint8_t availableAlgosNb=2;

	/********************************************************************/
	/*** Do a random generation and exchange with all available algos ***/
	/********************************************************************/
	/* Init the RNG */
	RNG = bctbx_rng_context_new();

	for (i=0; i<availableAlgosNb; i++) {

		/* Create Alice and Bob contexts */
		alice = bctbx_CreateECDHContext(availableAlgos[i]);
		bob = bctbx_CreateECDHContext(availableAlgos[i]);

		/* Generate keys pairs */
		bctbx_ECDHCreateKeyPair(alice, (int (*)(void *, uint8_t *, size_t))bctbx_rng_get, RNG);
		bctbx_ECDHCreateKeyPair(bob, (int (*)(void *, uint8_t *, size_t))bctbx_rng_get, RNG);

		/* do the exchange, it will check secrets are equals */
		ECDH_exchange(alice, bob);

		/* clear contexts */
		bctbx_DestroyECDHContext(alice);
		bctbx_DestroyECDHContext(bob);
	}

	/* clear contexts */
	bctbx_rng_context_free(RNG);

	/********************************************************************/
	/*** Run an exchange using patterns from RFC7748                    */
	/********************************************************************/
	/*** Run it on the X25519 patterns ***/
	/* set contexts */
	alice = bctbx_CreateECDHContext(BCTBX_ECDH_X25519);
	bob = bctbx_CreateECDHContext(BCTBX_ECDH_X25519);

	/* Set private and public value */
	bctbx_ECDHSetSecretKey(alice, ECDHpattern_X25519_alicePrivate, 32);
	bctbx_ECDHSetSelfPublicKey(alice, ECDHpattern_X25519_alicePublic, 32);
	bctbx_ECDHSetSecretKey(bob, ECDHpattern_X25519_bobPrivate, 32);
	bctbx_ECDHSetSelfPublicKey(bob, ECDHpattern_X25519_bobPublic, 32);

	/* Perform the key exchange and compute shared secret, it will check shared secrets are matching */
	ECDH_exchange(alice, bob);

	/* check shared secret according to RFC7748 patterns */
	BC_ASSERT_TRUE(memcmp(alice->sharedSecret, ECDHpattern_X25519_sharedSecret, 32)==0);

	/* clear contexts */
	bctbx_DestroyECDHContext(alice);
	bctbx_DestroyECDHContext(bob);

	/********************************************************************/
	/*** Run an key derivation and exchange using patterns from RFC7748 */
	/********************************************************************/
	/*** Run it on the X25519 patterns ***/
	/* set contexts */
	alice = bctbx_CreateECDHContext(BCTBX_ECDH_X25519);
	bob = bctbx_CreateECDHContext(BCTBX_ECDH_X25519);

	/* Set private and derive the public value */
	bctbx_ECDHSetSecretKey(alice, ECDHpattern_X25519_alicePrivate, 32);
	bctbx_ECDHDerivePublicKey(alice);
	bctbx_ECDHSetSecretKey(bob, ECDHpattern_X25519_bobPrivate, 32);
	bctbx_ECDHDerivePublicKey(bob);

	/* check the public value according to RFC7748 patterns */
	BC_ASSERT_TRUE(memcmp(alice->selfPublic, ECDHpattern_X25519_alicePublic, 32)==0);
	BC_ASSERT_TRUE(memcmp(bob->selfPublic, ECDHpattern_X25519_bobPublic, 32)==0);

	/* Perform the key exchange and compute shared secret, it will check shared secrets are matching */
	ECDH_exchange(alice, bob);

	/* check shared secret according to RFC7748 patterns */
	BC_ASSERT_TRUE(memcmp(alice->sharedSecret, ECDHpattern_X25519_sharedSecret, 32)==0);

	/* clear contexts */
	bctbx_DestroyECDHContext(alice);
	bctbx_DestroyECDHContext(bob);
	/**********************************************************************/
	/*** Run an key derivation and exchange using patterns from libsignal */
	/**********************************************************************/
	/* Do another one using pattern retrieved from libsignal tests */
	/* set contexts */
	alice = bctbx_CreateECDHContext(BCTBX_ECDH_X25519);
	bob = bctbx_CreateECDHContext(BCTBX_ECDH_X25519);

	/* Set private and derive the public value */
	bctbx_ECDHSetSecretKey(alice, alicePrivate_libSignalPattern, 32);
	bctbx_ECDHDerivePublicKey(alice);
	bctbx_ECDHSetSecretKey(bob, bobPrivate_libSignalPattern, 32);
	bctbx_ECDHDerivePublicKey(bob);

	/* check the public value according to libsignal patterns */
	BC_ASSERT_TRUE(memcmp(alice->selfPublic, alicePublic_libSignalPattern, 32)==0);
	BC_ASSERT_TRUE(memcmp(bob->selfPublic, bobPublic_libSignalPattern, 32)==0);

	/* Perform the key exchange and compute shared secret, it will check shared secrets are matching */
	ECDH_exchange(alice, bob);

	/* check shared secret according to libsignal patterns */
	BC_ASSERT_TRUE(memcmp(alice->sharedSecret, shared_libSignalPattern, 32)==0);

	/* clear contexts */
	bctbx_DestroyECDHContext(alice);
	bctbx_DestroyECDHContext(bob);

	/********************************************************************/
	/*** Run an exchange using patterns from RFC7748                    */
	/********************************************************************/
	/*** Run it on the X448 patterns ***/
	/* set contexts */
	alice = bctbx_CreateECDHContext(BCTBX_ECDH_X448);
	bob = bctbx_CreateECDHContext(BCTBX_ECDH_X448);

	/* Set private and derive the public value */
	bctbx_ECDHSetSecretKey(alice, ECDHpattern_X448_alicePrivate, 56);
	bctbx_ECDHSetSelfPublicKey(alice, ECDHpattern_X448_alicePublic, 56);
	bctbx_ECDHSetSecretKey(bob, ECDHpattern_X448_bobPrivate, 56);
	bctbx_ECDHSetSelfPublicKey(bob, ECDHpattern_X448_bobPublic, 56);

	/* Perform the key exchange and compute shared secret, it will check shared secrets are matching */
	ECDH_exchange(alice, bob);

	/* check shared secret according to RFC7748 patterns */
	BC_ASSERT_TRUE(memcmp(alice->sharedSecret, ECDHpattern_X448_sharedSecret, 56)==0);

	/* clear contexts */
	bctbx_DestroyECDHContext(alice);
	bctbx_DestroyECDHContext(bob);

	/********************************************************************/
	/*** Run an key derivation and exchange using patterns from RFC7748 */
	/********************************************************************/
	/*** Run it on the X448 patterns ***/
	/* set contexts */
	alice = bctbx_CreateECDHContext(BCTBX_ECDH_X448);
	bob = bctbx_CreateECDHContext(BCTBX_ECDH_X448);

	/* Set private and derive the public value */
	bctbx_ECDHSetSecretKey(alice, ECDHpattern_X448_alicePrivate, 56);
	bctbx_ECDHDerivePublicKey(alice);
	bctbx_ECDHSetSecretKey(bob, ECDHpattern_X448_bobPrivate, 56);
	bctbx_ECDHDerivePublicKey(bob);

	/* check the public value according to RFC7748 patterns */
	BC_ASSERT_TRUE(memcmp(alice->selfPublic, ECDHpattern_X448_alicePublic, 56)==0);
	BC_ASSERT_TRUE(memcmp(bob->selfPublic, ECDHpattern_X448_bobPublic, 56)==0);

	/* Perform the key exchange and compute shared secret, it will check shared secrets are matching */
	ECDH_exchange(alice, bob);

	/* check shared secret according to RFC7748 patterns */
	BC_ASSERT_TRUE(memcmp(alice->sharedSecret, ECDHpattern_X448_sharedSecret, 56)==0);

	/* clear contexts */
	bctbx_DestroyECDHContext(alice);
	bctbx_DestroyECDHContext(bob);
}

/* just check compatibility of ECDH exchange between mbedtls implementation and decaf one */
/* mbedtls works with all buffer in big endian, while rfc 7748 specify little endian encoding, so all buffer in or out of mbedtls_mpi_read/write must be reversed */
static void ECDH25519compat(void) {

#ifdef HAVE_MBEDTLS
	int i;
	bctbx_ECDHContext_t *alice=NULL;
	bctbx_rng_context_t *RNG;
	mbedtls_ecdh_context *bob=NULL;
	uint8_t tmpBuffer[32];
	uint8_t bobPublic[32];
	uint8_t bobShared[32];

	if (!bctbx_crypto_have_ecc()) {
		bctbx_warning("test skipped as we don't have Elliptic Curve Cryptography in bctoolbox");
		return;
	}


	/* Init the RNG */
	RNG = bctbx_rng_context_new();

	/* Create Alice and Bob contexts */
	alice = bctbx_CreateECDHContext(BCTBX_ECDH_X25519);

	bob=(mbedtls_ecdh_context *)bctbx_malloc(sizeof(mbedtls_ecdh_context));
	mbedtls_ecdh_init(bob);
	mbedtls_ecp_group_load(&(bob->grp), MBEDTLS_ECP_DP_CURVE25519 );

	/* Generate keys pairs */
	bctbx_ECDHCreateKeyPair(alice, (int (*)(void *, uint8_t *, size_t))bctbx_rng_get, RNG);
	mbedtls_ecdh_gen_public(&(bob->grp), &(bob->d), &(bob->Q), (int (*)(void *, unsigned char *, size_t))bctbx_rng_get, RNG);
	mbedtls_mpi_write_binary(&(bob->Q.X), tmpBuffer, 32 );
	/* tmpBuffer is in big endian, but we need it in little endian, reverse it */
	for (i=0; i<32; i++) {
		bobPublic[i]=tmpBuffer[31-i];
	}

	/* exchange public keys */
	alice->peerPublic = (uint8_t *)malloc(alice->pointCoordinateLength*sizeof(uint8_t));
	memcpy(alice->peerPublic, bobPublic, alice->pointCoordinateLength);

	/* compute shared secrets */
	bctbx_ECDHComputeSecret(alice, NULL, NULL);

	mbedtls_mpi_lset(&(bob->Qp.Z), 1);
	/* alice->selfPublic is in little endian, but mbedtls expect it in big, reverse it */
	for (i=0; i<32; i++) {
		tmpBuffer[i]=alice->selfPublic[31-i];
	}

	mbedtls_mpi_read_binary(&(bob->Qp.X), tmpBuffer, 32);
	/* generate shared secret */
	mbedtls_ecdh_compute_shared(&(bob->grp), &(bob->z), &(bob->Qp), &(bob->d),  (int (*)(void *, unsigned char *, size_t))bctbx_rng_get, RNG);
	/* copy it in the output buffer */
	mbedtls_mpi_write_binary(&(bob->z), tmpBuffer, 32);
	/* reverse it as we need it in little endian */
	for (i=0; i<32; i++) {
		bobShared[i]=tmpBuffer[31-i];
	}

	/* compare the secrets */
	BC_ASSERT_TRUE(memcmp(alice->sharedSecret, bobShared, alice->pointCoordinateLength)==0);

	/* clear contexts */
	bctbx_DestroyECDHContext(alice);
	mbedtls_ecdh_free(bob);
	free(bob);

	/* clear contexts */
	bctbx_rng_context_free(RNG);
#else /* HAVE_MBEDTLS */
	bctbx_warning("test skipped as we don't have mbedtls in bctoolbox");
#endif /* HAVE_MBEDTLS */
}

static char const *importantMessage1 = "The most obvious mechanical phenomenon in electrical and magnetical experiments is the mutual action by which bodies in certain states set each other in motion while still at a sensible distance from each other. The first step, therefore, in reducing these phenomena into scientific form, is to ascertain the magnitude and direction of the force acting between the bodies, and when it is found that this force depends in a certain way upon the relative position of the bodies and on their electric or magnetic condition, it seems at first sight natural to explain the facts by assuming the existence of something either at rest or in motion in each body, constituting its electric or magnetic state, and capable of acting at a distance according to mathematical laws.In this way mathematical theories of statical electricity, of magnetism, of the mechanical action between conductors carrying currents, and of the induction of currents have been formed. In these theories the force acting between the two bodies is treated with reference only to the condition of the bodies and their relative position, and without any express consideration of the surrounding medium. These theories assume, more or less explicitly, the existence of substances the particles of which have the property of acting on one another at a distance by attraction or repulsion. The most complete development of a theory of this kind is that of M.W. Weber[1], who has made the same theory include electrostatic and electromagnetic phenomena. In doing so, however, he has found it necessary to assume that the force between two particles depends on their relative velocity, as well as on their distance. This theory, as developed by MM. W. Weber and C. Neumann[2], is exceedingly ingenious, and wonderfully comprehensive in its application to the phenomena of statical electricity, electromagnetic attractions, induction of current and diamagnetic phenomena; and it comes to us with the more authority, as it has served to guide the speculations of one who has made so great an advance in the practical part of electric science, both by introducing a consistent system of units in electrical measurement, and by actually determining electrical quantities with an accuracy hitherto unknown.";

static char const *importantMessage2 = " The mechanical difficulties, however, which are involved in the assumption of particles acting at a distance with forces which depend on their velocities are such as to prevent me from considering this theory as an ultimate one though it may have been, and may yet be useful in leading to the coordination of phenomena. I have therefore preferred to seek an explanation of the fact in another direction, by supposing them to be produced by actions which go on in the surrounding medium as well as in the excited bodies, and endeavouring to explain the action between distant bodies without assuming the existence of forces capable of acting directly at sensible distances.";

static void EdDSA(void) {
	int i;
	bctbx_EDDSAContext_t *james,*world;
	bctbx_rng_context_t *RNG;
	uint8_t availableAlgos[2]={BCTBX_EDDSA_25519, BCTBX_EDDSA_448};
	uint8_t availableAlgosNb=2;
	uint8_t signature[128]; /* buffer to store the signature, must be at least twice the size of the longer point coordinate (57*2) */
	size_t signatureLength = 128;
	uint8_t context[250];

	if (!bctbx_crypto_have_ecc()) {
		bctbx_warning("test skipped as we don't have Elliptic Curve Cryptography in bctoolbox");
		return;
	}


	/* Init the RNG */
	RNG = bctbx_rng_context_new();


	for (i=0; i<availableAlgosNb; i++) {
		signatureLength = 128; /* reset buffer length */
		/* create contexts */
		james = bctbx_CreateEDDSAContext(availableAlgos[i]);
		world = bctbx_CreateEDDSAContext(availableAlgos[i]);

		/* generate a random context */
		bctbx_rng_get(RNG, context, 250);

		/* generate a private and public key for james */
		bctbx_EDDSACreateKeyPair(james,  (int (*)(void *, unsigned char *, size_t))bctbx_rng_get, RNG);

		/* james sign the important message */
		bctbx_EDDSA_sign(james, (uint8_t *)importantMessage1, strlen(importantMessage1), context, 250, signature, &signatureLength);
		BC_ASSERT_NOT_EQUAL(signatureLength, 0, int, "%d");

		/* world get james public key */
		bctbx_EDDSA_setPublicKey(world, james->publicKey, james->pointCoordinateLength);

		/* world verifies that the important message was signed by james */
		BC_ASSERT_EQUAL(bctbx_EDDSA_verify(world, (uint8_t *)importantMessage1, strlen(importantMessage1), context, 250, signature, signatureLength), BCTBX_VERIFY_SUCCESS, int, "%d");

		/* twist the signature to get it wrong and verify again, it shall fail */
		signature[0] ^=0xFF;
		BC_ASSERT_EQUAL(bctbx_EDDSA_verify(world, (uint8_t *)importantMessage1, strlen(importantMessage1), context, 250, signature, signatureLength), BCTBX_VERIFY_FAILED, int, "%d");

		/* twist the context to get it wrong and verify again, it shall fail */
		signature[0] ^=0xFF;
		context[0] ^=0xFF;
		BC_ASSERT_EQUAL(bctbx_EDDSA_verify(world, (uint8_t *)importantMessage1, strlen(importantMessage1), context, 250, signature, signatureLength), BCTBX_VERIFY_FAILED, int, "%d");

		/* cleaning */
		bctbx_DestroyEDDSAContext(james);
		bctbx_DestroyEDDSAContext(world);
	}

	bctbx_rng_context_free(RNG);
}

static void ed25519_to_x25519_keyconversion(void) {
	uint8_t pattern_ed25519_publicKey[] = {0xA4, 0xBF, 0x35, 0x3D, 0x6C, 0x9D, 0x51, 0xCA,  0x6D, 0x98, 0x88, 0xA6, 0x26, 0x8C, 0xF2, 0xE8, 0xA5, 0xAD, 0x58, 0x97, 0x00, 0x5B, 0x58, 0xCC,  0x46, 0x82, 0xEB, 0x88, 0x21, 0x9A, 0xC0, 0x18};
	uint8_t pattern_ed25519_secretKey[] = {0x9E, 0xEE, 0x80, 0x89, 0xA1, 0x47, 0x6E, 0x4B,  0x01, 0x70, 0xE4, 0x74, 0x06, 0xE1, 0xCE, 0xF8, 0x62, 0x53, 0xE1, 0xC2, 0x3C, 0xDD, 0x63, 0x53,  0x8D, 0x2B, 0xF0, 0x3B, 0x52, 0xD9, 0x6C, 0x39};
	uint8_t pattern_x25519_publicKey[] = {0x53, 0x97, 0x95, 0x45, 0x1A, 0x04, 0xB5, 0xDD,  0x42, 0xD2, 0x73, 0x32, 0x9C, 0x1A, 0xC9, 0xFE, 0x58, 0x3A, 0x82, 0xF1, 0x82, 0xE8, 0xD7, 0xA5,  0xAD, 0xCB, 0xD0, 0x27, 0x6E, 0x03, 0xD7, 0x70};
	bctbx_ECDHContext_t *aliceECDH =  bctbx_CreateECDHContext(BCTBX_ECDH_X25519);
	bctbx_EDDSAContext_t *aliceEDDSA =  bctbx_CreateEDDSAContext(BCTBX_EDDSA_25519);

	if (!bctbx_crypto_have_ecc()) {
		bctbx_warning("test skipped as we don't have Elliptic Curve Cryptography in bctoolbox");
		return;
	}


	/* Start from ed25519 secret key and derive the public one */
	bctbx_EDDSA_setSecretKey(aliceEDDSA, pattern_ed25519_secretKey, 32);
	bctbx_EDDSADerivePublicKey(aliceEDDSA);
	BC_ASSERT_TRUE(memcmp(aliceEDDSA->publicKey, pattern_ed25519_publicKey, 32)==0);

	/* Convert ed25519 private to x25519 and check it derives to the correct public key
	(do not check direct value of private as some bits are masked at use but not necessarily during conversion) */
	bctbx_EDDSA_ECDH_privateKeyConversion(aliceEDDSA, aliceECDH);
	bctbx_ECDHDerivePublicKey(aliceECDH);
	BC_ASSERT_TRUE(memcmp(aliceECDH->selfPublic, pattern_x25519_publicKey, 32)==0);

	/* Convert directly ed25519 public to x25519 and check we stick to pattern */
	bctbx_EDDSA_ECDH_publicKeyConversion(aliceEDDSA, aliceECDH, BCTBX_ECDH_ISPEER);/* store it in peerPublic just for this test purpose */
	BC_ASSERT_TRUE(memcmp(aliceECDH->peerPublic, pattern_x25519_publicKey, 32)==0);

	/* cleaning */
	bctbx_DestroyEDDSAContext(aliceEDDSA);
	bctbx_DestroyECDHContext(aliceECDH);
}

static void sign_and_key_exchange(void) {
	int i;
	bctbx_rng_context_t *RNG;
	bctbx_ECDHContext_t *aliceECDH = NULL;
	bctbx_EDDSAContext_t *aliceEDDSA =  NULL;
	bctbx_ECDHContext_t *bobECDH =  NULL;
	bctbx_EDDSAContext_t *bobEDDSA =  NULL;
	uint8_t availableAlgosEDDSA[2]={BCTBX_EDDSA_25519, BCTBX_EDDSA_448};
	uint8_t availableAlgosECDH[2]={BCTBX_ECDH_X25519, BCTBX_ECDH_X448};
	uint8_t availableAlgosNb=2;
	uint8_t signature1[128]; /* buffer to store the signature, must be at least twice the size of the longer point coordinate (57*2) */
	size_t signatureLength1 = 128;
	uint8_t signature2[128]; /* buffer to store the signature, must be at least twice the size of the longer point coordinate (57*2) */
	size_t signatureLength2 = 128;
	uint8_t tmpKeyBuffer[64]; /* hold the EDDSA public key while swapping them between bob and alice */
	uint8_t context1[250];
	uint8_t context2[250];

	if (!bctbx_crypto_have_ecc()) {
		bctbx_warning("test skipped as we don't have Elliptic Curve Cryptography in bctoolbox");
		return;
	}

	/* Init the RNG */
	RNG = bctbx_rng_context_new();

	for (i=0; i<availableAlgosNb; i++) {
		/* generate EdDSA keys */
		aliceEDDSA = bctbx_CreateEDDSAContext(availableAlgosEDDSA[i]);
		bobEDDSA = bctbx_CreateEDDSAContext(availableAlgosEDDSA[i]);
		bctbx_EDDSACreateKeyPair(aliceEDDSA,  (int (*)(void *, unsigned char *, size_t))bctbx_rng_get, RNG);
		bctbx_EDDSACreateKeyPair(bobEDDSA,  (int (*)(void *, unsigned char *, size_t))bctbx_rng_get, RNG);

		/* Convert self keys to Montgomery form */
		aliceECDH = bctbx_CreateECDHContext(availableAlgosECDH[i]);
		bobECDH = bctbx_CreateECDHContext(availableAlgosECDH[i]);
		bctbx_EDDSA_ECDH_privateKeyConversion(aliceEDDSA, aliceECDH);
		bctbx_EDDSA_ECDH_publicKeyConversion(aliceEDDSA, aliceECDH, BCTBX_ECDH_ISSELF);
		bctbx_EDDSA_ECDH_privateKeyConversion(bobEDDSA, bobECDH);
		bctbx_EDDSA_ECDH_publicKeyConversion(bobEDDSA, bobECDH, BCTBX_ECDH_ISSELF);

		/* generate a random context */
		bctbx_rng_get(RNG, context1, 250);
		bctbx_rng_get(RNG, context2, 250);

		/* sign a message */
		bctbx_EDDSA_sign(aliceEDDSA, (uint8_t *)importantMessage1, strlen(importantMessage1), context1, 250, signature1, &signatureLength1);
		BC_ASSERT_NOT_EQUAL(signatureLength1, 0, int, "%d");
		bctbx_EDDSA_sign(bobEDDSA, (uint8_t *)importantMessage2, strlen(importantMessage2), context2, 250, signature2, &signatureLength2);
		BC_ASSERT_NOT_EQUAL(signatureLength2, 0, int, "%d");

		/* exchange EDDSA keys: Warning: reuse the original EDDSA context, it means we will loose our self EDDSA public key */
		memcpy(tmpKeyBuffer, bobEDDSA->publicKey, bobEDDSA->pointCoordinateLength);
		bctbx_EDDSA_setPublicKey(bobEDDSA, aliceEDDSA->publicKey, aliceEDDSA->pointCoordinateLength);
		bctbx_EDDSA_setPublicKey(aliceEDDSA, tmpKeyBuffer, bobEDDSA->pointCoordinateLength);
		/* convert peer public key to ECDH format, peer public keys are now in the EDDSA context  */
		bctbx_EDDSA_ECDH_publicKeyConversion(aliceEDDSA, aliceECDH, BCTBX_ECDH_ISPEER);
		bctbx_EDDSA_ECDH_publicKeyConversion(bobEDDSA, bobECDH, BCTBX_ECDH_ISPEER);

		/* Verify signed messages */
		BC_ASSERT_EQUAL(bctbx_EDDSA_verify(bobEDDSA, (uint8_t *)importantMessage1, strlen(importantMessage1), context1, 250, signature1, signatureLength1), BCTBX_VERIFY_SUCCESS, int, "%d");
		BC_ASSERT_EQUAL(bctbx_EDDSA_verify(aliceEDDSA, (uint8_t *)importantMessage2, strlen(importantMessage2), context2, 250, signature2, signatureLength2), BCTBX_VERIFY_SUCCESS, int, "%d");

		/* Compute shared secret and compare them */
		bctbx_ECDHComputeSecret(aliceECDH, NULL, NULL);
		bctbx_ECDHComputeSecret(bobECDH, NULL, NULL);

		/* compare the secrets */
		BC_ASSERT_TRUE(memcmp(aliceECDH->sharedSecret, bobECDH->sharedSecret, aliceECDH->pointCoordinateLength)==0);

		/* reset signatureLength for next run */
		signatureLength1=signatureLength2=128;

		/* cleaning */
		bctbx_DestroyEDDSAContext(aliceEDDSA);
		bctbx_DestroyECDHContext(aliceECDH);
		bctbx_DestroyEDDSAContext(bobEDDSA);
		bctbx_DestroyECDHContext(bobECDH);
	}

	bctbx_rng_context_free(RNG);
}

static void hash_test(void) {
	/* SHA patterns */
	char const *sha_input = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu";

	uint8_t sha256_pattern[] = {0xcf, 0x5b, 0x16, 0xa7, 0x78, 0xaf, 0x83, 0x80, 0x03, 0x6c, 0xe5, 0x9e, 0x7b, 0x04, 0x92, 0x37, 0x0b, 0x24, 0x9b, 0x11, 0xe8, 0xf0, 0x7a, 0x51, 0xaf, 0xac, 0x45, 0x03, 0x7a, 0xfe, 0xe9, 0xd1};
	uint8_t sha384_pattern[] = {0x09, 0x33, 0x0c, 0x33, 0xf7, 0x11, 0x47, 0xe8, 0x3d, 0x19, 0x2f, 0xc7, 0x82, 0xcd, 0x1b, 0x47, 0x53, 0x11, 0x1b, 0x17, 0x3b, 0x3b, 0x05, 0xd2, 0x2f, 0xa0, 0x80, 0x86, 0xe3, 0xb0, 0xf7, 0x12, 0xfc, 0xc7, 0xc7, 0x1a, 0x55, 0x7e, 0x2d, 0xb9, 0x66, 0xc3, 0xe9, 0xfa, 0x91, 0x74, 0x60, 0x39};
	uint8_t sha512_pattern[] = {0x8e, 0x95, 0x9b, 0x75, 0xda, 0xe3, 0x13, 0xda, 0x8c, 0xf4, 0xf7, 0x28, 0x14, 0xfc, 0x14, 0x3f, 0x8f, 0x77, 0x79, 0xc6, 0xeb, 0x9f, 0x7f, 0xa1, 0x72, 0x99, 0xae, 0xad, 0xb6, 0x88, 0x90, 0x18, 0x50, 0x1d, 0x28, 0x9e, 0x49, 0x00, 0xf7, 0xe4, 0x33, 0x1b, 0x99, 0xde, 0xc4, 0xb5, 0x43, 0x3a, 0xc7, 0xd3, 0x29, 0xee, 0xb6, 0xdd, 0x26, 0x54, 0x5e, 0x96, 0xe5, 0x5b, 0x87, 0x4b, 0xe9, 0x09};

	/* HMAC SHA patterns from RFC 4231 test case 7 */
	std::vector<uint8_t> hmac_sha_key{0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa};
	std::vector<uint8_t> hmac_sha_data{0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x68, 0x61, 0x73, 0x68, 0x65, 0x64, 0x20, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x20, 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x48, 0x4d, 0x41, 0x43, 0x20, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x2e};
	std::vector<uint8_t> hmac_sha256_pattern{0x9b, 0x09, 0xff, 0xa7, 0x1b, 0x94, 0x2f, 0xcb, 0x27, 0x63, 0x5f, 0xbc, 0xd5, 0xb0, 0xe9, 0x44, 0xbf, 0xdc, 0x63, 0x64, 0x4f, 0x07, 0x13, 0x93, 0x8a, 0x7f, 0x51, 0x53, 0x5c, 0x3a, 0x35, 0xe2};
	std::vector<uint8_t> hmac_sha384_pattern{0x66, 0x17, 0x17, 0x8e, 0x94, 0x1f, 0x02, 0x0d, 0x35, 0x1e, 0x2f, 0x25, 0x4e, 0x8f, 0xd3, 0x2c, 0x60, 0x24, 0x20, 0xfe, 0xb0, 0xb8, 0xfb, 0x9a, 0xdc, 0xce, 0xbb, 0x82, 0x46, 0x1e, 0x99, 0xc5, 0xa6, 0x78, 0xcc, 0x31, 0xe7, 0x99, 0x17, 0x6d, 0x38, 0x60, 0xe6, 0x11, 0x0c, 0x46, 0x52, 0x3e};
	std::vector<uint8_t> hmac_sha512_pattern{0xe3, 0x7b, 0x6a, 0x77, 0x5d, 0xc8, 0x7d, 0xba, 0xa4, 0xdf, 0xa9, 0xf9, 0x6e, 0x5e, 0x3f, 0xfd, 0xde, 0xbd, 0x71, 0xf8, 0x86, 0x72, 0x89, 0x86, 0x5d, 0xf5, 0xa3, 0x2d, 0x20, 0xcd, 0xc9, 0x44, 0xb6, 0x02, 0x2c, 0xac, 0x3c, 0x49, 0x82, 0xb1, 0x0d, 0x5e, 0xeb, 0x55, 0xc3, 0xe4, 0xde, 0x15, 0x13, 0x46, 0x76, 0xfb, 0x6d, 0xe0, 0x44, 0x60, 0x65, 0xc9, 0x74, 0x40, 0xfa, 0x8c, 0x6a, 0x58};

	/* HMAC SHA 1 pattern from RFC 2202 */
	std::vector<uint8_t> hmac_sha1_key{0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b};
	std::string data = "Hi There";
	std::vector<uint8_t> hmac_sha1_data(data.begin(), data.end());
	std::vector<uint8_t> hmac_sha1_pattern{0xb6, 0x17, 0x31, 0x86, 0x55, 0x05, 0x72, 0x64, 0xe2, 0x8b, 0xc0, 0xb6, 0xfb, 0x37, 0x8c, 0x8e, 0xf1, 0x46, 0xbe, 0x00};

	uint8_t outputBuffer[64];

	bctbx_sha256((uint8_t *)sha_input, strlen(sha_input), 32, outputBuffer);
	BC_ASSERT_TRUE(memcmp(outputBuffer, sha256_pattern, 32)==0);

	bctbx_sha384((uint8_t *)sha_input, strlen(sha_input), 48, outputBuffer);
	BC_ASSERT_TRUE(memcmp(outputBuffer, sha384_pattern, 48)==0);

	bctbx_sha512((uint8_t *)sha_input, strlen(sha_input), 64, outputBuffer);
	BC_ASSERT_TRUE(memcmp(outputBuffer, sha512_pattern, 64)==0);

	/* C api */
	bctbx_hmacSha1(hmac_sha1_key.data(), hmac_sha1_key.size(), hmac_sha1_data.data(), hmac_sha1_data.size(), 20, outputBuffer);
	BC_ASSERT_TRUE(memcmp(outputBuffer, hmac_sha1_pattern.data(), 20)==0);

	bctbx_hmacSha256(hmac_sha_key.data(), hmac_sha_key.size(), hmac_sha_data.data(), hmac_sha_data.size(), 32, outputBuffer);
	BC_ASSERT_TRUE(memcmp(outputBuffer, hmac_sha256_pattern.data(), 32)==0);

	bctbx_hmacSha384(hmac_sha_key.data(), hmac_sha_key.size(), hmac_sha_data.data(), hmac_sha_data.size(), 48, outputBuffer);
	BC_ASSERT_TRUE(memcmp(outputBuffer, hmac_sha384_pattern.data(), 48)==0);

	bctbx_hmacSha512(hmac_sha_key.data(), hmac_sha_key.size(), hmac_sha_data.data(), hmac_sha_data.size(), 64, outputBuffer);
	BC_ASSERT_TRUE(memcmp(outputBuffer, hmac_sha512_pattern.data(), 64)==0);

#ifdef HAVE_MBEDTLS
	/* C++ api */
	BC_ASSERT_TRUE(std::equal(hmac_sha1_pattern.cbegin(), hmac_sha1_pattern.cend(), bctoolbox::HMAC<SHA1>(hmac_sha1_key, hmac_sha1_data).cbegin()));
	BC_ASSERT_TRUE(std::equal(hmac_sha256_pattern.cbegin(), hmac_sha256_pattern.cend(), bctoolbox::HMAC<SHA256>(hmac_sha_key, hmac_sha_data).cbegin()));
	BC_ASSERT_TRUE(std::equal(hmac_sha384_pattern.cbegin(), hmac_sha384_pattern.cend(), bctoolbox::HMAC<SHA384>(hmac_sha_key, hmac_sha_data).cbegin()));
	BC_ASSERT_TRUE(std::equal(hmac_sha384_pattern.cbegin(), hmac_sha384_pattern.cend(), bctoolbox::HMAC<SHA384>(hmac_sha_key, hmac_sha_data).cbegin()));


	/* HKDF test patterns from RFC5869 for SHA256 and generated for SHA512 using https://github.com/casebeer/python-hkdf */
	/* test A.1 */
	std::vector<uint8_t> IKM{0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b};
	std::vector<uint8_t> salt{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c};
	std::vector<uint8_t> info{0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9};
	std::vector<uint8_t> OKM{0x83, 0x23, 0x90, 0x08, 0x6c, 0xda, 0x71, 0xfb, 0x47, 0x62, 0x5b, 0xb5, 0xce, 0xb1, 0x68, 0xe4, 0xc8, 0xe2, 0x6a, 0x1a, 0x16, 0xed, 0x34, 0xd9, 0xfc, 0x7f, 0xe9, 0x2c, 0x14, 0x81, 0x57, 0x93, 0x38, 0xda, 0x36, 0x2c, 0xb8, 0xd9, 0xf9, 0x25, 0xd7, 0xcb};
	BC_ASSERT_TRUE(OKM == bctoolbox::HKDF<SHA512>(salt, IKM, info, OKM.size()));
	OKM.assign({0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a, 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf, 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, 0x58, 0x65});
	BC_ASSERT_TRUE(OKM == bctoolbox::HKDF<SHA256>(salt, IKM, info, OKM.size()));

	/* test A.2 */
	IKM.assign({0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f});
	salt.assign({0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf});
	info.assign({0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff});
	OKM.assign({0xce, 0x6c, 0x97, 0x19, 0x28, 0x05, 0xb3, 0x46, 0xe6, 0x16, 0x1e, 0x82, 0x1e, 0xd1, 0x65, 0x67, 0x3b, 0x84, 0xf4, 0x00, 0xa2, 0xb5, 0x14, 0xb2, 0xfe, 0x23, 0xd8, 0x4c, 0xd1, 0x89, 0xdd, 0xf1, 0xb6, 0x95, 0xb4, 0x8c, 0xbd, 0x1c, 0x83, 0x88, 0x44, 0x11, 0x37, 0xb3, 0xce, 0x28, 0xf1, 0x6a, 0xa6, 0x4b, 0xa3, 0x3b, 0xa4, 0x66, 0xb2, 0x4d, 0xf6, 0xcf, 0xcb, 0x02, 0x1e, 0xcf, 0xf2, 0x35, 0xf6, 0xa2, 0x05, 0x6c, 0xe3, 0xaf, 0x1d, 0xe4, 0x4d, 0x57, 0x20, 0x97, 0xa8, 0x50, 0x5d, 0x9e, 0x7a, 0x93});
	BC_ASSERT_TRUE(OKM == bctoolbox::HKDF<SHA512>(salt, IKM, info, OKM.size()));
	OKM.assign({0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1, 0xc8, 0xe7, 0xf7, 0x8c, 0x59, 0x6a, 0x49, 0x34, 0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e, 0xfa, 0xd8, 0xa0, 0x50, 0xcc, 0x4c, 0x19, 0xaf, 0xa9, 0x7c, 0x59, 0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72, 0x71, 0xcb, 0x41, 0xc6, 0x5e, 0x59, 0x0e, 0x09, 0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8, 0x36, 0x77, 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71, 0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec, 0x3e, 0x87, 0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f});
	BC_ASSERT_TRUE(OKM == bctoolbox::HKDF<SHA256>(salt, IKM, info, OKM.size()));

	/* test A.3 */
	IKM.assign({0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b});
	salt.clear();
	info.clear();
	OKM.assign({0xf5, 0xfa, 0x02, 0xb1, 0x82, 0x98, 0xa7, 0x2a, 0x8c, 0x23, 0x89, 0x8a, 0x87, 0x03, 0x47, 0x2c, 0x6e, 0xb1, 0x79, 0xdc, 0x20, 0x4c, 0x03, 0x42, 0x5c, 0x97, 0x0e, 0x3b, 0x16, 0x4b, 0xf9, 0x0f, 0xff, 0x22, 0xd0, 0x48, 0x36, 0xd0, 0xe2, 0x34, 0x3b, 0xac});
	BC_ASSERT_TRUE(OKM == bctoolbox::HKDF<SHA512>(salt, IKM, info, OKM.size()));
	OKM.assign({0x8d, 0xa4, 0xe7, 0x75, 0xa5, 0x63, 0xc1, 0x8f, 0x71, 0x5f, 0x80, 0x2a, 0x06, 0x3c, 0x5a, 0x31, 0xb8, 0xa1, 0x1f, 0x5c, 0x5e, 0xe1, 0x87, 0x9e, 0xc3, 0x45, 0x4e, 0x5f, 0x3c, 0x73, 0x8d, 0x2d, 0x9d, 0x20, 0x13, 0x95, 0xfa, 0xa4, 0xb6, 0x1a, 0x96, 0xc8});
	BC_ASSERT_TRUE(OKM == bctoolbox::HKDF<SHA256>(salt, IKM, info, OKM.size()));

	/* test A.4 */
	IKM.assign({0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b});
	salt.assign({0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c});
	info.assign({0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9});
	OKM.assign({0x74, 0x13, 0xe8, 0x99, 0x7e, 0x02, 0x06, 0x10, 0xfb, 0xf6, 0x82, 0x3f, 0x2c, 0xe1, 0x4b, 0xff, 0x01, 0x87, 0x5d, 0xb1, 0xca, 0x55, 0xf6, 0x8c, 0xfc, 0xf3, 0x95, 0x4d, 0xc8, 0xaf, 0xf5, 0x35, 0x59, 0xbd, 0x5e, 0x30, 0x28, 0xb0, 0x80, 0xf7, 0xc0, 0x68});
	BC_ASSERT_TRUE(OKM == bctoolbox::HKDF<SHA512>(salt, IKM, info, OKM.size()));

	/* test A.7 */
	IKM.assign({0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c});
	salt.clear();
	info.clear();
	OKM.assign({0x14, 0x07, 0xd4, 0x60, 0x13, 0xd9, 0x8b, 0xc6, 0xde, 0xce, 0xfc, 0xfe, 0xe5, 0x5f, 0x0f, 0x90, 0xb0, 0xc7, 0xf6, 0x3d, 0x68, 0xeb, 0x1a, 0x80, 0xea, 0xf0, 0x7e, 0x95, 0x3c, 0xfc, 0x0a, 0x3a, 0x52, 0x40, 0xa1, 0x55, 0xd6, 0xe4, 0xda, 0xa9, 0x65, 0xbb});
	BC_ASSERT_TRUE(OKM == bctoolbox::HKDF<SHA512>(salt, IKM, info, OKM.size()));

#endif // HAVE_MBEDTLS

}

#ifdef HAVE_MBEDTLS
template <typename U>
static void rng_stats_update(size_t &count, double &mean, double &m2, U r) noexcept {
	count++;
	double delta = r - mean;
	mean += delta/count;
	double delta2 = r - mean;
	m2 += delta*delta2;
}



static void rng_test_32_args(size_t calls_nb) noexcept {
	size_t stat_count = 0;
	double stat_mean = 0;
	double stat_m2 = 0;

	try {
		for (size_t i=0; i<calls_nb; i++) {
			rng_stats_update(stat_count, stat_mean, stat_m2, bctoolbox::RNG::cRandomize());
		}
		// normalized mean shall be around 0.5
		stat_mean /= std::pow(2,32)-1;
		stat_m2 /= stat_count*std::pow(std::pow(2,32)-1,2);
		if (stat_mean>1.1*0.5 || stat_mean<0.9*0.5) {
			BCTBX_SLOGE<<"RNG mean value on uint32_t running "<<calls_nb<<" is " <<stat_mean;
			BC_FAIL("RNG output mean out of expected values");
		}
		// normalised variance shall be around 1/12
		if (stat_m2>1.1/12 || stat_m2<0.9/12) {
			BCTBX_SLOGE<<"RNG variance value on uint32_t running "<<calls_nb<<" is " <<stat_m2;
			BC_FAIL("RNG output variance out of expected values");
		}
	} catch (BctbxException const &e) {
		BCTBX_SLOGE<<"static RNG test fail "<<e.str();
		BC_FAIL("static RNG test fail");
	}

	stat_count = 0;
	stat_mean = 0;
	stat_m2 = 0;

	try {
		auto r = std::unique_ptr<bctoolbox::RNG>(new bctoolbox::RNG());

		for (size_t i=0; i<calls_nb; i++) {
			rng_stats_update(stat_count, stat_mean, stat_m2, r->randomize());
		}
		// normalized mean shall be around 0.5
		stat_mean /= std::pow(2,32)-1;
		stat_m2 /= stat_count*std::pow(std::pow(2,32)-1,2);
		if (stat_mean>1.1*0.5 || stat_mean<0.9*0.5) {
			BCTBX_SLOGE<<"RNG mean value on uint32_t running "<<calls_nb<<" is " <<stat_mean;
			BC_FAIL("RNG output mean out of expected values");
		}
		// normalised variance shall be around 1/12
		if (stat_m2>1.1/12 || stat_m2<0.9/12) {
			BCTBX_SLOGE<<"RNG variance value on uint32_t running "<<calls_nb<<" is " <<stat_m2;
			BC_FAIL("RNG output variance out of expected values");
		}
	} catch (BctbxException const &e) {
		BCTBX_SLOGE<<"context RNG test fail "<<e.str();
		BC_FAIL("context RNG test fail");
	}
}

static void rng_test_args(size_t buffer_size, size_t calls_nb) noexcept {
	uint8_t alea[1025];
	size_t stat_count = 0;
	double stat_mean = 0;
	double stat_m2 = 0;

	try {
		for (size_t i=0; i<calls_nb; i++) {
			bctoolbox::RNG::cRandomize(alea, buffer_size);
			for (size_t j=0; j<buffer_size; j++) {
				rng_stats_update(stat_count, stat_mean, stat_m2, alea[j]);
			}
		}
		// normalized mean shall be around 0.5
		stat_mean /= 255;
		stat_m2 /= stat_count*255*255;
		if (stat_mean>1.1*0.5 || stat_mean<0.9*0.5) {
			BCTBX_SLOGE<<"RNG mean value on buffer size "<<buffer_size<<" running "<<calls_nb<<" is " <<stat_mean;
			BC_FAIL("RNG output mean out of expected values");
		}
		// normalised variance shall be around 1/12
		if (stat_m2>1.1/12 || stat_m2<0.9/12) {
			BCTBX_SLOGE<<"RNG variance value on buffer size "<<buffer_size<<" running "<<calls_nb<<" is " <<stat_m2;
			BC_FAIL("RNG output variance out of expected values");
		}
	} catch (BctbxException const &e) {
		BCTBX_SLOGE<<"static RNG test fail "<<e.str();
		BC_FAIL("static RNG test fail");
	}

	stat_count = 0;
	stat_mean = 0;
	stat_m2 = 0;

	try {
		auto r = std::unique_ptr<bctoolbox::RNG>(new bctoolbox::RNG());

		for (size_t i=0; i<calls_nb; i++) {
			r->randomize(alea, buffer_size);
			for (size_t j=0; j<buffer_size; j++) {
				rng_stats_update(stat_count, stat_mean, stat_m2, alea[j]);
			}
		}
		// normalized mean shall be around 0.5
		stat_mean /= 255;
		stat_m2 /= stat_count*255*255;
		if (stat_mean>1.1*0.5 || stat_mean<0.9*0.5) {
			BCTBX_SLOGE<<"RNG mean value on buffer size "<<buffer_size<<" running "<<calls_nb<<" is " <<stat_mean;
			BC_FAIL("RNG output mean out of expected values");
		}
		// normalised variance shall be around 1/12
		if (stat_m2>1.1/12 || stat_m2<0.9/12) {
			BCTBX_SLOGE<<"RNG variance value on buffer size "<<buffer_size<<" running "<<calls_nb<<" is " <<stat_m2;
			BC_FAIL("RNG output variance out of expected values");
		}
	} catch (BctbxException const &e) {
		BCTBX_SLOGE<<"context RNG test fail "<<e.str();
		BC_FAIL("context RNG test fail");
	}
}
#endif // HAVE_MBEDTLS

static void rng_test(void) {
#ifdef HAVE_MBEDTLS
	rng_test_args(4, 4096);
	rng_test_args(32, 1024);
	rng_test_args(64, 1024);
	rng_test_args(128, 256);
	rng_test_args(512, 64);
	rng_test_args(1024, 32);
	rng_test_32_args(500);
	rng_test_32_args(1000);
	rng_test_32_args(10000);

	/* try to generate a very large buffer, we shall get an exception */
	auto exceptionRaised = false;
	try {
		constexpr size_t buffer_size=50000;
		uint8_t buffer[buffer_size];
		bctoolbox::RNG::cRandomize(buffer, buffer_size);
	} catch (BctbxException const &e) {
		exceptionRaised = true;
	}
	if (exceptionRaised == false) {
		BC_FAIL("No exception raised when trying to generate a huge amount of random data");
	}
#else // HAVE_MBEDTLS
	bctbx_warning("test skipped as we don't have mbedtls in bctoolbox");
#endif //HAVE_MBEDTLS
}

static void AEAD(void) {
	std::vector<uint8_t> cipher{};
	std::vector<uint8_t> tag{};
	std::vector<uint8_t> plain{};

	/* Test vectors for AES256-GCM128 from IEEE P1619.1/D22 - Annex D.3 */
	/* Test D3.1*/
	std::vector<uint8_t> key{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
	std::vector<uint8_t> IV{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
	std::vector<uint8_t> pattern_plain{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
	std::vector<uint8_t> pattern_cipher{0xce, 0xa7, 0x40, 0x3d, 0x4d, 0x60, 0x6b, 0x6e, 0x07, 0x4e, 0xc5, 0xd3, 0xba, 0xf3, 0x9d, 0x18};
	std::vector<uint8_t> pattern_tag{0xd0, 0xd1, 0xc8, 0xa7, 0x99, 0x99, 0x6b, 0xf0, 0x26, 0x5b, 0x98, 0xb5, 0xd4, 0x8a, 0xb9, 0x19};
	std::vector<uint8_t> AD{};
	AD.clear();

	cipher = AEADEncrypt<AES256GCM128>(key, IV, pattern_plain, AD, tag);
	BC_ASSERT_TRUE(cipher==pattern_cipher);
	BC_ASSERT_TRUE(tag==pattern_tag);
	BC_ASSERT_TRUE(AEADDecrypt<AES256GCM128>(key, IV, pattern_cipher, AD, pattern_tag, plain));
	BC_ASSERT_TRUE(plain==pattern_plain);

	/* Test D3.2 */
	key = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
	IV.assign({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
	AD.assign({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
	pattern_plain.clear();
	pattern_cipher.clear();
	pattern_tag={0x2d, 0x45, 0x55, 0x2d, 0x85, 0x75, 0x92, 0x2b, 0x3c, 0xa3, 0xcc, 0x53, 0x84, 0x42, 0xfa, 0x26};
	cipher = AEADEncrypt<AES256GCM128>(key, IV, pattern_plain, AD, tag);
	BC_ASSERT_TRUE(tag==pattern_tag);

	/* Test D3.3 */
	key ={0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
	IV.assign({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
	AD.assign({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
	pattern_plain.assign({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
	pattern_cipher.assign({0xce, 0xa7, 0x40, 0x3d, 0x4d, 0x60, 0x6b, 0x6e, 0x07, 0x4e, 0xc5, 0xd3, 0xba, 0xf3, 0x9d, 0x18});
	pattern_tag={0xae, 0x9b, 0x17, 0x71, 0xdb, 0xa9, 0xcf, 0x62, 0xb3, 0x9b, 0xe0, 0x17, 0x94, 0x03, 0x30, 0xb4};
	cipher.resize(pattern_plain.size());
	plain.resize(pattern_plain.size());

	cipher = AEADEncrypt<AES256GCM128>(key, IV, pattern_plain, AD, tag);
	BC_ASSERT_TRUE(cipher==pattern_cipher);
	BC_ASSERT_TRUE(tag==pattern_tag);
	BC_ASSERT_TRUE(AEADDecrypt<AES256GCM128>(key, IV, pattern_cipher, AD, pattern_tag, plain));
	BC_ASSERT_TRUE(plain==pattern_plain);

	/* Test D3.4 */
	key={0xfb, 0x76, 0x15, 0xb2, 0x3d, 0x80, 0x89, 0x1d, 0xd4, 0x70, 0x98, 0x0b, 0xc7, 0x95, 0x84, 0xc8, 0xb2, 0xfb, 0x64, 0xce, 0x60, 0x97, 0x8f, 0x4d, 0x17, 0xfc, 0xe4, 0x5a, 0x49, 0xe8, 0x30, 0xb7};
	IV.assign({0xdb, 0xd1, 0xa3, 0x63, 0x60, 0x24, 0xb7, 0xb4, 0x02, 0xda, 0x7d, 0x6f});
	AD.clear();
	pattern_plain.assign({0xa8, 0x45, 0x34, 0x8e, 0xc8, 0xc5, 0xb5, 0xf1, 0x26, 0xf5, 0x0e, 0x76, 0xfe, 0xfd, 0x1b, 0x1e});
	pattern_cipher.assign({0x5d, 0xf5, 0xd1, 0xfa, 0xbc, 0xbb, 0xdd, 0x05, 0x15, 0x38, 0x25, 0x24, 0x44, 0x17, 0x87, 0x04});
	pattern_tag={0x4c, 0x43, 0xcc, 0xe5, 0xa5, 0x74, 0xd8, 0xa8, 0x8b, 0x43, 0xd4, 0x35, 0x3b, 0xd6, 0x0f, 0x9f};
	cipher.resize(pattern_plain.size());
	plain.resize(pattern_plain.size());

	cipher = AEADEncrypt<AES256GCM128>(key, IV, pattern_plain, AD, tag);
	BC_ASSERT_TRUE(cipher==pattern_cipher);
	BC_ASSERT_TRUE(tag==pattern_tag);
	BC_ASSERT_TRUE(AEADDecrypt<AES256GCM128>(key, IV, pattern_cipher, AD, pattern_tag, plain));
	BC_ASSERT_TRUE(plain==pattern_plain);

	/* Test D3.5 */
	key={0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f};
	IV.assign({0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b});
	AD.assign({0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13});
	pattern_plain.assign({0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37});
	pattern_cipher.assign({0x59, 0x1b, 0x1f, 0xf2, 0x72, 0xb4, 0x32, 0x04, 0x86, 0x8f, 0xfc, 0x7b, 0xc7, 0xd5, 0x21, 0x99, 0x35, 0x26, 0xb6, 0xfa, 0x32, 0x24, 0x7c, 0x3c});
	pattern_tag={0x7d, 0xe1, 0x2a, 0x56, 0x70, 0xe5, 0x70, 0xd8, 0xca, 0xe6, 0x24, 0xa1, 0x6d, 0xf0, 0x9c, 0x08};
	cipher.resize(pattern_plain.size());
	plain.resize(pattern_plain.size());

	cipher = AEADEncrypt<AES256GCM128>(key, IV, pattern_plain, AD, tag);
	BC_ASSERT_TRUE(cipher==pattern_cipher);
	BC_ASSERT_TRUE(tag==pattern_tag);
	BC_ASSERT_TRUE(AEADDecrypt<AES256GCM128>(key, IV, pattern_cipher, AD, pattern_tag, plain));
	BC_ASSERT_TRUE(plain==pattern_plain);

	/* Test D3.6 */
	key={0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f};
	IV.assign({0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b});
	AD.assign({0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff});
	pattern_plain.assign({0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f});
	pattern_cipher.assign({0x59, 0x1b, 0x1f, 0xf2, 0x72, 0xb4, 0x32, 0x04, 0x86, 0x8f, 0xfc, 0x7b, 0xc7, 0xd5, 0x21, 0x99, 0x35, 0x26, 0xb6, 0xfa, 0x32, 0x24, 0x7c, 0x3c, 0x40, 0x57, 0xf3, 0xea, 0xe7, 0x54, 0x8c, 0xef});
	pattern_tag={0xa1, 0xde, 0x55, 0x36, 0xe9, 0x7e, 0xdd, 0xdc, 0xcd, 0x26, 0xee, 0xb1, 0xb5, 0xff, 0x7b, 0x32};
	cipher.resize(pattern_plain.size());
	plain.resize(pattern_plain.size());
	std::vector<uint8_t> repeatAD{};
	for (auto i=0; i<256; i++) {
		repeatAD.insert(repeatAD.end(), AD.cbegin(), AD.cend());
	}

	cipher = AEADEncrypt<AES256GCM128>(key, IV, pattern_plain, repeatAD, tag);
	BC_ASSERT_TRUE(cipher==pattern_cipher);
	BC_ASSERT_TRUE(tag==pattern_tag);
	BC_ASSERT_TRUE(AEADDecrypt<AES256GCM128>(key, IV, pattern_cipher, repeatAD, pattern_tag, plain));
	BC_ASSERT_TRUE(plain==pattern_plain);

	/* Test D3.7 */
	key={0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f};
	IV.assign({0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b});
	AD.assign({0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f});
	pattern_plain.assign({0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff});
	pattern_cipher.assign({0x79, 0x3b, 0x3f, 0xd2, 0x52, 0x94, 0x12, 0x24, 0xa6, 0xaf, 0xdc, 0x5b, 0xe7, 0xf5, 0x01, 0xb9, 0x15, 0x06, 0x96, 0xda, 0x12, 0x04, 0x5c, 0x1c, 0x60, 0x77, 0xd3, 0xca, 0xc7, 0x74, 0xac, 0xcf, 0xc3, 0xd5, 0x30, 0xd8, 0x48, 0xd6, 0x65, 0xd8, 0x1a, 0x49, 0xcb, 0xb5, 0x00, 0xb8, 0x8b, 0xbb, 0x62, 0x4a, 0xe6, 0x1d, 0x16, 0x67, 0x22, 0x9c, 0x30, 0x2d, 0xc6, 0xff, 0x0b, 0xb4, 0xd7, 0x0b, 0xdb, 0xbc, 0x85, 0x66, 0xd6, 0xf5, 0xb1, 0x58, 0xda, 0x99, 0xa2, 0xff, 0x2e, 0x01, 0xdd, 0xa6, 0x29, 0xb8, 0x9c, 0x34, 0xad, 0x1e, 0x5f, 0xeb, 0xa7, 0x0e, 0x7a, 0xae, 0x43, 0x28, 0x28, 0x9c, 0x36, 0x29, 0xb0, 0x58, 0x83, 0x50, 0x58, 0x1c, 0xa8, 0xb9, 0x7c, 0xcf, 0x12, 0x58, 0xfa, 0x3b, 0xbe, 0x2c, 0x50, 0x26, 0x04, 0x7b, 0xa7, 0x26, 0x48, 0x96, 0x9c, 0xff, 0x8b, 0xa1, 0x0a, 0xe3, 0x0e, 0x05, 0x93, 0x5d, 0xf0, 0xc6, 0x93, 0x74, 0x18, 0x92, 0xb7, 0x6f, 0xaf, 0x67, 0x13, 0x3a, 0xbd, 0x2c, 0xf2, 0x03, 0x11, 0x21, 0xbd, 0x8b, 0xb3, 0x81, 0x27, 0xa4, 0xd2, 0xee, 0xde, 0xea, 0x13, 0x27, 0x64, 0x94, 0xf4, 0x02, 0xcd, 0x7c, 0x10, 0x7f, 0xb3, 0xec, 0x3b, 0x24, 0x78, 0x48, 0x34, 0x33, 0x8e, 0x55, 0x43, 0x62, 0x87, 0x09, 0x2a, 0xc4, 0xa2, 0x6f, 0x5e, 0xa7, 0xea, 0x4a, 0xd6, 0x8d, 0x73, 0x15, 0x16, 0x39, 0xb0, 0x5b, 0x24, 0xe6, 0x8b, 0x98, 0x16, 0xd1, 0x39, 0x83, 0x76, 0xd8, 0xe4, 0x13, 0x85, 0x94, 0x75, 0x8d, 0xb9, 0xad, 0x3b, 0x40, 0x92, 0x59, 0xb2, 0x6d, 0xcf, 0xc0, 0x6e, 0x72, 0x2b, 0xe9, 0x87, 0xb3, 0x76, 0x7f, 0x70, 0xa7, 0xb8, 0x56, 0xb7, 0x74, 0xb1, 0xba, 0x26, 0x85, 0xb3, 0x68, 0x09, 0x14, 0x29, 0xfc, 0xcb, 0x8d, 0xcd, 0xde, 0x09, 0xe4});
	pattern_tag={0x87, 0xec, 0x83, 0x7a, 0xbf, 0x53, 0x28, 0x55, 0xb2, 0xce, 0xa1, 0x69, 0xd6, 0x94, 0x3f, 0xcd};
	cipher.resize(pattern_plain.size());
	plain.resize(pattern_plain.size());

	cipher = AEADEncrypt<AES256GCM128>(key, IV, pattern_plain, AD, tag);
	BC_ASSERT_TRUE(cipher==pattern_cipher);
	BC_ASSERT_TRUE(tag==pattern_tag);
	BC_ASSERT_TRUE(AEADDecrypt<AES256GCM128>(key, IV, pattern_cipher, AD, pattern_tag, plain));
	BC_ASSERT_TRUE(plain==pattern_plain);

	/* Test D3.8 */
	key={0xfb, 0x76, 0x15, 0xb2, 0x3d, 0x80, 0x89, 0x1d, 0xd4, 0x70, 0x98, 0x0b, 0xc7, 0x95, 0x84, 0xc8, 0xb2, 0xfb, 0x64, 0xce, 0x60, 0x97, 0x87, 0x8d, 0x17, 0xfc, 0xe4, 0x5a, 0x49, 0xe8, 0x30, 0xb7};
	IV.assign({0xdb, 0xd1, 0xa3, 0x63, 0x60, 0x24, 0xb7, 0xb4, 0x02, 0xda, 0x7d, 0x6f});
	AD.assign({0x36});
	pattern_plain.assign({0xa9});
	pattern_cipher.assign({0x0a});
	pattern_tag={0xbe, 0x98, 0x7d, 0x00, 0x9a, 0x4b, 0x34, 0x9a, 0xa8, 0x0c, 0xb9, 0xc4, 0xeb, 0xc1, 0xe9, 0xf4};
	cipher.resize(pattern_plain.size());
	plain.resize(pattern_plain.size());

	cipher = AEADEncrypt<AES256GCM128>(key, IV, pattern_plain, AD, tag);
	BC_ASSERT_TRUE(cipher==pattern_cipher);
	BC_ASSERT_TRUE(tag==pattern_tag);
	BC_ASSERT_TRUE(AEADDecrypt<AES256GCM128>(key, IV, pattern_cipher, AD, pattern_tag, plain));
	BC_ASSERT_TRUE(plain==pattern_plain);

	/* Test D3.9 */
	key={0xf8, 0xd4, 0x76, 0xcf, 0xd6, 0x46, 0xea, 0x6c, 0x23, 0x84, 0xcb, 0x1c, 0x27, 0xd6, 0x19, 0x5d, 0xfe, 0xf1, 0xa9, 0xf3, 0x7b, 0x9c, 0x8d, 0x21, 0xa7, 0x9c, 0x21, 0xf8, 0xcb, 0x90, 0xd2, 0x89};
	IV.assign({0xdb, 0xd1, 0xa3, 0x63, 0x60, 0x24, 0xb7, 0xb4, 0x02, 0xda, 0x7d, 0x6f});
	AD.assign({0x7b, 0xd8, 0x59, 0xa2, 0x47, 0x96, 0x1a, 0x21, 0x82, 0x3b, 0x38, 0x0e, 0x9f, 0xe8, 0xb6, 0x50, 0x82, 0xba, 0x61, 0xd3});
	pattern_plain.assign({0x90, 0xae, 0x61, 0xcf, 0x7b, 0xae, 0xbd, 0x4c, 0xad, 0xe4, 0x94, 0xc5, 0x4a, 0x29, 0xae, 0x70, 0x26, 0x9a, 0xec, 0x71});
	pattern_cipher.assign({0xce, 0x20, 0x27, 0xb4, 0x7a, 0x84, 0x32, 0x52, 0x01, 0x34, 0x65, 0x83, 0x4d, 0x75, 0xfd, 0x0f, 0x07, 0x29, 0x75, 0x2e});
	pattern_tag={0xac, 0xd8, 0x83, 0x38, 0x37, 0xab, 0x0e, 0xde, 0x84, 0xf4, 0x74, 0x8d, 0xa8, 0x89, 0x9c, 0x15};
	cipher.resize(pattern_plain.size());
	plain.resize(pattern_plain.size());

	cipher = AEADEncrypt<AES256GCM128>(key, IV, pattern_plain, AD, tag);
	BC_ASSERT_TRUE(cipher==pattern_cipher);
	BC_ASSERT_TRUE(tag==pattern_tag);
	BC_ASSERT_TRUE(AEADDecrypt<AES256GCM128>(key, IV, pattern_cipher, AD, pattern_tag, plain));
	BC_ASSERT_TRUE(plain==pattern_plain);

	/* Test D3.10 */
	key={0xdb, 0xbc, 0x85, 0x66, 0xd6, 0xf5, 0xb1, 0x58, 0xda, 0x99, 0xa2, 0xff, 0x2e, 0x01, 0xdd, 0xa6, 0x29, 0xb8, 0x9c, 0x34, 0xad, 0x1e, 0x5f, 0xeb, 0xa7, 0x0e, 0x7a, 0xae, 0x43, 0x28, 0x28, 0x9c};
	IV.assign({0xcf, 0xc0, 0x6e, 0x72, 0x2b, 0xe9, 0x87, 0xb3, 0x76, 0x7f, 0x70, 0xa7, 0xb8, 0x56, 0xb7, 0x74});
	AD.clear();
	pattern_plain.assign({0xce, 0x20, 0x27, 0xb4, 0x7a, 0x84, 0x32, 0x52, 0x01, 0x34, 0x65, 0x83, 0x4d, 0x75, 0xfd, 0x0f});
	pattern_cipher.assign({0xdc, 0x03, 0xe5, 0x24, 0x83, 0x0d, 0x30, 0xf8, 0x8e, 0x19, 0x7f, 0x3a, 0xca, 0xce, 0x66, 0xef});
	pattern_tag={0x99, 0x84, 0xef, 0xf6, 0x90, 0x57, 0x55, 0xd1, 0x83, 0x6f, 0x2d, 0xb0, 0x40, 0x89, 0x63, 0x4c};
	cipher.resize(pattern_plain.size());
	plain.resize(pattern_plain.size());

	cipher = AEADEncrypt<AES256GCM128>(key, IV, pattern_plain, AD, tag);
	BC_ASSERT_TRUE(cipher==pattern_cipher);
	BC_ASSERT_TRUE(tag==pattern_tag);
	BC_ASSERT_TRUE(AEADDecrypt<AES256GCM128>(key, IV, pattern_cipher, AD, pattern_tag, plain));
	BC_ASSERT_TRUE(plain==pattern_plain);

	/* Test D3.11 */
	key={0x0e, 0x05, 0x93, 0x5d, 0xf0, 0xc6, 0x93, 0x74, 0x18, 0x92, 0xb7, 0x6f, 0xaf, 0x67, 0x13, 0x3a, 0xbd, 0x2c, 0xf2, 0x03, 0x11, 0x21, 0xbd, 0x8b, 0xb3, 0x81, 0x27, 0xa4, 0xd2, 0xee, 0xde, 0xea};
	IV.assign({0x74, 0xb1, 0xba, 0x26, 0x85, 0xb3, 0x68, 0x09, 0x14, 0x29, 0xfc, 0xcb, 0x8d, 0xcd, 0xde, 0x09, 0xe4});
	AD.assign({0x7b, 0xd8, 0x59, 0xa2, 0x47, 0x96, 0x1a, 0x21, 0x82, 0x3b, 0x38, 0x0e, 0x9f, 0xe8, 0xb6, 0x50, 0x82, 0xba, 0x61, 0xd3});
	pattern_plain.assign({0x90, 0xae, 0x61, 0xcf, 0x7b, 0xae, 0xbd, 0x4c, 0xad, 0xe4, 0x94, 0xc5, 0x4a, 0x29, 0xae, 0x70, 0x26, 0x9a, 0xec, 0x71});
	pattern_cipher.assign({0x6b, 0xe6, 0x5e, 0x56, 0x06, 0x6c, 0x40, 0x56, 0x73, 0x8c, 0x03, 0xfe, 0x23, 0x20, 0x97, 0x4b, 0xa3, 0xf6, 0x5e, 0x09});
	pattern_tag={0x61, 0x08, 0xdc, 0x41, 0x7b, 0xf3, 0x2f, 0x7f, 0xb7, 0x55, 0x4a, 0xe5, 0x2f, 0x08, 0x8f, 0x87};
	cipher.resize(pattern_plain.size());
	plain.resize(pattern_plain.size());

	cipher = AEADEncrypt<AES256GCM128>(key, IV, pattern_plain, AD, tag);
	BC_ASSERT_TRUE(cipher==pattern_cipher);
	BC_ASSERT_TRUE(tag==pattern_tag);
	BC_ASSERT_TRUE(AEADDecrypt<AES256GCM128>(key, IV, pattern_cipher, AD, pattern_tag, plain));
	BC_ASSERT_TRUE(plain==pattern_plain);

	/* Test D3.12 */
	key={0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
	IV.assign({0x02, 0xcb, 0xbc, 0x7a, 0x03, 0xeb, 0x4d, 0xe3, 0x9d, 0x80, 0xd1, 0xeb, 0xc9, 0x88, 0xbf, 0xdf});
	AD.assign({0x68, 0x8e, 0x1a, 0xa9, 0x84, 0xde, 0x92, 0x6d, 0xc7, 0xb4, 0xc4, 0x7f, 0x44});
	pattern_plain.assign({0xa2, 0xaa, 0xb3, 0xad, 0x8b, 0x17, 0xac, 0xdd, 0xa2, 0x88, 0x42, 0x6c, 0xd7, 0xc4, 0x29, 0xb7, 0xca, 0x86, 0xb7, 0xac, 0xa0, 0x58, 0x09, 0xc7, 0x0c, 0xe8, 0x2d, 0xb2, 0x57, 0x11, 0xcb, 0x53, 0x02, 0xeb, 0x27, 0x43, 0xb0, 0x36, 0xf3, 0xd7, 0x50, 0xd6, 0xcf, 0x0d, 0xc0, 0xac, 0xb9, 0x29, 0x50, 0xd5, 0x46, 0xdb, 0x30, 0x8f, 0x93, 0xb4, 0xff, 0x24, 0x4a, 0xfa, 0x9d, 0xc7, 0x2b, 0xcd, 0x75, 0x8d, 0x2c});
	pattern_cipher.assign({0xee, 0x62, 0x55, 0x2a, 0xeb, 0xc0, 0xc3, 0xc7, 0xda, 0xae, 0x12, 0xbb, 0x6c, 0x32, 0xca, 0x5a, 0x00, 0x5f, 0x4a, 0x1a, 0xaa, 0xb0, 0x04, 0xed, 0x0f, 0x0b, 0x30, 0xab, 0xbf, 0x15, 0xac, 0xf4, 0xc5, 0x0c, 0x59, 0x66, 0x2d, 0x4b, 0x44, 0x68, 0x41, 0x95, 0x44, 0xe7, 0xf9, 0x81, 0x97, 0x35, 0x63, 0xce, 0x55, 0x6a, 0xe5, 0x08, 0x59, 0xee, 0x09, 0xb1, 0x4d, 0x31, 0xa0, 0x53, 0x98, 0x6f, 0x9a, 0xc8, 0x9b});
	pattern_tag={0x9c, 0xd0, 0xdb, 0x93, 0x6e, 0x26, 0xd4, 0x4b, 0xe9, 0x74, 0xba, 0x86, 0x82, 0x85, 0xa2, 0xe1};
	cipher.resize(pattern_plain.size());
	plain.resize(pattern_plain.size());

	cipher = AEADEncrypt<AES256GCM128>(key, IV, pattern_plain, AD, tag);
	BC_ASSERT_TRUE(cipher==pattern_cipher);
	BC_ASSERT_TRUE(tag==pattern_tag);
	BC_ASSERT_TRUE(AEADDecrypt<AES256GCM128>(key, IV, pattern_cipher, AD, pattern_tag, plain));
	BC_ASSERT_TRUE(plain==pattern_plain);
}

static void key_wrap_test(){
	int ret;
	std::vector<uint8_t> plaintext;
	std::vector<uint8_t> key;
	std::vector<uint8_t> ciphertext;
	std::vector<uint8_t> wrapped_ct;
	std::vector<uint8_t> unwrapped_pt;

	/* Test vectors from Section 4.1 of the RFC 3394 : https://datatracker.ietf.org/doc/html/rfc3394#section-4.1 */

	/*plaintext = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
	key = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
	ciphertext = {0x1F, 0xA6, 0x8B, 0x0A, 0x81, 0x12, 0xB4, 0x47, 0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B, 0x82, 0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5};

	ret = AES_key_wrap(plaintext, key, wrapped_ct);
	BC_ASSERT_EQUAL(ret, 0, int, "%0x");
	BC_ASSERT_TRUE(ciphertext==wrapped_ct);

	ret = AES_key_unwrap(ciphertext, key, unwrapped_pt);
	BC_ASSERT_EQUAL(ret, 0, int, "%0x");
	BC_ASSERT_TRUE(plaintext==unwrapped_pt);

	plaintext.clear();
	key.clear();
	ciphertext.clear();
	wrapped_ct.clear();*/

	/* Test vectors from Section 6 of the RFC 5649 : https://datatracker.ietf.org/doc/html/rfc5649#section-6 */

	plaintext = {0xc3, 0x7b, 0x7e, 0x64, 0x92, 0x58, 0x43, 0x40, 0xbe, 0xd1, 0x22, 0x07, 0x80, 0x89, 0x41, 0x15, 0x50, 0x68, 0xf7, 0x38};
	key = {0x58, 0x40, 0xdf, 0x6e, 0x29, 0xb0, 0x2a, 0xf1, 0xab, 0x49, 0x3b, 0x70, 0x5b, 0xf1, 0x6e, 0xa1, 0xae, 0x83, 0x38, 0xf4, 0xdc, 0xc1, 0x76, 0xa8};
	ciphertext = {0x13, 0x8b, 0xde, 0xaa, 0x9b, 0x8f, 0xa7, 0xfc, 0x61, 0xf9, 0x77, 0x42, 0xe7, 0x22, 0x48, 0xee, 0x5a, 0xe6, 0xae, 0x53, 0x60, 0xd1, 0xae, 0x6a, 0x5f, 0x54, 0xf3, 0x73, 0xfa, 0x54, 0x3b, 0x6a};

	ret = AES_key_wrap(plaintext, key, wrapped_ct, AesId::AES192);
	BC_ASSERT_EQUAL(ret, 0, int, "%0x");
	BC_ASSERT_TRUE(ciphertext==wrapped_ct);

	ret = AES_key_unwrap(ciphertext, key, unwrapped_pt, AesId::AES192);
	BC_ASSERT_EQUAL(ret, 0, int, "%0x");
	BC_ASSERT_TRUE(plaintext==unwrapped_pt);

	plaintext.clear();
	unwrapped_pt.clear();
	key.clear();
	ciphertext.clear();
	wrapped_ct.clear();

	plaintext = {0x46, 0x6f, 0x72, 0x50, 0x61, 0x73, 0x69};
	key = {0x58, 0x40, 0xdf, 0x6e, 0x29, 0xb0, 0x2a, 0xf1, 0xab, 0x49, 0x3b, 0x70, 0x5b, 0xf1, 0x6e, 0xa1, 0xae, 0x83, 0x38, 0xf4, 0xdc, 0xc1, 0x76, 0xa8};
	ciphertext = {0xaf, 0xbe, 0xb0, 0xf0, 0x7d, 0xfb, 0xf5, 0x41, 0x92, 0x00, 0xf2, 0xcc, 0xb5, 0x0b, 0xb2, 0x4f};

	ret = AES_key_wrap(plaintext, key, wrapped_ct, AesId::AES192);
	BC_ASSERT_EQUAL(ret, 0, int, "%0x");
	BC_ASSERT_TRUE(ciphertext==wrapped_ct);

	ret = AES_key_unwrap(ciphertext, key, unwrapped_pt, AesId::AES192);
	BC_ASSERT_EQUAL(ret, 0, int, "%0x");
	BC_ASSERT_TRUE(plaintext==unwrapped_pt);
}


static test_t crypto_tests[] = {
	TEST_NO_TAG("Diffie-Hellman Key exchange", DHM),
	TEST_NO_TAG("Elliptic Curve Diffie-Hellman Key exchange", ECDH),
	TEST_NO_TAG("ECDH25519 decaf-mbedtls", ECDH25519compat),
	TEST_NO_TAG("EdDSA sign and verify", EdDSA),
	TEST_NO_TAG("Ed25519 to X25519 key conversion", ed25519_to_x25519_keyconversion),
	TEST_NO_TAG("Sign message and exchange key using the same base secret", sign_and_key_exchange),
	TEST_NO_TAG("Hash functions", hash_test),
	TEST_NO_TAG("RNG", rng_test),
	TEST_NO_TAG("AEAD", AEAD),
	TEST_NO_TAG("Key wrap", key_wrap_test),
};

test_suite_t crypto_test_suite = {"Crypto", NULL, NULL, NULL, NULL,
							   sizeof(crypto_tests) / sizeof(crypto_tests[0]), crypto_tests};
