From f21b806efb3066c76aa8f186590c5b10a790ec8d Mon Sep 17 00:00:00 2001
From: Tobias Brunner <tobias@strongswan.org>
Date: Thu, 24 Mar 2022 15:02:45 +0100
Subject: [PATCH] Modularize the IKEv2 key derivation so it can be provided by
 plugins

In particular, the openssl plugin provides implementations of the two KDFs
to derive SKEYSEED (prf) and the rest of the key material (prf+) via
OpenSSL's HKDF-Extract and HKDF-Expand implementations, respectively.

Note that this limits the PRFs to HMACs, the others (AES128-XCBC and
AES128-CMAC) are rarely used, though.
---
 src/libcharon/bus/bus.c                       |   9 +-
 src/libcharon/bus/bus.h                       |  12 +-
 src/libcharon/bus/listeners/listener.h        |  12 +-
 src/libcharon/kernel/kernel_interface.h       |   1 -
 .../plugins/save_keys/save_keys_listener.c    |   5 +-
 src/libcharon/plugins/stroke/stroke_list.c    |   9 +
 src/libcharon/plugins/vici/vici_query.c       |  10 +
 src/libcharon/sa/child_sa.h                   |   1 -
 src/libcharon/sa/ikev1/keymat_v1.c            |  50 +-
 src/libcharon/sa/ikev2/keymat_v2.c            | 472 ++++++++----------
 src/libcharon/tests/utils/mock_dh.c           |  11 +-
 src/libstrongswan/Android.mk                  |   4 +-
 src/libstrongswan/Makefile.am                 |   7 +-
 src/libstrongswan/crypto/crypto_factory.c     | 124 +++++
 src/libstrongswan/crypto/crypto_factory.h     |  48 ++
 src/libstrongswan/crypto/crypto_tester.c      | 220 ++++++++
 src/libstrongswan/crypto/crypto_tester.h      |  45 ++
 src/libstrongswan/crypto/kdfs/kdf.c           |  45 ++
 src/libstrongswan/crypto/kdfs/kdf.h           | 152 ++++++
 src/libstrongswan/crypto/pkcs5.c              |   6 +-
 src/libstrongswan/crypto/prf_plus.c           | 170 -------
 src/libstrongswan/crypto/prf_plus.h           |  70 ---
 src/libstrongswan/crypto/transform.c          |  11 +-
 src/libstrongswan/crypto/transform.h          |   1 +
 src/libstrongswan/plugins/openssl/Makefile.am |   1 +
 .../plugins/openssl/openssl_kdf.c             | 223 +++++++++
 .../plugins/openssl/openssl_kdf.h             |  45 ++
 .../plugins/openssl/openssl_plugin.c          |   7 +
 src/libstrongswan/plugins/plugin_feature.c    |  47 +-
 src/libstrongswan/plugins/plugin_feature.h    |   8 +-
 .../plugins/test_vectors/Makefile.am          |   2 +
 .../plugins/test_vectors/test_vectors.h       |  14 +
 .../test_vectors/test_vectors/kdf_prf.c       | 236 +++++++++
 .../test_vectors/test_vectors/kdf_prf_plus.c  | 290 +++++++++++
 .../test_vectors/test_vectors_plugin.c        |  16 +
 .../tests/suites/test_prf_plus.c              | 208 ++++----
 src/libstrongswan/tests/suites/test_vectors.c |   1 +
 src/libstrongswan/tests/tests.h               |   2 +-
 38 files changed, 1931 insertions(+), 664 deletions(-)
 create mode 100644 src/libstrongswan/crypto/kdfs/kdf.c
 create mode 100644 src/libstrongswan/crypto/kdfs/kdf.h
 delete mode 100644 src/libstrongswan/crypto/prf_plus.c
 delete mode 100644 src/libstrongswan/crypto/prf_plus.h
 create mode 100644 src/libstrongswan/plugins/openssl/openssl_kdf.c
 create mode 100644 src/libstrongswan/plugins/openssl/openssl_kdf.h
 create mode 100644 src/libstrongswan/plugins/test_vectors/test_vectors/kdf_prf.c
 create mode 100644 src/libstrongswan/plugins/test_vectors/test_vectors/kdf_prf_plus.c

diff --git a/src/libcharon/bus/bus.c b/src/libcharon/bus/bus.c
index b7348f0f93e3..773ee0c9d50f 100644
--- a/src/libcharon/bus/bus.c
+++ b/src/libcharon/bus/bus.c
@@ -604,8 +604,8 @@ METHOD(bus_t, ike_keys, void,
 }
 
 METHOD(bus_t, ike_derived_keys, void,
-	private_bus_t *this, chunk_t sk_ei, chunk_t sk_er, chunk_t sk_ai,
-	chunk_t sk_ar)
+	private_bus_t *this, chunk_t sk_d, chunk_t sk_ai, chunk_t sk_ar,
+	chunk_t sk_ei, chunk_t sk_er, chunk_t sk_pi, chunk_t sk_pr)
 {
 	enumerator_t *enumerator;
 	ike_sa_t *ike_sa;
@@ -623,8 +623,9 @@ METHOD(bus_t, ike_derived_keys, void,
 			continue;
 		}
 		entry->calling++;
-		keep = entry->listener->ike_derived_keys(entry->listener, ike_sa, sk_ei,
-												 sk_er, sk_ai, sk_ar);
+		keep = entry->listener->ike_derived_keys(entry->listener, ike_sa, sk_d,
+												 sk_ai, sk_ar, sk_ei, sk_er,
+												 sk_pi, sk_pr);
 		entry->calling--;
 		if (!keep)
 		{
diff --git a/src/libcharon/bus/bus.h b/src/libcharon/bus/bus.h
index 8a97e8dfc7db..16e7000cdba6 100644
--- a/src/libcharon/bus/bus.h
+++ b/src/libcharon/bus/bus.h
@@ -363,13 +363,17 @@ struct bus_t {
 	/**
 	 * IKE_SA derived keys hook.
 	 *
-	 * @param sk_ei		SK_ei, or Ka for IKEv1
-	 * @param sk_er		SK_er
+	 * @param sk_d		SK_d, or SKEYID_d for IKEv1
 	 * @param sk_ai		SK_ai, or SKEYID_a for IKEv1
 	 * @param sk_ar		SK_ar
+	 * @param sk_ei		SK_ei, or Ka for IKEv1
+	 * @param sk_er		SK_er
+	 * @param sk_pi		SK_pi
+	 * @param sk_pr		SK_pr
 	 */
-	void (*ike_derived_keys)(bus_t *this, chunk_t sk_ei, chunk_t sk_er,
-							 chunk_t sk_ai, chunk_t sk_ar);
+	void (*ike_derived_keys)(bus_t *this, chunk_t sk_d, chunk_t sk_ai,
+							 chunk_t sk_ar, chunk_t sk_ei, chunk_t sk_er,
+							 chunk_t sk_pi, chunk_t sk_pr);
 
 	/**
 	 * CHILD_SA keymat hook.
diff --git a/src/libcharon/bus/listeners/listener.h b/src/libcharon/bus/listeners/listener.h
index 0f3b8578a850..6b4ebb84a7a9 100644
--- a/src/libcharon/bus/listeners/listener.h
+++ b/src/libcharon/bus/listeners/listener.h
@@ -100,13 +100,17 @@ struct listener_t {
 	 * Hook called with derived IKE_SA keys.
 	 *
 	 * @param ike_sa	IKE_SA these keys belong to
-	 * @param sk_ei		SK_ei, or Ka for IKEv1
-	 * @param sk_er		SK_er
+	 * @param sk_d		SK_d, or SKEYID_d for IKEv1
 	 * @param sk_ai		SK_ai, or SKEYID_a for IKEv1
 	 * @param sk_ar		SK_ar
+	 * @param sk_ei		SK_ei, or Ka for IKEv1
+	 * @param sk_er		SK_er
+	 * @param sk_pi		SK_pi
+	 * @param sk_pr		SK_pr
 	 */
-	bool (*ike_derived_keys)(listener_t *this, ike_sa_t *ike_sa, chunk_t sk_ei,
-							 chunk_t sk_er, chunk_t sk_ai, chunk_t sk_ar);
+	bool (*ike_derived_keys)(listener_t *this, ike_sa_t *ike_sa, chunk_t sk_d,
+							 chunk_t sk_ai, chunk_t sk_ar, chunk_t sk_ei,
+							 chunk_t sk_er, chunk_t sk_pi, chunk_t sk_pr);
 
 	/**
 	 * Hook called with CHILD_SA key material.
diff --git a/src/libcharon/kernel/kernel_interface.h b/src/libcharon/kernel/kernel_interface.h
index b7e3686d1bca..e589db46111e 100644
--- a/src/libcharon/kernel/kernel_interface.h
+++ b/src/libcharon/kernel/kernel_interface.h
@@ -50,7 +50,6 @@ typedef struct kernel_interface_t kernel_interface_t;
 typedef enum kernel_feature_t kernel_feature_t;
 
 #include <networking/host.h>
-#include <crypto/prf_plus.h>
 
 #include <kernel/kernel_listener.h>
 #include <kernel/kernel_ipsec.h>
diff --git a/src/libcharon/plugins/save_keys/save_keys_listener.c b/src/libcharon/plugins/save_keys/save_keys_listener.c
index fc16f20e66a9..7171f3f45b58 100644
--- a/src/libcharon/plugins/save_keys/save_keys_listener.c
+++ b/src/libcharon/plugins/save_keys/save_keys_listener.c
@@ -275,8 +275,9 @@ static inline void esp_names(proposal_t *proposal, const char **enc,
 }
 
 METHOD(listener_t, ike_derived_keys, bool,
-	private_save_keys_listener_t *this, ike_sa_t *ike_sa, chunk_t sk_ei,
-	chunk_t sk_er, chunk_t sk_ai, chunk_t sk_ar)
+	private_save_keys_listener_t *this, ike_sa_t *ike_sa, chunk_t sk_d,
+	chunk_t sk_ai, chunk_t sk_ar, chunk_t sk_ei, chunk_t sk_er, chunk_t sk_pi,
+	chunk_t sk_pr)
 {
 	ike_version_t version;
 	ike_sa_id_t *id;
diff --git a/src/libcharon/plugins/stroke/stroke_list.c b/src/libcharon/plugins/stroke/stroke_list.c
index 92b18a8a67ed..1c44fefbaf83 100644
--- a/src/libcharon/plugins/stroke/stroke_list.c
+++ b/src/libcharon/plugins/stroke/stroke_list.c
@@ -849,6 +849,7 @@ static void list_algs(FILE *out)
 	hash_algorithm_t hash;
 	pseudo_random_function_t prf;
 	ext_out_function_t xof;
+	key_derivation_function_t kdf;
 	drbg_type_t drbg;
 	diffie_hellman_group_t group;
 	rng_quality_t quality;
@@ -905,6 +906,14 @@ static void list_algs(FILE *out)
 		print_alg(out, &len, ext_out_function_names, xof, plugin_name);
 	}
 	enumerator->destroy(enumerator);
+	fprintf(out, "\n  kdf:       ");
+	len = 13;
+	enumerator = lib->crypto->create_kdf_enumerator(lib->crypto);
+	while (enumerator->enumerate(enumerator, &kdf, &plugin_name))
+	{
+		print_alg(out, &len, key_derivation_function_names, kdf, plugin_name);
+	}
+	enumerator->destroy(enumerator);
 	fprintf(out, "\n  drbg:      ");
 	len = 13;
 	enumerator = lib->crypto->create_drbg_enumerator(lib->crypto);
diff --git a/src/libcharon/plugins/vici/vici_query.c b/src/libcharon/plugins/vici/vici_query.c
index ad07ff12d047..fd52c48cd9ec 100644
--- a/src/libcharon/plugins/vici/vici_query.c
+++ b/src/libcharon/plugins/vici/vici_query.c
@@ -1265,6 +1265,7 @@ CALLBACK(get_algorithms, vici_message_t*,
 	hash_algorithm_t hash;
 	pseudo_random_function_t prf;
 	ext_out_function_t xof;
+	key_derivation_function_t kdf;
 	drbg_type_t drbg;
 	diffie_hellman_group_t group;
 	rng_quality_t quality;
@@ -1326,6 +1327,15 @@ CALLBACK(get_algorithms, vici_message_t*,
 	enumerator->destroy(enumerator);
 	b->end_section(b);
 
+	b->begin_section(b, "kdf");
+	enumerator = lib->crypto->create_kdf_enumerator(lib->crypto);
+	while (enumerator->enumerate(enumerator, &kdf, &plugin_name))
+	{
+		add_algorithm(b, key_derivation_function_names, kdf, plugin_name);
+	}
+	enumerator->destroy(enumerator);
+	b->end_section(b);
+
 	b->begin_section(b, "drbg");
 	enumerator = lib->crypto->create_drbg_enumerator(lib->crypto);
 	while (enumerator->enumerate(enumerator, &drbg, &plugin_name))
diff --git a/src/libcharon/sa/child_sa.h b/src/libcharon/sa/child_sa.h
index c9b3f63e2bce..825dcfad75c0 100644
--- a/src/libcharon/sa/child_sa.h
+++ b/src/libcharon/sa/child_sa.h
@@ -29,7 +29,6 @@ typedef struct child_sa_t child_sa_t;
 typedef struct child_sa_create_t child_sa_create_t;
 
 #include <library.h>
-#include <crypto/prf_plus.h>
 #include <encoding/payloads/proposal_substructure.h>
 #include <crypto/proposal/proposal.h>
 #include <config/child_cfg.h>
diff --git a/src/libcharon/sa/ikev1/keymat_v1.c b/src/libcharon/sa/ikev1/keymat_v1.c
index 34bae4391c31..9eedce21ccb3 100644
--- a/src/libcharon/sa/ikev1/keymat_v1.c
+++ b/src/libcharon/sa/ikev1/keymat_v1.c
@@ -484,7 +484,8 @@ METHOD(keymat_v1_t, derive_ike_keys, bool,
 	{
 		return FALSE;
 	}
-	charon->bus->ike_derived_keys(charon->bus, ka, chunk_empty, this->skeyid_a,
+	charon->bus->ike_derived_keys(charon->bus, this->skeyid_d, this->skeyid_a,
+								  chunk_empty, ka, chunk_empty, chunk_empty,
 								  chunk_empty);
 	chunk_clear(&ka);
 	if (!this->hasher && !this->public.create_hasher(&this->public, proposal))
@@ -506,6 +507,36 @@ METHOD(keymat_v1_t, derive_ike_keys, bool,
 										this->aead->get_block_size(this->aead));
 }
 
+/**
+ * Derive key material for CHILD_SAs according to section 5.5. in RFC 2409.
+ */
+static bool derive_child_keymat(private_keymat_v1_t *this, chunk_t seed,
+								uint16_t enc_size, chunk_t *encr,
+								uint16_t int_size, chunk_t *integ)
+{
+	size_t block_size, i;
+	chunk_t keymat, prev = chunk_empty;
+
+	block_size = this->prf->get_block_size(this->prf);
+	keymat = chunk_alloc(round_up(enc_size + int_size, block_size));
+	keymat.len = enc_size + int_size;
+
+	for (i = 0; i < keymat.len; i += block_size)
+	{
+		if (!this->prf->get_bytes(this->prf, prev, NULL) ||
+			!this->prf->get_bytes(this->prf, seed, keymat.ptr + i))
+		{
+			chunk_clear(&keymat);
+			return FALSE;
+		}
+		prev = chunk_create(keymat.ptr + i, block_size);
+	}
+
+	chunk_split(keymat, "aa", enc_size, encr, int_size, integ);
+	chunk_clear(&keymat);
+	return TRUE;
+}
+
 METHOD(keymat_v1_t, derive_child_keys, bool,
 	private_keymat_v1_t *this, proposal_t *proposal, diffie_hellman_t *dh,
 	uint32_t spi_i, uint32_t spi_r, chunk_t nonce_i, chunk_t nonce_r,
@@ -513,8 +544,7 @@ METHOD(keymat_v1_t, derive_child_keys, bool,
 {
 	uint16_t enc_alg, int_alg, enc_size = 0, int_size = 0;
 	uint8_t protocol;
-	prf_plus_t *prf_plus;
-	chunk_t seed, secret = chunk_empty;
+	chunk_t seed = chunk_empty, secret = chunk_empty;
 	bool success = FALSE;
 
 	if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM,
@@ -598,11 +628,7 @@ METHOD(keymat_v1_t, derive_child_keys, bool,
 	seed = chunk_cata("ccccc", secret, chunk_from_thing(protocol),
 					  chunk_from_thing(spi_r), nonce_i, nonce_r);
 	DBG4(DBG_CHD, "initiator SA seed %B", &seed);
-
-	prf_plus = prf_plus_create(this->prf, FALSE, seed);
-	if (!prf_plus ||
-		!prf_plus->allocate_bytes(prf_plus, enc_size, encr_i) ||
-		!prf_plus->allocate_bytes(prf_plus, int_size, integ_i))
+	if (!derive_child_keymat(this, seed, enc_size, encr_i, int_size, integ_i))
 	{
 		goto failure;
 	}
@@ -610,11 +636,7 @@ METHOD(keymat_v1_t, derive_child_keys, bool,
 	seed = chunk_cata("ccccc", secret, chunk_from_thing(protocol),
 					  chunk_from_thing(spi_i), nonce_i, nonce_r);
 	DBG4(DBG_CHD, "responder SA seed %B", &seed);
-	prf_plus->destroy(prf_plus);
-	prf_plus = prf_plus_create(this->prf, FALSE, seed);
-	if (!prf_plus ||
-		!prf_plus->allocate_bytes(prf_plus, enc_size, encr_r) ||
-		!prf_plus->allocate_bytes(prf_plus, int_size, integ_r))
+	if (!derive_child_keymat(this, seed, enc_size, encr_r, int_size, integ_r))
 	{
 		goto failure;
 	}
@@ -639,7 +661,7 @@ failure:
 		chunk_clear(encr_r);
 		chunk_clear(integ_r);
 	}
-	DESTROY_IF(prf_plus);
+	memwipe(seed.ptr, seed.len);
 	chunk_clear(&secret);
 
 	return success;
diff --git a/src/libcharon/sa/ikev2/keymat_v2.c b/src/libcharon/sa/ikev2/keymat_v2.c
index db46b816b318..684ba83f9eae 100644
--- a/src/libcharon/sa/ikev2/keymat_v2.c
+++ b/src/libcharon/sa/ikev2/keymat_v2.c
@@ -17,7 +17,6 @@
 #include "keymat_v2.h"
 
 #include <daemon.h>
-#include <crypto/prf_plus.h>
 #include <crypto/hashers/hash_algorithm_set.h>
 
 typedef struct private_keymat_v2_t private_keymat_v2_t;
@@ -97,13 +96,12 @@ METHOD(keymat_t, create_nonce_gen, nonce_gen_t*,
 }
 
 /**
- * Derive IKE keys for a combined AEAD algorithm
+ * Create aead_t objects for a combined-mode AEAD algorithm, sets the length of
+ * sk_ei and sk_er
  */
-static bool derive_ike_aead(private_keymat_v2_t *this, uint16_t alg,
-							uint16_t key_size, prf_plus_t *prf_plus)
+static bool create_ike_aead(private_keymat_v2_t *this, uint16_t alg,
+							uint16_t key_size, chunk_t *sk_ei, chunk_t *sk_er)
 {
-	aead_t *aead_i, *aead_r;
-	chunk_t sk_ei = chunk_empty, sk_er = chunk_empty;
 	u_int salt_size;
 
 	switch (alg)
@@ -131,208 +129,177 @@ static bool derive_ike_aead(private_keymat_v2_t *this, uint16_t alg,
 			return FALSE;
 	}
 
-	/* SK_ei/SK_er used for encryption */
-	aead_i = lib->crypto->create_aead(lib->crypto, alg, key_size / 8, salt_size);
-	aead_r = lib->crypto->create_aead(lib->crypto, alg, key_size / 8, salt_size);
-	if (aead_i == NULL || aead_r == NULL)
+	this->aead_in = lib->crypto->create_aead(lib->crypto, alg, key_size / 8,
+											 salt_size);
+	this->aead_out = lib->crypto->create_aead(lib->crypto, alg, key_size / 8,
+											  salt_size);
+	if (!this->aead_in || !this->aead_out)
 	{
 		DBG1(DBG_IKE, "%N %N (key size %d) not supported!",
 			 transform_type_names, ENCRYPTION_ALGORITHM,
 			 encryption_algorithm_names, alg, key_size);
-		goto failure;
-	}
-	key_size = aead_i->get_key_size(aead_i);
-	if (key_size != aead_r->get_key_size(aead_r))
-	{
-		goto failure;
-	}
-	if (!prf_plus->allocate_bytes(prf_plus, key_size, &sk_ei))
-	{
-		goto failure;
-	}
-	DBG4(DBG_IKE, "Sk_ei secret %B", &sk_ei);
-	if (!aead_i->set_key(aead_i, sk_ei))
-	{
-		goto failure;
-	}
-
-	if (!prf_plus->allocate_bytes(prf_plus, key_size, &sk_er))
-	{
-		goto failure;
-	}
-	DBG4(DBG_IKE, "Sk_er secret %B", &sk_er);
-	if (!aead_r->set_key(aead_r, sk_er))
-	{
-		goto failure;
-	}
-
-	if (this->initiator)
-	{
-		this->aead_in = aead_r;
-		this->aead_out = aead_i;
+		return FALSE;
 	}
-	else
+	sk_ei->len = this->aead_in->get_key_size(this->aead_in);
+	sk_er->len = this->aead_out->get_key_size(this->aead_out);
+	if (sk_ei->len != sk_er->len)
 	{
-		this->aead_in = aead_i;
-		this->aead_out = aead_r;
+		return FALSE;
 	}
-	aead_i = aead_r = NULL;
-	charon->bus->ike_derived_keys(charon->bus, sk_ei, sk_er, chunk_empty,
-								  chunk_empty);
-
-failure:
-	DESTROY_IF(aead_i);
-	DESTROY_IF(aead_r);
-	chunk_clear(&sk_ei);
-	chunk_clear(&sk_er);
-	return this->aead_in && this->aead_out;
+	return TRUE;
 }
 
 /**
- * Derive IKE keys for traditional encryption and MAC algorithms
+ * Create aead_t objects for traditional encryption and MAC algorithms, sets the
+ * length of key chunks
  */
-static bool derive_ike_traditional(private_keymat_v2_t *this, uint16_t enc_alg,
-					uint16_t enc_size, uint16_t int_alg, prf_plus_t *prf_plus)
+static bool create_ike_traditional(private_keymat_v2_t *this, uint16_t enc_alg,
+					uint16_t enc_size, uint16_t int_alg, chunk_t *sk_ai,
+					chunk_t *sk_ar, chunk_t *sk_ei, chunk_t *sk_er)
 {
-	crypter_t *crypter_i = NULL, *crypter_r = NULL;
-	signer_t *signer_i, *signer_r;
-	iv_gen_t *ivg_i, *ivg_r;
-	size_t key_size;
-	chunk_t sk_ei = chunk_empty, sk_er = chunk_empty,
-			sk_ai = chunk_empty, sk_ar = chunk_empty;
+	crypter_t *crypter_i = NULL, *crypter_o = NULL;
+	signer_t *signer_i, *signer_o;
+	iv_gen_t *ivg_i, *ivg_o;
 
 	signer_i = lib->crypto->create_signer(lib->crypto, int_alg);
-	signer_r = lib->crypto->create_signer(lib->crypto, int_alg);
-	crypter_i = lib->crypto->create_crypter(lib->crypto, enc_alg, enc_size / 8);
-	crypter_r = lib->crypto->create_crypter(lib->crypto, enc_alg, enc_size / 8);
-	if (signer_i == NULL || signer_r == NULL)
+	signer_o = lib->crypto->create_signer(lib->crypto, int_alg);
+	if (!signer_i || !signer_o)
 	{
 		DBG1(DBG_IKE, "%N %N not supported!",
 			 transform_type_names, INTEGRITY_ALGORITHM,
 			 integrity_algorithm_names, int_alg);
 		goto failure;
 	}
-	if (crypter_i == NULL || crypter_r == NULL)
+	crypter_i = lib->crypto->create_crypter(lib->crypto, enc_alg, enc_size / 8);
+	crypter_o = lib->crypto->create_crypter(lib->crypto, enc_alg, enc_size / 8);
+	if (!crypter_i || !crypter_o)
 	{
 		DBG1(DBG_IKE, "%N %N (key size %d) not supported!",
 			 transform_type_names, ENCRYPTION_ALGORITHM,
 			 encryption_algorithm_names, enc_alg, enc_size);
 		goto failure;
 	}
-
-	/* SK_ai/SK_ar used for integrity protection */
-	key_size = signer_i->get_key_size(signer_i);
-
-	if (!prf_plus->allocate_bytes(prf_plus, key_size, &sk_ai))
+	sk_ai->len = signer_i->get_key_size(signer_i);
+	sk_ar->len = signer_o->get_key_size(signer_o);
+	if (sk_ai->len != sk_ar->len)
 	{
 		goto failure;
 	}
-	DBG4(DBG_IKE, "Sk_ai secret %B", &sk_ai);
-	if (!signer_i->set_key(signer_i, sk_ai))
+	sk_ei->len = crypter_i->get_key_size(crypter_i);
+	sk_er->len = crypter_o->get_key_size(crypter_o);
+	if (sk_ei->len != sk_er->len)
 	{
 		goto failure;
 	}
-
-	if (!prf_plus->allocate_bytes(prf_plus, key_size, &sk_ar))
-	{
-		goto failure;
-	}
-	DBG4(DBG_IKE, "Sk_ar secret %B", &sk_ar);
-	if (!signer_r->set_key(signer_r, sk_ar))
-	{
-		goto failure;
-	}
-
-	/* SK_ei/SK_er used for encryption */
-	key_size = crypter_i->get_key_size(crypter_i);
-
-	if (!prf_plus->allocate_bytes(prf_plus, key_size, &sk_ei))
-	{
-		goto failure;
-	}
-	DBG4(DBG_IKE, "Sk_ei secret %B", &sk_ei);
-	if (!crypter_i->set_key(crypter_i, sk_ei))
-	{
-		goto failure;
-	}
-
-	if (!prf_plus->allocate_bytes(prf_plus, key_size, &sk_er))
-	{
-		goto failure;
-	}
-	DBG4(DBG_IKE, "Sk_er secret %B", &sk_er);
-	if (!crypter_r->set_key(crypter_r, sk_er))
-	{
-		goto failure;
-	}
-
 	ivg_i = iv_gen_create_for_alg(enc_alg);
-	ivg_r = iv_gen_create_for_alg(enc_alg);
-	if (!ivg_i || !ivg_r)
+	ivg_o = iv_gen_create_for_alg(enc_alg);
+	if (!ivg_i || !ivg_o)
 	{
 		goto failure;
 	}
-	if (this->initiator)
-	{
-		this->aead_in = aead_create(crypter_r, signer_r, ivg_r);
-		this->aead_out = aead_create(crypter_i, signer_i, ivg_i);
-	}
-	else
-	{
-		this->aead_in = aead_create(crypter_i, signer_i, ivg_i);
-		this->aead_out = aead_create(crypter_r, signer_r, ivg_r);
-	}
-	signer_i = signer_r = NULL;
-	crypter_i = crypter_r = NULL;
-	charon->bus->ike_derived_keys(charon->bus, sk_ei, sk_er, sk_ai, sk_ar);
+	this->aead_in = aead_create(crypter_i, signer_i, ivg_i);
+	this->aead_out = aead_create(crypter_o, signer_o, ivg_o);
+	signer_i = signer_o = NULL;
+	crypter_i = crypter_o = NULL;
 
 failure:
-	chunk_clear(&sk_ai);
-	chunk_clear(&sk_ar);
-	chunk_clear(&sk_ei);
-	chunk_clear(&sk_er);
 	DESTROY_IF(signer_i);
-	DESTROY_IF(signer_r);
+	DESTROY_IF(signer_o);
 	DESTROY_IF(crypter_i);
-	DESTROY_IF(crypter_r);
+	DESTROY_IF(crypter_o);
 	return this->aead_in && this->aead_out;
 }
 
+/**
+ * Set keys on AEAD objects
+ */
+static bool set_aead_keys(private_keymat_v2_t *this, uint16_t enc_alg,
+						  chunk_t sk_ai, chunk_t sk_ar,
+						  chunk_t sk_ei, chunk_t sk_er)
+{
+	aead_t *aead_i, *aead_r;
+	chunk_t sk_i, sk_r;
+	bool success;
+
+	aead_i = this->initiator ? this->aead_out : this->aead_in;
+	aead_r = this->initiator ? this->aead_in : this->aead_out;
+
+	sk_i = chunk_cat("cc", sk_ai, sk_ei);
+	sk_r = chunk_cat("cc", sk_ar, sk_er);
+
+	success = aead_i->set_key(aead_i, sk_i) &&
+			  aead_r->set_key(aead_r, sk_r);
+
+	chunk_clear(&sk_i);
+	chunk_clear(&sk_r);
+	return success;
+}
+
 METHOD(keymat_v2_t, derive_ike_keys, bool,
 	private_keymat_v2_t *this, proposal_t *proposal, diffie_hellman_t *dh,
 	chunk_t nonce_i, chunk_t nonce_r, ike_sa_id_t *id,
 	pseudo_random_function_t rekey_function, chunk_t rekey_skd)
 {
-	chunk_t skeyseed = chunk_empty, key, secret, full_nonce, fixed_nonce;
-	chunk_t prf_plus_seed, spi_i, spi_r;
-	prf_plus_t *prf_plus = NULL;
-	uint16_t alg, key_size, int_alg;
-	prf_t *rekey_prf = NULL;
+	chunk_t skeyseed = chunk_empty, secret, full_nonce, fixed_nonce;
+	chunk_t prf_plus_seed, spi_i, spi_r, keymat = chunk_empty;
+	chunk_t sk_ei = chunk_empty, sk_er = chunk_empty;
+	chunk_t sk_ai = chunk_empty, sk_ar = chunk_empty, sk_pi, sk_pr;
+	kdf_t *prf = NULL, *prf_plus = NULL;
+	uint16_t prf_alg, key_size, enc_alg, enc_size, int_alg;
+	bool success = FALSE;
 
 	spi_i = chunk_alloca(sizeof(uint64_t));
 	spi_r = chunk_alloca(sizeof(uint64_t));
 
-	if (!dh->get_shared_secret(dh, &secret))
+	/* create SA's general purpose PRF first, we may use it here */
+	if (!proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &prf_alg,
+								 NULL))
 	{
+		DBG1(DBG_IKE, "no %N selected",
+			 transform_type_names, PSEUDO_RANDOM_FUNCTION);
 		return FALSE;
 	}
+	this->prf_alg = prf_alg;
+	this->prf = lib->crypto->create_prf(lib->crypto, this->prf_alg);
+	if (!this->prf)
+	{
+		DBG1(DBG_IKE, "%N %N not supported!", transform_type_names,
+			 PSEUDO_RANDOM_FUNCTION, pseudo_random_function_names,
+			 this->prf_alg);
+		return FALSE;
+	}
+	key_size = this->prf->get_key_size(this->prf);
 
-	/* Create SAs general purpose PRF first, we may use it here */
-	if (!proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &alg, NULL))
+	/* create SA's AEAD instances to determine key sizes */
+	if (!proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &enc_alg,
+								 &enc_size))
 	{
-		DBG1(DBG_IKE, "no %N selected",
-			 transform_type_names, PSEUDO_RANDOM_FUNCTION);
-		chunk_clear(&secret);
+		DBG1(DBG_IKE, "no %N selected", transform_type_names,
+			 ENCRYPTION_ALGORITHM);
 		return FALSE;
 	}
-	this->prf_alg = alg;
-	this->prf = lib->crypto->create_prf(lib->crypto, alg);
-	if (this->prf == NULL)
+	if (!encryption_algorithm_is_aead(enc_alg))
+	{
+		if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &int_alg,
+									 NULL))
+		{
+			DBG1(DBG_IKE, "no %N selected", transform_type_names,
+				 INTEGRITY_ALGORITHM);
+			return FALSE;
+		}
+		if (!create_ike_traditional(this, enc_alg, enc_size, int_alg,
+									&sk_ai, &sk_ar, &sk_ei, &sk_er))
+		{
+			return FALSE;
+		}
+	}
+	else if (!create_ike_aead(this, enc_alg, enc_size, &sk_ei, &sk_er))
+	{
+		return FALSE;
+	}
+
+	if (!dh->get_shared_secret(dh, &secret))
 	{
-		DBG1(DBG_IKE, "%N %N not supported!",
-			 transform_type_names, PSEUDO_RANDOM_FUNCTION,
-			 pseudo_random_function_names, alg);
-		chunk_clear(&secret);
 		return FALSE;
 	}
 	DBG4(DBG_IKE, "shared Diffie Hellman secret %B", &secret);
@@ -340,7 +307,7 @@ METHOD(keymat_v2_t, derive_ike_keys, bool,
 	full_nonce = chunk_cat("cc", nonce_i, nonce_r);
 	/* but the PRF may need a fixed key which only uses the first bytes of
 	 * the nonces. */
-	switch (alg)
+	switch (prf_alg)
 	{
 		case PRF_AES128_CMAC:
 			/* while variable keys may be used according to RFC 4615, RFC 7296
@@ -352,9 +319,8 @@ METHOD(keymat_v2_t, derive_ike_keys, bool,
 		case PRF_CAMELLIA128_XCBC:
 			/* draft-kanno-ipsecme-camellia-xcbc refers to rfc 4434, we
 			 * assume fixed key length. */
-			key_size = this->prf->get_key_size(this->prf)/2;
-			nonce_i.len = min(nonce_i.len, key_size);
-			nonce_r.len = min(nonce_r.len, key_size);
+			nonce_i.len = min(nonce_i.len, key_size / 2);
+			nonce_r.len = min(nonce_r.len, key_size / 2);
 			break;
 		default:
 			/* all other algorithms use variable key length, full nonce */
@@ -372,19 +338,22 @@ METHOD(keymat_v2_t, derive_ike_keys, bool,
 	if (rekey_function == PRF_UNDEFINED) /* not rekeying */
 	{
 		/* SKEYSEED = prf(Ni | Nr, g^ir) */
-		if (this->prf->set_key(this->prf, fixed_nonce) &&
-			this->prf->allocate_bytes(this->prf, secret, &skeyseed) &&
-			this->prf->set_key(this->prf, skeyseed))
+		prf = lib->crypto->create_kdf(lib->crypto, KDF_PRF, this->prf_alg);
+		if (prf &&
+			prf->set_param(prf, KDF_PARAM_KEY, fixed_nonce) &&
+			prf->set_param(prf, KDF_PARAM_SALT, secret) &&
+			prf->allocate_bytes(prf, 0, &skeyseed))
 		{
-			prf_plus = prf_plus_create(this->prf, TRUE, prf_plus_seed);
+			prf_plus = lib->crypto->create_kdf(lib->crypto, KDF_PRF_PLUS,
+											   this->prf_alg);
 		}
 	}
 	else
 	{
 		/* SKEYSEED = prf(SK_d (old), [g^ir (new)] | Ni | Nr)
 		 * use OLD SAs PRF functions for both prf_plus and prf */
-		rekey_prf = lib->crypto->create_prf(lib->crypto, rekey_function);
-		if (!rekey_prf)
+		prf = lib->crypto->create_kdf(lib->crypto, KDF_PRF, rekey_function);
+		if (!prf)
 		{
 			DBG1(DBG_IKE, "PRF of old SA %N not supported!",
 				 pseudo_random_function_names, rekey_function);
@@ -394,113 +363,98 @@ METHOD(keymat_v2_t, derive_ike_keys, bool,
 			chunk_clear(&prf_plus_seed);
 			return FALSE;
 		}
-		secret = chunk_cat("mc", secret, full_nonce);
-		if (rekey_prf->set_key(rekey_prf, rekey_skd) &&
-			rekey_prf->allocate_bytes(rekey_prf, secret, &skeyseed) &&
-			rekey_prf->set_key(rekey_prf, skeyseed))
+		secret = chunk_cat("sc", secret, full_nonce);
+		if (prf->set_param(prf, KDF_PARAM_KEY, rekey_skd) &&
+			prf->set_param(prf, KDF_PARAM_SALT, secret) &&
+			prf->allocate_bytes(prf, 0, &skeyseed))
 		{
-			prf_plus = prf_plus_create(rekey_prf, TRUE, prf_plus_seed);
+			prf_plus = lib->crypto->create_kdf(lib->crypto, KDF_PRF_PLUS,
+											   rekey_function);
 		}
 	}
 	DBG4(DBG_IKE, "SKEYSEED %B", &skeyseed);
-
-	chunk_clear(&skeyseed);
 	chunk_clear(&secret);
 	chunk_free(&full_nonce);
 	chunk_free(&fixed_nonce);
-	chunk_clear(&prf_plus_seed);
+	DESTROY_IF(prf);
 
-	if (!prf_plus)
+	if (prf_plus &&
+		(!prf_plus->set_param(prf_plus, KDF_PARAM_KEY, skeyseed) ||
+		 !prf_plus->set_param(prf_plus, KDF_PARAM_SALT, prf_plus_seed)))
 	{
-		goto failure;
+		prf_plus->destroy(prf_plus);
+		prf_plus = NULL;
 	}
+	chunk_clear(&skeyseed);
+	chunk_clear(&prf_plus_seed);
 
-	/* KEYMAT = SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr */
-
-	/* SK_d is used for generating CHILD_SA key mat => store for later use */
-	key_size = this->prf->get_key_size(this->prf);
-	if (!prf_plus->allocate_bytes(prf_plus, key_size, &this->skd))
+	if (!prf_plus)
 	{
 		goto failure;
 	}
-	DBG4(DBG_IKE, "Sk_d secret %B", &this->skd);
 
-	if (!proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &alg, &key_size))
+	/* KEYMAT = SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr
+	 *
+	 * SK_d, SK_pi and SK_pr have the size of the PRF key
+	 */
+	keymat.len = 3 * key_size + sk_ai.len + sk_ar.len + sk_ei.len + sk_er.len;
+	if (!prf_plus->allocate_bytes(prf_plus, keymat.len, &keymat))
 	{
-		DBG1(DBG_IKE, "no %N selected",
-			 transform_type_names, ENCRYPTION_ALGORITHM);
 		goto failure;
 	}
+	chunk_split(keymat, "ammmmaa", key_size, &this->skd, sk_ai.len, &sk_ai,
+				sk_ar.len, &sk_ar, sk_ei.len, &sk_ei, sk_er.len, &sk_er,
+				key_size, &sk_pi, key_size, &sk_pr);
 
-	if (encryption_algorithm_is_aead(alg))
-	{
-		if (!derive_ike_aead(this, alg, key_size, prf_plus))
-		{
-			goto failure;
-		}
-	}
-	else
-	{
-		if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM,
-									 &int_alg, NULL))
-		{
-			DBG1(DBG_IKE, "no %N selected",
-				 transform_type_names, INTEGRITY_ALGORITHM);
-			goto failure;
-		}
-		if (!derive_ike_traditional(this, alg, key_size, int_alg, prf_plus))
-		{
-			goto failure;
-		}
-	}
-
-	/* SK_pi/SK_pr used for authentication => stored for later */
-	key_size = this->prf->get_key_size(this->prf);
-	if (!prf_plus->allocate_bytes(prf_plus, key_size, &key))
-	{
-		goto failure;
-	}
-	DBG4(DBG_IKE, "Sk_pi secret %B", &key);
-	if (this->initiator)
-	{
-		this->skp_build = key;
-	}
-	else
-	{
-		this->skp_verify = key;
+	/* SK_d is used for generating CHILD_SA key mat => store for later use */
+	DBG4(DBG_IKE, "Sk_d secret %B", &this->skd);
+	if (!encryption_algorithm_is_aead(enc_alg))
+	{	/* SK_ai/SK_ar used for integrity protection */
+		DBG4(DBG_IKE, "Sk_ai secret %B", &sk_ai);
+		DBG4(DBG_IKE, "Sk_ar secret %B", &sk_ar);
 	}
-	if (!prf_plus->allocate_bytes(prf_plus, key_size, &key))
+	/* SK_ei/SK_er used for encryption */
+	DBG4(DBG_IKE, "Sk_ei secret %B", &sk_ei);
+	DBG4(DBG_IKE, "Sk_er secret %B", &sk_er);
+	if (!set_aead_keys(this, enc_alg, sk_ai, sk_ar, sk_ei, sk_er))
 	{
 		goto failure;
 	}
-	DBG4(DBG_IKE, "Sk_pr secret %B", &key);
+	/* SK_pi/SK_pr used for authentication => stored for later */
+	DBG4(DBG_IKE, "Sk_pi secret %B", &sk_pi);
+	DBG4(DBG_IKE, "Sk_pr secret %B", &sk_pr);
 	if (this->initiator)
 	{
-		this->skp_verify = key;
+		this->skp_build = sk_pi;
+		this->skp_verify = sk_pr;
 	}
 	else
 	{
-		this->skp_build = key;
+		this->skp_build = sk_pr;
+		this->skp_verify = sk_pi;
 	}
+	charon->bus->ike_derived_keys(charon->bus, this->skd, sk_ai, sk_ar,
+								  sk_ei, sk_er, sk_pi, sk_pr);
+	success = TRUE;
 
-	/* all done, prf_plus not needed anymore */
 failure:
+	chunk_clear(&keymat);
 	DESTROY_IF(prf_plus);
-	DESTROY_IF(rekey_prf);
-
-	return this->skp_build.len && this->skp_verify.len;
+	return success;
 }
 
 /**
- * Derives a key from the given key and a PRF that was initialized with a PPK
+ * Derives a new key from the given PPK and old key
  */
-static bool derive_ppk_key(prf_t *prf, char *name, chunk_t key,
-						   chunk_t *new_key)
+static bool derive_ppk_key(private_keymat_v2_t *this, char *name, chunk_t ppk,
+						   chunk_t key, chunk_t *new_key)
 {
-	prf_plus_t *prf_plus;
+	kdf_t *prf_plus;
 
-	prf_plus = prf_plus_create(prf, TRUE, key);
+	prf_plus = lib->crypto->create_kdf(lib->crypto, KDF_PRF_PLUS, this->prf_alg);
 	if (!prf_plus ||
+		!prf_plus->set_param(prf_plus, KDF_PARAM_KEY, ppk) ||
+		!prf_plus->set_param(prf_plus, KDF_PARAM_SALT, key) ||
 		!prf_plus->allocate_bytes(prf_plus, key.len, new_key))
 	{
 		DBG1(DBG_IKE, "unable to derive %s with PPK", name);
@@ -511,20 +465,6 @@ static bool derive_ppk_key(prf_t *prf, char *name, chunk_t key,
 	return TRUE;
 }
 
-/**
- * Use the given PPK to derive a new SK_pi/r
- */
-static bool derive_skp_ppk(private_keymat_v2_t *this, chunk_t ppk, chunk_t skp,
-						   chunk_t *new_skp)
-{
-	if (!this->prf->set_key(this->prf, ppk))
-	{
-		DBG1(DBG_IKE, "unable to set PPK in PRF");
-		return FALSE;
-	}
-	return derive_ppk_key(this->prf, "SK_p", skp, new_skp);
-}
-
 METHOD(keymat_v2_t, derive_ike_keys_ppk, bool,
 	private_keymat_v2_t *this, chunk_t ppk)
 {
@@ -549,14 +489,9 @@ METHOD(keymat_v2_t, derive_ike_keys_ppk, bool,
 
 	DBG4(DBG_IKE, "derive keys using PPK %B", &ppk);
 
-	if (!this->prf->set_key(this->prf, ppk))
-	{
-		DBG1(DBG_IKE, "unable to set PPK in PRF");
-		return FALSE;
-	}
-	if (!derive_ppk_key(this->prf, "Sk_d", this->skd, &skd) ||
-		!derive_ppk_key(this->prf, "Sk_pi", *skpi, &new_skpi) ||
-		!derive_ppk_key(this->prf, "Sk_pr", *skpr, &new_skpr))
+	if (!derive_ppk_key(this, "Sk_d", ppk, this->skd, &skd) ||
+		!derive_ppk_key(this, "Sk_pi", ppk, *skpi, &new_skpi) ||
+		!derive_ppk_key(this, "Sk_pr", ppk, *skpr, &new_skpr))
 	{
 		chunk_clear(&skd);
 		chunk_clear(&new_skpi);
@@ -584,8 +519,8 @@ METHOD(keymat_v2_t, derive_child_keys, bool,
 	chunk_t *encr_r, chunk_t *integ_r)
 {
 	uint16_t enc_alg, int_alg, enc_size = 0, int_size = 0;
-	chunk_t seed, secret = chunk_empty;
-	prf_plus_t *prf_plus;
+	chunk_t seed, secret = chunk_empty, keymat = chunk_empty;
+	kdf_t *prf_plus;
 
 	if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM,
 								&enc_alg, &enc_size))
@@ -651,11 +586,6 @@ METHOD(keymat_v2_t, derive_child_keys, bool,
 		int_size /= 8;
 	}
 
-	if (!this->prf->set_key(this->prf, this->skd))
-	{
-		return FALSE;
-	}
-
 	if (dh)
 	{
 		if (!dh->get_shared_secret(dh, &secret))
@@ -667,30 +597,30 @@ METHOD(keymat_v2_t, derive_child_keys, bool,
 	seed = chunk_cata("scc", secret, nonce_i, nonce_r);
 	DBG4(DBG_CHD, "seed %B", &seed);
 
-	prf_plus = prf_plus_create(this->prf, TRUE, seed);
-	memwipe(seed.ptr, seed.len);
-
-	if (!prf_plus)
+	prf_plus = lib->crypto->create_kdf(lib->crypto, KDF_PRF_PLUS, this->prf_alg);
+	if (!prf_plus ||
+		!prf_plus->set_param(prf_plus, KDF_PARAM_KEY, this->skd) ||
+		!prf_plus->set_param(prf_plus, KDF_PARAM_SALT, seed))
 	{
+		DESTROY_IF(prf_plus);
+		memwipe(seed.ptr, seed.len);
 		return FALSE;
 	}
+	memwipe(seed.ptr, seed.len);
 
 	*encr_i = *integ_i = *encr_r = *integ_r = chunk_empty;
-	if (!prf_plus->allocate_bytes(prf_plus, enc_size, encr_i) ||
-		!prf_plus->allocate_bytes(prf_plus, int_size, integ_i) ||
-		!prf_plus->allocate_bytes(prf_plus, enc_size, encr_r) ||
-		!prf_plus->allocate_bytes(prf_plus, int_size, integ_r))
-	{
-		chunk_free(encr_i);
-		chunk_free(integ_i);
-		chunk_free(encr_r);
-		chunk_free(integ_r);
+	keymat.len = 2 * enc_size + 2 * int_size;
+	if (!prf_plus->allocate_bytes(prf_plus, keymat.len, &keymat))
+	{
 		prf_plus->destroy(prf_plus);
 		return FALSE;
 	}
-
 	prf_plus->destroy(prf_plus);
 
+	chunk_split(keymat, "aaaa", enc_size, encr_i, int_size, integ_i,
+				enc_size, encr_r, int_size, integ_r);
+	chunk_clear(&keymat);
+
 	if (enc_size)
 	{
 		DBG4(DBG_CHD, "encryption initiator key %B", encr_i);
@@ -730,7 +660,7 @@ METHOD(keymat_v2_t, get_auth_octets, bool,
 	if (ppk.ptr)
 	{
 		DBG4(DBG_IKE, "PPK %B", &ppk);
-		if (!derive_skp_ppk(this, ppk, skp, &skp_ppk))
+		if (!derive_ppk_key(this, "SK_p", ppk, skp, &skp_ppk))
 		{
 			return FALSE;
 		}
@@ -776,7 +706,7 @@ METHOD(keymat_v2_t, get_psk_sig, bool,
 		secret = verify ? this->skp_verify : this->skp_build;
 		if (ppk.ptr)
 		{
-			if (!derive_skp_ppk(this, ppk, secret, &skp_ppk))
+			if (!derive_ppk_key(this, "SK_p", ppk, secret, &skp_ppk))
 			{
 				return FALSE;
 			}
diff --git a/src/libcharon/tests/utils/mock_dh.c b/src/libcharon/tests/utils/mock_dh.c
index 153bf1166455..f11679f0b603 100644
--- a/src/libcharon/tests/utils/mock_dh.c
+++ b/src/libcharon/tests/utils/mock_dh.c
@@ -18,6 +18,13 @@
 
 typedef struct private_diffie_hellman_t private_diffie_hellman_t;
 
+/** Mock DH public and shared key */
+static chunk_t mock_key = chunk_from_chars(
+									0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,
+									0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,
+									0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,
+									0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08);
+
 /**
  * Private data
  */
@@ -37,7 +44,7 @@ struct private_diffie_hellman_t {
 METHOD(diffie_hellman_t, get_my_public_value, bool,
 	private_diffie_hellman_t *this, chunk_t *value)
 {
-	*value = chunk_empty;
+	*value = chunk_clone(mock_key);
 	return TRUE;
 }
 
@@ -50,7 +57,7 @@ METHOD(diffie_hellman_t, set_other_public_value, bool,
 METHOD(diffie_hellman_t, get_shared_secret, bool,
 	private_diffie_hellman_t *this, chunk_t *secret)
 {
-	*secret = chunk_empty;
+	*secret = chunk_clone(mock_key);
 	return TRUE;
 }
 
diff --git a/src/libstrongswan/Android.mk b/src/libstrongswan/Android.mk
index 97a9f68704c5..cf05f3ea25e1 100644
--- a/src/libstrongswan/Android.mk
+++ b/src/libstrongswan/Android.mk
@@ -13,11 +13,11 @@ crypto/hashers/hash_algorithm_set.c crypto/proposal/proposal.c \
 crypto/proposal/proposal_keywords.c crypto/proposal/proposal_keywords_static.c \
 crypto/prfs/prf.c crypto/prfs/mac_prf.c crypto/pkcs5.c \
 crypto/rngs/rng.c crypto/rngs/rng_tester.c \
-crypto/prf_plus.c crypto/signers/signer.c \
+crypto/signers/signer.c \
 crypto/signers/mac_signer.c crypto/crypto_factory.c crypto/crypto_tester.c \
 crypto/diffie_hellman.c crypto/aead.c crypto/transform.c \
 crypto/iv/iv_gen.c crypto/iv/iv_gen_rand.c crypto/iv/iv_gen_seq.c \
-crypto/iv/iv_gen_null.c \
+crypto/iv/iv_gen_null.c crypto/kdfs/kdf.c \
 crypto/xofs/xof.c crypto/xofs/xof_bitspender.c \
 credentials/credential_factory.c credentials/builder.c \
 credentials/cred_encoding.c credentials/keys/private_key.c \
diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am
index fda7463edd00..441f969329ad 100644
--- a/src/libstrongswan/Makefile.am
+++ b/src/libstrongswan/Makefile.am
@@ -11,11 +11,11 @@ crypto/hashers/hash_algorithm_set.c crypto/proposal/proposal.c \
 crypto/proposal/proposal_keywords.c crypto/proposal/proposal_keywords_static.c \
 crypto/prfs/prf.c crypto/prfs/mac_prf.c crypto/pkcs5.c \
 crypto/rngs/rng.c crypto/rngs/rng_tester.c \
-crypto/prf_plus.c crypto/signers/signer.c \
+crypto/signers/signer.c \
 crypto/signers/mac_signer.c crypto/crypto_factory.c crypto/crypto_tester.c \
 crypto/diffie_hellman.c crypto/aead.c crypto/transform.c \
 crypto/iv/iv_gen.c crypto/iv/iv_gen_rand.c crypto/iv/iv_gen_seq.c \
-crypto/iv/iv_gen_null.c \
+crypto/iv/iv_gen_null.c crypto/kdfs/kdf.c \
 crypto/xofs/xof.c crypto/xofs/xof_bitspender.c \
 credentials/credential_factory.c credentials/builder.c \
 credentials/cred_encoding.c credentials/keys/private_key.c \
@@ -75,11 +75,12 @@ crypto/hashers/hash_algorithm_set.h crypto/mac.h crypto/proposal/proposal.h \
 crypto/proposal/proposal_keywords.h crypto/proposal/proposal_keywords_static.h \
 crypto/rngs/rng.h crypto/rngs/rng_tester.h \
 crypto/prfs/prf.h crypto/prfs/mac_prf.h crypto/nonce_gen.h \
-crypto/prf_plus.h crypto/signers/signer.h crypto/signers/mac_signer.h \
+crypto/signers/signer.h crypto/signers/mac_signer.h \
 crypto/crypto_factory.h crypto/crypto_tester.h crypto/diffie_hellman.h \
 crypto/aead.h crypto/transform.h crypto/pkcs5.h crypto/iv/iv_gen.h \
 crypto/iv/iv_gen_rand.h crypto/iv/iv_gen_seq.h crypto/iv/iv_gen_null.h \
 crypto/xofs/xof.h crypto/xofs/xof_bitspender.h crypto/xofs/mgf1.h \
+crypto/kdfs/kdf.h \
 credentials/credential_factory.h credentials/builder.h \
 credentials/cred_encoding.h credentials/keys/private_key.h \
 credentials/keys/public_key.h credentials/keys/shared_key.h \
diff --git a/src/libstrongswan/crypto/crypto_factory.c b/src/libstrongswan/crypto/crypto_factory.c
index 62b9806966fa..c4c045175c79 100644
--- a/src/libstrongswan/crypto/crypto_factory.c
+++ b/src/libstrongswan/crypto/crypto_factory.c
@@ -53,6 +53,7 @@ struct entry_t {
 		hasher_constructor_t create_hasher;
 		prf_constructor_t create_prf;
 		xof_constructor_t create_xof;
+		kdf_constructor_t create_kdf;
 		drbg_constructor_t create_drbg;
 		rng_constructor_t create_rng;
 		nonce_gen_constructor_t create_nonce_gen;
@@ -103,6 +104,11 @@ struct private_crypto_factory_t {
 	 */
 	linked_list_t *xofs;
 
+	/**
+	 * registered kdfs, as entry_t
+	 */
+	linked_list_t *kdfs;
+
 	/**
 	 * registered drbgs, as entry_t
 	 */
@@ -348,6 +354,48 @@ METHOD(crypto_factory_t, create_xof, xof_t*,
 	return xof;
 }
 
+METHOD(crypto_factory_t, create_kdf, kdf_t*,
+	private_crypto_factory_t *this, key_derivation_function_t algo, ...)
+{
+	enumerator_t *enumerator;
+	entry_t *entry;
+	va_list args;
+	kdf_t *kdf = NULL;
+
+	this->lock->read_lock(this->lock);
+	enumerator = this->kdfs->create_enumerator(this->kdfs);
+	while (enumerator->enumerate(enumerator, &entry))
+	{
+		if (entry->algo == algo)
+		{
+			if (this->test_on_create)
+			{
+				kdf_test_args_t test_args = {};
+
+				va_start(test_args.args, algo);
+				if (!this->tester->test_kdf(this->tester, algo,
+											entry->create_kdf, &test_args, NULL,
+											default_plugin_name))
+				{
+					va_end(test_args.args);
+					continue;
+				}
+				va_end(test_args.args);
+			}
+			va_start(args, algo);
+			kdf = entry->create_kdf(algo, args);
+			va_end(args);
+			if (kdf)
+			{
+				break;
+			}
+		}
+	}
+	enumerator->destroy(enumerator);
+	this->lock->unlock(this->lock);
+	return kdf;
+}
+
 METHOD(crypto_factory_t, create_drbg, drbg_t*,
 	private_crypto_factory_t *this, drbg_type_t type, uint32_t strength,
 	rng_t *entropy, chunk_t personalization_str)
@@ -749,6 +797,43 @@ METHOD(crypto_factory_t, remove_xof, void,
 	this->lock->unlock(this->lock);
 }
 
+METHOD(crypto_factory_t, add_kdf, bool,
+	private_crypto_factory_t *this, key_derivation_function_t algo,
+	const char *plugin_name, kdf_constructor_t create)
+{
+	u_int speed = 0;
+
+	if (!this->test_on_add ||
+		this->tester->test_kdf(this->tester, algo, create, NULL,
+							   this->bench ? &speed : NULL, plugin_name))
+	{
+		add_entry(this, this->kdfs, algo, plugin_name, 0, create);
+		return TRUE;
+	}
+	this->test_failures++;
+	return FALSE;
+}
+
+METHOD(crypto_factory_t, remove_kdf, void,
+	private_crypto_factory_t *this, kdf_constructor_t create)
+{
+	entry_t *entry;
+	enumerator_t *enumerator;
+
+	this->lock->write_lock(this->lock);
+	enumerator = this->kdfs->create_enumerator(this->kdfs);
+	while (enumerator->enumerate(enumerator, &entry))
+	{
+		if (entry->create_kdf == create)
+		{
+			this->kdfs->remove_at(this->kdfs, enumerator);
+			free(entry);
+		}
+	}
+	enumerator->destroy(enumerator);
+	this->lock->unlock(this->lock);
+}
+
 METHOD(crypto_factory_t, add_drbg, bool,
 	private_crypto_factory_t *this, drbg_type_t type,
 	const char *plugin_name, drbg_constructor_t create)
@@ -1058,6 +1143,30 @@ METHOD(crypto_factory_t, create_xof_enumerator, enumerator_t*,
 	return create_enumerator(this, this->xofs, xof_filter);
 }
 
+CALLBACK(kdf_filter, bool,
+	void *n, enumerator_t *orig, va_list args)
+{
+	entry_t *entry;
+	key_derivation_function_t *algo;
+	const char **plugin_name;
+
+	VA_ARGS_VGET(args, algo, plugin_name);
+
+	if (orig->enumerate(orig, &entry))
+	{
+		*algo = entry->algo;
+		*plugin_name = entry->plugin_name;
+		return TRUE;
+	}
+	return FALSE;
+}
+
+METHOD(crypto_factory_t, create_kdf_enumerator, enumerator_t*,
+	private_crypto_factory_t *this)
+{
+	return create_enumerator(this, this->kdfs, kdf_filter);
+}
+
 CALLBACK(drbg_filter, bool,
 	void *n, enumerator_t *orig, va_list args)
 {
@@ -1169,6 +1278,8 @@ METHOD(crypto_factory_t, add_test_vector, void,
 			return this->tester->add_prf_vector(this->tester, vector);
 		case EXTENDED_OUTPUT_FUNCTION:
 			return this->tester->add_xof_vector(this->tester, vector);
+		case KEY_DERIVATION_FUNCTION:
+			return this->tester->add_kdf_vector(this->tester, vector);
 		case DETERMINISTIC_RANDOM_BIT_GENERATOR:
 			return this->tester->add_drbg_vector(this->tester, vector);
 		case RANDOM_NUMBER_GENERATOR:
@@ -1232,6 +1343,10 @@ METHOD(enumerator_t, verify_enumerate, bool,
 			*valid = this->tester->test_xof(this->tester, entry->algo,
 							entry->create_xof, NULL, entry->plugin_name);
 			break;
+		case KEY_DERIVATION_FUNCTION:
+			*valid = this->tester->test_kdf(this->tester, entry->algo,
+							entry->create_kdf, NULL, NULL, entry->plugin_name);
+			break;
 		case DETERMINISTIC_RANDOM_BIT_GENERATOR:
 			*valid = this->tester->test_drbg(this->tester, entry->algo,
 							entry->create_drbg, NULL, entry->plugin_name);
@@ -1287,6 +1402,9 @@ METHOD(crypto_factory_t, create_verify_enumerator, enumerator_t*,
 		case EXTENDED_OUTPUT_FUNCTION:
 			inner = this->xofs->create_enumerator(this->xofs);
 			break;
+		case KEY_DERIVATION_FUNCTION:
+			inner = this->kdfs->create_enumerator(this->kdfs);
+			break;
 		case DETERMINISTIC_RANDOM_BIT_GENERATOR:
 			inner = this->drbgs->create_enumerator(this->drbgs);
 			break;
@@ -1323,6 +1441,7 @@ METHOD(crypto_factory_t, destroy, void,
 	this->hashers->destroy(this->hashers);
 	this->prfs->destroy(this->prfs);
 	this->xofs->destroy(this->xofs);
+	this->kdfs->destroy(this->kdfs);
 	this->drbgs->destroy(this->drbgs);
 	this->rngs->destroy(this->rngs);
 	this->nonce_gens->destroy(this->nonce_gens);
@@ -1347,6 +1466,7 @@ crypto_factory_t *crypto_factory_create()
 			.create_hasher = _create_hasher,
 			.create_prf = _create_prf,
 			.create_xof = _create_xof,
+			.create_kdf = _create_kdf,
 			.create_drbg = _create_drbg,
 			.create_rng = _create_rng,
 			.create_nonce_gen = _create_nonce_gen,
@@ -1363,6 +1483,8 @@ crypto_factory_t *crypto_factory_create()
 			.remove_prf = _remove_prf,
 			.add_xof = _add_xof,
 			.remove_xof = _remove_xof,
+			.add_kdf = _add_kdf,
+			.remove_kdf = _remove_kdf,
 			.add_drbg = _add_drbg,
 			.remove_drbg = _remove_drbg,
 			.add_rng = _add_rng,
@@ -1377,6 +1499,7 @@ crypto_factory_t *crypto_factory_create()
 			.create_hasher_enumerator = _create_hasher_enumerator,
 			.create_prf_enumerator = _create_prf_enumerator,
 			.create_xof_enumerator = _create_xof_enumerator,
+			.create_kdf_enumerator = _create_kdf_enumerator,
 			.create_drbg_enumerator = _create_drbg_enumerator,
 			.create_dh_enumerator = _create_dh_enumerator,
 			.create_rng_enumerator = _create_rng_enumerator,
@@ -1391,6 +1514,7 @@ crypto_factory_t *crypto_factory_create()
 		.hashers = linked_list_create(),
 		.prfs = linked_list_create(),
 		.xofs = linked_list_create(),
+		.kdfs = linked_list_create(),
 		.drbgs = linked_list_create(),
 		.rngs = linked_list_create(),
 		.nonce_gens = linked_list_create(),
diff --git a/src/libstrongswan/crypto/crypto_factory.h b/src/libstrongswan/crypto/crypto_factory.h
index 3901cce80544..3dd29a2ff8f5 100644
--- a/src/libstrongswan/crypto/crypto_factory.h
+++ b/src/libstrongswan/crypto/crypto_factory.h
@@ -33,6 +33,7 @@ typedef struct crypto_factory_t crypto_factory_t;
 #include <crypto/prfs/prf.h>
 #include <crypto/rngs/rng.h>
 #include <crypto/xofs/xof.h>
+#include <crypto/kdfs/kdf.h>
 #include <crypto/drbgs/drbg.h>
 #include <crypto/nonce_gen.h>
 #include <crypto/diffie_hellman.h>
@@ -70,6 +71,14 @@ typedef prf_t* (*prf_constructor_t)(pseudo_random_function_t algo);
  */
 typedef xof_t* (*xof_constructor_t)(ext_out_function_t algo);
 
+/**
+ * Constructor function for key derivation functions
+ *
+ * The additional arguments depend on the algorithm, see comments
+ * for key_derivation_function_t.
+ */
+typedef kdf_t* (*kdf_constructor_t)(key_derivation_function_t algo, va_list args);
+
 /**
  * Constructor function for deterministic random bit generators
  */
@@ -154,6 +163,20 @@ struct crypto_factory_t {
 	 */
 	xof_t* (*create_xof)(crypto_factory_t *this, ext_out_function_t algo);
 
+
+	/**
+	 * Create a key derivation function instance.
+	 *
+	 * Additional arguments depend on the KDF, please refer to the comments in
+	 * key_derivation_function_t.
+	 *
+	 * @param algo			KDF to create
+	 * @param ...			arguments depending on algo
+	 * @return				kdf_t instance, NULL if not supported
+	 */
+	kdf_t* (*create_kdf)(crypto_factory_t *this,
+						 key_derivation_function_t algo, ...);
+
 	/**
 	 * Create a deterministic random bit generator instance.
 	 *
@@ -305,6 +328,24 @@ struct crypto_factory_t {
 	 */
 	void (*remove_xof)(crypto_factory_t *this, xof_constructor_t create);
 
+	/**
+	 * Register a kdf constructor.
+	 *
+	 * @param algo			algorithm to constructor
+	 * @param plugin_name	plugin that registered this algorithm
+	 * @param create		constructor function for that algorithm
+	 * @return				TRUE if registered, FALSE if test vector failed
+	 */
+	bool (*add_kdf)(crypto_factory_t *this, key_derivation_function_t algo,
+					const char *plugin_name, kdf_constructor_t create);
+
+	/**
+	 * Unregister a kdf constructor.
+	 *
+	 * @param create		constructor function to unregister
+	 */
+	void (*remove_kdf)(crypto_factory_t *this, kdf_constructor_t create);
+
 	/**
 	 * Register a drbg constructor.
 	 *
@@ -419,6 +460,13 @@ struct crypto_factory_t {
 	 */
 	enumerator_t* (*create_xof_enumerator)(crypto_factory_t *this);
 
+	/**
+	 * Create an enumerator over all registered KDFs.
+	 *
+	 * @return				enumerator over key_derivation_function_t, plugin
+	 */
+	enumerator_t* (*create_kdf_enumerator)(crypto_factory_t *this);
+
 	/**
 	 * Create an enumerator over all registered DRBGs.
 	 *
diff --git a/src/libstrongswan/crypto/crypto_tester.c b/src/libstrongswan/crypto/crypto_tester.c
index 04ed2288c0d1..0247ce6f634d 100644
--- a/src/libstrongswan/crypto/crypto_tester.c
+++ b/src/libstrongswan/crypto/crypto_tester.c
@@ -69,6 +69,11 @@ struct private_crypto_tester_t {
 	 */
 	linked_list_t *xof;
 
+	/**
+	 * List of KDF test vectors
+	 */
+	linked_list_t *kdf;
+
 	/**
 	 * List of DRBG test vectors
 	 */
@@ -1186,6 +1191,211 @@ failure:
 	return !failed;
 }
 
+
+
+/**
+ * Create a KDF using the given arguments
+ */
+static kdf_t *create_kdf_args(kdf_constructor_t create,
+							  key_derivation_function_t alg, ...)
+{
+	va_list args;
+	kdf_t *kdf;
+
+	va_start(args, alg);
+	kdf = create(alg, args);
+	va_end(args);
+	return kdf;
+}
+
+/**
+ * Create a KDF using arguments from the given test vector
+ */
+static kdf_t *create_kdf_vector(kdf_constructor_t create,
+								key_derivation_function_t alg,
+								kdf_test_vector_t *vector)
+{
+	switch (alg)
+	{
+		case KDF_PRF:
+		case KDF_PRF_PLUS:
+			return create_kdf_args(create, alg, vector->arg.prf);
+		case KDF_UNDEFINED:
+			break;
+	}
+	return NULL;
+}
+
+/**
+ * Check if the given test vector applies to the passed arguments
+ */
+static bool kdf_vector_applies(key_derivation_function_t alg,
+							   kdf_test_args_t *args, kdf_test_vector_t *vector)
+{
+	bool applies = FALSE;
+
+	switch (alg)
+	{
+		case KDF_PRF:
+		case KDF_PRF_PLUS:
+		{
+			pseudo_random_function_t prf;
+			VA_ARGS_VGET(args->args, prf);
+			applies = (prf == vector->arg.prf);
+			break;
+		}
+		case KDF_UNDEFINED:
+			break;
+	}
+	return applies;
+}
+
+METHOD(crypto_tester_t, test_kdf, bool,
+	private_crypto_tester_t *this, key_derivation_function_t alg,
+	kdf_constructor_t create, kdf_test_args_t *args, u_int *speed,
+	const char *plugin_name)
+{
+	enumerator_t *enumerator;
+	kdf_test_vector_t *vector;
+	va_list copy;
+	bool failed = FALSE;
+	u_int tested = 0, construction_failed = 0;
+
+	enumerator = this->kdf->create_enumerator(this->kdf);
+	while (enumerator->enumerate(enumerator, &vector))
+	{
+		kdf_t *kdf;
+		chunk_t out = chunk_empty;
+
+		if (vector->alg != alg ||
+			(args && !kdf_vector_applies(alg, args, vector)))
+		{
+			continue;
+		}
+
+		tested++;
+		failed = TRUE;
+		if (args)
+		{
+			va_copy(copy, args->args);
+			kdf = create(alg, copy);
+			va_end(copy);
+		}
+		else
+		{
+			kdf = create_kdf_vector(create, alg, vector);
+		}
+		if (!kdf)
+		{
+			if (args)
+			{
+				DBG1(DBG_LIB, "disabled %N[%s]: creating instance failed",
+					 key_derivation_function_names, alg, plugin_name);
+				break;
+			}
+			/* while there could be a problem, the constructor might just not
+			 * be able to create an instance for this test vector, we check
+			 * for that at the end */
+			construction_failed++;
+			failed = FALSE;
+			continue;
+		}
+
+		if (vector->key.len &&
+			!kdf->set_param(kdf, KDF_PARAM_KEY, vector->key))
+		{
+			goto failure;
+		}
+		if (vector->salt.len &&
+			!kdf->set_param(kdf, KDF_PARAM_SALT, vector->salt))
+		{
+			goto failure;
+		}
+		if (kdf_has_fixed_output_length(alg))
+		{
+			if (kdf->get_length(kdf) != vector->out.len)
+			{
+				goto failure;
+			}
+		}
+		else if (kdf->get_length(kdf) != SIZE_MAX)
+		{
+			goto failure;
+		}
+		/* allocated bytes */
+		if (!kdf->allocate_bytes(kdf, vector->out.len, &out))
+		{
+			goto failure;
+		}
+		if (!chunk_equals(out, vector->out))
+		{
+			goto failure;
+		}
+		/* allocate without knowing the length */
+		if (kdf_has_fixed_output_length(alg))
+		{
+			chunk_free(&out);
+			if (!kdf->allocate_bytes(kdf, 0, &out))
+			{
+				goto failure;
+			}
+			if (!chunk_equals(out, vector->out))
+			{
+				goto failure;
+			}
+		}
+		/* bytes to existing buffer */
+		memset(out.ptr, 0, out.len);
+		if (!kdf->get_bytes(kdf, out.len, out.ptr))
+		{
+			goto failure;
+		}
+		if (!chunk_equals(out, vector->out))
+		{
+			goto failure;
+		}
+
+		failed = FALSE;
+failure:
+		kdf->destroy(kdf);
+		chunk_free(&out);
+		if (failed)
+		{
+			DBG1(DBG_LIB, "disabled %N[%s]: %s test vector failed",
+				 key_derivation_function_names, alg, plugin_name,
+				 get_name(vector));
+			break;
+		}
+	}
+	enumerator->destroy(enumerator);
+	if (!tested)
+	{
+		DBG1(DBG_LIB, "%s %N[%s]: no test vectors found",
+			 this->required ? "disabled" : "enabled ",
+			 key_derivation_function_names, alg, plugin_name);
+		return !this->required;
+	}
+	tested -= construction_failed;
+	if (!tested)
+	{
+		DBG1(DBG_LIB, "%s %N[%s]: unable to apply any available test vectors",
+			 this->required ? "disabled" : "enabled ",
+			 key_derivation_function_names, alg, plugin_name);
+		return !this->required;
+	}
+	if (!failed)
+	{
+		if (speed)
+		{
+			DBG2(DBG_LIB, "benchmarking for %N is currently not supported",
+				 key_derivation_function_names, alg);
+		}
+		DBG1(DBG_LIB, "enabled  %N[%s]: passed %u test vectors",
+			 key_derivation_function_names, alg, plugin_name, tested);
+	}
+	return !failed;
+}
+
 /**
  * Benchmark a DRBG
  */
@@ -1622,6 +1832,12 @@ METHOD(crypto_tester_t, add_xof_vector, void,
 	this->xof->insert_last(this->xof, vector);
 }
 
+METHOD(crypto_tester_t, add_kdf_vector, void,
+	private_crypto_tester_t *this, kdf_test_vector_t *vector)
+{
+	this->kdf->insert_last(this->kdf, vector);
+}
+
 METHOD(crypto_tester_t, add_drbg_vector, void,
 	private_crypto_tester_t *this, drbg_test_vector_t *vector)
 {
@@ -1649,6 +1865,7 @@ METHOD(crypto_tester_t, destroy, void,
 	this->hasher->destroy(this->hasher);
 	this->prf->destroy(this->prf);
 	this->xof->destroy(this->xof);
+	this->kdf->destroy(this->kdf);
 	this->drbg->destroy(this->drbg);
 	this->rng->destroy(this->rng);
 	this->dh->destroy(this->dh);
@@ -1670,6 +1887,7 @@ crypto_tester_t *crypto_tester_create()
 			.test_hasher = _test_hasher,
 			.test_prf = _test_prf,
 			.test_xof = _test_xof,
+			.test_kdf = _test_kdf,
 			.test_drbg = _test_drbg,
 			.test_rng = _test_rng,
 			.test_dh = _test_dh,
@@ -1679,6 +1897,7 @@ crypto_tester_t *crypto_tester_create()
 			.add_hasher_vector = _add_hasher_vector,
 			.add_prf_vector = _add_prf_vector,
 			.add_xof_vector = _add_xof_vector,
+			.add_kdf_vector = _add_kdf_vector,
 			.add_drbg_vector = _add_drbg_vector,
 			.add_rng_vector = _add_rng_vector,
 			.add_dh_vector = _add_dh_vector,
@@ -1690,6 +1909,7 @@ crypto_tester_t *crypto_tester_create()
 		.hasher = linked_list_create(),
 		.prf = linked_list_create(),
 		.xof = linked_list_create(),
+		.kdf = linked_list_create(),
 		.drbg = linked_list_create(),
 		.rng = linked_list_create(),
 		.dh = linked_list_create(),
diff --git a/src/libstrongswan/crypto/crypto_tester.h b/src/libstrongswan/crypto/crypto_tester.h
index fcb64a6e256a..cd9eff069f95 100644
--- a/src/libstrongswan/crypto/crypto_tester.h
+++ b/src/libstrongswan/crypto/crypto_tester.h
@@ -32,6 +32,8 @@ typedef struct signer_test_vector_t signer_test_vector_t;
 typedef struct hasher_test_vector_t hasher_test_vector_t;
 typedef struct prf_test_vector_t prf_test_vector_t;
 typedef struct xof_test_vector_t xof_test_vector_t;
+typedef struct kdf_test_vector_t kdf_test_vector_t;
+typedef struct kdf_test_args_t kdf_test_args_t;
 typedef struct drbg_test_vector_t drbg_test_vector_t;
 typedef struct rng_test_vector_t rng_test_vector_t;
 typedef struct dh_test_vector_t dh_test_vector_t;
@@ -130,6 +132,26 @@ struct xof_test_vector_t {
 	u_char *out;
 };
 
+struct kdf_test_vector_t {
+	/** kdf algorithm this test vector tests */
+	key_derivation_function_t alg;
+	/** argument passed to constructor, type depends on alg */
+	union {
+		pseudo_random_function_t prf;
+	} arg;
+	/** optional key */
+	chunk_t key;
+	/** optional salt */
+	chunk_t salt;
+	/** expected output */
+	chunk_t out;
+};
+
+struct kdf_test_args_t {
+	/** the arguments used to construct the KDF */
+	va_list args;
+};
+
 struct drbg_test_vector_t {
 	/** drbg type this test vector tests */
 	drbg_type_t type;
@@ -256,6 +278,22 @@ struct crypto_tester_t {
 	bool (*test_xof)(crypto_tester_t *this, ext_out_function_t alg,
 					 xof_constructor_t create,
 					 u_int *speed, const char *plugin_name);
+	/**
+	 * Test a KDF algorithm.
+	 *
+	 * If constructor arguments are passed, only matching test vectors are
+	 * tried. Otherwise, all are tried and implementations are allowed to fail
+	 * construction with unsupported arguments.
+	 *
+	 * @param alg			algorithm to test
+	 * @param create		constructor function for the XOF
+	 * @param args			optional arguments to pass to constructor
+	 * @param speed			speed test result, NULL to omit
+	 * @return				TRUE if test passed
+	 */
+	bool (*test_kdf)(crypto_tester_t *this, key_derivation_function_t alg,
+					 kdf_constructor_t create, kdf_test_args_t *args,
+					 u_int *speed, const char *plugin_name);
 	/**
 	 * Test a DRBG type.
 	 *
@@ -332,6 +370,13 @@ struct crypto_tester_t {
 	 */
 	void (*add_xof_vector)(crypto_tester_t *this, xof_test_vector_t *vector);
 
+	/**
+	 * Add a test vector to test a KDF.
+	 *
+	 * @param vector		pointer to test vector
+	 */
+	void (*add_kdf_vector)(crypto_tester_t *this, kdf_test_vector_t *vector);
+
 	/**
 	 * Add a test vector to test a DRBG.
 	 *
diff --git a/src/libstrongswan/crypto/kdfs/kdf.c b/src/libstrongswan/crypto/kdfs/kdf.c
new file mode 100644
index 000000000000..f218013cb182
--- /dev/null
+++ b/src/libstrongswan/crypto/kdfs/kdf.c
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 Tobias Brunner, codelabs GmbH
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "kdf.h"
+
+ENUM(key_derivation_function_names, KDF_UNDEFINED, KDF_PRF_PLUS,
+	"KDF_UNDEFINED",
+	"KDF_PRF",
+	"KDF_PRF_PLUS",
+);
+
+/*
+ * Described in header
+ */
+bool kdf_has_fixed_output_length(key_derivation_function_t type)
+{
+	switch (type)
+	{
+		case KDF_PRF:
+			return TRUE;
+		case KDF_PRF_PLUS:
+		case KDF_UNDEFINED:
+			break;
+	}
+	return FALSE;
+}
diff --git a/src/libstrongswan/crypto/kdfs/kdf.h b/src/libstrongswan/crypto/kdfs/kdf.h
new file mode 100644
index 000000000000..c697d669ea8d
--- /dev/null
+++ b/src/libstrongswan/crypto/kdfs/kdf.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2022 Tobias Brunner, codelabs GmbH
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * @defgroup kdf kdf
+ * @{ @ingroup crypto
+ */
+
+#ifndef KDF_H_
+#define KDF_H_
+
+typedef enum key_derivation_function_t key_derivation_function_t;
+typedef enum kdf_param_t kdf_param_t;
+typedef struct kdf_t kdf_t;
+
+#include <library.h>
+
+/**
+ * Key Derivation Functions (KDF).
+ */
+enum key_derivation_function_t {
+
+	KDF_UNDEFINED,
+
+	/**
+	 * RFC 7296 prf, expects a pseudo_random_function_t in the constructor,
+	 * parameters are KEY and SALT. Has a fixed output length.
+	 */
+	KDF_PRF,
+
+	/**
+	 * RFC 7296 prf+, expects a pseudo_random_function_t in the constructor,
+	 * parameters are KEY and SALT.
+	 */
+	KDF_PRF_PLUS,
+};
+
+/**
+ * enum name for key_derivation_function_t.
+ */
+extern enum_name_t *key_derivation_function_names;
+
+/**
+ * Parameters for KDFs.
+ */
+enum kdf_param_t {
+
+	/**
+	 * Key used for the key derivation (chunk_t).
+	 */
+	KDF_PARAM_KEY,
+
+	/**
+	 * Salt used for the key derivation (chunk_t).
+	 */
+	KDF_PARAM_SALT,
+};
+
+/**
+ * Generic interface for Key Derivation Functions (KDF).
+ *
+ * Note that in comparison to xof_t, this interface does not support streaming.
+ * That is, calling get_bytes() or allocate_bytes() multiple times without
+ * changing the input parameters will result in the same output.
+ */
+struct kdf_t {
+
+	/**
+	 * Return the type of KDF.
+	 *
+	 * @return			KDF type
+	 */
+	key_derivation_function_t (*get_type)(kdf_t *this);
+
+	/**
+	 * Output length for KDFs that produce a fixed amount of output.
+	 *
+	 * @return			fixed output length, SIZE_MAX for variable length
+	 */
+	size_t (*get_length)(kdf_t *this);
+
+	/**
+	 * Derives a key of the given length and writes it to the buffer.
+	 *
+	 * @note Fails if out_len doesn't match for KDFs with fixed output length.
+	 *
+	 * @param out_len	number of key bytes requested
+	 * @param buffer	pointer where the derived key will be written
+	 * @return			TRUE if key derived successfully
+	 */
+	bool (*get_bytes)(kdf_t *this, size_t out_len,
+					  uint8_t *buffer) __attribute__((warn_unused_result));
+
+	/**
+	 * Derives a key of the given length and allocates space for it.
+	 *
+	 * @note Fails if out_len doesn't match for KDFs with fixed output length.
+	 * However, for simplified usage, 0 can be passed for out_len to
+	 * automatically allocate a chunk of the correct size.
+	 *
+	 * @param out_len	number of key bytes requested, or 0 for KDFs with fixed
+	 *					output length
+	 * @param chunk		chunk which will hold the derived key
+	 * @return			TRUE if key derived successfully
+	 */
+	bool (*allocate_bytes)(kdf_t *this, size_t out_len,
+						   chunk_t *chunk) __attribute__((warn_unused_result));
+
+	/**
+	 * Set a parameter for this KDF.
+	 *
+	 * @param param		parameter to set
+	 * @param ...		parameter values
+	 * @return			TRUE if parameter set successfully
+	 */
+	bool (*set_param)(kdf_t *this, kdf_param_t param,
+					 ...) __attribute__((warn_unused_result));
+
+	/**
+	 * Destroys this KDF object.
+	 */
+	void (*destroy)(kdf_t *this);
+};
+
+/**
+ * Check if the given KDF type has a fixed output length.
+ *
+ * @param type			KDF type
+ * @return				TRUE if the KDF type has a fixed output length
+ */
+bool kdf_has_fixed_output_length(key_derivation_function_t type);
+
+#endif /** KDF_H_ @}*/
diff --git a/src/libstrongswan/crypto/pkcs5.c b/src/libstrongswan/crypto/pkcs5.c
index e7677a9c19c1..b3f1acd67fc0 100644
--- a/src/libstrongswan/crypto/pkcs5.c
+++ b/src/libstrongswan/crypto/pkcs5.c
@@ -131,7 +131,7 @@ static bool verify_padding(crypter_t *crypter, chunk_t *blob)
 /**
  * Prototype for key derivation functions.
  */
-typedef bool (*kdf_t)(private_pkcs5_t *this, chunk_t password, chunk_t key);
+typedef bool (*derive_t)(private_pkcs5_t *this, chunk_t password, chunk_t key);
 
 /**
  * Try to decrypt the given data with the given password using the given
@@ -139,7 +139,7 @@ typedef bool (*kdf_t)(private_pkcs5_t *this, chunk_t password, chunk_t key);
  * to, key and iv point to the actual keys and initialization vectors resp.
  */
 static bool decrypt_generic(private_pkcs5_t *this, chunk_t password,
-							chunk_t data, chunk_t *decrypted, kdf_t kdf,
+							chunk_t data, chunk_t *decrypted, derive_t kdf,
 							chunk_t keymat, chunk_t key, chunk_t iv)
 {
 	if (!kdf(this, password, keymat))
@@ -341,7 +341,7 @@ METHOD(pkcs5_t, decrypt, bool,
 	private_pkcs5_t *this, chunk_t password, chunk_t data, chunk_t *decrypted)
 {
 	chunk_t keymat, key, iv;
-	kdf_t kdf;
+	derive_t kdf;
 
 	if (!ensure_crypto_primitives(this, data) || !decrypted)
 	{
diff --git a/src/libstrongswan/crypto/prf_plus.c b/src/libstrongswan/crypto/prf_plus.c
deleted file mode 100644
index 55da146427f3..000000000000
--- a/src/libstrongswan/crypto/prf_plus.c
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (C) 2005-2006 Martin Willi
- * Copyright (C) 2005 Jan Hutter
- * HSR Hochschule fuer Technik Rapperswil
- *
- * 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 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * 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.
- */
-
-#include <string.h>
-
-#include "prf_plus.h"
-
-typedef struct private_prf_plus_t private_prf_plus_t;
-
-typedef bool (*apply_prf_t)(private_prf_plus_t *this);
-
-/**
- * Private data of an prf_plus_t object.
- *
- */
-struct private_prf_plus_t {
-
-	/**
-	 * Public interface of prf_plus_t.
-	 */
-	prf_plus_t public;
-
-	/**
-	 * PRF to use.
-	 */
-	prf_t *prf;
-
-	/**
-	 * Initial seed.
-	 */
-	chunk_t seed;
-
-	/**
-	 * Octet which will be appended to the seed if a counter is used.
-	 */
-	uint8_t counter;
-
-	/**
-	 * Already given out bytes in current buffer.
-	 */
-	size_t used;
-
-	/**
-	 * Buffer to store current PRF result.
-	 */
-	chunk_t buffer;
-
-	/**
-	 * The prf application method depending on whether a counter is used.
-	 */
-	apply_prf_t apply_prf;
-};
-
-/**
- * Apply the PRF using the running counter
- */
-static bool apply_prf_counter(private_prf_plus_t *this)
-{
-	if (!this->prf->get_bytes(this->prf, this->seed, NULL) ||
-		!this->prf->get_bytes(this->prf, chunk_from_thing(this->counter),
-							  this->buffer.ptr))
-	{
-		return FALSE;
-	}
-	this->counter++;
-	if (!this->counter)
-	{	/* according to RFC 7296, section 2.13, prf+ is undefined once the
-		 * counter wrapped, so let's fail for future calls */
-		this->apply_prf = (void*)return_false;
-	}
-	return TRUE;
-}
-
-/**
- * Apply the PRF using the running counter
- */
-static bool apply_prf(private_prf_plus_t *this)
-{
-	return this->prf->get_bytes(this->prf, this->seed, this->buffer.ptr);
-}
-
-METHOD(prf_plus_t, get_bytes, bool,
-	private_prf_plus_t *this, size_t length, uint8_t *buffer)
-{
-	size_t round, written = 0;
-
-	while (length > 0)
-	{
-		if (this->buffer.len == this->used)
-		{	/* buffer used, get next round */
-			if (!this->prf->get_bytes(this->prf, this->buffer, NULL))
-			{
-				return FALSE;
-			}
-			if (!this->apply_prf(this))
-			{
-				return FALSE;
-			}
-			this->used = 0;
-		}
-		round = min(length, this->buffer.len - this->used);
-		memcpy(buffer + written, this->buffer.ptr + this->used, round);
-
-		length -= round;
-		this->used += round;
-		written += round;
-	}
-	return TRUE;
-}
-
-METHOD(prf_plus_t, allocate_bytes, bool,
-	private_prf_plus_t *this, size_t length, chunk_t *chunk)
-{
-	*chunk = chunk_alloc(length);
-	if (!get_bytes(this, length, chunk->ptr))
-	{
-		chunk_free(chunk);
-		return FALSE;
-	}
-	return TRUE;
-}
-
-METHOD(prf_plus_t, destroy, void,
-	private_prf_plus_t *this)
-{
-	chunk_clear(&this->buffer);
-	chunk_clear(&this->seed);
-	free(this);
-}
-
-/*
- * Description in header.
- */
-prf_plus_t *prf_plus_create(prf_t *prf, bool counter, chunk_t seed)
-{
-	private_prf_plus_t *this;
-
-	INIT(this,
-		.public = {
-			.get_bytes = _get_bytes,
-			.allocate_bytes = _allocate_bytes,
-			.destroy = _destroy,
-		},
-		.prf = prf,
-		.seed = chunk_clone(seed),
-		.buffer = chunk_alloc(prf->get_block_size(prf)),
-		.apply_prf = counter ? apply_prf_counter : apply_prf,
-		.counter = 0x01,
-	);
-
-	if (!this->apply_prf(this))
-	{
-		destroy(this);
-		return NULL;
-	}
-	return &this->public;
-}
diff --git a/src/libstrongswan/crypto/prf_plus.h b/src/libstrongswan/crypto/prf_plus.h
deleted file mode 100644
index 41fdfbb86ee1..000000000000
--- a/src/libstrongswan/crypto/prf_plus.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2005-2006 Martin Willi
- * Copyright (C) 2005 Jan Hutter
- * HSR Hochschule fuer Technik Rapperswil
- *
- * 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 2 of the License, or (at your
- * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * 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.
- */
-
-/**
- * @defgroup prf_plus prf_plus
- * @{ @ingroup crypto
- */
-
-#ifndef PRF_PLUS_H_
-#define PRF_PLUS_H_
-
-typedef struct prf_plus_t prf_plus_t;
-
-#include <crypto/prfs/prf.h>
-
-/**
- * Implementation of the prf+ function used in IKEv1/IKEv2 keymat extension.
- */
-struct prf_plus_t {
-
-	/**
-	 * Get pseudo random bytes.
-	 *
-	 * @param length	number of bytes to get
-	 * @param buffer	pointer where the generated bytes will be written
-	 * @return			TRUE if bytes generated successfully
-	 */
-	bool (*get_bytes)(prf_plus_t *this, size_t length,
-					  uint8_t *buffer) __attribute__((warn_unused_result));
-
-	/**
-	 * Allocate pseudo random bytes.
-	 *
-	 * @param length	number of bytes to get
-	 * @param chunk		chunk which will hold generated bytes
-	 * @return			TRUE if bytes allocated successfully
-	 */
-	bool (*allocate_bytes)(prf_plus_t *this, size_t length,
-						   chunk_t *chunk) __attribute__((warn_unused_result));
-
-	/**
-	 * Destroys a prf_plus_t object.
-	 */
-	void (*destroy)(prf_plus_t *this);
-};
-
-/**
- * Creates a new prf_plus_t object.
- *
- * @param prf				prf object to use, must be destroyd after prf+.
- * @param counter			use an appending counter byte (for IKEv2 variant)
- * @param seed				input seed for prf
- * @return					prf_plus_t object, NULL on failure
- */
-prf_plus_t *prf_plus_create(prf_t *prf, bool counter, chunk_t seed);
-
-#endif /** PRF_PLUS_H_ @}*/
diff --git a/src/libstrongswan/crypto/transform.c b/src/libstrongswan/crypto/transform.c
index e30208ec0054..4ecc3e862b26 100644
--- a/src/libstrongswan/crypto/transform.c
+++ b/src/libstrongswan/crypto/transform.c
@@ -16,6 +16,7 @@
 #include <crypto/transform.h>
 #include <crypto/hashers/hasher.h>
 #include <crypto/rngs/rng.h>
+#include <crypto/kdfs/kdf.h>
 
 ENUM_BEGIN(transform_type_names, ENCRYPTION_ALGORITHM, EXTENDED_SEQUENCE_NUMBERS,
 	"ENCRYPTION_ALGORITHM",
@@ -23,16 +24,16 @@ ENUM_BEGIN(transform_type_names, ENCRYPTION_ALGORITHM, EXTENDED_SEQUENCE_NUMBERS
 	"INTEGRITY_ALGORITHM",
 	"DIFFIE_HELLMAN_GROUP",
 	"EXTENDED_SEQUENCE_NUMBERS");
-ENUM_NEXT(transform_type_names, HASH_ALGORITHM, DETERMINISTIC_RANDOM_BIT_GENERATOR,
+ENUM_NEXT(transform_type_names, HASH_ALGORITHM, KEY_DERIVATION_FUNCTION,
 		  EXTENDED_SEQUENCE_NUMBERS,
 	"HASH_ALGORITHM",
 	"RANDOM_NUMBER_GENERATOR",
 	"AEAD_ALGORITHM",
 	"COMPRESSION_ALGORITHM",
 	"EXTENDED OUTPUT FUNCTION",
-	"DETERMINISTIC RANDOM BIT GENERATOR");
-ENUM_END(transform_type_names, DETERMINISTIC_RANDOM_BIT_GENERATOR);
-
+	"DETERMINISTIC RANDOM BIT GENERATOR",
+	"KEY_DERIVATION_FUNCTION");
+ENUM_END(transform_type_names, KEY_DERIVATION_FUNCTION);
 
 ENUM(extended_sequence_numbers_names, NO_EXT_SEQ_NUMBERS, EXT_SEQ_NUMBERS,
 	"NO_EXT_SEQ",
@@ -65,6 +66,8 @@ enum_name_t* transform_get_enum_names(transform_type_t type)
 			return ext_out_function_names;
 		case DETERMINISTIC_RANDOM_BIT_GENERATOR:
 			return drbg_type_names;
+		case KEY_DERIVATION_FUNCTION:
+			return key_derivation_function_names;
 		case COMPRESSION_ALGORITHM:
 			break;
 	}
diff --git a/src/libstrongswan/crypto/transform.h b/src/libstrongswan/crypto/transform.h
index f7892b821e1c..73c7b534727b 100644
--- a/src/libstrongswan/crypto/transform.h
+++ b/src/libstrongswan/crypto/transform.h
@@ -40,6 +40,7 @@ enum transform_type_t {
 	COMPRESSION_ALGORITHM = 259,
 	EXTENDED_OUTPUT_FUNCTION = 260,
 	DETERMINISTIC_RANDOM_BIT_GENERATOR = 261,
+	KEY_DERIVATION_FUNCTION = 262,
 };
 
 /**
diff --git a/src/libstrongswan/plugins/openssl/Makefile.am b/src/libstrongswan/plugins/openssl/Makefile.am
index 7b83890fa0eb..e82374c19b07 100644
--- a/src/libstrongswan/plugins/openssl/Makefile.am
+++ b/src/libstrongswan/plugins/openssl/Makefile.am
@@ -29,6 +29,7 @@ libstrongswan_openssl_la_SOURCES = \
 	openssl_pkcs12.c openssl_pkcs12.h \
 	openssl_rng.c openssl_rng.h \
 	openssl_hmac.c openssl_hmac.h \
+	openssl_kdf.c openssl_kdf.h \
 	openssl_aead.c openssl_aead.h \
 	openssl_x_diffie_hellman.c openssl_x_diffie_hellman.h \
 	openssl_ed_private_key.c openssl_ed_private_key.h \
diff --git a/src/libstrongswan/plugins/openssl/openssl_kdf.c b/src/libstrongswan/plugins/openssl/openssl_kdf.c
new file mode 100644
index 000000000000..74def46daa19
--- /dev/null
+++ b/src/libstrongswan/plugins/openssl/openssl_kdf.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2022 Tobias Brunner, codelabs GmbH
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <openssl/opensslv.h>
+#include <openssl/opensslconf.h>
+
+#if !defined(OPENSSL_NO_HMAC) && OPENSSL_VERSION_NUMBER >= 0x10101000L
+
+#include <openssl/evp.h>
+#include <openssl/kdf.h>
+
+#include "openssl_kdf.h"
+
+typedef struct private_kdf_t private_kdf_t;
+
+/**
+ * Private data.
+ */
+struct private_kdf_t {
+
+	/**
+	 * Public interface.
+	 */
+	kdf_t public;
+
+	/**
+	 * KDF type.
+	 */
+	key_derivation_function_t type;
+
+	/**
+	 * Hasher to use for underlying PRF.
+	 */
+	const EVP_MD *hasher;
+
+	/**
+	 * Key for KDF. Stored here because OpenSSL's HKDF API does not provide a
+	 * way to clear the "info" field in the context, new data is always
+	 * appended (up to 1024 bytes).
+	 */
+	chunk_t key;
+
+	/**
+	 * Salt for prf+ (see above).
+	 */
+	chunk_t salt;
+};
+
+METHOD(kdf_t, get_type, key_derivation_function_t,
+	private_kdf_t *this)
+{
+	return this->type;
+}
+
+METHOD(kdf_t, get_length, size_t,
+	private_kdf_t *this)
+{
+	if (this->type == KDF_PRF_PLUS)
+	{
+		return SIZE_MAX;
+	}
+	return EVP_MD_size(this->hasher);
+}
+
+/**
+ * Set the parameters as a appropriate for the given KDF type.
+ */
+static bool set_params(private_kdf_t *this, EVP_PKEY_CTX *ctx)
+{
+	/* IKEv2 uses the nonces as PRF key and the DH secret as salt, however,
+	 * HKDF-Extract() does the same again (mapping the salt to the HMAC key),
+	 * so we have to switch key and salt here */
+	if (this->type == KDF_PRF)
+	{
+		return EVP_PKEY_CTX_hkdf_mode(ctx, EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY) > 0 &&
+			   EVP_PKEY_CTX_set1_hkdf_key(ctx, this->salt.ptr, this->salt.len) > 0 &&
+			   EVP_PKEY_CTX_set1_hkdf_salt(ctx, this->key.ptr, this->key.len) > 0;
+	}
+	/* for HKDF-Expand() we map the salt to the "info" field */
+	return EVP_PKEY_CTX_hkdf_mode(ctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) > 0 &&
+		   EVP_PKEY_CTX_set1_hkdf_key(ctx, this->key.ptr, this->key.len) > 0 &&
+		   EVP_PKEY_CTX_add1_hkdf_info(ctx, this->salt.ptr, this->salt.len) > 0;
+}
+
+METHOD(kdf_t, get_bytes, bool,
+	private_kdf_t *this, size_t out_len, uint8_t *buffer)
+{
+	EVP_PKEY_CTX *ctx;
+
+	if (this->type == KDF_PRF && out_len != get_length(this))
+	{
+		return FALSE;
+	}
+
+	ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
+	if (!ctx ||
+		EVP_PKEY_derive_init(ctx) <= 0 ||
+		EVP_PKEY_CTX_set_hkdf_md(ctx, this->hasher) <= 0 ||
+		!set_params(this, ctx) ||
+		EVP_PKEY_derive(ctx, buffer, &out_len) <= 0)
+	{
+		EVP_PKEY_CTX_free(ctx);
+		return FALSE;
+	}
+	EVP_PKEY_CTX_free(ctx);
+	return TRUE;
+}
+
+METHOD(kdf_t, allocate_bytes, bool,
+	private_kdf_t *this, size_t out_len, chunk_t *chunk)
+{
+	if (this->type == KDF_PRF)
+	{
+		out_len = out_len ?: get_length(this);
+	}
+
+	*chunk = chunk_alloc(out_len);
+
+	if (!get_bytes(this, out_len, chunk->ptr))
+	{
+		chunk_free(chunk);
+		return FALSE;
+	}
+	return TRUE;
+}
+
+METHOD(kdf_t, set_param, bool,
+	private_kdf_t *this, kdf_param_t param, ...)
+{
+	chunk_t chunk;
+
+	switch (param)
+	{
+		case KDF_PARAM_KEY:
+			VA_ARGS_GET(param, chunk);
+			chunk_clear(&this->key);
+			this->key = chunk_clone(chunk);
+			break;
+		case KDF_PARAM_SALT:
+			VA_ARGS_GET(param, chunk);
+			chunk_clear(&this->salt);
+			this->salt = chunk_clone(chunk);
+			break;
+	}
+	return TRUE;
+}
+
+METHOD(kdf_t, destroy, void,
+	private_kdf_t *this)
+{
+	chunk_clear(&this->salt);
+	chunk_clear(&this->key);
+	free(this);
+}
+
+/*
+ * Described in header
+ */
+kdf_t *openssl_kdf_create(key_derivation_function_t algo, va_list args)
+{
+	private_kdf_t *this;
+	pseudo_random_function_t prf_alg;
+	char *name, buf[EVP_MAX_MD_SIZE];
+
+	if (algo != KDF_PRF && algo != KDF_PRF_PLUS)
+	{
+		return NULL;
+	}
+
+	VA_ARGS_VGET(args, prf_alg);
+	name = enum_to_name(hash_algorithm_short_names,
+						hasher_algorithm_from_prf(prf_alg));
+	if (!name)
+	{
+		return NULL;
+	}
+
+	INIT(this,
+		.public = {
+			.get_type = _get_type,
+			.get_length = _get_length,
+			.get_bytes = _get_bytes,
+			.allocate_bytes = _allocate_bytes,
+			.set_param = _set_param,
+			.destroy = _destroy,
+		},
+		.type = algo,
+		.hasher = EVP_get_digestbyname(name),
+		/* use a lengthy key/salt to test the implementation below to make sure
+		 * the algorithms are usable, see openssl_hmac.c for details */
+		.key = chunk_clone(chunk_from_str("00000000000000000000000000000000")),
+		.salt = chunk_clone(chunk_from_str("00000000000000000000000000000000")),
+	);
+
+	if (!this->hasher ||
+		!get_bytes(this, algo == KDF_PRF ? get_length(this) : sizeof(buf), buf))
+	{
+		destroy(this);
+		return NULL;
+	}
+	return &this->public;
+}
+
+#endif /* OPENSSL_NO_HMAC && OPENSSL_VERSION_NUMBER */
diff --git a/src/libstrongswan/plugins/openssl/openssl_kdf.h b/src/libstrongswan/plugins/openssl/openssl_kdf.h
new file mode 100644
index 000000000000..4480c38c2a92
--- /dev/null
+++ b/src/libstrongswan/plugins/openssl/openssl_kdf.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 Tobias Brunner, codelabs GmbH
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * Implements key derivation functions (KDF) via OpenSSL, in particular prf+,
+ * which is implemented via OpenSSL's HKDF implementation.
+ *
+ * @defgroup openssl_kdf openssl_kdf
+ * @{ @ingroup openssl_p
+ */
+
+#ifndef OPENSSL_KDF_H_
+#define OPENSSL_KDF_H_
+
+#include <crypto/kdfs/kdf.h>
+
+/**
+ * Creates a new kdf_t object.
+ *
+ * @param algo		algorithm to instantiate
+ * @param args		algorithm-specific arguments
+ * @return			kdf_t object, NULL if not supported
+ */
+kdf_t *openssl_kdf_create(key_derivation_function_t algo, va_list args);
+
+#endif /** OPENSSL_KDF_H_ @}*/
diff --git a/src/libstrongswan/plugins/openssl/openssl_plugin.c b/src/libstrongswan/plugins/openssl/openssl_plugin.c
index 71369e9b10f4..c2451ebd6eca 100644
--- a/src/libstrongswan/plugins/openssl/openssl_plugin.c
+++ b/src/libstrongswan/plugins/openssl/openssl_plugin.c
@@ -46,6 +46,7 @@
 #include "openssl_pkcs12.h"
 #include "openssl_rng.h"
 #include "openssl_hmac.h"
+#include "openssl_kdf.h"
 #include "openssl_aead.h"
 #include "openssl_x_diffie_hellman.h"
 #include "openssl_ed_public_key.h"
@@ -586,6 +587,12 @@ METHOD(plugin_t, get_features, int,
 			PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_512_256),
 			PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_512_512),
 #endif
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L
+		/* HKDF is available since 1.1.0, expand-only mode only since 1.1.1 */
+		PLUGIN_REGISTER(KDF, openssl_kdf_create),
+			PLUGIN_PROVIDE(KDF, KDF_PRF),
+			PLUGIN_PROVIDE(KDF, KDF_PRF_PLUS),
+#endif
 #endif /* OPENSSL_NO_HMAC */
 #if (OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_AES)) || \
 	(OPENSSL_VERSION_NUMBER >= 0x1010000fL && !defined(OPENSSL_NO_CHACHA))
diff --git a/src/libstrongswan/plugins/plugin_feature.c b/src/libstrongswan/plugins/plugin_feature.c
index fa3d1f31b68c..5f045523bcf5 100644
--- a/src/libstrongswan/plugins/plugin_feature.c
+++ b/src/libstrongswan/plugins/plugin_feature.c
@@ -32,6 +32,7 @@ ENUM(plugin_feature_names, FEATURE_NONE, FEATURE_CUSTOM,
 	"HASHER",
 	"PRF",
 	"XOF",
+	"KDF",
 	"DRBG",
 	"DH",
 	"RNG",
@@ -93,6 +94,9 @@ uint32_t plugin_feature_hash(plugin_feature_t *feature)
 		case FEATURE_XOF:
 			data = chunk_from_thing(feature->arg.xof);
 			break;
+		case FEATURE_KDF:
+			data = chunk_from_thing(feature->arg.kdf);
+			break;
 		case FEATURE_DRBG:
 			data = chunk_from_thing(feature->arg.drbg);
 			break;
@@ -171,6 +175,8 @@ bool plugin_feature_matches(plugin_feature_t *a, plugin_feature_t *b)
 				return a->arg.prf == b->arg.prf;
 			case FEATURE_XOF:
 				return a->arg.xof == b->arg.xof;
+			case FEATURE_KDF:
+				return a->arg.kdf == b->arg.kdf;
 			case FEATURE_DRBG:
 				return a->arg.drbg == b->arg.drbg;
 			case FEATURE_DH:
@@ -232,6 +238,7 @@ bool plugin_feature_equals(plugin_feature_t *a, plugin_feature_t *b)
 			case FEATURE_HASHER:
 			case FEATURE_PRF:
 			case FEATURE_XOF:
+			case FEATURE_KDF:
 			case FEATURE_DRBG:
 			case FEATURE_DH:
 			case FEATURE_NONCE_GEN:
@@ -327,6 +334,13 @@ char* plugin_feature_get_string(plugin_feature_t *feature)
 				return str;
 			}
 			break;
+		case FEATURE_KDF:
+			if (asprintf(&str, "%N:%N", plugin_feature_names, feature->type,
+					key_derivation_function_names, feature->arg.kdf) > 0)
+			{
+				return str;
+			}
+			break;
 		case FEATURE_DRBG:
 			if (asprintf(&str, "%N:%N", plugin_feature_names, feature->type,
 					drbg_type_names, feature->arg.drbg) > 0)
@@ -472,6 +486,17 @@ bool plugin_feature_load(plugin_t *plugin, plugin_feature_t *feature,
 	name = plugin->get_name(plugin);
 	switch (feature->type)
 	{
+		case FEATURE_NONE:
+		case FEATURE_PRIVKEY_SIGN:
+		case FEATURE_PRIVKEY_DECRYPT:
+		case FEATURE_PUBKEY_VERIFY:
+		case FEATURE_PUBKEY_ENCRYPT:
+		case FEATURE_EAP_SERVER:
+		case FEATURE_EAP_PEER:
+		case FEATURE_XAUTH_SERVER:
+		case FEATURE_XAUTH_PEER:
+		case FEATURE_CUSTOM:
+			break;
 		case FEATURE_CRYPTER:
 			lib->crypto->add_crypter(lib->crypto, feature->arg.crypter.alg,
 								feature->arg.crypter.key_size,
@@ -498,6 +523,10 @@ bool plugin_feature_load(plugin_t *plugin, plugin_feature_t *feature,
 			lib->crypto->add_xof(lib->crypto, feature->arg.xof,
 								name, reg->arg.reg.f);
 			break;
+		case FEATURE_KDF:
+			lib->crypto->add_kdf(lib->crypto, feature->arg.kdf,
+								name, reg->arg.reg.f);
+			break;
 		case FEATURE_DRBG:
 			lib->crypto->add_drbg(lib->crypto, feature->arg.drbg,
 								name, reg->arg.reg.f);
@@ -547,8 +576,6 @@ bool plugin_feature_load(plugin_t *plugin, plugin_feature_t *feature,
 		case FEATURE_RESOLVER:
 			lib->resolver->add_resolver(lib->resolver, reg->arg.reg.f);
 			break;
-		default:
-			break;
 	}
 	return TRUE;
 }
@@ -574,6 +601,17 @@ bool plugin_feature_unload(plugin_t *plugin, plugin_feature_t *feature,
 	}
 	switch (feature->type)
 	{
+		case FEATURE_NONE:
+		case FEATURE_PRIVKEY_SIGN:
+		case FEATURE_PRIVKEY_DECRYPT:
+		case FEATURE_PUBKEY_VERIFY:
+		case FEATURE_PUBKEY_ENCRYPT:
+		case FEATURE_EAP_SERVER:
+		case FEATURE_EAP_PEER:
+		case FEATURE_XAUTH_SERVER:
+		case FEATURE_XAUTH_PEER:
+		case FEATURE_CUSTOM:
+			break;
 		case FEATURE_CRYPTER:
 			lib->crypto->remove_crypter(lib->crypto, reg->arg.reg.f);
 			break;
@@ -592,6 +630,9 @@ bool plugin_feature_unload(plugin_t *plugin, plugin_feature_t *feature,
 		case FEATURE_XOF:
 			lib->crypto->remove_xof(lib->crypto, reg->arg.reg.f);
 			break;
+		case FEATURE_KDF:
+			lib->crypto->remove_kdf(lib->crypto, reg->arg.reg.f);
+			break;
 		case FEATURE_DRBG:
 			lib->crypto->remove_drbg(lib->crypto, reg->arg.reg.f);
 			break;
@@ -628,8 +669,6 @@ bool plugin_feature_unload(plugin_t *plugin, plugin_feature_t *feature,
 		case FEATURE_RESOLVER:
 			lib->resolver->remove_resolver(lib->resolver, reg->arg.reg.f);
 			break;
-		default:
-			break;
 	}
 	return TRUE;
 }
diff --git a/src/libstrongswan/plugins/plugin_feature.h b/src/libstrongswan/plugins/plugin_feature.h
index 2692de2ceb12..b2080c05c712 100644
--- a/src/libstrongswan/plugins/plugin_feature.h
+++ b/src/libstrongswan/plugins/plugin_feature.h
@@ -113,6 +113,8 @@ struct plugin_feature_t {
 		FEATURE_PRF,
 		/** xof_t */
 		FEATURE_XOF,
+		/** kdf_t */
+		FEATURE_KDF,
 		/** drbg_t */
 		FEATURE_DRBG,
 		/** diffie_hellman_t */
@@ -176,8 +178,10 @@ struct plugin_feature_t {
 		integrity_algorithm_t signer;
 		/** FEATURE_PRF */
 		pseudo_random_function_t prf;
-		/** FEATURE_XOFF */
+		/** FEATURE_XOF */
 		ext_out_function_t xof;
+		/** FEATURE_KDF */
+		key_derivation_function_t kdf;
 		/** FEATURE_DRBG */
 		drbg_type_t drbg;
 		/** FEATURE_HASHER */
@@ -288,6 +292,7 @@ struct plugin_feature_t {
 #define _PLUGIN_FEATURE_HASHER(kind, alg)					__PLUGIN_FEATURE(kind, HASHER, .hasher = alg)
 #define _PLUGIN_FEATURE_PRF(kind, alg)						__PLUGIN_FEATURE(kind, PRF, .prf = alg)
 #define _PLUGIN_FEATURE_XOF(kind, alg)						__PLUGIN_FEATURE(kind, XOF, .xof = alg)
+#define _PLUGIN_FEATURE_KDF(kind, alg)						__PLUGIN_FEATURE(kind, KDF, .kdf = alg)
 #define _PLUGIN_FEATURE_DRBG(kind, type)					__PLUGIN_FEATURE(kind, DRBG, .drbg = type)
 #define _PLUGIN_FEATURE_DH(kind, group)						__PLUGIN_FEATURE(kind, DH, .dh_group = group)
 #define _PLUGIN_FEATURE_RNG(kind, quality)					__PLUGIN_FEATURE(kind, RNG, .rng_quality = quality)
@@ -322,6 +327,7 @@ struct plugin_feature_t {
 #define _PLUGIN_FEATURE_REGISTER_HASHER(type, f)			__PLUGIN_FEATURE_REGISTER(type, f)
 #define _PLUGIN_FEATURE_REGISTER_PRF(type, f)				__PLUGIN_FEATURE_REGISTER(type, f)
 #define _PLUGIN_FEATURE_REGISTER_XOF(type, f)				__PLUGIN_FEATURE_REGISTER(type, f)
+#define _PLUGIN_FEATURE_REGISTER_KDF(type, f)				__PLUGIN_FEATURE_REGISTER(type, f)
 #define _PLUGIN_FEATURE_REGISTER_DRBG(type, f)				__PLUGIN_FEATURE_REGISTER(type, f)
 #define _PLUGIN_FEATURE_REGISTER_DH(type, f)				__PLUGIN_FEATURE_REGISTER(type, f)
 #define _PLUGIN_FEATURE_REGISTER_RNG(type, f)				__PLUGIN_FEATURE_REGISTER(type, f)
diff --git a/src/libstrongswan/plugins/test_vectors/Makefile.am b/src/libstrongswan/plugins/test_vectors/Makefile.am
index fda2485f2f03..587328ad66de 100644
--- a/src/libstrongswan/plugins/test_vectors/Makefile.am
+++ b/src/libstrongswan/plugins/test_vectors/Makefile.am
@@ -29,6 +29,8 @@ libstrongswan_test_vectors_la_SOURCES = \
 	test_vectors/cast.c \
 	test_vectors/des.c \
 	test_vectors/idea.c \
+	test_vectors/kdf_prf.c \
+	test_vectors/kdf_prf_plus.c \
 	test_vectors/null.c \
 	test_vectors/rc2.c \
 	test_vectors/rc5.c \
diff --git a/src/libstrongswan/plugins/test_vectors/test_vectors.h b/src/libstrongswan/plugins/test_vectors/test_vectors.h
index e108d6fc210f..7841ae72ae2a 100644
--- a/src/libstrongswan/plugins/test_vectors/test_vectors.h
+++ b/src/libstrongswan/plugins/test_vectors/test_vectors.h
@@ -214,6 +214,20 @@ TEST_VECTOR_HASHER(sha3_256_255)
 TEST_VECTOR_HASHER(sha3_384_255)
 TEST_VECTOR_HASHER(sha3_512_255)
 
+TEST_VECTOR_KDF(prf_sha256_1)
+TEST_VECTOR_KDF(prf_sha256_2)
+TEST_VECTOR_KDF(prf_sha384_1)
+TEST_VECTOR_KDF(prf_sha384_2)
+TEST_VECTOR_KDF(prf_sha512_1)
+TEST_VECTOR_KDF(prf_sha512_2)
+TEST_VECTOR_KDF(prf_plus_sha256_old)
+TEST_VECTOR_KDF(prf_plus_sha256_1)
+TEST_VECTOR_KDF(prf_plus_sha256_2)
+TEST_VECTOR_KDF(prf_plus_sha384_1)
+TEST_VECTOR_KDF(prf_plus_sha384_2)
+TEST_VECTOR_KDF(prf_plus_sha512_1)
+TEST_VECTOR_KDF(prf_plus_sha512_2)
+
 TEST_VECTOR_PRF(aes_xcbc_p1)
 TEST_VECTOR_PRF(aes_xcbc_p2)
 TEST_VECTOR_PRF(aes_xcbc_p3)
diff --git a/src/libstrongswan/plugins/test_vectors/test_vectors/kdf_prf.c b/src/libstrongswan/plugins/test_vectors/test_vectors/kdf_prf.c
new file mode 100644
index 000000000000..b2b8aa0f8472
--- /dev/null
+++ b/src/libstrongswan/plugins/test_vectors/test_vectors/kdf_prf.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2022 Tobias Brunner, codelabs GmbH
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <crypto/crypto_tester.h>
+
+/**
+ * The following test vectors are from CAVP/SP 800-135 for IKEv2.
+ *
+ * key = Ni | Nr, salt = g^ir (one vector with min. and one with max. size for
+ * nonces)
+ */
+kdf_test_vector_t prf_sha256_1 = {
+	.alg = KDF_PRF, .arg.prf = PRF_HMAC_SHA2_256,
+	.key = chunk_from_chars(
+		0xed,0x80,0xdc,0x79,0x91,0x2c,0x32,0xa9,0x35,0xfb,0x6d,0x1a,0x3f,0xea,0xc0,0x78),
+	.salt = chunk_from_chars(
+		0x42,0x96,0x8e,0x5d,0x0c,0xcc,0x3c,0xfc,0x5a,0x3e,0x4b,0xc1,0xbb,0xa3,0x70,0xce,
+		0xa1,0xfa,0xe0,0xd5,0x4c,0x49,0xcc,0xba,0x34,0xb2,0xbe,0xe8,0x04,0xbe,0xeb,0x2e,
+		0x9e,0x8c,0x57,0xa4,0xe0,0x1b,0xd4,0x51,0x02,0xcf,0x24,0x33,0xaa,0xcc,0x6c,0xfe,
+		0xc0,0x67,0x92,0xf3,0x63,0xe5,0x17,0x0e,0x6a,0xa6,0x65,0x02,0x74,0xe9,0x06,0x64,
+		0x8e,0x44,0x9d,0x27,0xa8,0xf0,0x0b,0x5b,0x44,0x26,0x19,0x82,0xc9,0x83,0x5c,0x74,
+		0x8a,0x75,0x1e,0xc5,0x13,0x8e,0xaa,0xcc,0x5e,0x02,0x56,0x61,0x33,0x95,0x38,0xa6,
+		0x1b,0xf4,0x18,0xe4,0x54,0x69,0x9e,0x19,0xc3,0x2d,0xb8,0xd9,0xce,0x5d,0xd8,0x6b,
+		0x22,0x0f,0x1e,0x89,0xaf,0xc5,0x87,0x2e,0x68,0xbe,0x36,0xcb,0x1a,0x0c,0x88,0x66),
+	.out = chunk_from_chars(
+		0x37,0xfd,0xe9,0x0b,0x81,0xd6,0x36,0x92,0x62,0x0f,0x67,0x36,0x7b,0x62,0x09,0x2e,
+		0x3e,0xfd,0xc6,0xa0,0x35,0x09,0x13,0x7d,0x73,0x10,0x04,0x76,0x7f,0xf3,0x50,0x10),
+};
+
+kdf_test_vector_t prf_sha256_2 = {
+	.alg = KDF_PRF, .arg.prf = PRF_HMAC_SHA2_256,
+	.key = chunk_from_chars(
+		0xef,0xa7,0x29,0x13,0x18,0x22,0x78,0xff,0xbd,0x14,0xe7,0x89,0x20,0xc0,0x62,0x51,
+		0x9a,0xba,0xb8,0xc1,0x6e,0x5e,0xd7,0x0c,0x08,0x41,0xa4,0x8c,0xdb,0x98,0x23,0x7e,
+		0xe9,0x3f,0x73,0x5f,0xb2,0xdf,0x18,0x43,0x58,0xaa,0xdc,0x13,0xd8,0x3f,0x43,0xfe,
+		0x8d,0x87,0x05,0x17,0x6c,0x8c,0xa3,0x13,0x82,0x5a,0x1b,0xcd,0xf7,0x79,0x11,0xc5,
+		0x98,0x00,0x16,0x71,0xff,0xbf,0x01,0x4e,0x37,0xa8,0xc0,0x4d,0x49,0xa7,0x83,0x9d,
+		0xfe,0xa6,0xcd,0xc5,0x87,0x68,0x8d,0x45,0x88,0xfe,0x43,0x23,0x5b,0x71,0x69,0x3f,
+		0xfd,0x07,0x29,0x33,0x86,0xb6,0xbf,0x4c,0x19,0x9e,0x33,0x61,0x65,0xb2,0x60,0x78,
+		0x77,0x36,0xf5,0x4b,0xe9,0x5d,0xb6,0x91,0x16,0x38,0x8b,0xc2,0xec,0xa2,0xb3,0xb2,
+		0x94,0x84,0x71,0x74,0x17,0xbb,0x3c,0x71,0x81,0x4c,0xe1,0x3b,0x84,0x44,0x6d,0xc3,
+		0x96,0x4c,0x30,0x29,0x84,0xf9,0x77,0x81,0xf6,0x31,0x66,0x24,0x08,0x90,0x10,0x7c,
+		0x2e,0x75,0x1a,0x00,0x43,0x6f,0x7c,0x3c,0x9f,0xf1,0x27,0x60,0xe4,0x9d,0x91,0x56,
+		0x3b,0xe6,0x03,0xfd,0x96,0x41,0xa0,0xa6,0x49,0x18,0xa9,0x32,0x91,0xed,0x11,0x3d,
+		0xb1,0x2f,0x97,0x07,0x60,0x9d,0x17,0x20,0x96,0xeb,0x58,0xf9,0x15,0x44,0x74,0xda,
+		0x40,0xc1,0xf5,0xc0,0x90,0x3e,0x9c,0xa2,0xf9,0x1b,0xa6,0x60,0x07,0x75,0xdf,0x71,
+		0x66,0xca,0xf8,0xe3,0x27,0x85,0x9e,0x67,0x62,0x32,0xd3,0x40,0x46,0x04,0x4c,0xee,
+		0x43,0xf9,0x01,0x9f,0x04,0x68,0x56,0x12,0x63,0x5b,0x99,0xcb,0xeb,0xcb,0x36,0x3d,
+		0x56,0x5e,0xaf,0x0e,0x54,0x7e,0xec,0xb9,0x41,0xc9,0x94,0xdf,0xd4,0x71,0xed,0x56,
+		0x43,0xc0,0x87,0x74,0x4f,0x77,0x09,0xcc,0x3e,0x25,0x10,0xf2,0x74,0x26,0xc6,0x2c,
+		0x0f,0xf3,0xac,0xb3,0xc2,0x76,0x61,0xd2,0x6a,0x6d,0x83,0xc2,0xa2,0x5e,0x13,0xa6,
+		0xd4,0x65,0xbd,0x04,0x7f,0x90,0x55,0x00,0xe5,0xeb,0xbe,0x42,0x66,0x43,0x0d,0x56,
+		0x67,0x14,0x0f,0x77,0xe7,0x97,0x71,0x2a,0x8c,0x8f,0x63,0xc5,0x83,0xf4,0xb4,0x64,
+		0x9b,0x72,0x89,0x9e,0xa3,0x4a,0xbf,0xdb,0x17,0x61,0x7c,0x46,0x0c,0x35,0xf2,0x50,
+		0x64,0x94,0x49,0x4f,0x22,0x3e,0x25,0x1a,0xc7,0x1a,0x5b,0x9b,0x7e,0xea,0x87,0xf4,
+		0xf5,0xe3,0x33,0xa3,0xc1,0xbb,0xb4,0xbb,0x09,0x25,0x8b,0x6a,0x4b,0x5f,0x8c,0x9f,
+		0xb8,0x2b,0xf4,0x2d,0xa9,0xd4,0xa4,0x65,0x43,0xc6,0xa9,0xeb,0x9a,0xa3,0x0e,0xa9,
+		0xda,0x80,0x19,0x15,0xab,0xcc,0x17,0x12,0xd0,0x82,0xf2,0x92,0xa6,0x3f,0xd9,0xaf,
+		0x71,0x54,0xa9,0x7c,0xc7,0x38,0x59,0xa4,0xbe,0x3c,0xba,0x35,0x9d,0x32,0x18,0x00,
+		0x4e,0x14,0xdf,0x02,0xd0,0x9e,0xdf,0x0a,0xd5,0x79,0x6b,0xb0,0x10,0x99,0x52,0x93,
+		0xab,0x5d,0x04,0x2c,0x31,0x05,0x53,0x80,0xcc,0x9c,0xb2,0xe3,0x61,0x79,0x82,0xc4,
+		0x5f,0x6b,0xce,0x1f,0xb8,0xa4,0x0e,0xf9,0xea,0xc4,0x8a,0xe1,0x77,0x20,0xdf,0xec,
+		0xc7,0x1e,0xc8,0x57,0xea,0x33,0xf3,0x2e,0xb3,0x46,0xba,0x60,0x36,0xe8,0xf9,0xcc,
+		0xd7,0xbc,0xad,0xc6,0xc1,0xab,0x92,0xa8,0x0c,0x57,0xe7,0x89,0x59,0xd8,0xb8,0x28),
+	.salt = chunk_from_chars(
+		0xb0,0x70,0x20,0x8f,0x89,0x47,0xdf,0x4f,0x7d,0x12,0x76,0x16,0x1f,0x40,0x7f,0x7b,
+		0x7f,0x5c,0x4a,0x49,0xac,0x79,0xf0,0xcc,0x0c,0x7d,0x4e,0x28,0x48,0x4c,0x6f,0x85,
+		0x84,0xf0,0x00,0x7b,0x9b,0xf0,0xe0,0x5f,0xdb,0x59,0x61,0xa1,0x7d,0x3a,0xa8,0x5c,
+		0x6e,0x3f,0x55,0x71,0x29,0x6a,0x43,0xba,0x89,0x6c,0xdc,0x88,0xc1,0xa4,0x45,0x7f,
+		0xb1,0x2c,0xbe,0x56,0xca,0x4a,0x20,0xc9,0xa7,0xe1,0x9a,0xdc,0x67,0x45,0x3c,0x4a,
+		0xde,0x53,0x9e,0x25,0x9f,0x82,0x5f,0xf9,0x4c,0x9a,0x83,0xf8,0x39,0x60,0x2f,0x86,
+		0x51,0xc9,0x27,0x6d,0x8e,0x44,0x4e,0xcb,0x95,0xa0,0x54,0x0e,0xe7,0xea,0x32,0x20,
+		0xa9,0x22,0x34,0x5e,0xd9,0x9e,0xf7,0xe7,0xad,0x32,0xb1,0x9d,0x46,0x10,0xe9,0xef),
+	.out = chunk_from_chars(
+		0x29,0xbd,0x11,0x55,0x68,0xae,0x09,0x88,0x27,0x0f,0xc3,0x86,0xd3,0x95,0xfe,0x37,
+		0x07,0xa4,0xd0,0x62,0x89,0xf3,0x52,0xbb,0xa4,0xc0,0x0a,0x9a,0xd8,0x55,0xa0,0x8d),
+};
+
+kdf_test_vector_t prf_sha384_1 = {
+	.alg = KDF_PRF, .arg.prf = PRF_HMAC_SHA2_384,
+	.key = chunk_from_chars(
+		0xd6,0x72,0xb0,0xbc,0x85,0x28,0x29,0xb9,0x35,0x09,0xf3,0xb7,0x24,0x70,0x63,0x64),
+	.salt = chunk_from_chars(
+		0x4d,0xf0,0x40,0xb7,0x09,0x78,0x62,0x9c,0x49,0x43,0x7c,0xff,0x41,0xa0,0xd4,0x6c,
+		0xbe,0xa6,0x8c,0x8d,0x75,0xb3,0x70,0xff,0xc1,0x1d,0x7b,0x38,0x71,0x44,0xea,0x83,
+		0xbb,0x59,0x03,0xfb,0xb9,0x2e,0x47,0x3d,0xf5,0x0a,0x9b,0x19,0xea,0x43,0xe9,0xc2,
+		0xf3,0xda,0x9a,0x84,0x9c,0x03,0x86,0x42,0x76,0xc6,0xf2,0x64,0xec,0xf0,0x2a,0x60,
+		0xd4,0x0b,0xa6,0x5c,0x06,0x65,0x6b,0x63,0x3f,0x02,0xa8,0x74,0x27,0xe9,0x28,0xeb,
+		0x66,0xa2,0xda,0xbd,0x9d,0xc0,0x57,0x44,0x71,0x7b,0xca,0xf7,0xae,0x78,0xc2,0x96,
+		0x87,0x2f,0x5c,0x48,0xd1,0xa8,0x12,0x0c,0x21,0x55,0xb7,0x0c,0x56,0x5c,0xe2,0x71,
+		0x99,0x8e,0x3e,0x44,0xaf,0x26,0x3d,0x48,0x7e,0xa3,0xba,0x7f,0x56,0x13,0x2d,0x7d),
+	.out = chunk_from_chars(
+		0x54,0x43,0x6a,0x9e,0xa9,0x5d,0x6f,0xf7,0x9b,0x96,0x7f,0x4b,0x07,0xf6,0xde,0x97,
+		0x6a,0x37,0x6e,0x8e,0xa2,0x6a,0xa9,0x57,0x47,0x09,0xaf,0xc6,0x02,0x43,0xc9,0xc1,
+		0x41,0xda,0x4c,0xa0,0xe1,0x58,0xe6,0x27,0xa7,0x5e,0xa8,0x7f,0x6f,0xeb,0x07,0xef),
+};
+
+kdf_test_vector_t prf_sha384_2 = {
+	.alg = KDF_PRF, .arg.prf = PRF_HMAC_SHA2_384,
+	.key = chunk_from_chars(
+		0x9e,0x2a,0x62,0xf9,0x36,0x28,0x93,0xdd,0xf8,0x47,0x16,0xfe,0xc2,0xf2,0x3f,0x9d,
+		0xcb,0xd9,0x01,0x0d,0xf6,0xfe,0x9e,0x0e,0xb4,0x6d,0x03,0xd9,0x14,0xf3,0x04,0xd8,
+		0xfe,0x4d,0x3e,0xe2,0xd6,0xa0,0x3b,0x40,0xe5,0x6a,0x32,0x5e,0x82,0x2a,0x17,0x36,
+		0x19,0x29,0x18,0x4a,0xde,0x09,0xea,0xa4,0x45,0x27,0x8d,0x38,0x70,0x41,0x7a,0x7c,
+		0xf5,0x65,0x58,0x4f,0x57,0x2a,0xd3,0x4f,0xf7,0x2b,0xc7,0x78,0x1a,0x39,0xa4,0x8b,
+		0x54,0xb5,0x5d,0x6e,0xb0,0xed,0x68,0x55,0x1b,0x22,0x2c,0x7a,0xfa,0xda,0x0b,0xc8,
+		0x22,0x36,0xec,0x31,0xce,0x6c,0x04,0x6b,0x3b,0x2e,0xdb,0x2d,0xef,0x61,0xf4,0xd2,
+		0xd8,0x57,0xb2,0xd1,0xcb,0x36,0x96,0xc0,0x23,0xe0,0x8c,0x08,0xd6,0xab,0xd0,0x4d,
+		0x4f,0x69,0x09,0x2b,0x14,0x58,0x37,0x35,0xb9,0xe9,0x18,0xae,0xe5,0xa3,0x99,0x9d,
+		0xcb,0xf5,0x8f,0xda,0xd6,0xfc,0xd4,0x7c,0x95,0x92,0x98,0x77,0x03,0x0e,0x54,0xb7,
+		0x08,0x23,0x5f,0x2a,0x2e,0x11,0xe7,0xc5,0x85,0x84,0x61,0x9a,0xa2,0xfa,0x69,0x31,
+		0x53,0x44,0xd3,0x65,0x7b,0x55,0x72,0x0a,0x25,0xeb,0xe8,0x8e,0xa0,0x77,0x69,0x72,
+		0xc9,0xe2,0x24,0x69,0xb7,0xed,0x5d,0xa9,0x6b,0x3c,0x76,0x85,0xf7,0xb0,0x56,0x99,
+		0x60,0xbd,0x64,0x4f,0x13,0x0b,0x44,0xa0,0xd5,0x51,0xbb,0x0e,0x90,0x2e,0xd6,0x8a,
+		0xb3,0x84,0xfd,0xc2,0xfa,0xca,0xf7,0x9b,0xbf,0x8d,0x6d,0x37,0x36,0xdf,0xa9,0x52,
+		0xcd,0x70,0xf4,0x74,0x6f,0x1a,0x4a,0xea,0xc2,0xbd,0xbe,0xca,0x97,0xb7,0x8f,0xc1,
+		0x77,0x78,0x78,0xc1,0x28,0x59,0x43,0x35,0x52,0xa7,0x7d,0x7f,0x94,0x2e,0x5b,0x60,
+		0x47,0x69,0x91,0xa1,0xe3,0xd0,0x42,0x7c,0xd6,0x77,0x34,0x5a,0x1c,0xe2,0x06,0x3a,
+		0x2e,0x0e,0xc5,0x47,0xa9,0xd8,0x21,0xda,0x75,0x9f,0x1a,0x91,0xb5,0x88,0x17,0xbd,
+		0x0d,0xc4,0xef,0xfc,0x12,0x6f,0x6f,0x4e,0xb0,0xb9,0x11,0xe9,0x04,0xed,0x21,0xdc,
+		0x43,0x9d,0x65,0x8a,0x77,0x3f,0x97,0xe1,0x79,0xad,0x20,0xbc,0x3c,0x63,0x60,0x9f,
+		0x28,0x74,0x06,0x2e,0x83,0x5f,0x6a,0xe0,0x8d,0x59,0x65,0x4f,0x9c,0x88,0x61,0xe5,
+		0x27,0x03,0x9e,0xea,0xc3,0x2b,0x9e,0xed,0x29,0x3b,0xd8,0xb1,0xe0,0xe7,0xc6,0x7b,
+		0xf1,0xd7,0x55,0x24,0x6a,0x1b,0x06,0x3f,0xf0,0x06,0x32,0xa4,0x6d,0xd5,0xcf,0x69,
+		0x48,0xf0,0xee,0xd9,0xb6,0x5c,0x59,0x39,0xdf,0xd2,0x14,0x80,0xa6,0x3d,0xf0,0xca,
+		0xb0,0xa7,0x50,0x9c,0x42,0x45,0xa1,0xbe,0x75,0x47,0xcc,0xc6,0xf7,0x3c,0x72,0x4b,
+		0x48,0xb7,0x86,0x70,0x12,0xe0,0xca,0x3e,0x47,0x2e,0x0d,0x55,0x06,0x49,0xa7,0x34,
+		0x44,0x0f,0xef,0xfc,0x8e,0x73,0x68,0x2c,0xb6,0x04,0x53,0xe7,0xa4,0x7e,0x72,0xfe,
+		0x08,0x74,0xeb,0x40,0xac,0xd0,0xd4,0x8e,0x4e,0x57,0x19,0x74,0x16,0x11,0x2b,0xcd,
+		0xc8,0xbb,0x7a,0x58,0xbe,0xa7,0x45,0xfd,0xd6,0x4c,0x16,0xf6,0x66,0xc8,0x8d,0x9e,
+		0x3b,0xd2,0x35,0xb1,0x37,0x20,0x6f,0x6c,0xdb,0xa1,0x90,0xbe,0x65,0xec,0x03,0x3c,
+		0x19,0x1f,0x67,0x6b,0x42,0x8e,0xc1,0x20,0x5d,0xc5,0xe9,0x45,0x82,0x85,0x08,0xd8),
+	.salt = chunk_from_chars(
+		0xae,0x50,0x50,0x82,0xac,0x47,0xff,0x9a,0xa3,0x54,0xb7,0xaf,0x2b,0x07,0x2c,0xb4,
+		0x9c,0xec,0x83,0x8d,0x00,0xee,0x36,0x13,0x88,0x1a,0x99,0x77,0xb2,0x15,0x95,0x99,
+		0xa0,0x24,0x95,0xf0,0xe5,0x2d,0x96,0x1a,0x51,0x6c,0x6b,0xb6,0x1e,0xd0,0x3a,0x86,
+		0x37,0xbb,0x50,0x7c,0x5c,0x27,0xba,0xb5,0x8d,0xf1,0x54,0xe8,0xe5,0x01,0x48,0x21,
+		0x84,0x0c,0xfc,0x50,0xb3,0xa4,0x78,0xb4,0x5d,0xd1,0x68,0xeb,0x18,0x0d,0x69,0xcb,
+		0xa6,0x1a,0x1b,0x42,0x59,0x19,0x3a,0x51,0xa7,0xa4,0x95,0xc9,0x58,0x05,0x38,0x2e,
+		0x3a,0xbf,0x55,0x87,0x68,0x8f,0x34,0xb6,0x3f,0x71,0x16,0x39,0x82,0xde,0x3d,0xdf,
+		0x7f,0x26,0x3b,0xb6,0x9f,0x65,0xc3,0xec,0xae,0x61,0x65,0xbf,0x7f,0xdd,0x53,0x17),
+	.out = chunk_from_chars(
+		0xff,0x66,0xe9,0xd0,0x92,0xdc,0x01,0xe0,0xb8,0x1f,0x93,0x9f,0x52,0xf5,0xc0,0x7d,
+		0x38,0xd8,0x05,0xb9,0x86,0x28,0xce,0x1a,0xc5,0xfe,0x94,0xc0,0x98,0x57,0x76,0x47,
+		0x33,0x9f,0xad,0x68,0x94,0x1f,0xfe,0x21,0xe0,0x1e,0xfb,0x4e,0x70,0x50,0x21,0x3b),
+};
+
+kdf_test_vector_t prf_sha512_1 = {
+	.alg = KDF_PRF, .arg.prf = PRF_HMAC_SHA2_512,
+	.key = chunk_from_chars(
+		0xdf,0x79,0x31,0xdb,0x9b,0x42,0x9e,0x10,0xb8,0xaa,0x8e,0x4d,0x46,0x04,0x23,0x93),
+	.salt = chunk_from_chars(
+		0x1e,0x3b,0x00,0x7d,0x2d,0xa9,0x13,0xca,0x60,0xec,0xc9,0x8c,0x25,0xa2,0x2d,0xb0,
+		0x80,0x73,0xd5,0xc3,0x5c,0x11,0xb2,0x52,0x4b,0x29,0x8a,0x92,0x2b,0x6a,0xbf,0xe6,
+		0xac,0xf7,0x35,0x9d,0xb6,0x6a,0xe5,0xf8,0x5d,0x67,0xaa,0xcf,0xf6,0x86,0x41,0x9c,
+		0xd8,0x66,0x6d,0x05,0xae,0x79,0x77,0xce,0xfa,0xd7,0xf5,0x4d,0xd8,0xe3,0x12,0xa8,
+		0xe8,0xe5,0xe0,0x37,0x0f,0x88,0x14,0x2f,0xbd,0xd6,0x59,0xdd,0x6f,0xde,0x22,0xbd,
+		0xd5,0x31,0xf5,0x40,0x28,0x81,0xa8,0xde,0x85,0xc1,0x02,0x4e,0x59,0x5e,0xc9,0x3c,
+		0x57,0x56,0x18,0xaf,0x7f,0xd3,0xdb,0xac,0x79,0x82,0x91,0x90,0x78,0xd7,0x1c,0xc1,
+		0x3e,0xff,0x19,0x10,0xa0,0x32,0x75,0x0e,0x1f,0xf4,0x28,0x67,0x5d,0xe1,0x89,0xee),
+	.out = chunk_from_chars(
+		0xda,0xf9,0xbd,0x6f,0x2f,0x91,0x2d,0xa5,0x53,0x86,0x79,0x66,0xaf,0x38,0x6e,0x67,
+		0x90,0x9a,0x8d,0xf0,0xca,0x7e,0x84,0xb8,0x3b,0x35,0x5c,0xb7,0xd7,0xf1,0x02,0x6f,
+		0x17,0xd8,0xea,0x34,0xb5,0xd5,0x7f,0xd0,0xd1,0xba,0x38,0x95,0x28,0xfc,0xa1,0xe8,
+		0x1d,0x1c,0x8c,0xe5,0x11,0xb2,0x8a,0x24,0x58,0x24,0x11,0x43,0xfe,0xe3,0x0c,0xcc),
+};
+
+kdf_test_vector_t prf_sha512_2 = {
+	.alg = KDF_PRF, .arg.prf = PRF_HMAC_SHA2_512,
+	.key = chunk_from_chars(
+		0xd8,0x96,0x84,0xe3,0xcb,0x17,0xf3,0xaa,0xbd,0x85,0x3a,0x78,0xdb,0x3e,0xcd,0x5a,
+		0xac,0xc1,0xed,0x71,0xc7,0x0b,0x88,0xa2,0x97,0x56,0xf4,0x6f,0xc7,0x19,0x7c,0x80,
+		0x4e,0xc0,0x01,0x54,0x40,0x02,0xa8,0xae,0xa3,0x60,0x68,0x4b,0x18,0x00,0x6d,0xef,
+		0x0d,0xbd,0x86,0x33,0xb1,0x01,0x9f,0xbc,0xfa,0x85,0xb9,0x4c,0xac,0x2b,0xb8,0x21,
+		0x25,0x84,0xbe,0x62,0xad,0xab,0x0e,0xe4,0xbb,0x8a,0x36,0xae,0xe2,0x52,0x75,0xef,
+		0x07,0x13,0x90,0x48,0x0e,0xef,0xa2,0x09,0x2e,0xb3,0x08,0xaa,0x73,0x37,0xc5,0xce,
+		0xb6,0x06,0x9a,0xb6,0x90,0xe8,0x96,0x2f,0xbf,0xe0,0x98,0x6e,0x4f,0x5c,0x18,0xf4,
+		0x86,0x86,0x1e,0xd3,0xf1,0xdc,0xbe,0xe4,0xc9,0xe4,0xa7,0x66,0x9d,0x74,0x0c,0xa2,
+		0xb0,0xe8,0xed,0x40,0x31,0xb0,0xa4,0x99,0xdc,0x31,0x5c,0xed,0xe7,0xef,0x03,0x39,
+		0x9c,0xbc,0x33,0xdc,0xd6,0x29,0x70,0x34,0x9f,0x12,0x20,0x88,0x1b,0x55,0x45,0x2e,
+		0x0c,0x6c,0x9b,0x52,0xa8,0x8b,0x67,0xf5,0x97,0x58,0x67,0x95,0xb2,0x25,0x70,0x73,
+		0x3f,0xd4,0xff,0x2c,0xc2,0xad,0x93,0x1d,0x83,0x30,0x16,0x5a,0x9e,0x45,0x0e,0x38,
+		0x88,0x59,0xce,0x62,0x4f,0x01,0xdb,0x17,0xc2,0x50,0x2b,0x4e,0x66,0xad,0xf9,0x65,
+		0x27,0x36,0x3d,0x6f,0x90,0x6b,0x20,0x23,0xe8,0xed,0x74,0xd5,0xaf,0x0a,0xa6,0x02,
+		0x46,0xb0,0xb9,0x2f,0x49,0xc4,0x93,0x3e,0xf3,0x12,0xf8,0xa2,0x54,0x34,0xee,0x96,
+		0x98,0xd6,0xd9,0x20,0x43,0x45,0xbd,0x10,0xbb,0x11,0xaa,0x39,0x86,0x56,0x16,0xd5,
+		0xad,0x1b,0x57,0x44,0x70,0x6c,0xfd,0x4e,0xa1,0x40,0x8e,0x20,0xc8,0xfd,0xcf,0x85,
+		0x51,0xee,0xe8,0x81,0x4b,0x7b,0x37,0x33,0x0b,0x05,0x26,0xf0,0xbc,0x5e,0xe1,0x5d,
+		0x4e,0xcd,0xa7,0xa1,0xbd,0x25,0xaa,0x97,0xf2,0x45,0x84,0xd8,0x5d,0x3f,0x52,0x49,
+		0x69,0x4b,0x9f,0x43,0x53,0x9e,0x69,0xea,0x35,0xbf,0xe7,0xfd,0x44,0x07,0xbc,0x8e,
+		0x9d,0xca,0x8a,0x9f,0xae,0x4b,0xdc,0x6b,0x7b,0xb3,0x8c,0x6d,0x68,0xf8,0x99,0xe1,
+		0xd3,0x2c,0x85,0xbc,0xd6,0x17,0xa5,0x67,0x67,0x8c,0xf8,0x5d,0x22,0x17,0xa4,0xe8,
+		0x6a,0x75,0x56,0x24,0xb6,0x40,0x02,0x35,0x4c,0x02,0x68,0x42,0xbc,0x95,0x42,0x49,
+		0x1a,0xf1,0xc3,0xd6,0x29,0x09,0x70,0x55,0x9a,0xf1,0x1b,0xdc,0x2c,0x83,0xb5,0x4c,
+		0x74,0x14,0x49,0x05,0xc0,0xa3,0x58,0xf3,0x15,0x3d,0xb7,0x67,0xa5,0xda,0x2a,0x86,
+		0x27,0xf6,0x96,0x27,0xe1,0xd4,0x1e,0xde,0x9c,0x90,0x7c,0x79,0xb5,0x1f,0xf8,0x15,
+		0xe4,0x64,0x5c,0x33,0x75,0xe0,0xf6,0x3f,0x84,0xfc,0xf5,0xd7,0xc3,0x40,0x7a,0x1d,
+		0xd6,0x83,0x9e,0x19,0x06,0xa1,0xe3,0x80,0x2c,0xcf,0x5e,0x82,0x30,0xd9,0x5c,0xf4,
+		0xb8,0x27,0xb4,0x1c,0x48,0x34,0x25,0xa8,0xa6,0x0b,0xfa,0x51,0x89,0xda,0xc4,0x38,
+		0x06,0x0f,0x2f,0x5c,0xd5,0x26,0x66,0x2f,0x29,0x06,0xc1,0xdd,0x64,0xf4,0x84,0x4e,
+		0x94,0x2c,0xa8,0x4d,0xae,0xce,0x6d,0xd7,0xbb,0xf7,0x19,0x4d,0x8c,0xe5,0x6b,0xc2,
+		0x83,0x10,0x85,0xa7,0xd3,0x10,0xe4,0x94,0x4c,0xfa,0xe7,0x62,0x60,0xaa,0xbf,0x6b),
+	.salt = chunk_from_chars(
+		0x9c,0xb4,0xbf,0x24,0x46,0x17,0x0a,0xc3,0x81,0x02,0x52,0x66,0xa1,0xa4,0xb8,0x65,
+		0x13,0xdf,0x60,0xea,0x7d,0x07,0xb1,0xb2,0x13,0x9d,0x78,0xf3,0x31,0xef,0x7e,0xb7,
+		0xbe,0x8b,0x15,0xd8,0x6c,0xd8,0x5e,0x2a,0x6a,0x34,0xa5,0x58,0x69,0xf8,0xdc,0xc9,
+		0x75,0x4f,0x49,0x69,0x73,0x31,0xb9,0xb6,0x50,0xce,0x25,0x6e,0xdb,0x33,0x71,0xa9,
+		0x4b,0x7c,0x2a,0x13,0x2f,0x2f,0xc9,0x9d,0x22,0x30,0x37,0x17,0xc3,0x67,0x39,0xd1,
+		0x7c,0x0e,0x97,0x18,0xd7,0xc0,0x52,0xe7,0xab,0x4d,0x48,0x58,0xad,0xeb,0x9b,0x8b,
+		0x4d,0x33,0x03,0xa9,0xe1,0xb6,0xbe,0xf2,0x9f,0x03,0x03,0x5b,0xee,0xd0,0x71,0xd3,
+		0xbb,0x81,0x0d,0x85,0x39,0x71,0xa7,0x48,0xc5,0x6c,0x59,0xe0,0xba,0xfb,0x9b,0x5a),
+	.out = chunk_from_chars(
+		0xe5,0x70,0xe7,0x48,0x46,0x34,0x30,0x0d,0x7a,0xdd,0xf5,0xa8,0x52,0x7c,0x13,0x8b,
+		0x76,0x96,0xdb,0xc3,0xd8,0xbe,0x09,0x69,0xb7,0x52,0x15,0x31,0x8a,0x11,0xad,0xa2,
+		0x13,0x53,0x8f,0x62,0x93,0xb0,0xee,0xe5,0xb3,0x09,0xad,0x8f,0x5d,0x8d,0x94,0xdb,
+		0xe5,0x73,0x61,0x27,0xe0,0xd2,0x56,0x0f,0x28,0x1c,0x9c,0x58,0x6b,0xf7,0xc8,0x6f),
+};
diff --git a/src/libstrongswan/plugins/test_vectors/test_vectors/kdf_prf_plus.c b/src/libstrongswan/plugins/test_vectors/test_vectors/kdf_prf_plus.c
new file mode 100644
index 000000000000..c8ace32967b2
--- /dev/null
+++ b/src/libstrongswan/plugins/test_vectors/test_vectors/kdf_prf_plus.c
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2022 Tobias Brunner, codelabs GmbH
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <crypto/crypto_tester.h>
+
+/**
+ * This is an old test vector derived from one in RFC 4868.
+ */
+kdf_test_vector_t prf_plus_sha256_old = {
+	.alg = KDF_PRF_PLUS, .arg.prf = PRF_HMAC_SHA2_256,
+	.key = chunk_from_chars(
+		0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,
+		0x0b,0x0b,0x0b,0x0b),
+	.salt = chunk_from_chars(
+		0x48,0x69,0x20,0x54,0x68,0x65,0x72,0x65),
+	.out = chunk_from_chars(
+		0xb9,0xbd,0xc0,0x89,0x88,0xb4,0xc2,0xb7,0x5a,0xa9,0x3e,0x59,0x6a,0xc8,0x42,0x05,
+		0xfa,0x2d,0xdd,0xe1,0xbf,0x7a,0x25,0x72,0x06,0x7b,0x00,0xe1,0x4b,0x23,0x77,0x32,
+		0x83,0x05,0x09,0x98,0x1a,0xd2,0xf9,0x4a,0x8c,0x32,0xa4,0x7d,0xaa,0x22,0x55,0xb6,
+		0x60,0xc4,0x36,0x34,0x7a,0xe7,0x56,0xa6,0xed,0xc0,0x23,0x47,0x7d,0x80,0x95,0x90,
+		0xe6,0x82,0xf6,0x1d,0x9c,0x04,0xb0,0x6b,0x4a,0xd9,0x71,0xa3,0x4c,0x81,0x47,0xfa,
+		0x66,0x79,0x2f,0xf1,0x43,0x4b,0x93,0xc7,0x22,0xb3,0x2e,0x12,0xf4,0x88,0x32,0xeb,
+		0xc1,0x5c,0xe2,0x36,0x9c,0xe7,0x1f,0xe9,0xb7,0xb8,0x1e,0x57,0x04,0xc1,0x4d,0x0f,
+		0x52,0x80,0xa6,0xec,0x62,0x6e,0x99,0x2d,0x7a,0x9f),
+};
+
+/**
+ * The following test vectors are from CAVP/SP 800-135 for IKEv2.
+ *
+ * key = SKEYSEED, salt = Ni | Nr | SPIi | SPIr (one vector with min. and one
+ * with max. size for nonces)
+ */
+kdf_test_vector_t prf_plus_sha256_1 = {
+	.alg = KDF_PRF_PLUS, .arg.prf = PRF_HMAC_SHA2_256,
+	.key = chunk_from_chars(
+		0x37,0xfd,0xe9,0x0b,0x81,0xd6,0x36,0x92,0x62,0x0f,0x67,0x36,0x7b,0x62,0x09,0x2e,
+		0x3e,0xfd,0xc6,0xa0,0x35,0x09,0x13,0x7d,0x73,0x10,0x04,0x76,0x7f,0xf3,0x50,0x10),
+	.salt = chunk_from_chars(
+		0xed,0x80,0xdc,0x79,0x91,0x2c,0x32,0xa9,0x35,0xfb,0x6d,0x1a,0x3f,0xea,0xc0,0x78,
+		0x47,0xc1,0x85,0x8e,0xfc,0x93,0x2e,0xa4,0x60,0x6f,0xd0,0x56,0x09,0x62,0x40,0x02),
+	.out = chunk_from_chars(
+		0x63,0xd0,0x18,0xa0,0x8f,0x7a,0x29,0xda,0xa7,0xd1,0xf9,0x2c,0xb6,0x3f,0x45,0xfa,
+		0x39,0xbb,0xaf,0x5e,0x5f,0x1a,0x78,0x13,0x96,0x13,0xbb,0x89,0x1e,0x3e,0x6b,0xf9,
+		0xd0,0x44,0x8f,0x7c,0x74,0xe0,0x5b,0x4c,0x1a,0x25,0xc0,0xb6,0x1e,0x62,0xc8,0x75,
+		0xe8,0x52,0xf2,0x87,0xe1,0x92,0xd4,0xae,0x53,0x61,0x12,0xdb,0x51,0x97,0x21,0x64,
+		0x00,0xbd,0x5e,0x12,0x34,0x70,0xef,0xc1,0x5f,0x53,0x16,0x18,0x65,0xfe,0x8b,0x19,
+		0xa1,0x41,0x65,0x18,0xa1,0x57,0xd6,0x51,0xec,0xd2,0xca,0xdd,0xab,0x6a,0x9e,0xae,
+		0x6a,0x27,0xa5,0x5a,0xf0,0x88,0x21,0x8d,0x51,0x56,0xed,0xaa,0x97,0x89,0xf9,0x34,
+		0xd9,0x83,0x6e,0xd4,0x3e,0xdf,0xfd,0xa5,0x53,0x82,0x22,0x02,0xff,0x9f,0x7d,0x48,
+		0x60,0xca,0x72,0xe0),
+};
+
+kdf_test_vector_t prf_plus_sha256_2 = {
+	.alg = KDF_PRF_PLUS, .arg.prf = PRF_HMAC_SHA2_256,
+	.key = chunk_from_chars(
+		0x29,0xbd,0x11,0x55,0x68,0xae,0x09,0x88,0x27,0x0f,0xc3,0x86,0xd3,0x95,0xfe,0x37,
+		0x07,0xa4,0xd0,0x62,0x89,0xf3,0x52,0xbb,0xa4,0xc0,0x0a,0x9a,0xd8,0x55,0xa0,0x8d),
+	.salt = chunk_from_chars(
+		0xef,0xa7,0x29,0x13,0x18,0x22,0x78,0xff,0xbd,0x14,0xe7,0x89,0x20,0xc0,0x62,0x51,
+		0x9a,0xba,0xb8,0xc1,0x6e,0x5e,0xd7,0x0c,0x08,0x41,0xa4,0x8c,0xdb,0x98,0x23,0x7e,
+		0xe9,0x3f,0x73,0x5f,0xb2,0xdf,0x18,0x43,0x58,0xaa,0xdc,0x13,0xd8,0x3f,0x43,0xfe,
+		0x8d,0x87,0x05,0x17,0x6c,0x8c,0xa3,0x13,0x82,0x5a,0x1b,0xcd,0xf7,0x79,0x11,0xc5,
+		0x98,0x00,0x16,0x71,0xff,0xbf,0x01,0x4e,0x37,0xa8,0xc0,0x4d,0x49,0xa7,0x83,0x9d,
+		0xfe,0xa6,0xcd,0xc5,0x87,0x68,0x8d,0x45,0x88,0xfe,0x43,0x23,0x5b,0x71,0x69,0x3f,
+		0xfd,0x07,0x29,0x33,0x86,0xb6,0xbf,0x4c,0x19,0x9e,0x33,0x61,0x65,0xb2,0x60,0x78,
+		0x77,0x36,0xf5,0x4b,0xe9,0x5d,0xb6,0x91,0x16,0x38,0x8b,0xc2,0xec,0xa2,0xb3,0xb2,
+		0x94,0x84,0x71,0x74,0x17,0xbb,0x3c,0x71,0x81,0x4c,0xe1,0x3b,0x84,0x44,0x6d,0xc3,
+		0x96,0x4c,0x30,0x29,0x84,0xf9,0x77,0x81,0xf6,0x31,0x66,0x24,0x08,0x90,0x10,0x7c,
+		0x2e,0x75,0x1a,0x00,0x43,0x6f,0x7c,0x3c,0x9f,0xf1,0x27,0x60,0xe4,0x9d,0x91,0x56,
+		0x3b,0xe6,0x03,0xfd,0x96,0x41,0xa0,0xa6,0x49,0x18,0xa9,0x32,0x91,0xed,0x11,0x3d,
+		0xb1,0x2f,0x97,0x07,0x60,0x9d,0x17,0x20,0x96,0xeb,0x58,0xf9,0x15,0x44,0x74,0xda,
+		0x40,0xc1,0xf5,0xc0,0x90,0x3e,0x9c,0xa2,0xf9,0x1b,0xa6,0x60,0x07,0x75,0xdf,0x71,
+		0x66,0xca,0xf8,0xe3,0x27,0x85,0x9e,0x67,0x62,0x32,0xd3,0x40,0x46,0x04,0x4c,0xee,
+		0x43,0xf9,0x01,0x9f,0x04,0x68,0x56,0x12,0x63,0x5b,0x99,0xcb,0xeb,0xcb,0x36,0x3d,
+		0x56,0x5e,0xaf,0x0e,0x54,0x7e,0xec,0xb9,0x41,0xc9,0x94,0xdf,0xd4,0x71,0xed,0x56,
+		0x43,0xc0,0x87,0x74,0x4f,0x77,0x09,0xcc,0x3e,0x25,0x10,0xf2,0x74,0x26,0xc6,0x2c,
+		0x0f,0xf3,0xac,0xb3,0xc2,0x76,0x61,0xd2,0x6a,0x6d,0x83,0xc2,0xa2,0x5e,0x13,0xa6,
+		0xd4,0x65,0xbd,0x04,0x7f,0x90,0x55,0x00,0xe5,0xeb,0xbe,0x42,0x66,0x43,0x0d,0x56,
+		0x67,0x14,0x0f,0x77,0xe7,0x97,0x71,0x2a,0x8c,0x8f,0x63,0xc5,0x83,0xf4,0xb4,0x64,
+		0x9b,0x72,0x89,0x9e,0xa3,0x4a,0xbf,0xdb,0x17,0x61,0x7c,0x46,0x0c,0x35,0xf2,0x50,
+		0x64,0x94,0x49,0x4f,0x22,0x3e,0x25,0x1a,0xc7,0x1a,0x5b,0x9b,0x7e,0xea,0x87,0xf4,
+		0xf5,0xe3,0x33,0xa3,0xc1,0xbb,0xb4,0xbb,0x09,0x25,0x8b,0x6a,0x4b,0x5f,0x8c,0x9f,
+		0xb8,0x2b,0xf4,0x2d,0xa9,0xd4,0xa4,0x65,0x43,0xc6,0xa9,0xeb,0x9a,0xa3,0x0e,0xa9,
+		0xda,0x80,0x19,0x15,0xab,0xcc,0x17,0x12,0xd0,0x82,0xf2,0x92,0xa6,0x3f,0xd9,0xaf,
+		0x71,0x54,0xa9,0x7c,0xc7,0x38,0x59,0xa4,0xbe,0x3c,0xba,0x35,0x9d,0x32,0x18,0x00,
+		0x4e,0x14,0xdf,0x02,0xd0,0x9e,0xdf,0x0a,0xd5,0x79,0x6b,0xb0,0x10,0x99,0x52,0x93,
+		0xab,0x5d,0x04,0x2c,0x31,0x05,0x53,0x80,0xcc,0x9c,0xb2,0xe3,0x61,0x79,0x82,0xc4,
+		0x5f,0x6b,0xce,0x1f,0xb8,0xa4,0x0e,0xf9,0xea,0xc4,0x8a,0xe1,0x77,0x20,0xdf,0xec,
+		0xc7,0x1e,0xc8,0x57,0xea,0x33,0xf3,0x2e,0xb3,0x46,0xba,0x60,0x36,0xe8,0xf9,0xcc,
+		0xd7,0xbc,0xad,0xc6,0xc1,0xab,0x92,0xa8,0x0c,0x57,0xe7,0x89,0x59,0xd8,0xb8,0x28,
+		0x57,0xe9,0x1d,0xf9,0xc5,0xff,0xb8,0x42,0x4d,0x5e,0xad,0xac,0x0e,0x57,0x0f,0x7c),
+	.out = chunk_from_chars(
+		0xd5,0x03,0x3d,0x08,0x79,0x34,0xc6,0x15,0x38,0xb2,0x3d,0xff,0x87,0x5b,0x3b,0xa9,
+		0x20,0xe0,0x5c,0x1b,0x42,0xac,0x7f,0x97,0x93,0x99,0x5e,0x76,0xba,0x3e,0x46,0x1d,
+		0x6e,0x83,0xb2,0xfb,0xe0,0xfa,0x68,0x1b,0xa2,0x85,0x69,0x6d,0x53,0xb1,0x75,0xe0,
+		0x70,0xc4,0xc1,0xcb,0xc7,0x40,0x43,0xf7,0xca,0xc5,0x58,0xc4,0x94,0xac,0xd2,0x3a,
+		0xab,0xfc,0x7a,0x68,0x5b,0x62,0x74,0x13,0x44,0x08,0xca,0xfc,0x16,0x92,0x85,0x2a,
+		0xca,0x66,0x3c,0xef,0xd7,0xdd,0x0c,0x8a,0x87,0x2a,0x36,0xa1,0x41,0x4d,0xb4,0xb7,
+		0xf3,0x14,0xad,0x0e,0x49,0xf2,0xc1,0x87,0x16,0x2e,0x28,0x76,0xc6,0x05,0x3d,0xbe,
+		0xf5,0xa2,0xea,0x8c,0x33,0xdc,0xd3,0xdd,0x91,0x68,0x76,0x44,0x37,0x7c,0x45,0xf4,
+		0xb5,0xc4,0x18,0xe1,0xe1,0x0b,0xec,0x22,0xba,0xac,0x31,0x3b,0x90,0x2b,0xdf,0xfc,
+		0xfb,0x69,0xc6,0x8e,0xf4,0xe7,0x1c,0x5a,0xcc,0x39,0xa9,0xf0,0x7a,0xcb,0x1f,0xc3,
+		0xba,0x8e,0x92,0xc6,0xdc,0xa8,0x61,0xa6,0x41,0x63,0xb0,0x68,0x9d,0xa4,0xa9,0x7f,
+		0x12,0x40,0x26,0x36,0x09,0x90,0xc9,0xf1,0xae,0x4e,0x8d,0x02,0x98,0xf0,0x77,0xfc,
+		0xbb,0x0c,0x20,0x3f,0xd2,0xca,0x82,0x1d,0xea,0xb0,0x59,0x1a,0x22,0x1d,0x9d,0x0a,
+		0x79,0x8d,0x02,0xf0,0x78,0xd8,0xfb,0x6e,0x93,0x1c,0xa4,0x46,0xa1,0x3f,0x11,0x63,
+		0x8c,0x6b,0x6a,0xfb,0x82,0x25,0xa0,0x08,0xf9,0x38,0xa6,0x87,0x8c,0x5b,0x57,0x7e,
+		0x03,0xae,0xcf,0xa7,0x31,0x41,0x01,0xed,0xd0,0xfd,0xa3,0xbd,0xbe,0xa4,0x98,0x5c),
+};
+
+kdf_test_vector_t prf_plus_sha384_1 = {
+	.alg = KDF_PRF_PLUS, .arg.prf = PRF_HMAC_SHA2_384,
+	.key = chunk_from_chars(
+		0x54,0x43,0x6a,0x9e,0xa9,0x5d,0x6f,0xf7,0x9b,0x96,0x7f,0x4b,0x07,0xf6,0xde,0x97,
+		0x6a,0x37,0x6e,0x8e,0xa2,0x6a,0xa9,0x57,0x47,0x09,0xaf,0xc6,0x02,0x43,0xc9,0xc1,
+		0x41,0xda,0x4c,0xa0,0xe1,0x58,0xe6,0x27,0xa7,0x5e,0xa8,0x7f,0x6f,0xeb,0x07,0xef),
+	.salt = chunk_from_chars(
+		0xd6,0x72,0xb0,0xbc,0x85,0x28,0x29,0xb9,0x35,0x09,0xf3,0xb7,0x24,0x70,0x63,0x64,
+		0x0f,0x9a,0x01,0x6d,0x7b,0x22,0x22,0x3c,0x85,0xf7,0x10,0xda,0xf8,0xb9,0x82,0xd8),
+	.out = chunk_from_chars(
+		0x10,0xb0,0x0a,0x65,0x46,0x43,0xb3,0xb9,0x85,0x76,0x4f,0x86,0xe2,0x3e,0xf6,0x24,
+		0x31,0x12,0x2f,0xcc,0x96,0xde,0x16,0xe3,0x38,0xa3,0x72,0xbe,0xe4,0x93,0x91,0xdb,
+		0x1f,0x83,0xa7,0x35,0x8c,0x08,0x03,0x80,0xab,0xbf,0x4a,0xf4,0x7c,0xdb,0x83,0xb9,
+		0x53,0x03,0x43,0x16,0x8f,0x7d,0xbc,0x92,0x21,0x26,0x0e,0x15,0x46,0xc6,0xb3,0x83,
+		0xe2,0x23,0x5d,0x27,0x05,0xa7,0x27,0xf0,0xb4,0x1a,0xeb,0xf6,0xa1,0xb4,0xc6,0x1c,
+		0xb4,0x60,0x66,0x01,0x52,0xa1,0x7b,0xa7,0x4c,0x20,0xd5,0x19,0x1b,0xa6,0x84,0x17,
+		0x94,0x94,0x52,0x70,0xd8,0x29,0x58,0x7b,0x7f,0x32,0x94,0x63,0x9e,0xa7,0xb7,0x71,
+		0x49,0xe1,0xd0,0x72,0x30,0xac,0x16,0xe0,0xcd,0x5a,0xb2,0x36,0xfa,0x32,0x12,0xbd,
+		0x56,0x9e,0xe7,0x0f),
+};
+
+kdf_test_vector_t prf_plus_sha384_2 = {
+	.alg = KDF_PRF_PLUS, .arg.prf = PRF_HMAC_SHA2_384,
+	.key = chunk_from_chars(
+		0xff,0x66,0xe9,0xd0,0x92,0xdc,0x01,0xe0,0xb8,0x1f,0x93,0x9f,0x52,0xf5,0xc0,0x7d,
+		0x38,0xd8,0x05,0xb9,0x86,0x28,0xce,0x1a,0xc5,0xfe,0x94,0xc0,0x98,0x57,0x76,0x47,
+		0x33,0x9f,0xad,0x68,0x94,0x1f,0xfe,0x21,0xe0,0x1e,0xfb,0x4e,0x70,0x50,0x21,0x3b),
+	.salt = chunk_from_chars(
+		0x9e,0x2a,0x62,0xf9,0x36,0x28,0x93,0xdd,0xf8,0x47,0x16,0xfe,0xc2,0xf2,0x3f,0x9d,
+		0xcb,0xd9,0x01,0x0d,0xf6,0xfe,0x9e,0x0e,0xb4,0x6d,0x03,0xd9,0x14,0xf3,0x04,0xd8,
+		0xfe,0x4d,0x3e,0xe2,0xd6,0xa0,0x3b,0x40,0xe5,0x6a,0x32,0x5e,0x82,0x2a,0x17,0x36,
+		0x19,0x29,0x18,0x4a,0xde,0x09,0xea,0xa4,0x45,0x27,0x8d,0x38,0x70,0x41,0x7a,0x7c,
+		0xf5,0x65,0x58,0x4f,0x57,0x2a,0xd3,0x4f,0xf7,0x2b,0xc7,0x78,0x1a,0x39,0xa4,0x8b,
+		0x54,0xb5,0x5d,0x6e,0xb0,0xed,0x68,0x55,0x1b,0x22,0x2c,0x7a,0xfa,0xda,0x0b,0xc8,
+		0x22,0x36,0xec,0x31,0xce,0x6c,0x04,0x6b,0x3b,0x2e,0xdb,0x2d,0xef,0x61,0xf4,0xd2,
+		0xd8,0x57,0xb2,0xd1,0xcb,0x36,0x96,0xc0,0x23,0xe0,0x8c,0x08,0xd6,0xab,0xd0,0x4d,
+		0x4f,0x69,0x09,0x2b,0x14,0x58,0x37,0x35,0xb9,0xe9,0x18,0xae,0xe5,0xa3,0x99,0x9d,
+		0xcb,0xf5,0x8f,0xda,0xd6,0xfc,0xd4,0x7c,0x95,0x92,0x98,0x77,0x03,0x0e,0x54,0xb7,
+		0x08,0x23,0x5f,0x2a,0x2e,0x11,0xe7,0xc5,0x85,0x84,0x61,0x9a,0xa2,0xfa,0x69,0x31,
+		0x53,0x44,0xd3,0x65,0x7b,0x55,0x72,0x0a,0x25,0xeb,0xe8,0x8e,0xa0,0x77,0x69,0x72,
+		0xc9,0xe2,0x24,0x69,0xb7,0xed,0x5d,0xa9,0x6b,0x3c,0x76,0x85,0xf7,0xb0,0x56,0x99,
+		0x60,0xbd,0x64,0x4f,0x13,0x0b,0x44,0xa0,0xd5,0x51,0xbb,0x0e,0x90,0x2e,0xd6,0x8a,
+		0xb3,0x84,0xfd,0xc2,0xfa,0xca,0xf7,0x9b,0xbf,0x8d,0x6d,0x37,0x36,0xdf,0xa9,0x52,
+		0xcd,0x70,0xf4,0x74,0x6f,0x1a,0x4a,0xea,0xc2,0xbd,0xbe,0xca,0x97,0xb7,0x8f,0xc1,
+		0x77,0x78,0x78,0xc1,0x28,0x59,0x43,0x35,0x52,0xa7,0x7d,0x7f,0x94,0x2e,0x5b,0x60,
+		0x47,0x69,0x91,0xa1,0xe3,0xd0,0x42,0x7c,0xd6,0x77,0x34,0x5a,0x1c,0xe2,0x06,0x3a,
+		0x2e,0x0e,0xc5,0x47,0xa9,0xd8,0x21,0xda,0x75,0x9f,0x1a,0x91,0xb5,0x88,0x17,0xbd,
+		0x0d,0xc4,0xef,0xfc,0x12,0x6f,0x6f,0x4e,0xb0,0xb9,0x11,0xe9,0x04,0xed,0x21,0xdc,
+		0x43,0x9d,0x65,0x8a,0x77,0x3f,0x97,0xe1,0x79,0xad,0x20,0xbc,0x3c,0x63,0x60,0x9f,
+		0x28,0x74,0x06,0x2e,0x83,0x5f,0x6a,0xe0,0x8d,0x59,0x65,0x4f,0x9c,0x88,0x61,0xe5,
+		0x27,0x03,0x9e,0xea,0xc3,0x2b,0x9e,0xed,0x29,0x3b,0xd8,0xb1,0xe0,0xe7,0xc6,0x7b,
+		0xf1,0xd7,0x55,0x24,0x6a,0x1b,0x06,0x3f,0xf0,0x06,0x32,0xa4,0x6d,0xd5,0xcf,0x69,
+		0x48,0xf0,0xee,0xd9,0xb6,0x5c,0x59,0x39,0xdf,0xd2,0x14,0x80,0xa6,0x3d,0xf0,0xca,
+		0xb0,0xa7,0x50,0x9c,0x42,0x45,0xa1,0xbe,0x75,0x47,0xcc,0xc6,0xf7,0x3c,0x72,0x4b,
+		0x48,0xb7,0x86,0x70,0x12,0xe0,0xca,0x3e,0x47,0x2e,0x0d,0x55,0x06,0x49,0xa7,0x34,
+		0x44,0x0f,0xef,0xfc,0x8e,0x73,0x68,0x2c,0xb6,0x04,0x53,0xe7,0xa4,0x7e,0x72,0xfe,
+		0x08,0x74,0xeb,0x40,0xac,0xd0,0xd4,0x8e,0x4e,0x57,0x19,0x74,0x16,0x11,0x2b,0xcd,
+		0xc8,0xbb,0x7a,0x58,0xbe,0xa7,0x45,0xfd,0xd6,0x4c,0x16,0xf6,0x66,0xc8,0x8d,0x9e,
+		0x3b,0xd2,0x35,0xb1,0x37,0x20,0x6f,0x6c,0xdb,0xa1,0x90,0xbe,0x65,0xec,0x03,0x3c,
+		0x19,0x1f,0x67,0x6b,0x42,0x8e,0xc1,0x20,0x5d,0xc5,0xe9,0x45,0x82,0x85,0x08,0xd8,
+		0x6f,0xd4,0xbc,0x62,0x54,0x02,0xaa,0x68,0x5f,0x60,0x72,0x5e,0xfd,0xb4,0x25,0x96),
+	.out = chunk_from_chars(
+		0x28,0x10,0x28,0x8a,0x45,0xa5,0x02,0x21,0x1f,0xea,0xde,0x99,0x43,0xb4,0x22,0x4a,
+		0x3f,0x54,0xcf,0x97,0xf1,0x91,0xb6,0x21,0xd8,0x0a,0x19,0xf3,0xf1,0xb0,0x36,0x6d,
+		0x5e,0xd7,0x3d,0x19,0x5b,0x73,0x2a,0x15,0xb5,0x99,0x05,0x1a,0x13,0xb7,0xc5,0x84,
+		0x17,0x55,0x21,0xe3,0xa9,0x7c,0xdb,0x22,0x0c,0x89,0x24,0xb0,0xec,0x23,0x4a,0x94,
+		0x2e,0x05,0x79,0x67,0x5f,0x19,0x39,0x26,0xab,0x33,0x29,0x70,0x58,0x55,0x53,0xe2,
+		0x30,0xe3,0x42,0x9c,0x6e,0x8b,0xa7,0x1b,0x93,0x74,0xbd,0x4a,0x8d,0xf9,0x69,0x45,
+		0x68,0x40,0x52,0x7f,0x2c,0xf6,0x35,0x39,0x2b,0xe9,0xc1,0x7c,0xa8,0x47,0x4c,0xf0,
+		0x1b,0x33,0x2b,0x81,0x08,0xb5,0x4c,0x48,0xaa,0xfc,0x86,0xac,0x4c,0xfc,0xe7,0x05,
+		0x8e,0xe1,0x14,0x54,0x0e,0x5a,0x1b,0x95,0x22,0x01,0xee,0x3d,0x0a,0xc9,0xd3,0xe3,
+		0x3f,0x01,0x80,0x75,0x84,0x33,0x9a,0xc3,0xf4,0x3c,0xcd,0xd3,0x95,0xc5,0x8b,0xb3,
+		0xf4,0xa4,0xcc,0x5c,0x57,0x87,0x1e,0xa1,0xd3,0xea,0xa8,0xd0,0x30,0x2b,0xb3,0x08,
+		0xd1,0xd2,0x03,0xaf,0x13,0x14,0x7e,0x87,0xab,0x2b,0x91,0x59,0xae,0x36,0x1e,0xc0,
+		0x11,0xd6,0x73,0xfe,0x3f,0x16,0x2b,0x39,0x89,0xda,0x34,0x88,0xd8,0xff,0xea,0x2b,
+		0x51,0xe7,0x46,0x5f,0xad,0xdc,0xa6,0x7d,0x1d,0x54,0xc8,0x94,0x0a,0x8a,0xb6,0x2e,
+		0x8a,0xbe,0x41,0x45,0xcb,0x25,0x15,0x91,0x25,0x95,0x01,0x19,0xe3,0xc0,0x1a,0x81,
+		0x35,0x75,0xf0,0xa9,0x59,0xb1,0x43,0xbc,0x31,0xf6,0x2b,0x47,0x34,0xaa,0x45,0xe2),
+};
+
+kdf_test_vector_t prf_plus_sha512_1 = {
+	.alg = KDF_PRF_PLUS, .arg.prf = PRF_HMAC_SHA2_512,
+	.key = chunk_from_chars(
+		0xda,0xf9,0xbd,0x6f,0x2f,0x91,0x2d,0xa5,0x53,0x86,0x79,0x66,0xaf,0x38,0x6e,0x67,
+		0x90,0x9a,0x8d,0xf0,0xca,0x7e,0x84,0xb8,0x3b,0x35,0x5c,0xb7,0xd7,0xf1,0x02,0x6f,
+		0x17,0xd8,0xea,0x34,0xb5,0xd5,0x7f,0xd0,0xd1,0xba,0x38,0x95,0x28,0xfc,0xa1,0xe8,
+		0x1d,0x1c,0x8c,0xe5,0x11,0xb2,0x8a,0x24,0x58,0x24,0x11,0x43,0xfe,0xe3,0x0c,0xcc),
+	.salt = chunk_from_chars(
+		0xdf,0x79,0x31,0xdb,0x9b,0x42,0x9e,0x10,0xb8,0xaa,0x8e,0x4d,0x46,0x04,0x23,0x93,
+		0x9b,0xa4,0x0a,0xf9,0x15,0x7f,0x47,0x92,0x53,0x83,0xc7,0xcd,0x3d,0x10,0x46,0xe6),
+	.out = chunk_from_chars(
+		0x91,0x9a,0x75,0xde,0xae,0xcc,0xbd,0x39,0x3b,0xeb,0xc8,0x96,0x15,0xf8,0xa2,0x15,
+		0xed,0xb7,0x07,0x3b,0x23,0x19,0xd8,0x67,0x40,0x22,0x1e,0xee,0x7a,0x2d,0xeb,0x92,
+		0x50,0x90,0x44,0xd7,0xe6,0x1f,0xcb,0x1b,0xf9,0x67,0x6b,0x38,0x81,0xc7,0xce,0xd4,
+		0x9e,0x6b,0xba,0x15,0x49,0xda,0x31,0x13,0x60,0x16,0x77,0xa2,0x44,0x5d,0x91,0xac,
+		0x13,0xa5,0x97,0x33,0x57,0x82,0xdd,0x3a,0x9c,0xf4,0x96,0xd9,0xe1,0x9b,0x93,0x2f,
+		0x27,0xdd,0x35,0xe3,0x6f,0x80,0x70,0xd2,0x32,0xd0,0xa3,0xb3,0xf4,0xc4,0xa0,0xb4,
+		0xba,0x44,0x70,0x77,0x8d,0xc0,0x90,0xf3,0x1f,0x52,0xab,0x1b,0x6d,0x81,0xe4,0xc7,
+		0xdf,0xb3,0x50,0x63,0xad,0x96,0xc3,0x27,0xad,0xcd,0xbd,0xe7,0x14,0xcc,0x9a,0x6b,
+		0x5e,0xf0,0x49,0xb8),
+};
+
+kdf_test_vector_t prf_plus_sha512_2 = {
+	.alg = KDF_PRF_PLUS, .arg.prf = PRF_HMAC_SHA2_512,
+	.key = chunk_from_chars(
+		0xe5,0x70,0xe7,0x48,0x46,0x34,0x30,0x0d,0x7a,0xdd,0xf5,0xa8,0x52,0x7c,0x13,0x8b,
+		0x76,0x96,0xdb,0xc3,0xd8,0xbe,0x09,0x69,0xb7,0x52,0x15,0x31,0x8a,0x11,0xad,0xa2,
+		0x13,0x53,0x8f,0x62,0x93,0xb0,0xee,0xe5,0xb3,0x09,0xad,0x8f,0x5d,0x8d,0x94,0xdb,
+		0xe5,0x73,0x61,0x27,0xe0,0xd2,0x56,0x0f,0x28,0x1c,0x9c,0x58,0x6b,0xf7,0xc8,0x6f),
+	.salt = chunk_from_chars(
+		0xd8,0x96,0x84,0xe3,0xcb,0x17,0xf3,0xaa,0xbd,0x85,0x3a,0x78,0xdb,0x3e,0xcd,0x5a,
+		0xac,0xc1,0xed,0x71,0xc7,0x0b,0x88,0xa2,0x97,0x56,0xf4,0x6f,0xc7,0x19,0x7c,0x80,
+		0x4e,0xc0,0x01,0x54,0x40,0x02,0xa8,0xae,0xa3,0x60,0x68,0x4b,0x18,0x00,0x6d,0xef,
+		0x0d,0xbd,0x86,0x33,0xb1,0x01,0x9f,0xbc,0xfa,0x85,0xb9,0x4c,0xac,0x2b,0xb8,0x21,
+		0x25,0x84,0xbe,0x62,0xad,0xab,0x0e,0xe4,0xbb,0x8a,0x36,0xae,0xe2,0x52,0x75,0xef,
+		0x07,0x13,0x90,0x48,0x0e,0xef,0xa2,0x09,0x2e,0xb3,0x08,0xaa,0x73,0x37,0xc5,0xce,
+		0xb6,0x06,0x9a,0xb6,0x90,0xe8,0x96,0x2f,0xbf,0xe0,0x98,0x6e,0x4f,0x5c,0x18,0xf4,
+		0x86,0x86,0x1e,0xd3,0xf1,0xdc,0xbe,0xe4,0xc9,0xe4,0xa7,0x66,0x9d,0x74,0x0c,0xa2,
+		0xb0,0xe8,0xed,0x40,0x31,0xb0,0xa4,0x99,0xdc,0x31,0x5c,0xed,0xe7,0xef,0x03,0x39,
+		0x9c,0xbc,0x33,0xdc,0xd6,0x29,0x70,0x34,0x9f,0x12,0x20,0x88,0x1b,0x55,0x45,0x2e,
+		0x0c,0x6c,0x9b,0x52,0xa8,0x8b,0x67,0xf5,0x97,0x58,0x67,0x95,0xb2,0x25,0x70,0x73,
+		0x3f,0xd4,0xff,0x2c,0xc2,0xad,0x93,0x1d,0x83,0x30,0x16,0x5a,0x9e,0x45,0x0e,0x38,
+		0x88,0x59,0xce,0x62,0x4f,0x01,0xdb,0x17,0xc2,0x50,0x2b,0x4e,0x66,0xad,0xf9,0x65,
+		0x27,0x36,0x3d,0x6f,0x90,0x6b,0x20,0x23,0xe8,0xed,0x74,0xd5,0xaf,0x0a,0xa6,0x02,
+		0x46,0xb0,0xb9,0x2f,0x49,0xc4,0x93,0x3e,0xf3,0x12,0xf8,0xa2,0x54,0x34,0xee,0x96,
+		0x98,0xd6,0xd9,0x20,0x43,0x45,0xbd,0x10,0xbb,0x11,0xaa,0x39,0x86,0x56,0x16,0xd5,
+		0xad,0x1b,0x57,0x44,0x70,0x6c,0xfd,0x4e,0xa1,0x40,0x8e,0x20,0xc8,0xfd,0xcf,0x85,
+		0x51,0xee,0xe8,0x81,0x4b,0x7b,0x37,0x33,0x0b,0x05,0x26,0xf0,0xbc,0x5e,0xe1,0x5d,
+		0x4e,0xcd,0xa7,0xa1,0xbd,0x25,0xaa,0x97,0xf2,0x45,0x84,0xd8,0x5d,0x3f,0x52,0x49,
+		0x69,0x4b,0x9f,0x43,0x53,0x9e,0x69,0xea,0x35,0xbf,0xe7,0xfd,0x44,0x07,0xbc,0x8e,
+		0x9d,0xca,0x8a,0x9f,0xae,0x4b,0xdc,0x6b,0x7b,0xb3,0x8c,0x6d,0x68,0xf8,0x99,0xe1,
+		0xd3,0x2c,0x85,0xbc,0xd6,0x17,0xa5,0x67,0x67,0x8c,0xf8,0x5d,0x22,0x17,0xa4,0xe8,
+		0x6a,0x75,0x56,0x24,0xb6,0x40,0x02,0x35,0x4c,0x02,0x68,0x42,0xbc,0x95,0x42,0x49,
+		0x1a,0xf1,0xc3,0xd6,0x29,0x09,0x70,0x55,0x9a,0xf1,0x1b,0xdc,0x2c,0x83,0xb5,0x4c,
+		0x74,0x14,0x49,0x05,0xc0,0xa3,0x58,0xf3,0x15,0x3d,0xb7,0x67,0xa5,0xda,0x2a,0x86,
+		0x27,0xf6,0x96,0x27,0xe1,0xd4,0x1e,0xde,0x9c,0x90,0x7c,0x79,0xb5,0x1f,0xf8,0x15,
+		0xe4,0x64,0x5c,0x33,0x75,0xe0,0xf6,0x3f,0x84,0xfc,0xf5,0xd7,0xc3,0x40,0x7a,0x1d,
+		0xd6,0x83,0x9e,0x19,0x06,0xa1,0xe3,0x80,0x2c,0xcf,0x5e,0x82,0x30,0xd9,0x5c,0xf4,
+		0xb8,0x27,0xb4,0x1c,0x48,0x34,0x25,0xa8,0xa6,0x0b,0xfa,0x51,0x89,0xda,0xc4,0x38,
+		0x06,0x0f,0x2f,0x5c,0xd5,0x26,0x66,0x2f,0x29,0x06,0xc1,0xdd,0x64,0xf4,0x84,0x4e,
+		0x94,0x2c,0xa8,0x4d,0xae,0xce,0x6d,0xd7,0xbb,0xf7,0x19,0x4d,0x8c,0xe5,0x6b,0xc2,
+		0x83,0x10,0x85,0xa7,0xd3,0x10,0xe4,0x94,0x4c,0xfa,0xe7,0x62,0x60,0xaa,0xbf,0x6b,
+		0x60,0x9d,0x88,0x78,0x9b,0x8a,0x1a,0xbf,0x50,0x01,0x86,0xc3,0xd9,0x7e,0xd2,0xab),
+	.out = chunk_from_chars(
+		0x98,0x7f,0xad,0xd8,0x04,0x99,0x45,0x67,0xd5,0x7f,0x98,0x9b,0x6c,0xda,0x66,0xef,
+		0xbf,0xfc,0xab,0x28,0xac,0x3e,0x3b,0xc6,0x10,0x78,0x05,0x95,0x4e,0xb1,0xea,0xd0,
+		0xce,0xa6,0xfb,0x49,0xaa,0x96,0x1c,0xbe,0x98,0xfd,0xad,0x57,0xdd,0x9c,0x45,0x56,
+		0x7c,0xe4,0x96,0x74,0x78,0x62,0xf8,0x8b,0xcd,0x9f,0xc5,0x75,0x59,0xa0,0x7e,0xa9,
+		0x27,0xa4,0x20,0x82,0xba,0x87,0xe0,0xf5,0x1b,0x3d,0x5b,0x8c,0xbd,0xea,0xdd,0xed,
+		0xa3,0xae,0x4c,0x73,0xb1,0xa5,0x1d,0xd0,0xdc,0xe3,0xcc,0x8e,0xb3,0xb1,0x41,0x9b,
+		0x93,0xf2,0x9d,0xc6,0x52,0x2b,0x2f,0x5e,0x93,0x75,0x23,0x82,0x03,0xec,0xa9,0x8e,
+		0xd8,0x6b,0xf1,0xe6,0x86,0x8a,0xb4,0x28,0xd8,0x63,0xa5,0xf3,0xb9,0x87,0xd6,0xc0,
+		0xf9,0x57,0xaf,0x08,0xa1,0x63,0x1f,0x7c,0xdc,0x00,0x8c,0xbb,0xff,0xc2,0x4b,0x02,
+		0x93,0x1a,0x02,0x01,0x94,0xe3,0x68,0xd6,0xb5,0x70,0x2e,0x7f,0x8b,0x96,0x0d,0xbc,
+		0x93,0xca,0x56,0xed,0x5a,0x68,0xda,0xe0,0x20,0x69,0x94,0xed,0xf1,0x47,0x6f,0x5f,
+		0xa3,0x4e,0xfb,0xda,0xa9,0x73,0xd2,0x7b,0xa3,0x68,0xce,0xdc,0xc6,0x66,0xd7,0x63,
+		0xbd,0x6a,0xd5,0x60,0x57,0x38,0x51,0xbf,0xfb,0x70,0x99,0xaf,0x58,0x8f,0x34,0xff,
+		0x2e,0x59,0x9b,0x0e,0x87,0xf7,0x0a,0x7f,0x4a,0xa4,0x8e,0x95,0x1d,0x0c,0x5c,0x30,
+		0xdd,0xfc,0x1f,0x98,0xc1,0x28,0x63,0x15,0x90,0xcd,0xe0,0x0a,0x7c,0x93,0x15,0x8d,
+		0xbf,0x4c,0xaa,0x53,0x7b,0x31,0x59,0x31,0xb7,0x14,0xd7,0x2f,0x4d,0x2a,0x01,0xc9),
+};
diff --git a/src/libstrongswan/plugins/test_vectors/test_vectors_plugin.c b/src/libstrongswan/plugins/test_vectors/test_vectors_plugin.c
index 7b11b19aa58d..1dddcb7c25c1 100644
--- a/src/libstrongswan/plugins/test_vectors/test_vectors_plugin.c
+++ b/src/libstrongswan/plugins/test_vectors/test_vectors_plugin.c
@@ -25,6 +25,7 @@
 #define TEST_VECTOR_HASHER(x) extern hasher_test_vector_t x;
 #define TEST_VECTOR_PRF(x) extern prf_test_vector_t x;
 #define TEST_VECTOR_XOF(x) extern xof_test_vector_t x;
+#define TEST_VECTOR_KDF(x) extern kdf_test_vector_t x;
 #define TEST_VECTOR_DRBG(x) extern drbg_test_vector_t x;
 #define TEST_VECTOR_RNG(x) extern rng_test_vector_t x;
 #define TEST_VECTOR_DH(x) extern dh_test_vector_t x;
@@ -37,6 +38,7 @@
 #undef TEST_VECTOR_HASHER
 #undef TEST_VECTOR_PRF
 #undef TEST_VECTOR_XOF
+#undef TEST_VECTOR_KDF
 #undef TEST_VECTOR_DRBG
 #undef TEST_VECTOR_RNG
 #undef TEST_VECTOR_DH
@@ -47,6 +49,7 @@
 #define TEST_VECTOR_HASHER(x)
 #define TEST_VECTOR_PRF(x)
 #define TEST_VECTOR_XOF(x)
+#define TEST_VECTOR_KDF(x)
 #define TEST_VECTOR_DRBG(x)
 #define TEST_VECTOR_RNG(x)
 #define TEST_VECTOR_DH(x)
@@ -100,6 +103,14 @@ static xof_test_vector_t *xof[] = {
 #undef TEST_VECTOR_XOF
 #define TEST_VECTOR_XOF(x)
 
+#undef TEST_VECTOR_KDF
+#define TEST_VECTOR_KDF(x) &x,
+static kdf_test_vector_t *kdf[] = {
+#include "test_vectors.h"
+};
+#undef TEST_VECTOR_KDF
+#define TEST_VECTOR_KDF(x)
+
 #undef TEST_VECTOR_DRBG
 #define TEST_VECTOR_DRBG(x) &x,
 static drbg_test_vector_t *drbg[] = {
@@ -208,6 +219,11 @@ plugin_t *test_vectors_plugin_create()
 		lib->crypto->add_test_vector(lib->crypto,
 									 EXTENDED_OUTPUT_FUNCTION, xof[i]);
 	}
+	for (i = 0; i < countof(kdf); i++)
+	{
+		lib->crypto->add_test_vector(lib->crypto,
+									 KEY_DERIVATION_FUNCTION, kdf[i]);
+	}
 	for (i = 0; i < countof(drbg); i++)
 	{
 		lib->crypto->add_test_vector(lib->crypto,
diff --git a/src/libstrongswan/tests/suites/test_prf_plus.c b/src/libstrongswan/tests/suites/test_prf_plus.c
index 213b8ec418e1..cc1e47e26690 100644
--- a/src/libstrongswan/tests/suites/test_prf_plus.c
+++ b/src/libstrongswan/tests/suites/test_prf_plus.c
@@ -15,129 +15,132 @@
 
 #include "test_suite.h"
 
-#include <crypto/prf_plus.h>
+#include <library.h>
 
 static struct {
 	chunk_t key;
 	chunk_t seed;
-	chunk_t iterations[10];
-} counter_data[] = {
+	chunk_t expected;
+} test_data[] = {
 	{ .key = chunk_from_chars(0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,
 							  0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,
 							  0x0b,0x0b,0x0b,0x0b),
 	  .seed = chunk_from_chars(0x48,0x69,0x20,0x54,0x68,0x65,0x72,0x65),
-	  .iterations = {
-		chunk_from_chars(0xb9,0xbd,0xc0),
-		chunk_from_chars(0x89,0x88,0xb4,0xc2,0xb7,0x5a),
-		chunk_from_chars(0xa9,0x3e,0x59,0x6a,0xc8,0x42,0x05),
-		chunk_from_chars(0xfa,0x2d,0xdd,0xe1,0xbf,0x7a,0x25,0x72,
-						 0x06,0x7b,0x00,0xe1,0x4b,0x23,0x77,0x32),
-		chunk_from_chars(0x83,0x05,0x09,0x98,0x1a,0xd2,0xf9,0x4a),
-		chunk_from_chars(0x8c,0x32,0xa4,0x7d,0xaa,0x22,0x55,0xb6),
-		chunk_from_chars(0x60,0xc4,0x36,0x34,0x7a,0xe7,0x56,0xa6,
-						 0xed,0xc0,0x23,0x47,0x7d,0x80),
-		chunk_from_chars(0x95,0x90,0xe6,0x82,0xf6,0x1d,0x9c,0x04,
-						 0xb0,0x6b,0x4a,0xd9,0x71,0xa3,0x4c,0x81,
-						 0x47,0xfa,0x66,0x79),
-		chunk_from_chars(0x2f,0xf1,0x43,0x4b,0x93,0xc7,0x22,0xb3,
-						 0x2e,0x12,0xf4,0x88,0x32,0xeb,0xc1,0x5c,
-						 0xe2,0x36,0x9c,0xe7,0x1f,0xe9,0xb7,0xb8,
-						 0x1e,0x57,0x04,0xc1,0x4d,0x0f,0x52,0x80,
-						 0xa6,0xec,0x62,0x6e,0x99,0x2d,0x7a,0x9f),
-	  },
+	  .expected = chunk_from_chars(0xb9,0xbd,0xc0,0x89,0x88,0xb4,0xc2,0xb7,
+								   0x5a,0xa9,0x3e,0x59,0x6a,0xc8,0x42,0x05,
+								   0xfa,0x2d,0xdd,0xe1,0xbf,0x7a,0x25,0x72,
+								   0x06,0x7b,0x00,0xe1,0x4b,0x23,0x77,0x32,
+								   0x83,0x05,0x09,0x98,0x1a,0xd2,0xf9,0x4a,
+								   0x8c,0x32,0xa4,0x7d,0xaa,0x22,0x55,0xb6,
+								   0x60,0xc4,0x36,0x34,0x7a,0xe7,0x56,0xa6,
+								   0xed,0xc0,0x23,0x47,0x7d,0x80,0x95,0x90,
+								   0xe6,0x82,0xf6,0x1d,0x9c,0x04,0xb0,0x6b,
+								   0x4a,0xd9,0x71,0xa3,0x4c,0x81,0x47,0xfa,
+								   0x66,0x79,0x2f,0xf1,0x43,0x4b,0x93,0xc7,
+								   0x22,0xb3,0x2e,0x12,0xf4,0x88,0x32,0xeb,
+								   0xc1,0x5c,0xe2,0x36,0x9c,0xe7,0x1f,0xe9,
+								   0xb7,0xb8,0x1e,0x57,0x04,0xc1,0x4d,0x0f,
+								   0x52,0x80,0xa6,0xec,0x62,0x6e,0x99,0x2d,
+								   0x7a,0x9f),
+	},
+	/* change the key, keep the seed */
+	{ .key = chunk_from_chars(0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,
+							  0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,
+							  0x0a,0x0a,0x0a,0x0a),
+	  .expected = chunk_from_chars(0x1a,0x2f,0xc7,0x4a,0x06,0x8c,0xae,0x76,
+								   0xfa,0xb3,0xd6,0x34,0xae,0xe9,0x81,0x55,
+								   0x11,0x6a,0x4b,0x21,0xe1,0x0d,0x1b,0x16,
+								   0x45,0x7a,0x06,0xd9,0x42,0x27,0x93,0x98,
+								   0xf7,0x4d,0xf1,0x59,0xc1,0x25,0x21,0xae,
+								   0xe6,0xf4,0x80,0x01,0xe5,0x86,0x8e,0xa7,
+								   0x4b,0x1e,0x13,0xd1,0xcf,0xdc,0xb7,0x7b,
+								   0xf8,0xcf,0x75,0x2c,0x67,0x13,0x18,0x7a,
+								   0x38,0x55,0xba,0x4b,0xf2,0x57,0x55,0xcd,
+								   0x96,0x20,0xcb,0xe3,0xc4,0x8a,0x7f,0xa3,
+								   0x86,0xa5,0xc6,0x26,0x8e,0x57,0xd8,0xe0,
+								   0xb6,0xf9,0x8a,0x41,0x16,0x82,0x64,0x8a),
+	},
+	/* change the seed, keep the key */
+	{ .seed = chunk_from_chars(0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,
+							   0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10),
+	  .expected = chunk_from_chars(0x8b,0x90,0x9c,0xbc,0xbb,0xf1,0x72,0x63,
+								   0x6b,0x4e,0x63,0xcd,0x7e,0xc5,0xe3,0x45,
+								   0x57,0x3e,0xbf,0x72,0x8f,0x62,0xa4,0x9b,
+								   0x83,0x7b,0xd9,0x53,0xc0,0x0c,0xad,0x3d,
+								   0x19,0x16,0x28,0x81,0x85,0xf9,0x27,0xb6,
+								   0xc4,0x0c,0x48,0x31,0x45,0x12,0x3b,0x5a,
+								   0xb8,0x47,0xd0,0x19,0x6e,0x6b,0x1c,0x5a,
+								   0x2a,0xc1,0xe5,0x1e,0xc5,0x43,0xcc,0xd4,
+								   0x28,0xba,0x30,0x4b,0x5e,0xad,0x97,0xa7,
+								   0xc0,0x9d,0x13,0xdd,0xfb,0x4a,0x42,0x43,
+								   0x87,0xd6,0x22,0xf6,0x03,0x19,0x21,0x31,
+								   0x6a,0xa6,0x38,0x44,0xa5,0x61,0xf6,0x23,
+								   0x0c,0x50,0x14,0xcc,0xce,0x09,0x5f,0xb3,
+								   0xcc,0xe4,0xcb,0x8f,0x43,0xdc,0x2b,0x65,
+								   0xfd,0x42,0xcc,0xeb,0x49,0x0e,0xcb,0xeb,
+								   0x47,0x30,0xb5,0x18,0x6d,0x34,0x7a,0xea,
+								   0xad,0xfd,0x66,0xa7,0x7e,0xd3,0x3f,0x42,
+								   0xdf,0x75,0x54,0xef,0x5f,0x4f,0x7e,0x26,
+								   0xf9,0x38,0x73,0x26,0x92,0x7a,0xc7,0x80),
 	},
 };
 
-START_TEST(test_vectors_counter)
+START_TEST(test_params)
 {
-	prf_plus_t *prf_plus;
-	prf_t *prf;
-	chunk_t *iter = counter_data[_i].iterations, out;
+	kdf_t *kdf;
+	chunk_t out;
+	int i;
 
-	prf = lib->crypto->create_prf(lib->crypto, PRF_HMAC_SHA2_256);
-	ck_assert(prf->set_key(prf, counter_data[_i].key));
-	prf_plus = prf_plus_create(prf, TRUE, counter_data[_i].seed);
-	while (iter->ptr)
+	kdf = lib->crypto->create_kdf(lib->crypto, KDF_PRF_PLUS, PRF_HMAC_SHA2_256);
+	if (!kdf)
 	{
-		ck_assert(prf_plus->allocate_bytes(prf_plus, iter->len, &out));
-		ck_assert_chunk_eq(*iter, out);
+		warn("%N (%N) not supported", key_derivation_function_names,
+			 KDF_PRF_PLUS, pseudo_random_function_names, PRF_HMAC_SHA2_256);
+		return;
+	}
+	for (i = 0; i < countof(test_data); i++)
+	{
+		if (test_data[i].key.len)
+		{
+			ck_assert(kdf->set_param(kdf, KDF_PARAM_KEY, test_data[i].key));
+		}
+		if (test_data[i].seed.len)
+		{
+			ck_assert(kdf->set_param(kdf, KDF_PARAM_SALT, test_data[i].seed));
+		}
+		ck_assert(kdf->allocate_bytes(kdf, test_data[i].expected.len, &out));
+		ck_assert_chunk_eq(test_data[i].expected, out);
+		chunk_free(&out);
+		/* same output the second time */
+		ck_assert(kdf->allocate_bytes(kdf, test_data[i].expected.len, &out));
+		ck_assert_chunk_eq(test_data[i].expected, out);
 		chunk_free(&out);
-		iter++;
 	}
-	prf_plus->destroy(prf_plus);
-	prf->destroy(prf);
+	kdf->destroy(kdf);
 }
 END_TEST
 
 START_TEST(test_wrap)
 {
-	prf_plus_t *prf_plus;
-	prf_t *prf;
-	u_char buf[32];
-	int i;
+	kdf_t *kdf;
+	chunk_t out;
 
-	prf = lib->crypto->create_prf(lib->crypto, PRF_HMAC_SHA2_256);
-	ck_assert(prf->set_key(prf, counter_data[0].key));
-	prf_plus = prf_plus_create(prf, TRUE, counter_data[0].seed);
-	for (i = 1; i < 256; i++)
+	kdf = lib->crypto->create_kdf(lib->crypto, KDF_PRF_PLUS, PRF_HMAC_SHA2_256);
+	if (!kdf)
 	{
-		ck_assert(prf_plus->get_bytes(prf_plus, sizeof(buf), buf));
+		warn("%N (%N) not supported", key_derivation_function_names,
+			 KDF_PRF_PLUS, pseudo_random_function_names, PRF_HMAC_SHA2_256);
+		return;
 	}
-	ck_assert(!prf_plus->get_bytes(prf_plus, sizeof(buf), buf));
-	prf_plus->destroy(prf_plus);
-	prf->destroy(prf);
-}
-END_TEST
-
-static struct {
-	chunk_t key;
-	chunk_t seed;
-	chunk_t iterations[10];
-} classic_data[] = {
-	{ .key = chunk_from_chars(0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,
-							  0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,
-							  0x0b,0x0b,0x0b,0x0b),
-	  .seed = chunk_from_chars(0x48,0x69,0x20,0x54,0x68,0x65,0x72,0x65),
-	  .iterations = {
-		chunk_from_chars(0xb0,0x34,0x4c),
-		chunk_from_chars(0x61,0xd8,0xdb,0x38,0x53,0x5c),
-		chunk_from_chars(0xa8,0xaf,0xce,0xaf,0x0b,0xf1,0x2b),
-		chunk_from_chars(0x88,0x1d,0xc2,0x00,0xc9,0x83,0x3d,0xa7,
-						 0x26,0xe9,0x37,0x6c,0x2e,0x32,0xcf,0xf7),
-		chunk_from_chars(0xd0,0x9a,0xe2,0x4b,0x3a,0x83,0xff,0xd4),
-		chunk_from_chars(0xb1,0xef,0xa5,0x94,0x5c,0xc5,0xed,0x85),
-		chunk_from_chars(0xb0,0xb2,0xcc,0x56,0xfc,0xf7,0x5d,0x23,
-						 0xa0,0xa3,0x4c,0xa4,0xdb,0xff,),
-		chunk_from_chars(0xea,0xfd,0xaa,0x6a,0x3b,0xf4,0x11,0x34,
-						 0x24,0xe4,0x50,0x2d,0xf9,0x7a,0x76,0x93,
-						 0x24,0xf6,0x11,0x24),
-		chunk_from_chars(0x24,0x3b,0x99,0x6e,0x7d,0x0f,0x35,0x99,
-						 0x88,0x79,0x73,0x6b,0xdb,0x70,0x65,0x9a,
-						 0x6e,0xfa,0xd2,0x39,0x94,0x10,0xe6,0xce,
-						 0x80,0x45,0x6e,0xb6,0x07,0x07,0x8f,0xe1,
-						 0xc4,0x7c,0x6b,0x5e,0x81,0x65,0x47,0x8a),
-	  },
-	},
-};
+	ck_assert(kdf->set_param(kdf, KDF_PARAM_KEY, test_data[0].key));
+	ck_assert(kdf->set_param(kdf, KDF_PARAM_SALT, test_data[0].seed));
 
-START_TEST(test_vectors_classic)
-{
-	prf_plus_t *prf_plus;
-	prf_t *prf;
-	chunk_t *iter = classic_data[_i].iterations, out;
-
-	prf = lib->crypto->create_prf(lib->crypto, PRF_HMAC_SHA2_256);
-	ck_assert(prf->set_key(prf, classic_data[_i].key));
-	prf_plus = prf_plus_create(prf, FALSE, classic_data[_i].seed);
-	while (iter->ptr)
-	{
-		ck_assert(prf_plus->allocate_bytes(prf_plus, iter->len, &out));
-		ck_assert_chunk_eq(*iter, out);
-		chunk_free(&out);
-		iter++;
-	}
-	prf_plus->destroy(prf_plus);
-	prf->destroy(prf);
+	/* the 1-byte counter overflows after 255 blocks of the underlying PRF */
+	out = chunk_alloc(32 * 255 + 1);
+	ck_assert(kdf->get_bytes(kdf, out.len - 1 , out.ptr));
+	ck_assert(!kdf->get_bytes(kdf, out.len, out.ptr));
+	chunk_free(&out);
+	kdf->destroy(kdf);
 }
 END_TEST
 
@@ -148,13 +151,12 @@ Suite *prf_plus_suite_create()
 
 	s = suite_create("prf_plus");
 
-	tc = tcase_create("counter");
-	tcase_add_loop_test(tc, test_vectors_counter, 0, countof(counter_data));
-	tcase_add_test(tc, test_wrap);
+	tc = tcase_create("params");
+	tcase_add_test(tc, test_params);
 	suite_add_tcase(s, tc);
 
-	tc = tcase_create("no counter");
-	tcase_add_loop_test(tc, test_vectors_classic, 0, countof(classic_data));
+	tc = tcase_create("wrap");
+	tcase_add_test(tc, test_wrap);
 	suite_add_tcase(s, tc);
 
 	return s;
diff --git a/src/libstrongswan/tests/suites/test_vectors.c b/src/libstrongswan/tests/suites/test_vectors.c
index b5733fef880d..109dffa7effa 100644
--- a/src/libstrongswan/tests/suites/test_vectors.c
+++ b/src/libstrongswan/tests/suites/test_vectors.c
@@ -29,6 +29,7 @@ static transform_type_t tfs[] = {
 	HASH_ALGORITHM,
 	PSEUDO_RANDOM_FUNCTION,
 	EXTENDED_OUTPUT_FUNCTION,
+	KEY_DERIVATION_FUNCTION,
 	DETERMINISTIC_RANDOM_BIT_GENERATOR,
 	RANDOM_NUMBER_GENERATOR,
 	DIFFIE_HELLMAN_GROUP,
diff --git a/src/libstrongswan/tests/tests.h b/src/libstrongswan/tests/tests.h
index 901410331c9f..603ee05bfd5e 100644
--- a/src/libstrongswan/tests/tests.h
+++ b/src/libstrongswan/tests/tests.h
@@ -49,7 +49,7 @@ TEST_SUITE(asn1_parser_suite_create)
 TEST_SUITE(rng_tester_suite_create)
 TEST_SUITE_DEPEND(mgf1_sha1_suite_create, XOF, XOF_MGF1_SHA1)
 TEST_SUITE_DEPEND(mgf1_sha256_suite_create, XOF, XOF_MGF1_SHA256)
-TEST_SUITE_DEPEND(prf_plus_suite_create, PRF, PRF_HMAC_SHA2_256)
+TEST_SUITE_DEPEND(prf_plus_suite_create, KDF, KDF_PRF_PLUS)
 TEST_SUITE_DEPEND(ntru_suite_create, DH, NTRU_112_BIT)
 TEST_SUITE_DEPEND(fetch_http_suite_create, FETCHER, "http://")
 TEST_SUITE_DEPEND(ed25519_suite_create, PRIVKEY_GEN, KEY_ED25519)
-- 
2.25.1

