ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/Makefile b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/Makefile
new file mode 100644
index 0000000..96bac94
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/Makefile
@@ -0,0 +1,181 @@
+#CFLAGS += -DALL_DH_GROUPS
+CFLAGS += -DCONFIG_SHA256
+CFLAGS += -DCONFIG_SHA384
+CFLAGS += -DCONFIG_HMAC_SHA256_KDF
+CFLAGS += -DCONFIG_HMAC_SHA384_KDF
+
+# crypto_module_tests.c
+CFLAGS += -DCONFIG_MODULE_TESTS
+CFLAGS += -DCONFIG_DPP
+#CFLAGS += -DCONFIG_DPP2
+#CFLAGS += -DCONFIG_DPP3
+CFLAGS += -DCONFIG_ECC
+CFLAGS += -DCONFIG_MESH
+CFLAGS += -DEAP_PSK
+CFLAGS += -DEAP_FAST
+
+ifeq ($(CONFIG_TLS),mbedtls)
+
+# (enable features for 'cd tests; make run-tests CONFIG_TLS=mbedtls')
+CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
+CFLAGS += -DCONFIG_DES
+CFLAGS += -DEAP_IKEV2
+CFLAGS += -DEAP_MSCHAPv2
+CFLAGS += -DEAP_SIM
+
+LIB_OBJS = tls_mbedtls.o crypto_mbedtls.o
+LIB_OBJS+= \
+	aes-eax.o \
+	aes-siv.o \
+	dh_groups.o \
+	milenage.o \
+	ms_funcs.o
+
+else
+ifeq ($(CONFIG_TLS),openssl)
+
+# (enable features for 'cd tests; make run-tests CONFIG_TLS=openssl')
+ifndef CONFIG_TLS_DEFAULT_CIPHERS
+CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW"
+endif
+CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
+CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
+CFLAGS += -DEAP_TLS_OPENSSL
+
+LIB_OBJS = tls_openssl.o fips_prf_openssl.o crypto_openssl.o
+LIB_OBJS+= \
+	aes-ctr.o \
+	aes-eax.o \
+	aes-encblock.o \
+	aes-siv.o \
+	dh_groups.o \
+	milenage.o \
+	ms_funcs.o \
+	sha1-prf.o \
+	sha1-tlsprf.o \
+	sha1-tprf.o \
+	sha256-kdf.o \
+	sha256-prf.o \
+	sha256-tlsprf.o
+
+else
+ifeq ($(CONFIG_TLS),wolfssl)
+
+# (wolfssl libraries must be built with ./configure --enable-wpas)
+# (enable features for 'cd tests; make run-tests CONFIG_TLS=wolfssl')
+CFLAGS += -DWOLFSSL_DER_LOAD
+CFLAGS += -DCONFIG_DES
+
+LIB_OBJS = tls_wolfssl.o fips_prf_wolfssl.o crypto_wolfssl.o
+LIB_OBJS+= \
+	aes-ctr.o \
+	aes-eax.o \
+	aes-encblock.o \
+	aes-siv.o \
+	dh_groups.o \
+	milenage.o \
+	ms_funcs.o \
+	sha1-prf.o \
+	sha1-tlsprf.o \
+	sha1-tprf.o \
+	sha256-kdf.o \
+	sha256-prf.o \
+	sha256-tlsprf.o
+
+else
+ifeq ($(CONFIG_TLS),gnutls)
+
+# (enable features for 'cd tests; make run-tests CONFIG_TLS=gnutls')
+LIB_OBJS = tls_gnutls.o crypto_gnutls.o
+LIB_OBJS+= \
+	aes-cbc.o \
+	aes-ctr.o \
+	aes-eax.o \
+	aes-encblock.o \
+	aes-omac1.o \
+	aes-siv.o \
+	aes-unwrap.o \
+	aes-wrap.o \
+	dh_group5.o \
+	dh_groups.o \
+	milenage.o \
+	ms_funcs.o \
+	rc4.o \
+	sha1-pbkdf2.o \
+	sha1-prf.o \
+	fips_prf_internal.o \
+	sha1-internal.o \
+	sha1-tlsprf.o \
+	sha1-tprf.o \
+	sha256-kdf.o \
+	sha256-prf.o \
+	sha256-tlsprf.o
+
+else
+
+CFLAGS += -DCONFIG_CRYPTO_INTERNAL
+CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
+CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
+CFLAGS += -DCONFIG_INTERNAL_SHA384
+
+LIB_OBJS= \
+	aes-cbc.o \
+	aes-ccm.o \
+	aes-ctr.o \
+	aes-eax.o \
+	aes-encblock.o \
+	aes-internal.o \
+	aes-internal-dec.o \
+	aes-internal-enc.o \
+	aes-omac1.o \
+	aes-siv.o \
+	aes-unwrap.o \
+	aes-wrap.o \
+	des-internal.o \
+	dh_group5.o \
+	dh_groups.o \
+	md4-internal.o \
+	md5.o \
+	md5-internal.o \
+	milenage.o \
+	ms_funcs.o \
+	rc4.o \
+	sha1.o \
+	sha1-internal.o \
+	sha1-pbkdf2.o \
+	sha1-prf.o \
+	sha1-tlsprf.o \
+	sha1-tprf.o \
+	sha256.o \
+	sha256-kdf.o \
+	sha256-prf.o \
+	sha256-tlsprf.o \
+	sha256-internal.o \
+	sha384.o \
+	sha384-prf.o \
+	sha384-internal.o \
+	sha512.o \
+	sha512-prf.o \
+	sha512-internal.o
+
+LIB_OBJS += crypto_internal.o
+LIB_OBJS += crypto_internal-cipher.o
+LIB_OBJS += crypto_internal-modexp.o
+LIB_OBJS += crypto_internal-rsa.o
+LIB_OBJS += tls_internal.o
+LIB_OBJS += fips_prf_internal.o
+
+endif
+endif
+endif
+endif
+
+
+# (used by wlantest/{bip,gcmp,rx_mgmt}.c and tests/test-aes.c)
+LIB_OBJS += aes-gcm.o
+
+ifndef TEST_FUZZ
+LIB_OBJS += random.o
+endif
+
+include ../lib.rules
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-cbc.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-cbc.c
new file mode 100644
index 0000000..0835f2c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-cbc.c
@@ -0,0 +1,86 @@
+/*
+ * AES-128 CBC
+ *
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_128_cbc_encrypt - AES-128 CBC encryption
+ * @key: Encryption key
+ * @iv: Encryption IV for CBC mode (16 bytes)
+ * @data: Data to encrypt in-place
+ * @data_len: Length of data in bytes (must be divisible by 16)
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+	void *ctx;
+	u8 cbc[AES_BLOCK_SIZE];
+	u8 *pos = data;
+	int i, j, blocks;
+
+	if (TEST_FAIL())
+		return -1;
+
+	ctx = aes_encrypt_init(key, 16);
+	if (ctx == NULL)
+		return -1;
+	os_memcpy(cbc, iv, AES_BLOCK_SIZE);
+
+	blocks = data_len / AES_BLOCK_SIZE;
+	for (i = 0; i < blocks; i++) {
+		for (j = 0; j < AES_BLOCK_SIZE; j++)
+			cbc[j] ^= pos[j];
+		aes_encrypt(ctx, cbc, cbc);
+		os_memcpy(pos, cbc, AES_BLOCK_SIZE);
+		pos += AES_BLOCK_SIZE;
+	}
+	aes_encrypt_deinit(ctx);
+	return 0;
+}
+
+
+/**
+ * aes_128_cbc_decrypt - AES-128 CBC decryption
+ * @key: Decryption key
+ * @iv: Decryption IV for CBC mode (16 bytes)
+ * @data: Data to decrypt in-place
+ * @data_len: Length of data in bytes (must be divisible by 16)
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+	void *ctx;
+	u8 cbc[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE];
+	u8 *pos = data;
+	int i, j, blocks;
+
+	if (TEST_FAIL())
+		return -1;
+
+	ctx = aes_decrypt_init(key, 16);
+	if (ctx == NULL)
+		return -1;
+	os_memcpy(cbc, iv, AES_BLOCK_SIZE);
+
+	blocks = data_len / AES_BLOCK_SIZE;
+	for (i = 0; i < blocks; i++) {
+		os_memcpy(tmp, pos, AES_BLOCK_SIZE);
+		aes_decrypt(ctx, pos, pos);
+		for (j = 0; j < AES_BLOCK_SIZE; j++)
+			pos[j] ^= cbc[j];
+		os_memcpy(cbc, tmp, AES_BLOCK_SIZE);
+		pos += AES_BLOCK_SIZE;
+	}
+	aes_decrypt_deinit(ctx);
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-ccm.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-ccm.c
new file mode 100644
index 0000000..cf22778
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-ccm.c
@@ -0,0 +1,212 @@
+/*
+ * Counter with CBC-MAC (CCM) with AES
+ *
+ * Copyright (c) 2010-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+
+static void xor_aes_block(u8 *dst, const u8 *src)
+{
+	u32 *d = (u32 *) dst;
+	u32 *s = (u32 *) src;
+	*d++ ^= *s++;
+	*d++ ^= *s++;
+	*d++ ^= *s++;
+	*d++ ^= *s++;
+}
+
+
+static void aes_ccm_auth_start(void *aes, size_t M, size_t L, const u8 *nonce,
+			       const u8 *aad, size_t aad_len, size_t plain_len,
+			       u8 *x)
+{
+	u8 aad_buf[2 * AES_BLOCK_SIZE];
+	u8 b[AES_BLOCK_SIZE];
+
+	/* Authentication */
+	/* B_0: Flags | Nonce N | l(m) */
+	b[0] = aad_len ? 0x40 : 0 /* Adata */;
+	b[0] |= (((M - 2) / 2) /* M' */ << 3);
+	b[0] |= (L - 1) /* L' */;
+	os_memcpy(&b[1], nonce, 15 - L);
+	WPA_PUT_BE16(&b[AES_BLOCK_SIZE - L], plain_len);
+
+	wpa_hexdump_key(MSG_EXCESSIVE, "CCM B_0", b, AES_BLOCK_SIZE);
+	aes_encrypt(aes, b, x); /* X_1 = E(K, B_0) */
+
+	if (!aad_len)
+		return;
+
+	WPA_PUT_BE16(aad_buf, aad_len);
+	os_memcpy(aad_buf + 2, aad, aad_len);
+	os_memset(aad_buf + 2 + aad_len, 0, sizeof(aad_buf) - 2 - aad_len);
+
+	xor_aes_block(aad_buf, x);
+	aes_encrypt(aes, aad_buf, x); /* X_2 = E(K, X_1 XOR B_1) */
+
+	if (aad_len > AES_BLOCK_SIZE - 2) {
+		xor_aes_block(&aad_buf[AES_BLOCK_SIZE], x);
+		/* X_3 = E(K, X_2 XOR B_2) */
+		aes_encrypt(aes, &aad_buf[AES_BLOCK_SIZE], x);
+	}
+}
+
+
+static void aes_ccm_auth(void *aes, const u8 *data, size_t len, u8 *x)
+{
+	size_t last = len % AES_BLOCK_SIZE;
+	size_t i;
+
+	for (i = 0; i < len / AES_BLOCK_SIZE; i++) {
+		/* X_i+1 = E(K, X_i XOR B_i) */
+		xor_aes_block(x, data);
+		data += AES_BLOCK_SIZE;
+		aes_encrypt(aes, x, x);
+	}
+	if (last) {
+		/* XOR zero-padded last block */
+		for (i = 0; i < last; i++)
+			x[i] ^= *data++;
+		aes_encrypt(aes, x, x);
+	}
+}
+
+
+static void aes_ccm_encr_start(size_t L, const u8 *nonce, u8 *a)
+{
+	/* A_i = Flags | Nonce N | Counter i */
+	a[0] = L - 1; /* Flags = L' */
+	os_memcpy(&a[1], nonce, 15 - L);
+}
+
+
+static void aes_ccm_encr(void *aes, size_t L, const u8 *in, size_t len, u8 *out,
+			 u8 *a)
+{
+	size_t last = len % AES_BLOCK_SIZE;
+	size_t i;
+
+	/* crypt = msg XOR (S_1 | S_2 | ... | S_n) */
+	for (i = 1; i <= len / AES_BLOCK_SIZE; i++) {
+		WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i);
+		/* S_i = E(K, A_i) */
+		aes_encrypt(aes, a, out);
+		xor_aes_block(out, in);
+		out += AES_BLOCK_SIZE;
+		in += AES_BLOCK_SIZE;
+	}
+	if (last) {
+		WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i);
+		aes_encrypt(aes, a, out);
+		/* XOR zero-padded last block */
+		for (i = 0; i < last; i++)
+			*out++ ^= *in++;
+	}
+}
+
+
+static void aes_ccm_encr_auth(void *aes, size_t M, u8 *x, u8 *a, u8 *auth)
+{
+	size_t i;
+	u8 tmp[AES_BLOCK_SIZE];
+
+	wpa_hexdump_key(MSG_EXCESSIVE, "CCM T", x, M);
+	/* U = T XOR S_0; S_0 = E(K, A_0) */
+	WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0);
+	aes_encrypt(aes, a, tmp);
+	for (i = 0; i < M; i++)
+		auth[i] = x[i] ^ tmp[i];
+	wpa_hexdump_key(MSG_EXCESSIVE, "CCM U", auth, M);
+}
+
+
+static void aes_ccm_decr_auth(void *aes, size_t M, u8 *a, const u8 *auth, u8 *t)
+{
+	size_t i;
+	u8 tmp[AES_BLOCK_SIZE];
+
+	wpa_hexdump_key(MSG_EXCESSIVE, "CCM U", auth, M);
+	/* U = T XOR S_0; S_0 = E(K, A_0) */
+	WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0);
+	aes_encrypt(aes, a, tmp);
+	for (i = 0; i < M; i++)
+		t[i] = auth[i] ^ tmp[i];
+	wpa_hexdump_key(MSG_EXCESSIVE, "CCM T", t, M);
+}
+
+
+/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */
+int aes_ccm_ae(const u8 *key, size_t key_len, const u8 *nonce,
+	       size_t M, const u8 *plain, size_t plain_len,
+	       const u8 *aad, size_t aad_len, u8 *crypt, u8 *auth)
+{
+	const size_t L = 2;
+	void *aes;
+	u8 x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE];
+
+	if (aad_len > 30 || M > AES_BLOCK_SIZE)
+		return -1;
+
+	aes = aes_encrypt_init(key, key_len);
+	if (aes == NULL)
+		return -1;
+
+	aes_ccm_auth_start(aes, M, L, nonce, aad, aad_len, plain_len, x);
+	aes_ccm_auth(aes, plain, plain_len, x);
+
+	/* Encryption */
+	aes_ccm_encr_start(L, nonce, a);
+	aes_ccm_encr(aes, L, plain, plain_len, crypt, a);
+	aes_ccm_encr_auth(aes, M, x, a, auth);
+
+	aes_encrypt_deinit(aes);
+
+	return 0;
+}
+
+
+/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */
+int aes_ccm_ad(const u8 *key, size_t key_len, const u8 *nonce,
+	       size_t M, const u8 *crypt, size_t crypt_len,
+	       const u8 *aad, size_t aad_len, const u8 *auth, u8 *plain)
+{
+	const size_t L = 2;
+	void *aes;
+	u8 x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE];
+	u8 t[AES_BLOCK_SIZE];
+
+	if (aad_len > 30 || M > AES_BLOCK_SIZE)
+		return -1;
+
+	aes = aes_encrypt_init(key, key_len);
+	if (aes == NULL)
+		return -1;
+
+	/* Decryption */
+	aes_ccm_encr_start(L, nonce, a);
+	aes_ccm_decr_auth(aes, M, a, auth, t);
+
+	/* plaintext = msg XOR (S_1 | S_2 | ... | S_n) */
+	aes_ccm_encr(aes, L, crypt, crypt_len, plain, a);
+
+	aes_ccm_auth_start(aes, M, L, nonce, aad, aad_len, crypt_len, x);
+	aes_ccm_auth(aes, plain, crypt_len, x);
+
+	aes_encrypt_deinit(aes);
+
+	if (os_memcmp_const(x, t, M) != 0) {
+		wpa_printf(MSG_EXCESSIVE, "CCM: Auth mismatch");
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-ctr.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-ctr.c
new file mode 100644
index 0000000..e27f3bb
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-ctr.c
@@ -0,0 +1,71 @@
+/*
+ * AES-128/192/256 CTR
+ *
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_ctr_encrypt - AES-128/192/256 CTR mode encryption
+ * @key: Key for encryption (key_len bytes)
+ * @key_len: Length of the key (16, 24, or 32 bytes)
+ * @nonce: Nonce for counter mode (16 bytes)
+ * @data: Data to encrypt in-place
+ * @data_len: Length of data in bytes
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce,
+		    u8 *data, size_t data_len)
+{
+	void *ctx;
+	size_t j, len, left = data_len;
+	int i;
+	u8 *pos = data;
+	u8 counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE];
+
+	ctx = aes_encrypt_init(key, key_len);
+	if (ctx == NULL)
+		return -1;
+	os_memcpy(counter, nonce, AES_BLOCK_SIZE);
+
+	while (left > 0) {
+		aes_encrypt(ctx, counter, buf);
+
+		len = (left < AES_BLOCK_SIZE) ? left : AES_BLOCK_SIZE;
+		for (j = 0; j < len; j++)
+			pos[j] ^= buf[j];
+		pos += len;
+		left -= len;
+
+		for (i = AES_BLOCK_SIZE - 1; i >= 0; i--) {
+			counter[i]++;
+			if (counter[i])
+				break;
+		}
+	}
+	aes_encrypt_deinit(ctx);
+	return 0;
+}
+
+
+/**
+ * aes_128_ctr_encrypt - AES-128 CTR mode encryption
+ * @key: Key for encryption (key_len bytes)
+ * @nonce: Nonce for counter mode (16 bytes)
+ * @data: Data to encrypt in-place
+ * @data_len: Length of data in bytes
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
+			u8 *data, size_t data_len)
+{
+	return aes_ctr_encrypt(key, 16, nonce, data, data_len);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-eax.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-eax.c
new file mode 100644
index 0000000..15a09f8
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-eax.c
@@ -0,0 +1,145 @@
+/*
+ * AES-128 EAX
+ *
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_128_eax_encrypt - AES-128 EAX mode encryption
+ * @key: Key for encryption (16 bytes)
+ * @nonce: Nonce for counter mode
+ * @nonce_len: Nonce length in bytes
+ * @hdr: Header data to be authenticity protected
+ * @hdr_len: Length of the header data bytes
+ * @data: Data to encrypt in-place
+ * @data_len: Length of data in bytes
+ * @tag: 16-byte tag value
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len,
+			const u8 *hdr, size_t hdr_len,
+			u8 *data, size_t data_len, u8 *tag)
+{
+	u8 *buf;
+	size_t buf_len;
+	u8 nonce_mac[AES_BLOCK_SIZE], hdr_mac[AES_BLOCK_SIZE],
+		data_mac[AES_BLOCK_SIZE];
+	int i, ret = -1;
+
+	if (nonce_len > data_len)
+		buf_len = nonce_len;
+	else
+		buf_len = data_len;
+	if (hdr_len > buf_len)
+		buf_len = hdr_len;
+	buf_len += 16;
+
+	buf = os_malloc(buf_len);
+	if (buf == NULL)
+		return -1;
+
+	os_memset(buf, 0, 15);
+
+	buf[15] = 0;
+	os_memcpy(buf + 16, nonce, nonce_len);
+	if (omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac))
+		goto fail;
+
+	buf[15] = 1;
+	os_memcpy(buf + 16, hdr, hdr_len);
+	if (omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac))
+		goto fail;
+
+	if (aes_128_ctr_encrypt(key, nonce_mac, data, data_len))
+		goto fail;
+	buf[15] = 2;
+	os_memcpy(buf + 16, data, data_len);
+	if (omac1_aes_128(key, buf, 16 + data_len, data_mac))
+		goto fail;
+
+	for (i = 0; i < AES_BLOCK_SIZE; i++)
+		tag[i] = nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i];
+
+	ret = 0;
+fail:
+	bin_clear_free(buf, buf_len);
+
+	return ret;
+}
+
+
+/**
+ * aes_128_eax_decrypt - AES-128 EAX mode decryption
+ * @key: Key for decryption (16 bytes)
+ * @nonce: Nonce for counter mode
+ * @nonce_len: Nonce length in bytes
+ * @hdr: Header data to be authenticity protected
+ * @hdr_len: Length of the header data bytes
+ * @data: Data to encrypt in-place
+ * @data_len: Length of data in bytes
+ * @tag: 16-byte tag value
+ * Returns: 0 on success, -1 on failure, -2 if tag does not match
+ */
+int aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len,
+			const u8 *hdr, size_t hdr_len,
+			u8 *data, size_t data_len, const u8 *tag)
+{
+	u8 *buf;
+	size_t buf_len;
+	u8 nonce_mac[AES_BLOCK_SIZE], hdr_mac[AES_BLOCK_SIZE],
+		data_mac[AES_BLOCK_SIZE];
+	int i;
+
+	if (nonce_len > data_len)
+		buf_len = nonce_len;
+	else
+		buf_len = data_len;
+	if (hdr_len > buf_len)
+		buf_len = hdr_len;
+	buf_len += 16;
+
+	buf = os_malloc(buf_len);
+	if (buf == NULL)
+		return -1;
+
+	os_memset(buf, 0, 15);
+
+	buf[15] = 0;
+	os_memcpy(buf + 16, nonce, nonce_len);
+	if (omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac)) {
+		os_free(buf);
+		return -1;
+	}
+
+	buf[15] = 1;
+	os_memcpy(buf + 16, hdr, hdr_len);
+	if (omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac)) {
+		os_free(buf);
+		return -1;
+	}
+
+	buf[15] = 2;
+	os_memcpy(buf + 16, data, data_len);
+	if (omac1_aes_128(key, buf, 16 + data_len, data_mac)) {
+		os_free(buf);
+		return -1;
+	}
+
+	os_free(buf);
+
+	for (i = 0; i < AES_BLOCK_SIZE; i++) {
+		if (tag[i] != (nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i]))
+			return -2;
+	}
+
+	return aes_128_ctr_encrypt(key, nonce_mac, data, data_len);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-encblock.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-encblock.c
new file mode 100644
index 0000000..a521621
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-encblock.c
@@ -0,0 +1,32 @@
+/*
+ * AES encrypt_block
+ *
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_128_encrypt_block - Perform one AES 128-bit block operation
+ * @key: Key for AES
+ * @in: Input data (16 bytes)
+ * @out: Output of the AES block operation (16 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out)
+{
+	void *ctx;
+	ctx = aes_encrypt_init(key, 16);
+	if (ctx == NULL)
+		return -1;
+	aes_encrypt(ctx, in, out);
+	aes_encrypt_deinit(ctx);
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-gcm.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-gcm.c
new file mode 100644
index 0000000..84294d2
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-gcm.c
@@ -0,0 +1,327 @@
+/*
+ * Galois/Counter Mode (GCM) and GMAC with AES
+ *
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+static void inc32(u8 *block)
+{
+	u32 val;
+	val = WPA_GET_BE32(block + AES_BLOCK_SIZE - 4);
+	val++;
+	WPA_PUT_BE32(block + AES_BLOCK_SIZE - 4, val);
+}
+
+
+static void xor_block(u8 *dst, const u8 *src)
+{
+	u32 *d = (u32 *) dst;
+	u32 *s = (u32 *) src;
+	*d++ ^= *s++;
+	*d++ ^= *s++;
+	*d++ ^= *s++;
+	*d++ ^= *s++;
+}
+
+
+static void shift_right_block(u8 *v)
+{
+	u32 val;
+
+	val = WPA_GET_BE32(v + 12);
+	val >>= 1;
+	if (v[11] & 0x01)
+		val |= 0x80000000;
+	WPA_PUT_BE32(v + 12, val);
+
+	val = WPA_GET_BE32(v + 8);
+	val >>= 1;
+	if (v[7] & 0x01)
+		val |= 0x80000000;
+	WPA_PUT_BE32(v + 8, val);
+
+	val = WPA_GET_BE32(v + 4);
+	val >>= 1;
+	if (v[3] & 0x01)
+		val |= 0x80000000;
+	WPA_PUT_BE32(v + 4, val);
+
+	val = WPA_GET_BE32(v);
+	val >>= 1;
+	WPA_PUT_BE32(v, val);
+}
+
+
+/* Multiplication in GF(2^128) */
+static void gf_mult(const u8 *x, const u8 *y, u8 *z)
+{
+	u8 v[16];
+	int i, j;
+
+	os_memset(z, 0, 16); /* Z_0 = 0^128 */
+	os_memcpy(v, y, 16); /* V_0 = Y */
+
+	for (i = 0; i < 16; i++) {
+		for (j = 0; j < 8; j++) {
+			if (x[i] & BIT(7 - j)) {
+				/* Z_(i + 1) = Z_i XOR V_i */
+				xor_block(z, v);
+			} else {
+				/* Z_(i + 1) = Z_i */
+			}
+
+			if (v[15] & 0x01) {
+				/* V_(i + 1) = (V_i >> 1) XOR R */
+				shift_right_block(v);
+				/* R = 11100001 || 0^120 */
+				v[0] ^= 0xe1;
+			} else {
+				/* V_(i + 1) = V_i >> 1 */
+				shift_right_block(v);
+			}
+		}
+	}
+}
+
+
+static void ghash_start(u8 *y)
+{
+	/* Y_0 = 0^128 */
+	os_memset(y, 0, 16);
+}
+
+
+static void ghash(const u8 *h, const u8 *x, size_t xlen, u8 *y)
+{
+	size_t m, i;
+	const u8 *xpos = x;
+	u8 tmp[16];
+
+	m = xlen / 16;
+
+	for (i = 0; i < m; i++) {
+		/* Y_i = (Y^(i-1) XOR X_i) dot H */
+		xor_block(y, xpos);
+		xpos += 16;
+
+		/* dot operation:
+		 * multiplication operation for binary Galois (finite) field of
+		 * 2^128 elements */
+		gf_mult(y, h, tmp);
+		os_memcpy(y, tmp, 16);
+	}
+
+	if (x + xlen > xpos) {
+		/* Add zero padded last block */
+		size_t last = x + xlen - xpos;
+		os_memcpy(tmp, xpos, last);
+		os_memset(tmp + last, 0, sizeof(tmp) - last);
+
+		/* Y_i = (Y^(i-1) XOR X_i) dot H */
+		xor_block(y, tmp);
+
+		/* dot operation:
+		 * multiplication operation for binary Galois (finite) field of
+		 * 2^128 elements */
+		gf_mult(y, h, tmp);
+		os_memcpy(y, tmp, 16);
+	}
+
+	/* Return Y_m */
+}
+
+
+static void aes_gctr(void *aes, const u8 *icb, const u8 *x, size_t xlen, u8 *y)
+{
+	size_t i, n, last;
+	u8 cb[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE];
+	const u8 *xpos = x;
+	u8 *ypos = y;
+
+	if (xlen == 0)
+		return;
+
+	n = xlen / 16;
+
+	os_memcpy(cb, icb, AES_BLOCK_SIZE);
+	/* Full blocks */
+	for (i = 0; i < n; i++) {
+		aes_encrypt(aes, cb, ypos);
+		xor_block(ypos, xpos);
+		xpos += AES_BLOCK_SIZE;
+		ypos += AES_BLOCK_SIZE;
+		inc32(cb);
+	}
+
+	last = x + xlen - xpos;
+	if (last) {
+		/* Last, partial block */
+		aes_encrypt(aes, cb, tmp);
+		for (i = 0; i < last; i++)
+			*ypos++ = *xpos++ ^ tmp[i];
+	}
+}
+
+
+static void * aes_gcm_init_hash_subkey(const u8 *key, size_t key_len, u8 *H)
+{
+	void *aes;
+
+	aes = aes_encrypt_init(key, key_len);
+	if (aes == NULL)
+		return NULL;
+
+	/* Generate hash subkey H = AES_K(0^128) */
+	os_memset(H, 0, AES_BLOCK_SIZE);
+	aes_encrypt(aes, H, H);
+	wpa_hexdump_key(MSG_EXCESSIVE, "Hash subkey H for GHASH",
+			H, AES_BLOCK_SIZE);
+	return aes;
+}
+
+
+static void aes_gcm_prepare_j0(const u8 *iv, size_t iv_len, const u8 *H, u8 *J0)
+{
+	u8 len_buf[16];
+
+	if (iv_len == 12) {
+		/* Prepare block J_0 = IV || 0^31 || 1 [len(IV) = 96] */
+		os_memcpy(J0, iv, iv_len);
+		os_memset(J0 + iv_len, 0, AES_BLOCK_SIZE - iv_len);
+		J0[AES_BLOCK_SIZE - 1] = 0x01;
+	} else {
+		/*
+		 * s = 128 * ceil(len(IV)/128) - len(IV)
+		 * J_0 = GHASH_H(IV || 0^(s+64) || [len(IV)]_64)
+		 */
+		ghash_start(J0);
+		ghash(H, iv, iv_len, J0);
+		WPA_PUT_BE64(len_buf, 0);
+		WPA_PUT_BE64(len_buf + 8, iv_len * 8);
+		ghash(H, len_buf, sizeof(len_buf), J0);
+	}
+}
+
+
+static void aes_gcm_gctr(void *aes, const u8 *J0, const u8 *in, size_t len,
+			 u8 *out)
+{
+	u8 J0inc[AES_BLOCK_SIZE];
+
+	if (len == 0)
+		return;
+
+	os_memcpy(J0inc, J0, AES_BLOCK_SIZE);
+	inc32(J0inc);
+	aes_gctr(aes, J0inc, in, len, out);
+}
+
+
+static void aes_gcm_ghash(const u8 *H, const u8 *aad, size_t aad_len,
+			  const u8 *crypt, size_t crypt_len, u8 *S)
+{
+	u8 len_buf[16];
+
+	/*
+	 * u = 128 * ceil[len(C)/128] - len(C)
+	 * v = 128 * ceil[len(A)/128] - len(A)
+	 * S = GHASH_H(A || 0^v || C || 0^u || [len(A)]64 || [len(C)]64)
+	 * (i.e., zero padded to block size A || C and lengths of each in bits)
+	 */
+	ghash_start(S);
+	ghash(H, aad, aad_len, S);
+	ghash(H, crypt, crypt_len, S);
+	WPA_PUT_BE64(len_buf, aad_len * 8);
+	WPA_PUT_BE64(len_buf + 8, crypt_len * 8);
+	ghash(H, len_buf, sizeof(len_buf), S);
+
+	wpa_hexdump_key(MSG_EXCESSIVE, "S = GHASH_H(...)", S, 16);
+}
+
+
+/**
+ * aes_gcm_ae - GCM-AE_K(IV, P, A)
+ */
+int aes_gcm_ae(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len,
+	       const u8 *plain, size_t plain_len,
+	       const u8 *aad, size_t aad_len, u8 *crypt, u8 *tag)
+{
+	u8 H[AES_BLOCK_SIZE];
+	u8 J0[AES_BLOCK_SIZE];
+	u8 S[16];
+	void *aes;
+
+	aes = aes_gcm_init_hash_subkey(key, key_len, H);
+	if (aes == NULL)
+		return -1;
+
+	aes_gcm_prepare_j0(iv, iv_len, H, J0);
+
+	/* C = GCTR_K(inc_32(J_0), P) */
+	aes_gcm_gctr(aes, J0, plain, plain_len, crypt);
+
+	aes_gcm_ghash(H, aad, aad_len, crypt, plain_len, S);
+
+	/* T = MSB_t(GCTR_K(J_0, S)) */
+	aes_gctr(aes, J0, S, sizeof(S), tag);
+
+	/* Return (C, T) */
+
+	aes_encrypt_deinit(aes);
+
+	return 0;
+}
+
+
+/**
+ * aes_gcm_ad - GCM-AD_K(IV, C, A, T)
+ */
+int aes_gcm_ad(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len,
+	       const u8 *crypt, size_t crypt_len,
+	       const u8 *aad, size_t aad_len, const u8 *tag, u8 *plain)
+{
+	u8 H[AES_BLOCK_SIZE];
+	u8 J0[AES_BLOCK_SIZE];
+	u8 S[16], T[16];
+	void *aes;
+
+	aes = aes_gcm_init_hash_subkey(key, key_len, H);
+	if (aes == NULL)
+		return -1;
+
+	aes_gcm_prepare_j0(iv, iv_len, H, J0);
+
+	/* P = GCTR_K(inc_32(J_0), C) */
+	aes_gcm_gctr(aes, J0, crypt, crypt_len, plain);
+
+	aes_gcm_ghash(H, aad, aad_len, crypt, crypt_len, S);
+
+	/* T' = MSB_t(GCTR_K(J_0, S)) */
+	aes_gctr(aes, J0, S, sizeof(S), T);
+
+	aes_encrypt_deinit(aes);
+
+	if (os_memcmp_const(tag, T, 16) != 0) {
+		wpa_printf(MSG_EXCESSIVE, "GCM: Tag mismatch");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int aes_gmac(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len,
+	     const u8 *aad, size_t aad_len, u8 *tag)
+{
+	return aes_gcm_ae(key, key_len, iv, iv_len, NULL, 0, aad, aad_len, NULL,
+			  tag);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-internal-dec.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-internal-dec.c
new file mode 100644
index 0000000..7482295
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-internal-dec.c
@@ -0,0 +1,163 @@
+/*
+ * AES (Rijndael) cipher - decrypt
+ *
+ * Modifications to public domain implementation:
+ * - cleanup
+ * - use C pre-processor to make it easier to change S table access
+ * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at
+ *   cost of reduced throughput (quite small difference on Pentium 4,
+ *   10-25% when using -O1 or -O2 optimization)
+ *
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+#include "aes_i.h"
+
+/**
+ * Expand the cipher key into the decryption key schedule.
+ *
+ * @return	the number of rounds for the given cipher key size.
+ */
+static int rijndaelKeySetupDec(u32 rk[], const u8 cipherKey[], int keyBits)
+{
+	int Nr, i, j;
+	u32 temp;
+
+	/* expand the cipher key: */
+	Nr = rijndaelKeySetupEnc(rk, cipherKey, keyBits);
+	if (Nr < 0)
+		return Nr;
+	/* invert the order of the round keys: */
+	for (i = 0, j = 4*Nr; i < j; i += 4, j -= 4) {
+		temp = rk[i    ]; rk[i    ] = rk[j    ]; rk[j    ] = temp;
+		temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp;
+		temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp;
+		temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp;
+	}
+	/* apply the inverse MixColumn transform to all round keys but the
+	 * first and the last: */
+	for (i = 1; i < Nr; i++) {
+		rk += 4;
+		for (j = 0; j < 4; j++) {
+			rk[j] = TD0_(TE4((rk[j] >> 24)       )) ^
+				TD1_(TE4((rk[j] >> 16) & 0xff)) ^
+				TD2_(TE4((rk[j] >>  8) & 0xff)) ^
+				TD3_(TE4((rk[j]      ) & 0xff));
+		}
+	}
+
+	return Nr;
+}
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+	u32 *rk;
+	int res;
+	rk = os_malloc(AES_PRIV_SIZE);
+	if (rk == NULL)
+		return NULL;
+	res = rijndaelKeySetupDec(rk, key, len * 8);
+	if (res < 0) {
+		os_free(rk);
+		return NULL;
+	}
+	rk[AES_PRIV_NR_POS] = res;
+	return rk;
+}
+
+static void rijndaelDecrypt(const u32 rk[/*44*/], int Nr, const u8 ct[16],
+			    u8 pt[16])
+{
+	u32 s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+	int r;
+#endif /* ?FULL_UNROLL */
+
+	/*
+	 * map byte array block to cipher state
+	 * and add initial round key:
+	 */
+	s0 = GETU32(ct     ) ^ rk[0];
+	s1 = GETU32(ct +  4) ^ rk[1];
+	s2 = GETU32(ct +  8) ^ rk[2];
+	s3 = GETU32(ct + 12) ^ rk[3];
+
+#define ROUND(i,d,s) \
+d##0 = TD0(s##0) ^ TD1(s##3) ^ TD2(s##2) ^ TD3(s##1) ^ rk[4 * i]; \
+d##1 = TD0(s##1) ^ TD1(s##0) ^ TD2(s##3) ^ TD3(s##2) ^ rk[4 * i + 1]; \
+d##2 = TD0(s##2) ^ TD1(s##1) ^ TD2(s##0) ^ TD3(s##3) ^ rk[4 * i + 2]; \
+d##3 = TD0(s##3) ^ TD1(s##2) ^ TD2(s##1) ^ TD3(s##0) ^ rk[4 * i + 3]
+
+#ifdef FULL_UNROLL
+
+	ROUND(1,t,s);
+	ROUND(2,s,t);
+	ROUND(3,t,s);
+	ROUND(4,s,t);
+	ROUND(5,t,s);
+	ROUND(6,s,t);
+	ROUND(7,t,s);
+	ROUND(8,s,t);
+	ROUND(9,t,s);
+	if (Nr > 10) {
+		ROUND(10,s,t);
+		ROUND(11,t,s);
+		if (Nr > 12) {
+			ROUND(12,s,t);
+			ROUND(13,t,s);
+		}
+	}
+
+	rk += Nr << 2;
+
+#else  /* !FULL_UNROLL */
+
+	/* Nr - 1 full rounds: */
+	r = Nr >> 1;
+	for (;;) {
+		ROUND(1,t,s);
+		rk += 8;
+		if (--r == 0)
+			break;
+		ROUND(0,s,t);
+	}
+
+#endif /* ?FULL_UNROLL */
+
+#undef ROUND
+
+	/*
+	 * apply last round and
+	 * map cipher state to byte array block:
+	 */
+	s0 = TD41(t0) ^ TD42(t3) ^ TD43(t2) ^ TD44(t1) ^ rk[0];
+	PUTU32(pt     , s0);
+	s1 = TD41(t1) ^ TD42(t0) ^ TD43(t3) ^ TD44(t2) ^ rk[1];
+	PUTU32(pt +  4, s1);
+	s2 = TD41(t2) ^ TD42(t1) ^ TD43(t0) ^ TD44(t3) ^ rk[2];
+	PUTU32(pt +  8, s2);
+	s3 = TD41(t3) ^ TD42(t2) ^ TD43(t1) ^ TD44(t0) ^ rk[3];
+	PUTU32(pt + 12, s3);
+}
+
+
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+	u32 *rk = ctx;
+	rijndaelDecrypt(ctx, rk[AES_PRIV_NR_POS], crypt, plain);
+	return 0;
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+	os_memset(ctx, 0, AES_PRIV_SIZE);
+	os_free(ctx);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-internal-enc.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-internal-enc.c
new file mode 100644
index 0000000..baeffca
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-internal-enc.c
@@ -0,0 +1,131 @@
+/*
+ * AES (Rijndael) cipher - encrypt
+ *
+ * Modifications to public domain implementation:
+ * - cleanup
+ * - use C pre-processor to make it easier to change S table access
+ * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at
+ *   cost of reduced throughput (quite small difference on Pentium 4,
+ *   10-25% when using -O1 or -O2 optimization)
+ *
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+#include "aes_i.h"
+
+static void rijndaelEncrypt(const u32 rk[], int Nr, const u8 pt[16], u8 ct[16])
+{
+	u32 s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+	int r;
+#endif /* ?FULL_UNROLL */
+
+	/*
+	 * map byte array block to cipher state
+	 * and add initial round key:
+	 */
+	s0 = GETU32(pt     ) ^ rk[0];
+	s1 = GETU32(pt +  4) ^ rk[1];
+	s2 = GETU32(pt +  8) ^ rk[2];
+	s3 = GETU32(pt + 12) ^ rk[3];
+
+#define ROUND(i,d,s) \
+d##0 = TE0(s##0) ^ TE1(s##1) ^ TE2(s##2) ^ TE3(s##3) ^ rk[4 * i]; \
+d##1 = TE0(s##1) ^ TE1(s##2) ^ TE2(s##3) ^ TE3(s##0) ^ rk[4 * i + 1]; \
+d##2 = TE0(s##2) ^ TE1(s##3) ^ TE2(s##0) ^ TE3(s##1) ^ rk[4 * i + 2]; \
+d##3 = TE0(s##3) ^ TE1(s##0) ^ TE2(s##1) ^ TE3(s##2) ^ rk[4 * i + 3]
+
+#ifdef FULL_UNROLL
+
+	ROUND(1,t,s);
+	ROUND(2,s,t);
+	ROUND(3,t,s);
+	ROUND(4,s,t);
+	ROUND(5,t,s);
+	ROUND(6,s,t);
+	ROUND(7,t,s);
+	ROUND(8,s,t);
+	ROUND(9,t,s);
+	if (Nr > 10) {
+		ROUND(10,s,t);
+		ROUND(11,t,s);
+		if (Nr > 12) {
+			ROUND(12,s,t);
+			ROUND(13,t,s);
+		}
+	}
+
+	rk += Nr << 2;
+
+#else  /* !FULL_UNROLL */
+
+	/* Nr - 1 full rounds: */
+	r = Nr >> 1;
+	for (;;) {
+		ROUND(1,t,s);
+		rk += 8;
+		if (--r == 0)
+			break;
+		ROUND(0,s,t);
+	}
+
+#endif /* ?FULL_UNROLL */
+
+#undef ROUND
+
+	/*
+	 * apply last round and
+	 * map cipher state to byte array block:
+	 */
+	s0 = TE41(t0) ^ TE42(t1) ^ TE43(t2) ^ TE44(t3) ^ rk[0];
+	PUTU32(ct     , s0);
+	s1 = TE41(t1) ^ TE42(t2) ^ TE43(t3) ^ TE44(t0) ^ rk[1];
+	PUTU32(ct +  4, s1);
+	s2 = TE41(t2) ^ TE42(t3) ^ TE43(t0) ^ TE44(t1) ^ rk[2];
+	PUTU32(ct +  8, s2);
+	s3 = TE41(t3) ^ TE42(t0) ^ TE43(t1) ^ TE44(t2) ^ rk[3];
+	PUTU32(ct + 12, s3);
+}
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+	u32 *rk;
+	int res;
+
+	if (TEST_FAIL())
+		return NULL;
+
+	rk = os_malloc(AES_PRIV_SIZE);
+	if (rk == NULL)
+		return NULL;
+	res = rijndaelKeySetupEnc(rk, key, len * 8);
+	if (res < 0) {
+		os_free(rk);
+		return NULL;
+	}
+	rk[AES_PRIV_NR_POS] = res;
+	return rk;
+}
+
+
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+	u32 *rk = ctx;
+	rijndaelEncrypt(ctx, rk[AES_PRIV_NR_POS], plain, crypt);
+	return 0;
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+	os_memset(ctx, 0, AES_PRIV_SIZE);
+	os_free(ctx);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-internal.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-internal.c
new file mode 100644
index 0000000..bd4535d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-internal.c
@@ -0,0 +1,845 @@
+/*
+ * AES (Rijndael) cipher
+ *
+ * Modifications to public domain implementation:
+ * - cleanup
+ * - use C pre-processor to make it easier to change S table access
+ * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at
+ *   cost of reduced throughput (quite small difference on Pentium 4,
+ *   10-25% when using -O1 or -O2 optimization)
+ *
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+#include "aes_i.h"
+
+/*
+ * rijndael-alg-fst.c
+ *
+ * @version 3.0 (December 2000)
+ *
+ * Optimised ANSI C code for the Rijndael cipher (now AES)
+ *
+ * @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be>
+ * @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be>
+ * @author Paulo Barreto <paulo.barreto@terra.com.br>
+ *
+ * This code is hereby placed in the public domain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+Te0[x] = S [x].[02, 01, 01, 03];
+Te1[x] = S [x].[03, 02, 01, 01];
+Te2[x] = S [x].[01, 03, 02, 01];
+Te3[x] = S [x].[01, 01, 03, 02];
+Te4[x] = S [x].[01, 01, 01, 01];
+
+Td0[x] = Si[x].[0e, 09, 0d, 0b];
+Td1[x] = Si[x].[0b, 0e, 09, 0d];
+Td2[x] = Si[x].[0d, 0b, 0e, 09];
+Td3[x] = Si[x].[09, 0d, 0b, 0e];
+Td4[x] = Si[x].[01, 01, 01, 01];
+*/
+
+const u32 Te0[256] = {
+    0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU,
+    0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U,
+    0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU,
+    0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU,
+    0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,
+    0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU,
+    0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU,
+    0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU,
+    0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU,
+    0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,
+    0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U,
+    0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU,
+    0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU,
+    0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U,
+    0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,
+    0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU,
+    0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU,
+    0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU,
+    0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU,
+    0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,
+    0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU,
+    0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU,
+    0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU,
+    0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU,
+    0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,
+    0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U,
+    0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U,
+    0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U,
+    0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU,
+    0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,
+    0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U,
+    0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU,
+    0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU,
+    0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U,
+    0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,
+    0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U,
+    0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU,
+    0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U,
+    0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU,
+    0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,
+    0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU,
+    0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U,
+    0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U,
+    0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU,
+    0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,
+    0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U,
+    0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U,
+    0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U,
+    0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U,
+    0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,
+    0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U,
+    0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U,
+    0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU,
+    0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U,
+    0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,
+    0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U,
+    0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U,
+    0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U,
+    0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U,
+    0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,
+    0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U,
+    0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U,
+    0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U,
+    0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU,
+};
+#ifndef AES_SMALL_TABLES
+const u32 Te1[256] = {
+    0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU,
+    0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U,
+    0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU,
+    0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U,
+    0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU,
+    0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U,
+    0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU,
+    0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U,
+    0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U,
+    0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU,
+    0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U,
+    0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U,
+    0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U,
+    0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU,
+    0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U,
+    0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U,
+    0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU,
+    0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U,
+    0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U,
+    0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U,
+    0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU,
+    0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU,
+    0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U,
+    0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU,
+    0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU,
+    0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U,
+    0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU,
+    0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U,
+    0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU,
+    0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U,
+    0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U,
+    0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U,
+    0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU,
+    0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U,
+    0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU,
+    0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U,
+    0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU,
+    0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U,
+    0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U,
+    0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU,
+    0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU,
+    0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU,
+    0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U,
+    0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U,
+    0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU,
+    0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U,
+    0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU,
+    0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U,
+    0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU,
+    0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U,
+    0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU,
+    0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU,
+    0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U,
+    0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU,
+    0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U,
+    0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU,
+    0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U,
+    0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U,
+    0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U,
+    0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU,
+    0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU,
+    0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U,
+    0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU,
+    0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U,
+};
+const u32 Te2[256] = {
+    0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU,
+    0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U,
+    0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU,
+    0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U,
+    0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU,
+    0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U,
+    0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU,
+    0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U,
+    0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U,
+    0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU,
+    0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U,
+    0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U,
+    0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U,
+    0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU,
+    0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U,
+    0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U,
+    0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU,
+    0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U,
+    0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U,
+    0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U,
+    0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU,
+    0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU,
+    0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U,
+    0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU,
+    0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU,
+    0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U,
+    0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU,
+    0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U,
+    0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU,
+    0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U,
+    0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U,
+    0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U,
+    0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU,
+    0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U,
+    0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU,
+    0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U,
+    0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU,
+    0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U,
+    0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U,
+    0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU,
+    0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU,
+    0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU,
+    0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U,
+    0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U,
+    0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU,
+    0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U,
+    0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU,
+    0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U,
+    0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU,
+    0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U,
+    0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU,
+    0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU,
+    0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U,
+    0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU,
+    0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U,
+    0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU,
+    0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U,
+    0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U,
+    0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U,
+    0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU,
+    0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU,
+    0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U,
+    0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU,
+    0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U,
+};
+const u32 Te3[256] = {
+
+    0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U,
+    0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U,
+    0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U,
+    0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU,
+    0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU,
+    0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU,
+    0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U,
+    0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU,
+    0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU,
+    0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U,
+    0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U,
+    0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU,
+    0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU,
+    0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU,
+    0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU,
+    0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU,
+    0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U,
+    0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU,
+    0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU,
+    0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U,
+    0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U,
+    0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U,
+    0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U,
+    0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U,
+    0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU,
+    0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U,
+    0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU,
+    0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU,
+    0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U,
+    0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U,
+    0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U,
+    0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU,
+    0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U,
+    0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU,
+    0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU,
+    0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U,
+    0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U,
+    0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU,
+    0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U,
+    0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU,
+    0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U,
+    0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U,
+    0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U,
+    0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U,
+    0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU,
+    0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U,
+    0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU,
+    0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U,
+    0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU,
+    0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U,
+    0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU,
+    0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU,
+    0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU,
+    0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU,
+    0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U,
+    0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U,
+    0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U,
+    0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U,
+    0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U,
+    0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U,
+    0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU,
+    0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U,
+    0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU,
+    0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU,
+};
+const u32 Te4[256] = {
+    0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU,
+    0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U,
+    0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU,
+    0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U,
+    0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU,
+    0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U,
+    0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU,
+    0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U,
+    0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U,
+    0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU,
+    0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U,
+    0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U,
+    0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U,
+    0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU,
+    0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U,
+    0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U,
+    0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU,
+    0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U,
+    0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U,
+    0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U,
+    0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU,
+    0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU,
+    0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U,
+    0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU,
+    0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU,
+    0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U,
+    0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU,
+    0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U,
+    0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU,
+    0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U,
+    0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U,
+    0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U,
+    0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU,
+    0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U,
+    0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU,
+    0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U,
+    0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU,
+    0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U,
+    0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U,
+    0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU,
+    0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU,
+    0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU,
+    0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U,
+    0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U,
+    0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU,
+    0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U,
+    0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU,
+    0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U,
+    0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU,
+    0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U,
+    0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU,
+    0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU,
+    0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U,
+    0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU,
+    0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U,
+    0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU,
+    0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U,
+    0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U,
+    0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U,
+    0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU,
+    0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU,
+    0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U,
+    0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU,
+    0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U,
+};
+#endif /* AES_SMALL_TABLES */
+const u32 Td0[256] = {
+    0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U,
+    0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U,
+    0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U,
+    0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU,
+    0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U,
+    0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U,
+    0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU,
+    0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U,
+    0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU,
+    0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U,
+    0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U,
+    0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U,
+    0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U,
+    0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU,
+    0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U,
+    0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU,
+    0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U,
+    0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU,
+    0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U,
+    0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U,
+    0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U,
+    0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU,
+    0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U,
+    0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU,
+    0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U,
+    0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU,
+    0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U,
+    0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU,
+    0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU,
+    0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U,
+    0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU,
+    0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U,
+    0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU,
+    0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U,
+    0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U,
+    0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U,
+    0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU,
+    0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U,
+    0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U,
+    0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU,
+    0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U,
+    0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U,
+    0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U,
+    0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U,
+    0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U,
+    0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU,
+    0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U,
+    0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U,
+    0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U,
+    0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U,
+    0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U,
+    0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU,
+    0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU,
+    0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU,
+    0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU,
+    0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U,
+    0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U,
+    0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU,
+    0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU,
+    0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U,
+    0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU,
+    0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U,
+    0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U,
+    0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U,
+};
+#ifndef AES_SMALL_TABLES
+const u32 Td1[256] = {
+    0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU,
+    0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U,
+    0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU,
+    0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U,
+    0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U,
+    0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U,
+    0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U,
+    0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U,
+    0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U,
+    0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU,
+    0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU,
+    0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU,
+    0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U,
+    0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU,
+    0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U,
+    0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U,
+    0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U,
+    0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU,
+    0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU,
+    0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U,
+    0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU,
+    0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U,
+    0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU,
+    0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU,
+    0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U,
+    0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U,
+    0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U,
+    0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU,
+    0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U,
+    0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU,
+    0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U,
+    0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U,
+    0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U,
+    0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU,
+    0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U,
+    0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U,
+    0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U,
+    0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U,
+    0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U,
+    0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U,
+    0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU,
+    0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU,
+    0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U,
+    0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU,
+    0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U,
+    0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU,
+    0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU,
+    0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U,
+    0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU,
+    0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U,
+    0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U,
+    0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U,
+    0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U,
+    0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U,
+    0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U,
+    0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U,
+    0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU,
+    0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U,
+    0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U,
+    0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU,
+    0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U,
+    0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U,
+    0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U,
+    0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U,
+};
+const u32 Td2[256] = {
+    0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U,
+    0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U,
+    0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U,
+    0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U,
+    0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU,
+    0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U,
+    0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U,
+    0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U,
+    0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U,
+    0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU,
+    0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U,
+    0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U,
+    0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU,
+    0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U,
+    0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U,
+    0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U,
+    0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U,
+    0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U,
+    0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U,
+    0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU,
+
+    0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U,
+    0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U,
+    0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U,
+    0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U,
+    0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U,
+    0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU,
+    0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU,
+    0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U,
+    0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU,
+    0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U,
+    0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU,
+    0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU,
+    0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU,
+    0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU,
+    0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U,
+    0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U,
+    0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U,
+    0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U,
+    0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U,
+    0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U,
+    0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U,
+    0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU,
+    0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU,
+    0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U,
+    0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U,
+    0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU,
+    0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU,
+    0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U,
+    0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U,
+    0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U,
+    0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U,
+    0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U,
+    0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U,
+    0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U,
+    0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU,
+    0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U,
+    0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U,
+    0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U,
+    0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U,
+    0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U,
+    0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U,
+    0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU,
+    0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U,
+    0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U,
+};
+const u32 Td3[256] = {
+    0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU,
+    0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU,
+    0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U,
+    0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U,
+    0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU,
+    0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU,
+    0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U,
+    0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU,
+    0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U,
+    0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU,
+    0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U,
+    0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U,
+    0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U,
+    0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U,
+    0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U,
+    0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU,
+    0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU,
+    0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U,
+    0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U,
+    0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU,
+    0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU,
+    0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U,
+    0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U,
+    0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U,
+    0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U,
+    0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU,
+    0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U,
+    0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U,
+    0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU,
+    0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU,
+    0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U,
+    0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U,
+    0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U,
+    0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU,
+    0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U,
+    0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U,
+    0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U,
+    0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U,
+    0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U,
+    0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U,
+    0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U,
+    0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU,
+    0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U,
+    0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U,
+    0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU,
+    0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU,
+    0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U,
+    0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU,
+    0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U,
+    0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U,
+    0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U,
+    0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U,
+    0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U,
+    0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U,
+    0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU,
+    0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU,
+    0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU,
+    0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU,
+    0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U,
+    0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U,
+    0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U,
+    0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU,
+    0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U,
+    0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U,
+};
+const u32 Td4[256] = {
+    0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U,
+    0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U,
+    0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU,
+    0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU,
+    0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U,
+    0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U,
+    0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U,
+    0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU,
+    0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U,
+    0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU,
+    0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU,
+    0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU,
+    0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U,
+    0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U,
+    0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U,
+    0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U,
+    0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U,
+    0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U,
+    0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU,
+    0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U,
+    0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U,
+    0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU,
+    0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U,
+    0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U,
+    0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U,
+    0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU,
+    0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U,
+    0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U,
+    0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU,
+    0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U,
+    0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U,
+    0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU,
+    0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U,
+    0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU,
+    0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU,
+    0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U,
+    0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U,
+    0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U,
+    0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U,
+    0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU,
+    0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U,
+    0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U,
+    0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU,
+    0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU,
+    0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU,
+    0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U,
+    0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU,
+    0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U,
+    0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U,
+    0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U,
+    0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U,
+    0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU,
+    0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U,
+    0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU,
+    0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU,
+    0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU,
+    0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU,
+    0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U,
+    0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU,
+    0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U,
+    0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU,
+    0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U,
+    0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U,
+    0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU,
+};
+const u32 rcon[] = {
+	0x01000000, 0x02000000, 0x04000000, 0x08000000,
+	0x10000000, 0x20000000, 0x40000000, 0x80000000,
+	0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+#else /* AES_SMALL_TABLES */
+const u8 Td4s[256] = {
+    0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U,
+    0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU,
+    0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU, 0xffU, 0x87U,
+    0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU,
+    0x54U, 0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU,
+    0xeeU, 0x4cU, 0x95U, 0x0bU, 0x42U, 0xfaU, 0xc3U, 0x4eU,
+    0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U, 0xb2U,
+    0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U,
+    0x72U, 0xf8U, 0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U,
+    0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU, 0x65U, 0xb6U, 0x92U,
+    0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU,
+    0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U,
+    0x90U, 0xd8U, 0xabU, 0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU,
+    0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U, 0x45U, 0x06U,
+    0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U,
+    0xc1U, 0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU,
+    0x3aU, 0x91U, 0x11U, 0x41U, 0x4fU, 0x67U, 0xdcU, 0xeaU,
+    0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U, 0x73U,
+    0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U,
+    0xe2U, 0xf9U, 0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU,
+    0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU, 0x29U, 0xc5U, 0x89U,
+    0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU,
+    0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U,
+    0x9aU, 0xdbU, 0xc0U, 0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U,
+    0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U, 0xc7U, 0x31U,
+    0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU,
+    0x60U, 0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU,
+    0x2dU, 0xe5U, 0x7aU, 0x9fU, 0x93U, 0xc9U, 0x9cU, 0xefU,
+    0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U, 0xb0U,
+    0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U,
+    0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U,
+    0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU,
+};
+const u8 rcons[] = {
+	0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36
+	/* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+#endif /* AES_SMALL_TABLES */
+/**
+ * Expand the cipher key into the encryption key schedule.
+ *
+ * @return	the number of rounds for the given cipher key size.
+ */
+int rijndaelKeySetupEnc(u32 rk[], const u8 cipherKey[], int keyBits)
+{
+	int i;
+	u32 temp;
+
+	rk[0] = GETU32(cipherKey     );
+	rk[1] = GETU32(cipherKey +  4);
+	rk[2] = GETU32(cipherKey +  8);
+	rk[3] = GETU32(cipherKey + 12);
+
+	if (keyBits == 128) {
+		for (i = 0; i < 10; i++) {
+			temp  = rk[3];
+			rk[4] = rk[0] ^ TE421(temp) ^ TE432(temp) ^
+				TE443(temp) ^ TE414(temp) ^ RCON(i);
+			rk[5] = rk[1] ^ rk[4];
+			rk[6] = rk[2] ^ rk[5];
+			rk[7] = rk[3] ^ rk[6];
+			rk += 4;
+		}
+		return 10;
+	}
+
+	rk[4] = GETU32(cipherKey + 16);
+	rk[5] = GETU32(cipherKey + 20);
+
+	if (keyBits == 192) {
+		for (i = 0; i < 8; i++) {
+			temp  = rk[5];
+			rk[6] = rk[0] ^ TE421(temp) ^ TE432(temp) ^
+				TE443(temp) ^ TE414(temp) ^ RCON(i);
+			rk[7] = rk[1] ^ rk[6];
+			rk[8] = rk[2] ^ rk[7];
+			rk[9] = rk[3] ^ rk[8];
+			if (i == 7)
+				return 12;
+			rk[10] = rk[4] ^ rk[9];
+			rk[11] = rk[5] ^ rk[10];
+			rk += 6;
+		}
+	}
+
+	rk[6] = GETU32(cipherKey + 24);
+	rk[7] = GETU32(cipherKey + 28);
+
+	if (keyBits == 256) {
+		for (i = 0; i < 7; i++) {
+			temp  = rk[7];
+			rk[8] = rk[0] ^ TE421(temp) ^ TE432(temp) ^
+				TE443(temp) ^ TE414(temp) ^ RCON(i);
+			rk[9] = rk[1] ^ rk[8];
+			rk[10] = rk[2] ^ rk[9];
+			rk[11] = rk[3] ^ rk[10];
+			if (i == 6)
+				return 14;
+			temp  = rk[11];
+			rk[12] = rk[4] ^ TE411(temp) ^ TE422(temp) ^
+				TE433(temp) ^ TE444(temp);
+			rk[13] = rk[5] ^ rk[12];
+			rk[14] = rk[6] ^ rk[13];
+			rk[15] = rk[7] ^ rk[14];
+			rk += 8;
+		}
+	}
+
+	return -1;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-omac1.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-omac1.c
new file mode 100644
index 0000000..8642516
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-omac1.c
@@ -0,0 +1,173 @@
+/*
+ * One-key CBC MAC (OMAC1) hash with AES
+ *
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+static void gf_mulx(u8 *pad)
+{
+	int i, carry;
+
+	carry = pad[0] & 0x80;
+	for (i = 0; i < AES_BLOCK_SIZE - 1; i++)
+		pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7);
+	pad[AES_BLOCK_SIZE - 1] <<= 1;
+	if (carry)
+		pad[AES_BLOCK_SIZE - 1] ^= 0x87;
+}
+
+
+/**
+ * omac1_aes_vector - One-Key CBC MAC (OMAC1) hash with AES
+ * @key: Key for the hash operation
+ * @key_len: Key length in octets
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for MAC (128 bits, i.e., 16 bytes)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is a mode for using block cipher (AES in this case) for authentication.
+ * OMAC1 was standardized with the name CMAC by NIST in a Special Publication
+ * (SP) 800-38B.
+ */
+int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	void *ctx;
+	u8 cbc[AES_BLOCK_SIZE], pad[AES_BLOCK_SIZE];
+	const u8 *pos, *end;
+	size_t i, e, left, total_len;
+
+	if (TEST_FAIL())
+		return -1;
+
+	ctx = aes_encrypt_init(key, key_len);
+	if (ctx == NULL)
+		return -1;
+	os_memset(cbc, 0, AES_BLOCK_SIZE);
+
+	total_len = 0;
+	for (e = 0; e < num_elem; e++)
+		total_len += len[e];
+	left = total_len;
+
+	e = 0;
+	pos = addr[0];
+	end = pos + len[0];
+
+	while (left >= AES_BLOCK_SIZE) {
+		for (i = 0; i < AES_BLOCK_SIZE; i++) {
+			cbc[i] ^= *pos++;
+			if (pos >= end) {
+				/*
+				 * Stop if there are no more bytes to process
+				 * since there are no more entries in the array.
+				 */
+				if (i + 1 == AES_BLOCK_SIZE &&
+				    left == AES_BLOCK_SIZE)
+					break;
+				e++;
+				pos = addr[e];
+				end = pos + len[e];
+			}
+		}
+		if (left > AES_BLOCK_SIZE)
+			aes_encrypt(ctx, cbc, cbc);
+		left -= AES_BLOCK_SIZE;
+	}
+
+	os_memset(pad, 0, AES_BLOCK_SIZE);
+	aes_encrypt(ctx, pad, pad);
+	gf_mulx(pad);
+
+	if (left || total_len == 0) {
+		for (i = 0; i < left; i++) {
+			cbc[i] ^= *pos++;
+			if (pos >= end) {
+				/*
+				 * Stop if there are no more bytes to process
+				 * since there are no more entries in the array.
+				 */
+				if (i + 1 == left)
+					break;
+				e++;
+				pos = addr[e];
+				end = pos + len[e];
+			}
+		}
+		cbc[left] ^= 0x80;
+		gf_mulx(pad);
+	}
+
+	for (i = 0; i < AES_BLOCK_SIZE; i++)
+		pad[i] ^= cbc[i];
+	aes_encrypt(ctx, pad, mac);
+	aes_encrypt_deinit(ctx);
+	return 0;
+}
+
+
+/**
+ * omac1_aes_128_vector - One-Key CBC MAC (OMAC1) hash with AES-128
+ * @key: 128-bit key for the hash operation
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for MAC (128 bits, i.e., 16 bytes)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is a mode for using block cipher (AES in this case) for authentication.
+ * OMAC1 was standardized with the name CMAC by NIST in a Special Publication
+ * (SP) 800-38B.
+ */
+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
+			 const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
+}
+
+
+/**
+ * omac1_aes_128 - One-Key CBC MAC (OMAC1) hash with AES-128 (aka AES-CMAC)
+ * @key: 128-bit key for the hash operation
+ * @data: Data buffer for which a MAC is determined
+ * @data_len: Length of data buffer in bytes
+ * @mac: Buffer for MAC (128 bits, i.e., 16 bytes)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is a mode for using block cipher (AES in this case) for authentication.
+ * OMAC1 was standardized with the name CMAC by NIST in a Special Publication
+ * (SP) 800-38B.
+ */
+int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+	return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
+}
+
+
+/**
+ * omac1_aes_256 - One-Key CBC MAC (OMAC1) hash with AES-256 (aka AES-CMAC)
+ * @key: 256-bit key for the hash operation
+ * @data: Data buffer for which a MAC is determined
+ * @data_len: Length of data buffer in bytes
+ * @mac: Buffer for MAC (128 bits, i.e., 16 bytes)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is a mode for using block cipher (AES in this case) for authentication.
+ * OMAC1 was standardized with the name CMAC by NIST in a Special Publication
+ * (SP) 800-38B.
+ */
+int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+	return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-siv.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-siv.c
new file mode 100644
index 0000000..b682f3a
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-siv.c
@@ -0,0 +1,208 @@
+/*
+ * AES SIV (RFC 5297)
+ * Copyright (c) 2013 Cozybit, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+#include "aes_siv.h"
+
+
+static const u8 zero[AES_BLOCK_SIZE];
+
+
+static void dbl(u8 *pad)
+{
+	int i, carry;
+
+	carry = pad[0] & 0x80;
+	for (i = 0; i < AES_BLOCK_SIZE - 1; i++)
+		pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7);
+	pad[AES_BLOCK_SIZE - 1] <<= 1;
+	if (carry)
+		pad[AES_BLOCK_SIZE - 1] ^= 0x87;
+}
+
+
+static void xor(u8 *a, const u8 *b)
+{
+	int i;
+
+	for (i = 0; i < AES_BLOCK_SIZE; i++)
+		*a++ ^= *b++;
+}
+
+
+static void xorend(u8 *a, int alen, const u8 *b, int blen)
+{
+	int i;
+
+	if (alen < blen)
+		return;
+
+	for (i = 0; i < blen; i++)
+		a[alen - blen + i] ^= b[i];
+}
+
+
+static void pad_block(u8 *pad, const u8 *addr, size_t len)
+{
+	os_memset(pad, 0, AES_BLOCK_SIZE);
+	os_memcpy(pad, addr, len);
+
+	if (len < AES_BLOCK_SIZE)
+		pad[len] = 0x80;
+}
+
+
+static int aes_s2v(const u8 *key, size_t key_len,
+		   size_t num_elem, const u8 *addr[], size_t *len, u8 *mac)
+{
+	u8 tmp[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE];
+	u8 *buf = NULL;
+	int ret;
+	size_t i;
+	const u8 *data[1];
+	size_t data_len[1];
+
+	if (!num_elem) {
+		os_memcpy(tmp, zero, sizeof(zero));
+		tmp[AES_BLOCK_SIZE - 1] = 1;
+		data[0] = tmp;
+		data_len[0] = sizeof(tmp);
+		return omac1_aes_vector(key, key_len, 1, data, data_len, mac);
+	}
+
+	data[0] = zero;
+	data_len[0] = sizeof(zero);
+	ret = omac1_aes_vector(key, key_len, 1, data, data_len, tmp);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < num_elem - 1; i++) {
+		ret = omac1_aes_vector(key, key_len, 1, &addr[i], &len[i],
+				       tmp2);
+		if (ret)
+			return ret;
+
+		dbl(tmp);
+		xor(tmp, tmp2);
+	}
+	if (len[i] >= AES_BLOCK_SIZE) {
+		buf = os_memdup(addr[i], len[i]);
+		if (!buf)
+			return -ENOMEM;
+
+		xorend(buf, len[i], tmp, AES_BLOCK_SIZE);
+		data[0] = buf;
+		ret = omac1_aes_vector(key, key_len, 1, data, &len[i], mac);
+		bin_clear_free(buf, len[i]);
+		return ret;
+	}
+
+	dbl(tmp);
+	pad_block(tmp2, addr[i], len[i]);
+	xor(tmp, tmp2);
+
+	data[0] = tmp;
+	data_len[0] = sizeof(tmp);
+	return omac1_aes_vector(key, key_len, 1, data, data_len, mac);
+}
+
+
+int aes_siv_encrypt(const u8 *key, size_t key_len,
+		    const u8 *pw, size_t pwlen,
+		    size_t num_elem, const u8 *addr[], const size_t *len,
+		    u8 *out)
+{
+	const u8 *_addr[6];
+	size_t _len[6];
+	const u8 *k1, *k2;
+	u8 v[AES_BLOCK_SIZE];
+	size_t i;
+	u8 *iv, *crypt_pw;
+
+	if (num_elem > ARRAY_SIZE(_addr) - 1 ||
+	    (key_len != 32 && key_len != 48 && key_len != 64))
+		return -1;
+
+	key_len /= 2;
+	k1 = key;
+	k2 = key + key_len;
+
+	for (i = 0; i < num_elem; i++) {
+		_addr[i] = addr[i];
+		_len[i] = len[i];
+	}
+	_addr[num_elem] = pw;
+	_len[num_elem] = pwlen;
+
+	if (aes_s2v(k1, key_len, num_elem + 1, _addr, _len, v))
+		return -1;
+
+	iv = out;
+	crypt_pw = out + AES_BLOCK_SIZE;
+
+	os_memcpy(iv, v, AES_BLOCK_SIZE);
+	os_memcpy(crypt_pw, pw, pwlen);
+
+	/* zero out 63rd and 31st bits of ctr (from right) */
+	v[8] &= 0x7f;
+	v[12] &= 0x7f;
+	return aes_ctr_encrypt(k2, key_len, v, crypt_pw, pwlen);
+}
+
+
+int aes_siv_decrypt(const u8 *key, size_t key_len,
+		    const u8 *iv_crypt, size_t iv_c_len,
+		    size_t num_elem, const u8 *addr[], const size_t *len,
+		    u8 *out)
+{
+	const u8 *_addr[6];
+	size_t _len[6];
+	const u8 *k1, *k2;
+	size_t crypt_len;
+	size_t i;
+	int ret;
+	u8 iv[AES_BLOCK_SIZE];
+	u8 check[AES_BLOCK_SIZE];
+
+	if (iv_c_len < AES_BLOCK_SIZE || num_elem > ARRAY_SIZE(_addr) - 1 ||
+	    (key_len != 32 && key_len != 48 && key_len != 64))
+		return -1;
+	crypt_len = iv_c_len - AES_BLOCK_SIZE;
+	key_len /= 2;
+	k1 = key;
+	k2 = key + key_len;
+
+	for (i = 0; i < num_elem; i++) {
+		_addr[i] = addr[i];
+		_len[i] = len[i];
+	}
+	_addr[num_elem] = out;
+	_len[num_elem] = crypt_len;
+
+	os_memcpy(iv, iv_crypt, AES_BLOCK_SIZE);
+	os_memcpy(out, iv_crypt + AES_BLOCK_SIZE, crypt_len);
+
+	iv[8] &= 0x7f;
+	iv[12] &= 0x7f;
+
+	ret = aes_ctr_encrypt(k2, key_len, iv, out, crypt_len);
+	if (ret)
+		return ret;
+
+	ret = aes_s2v(k1, key_len, num_elem + 1, _addr, _len, check);
+	if (ret)
+		return ret;
+	if (os_memcmp(check, iv_crypt, AES_BLOCK_SIZE) == 0)
+		return 0;
+
+	return -1;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-unwrap.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-unwrap.c
new file mode 100644
index 0000000..ec793d9
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-unwrap.c
@@ -0,0 +1,80 @@
+/*
+ * AES key unwrap (RFC3394)
+ *
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_unwrap - Unwrap key with AES Key Wrap Algorithm (RFC3394)
+ * @kek: Key encryption key (KEK)
+ * @kek_len: Length of KEK in octets
+ * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16
+ * bytes
+ * @cipher: Wrapped key to be unwrapped, (n + 1) * 64 bits
+ * @plain: Plaintext key, n * 64 bits
+ * Returns: 0 on success, -1 on failure (e.g., integrity verification failed)
+ */
+int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
+	       u8 *plain)
+{
+	u8 a[8], *r, b[AES_BLOCK_SIZE];
+	int i, j;
+	void *ctx;
+	unsigned int t;
+
+	/* 1) Initialize variables. */
+	os_memcpy(a, cipher, 8);
+	r = plain;
+	os_memcpy(r, cipher + 8, 8 * n);
+
+	ctx = aes_decrypt_init(kek, kek_len);
+	if (ctx == NULL)
+		return -1;
+
+	/* 2) Compute intermediate values.
+	 * For j = 5 to 0
+	 *     For i = n to 1
+	 *         B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i
+	 *         A = MSB(64, B)
+	 *         R[i] = LSB(64, B)
+	 */
+	for (j = 5; j >= 0; j--) {
+		r = plain + (n - 1) * 8;
+		for (i = n; i >= 1; i--) {
+			os_memcpy(b, a, 8);
+			t = n * j + i;
+			b[7] ^= t;
+			b[6] ^= t >> 8;
+			b[5] ^= t >> 16;
+			b[4] ^= t >> 24;
+
+			os_memcpy(b + 8, r, 8);
+			aes_decrypt(ctx, b, b);
+			os_memcpy(a, b, 8);
+			os_memcpy(r, b + 8, 8);
+			r -= 8;
+		}
+	}
+	aes_decrypt_deinit(ctx);
+
+	/* 3) Output results.
+	 *
+	 * These are already in @plain due to the location of temporary
+	 * variables. Just verify that the IV matches with the expected value.
+	 */
+	for (i = 0; i < 8; i++) {
+		if (a[i] != 0xa6)
+			return -1;
+	}
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-wrap.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-wrap.c
new file mode 100644
index 0000000..7ed34e8
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes-wrap.c
@@ -0,0 +1,76 @@
+/*
+ * AES Key Wrap Algorithm (RFC3394)
+ *
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_wrap - Wrap keys with AES Key Wrap Algorithm (RFC3394)
+ * @kek: Key encryption key (KEK)
+ * @kek_len: Length of KEK in octets
+ * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16
+ * bytes
+ * @plain: Plaintext key to be wrapped, n * 64 bits
+ * @cipher: Wrapped key, (n + 1) * 64 bits
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
+{
+	u8 *a, *r, b[AES_BLOCK_SIZE];
+	int i, j;
+	void *ctx;
+	unsigned int t;
+
+	a = cipher;
+	r = cipher + 8;
+
+	/* 1) Initialize variables. */
+	os_memset(a, 0xa6, 8);
+	os_memcpy(r, plain, 8 * n);
+
+	ctx = aes_encrypt_init(kek, kek_len);
+	if (ctx == NULL)
+		return -1;
+
+	/* 2) Calculate intermediate values.
+	 * For j = 0 to 5
+	 *     For i=1 to n
+	 *         B = AES(K, A | R[i])
+	 *         A = MSB(64, B) ^ t where t = (n*j)+i
+	 *         R[i] = LSB(64, B)
+	 */
+	for (j = 0; j <= 5; j++) {
+		r = cipher + 8;
+		for (i = 1; i <= n; i++) {
+			os_memcpy(b, a, 8);
+			os_memcpy(b + 8, r, 8);
+			aes_encrypt(ctx, b, b);
+			os_memcpy(a, b, 8);
+			t = n * j + i;
+			a[7] ^= t;
+			a[6] ^= t >> 8;
+			a[5] ^= t >> 16;
+			a[4] ^= t >> 24;
+			os_memcpy(r, b + 8, 8);
+			r += 8;
+		}
+	}
+	aes_encrypt_deinit(ctx);
+
+	/* 3) Output the results.
+	 *
+	 * These are already in @cipher due to the location of temporary
+	 * variables.
+	 */
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes.h
new file mode 100644
index 0000000..8ab3de2
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes.h
@@ -0,0 +1,21 @@
+/*
+ * AES functions
+ * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AES_H
+#define AES_H
+
+#define AES_BLOCK_SIZE 16
+
+void * aes_encrypt_init(const u8 *key, size_t len);
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
+void aes_encrypt_deinit(void *ctx);
+void * aes_decrypt_init(const u8 *key, size_t len);
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
+void aes_decrypt_deinit(void *ctx);
+
+#endif /* AES_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes_i.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes_i.h
new file mode 100644
index 0000000..b20ec92
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes_i.h
@@ -0,0 +1,125 @@
+/*
+ * AES (Rijndael) cipher
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AES_I_H
+#define AES_I_H
+
+#include "aes.h"
+
+/* #define FULL_UNROLL */
+#define AES_SMALL_TABLES
+
+extern const u32 Te0[256];
+extern const u32 Te1[256];
+extern const u32 Te2[256];
+extern const u32 Te3[256];
+extern const u32 Te4[256];
+extern const u32 Td0[256];
+extern const u32 Td1[256];
+extern const u32 Td2[256];
+extern const u32 Td3[256];
+extern const u32 Td4[256];
+extern const u32 rcon[10];
+extern const u8 Td4s[256];
+extern const u8 rcons[10];
+
+#ifndef AES_SMALL_TABLES
+
+#define RCON(i) rcon[(i)]
+
+#define TE0(i) Te0[((i) >> 24) & 0xff]
+#define TE1(i) Te1[((i) >> 16) & 0xff]
+#define TE2(i) Te2[((i) >> 8) & 0xff]
+#define TE3(i) Te3[(i) & 0xff]
+#define TE41(i) (Te4[((i) >> 24) & 0xff] & 0xff000000)
+#define TE42(i) (Te4[((i) >> 16) & 0xff] & 0x00ff0000)
+#define TE43(i) (Te4[((i) >> 8) & 0xff] & 0x0000ff00)
+#define TE44(i) (Te4[(i) & 0xff] & 0x000000ff)
+#define TE421(i) (Te4[((i) >> 16) & 0xff] & 0xff000000)
+#define TE432(i) (Te4[((i) >> 8) & 0xff] & 0x00ff0000)
+#define TE443(i) (Te4[(i) & 0xff] & 0x0000ff00)
+#define TE414(i) (Te4[((i) >> 24) & 0xff] & 0x000000ff)
+#define TE411(i) (Te4[((i) >> 24) & 0xff] & 0xff000000)
+#define TE422(i) (Te4[((i) >> 16) & 0xff] & 0x00ff0000)
+#define TE433(i) (Te4[((i) >> 8) & 0xff] & 0x0000ff00)
+#define TE444(i) (Te4[(i) & 0xff] & 0x000000ff)
+#define TE4(i) (Te4[(i)] & 0x000000ff)
+
+#define TD0(i) Td0[((i) >> 24) & 0xff]
+#define TD1(i) Td1[((i) >> 16) & 0xff]
+#define TD2(i) Td2[((i) >> 8) & 0xff]
+#define TD3(i) Td3[(i) & 0xff]
+#define TD41(i) (Td4[((i) >> 24) & 0xff] & 0xff000000)
+#define TD42(i) (Td4[((i) >> 16) & 0xff] & 0x00ff0000)
+#define TD43(i) (Td4[((i) >> 8) & 0xff] & 0x0000ff00)
+#define TD44(i) (Td4[(i) & 0xff] & 0x000000ff)
+#define TD0_(i) Td0[(i) & 0xff]
+#define TD1_(i) Td1[(i) & 0xff]
+#define TD2_(i) Td2[(i) & 0xff]
+#define TD3_(i) Td3[(i) & 0xff]
+
+#else /* AES_SMALL_TABLES */
+
+#define RCON(i) ((u32) rcons[(i)] << 24)
+
+static inline u32 rotr(u32 val, int bits)
+{
+	return (val >> bits) | (val << (32 - bits));
+}
+
+#define TE0(i) Te0[((i) >> 24) & 0xff]
+#define TE1(i) rotr(Te0[((i) >> 16) & 0xff], 8)
+#define TE2(i) rotr(Te0[((i) >> 8) & 0xff], 16)
+#define TE3(i) rotr(Te0[(i) & 0xff], 24)
+#define TE41(i) ((Te0[((i) >> 24) & 0xff] << 8) & 0xff000000)
+#define TE42(i) (Te0[((i) >> 16) & 0xff] & 0x00ff0000)
+#define TE43(i) (Te0[((i) >> 8) & 0xff] & 0x0000ff00)
+#define TE44(i) ((Te0[(i) & 0xff] >> 8) & 0x000000ff)
+#define TE421(i) ((Te0[((i) >> 16) & 0xff] << 8) & 0xff000000)
+#define TE432(i) (Te0[((i) >> 8) & 0xff] & 0x00ff0000)
+#define TE443(i) (Te0[(i) & 0xff] & 0x0000ff00)
+#define TE414(i) ((Te0[((i) >> 24) & 0xff] >> 8) & 0x000000ff)
+#define TE411(i) ((Te0[((i) >> 24) & 0xff] << 8) & 0xff000000)
+#define TE422(i) (Te0[((i) >> 16) & 0xff] & 0x00ff0000)
+#define TE433(i) (Te0[((i) >> 8) & 0xff] & 0x0000ff00)
+#define TE444(i) ((Te0[(i) & 0xff] >> 8) & 0x000000ff)
+#define TE4(i) ((Te0[(i)] >> 8) & 0x000000ff)
+
+#define TD0(i) Td0[((i) >> 24) & 0xff]
+#define TD1(i) rotr(Td0[((i) >> 16) & 0xff], 8)
+#define TD2(i) rotr(Td0[((i) >> 8) & 0xff], 16)
+#define TD3(i) rotr(Td0[(i) & 0xff], 24)
+#define TD41(i) ((u32) Td4s[((i) >> 24) & 0xff] << 24)
+#define TD42(i) ((u32) Td4s[((i) >> 16) & 0xff] << 16)
+#define TD43(i) ((u32) Td4s[((i) >> 8) & 0xff] << 8)
+#define TD44(i) ((u32) Td4s[(i) & 0xff])
+#define TD0_(i) Td0[(i) & 0xff]
+#define TD1_(i) rotr(Td0[(i) & 0xff], 8)
+#define TD2_(i) rotr(Td0[(i) & 0xff], 16)
+#define TD3_(i) rotr(Td0[(i) & 0xff], 24)
+
+#endif /* AES_SMALL_TABLES */
+
+#ifdef _MSC_VER
+#define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00)
+#define GETU32(p) SWAP(*((u32 *)(p)))
+#define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); }
+#else
+#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ \
+((u32)(pt)[2] <<  8) ^ ((u32)(pt)[3]))
+#define PUTU32(ct, st) { \
+(ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); \
+(ct)[2] = (u8)((st) >>  8); (ct)[3] = (u8)(st); }
+#endif
+
+#define AES_PRIV_SIZE (4 * 4 * 15 + 4)
+#define AES_PRIV_NR_POS (4 * 15)
+
+int rijndaelKeySetupEnc(u32 rk[], const u8 cipherKey[], int keyBits);
+
+#endif /* AES_I_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes_siv.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes_siv.h
new file mode 100644
index 0000000..fb05d80
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes_siv.h
@@ -0,0 +1,21 @@
+/*
+ * AES SIV (RFC 5297)
+ * Copyright (c) 2013 Cozybit, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AES_SIV_H
+#define AES_SIV_H
+
+int aes_siv_encrypt(const u8 *key, size_t key_len,
+		    const u8 *pw, size_t pwlen,
+		    size_t num_elem, const u8 *addr[], const size_t *len,
+		    u8 *out);
+int aes_siv_decrypt(const u8 *key, size_t key_len,
+		    const u8 *iv_crypt, size_t iv_c_len,
+		    size_t num_elem, const u8 *addr[], const size_t *len,
+		    u8 *out);
+
+#endif /* AES_SIV_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes_wrap.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes_wrap.h
new file mode 100644
index 0000000..b70b1d2
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/aes_wrap.h
@@ -0,0 +1,73 @@
+/*
+ * AES-based functions
+ *
+ * - AES Key Wrap Algorithm (RFC3394)
+ * - One-Key CBC MAC (OMAC1) hash with AES-128 and AES-256
+ * - AES-128/192/256 CTR mode encryption
+ * - AES-128 EAX mode encryption/decryption
+ * - AES-128 CBC
+ * - AES-GCM
+ * - AES-CCM
+ *
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AES_WRAP_H
+#define AES_WRAP_H
+
+int __must_check aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain,
+			  u8 *cipher);
+int __must_check aes_unwrap(const u8 *kek, size_t kek_len, int n,
+			    const u8 *cipher, u8 *plain);
+int __must_check omac1_aes_vector(const u8 *key, size_t key_len,
+				  size_t num_elem, const u8 *addr[],
+				  const size_t *len, u8 *mac);
+int __must_check omac1_aes_128_vector(const u8 *key, size_t num_elem,
+				      const u8 *addr[], const size_t *len,
+				      u8 *mac);
+int __must_check omac1_aes_128(const u8 *key, const u8 *data, size_t data_len,
+			       u8 *mac);
+int __must_check omac1_aes_256(const u8 *key, const u8 *data, size_t data_len,
+			       u8 *mac);
+int __must_check aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out);
+int __must_check aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce,
+				 u8 *data, size_t data_len);
+int __must_check aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
+				     u8 *data, size_t data_len);
+int __must_check aes_128_eax_encrypt(const u8 *key,
+				     const u8 *nonce, size_t nonce_len,
+				     const u8 *hdr, size_t hdr_len,
+				     u8 *data, size_t data_len, u8 *tag);
+int __must_check aes_128_eax_decrypt(const u8 *key,
+				     const u8 *nonce, size_t nonce_len,
+				     const u8 *hdr, size_t hdr_len,
+				     u8 *data, size_t data_len, const u8 *tag);
+int __must_check aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data,
+				     size_t data_len);
+int __must_check aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data,
+				     size_t data_len);
+int __must_check aes_gcm_ae(const u8 *key, size_t key_len,
+			    const u8 *iv, size_t iv_len,
+			    const u8 *plain, size_t plain_len,
+			    const u8 *aad, size_t aad_len,
+			    u8 *crypt, u8 *tag);
+int __must_check aes_gcm_ad(const u8 *key, size_t key_len,
+			    const u8 *iv, size_t iv_len,
+			    const u8 *crypt, size_t crypt_len,
+			    const u8 *aad, size_t aad_len, const u8 *tag,
+			    u8 *plain);
+int __must_check aes_gmac(const u8 *key, size_t key_len,
+			  const u8 *iv, size_t iv_len,
+			  const u8 *aad, size_t aad_len, u8 *tag);
+int __must_check aes_ccm_ae(const u8 *key, size_t key_len, const u8 *nonce,
+			    size_t M, const u8 *plain, size_t plain_len,
+			    const u8 *aad, size_t aad_len, u8 *crypt, u8 *auth);
+int __must_check aes_ccm_ad(const u8 *key, size_t key_len, const u8 *nonce,
+			    size_t M, const u8 *crypt, size_t crypt_len,
+			    const u8 *aad, size_t aad_len, const u8 *auth,
+			    u8 *plain);
+
+#endif /* AES_WRAP_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto.h
new file mode 100644
index 0000000..0ac8fc1
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto.h
@@ -0,0 +1,1389 @@
+/*
+ * Wrapper functions for crypto libraries
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file defines the cryptographic functions that need to be implemented
+ * for wpa_supplicant and hostapd. When TLS is not used, internal
+ * implementation of MD5, SHA1, and AES is used and no external libraries are
+ * required. When TLS is enabled (e.g., by enabling EAP-TLS or EAP-PEAP), the
+ * crypto library used by the TLS implementation is expected to be used for
+ * non-TLS needs, too, in order to save space by not implementing these
+ * functions twice.
+ *
+ * Wrapper code for using each crypto library is in its own file (crypto*.c)
+ * and one of these files is build and linked in to provide the functions
+ * defined here.
+ */
+
+#ifndef CRYPTO_H
+#define CRYPTO_H
+
+#define HMAC_VECTOR_MAX_ELEM 11
+
+/**
+ * md4_vector - MD4 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
+
+/**
+ * md5_vector - MD5 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
+
+
+/**
+ * sha1_vector - SHA-1 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		u8 *mac);
+
+/**
+ * fips186_2-prf - NIST FIPS Publication 186-2 change notice 1 PRF
+ * @seed: Seed/key for the PRF
+ * @seed_len: Seed length in bytes
+ * @x: Buffer for PRF output
+ * @xlen: Output length in bytes
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function implements random number generation specified in NIST FIPS
+ * Publication 186-2 for EAP-SIM. This PRF uses a function that is similar to
+ * SHA-1, but has different message padding.
+ */
+int __must_check fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x,
+			       size_t xlen);
+
+/**
+ * sha256_vector - SHA256 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac);
+
+/**
+ * sha384_vector - SHA384 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac);
+
+/**
+ * sha512_vector - SHA512 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac);
+
+/**
+ * des_encrypt - Encrypt one block with DES
+ * @clear: 8 octets (in)
+ * @key: 7 octets (in) (no parity bits included)
+ * @cypher: 8 octets (out)
+ * Returns: 0 on success, -1 on failure
+ */
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher);
+
+/**
+ * aes_encrypt_init - Initialize AES for encryption
+ * @key: Encryption key
+ * @len: Key length in bytes (usually 16, i.e., 128 bits)
+ * Returns: Pointer to context data or %NULL on failure
+ */
+void * aes_encrypt_init(const u8 *key, size_t len);
+
+/**
+ * aes_encrypt - Encrypt one AES block
+ * @ctx: Context pointer from aes_encrypt_init()
+ * @plain: Plaintext data to be encrypted (16 bytes)
+ * @crypt: Buffer for the encrypted data (16 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
+
+/**
+ * aes_encrypt_deinit - Deinitialize AES encryption
+ * @ctx: Context pointer from aes_encrypt_init()
+ */
+void aes_encrypt_deinit(void *ctx);
+
+/**
+ * aes_decrypt_init - Initialize AES for decryption
+ * @key: Decryption key
+ * @len: Key length in bytes (usually 16, i.e., 128 bits)
+ * Returns: Pointer to context data or %NULL on failure
+ */
+void * aes_decrypt_init(const u8 *key, size_t len);
+
+/**
+ * aes_decrypt - Decrypt one AES block
+ * @ctx: Context pointer from aes_encrypt_init()
+ * @crypt: Encrypted data (16 bytes)
+ * @plain: Buffer for the decrypted data (16 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
+
+/**
+ * aes_decrypt_deinit - Deinitialize AES decryption
+ * @ctx: Context pointer from aes_encrypt_init()
+ */
+void aes_decrypt_deinit(void *ctx);
+
+
+enum crypto_hash_alg {
+	CRYPTO_HASH_ALG_MD5, CRYPTO_HASH_ALG_SHA1,
+	CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1,
+	CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256,
+	CRYPTO_HASH_ALG_SHA384, CRYPTO_HASH_ALG_SHA512
+};
+
+struct crypto_hash;
+
+/**
+ * crypto_hash_init - Initialize hash/HMAC function
+ * @alg: Hash algorithm
+ * @key: Key for keyed hash (e.g., HMAC) or %NULL if not needed
+ * @key_len: Length of the key in bytes
+ * Returns: Pointer to hash context to use with other hash functions or %NULL
+ * on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+				      size_t key_len);
+
+/**
+ * crypto_hash_update - Add data to hash calculation
+ * @ctx: Context pointer from crypto_hash_init()
+ * @data: Data buffer to add
+ * @len: Length of the buffer
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len);
+
+/**
+ * crypto_hash_finish - Complete hash calculation
+ * @ctx: Context pointer from crypto_hash_init()
+ * @hash: Buffer for hash value or %NULL if caller is just freeing the hash
+ * context
+ * @len: Pointer to length of the buffer or %NULL if caller is just freeing the
+ * hash context; on return, this is set to the actual length of the hash value
+ * Returns: 0 on success, -1 if buffer is too small (len set to needed length),
+ * or -2 on other failures (including failed crypto_hash_update() operations)
+ *
+ * This function calculates the hash value and frees the context buffer that
+ * was used for hash calculation.
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *hash, size_t *len);
+
+
+enum crypto_cipher_alg {
+	CRYPTO_CIPHER_NULL = 0, CRYPTO_CIPHER_ALG_AES, CRYPTO_CIPHER_ALG_3DES,
+	CRYPTO_CIPHER_ALG_DES, CRYPTO_CIPHER_ALG_RC2, CRYPTO_CIPHER_ALG_RC4
+};
+
+struct crypto_cipher;
+
+/**
+ * crypto_cipher_init - Initialize block/stream cipher function
+ * @alg: Cipher algorithm
+ * @iv: Initialization vector for block ciphers or %NULL for stream ciphers
+ * @key: Cipher key
+ * @key_len: Length of key in bytes
+ * Returns: Pointer to cipher context to use with other cipher functions or
+ * %NULL on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+					  const u8 *iv, const u8 *key,
+					  size_t key_len);
+
+/**
+ * crypto_cipher_encrypt - Cipher encrypt
+ * @ctx: Context pointer from crypto_cipher_init()
+ * @plain: Plaintext to cipher
+ * @crypt: Resulting ciphertext
+ * @len: Length of the plaintext
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_cipher_encrypt(struct crypto_cipher *ctx,
+				       const u8 *plain, u8 *crypt, size_t len);
+
+/**
+ * crypto_cipher_decrypt - Cipher decrypt
+ * @ctx: Context pointer from crypto_cipher_init()
+ * @crypt: Ciphertext to decrypt
+ * @plain: Resulting plaintext
+ * @len: Length of the cipher text
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_cipher_decrypt(struct crypto_cipher *ctx,
+				       const u8 *crypt, u8 *plain, size_t len);
+
+/**
+ * crypto_cipher_decrypt - Free cipher context
+ * @ctx: Context pointer from crypto_cipher_init()
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_cipher_deinit(struct crypto_cipher *ctx);
+
+
+struct crypto_public_key;
+struct crypto_private_key;
+
+/**
+ * crypto_public_key_import - Import an RSA public key
+ * @key: Key buffer (DER encoded RSA public key)
+ * @len: Key buffer length in bytes
+ * Returns: Pointer to the public key or %NULL on failure
+ *
+ * This function can just return %NULL if the crypto library supports X.509
+ * parsing. In that case, crypto_public_key_from_cert() is used to import the
+ * public key from a certificate.
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len);
+
+struct crypto_public_key *
+crypto_public_key_import_parts(const u8 *n, size_t n_len,
+			       const u8 *e, size_t e_len);
+
+/**
+ * crypto_private_key_import - Import an RSA private key
+ * @key: Key buffer (DER encoded RSA private key)
+ * @len: Key buffer length in bytes
+ * @passwd: Key encryption password or %NULL if key is not encrypted
+ * Returns: Pointer to the private key or %NULL on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_private_key * crypto_private_key_import(const u8 *key,
+						      size_t len,
+						      const char *passwd);
+
+/**
+ * crypto_public_key_from_cert - Import an RSA public key from a certificate
+ * @buf: DER encoded X.509 certificate
+ * @len: Certificate buffer length in bytes
+ * Returns: Pointer to public key or %NULL on failure
+ *
+ * This function can just return %NULL if the crypto library does not support
+ * X.509 parsing. In that case, internal code will be used to parse the
+ * certificate and public key is imported using crypto_public_key_import().
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf,
+						       size_t len);
+
+/**
+ * crypto_public_key_encrypt_pkcs1_v15 - Public key encryption (PKCS #1 v1.5)
+ * @key: Public key
+ * @in: Plaintext buffer
+ * @inlen: Length of plaintext buffer in bytes
+ * @out: Output buffer for encrypted data
+ * @outlen: Length of output buffer in bytes; set to used length on success
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_public_key_encrypt_pkcs1_v15(
+	struct crypto_public_key *key, const u8 *in, size_t inlen,
+	u8 *out, size_t *outlen);
+
+/**
+ * crypto_private_key_decrypt_pkcs1_v15 - Private key decryption (PKCS #1 v1.5)
+ * @key: Private key
+ * @in: Encrypted buffer
+ * @inlen: Length of encrypted buffer in bytes
+ * @out: Output buffer for encrypted data
+ * @outlen: Length of output buffer in bytes; set to used length on success
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_private_key_decrypt_pkcs1_v15(
+	struct crypto_private_key *key, const u8 *in, size_t inlen,
+	u8 *out, size_t *outlen);
+
+/**
+ * crypto_private_key_sign_pkcs1 - Sign with private key (PKCS #1)
+ * @key: Private key from crypto_private_key_import()
+ * @in: Plaintext buffer
+ * @inlen: Length of plaintext buffer in bytes
+ * @out: Output buffer for encrypted (signed) data
+ * @outlen: Length of output buffer in bytes; set to used length on success
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_private_key_sign_pkcs1(struct crypto_private_key *key,
+					       const u8 *in, size_t inlen,
+					       u8 *out, size_t *outlen);
+
+/**
+ * crypto_public_key_free - Free public key
+ * @key: Public key
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_public_key_free(struct crypto_public_key *key);
+
+/**
+ * crypto_private_key_free - Free private key
+ * @key: Private key from crypto_private_key_import()
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_private_key_free(struct crypto_private_key *key);
+
+/**
+ * crypto_public_key_decrypt_pkcs1 - Decrypt PKCS #1 signature
+ * @key: Public key
+ * @crypt: Encrypted signature data (using the private key)
+ * @crypt_len: Encrypted signature data length
+ * @plain: Buffer for plaintext (at least crypt_len bytes)
+ * @plain_len: Plaintext length (max buffer size on input, real len on output);
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check crypto_public_key_decrypt_pkcs1(
+	struct crypto_public_key *key, const u8 *crypt, size_t crypt_len,
+	u8 *plain, size_t *plain_len);
+
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+		   u8 *pubkey);
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+			    const u8 *order, size_t order_len,
+			    const u8 *privkey, size_t privkey_len,
+			    const u8 *pubkey, size_t pubkey_len,
+			    u8 *secret, size_t *len);
+
+/**
+ * crypto_global_init - Initialize crypto wrapper
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_global_init(void);
+
+/**
+ * crypto_global_deinit - Deinitialize crypto wrapper
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_global_deinit(void);
+
+/**
+ * crypto_mod_exp - Modular exponentiation of large integers
+ * @base: Base integer (big endian byte array)
+ * @base_len: Length of base integer in bytes
+ * @power: Power integer (big endian byte array)
+ * @power_len: Length of power integer in bytes
+ * @modulus: Modulus integer (big endian byte array)
+ * @modulus_len: Length of modulus integer in bytes
+ * @result: Buffer for the result
+ * @result_len: Result length (max buffer size on input, real len on output)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function calculates result = base ^ power mod modulus. modules_len is
+ * used as the maximum size of modulus buffer. It is set to the used size on
+ * success.
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_mod_exp(const u8 *base, size_t base_len,
+				const u8 *power, size_t power_len,
+				const u8 *modulus, size_t modulus_len,
+				u8 *result, size_t *result_len);
+
+/**
+ * rc4_skip - XOR RC4 stream to given data with skip-stream-start
+ * @key: RC4 key
+ * @keylen: RC4 key length
+ * @skip: number of bytes to skip from the beginning of the RC4 stream
+ * @data: data to be XOR'ed with RC4 stream
+ * @data_len: buf length
+ * Returns: 0 on success, -1 on failure
+ *
+ * Generate RC4 pseudo random stream for the given key, skip beginning of the
+ * stream, and XOR the end result with the data buffer to perform RC4
+ * encryption/decryption.
+ */
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+	     u8 *data, size_t data_len);
+
+/**
+ * crypto_get_random - Generate cryptographically strong pseudo-random bytes
+ * @buf: Buffer for data
+ * @len: Number of bytes to generate
+ * Returns: 0 on success, -1 on failure
+ *
+ * If the PRNG does not have enough entropy to ensure unpredictable byte
+ * sequence, this functions must return -1.
+ */
+int crypto_get_random(void *buf, size_t len);
+
+/**
+ * crypto_pkcs7_get_certificates - Extract X.509 certificates from PKCS#7 data
+ * @pkcs7: DER encoded PKCS#7 data
+ * Returns: Buffer of the extracted PEM X.509 certificates or %NULL on failure
+ */
+struct wpabuf * crypto_pkcs7_get_certificates(const struct wpabuf *pkcs7);
+
+
+/**
+ * struct crypto_bignum - bignum
+ *
+ * Internal data structure for bignum implementation. The contents is specific
+ * to the used crypto library.
+ */
+struct crypto_bignum;
+
+/**
+ * crypto_bignum_init - Allocate memory for bignum
+ * Returns: Pointer to allocated bignum or %NULL on failure
+ */
+struct crypto_bignum * crypto_bignum_init(void);
+
+/**
+ * crypto_bignum_init_set - Allocate memory for bignum and set the value
+ * @buf: Buffer with unsigned binary value
+ * @len: Length of buf in octets
+ * Returns: Pointer to allocated bignum or %NULL on failure
+ */
+struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len);
+
+/**
+ * crypto_bignum_init_set - Allocate memory for bignum and set the value (uint)
+ * @val: Value to set
+ * Returns: Pointer to allocated bignum or %NULL on failure
+ */
+struct crypto_bignum * crypto_bignum_init_uint(unsigned int val);
+
+/**
+ * crypto_bignum_deinit - Free bignum
+ * @n: Bignum from crypto_bignum_init() or crypto_bignum_init_set()
+ * @clear: Whether to clear the value from memory
+ */
+void crypto_bignum_deinit(struct crypto_bignum *n, int clear);
+
+/**
+ * crypto_bignum_to_bin - Set binary buffer to unsigned bignum
+ * @a: Bignum
+ * @buf: Buffer for the binary number
+ * @len: Length of @buf in octets
+ * @padlen: Length in octets to pad the result to or 0 to indicate no padding
+ * Returns: Number of octets written on success, -1 on failure
+ */
+int crypto_bignum_to_bin(const struct crypto_bignum *a,
+			 u8 *buf, size_t buflen, size_t padlen);
+
+/**
+ * crypto_bignum_rand - Create a random number in range of modulus
+ * @r: Bignum; set to a random value
+ * @m: Bignum; modulus
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m);
+
+/**
+ * crypto_bignum_add - c = a + b
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a + b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_add(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_mod - c = a % b
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a % b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_mod(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_exptmod - Modular exponentiation: d = a^b (mod c)
+ * @a: Bignum; base
+ * @b: Bignum; exponent
+ * @c: Bignum; modulus
+ * @d: Bignum; used to store the result of a^b (mod c)
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_exptmod(const struct crypto_bignum *a,
+			  const struct crypto_bignum *b,
+			  const struct crypto_bignum *c,
+			  struct crypto_bignum *d);
+
+/**
+ * crypto_bignum_inverse - Inverse a bignum so that a * c = 1 (mod b)
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_inverse(const struct crypto_bignum *a,
+			  const struct crypto_bignum *b,
+			  struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_sub - c = a - b
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a - b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_sub(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_div - c = a / b
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a / b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_div(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_addmod - d = a + b (mod c)
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum
+ * @d: Bignum; used to store the result of (a + b) % c
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_addmod(const struct crypto_bignum *a,
+			 const struct crypto_bignum *b,
+			 const struct crypto_bignum *c,
+			 struct crypto_bignum *d);
+
+/**
+ * crypto_bignum_mulmod - d = a * b (mod c)
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum
+ * @d: Bignum; used to store the result of (a * b) % c
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_mulmod(const struct crypto_bignum *a,
+			 const struct crypto_bignum *b,
+			 const struct crypto_bignum *c,
+			 struct crypto_bignum *d);
+
+/**
+ * crypto_bignum_sqrmod - c = a^2 (mod b)
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a^2 % b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_sqrmod(const struct crypto_bignum *a,
+			 const struct crypto_bignum *b,
+			 struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_rshift - r = a >> n
+ * @a: Bignum
+ * @n: Number of bits
+ * @r: Bignum; used to store the result of a >> n
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
+			 struct crypto_bignum *r);
+
+/**
+ * crypto_bignum_cmp - Compare two bignums
+ * @a: Bignum
+ * @b: Bignum
+ * Returns: -1 if a < b, 0 if a == b, or 1 if a > b
+ */
+int crypto_bignum_cmp(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b);
+
+/**
+ * crypto_bignum_is_zero - Is the given bignum zero
+ * @a: Bignum
+ * Returns: 1 if @a is zero or 0 if not
+ */
+int crypto_bignum_is_zero(const struct crypto_bignum *a);
+
+/**
+ * crypto_bignum_is_one - Is the given bignum one
+ * @a: Bignum
+ * Returns: 1 if @a is one or 0 if not
+ */
+int crypto_bignum_is_one(const struct crypto_bignum *a);
+
+/**
+ * crypto_bignum_is_odd - Is the given bignum odd
+ * @a: Bignum
+ * Returns: 1 if @a is odd or 0 if not
+ */
+int crypto_bignum_is_odd(const struct crypto_bignum *a);
+
+/**
+ * crypto_bignum_legendre - Compute the Legendre symbol (a/p)
+ * @a: Bignum
+ * @p: Bignum
+ * Returns: Legendre symbol -1,0,1 on success; -2 on calculation failure
+ */
+int crypto_bignum_legendre(const struct crypto_bignum *a,
+			   const struct crypto_bignum *p);
+
+/**
+ * struct crypto_ec - Elliptic curve context
+ *
+ * Internal data structure for EC implementation. The contents is specific
+ * to the used crypto library.
+ */
+struct crypto_ec;
+
+/**
+ * struct crypto_ec_point - Elliptic curve point
+ *
+ * Internal data structure for EC implementation to represent a point. The
+ * contents is specific to the used crypto library.
+ */
+struct crypto_ec_point;
+
+/**
+ * crypto_ec_init - Initialize elliptic curve context
+ * @group: Identifying number for the ECC group (IANA "Group Description"
+ *	attribute registrty for RFC 2409)
+ * Returns: Pointer to EC context or %NULL on failure
+ */
+struct crypto_ec * crypto_ec_init(int group);
+
+/**
+ * crypto_ec_deinit - Deinitialize elliptic curve context
+ * @e: EC context from crypto_ec_init()
+ */
+void crypto_ec_deinit(struct crypto_ec *e);
+
+/**
+ * crypto_ec_prime_len - Get length of the prime in octets
+ * @e: EC context from crypto_ec_init()
+ * Returns: Length of the prime defining the group
+ */
+size_t crypto_ec_prime_len(struct crypto_ec *e);
+
+/**
+ * crypto_ec_prime_len_bits - Get length of the prime in bits
+ * @e: EC context from crypto_ec_init()
+ * Returns: Length of the prime defining the group in bits
+ */
+size_t crypto_ec_prime_len_bits(struct crypto_ec *e);
+
+/**
+ * crypto_ec_order_len - Get length of the order in octets
+ * @e: EC context from crypto_ec_init()
+ * Returns: Length of the order defining the group
+ */
+size_t crypto_ec_order_len(struct crypto_ec *e);
+
+/**
+ * crypto_ec_get_prime - Get prime defining an EC group
+ * @e: EC context from crypto_ec_init()
+ * Returns: Prime (bignum) defining the group
+ */
+const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e);
+
+/**
+ * crypto_ec_get_order - Get order of an EC group
+ * @e: EC context from crypto_ec_init()
+ * Returns: Order (bignum) of the group
+ */
+const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e);
+
+/**
+ * crypto_ec_get_a - Get 'a' coefficient of an EC group's curve
+ * @e: EC context from crypto_ec_init()
+ * Returns: 'a' coefficient (bignum) of the group
+ */
+const struct crypto_bignum * crypto_ec_get_a(struct crypto_ec *e);
+
+/**
+ * crypto_ec_get_b - Get 'b' coeffiecient of an EC group's curve
+ * @e: EC context from crypto_ec_init()
+ * Returns: 'b' coefficient (bignum) of the group
+ */
+const struct crypto_bignum * crypto_ec_get_b(struct crypto_ec *e);
+
+/**
+ * crypto_ec_get_generator - Get generator point of the EC group's curve
+ * @e: EC context from crypto_ec_init()
+ * Returns: Pointer to generator point
+ */
+const struct crypto_ec_point * crypto_ec_get_generator(struct crypto_ec *e);
+
+/**
+ * crypto_ec_point_init - Initialize data for an EC point
+ * @e: EC context from crypto_ec_init()
+ * Returns: Pointer to EC point data or %NULL on failure
+ */
+struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e);
+
+/**
+ * crypto_ec_point_deinit - Deinitialize EC point data
+ * @p: EC point data from crypto_ec_point_init()
+ * @clear: Whether to clear the EC point value from memory
+ */
+void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear);
+
+/**
+ * crypto_ec_point_x - Copies the x-ordinate point into big number
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point data
+ * @x: Big number to set to the copy of x-ordinate
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
+		      struct crypto_bignum *x);
+
+/**
+ * crypto_ec_point_to_bin - Write EC point value as binary data
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point data from crypto_ec_point_init()
+ * @x: Buffer for writing the binary data for x coordinate or %NULL if not used
+ * @y: Buffer for writing the binary data for y coordinate or %NULL if not used
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to write an EC point as binary data in a format
+ * that has the x and y coordinates in big endian byte order fields padded to
+ * the length of the prime defining the group.
+ */
+int crypto_ec_point_to_bin(struct crypto_ec *e,
+			   const struct crypto_ec_point *point, u8 *x, u8 *y);
+
+/**
+ * crypto_ec_point_from_bin - Create EC point from binary data
+ * @e: EC context from crypto_ec_init()
+ * @val: Binary data to read the EC point from
+ * Returns: Pointer to EC point data or %NULL on failure
+ *
+ * This function readers x and y coordinates of the EC point from the provided
+ * buffer assuming the values are in big endian byte order with fields padded to
+ * the length of the prime defining the group.
+ */
+struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
+						  const u8 *val);
+
+/**
+ * crypto_ec_point_add - c = a + b
+ * @e: EC context from crypto_ec_init()
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a + b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
+			const struct crypto_ec_point *b,
+			struct crypto_ec_point *c);
+
+/**
+ * crypto_ec_point_mul - res = b * p
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point
+ * @b: Bignum
+ * @res: EC point; used to store the result of b * p
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
+			const struct crypto_bignum *b,
+			struct crypto_ec_point *res);
+
+/**
+ * crypto_ec_point_invert - Compute inverse of an EC point
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point to invert (and result of the operation)
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p);
+
+/**
+ * crypto_ec_point_compute_y_sqr - Compute y^2 = x^3 + ax + b
+ * @e: EC context from crypto_ec_init()
+ * @x: x coordinate
+ * Returns: y^2 on success, %NULL failure
+ */
+struct crypto_bignum *
+crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
+			      const struct crypto_bignum *x);
+
+/**
+ * crypto_ec_point_is_at_infinity - Check whether EC point is neutral element
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point
+ * Returns: 1 if the specified EC point is the neutral element of the group or
+ *	0 if not
+ */
+int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
+				   const struct crypto_ec_point *p);
+
+/**
+ * crypto_ec_point_is_on_curve - Check whether EC point is on curve
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point
+ * Returns: 1 if the specified EC point is on the curve or 0 if not
+ */
+int crypto_ec_point_is_on_curve(struct crypto_ec *e,
+				const struct crypto_ec_point *p);
+
+/**
+ * crypto_ec_point_cmp - Compare two EC points
+ * @e: EC context from crypto_ec_init()
+ * @a: EC point
+ * @b: EC point
+ * Returns: 0 on equal, non-zero otherwise
+ */
+int crypto_ec_point_cmp(const struct crypto_ec *e,
+			const struct crypto_ec_point *a,
+			const struct crypto_ec_point *b);
+
+/**
+ * crypto_ec_point_debug_print - Dump EC point to debug log
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point
+ * @title: Name of the EC point in the trace
+ */
+void crypto_ec_point_debug_print(const struct crypto_ec *e,
+				 const struct crypto_ec_point *p,
+				 const char *title);
+
+/**
+ * struct crypto_ec_key - Elliptic curve key pair
+ *
+ * Internal data structure for EC key pair. The contents is specific to the used
+ * crypto library.
+ */
+struct crypto_ec_key;
+
+/**
+ * struct crypto_ecdh - Elliptic Curve Diffie–Hellman context
+ *
+ * Internal data structure for ECDH. The contents is specific to the used
+ * crypto library.
+ */
+struct crypto_ecdh;
+
+/**
+ * crypto_ecdh_init - Initialize elliptic curve Diffie–Hellman context
+ * @group: Identifying number for the ECC group (IANA "Group Description"
+ *	attribute registry for RFC 2409)
+ * This function generates an ephemeral key pair.
+ * Returns: Pointer to ECDH context or %NULL on failure
+ */
+struct crypto_ecdh * crypto_ecdh_init(int group);
+
+/**
+ * crypto_ecdh_init2 - Initialize elliptic curve Diffie–Hellman context with a
+ * given EC key
+ * @group: Identifying number for the ECC group (IANA "Group Description"
+ *	attribute registry for RFC 2409)
+ * @own_key: Our own EC Key
+ * Returns: Pointer to ECDH context or %NULL on failure
+ */
+struct crypto_ecdh * crypto_ecdh_init2(int group,
+				       struct crypto_ec_key *own_key);
+
+/**
+ * crypto_ecdh_get_pubkey - Retrieve public key from ECDH context
+ * @ecdh: ECDH context from crypto_ecdh_init() or crypto_ecdh_init2()
+ * @inc_y: Whether public key should include y coordinate (explicit form)
+ * or not (compressed form)
+ * Returns: Binary data f the public key or %NULL on failure
+ */
+struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y);
+
+/**
+ * crypto_ecdh_set_peerkey - Compute ECDH secret
+ * @ecdh: ECDH context from crypto_ecdh_init() or crypto_ecdh_init2()
+ * @inc_y: Whether peer's public key includes y coordinate (explicit form)
+ * or not (compressed form)
+ * @key: Binary data of the peer's public key
+ * @len: Length of the @key buffer
+ * Returns: Binary data with the EDCH secret or %NULL on failure
+ */
+struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
+					const u8 *key, size_t len);
+
+/**
+ * crypto_ecdh_deinit - Free ECDH context
+ * @ecdh: ECDH context from crypto_ecdh_init() or crypto_ecdh_init2()
+ */
+void crypto_ecdh_deinit(struct crypto_ecdh *ecdh);
+
+/**
+ * crypto_ecdh_prime_len - Get length of the prime in octets
+ * @e: ECDH context from crypto_ecdh_init()
+ * Returns: Length of the prime defining the group
+ */
+size_t crypto_ecdh_prime_len(struct crypto_ecdh *ecdh);
+
+/**
+ * crypto_ec_key_parse_priv - Initialize EC key pair from ECPrivateKey ASN.1
+ * @der: DER encoding of ASN.1 ECPrivateKey
+ * @der_len: Length of @der buffer
+ * Returns: EC key or %NULL on failure
+ */
+struct crypto_ec_key * crypto_ec_key_parse_priv(const u8 *der, size_t der_len);
+
+/**
+ * crypto_ec_key_set_priv - Initialize EC key pair from raw key data
+ * @group: Identifying number for the ECC group
+ * @raw: Raw key data
+ * @raw_len: Length of @raw buffer
+ * Returns: EC key or %NULL on failure
+ */
+struct crypto_ec_key * crypto_ec_key_set_priv(int group,
+					      const u8 *raw, size_t raw_len);
+
+/**
+ * crypto_ec_key_parse_pub - Initialize EC key pair from SubjectPublicKeyInfo ASN.1
+ * @der: DER encoding of ASN.1 SubjectPublicKeyInfo
+ * @der_len: Length of @der buffer
+ * Returns: EC key or %NULL on failure
+ */
+struct crypto_ec_key * crypto_ec_key_parse_pub(const u8 *der, size_t der_len);
+
+/**
+ * crypto_ec_key_set_pub - Initialize an EC public key from EC point coordinates
+ * @group: Identifying number for the ECC group
+ * @x: X coordinate of the public key
+ * @y: Y coordinate of the public key
+ * @len: Length of @x and @y buffer
+ * Returns: EC key or %NULL on failure
+ *
+ * This function initialize an EC key from public key coordinates, in big endian
+ * byte order padded to the length of the prime defining the group.
+ */
+struct crypto_ec_key * crypto_ec_key_set_pub(int group, const u8 *x,
+					     const u8 *y, size_t len);
+
+/**
+ * crypto_ec_key_set_pub_point - Initialize an EC public key from EC point
+ * @e: EC context from crypto_ec_init()
+ * @pub: Public key point
+ * Returns: EC key or %NULL on failure
+ */
+struct crypto_ec_key *
+crypto_ec_key_set_pub_point(struct crypto_ec *e,
+			    const struct crypto_ec_point *pub);
+
+/**
+ * crypto_ec_key_gen - Generate EC key pair
+ * @group: Identifying number for the ECC group
+ * Returns: EC key or %NULL on failure
+ */
+struct crypto_ec_key * crypto_ec_key_gen(int group);
+
+/**
+ * crypto_ec_key_deinit - Free EC key
+ * @key: EC key from crypto_ec_key_parse_pub/priv() or crypto_ec_key_gen()
+ */
+void crypto_ec_key_deinit(struct crypto_ec_key *key);
+
+/**
+ * crypto_ec_key_get_subject_public_key - Get SubjectPublicKeyInfo ASN.1 for an EC key
+ * @key: EC key from crypto_ec_key_parse/set_pub/priv() or crypto_ec_key_gen()
+ * Returns: Buffer with DER encoding of ASN.1 SubjectPublicKeyInfo using
+ * compressed point format, or %NULL on failure
+ */
+struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key);
+
+/**
+ * crypto_ec_key_get_ecprivate_key - Get ECPrivateKey ASN.1 for a EC key
+ * @key: EC key from crypto_ec_key_parse_priv() or crypto_ec_key_gen()
+ * @include_pub: Whether to include public key in the ASN.1 sequence
+ * Returns: Buffer with DER encoding of ASN.1 ECPrivateKey or %NULL on failure
+ */
+struct wpabuf * crypto_ec_key_get_ecprivate_key(struct crypto_ec_key *key,
+						bool include_pub);
+
+/**
+ * crypto_ec_key_get_pubkey_point - Get public key point coordinates
+ * @key: EC key from crypto_ec_key_parse/set_pub() or crypto_ec_key_parse_priv()
+ * @prefix: Whether output buffer should include the octet to indicate
+ * coordinate form (as defined for SubjectPublicKeyInfo)
+ * Returns: Buffer with coordinates of public key in uncompressed form or %NULL
+ * on failure
+ */
+struct wpabuf * crypto_ec_key_get_pubkey_point(struct crypto_ec_key *key,
+					       int prefix);
+
+/**
+ * crypto_ec_key_get_public_key - Get EC public key as an EC point
+ * @key: EC key from crypto_ec_key_parse/set_pub() or crypto_ec_key_parse_priv()
+ * Returns: Public key as an EC point or %NULL on failure
+ *
+ * The caller needs to free the returned value with crypto_ec_point_deinit().
+ */
+struct crypto_ec_point *
+crypto_ec_key_get_public_key(struct crypto_ec_key *key);
+
+/**
+ * crypto_ec_key_get_private_key - Get EC private key as a bignum
+ * @key: EC key from crypto_ec_key_parse/set_pub() or crypto_ec_key_parse_priv()
+ * Returns: Private key as a bignum or %NULL on failure
+ *
+ * The caller needs to free the returned value with crypto_bignum_deinit().
+ */
+struct crypto_bignum *
+crypto_ec_key_get_private_key(struct crypto_ec_key *key);
+
+/**
+ * crypto_ec_key_sign - Sign a buffer with an EC key
+ * @key: EC key from crypto_ec_key_parse_priv() or crypto_ec_key_gen()
+ * @data: Data to sign
+ * @len: Length of @data buffer
+ * Returns: Buffer with DER encoding of ASN.1 Ecdsa-Sig-Value or %NULL on failure
+ */
+struct wpabuf * crypto_ec_key_sign(struct crypto_ec_key *key, const u8 *data,
+				   size_t len);
+
+/**
+ * crypto_ec_key_sign_r_s - Sign a buffer with an EC key
+ * @key: EC key from crypto_ec_key_parse_priv() or crypto_ec_key_gen()
+ * @data: Data to sign
+ * @len: Length of @data buffer
+ * Returns: Buffer with the concatenated r and s values. Each value is in big
+ * endian byte order padded to the length of the prime defining the group of
+ * the key.
+ */
+struct wpabuf * crypto_ec_key_sign_r_s(struct crypto_ec_key *key,
+				       const u8 *data, size_t len);
+
+/**
+ * crypto_ec_key_verify_signature - Verify ECDSA signature
+ * @key: EC key from crypto_ec_key_parse/set_pub() or crypto_ec_key_gen()
+ * @data: Data to be signed
+ * @len: Length of @data buffer
+ * @sig: DER encoding of ASN.1 Ecdsa-Sig-Value
+ * @sig_len: Length of @sig buffer
+ * Returns: 1 if signature is valid, 0 if signature is invalid and -1 on failure
+ */
+int crypto_ec_key_verify_signature(struct crypto_ec_key *key, const u8 *data,
+				   size_t len, const u8 *sig, size_t sig_len);
+
+/**
+ * crypto_ec_key_verify_signature_r_s - Verify signature
+ * @key: EC key from crypto_ec_key_parse/set_pub() or crypto_ec_key_gen()
+ * @data: Data to signed
+ * @len: Length of @data buffer
+ * @r: Binary data, in big endian byte order, of the 'r' field of the ECDSA
+ * signature.
+ * @s: Binary data, in big endian byte order, of the 's' field of the ECDSA
+ * signature.
+ * @r_len: Length of @r buffer
+ * @s_len: Length of @s buffer
+ * Returns: 1 if signature is valid, 0 if signature is invalid, or -1 on failure
+ */
+int crypto_ec_key_verify_signature_r_s(struct crypto_ec_key *key,
+				       const u8 *data, size_t len,
+				       const u8 *r, size_t r_len,
+				       const u8 *s, size_t s_len);
+
+/**
+ * crypto_ec_key_group - Get IANA group identifier for an EC key
+ * @key: EC key from crypto_ec_key_parse/set_pub/priv() or crypto_ec_key_gen()
+ * Returns: IANA group identifier and -1 on failure
+ */
+int crypto_ec_key_group(struct crypto_ec_key *key);
+
+/**
+ * crypto_ec_key_cmp - Compare two EC public keys
+ * @key1: Key 1
+ * @key2: Key 2
+ * Returns: 0 if public keys are identical, -1 otherwise
+ */
+int crypto_ec_key_cmp(struct crypto_ec_key *key1, struct crypto_ec_key *key2);
+
+/**
+ * crypto_ec_key_debug_print - Dump EC key to debug log
+ * @key:  EC key from crypto_ec_key_parse/set_pub/priv() or crypto_ec_key_gen()
+ * @title: Name of the EC point in the trace
+ */
+void crypto_ec_key_debug_print(const struct crypto_ec_key *key,
+			       const char *title);
+
+/**
+ * struct crypto_csr - Certification Signing Request
+ *
+ * Internal data structure for CSR. The contents is specific to the used
+ * crypto library.
+ * For now it is assumed that only an EC public key can be used
+ */
+struct crypto_csr;
+
+/**
+ * enum crypto_csr_name - CSR name type
+ */
+enum crypto_csr_name {
+	CSR_NAME_CN,
+	CSR_NAME_SN,
+	CSR_NAME_C,
+	CSR_NAME_O,
+	CSR_NAME_OU,
+};
+
+/**
+ * enum crypto_csr_attr - CSR attribute
+ */
+enum crypto_csr_attr {
+	CSR_ATTR_CHALLENGE_PASSWORD,
+};
+
+/**
+ * crypto_csr_init - Initialize empty CSR
+ * Returns: Pointer to CSR data or %NULL on failure
+ */
+struct crypto_csr * crypto_csr_init(void);
+
+/**
+ * crypto_csr_verify - Initialize CSR from CertificationRequest
+ * @req: DER encoding of ASN.1 CertificationRequest
+ *
+ * Returns: Pointer to CSR data or %NULL on failure or if signature is invalid
+ */
+struct crypto_csr * crypto_csr_verify(const struct wpabuf *req);
+
+/**
+ * crypto_csr_deinit - Free CSR structure
+ * @csr: CSR structure from @crypto_csr_init() or crypto_csr_verify()
+ */
+void crypto_csr_deinit(struct crypto_csr *csr);
+
+/**
+ * crypto_csr_set_ec_public_key - Set public key in CSR
+ * @csr: CSR structure from @crypto_csr_init()
+ * @key: EC public key to set as public key in the CSR
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_csr_set_ec_public_key(struct crypto_csr *csr,
+				 struct crypto_ec_key *key);
+
+/**
+ * crypto_csr_set_name - Set name entry in CSR SubjectName
+ * @csr: CSR structure from @crypto_csr_init()
+ * @type: Name type  to add into the CSR SubjectName
+ * @name: UTF-8 string to write in the CSR SubjectName
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_csr_set_name(struct crypto_csr *csr, enum crypto_csr_name type,
+			const char *name);
+
+/**
+ * crypto_csr_set_attribute - Set attribute in CSR
+ * @csr: CSR structure from @crypto_csr_init()
+ * @attr: Attribute identifier
+ * @attr_type: ASN.1 type of @value buffer
+ * @value: Attribute value
+ * @len: length of @value buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_csr_set_attribute(struct crypto_csr *csr, enum crypto_csr_attr attr,
+			     int attr_type, const u8 *value, size_t len);
+
+/**
+ * crypto_csr_get_attribute - Get attribute from CSR
+ * @csr: CSR structure from @crypto_csr_verify()
+ * @attr: Updated with atribute identifier
+ * @len: Updated with length of returned buffer
+ * @type: ASN.1 type of the attribute buffer
+ * Returns: Type, length, and pointer on attribute value or %NULL on failure
+ */
+const u8 * crypto_csr_get_attribute(struct crypto_csr *csr,
+				    enum crypto_csr_attr attr,
+				    size_t *len, int *type);
+
+/**
+ * crypto_csr_sign - Sign CSR and return ASN.1 CertificationRequest
+ * @csr: CSR structure from @crypto_csr_init()
+ * @key: Private key to sign the CSR (for now ony EC key are supported)
+ * @algo: Hash algorithm to use for the signature
+ * Returns: DER encoding of ASN.1 CertificationRequest for the CSR or %NULL on
+ * failure
+ */
+struct wpabuf * crypto_csr_sign(struct crypto_csr *csr,
+				struct crypto_ec_key *key,
+				enum crypto_hash_alg algo);
+
+struct crypto_rsa_key;
+
+/**
+ * crypto_rsa_key_read - Read an RSA key
+ * @file: File from which to read (PEM encoded, can be X.509v3 certificate)
+ * @private_key: Whether to read the private key instead of public key
+ * Returns: RSA key or %NULL on failure
+ */
+struct crypto_rsa_key * crypto_rsa_key_read(const char *file, bool private_key);
+
+/**
+ * crypto_rsa_oaep_sha256_encrypt - RSA-OAEP-SHA-256 encryption
+ * @key: RSA key from crypto_rsa_key_read()
+ * @in: Plaintext input data
+ * Returns: Encrypted output data or %NULL on failure
+ */
+struct wpabuf * crypto_rsa_oaep_sha256_encrypt(struct crypto_rsa_key *key,
+					       const struct wpabuf *in);
+
+/**
+ * crypto_rsa_oaep_sha256_decrypt - RSA-OAEP-SHA-256 decryption
+ * @key: RSA key from crypto_rsa_key_read()
+ * @in: Encrypted input data
+ * Returns: Decrypted output data or %NULL on failure
+ */
+struct wpabuf * crypto_rsa_oaep_sha256_decrypt(struct crypto_rsa_key *key,
+					       const struct wpabuf *in);
+
+/**
+ * crypto_rsa_key_free - Free an RSA key
+ * @key: RSA key from crypto_rsa_key_read()
+ */
+void crypto_rsa_key_free(struct crypto_rsa_key *key);
+
+enum hpke_mode {
+	HPKE_MODE_BASE = 0x00,
+	HPKE_MODE_PSK = 0x01,
+	HPKE_MODE_AUTH = 0x02,
+	HPKE_MODE_AUTH_PSK = 0x03,
+};
+
+enum hpke_kem_id {
+	HPKE_DHKEM_P256_HKDF_SHA256 = 0x0010,
+	HPKE_DHKEM_P384_HKDF_SHA384 = 0x0011,
+	HPKE_DHKEM_P521_HKDF_SHA512 = 0x0012,
+	HPKE_DHKEM_X5519_HKDF_SHA256 = 0x0020,
+	HPKE_DHKEM_X448_HKDF_SHA512 = 0x0021,
+};
+
+enum hpke_kdf_id {
+	HPKE_KDF_HKDF_SHA256 = 0x0001,
+	HPKE_KDF_HKDF_SHA384 = 0x0002,
+	HPKE_KDF_HKDF_SHA512 = 0x0003,
+};
+
+enum hpke_aead_id {
+	HPKE_AEAD_AES_128_GCM = 0x0001,
+	HPKE_AEAD_AES_256_GCM = 0x0002,
+	HPKE_AEAD_CHACHA20POLY1305 = 0x0003,
+};
+
+/**
+ * hpke_base_seal - HPKE base mode single-shot encrypt
+ * Returns: enc | ct; or %NULL on failure
+ */
+struct wpabuf * hpke_base_seal(enum hpke_kem_id kem_id,
+			       enum hpke_kdf_id kdf_id,
+			       enum hpke_aead_id aead_id,
+			       struct crypto_ec_key *peer_pub,
+			       const u8 *info, size_t info_len,
+			       const u8 *aad, size_t aad_len,
+			       const u8 *pt, size_t pt_len);
+
+/**
+ * hpke_base_open - HPKE base mode single-shot decrypt
+ * @enc_ct: enc | ct
+ * Returns: pt; or %NULL on failure
+ */
+struct wpabuf * hpke_base_open(enum hpke_kem_id kem_id,
+			       enum hpke_kdf_id kdf_id,
+			       enum hpke_aead_id aead_id,
+			       struct crypto_ec_key *own_priv,
+			       const u8 *info, size_t info_len,
+			       const u8 *aad, size_t aad_len,
+			       const u8 *enc_ct, size_t enc_ct_len);
+
+/**
+ * crypto_unload - Unload crypto resources
+ *
+ * This function is called just before the process exits to allow dynamic
+ * resource allocations to be freed.
+ */
+void crypto_unload(void);
+
+#endif /* CRYPTO_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_gnutls.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_gnutls.c
new file mode 100644
index 0000000..a7a163f
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_gnutls.c
@@ -0,0 +1,511 @@
+/*
+ * WPA Supplicant / wrapper functions for libgcrypt
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <gcrypt.h>
+
+#include "common.h"
+#include "md5.h"
+#include "sha1.h"
+#include "sha256.h"
+#include "sha384.h"
+#include "sha512.h"
+#include "crypto.h"
+
+static int gnutls_digest_vector(int algo, size_t num_elem,
+				const u8 *addr[], const size_t *len, u8 *mac)
+{
+	gcry_md_hd_t hd;
+	unsigned char *p;
+	size_t i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	if (gcry_md_open(&hd, algo, 0) != GPG_ERR_NO_ERROR)
+		return -1;
+	for (i = 0; i < num_elem; i++)
+		gcry_md_write(hd, addr[i], len[i]);
+	p = gcry_md_read(hd, algo);
+	if (p)
+		memcpy(mac, p, gcry_md_get_algo_dlen(algo));
+	gcry_md_close(hd);
+	return 0;
+}
+
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return gnutls_digest_vector(GCRY_MD_MD4, num_elem, addr, len, mac);
+}
+
+
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+	gcry_cipher_hd_t hd;
+	u8 pkey[8], next, tmp;
+	int i;
+
+	/* Add parity bits to the key */
+	next = 0;
+	for (i = 0; i < 7; i++) {
+		tmp = key[i];
+		pkey[i] = (tmp >> i) | next | 1;
+		next = tmp << (7 - i);
+	}
+	pkey[i] = next | 1;
+
+	gcry_cipher_open(&hd, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0);
+	gcry_err_code(gcry_cipher_setkey(hd, pkey, 8));
+	gcry_cipher_encrypt(hd, cypher, 8, clear, 8);
+	gcry_cipher_close(hd);
+	return 0;
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return gnutls_digest_vector(GCRY_MD_MD5, num_elem, addr, len, mac);
+}
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return gnutls_digest_vector(GCRY_MD_SHA1, num_elem, addr, len, mac);
+}
+
+
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return gnutls_digest_vector(GCRY_MD_SHA256, num_elem, addr, len, mac);
+}
+
+
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return gnutls_digest_vector(GCRY_MD_SHA384, num_elem, addr, len, mac);
+}
+
+
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return gnutls_digest_vector(GCRY_MD_SHA512, num_elem, addr, len, mac);
+}
+
+
+static int gnutls_hmac_vector(int algo, const u8 *key, size_t key_len,
+			      size_t num_elem, const u8 *addr[],
+			      const size_t *len, u8 *mac)
+{
+	gcry_md_hd_t hd;
+	unsigned char *p;
+	size_t i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	if (gcry_md_open(&hd, algo, GCRY_MD_FLAG_HMAC) != GPG_ERR_NO_ERROR)
+		return -1;
+	if (gcry_md_setkey(hd, key, key_len) != GPG_ERR_NO_ERROR) {
+		gcry_md_close(hd);
+		return -1;
+	}
+	for (i = 0; i < num_elem; i++)
+		gcry_md_write(hd, addr[i], len[i]);
+	p = gcry_md_read(hd, algo);
+	if (p)
+		memcpy(mac, p, gcry_md_get_algo_dlen(algo));
+	gcry_md_close(hd);
+	return 0;
+}
+
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+		    const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return gnutls_hmac_vector(GCRY_MD_MD5, key, key_len, num_elem, addr,
+				  len, mac);
+}
+
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	     u8 *mac)
+{
+	return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return gnutls_hmac_vector(GCRY_MD_SHA1, key, key_len, num_elem, addr,
+				  len, mac);
+}
+
+
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	       u8 *mac)
+{
+	return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+#ifdef CONFIG_SHA256
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return gnutls_hmac_vector(GCRY_MD_SHA256, key, key_len, num_elem, addr,
+				  len, mac);
+}
+
+
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA256 */
+
+
+#ifdef CONFIG_SHA384
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return gnutls_hmac_vector(GCRY_MD_SHA384, key, key_len, num_elem, addr,
+				  len, mac);
+}
+
+
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA384 */
+
+
+#ifdef CONFIG_SHA512
+
+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return gnutls_hmac_vector(GCRY_MD_SHA512, key, key_len, num_elem, addr,
+				  len, mac);
+}
+
+
+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA512 */
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+	gcry_cipher_hd_t hd;
+
+	if (TEST_FAIL())
+		return NULL;
+
+	if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) !=
+	    GPG_ERR_NO_ERROR) {
+		printf("cipher open failed\n");
+		return NULL;
+	}
+	if (gcry_cipher_setkey(hd, key, len) != GPG_ERR_NO_ERROR) {
+		printf("setkey failed\n");
+		gcry_cipher_close(hd);
+		return NULL;
+	}
+
+	return hd;
+}
+
+
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+	gcry_cipher_hd_t hd = ctx;
+	gcry_cipher_encrypt(hd, crypt, 16, plain, 16);
+	return 0;
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+	gcry_cipher_hd_t hd = ctx;
+	gcry_cipher_close(hd);
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+	gcry_cipher_hd_t hd;
+
+	if (TEST_FAIL())
+		return NULL;
+
+	if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) !=
+	    GPG_ERR_NO_ERROR)
+		return NULL;
+	if (gcry_cipher_setkey(hd, key, len) != GPG_ERR_NO_ERROR) {
+		gcry_cipher_close(hd);
+		return NULL;
+	}
+
+	return hd;
+}
+
+
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+	gcry_cipher_hd_t hd = ctx;
+	gcry_cipher_decrypt(hd, plain, 16, crypt, 16);
+	return 0;
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+	gcry_cipher_hd_t hd = ctx;
+	gcry_cipher_close(hd);
+}
+
+
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+		   u8 *pubkey)
+{
+	size_t pubkey_len, pad;
+
+	if (os_get_random(privkey, prime_len) < 0)
+		return -1;
+	if (os_memcmp(privkey, prime, prime_len) > 0) {
+		/* Make sure private value is smaller than prime */
+		privkey[0] = 0;
+	}
+
+	pubkey_len = prime_len;
+	if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
+			   pubkey, &pubkey_len) < 0)
+		return -1;
+	if (pubkey_len < prime_len) {
+		pad = prime_len - pubkey_len;
+		os_memmove(pubkey + pad, pubkey, pubkey_len);
+		os_memset(pubkey, 0, pad);
+	}
+
+	return 0;
+}
+
+
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+			    const u8 *order, size_t order_len,
+			    const u8 *privkey, size_t privkey_len,
+			    const u8 *pubkey, size_t pubkey_len,
+			    u8 *secret, size_t *len)
+{
+	gcry_mpi_t pub = NULL;
+	int res = -1;
+
+	if (pubkey_len > prime_len ||
+	    (pubkey_len == prime_len &&
+	     os_memcmp(pubkey, prime, prime_len) >= 0))
+		return -1;
+
+	if (gcry_mpi_scan(&pub, GCRYMPI_FMT_USG, pubkey, pubkey_len, NULL) !=
+	    GPG_ERR_NO_ERROR ||
+	    gcry_mpi_cmp_ui(pub, 1) <= 0)
+		goto fail;
+
+	if (order) {
+		gcry_mpi_t p = NULL, q = NULL, tmp;
+		int failed;
+
+		/* verify: pubkey^q == 1 mod p */
+		tmp = gcry_mpi_new(prime_len * 8);
+		failed = !tmp ||
+			gcry_mpi_scan(&p, GCRYMPI_FMT_USG, prime, prime_len,
+				      NULL) != GPG_ERR_NO_ERROR ||
+			gcry_mpi_scan(&q, GCRYMPI_FMT_USG, order, order_len,
+				      NULL) != GPG_ERR_NO_ERROR;
+		if (!failed) {
+			gcry_mpi_powm(tmp, pub, q, p);
+			failed = gcry_mpi_cmp_ui(tmp, 1) != 0;
+		}
+		gcry_mpi_release(p);
+		gcry_mpi_release(q);
+		gcry_mpi_release(tmp);
+		if (failed)
+			goto fail;
+	}
+
+	res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+			     prime, prime_len, secret, len);
+fail:
+	gcry_mpi_release(pub);
+	return res;
+}
+
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+		   const u8 *power, size_t power_len,
+		   const u8 *modulus, size_t modulus_len,
+		   u8 *result, size_t *result_len)
+{
+	gcry_mpi_t bn_base = NULL, bn_exp = NULL, bn_modulus = NULL,
+		bn_result = NULL;
+	int ret = -1;
+
+	if (gcry_mpi_scan(&bn_base, GCRYMPI_FMT_USG, base, base_len, NULL) !=
+	    GPG_ERR_NO_ERROR ||
+	    gcry_mpi_scan(&bn_exp, GCRYMPI_FMT_USG, power, power_len, NULL) !=
+	    GPG_ERR_NO_ERROR ||
+	    gcry_mpi_scan(&bn_modulus, GCRYMPI_FMT_USG, modulus, modulus_len,
+			  NULL) != GPG_ERR_NO_ERROR)
+		goto error;
+	bn_result = gcry_mpi_new(modulus_len * 8);
+
+	gcry_mpi_powm(bn_result, bn_base, bn_exp, bn_modulus);
+
+	if (gcry_mpi_print(GCRYMPI_FMT_USG, result, *result_len, result_len,
+			   bn_result) != GPG_ERR_NO_ERROR)
+		goto error;
+
+	ret = 0;
+
+error:
+	gcry_mpi_release(bn_base);
+	gcry_mpi_release(bn_exp);
+	gcry_mpi_release(bn_modulus);
+	gcry_mpi_release(bn_result);
+	return ret;
+}
+
+
+struct crypto_cipher {
+	gcry_cipher_hd_t enc;
+	gcry_cipher_hd_t dec;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+					  const u8 *iv, const u8 *key,
+					  size_t key_len)
+{
+	struct crypto_cipher *ctx;
+	gcry_error_t res;
+	enum gcry_cipher_algos a;
+	int ivlen;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+
+	switch (alg) {
+	case CRYPTO_CIPHER_ALG_RC4:
+		a = GCRY_CIPHER_ARCFOUR;
+		res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_STREAM,
+				       0);
+		gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_STREAM, 0);
+		break;
+	case CRYPTO_CIPHER_ALG_AES:
+		if (key_len == 24)
+			a = GCRY_CIPHER_AES192;
+		else if (key_len == 32)
+			a = GCRY_CIPHER_AES256;
+		else
+			a = GCRY_CIPHER_AES;
+		res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0);
+		gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0);
+		break;
+	case CRYPTO_CIPHER_ALG_3DES:
+		a = GCRY_CIPHER_3DES;
+		res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0);
+		gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0);
+		break;
+	case CRYPTO_CIPHER_ALG_DES:
+		a = GCRY_CIPHER_DES;
+		res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0);
+		gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0);
+		break;
+	case CRYPTO_CIPHER_ALG_RC2:
+		if (key_len == 5)
+			a = GCRY_CIPHER_RFC2268_40;
+		else
+			a = GCRY_CIPHER_RFC2268_128;
+		res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0);
+		gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0);
+		break;
+	default:
+		os_free(ctx);
+		return NULL;
+	}
+
+	if (res != GPG_ERR_NO_ERROR) {
+		os_free(ctx);
+		return NULL;
+	}
+
+	if (gcry_cipher_setkey(ctx->enc, key, key_len) != GPG_ERR_NO_ERROR ||
+	    gcry_cipher_setkey(ctx->dec, key, key_len) != GPG_ERR_NO_ERROR) {
+		gcry_cipher_close(ctx->enc);
+		gcry_cipher_close(ctx->dec);
+		os_free(ctx);
+		return NULL;
+	}
+
+	ivlen = gcry_cipher_get_algo_blklen(a);
+	if (gcry_cipher_setiv(ctx->enc, iv, ivlen) != GPG_ERR_NO_ERROR ||
+	    gcry_cipher_setiv(ctx->dec, iv, ivlen) != GPG_ERR_NO_ERROR) {
+		gcry_cipher_close(ctx->enc);
+		gcry_cipher_close(ctx->dec);
+		os_free(ctx);
+		return NULL;
+	}
+
+	return ctx;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+			  u8 *crypt, size_t len)
+{
+	if (gcry_cipher_encrypt(ctx->enc, crypt, len, plain, len) !=
+	    GPG_ERR_NO_ERROR)
+		return -1;
+	return 0;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+			  u8 *plain, size_t len)
+{
+	if (gcry_cipher_decrypt(ctx->dec, plain, len, crypt, len) !=
+	    GPG_ERR_NO_ERROR)
+		return -1;
+	return 0;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+	gcry_cipher_close(ctx->enc);
+	gcry_cipher_close(ctx->dec);
+	os_free(ctx);
+}
+
+
+void crypto_unload(void)
+{
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_internal-cipher.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_internal-cipher.c
new file mode 100644
index 0000000..ad0930a
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_internal-cipher.c
@@ -0,0 +1,243 @@
+/*
+ * Crypto wrapper for internal crypto implementation - Cipher wrappers
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+#include "aes.h"
+#include "des_i.h"
+
+
+struct crypto_cipher {
+	enum crypto_cipher_alg alg;
+	union {
+		struct {
+			size_t used_bytes;
+			u8 key[16];
+			size_t keylen;
+		} rc4;
+		struct {
+			u8 cbc[32];
+			void *ctx_enc;
+			void *ctx_dec;
+		} aes;
+		struct {
+			struct des3_key_s key;
+			u8 cbc[8];
+		} des3;
+		struct {
+			u32 ek[32];
+			u32 dk[32];
+			u8 cbc[8];
+		} des;
+	} u;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+					  const u8 *iv, const u8 *key,
+					  size_t key_len)
+{
+	struct crypto_cipher *ctx;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+
+	ctx->alg = alg;
+
+	switch (alg) {
+	case CRYPTO_CIPHER_ALG_RC4:
+		if (key_len > sizeof(ctx->u.rc4.key)) {
+			os_free(ctx);
+			return NULL;
+		}
+		ctx->u.rc4.keylen = key_len;
+		os_memcpy(ctx->u.rc4.key, key, key_len);
+		break;
+	case CRYPTO_CIPHER_ALG_AES:
+		ctx->u.aes.ctx_enc = aes_encrypt_init(key, key_len);
+		if (ctx->u.aes.ctx_enc == NULL) {
+			os_free(ctx);
+			return NULL;
+		}
+		ctx->u.aes.ctx_dec = aes_decrypt_init(key, key_len);
+		if (ctx->u.aes.ctx_dec == NULL) {
+			aes_encrypt_deinit(ctx->u.aes.ctx_enc);
+			os_free(ctx);
+			return NULL;
+		}
+		os_memcpy(ctx->u.aes.cbc, iv, AES_BLOCK_SIZE);
+		break;
+	case CRYPTO_CIPHER_ALG_3DES:
+		if (key_len != 24) {
+			os_free(ctx);
+			return NULL;
+		}
+		des3_key_setup(key, &ctx->u.des3.key);
+		os_memcpy(ctx->u.des3.cbc, iv, 8);
+		break;
+	case CRYPTO_CIPHER_ALG_DES:
+		if (key_len != 8) {
+			os_free(ctx);
+			return NULL;
+		}
+		des_key_setup(key, ctx->u.des.ek, ctx->u.des.dk);
+		os_memcpy(ctx->u.des.cbc, iv, 8);
+		break;
+	default:
+		os_free(ctx);
+		return NULL;
+	}
+
+	return ctx;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+			  u8 *crypt, size_t len)
+{
+	size_t i, j, blocks;
+
+	switch (ctx->alg) {
+	case CRYPTO_CIPHER_ALG_RC4:
+		if (plain != crypt)
+			os_memcpy(crypt, plain, len);
+		rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen,
+			 ctx->u.rc4.used_bytes, crypt, len);
+		ctx->u.rc4.used_bytes += len;
+		break;
+	case CRYPTO_CIPHER_ALG_AES:
+		if (len % AES_BLOCK_SIZE)
+			return -1;
+		blocks = len / AES_BLOCK_SIZE;
+		for (i = 0; i < blocks; i++) {
+			for (j = 0; j < AES_BLOCK_SIZE; j++)
+				ctx->u.aes.cbc[j] ^= plain[j];
+			aes_encrypt(ctx->u.aes.ctx_enc, ctx->u.aes.cbc,
+				    ctx->u.aes.cbc);
+			os_memcpy(crypt, ctx->u.aes.cbc, AES_BLOCK_SIZE);
+			plain += AES_BLOCK_SIZE;
+			crypt += AES_BLOCK_SIZE;
+		}
+		break;
+	case CRYPTO_CIPHER_ALG_3DES:
+		if (len % 8)
+			return -1;
+		blocks = len / 8;
+		for (i = 0; i < blocks; i++) {
+			for (j = 0; j < 8; j++)
+				ctx->u.des3.cbc[j] ^= plain[j];
+			des3_encrypt(ctx->u.des3.cbc, &ctx->u.des3.key,
+				     ctx->u.des3.cbc);
+			os_memcpy(crypt, ctx->u.des3.cbc, 8);
+			plain += 8;
+			crypt += 8;
+		}
+		break;
+	case CRYPTO_CIPHER_ALG_DES:
+		if (len % 8)
+			return -1;
+		blocks = len / 8;
+		for (i = 0; i < blocks; i++) {
+			for (j = 0; j < 8; j++)
+				ctx->u.des3.cbc[j] ^= plain[j];
+			des_block_encrypt(ctx->u.des.cbc, ctx->u.des.ek,
+					  ctx->u.des.cbc);
+			os_memcpy(crypt, ctx->u.des.cbc, 8);
+			plain += 8;
+			crypt += 8;
+		}
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+			  u8 *plain, size_t len)
+{
+	size_t i, j, blocks;
+	u8 tmp[32];
+
+	switch (ctx->alg) {
+	case CRYPTO_CIPHER_ALG_RC4:
+		if (plain != crypt)
+			os_memcpy(plain, crypt, len);
+		rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen,
+			 ctx->u.rc4.used_bytes, plain, len);
+		ctx->u.rc4.used_bytes += len;
+		break;
+	case CRYPTO_CIPHER_ALG_AES:
+		if (len % AES_BLOCK_SIZE)
+			return -1;
+		blocks = len / AES_BLOCK_SIZE;
+		for (i = 0; i < blocks; i++) {
+			os_memcpy(tmp, crypt, AES_BLOCK_SIZE);
+			aes_decrypt(ctx->u.aes.ctx_dec, crypt, plain);
+			for (j = 0; j < AES_BLOCK_SIZE; j++)
+				plain[j] ^= ctx->u.aes.cbc[j];
+			os_memcpy(ctx->u.aes.cbc, tmp, AES_BLOCK_SIZE);
+			plain += AES_BLOCK_SIZE;
+			crypt += AES_BLOCK_SIZE;
+		}
+		break;
+	case CRYPTO_CIPHER_ALG_3DES:
+		if (len % 8)
+			return -1;
+		blocks = len / 8;
+		for (i = 0; i < blocks; i++) {
+			os_memcpy(tmp, crypt, 8);
+			des3_decrypt(crypt, &ctx->u.des3.key, plain);
+			for (j = 0; j < 8; j++)
+				plain[j] ^= ctx->u.des3.cbc[j];
+			os_memcpy(ctx->u.des3.cbc, tmp, 8);
+			plain += 8;
+			crypt += 8;
+		}
+		break;
+	case CRYPTO_CIPHER_ALG_DES:
+		if (len % 8)
+			return -1;
+		blocks = len / 8;
+		for (i = 0; i < blocks; i++) {
+			os_memcpy(tmp, crypt, 8);
+			des_block_decrypt(crypt, ctx->u.des.dk, plain);
+			for (j = 0; j < 8; j++)
+				plain[j] ^= ctx->u.des.cbc[j];
+			os_memcpy(ctx->u.des.cbc, tmp, 8);
+			plain += 8;
+			crypt += 8;
+		}
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+	switch (ctx->alg) {
+	case CRYPTO_CIPHER_ALG_AES:
+		aes_encrypt_deinit(ctx->u.aes.ctx_enc);
+		aes_decrypt_deinit(ctx->u.aes.ctx_dec);
+		break;
+	case CRYPTO_CIPHER_ALG_3DES:
+		break;
+	default:
+		break;
+	}
+	os_free(ctx);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_internal-modexp.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_internal-modexp.c
new file mode 100644
index 0000000..6819f1a
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_internal-modexp.c
@@ -0,0 +1,122 @@
+/*
+ * Crypto wrapper for internal crypto implementation - modexp
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "tls/bignum.h"
+#include "crypto.h"
+
+
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+		   u8 *pubkey)
+{
+	size_t pubkey_len, pad;
+
+	if (os_get_random(privkey, prime_len) < 0)
+		return -1;
+	if (os_memcmp(privkey, prime, prime_len) > 0) {
+		/* Make sure private value is smaller than prime */
+		privkey[0] = 0;
+	}
+
+	pubkey_len = prime_len;
+	if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
+			   pubkey, &pubkey_len) < 0)
+		return -1;
+	if (pubkey_len < prime_len) {
+		pad = prime_len - pubkey_len;
+		os_memmove(pubkey + pad, pubkey, pubkey_len);
+		os_memset(pubkey, 0, pad);
+	}
+
+	return 0;
+}
+
+
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+			    const u8 *order, size_t order_len,
+			    const u8 *privkey, size_t privkey_len,
+			    const u8 *pubkey, size_t pubkey_len,
+			    u8 *secret, size_t *len)
+{
+	struct bignum *pub;
+	int res = -1;
+
+	if (pubkey_len > prime_len ||
+	    (pubkey_len == prime_len &&
+	     os_memcmp(pubkey, prime, prime_len) >= 0))
+		return -1;
+
+	pub = bignum_init();
+	if (!pub || bignum_set_unsigned_bin(pub, pubkey, pubkey_len) < 0 ||
+	    bignum_cmp_d(pub, 1) <= 0)
+		goto fail;
+
+	if (order) {
+		struct bignum *p, *q, *tmp;
+		int failed;
+
+		/* verify: pubkey^q == 1 mod p */
+		p = bignum_init();
+		q = bignum_init();
+		tmp = bignum_init();
+		failed = !p || !q || !tmp ||
+			bignum_set_unsigned_bin(p, prime, prime_len) < 0 ||
+			bignum_set_unsigned_bin(q, order, order_len) < 0 ||
+			bignum_exptmod(pub, q, p, tmp) < 0 ||
+			bignum_cmp_d(tmp, 1) != 0;
+		bignum_deinit(p);
+		bignum_deinit(q);
+		bignum_deinit(tmp);
+		if (failed)
+			goto fail;
+	}
+
+	res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+			     prime, prime_len, secret, len);
+fail:
+	bignum_deinit(pub);
+	return res;
+}
+
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+		   const u8 *power, size_t power_len,
+		   const u8 *modulus, size_t modulus_len,
+		   u8 *result, size_t *result_len)
+{
+	struct bignum *bn_base, *bn_exp, *bn_modulus, *bn_result;
+	int ret = -1;
+
+	bn_base = bignum_init();
+	bn_exp = bignum_init();
+	bn_modulus = bignum_init();
+	bn_result = bignum_init();
+
+	if (bn_base == NULL || bn_exp == NULL || bn_modulus == NULL ||
+	    bn_result == NULL)
+		goto error;
+
+	if (bignum_set_unsigned_bin(bn_base, base, base_len) < 0 ||
+	    bignum_set_unsigned_bin(bn_exp, power, power_len) < 0 ||
+	    bignum_set_unsigned_bin(bn_modulus, modulus, modulus_len) < 0)
+		goto error;
+
+	if (bignum_exptmod(bn_base, bn_exp, bn_modulus, bn_result) < 0)
+		goto error;
+
+	ret = bignum_get_unsigned_bin(bn_result, result, result_len);
+
+error:
+	bignum_deinit(bn_base);
+	bignum_deinit(bn_exp);
+	bignum_deinit(bn_modulus);
+	bignum_deinit(bn_result);
+	return ret;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_internal-rsa.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_internal-rsa.c
new file mode 100644
index 0000000..0c5cead
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_internal-rsa.c
@@ -0,0 +1,117 @@
+/*
+ * Crypto wrapper for internal crypto implementation - RSA parts
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+#include "tls/rsa.h"
+#include "tls/pkcs1.h"
+#include "tls/pkcs8.h"
+
+/* Stub structures; these are just typecast to struct crypto_rsa_key */
+struct crypto_public_key;
+struct crypto_private_key;
+
+
+struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len)
+{
+	return (struct crypto_public_key *)
+		crypto_rsa_import_public_key(key, len);
+}
+
+
+struct crypto_public_key *
+crypto_public_key_import_parts(const u8 *n, size_t n_len,
+			       const u8 *e, size_t e_len)
+{
+	return (struct crypto_public_key *)
+		crypto_rsa_import_public_key_parts(n, n_len, e, e_len);
+}
+
+
+struct crypto_private_key * crypto_private_key_import(const u8 *key,
+						      size_t len,
+						      const char *passwd)
+{
+	struct crypto_private_key *res;
+
+	/* First, check for possible PKCS #8 encoding */
+	res = pkcs8_key_import(key, len);
+	if (res)
+		return res;
+
+	if (passwd) {
+		/* Try to parse as encrypted PKCS #8 */
+		res = pkcs8_enc_key_import(key, len, passwd);
+		if (res)
+			return res;
+	}
+
+	/* Not PKCS#8, so try to import PKCS #1 encoded RSA private key */
+	wpa_printf(MSG_DEBUG, "Trying to parse PKCS #1 encoded RSA private "
+		   "key");
+	return (struct crypto_private_key *)
+		crypto_rsa_import_private_key(key, len);
+}
+
+
+struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf,
+						       size_t len)
+{
+	/* No X.509 support in crypto_internal.c */
+	return NULL;
+}
+
+
+int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key,
+					const u8 *in, size_t inlen,
+					u8 *out, size_t *outlen)
+{
+	return pkcs1_encrypt(2, (struct crypto_rsa_key *) key,
+			     0, in, inlen, out, outlen);
+}
+
+
+int crypto_private_key_decrypt_pkcs1_v15(struct crypto_private_key *key,
+					 const u8 *in, size_t inlen,
+					 u8 *out, size_t *outlen)
+{
+	return pkcs1_v15_private_key_decrypt((struct crypto_rsa_key *) key,
+					     in, inlen, out, outlen);
+}
+
+
+int crypto_private_key_sign_pkcs1(struct crypto_private_key *key,
+				  const u8 *in, size_t inlen,
+				  u8 *out, size_t *outlen)
+{
+	return pkcs1_encrypt(1, (struct crypto_rsa_key *) key,
+			     1, in, inlen, out, outlen);
+}
+
+
+void crypto_public_key_free(struct crypto_public_key *key)
+{
+	crypto_rsa_free((struct crypto_rsa_key *) key);
+}
+
+
+void crypto_private_key_free(struct crypto_private_key *key)
+{
+	crypto_rsa_free((struct crypto_rsa_key *) key);
+}
+
+
+int crypto_public_key_decrypt_pkcs1(struct crypto_public_key *key,
+				    const u8 *crypt, size_t crypt_len,
+				    u8 *plain, size_t *plain_len)
+{
+	return pkcs1_decrypt_public_key((struct crypto_rsa_key *) key,
+					crypt, crypt_len, plain, plain_len);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_internal.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_internal.c
new file mode 100644
index 0000000..d15c363
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_internal.c
@@ -0,0 +1,333 @@
+/*
+ * Crypto wrapper for internal crypto implementation
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+#include "sha256_i.h"
+#include "sha384_i.h"
+#include "sha512_i.h"
+#include "sha1_i.h"
+#include "md5_i.h"
+
+struct crypto_hash {
+	enum crypto_hash_alg alg;
+	union {
+		struct MD5Context md5;
+		struct SHA1Context sha1;
+#ifdef CONFIG_SHA256
+		struct sha256_state sha256;
+#endif /* CONFIG_SHA256 */
+#ifdef CONFIG_INTERNAL_SHA384
+		struct sha384_state sha384;
+#endif /* CONFIG_INTERNAL_SHA384 */
+#ifdef CONFIG_INTERNAL_SHA512
+		struct sha512_state sha512;
+#endif /* CONFIG_INTERNAL_SHA512 */
+	} u;
+	u8 key[64];
+	size_t key_len;
+};
+
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+				      size_t key_len)
+{
+	struct crypto_hash *ctx;
+	u8 k_pad[64];
+	u8 tk[32];
+	size_t i;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+
+	ctx->alg = alg;
+
+	switch (alg) {
+	case CRYPTO_HASH_ALG_MD5:
+		MD5Init(&ctx->u.md5);
+		break;
+	case CRYPTO_HASH_ALG_SHA1:
+		SHA1Init(&ctx->u.sha1);
+		break;
+#ifdef CONFIG_SHA256
+	case CRYPTO_HASH_ALG_SHA256:
+		sha256_init(&ctx->u.sha256);
+		break;
+#endif /* CONFIG_SHA256 */
+#ifdef CONFIG_INTERNAL_SHA384
+	case CRYPTO_HASH_ALG_SHA384:
+		sha384_init(&ctx->u.sha384);
+		break;
+#endif /* CONFIG_INTERNAL_SHA384 */
+#ifdef CONFIG_INTERNAL_SHA512
+	case CRYPTO_HASH_ALG_SHA512:
+		sha512_init(&ctx->u.sha512);
+		break;
+#endif /* CONFIG_INTERNAL_SHA512 */
+	case CRYPTO_HASH_ALG_HMAC_MD5:
+		if (key_len > sizeof(k_pad)) {
+			MD5Init(&ctx->u.md5);
+			MD5Update(&ctx->u.md5, key, key_len);
+			MD5Final(tk, &ctx->u.md5);
+			key = tk;
+			key_len = 16;
+		}
+		os_memcpy(ctx->key, key, key_len);
+		ctx->key_len = key_len;
+
+		os_memcpy(k_pad, key, key_len);
+		if (key_len < sizeof(k_pad))
+			os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
+		for (i = 0; i < sizeof(k_pad); i++)
+			k_pad[i] ^= 0x36;
+		MD5Init(&ctx->u.md5);
+		MD5Update(&ctx->u.md5, k_pad, sizeof(k_pad));
+		break;
+	case CRYPTO_HASH_ALG_HMAC_SHA1:
+		if (key_len > sizeof(k_pad)) {
+			SHA1Init(&ctx->u.sha1);
+			SHA1Update(&ctx->u.sha1, key, key_len);
+			SHA1Final(tk, &ctx->u.sha1);
+			key = tk;
+			key_len = 20;
+		}
+		os_memcpy(ctx->key, key, key_len);
+		ctx->key_len = key_len;
+
+		os_memcpy(k_pad, key, key_len);
+		if (key_len < sizeof(k_pad))
+			os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
+		for (i = 0; i < sizeof(k_pad); i++)
+			k_pad[i] ^= 0x36;
+		SHA1Init(&ctx->u.sha1);
+		SHA1Update(&ctx->u.sha1, k_pad, sizeof(k_pad));
+		break;
+#ifdef CONFIG_SHA256
+	case CRYPTO_HASH_ALG_HMAC_SHA256:
+		if (key_len > sizeof(k_pad)) {
+			sha256_init(&ctx->u.sha256);
+			sha256_process(&ctx->u.sha256, key, key_len);
+			sha256_done(&ctx->u.sha256, tk);
+			key = tk;
+			key_len = 32;
+		}
+		os_memcpy(ctx->key, key, key_len);
+		ctx->key_len = key_len;
+
+		os_memcpy(k_pad, key, key_len);
+		if (key_len < sizeof(k_pad))
+			os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
+		for (i = 0; i < sizeof(k_pad); i++)
+			k_pad[i] ^= 0x36;
+		sha256_init(&ctx->u.sha256);
+		sha256_process(&ctx->u.sha256, k_pad, sizeof(k_pad));
+		break;
+#endif /* CONFIG_SHA256 */
+	default:
+		os_free(ctx);
+		return NULL;
+	}
+
+	return ctx;
+}
+
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+	if (ctx == NULL)
+		return;
+
+	switch (ctx->alg) {
+	case CRYPTO_HASH_ALG_MD5:
+	case CRYPTO_HASH_ALG_HMAC_MD5:
+		MD5Update(&ctx->u.md5, data, len);
+		break;
+	case CRYPTO_HASH_ALG_SHA1:
+	case CRYPTO_HASH_ALG_HMAC_SHA1:
+		SHA1Update(&ctx->u.sha1, data, len);
+		break;
+#ifdef CONFIG_SHA256
+	case CRYPTO_HASH_ALG_SHA256:
+	case CRYPTO_HASH_ALG_HMAC_SHA256:
+		sha256_process(&ctx->u.sha256, data, len);
+		break;
+#endif /* CONFIG_SHA256 */
+#ifdef CONFIG_INTERNAL_SHA384
+	case CRYPTO_HASH_ALG_SHA384:
+		sha384_process(&ctx->u.sha384, data, len);
+		break;
+#endif /* CONFIG_INTERNAL_SHA384 */
+#ifdef CONFIG_INTERNAL_SHA512
+	case CRYPTO_HASH_ALG_SHA512:
+		sha512_process(&ctx->u.sha512, data, len);
+		break;
+#endif /* CONFIG_INTERNAL_SHA512 */
+	default:
+		break;
+	}
+}
+
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+	u8 k_pad[64];
+	size_t i;
+
+	if (ctx == NULL)
+		return -2;
+
+	if (mac == NULL || len == NULL) {
+		os_free(ctx);
+		return 0;
+	}
+
+	switch (ctx->alg) {
+	case CRYPTO_HASH_ALG_MD5:
+		if (*len < 16) {
+			*len = 16;
+			os_free(ctx);
+			return -1;
+		}
+		*len = 16;
+		MD5Final(mac, &ctx->u.md5);
+		break;
+	case CRYPTO_HASH_ALG_SHA1:
+		if (*len < 20) {
+			*len = 20;
+			os_free(ctx);
+			return -1;
+		}
+		*len = 20;
+		SHA1Final(mac, &ctx->u.sha1);
+		break;
+#ifdef CONFIG_SHA256
+	case CRYPTO_HASH_ALG_SHA256:
+		if (*len < 32) {
+			*len = 32;
+			os_free(ctx);
+			return -1;
+		}
+		*len = 32;
+		sha256_done(&ctx->u.sha256, mac);
+		break;
+#endif /* CONFIG_SHA256 */
+#ifdef CONFIG_INTERNAL_SHA384
+	case CRYPTO_HASH_ALG_SHA384:
+		if (*len < 48) {
+			*len = 48;
+			os_free(ctx);
+			return -1;
+		}
+		*len = 48;
+		sha384_done(&ctx->u.sha384, mac);
+		break;
+#endif /* CONFIG_INTERNAL_SHA384 */
+#ifdef CONFIG_INTERNAL_SHA512
+	case CRYPTO_HASH_ALG_SHA512:
+		if (*len < 64) {
+			*len = 64;
+			os_free(ctx);
+			return -1;
+		}
+		*len = 64;
+		sha512_done(&ctx->u.sha512, mac);
+		break;
+#endif /* CONFIG_INTERNAL_SHA512 */
+	case CRYPTO_HASH_ALG_HMAC_MD5:
+		if (*len < 16) {
+			*len = 16;
+			os_free(ctx);
+			return -1;
+		}
+		*len = 16;
+
+		MD5Final(mac, &ctx->u.md5);
+
+		os_memcpy(k_pad, ctx->key, ctx->key_len);
+		os_memset(k_pad + ctx->key_len, 0,
+			  sizeof(k_pad) - ctx->key_len);
+		for (i = 0; i < sizeof(k_pad); i++)
+			k_pad[i] ^= 0x5c;
+		MD5Init(&ctx->u.md5);
+		MD5Update(&ctx->u.md5, k_pad, sizeof(k_pad));
+		MD5Update(&ctx->u.md5, mac, 16);
+		MD5Final(mac, &ctx->u.md5);
+		break;
+	case CRYPTO_HASH_ALG_HMAC_SHA1:
+		if (*len < 20) {
+			*len = 20;
+			os_free(ctx);
+			return -1;
+		}
+		*len = 20;
+
+		SHA1Final(mac, &ctx->u.sha1);
+
+		os_memcpy(k_pad, ctx->key, ctx->key_len);
+		os_memset(k_pad + ctx->key_len, 0,
+			  sizeof(k_pad) - ctx->key_len);
+		for (i = 0; i < sizeof(k_pad); i++)
+			k_pad[i] ^= 0x5c;
+		SHA1Init(&ctx->u.sha1);
+		SHA1Update(&ctx->u.sha1, k_pad, sizeof(k_pad));
+		SHA1Update(&ctx->u.sha1, mac, 20);
+		SHA1Final(mac, &ctx->u.sha1);
+		break;
+#ifdef CONFIG_SHA256
+	case CRYPTO_HASH_ALG_HMAC_SHA256:
+		if (*len < 32) {
+			*len = 32;
+			os_free(ctx);
+			return -1;
+		}
+		*len = 32;
+
+		sha256_done(&ctx->u.sha256, mac);
+
+		os_memcpy(k_pad, ctx->key, ctx->key_len);
+		os_memset(k_pad + ctx->key_len, 0,
+			  sizeof(k_pad) - ctx->key_len);
+		for (i = 0; i < sizeof(k_pad); i++)
+			k_pad[i] ^= 0x5c;
+		sha256_init(&ctx->u.sha256);
+		sha256_process(&ctx->u.sha256, k_pad, sizeof(k_pad));
+		sha256_process(&ctx->u.sha256, mac, 32);
+		sha256_done(&ctx->u.sha256, mac);
+		break;
+#endif /* CONFIG_SHA256 */
+	default:
+		os_free(ctx);
+		return -1;
+	}
+
+	os_free(ctx);
+
+	if (TEST_FAIL())
+		return -1;
+
+	return 0;
+}
+
+
+int crypto_global_init(void)
+{
+	return 0;
+}
+
+
+void crypto_global_deinit(void)
+{
+}
+
+
+void crypto_unload(void)
+{
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_libtomcrypt.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_libtomcrypt.c
new file mode 100644
index 0000000..fd79c1a
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_libtomcrypt.c
@@ -0,0 +1,773 @@
+/*
+ * WPA Supplicant / Crypto wrapper for LibTomCrypt (for internal TLSv1)
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <tomcrypt.h>
+
+#include "common.h"
+#include "crypto.h"
+
+#ifndef mp_init_multi
+#define mp_init_multi                ltc_init_multi
+#define mp_clear_multi               ltc_deinit_multi
+#define mp_unsigned_bin_size(a)      ltc_mp.unsigned_size(a)
+#define mp_to_unsigned_bin(a, b)     ltc_mp.unsigned_write(a, b)
+#define mp_read_unsigned_bin(a, b, c) ltc_mp.unsigned_read(a, b, c)
+#define mp_exptmod(a,b,c,d)          ltc_mp.exptmod(a,b,c,d)
+#endif
+
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	hash_state md;
+	size_t i;
+
+	md4_init(&md);
+	for (i = 0; i < num_elem; i++)
+		md4_process(&md, addr[i], len[i]);
+	md4_done(&md, mac);
+	return 0;
+}
+
+
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+	u8 pkey[8], next, tmp;
+	int i;
+	symmetric_key skey;
+
+	/* Add parity bits to the key */
+	next = 0;
+	for (i = 0; i < 7; i++) {
+		tmp = key[i];
+		pkey[i] = (tmp >> i) | next | 1;
+		next = tmp << (7 - i);
+	}
+	pkey[i] = next | 1;
+
+	des_setup(pkey, 8, 0, &skey);
+	des_ecb_encrypt(clear, cypher, &skey);
+	des_done(&skey);
+	return 0;
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	hash_state md;
+	size_t i;
+
+	md5_init(&md);
+	for (i = 0; i < num_elem; i++)
+		md5_process(&md, addr[i], len[i]);
+	md5_done(&md, mac);
+	return 0;
+}
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	hash_state md;
+	size_t i;
+
+	sha1_init(&md);
+	for (i = 0; i < num_elem; i++)
+		sha1_process(&md, addr[i], len[i]);
+	sha1_done(&md, mac);
+	return 0;
+}
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+	symmetric_key *skey;
+	skey = os_malloc(sizeof(*skey));
+	if (skey == NULL)
+		return NULL;
+	if (aes_setup(key, len, 0, skey) != CRYPT_OK) {
+		os_free(skey);
+		return NULL;
+	}
+	return skey;
+}
+
+
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+	symmetric_key *skey = ctx;
+	return aes_ecb_encrypt(plain, crypt, skey) == CRYPT_OK ? 0 : -1;
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+	symmetric_key *skey = ctx;
+	aes_done(skey);
+	os_free(skey);
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+	symmetric_key *skey;
+	skey = os_malloc(sizeof(*skey));
+	if (skey == NULL)
+		return NULL;
+	if (aes_setup(key, len, 0, skey) != CRYPT_OK) {
+		os_free(skey);
+		return NULL;
+	}
+	return skey;
+}
+
+
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+	symmetric_key *skey = ctx;
+	return aes_ecb_encrypt(plain, (u8 *) crypt, skey) == CRYPT_OK ? 0 : -1;
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+	symmetric_key *skey = ctx;
+	aes_done(skey);
+	os_free(skey);
+}
+
+
+struct crypto_hash {
+	enum crypto_hash_alg alg;
+	int error;
+	union {
+		hash_state md;
+		hmac_state hmac;
+	} u;
+};
+
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+				      size_t key_len)
+{
+	struct crypto_hash *ctx;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+
+	ctx->alg = alg;
+
+	switch (alg) {
+	case CRYPTO_HASH_ALG_MD5:
+		if (md5_init(&ctx->u.md) != CRYPT_OK)
+			goto fail;
+		break;
+	case CRYPTO_HASH_ALG_SHA1:
+		if (sha1_init(&ctx->u.md) != CRYPT_OK)
+			goto fail;
+		break;
+	case CRYPTO_HASH_ALG_HMAC_MD5:
+		if (hmac_init(&ctx->u.hmac, find_hash("md5"), key, key_len) !=
+		    CRYPT_OK)
+			goto fail;
+		break;
+	case CRYPTO_HASH_ALG_HMAC_SHA1:
+		if (hmac_init(&ctx->u.hmac, find_hash("sha1"), key, key_len) !=
+		    CRYPT_OK)
+			goto fail;
+		break;
+	default:
+		goto fail;
+	}
+
+	return ctx;
+
+fail:
+	os_free(ctx);
+	return NULL;
+}
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+	if (ctx == NULL || ctx->error)
+		return;
+
+	switch (ctx->alg) {
+	case CRYPTO_HASH_ALG_MD5:
+		ctx->error = md5_process(&ctx->u.md, data, len) != CRYPT_OK;
+		break;
+	case CRYPTO_HASH_ALG_SHA1:
+		ctx->error = sha1_process(&ctx->u.md, data, len) != CRYPT_OK;
+		break;
+	case CRYPTO_HASH_ALG_HMAC_MD5:
+	case CRYPTO_HASH_ALG_HMAC_SHA1:
+		ctx->error = hmac_process(&ctx->u.hmac, data, len) != CRYPT_OK;
+		break;
+	}
+}
+
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+	int ret = 0;
+	unsigned long clen;
+
+	if (ctx == NULL)
+		return -2;
+
+	if (mac == NULL || len == NULL) {
+		os_free(ctx);
+		return 0;
+	}
+
+	if (ctx->error) {
+		os_free(ctx);
+		return -2;
+	}
+
+	switch (ctx->alg) {
+	case CRYPTO_HASH_ALG_MD5:
+		if (*len < 16) {
+			*len = 16;
+			os_free(ctx);
+			return -1;
+		}
+		*len = 16;
+		if (md5_done(&ctx->u.md, mac) != CRYPT_OK)
+			ret = -2;
+		break;
+	case CRYPTO_HASH_ALG_SHA1:
+		if (*len < 20) {
+			*len = 20;
+			os_free(ctx);
+			return -1;
+		}
+		*len = 20;
+		if (sha1_done(&ctx->u.md, mac) != CRYPT_OK)
+			ret = -2;
+		break;
+	case CRYPTO_HASH_ALG_HMAC_SHA1:
+		if (*len < 20) {
+			*len = 20;
+			os_free(ctx);
+			return -1;
+		}
+		/* continue */
+	case CRYPTO_HASH_ALG_HMAC_MD5:
+		if (*len < 16) {
+			*len = 16;
+			os_free(ctx);
+			return -1;
+		}
+		clen = *len;
+		if (hmac_done(&ctx->u.hmac, mac, &clen) != CRYPT_OK) {
+			os_free(ctx);
+			return -1;
+		}
+		*len = clen;
+		break;
+	default:
+		ret = -2;
+		break;
+	}
+
+	os_free(ctx);
+
+	if (TEST_FAIL())
+		return -1;
+
+	return ret;
+}
+
+
+struct crypto_cipher {
+	int rc4;
+	union {
+		symmetric_CBC cbc;
+		struct {
+			size_t used_bytes;
+			u8 key[16];
+			size_t keylen;
+		} rc4;
+	} u;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+					  const u8 *iv, const u8 *key,
+					  size_t key_len)
+{
+	struct crypto_cipher *ctx;
+	int idx, res, rc4 = 0;
+
+	switch (alg) {
+	case CRYPTO_CIPHER_ALG_AES:
+		idx = find_cipher("aes");
+		break;
+	case CRYPTO_CIPHER_ALG_3DES:
+		idx = find_cipher("3des");
+		break;
+	case CRYPTO_CIPHER_ALG_DES:
+		idx = find_cipher("des");
+		break;
+	case CRYPTO_CIPHER_ALG_RC2:
+		idx = find_cipher("rc2");
+		break;
+	case CRYPTO_CIPHER_ALG_RC4:
+		idx = -1;
+		rc4 = 1;
+		break;
+	default:
+		return NULL;
+	}
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+
+	if (rc4) {
+		ctx->rc4 = 1;
+		if (key_len > sizeof(ctx->u.rc4.key)) {
+			os_free(ctx);
+			return NULL;
+		}
+		ctx->u.rc4.keylen = key_len;
+		os_memcpy(ctx->u.rc4.key, key, key_len);
+	} else {
+		res = cbc_start(idx, iv, key, key_len, 0, &ctx->u.cbc);
+		if (res != CRYPT_OK) {
+			wpa_printf(MSG_DEBUG, "LibTomCrypt: Cipher start "
+				   "failed: %s", error_to_string(res));
+			os_free(ctx);
+			return NULL;
+		}
+	}
+
+	return ctx;
+}
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+			  u8 *crypt, size_t len)
+{
+	int res;
+
+	if (ctx->rc4) {
+		if (plain != crypt)
+			os_memcpy(crypt, plain, len);
+		rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen,
+			 ctx->u.rc4.used_bytes, crypt, len);
+		ctx->u.rc4.used_bytes += len;
+		return 0;
+	}
+
+	res = cbc_encrypt(plain, crypt, len, &ctx->u.cbc);
+	if (res != CRYPT_OK) {
+		wpa_printf(MSG_DEBUG, "LibTomCrypt: CBC encryption "
+			   "failed: %s", error_to_string(res));
+		return -1;
+	}
+	return 0;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+			  u8 *plain, size_t len)
+{
+	int res;
+
+	if (ctx->rc4) {
+		if (plain != crypt)
+			os_memcpy(plain, crypt, len);
+		rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen,
+			 ctx->u.rc4.used_bytes, plain, len);
+		ctx->u.rc4.used_bytes += len;
+		return 0;
+	}
+
+	res = cbc_decrypt(crypt, plain, len, &ctx->u.cbc);
+	if (res != CRYPT_OK) {
+		wpa_printf(MSG_DEBUG, "LibTomCrypt: CBC decryption "
+			   "failed: %s", error_to_string(res));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+	if (!ctx->rc4)
+		cbc_done(&ctx->u.cbc);
+	os_free(ctx);
+}
+
+
+struct crypto_public_key {
+	rsa_key rsa;
+};
+
+struct crypto_private_key {
+	rsa_key rsa;
+};
+
+
+struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len)
+{
+	int res;
+	struct crypto_public_key *pk;
+
+	pk = os_zalloc(sizeof(*pk));
+	if (pk == NULL)
+		return NULL;
+
+	res = rsa_import(key, len, &pk->rsa);
+	if (res != CRYPT_OK) {
+		wpa_printf(MSG_ERROR, "LibTomCrypt: Failed to import "
+			   "public key (res=%d '%s')",
+			   res, error_to_string(res));
+		os_free(pk);
+		return NULL;
+	}
+
+	if (pk->rsa.type != PK_PUBLIC) {
+		wpa_printf(MSG_ERROR, "LibTomCrypt: Public key was not of "
+			   "correct type");
+		rsa_free(&pk->rsa);
+		os_free(pk);
+		return NULL;
+	}
+
+	return pk;
+}
+
+
+struct crypto_private_key * crypto_private_key_import(const u8 *key,
+						      size_t len,
+						      const char *passwd)
+{
+	int res;
+	struct crypto_private_key *pk;
+
+	pk = os_zalloc(sizeof(*pk));
+	if (pk == NULL)
+		return NULL;
+
+	res = rsa_import(key, len, &pk->rsa);
+	if (res != CRYPT_OK) {
+		wpa_printf(MSG_ERROR, "LibTomCrypt: Failed to import "
+			   "private key (res=%d '%s')",
+			   res, error_to_string(res));
+		os_free(pk);
+		return NULL;
+	}
+
+	if (pk->rsa.type != PK_PRIVATE) {
+		wpa_printf(MSG_ERROR, "LibTomCrypt: Private key was not of "
+			   "correct type");
+		rsa_free(&pk->rsa);
+		os_free(pk);
+		return NULL;
+	}
+
+	return pk;
+}
+
+
+struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf,
+						       size_t len)
+{
+	/* No X.509 support in LibTomCrypt */
+	return NULL;
+}
+
+
+static int pkcs1_generate_encryption_block(u8 block_type, size_t modlen,
+					   const u8 *in, size_t inlen,
+					   u8 *out, size_t *outlen)
+{
+	size_t ps_len;
+	u8 *pos;
+
+	/*
+	 * PKCS #1 v1.5, 8.1:
+	 *
+	 * EB = 00 || BT || PS || 00 || D
+	 * BT = 00 or 01 for private-key operation; 02 for public-key operation
+	 * PS = k-3-||D||; at least eight octets
+	 * (BT=0: PS=0x00, BT=1: PS=0xff, BT=2: PS=pseudorandom non-zero)
+	 * k = length of modulus in octets (modlen)
+	 */
+
+	if (modlen < 12 || modlen > *outlen || inlen > modlen - 11) {
+		wpa_printf(MSG_DEBUG, "PKCS #1: %s - Invalid buffer "
+			   "lengths (modlen=%lu outlen=%lu inlen=%lu)",
+			   __func__, (unsigned long) modlen,
+			   (unsigned long) *outlen,
+			   (unsigned long) inlen);
+		return -1;
+	}
+
+	pos = out;
+	*pos++ = 0x00;
+	*pos++ = block_type; /* BT */
+	ps_len = modlen - inlen - 3;
+	switch (block_type) {
+	case 0:
+		os_memset(pos, 0x00, ps_len);
+		pos += ps_len;
+		break;
+	case 1:
+		os_memset(pos, 0xff, ps_len);
+		pos += ps_len;
+		break;
+	case 2:
+		if (os_get_random(pos, ps_len) < 0) {
+			wpa_printf(MSG_DEBUG, "PKCS #1: %s - Failed to get "
+				   "random data for PS", __func__);
+			return -1;
+		}
+		while (ps_len--) {
+			if (*pos == 0x00)
+				*pos = 0x01;
+			pos++;
+		}
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "PKCS #1: %s - Unsupported block type "
+			   "%d", __func__, block_type);
+		return -1;
+	}
+	*pos++ = 0x00;
+	os_memcpy(pos, in, inlen); /* D */
+
+	return 0;
+}
+
+
+static int crypto_rsa_encrypt_pkcs1(int block_type, rsa_key *key, int key_type,
+				    const u8 *in, size_t inlen,
+				    u8 *out, size_t *outlen)
+{
+	unsigned long len, modlen;
+	int res;
+
+	modlen = mp_unsigned_bin_size(key->N);
+
+	if (pkcs1_generate_encryption_block(block_type, modlen, in, inlen,
+					    out, outlen) < 0)
+		return -1;
+
+	len = *outlen;
+	res = rsa_exptmod(out, modlen, out, &len, key_type, key);
+	if (res != CRYPT_OK) {
+		wpa_printf(MSG_DEBUG, "LibTomCrypt: rsa_exptmod failed: %s",
+			   error_to_string(res));
+		return -1;
+	}
+	*outlen = len;
+
+	return 0;
+}
+
+
+int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key,
+					const u8 *in, size_t inlen,
+					u8 *out, size_t *outlen)
+{
+	return crypto_rsa_encrypt_pkcs1(2, &key->rsa, PK_PUBLIC, in, inlen,
+					out, outlen);
+}
+
+
+int crypto_private_key_sign_pkcs1(struct crypto_private_key *key,
+				  const u8 *in, size_t inlen,
+				  u8 *out, size_t *outlen)
+{
+	return crypto_rsa_encrypt_pkcs1(1, &key->rsa, PK_PRIVATE, in, inlen,
+					out, outlen);
+}
+
+
+void crypto_public_key_free(struct crypto_public_key *key)
+{
+	if (key) {
+		rsa_free(&key->rsa);
+		os_free(key);
+	}
+}
+
+
+void crypto_private_key_free(struct crypto_private_key *key)
+{
+	if (key) {
+		rsa_free(&key->rsa);
+		os_free(key);
+	}
+}
+
+
+int crypto_public_key_decrypt_pkcs1(struct crypto_public_key *key,
+				    const u8 *crypt, size_t crypt_len,
+				    u8 *plain, size_t *plain_len)
+{
+	int res;
+	unsigned long len;
+	u8 *pos;
+
+	len = *plain_len;
+	res = rsa_exptmod(crypt, crypt_len, plain, &len, PK_PUBLIC,
+			  &key->rsa);
+	if (res != CRYPT_OK) {
+		wpa_printf(MSG_DEBUG, "LibTomCrypt: rsa_exptmod failed: %s",
+			   error_to_string(res));
+		return -1;
+	}
+
+	/*
+	 * PKCS #1 v1.5, 8.1:
+	 *
+	 * EB = 00 || BT || PS || 00 || D
+	 * BT = 01
+	 * PS = k-3-||D|| times FF
+	 * k = length of modulus in octets
+	 */
+
+	if (len < 3 + 8 + 16 /* min hash len */ ||
+	    plain[0] != 0x00 || plain[1] != 0x01 || plain[2] != 0xff) {
+		wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
+			   "structure");
+		return -1;
+	}
+
+	pos = plain + 3;
+	while (pos < plain + len && *pos == 0xff)
+		pos++;
+	if (pos - plain - 2 < 8) {
+		/* PKCS #1 v1.5, 8.1: At least eight octets long PS */
+		wpa_printf(MSG_INFO, "LibTomCrypt: Too short signature "
+			   "padding");
+		return -1;
+	}
+
+	if (pos + 16 /* min hash len */ >= plain + len || *pos != 0x00) {
+		wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
+			   "structure (2)");
+		return -1;
+	}
+	pos++;
+	len -= pos - plain;
+
+	/* Strip PKCS #1 header */
+	os_memmove(plain, pos, len);
+	*plain_len = len;
+
+	return 0;
+}
+
+
+int crypto_global_init(void)
+{
+	ltc_mp = tfm_desc;
+	/* TODO: only register algorithms that are really needed */
+	if (register_hash(&md4_desc) < 0 ||
+	    register_hash(&md5_desc) < 0 ||
+	    register_hash(&sha1_desc) < 0 ||
+	    register_cipher(&aes_desc) < 0 ||
+	    register_cipher(&des_desc) < 0 ||
+	    register_cipher(&des3_desc) < 0) {
+		wpa_printf(MSG_ERROR, "TLSv1: Failed to register "
+			   "hash/cipher functions");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void crypto_global_deinit(void)
+{
+}
+
+
+#ifdef CONFIG_MODEXP
+
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+		   u8 *pubkey)
+{
+	size_t pubkey_len, pad;
+
+	if (os_get_random(privkey, prime_len) < 0)
+		return -1;
+	if (os_memcmp(privkey, prime, prime_len) > 0) {
+		/* Make sure private value is smaller than prime */
+		privkey[0] = 0;
+	}
+
+	pubkey_len = prime_len;
+	if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
+			   pubkey, &pubkey_len) < 0)
+		return -1;
+	if (pubkey_len < prime_len) {
+		pad = prime_len - pubkey_len;
+		os_memmove(pubkey + pad, pubkey, pubkey_len);
+		os_memset(pubkey, 0, pad);
+	}
+
+	return 0;
+}
+
+
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+			    const u8 *order, size_t order_len,
+			    const u8 *privkey, size_t privkey_len,
+			    const u8 *pubkey, size_t pubkey_len,
+			    u8 *secret, size_t *len)
+{
+	/* TODO: check pubkey */
+	return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+			      prime, prime_len, secret, len);
+}
+
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+		   const u8 *power, size_t power_len,
+		   const u8 *modulus, size_t modulus_len,
+		   u8 *result, size_t *result_len)
+{
+	void *b, *p, *m, *r;
+
+	if (mp_init_multi(&b, &p, &m, &r, NULL) != CRYPT_OK)
+		return -1;
+
+	if (mp_read_unsigned_bin(b, (u8 *) base, base_len) != CRYPT_OK ||
+	    mp_read_unsigned_bin(p, (u8 *) power, power_len) != CRYPT_OK ||
+	    mp_read_unsigned_bin(m, (u8 *) modulus, modulus_len) != CRYPT_OK)
+		goto fail;
+
+	if (mp_exptmod(b, p, m, r) != CRYPT_OK)
+		goto fail;
+
+	*result_len = mp_unsigned_bin_size(r);
+	if (mp_to_unsigned_bin(r, result) != CRYPT_OK)
+		goto fail;
+
+	mp_clear_multi(b, p, m, r, NULL);
+	return 0;
+
+fail:
+	mp_clear_multi(b, p, m, r, NULL);
+	return -1;
+}
+
+#endif /* CONFIG_MODEXP */
+
+
+void crypto_unload(void)
+{
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_linux.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_linux.c
new file mode 100644
index 0000000..9278e27
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_linux.c
@@ -0,0 +1,1014 @@
+/*
+ * Crypto wrapper for Linux kernel AF_ALG
+ * Copyright (c) 2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <linux/if_alg.h>
+
+#include "common.h"
+#include "crypto.h"
+#include "md5.h"
+#include "sha1.h"
+#include "sha256.h"
+#include "sha384.h"
+#include "aes.h"
+
+
+#ifndef SOL_ALG
+#define SOL_ALG 279
+#endif /* SOL_ALG */
+
+
+static int linux_af_alg_socket(const char *type, const char *name)
+{
+	struct sockaddr_alg sa;
+	int s;
+
+	if (TEST_FAIL())
+		return -1;
+
+	s = socket(AF_ALG, SOCK_SEQPACKET, 0);
+	if (s < 0) {
+		wpa_printf(MSG_ERROR, "%s: Failed to open AF_ALG socket: %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+
+	os_memset(&sa, 0, sizeof(sa));
+	sa.salg_family = AF_ALG;
+	os_strlcpy((char *) sa.salg_type, type, sizeof(sa.salg_type));
+	os_strlcpy((char *) sa.salg_name, name, sizeof(sa.salg_type));
+	if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "%s: Failed to bind AF_ALG socket(%s,%s): %s",
+			   __func__, type, name, strerror(errno));
+		close(s);
+		return -1;
+	}
+
+	return s;
+}
+
+
+static int linux_af_alg_hash_vector(const char *alg, const u8 *key,
+				    size_t key_len, size_t num_elem,
+				    const u8 *addr[], const size_t *len,
+				    u8 *mac, size_t mac_len)
+{
+	int s, t;
+	size_t i;
+	ssize_t res;
+	int ret = -1;
+
+	s = linux_af_alg_socket("hash", alg);
+	if (s < 0)
+		return -1;
+
+	if (key && setsockopt(s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) {
+		wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s",
+			   __func__, strerror(errno));
+		close(s);
+		return -1;
+	}
+
+	t = accept(s, NULL, NULL);
+	if (t < 0) {
+		wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s",
+			   __func__, strerror(errno));
+		close(s);
+		return -1;
+	}
+
+	for (i = 0; i < num_elem; i++) {
+		res = send(t, addr[i], len[i], i + 1 < num_elem ? MSG_MORE : 0);
+		if (res < 0) {
+			wpa_printf(MSG_ERROR,
+				   "%s: send on AF_ALG socket failed: %s",
+				   __func__, strerror(errno));
+			goto fail;
+		}
+		if ((size_t) res < len[i]) {
+			wpa_printf(MSG_ERROR,
+				   "%s: send on AF_ALG socket did not accept full buffer (%d/%d)",
+				   __func__, (int) res, (int) len[i]);
+			goto fail;
+		}
+	}
+
+	res = recv(t, mac, mac_len, 0);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR,
+			   "%s: recv on AF_ALG socket failed: %s",
+			   __func__, strerror(errno));
+		goto fail;
+	}
+	if ((size_t) res < mac_len) {
+		wpa_printf(MSG_ERROR,
+			   "%s: recv on AF_ALG socket did not return full buffer (%d/%d)",
+			   __func__, (int) res, (int) mac_len);
+		goto fail;
+	}
+
+	ret = 0;
+fail:
+	close(t);
+	close(s);
+
+	return ret;
+}
+
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return linux_af_alg_hash_vector("md4", NULL, 0, num_elem, addr, len,
+					mac, 16);
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return linux_af_alg_hash_vector("md5", NULL, 0, num_elem, addr, len,
+					mac, MD5_MAC_LEN);
+}
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		u8 *mac)
+{
+	return linux_af_alg_hash_vector("sha1", NULL, 0, num_elem, addr, len,
+					mac, SHA1_MAC_LEN);
+}
+
+
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	return linux_af_alg_hash_vector("sha256", NULL, 0, num_elem, addr, len,
+					mac, SHA256_MAC_LEN);
+}
+
+
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	return linux_af_alg_hash_vector("sha384", NULL, 0, num_elem, addr, len,
+					mac, SHA384_MAC_LEN);
+}
+
+
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	return linux_af_alg_hash_vector("sha512", NULL, 0, num_elem, addr, len,
+					mac, 64);
+}
+
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+		    const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return linux_af_alg_hash_vector("hmac(md5)", key, key_len, num_elem,
+					addr, len, mac, 16);
+}
+
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	     u8 *mac)
+{
+	return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return linux_af_alg_hash_vector("hmac(sha1)", key, key_len, num_elem,
+					addr, len, mac, SHA1_MAC_LEN);
+}
+
+
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	      u8 *mac)
+{
+	return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return linux_af_alg_hash_vector("hmac(sha256)", key, key_len, num_elem,
+					addr, len, mac, SHA256_MAC_LEN);
+}
+
+
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return linux_af_alg_hash_vector("hmac(sha384)", key, key_len, num_elem,
+					addr, len, mac, SHA384_MAC_LEN);
+}
+
+
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+struct crypto_hash {
+	int s;
+	int t;
+	size_t mac_len;
+	int failed;
+};
+
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+				      size_t key_len)
+{
+	struct crypto_hash *ctx;
+	const char *name;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (!ctx)
+		return NULL;
+
+	switch (alg) {
+	case CRYPTO_HASH_ALG_MD5:
+		name = "md5";
+		ctx->mac_len = MD5_MAC_LEN;
+		break;
+	case CRYPTO_HASH_ALG_SHA1:
+		name = "sha1";
+		ctx->mac_len = SHA1_MAC_LEN;
+		break;
+	case CRYPTO_HASH_ALG_HMAC_MD5:
+		name = "hmac(md5)";
+		ctx->mac_len = MD5_MAC_LEN;
+		break;
+	case CRYPTO_HASH_ALG_HMAC_SHA1:
+		name = "hmac(sha1)";
+		ctx->mac_len = SHA1_MAC_LEN;
+		break;
+	case CRYPTO_HASH_ALG_SHA256:
+		name = "sha256";
+		ctx->mac_len = SHA256_MAC_LEN;
+		break;
+	case CRYPTO_HASH_ALG_HMAC_SHA256:
+		name = "hmac(sha256)";
+		ctx->mac_len = SHA256_MAC_LEN;
+		break;
+	case CRYPTO_HASH_ALG_SHA384:
+		name = "sha384";
+		ctx->mac_len = SHA384_MAC_LEN;
+		break;
+	case CRYPTO_HASH_ALG_SHA512:
+		name = "sha512";
+		ctx->mac_len = 64;
+		break;
+	default:
+		os_free(ctx);
+		return NULL;
+	}
+
+	ctx->s = linux_af_alg_socket("hash", name);
+	if (ctx->s < 0) {
+		os_free(ctx);
+		return NULL;
+	}
+
+	if (key && key_len &&
+	    setsockopt(ctx->s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) {
+		wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s",
+			   __func__, strerror(errno));
+		close(ctx->s);
+		os_free(ctx);
+		return NULL;
+	}
+
+	ctx->t = accept(ctx->s, NULL, NULL);
+	if (ctx->t < 0) {
+		wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s",
+			   __func__, strerror(errno));
+		close(ctx->s);
+		os_free(ctx);
+		return NULL;
+	}
+
+	return ctx;
+}
+
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+	ssize_t res;
+
+	if (!ctx)
+		return;
+
+	res = send(ctx->t, data, len, MSG_MORE);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR,
+			   "%s: send on AF_ALG socket failed: %s",
+			   __func__, strerror(errno));
+		ctx->failed = 1;
+		return;
+	}
+	if ((size_t) res < len) {
+		wpa_printf(MSG_ERROR,
+			   "%s: send on AF_ALG socket did not accept full buffer (%d/%d)",
+			   __func__, (int) res, (int) len);
+		ctx->failed = 1;
+		return;
+	}
+}
+
+
+static void crypto_hash_deinit(struct crypto_hash *ctx)
+{
+	close(ctx->s);
+	close(ctx->t);
+	os_free(ctx);
+}
+
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+	ssize_t res;
+
+	if (!ctx)
+		return -2;
+
+	if (!mac || !len) {
+		crypto_hash_deinit(ctx);
+		return 0;
+	}
+
+	if (ctx->failed) {
+		crypto_hash_deinit(ctx);
+		return -2;
+	}
+
+	if (*len < ctx->mac_len) {
+		crypto_hash_deinit(ctx);
+		*len = ctx->mac_len;
+		return -1;
+	}
+	*len = ctx->mac_len;
+
+	res = recv(ctx->t, mac, ctx->mac_len, 0);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR,
+			   "%s: recv on AF_ALG socket failed: %s",
+			   __func__, strerror(errno));
+		crypto_hash_deinit(ctx);
+		return -2;
+	}
+	if ((size_t) res < ctx->mac_len) {
+		wpa_printf(MSG_ERROR,
+			   "%s: recv on AF_ALG socket did not return full buffer (%d/%d)",
+			   __func__, (int) res, (int) ctx->mac_len);
+		crypto_hash_deinit(ctx);
+		return -2;
+	}
+
+	crypto_hash_deinit(ctx);
+
+	if (TEST_FAIL())
+		return -1;
+	return 0;
+}
+
+
+struct linux_af_alg_skcipher {
+	int s;
+	int t;
+};
+
+
+static void linux_af_alg_skcipher_deinit(struct linux_af_alg_skcipher *skcipher)
+{
+	if (!skcipher)
+		return;
+	if (skcipher->s >= 0)
+		close(skcipher->s);
+	if (skcipher->t >= 0)
+		close(skcipher->t);
+	os_free(skcipher);
+}
+
+
+static struct linux_af_alg_skcipher *
+linux_af_alg_skcipher(const char *alg, const u8 *key, size_t key_len)
+{
+	struct linux_af_alg_skcipher *skcipher;
+
+	skcipher = os_zalloc(sizeof(*skcipher));
+	if (!skcipher)
+		goto fail;
+	skcipher->t = -1;
+
+	skcipher->s = linux_af_alg_socket("skcipher", alg);
+	if (skcipher->s < 0)
+		goto fail;
+
+	if (setsockopt(skcipher->s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) {
+		wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s",
+			   __func__, strerror(errno));
+		goto fail;
+	}
+
+	skcipher->t = accept(skcipher->s, NULL, NULL);
+	if (skcipher->t < 0) {
+		wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s",
+			   __func__, strerror(errno));
+		goto fail;
+	}
+
+	return skcipher;
+fail:
+	linux_af_alg_skcipher_deinit(skcipher);
+	return NULL;
+}
+
+
+static int linux_af_alg_skcipher_oper(struct linux_af_alg_skcipher *skcipher,
+				      int enc, const u8 *in, u8 *out)
+{
+	char buf[CMSG_SPACE(sizeof(u32))];
+	struct iovec io[1];
+	struct msghdr msg;
+	struct cmsghdr *hdr;
+	ssize_t ret;
+	u32 *op;
+
+	io[0].iov_base = (void *) in;
+	io[0].iov_len = AES_BLOCK_SIZE;
+	os_memset(&msg, 0, sizeof(msg));
+	os_memset(buf, 0, sizeof(buf));
+	msg.msg_control = buf;
+	msg.msg_controllen = CMSG_SPACE(sizeof(u32));
+	msg.msg_iov = io;
+	msg.msg_iovlen = 1;
+	hdr = CMSG_FIRSTHDR(&msg);
+	hdr->cmsg_level = SOL_ALG;
+	hdr->cmsg_type = ALG_SET_OP;
+	hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+	op = (u32 *) CMSG_DATA(hdr);
+	*op = enc ? ALG_OP_ENCRYPT : ALG_OP_DECRYPT;
+
+	ret = sendmsg(skcipher->t, &msg, 0);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+
+	ret = read(skcipher->t, out, AES_BLOCK_SIZE);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: read failed: %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+	if (ret < AES_BLOCK_SIZE) {
+		wpa_printf(MSG_ERROR,
+			   "%s: read did not return full data (%d/%d)",
+			   __func__, (int) ret, AES_BLOCK_SIZE);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+	return linux_af_alg_skcipher("ecb(aes)", key, len);
+}
+
+
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+	struct linux_af_alg_skcipher *skcipher = ctx;
+
+	return linux_af_alg_skcipher_oper(skcipher, 1, plain, crypt);
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+	linux_af_alg_skcipher_deinit(ctx);
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+	return linux_af_alg_skcipher("ecb(aes)", key, len);
+}
+
+
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+	struct linux_af_alg_skcipher *skcipher = ctx;
+
+	return linux_af_alg_skcipher_oper(skcipher, 0, crypt, plain);
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+	linux_af_alg_skcipher_deinit(ctx);
+}
+
+
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+	     u8 *data, size_t data_len)
+{
+	struct linux_af_alg_skcipher *skcipher;
+	u8 *skip_buf;
+	char buf[CMSG_SPACE(sizeof(u32))];
+	struct iovec io[2];
+	struct msghdr msg;
+	struct cmsghdr *hdr;
+	ssize_t ret;
+	u32 *op;
+
+	skip_buf = os_zalloc(skip + 1);
+	if (!skip_buf)
+		return -1;
+	skcipher = linux_af_alg_skcipher("ecb(arc4)", key, keylen);
+	if (!skcipher) {
+		os_free(skip_buf);
+		return -1;
+	}
+
+	io[0].iov_base = skip_buf;
+	io[0].iov_len = skip;
+	io[1].iov_base = data;
+	io[1].iov_len = data_len;
+	os_memset(&msg, 0, sizeof(msg));
+	os_memset(buf, 0, sizeof(buf));
+	msg.msg_control = buf;
+	msg.msg_controllen = CMSG_SPACE(sizeof(u32));
+	msg.msg_iov = io;
+	msg.msg_iovlen = 2;
+	hdr = CMSG_FIRSTHDR(&msg);
+	hdr->cmsg_level = SOL_ALG;
+	hdr->cmsg_type = ALG_SET_OP;
+	hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+	op = (u32 *) CMSG_DATA(hdr);
+	*op = ALG_OP_ENCRYPT;
+
+	ret = sendmsg(skcipher->t, &msg, 0);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+			   __func__, strerror(errno));
+		os_free(skip_buf);
+		linux_af_alg_skcipher_deinit(skcipher);
+		return -1;
+	}
+	os_free(skip_buf);
+
+	msg.msg_control = NULL;
+	msg.msg_controllen = 0;
+	ret = recvmsg(skcipher->t, &msg, 0);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: recvmsg failed: %s",
+			   __func__, strerror(errno));
+		linux_af_alg_skcipher_deinit(skcipher);
+		return -1;
+	}
+	linux_af_alg_skcipher_deinit(skcipher);
+
+	if ((size_t) ret < skip + data_len) {
+		wpa_printf(MSG_ERROR,
+			   "%s: recvmsg did not return full data (%d/%d)",
+			   __func__, (int) ret, (int) (skip + data_len));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+	u8 pkey[8], next, tmp;
+	int i;
+	struct linux_af_alg_skcipher *skcipher;
+	char buf[CMSG_SPACE(sizeof(u32))];
+	struct iovec io[1];
+	struct msghdr msg;
+	struct cmsghdr *hdr;
+	ssize_t ret;
+	u32 *op;
+	int res = -1;
+
+	/* Add parity bits to the key */
+	next = 0;
+	for (i = 0; i < 7; i++) {
+		tmp = key[i];
+		pkey[i] = (tmp >> i) | next | 1;
+		next = tmp << (7 - i);
+	}
+	pkey[i] = next | 1;
+
+	skcipher = linux_af_alg_skcipher("ecb(des)", pkey, sizeof(pkey));
+	if (!skcipher)
+		goto fail;
+
+	io[0].iov_base = (void *) clear;
+	io[0].iov_len = 8;
+	os_memset(&msg, 0, sizeof(msg));
+	os_memset(buf, 0, sizeof(buf));
+	msg.msg_control = buf;
+	msg.msg_controllen = CMSG_SPACE(sizeof(u32));
+	msg.msg_iov = io;
+	msg.msg_iovlen = 1;
+	hdr = CMSG_FIRSTHDR(&msg);
+	hdr->cmsg_level = SOL_ALG;
+	hdr->cmsg_type = ALG_SET_OP;
+	hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+	op = (u32 *) CMSG_DATA(hdr);
+	*op = ALG_OP_ENCRYPT;
+
+	ret = sendmsg(skcipher->t, &msg, 0);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+			   __func__, strerror(errno));
+		goto fail;
+	}
+
+	ret = read(skcipher->t, cypher, 8);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: read failed: %s",
+			   __func__, strerror(errno));
+		goto fail;
+	}
+	if (ret < 8) {
+		wpa_printf(MSG_ERROR,
+			   "%s: read did not return full data (%d/8)",
+			   __func__, (int) ret);
+		goto fail;
+	}
+
+	res = 0;
+fail:
+	linux_af_alg_skcipher_deinit(skcipher);
+	return res;
+}
+
+
+static int aes_128_cbc_oper(const u8 *key, int enc, const u8 *iv,
+			    u8 *data, size_t data_len)
+{
+	struct linux_af_alg_skcipher *skcipher;
+	char buf[100];
+	struct iovec io[1];
+	struct msghdr msg;
+	struct cmsghdr *hdr;
+	ssize_t ret;
+	u32 *op;
+	struct af_alg_iv *alg_iv;
+	size_t iv_len = AES_BLOCK_SIZE;
+
+	skcipher = linux_af_alg_skcipher("cbc(aes)", key, 16);
+	if (!skcipher)
+		return -1;
+
+	io[0].iov_base = (void *) data;
+	io[0].iov_len = data_len;
+	os_memset(&msg, 0, sizeof(msg));
+	os_memset(buf, 0, sizeof(buf));
+	msg.msg_control = buf;
+	msg.msg_controllen = CMSG_SPACE(sizeof(u32)) +
+		CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+	msg.msg_iov = io;
+	msg.msg_iovlen = 1;
+
+	hdr = CMSG_FIRSTHDR(&msg);
+	hdr->cmsg_level = SOL_ALG;
+	hdr->cmsg_type = ALG_SET_OP;
+	hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+	op = (u32 *) CMSG_DATA(hdr);
+	*op = enc ? ALG_OP_ENCRYPT : ALG_OP_DECRYPT;
+
+	hdr = CMSG_NXTHDR(&msg, hdr);
+	hdr->cmsg_level = SOL_ALG;
+	hdr->cmsg_type = ALG_SET_IV;
+	hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+	alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr);
+	alg_iv->ivlen = iv_len;
+	os_memcpy(alg_iv->iv, iv, iv_len);
+
+	ret = sendmsg(skcipher->t, &msg, 0);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+			   __func__, strerror(errno));
+		linux_af_alg_skcipher_deinit(skcipher);
+		return -1;
+	}
+
+	ret = recvmsg(skcipher->t, &msg, 0);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: recvmsg failed: %s",
+			   __func__, strerror(errno));
+		linux_af_alg_skcipher_deinit(skcipher);
+		return -1;
+	}
+	if ((size_t) ret < data_len) {
+		wpa_printf(MSG_ERROR,
+			   "%s: recvmsg not return full data (%d/%d)",
+			   __func__, (int) ret, (int) data_len);
+		linux_af_alg_skcipher_deinit(skcipher);
+		return -1;
+	}
+
+	linux_af_alg_skcipher_deinit(skcipher);
+	return 0;
+}
+
+
+int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+	return aes_128_cbc_oper(key, 1, iv, data, data_len);
+}
+
+
+int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+	return aes_128_cbc_oper(key, 0, iv, data, data_len);
+}
+
+
+int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return linux_af_alg_hash_vector("cmac(aes)", key, key_len, num_elem,
+					addr, len, mac, AES_BLOCK_SIZE);
+}
+
+
+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
+			 const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
+}
+
+
+int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+	return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
+}
+
+
+int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+	return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
+}
+
+
+int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
+	       u8 *plain)
+{
+	struct linux_af_alg_skcipher *skcipher;
+	char buf[100];
+	struct iovec io[1];
+	struct msghdr msg;
+	struct cmsghdr *hdr;
+	ssize_t ret;
+	u32 *op;
+	struct af_alg_iv *alg_iv;
+	size_t iv_len = 8;
+
+	skcipher = linux_af_alg_skcipher("kw(aes)", kek, kek_len);
+	if (!skcipher)
+		return -1;
+
+	io[0].iov_base = (void *) (cipher + iv_len);
+	io[0].iov_len = n * 8;
+	os_memset(&msg, 0, sizeof(msg));
+	os_memset(buf, 0, sizeof(buf));
+	msg.msg_control = buf;
+	msg.msg_controllen = CMSG_SPACE(sizeof(u32)) +
+		CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+	msg.msg_iov = io;
+	msg.msg_iovlen = 1;
+
+	hdr = CMSG_FIRSTHDR(&msg);
+	hdr->cmsg_level = SOL_ALG;
+	hdr->cmsg_type = ALG_SET_OP;
+	hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+	op = (u32 *) CMSG_DATA(hdr);
+	*op = ALG_OP_DECRYPT;
+
+	hdr = CMSG_NXTHDR(&msg, hdr);
+	hdr->cmsg_level = SOL_ALG;
+	hdr->cmsg_type = ALG_SET_IV;
+	hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+	alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr);
+	alg_iv->ivlen = iv_len;
+	os_memcpy(alg_iv->iv, cipher, iv_len);
+
+	ret = sendmsg(skcipher->t, &msg, 0);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+
+	ret = read(skcipher->t, plain, n * 8);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: read failed: %s",
+			   __func__, strerror(errno));
+		linux_af_alg_skcipher_deinit(skcipher);
+		return -1;
+	}
+	if (ret < n * 8) {
+		wpa_printf(MSG_ERROR,
+			   "%s: read not return full data (%d/%d)",
+			   __func__, (int) ret, n * 8);
+		linux_af_alg_skcipher_deinit(skcipher);
+		return -1;
+	}
+
+	linux_af_alg_skcipher_deinit(skcipher);
+	return 0;
+}
+
+
+struct crypto_cipher {
+	struct linux_af_alg_skcipher *skcipher;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+					  const u8 *iv, const u8 *key,
+					  size_t key_len)
+{
+	struct crypto_cipher *ctx;
+	const char *name;
+	struct af_alg_iv *alg_iv;
+	size_t iv_len = 0;
+	char buf[100];
+	struct msghdr msg;
+	struct cmsghdr *hdr;
+	ssize_t ret;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (!ctx)
+		return NULL;
+
+	switch (alg) {
+	case CRYPTO_CIPHER_ALG_RC4:
+		name = "ecb(arc4)";
+		break;
+	case CRYPTO_CIPHER_ALG_AES:
+		name = "cbc(aes)";
+		iv_len = AES_BLOCK_SIZE;
+		break;
+	case CRYPTO_CIPHER_ALG_3DES:
+		name = "cbc(des3_ede)";
+		iv_len = 8;
+		break;
+	case CRYPTO_CIPHER_ALG_DES:
+		name = "cbc(des)";
+		iv_len = 8;
+		break;
+	default:
+		os_free(ctx);
+		return NULL;
+	}
+
+	ctx->skcipher = linux_af_alg_skcipher(name, key, key_len);
+	if (!ctx->skcipher) {
+		os_free(ctx);
+		return NULL;
+	}
+
+	if (iv && iv_len) {
+		os_memset(&msg, 0, sizeof(msg));
+		os_memset(buf, 0, sizeof(buf));
+		msg.msg_control = buf;
+		msg.msg_controllen = CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+		hdr = CMSG_FIRSTHDR(&msg);
+		hdr->cmsg_level = SOL_ALG;
+		hdr->cmsg_type = ALG_SET_IV;
+		hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len);
+		alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr);
+		alg_iv->ivlen = iv_len;
+		os_memcpy(alg_iv->iv, iv, iv_len);
+
+		ret = sendmsg(ctx->skcipher->t, &msg, 0);
+		if (ret < 0) {
+			wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+				   __func__, strerror(errno));
+			linux_af_alg_skcipher_deinit(ctx->skcipher);
+			os_free(ctx);
+			return NULL;
+		}
+	}
+
+	return ctx;
+}
+
+
+static int crypto_cipher_oper(struct crypto_cipher *ctx, u32 type, const u8 *in,
+			      u8 *out, size_t len)
+{
+	char buf[CMSG_SPACE(sizeof(u32))];
+	struct iovec io[1];
+	struct msghdr msg;
+	struct cmsghdr *hdr;
+	ssize_t ret;
+	u32 *op;
+
+	io[0].iov_base = (void *) in;
+	io[0].iov_len = len;
+	os_memset(&msg, 0, sizeof(msg));
+	os_memset(buf, 0, sizeof(buf));
+	msg.msg_control = buf;
+	msg.msg_controllen = CMSG_SPACE(sizeof(u32));
+	msg.msg_iov = io;
+	msg.msg_iovlen = 1;
+	hdr = CMSG_FIRSTHDR(&msg);
+	hdr->cmsg_level = SOL_ALG;
+	hdr->cmsg_type = ALG_SET_OP;
+	hdr->cmsg_len = CMSG_LEN(sizeof(u32));
+	op = (u32 *) CMSG_DATA(hdr);
+	*op = type;
+
+	ret = sendmsg(ctx->skcipher->t, &msg, 0);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+
+	ret = read(ctx->skcipher->t, out, len);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: read failed: %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+	if (ret < (ssize_t) len) {
+		wpa_printf(MSG_ERROR,
+			   "%s: read did not return full data (%d/%d)",
+			   __func__, (int) ret, (int) len);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+			  u8 *crypt, size_t len)
+{
+	return crypto_cipher_oper(ctx, ALG_OP_ENCRYPT, plain, crypt, len);
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+			  u8 *plain, size_t len)
+{
+	return crypto_cipher_oper(ctx, ALG_OP_DECRYPT, crypt, plain, len);
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+	if (ctx) {
+		linux_af_alg_skcipher_deinit(ctx->skcipher);
+		os_free(ctx);
+	}
+}
+
+
+int crypto_global_init(void)
+{
+	return 0;
+}
+
+
+void crypto_global_deinit(void)
+{
+}
+
+
+void crypto_unload(void)
+{
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_mbedtls.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_mbedtls.c
new file mode 100644
index 0000000..7a91c96
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_mbedtls.c
@@ -0,0 +1,4228 @@
+/*
+ * crypto wrapper functions for mbed TLS
+ *
+ * SPDX-FileCopyrightText: 2022 Glenn Strauss <gstrauss@gluelogic.com>
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "utils/includes.h"
+#include "utils/common.h"
+
+#include <mbedtls/version.h>
+#include <mbedtls/entropy.h>
+#include <mbedtls/ctr_drbg.h>
+#include <mbedtls/platform_util.h> /* mbedtls_platform_zeroize() */
+#include <mbedtls/asn1.h>
+#include <mbedtls/asn1write.h>
+#include <mbedtls/aes.h>
+#include <mbedtls/md.h>
+#include <mbedtls/md5.h>
+#include <mbedtls/sha1.h>
+#include <mbedtls/sha256.h>
+#include <mbedtls/sha512.h>
+
+#ifndef MBEDTLS_PRIVATE
+#define MBEDTLS_PRIVATE(x) x
+#endif
+
+/* hostapd/wpa_supplicant provides forced_memzero(),
+ * but prefer mbedtls_platform_zeroize() */
+#define forced_memzero(ptr,sz) mbedtls_platform_zeroize(ptr,sz)
+
+#ifndef __has_attribute
+#define __has_attribute(x) 0
+#endif
+
+#ifndef __GNUC_PREREQ
+#define __GNUC_PREREQ(maj,min) 0
+#endif
+
+#ifndef __attribute_cold__
+#if __has_attribute(cold) \
+ || __GNUC_PREREQ(4,3)
+#define __attribute_cold__  __attribute__((__cold__))
+#else
+#define __attribute_cold__
+#endif
+#endif
+
+#ifndef __attribute_noinline__
+#if __has_attribute(noinline) \
+ || __GNUC_PREREQ(3,1)
+#define __attribute_noinline__  __attribute__((__noinline__))
+#else
+#define __attribute_noinline__
+#endif
+#endif
+
+#include "crypto.h"
+#include "aes_wrap.h"
+#include "aes.h"
+#include "md5.h"
+#include "sha1.h"
+#include "sha256.h"
+#include "sha384.h"
+#include "sha512.h"
+
+
+/*
+ * selective code inclusion based on preprocessor defines
+ *
+ * future: additional code could be wrapped with preprocessor checks if
+ * wpa_supplicant/Makefile and hostap/Makefile were more consistent with
+ * setting preprocessor defines for named groups of functionality
+ */
+
+#if defined(CONFIG_FIPS)
+#undef MBEDTLS_MD4_C     /* omit md4_vector() */
+#undef MBEDTLS_MD5_C     /* omit md5_vector() hmac_md5_vector() hmac_md5() */
+#undef MBEDTLS_DES_C     /* omit des_encrypt() */
+#undef MBEDTLS_NIST_KW_C /* omit aes_wrap() aes_unwrap() */
+#define CRYPTO_MBEDTLS_CONFIG_FIPS
+#endif
+
+#if !defined(CONFIG_FIPS)
+#if defined(EAP_PWD) \
+ || defined(EAP_LEAP) || defined(EAP_LEAP_DYNAMIC) \
+ || defined(EAP_TTLS) || defined(EAP_TTLS_DYNAMIC) \
+ || defined(EAP_MSCHAPv2) || defined(EAP_MSCHAPv2_DYNAMIC) \
+ || defined(EAP_SERVER_MSCHAPV2)
+#ifndef MBEDTLS_MD4_C    /* (MD4 not in mbedtls 3.x) */
+#include "md4-internal.c"/* pull in hostap local implementation */
+#endif /* md4_vector() */
+#else
+#undef MBEDTLS_MD4_C     /* omit md4_vector() */
+#endif
+#endif
+
+#if !defined(CONFIG_NO_RC4) && !defined(CONFIG_NO_WPA)
+#ifndef MBEDTLS_ARC4_C   /* (RC4 not in mbedtls 3.x) */
+#include "rc4.c"         /* pull in hostap local implementation */
+#endif /* rc4_skip() */
+#else
+#undef MBEDTLS_ARC4_C    /* omit rc4_skip() */
+#endif
+
+#if defined(CONFIG_MACSEC)     \
+ || defined(CONFIG_NO_RADIUS)  \
+ || defined(CONFIG_IEEE80211R) \
+ || defined(EAP_SERVER_FAST)   \
+ || defined(EAP_SERVER_TEAP)   \
+ || !defined(CONFIG_NO_WPA)
+       /* aes_wrap() aes_unwrap() */
+#else
+#undef MBEDTLS_NIST_KW_C /* omit aes_wrap() aes_unwrap() */
+#endif
+
+#if !defined(CONFIG_SHA256)
+#undef MBEDTLS_SHA256_C
+#endif
+
+#if !defined(CONFIG_SHA384) && !defined(CONFIG_SHA512)
+#undef MBEDTLS_SHA512_C
+#endif
+
+#if defined(CONFIG_HMAC_SHA256_KDF)
+#define CRYPTO_MBEDTLS_HMAC_KDF_SHA256
+#endif
+#if defined(CONFIG_HMAC_SHA384_KDF)
+#define CRYPTO_MBEDTLS_HMAC_KDF_SHA384
+#endif
+#if defined(CONFIG_HMAC_SHA512_KDF)
+#define CRYPTO_MBEDTLS_HMAC_KDF_SHA512
+#endif
+
+#if defined(EAP_SIM) || defined(EAP_SIM_DYNAMIC) || defined(EAP_SERVER_SIM) \
+ || defined(EAP_AKA) || defined(EAP_AKA_DYNAMIC) || defined(EAP_SERVER_AKA)
+/* EAP_SIM=y EAP_AKA=y */
+#define CRYPTO_MBEDTLS_FIPS186_2_PRF
+#endif
+
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) \
+ || defined(EAP_TEAP) || defined(EAP_TEAP_DYNAMIC) || defined(EAP_SERVER_FAST)
+#define CRYPTO_MBEDTLS_SHA1_T_PRF
+#endif
+
+#if defined(CONFIG_DES)
+#define CRYPTO_MBEDTLS_DES_ENCRYPT
+#endif /* des_encrypt() */
+
+#if !defined(CONFIG_NO_PBKDF2)
+#define CRYPTO_MBEDTLS_PBKDF2_SHA1
+#endif /* pbkdf2_sha1() */
+
+#if defined(EAP_IKEV2) \
+ || defined(EAP_IKEV2_DYNAMIC) \
+ || defined(EAP_SERVER_IKEV2) /* CONFIG_EAP_IKEV2=y */
+#define CRYPTO_MBEDTLS_CRYPTO_CIPHER
+#endif /* crypto_cipher_*() */
+
+#if defined(EAP_PWD) || defined(EAP_SERVER_PWD) /* CONFIG_EAP_PWD=y */
+#define CRYPTO_MBEDTLS_CRYPTO_HASH
+#endif /* crypto_hash_*() */
+
+#if defined(EAP_PWD) || defined(EAP_SERVER_PWD) /* CONFIG_EAP_PWD=y */ \
+ || defined(CONFIG_SAE) /* CONFIG_SAE=y */
+#define CRYPTO_MBEDTLS_CRYPTO_BIGNUM
+#endif /* crypto_bignum_*() */
+
+#if defined(EAP_PWD)          /* CONFIG_EAP_PWD=y */    \
+ || defined(EAP_EKE)          /* CONFIG_EAP_EKE=y */    \
+ || defined(EAP_EKE_DYNAMIC)  /* CONFIG_EAP_EKE=y */    \
+ || defined(EAP_SERVER_EKE)   /* CONFIG_EAP_EKE=y */    \
+ || defined(EAP_IKEV2)        /* CONFIG_EAP_IKEV2y */   \
+ || defined(EAP_IKEV2_DYNAMIC)/* CONFIG_EAP_IKEV2=y */  \
+ || defined(EAP_SERVER_IKEV2) /* CONFIG_EAP_IKEV2=y */  \
+ || defined(CONFIG_SAE)       /* CONFIG_SAE=y */        \
+ || defined(CONFIG_WPS)       /* CONFIG_WPS=y */
+#define CRYPTO_MBEDTLS_CRYPTO_DH
+#if defined(CONFIG_WPS_NFC)
+#define CRYPTO_MBEDTLS_DH5_INIT_FIXED
+#endif /* dh5_init_fixed() */
+#endif /* crypto_dh_*() */
+
+#if !defined(CONFIG_NO_WPA) /* CONFIG_NO_WPA= */
+#define CRYPTO_MBEDTLS_CRYPTO_ECDH
+#endif /* crypto_ecdh_*() */
+
+#if defined(CONFIG_ECC)
+#define CRYPTO_MBEDTLS_CRYPTO_BIGNUM
+#define CRYPTO_MBEDTLS_CRYPTO_EC
+#endif /* crypto_ec_*() crypto_ec_key_*() */
+
+#if defined(CONFIG_DPP) /* CONFIG_DPP=y */
+#define CRYPTO_MBEDTLS_CRYPTO_EC_DPP /* extra for DPP */
+#define CRYPTO_MBEDTLS_CRYPTO_CSR
+#endif /* crypto_csr_*() */
+
+#if defined(CONFIG_DPP3) /* CONFIG_DPP3=y */
+#define CRYPTO_MBEDTLS_CRYPTO_HPKE
+#endif
+
+#if defined(CONFIG_DPP2) /* CONFIG_DPP2=y */
+#define CRYPTO_MBEDTLS_CRYPTO_PKCS7
+#endif /* crypto_pkcs7_*() */
+
+#if defined(EAP_SIM) || defined(EAP_SIM_DYNAMIC) || defined(EAP_SERVER_SIM) \
+ || defined(EAP_AKA) || defined(EAP_AKA_DYNAMIC) || defined(EAP_SERVER_AKA) \
+ || defined(CONFIG_AP) || defined(HOSTAPD)
+/* CONFIG_EAP_SIM=y CONFIG_EAP_AKA=y CONFIG_AP=y HOSTAPD */
+#if defined(CRYPTO_RSA_OAEP_SHA256)
+#define CRYPTO_MBEDTLS_CRYPTO_RSA
+#endif
+#endif /* crypto_rsa_*() */
+
+
+static int ctr_drbg_init_state;
+static mbedtls_ctr_drbg_context ctr_drbg;
+static mbedtls_entropy_context entropy;
+
+#ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
+#include <mbedtls/bignum.h>
+static mbedtls_mpi mpi_sw_A;
+#endif
+
+__attribute_cold__
+__attribute_noinline__
+static mbedtls_ctr_drbg_context * ctr_drbg_init(void)
+{
+	mbedtls_ctr_drbg_init(&ctr_drbg);
+	mbedtls_entropy_init(&entropy);
+	if (mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
+	                          NULL, 0)) {
+		wpa_printf(MSG_ERROR, "Init of random number generator failed");
+		/* XXX: abort? */
+	}
+	else
+		ctr_drbg_init_state = 1;
+
+	return &ctr_drbg;
+}
+
+__attribute_cold__
+void crypto_unload(void)
+{
+	if (ctr_drbg_init_state) {
+		mbedtls_ctr_drbg_free(&ctr_drbg);
+		mbedtls_entropy_free(&entropy);
+	  #ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
+		mbedtls_mpi_free(&mpi_sw_A);
+	  #endif
+		ctr_drbg_init_state = 0;
+	}
+}
+
+/* init ctr_drbg on first use
+ * crypto_global_init() and crypto_global_deinit() are not available here
+ * (available only when CONFIG_TLS=internal, which is not CONFIG_TLS=mbedtls) */
+mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void); /*(not in header)*/
+inline
+mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void)
+{
+	return ctr_drbg_init_state ? &ctr_drbg : ctr_drbg_init();
+}
+
+#ifdef CRYPTO_MBEDTLS_CONFIG_FIPS
+int crypto_get_random(void *buf, size_t len)
+{
+	return mbedtls_ctr_drbg_random(crypto_mbedtls_ctr_drbg(),buf,len) ? -1 : 0;
+}
+#endif
+
+
+#if 1
+
+/* tradeoff: slightly smaller code size here at cost of slight increase
+ * in instructions and function calls at runtime versus the expanded
+ * per-message-digest code that follows in #else (~0.5 kib .text larger) */
+
+__attribute_noinline__
+static int md_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+                     u8 *mac, mbedtls_md_type_t md_type)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	mbedtls_md_context_t ctx;
+	mbedtls_md_init(&ctx);
+	if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 0) != 0){
+		mbedtls_md_free(&ctx);
+		return -1;
+	}
+	mbedtls_md_starts(&ctx);
+	for (size_t i = 0; i < num_elem; ++i)
+		mbedtls_md_update(&ctx, addr[i], len[i]);
+	mbedtls_md_finish(&ctx, mac);
+	mbedtls_md_free(&ctx);
+	return 0;
+}
+
+#ifdef MBEDTLS_SHA512_C
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA512);
+}
+
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA384);
+}
+#endif
+
+#ifdef MBEDTLS_SHA256_C
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA256);
+}
+#endif
+
+#ifdef MBEDTLS_SHA1_C
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA1);
+}
+#endif
+
+#ifdef MBEDTLS_MD5_C
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_MD5);
+}
+#endif
+
+#ifdef MBEDTLS_MD4_C
+#include <mbedtls/md4.h>
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_MD4);
+}
+#endif
+
+#else  /* expanded per-message-digest functions */
+
+#ifdef MBEDTLS_SHA512_C
+#include <mbedtls/sha512.h>
+__attribute_noinline__
+static int sha384_512_vector(size_t num_elem, const u8 *addr[],
+                             const size_t *len, u8 *mac, int is384)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	struct mbedtls_sha512_context ctx;
+	mbedtls_sha512_init(&ctx);
+  #if MBEDTLS_VERSION_MAJOR >= 3
+	mbedtls_sha512_starts(&ctx, is384);
+	for (size_t i = 0; i < num_elem; ++i)
+		mbedtls_sha512_update(&ctx, addr[i], len[i]);
+	mbedtls_sha512_finish(&ctx, mac);
+  #else
+	mbedtls_sha512_starts_ret(&ctx, is384);
+	for (size_t i = 0; i < num_elem; ++i)
+		mbedtls_sha512_update_ret(&ctx, addr[i], len[i]);
+	mbedtls_sha512_finish_ret(&ctx, mac);
+  #endif
+	mbedtls_sha512_free(&ctx);
+	return 0;
+}
+
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return sha384_512_vector(num_elem, addr, len, mac, 0);
+}
+
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return sha384_512_vector(num_elem, addr, len, mac, 1);
+}
+#endif
+
+#ifdef MBEDTLS_SHA256_C
+#include <mbedtls/sha256.h>
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	struct mbedtls_sha256_context ctx;
+	mbedtls_sha256_init(&ctx);
+  #if MBEDTLS_VERSION_MAJOR >= 3
+	mbedtls_sha256_starts(&ctx, 0);
+	for (size_t i = 0; i < num_elem; ++i)
+		mbedtls_sha256_update(&ctx, addr[i], len[i]);
+	mbedtls_sha256_finish(&ctx, mac);
+  #else
+	mbedtls_sha256_starts_ret(&ctx, 0);
+	for (size_t i = 0; i < num_elem; ++i)
+		mbedtls_sha256_update_ret(&ctx, addr[i], len[i]);
+	mbedtls_sha256_finish_ret(&ctx, mac);
+  #endif
+	mbedtls_sha256_free(&ctx);
+	return 0;
+}
+#endif
+
+#ifdef MBEDTLS_SHA1_C
+#include <mbedtls/sha1.h>
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	struct mbedtls_sha1_context ctx;
+	mbedtls_sha1_init(&ctx);
+  #if MBEDTLS_VERSION_MAJOR >= 3
+	mbedtls_sha1_starts(&ctx);
+	for (size_t i = 0; i < num_elem; ++i)
+		mbedtls_sha1_update(&ctx, addr[i], len[i]);
+	mbedtls_sha1_finish(&ctx, mac);
+  #else
+	mbedtls_sha1_starts_ret(&ctx);
+	for (size_t i = 0; i < num_elem; ++i)
+		mbedtls_sha1_update_ret(&ctx, addr[i], len[i]);
+	mbedtls_sha1_finish_ret(&ctx, mac);
+  #endif
+	mbedtls_sha1_free(&ctx);
+	return 0;
+}
+#endif
+
+#ifdef MBEDTLS_MD5_C
+#include <mbedtls/md5.h>
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	struct mbedtls_md5_context ctx;
+	mbedtls_md5_init(&ctx);
+  #if MBEDTLS_VERSION_MAJOR >= 3
+	mbedtls_md5_starts(&ctx);
+	for (size_t i = 0; i < num_elem; ++i)
+		mbedtls_md5_update(&ctx, addr[i], len[i]);
+	mbedtls_md5_finish(&ctx, mac);
+  #else
+	mbedtls_md5_starts_ret(&ctx);
+	for (size_t i = 0; i < num_elem; ++i)
+		mbedtls_md5_update_ret(&ctx, addr[i], len[i]);
+	mbedtls_md5_finish_ret(&ctx, mac);
+  #endif
+	mbedtls_md5_free(&ctx);
+	return 0;
+}
+#endif
+
+#ifdef MBEDTLS_MD4_C
+#include <mbedtls/md4.h>
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	struct mbedtls_md4_context ctx;
+	mbedtls_md4_init(&ctx);
+	mbedtls_md4_starts_ret(&ctx);
+	for (size_t i = 0; i < num_elem; ++i)
+		mbedtls_md4_update_ret(&ctx, addr[i], len[i]);
+	mbedtls_md4_finish_ret(&ctx, mac);
+	mbedtls_md4_free(&ctx);
+	return 0;
+}
+#endif
+
+#endif /* expanded per-message-digest functions */
+
+
+__attribute_noinline__
+static int hmac_vector(const u8 *key, size_t key_len, size_t num_elem,
+                       const u8 *addr[], const size_t *len, u8 *mac,
+                       mbedtls_md_type_t md_type)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	mbedtls_md_context_t ctx;
+	mbedtls_md_init(&ctx);
+	if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1) != 0){
+		mbedtls_md_free(&ctx);
+		return -1;
+	}
+	mbedtls_md_hmac_starts(&ctx, key, key_len);
+	for (size_t i = 0; i < num_elem; ++i)
+		mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
+	mbedtls_md_hmac_finish(&ctx, mac);
+	mbedtls_md_free(&ctx);
+	return 0;
+}
+
+#ifdef MBEDTLS_SHA512_C
+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
+                       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return hmac_vector(key, key_len, num_elem, addr, len, mac,
+			   MBEDTLS_MD_SHA512);
+}
+
+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+                u8 *mac)
+{
+	return hmac_vector(key, key_len, 1, &data, &data_len, mac,
+			   MBEDTLS_MD_SHA512);
+}
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+                       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return hmac_vector(key, key_len, num_elem, addr, len, mac,
+			   MBEDTLS_MD_SHA384);
+}
+
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+                u8 *mac)
+{
+	return hmac_vector(key, key_len, 1, &data, &data_len, mac,
+			   MBEDTLS_MD_SHA384);
+}
+#endif
+
+#ifdef MBEDTLS_SHA256_C
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+                       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return hmac_vector(key, key_len, num_elem, addr, len, mac,
+			   MBEDTLS_MD_SHA256);
+}
+
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+                u8 *mac)
+{
+	return hmac_vector(key, key_len, 1, &data, &data_len, mac,
+			   MBEDTLS_MD_SHA256);
+}
+#endif
+
+#ifdef MBEDTLS_SHA1_C
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+                     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return hmac_vector(key, key_len, num_elem, addr, len, mac,
+			   MBEDTLS_MD_SHA1);
+}
+
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+              u8 *mac)
+{
+	return hmac_vector(key, key_len, 1, &data, &data_len, mac,
+			   MBEDTLS_MD_SHA1);
+}
+#endif
+
+#ifdef MBEDTLS_MD5_C
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+                    const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return hmac_vector(key, key_len, num_elem, addr, len, mac,
+			   MBEDTLS_MD_MD5);
+}
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+             u8 *mac)
+{
+	return hmac_vector(key, key_len, 1, &data, &data_len, mac,
+			   MBEDTLS_MD_MD5);
+}
+#endif
+
+
+#if defined(MBEDTLS_SHA256_C) || defined(MBEDTLS_SHA512_C)
+
+#if defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA256) \
+ || defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA384) \
+ || defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA512)
+
+#include <mbedtls/hkdf.h>
+
+/* sha256-kdf.c sha384-kdf.c sha512-kdf.c */
+
+/* HMAC-SHA256 KDF (RFC 5295) and HKDF-Expand(SHA256) (RFC 5869) */
+/* HMAC-SHA384 KDF (RFC 5295) and HKDF-Expand(SHA384) (RFC 5869) */
+/* HMAC-SHA512 KDF (RFC 5295) and HKDF-Expand(SHA512) (RFC 5869) */
+__attribute_noinline__
+static int hmac_kdf_expand(const u8 *prk, size_t prk_len,
+                           const char *label, const u8 *info, size_t info_len,
+                           u8 *okm, size_t okm_len, mbedtls_md_type_t md_type)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
+  #ifdef MBEDTLS_HKDF_C
+	if (label == NULL)  /* RFC 5869 HKDF-Expand when (label == NULL) */
+		return mbedtls_hkdf_expand(md_info, prk, prk_len, info,
+		                           info_len, okm, okm_len) ? -1 : 0;
+  #endif
+
+	const size_t mac_len = mbedtls_md_get_size(md_info);
+	/* okm_len must not exceed 255 times hash len (RFC 5869 Section 2.3) */
+	if (okm_len > ((mac_len << 8) - mac_len))
+		return -1;
+
+	mbedtls_md_context_t ctx;
+	mbedtls_md_init(&ctx);
+	if (mbedtls_md_setup(&ctx, md_info, 1) != 0) {
+		mbedtls_md_free(&ctx);
+		return -1;
+	}
+	mbedtls_md_hmac_starts(&ctx, prk, prk_len);
+
+	u8 iter = 1;
+	const u8 *addr[4] = { okm, (const u8 *)label, info, &iter };
+	size_t len[4] = { 0, label ? os_strlen(label)+1 : 0, info_len, 1 };
+
+	for (; okm_len >= mac_len; okm_len -= mac_len, ++iter) {
+		for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
+			mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
+		mbedtls_md_hmac_finish(&ctx, okm);
+		mbedtls_md_hmac_reset(&ctx);
+		addr[0] = okm;
+		okm += mac_len;
+		len[0] = mac_len; /*(include digest in subsequent rounds)*/
+	}
+
+	if (okm_len) {
+		u8 hash[MBEDTLS_MD_MAX_SIZE];
+		for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
+			mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
+		mbedtls_md_hmac_finish(&ctx, hash);
+		os_memcpy(okm, hash, okm_len);
+		forced_memzero(hash, mac_len);
+	}
+
+	mbedtls_md_free(&ctx);
+	return 0;
+}
+
+#ifdef MBEDTLS_SHA512_C
+#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA512
+int hmac_sha512_kdf(const u8 *secret, size_t secret_len,
+		    const char *label, const u8 *seed, size_t seed_len,
+		    u8 *out, size_t outlen)
+{
+	return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
+	                       out, outlen, MBEDTLS_MD_SHA512);
+}
+#endif
+
+#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA384
+int hmac_sha384_kdf(const u8 *secret, size_t secret_len,
+		    const char *label, const u8 *seed, size_t seed_len,
+		    u8 *out, size_t outlen)
+{
+	return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
+	                       out, outlen, MBEDTLS_MD_SHA384);
+}
+#endif
+#endif
+
+#ifdef MBEDTLS_SHA256_C
+#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA256
+int hmac_sha256_kdf(const u8 *secret, size_t secret_len,
+		    const char *label, const u8 *seed, size_t seed_len,
+		    u8 *out, size_t outlen)
+{
+	return hmac_kdf_expand(secret, secret_len, label, seed, seed_len,
+	                       out, outlen, MBEDTLS_MD_SHA256);
+}
+#endif
+#endif
+
+#endif /* CRYPTO_MBEDTLS_HMAC_KDF_* */
+
+
+/* sha256-prf.c sha384-prf.c sha512-prf.c */
+
+/* hmac_prf_bits - IEEE Std 802.11ac-2013, 11.6.1.7.2 Key derivation function */
+__attribute_noinline__
+static int hmac_prf_bits(const u8 *key, size_t key_len, const char *label,
+                         const u8 *data, size_t data_len, u8 *buf,
+                         size_t buf_len_bits, mbedtls_md_type_t md_type)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	mbedtls_md_context_t ctx;
+	mbedtls_md_init(&ctx);
+	const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
+	if (mbedtls_md_setup(&ctx, md_info, 1) != 0) {
+		mbedtls_md_free(&ctx);
+		return -1;
+	}
+	mbedtls_md_hmac_starts(&ctx, key, key_len);
+
+	u16 ctr, n_le = host_to_le16(buf_len_bits);
+	const u8 * const addr[] = { (u8 *)&ctr,(u8 *)label,data,(u8 *)&n_le };
+	const size_t len[] =      { 2, os_strlen(label), data_len, 2 };
+	const size_t mac_len = mbedtls_md_get_size(md_info);
+	size_t buf_len = (buf_len_bits + 7) / 8;
+	for (ctr = 1; buf_len >= mac_len; buf_len -= mac_len, ++ctr) {
+	  #if __BYTE_ORDER == __BIG_ENDIAN
+		ctr = host_to_le16(ctr);
+	  #endif
+		for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
+			mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
+		mbedtls_md_hmac_finish(&ctx, buf);
+		mbedtls_md_hmac_reset(&ctx);
+		buf += mac_len;
+	  #if __BYTE_ORDER == __BIG_ENDIAN
+		ctr = le_to_host16(ctr);
+	  #endif
+	}
+
+	if (buf_len) {
+		u8 hash[MBEDTLS_MD_MAX_SIZE];
+	  #if __BYTE_ORDER == __BIG_ENDIAN
+		ctr = host_to_le16(ctr);
+	  #endif
+		for (size_t i = 0; i < ARRAY_SIZE(addr); ++i)
+			mbedtls_md_hmac_update(&ctx, addr[i], len[i]);
+		mbedtls_md_hmac_finish(&ctx, hash);
+		os_memcpy(buf, hash, buf_len);
+		buf += buf_len;
+		forced_memzero(hash, mac_len);
+	}
+
+	/* Mask out unused bits in last octet if it does not use all the bits */
+	if ((buf_len_bits &= 0x7))
+		buf[-1] &= (u8)(0xff << (8 - buf_len_bits));
+
+	mbedtls_md_free(&ctx);
+	return 0;
+}
+
+#ifdef MBEDTLS_SHA512_C
+int sha512_prf(const u8 *key, size_t key_len, const char *label,
+               const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+	return hmac_prf_bits(key, key_len, label, data, data_len, buf,
+	                     buf_len * 8, MBEDTLS_MD_SHA512);
+}
+
+int sha384_prf(const u8 *key, size_t key_len, const char *label,
+               const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+	return hmac_prf_bits(key, key_len, label, data, data_len, buf,
+	                     buf_len * 8, MBEDTLS_MD_SHA384);
+}
+#endif
+
+#ifdef MBEDTLS_SHA256_C
+int sha256_prf(const u8 *key, size_t key_len, const char *label,
+               const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+	return hmac_prf_bits(key, key_len, label, data, data_len, buf,
+	                     buf_len * 8, MBEDTLS_MD_SHA256);
+}
+
+int sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
+                    const u8 *data, size_t data_len, u8 *buf,
+                    size_t buf_len_bits)
+{
+	return hmac_prf_bits(key, key_len, label, data, data_len, buf,
+	                     buf_len_bits, MBEDTLS_MD_SHA256);
+}
+#endif
+
+#endif /* MBEDTLS_SHA256_C || MBEDTLS_SHA512_C */
+
+
+#ifdef MBEDTLS_SHA1_C
+
+/* sha1-prf.c */
+
+/* sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1) */
+
+int sha1_prf(const u8 *key, size_t key_len, const char *label,
+	     const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+	/*(note: algorithm differs from hmac_prf_bits() */
+	/*(note: smaller code size instead of expanding hmac_sha1_vector()
+	 * as is done in hmac_prf_bits(); not expecting large num of loops) */
+	u8 counter = 0;
+	const u8 *addr[] = { (u8 *)label, data, &counter };
+	const size_t len[] = { os_strlen(label)+1, data_len, 1 };
+
+	for (; buf_len >= SHA1_MAC_LEN; buf_len -= SHA1_MAC_LEN, ++counter) {
+		if (hmac_sha1_vector(key, key_len, 3, addr, len, buf))
+			return -1;
+		buf += SHA1_MAC_LEN;
+	}
+
+	if (buf_len) {
+		u8 hash[SHA1_MAC_LEN];
+		if (hmac_sha1_vector(key, key_len, 3, addr, len, hash))
+			return -1;
+		os_memcpy(buf, hash, buf_len);
+		forced_memzero(hash, sizeof(hash));
+	}
+
+	return 0;
+}
+
+#ifdef CRYPTO_MBEDTLS_SHA1_T_PRF
+
+/* sha1-tprf.c */
+
+/* sha1_t_prf - EAP-FAST Pseudo-Random Function (T-PRF) (RFC 4851,Section 5.5)*/
+
+int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
+	       const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len)
+{
+	/*(note: algorithm differs from hmac_prf_bits() and hmac_kdf() above)*/
+	/*(note: smaller code size instead of expanding hmac_sha1_vector()
+	 * as is done in hmac_prf_bits(); not expecting large num of loops) */
+	u8 ctr;
+	u16 olen = host_to_be16(buf_len);
+	const u8 *addr[] = { buf, (u8 *)label, seed, (u8 *)&olen, &ctr };
+	size_t len[] = { 0, os_strlen(label)+1, seed_len, 2, 1 };
+
+	for (ctr = 1; buf_len >= SHA1_MAC_LEN; buf_len -= SHA1_MAC_LEN, ++ctr) {
+		if (hmac_sha1_vector(key, key_len, 5, addr, len, buf))
+			return -1;
+		addr[0] = buf;
+		buf += SHA1_MAC_LEN;
+		len[0] = SHA1_MAC_LEN; /*(include digest in subsequent rounds)*/
+	}
+
+	if (buf_len) {
+		u8 hash[SHA1_MAC_LEN];
+		if (hmac_sha1_vector(key, key_len, 5, addr, len, hash))
+			return -1;
+		os_memcpy(buf, hash, buf_len);
+		forced_memzero(hash, sizeof(hash));
+	}
+
+	return 0;
+}
+
+#endif /* CRYPTO_MBEDTLS_SHA1_T_PRF */
+
+#ifdef CRYPTO_MBEDTLS_FIPS186_2_PRF
+
+/* fips_prf_internal.c sha1-internal.c */
+
+/* used only by src/eap_common/eap_sim_common.c:eap_sim_prf()
+ * for eap_sim_derive_keys() and eap_sim_derive_keys_reauth()
+ * where xlen is 160 */
+
+int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
+{
+	/* FIPS 186-2 + change notice 1 */
+
+	mbedtls_sha1_context ctx;
+	u8 * const xkey = ctx.MBEDTLS_PRIVATE(buffer);
+	u32 * const xstate = ctx.MBEDTLS_PRIVATE(state);
+	const u32 xstate_init[] =
+	  { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 };
+
+	mbedtls_sha1_init(&ctx);
+	os_memcpy(xkey, seed, seed_len < 64 ? seed_len : 64);
+
+	/* note: does not fill extra bytes if (xlen % 20) (SHA1_MAC_LEN) */
+	for (; xlen >= 20; xlen -= 20) {
+		/* XSEED_j = 0 */
+		/* XVAL = (XKEY + XSEED_j) mod 2^b */
+
+		/* w_i = G(t, XVAL) */
+		os_memcpy(xstate, xstate_init, sizeof(xstate_init));
+		mbedtls_internal_sha1_process(&ctx, xkey);
+
+	  #if __BYTE_ORDER == __LITTLE_ENDIAN
+		xstate[0] = host_to_be32(xstate[0]);
+		xstate[1] = host_to_be32(xstate[1]);
+		xstate[2] = host_to_be32(xstate[2]);
+		xstate[3] = host_to_be32(xstate[3]);
+		xstate[4] = host_to_be32(xstate[4]);
+	  #endif
+		os_memcpy(x, xstate, 20);
+		if (xlen == 20) /*(done; skip prep for next loop)*/
+			break;
+
+		/* XKEY = (1 + XKEY + w_i) mod 2^b */
+		for (u32 carry = 1, k = 20; k-- > 0; carry >>= 8)
+			xkey[k] = (carry += xkey[k] + x[k]) & 0xff;
+		x += 20;
+		/* x_j = w_0|w_1 (each pair of iterations through loop)*/
+	}
+
+	mbedtls_sha1_free(&ctx);
+	return 0;
+}
+
+#endif /* CRYPTO_MBEDTLS_FIPS186_2_PRF */
+
+#endif /* MBEDTLS_SHA1_C */
+
+
+#ifdef CRYPTO_MBEDTLS_DES_ENCRYPT
+#ifdef MBEDTLS_DES_C
+#include <mbedtls/des.h>
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+	u8 pkey[8], next, tmp;
+	int i;
+
+	/* Add parity bits to the key */
+	next = 0;
+	for (i = 0; i < 7; i++) {
+		tmp = key[i];
+		pkey[i] = (tmp >> i) | next | 1;
+		next = tmp << (7 - i);
+	}
+	pkey[i] = next | 1;
+
+	mbedtls_des_context des;
+	mbedtls_des_init(&des);
+	int ret = mbedtls_des_setkey_enc(&des, pkey)
+	       || mbedtls_des_crypt_ecb(&des, clear, cypher) ? -1 : 0;
+	mbedtls_des_free(&des);
+	return ret;
+}
+#else
+#include "des-internal.c"/* pull in hostap local implementation */
+#endif
+#endif
+
+
+#ifdef CRYPTO_MBEDTLS_PBKDF2_SHA1
+/* sha1-pbkdf2.c */
+#include <mbedtls/pkcs5.h>
+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
+                int iterations, u8 *buf, size_t buflen)
+{
+  #if MBEDTLS_VERSION_NUMBER >= 0x03020200 /* mbedtls 3.2.2 */
+	return mbedtls_pkcs5_pbkdf2_hmac_ext(MBEDTLS_MD_SHA1,
+			(const u8 *)passphrase, os_strlen(passphrase),
+			ssid, ssid_len, iterations, 32, buf) ? -1 : 0;
+  #else
+	const mbedtls_md_info_t *md_info;
+	md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
+	if (md_info == NULL)
+		return -1;
+	mbedtls_md_context_t ctx;
+	mbedtls_md_init(&ctx);
+	int ret = mbedtls_md_setup(&ctx, md_info, 1)
+	       || mbedtls_pkcs5_pbkdf2_hmac(&ctx,
+			(const u8 *)passphrase, os_strlen(passphrase),
+			ssid, ssid_len, iterations, 32, buf) ? -1 : 0;
+	mbedtls_md_free(&ctx);
+	return ret;
+  #endif
+}
+#endif
+
+
+/*#include "aes.h"*/ /* prototypes also included in "crypto.h" */
+
+static void *aes_crypt_init_mode(const u8 *key, size_t len, int mode)
+{
+	if (TEST_FAIL())
+		return NULL;
+
+	mbedtls_aes_context *aes = os_malloc(sizeof(*aes));
+	if (!aes)
+		return NULL;
+
+	mbedtls_aes_init(aes);
+	if ((mode == MBEDTLS_AES_ENCRYPT
+	    ? mbedtls_aes_setkey_enc(aes, key, len * 8)
+	    : mbedtls_aes_setkey_dec(aes, key, len * 8)) == 0)
+		return aes;
+
+	mbedtls_aes_free(aes);
+	os_free(aes);
+	return NULL;
+}
+
+void *aes_encrypt_init(const u8 *key, size_t len)
+{
+	return aes_crypt_init_mode(key, len, MBEDTLS_AES_ENCRYPT);
+}
+
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+	return mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_ENCRYPT, plain, crypt);
+}
+
+void aes_encrypt_deinit(void *ctx)
+{
+	mbedtls_aes_free(ctx);
+	os_free(ctx);
+}
+
+void *aes_decrypt_init(const u8 *key, size_t len)
+{
+	return aes_crypt_init_mode(key, len, MBEDTLS_AES_DECRYPT);
+}
+
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+	return mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_DECRYPT, crypt, plain);
+}
+
+void aes_decrypt_deinit(void *ctx)
+{
+	mbedtls_aes_free(ctx);
+	os_free(ctx);
+}
+
+
+#include "aes_wrap.h"
+
+
+#ifdef MBEDTLS_NIST_KW_C
+
+#include <mbedtls/nist_kw.h>
+
+/* aes-wrap.c */
+int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	mbedtls_nist_kw_context ctx;
+	mbedtls_nist_kw_init(&ctx);
+	size_t olen;
+	int ret = mbedtls_nist_kw_setkey(&ctx, MBEDTLS_CIPHER_ID_AES,
+	                                 kek, kek_len*8, 1)
+	       || mbedtls_nist_kw_wrap(&ctx, MBEDTLS_KW_MODE_KW, plain, n*8,
+	                               cipher, &olen, (n+1)*8) ? -1 : 0;
+	mbedtls_nist_kw_free(&ctx);
+	return ret;
+}
+
+/* aes-unwrap.c */
+int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, u8 *plain)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	mbedtls_nist_kw_context ctx;
+	mbedtls_nist_kw_init(&ctx);
+	size_t olen;
+	int ret = mbedtls_nist_kw_setkey(&ctx, MBEDTLS_CIPHER_ID_AES,
+	                                 kek, kek_len*8, 0)
+	       || mbedtls_nist_kw_unwrap(&ctx, MBEDTLS_KW_MODE_KW, cipher,
+	                                 (n+1)*8, plain, &olen, n*8) ? -1 : 0;
+	mbedtls_nist_kw_free(&ctx);
+	return ret;
+}
+
+#else
+
+#ifndef CRYPTO_MBEDTLS_CONFIG_FIPS
+#include "aes-wrap.c"    /* pull in hostap local implementation */
+#include "aes-unwrap.c"  /* pull in hostap local implementation */
+#endif
+
+#endif /* MBEDTLS_NIST_KW_C */
+
+
+#ifdef MBEDTLS_CMAC_C
+
+/* aes-omac1.c */
+
+#include <mbedtls/cmac.h>
+
+int omac1_aes_vector(
+    const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[],
+    const size_t *len, u8 *mac)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	mbedtls_cipher_type_t cipher_type;
+	switch (key_len) {
+	case 16: cipher_type = MBEDTLS_CIPHER_AES_128_ECB; break;
+	case 24: cipher_type = MBEDTLS_CIPHER_AES_192_ECB; break;
+	case 32: cipher_type = MBEDTLS_CIPHER_AES_256_ECB; break;
+	default: return -1;
+	}
+	const mbedtls_cipher_info_t *cipher_info;
+	cipher_info = mbedtls_cipher_info_from_type(cipher_type);
+	if (cipher_info == NULL)
+		return -1;
+
+	mbedtls_cipher_context_t ctx;
+	mbedtls_cipher_init(&ctx);
+	int ret = -1;
+	if (mbedtls_cipher_setup(&ctx, cipher_info) == 0
+	    && mbedtls_cipher_cmac_starts(&ctx, key, key_len*8) == 0) {
+		ret = 0;
+		for (size_t i = 0; i < num_elem && ret == 0; ++i)
+			ret = mbedtls_cipher_cmac_update(&ctx, addr[i], len[i]);
+	}
+	if (ret == 0)
+		ret = mbedtls_cipher_cmac_finish(&ctx, mac);
+	mbedtls_cipher_free(&ctx);
+	return ret ? -1 : 0;
+}
+
+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
+			 const u8 *addr[], const size_t *len,
+			 u8 *mac)
+{
+	return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
+}
+
+int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+	return omac1_aes_vector(key, 16, 1, &data, &data_len, mac);
+}
+
+int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+	return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
+}
+
+#else
+
+#include "aes-omac1.c"  /* pull in hostap local implementation */
+
+#ifndef MBEDTLS_AES_BLOCK_SIZE
+#define MBEDTLS_AES_BLOCK_SIZE 16
+#endif
+
+#endif /* MBEDTLS_CMAC_C */
+
+
+/* These interfaces can be inefficient when used in loops, as the overhead of
+ * initialization each call is large for each block input (e.g. 16 bytes) */
+
+
+/* aes-encblock.c */
+int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	mbedtls_aes_context aes;
+	mbedtls_aes_init(&aes);
+	int ret = mbedtls_aes_setkey_enc(&aes, key, 128)
+	       || mbedtls_aes_crypt_ecb(&aes, MBEDTLS_AES_ENCRYPT, in, out)
+	  ? -1
+	  : 0;
+	mbedtls_aes_free(&aes);
+	return ret;
+}
+
+
+/* aes-ctr.c */
+int aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce,
+		    u8 *data, size_t data_len)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	unsigned char counter[MBEDTLS_AES_BLOCK_SIZE];
+	unsigned char stream_block[MBEDTLS_AES_BLOCK_SIZE];
+	os_memcpy(counter, nonce, MBEDTLS_AES_BLOCK_SIZE);/*(must be writable)*/
+
+	mbedtls_aes_context ctx;
+	mbedtls_aes_init(&ctx);
+	size_t nc_off = 0;
+	int ret = mbedtls_aes_setkey_enc(&ctx, key, key_len*8)
+	       || mbedtls_aes_crypt_ctr(&ctx, data_len, &nc_off,
+	                                counter, stream_block,
+	                                data, data) ? -1 : 0;
+	forced_memzero(stream_block, sizeof(stream_block));
+	mbedtls_aes_free(&ctx);
+	return ret;
+}
+
+int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
+			u8 *data, size_t data_len)
+{
+	return aes_ctr_encrypt(key, 16, nonce, data, data_len);
+}
+
+
+/* aes-cbc.c */
+static int aes_128_cbc_oper(const u8 *key, const u8 *iv,
+                            u8 *data, size_t data_len, int mode)
+{
+	unsigned char ivec[MBEDTLS_AES_BLOCK_SIZE];
+	os_memcpy(ivec, iv, MBEDTLS_AES_BLOCK_SIZE); /*(must be writable)*/
+
+	mbedtls_aes_context ctx;
+	mbedtls_aes_init(&ctx);
+	int ret = (mode == MBEDTLS_AES_ENCRYPT
+	           ? mbedtls_aes_setkey_enc(&ctx, key, 128)
+	           : mbedtls_aes_setkey_dec(&ctx, key, 128))
+	       || mbedtls_aes_crypt_cbc(&ctx, mode, data_len, ivec, data, data);
+	mbedtls_aes_free(&ctx);
+	return ret ? -1 : 0;
+}
+
+int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_ENCRYPT);
+}
+
+int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_DECRYPT);
+}
+
+
+/*
+ * Much of the following is documented in crypto.h as for CONFIG_TLS=internal
+ * but such comments are not accurate:
+ *
+ * "This function is only used with internal TLSv1 implementation
+ *  (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ *  to implement this."
+ */
+
+
+#ifdef CRYPTO_MBEDTLS_CRYPTO_CIPHER
+
+#include <mbedtls/cipher.h>
+
+struct crypto_cipher
+{
+	mbedtls_cipher_context_t ctx_enc;
+	mbedtls_cipher_context_t ctx_dec;
+};
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+					  const u8 *iv, const u8 *key,
+					  size_t key_len)
+{
+	/* IKEv2 src/eap_common/ikev2_common.c:ikev2_{encr,decr}_encrypt()
+	 * uses one of CRYPTO_CIPHER_ALG_AES or CRYPTO_CIPHER_ALG_3DES */
+
+	mbedtls_cipher_type_t cipher_type;
+	size_t iv_len;
+	switch (alg) {
+  #ifdef MBEDTLS_ARC4_C
+  #if 0
+	case CRYPTO_CIPHER_ALG_RC4:
+		cipher_type = MBEDTLS_CIPHER_ARC4_128;
+		iv_len = 0;
+		break;
+  #endif
+  #endif
+  #ifdef MBEDTLS_AES_C
+	case CRYPTO_CIPHER_ALG_AES:
+		if (key_len == 16) cipher_type = MBEDTLS_CIPHER_AES_128_CTR;
+		if (key_len == 24) cipher_type = MBEDTLS_CIPHER_AES_192_CTR;
+		if (key_len == 32) cipher_type = MBEDTLS_CIPHER_AES_256_CTR;
+		iv_len = 16;
+		break;
+  #endif
+  #ifdef MBEDTLS_DES_C
+	case CRYPTO_CIPHER_ALG_3DES:
+		cipher_type = MBEDTLS_CIPHER_DES_EDE3_CBC;
+		iv_len = 8;
+		break;
+  #if 0
+	case CRYPTO_CIPHER_ALG_DES:
+		cipher_type = MBEDTLS_CIPHER_DES_CBC;
+		iv_len = 8;
+		break;
+  #endif
+  #endif
+	default:
+		return NULL;
+	}
+
+	const mbedtls_cipher_info_t *cipher_info;
+	cipher_info = mbedtls_cipher_info_from_type(cipher_type);
+	if (cipher_info == NULL)
+		return NULL;
+
+	key_len *= 8; /* key_bitlen */
+  #if 0 /*(were key_bitlen not already available)*/
+  #if MBEDTLS_VERSION_NUMBER >= 0x03010000 /* mbedtls 3.1.0 */
+	key_len = mbedtls_cipher_info_get_key_bitlen(cipher_info);
+  #else
+	key_len = cipher_info->MBEDTLS_PRIVATE(key_bitlen);
+  #endif
+  #endif
+
+  #if 0 /*(were iv_len not known above, would need MBEDTLS_PRIVATE(iv_size))*/
+	iv_len = cipher_info->MBEDTLS_PRIVATE(iv_size);
+  #endif
+
+	struct crypto_cipher *ctx = os_malloc(sizeof(*ctx));
+	if (!ctx)
+		return NULL;
+
+	mbedtls_cipher_init(&ctx->ctx_enc);
+	mbedtls_cipher_init(&ctx->ctx_dec);
+	if (   mbedtls_cipher_setup(&ctx->ctx_enc,cipher_info) == 0
+	    && mbedtls_cipher_setup(&ctx->ctx_dec,cipher_info) == 0
+	    && mbedtls_cipher_setkey(&ctx->ctx_enc,key,key_len,MBEDTLS_ENCRYPT) == 0
+	    && mbedtls_cipher_setkey(&ctx->ctx_dec,key,key_len,MBEDTLS_DECRYPT) == 0
+	    && mbedtls_cipher_set_iv(&ctx->ctx_enc,iv,iv_len) == 0
+	    && mbedtls_cipher_set_iv(&ctx->ctx_dec,iv,iv_len) == 0
+	    && mbedtls_cipher_reset(&ctx->ctx_enc) == 0
+	    && mbedtls_cipher_reset(&ctx->ctx_dec) == 0) {
+		return ctx;
+	}
+
+	mbedtls_cipher_free(&ctx->ctx_enc);
+	mbedtls_cipher_free(&ctx->ctx_dec);
+	os_free(ctx);
+	return NULL;
+}
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx,
+			  const u8 *plain, u8 *crypt, size_t len)
+{
+	size_t olen = 0; /*(poor interface above; unknown size of u8 *crypt)*/
+	return (mbedtls_cipher_update(&ctx->ctx_enc, plain, len, crypt, &olen)
+	        || mbedtls_cipher_finish(&ctx->ctx_enc, crypt + olen, &olen)) ? -1 : 0;
+}
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx,
+			  const u8 *crypt, u8 *plain, size_t len)
+{
+	size_t olen = 0; /*(poor interface above; unknown size of u8 *plain)*/
+	return (mbedtls_cipher_update(&ctx->ctx_dec, crypt, len, plain, &olen)
+	        || mbedtls_cipher_finish(&ctx->ctx_dec, plain + olen, &olen)) ? -1 : 0;
+}
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+	mbedtls_cipher_free(&ctx->ctx_enc);
+	mbedtls_cipher_free(&ctx->ctx_dec);
+	os_free(ctx);
+}
+
+#endif /* CRYPTO_MBEDTLS_CRYPTO_CIPHER */
+
+
+#ifdef CRYPTO_MBEDTLS_CRYPTO_HASH
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+				      size_t key_len)
+{
+	mbedtls_md_type_t md_type;
+	int is_hmac = 0;
+
+	switch (alg) {
+  #ifdef MBEDTLS_MD5_C
+	case CRYPTO_HASH_ALG_MD5:
+		md_type = MBEDTLS_MD_MD5;
+		break;
+  #endif
+  #ifdef MBEDTLS_SHA1_C
+	case CRYPTO_HASH_ALG_SHA1:
+		md_type = MBEDTLS_MD_SHA1;
+		break;
+  #endif
+  #ifdef MBEDTLS_MD5_C
+	case CRYPTO_HASH_ALG_HMAC_MD5:
+		md_type = MBEDTLS_MD_MD5;
+		is_hmac = 1;
+		break;
+  #endif
+  #ifdef MBEDTLS_SHA1_C
+	case CRYPTO_HASH_ALG_HMAC_SHA1:
+		md_type = MBEDTLS_MD_SHA1;
+		is_hmac = 1;
+		break;
+  #endif
+  #ifdef MBEDTLS_SHA256_C
+	case CRYPTO_HASH_ALG_SHA256:
+		md_type = MBEDTLS_MD_SHA256;
+		break;
+	case CRYPTO_HASH_ALG_HMAC_SHA256:
+		md_type = MBEDTLS_MD_SHA256;
+		is_hmac = 1;
+		break;
+  #endif
+  #ifdef MBEDTLS_SHA512_C
+	case CRYPTO_HASH_ALG_SHA384:
+		md_type = MBEDTLS_MD_SHA384;
+		break;
+	case CRYPTO_HASH_ALG_SHA512:
+		md_type = MBEDTLS_MD_SHA512;
+		break;
+  #endif
+	default:
+		return NULL;
+	}
+
+	const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type);
+	if (!md_info)
+		return NULL;
+
+	mbedtls_md_context_t *mctx = os_malloc(sizeof(*mctx));
+	if (mctx == NULL)
+		return NULL;
+
+	mbedtls_md_init(mctx);
+	if (mbedtls_md_setup(mctx, md_info, is_hmac) != 0) {
+		os_free(mctx);
+		return NULL;
+	}
+
+	if (is_hmac)
+		mbedtls_md_hmac_starts(mctx, key, key_len);
+	else
+		mbedtls_md_starts(mctx);
+	return (struct crypto_hash *)((uintptr_t)mctx | is_hmac);
+}
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+	mbedtls_md_context_t *mctx = (mbedtls_md_context_t*)((uintptr_t)ctx & ~1uL);
+  #if 0
+	/*(mbedtls_md_hmac_update() and mbedtls_md_update()
+	 * make same modifications under the hood in mbedtls)*/
+	if ((uintptr_t)ctx & 1uL)
+		mbedtls_md_hmac_update(mctx, data, len);
+	else
+  #endif
+		mbedtls_md_update(mctx, data, len);
+}
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+	mbedtls_md_context_t *mctx = (mbedtls_md_context_t*)((uintptr_t)ctx & ~1uL);
+	if (mac != NULL && len != NULL) { /*(NULL if caller just freeing context)*/
+	  #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
+		const mbedtls_md_info_t *md_info = mbedtls_md_info_from_ctx(mctx);
+	  #else
+		const mbedtls_md_info_t *md_info = mctx->MBEDTLS_PRIVATE(md_info);
+	  #endif
+		size_t maclen = mbedtls_md_get_size(md_info);
+		if (*len < maclen) {
+			*len = maclen;
+			/*(note: ctx not freed; can call again with larger *len)*/
+			return -1;
+		}
+		*len = maclen;
+		if ((uintptr_t)ctx & 1uL)
+			mbedtls_md_hmac_finish(mctx, mac);
+		else
+			mbedtls_md_finish(mctx, mac);
+	}
+	mbedtls_md_free(mctx);
+	os_free(mctx);
+
+	if (TEST_FAIL())
+		return -1;
+
+	return 0;
+}
+
+#endif /* CRYPTO_MBEDTLS_CRYPTO_HASH */
+
+
+#ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM
+
+#include <mbedtls/bignum.h>
+
+/* crypto.h bignum interfaces */
+
+struct crypto_bignum *crypto_bignum_init(void)
+{
+	if (TEST_FAIL())
+		return NULL;
+
+	mbedtls_mpi *bn = os_malloc(sizeof(*bn));
+	if (bn)
+		mbedtls_mpi_init(bn);
+	return (struct crypto_bignum *)bn;
+}
+
+struct crypto_bignum *crypto_bignum_init_set(const u8 *buf, size_t len)
+{
+	if (TEST_FAIL())
+		return NULL;
+
+	mbedtls_mpi *bn = os_malloc(sizeof(*bn));
+	if (bn) {
+		mbedtls_mpi_init(bn);
+		if (mbedtls_mpi_read_binary(bn, buf, len) == 0)
+			return (struct crypto_bignum *)bn;
+	}
+
+	os_free(bn);
+	return NULL;
+}
+
+struct crypto_bignum *crypto_bignum_init_uint(unsigned int val)
+{
+	if (TEST_FAIL())
+		return NULL;
+
+  #if 0 /*(hostap use of this interface passes int, not uint)*/
+	val = host_to_be32(val);
+	return crypto_bignum_init_set((const u8 *)&val, sizeof(val));
+  #else
+	mbedtls_mpi *bn = os_malloc(sizeof(*bn));
+	if (bn) {
+		mbedtls_mpi_init(bn);
+		if (mbedtls_mpi_lset(bn, (int)val) == 0)
+			return (struct crypto_bignum *)bn;
+	}
+
+	os_free(bn);
+	return NULL;
+  #endif
+}
+
+void crypto_bignum_deinit(struct crypto_bignum *n, int clear)
+{
+	mbedtls_mpi_free((mbedtls_mpi *)n);
+	os_free(n);
+}
+
+int crypto_bignum_to_bin(const struct crypto_bignum *a,
+			 u8 *buf, size_t buflen, size_t padlen)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	size_t n = mbedtls_mpi_size((mbedtls_mpi *)a);
+	if (n < padlen)
+		n = padlen;
+	return n > buflen || mbedtls_mpi_write_binary((mbedtls_mpi *)a, buf, n)
+	  ? -1
+	  : (int)(n);
+}
+
+int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	/*assert(r != m);*//* r must not be same as m for mbedtls_mpi_random()*/
+  #if MBEDTLS_VERSION_NUMBER >= 0x021B0000 /* mbedtls 2.27.0 */
+	return mbedtls_mpi_random((mbedtls_mpi *)r, 0, (mbedtls_mpi *)m,
+				  mbedtls_ctr_drbg_random,
+				  crypto_mbedtls_ctr_drbg()) ? -1 : 0;
+  #else
+	/* (needed by EAP_PWD, SAE, DPP) */
+	wpa_printf(MSG_ERROR,
+	           "mbedtls 2.27.0 or later required for mbedtls_mpi_random()");
+	return -1;
+  #endif
+}
+
+int crypto_bignum_add(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c)
+{
+	return mbedtls_mpi_add_mpi((mbedtls_mpi *)c,
+				   (const mbedtls_mpi *)a,
+				   (const mbedtls_mpi *)b) ? -1 : 0;
+}
+
+int crypto_bignum_mod(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c)
+{
+	return mbedtls_mpi_mod_mpi((mbedtls_mpi *)c,
+				   (const mbedtls_mpi *)a,
+				   (const mbedtls_mpi *)b) ? -1 : 0;
+}
+
+int crypto_bignum_exptmod(const struct crypto_bignum *a,
+			  const struct crypto_bignum *b,
+			  const struct crypto_bignum *c,
+			  struct crypto_bignum *d)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	/* (check if input params match d; d is the result) */
+	/* (a == d) is ok in current mbedtls implementation */
+	if (b == d || c == d) { /*(not ok; store result in intermediate)*/
+		mbedtls_mpi R;
+		mbedtls_mpi_init(&R);
+		int rc = mbedtls_mpi_exp_mod(&R,
+		                             (const mbedtls_mpi *)a,
+		                             (const mbedtls_mpi *)b,
+		                             (const mbedtls_mpi *)c,
+		                             NULL)
+		      || mbedtls_mpi_copy((mbedtls_mpi *)d, &R) ? -1 : 0;
+		mbedtls_mpi_free(&R);
+		return rc;
+	}
+	else {
+		return mbedtls_mpi_exp_mod((mbedtls_mpi *)d,
+		                           (const mbedtls_mpi *)a,
+		                           (const mbedtls_mpi *)b,
+		                           (const mbedtls_mpi *)c,
+		                           NULL) ? -1 : 0;
+	}
+}
+
+int crypto_bignum_inverse(const struct crypto_bignum *a,
+			  const struct crypto_bignum *b,
+			  struct crypto_bignum *c)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	return mbedtls_mpi_inv_mod((mbedtls_mpi *)c,
+				   (const mbedtls_mpi *)a,
+				   (const mbedtls_mpi *)b) ? -1 : 0;
+}
+
+int crypto_bignum_sub(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	return mbedtls_mpi_sub_mpi((mbedtls_mpi *)c,
+				   (const mbedtls_mpi *)a,
+				   (const mbedtls_mpi *)b) ? -1 : 0;
+}
+
+int crypto_bignum_div(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	/*(most current use of this crypto.h interface has a == c (result),
+	 * so store result in an intermediate to avoid overwritten input)*/
+	mbedtls_mpi R;
+	mbedtls_mpi_init(&R);
+	int rc = mbedtls_mpi_div_mpi(&R, NULL,
+				     (const mbedtls_mpi *)a,
+				     (const mbedtls_mpi *)b)
+	      || mbedtls_mpi_copy((mbedtls_mpi *)c, &R) ? -1 : 0;
+	mbedtls_mpi_free(&R);
+	return rc;
+}
+
+int crypto_bignum_addmod(const struct crypto_bignum *a,
+			 const struct crypto_bignum *b,
+			 const struct crypto_bignum *c,
+			 struct crypto_bignum *d)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	return mbedtls_mpi_add_mpi((mbedtls_mpi *)d,
+				   (const mbedtls_mpi *)a,
+				   (const mbedtls_mpi *)b)
+	    || mbedtls_mpi_mod_mpi((mbedtls_mpi *)d,
+				   (mbedtls_mpi *)d,
+				   (const mbedtls_mpi *)c) ? -1 : 0;
+}
+
+int crypto_bignum_mulmod(const struct crypto_bignum *a,
+			 const struct crypto_bignum *b,
+			 const struct crypto_bignum *c,
+			 struct crypto_bignum *d)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	return mbedtls_mpi_mul_mpi((mbedtls_mpi *)d,
+				   (const mbedtls_mpi *)a,
+				   (const mbedtls_mpi *)b)
+	    || mbedtls_mpi_mod_mpi((mbedtls_mpi *)d,
+				   (mbedtls_mpi *)d,
+				   (const mbedtls_mpi *)c) ? -1 : 0;
+}
+
+int crypto_bignum_sqrmod(const struct crypto_bignum *a,
+			 const struct crypto_bignum *b,
+			 struct crypto_bignum *c)
+{
+	if (TEST_FAIL())
+		return -1;
+
+  #if 1
+	return crypto_bignum_mulmod(a, a, b, c);
+  #else
+	mbedtls_mpi bn;
+	mbedtls_mpi_init(&bn);
+	if (mbedtls_mpi_lset(&bn, 2)) /* alt?: mbedtls_mpi_set_bit(&bn, 1) */
+		return -1;
+	int ret = mbedtls_mpi_exp_mod((mbedtls_mpi *)c,
+				      (const mbedtls_mpi *)a, &bn,
+				      (const mbedtls_mpi *)b, NULL) ? -1 : 0;
+	mbedtls_mpi_free(&bn);
+	return ret;
+  #endif
+}
+
+int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
+			 struct crypto_bignum *r)
+{
+	return mbedtls_mpi_copy((mbedtls_mpi *)r, (const mbedtls_mpi *)a)
+	    || mbedtls_mpi_shift_r((mbedtls_mpi *)r, n) ? -1 : 0;
+}
+
+int crypto_bignum_cmp(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b)
+{
+	return mbedtls_mpi_cmp_mpi((const mbedtls_mpi *)a, (const mbedtls_mpi *)b);
+}
+
+int crypto_bignum_is_zero(const struct crypto_bignum *a)
+{
+	/* XXX: src/common/sae.c:sswu() contains comment:
+	 * "TODO: Make sure crypto_bignum_is_zero() is constant time"
+	 * Note: mbedtls_mpi_cmp_int() *is not* constant time */
+	return (mbedtls_mpi_cmp_int((const mbedtls_mpi *)a, 0) == 0);
+}
+
+int crypto_bignum_is_one(const struct crypto_bignum *a)
+{
+	return (mbedtls_mpi_cmp_int((const mbedtls_mpi *)a, 1) == 0);
+}
+
+int crypto_bignum_is_odd(const struct crypto_bignum *a)
+{
+	return mbedtls_mpi_get_bit((const mbedtls_mpi *)a, 0);
+}
+
+#include "utils/const_time.h"
+int crypto_bignum_legendre(const struct crypto_bignum *a,
+			   const struct crypto_bignum *p)
+{
+	if (TEST_FAIL())
+		return -2;
+
+	/* Security Note:
+	 * mbedtls_mpi_exp_mod() is not documented to run in constant time,
+	 * though mbedtls/library/bignum.c uses constant_time_internal.h funcs.
+	 * Compare to crypto_openssl.c:crypto_bignum_legendre()
+	 * which uses openssl BN_mod_exp_mont_consttime()
+	 * mbedtls/library/ecp.c has further countermeasures to timing attacks,
+	 * (but ecp.c funcs are not used here) */
+
+	mbedtls_mpi exp, tmp;
+	mbedtls_mpi_init(&exp);
+	mbedtls_mpi_init(&tmp);
+
+	/* exp = (p-1) / 2 */
+	int res;
+	if (mbedtls_mpi_sub_int(&exp, (const mbedtls_mpi *)p, 1) == 0
+	    && mbedtls_mpi_shift_r(&exp, 1) == 0
+	    && mbedtls_mpi_exp_mod(&tmp, (const mbedtls_mpi *)a, &exp,
+	                           (const mbedtls_mpi *)p, NULL) == 0) {
+		/*(modified from crypto_openssl.c:crypto_bignum_legendre())*/
+		/* Return 1 if tmp == 1, 0 if tmp == 0, or -1 otherwise. Need
+		 * to use constant time selection to avoid branches here. */
+		unsigned int mask;
+		res = -1;
+		mask = const_time_eq((mbedtls_mpi_cmp_int(&tmp, 1) == 0), 1);
+		res = const_time_select_int(mask, 1, res);
+		mask = const_time_eq((mbedtls_mpi_cmp_int(&tmp, 0) == 0), 1);
+		res = const_time_select_int(mask, 0, res);
+	} else {
+		res = -2;
+	}
+
+	mbedtls_mpi_free(&tmp);
+	mbedtls_mpi_free(&exp);
+	return res;
+}
+
+#endif /* CRYPTO_MBEDTLS_CRYPTO_BIGNUM */
+
+
+#ifdef CRYPTO_MBEDTLS_CRYPTO_DH
+
+/* crypto_internal-modexp.c */
+
+#include <mbedtls/bignum.h>
+#include <mbedtls/dhm.h>
+
+#if 0 /* crypto_dh_init() and crypto_dh_derive_secret() prefer to use mbedtls */
+int crypto_mod_exp(const u8 *base, size_t base_len,
+		   const u8 *power, size_t power_len,
+		   const u8 *modulus, size_t modulus_len,
+		   u8 *result, size_t *result_len)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	mbedtls_mpi bn_base, bn_exp, bn_modulus, bn_result;
+	mbedtls_mpi_init(&bn_base);
+	mbedtls_mpi_init(&bn_exp);
+	mbedtls_mpi_init(&bn_modulus);
+	mbedtls_mpi_init(&bn_result);
+
+	size_t len;
+	int ret =  mbedtls_mpi_read_binary(&bn_base, base, base_len)
+	        || mbedtls_mpi_read_binary(&bn_exp, power, power_len)
+	        || mbedtls_mpi_read_binary(&bn_modulus, modulus, modulus_len)
+	        || mbedtls_mpi_exp_mod(&bn_result,&bn_base,&bn_exp,&bn_modulus,NULL)
+	        || (len = mbedtls_mpi_size(&bn_result)) > *result_len
+	        || mbedtls_mpi_write_binary(&bn_result, result, (*result_len = len))
+	  ? -1
+	  : 0;
+
+	mbedtls_mpi_free(&bn_base);
+	mbedtls_mpi_free(&bn_exp);
+	mbedtls_mpi_free(&bn_modulus);
+	mbedtls_mpi_free(&bn_result);
+	return ret;
+}
+#endif
+
+static int crypto_mbedtls_dh_set_bin_pg(mbedtls_dhm_context *ctx, u8 generator,
+                                        const u8 *prime, size_t prime_len)
+{
+	/*(could set these directly in MBEDTLS_PRIVATE members)*/
+	mbedtls_mpi P, G;
+	mbedtls_mpi_init(&P);
+	mbedtls_mpi_init(&G);
+	int ret = mbedtls_mpi_lset(&G, generator)
+	       || mbedtls_mpi_read_binary(&P, prime, prime_len)
+	       || mbedtls_dhm_set_group(ctx, &P, &G);
+	mbedtls_mpi_free(&P);
+	mbedtls_mpi_free(&G);
+	return ret;
+}
+
+__attribute_noinline__
+static int crypto_mbedtls_dh_init_public(mbedtls_dhm_context *ctx, u8 generator,
+                                         const u8 *prime, size_t prime_len,
+                                         u8 *privkey, u8 *pubkey)
+{
+	if (crypto_mbedtls_dh_set_bin_pg(ctx, generator, prime, prime_len)
+	    || mbedtls_dhm_make_public(ctx, (int)prime_len, pubkey, prime_len,
+	                               mbedtls_ctr_drbg_random,
+	                               crypto_mbedtls_ctr_drbg()))
+		return -1;
+
+  /*(enable later when upstream mbedtls interface changes require)*/
+  #if 0 && MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
+	mbedtls_mpi X;
+	mbedtls_mpi_init(&X);
+	int ret = mbedtls_dhm_get_value(ctx, MBEDTLS_DHM_PARAM_X, &X)
+	       || mbedtls_mpi_write_binary(&X, privkey, prime_len) ? -1 : 0;
+	mbedtls_mpi_free(&X);
+	return ret;
+  #else
+	return mbedtls_mpi_write_binary(&ctx->MBEDTLS_PRIVATE(X),
+	                                privkey, prime_len) ? -1 : 0;
+  #endif
+}
+
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+		   u8 *pubkey)
+{
+	if (TEST_FAIL())
+		return -1;
+
+  #if 0 /*(crypto_dh_init() duplicated (and identical) in crypto_*.c modules)*/
+	size_t pubkey_len, pad;
+
+	if (os_get_random(privkey, prime_len) < 0)
+		return -1;
+	if (os_memcmp(privkey, prime, prime_len) > 0) {
+		/* Make sure private value is smaller than prime */
+		privkey[0] = 0;
+	}
+
+	pubkey_len = prime_len;
+	if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
+			   pubkey, &pubkey_len) < 0)
+		return -1;
+	if (pubkey_len < prime_len) {
+		pad = prime_len - pubkey_len;
+		os_memmove(pubkey + pad, pubkey, pubkey_len);
+		os_memset(pubkey, 0, pad);
+	}
+
+	return 0;
+  #else
+	/* Prefer to use mbedtls to derive our public/private key, as doing so
+	 * leverages mbedtls to properly format output and to perform blinding*/
+	mbedtls_dhm_context ctx;
+	mbedtls_dhm_init(&ctx);
+	int ret = crypto_mbedtls_dh_init_public(&ctx, generator, prime,
+	                                        prime_len, privkey, pubkey);
+	mbedtls_dhm_free(&ctx);
+	return ret;
+  #endif
+}
+
+/*(crypto_dh_derive_secret() could be implemented using crypto.h APIs
+ * instead of being reimplemented in each crypto_*.c)*/
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+			    const u8 *order, size_t order_len,
+			    const u8 *privkey, size_t privkey_len,
+			    const u8 *pubkey, size_t pubkey_len,
+			    u8 *secret, size_t *len)
+{
+	if (TEST_FAIL())
+		return -1;
+
+  #if 0
+	if (pubkey_len > prime_len ||
+	    (pubkey_len == prime_len &&
+	     os_memcmp(pubkey, prime, prime_len) >= 0))
+		return -1;
+
+	int res = 0;
+	mbedtls_mpi pub;
+	mbedtls_mpi_init(&pub);
+	if (mbedtls_mpi_read_binary(&pub, pubkey, pubkey_len)
+	    || mbedtls_mpi_cmp_int(&pub, 1) <= 0) {
+		res = -1;
+	} else if (order) {
+		mbedtls_mpi p, q, tmp;
+		mbedtls_mpi_init(&p);
+		mbedtls_mpi_init(&q);
+		mbedtls_mpi_init(&tmp);
+
+		/* verify: pubkey^q == 1 mod p */
+		res = (mbedtls_mpi_read_binary(&p, prime, prime_len)
+		    || mbedtls_mpi_read_binary(&q, order, order_len)
+		    || mbedtls_mpi_exp_mod(&tmp, &pub, &q, &p, NULL)
+		    || mbedtls_mpi_cmp_int(&tmp, 1) != 0);
+
+		mbedtls_mpi_free(&p);
+		mbedtls_mpi_free(&q);
+		mbedtls_mpi_free(&tmp);
+	}
+	mbedtls_mpi_free(&pub);
+
+	return (res == 0)
+	  ? crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+			   prime, prime_len, secret, len)
+	  : -1;
+  #else
+	/* Prefer to use mbedtls to derive DH shared secret, as doing so
+	 * leverages mbedtls to validate params and to perform blinding.
+	 *
+	 * Attempt to reconstitute DH context to derive shared secret
+	 * (due to limitations of the interface, which ought to pass context).
+	 * Force provided G (our private key) into context without validation.
+	 * Regenerating GX (our public key) not needed to derive shared secret.
+	 */
+	/*(older compilers might not support VLAs)*/
+	/*unsigned char buf[2+prime_len+2+1+2+pubkey_len];*/
+	unsigned char buf[2+MBEDTLS_MPI_MAX_SIZE+2+1+2+MBEDTLS_MPI_MAX_SIZE];
+	unsigned char *p = buf + 2 + prime_len;
+	if (2+prime_len+2+1+2+pubkey_len > sizeof(buf))
+		return -1;
+	WPA_PUT_BE16(buf, prime_len);  /*(2-byte big-endian size of prime)*/
+	p[0] = 0;                      /*(2-byte big-endian size of generator)*/
+	p[1] = 1;
+	p[2] = generator;
+	WPA_PUT_BE16(p+3, pubkey_len); /*(2-byte big-endian size of pubkey)*/
+	os_memcpy(p+5, pubkey, pubkey_len);
+	os_memcpy(buf+2, prime, prime_len);
+
+	mbedtls_dhm_context ctx;
+	mbedtls_dhm_init(&ctx);
+	p = buf;
+	int ret = mbedtls_dhm_read_params(&ctx, &p, p+2+prime_len+5+pubkey_len)
+	       || mbedtls_mpi_read_binary(&ctx.MBEDTLS_PRIVATE(X),
+	                                  privkey, privkey_len)
+	       || mbedtls_dhm_calc_secret(&ctx, secret, *len, len,
+	                                  mbedtls_ctr_drbg_random,
+	                                  crypto_mbedtls_ctr_drbg()) ? -1 : 0;
+	mbedtls_dhm_free(&ctx);
+	return ret;
+  #endif
+}
+
+/* dh_group5.c */
+
+#include "dh_group5.h"
+
+/* RFC3526_PRIME_1536[] and RFC3526_GENERATOR_1536[] from crypto_wolfssl.c */
+
+static const unsigned char RFC3526_PRIME_1536[] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
+	0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+	0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
+	0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+	0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
+	0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+	0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
+	0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+	0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
+	0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+	0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36,
+	0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+	0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56,
+	0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+	0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08,
+	0xCA, 0x23, 0x73, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static const unsigned char RFC3526_GENERATOR_1536[] = {
+	0x02
+};
+
+void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
+{
+	const unsigned char * const prime = RFC3526_PRIME_1536;
+	const size_t prime_len = sizeof(RFC3526_PRIME_1536);
+	const u8 generator = *RFC3526_GENERATOR_1536;
+	struct wpabuf *wpubl = NULL, *wpriv = NULL;
+
+	mbedtls_dhm_context *ctx = os_malloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+	mbedtls_dhm_init(ctx);
+
+	if (   (wpubl = wpabuf_alloc(prime_len))
+	    && (wpriv = wpabuf_alloc(prime_len))
+	    && crypto_mbedtls_dh_init_public(ctx, generator, prime, prime_len,
+	                                     wpabuf_put(wpriv, prime_len),
+	                                     wpabuf_put(wpubl, prime_len))==0) {
+		wpabuf_free(*publ);
+		wpabuf_clear_free(*priv);
+		*publ = wpubl;
+		*priv = wpriv;
+		return ctx;
+	}
+
+	wpabuf_clear_free(wpriv);
+	wpabuf_free(wpubl);
+	mbedtls_dhm_free(ctx);
+	os_free(ctx);
+	return NULL;
+}
+
+#ifdef CRYPTO_MBEDTLS_DH5_INIT_FIXED
+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
+{
+	const unsigned char * const prime = RFC3526_PRIME_1536;
+	const size_t prime_len = sizeof(RFC3526_PRIME_1536);
+	const u8 generator = *RFC3526_GENERATOR_1536;
+
+	mbedtls_dhm_context *ctx = os_malloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+	mbedtls_dhm_init(ctx);
+
+	if (crypto_mbedtls_dh_set_bin_pg(ctx, generator, prime, prime_len)==0
+	   #if 0 /*(ignore; not required to derive shared secret)*/
+	    && mbedtls_mpi_read_binary(&ctx->MBEDTLS_PRIVATE(GX),
+				       wpabuf_head(publ),wpabuf_len(publ))==0
+	   #endif
+	    && mbedtls_mpi_read_binary(&ctx->MBEDTLS_PRIVATE(X),
+				       wpabuf_head(priv),wpabuf_len(priv))==0) {
+		return ctx;
+	}
+
+	mbedtls_dhm_free(ctx);
+	os_free(ctx);
+	return NULL;
+}
+#endif
+
+struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
+				  const struct wpabuf *own_private)
+{
+	/*((mbedtls_dhm_context *)ctx must already contain own_private)*/
+	/* mbedtls 2.x: prime_len = ctx->len; */
+	/* mbedtls 3.x: prime_len = mbedtls_dhm_get_len(ctx); */
+	size_t olen = sizeof(RFC3526_PRIME_1536); /*(sizeof(); prime known)*/
+	struct wpabuf *buf = wpabuf_alloc(olen);
+	if (buf == NULL)
+		return NULL;
+	if (mbedtls_dhm_read_public((mbedtls_dhm_context *)ctx,
+	                            wpabuf_head(peer_public),
+	                            wpabuf_len(peer_public)) == 0
+	    && mbedtls_dhm_calc_secret(ctx, wpabuf_mhead(buf), olen, &olen,
+	                               mbedtls_ctr_drbg_random,
+	                               crypto_mbedtls_ctr_drbg()) == 0) {
+		wpabuf_put(buf, olen);
+		return buf;
+	}
+
+	wpabuf_free(buf);
+	return NULL;
+}
+
+void dh5_free(void *ctx)
+{
+	mbedtls_dhm_free(ctx);
+	os_free(ctx);
+}
+
+#endif /* CRYPTO_MBEDTLS_CRYPTO_DH */
+
+
+#if defined(CRYPTO_MBEDTLS_CRYPTO_ECDH) || defined(CRYPTO_MBEDTLS_CRYPTO_EC)
+
+#include <mbedtls/ecp.h>
+
+#define CRYPTO_EC_pbits(e) (((mbedtls_ecp_group *)(e))->pbits)
+#define CRYPTO_EC_plen(e) ((((mbedtls_ecp_group *)(e))->pbits+7)>>3)
+#define CRYPTO_EC_P(e)    (&((mbedtls_ecp_group *)(e))->P)
+#define CRYPTO_EC_N(e)    (&((mbedtls_ecp_group *)(e))->N)
+#define CRYPTO_EC_A(e)    (&((mbedtls_ecp_group *)(e))->A)
+#define CRYPTO_EC_B(e)    (&((mbedtls_ecp_group *)(e))->B)
+#define CRYPTO_EC_G(e)    (&((mbedtls_ecp_group *)(e))->G)
+
+static mbedtls_ecp_group_id crypto_mbedtls_ecp_group_id_from_ike_id(int group)
+{
+	/* https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml */
+	switch (group) {
+  #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
+	case 19: return MBEDTLS_ECP_DP_SECP256R1;
+  #endif
+  #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
+	case 20: return MBEDTLS_ECP_DP_SECP384R1;
+  #endif
+  #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
+	case 21: return MBEDTLS_ECP_DP_SECP521R1;
+  #endif
+  #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
+	case 25: return MBEDTLS_ECP_DP_SECP192R1;
+  #endif
+  #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
+	case 26: return MBEDTLS_ECP_DP_SECP224R1;
+  #endif
+  #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
+	case 28: return MBEDTLS_ECP_DP_BP256R1;
+  #endif
+  #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
+	case 29: return MBEDTLS_ECP_DP_BP384R1;
+  #endif
+  #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
+	case 30: return MBEDTLS_ECP_DP_BP512R1;
+  #endif
+  #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
+	case 31: return MBEDTLS_ECP_DP_CURVE25519;
+  #endif
+  #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
+	case 32: return MBEDTLS_ECP_DP_CURVE448;
+  #endif
+	default: return MBEDTLS_ECP_DP_NONE;
+	}
+}
+
+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC
+static int crypto_mbedtls_ike_id_from_ecp_group_id(mbedtls_ecp_group_id grp_id)
+{
+	/* https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml */
+	/*(for crypto_ec_key_group())*/
+	switch (grp_id) {
+  #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
+	case MBEDTLS_ECP_DP_SECP256R1:  return 19;
+  #endif
+  #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
+	case MBEDTLS_ECP_DP_SECP384R1:  return 20;
+  #endif
+  #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
+	case MBEDTLS_ECP_DP_SECP521R1:  return 21;
+  #endif
+  #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
+	case MBEDTLS_ECP_DP_SECP192R1:  return 25;
+  #endif
+  #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
+	case MBEDTLS_ECP_DP_SECP224R1:  return 26;
+  #endif
+  #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
+	case MBEDTLS_ECP_DP_BP256R1:    return 28;
+  #endif
+  #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
+	case MBEDTLS_ECP_DP_BP384R1:    return 29;
+  #endif
+  #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
+	case MBEDTLS_ECP_DP_BP512R1:    return 30;
+  #endif
+  #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
+	case MBEDTLS_ECP_DP_CURVE25519: return 31;
+  #endif
+  #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
+	case MBEDTLS_ECP_DP_CURVE448:   return 32;
+  #endif
+	default: return -1;
+	}
+}
+#endif
+
+#endif /* CRYPTO_MBEDTLS_CRYPTO_ECDH || CRYPTO_MBEDTLS_CRYPTO_EC */
+
+
+#if defined(CRYPTO_MBEDTLS_CRYPTO_ECDH) || defined(CRYPTO_MBEDTLS_CRYPTO_EC_DPP)
+
+#include <mbedtls/ecp.h>
+#include <mbedtls/pk.h>
+
+static int crypto_mbedtls_keypair_gen(int group, mbedtls_pk_context *pk)
+{
+	mbedtls_ecp_group_id grp_id =
+	  crypto_mbedtls_ecp_group_id_from_ike_id(group);
+	if (grp_id == MBEDTLS_ECP_DP_NONE)
+		return -1;
+	const mbedtls_pk_info_t *pk_info =
+	  mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
+	if (pk_info == NULL)
+		return -1;
+	return mbedtls_pk_setup(pk, pk_info)
+	    || mbedtls_ecp_gen_key(grp_id, mbedtls_pk_ec(*pk),
+	                           mbedtls_ctr_drbg_random,
+	                           crypto_mbedtls_ctr_drbg()) ? -1 : 0;
+}
+
+#endif
+
+
+#ifdef CRYPTO_MBEDTLS_CRYPTO_ECDH
+
+#include <mbedtls/ecdh.h>
+#include <mbedtls/ecdsa.h>
+#include <mbedtls/ecp.h>
+#include <mbedtls/pk.h>
+
+/* wrap mbedtls_ecdh_context for more future-proof direct access to components
+ * (mbedtls_ecdh_context internal implementation may change between releases)
+ *
+ * If mbedtls_pk_context -- specifically underlying mbedtls_ecp_keypair --
+ * lifetime were guaranteed to be longer than that of mbedtls_ecdh_context,
+ * then mbedtls_pk_context or mbedtls_ecp_keypair could be stored in crypto_ecdh
+ * (or crypto_ec_key could be stored in crypto_ecdh, and crypto_ec_key could
+ *  wrap mbedtls_ecp_keypair and components, to avoid MBEDTLS_PRIVATE access) */
+struct crypto_ecdh {
+	mbedtls_ecdh_context ctx;
+	mbedtls_ecp_group grp;
+	mbedtls_ecp_point Q;
+};
+
+struct crypto_ecdh * crypto_ecdh_init(int group)
+{
+	mbedtls_pk_context pk;
+	mbedtls_pk_init(&pk);
+	struct crypto_ecdh *ecdh = crypto_mbedtls_keypair_gen(group, &pk) == 0
+	  ? crypto_ecdh_init2(group, (struct crypto_ec_key *)&pk)
+	  : NULL;
+	mbedtls_pk_free(&pk);
+	return ecdh;
+}
+
+struct crypto_ecdh * crypto_ecdh_init2(int group,
+				       struct crypto_ec_key *own_key)
+{
+	mbedtls_ecp_group_id grp_id =
+	  crypto_mbedtls_ecp_group_id_from_ike_id(group);
+	if (grp_id == MBEDTLS_ECP_DP_NONE)
+		return NULL;
+	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)own_key);
+	struct crypto_ecdh *ecdh = os_malloc(sizeof(*ecdh));
+	if (ecdh == NULL)
+		return NULL;
+	mbedtls_ecdh_init(&ecdh->ctx);
+	mbedtls_ecp_group_init(&ecdh->grp);
+	mbedtls_ecp_point_init(&ecdh->Q);
+	if (mbedtls_ecdh_setup(&ecdh->ctx, grp_id) == 0
+	    && mbedtls_ecdh_get_params(&ecdh->ctx,ecp_kp,MBEDTLS_ECDH_OURS) == 0) {
+		/* copy grp and Q for later use
+		 * (retrieving this info later is more convoluted
+		 *  even if mbedtls_ecdh_make_public() is considered)*/
+	  #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
+		mbedtls_mpi d;
+		mbedtls_mpi_init(&d);
+		if (mbedtls_ecp_export(ecp_kp, &ecdh->grp, &d, &ecdh->Q) == 0) {
+			mbedtls_mpi_free(&d);
+			return ecdh;
+		}
+		mbedtls_mpi_free(&d);
+	  #else
+		if (mbedtls_ecp_group_load(&ecdh->grp, grp_id) == 0
+		    && mbedtls_ecp_copy(&ecdh->Q, &ecp_kp->MBEDTLS_PRIVATE(Q)) == 0)
+			return ecdh;
+	  #endif
+	}
+
+	mbedtls_ecp_point_free(&ecdh->Q);
+	mbedtls_ecp_group_free(&ecdh->grp);
+	mbedtls_ecdh_free(&ecdh->ctx);
+	os_free(ecdh);
+	return NULL;
+}
+
+struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y)
+{
+	mbedtls_ecp_group *grp = &ecdh->grp;
+	size_t prime_len = CRYPTO_EC_plen(grp);
+	size_t output_len = prime_len;
+	u8 output_offset = 0;
+	u8 buf[256];
+
+  #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
+	/* len */
+  #endif
+  #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
+	if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
+		output_len = inc_y ? prime_len * 2 + 1 : prime_len + 1;
+		output_offset = 1;
+	}
+  #endif
+
+	if (output_len > sizeof(buf))
+		return NULL;
+
+	inc_y = inc_y ? MBEDTLS_ECP_PF_UNCOMPRESSED : MBEDTLS_ECP_PF_COMPRESSED;
+	if (mbedtls_ecp_point_write_binary(grp, &ecdh->Q, inc_y, &output_len,
+	                                   buf, output_len) == 0) {
+		return wpabuf_alloc_copy(buf + output_offset, output_len - output_offset);
+	}
+
+	return NULL;
+}
+
+#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED)
+static int crypto_mbedtls_short_weierstrass_derive_y(mbedtls_ecp_group *grp,
+                                                     mbedtls_mpi *bn,
+                                                     int parity_bit)
+{
+	/* y^2 = x^3 + ax + b
+	 * sqrt(w) = w^((p+1)/4) mod p   (for prime p where p = 3 mod 4) */
+	mbedtls_mpi *cy2 = (mbedtls_mpi *)
+	  crypto_ec_point_compute_y_sqr((struct crypto_ec *)grp,
+	                                (const struct crypto_bignum *)bn); /*x*/
+	if (cy2 == NULL)
+		return -1;
+
+	/*mbedtls_mpi_free(bn);*/
+	/*(reuse bn to store result (y))*/
+
+	mbedtls_mpi exp;
+	mbedtls_mpi_init(&exp);
+	int ret = mbedtls_mpi_get_bit(&grp->P, 0) != 1 /*(p = 3 mod 4)*/
+	       || mbedtls_mpi_get_bit(&grp->P, 1) != 1 /*(p = 3 mod 4)*/
+	       || mbedtls_mpi_add_int(&exp, &grp->P, 1)
+	       || mbedtls_mpi_shift_r(&exp, 2)
+	       || mbedtls_mpi_exp_mod(bn, cy2, &exp, &grp->P, NULL)
+	       || (mbedtls_mpi_get_bit(bn, 0) != parity_bit
+	           && mbedtls_mpi_sub_mpi(bn, &grp->P, bn));
+	mbedtls_mpi_free(&exp);
+	mbedtls_mpi_free(cy2);
+	os_free(cy2);
+	return ret;
+}
+#endif
+
+struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
+					const u8 *key, size_t len)
+{
+	if (len == 0) /*(invalid peer key)*/
+		return NULL;
+
+	mbedtls_ecp_group *grp = &ecdh->grp;
+
+  #if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED)
+	if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
+		/* add header for mbedtls_ecdh_read_public() */
+		u8 buf[256];
+		if (sizeof(buf)-1 < len)
+			return NULL;
+		buf[0] = (u8)(len);
+		os_memcpy(buf+1, key, len);
+
+		if (inc_y) {
+			if (!(len & 1)) { /*(dpp code/tests does not include tag?!?)*/
+				if (sizeof(buf)-2 < len)
+					return NULL;
+				buf[0] = (u8)(1+len);
+				buf[1] = 0x04;
+				os_memcpy(buf+2, key, len);
+			}
+			len >>= 1; /*(repurpose len to prime_len)*/
+		} else { /* (inc_y == 0) */
+			/* mbedtls_ecp_point_read_binary() does not currently support
+			 * MBEDTLS_ECP_PF_COMPRESSED format (buf[1] = 0x02 or 0x03)
+			 * (returns MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) */
+
+			/* derive y, amend buf[] with y for UNCOMPRESSED format */
+			if (sizeof(buf)-2 < len*2 || len == 0)
+				return NULL;
+
+			buf[0] = (u8)(1+len*2);
+			buf[1] = 0x04;
+			os_memcpy(buf+2, key, len);
+
+			mbedtls_mpi bn;
+			mbedtls_mpi_init(&bn);
+			int ret = mbedtls_mpi_read_binary(&bn, key, len)
+			       || crypto_mbedtls_short_weierstrass_derive_y(grp, &bn, 0)
+			       || mbedtls_mpi_write_binary(&bn, buf+2+len, len);
+			mbedtls_mpi_free(&bn);
+			if (ret != 0)
+				return NULL;
+		}
+
+		if (mbedtls_ecdh_read_public(&ecdh->ctx, buf, buf[0]+1))
+			return NULL;
+	}
+  #endif
+  #if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED)
+	if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) {
+		if (mbedtls_ecdh_read_public(&ecdh->ctx, key, len))
+			return NULL;
+	}
+  #endif
+
+	struct wpabuf *buf = wpabuf_alloc(len);
+	if (buf == NULL)
+		return NULL;
+
+	if (mbedtls_ecdh_calc_secret(&ecdh->ctx, &len,
+	                             wpabuf_mhead(buf), len,
+	                             mbedtls_ctr_drbg_random,
+	                             crypto_mbedtls_ctr_drbg()) == 0) {
+		wpabuf_put(buf, len);
+		return buf;
+	}
+
+	wpabuf_clear_free(buf);
+	return NULL;
+}
+
+void crypto_ecdh_deinit(struct crypto_ecdh *ecdh)
+{
+	if (ecdh == NULL)
+		return;
+	mbedtls_ecp_point_free(&ecdh->Q);
+	mbedtls_ecp_group_free(&ecdh->grp);
+	mbedtls_ecdh_free(&ecdh->ctx);
+	os_free(ecdh);
+}
+
+size_t crypto_ecdh_prime_len(struct crypto_ecdh *ecdh)
+{
+	return CRYPTO_EC_plen(&ecdh->grp);
+}
+
+#endif /* CRYPTO_MBEDTLS_CRYPTO_ECDH */
+
+
+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC
+
+#include <mbedtls/ecp.h>
+
+struct crypto_ec *crypto_ec_init(int group)
+{
+	mbedtls_ecp_group_id grp_id =
+	  crypto_mbedtls_ecp_group_id_from_ike_id(group);
+	if (grp_id == MBEDTLS_ECP_DP_NONE)
+		return NULL;
+	mbedtls_ecp_group *e = os_malloc(sizeof(*e));
+	if (e == NULL)
+		return NULL;
+	mbedtls_ecp_group_init(e);
+	if (mbedtls_ecp_group_load(e, grp_id) == 0)
+		return (struct crypto_ec *)e;
+
+	mbedtls_ecp_group_free(e);
+	os_free(e);
+	return NULL;
+}
+
+void crypto_ec_deinit(struct crypto_ec *e)
+{
+	mbedtls_ecp_group_free((mbedtls_ecp_group *)e);
+	os_free(e);
+}
+
+size_t crypto_ec_prime_len(struct crypto_ec *e)
+{
+	return CRYPTO_EC_plen(e);
+}
+
+size_t crypto_ec_prime_len_bits(struct crypto_ec *e)
+{
+	return CRYPTO_EC_pbits(e);
+}
+
+size_t crypto_ec_order_len(struct crypto_ec *e)
+{
+	return (mbedtls_mpi_bitlen(CRYPTO_EC_N(e)) + 7) / 8;
+}
+
+const struct crypto_bignum *crypto_ec_get_prime(struct crypto_ec *e)
+{
+	return (const struct crypto_bignum *)CRYPTO_EC_P(e);
+}
+
+const struct crypto_bignum *crypto_ec_get_order(struct crypto_ec *e)
+{
+	return (const struct crypto_bignum *)CRYPTO_EC_N(e);
+}
+
+const struct crypto_bignum *crypto_ec_get_a(struct crypto_ec *e)
+{
+	static const uint8_t secp256r1_a[] =
+	  {0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x01,
+	   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+	   0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,
+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc};
+	static const uint8_t secp384r1_a[] =
+	  {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
+	   0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,
+	   0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xfc};
+	static const uint8_t secp521r1_a[] =
+	  {0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+	   0xff,0xfc};
+	static const uint8_t secp192r1_a[] =
+	  {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc};
+	static const uint8_t secp224r1_a[] =
+	  {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+	   0xff,0xff,0xff,0xfe};
+
+	const uint8_t *bin = NULL;
+	size_t len = 0;
+
+	/* (mbedtls groups matching supported sswu_curve_param() IKE groups) */
+	switch (((mbedtls_ecp_group *)e)->id) {
+  #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
+	case MBEDTLS_ECP_DP_SECP256R1:
+		bin = secp256r1_a;
+		len = sizeof(secp256r1_a);
+		break;
+  #endif
+  #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
+	case MBEDTLS_ECP_DP_SECP384R1:
+		bin = secp384r1_a;
+		len = sizeof(secp384r1_a);
+		break;
+  #endif
+  #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
+	case MBEDTLS_ECP_DP_SECP521R1:
+		bin = secp521r1_a;
+		len = sizeof(secp521r1_a);
+		break;
+  #endif
+  #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED
+	case MBEDTLS_ECP_DP_SECP192R1:
+		bin = secp192r1_a;
+		len = sizeof(secp192r1_a);
+		break;
+  #endif
+  #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED
+	case MBEDTLS_ECP_DP_SECP224R1:
+		bin = secp224r1_a;
+		len = sizeof(secp224r1_a);
+		break;
+  #endif
+  #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED
+	case MBEDTLS_ECP_DP_BP256R1:
+		return (const struct crypto_bignum *)CRYPTO_EC_A(e);
+  #endif
+  #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED
+	case MBEDTLS_ECP_DP_BP384R1:
+		return (const struct crypto_bignum *)CRYPTO_EC_A(e);
+  #endif
+  #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED
+	case MBEDTLS_ECP_DP_BP512R1:
+		return (const struct crypto_bignum *)CRYPTO_EC_A(e);
+  #endif
+  #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
+	case MBEDTLS_ECP_DP_CURVE25519:
+		return (const struct crypto_bignum *)CRYPTO_EC_A(e);
+  #endif
+  #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
+	case MBEDTLS_ECP_DP_CURVE448:
+		return (const struct crypto_bignum *)CRYPTO_EC_A(e);
+  #endif
+	default:
+		return NULL;
+	}
+
+	/*(note: not thread-safe; returns file-scoped static storage)*/
+	if (mbedtls_mpi_read_binary(&mpi_sw_A, bin, len) == 0)
+		return (const struct crypto_bignum *)&mpi_sw_A;
+	return NULL;
+}
+
+const struct crypto_bignum *crypto_ec_get_b(struct crypto_ec *e)
+{
+	return (const struct crypto_bignum *)CRYPTO_EC_B(e);
+}
+
+const struct crypto_ec_point * crypto_ec_get_generator(struct crypto_ec *e)
+{
+	return (const struct crypto_ec_point *)CRYPTO_EC_G(e);
+}
+
+struct crypto_ec_point *crypto_ec_point_init(struct crypto_ec *e)
+{
+	if (TEST_FAIL())
+		return NULL;
+
+	mbedtls_ecp_point *p = os_malloc(sizeof(*p));
+	if (p != NULL)
+		mbedtls_ecp_point_init(p);
+	return (struct crypto_ec_point *)p;
+}
+
+void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear)
+{
+	mbedtls_ecp_point_free((mbedtls_ecp_point *)p);
+	os_free(p);
+}
+
+int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
+		      struct crypto_bignum *x)
+{
+	mbedtls_mpi *px = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
+	return mbedtls_mpi_copy((mbedtls_mpi *)x, px)
+	  ? -1
+	  : 0;
+}
+
+int crypto_ec_point_to_bin(struct crypto_ec *e,
+			   const struct crypto_ec_point *point, u8 *x, u8 *y)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	/* crypto.h documents crypto_ec_point_to_bin() output is big-endian */
+	size_t len = CRYPTO_EC_plen(e);
+	if (x) {
+		mbedtls_mpi *px = &((mbedtls_ecp_point *)point)->MBEDTLS_PRIVATE(X);
+		if (mbedtls_mpi_write_binary(px, x, len))
+			return -1;
+	}
+	if (y) {
+	  #if 0 /*(should not be necessary; py mpi should be in initial state)*/
+	  #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
+		if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
+		    == MBEDTLS_ECP_TYPE_MONTGOMERY) {
+			os_memset(y, 0, len);
+			return 0;
+		}
+	  #endif
+	  #endif
+		mbedtls_mpi *py = &((mbedtls_ecp_point *)point)->MBEDTLS_PRIVATE(Y);
+		if (mbedtls_mpi_write_binary(py, y, len))
+			return -1;
+	}
+	return 0;
+}
+
+struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
+						  const u8 *val)
+{
+	if (TEST_FAIL())
+		return NULL;
+
+	size_t len = CRYPTO_EC_plen(e);
+	mbedtls_ecp_point *p = os_malloc(sizeof(*p));
+	u8 buf[1+MBEDTLS_MPI_MAX_SIZE*2];
+	if (p == NULL)
+		return NULL;
+	mbedtls_ecp_point_init(p);
+
+  #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
+	if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
+	    == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
+	  #if 0 /* prefer alternative to MBEDTLS_PRIVATE() access */
+		mbedtls_mpi *px = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
+		mbedtls_mpi *py = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
+		mbedtls_mpi *pz = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Z);
+
+		if (mbedtls_mpi_read_binary(px, val, len) == 0
+		    && mbedtls_mpi_read_binary(py, val + len, len) == 0
+		    && mbedtls_mpi_lset(pz, 1) == 0)
+			return (struct crypto_ec_point *)p;
+	  #else
+		buf[0] = 0x04;
+		os_memcpy(buf+1, val, len*2);
+		if (mbedtls_ecp_point_read_binary((mbedtls_ecp_group *)e, p,
+		                                  buf, 1+len*2) == 0)
+			return (struct crypto_ec_point *)p;
+	  #endif
+	}
+  #endif
+  #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
+	if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
+	    == MBEDTLS_ECP_TYPE_MONTGOMERY) {
+		/* crypto.h interface documents crypto_ec_point_from_bin()
+		 * val is length: prime_len * 2 and is big-endian
+		 * (Short Weierstrass is assumed by hostap)
+		 * Reverse to little-endian format for Montgomery */
+		for (unsigned int i = 0; i < len; ++i)
+			buf[i] = val[len-1-i];
+		if (mbedtls_ecp_point_read_binary((mbedtls_ecp_group *)e, p,
+		                                  buf, len) == 0)
+			return (struct crypto_ec_point *)p;
+	}
+  #endif
+
+	mbedtls_ecp_point_free(p);
+	os_free(p);
+	return NULL;
+}
+
+int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
+			const struct crypto_ec_point *b,
+			struct crypto_ec_point *c)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	/* mbedtls does not provide an mbedtls_ecp_point add function */
+	mbedtls_mpi one;
+	mbedtls_mpi_init(&one);
+	int ret = mbedtls_mpi_lset(&one, 1)
+	       || mbedtls_ecp_muladd(
+			(mbedtls_ecp_group *)e, (mbedtls_ecp_point *)c,
+			&one, (const mbedtls_ecp_point *)a,
+			&one, (const mbedtls_ecp_point *)b) ? -1 : 0;
+	mbedtls_mpi_free(&one);
+	return ret;
+}
+
+int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
+			const struct crypto_bignum *b,
+			struct crypto_ec_point *res)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	return mbedtls_ecp_mul(
+		(mbedtls_ecp_group *)e, (mbedtls_ecp_point *)res,
+		(const mbedtls_mpi *)b, (const mbedtls_ecp_point *)p,
+		mbedtls_ctr_drbg_random, crypto_mbedtls_ctr_drbg()) ? -1 : 0;
+}
+
+int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
+	    == MBEDTLS_ECP_TYPE_MONTGOMERY) {
+		/* e.g. MBEDTLS_ECP_DP_CURVE25519 and MBEDTLS_ECP_DP_CURVE448 */
+		wpa_printf(MSG_ERROR,
+		           "%s not implemented for Montgomery curves",__func__);
+		return -1;
+	}
+
+	/* mbedtls does not provide an mbedtls_ecp_point invert function */
+	/* below works for Short Weierstrass; incorrect for Montgomery curves */
+	mbedtls_mpi *py = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
+	return mbedtls_ecp_is_zero((mbedtls_ecp_point *)p) /*point at infinity*/
+	    || mbedtls_mpi_cmp_int(py, 0) == 0      /*point is its own inverse*/
+	    || mbedtls_mpi_sub_abs(py, CRYPTO_EC_P(e), py) == 0 ? 0 : -1;
+}
+
+#ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
+static int
+crypto_ec_point_y_sqr_weierstrass(mbedtls_ecp_group *e, const mbedtls_mpi *x,
+                                  mbedtls_mpi *y2)
+{
+	/* MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS  y^2 = x^3 + a x + b    */
+
+	/* Short Weierstrass elliptic curve group w/o A set treated as A = -3 */
+	/* Attempt to match mbedtls/library/ecp.c:ecp_check_pubkey_sw() behavior
+	 * and elsewhere in mbedtls/library/ecp.c where if A is not set, it is
+	 * treated as if A = -3. */
+
+  #if 0
+	/* y^2 = x^3 + ax + b */
+	mbedtls_mpi *A = &e->A;
+	mbedtls_mpi t, A_neg3;
+	if (&e->A.p == NULL) {
+		mbedtls_mpi_init(&A_neg3);
+		if (mbedtls_mpi_lset(&A_neg3, -3) != 0) {
+			mbedtls_mpi_free(&A_neg3);
+			return -1;
+		}
+		A = &A_neg3;
+	}
+	mbedtls_mpi_init(&t);
+	int ret = /* x^3 */
+	          mbedtls_mpi_lset(&t, 3)
+	       || mbedtls_mpi_exp_mod(y2,  x, &t, &e->P, NULL)
+		  /* ax */
+	       || mbedtls_mpi_mul_mpi(y2, y2, A)
+	       || mbedtls_mpi_mod_mpi(&t, &t, &e->P)
+		  /* ax + b */
+	       || mbedtls_mpi_add_mpi(&t, &t, &e->B)
+	       || mbedtls_mpi_mod_mpi(&t, &t, &e->P)
+		  /* x^3 + ax + b */
+	       || mbedtls_mpi_add_mpi(&t, &t, y2) /* ax + b + x^3 */
+	       || mbedtls_mpi_mod_mpi(y2, &t, &e->P);
+	mbedtls_mpi_free(&t);
+	if (A == &A_neg3)
+		mbedtls_mpi_free(&A_neg3);
+	return ret; /* 0: success, non-zero: failure */
+  #else
+	/* y^2 = x^3 + ax + b = (x^2 + a)x + b */
+	return    /* x^2 */
+	          mbedtls_mpi_mul_mpi(y2,  x, x)
+	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
+		  /* x^2 + a */
+	       || (e->A.MBEDTLS_PRIVATE(p)
+	           ? mbedtls_mpi_add_mpi(y2, y2, &e->A)
+	           : mbedtls_mpi_sub_int(y2, y2, 3))
+	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
+		  /* (x^2 + a)x */
+	       || mbedtls_mpi_mul_mpi(y2, y2, x)
+	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
+		  /* (x^2 + a)x + b */
+	       || mbedtls_mpi_add_mpi(y2, y2, &e->B)
+	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P);
+  #endif
+}
+#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */
+
+#if 0 /* not used by hostap */
+#ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
+static int
+crypto_ec_point_y_sqr_montgomery(mbedtls_ecp_group *e, const mbedtls_mpi *x,
+                                 mbedtls_mpi *y2)
+{
+	/* XXX: !!! must be reviewed and audited for correctness !!! */
+
+	/* MBEDTLS_ECP_TYPE_MONTGOMERY         y^2 = x^3 + a x^2 + x  */
+
+	/* y^2 = x^3 + a x^2 + x = (x + a)x^2 + x */
+	mbedtls_mpi x2;
+	mbedtls_mpi_init(&x2);
+	int ret = /* x^2 */
+	          mbedtls_mpi_mul_mpi(&x2, x, x)
+	       || mbedtls_mpi_mod_mpi(&x2, &x2, &e->P)
+		  /* x + a */
+	       || mbedtls_mpi_add_mpi(y2,  x, &e->A)
+	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
+		  /* (x + a)x^2 */
+	       || mbedtls_mpi_mul_mpi(y2, y2, &x2)
+	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P)
+		  /* (x + a)x^2 + x */
+	       || mbedtls_mpi_add_mpi(y2, y2, x)
+	       || mbedtls_mpi_mod_mpi(y2, y2, &e->P);
+	mbedtls_mpi_free(&x2);
+	return ret; /* 0: success, non-zero: failure */
+}
+#endif /* MBEDTLS_ECP_MONTGOMERY_ENABLED */
+#endif
+
+struct crypto_bignum *
+crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
+			      const struct crypto_bignum *x)
+{
+	if (TEST_FAIL())
+		return NULL;
+
+	mbedtls_mpi *y2 = os_malloc(sizeof(*y2));
+	if (y2 == NULL)
+		return NULL;
+	mbedtls_mpi_init(y2);
+
+  #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
+	if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
+	      == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS
+	    && crypto_ec_point_y_sqr_weierstrass((mbedtls_ecp_group *)e,
+	                                         (const mbedtls_mpi *)x,
+	                                         y2) == 0)
+		return (struct crypto_bignum *)y2;
+  #endif
+  #if 0 /* not used by hostap */
+  #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
+	if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e)
+	      == MBEDTLS_ECP_TYPE_MONTGOMERY
+	    && crypto_ec_point_y_sqr_montgomery((mbedtls_ecp_group *)e,
+	                                        (const mbedtls_mpi *)x,
+	                                        y2) == 0)
+		return (struct crypto_bignum *)y2;
+  #endif
+  #endif
+
+	mbedtls_mpi_free(y2);
+	os_free(y2);
+	return NULL;
+}
+
+int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
+				   const struct crypto_ec_point *p)
+{
+	return mbedtls_ecp_is_zero((mbedtls_ecp_point *)p);
+}
+
+int crypto_ec_point_is_on_curve(struct crypto_ec *e,
+				const struct crypto_ec_point *p)
+{
+  #if 1
+	return mbedtls_ecp_check_pubkey((const mbedtls_ecp_group *)e,
+	                                (const mbedtls_ecp_point *)p) == 0;
+  #else
+	/* compute y^2 mod P and compare to y^2 mod P */
+	/*(ref: src/eap_common/eap_pwd_common.c:compute_password_element())*/
+	const mbedtls_mpi *px = &((const mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X);
+	mbedtls_mpi *cy2 = (mbedtls_mpi *)
+	  crypto_ec_point_compute_y_sqr(e, (const struct crypto_bignum *)px);
+	if (cy2 == NULL)
+		return 0;
+
+	mbedtls_mpi y2;
+	mbedtls_mpi_init(&y2);
+	const mbedtls_mpi *py = &((const mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y);
+	int is_on_curve = mbedtls_mpi_mul_mpi(&y2, py, py) /* y^2 mod P */
+	               || mbedtls_mpi_mod_mpi(&y2, &y2, CRYPTO_EC_P(e))
+	               || mbedtls_mpi_cmp_mpi(&y2, cy2) != 0 ? 0 : 1;
+
+	mbedtls_mpi_free(&y2);
+	mbedtls_mpi_free(cy2);
+	os_free(cy2);
+	return is_on_curve;
+  #endif
+}
+
+int crypto_ec_point_cmp(const struct crypto_ec *e,
+			const struct crypto_ec_point *a,
+			const struct crypto_ec_point *b)
+{
+	return mbedtls_ecp_point_cmp((const mbedtls_ecp_point *)a,
+	                             (const mbedtls_ecp_point *)b);
+}
+
+#if !defined(CONFIG_NO_STDOUT_DEBUG)
+void crypto_ec_point_debug_print(const struct crypto_ec *e,
+				 const struct crypto_ec_point *p,
+				 const char *title)
+{
+	u8 x[MBEDTLS_MPI_MAX_SIZE];
+	u8 y[MBEDTLS_MPI_MAX_SIZE];
+	size_t len = CRYPTO_EC_plen(e);
+	/* crypto_ec_point_to_bin ought to take (const struct crypto_ec *e) */
+	struct crypto_ec *ee;
+	*(const struct crypto_ec **)&ee = e; /*(cast away const)*/
+	if (crypto_ec_point_to_bin(ee, p, x, y) == 0) {
+		if (title)
+			wpa_printf(MSG_DEBUG, "%s", title);
+		wpa_hexdump(MSG_DEBUG, "x:", x, len);
+		wpa_hexdump(MSG_DEBUG, "y:", y, len);
+	}
+}
+#endif
+
+
+struct crypto_ec_key * crypto_ec_key_parse_priv(const u8 *der, size_t der_len)
+{
+	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+	mbedtls_pk_init(ctx);
+  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
+	if (mbedtls_pk_parse_key(ctx, der, der_len, NULL, 0) == 0)
+  #else
+	if (mbedtls_pk_parse_key(ctx, der, der_len, NULL, 0,
+	                         mbedtls_ctr_drbg_random,
+	                         crypto_mbedtls_ctr_drbg()) == 0)
+  #endif
+		return (struct crypto_ec_key *)ctx;
+
+	mbedtls_pk_free(ctx);
+	os_free(ctx);
+	return NULL;
+}
+
+#ifdef CRYPTO_MBEDTLS_CRYPTO_HPKE
+#ifdef CONFIG_MODULE_TESTS
+/*(for crypto_module_tests.c)*/
+struct crypto_ec_key * crypto_ec_key_set_priv(int group,
+					      const u8 *raw, size_t raw_len)
+{
+	mbedtls_ecp_group_id grp_id =
+	  crypto_mbedtls_ecp_group_id_from_ike_id(group);
+	if (grp_id == MBEDTLS_ECP_DP_NONE)
+		return NULL;
+	const mbedtls_pk_info_t *pk_info =
+	  mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
+	if (pk_info == NULL)
+		return NULL;
+	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+	mbedtls_pk_init(ctx);
+	if (mbedtls_pk_setup(ctx, pk_info) == 0
+	    && mbedtls_ecp_read_key(grp_id,mbedtls_pk_ec(*ctx),raw,raw_len) == 0) {
+		return (struct crypto_ec_key *)ctx;
+	}
+
+	mbedtls_pk_free(ctx);
+	os_free(ctx);
+	return NULL;
+}
+#endif
+#endif
+
+#include <mbedtls/error.h>
+#include <mbedtls/oid.h>
+static int crypto_mbedtls_pk_parse_subpubkey_compressed(mbedtls_pk_context *ctx, const u8 *der, size_t der_len)
+{
+    /* The following is modified from:
+     *   mbedtls/library/pkparse.c:mbedtls_pk_parse_subpubkey()
+     *   mbedtls/library/pkparse.c:pk_get_pk_alg()
+     *   mbedtls/library/pkparse.c:pk_use_ecparams()
+     */
+    mbedtls_pk_type_t pk_alg = MBEDTLS_PK_NONE;
+    const mbedtls_pk_info_t *pk_info;
+    int ret;
+    size_t len;
+    const unsigned char *end = der+der_len;
+    unsigned char *p;
+    *(const unsigned char **)&p = der;
+
+    if( ( ret = mbedtls_asn1_get_tag( &p, end, &len,
+                    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 )
+    {
+        return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret ) );
+    }
+
+    end = p + len;
+
+    /*
+    if( ( ret = pk_get_pk_alg( &p, end, &pk_alg, &alg_params ) ) != 0 )
+        return( ret );
+    */
+    mbedtls_asn1_buf alg_oid, params;
+    memset( &params, 0, sizeof(mbedtls_asn1_buf) );
+    if( ( ret = mbedtls_asn1_get_alg( &p, end, &alg_oid, &params ) ) != 0 )
+        return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_ALG, ret ) );
+    if( mbedtls_oid_get_pk_alg( &alg_oid, &pk_alg ) != 0 )
+        return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG );
+
+    if( ( ret = mbedtls_asn1_get_bitstring_null( &p, end, &len ) ) != 0 )
+        return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_PUBKEY, ret ) );
+
+    if( p + len != end )
+        return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_PUBKEY,
+                MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ) );
+
+    if( ( pk_info = mbedtls_pk_info_from_type( pk_alg ) ) == NULL )
+        return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG );
+
+    if( ( ret = mbedtls_pk_setup( ctx, pk_info ) ) != 0 )
+        return( ret );
+
+    /* assume mbedtls_pk_parse_subpubkey(&der, der+der_len, ctx)
+     * has already run with ctx initialized up to pk_get_ecpubkey(),
+     * and pk_get_ecpubkey() has returned MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE
+     *
+     * mbedtls mbedtls_ecp_point_read_binary()
+     * does not handle point in COMPRESSED format
+     *
+     * (validate assumption that algorithm is EC) */
+    mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*ctx);
+    if (ecp_kp == NULL)
+        return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE );
+    mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
+    mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
+    mbedtls_ecp_group_id grp_id;
+
+
+    /* mbedtls/library/pkparse.c:pk_use_ecparams() */
+
+    if( params.tag == MBEDTLS_ASN1_OID )
+    {
+        if( mbedtls_oid_get_ec_grp( &params, &grp_id ) != 0 )
+            return( MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE );
+    }
+    else
+    {
+#if defined(MBEDTLS_PK_PARSE_EC_EXTENDED)
+        /*(large code block not copied from mbedtls; unsupported)*/
+      #if 0
+        if( ( ret = pk_group_id_from_specified( &params, &grp_id ) ) != 0 )
+            return( ret );
+      #endif
+#endif
+        return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT );
+    }
+
+    /*
+     * grp may already be initialized; if so, make sure IDs match
+     */
+    if( ecp_kp_grp->id != MBEDTLS_ECP_DP_NONE && ecp_kp_grp->id != grp_id )
+        return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT );
+
+    if( ( ret = mbedtls_ecp_group_load( ecp_kp_grp, grp_id ) ) != 0 )
+        return( ret );
+
+
+    /* (validate assumption that EC point is in COMPRESSED format) */
+    len = CRYPTO_EC_plen(ecp_kp_grp);
+    if( mbedtls_ecp_get_type(ecp_kp_grp) != MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS
+        || (end - p) != 1+len
+        || (*p != 0x02 && *p != 0x03) )
+        return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE );
+
+    /* Instead of calling mbedtls/library/pkparse.c:pk_get_ecpubkey() to call
+     * mbedtls_ecp_point_read_binary(), manually parse point into ecp_kp_Q */
+    mbedtls_mpi *X = &ecp_kp_Q->MBEDTLS_PRIVATE(X);
+    mbedtls_mpi *Y = &ecp_kp_Q->MBEDTLS_PRIVATE(Y);
+    mbedtls_mpi *Z = &ecp_kp_Q->MBEDTLS_PRIVATE(Z);
+    ret = mbedtls_mpi_lset(Z, 1);
+    if (ret != 0)
+        return( ret );
+    ret = mbedtls_mpi_read_binary(X, p+1, len);
+    if (ret != 0)
+        return( ret );
+    /* derive Y
+     * (similar derivation of Y in crypto_mbedtls.c:crypto_ecdh_set_peerkey())*/
+    ret = mbedtls_mpi_copy(Y, X) /*(Y is used as input and output obj below)*/
+       || crypto_mbedtls_short_weierstrass_derive_y(ecp_kp_grp, Y, (*p & 1));
+    if (ret != 0)
+        return( ret );
+
+    return mbedtls_ecp_check_pubkey( ecp_kp_grp, ecp_kp_Q );
+}
+
+struct crypto_ec_key * crypto_ec_key_parse_pub(const u8 *der, size_t der_len)
+{
+	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+	mbedtls_pk_init(ctx);
+	/*int rc = mbedtls_pk_parse_subpubkey(&der, der+der_len, ctx);*/
+	int rc = mbedtls_pk_parse_public_key(ctx, der, der_len);
+	if (rc == 0)
+		return (struct crypto_ec_key *)ctx;
+	else if (rc == MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) {
+		/* mbedtls mbedtls_ecp_point_read_binary()
+		 * does not handle point in COMPRESSED format; parse internally */
+		rc = crypto_mbedtls_pk_parse_subpubkey_compressed(ctx,der,der_len);
+		if (rc == 0)
+			return (struct crypto_ec_key *)ctx;
+	}
+
+	mbedtls_pk_free(ctx);
+	os_free(ctx);
+	return NULL;
+}
+
+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
+
+static struct crypto_ec_key *
+crypto_ec_key_set_pub_point_for_group(mbedtls_ecp_group_id grp_id,
+                                      const mbedtls_ecp_point *pub,
+                                      const u8 *buf, size_t len)
+{
+	const mbedtls_pk_info_t *pk_info =
+	  mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
+	if (pk_info == NULL)
+		return NULL;
+	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+	mbedtls_pk_init(ctx);
+	if (mbedtls_pk_setup(ctx, pk_info) == 0) {
+		/* (Is private key generation necessary for callers?)
+		 * alt: gen key then overwrite Q
+		 *   mbedtls_ecp_gen_key(grp_id, ecp_kp,
+	         *                       mbedtls_ctr_drbg_random,
+	         *                       crypto_mbedtls_ctr_drbg()) == 0
+	         */
+		mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*ctx);
+		mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
+		mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
+		mbedtls_mpi *ecp_kp_d = &ecp_kp->MBEDTLS_PRIVATE(d);
+		if (mbedtls_ecp_group_load(ecp_kp_grp, grp_id) == 0
+		    && (pub
+		         ? mbedtls_ecp_copy(ecp_kp_Q, pub) == 0
+		         : mbedtls_ecp_point_read_binary(ecp_kp_grp, ecp_kp_Q,
+		                                         buf, len) == 0)
+		    && mbedtls_ecp_gen_privkey(ecp_kp_grp, ecp_kp_d,
+		                               mbedtls_ctr_drbg_random,
+		                               crypto_mbedtls_ctr_drbg()) == 0){
+			return (struct crypto_ec_key *)ctx;
+		}
+	}
+
+	mbedtls_pk_free(ctx);
+	os_free(ctx);
+	return NULL;
+}
+
+struct crypto_ec_key * crypto_ec_key_set_pub(int group, const u8 *x,
+					     const u8 *y, size_t len)
+{
+	mbedtls_ecp_group_id grp_id =
+	  crypto_mbedtls_ecp_group_id_from_ike_id(group);
+	if (grp_id == MBEDTLS_ECP_DP_NONE)
+		return NULL;
+	if (len > MBEDTLS_MPI_MAX_SIZE)
+		return NULL;
+	u8 buf[1+MBEDTLS_MPI_MAX_SIZE*2];
+	buf[0] = 0x04; /* assume x,y for Short Weierstrass */
+	os_memcpy(buf+1, x, len);
+	os_memcpy(buf+1+len, y, len);
+
+	return crypto_ec_key_set_pub_point_for_group(grp_id,NULL,buf,1+len*2);
+}
+
+struct crypto_ec_key *
+crypto_ec_key_set_pub_point(struct crypto_ec *e,
+			    const struct crypto_ec_point *pub)
+{
+	mbedtls_ecp_group_id grp_id = ((mbedtls_ecp_group *)e)->id;
+	mbedtls_ecp_point *p = (mbedtls_ecp_point *)pub;
+	return crypto_ec_key_set_pub_point_for_group(grp_id, p, NULL, 0);
+}
+
+
+struct crypto_ec_key * crypto_ec_key_gen(int group)
+{
+	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+	mbedtls_pk_init(ctx);
+	if (crypto_mbedtls_keypair_gen(group, ctx) == 0)
+		return (struct crypto_ec_key *)ctx;
+	mbedtls_pk_free(ctx);
+	os_free(ctx);
+	return NULL;
+}
+
+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
+
+void crypto_ec_key_deinit(struct crypto_ec_key *key)
+{
+	mbedtls_pk_free((mbedtls_pk_context *)key);
+	os_free(key);
+}
+
+struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key)
+{
+	/* (similar to crypto_ec_key_get_pubkey_point(),
+	 *  but compressed point format and ASN.1 DER wrapping)*/
+#ifndef MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES /*(mbedtls/library/pkwrite.h)*/
+#define MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES    ( 30 + 2 * MBEDTLS_ECP_MAX_BYTES )
+#endif
+	unsigned char buf[MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES];
+	int len = mbedtls_pk_write_pubkey_der((mbedtls_pk_context *)key,
+	                                      buf, sizeof(buf));
+	if (len < 0)
+		return NULL;
+	/*  Note: data is written at the end of the buffer! Use the
+	 *        return value to determine where you should start
+	 *        using the buffer */
+	unsigned char *p = buf+sizeof(buf)-len;
+
+  #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
+	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
+	if (ecp_kp == NULL)
+		return NULL;
+	mbedtls_ecp_group *grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
+	/*  Note: sae_pk.c expects pubkey point in compressed format,
+	 *        but mbedtls_pk_write_pubkey_der() writes uncompressed format.
+	 *        Manually translate format and update lengths in DER format */
+	if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
+		unsigned char *end = buf+sizeof(buf);
+		size_t n;
+		/* SubjectPublicKeyInfo SEQUENCE */
+		mbedtls_asn1_get_tag(&p, end, &n,
+		    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
+		/* algorithm AlgorithmIdentifier */
+		unsigned char *a = p;
+		size_t alen;
+		mbedtls_asn1_get_tag(&p, end, &alen,
+		    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
+		p += alen;
+		alen = (size_t)(p - a);
+		/* subjectPublicKey BIT STRING */
+		mbedtls_asn1_get_tag(&p, end, &n, MBEDTLS_ASN1_BIT_STRING);
+		/* rewrite into compressed point format and rebuild ASN.1 */
+		p[1] = (buf[sizeof(buf)-1] & 1) ? 0x03 : 0x02;
+		n = 1 + 1 + (n-2)/2;
+		len = mbedtls_asn1_write_len(&p, buf, n) + (int)n;
+		len += mbedtls_asn1_write_tag(&p, buf, MBEDTLS_ASN1_BIT_STRING);
+		os_memmove(p-alen, a, alen);
+		len += alen;
+		p -= alen;
+		len += mbedtls_asn1_write_len(&p, buf, (size_t)len);
+		len += mbedtls_asn1_write_tag(&p, buf,
+		    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
+	}
+  #endif
+	return wpabuf_alloc_copy(p, (size_t)len);
+}
+
+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
+
+struct wpabuf * crypto_ec_key_get_ecprivate_key(struct crypto_ec_key *key,
+						bool include_pub)
+{
+#ifndef MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES /*(mbedtls/library/pkwrite.h)*/
+#define MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES    ( 29 + 3 * MBEDTLS_ECP_MAX_BYTES )
+#endif
+	unsigned char priv[MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES];
+	int privlen = mbedtls_pk_write_key_der((mbedtls_pk_context *)key,
+	                                       priv, sizeof(priv));
+	if (privlen < 0)
+		return NULL;
+
+	struct wpabuf *wbuf;
+
+	/*  Note: data is written at the end of the buffer! Use the
+	 *        return value to determine where you should start
+	 *        using the buffer */
+	/* mbedtls_pk_write_key_der() includes publicKey in DER */
+	if (include_pub)
+		wbuf = wpabuf_alloc_copy(priv+sizeof(priv)-privlen, privlen);
+	else {
+		/* calculate publicKey offset and skip from end of buffer */
+		unsigned char *p = priv+sizeof(priv)-privlen;
+		unsigned char *end = priv+sizeof(priv);
+		size_t len;
+		/* ECPrivateKey SEQUENCE */
+		mbedtls_asn1_get_tag(&p, end, &len,
+		    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
+		/* version INTEGER */
+		unsigned char *v = p;
+		mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_INTEGER);
+		p += len;
+		/* privateKey OCTET STRING */
+		mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING);
+		p += len;
+		/* parameters ECParameters */
+		mbedtls_asn1_get_tag(&p, end, &len,
+		    MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED);
+		p += len;
+
+		/* write new SEQUENCE header (we know that it fits in priv[]) */
+		len = (size_t)(p - v);
+		p = v;
+		len += mbedtls_asn1_write_len(&p, priv, len);
+		len += mbedtls_asn1_write_tag(&p, priv,
+		    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
+		wbuf = wpabuf_alloc_copy(p, len);
+	}
+
+	forced_memzero(priv, sizeof(priv));
+	return wbuf;
+}
+
+struct wpabuf * crypto_ec_key_get_pubkey_point(struct crypto_ec_key *key,
+					       int prefix)
+{
+	/*(similarities to crypto_ecdh_get_pubkey(), but different struct)*/
+	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
+	if (ecp_kp == NULL)
+		return NULL;
+	mbedtls_ecp_group *grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
+	size_t len = CRYPTO_EC_plen(grp);
+  #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED
+	/* len */
+  #endif
+  #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED
+	if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS)
+		len = len*2+1;
+  #endif
+	struct wpabuf *buf = wpabuf_alloc(len);
+	if (buf == NULL)
+		return NULL;
+	mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
+	if (mbedtls_ecp_point_write_binary(grp, ecp_kp_Q,
+	                                   MBEDTLS_ECP_PF_UNCOMPRESSED, &len,
+	                                   wpabuf_mhead_u8(buf), len) == 0) {
+		if (!prefix) /* Remove 0x04 prefix if requested */
+			os_memmove(wpabuf_mhead(buf),wpabuf_mhead(buf)+1,--len);
+		wpabuf_put(buf, len);
+		return buf;
+	}
+
+	wpabuf_free(buf);
+	return NULL;
+}
+
+struct crypto_ec_point *
+crypto_ec_key_get_public_key(struct crypto_ec_key *key)
+{
+	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
+	if (ecp_kp == NULL)
+		return NULL;
+	mbedtls_ecp_point *p = os_malloc(sizeof(*p));
+	if (p != NULL) {
+		/*(mbedtls_ecp_export() uses &ecp_kp->MBEDTLS_PRIVATE(grp))*/
+		mbedtls_ecp_point_init(p);
+		mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
+		if (mbedtls_ecp_copy(p, ecp_kp_Q)) {
+			mbedtls_ecp_point_free(p);
+			os_free(p);
+			p = NULL;
+		}
+	}
+	return (struct crypto_ec_point *)p;
+}
+
+struct crypto_bignum *
+crypto_ec_key_get_private_key(struct crypto_ec_key *key)
+{
+	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
+	if (ecp_kp == NULL)
+		return NULL;
+	mbedtls_mpi *bn = os_malloc(sizeof(*bn));
+	if (bn) {
+		/*(mbedtls_ecp_export() uses &ecp_kp->MBEDTLS_PRIVATE(grp))*/
+		mbedtls_mpi_init(bn);
+		mbedtls_mpi *ecp_kp_d = &ecp_kp->MBEDTLS_PRIVATE(d);
+		if (mbedtls_mpi_copy(bn, ecp_kp_d)) {
+			mbedtls_mpi_free(bn);
+			os_free(bn);
+			bn = NULL;
+		}
+	}
+	return (struct crypto_bignum *)bn;
+}
+
+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
+
+static mbedtls_md_type_t crypto_ec_key_sign_md(size_t len)
+{
+	/* get mbedtls_md_type_t from length of hash data to be signed */
+	switch (len) {
+	case 64: return MBEDTLS_MD_SHA512;
+	case 48: return MBEDTLS_MD_SHA384;
+	case 32: return MBEDTLS_MD_SHA256;
+	case 20: return MBEDTLS_MD_SHA1;
+	case 16: return MBEDTLS_MD_MD5;
+	default: return MBEDTLS_MD_NONE;
+	}
+}
+
+struct wpabuf * crypto_ec_key_sign(struct crypto_ec_key *key, const u8 *data,
+				   size_t len)
+{
+  #ifndef MBEDTLS_PK_SIGNATURE_MAX_SIZE /*(defined since mbedtls 2.20.0)*/
+  #if MBEDTLS_ECDSA_MAX_LEN > MBEDTLS_MPI_MAX_SIZE
+  #define MBEDTLS_PK_SIGNATURE_MAX_SIZE MBEDTLS_ECDSA_MAX_LEN
+  #else
+  #define MBEDTLS_PK_SIGNATURE_MAX_SIZE MBEDTLS_MPI_MAX_SIZE
+  #endif
+  #endif
+	size_t sig_len = MBEDTLS_PK_SIGNATURE_MAX_SIZE;
+	struct wpabuf *buf = wpabuf_alloc(sig_len);
+	if (buf == NULL)
+		return NULL;
+	if (mbedtls_pk_sign((mbedtls_pk_context *)key,
+	                    crypto_ec_key_sign_md(len), data, len,
+	                    wpabuf_mhead_u8(buf),
+  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
+	                    sig_len,
+  #endif
+	                    &sig_len,
+	                    mbedtls_ctr_drbg_random,
+	                    crypto_mbedtls_ctr_drbg()) == 0) {
+		wpabuf_put(buf, sig_len);
+		return buf;
+	}
+
+	wpabuf_free(buf);
+	return NULL;
+}
+
+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
+struct wpabuf * crypto_ec_key_sign_r_s(struct crypto_ec_key *key,
+				       const u8 *data, size_t len)
+{
+	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
+	if (ecp_kp == NULL)
+		return NULL;
+
+	size_t sig_len = MBEDTLS_ECDSA_MAX_LEN;
+	u8 buf[MBEDTLS_ECDSA_MAX_LEN];
+	if (mbedtls_ecdsa_write_signature(ecp_kp, crypto_ec_key_sign_md(len),
+	                                  data, len, buf,
+  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
+	                                  sig_len,
+  #endif
+	                                  &sig_len,
+	                                  mbedtls_ctr_drbg_random,
+	                                  crypto_mbedtls_ctr_drbg())) {
+		return NULL;
+	}
+
+	/*(mbedtls_ecdsa_write_signature() writes signature in ASN.1)*/
+	/* parse ASN.1 to get r and s and lengths */
+	u8 *p = buf, *r, *s;
+	u8 *end = p + sig_len;
+	size_t rlen, slen;
+	mbedtls_asn1_get_tag(&p, end, &rlen,
+	  MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
+	mbedtls_asn1_get_tag(&p, end, &rlen, MBEDTLS_ASN1_INTEGER);
+	r = p;
+	p += rlen;
+	mbedtls_asn1_get_tag(&p, end, &slen, MBEDTLS_ASN1_INTEGER);
+	s = p;
+
+	/* write raw r and s into out
+	 * (including removal of leading 0 if added for ASN.1 integer)
+	 * note: DPP caller expects raw r, s each padded to prime len */
+	mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
+	size_t plen = CRYPTO_EC_plen(ecp_kp_grp);
+	if (rlen > plen) {
+		r += (rlen - plen);
+		rlen = plen;
+	}
+	if (slen > plen) {
+		s += (slen - plen);
+		slen = plen;
+	}
+	struct wpabuf *out = wpabuf_alloc(plen*2);
+	if (out) {
+		wpabuf_put(out, plen*2);
+		p = wpabuf_mhead_u8(out);
+		os_memset(p, 0, plen*2);
+		os_memcpy(p+plen*1-rlen, r, rlen);
+		os_memcpy(p+plen*2-slen, s, slen);
+	}
+	return out;
+}
+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
+
+int crypto_ec_key_verify_signature(struct crypto_ec_key *key, const u8 *data,
+				   size_t len, const u8 *sig, size_t sig_len)
+{
+	switch (mbedtls_pk_verify((mbedtls_pk_context *)key,
+	                          crypto_ec_key_sign_md(len), data, len,
+	                          sig, sig_len)) {
+	case 0:
+	/*case MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH:*//* XXX: allow? */
+		return 1;
+	case MBEDTLS_ERR_ECP_VERIFY_FAILED:
+		return 0;
+	default:
+		return -1;
+	}
+}
+
+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
+int crypto_ec_key_verify_signature_r_s(struct crypto_ec_key *key,
+				       const u8 *data, size_t len,
+				       const u8 *r, size_t r_len,
+				       const u8 *s, size_t s_len)
+{
+	/* reimplement mbedtls_ecdsa_read_signature() without encoding r and s
+	 * into ASN.1 just for mbedtls_ecdsa_read_signature() to decode ASN.1 */
+	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
+	if (ecp_kp == NULL)
+		return -1;
+	mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp);
+	mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q);
+
+	mbedtls_mpi mpi_r;
+	mbedtls_mpi mpi_s;
+	mbedtls_mpi_init(&mpi_r);
+	mbedtls_mpi_init(&mpi_s);
+	int ret = mbedtls_mpi_read_binary(&mpi_r, r, r_len)
+	       || mbedtls_mpi_read_binary(&mpi_s, s, s_len) ? -1 : 0;
+	if (ret == 0) {
+		ret = mbedtls_ecdsa_verify(ecp_kp_grp, data, len,
+		                           ecp_kp_Q, &mpi_r, &mpi_s);
+		ret = ret ? ret == MBEDTLS_ERR_ECP_BAD_INPUT_DATA ? 0 : -1 : 1;
+	}
+	mbedtls_mpi_free(&mpi_r);
+	mbedtls_mpi_free(&mpi_s);
+	return ret;
+}
+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
+
+int crypto_ec_key_group(struct crypto_ec_key *key)
+{
+	mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key);
+	if (ecp_kp == NULL)
+		return -1;
+	mbedtls_ecp_group *ecp_group = &ecp_kp->MBEDTLS_PRIVATE(grp);
+	return crypto_mbedtls_ike_id_from_ecp_group_id(ecp_group->id);
+}
+
+#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP
+
+int crypto_ec_key_cmp(struct crypto_ec_key *key1, struct crypto_ec_key *key2)
+{
+#if 0 /*(DPP is passing two public keys; unable to use pk_check_pair())*/
+  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
+	return mbedtls_pk_check_pair((const mbedtls_pk_context *)key1,
+	                             (const mbedtls_pk_context *)key2) ? -1 : 0;
+  #else
+	return mbedtls_pk_check_pair((const mbedtls_pk_context *)key1,
+	                             (const mbedtls_pk_context *)key2,
+	                             mbedtls_ctr_drbg_random,
+	                             crypto_mbedtls_ctr_drbg()) ? -1 : 0;
+  #endif
+#else
+	mbedtls_ecp_keypair *ecp_kp1=mbedtls_pk_ec(*(mbedtls_pk_context *)key1);
+	mbedtls_ecp_keypair *ecp_kp2=mbedtls_pk_ec(*(mbedtls_pk_context *)key2);
+	if (ecp_kp1 == NULL || ecp_kp2 == NULL)
+		return -1;
+	mbedtls_ecp_group *ecp_kp1_grp = &ecp_kp1->MBEDTLS_PRIVATE(grp);
+	mbedtls_ecp_group *ecp_kp2_grp = &ecp_kp2->MBEDTLS_PRIVATE(grp);
+	mbedtls_ecp_point *ecp_kp1_Q = &ecp_kp1->MBEDTLS_PRIVATE(Q);
+	mbedtls_ecp_point *ecp_kp2_Q = &ecp_kp2->MBEDTLS_PRIVATE(Q);
+	return ecp_kp1_grp->id != ecp_kp2_grp->id
+	    || mbedtls_ecp_point_cmp(ecp_kp1_Q, ecp_kp2_Q) ? -1 : 0;
+#endif
+}
+
+void crypto_ec_key_debug_print(const struct crypto_ec_key *key,
+			       const char *title)
+{
+	/* TBD: what info is desirable here and in what human readable format?*/
+	/*(crypto_openssl.c prints a human-readably public key and attributes)*/
+  #if 0
+	struct mbedtls_pk_debug_item debug_item;
+	if (mbedtls_pk_debug((const mbedtls_pk_context *)key, &debug_item))
+		return;
+	/* ... */
+  #endif
+	wpa_printf(MSG_DEBUG, "%s: %s not implemented", title, __func__);
+}
+
+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */
+
+#endif /* CRYPTO_MBEDTLS_CRYPTO_EC */
+
+
+#ifdef CRYPTO_MBEDTLS_CRYPTO_CSR
+
+#include <mbedtls/x509_csr.h>
+#include <mbedtls/oid.h>
+
+struct crypto_csr * crypto_csr_init(void)
+{
+	mbedtls_x509write_csr *csr = os_malloc(sizeof(*csr));
+	if (csr != NULL)
+		mbedtls_x509write_csr_init(csr);
+	return (struct crypto_csr *)csr;
+}
+
+struct crypto_csr * crypto_csr_verify(const struct wpabuf *req)
+{
+	/* future: look for alternatives to MBEDTLS_PRIVATE() access */
+
+	/* sole caller src/common/dpp_crypto.c:dpp_validate_csr()
+	 * uses (mbedtls_x509_csr *) to obtain CSR_ATTR_CHALLENGE_PASSWORD
+	 * so allocate different object (mbedtls_x509_csr *) and special-case
+	 * object when used in crypto_csr_get_attribute() and when free()d in
+	 * crypto_csr_deinit(). */
+
+	mbedtls_x509_csr *csr = os_malloc(sizeof(*csr));
+	if (csr == NULL)
+		return NULL;
+	mbedtls_x509_csr_init(csr);
+	const mbedtls_md_info_t *md_info;
+	unsigned char digest[MBEDTLS_MD_MAX_SIZE];
+	if (mbedtls_x509_csr_parse_der(csr,wpabuf_head(req),wpabuf_len(req))==0
+	    && (md_info=mbedtls_md_info_from_type(csr->MBEDTLS_PRIVATE(sig_md)))
+	       != NULL
+	    && mbedtls_md(md_info, csr->cri.p, csr->cri.len, digest) == 0) {
+		switch (mbedtls_pk_verify(&csr->pk,csr->MBEDTLS_PRIVATE(sig_md),
+		                          digest, mbedtls_md_get_size(md_info),
+		                          csr->MBEDTLS_PRIVATE(sig).p,
+		                          csr->MBEDTLS_PRIVATE(sig).len)) {
+		case 0:
+		/*case MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH:*//* XXX: allow? */
+			return (struct crypto_csr *)((uintptr_t)csr | 1uL);
+		default:
+			break;
+		}
+	}
+
+	mbedtls_x509_csr_free(csr);
+	os_free(csr);
+	return NULL;
+}
+
+void crypto_csr_deinit(struct crypto_csr *csr)
+{
+	if ((uintptr_t)csr & 1uL) {
+		csr = (struct crypto_csr *)((uintptr_t)csr & ~1uL);
+		mbedtls_x509_csr_free((mbedtls_x509_csr *)csr);
+	}
+	else
+		mbedtls_x509write_csr_free((mbedtls_x509write_csr *)csr);
+	os_free(csr);
+}
+
+int crypto_csr_set_ec_public_key(struct crypto_csr *csr,
+				 struct crypto_ec_key *key)
+{
+	mbedtls_x509write_csr_set_key((mbedtls_x509write_csr *)csr,
+	                              (mbedtls_pk_context *)key);
+	return 0;
+}
+
+int crypto_csr_set_name(struct crypto_csr *csr, enum crypto_csr_name type,
+			const char *name)
+{
+	/* specialized for src/common/dpp_crypto.c */
+
+	/* sole caller src/common/dpp_crypto.c:dpp_build_csr()
+	 * calls this function only once, using type == CSR_NAME_CN
+	 * (If called more than once, this code would need to append
+	 *  components to the subject name, which we could do by
+	 *  appending to (mbedtls_x509write_csr *) private member
+	 *  mbedtls_asn1_named_data *MBEDTLS_PRIVATE(subject)) */
+
+	const char *label;
+	switch (type) {
+	case CSR_NAME_CN: label = "CN="; break;
+	case CSR_NAME_SN: label = "SN="; break;
+	case CSR_NAME_C:  label = "C=";  break;
+	case CSR_NAME_O:  label = "O=";  break;
+	case CSR_NAME_OU: label = "OU="; break;
+	default: return -1;
+	}
+
+	size_t len = strlen(name);
+	struct wpabuf *buf = wpabuf_alloc(3+len+1);
+	if (buf == NULL)
+		return -1;
+	wpabuf_put_data(buf, label, strlen(label));
+	wpabuf_put_data(buf, name, len+1); /*(include trailing '\0')*/
+	/* Note: 'name' provided is set as given and should be backslash-escaped
+	 * by caller when necessary, e.g. literal ',' which are not separating
+	 * components should be backslash-escaped */
+
+	int ret =
+	  mbedtls_x509write_csr_set_subject_name((mbedtls_x509write_csr *)csr,
+	                                         wpabuf_head(buf)) ? -1 : 0;
+	wpabuf_free(buf);
+	return ret;
+}
+
+/* OBJ_pkcs9_challengePassword  1 2 840 113549 1 9 7 */
+static const char OBJ_pkcs9_challengePassword[] = MBEDTLS_OID_PKCS9 "\x07";
+
+int crypto_csr_set_attribute(struct crypto_csr *csr, enum crypto_csr_attr attr,
+			     int attr_type, const u8 *value, size_t len)
+{
+	/* specialized for src/common/dpp_crypto.c */
+	/* sole caller src/common/dpp_crypto.c:dpp_build_csr() passes
+	 *   attr      == CSR_ATTR_CHALLENGE_PASSWORD
+	 *   attr_type == ASN1_TAG_UTF8STRING */
+
+	const char *oid;
+	size_t oid_len;
+	switch (attr) {
+	case CSR_ATTR_CHALLENGE_PASSWORD:
+		oid = OBJ_pkcs9_challengePassword;
+		oid_len = sizeof(OBJ_pkcs9_challengePassword)-1;
+		break;
+	default:
+		return -1;
+	}
+
+  #if 0 /*(incorrect; sets an extension, not an attribute)*/
+	return mbedtls_x509write_csr_set_extension((mbedtls_x509write_csr *)csr,
+	                                           oid, oid_len,
+	  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
+	                                           0, /*(critical flag)*/
+	  #endif
+	                                           value, len) ? -1 : 0;
+  #else
+	(void)oid;
+	(void)oid_len;
+  #endif
+
+	/* mbedtls does not currently provide way to set an attribute in a CSR:
+	 *   https://github.com/Mbed-TLS/mbedtls/issues/4886 */
+	wpa_printf(MSG_ERROR,
+	  "mbedtls does not currently support setting challengePassword "
+	  "attribute in CSR");
+	return -1;
+}
+
+const u8 * mbedtls_x509_csr_attr_oid_value(mbedtls_x509_csr *csr,
+                                           const char *oid, size_t oid_len,
+                                           size_t *vlen, int *vtype)
+{
+	/* Note: mbedtls_x509_csr_parse_der() has parsed and validated CSR,
+	 *	   so validation checks are not repeated here
+	 *
+	 * It would be nicer if (mbedtls_x509_csr *) had an mbedtls_x509_buf of
+	 * Attributes (or at least a pointer) since mbedtls_x509_csr_parse_der()
+	 * already parsed the rest of CertificationRequestInfo, some of which is
+	 * repeated here to step to Attributes.  Since csr->subject_raw.p points
+	 * into csr->cri.p, which points into csr->raw.p, step over version and
+	 * subject of CertificationRequestInfo (SEQUENCE) */
+	unsigned char *p = csr->subject_raw.p + csr->subject_raw.len;
+	unsigned char *end = csr->cri.p + csr->cri.len, *ext;
+	size_t len;
+
+	/* step over SubjectPublicKeyInfo */
+	mbedtls_asn1_get_tag(&p, end, &len,
+	    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
+	p += len;
+
+	/* Attributes
+	 *   { ATTRIBUTE:IOSet } ::= SET OF { SEQUENCE { OID, value } }
+	 */
+	if (mbedtls_asn1_get_tag(&p, end, &len,
+	      MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC) != 0) {
+		return NULL;
+	}
+	while (p < end) {
+		if (mbedtls_asn1_get_tag(&p, end, &len,
+		      MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) {
+			return NULL;
+		}
+		ext = p;
+		p += len;
+
+		if (mbedtls_asn1_get_tag(&ext,end,&len,MBEDTLS_ASN1_OID) != 0)
+			return NULL;
+		if (oid_len != len || 0 != memcmp(ext, oid, oid_len))
+			continue;
+
+		/* found oid; return value */
+		*vtype = *ext++; /* tag */
+		return (mbedtls_asn1_get_len(&ext,end,vlen) == 0) ? ext : NULL;
+	}
+
+	return NULL;
+}
+
+const u8 * crypto_csr_get_attribute(struct crypto_csr *csr,
+				    enum crypto_csr_attr attr,
+				    size_t *len, int *type)
+{
+	/* specialized for src/common/dpp_crypto.c */
+	/* sole caller src/common/dpp_crypto.c:dpp_build_csr() passes
+	 *   attr == CSR_ATTR_CHALLENGE_PASSWORD */
+
+	const char *oid;
+	size_t oid_len;
+	switch (attr) {
+	case CSR_ATTR_CHALLENGE_PASSWORD:
+		oid = OBJ_pkcs9_challengePassword;
+		oid_len = sizeof(OBJ_pkcs9_challengePassword)-1;
+		break;
+	default:
+		return NULL;
+	}
+
+	/* see crypto_csr_verify(); expecting (mbedtls_x509_csr *) tagged |=1 */
+	if (!((uintptr_t)csr & 1uL))
+		return NULL;
+	csr = (struct crypto_csr *)((uintptr_t)csr & ~1uL);
+
+	return mbedtls_x509_csr_attr_oid_value((mbedtls_x509_csr *)csr,
+	                                       oid, oid_len, len, type);
+}
+
+struct wpabuf * crypto_csr_sign(struct crypto_csr *csr,
+				struct crypto_ec_key *key,
+				enum crypto_hash_alg algo)
+{
+	mbedtls_md_type_t sig_md;
+	switch (algo) {
+  #ifdef MBEDTLS_SHA256_C
+	case CRYPTO_HASH_ALG_SHA256: sig_md = MBEDTLS_MD_SHA256; break;
+  #endif
+  #ifdef MBEDTLS_SHA512_C
+	case CRYPTO_HASH_ALG_SHA384: sig_md = MBEDTLS_MD_SHA384; break;
+	case CRYPTO_HASH_ALG_SHA512: sig_md = MBEDTLS_MD_SHA512; break;
+  #endif
+	default:
+		return NULL;
+	}
+	mbedtls_x509write_csr_set_md_alg((mbedtls_x509write_csr *)csr, sig_md);
+
+  #if 0
+	unsigned char key_usage = MBEDTLS_X509_KU_DIGITAL_SIGNATURE
+	                        | MBEDTLS_X509_KU_KEY_CERT_SIGN;
+	if (mbedtls_x509write_csr_set_key_usage((mbedtls_x509write_csr *)csr,
+	                                        key_usage))
+		return NULL;
+  #endif
+
+  #if 0
+	unsigned char ns_cert_type = MBEDTLS_X509_NS_CERT_TYPE_SSL_CLIENT
+	                           | MBEDTLS_X509_NS_CERT_TYPE_EMAIL;
+	if (mbedtls_x509write_csr_set_ns_cert_type((mbedtls_x509write_csr *)csr,
+	                                           ns_cert_type))
+		return NULL;
+  #endif
+
+  #if 0
+	/* mbedtls does not currently provide way to set an attribute in a CSR:
+	 *   https://github.com/Mbed-TLS/mbedtls/issues/4886
+	 * XXX: hwsim dpp_enterprise test fails due to this limitation.
+	 *
+	 * Current usage of this function is solely by dpp_build_csr(),
+	 * so as a kludge, might consider custom (struct crypto_csr *)
+	 * containing (mbedtls_x509write_csr *) and a list of attributes
+	 * (i.e. challengePassword).  Might have to totally reimplement
+	 * mbedtls_x509write_csr_der(); underlying x509write_csr_der_internal()
+	 * handles signing the CSR.  (This is more work that appending an
+	 * Attributes section to end of CSR and adjusting ASN.1 length of CSR.)
+	 */
+  #endif
+
+	unsigned char buf[4096]; /* XXX: large enough?  too large? */
+	int len = mbedtls_x509write_csr_der((mbedtls_x509write_csr *)csr,
+	                                    buf, sizeof(buf),
+	                                    mbedtls_ctr_drbg_random,
+	                                    crypto_mbedtls_ctr_drbg());
+	if (len < 0)
+		return NULL;
+	/*  Note: data is written at the end of the buffer! Use the
+	 *        return value to determine where you should start
+	 *        using the buffer */
+	return wpabuf_alloc_copy(buf+sizeof(buf)-len, (size_t)len);
+}
+
+#endif /* CRYPTO_MBEDTLS_CRYPTO_CSR */
+
+
+#ifdef CRYPTO_MBEDTLS_CRYPTO_PKCS7
+
+#if 0
+#include <mbedtls/pkcs7.h> /* PKCS7 is not currently supported in mbedtls */
+#include <mbedtls/pem.h>
+#endif
+
+struct wpabuf * crypto_pkcs7_get_certificates(const struct wpabuf *pkcs7)
+{
+	/* PKCS7 is not currently supported in mbedtls */
+	return NULL;
+
+#if 0
+	/* https://github.com/naynajain/mbedtls-1 branch: development-pkcs7
+	 * (??? potential future contribution to mbedtls ???) */
+
+	/* Note: PKCS7 signature *is not* verified by this function.
+	 * The function interface does not provide for passing a certificate */
+
+	mbedtls_pkcs7 mpkcs7;
+	mbedtls_pkcs7_init(&mpkcs7);
+	int pkcs7_type = mbedtls_pkcs7_parse_der(wpabuf_head(pkcs7),
+	                                         wpabuf_len(pkcs7),
+	                                         &mpkcs7);
+	wpabuf *buf = NULL;
+	do {
+		if (pkcs7_type < 0)
+			break;
+
+		/* src/common/dpp.c:dpp_parse_cred_dot1x() interested in certs
+		 * for wpa_supplicant/dpp_supplicant.c:wpas_dpp_add_network()
+		 * (? are adding certificate headers and footers desired ?) */
+
+		/* development-pkcs7 branch does not currently provide
+		 * additional interfaces to retrieve the parsed data */
+
+		mbedtls_x509_crt *certs =
+		  &mpkcs7.MBEDTLS_PRIVATE(signed_data).MBEDTLS_PRIVATE(certs);
+		int ncerts =
+		  mpkcs7.MBEDTLS_PRIVATE(signed_data).MBEDTLS_PRIVATE(no_of_certs);
+
+		/* allocate buffer for PEM (base64-encoded DER)
+		 * plus header, footer, newlines, and some extra */
+		buf = wpabuf_alloc((wpabuf_len(pkcs7)+2)/3*4 + ncerts*64);
+		if (buf == NULL)
+			break;
+
+		#define PEM_BEGIN_CRT "-----BEGIN CERTIFICATE-----\n"
+		#define PEM_END_CRT   "-----END CERTIFICATE-----\n"
+		size_t olen;
+		for (int i = 0; i < ncerts; ++i) {
+			int ret = mbedtls_pem_write_buffer(
+			            PEM_BEGIN_CRT, PEM_END_CRT,
+			            certs[i].raw.p, certs[i].raw.len,
+			            wpabuf_mhead(buf, 0), wpabuf_tailroom(buf),
+			            &olen));
+			if (ret == 0)
+				wpabuf_put(buf, olen);
+			} else {
+				if (ret == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL)
+					ret = wpabuf_resize(
+					        &buf,olen-wpabuf_tailroom(buf));
+				if (ret == 0) {
+					--i;/*(adjust loop iterator for retry)*/
+					continue;
+				}
+				wpabuf_free(buf);
+				buf = NULL;
+				break;
+			}
+		}
+	} while (0);
+
+	mbedtls_pkcs7_free(&mpkcs7);
+	return buf;
+#endif
+}
+
+#endif /* CRYPTO_MBEDTLS_CRYPTO_PKCS7 */
+
+
+#ifdef MBEDTLS_ARC4_C
+#include <mbedtls/arc4.h>
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+	     u8 *data, size_t data_len)
+{
+	mbedtls_arc4_context ctx;
+	mbedtls_arc4_init(&ctx);
+	mbedtls_arc4_setup(&ctx, key, keylen);
+
+	if (skip) {
+		/*(prefer [16] on ancient hardware with smaller cache lines)*/
+		unsigned char skip_buf[64]; /*('skip' is generally small)*/
+		/*os_memset(skip_buf, 0, sizeof(skip_buf));*/ /*(necessary?)*/
+		size_t len;
+		do {
+			len = skip > sizeof(skip_buf) ? sizeof(skip_buf) : skip;
+			mbedtls_arc4_crypt(&ctx, len, skip_buf, skip_buf);
+		} while ((skip -= len));
+	}
+
+	int ret = mbedtls_arc4_crypt(&ctx, data_len, data, data);
+	mbedtls_arc4_free(&ctx);
+	return ret;
+}
+#endif
+
+
+/* duplicated in tls_mbedtls.c:tls_mbedtls_readfile()*/
+__attribute_noinline__
+static int crypto_mbedtls_readfile(const char *path, u8 **buf, size_t *n)
+{
+  #if 0 /* #ifdef MBEDTLS_FS_IO */
+	/*(includes +1 for '\0' needed by mbedtls PEM parsing funcs)*/
+	if (mbedtls_pk_load_file(path, (unsigned char **)buf, n) != 0) {
+		wpa_printf(MSG_ERROR, "error: mbedtls_pk_load_file %s", path);
+		return -1;
+	}
+  #else
+	/*(use os_readfile() so that we can use os_free()
+	 *(if we use mbedtls_pk_load_file() above, macros prevent calling free()
+	 * directly #if defined(OS_REJECT_C_LIB_FUNCTIONS) and calling os_free()
+	 * on buf aborts in tests if buf not allocated via os_malloc())*/
+	*buf = (u8 *)os_readfile(path, n);
+	if (!*buf) {
+		wpa_printf(MSG_ERROR, "error: os_readfile %s", path);
+		return -1;
+	}
+	u8 *buf0 = os_realloc(*buf, *n+1);
+	if (!buf0) {
+		bin_clear_free(*buf, *n);
+		*buf = NULL;
+		return -1;
+	}
+	buf0[(*n)++] = '\0';
+	*buf = buf0;
+  #endif
+	return 0;
+}
+
+
+#ifdef CRYPTO_MBEDTLS_CRYPTO_RSA
+#ifdef MBEDTLS_RSA_C
+
+#include <mbedtls/pk.h>
+#include <mbedtls/rsa.h>
+
+struct crypto_rsa_key * crypto_rsa_key_read(const char *file, bool private_key)
+{
+	/* mbedtls_pk_parse_keyfile() and mbedtls_pk_parse_public_keyfile()
+	 * require #ifdef MBEDTLS_FS_IO in mbedtls library.  Prefer to use
+	 * crypto_mbedtls_readfile(), which wraps os_readfile() */
+	u8 *data;
+	size_t len;
+	if (crypto_mbedtls_readfile(file, &data, &len) != 0)
+		return NULL;
+
+	mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
+	if (ctx == NULL) {
+		bin_clear_free(data, len);
+		return NULL;
+	}
+	mbedtls_pk_init(ctx);
+
+	int rc;
+	rc = (private_key
+	      ? mbedtls_pk_parse_key(ctx, data, len, NULL, 0
+	  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
+	                            ,mbedtls_ctr_drbg_random,
+	                             crypto_mbedtls_ctr_drbg()
+	  #endif
+	                            )
+	      : mbedtls_pk_parse_public_key(ctx, data, len)) == 0
+	    && mbedtls_pk_can_do(ctx, MBEDTLS_PK_RSA);
+
+	bin_clear_free(data, len);
+
+	if (rc) {
+		/* use MBEDTLS_RSA_PKCS_V21 padding for RSAES-OAEP */
+		/* use MBEDTLS_MD_SHA256 for these hostap interfaces */
+	  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
+		/*(no return value in mbedtls 2.x)*/
+		mbedtls_rsa_set_padding(mbedtls_pk_rsa(*ctx),
+		                        MBEDTLS_RSA_PKCS_V21,
+		                        MBEDTLS_MD_SHA256);
+	  #else
+		if (mbedtls_rsa_set_padding(mbedtls_pk_rsa(*ctx),
+		                            MBEDTLS_RSA_PKCS_V21,
+		                            MBEDTLS_MD_SHA256) == 0)
+	  #endif
+			return (struct crypto_rsa_key *)ctx;
+	}
+
+	mbedtls_pk_free(ctx);
+	os_free(ctx);
+	return NULL;
+}
+
+struct wpabuf * crypto_rsa_oaep_sha256_encrypt(struct crypto_rsa_key *key,
+					       const struct wpabuf *in)
+{
+	mbedtls_rsa_context *pk_rsa = mbedtls_pk_rsa(*(mbedtls_pk_context*)key);
+	size_t olen = mbedtls_rsa_get_len(pk_rsa);
+	struct wpabuf *buf = wpabuf_alloc(olen);
+	if (buf == NULL)
+		return NULL;
+
+	/* mbedtls_pk_encrypt() takes a few more hops to get to same func */
+	if (mbedtls_rsa_rsaes_oaep_encrypt(pk_rsa,
+	                                   mbedtls_ctr_drbg_random,
+	                                   crypto_mbedtls_ctr_drbg(),
+	  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
+	                                   MBEDTLS_RSA_PRIVATE,
+	  #endif
+	                                   NULL, 0,
+	                                   wpabuf_len(in), wpabuf_head(in),
+	                                   wpabuf_put(buf, olen)) == 0) {
+		return buf;
+	}
+
+	wpabuf_clear_free(buf);
+	return NULL;
+}
+
+struct wpabuf * crypto_rsa_oaep_sha256_decrypt(struct crypto_rsa_key *key,
+					       const struct wpabuf *in)
+{
+	mbedtls_rsa_context *pk_rsa = mbedtls_pk_rsa(*(mbedtls_pk_context*)key);
+	size_t olen = mbedtls_rsa_get_len(pk_rsa);
+	struct wpabuf *buf = wpabuf_alloc(olen);
+	if (buf == NULL)
+		return NULL;
+
+	/* mbedtls_pk_decrypt() takes a few more hops to get to same func */
+	if (mbedtls_rsa_rsaes_oaep_decrypt(pk_rsa,
+	                                   mbedtls_ctr_drbg_random,
+	                                   crypto_mbedtls_ctr_drbg(),
+	  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
+	                                   MBEDTLS_RSA_PUBLIC,
+	  #endif
+	                                   NULL, 0, &olen, wpabuf_head(in),
+	                                   wpabuf_mhead(buf), olen) == 0) {
+		wpabuf_put(buf, olen);
+		return buf;
+	}
+
+	wpabuf_clear_free(buf);
+	return NULL;
+}
+
+void crypto_rsa_key_free(struct crypto_rsa_key *key)
+{
+	mbedtls_pk_free((mbedtls_pk_context *)key);
+	os_free(key);
+}
+
+#endif /* MBEDTLS_RSA_C */
+#endif /* CRYPTO_MBEDTLS_CRYPTO_RSA */
+
+#ifdef CRYPTO_MBEDTLS_CRYPTO_HPKE
+
+struct wpabuf * hpke_base_seal(enum hpke_kem_id kem_id,
+			       enum hpke_kdf_id kdf_id,
+			       enum hpke_aead_id aead_id,
+			       struct crypto_ec_key *peer_pub,
+			       const u8 *info, size_t info_len,
+			       const u8 *aad, size_t aad_len,
+			       const u8 *pt, size_t pt_len)
+{
+	/* not yet implemented */
+	return NULL;
+}
+
+struct wpabuf * hpke_base_open(enum hpke_kem_id kem_id,
+			       enum hpke_kdf_id kdf_id,
+			       enum hpke_aead_id aead_id,
+			       struct crypto_ec_key *own_priv,
+			       const u8 *info, size_t info_len,
+			       const u8 *aad, size_t aad_len,
+			       const u8 *enc_ct, size_t enc_ct_len)
+{
+	/* not yet implemented */
+	return NULL;
+}
+
+#endif
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_module_tests.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_module_tests.c
new file mode 100644
index 0000000..07c36d8
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_module_tests.c
@@ -0,0 +1,2732 @@
+/*
+ * crypto module tests
+ * Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/module_tests.h"
+#include "crypto/aes_siv.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/aes.h"
+#include "crypto/ms_funcs.h"
+#include "crypto/crypto.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/sha384.h"
+
+
+static int test_siv(void)
+{
+#ifdef CONFIG_MESH
+	/* RFC 5297, A.1. Deterministic Authenticated Encryption Example */
+	u8 key[] = {
+		0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
+		0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0,
+		0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+		0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+	};
+	u8 ad[] = {
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+		0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+		0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27
+	};
+	u8 plaintext[] = {
+		0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+		0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee
+	};
+	u8 iv_c[] = {
+		0x85, 0x63, 0x2d, 0x07, 0xc6, 0xe8, 0xf3, 0x7f,
+		0x95, 0x0a, 0xcd, 0x32, 0x0a, 0x2e, 0xcc, 0x93,
+		0x40, 0xc0, 0x2b, 0x96, 0x90, 0xc4, 0xdc, 0x04,
+		0xda, 0xef, 0x7f, 0x6a, 0xfe, 0x5c
+	};
+	/* RFC 5297, A.2. Nonce-Based Authenticated Encryption Example */
+	u8 key_2[] = {
+		0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78,
+		0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x70,
+		0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+		0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f
+	};
+	u8 ad1_2[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+		0xde, 0xad, 0xda, 0xda, 0xde, 0xad, 0xda, 0xda,
+		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
+		0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00
+	};
+	u8 ad2_2[] = {
+		0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
+		0x90, 0xa0
+	};
+	u8 nonce_2[] = {
+		0x09, 0xf9, 0x11, 0x02, 0x9d, 0x74, 0xe3, 0x5b,
+		0xd8, 0x41, 0x56, 0xc5, 0x63, 0x56, 0x88, 0xc0
+	};
+	u8 plaintext_2[] = {
+		0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20,
+		0x73, 0x6f, 0x6d, 0x65, 0x20, 0x70, 0x6c, 0x61,
+		0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x74,
+		0x6f, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70,
+		0x74, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20,
+		0x53, 0x49, 0x56, 0x2d, 0x41, 0x45, 0x53
+	};
+	u8 iv_c_2[] = {
+		0x7b, 0xdb, 0x6e, 0x3b, 0x43, 0x26, 0x67, 0xeb,
+		0x06, 0xf4, 0xd1, 0x4b, 0xff, 0x2f, 0xbd, 0x0f,
+		0xcb, 0x90, 0x0f, 0x2f, 0xdd, 0xbe, 0x40, 0x43,
+		0x26, 0x60, 0x19, 0x65, 0xc8, 0x89, 0xbf, 0x17,
+		0xdb, 0xa7, 0x7c, 0xeb, 0x09, 0x4f, 0xa6, 0x63,
+		0xb7, 0xa3, 0xf7, 0x48, 0xba, 0x8a, 0xf8, 0x29,
+		0xea, 0x64, 0xad, 0x54, 0x4a, 0x27, 0x2e, 0x9c,
+		0x48, 0x5b, 0x62, 0xa3, 0xfd, 0x5c, 0x0d
+	};
+	u8 out[2 * AES_BLOCK_SIZE + sizeof(plaintext_2)];
+	const u8 *addr[3];
+	size_t len[3];
+
+	/* RFC 5297, A.1. Deterministic Authenticated Encryption Example */
+	addr[0] = ad;
+	len[0] = sizeof(ad);
+
+	if (aes_siv_encrypt(key, sizeof(key), plaintext, sizeof(plaintext),
+			    1, addr, len, out)) {
+		wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed");
+		return 1;
+	}
+	if (os_memcmp(out, iv_c, sizeof(iv_c)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-SIV mode encryption returned invalid cipher text");
+		return 1;
+	}
+
+	if (aes_siv_decrypt(key, sizeof(key), iv_c, sizeof(iv_c),
+			    1, addr, len, out)) {
+		wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed");
+		return 1;
+	}
+	if (os_memcmp(out, plaintext, sizeof(plaintext)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-SIV mode decryption returned invalid plain text");
+		return 1;
+	}
+
+	/* RFC 5297, A.2. Nonce-Based Authenticated Encryption Example */
+	addr[0] = ad1_2;
+	len[0] = sizeof(ad1_2);
+	addr[1] = ad2_2;
+	len[1] = sizeof(ad2_2);
+	addr[2] = nonce_2;
+	len[2] = sizeof(nonce_2);
+
+	if (aes_siv_encrypt(key_2, sizeof(key_2),
+			    plaintext_2, sizeof(plaintext_2),
+			    3, addr, len, out)) {
+		wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed");
+		return 1;
+	}
+	if (os_memcmp(out, iv_c_2, sizeof(iv_c_2)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-SIV mode encryption returned invalid cipher text");
+		return 1;
+	}
+
+	if (aes_siv_decrypt(key_2, sizeof(key_2), iv_c_2, sizeof(iv_c_2),
+			    3, addr, len, out)) {
+		wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed");
+		return 1;
+	}
+	if (os_memcmp(out, plaintext_2, sizeof(plaintext_2)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-SIV mode decryption returned invalid plain text");
+		return 1;
+	}
+
+	wpa_printf(MSG_INFO, "AES-SIV test cases passed");
+#endif /* CONFIG_MESH */
+
+	return 0;
+}
+
+
+/* OMAC1 AES-128 test vectors from
+ * http://csrc.nist.gov/CryptoToolkit/modes/proposedmodes/omac/omac-ad.pdf
+ * which are same as the examples from NIST SP800-38B
+ * http://csrc.nist.gov/CryptoToolkit/modes/800-38_Series_Publications/SP800-38B.pdf
+ */
+
+struct omac1_test_vector {
+	u8 k[16];
+	u8 msg[64];
+	int msg_len;
+	u8 tag[16];
+};
+
+static const struct omac1_test_vector omac1_test_vectors[] =
+{
+	{
+		{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+		  0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c },
+		{ },
+		0,
+		{ 0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28,
+		  0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46 }
+	},
+	{
+		{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+		  0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c },
+		{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+		  0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a},
+		16,
+		{ 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44,
+		  0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c }
+	},
+	{
+		{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+		  0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c },
+		{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+		  0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+		  0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+		  0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+		  0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11 },
+		40,
+		{ 0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30,
+		  0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27 }
+	},
+	{
+		{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+		  0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c },
+		{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+		  0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+		  0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+		  0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+		  0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+		  0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+		  0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+		  0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 },
+		64,
+		{ 0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92,
+		  0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe }
+	},
+};
+
+
+static int test_omac1_vector(const struct omac1_test_vector *tv,
+			     unsigned int i)
+{
+	u8 key[] = {
+		0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+		0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+	};
+	u8 msg[] = { 0x12, 0x34, 0x56 };
+	u8 result[24], result2[24];
+	const u8 *addr[3];
+	size_t len[3];
+
+	if (omac1_aes_128(tv->k, tv->msg, tv->msg_len, result) ||
+	    os_memcmp(result, tv->tag, 16) != 0) {
+		wpa_printf(MSG_ERROR, "OMAC1-AES-128 test vector %u failed", i);
+		return 1;
+	}
+
+	if (tv->msg_len > 1) {
+
+		addr[0] = tv->msg;
+		len[0] = 1;
+		addr[1] = tv->msg + 1;
+		len[1] = tv->msg_len - 1;
+
+		if (omac1_aes_128_vector(tv->k, 2, addr, len, result) ||
+		    os_memcmp(result, tv->tag, 16) != 0) {
+			wpa_printf(MSG_ERROR,
+				   "OMAC1-AES-128(vector) test vector %u failed",
+				   i);
+			return 1;
+		}
+
+		addr[0] = tv->msg;
+		len[0] = tv->msg_len - 2;
+		addr[1] = tv->msg + tv->msg_len - 2;
+		len[1] = 1;
+		addr[2] = tv->msg + tv->msg_len - 1;
+		len[2] = 1;
+
+		if (omac1_aes_128_vector(tv->k, 3, addr, len, result) ||
+		    os_memcmp(result, tv->tag, 16) != 0) {
+			wpa_printf(MSG_ERROR,
+				   "OMAC1-AES-128(vector2) test vector %u failed",
+				   i);
+			return 1;
+		}
+	}
+
+	addr[0] = &msg[0];
+	len[0] = 1;
+	addr[1] = &msg[1];
+	len[1] = 1;
+	addr[2] = &msg[2];
+	len[2] = 1;
+	if (omac1_aes_128(key, msg, sizeof(msg), result) ||
+	    omac1_aes_128_vector(key, 3, addr, len, result2) ||
+	    os_memcmp(result, result2, 16) != 0) {
+		wpa_printf(MSG_ERROR, "OMAC1-AES-128 short test mismatch");
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static int test_omac1(void)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(omac1_test_vectors); i++) {
+		if (test_omac1_vector(&omac1_test_vectors[i], i))
+			return 1;
+	}
+
+	wpa_printf(MSG_INFO, "OMAC1-AES-128 test cases passed");
+
+	return 0;
+}
+
+
+static int test_eax(void)
+{
+#ifdef EAP_PSK
+	u8 msg[] = { 0xF7, 0xFB };
+	u8 key[] = { 0x91, 0x94, 0x5D, 0x3F, 0x4D, 0xCB, 0xEE, 0x0B,
+		     0xF4, 0x5E, 0xF5, 0x22, 0x55, 0xF0, 0x95, 0xA4 };
+	u8 nonce[] = { 0xBE, 0xCA, 0xF0, 0x43, 0xB0, 0xA2, 0x3D, 0x84,
+		       0x31, 0x94, 0xBA, 0x97, 0x2C, 0x66, 0xDE, 0xBD };
+	u8 hdr[] = { 0xFA, 0x3B, 0xFD, 0x48, 0x06, 0xEB, 0x53, 0xFA };
+	u8 cipher[] = { 0x19, 0xDD, 0x5C, 0x4C, 0x93, 0x31, 0x04, 0x9D,
+			0x0B, 0xDA, 0xB0, 0x27, 0x74, 0x08, 0xF6, 0x79,
+			0x67, 0xE5 };
+	u8 data[sizeof(msg)], tag[AES_BLOCK_SIZE];
+
+	os_memcpy(data, msg, sizeof(msg));
+	if (aes_128_eax_encrypt(key, nonce, sizeof(nonce), hdr, sizeof(hdr),
+				data, sizeof(data), tag)) {
+		wpa_printf(MSG_ERROR, "AES-128 EAX mode encryption failed");
+		return 1;
+	}
+	if (os_memcmp(data, cipher, sizeof(data)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-128 EAX mode encryption returned invalid cipher text");
+		return 1;
+	}
+	if (os_memcmp(tag, cipher + sizeof(data), AES_BLOCK_SIZE) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-128 EAX mode encryption returned invalid tag");
+		return 1;
+	}
+
+	if (aes_128_eax_decrypt(key, nonce, sizeof(nonce), hdr, sizeof(hdr),
+				data, sizeof(data), tag)) {
+		wpa_printf(MSG_ERROR, "AES-128 EAX mode decryption failed");
+		return 1;
+	}
+	if (os_memcmp(data, msg, sizeof(data)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-128 EAX mode decryption returned invalid plain text");
+		return 1;
+	}
+
+	wpa_printf(MSG_INFO, "AES-128 EAX mode test cases passed");
+#endif /* EAP_PSK */
+
+	return 0;
+}
+
+
+static int test_cbc(void)
+{
+	struct cbc_test_vector {
+		u8 key[16];
+		u8 iv[16];
+		u8 plain[32];
+		u8 cipher[32];
+		size_t len;
+	} vectors[] = {
+		{
+			{ 0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b,
+			  0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06 },
+			{ 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30,
+			  0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41 },
+			"Single block msg",
+			{ 0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8,
+			  0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a },
+			16
+		},
+		{
+			{ 0xc2, 0x86, 0x69, 0x6d, 0x88, 0x7c, 0x9a, 0xa0,
+			  0x61, 0x1b, 0xbb, 0x3e, 0x20, 0x25, 0xa4, 0x5a },
+			{ 0x56, 0x2e, 0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28,
+			  0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58 },
+			{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+			  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+			  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+			  0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f },
+			{ 0xd2, 0x96, 0xcd, 0x94, 0xc2, 0xcc, 0xcf, 0x8a,
+			  0x3a, 0x86, 0x30, 0x28, 0xb5, 0xe1, 0xdc, 0x0a,
+			  0x75, 0x86, 0x60, 0x2d, 0x25, 0x3c, 0xff, 0xf9,
+			  0x1b, 0x82, 0x66, 0xbe, 0xa6, 0xd6, 0x1a, 0xb1 },
+			32
+		}
+	};
+	int ret = 0;
+	u8 *buf;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(vectors); i++) {
+		struct cbc_test_vector *tv = &vectors[i];
+
+		buf = os_malloc(tv->len);
+		if (buf == NULL) {
+			ret++;
+			break;
+		}
+
+		os_memcpy(buf, tv->plain, tv->len);
+		if (aes_128_cbc_encrypt(tv->key, tv->iv, buf, tv->len) ||
+		    os_memcmp(buf, tv->cipher, tv->len) != 0) {
+			wpa_printf(MSG_ERROR, "AES-CBC encrypt %d failed", i);
+			ret++;
+		}
+
+		os_memcpy(buf, tv->cipher, tv->len);
+		if (aes_128_cbc_decrypt(tv->key, tv->iv, buf, tv->len) ||
+		    os_memcmp(buf, tv->plain, tv->len) != 0) {
+			wpa_printf(MSG_ERROR, "AES-CBC decrypt %d failed", i);
+			ret++;
+		}
+
+		os_free(buf);
+	}
+
+	return ret;
+}
+
+
+static int test_ecb(void)
+{
+#ifdef EAP_PSK
+	struct ecb_test_vector {
+		char *key;
+		char *plaintext;
+		char *ciphertext;
+	} vectors[] = {
+		/* CAVS 11.1 - ECBGFSbox128.rsp */
+		{
+			"00000000000000000000000000000000",
+			"f34481ec3cc627bacd5dc3fb08f273e6",
+			"0336763e966d92595a567cc9ce537f5e"
+		},
+		{
+			"00000000000000000000000000000000",
+			"9798c4640bad75c7c3227db910174e72",
+			"a9a1631bf4996954ebc093957b234589"
+		},
+		{
+			"00000000000000000000000000000000",
+			"96ab5c2ff612d9dfaae8c31f30c42168",
+			"ff4f8391a6a40ca5b25d23bedd44a597"
+		},
+		{
+			"00000000000000000000000000000000",
+			"6a118a874519e64e9963798a503f1d35",
+			"dc43be40be0e53712f7e2bf5ca707209"
+		},
+		{
+			"00000000000000000000000000000000",
+			"cb9fceec81286ca3e989bd979b0cb284",
+			"92beedab1895a94faa69b632e5cc47ce"
+		},
+		{
+			"00000000000000000000000000000000",
+			"b26aeb1874e47ca8358ff22378f09144",
+			"459264f4798f6a78bacb89c15ed3d601"
+		},
+		{
+			"00000000000000000000000000000000",
+			"58c8e00b2631686d54eab84b91f0aca1",
+			"08a4e2efec8a8e3312ca7460b9040bbf"
+		},
+		/* CAVS 11.1 - ECBKeySbox128.rsp */
+		{
+			"10a58869d74be5a374cf867cfb473859",
+			"00000000000000000000000000000000",
+			"6d251e6944b051e04eaa6fb4dbf78465"
+		},
+		{
+			"caea65cdbb75e9169ecd22ebe6e54675",
+			"00000000000000000000000000000000",
+			"6e29201190152df4ee058139def610bb",
+		}
+	};
+	int ret = 0;
+	unsigned int i;
+	u8 key[16], plain[16], cipher[16], out[16];
+
+	for (i = 0; i < ARRAY_SIZE(vectors); i++) {
+		struct ecb_test_vector *tv = &vectors[i];
+
+		if (hexstr2bin(tv->key, key, sizeof(key)) ||
+		    hexstr2bin(tv->plaintext, plain, sizeof(plain)) ||
+		    hexstr2bin(tv->ciphertext, cipher, sizeof(cipher))) {
+			wpa_printf(MSG_ERROR, "Invalid AES-ECB test vector %u",
+				   i);
+			ret++;
+			continue;
+		}
+
+		if (aes_128_encrypt_block(key, plain, out) < 0 ||
+		    os_memcmp(out, cipher, 16) != 0) {
+			wpa_printf(MSG_ERROR, "AES-ECB encrypt %u failed", i);
+			ret++;
+		}
+	}
+
+	if (!ret)
+		wpa_printf(MSG_INFO, "AES ECB mode test cases passed");
+
+	return ret;
+#endif /* EAP_PSK */
+
+	return 0;
+}
+
+
+static int test_key_wrap(void)
+{
+	int ret = 0;
+
+	/* RFC 3394 - Test vector 4.1 */
+	u8 kek41[] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+	};
+	u8 plain41[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
+	};
+	u8 crypt41[] = {
+		0x1F, 0xA6, 0x8B, 0x0A, 0x81, 0x12, 0xB4, 0x47,
+		0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B, 0x82,
+		0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5
+	};
+#ifndef CONFIG_BORINGSSL
+	/* RFC 3394 - Test vector 4.2 */
+	u8 kek42[] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17
+	};
+	u8 plain42[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
+	};
+	u8 crypt42[] = {
+		0x96, 0x77, 0x8B, 0x25, 0xAE, 0x6C, 0xA4, 0x35,
+		0xF9, 0x2B, 0x5B, 0x97, 0xC0, 0x50, 0xAE, 0xD2,
+		0x46, 0x8A, 0xB8, 0xA1, 0x7A, 0xD8, 0x4E, 0x5D
+	};
+#endif /* CONFIG_BORINGSSL */
+	/* RFC 3394 - Test vector 4.3 */
+	u8 kek43[] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+		0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
+	};
+	u8 plain43[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
+	};
+	u8 crypt43[] = {
+		0x64, 0xE8, 0xC3, 0xF9, 0xCE, 0x0F, 0x5B, 0xA2,
+		0x63, 0xE9, 0x77, 0x79, 0x05, 0x81, 0x8A, 0x2A,
+		0x93, 0xC8, 0x19, 0x1E, 0x7D, 0x6E, 0x8A, 0xE7,
+	};
+#ifndef CONFIG_BORINGSSL
+	/* RFC 3394 - Test vector 4.4 */
+	u8 kek44[] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17
+	};
+	u8 plain44[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
+	};
+	u8 crypt44[] = {
+		0x03, 0x1D, 0x33, 0x26, 0x4E, 0x15, 0xD3, 0x32,
+		0x68, 0xF2, 0x4E, 0xC2, 0x60, 0x74, 0x3E, 0xDC,
+		0xE1, 0xC6, 0xC7, 0xDD, 0xEE, 0x72, 0x5A, 0x93,
+		0x6B, 0xA8, 0x14, 0x91, 0x5C, 0x67, 0x62, 0xD2
+	};
+#endif /* CONFIG_BORINGSSL */
+	/* RFC 3394 - Test vector 4.5 */
+	u8 kek45[] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+		0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
+	};
+	u8 plain45[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
+	};
+	u8 crypt45[] = {
+		0xA8, 0xF9, 0xBC, 0x16, 0x12, 0xC6, 0x8B, 0x3F,
+		0xF6, 0xE6, 0xF4, 0xFB, 0xE3, 0x0E, 0x71, 0xE4,
+		0x76, 0x9C, 0x8B, 0x80, 0xA3, 0x2C, 0xB8, 0x95,
+		0x8C, 0xD5, 0xD1, 0x7D, 0x6B, 0x25, 0x4D, 0xA1,
+	};
+	/* RFC 3394 - Test vector 4.6 */
+	u8 kek46[] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+		0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
+	};
+	u8 plain46[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
+	};
+	u8 crypt46[] = {
+		0x28, 0xC9, 0xF4, 0x04, 0xC4, 0xB8, 0x10, 0xF4,
+		0xCB, 0xCC, 0xB3, 0x5C, 0xFB, 0x87, 0xF8, 0x26,
+		0x3F, 0x57, 0x86, 0xE2, 0xD8, 0x0E, 0xD3, 0x26,
+		0xCB, 0xC7, 0xF0, 0xE7, 0x1A, 0x99, 0xF4, 0x3B,
+		0xFB, 0x98, 0x8B, 0x9B, 0x7A, 0x02, 0xDD, 0x21
+	};
+	u8 result[40];
+
+	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.1");
+	if (aes_wrap(kek41, sizeof(kek41), sizeof(plain41) / 8, plain41,
+		     result)) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-128 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, crypt41, sizeof(crypt41)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-128 failed");
+		ret++;
+	}
+	if (aes_unwrap(kek41, sizeof(kek41), sizeof(plain41) / 8, crypt41,
+		       result)) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-128 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, plain41, sizeof(plain41)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-128 failed");
+		ret++;
+	}
+
+#ifndef CONFIG_BORINGSSL
+	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.2");
+	if (aes_wrap(kek42, sizeof(kek42), sizeof(plain42) / 8, plain42,
+		     result)) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-192 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, crypt42, sizeof(crypt42)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-192 failed");
+		ret++;
+	}
+	if (aes_unwrap(kek42, sizeof(kek42), sizeof(plain42) / 8, crypt42,
+		       result)) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-192 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, plain42, sizeof(plain42)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-192 failed");
+		ret++;
+	}
+#endif /* CONFIG_BORINGSSL */
+
+	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.3");
+	if (aes_wrap(kek43, sizeof(kek43), sizeof(plain43) / 8, plain43,
+		     result)) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-256 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, crypt43, sizeof(crypt43)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-256 failed");
+		ret++;
+	}
+	if (aes_unwrap(kek43, sizeof(kek43), sizeof(plain43) / 8, crypt43,
+		       result)) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-256 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, plain43, sizeof(plain43)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-256 failed");
+		ret++;
+	}
+
+#ifndef CONFIG_BORINGSSL
+	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.4");
+	if (aes_wrap(kek44, sizeof(kek44), sizeof(plain44) / 8, plain44,
+		     result)) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-192 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, crypt44, sizeof(crypt44)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-192 failed");
+		ret++;
+	}
+	if (aes_unwrap(kek44, sizeof(kek44), sizeof(plain44) / 8, crypt44,
+		       result)) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-192 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, plain44, sizeof(plain44)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-192 failed");
+		ret++;
+	}
+#endif /* CONFIG_BORINGSSL */
+
+	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.5");
+	if (aes_wrap(kek45, sizeof(kek45), sizeof(plain45) / 8, plain45,
+		     result)) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-256 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, crypt45, sizeof(crypt45)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-256 failed");
+		ret++;
+	}
+	if (aes_unwrap(kek45, sizeof(kek45), sizeof(plain45) / 8, crypt45,
+		       result)) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-256 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, plain45, sizeof(plain45)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-256 failed");
+		ret++;
+	}
+
+	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.6");
+	if (aes_wrap(kek46, sizeof(kek46), sizeof(plain46) / 8, plain46,
+		     result)) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-256 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, crypt46, sizeof(crypt46)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-256 failed");
+		ret++;
+	}
+	if (aes_unwrap(kek46, sizeof(kek46), sizeof(plain46) / 8, crypt46,
+		       result)) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-256 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, plain46, sizeof(plain46)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-256 failed");
+		ret++;
+	}
+
+	if (!ret)
+		wpa_printf(MSG_INFO, "AES key wrap/unwrap test cases passed");
+
+	return ret;
+}
+
+
+static int test_aes_ctr(void)
+{
+	int res = 0;
+
+#if defined(CONFIG_MESH) || defined(CONFIG_PSK)
+	/* CTR-AES*.Encrypt test vectors from NIST SP 800-38a */
+	const u8 key128[] = {
+		0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+		0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+	};
+	const u8 counter128[] = {
+		0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+		0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+	};
+	const u8 plain128[] = {
+		0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+		0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+		0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+		0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+		0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+		0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+		0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+		0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+	};
+	const u8 cipher128[] = {
+		0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
+		0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+		0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
+		0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
+		0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e,
+		0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
+		0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1,
+		0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee
+	};
+	const u8 key192[] = {
+		0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52,
+		0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5,
+		0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b
+	};
+	const u8 counter192[] = {
+		0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+		0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+	};
+	const u8 plain192[] = {
+		0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+		0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+		0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+		0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+		0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+		0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+		0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+		0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+	};
+	const u8 cipher192[] = {
+		0x1a, 0xbc, 0x93, 0x24, 0x17, 0x52, 0x1c, 0xa2,
+		0x4f, 0x2b, 0x04, 0x59, 0xfe, 0x7e, 0x6e, 0x0b,
+		0x09, 0x03, 0x39, 0xec, 0x0a, 0xa6, 0xfa, 0xef,
+		0xd5, 0xcc, 0xc2, 0xc6, 0xf4, 0xce, 0x8e, 0x94,
+		0x1e, 0x36, 0xb2, 0x6b, 0xd1, 0xeb, 0xc6, 0x70,
+		0xd1, 0xbd, 0x1d, 0x66, 0x56, 0x20, 0xab, 0xf7,
+		0x4f, 0x78, 0xa7, 0xf6, 0xd2, 0x98, 0x09, 0x58,
+		0x5a, 0x97, 0xda, 0xec, 0x58, 0xc6, 0xb0, 0x50
+	};
+	const u8 key256[] = {
+		0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe,
+		0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
+		0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7,
+		0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4
+	};
+	const u8 counter256[] = {
+		0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+		0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+	};
+	const u8 plain256[] = {
+		0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+		0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+		0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+		0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+		0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+		0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+		0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+		0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+	};
+	const u8 cipher256[] = {
+		0x60, 0x1e, 0xc3, 0x13, 0x77, 0x57, 0x89, 0xa5,
+		0xb7, 0xa7, 0xf5, 0x04, 0xbb, 0xf3, 0xd2, 0x28,
+		0xf4, 0x43, 0xe3, 0xca, 0x4d, 0x62, 0xb5, 0x9a,
+		0xca, 0x84, 0xe9, 0x90, 0xca, 0xca, 0xf5, 0xc5,
+		0x2b, 0x09, 0x30, 0xda, 0xa2, 0x3d, 0xe9, 0x4c,
+		0xe8, 0x70, 0x17, 0xba, 0x2d, 0x84, 0x98, 0x8d,
+		0xdf, 0xc9, 0xc5, 0x8d, 0xb6, 0x7a, 0xad, 0xa6,
+		0x13, 0xc2, 0xdd, 0x08, 0x45, 0x79, 0x41, 0xa6
+	};
+	size_t len;
+	u8 *tmp;
+
+	wpa_printf(MSG_DEBUG, "CTR-AES128.Encrypt");
+	len = sizeof(plain128);
+	tmp = os_malloc(len);
+	if (!tmp)
+		return -1;
+	os_memcpy(tmp, plain128, len);
+	if (aes_ctr_encrypt(key128, sizeof(key128), counter128, tmp, len) < 0) {
+		wpa_printf(MSG_ERROR, "aes_ctr_encrypt() failed");
+		res = -1;
+	} else if (os_memcmp(tmp, cipher128, len) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "CTR-AES128.Encrypt test vector did not match");
+		res = -1;
+	}
+	os_free(tmp);
+
+	wpa_printf(MSG_DEBUG, "CTR-AES192.Encrypt");
+	len = sizeof(plain192);
+	tmp = os_malloc(len);
+	if (!tmp)
+		return -1;
+	os_memcpy(tmp, plain192, len);
+	if (aes_ctr_encrypt(key192, sizeof(key192), counter192, tmp, len) < 0) {
+		wpa_printf(MSG_ERROR, "aes_ctr_encrypt() failed");
+		res = -1;
+	} else if (os_memcmp(tmp, cipher192, len) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "CTR-AES192.Encrypt test vector did not match");
+		res = -1;
+	}
+	os_free(tmp);
+
+	wpa_printf(MSG_DEBUG, "CTR-AES256.Encrypt");
+	len = sizeof(plain256);
+	tmp = os_malloc(len);
+	if (!tmp)
+		return -1;
+	os_memcpy(tmp, plain256, len);
+	if (aes_ctr_encrypt(key256, sizeof(key256), counter256, tmp, len) < 0) {
+		wpa_printf(MSG_ERROR, "aes_ctr_encrypt() failed");
+		res = -1;
+	} else if (os_memcmp(tmp, cipher256, len) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "CTR-AES256.Encrypt test vector did not match");
+		res = -1;
+	}
+	os_free(tmp);
+#endif
+
+	return res;
+}
+
+
+static int test_md5(void)
+{
+#ifndef CONFIG_FIPS
+	struct {
+		char *data;
+		char *hash;
+	} tests[] = {
+		{
+			"",
+			"\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04"
+			"\xe9\x80\x09\x98\xec\xf8\x42\x7e"
+		},
+		{
+			"a",
+			"\x0c\xc1\x75\xb9\xc0\xf1\xb6\xa8"
+			"\x31\xc3\x99\xe2\x69\x77\x26\x61"
+		},
+		{
+			"abc",
+			"\x90\x01\x50\x98\x3c\xd2\x4f\xb0"
+			"\xd6\x96\x3f\x7d\x28\xe1\x7f\x72"
+		},
+		{
+			"message digest",
+			"\xf9\x6b\x69\x7d\x7c\xb7\x93\x8d"
+			"\x52\x5a\x2f\x31\xaa\xf1\x61\xd0"
+		},
+		{
+			"abcdefghijklmnopqrstuvwxyz",
+			"\xc3\xfc\xd3\xd7\x61\x92\xe4\x00"
+			"\x7d\xfb\x49\x6c\xca\x67\xe1\x3b"
+		},
+		{
+			"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+			"0123456789",
+			"\xd1\x74\xab\x98\xd2\x77\xd9\xf5"
+			"\xa5\x61\x1c\x2c\x9f\x41\x9d\x9f"
+		},
+		{
+			"12345678901234567890123456789012345678901234567890"
+			"123456789012345678901234567890",
+			"\x57\xed\xf4\xa2\x2b\xe3\xc9\x55"
+			"\xac\x49\xda\x2e\x21\x07\xb6\x7a"
+		}
+	};
+	unsigned int i;
+	u8 hash[16];
+	const u8 *addr[2];
+	size_t len[2];
+	int errors = 0;
+
+	for (i = 0; i < ARRAY_SIZE(tests); i++) {
+		wpa_printf(MSG_INFO, "MD5 test case %d", i);
+
+		addr[0] = (u8 *) tests[i].data;
+		len[0] = strlen(tests[i].data);
+		if (md5_vector(1, addr, len, hash) < 0 ||
+		    os_memcmp(hash, tests[i].hash, 16) != 0) {
+			wpa_printf(MSG_INFO, " FAIL");
+			errors++;
+		} else
+			wpa_printf(MSG_INFO, " OK");
+
+		if (len[0]) {
+			addr[0] = (u8 *) tests[i].data;
+			len[0] = strlen(tests[i].data);
+			addr[1] = (u8 *) tests[i].data + 1;
+			len[1] = strlen(tests[i].data) - 1;
+			if (md5_vector(1, addr, len, hash) < 0 ||
+			    os_memcmp(hash, tests[i].hash, 16) != 0) {
+				wpa_printf(MSG_INFO, " FAIL");
+				errors++;
+			} else
+				wpa_printf(MSG_INFO, " OK");
+		}
+	}
+
+	if (!errors)
+		wpa_printf(MSG_INFO, "MD5 test cases passed");
+
+	return errors;
+#else /* CONFIG_FIPS */
+	wpa_printf(MSG_INFO, "MD5 test cases skipped due to CONFIG_FIPS");
+	return 0;
+#endif /* CONFIG_FIPS */
+}
+
+
+static int test_eap_fast(void)
+{
+#ifdef EAP_FAST
+	/* RFC 4851, Appendix B.1 */
+	const u8 pac_key[] = {
+		0x0B, 0x97, 0x39, 0x0F, 0x37, 0x51, 0x78, 0x09,
+		0x81, 0x1E, 0xFD, 0x9C, 0x6E, 0x65, 0x94, 0x2B,
+		0x63, 0x2C, 0xE9, 0x53, 0x89, 0x38, 0x08, 0xBA,
+		0x36, 0x0B, 0x03, 0x7C, 0xD1, 0x85, 0xE4, 0x14
+	};
+	const u8 seed[] = {
+		0x3F, 0xFB, 0x11, 0xC4, 0x6C, 0xBF, 0xA5, 0x7A,
+		0x54, 0x40, 0xDA, 0xE8, 0x22, 0xD3, 0x11, 0xD3,
+		0xF7, 0x6D, 0xE4, 0x1D, 0xD9, 0x33, 0xE5, 0x93,
+		0x70, 0x97, 0xEB, 0xA9, 0xB3, 0x66, 0xF4, 0x2A,
+		0x00, 0x00, 0x00, 0x02, 0x6A, 0x66, 0x43, 0x2A,
+		0x8D, 0x14, 0x43, 0x2C, 0xEC, 0x58, 0x2D, 0x2F,
+		0xC7, 0x9C, 0x33, 0x64, 0xBA, 0x04, 0xAD, 0x3A,
+		0x52, 0x54, 0xD6, 0xA5, 0x79, 0xAD, 0x1E, 0x00
+	};
+	const u8 master_secret[] = {
+		0x4A, 0x1A, 0x51, 0x2C, 0x01, 0x60, 0xBC, 0x02,
+		0x3C, 0xCF, 0xBC, 0x83, 0x3F, 0x03, 0xBC, 0x64,
+		0x88, 0xC1, 0x31, 0x2F, 0x0B, 0xA9, 0xA2, 0x77,
+		0x16, 0xA8, 0xD8, 0xE8, 0xBD, 0xC9, 0xD2, 0x29,
+		0x38, 0x4B, 0x7A, 0x85, 0xBE, 0x16, 0x4D, 0x27,
+		0x33, 0xD5, 0x24, 0x79, 0x87, 0xB1, 0xC5, 0xA2
+	};
+#ifndef CONFIG_FIPS
+	const u8 key_block[] = {
+		0x59, 0x59, 0xBE, 0x8E, 0x41, 0x3A, 0x77, 0x74,
+		0x8B, 0xB2, 0xE5, 0xD3, 0x60, 0xAC, 0x4D, 0x35,
+		0xDF, 0xFB, 0xC8, 0x1E, 0x9C, 0x24, 0x9C, 0x8B,
+		0x0E, 0xC3, 0x1D, 0x72, 0xC8, 0x84, 0x9D, 0x57,
+		0x48, 0x51, 0x2E, 0x45, 0x97, 0x6C, 0x88, 0x70,
+		0xBE, 0x5F, 0x01, 0xD3, 0x64, 0xE7, 0x4C, 0xBB,
+		0x11, 0x24, 0xE3, 0x49, 0xE2, 0x3B, 0xCD, 0xEF,
+		0x7A, 0xB3, 0x05, 0x39, 0x5D, 0x64, 0x8A, 0x44,
+		0x11, 0xB6, 0x69, 0x88, 0x34, 0x2E, 0x8E, 0x29,
+		0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05,
+		0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96,
+		0x8F, 0x0B, 0x5E, 0x06, 0x46, 0x7A, 0x44, 0x84,
+		0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98,
+		0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71
+	};
+#endif /* CONFIG_FIPS */
+	const u8 sks[] = {
+		0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05,
+		0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96,
+		0x8F, 0x0B, 0x5E, 0x06, 0x46, 0x7A, 0x44, 0x84,
+		0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98,
+		0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71
+	};
+	const u8 isk[] = {
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+	};
+	const u8 imck[] = {
+		0x16, 0x15, 0x3C, 0x3F, 0x21, 0x55, 0xEF, 0xD9,
+		0x7F, 0x34, 0xAE, 0xC8, 0x1A, 0x4E, 0x66, 0x80,
+		0x4C, 0xC3, 0x76, 0xF2, 0x8A, 0xA9, 0x6F, 0x96,
+		0xC2, 0x54, 0x5F, 0x8C, 0xAB, 0x65, 0x02, 0xE1,
+		0x18, 0x40, 0x7B, 0x56, 0xBE, 0xEA, 0xA7, 0xC5,
+		0x76, 0x5D, 0x8F, 0x0B, 0xC5, 0x07, 0xC6, 0xB9,
+		0x04, 0xD0, 0x69, 0x56, 0x72, 0x8B, 0x6B, 0xB8,
+		0x15, 0xEC, 0x57, 0x7B
+	};
+	const u8 msk[] = {
+		0x4D, 0x83, 0xA9, 0xBE, 0x6F, 0x8A, 0x74, 0xED,
+		0x6A, 0x02, 0x66, 0x0A, 0x63, 0x4D, 0x2C, 0x33,
+		0xC2, 0xDA, 0x60, 0x15, 0xC6, 0x37, 0x04, 0x51,
+		0x90, 0x38, 0x63, 0xDA, 0x54, 0x3E, 0x14, 0xB9,
+		0x27, 0x99, 0x18, 0x1E, 0x07, 0xBF, 0x0F, 0x5A,
+		0x5E, 0x3C, 0x32, 0x93, 0x80, 0x8C, 0x6C, 0x49,
+		0x67, 0xED, 0x24, 0xFE, 0x45, 0x40, 0xA0, 0x59,
+		0x5E, 0x37, 0xC2, 0xE9, 0xD0, 0x5D, 0x0A, 0xE3
+	};
+	const u8 emsk[] = {
+		0x3A, 0xD4, 0xAB, 0xDB, 0x76, 0xB2, 0x7F, 0x3B,
+		0xEA, 0x32, 0x2C, 0x2B, 0x74, 0xF4, 0x28, 0x55,
+		0xEF, 0x2D, 0xBA, 0x78, 0xC9, 0x57, 0x2F, 0x0D,
+		0x06, 0xCD, 0x51, 0x7C, 0x20, 0x93, 0x98, 0xA9,
+		0x76, 0xEA, 0x70, 0x21, 0xD7, 0x0E, 0x25, 0x54,
+		0x97, 0xED, 0xB2, 0x8A, 0xF6, 0xED, 0xFD, 0x0A,
+		0x2A, 0xE7, 0xA1, 0x58, 0x90, 0x10, 0x50, 0x44,
+		0xB3, 0x82, 0x85, 0xDB, 0x06, 0x14, 0xD2, 0xF9
+	};
+	/* RFC 4851, Appendix B.2 */
+	u8 tlv[] = {
+		0x80, 0x0C, 0x00, 0x38, 0x00, 0x01, 0x01, 0x00,
+		0xD8, 0x6A, 0x8C, 0x68, 0x3C, 0x32, 0x31, 0xA8,
+		0x56, 0x63, 0xB6, 0x40, 0x21, 0xFE, 0x21, 0x14,
+		0x4E, 0xE7, 0x54, 0x20, 0x79, 0x2D, 0x42, 0x62,
+		0xC9, 0xBF, 0x53, 0x7F, 0x54, 0xFD, 0xAC, 0x58,
+		0x43, 0x24, 0x6E, 0x30, 0x92, 0x17, 0x6D, 0xCF,
+		0xE6, 0xE0, 0x69, 0xEB, 0x33, 0x61, 0x6A, 0xCC,
+		0x05, 0xC5, 0x5B, 0xB7
+	};
+	const u8 compound_mac[] = {
+		0x43, 0x24, 0x6E, 0x30, 0x92, 0x17, 0x6D, 0xCF,
+		0xE6, 0xE0, 0x69, 0xEB, 0x33, 0x61, 0x6A, 0xCC,
+		0x05, 0xC5, 0x5B, 0xB7
+	};
+	u8 buf[512];
+	const u8 *simck, *cmk;
+	int errors = 0;
+
+	wpa_printf(MSG_INFO, "EAP-FAST test cases");
+
+	wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / master_secret");
+	if (sha1_t_prf(pac_key, sizeof(pac_key),
+		       "PAC to master secret label hash",
+		       seed, sizeof(seed), buf, sizeof(master_secret)) < 0 ||
+	    os_memcmp(master_secret, buf, sizeof(master_secret)) != 0) {
+		wpa_printf(MSG_INFO, "T-PRF test - FAILED!");
+		errors++;
+	}
+
+#ifndef CONFIG_FIPS
+	wpa_printf(MSG_INFO, "- PRF (TLS, SHA1/MD5) test case / key_block");
+	if (tls_prf_sha1_md5(master_secret, sizeof(master_secret),
+			     "key expansion", seed, sizeof(seed),
+			     buf, sizeof(key_block)) ||
+	    os_memcmp(key_block, buf, sizeof(key_block)) != 0) {
+		wpa_printf(MSG_INFO, "PRF test - FAILED!");
+		errors++;
+	}
+#endif /* CONFIG_FIPS */
+
+	wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / IMCK");
+	if (sha1_t_prf(sks, sizeof(sks), "Inner Methods Compound Keys",
+		       isk, sizeof(isk), buf, sizeof(imck)) < 0 ||
+	    os_memcmp(imck, buf, sizeof(imck)) != 0) {
+		wpa_printf(MSG_INFO, "T-PRF test - FAILED!");
+		errors++;
+	}
+
+	simck = imck;
+	cmk = imck + 40;
+
+	wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / MSK");
+	if (sha1_t_prf(simck, 40, "Session Key Generating Function",
+		       (u8 *) "", 0, buf, sizeof(msk)) < 0 ||
+	    os_memcmp(msk, buf, sizeof(msk)) != 0) {
+		wpa_printf(MSG_INFO, "T-PRF test - FAILED!");
+		errors++;
+	}
+
+	wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / EMSK");
+	if (sha1_t_prf(simck, 40, "Extended Session Key Generating Function",
+		       (u8 *) "", 0, buf, sizeof(msk)) < 0 ||
+	    os_memcmp(emsk, buf, sizeof(emsk)) != 0) {
+		wpa_printf(MSG_INFO, "T-PRF test - FAILED!");
+		errors++;
+	}
+
+	wpa_printf(MSG_INFO, "- Compound MAC test case");
+	os_memset(tlv + sizeof(tlv) - 20, 0, 20);
+	if (hmac_sha1(cmk, 20, tlv, sizeof(tlv), tlv + sizeof(tlv) - 20) < 0 ||
+	    os_memcmp(tlv + sizeof(tlv) - 20, compound_mac,
+		      sizeof(compound_mac)) != 0) {
+		wpa_printf(MSG_INFO, "Compound MAC test - FAILED!");
+		errors++;
+	}
+
+	return errors;
+#else /* EAP_FAST */
+	return 0;
+#endif /* EAP_FAST */
+}
+
+
+static const u8 key0[] =
+{
+	0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+	0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+	0x0b, 0x0b, 0x0b, 0x0b
+};
+static const u8 data0[] = "Hi There";
+static const u8 prf0[] =
+{
+	0xbc, 0xd4, 0xc6, 0x50, 0xb3, 0x0b, 0x96, 0x84,
+	0x95, 0x18, 0x29, 0xe0, 0xd7, 0x5f, 0x9d, 0x54,
+	0xb8, 0x62, 0x17, 0x5e, 0xd9, 0xf0, 0x06, 0x06,
+	0xe1, 0x7d, 0x8d, 0xa3, 0x54, 0x02, 0xff, 0xee,
+	0x75, 0xdf, 0x78, 0xc3, 0xd3, 0x1e, 0x0f, 0x88,
+	0x9f, 0x01, 0x21, 0x20, 0xc0, 0x86, 0x2b, 0xeb,
+	0x67, 0x75, 0x3e, 0x74, 0x39, 0xae, 0x24, 0x2e,
+	0xdb, 0x83, 0x73, 0x69, 0x83, 0x56, 0xcf, 0x5a
+};
+
+static const u8 key1[] = "Jefe";
+static const u8 data1[] = "what do ya want for nothing?";
+static const u8 prf1[] =
+{
+	0x51, 0xf4, 0xde, 0x5b, 0x33, 0xf2, 0x49, 0xad,
+	0xf8, 0x1a, 0xeb, 0x71, 0x3a, 0x3c, 0x20, 0xf4,
+	0xfe, 0x63, 0x14, 0x46, 0xfa, 0xbd, 0xfa, 0x58,
+	0x24, 0x47, 0x59, 0xae, 0x58, 0xef, 0x90, 0x09,
+	0xa9, 0x9a, 0xbf, 0x4e, 0xac, 0x2c, 0xa5, 0xfa,
+	0x87, 0xe6, 0x92, 0xc4, 0x40, 0xeb, 0x40, 0x02,
+	0x3e, 0x7b, 0xab, 0xb2, 0x06, 0xd6, 0x1d, 0xe7,
+	0xb9, 0x2f, 0x41, 0x52, 0x90, 0x92, 0xb8, 0xfc
+};
+
+
+static const u8 key2[] =
+{
+	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+	0xaa, 0xaa, 0xaa, 0xaa
+};
+static const u8 data2[] =
+{
+	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+	0xdd, 0xdd
+};
+static const u8 prf2[] =
+{
+	0xe1, 0xac, 0x54, 0x6e, 0xc4, 0xcb, 0x63, 0x6f,
+	0x99, 0x76, 0x48, 0x7b, 0xe5, 0xc8, 0x6b, 0xe1,
+	0x7a, 0x02, 0x52, 0xca, 0x5d, 0x8d, 0x8d, 0xf1,
+	0x2c, 0xfb, 0x04, 0x73, 0x52, 0x52, 0x49, 0xce,
+	0x9d, 0xd8, 0xd1, 0x77, 0xea, 0xd7, 0x10, 0xbc,
+	0x9b, 0x59, 0x05, 0x47, 0x23, 0x91, 0x07, 0xae,
+	0xf7, 0xb4, 0xab, 0xd4, 0x3d, 0x87, 0xf0, 0xa6,
+	0x8f, 0x1c, 0xbd, 0x9e, 0x2b, 0x6f, 0x76, 0x07
+};
+
+
+struct passphrase_test {
+	char *passphrase;
+	char *ssid;
+	char psk[32];
+};
+
+static const struct passphrase_test passphrase_tests[] =
+{
+	{
+		"password",
+		"IEEE",
+		{
+			0xf4, 0x2c, 0x6f, 0xc5, 0x2d, 0xf0, 0xeb, 0xef,
+			0x9e, 0xbb, 0x4b, 0x90, 0xb3, 0x8a, 0x5f, 0x90,
+			0x2e, 0x83, 0xfe, 0x1b, 0x13, 0x5a, 0x70, 0xe2,
+			0x3a, 0xed, 0x76, 0x2e, 0x97, 0x10, 0xa1, 0x2e
+		}
+	},
+	{
+		"ThisIsAPassword",
+		"ThisIsASSID",
+		{
+			0x0d, 0xc0, 0xd6, 0xeb, 0x90, 0x55, 0x5e, 0xd6,
+			0x41, 0x97, 0x56, 0xb9, 0xa1, 0x5e, 0xc3, 0xe3,
+			0x20, 0x9b, 0x63, 0xdf, 0x70, 0x7d, 0xd5, 0x08,
+			0xd1, 0x45, 0x81, 0xf8, 0x98, 0x27, 0x21, 0xaf
+		}
+	},
+	{
+		"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+		"ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ",
+		{
+			0xbe, 0xcb, 0x93, 0x86, 0x6b, 0xb8, 0xc3, 0x83,
+			0x2c, 0xb7, 0x77, 0xc2, 0xf5, 0x59, 0x80, 0x7c,
+			0x8c, 0x59, 0xaf, 0xcb, 0x6e, 0xae, 0x73, 0x48,
+			0x85, 0x00, 0x13, 0x00, 0xa9, 0x81, 0xcc, 0x62
+		}
+	},
+};
+
+#define NUM_PASSPHRASE_TESTS ARRAY_SIZE(passphrase_tests)
+
+
+struct rfc6070_test {
+	char *p;
+	char *s;
+	int c;
+	char dk[32];
+	size_t dk_len;
+};
+
+static const struct rfc6070_test rfc6070_tests[] =
+{
+	{
+		"password",
+		"salt",
+		1,
+		{
+			0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71,
+			0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06,
+			0x2f, 0xe0, 0x37, 0xa6
+		},
+		20
+	},
+	{
+		"password",
+		"salt",
+		2,
+		{
+			0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c,
+			0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0,
+			0xd8, 0xde, 0x89, 0x57
+		},
+		20
+	},
+	{
+		"password",
+		"salt",
+		4096,
+		{
+			0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a,
+			0xbe, 0xad, 0x49, 0xd9, 0x26, 0xf7, 0x21, 0xd0,
+			0x65, 0xa4, 0x29, 0xc1
+		},
+		20
+	},
+#if 0 /* This takes quite long to derive.. */
+	{
+		"password",
+		"salt",
+		16777216,
+		{
+			0xee, 0xfe, 0x3d, 0x61, 0xcd, 0x4d, 0xa4, 0xe4,
+			0xe9, 0x94, 0x5b, 0x3d, 0x6b, 0xa2, 0x15, 0x8c,
+			0x26, 0x34, 0xe9, 0x84
+		},
+		20
+	},
+#endif
+	{
+		"passwordPASSWORDpassword",
+		"saltSALTsaltSALTsaltSALTsaltSALTsalt",
+		4096,
+		{
+			0x3d, 0x2e, 0xec, 0x4f, 0xe4, 0x1c, 0x84, 0x9b,
+			0x80, 0xc8, 0xd8, 0x36, 0x62, 0xc0, 0xe4, 0x4a,
+			0x8b, 0x29, 0x1a, 0x96, 0x4c, 0xf2, 0xf0, 0x70,
+			0x38
+		},
+		25
+	},
+#if 0 /* \0 not currently supported in passphrase parameters.. */
+	{
+		"pass\0word",
+		"sa\0lt",
+		4096,
+		{
+			0x56, 0xfa, 0x6a, 0xa7, 0x55, 0x48, 0x09, 0x9d,
+			0xcc, 0x37, 0xd7, 0xf0, 0x34, 0x25, 0xe0, 0xc3
+		},
+		16
+	},
+#endif
+};
+
+#define NUM_RFC6070_TESTS ARRAY_SIZE(rfc6070_tests)
+
+
+static int test_sha1(void)
+{
+	u8 res[512];
+	int ret = 0;
+	unsigned int i;
+
+	wpa_printf(MSG_INFO, "PRF-SHA1 test cases:");
+
+	if (sha1_prf(key0, sizeof(key0), "prefix", data0, sizeof(data0) - 1,
+		     res, sizeof(prf0)) == 0 &&
+	    os_memcmp(res, prf0, sizeof(prf0)) == 0)
+		wpa_printf(MSG_INFO, "Test case 0 - OK");
+	else {
+		wpa_printf(MSG_INFO, "Test case 0 - FAILED!");
+		ret++;
+	}
+
+	if (sha1_prf(key1, sizeof(key1) - 1, "prefix", data1, sizeof(data1) - 1,
+		     res, sizeof(prf1)) == 0 &&
+	    os_memcmp(res, prf1, sizeof(prf1)) == 0)
+		wpa_printf(MSG_INFO, "Test case 1 - OK");
+	else {
+		wpa_printf(MSG_INFO, "Test case 1 - FAILED!");
+		ret++;
+	}
+
+	if (sha1_prf(key2, sizeof(key2), "prefix", data2, sizeof(data2),
+		     res, sizeof(prf2)) == 0 &&
+	    os_memcmp(res, prf2, sizeof(prf2)) == 0)
+		wpa_printf(MSG_INFO, "Test case 2 - OK");
+	else {
+		wpa_printf(MSG_INFO, "Test case 2 - FAILED!");
+		ret++;
+	}
+
+	ret += test_eap_fast();
+
+	wpa_printf(MSG_INFO, "PBKDF2-SHA1 Passphrase test cases:");
+	for (i = 0; i < NUM_PASSPHRASE_TESTS; i++) {
+		u8 psk[32];
+		const struct passphrase_test *test = &passphrase_tests[i];
+
+		if (pbkdf2_sha1(test->passphrase,
+				(const u8 *) test->ssid, strlen(test->ssid),
+				4096, psk, 32) == 0 &&
+		    os_memcmp(psk, test->psk, 32) == 0)
+			wpa_printf(MSG_INFO, "Test case %d - OK", i);
+		else {
+			wpa_printf(MSG_INFO, "Test case %d - FAILED!", i);
+			ret++;
+		}
+	}
+
+	wpa_printf(MSG_INFO, "PBKDF2-SHA1 test cases (RFC 6070):");
+	for (i = 0; i < NUM_RFC6070_TESTS; i++) {
+		u8 dk[25];
+		const struct rfc6070_test *test = &rfc6070_tests[i];
+
+		if (pbkdf2_sha1(test->p, (const u8 *) test->s, strlen(test->s),
+				test->c, dk, test->dk_len) == 0 &&
+		    os_memcmp(dk, test->dk, test->dk_len) == 0)
+			wpa_printf(MSG_INFO, "Test case %d - OK", i);
+		else {
+			wpa_printf(MSG_INFO, "Test case %d - FAILED!", i);
+			ret++;
+		}
+	}
+
+	if (!ret)
+		wpa_printf(MSG_INFO, "SHA1 test cases passed");
+	return ret;
+}
+
+
+static const struct {
+	char *data;
+	u8 hash[32];
+} tests[] = {
+	{
+		"abc",
+		{
+			0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
+			0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
+			0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
+			0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad
+		}
+	},
+	{
+		"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+		{
+			0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8,
+			0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39,
+			0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67,
+			0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1
+		}
+	}
+};
+
+static const struct hmac_test {
+	u8 key[150];
+	size_t key_len;
+	u8 data[160];
+	size_t data_len;
+	u8 hash[32]; /* HMAC-SHA-256 */
+	u8 hash384[48]; /* HMAC-SHA-384 */
+} hmac_tests[] = {
+	/* draft-ietf-ipsec-ciph-sha-256-01.txt; RFC 4231 */
+	{
+		{
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+			0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+			0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+			0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20
+		},
+		32,
+		"abc", 3,
+		{
+			0xa2, 0x1b, 0x1f, 0x5d, 0x4c, 0xf4, 0xf7, 0x3a,
+			0x4d, 0xd9, 0x39, 0x75, 0x0f, 0x7a, 0x06, 0x6a,
+			0x7f, 0x98, 0xcc, 0x13, 0x1c, 0xb1, 0x6a, 0x66,
+			0x92, 0x75, 0x90, 0x21, 0xcf, 0xab, 0x81, 0x81
+		},
+		{ }
+	},
+	{
+		{
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+			0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+			0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+			0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20
+		},
+		32,
+		"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+		56,
+		{
+			0x10, 0x4f, 0xdc, 0x12, 0x57, 0x32, 0x8f, 0x08,
+			0x18, 0x4b, 0xa7, 0x31, 0x31, 0xc5, 0x3c, 0xae,
+			0xe6, 0x98, 0xe3, 0x61, 0x19, 0x42, 0x11, 0x49,
+			0xea, 0x8c, 0x71, 0x24, 0x56, 0x69, 0x7d, 0x30
+		},
+		{ }
+	},
+	{
+		{
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+			0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+			0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+			0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20
+		},
+		32,
+		"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+		"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+		112,
+		{
+			0x47, 0x03, 0x05, 0xfc, 0x7e, 0x40, 0xfe, 0x34,
+			0xd3, 0xee, 0xb3, 0xe7, 0x73, 0xd9, 0x5a, 0xab,
+			0x73, 0xac, 0xf0, 0xfd, 0x06, 0x04, 0x47, 0xa5,
+			0xeb, 0x45, 0x95, 0xbf, 0x33, 0xa9, 0xd1, 0xa3
+		},
+		{ }
+	},
+	{
+		{
+			0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+			0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+			0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+			0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b
+		},
+		32,
+		"Hi There",
+		8,
+		{
+			0x19, 0x8a, 0x60, 0x7e, 0xb4, 0x4b, 0xfb, 0xc6,
+			0x99, 0x03, 0xa0, 0xf1, 0xcf, 0x2b, 0xbd, 0xc5,
+			0xba, 0x0a, 0xa3, 0xf3, 0xd9, 0xae, 0x3c, 0x1c,
+			0x7a, 0x3b, 0x16, 0x96, 0xa0, 0xb6, 0x8c, 0xf7
+		},
+		{ }
+	},
+	{ /* RFC 4231 - Test Case 1 */
+		{
+			0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+			0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+			0x0b, 0x0b, 0x0b, 0x0b
+		},
+		20,
+		"Hi There",
+		8,
+		{
+			0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53,
+			0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0x0b, 0xf1, 0x2b,
+			0x88, 0x1d, 0xc2, 0x00, 0xc9, 0x83, 0x3d, 0xa7,
+			0x26, 0xe9, 0x37, 0x6c, 0x2e, 0x32, 0xcf, 0xf7
+		},
+		{
+			0xaf, 0xd0, 0x39, 0x44, 0xd8, 0x48, 0x95, 0x62,
+			0x6b, 0x08, 0x25, 0xf4, 0xab, 0x46, 0x90, 0x7f,
+			0x15, 0xf9, 0xda, 0xdb, 0xe4, 0x10, 0x1e, 0xc6,
+			0x82, 0xaa, 0x03, 0x4c, 0x7c, 0xeb, 0xc5, 0x9c,
+			0xfa, 0xea, 0x9e, 0xa9, 0x07, 0x6e, 0xde, 0x7f,
+			0x4a, 0xf1, 0x52, 0xe8, 0xb2, 0xfa, 0x9c, 0xb6
+		}
+	},
+	{ /* RFC 4231 - Test Case 2 */
+		"Jefe",
+		4,
+		"what do ya want for nothing?",
+		28,
+		{
+			0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e,
+			0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7,
+			0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83,
+			0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43
+		},
+		{
+			0xaf, 0x45, 0xd2, 0xe3, 0x76, 0x48, 0x40, 0x31,
+			0x61, 0x7f, 0x78, 0xd2, 0xb5, 0x8a, 0x6b, 0x1b,
+			0x9c, 0x7e, 0xf4, 0x64, 0xf5, 0xa0, 0x1b, 0x47,
+			0xe4, 0x2e, 0xc3, 0x73, 0x63, 0x22, 0x44, 0x5e,
+			0x8e, 0x22, 0x40, 0xca, 0x5e, 0x69, 0xe2, 0xc7,
+			0x8b, 0x32, 0x39, 0xec, 0xfa, 0xb2, 0x16, 0x49
+		}
+	},
+	{
+		{
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+		},
+		32,
+		{
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd
+		},
+		50,
+		{
+			0xcd, 0xcb, 0x12, 0x20, 0xd1, 0xec, 0xcc, 0xea,
+			0x91, 0xe5, 0x3a, 0xba, 0x30, 0x92, 0xf9, 0x62,
+			0xe5, 0x49, 0xfe, 0x6c, 0xe9, 0xed, 0x7f, 0xdc,
+			0x43, 0x19, 0x1f, 0xbd, 0xe4, 0x5c, 0x30, 0xb0
+		},
+		{ }
+	},
+	{ /* RFC 4231 - Test Case 3 */
+		{
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa
+		},
+		20,
+		{
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd
+		},
+		50,
+		{
+			0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46,
+			0x85, 0x4d, 0xb8, 0xeb, 0xd0, 0x91, 0x81, 0xa7,
+			0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8, 0xc1, 0x22,
+			0xd9, 0x63, 0x55, 0x14, 0xce, 0xd5, 0x65, 0xfe
+		},
+		{
+			0x88, 0x06, 0x26, 0x08, 0xd3, 0xe6, 0xad, 0x8a,
+			0x0a, 0xa2, 0xac, 0xe0, 0x14, 0xc8, 0xa8, 0x6f,
+			0x0a, 0xa6, 0x35, 0xd9, 0x47, 0xac, 0x9f, 0xeb,
+			0xe8, 0x3e, 0xf4, 0xe5, 0x59, 0x66, 0x14, 0x4b,
+			0x2a, 0x5a, 0xb3, 0x9d, 0xc1, 0x38, 0x14, 0xb9,
+			0x4e, 0x3a, 0xb6, 0xe1, 0x01, 0xa3, 0x4f, 0x27
+		}
+	},
+	{
+		{
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+			0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+			0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+			0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+			0x21, 0x22, 0x23, 0x24, 0x25
+		},
+		37,
+		{
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd
+		},
+		50,
+		{
+			0xd4, 0x63, 0x3c, 0x17, 0xf6, 0xfb, 0x8d, 0x74,
+			0x4c, 0x66, 0xde, 0xe0, 0xf8, 0xf0, 0x74, 0x55,
+			0x6e, 0xc4, 0xaf, 0x55, 0xef, 0x07, 0x99, 0x85,
+			0x41, 0x46, 0x8e, 0xb4, 0x9b, 0xd2, 0xe9, 0x17
+		},
+		{ }
+	},
+	{ /* RFC 4231 - Test Case 4 */
+		{
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+			0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+			0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+			0x19,
+		},
+		25,
+		{
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd
+		},
+		50,
+		{
+			0x82, 0x55, 0x8a, 0x38, 0x9a, 0x44, 0x3c, 0x0e,
+			0xa4, 0xcc, 0x81, 0x98, 0x99, 0xf2, 0x08, 0x3a,
+			0x85, 0xf0, 0xfa, 0xa3, 0xe5, 0x78, 0xf8, 0x07,
+			0x7a, 0x2e, 0x3f, 0xf4, 0x67, 0x29, 0x66, 0x5b
+		},
+		{
+			0x3e, 0x8a, 0x69, 0xb7, 0x78, 0x3c, 0x25, 0x85,
+			0x19, 0x33, 0xab, 0x62, 0x90, 0xaf, 0x6c, 0xa7,
+			0x7a, 0x99, 0x81, 0x48, 0x08, 0x50, 0x00, 0x9c,
+			0xc5, 0x57, 0x7c, 0x6e, 0x1f, 0x57, 0x3b, 0x4e,
+			0x68, 0x01, 0xdd, 0x23, 0xc4, 0xa7, 0xd6, 0x79,
+			0xcc, 0xf8, 0xa3, 0x86, 0xc6, 0x74, 0xcf, 0xfb
+		}
+	},
+	{
+		{
+			0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+			0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+			0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+			0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c
+		},
+		32,
+		"Test With Truncation",
+		20,
+		{
+			0x75, 0x46, 0xaf, 0x01, 0x84, 0x1f, 0xc0, 0x9b,
+			0x1a, 0xb9, 0xc3, 0x74, 0x9a, 0x5f, 0x1c, 0x17,
+			0xd4, 0xf5, 0x89, 0x66, 0x8a, 0x58, 0x7b, 0x27,
+			0x00, 0xa9, 0xc9, 0x7c, 0x11, 0x93, 0xcf, 0x42
+		},
+		{ }
+	},
+	{
+		{
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+		},
+		80,
+		"Test Using Larger Than Block-Size Key - Hash Key First",
+		54,
+		{
+			0x69, 0x53, 0x02, 0x5e, 0xd9, 0x6f, 0x0c, 0x09,
+			0xf8, 0x0a, 0x96, 0xf7, 0x8e, 0x65, 0x38, 0xdb,
+			0xe2, 0xe7, 0xb8, 0x20, 0xe3, 0xdd, 0x97, 0x0e,
+			0x7d, 0xdd, 0x39, 0x09, 0x1b, 0x32, 0x35, 0x2f
+		},
+		{ }
+	},
+	{ /* RFC 4231 - Test Case 6 */
+		{
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa
+		},
+		131,
+		"Test Using Larger Than Block-Size Key - Hash Key First",
+		54,
+		{
+			0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f,
+			0x0d, 0x8a, 0x26, 0xaa, 0xcb, 0xf5, 0xb7, 0x7f,
+			0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28, 0xc5, 0x14,
+			0x05, 0x46, 0x04, 0x0f, 0x0e, 0xe3, 0x7f, 0x54
+		},
+		{
+			0x4e, 0xce, 0x08, 0x44, 0x85, 0x81, 0x3e, 0x90,
+			0x88, 0xd2, 0xc6, 0x3a, 0x04, 0x1b, 0xc5, 0xb4,
+			0x4f, 0x9e, 0xf1, 0x01, 0x2a, 0x2b, 0x58, 0x8f,
+			0x3c, 0xd1, 0x1f, 0x05, 0x03, 0x3a, 0xc4, 0xc6,
+			0x0c, 0x2e, 0xf6, 0xab, 0x40, 0x30, 0xfe, 0x82,
+			0x96, 0x24, 0x8d, 0xf1, 0x63, 0xf4, 0x49, 0x52
+		}
+	},
+	{
+		{
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+		},
+		80,
+		"Test Using Larger Than Block-Size Key and Larger Than One "
+		"Block-Size Data",
+		73,
+		{
+			0x63, 0x55, 0xac, 0x22, 0xe8, 0x90, 0xd0, 0xa3,
+			0xc8, 0x48, 0x1a, 0x5c, 0xa4, 0x82, 0x5b, 0xc8,
+			0x84, 0xd3, 0xe7, 0xa1, 0xff, 0x98, 0xa2, 0xfc,
+			0x2a, 0xc7, 0xd8, 0xe0, 0x64, 0xc3, 0xb2, 0xe6
+		},
+		{ }
+	},
+	{ /* RFC 4231 - Test Case 7 */
+		{
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa
+		},
+		131,
+		"This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.",
+		152,
+		{
+			0x9b, 0x09, 0xff, 0xa7, 0x1b, 0x94, 0x2f, 0xcb,
+			0x27, 0x63, 0x5f, 0xbc, 0xd5, 0xb0, 0xe9, 0x44,
+			0xbf, 0xdc, 0x63, 0x64, 0x4f, 0x07, 0x13, 0x93,
+			0x8a, 0x7f, 0x51, 0x53, 0x5c, 0x3a, 0x35, 0xe2
+		},
+		{
+			0x66, 0x17, 0x17, 0x8e, 0x94, 0x1f, 0x02, 0x0d,
+			0x35, 0x1e, 0x2f, 0x25, 0x4e, 0x8f, 0xd3, 0x2c,
+			0x60, 0x24, 0x20, 0xfe, 0xb0, 0xb8, 0xfb, 0x9a,
+			0xdc, 0xce, 0xbb, 0x82, 0x46, 0x1e, 0x99, 0xc5,
+			0xa6, 0x78, 0xcc, 0x31, 0xe7, 0x99, 0x17, 0x6d,
+			0x38, 0x60, 0xe6, 0x11, 0x0c, 0x46, 0x52, 0x3e
+		}
+	}
+};
+
+
+static int test_sha256(void)
+{
+	unsigned int i;
+	u8 hash[32];
+	const u8 *addr[2];
+	size_t len[2];
+	int errors = 0;
+	u8 *key;
+
+	for (i = 0; i < ARRAY_SIZE(tests); i++) {
+		wpa_printf(MSG_INFO, "SHA256 test case %d:", i + 1);
+
+		addr[0] = (u8 *) tests[i].data;
+		len[0] = strlen(tests[i].data);
+		sha256_vector(1, addr, len, hash);
+		if (memcmp(hash, tests[i].hash, 32) != 0) {
+			wpa_printf(MSG_INFO, " FAIL");
+			errors++;
+		} else
+			wpa_printf(MSG_INFO, " OK");
+
+		if (len[0]) {
+			addr[0] = (u8 *) tests[i].data;
+			len[0] = 1;
+			addr[1] = (u8 *) tests[i].data + 1;
+			len[1] = strlen(tests[i].data) - 1;
+			sha256_vector(2, addr, len, hash);
+			if (memcmp(hash, tests[i].hash, 32) != 0) {
+				wpa_printf(MSG_INFO, " FAIL");
+				errors++;
+			} else
+				wpa_printf(MSG_INFO, " OK");
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(hmac_tests); i++) {
+		const struct hmac_test *t = &hmac_tests[i];
+
+		wpa_printf(MSG_INFO, "HMAC-SHA256 test case %d:", i + 1);
+
+		if (hmac_sha256(t->key, t->key_len, t->data, t->data_len,
+				hash) < 0 ||
+		    os_memcmp(hash, t->hash, 32) != 0) {
+			wpa_printf(MSG_INFO, " FAIL");
+			errors++;
+		} else
+			wpa_printf(MSG_INFO, " OK");
+
+		addr[0] = t->data;
+		len[0] = t->data_len;
+		if (hmac_sha256_vector(t->key, t->key_len, 1, addr, len,
+				       hash) < 0 ||
+		    os_memcmp(hash, t->hash, 32) != 0) {
+			wpa_printf(MSG_INFO, " FAIL");
+			errors++;
+		} else
+			wpa_printf(MSG_INFO, " OK");
+
+		if (len[0]) {
+			addr[0] = t->data;
+			len[0] = 1;
+			addr[1] = t->data + 1;
+			len[1] = t->data_len - 1;
+			if (hmac_sha256_vector(t->key, t->key_len, 2, addr, len,
+					       hash) < 0 ||
+			    os_memcmp(hash, t->hash, 32) != 0) {
+				wpa_printf(MSG_INFO, " FAIL");
+				errors++;
+			} else
+				wpa_printf(MSG_INFO, " OK");
+		}
+	}
+
+	wpa_printf(MSG_INFO, "Test IEEE 802.11r KDF");
+	sha256_prf((u8 *) "abc", 3, "KDF test", (u8 *) "data", 4,
+		   hash, sizeof(hash));
+	/* TODO: add proper test case for this */
+
+	key = os_malloc(8161);
+	if (key) {
+#ifdef CONFIG_HMAC_SHA256_KDF
+		int res;
+
+		res = hmac_sha256_kdf((u8 *) "secret", 6, "label",
+				      (u8 *) "seed", 4, key, 8160);
+		if (res) {
+			wpa_printf(MSG_INFO,
+				   "Unexpected hmac_sha256_kdf(outlen=8160) failure");
+			errors++;
+		}
+
+		res = hmac_sha256_kdf((u8 *) "secret", 6, "label",
+				      (u8 *) "seed", 4, key, 8161);
+		if (res == 0) {
+			wpa_printf(MSG_INFO,
+				   "Unexpected hmac_sha256_kdf(outlen=8161) success");
+			errors++;
+		}
+#endif /* CONFIG_HMAC_SHA256_KDF */
+
+		os_free(key);
+	}
+
+	if (!errors)
+		wpa_printf(MSG_INFO, "SHA256 test cases passed");
+	return errors;
+}
+
+
+static int test_sha384(void)
+{
+#ifdef CONFIG_SHA384
+	unsigned int i;
+	u8 hash[48];
+	const u8 *addr[2];
+	size_t len[2];
+	int errors = 0;
+	const char *data = "hello";
+	const u8 hash_res[] = {
+		0x59, 0xe1, 0x74, 0x87, 0x77, 0x44, 0x8c, 0x69,
+		0xde, 0x6b, 0x80, 0x0d, 0x7a, 0x33, 0xbb, 0xfb,
+		0x9f, 0xf1, 0xb4, 0x63, 0xe4, 0x43, 0x54, 0xc3,
+		0x55, 0x3b, 0xcd, 0xb9, 0xc6, 0x66, 0xfa, 0x90,
+		0x12, 0x5a, 0x3c, 0x79, 0xf9, 0x03, 0x97, 0xbd,
+		0xf5, 0xf6, 0xa1, 0x3d, 0xe8, 0x28, 0x68, 0x4f
+	};
+
+	addr[0] = (const u8 *) data;
+	len[0] = 5;
+	if (sha384_vector(1, addr, len, hash) < 0 ||
+	    os_memcmp(hash, hash_res, 48) != 0) {
+		wpa_printf(MSG_INFO, "SHA384 test case 1: FAIL");
+		errors++;
+	} else {
+		wpa_printf(MSG_INFO, "SHA384 test case 1: OK");
+	}
+
+	addr[0] = (const u8 *) data;
+	len[0] = 4;
+	addr[1] = (const u8 *) data + 4;
+	len[1] = 1;
+	if (sha384_vector(2, addr, len, hash) < 0 ||
+	    os_memcmp(hash, hash_res, 48) != 0) {
+		wpa_printf(MSG_INFO, "SHA384 test case 2: FAIL");
+		errors++;
+	} else {
+		wpa_printf(MSG_INFO, "SHA384 test case 2: OK");
+	}
+
+	for (i = 0; i < ARRAY_SIZE(hmac_tests); i++) {
+		const struct hmac_test *t = &hmac_tests[i];
+
+		if (t->hash384[0] == 0 && t->hash384[1] == 0 &&
+		    t->hash384[2] == 0 && t->hash384[3] == 0)
+			continue;
+		wpa_printf(MSG_INFO, "HMAC-SHA384 test case %d:", i + 1);
+
+		if (hmac_sha384(t->key, t->key_len, t->data, t->data_len,
+				hash) < 0 ||
+		    os_memcmp(hash, t->hash384, 48) != 0) {
+			wpa_printf(MSG_INFO, " FAIL");
+			errors++;
+		} else
+			wpa_printf(MSG_INFO, " OK");
+
+		addr[0] = t->data;
+		len[0] = t->data_len;
+		if (hmac_sha384_vector(t->key, t->key_len, 1, addr, len,
+				       hash) < 0 ||
+		    os_memcmp(hash, t->hash384, 48) != 0) {
+			wpa_printf(MSG_INFO, " FAIL");
+			errors++;
+		} else
+			wpa_printf(MSG_INFO, " OK");
+
+		if (len[0]) {
+			addr[0] = t->data;
+			len[0] = 1;
+			addr[1] = t->data + 1;
+			len[1] = t->data_len - 1;
+			if (hmac_sha384_vector(t->key, t->key_len, 2, addr, len,
+					       hash) < 0 ||
+			    os_memcmp(hash, t->hash384, 48) != 0) {
+				wpa_printf(MSG_INFO, " FAIL");
+				errors++;
+			} else
+				wpa_printf(MSG_INFO, " OK");
+		}
+	}
+
+	if (!errors)
+		wpa_printf(MSG_INFO, "SHA384 test cases passed");
+	return errors;
+#else /* CONFIG_SHA384 */
+	return 0;
+#endif /* CONFIG_SHA384 */
+}
+
+
+static int test_fips186_2_prf(void)
+{
+	/* http://csrc.nist.gov/encryption/dss/Examples-1024bit.pdf */
+	u8 xkey[] = {
+		0xbd, 0x02, 0x9b, 0xbe, 0x7f, 0x51, 0x96, 0x0b,
+		0xcf, 0x9e, 0xdb, 0x2b, 0x61, 0xf0, 0x6f, 0x0f,
+		0xeb, 0x5a, 0x38, 0xb6
+	};
+	u8 w[] = {
+		0x20, 0x70, 0xb3, 0x22, 0x3d, 0xba, 0x37, 0x2f,
+		0xde, 0x1c, 0x0f, 0xfc, 0x7b, 0x2e, 0x3b, 0x49,
+		0x8b, 0x26, 0x06, 0x14, 0x3c, 0x6c, 0x18, 0xba,
+		0xcb, 0x0f, 0x6c, 0x55, 0xba, 0xbb, 0x13, 0x78,
+		0x8e, 0x20, 0xd7, 0x37, 0xa3, 0x27, 0x51, 0x16
+	};
+	u8 buf[40];
+
+	wpa_printf(MSG_INFO,
+		   "Testing EAP-SIM PRF (FIPS 186-2 + change notice 1)");
+	if (fips186_2_prf(xkey, sizeof(xkey), buf, sizeof(buf)) < 0 ||
+	    os_memcmp(w, buf, sizeof(w)) != 0) {
+		wpa_printf(MSG_INFO, "fips186_2_prf failed");
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static int test_extract_expand_hkdf(void)
+{
+	u8 prk[SHA256_MAC_LEN];
+	u8 okm[82];
+
+	/* RFC 5869, A.1 */
+	u8 ikm1[22] = {
+		0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+		0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+		0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b
+	};
+	u8 salt1[13] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0a, 0x0b, 0x0c
+	};
+	u8 info1[10] = {
+		0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+		0xf8, 0xf9
+	};
+	u8 prk1[32] = {
+		0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf,
+		0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63,
+		0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31,
+		0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5
+	};
+	u8 okm1[42] = {
+		0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a,
+		0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a,
+		0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c,
+		0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf,
+		0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18,
+		0x58, 0x65
+	};
+
+	/* RFC 5869, A.2 */
+	u8 ikm2[80] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+		0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+		0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+		0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+		0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+		0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+		0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+		0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f
+	};
+	u8 salt2[80] = {
+		0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+		0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+		0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+		0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+		0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+		0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+		0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+		0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+		0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+		0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf
+	};
+	u8 info2[80] = {
+		0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+		0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+		0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+		0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+		0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+		0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+		0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+		0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+		0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+		0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+	};
+	u8 prk2[32] = {
+		0x06, 0xa6, 0xb8, 0x8c, 0x58, 0x53, 0x36, 0x1a,
+		0x06, 0x10, 0x4c, 0x9c, 0xeb, 0x35, 0xb4, 0x5c,
+		0xef, 0x76, 0x00, 0x14, 0x90, 0x46, 0x71, 0x01,
+		0x4a, 0x19, 0x3f, 0x40, 0xc1, 0x5f, 0xc2, 0x44
+	};
+	u8 okm2[82] = {
+		0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1,
+		0xc8, 0xe7, 0xf7, 0x8c, 0x59, 0x6a, 0x49, 0x34,
+		0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e, 0xfa, 0xd8,
+		0xa0, 0x50, 0xcc, 0x4c, 0x19, 0xaf, 0xa9, 0x7c,
+		0x59, 0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72,
+		0x71, 0xcb, 0x41, 0xc6, 0x5e, 0x59, 0x0e, 0x09,
+		0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8,
+		0x36, 0x77, 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71,
+		0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec, 0x3e, 0x87,
+		0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f,
+		0x1d, 0x87
+	};
+
+	wpa_printf(MSG_INFO, "Testing Extract-and-Expand HKDF (RFC 5869)");
+
+	wpa_printf(MSG_INFO, "RFC 5869 - Test Case 1");
+	if (hmac_sha256(salt1, sizeof(salt1), ikm1, sizeof(ikm1), prk) < 0)
+		return -1;
+	if (os_memcmp(prk, prk1, SHA256_MAC_LEN) != 0) {
+		wpa_printf(MSG_INFO, "HKDF-Extract mismatch in PRK");
+		return -1;
+	}
+	if (hmac_sha256_kdf(prk1, sizeof(prk1), NULL, info1, sizeof(info1),
+			    okm, sizeof(okm1)) < 0)
+		return -1;
+	if (os_memcmp(okm, okm1, sizeof(okm1)) != 0) {
+		wpa_printf(MSG_INFO, "HKDF-Expand mismatch in OKM");
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO, "RFC 5869 - Test Case 2");
+	if (hmac_sha256(salt2, sizeof(salt2), ikm2, sizeof(ikm2), prk) < 0)
+		return -1;
+	if (os_memcmp(prk, prk2, SHA256_MAC_LEN) != 0) {
+		wpa_printf(MSG_INFO, "HKDF-Extract mismatch in PRK");
+		return -1;
+	}
+	if (hmac_sha256_kdf(prk2, sizeof(prk2), NULL, info2, sizeof(info2),
+			    okm, sizeof(okm2)) < 0)
+		return -1;
+	if (os_memcmp(okm, okm2, sizeof(okm2)) != 0) {
+		wpa_printf(MSG_INFO, "HKDF-Expand mismatch in OKM");
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO, "Extract-and-Expand HKDF test cases passed");
+
+	return 0;
+}
+
+
+#ifdef CONFIG_DPP3
+
+static const struct hpke_test {
+	const char *name;
+	enum hpke_mode mode;
+	enum hpke_kem_id kem_id;
+	enum hpke_kdf_id kdf_id;
+	enum hpke_aead_id aead_id;
+	const char *info;
+	int sk_r_group;
+	const char *pk_r;
+	const char *sk_r;
+	const char *enc;
+	const char *pt;
+	const char *aad;
+	const char *ct;
+} hpke_tests[] = {
+	{
+		.name = "A.3. DHKEM(P-256, HKDF-SHA256), HKDF-SHA256, AES-128-GCM",
+		.mode = HPKE_MODE_BASE,
+		.kem_id = HPKE_DHKEM_P256_HKDF_SHA256,
+		.kdf_id = HPKE_KDF_HKDF_SHA256,
+		.aead_id = HPKE_AEAD_AES_128_GCM,
+		.info = "4f6465206f6e2061204772656369616e2055726e",
+		.sk_r_group = 19,
+		.pk_r = "04fe8c19ce0905191ebc298a9245792531f26f0cece2460639e8bc39cb7f706a826a779b4cf969b8a0e539c7f62fb3d30ad6aa8f80e30f1d128aafd68a2ce72ea0",
+		.sk_r = "f3ce7fdae57e1a310d87f1ebbde6f328be0a99cdbcadf4d6589cf29de4b8ffd2",
+		.enc = "04a92719c6195d5085104f469a8b9814d5838ff72b60501e2c4466e5e67b325ac98536d7b61a1af4b78e5b7f951c0900be863c403ce65c9bfcb9382657222d18c4",
+		.pt = "4265617574792069732074727574682c20747275746820626561757479",
+		.aad = "436f756e742d30",
+		.ct = "5ad590bb8baa577f8619db35a36311226a896e7342a6d836d8b7bcd2f20b6c7f9076ac232e3ab2523f39513434",
+	},
+	{
+		.name = "A.4. DHKEM(P-256, HKDF-SHA256), HKDF-SHA512, AES-128-GCM",
+		.mode = HPKE_MODE_BASE,
+		.kem_id = HPKE_DHKEM_P256_HKDF_SHA256,
+		.kdf_id = HPKE_KDF_HKDF_SHA512,
+		.aead_id = HPKE_AEAD_AES_128_GCM,
+		.info = "4f6465206f6e2061204772656369616e2055726e",
+		.sk_r_group = 19,
+		.pk_r = "04085aa5b665dc3826f9650ccbcc471be268c8ada866422f739e2d531d4a8818a9466bc6b449357096232919ec4fe9070ccbac4aac30f4a1a53efcf7af90610edd",
+		.sk_r = "3ac8530ad1b01885960fab38cf3cdc4f7aef121eaa239f222623614b4079fb38",
+		.enc = "0493ed86735bdfb978cc055c98b45695ad7ce61ce748f4dd63c525a3b8d53a15565c6897888070070c1579db1f86aaa56deb8297e64db7e8924e72866f9a472580",
+		.pt = "4265617574792069732074727574682c20747275746820626561757479",
+		.aad = "436f756e742d30",
+		.ct = "d3cf4984931484a080f74c1bb2a6782700dc1fef9abe8442e44a6f09044c88907200b332003543754eb51917ba",
+	},
+	{
+		.name = "A.6. DHKEM(P-521, HKDF-SHA512), HKDF-SHA512, AES-256-GCM",
+		.mode = HPKE_MODE_BASE,
+		.kem_id = HPKE_DHKEM_P521_HKDF_SHA512,
+		.kdf_id = HPKE_KDF_HKDF_SHA512,
+		.aead_id = HPKE_AEAD_AES_256_GCM,
+		.info = "4f6465206f6e2061204772656369616e2055726e",
+		.sk_r_group = 21,
+		.pk_r = "0401b45498c1714e2dce167d3caf162e45e0642afc7ed435df7902ccae0e84ba0f7d373f646b7738bbbdca11ed91bdeae3cdcba3301f2457be452f271fa6837580e661012af49583a62e48d44bed350c7118c0d8dc861c238c72a2bda17f64704f464b57338e7f40b60959480c0e58e6559b190d81663ed816e523b6b6a418f66d2451ec64",
+		.sk_r = "01462680369ae375e4b3791070a7458ed527842f6a98a79ff5e0d4cbde83c27196a3916956655523a6a2556a7af62c5cadabe2ef9da3760bb21e005202f7b2462847",
+		.enc = "040138b385ca16bb0d5fa0c0665fbbd7e69e3ee29f63991d3e9b5fa740aab8900aaeed46ed73a49055758425a0ce36507c54b29cc5b85a5cee6bae0cf1c21f2731ece2013dc3fb7c8d21654bb161b463962ca19e8c654ff24c94dd2898de12051f1ed0692237fb02b2f8d1dc1c73e9b366b529eb436e98a996ee522aef863dd5739d2f29b0",
+		.pt = "4265617574792069732074727574682c20747275746820626561757479",
+		.aad = "436f756e742d30",
+		.ct = "170f8beddfe949b75ef9c387e201baf4132fa7374593dfafa90768788b7b2b200aafcc6d80ea4c795a7c5b841a",
+	},
+	{ /* self-generated test vector for P-384 */
+		.name = "custom DHKEM(P-384, HKDF-SHA384), HKDF-SHA384, AES-256-GCM",
+		.mode = HPKE_MODE_BASE,
+		.kem_id = HPKE_DHKEM_P384_HKDF_SHA384,
+		.kdf_id = HPKE_KDF_HKDF_SHA384,
+		.aead_id = HPKE_AEAD_AES_256_GCM,
+		.info = "4f6465206f6e2061204772656369616e2055726e",
+		.sk_r_group = 20,
+		.pk_r = "049c0e4dcbbb3c80715cafaa1839d0bc3c3adcc95eb8062f84175f9c3cec115e6b799061c65a0605907785c25b3571564706a8ba6a204452b38c7c205db17d328f2353df05d5f1c568e7503331178c36c2d37bbed48401295407face3f8dae5ed8",
+		.sk_r = "cabffb07d20ffcfdaa043e1de49e1654659e0f0aba5de56523e8b73dc80c579a9e5c89ed3810ec21c4bafcf74ad2a245",
+		.enc = "04b30bea96d0e51582033b02a4d676d0464a5eb2d858be86cda1c4e6f8b2aa9fb80f5365483f781b1b3a8b3b8efd50b0f7bca16f06d0435fa3da1d671ea0a318b40fe170a074923c651e5dc824966b7b98d0e36bdf932875dae7130369a793cecc",
+		.pt = "4265617574792069732074727574682c20747275746820626561757479",
+		.aad = "436f756e742d30",
+		.ct = "ae7feccfea0f8fcd620d15369a28db8701cdc90d55c20efff6296bd441697b0da34671d1f3c4864183e86d27fc",
+	},
+	{ /* self-generated test vector for BP-256 */
+		.name = "custom PB-256 using DHKEM(P-256, HKDF-SHA256), HKDF-SHA256, AES-128-GCM",
+		.mode = HPKE_MODE_BASE,
+		.kem_id = HPKE_DHKEM_P256_HKDF_SHA256,
+		.kdf_id = HPKE_KDF_HKDF_SHA256,
+		.aead_id = HPKE_AEAD_AES_128_GCM,
+		.info = "4f6465206f6e2061204772656369616e2055726e",
+		.sk_r_group = 28,
+		.pk_r = "04a2cb9c4cae90cdc1c27516e9f84b6b166e4b1dcc517286268239ddb0bf74cca6390fed092ac4423ab2192b8bb41a4824d908d2053b93fc813830bebac5ce19b9",
+		.sk_r = "11d9db41c4341166ca52f5a1775595c0bdb4934350daeb7bce659c4b7a40e314",
+		.enc = "047a25e309c7ee50ec27f13d44734a3ccd8c703e3affcc728513df416511ef9bf02f5e7750e7415de8b5f306ebd3fc88ea9b9368523eb1733a8d82c1a877e5a0f4",
+		.pt = "4265617574792069732074727574682c20747275746820626561757479",
+		.aad = "436f756e742d30",
+		.ct = "17c84b3f07f6ffe08ff2be45c709ea782229504aa5b2253876725c6c39f8d8c992304fc5877994f79d6c10d462",
+	},
+	{ /* self-generated test vector for BP-384 */
+		.name = "custom PB-384 using DHKEM(P-384, HKDF-SHA384), HKDF-SHA384, AES-256-GCM",
+		.mode = HPKE_MODE_BASE,
+		.kem_id = HPKE_DHKEM_P384_HKDF_SHA384,
+		.kdf_id = HPKE_KDF_HKDF_SHA384,
+		.aead_id = HPKE_AEAD_AES_256_GCM,
+		.info = "4f6465206f6e2061204772656369616e2055726e",
+		.sk_r_group = 29,
+		.pk_r = "041f4199ad28835908079c45d165d55630098be53eb4beede9921f5b2204fa396111f99ac54c56411f7cb2c43ec18d8e604d895027228cf975f5a4b598f189d8fb03e3fefe020258c40d4d1b15fd7587d209925d67a41f9659a8ed6f662fb441e4",
+		.sk_r = "7017cf8a5a9a81ad4e0d755ccbea27a378b787561f8d5662639850805fefcbaab6b9a15729872abb7dc53d19a6cf77e4",
+		.enc = "0415d49dedc5bc1ffe9f8de9022c266bb605ec6cd7b77b6ce68974095398856f8aefa4b7abbfbd496b99a2dda3a9c65f1a71b9d40255aa1c7c4205a8b4ef611b96ed29fd2d7b0cde4c0e82058805e6276025cc4fc606f6e5771c31bd9704e9ba0b",
+		.pt = "4265617574792069732074727574682c20747275746820626561757479",
+		.aad = "436f756e742d30",
+		.ct = "5f5e9f82bedadec0e9b01a1b304cb48b05c0d6d397b1c8a95ed541218ec54f634a41cbc4066910a409e47b254e",
+	},
+	{ /* self-generated test vector for BP-512 */
+		.name = "custom PB-512 using DHKEM(P-521, HKDF-SHA512), HKDF-SHA512, AES-256-GCM",
+		.mode = HPKE_MODE_BASE,
+		.kem_id = HPKE_DHKEM_P521_HKDF_SHA512,
+		.kdf_id = HPKE_KDF_HKDF_SHA512,
+		.aead_id = HPKE_AEAD_AES_256_GCM,
+		.info = "4f6465206f6e2061204772656369616e2055726e",
+		.sk_r_group = 30,
+		.pk_r = "049e81046a531365a3b5215ac37e7b38f5fa34f86c4eb2e03113b197390a26c555bb007596e131c2541f336eb24a45f44283b5b53fedddfa5642675602fdec17d34120a35efffb44952e32dee7732f2f3245c3314269996b610703a63fb8555a75ca5092690a1125ae8712c1e31fd77aee42bd052e71f9f9459814d6f4065bcea0",
+		.sk_r = "483b6882608182b296843fa7dfffbdd61ed0372574d4aa32a035c8e33a493927aaf00d42bd9124ebe4df26010b38124668c02b35a749e74845d565734310cfe9",
+		.enc = "04158d18473aeb3b283d3345b1a87d3de2b192ff9e41b5a98f91daacfb24be72e698cbc04c33078681e507bf346c0ea70c927083a22ca9ea027f420067ee42285b798d95fea51002d097ce28371883202bfd300fb64943669e32c6f1a348087368bb480b757892ebd199a9389978c92cbc44076626d705a771fbbd90c030a6767e",
+		.pt = "4265617574792069732074727574682c20747275746820626561757479",
+		.aad = "436f756e742d30",
+		.ct = "033d91c4514857da5b833635180c1acc09f175cbf44777a7b71e177705cfd17437b1c85d671dd767bb4fe20e2e",
+	},
+};
+
+
+static int run_hpke_test(const struct hpke_test *test)
+{
+	struct wpabuf *info, *pk_r, *sk_r, *enc, *pt, *aad, *ct;
+	struct wpabuf *res_pt = NULL, *enc_ct = NULL, *res_ct = NULL;
+	struct crypto_ec_key *own_priv = NULL, *peer_pub = NULL;
+	int res = -1;
+	size_t coord_len;
+
+	wpa_printf(MSG_INFO, "- %s", test->name);
+
+	info = wpabuf_parse_bin(test->info);
+	pk_r = wpabuf_parse_bin(test->pk_r);
+	sk_r = wpabuf_parse_bin(test->sk_r);
+	enc = wpabuf_parse_bin(test->enc);
+	pt = wpabuf_parse_bin(test->pt);
+	aad = wpabuf_parse_bin(test->aad);
+	ct = wpabuf_parse_bin(test->ct);
+	if (!info || !pk_r || !sk_r || !enc || !pt || !aad || !ct) {
+		wpa_printf(MSG_ERROR, "Could not parse test data");
+		goto fail;
+	}
+
+	/* Receiver - decryption against the test vector */
+
+	enc_ct = wpabuf_concat(enc, ct);
+	enc = NULL;
+	ct = NULL;
+	if (!enc_ct)
+		goto fail;
+
+	own_priv = crypto_ec_key_set_priv(test->sk_r_group, wpabuf_head(sk_r),
+					  wpabuf_len(sk_r));
+	if (!own_priv) {
+		wpa_printf(MSG_ERROR,
+			   "HPKE base open - failed to set private key");
+		goto fail;
+	}
+
+	res_pt = hpke_base_open(test->kem_id, test->kdf_id, test->aead_id,
+				own_priv,
+				wpabuf_head(info), wpabuf_len(info),
+				wpabuf_head(aad), wpabuf_len(aad),
+				wpabuf_head(enc_ct), wpabuf_len(enc_ct));
+	if (!res_pt) {
+		wpa_printf(MSG_ERROR, "HPKE base open - failed to decrypt");
+		wpa_hexdump_buf(MSG_INFO, "pt", res_pt);
+		goto fail;
+	}
+	if (wpabuf_len(res_pt) != wpabuf_len(pt) ||
+	    os_memcmp(wpabuf_head(res_pt), wpabuf_head(pt),
+		      wpabuf_len(pt)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "HPKE base open - failed - decryption mismatch");
+		goto fail;
+	}
+
+	/* Sender - encryption (randomized algorithm) */
+
+	if (test->sk_r_group == 19)
+		coord_len = 32;
+	else if (test->sk_r_group == 20)
+		coord_len = 48;
+	else if (test->sk_r_group == 21)
+		coord_len = 66;
+	else if (test->sk_r_group == 28)
+		coord_len = 32;
+	else if (test->sk_r_group == 29)
+		coord_len = 48;
+	else if (test->sk_r_group == 30)
+		coord_len = 64;
+	else
+		goto fail;
+	if (wpabuf_len(pk_r) != 1 + 2 * coord_len) {
+		wpa_printf(MSG_ERROR, "Unexpected pkR length (%zu != %zu)",
+			   wpabuf_len(pk_r), 1 + 2 * coord_len);
+		goto fail;
+	}
+	peer_pub = crypto_ec_key_set_pub(test->sk_r_group,
+					 wpabuf_head_u8(pk_r) + 1,
+					 wpabuf_head_u8(pk_r) + 1 + coord_len,
+					 coord_len);
+	if (!peer_pub) {
+		wpa_printf(MSG_ERROR,
+			   "HPKE base open - failed to set public key");
+		goto fail;
+	}
+
+	res_ct = hpke_base_seal(test->kem_id, test->kdf_id, test->aead_id,
+				peer_pub,
+				wpabuf_head(info), wpabuf_len(info),
+				wpabuf_head(aad), wpabuf_len(aad),
+				wpabuf_head(pt), wpabuf_len(pt));
+	if (!res_ct) {
+		wpa_printf(MSG_ERROR, "HPKE base open - failed to encrypt");
+		goto fail;
+	}
+
+	/* Receiver - decryption (to verify own encryption) */
+
+	wpabuf_free(res_pt);
+	res_pt = hpke_base_open(test->kem_id, test->kdf_id, test->aead_id,
+				own_priv,
+				wpabuf_head(info), wpabuf_len(info),
+				wpabuf_head(aad), wpabuf_len(aad),
+				wpabuf_head(res_ct), wpabuf_len(res_ct));
+	if (!res_pt) {
+		wpa_printf(MSG_ERROR, "HPKE base open - failed to decrypt own encrypted version");
+		goto fail;
+	}
+	if (wpabuf_len(res_pt) != wpabuf_len(pt) ||
+	    os_memcmp(wpabuf_head(res_pt), wpabuf_head(pt),
+		      wpabuf_len(pt)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "HPKE base open - failed - decryption mismatch for own encrypted version");
+		wpa_hexdump_buf(MSG_INFO, "pt", res_pt);
+		goto fail;
+	}
+
+	res = 0;
+fail:
+	wpabuf_free(info);
+	wpabuf_free(pk_r);
+	wpabuf_free(sk_r);
+	wpabuf_free(enc);
+	wpabuf_free(pt);
+	wpabuf_free(aad);
+	wpabuf_free(ct);
+	wpabuf_free(enc_ct);
+	wpabuf_free(res_pt);
+	wpabuf_free(res_ct);
+	crypto_ec_key_deinit(own_priv);
+	crypto_ec_key_deinit(peer_pub);
+	return res;
+}
+
+#endif /* CONFIG_DPP3 */
+
+
+static int test_hpke(void)
+{
+#ifdef CONFIG_DPP3
+	unsigned int i;
+
+	wpa_printf(MSG_INFO, "RFC 9180 - HPKE");
+	for (i = 0; i < ARRAY_SIZE(hpke_tests); i++) {
+		if (run_hpke_test(&hpke_tests[i]) < 0)
+			return -1;
+	}
+
+	wpa_printf(MSG_INFO, "HPKE base open test cases passed");
+#endif /* CONFIG_DPP3 */
+	return 0;
+}
+
+
+static int test_ecc(void)
+{
+#ifdef CONFIG_ECC
+#ifndef CONFIG_TLS_INTERNAL
+#ifndef CONFIG_TLS_GNUTLS
+#if defined(CONFIG_TLS_MBEDTLS) \
+ || defined(CONFIG_TLS_OPENSSL) \
+ || defined(CONFIG_TLS_WOLFSSL)
+	wpa_printf(MSG_INFO, "Testing ECC");
+	/* Note: some tests below are valid on supported Short Weierstrass
+	 * curves, but not on Montgomery curves (e.g. IKE groups 31 and 32)
+	 * (e.g. deriving and comparing y^2 test below not valid on Montgomery)
+	 */
+#ifdef CONFIG_TLS_MBEDTLS
+	const int grps[] = {19, 20, 21, 25, 26, 28};
+#endif
+#ifdef CONFIG_TLS_OPENSSL
+	const int grps[] = {19, 20, 21, 26};
+#endif
+#ifdef CONFIG_TLS_WOLFSSL
+	const int grps[] = {19, 20, 21, 26};
+#endif
+	uint32_t i;
+	struct crypto_ec *e = NULL;
+	struct crypto_ec_point *p = NULL, *q = NULL;
+	struct crypto_bignum *x = NULL, *y = NULL;
+#ifdef CONFIG_DPP
+	u8 bin[4096];
+#endif
+	for (i = 0; i < ARRAY_SIZE(grps); ++i) {
+		e = crypto_ec_init(grps[i]);
+		if (e == NULL
+		    || crypto_ec_prime_len(e) == 0
+		    || crypto_ec_prime_len_bits(e) == 0
+		    || crypto_ec_order_len(e) == 0
+		    || crypto_ec_get_prime(e) == NULL
+		    || crypto_ec_get_order(e) == NULL
+		    || crypto_ec_get_a(e) == NULL
+		    || crypto_ec_get_b(e) == NULL
+		    || crypto_ec_get_generator(e) == NULL) {
+			break;
+		}
+#ifdef CONFIG_DPP
+		struct crypto_ec_key *key = crypto_ec_key_gen(grps[i]);
+		if (key == NULL)
+			break;
+		p = crypto_ec_key_get_public_key(key);
+		q = crypto_ec_key_get_public_key(key);
+		crypto_ec_key_deinit(key);
+		if (p == NULL || q == NULL)
+			break;
+		if (!crypto_ec_point_is_on_curve(e, p))
+			break;
+
+		/* inverted point should not match original;
+		 * double-invert should match */
+		if (crypto_ec_point_invert(e, q) != 0
+		    || crypto_ec_point_cmp(e, p, q) == 0
+		    || crypto_ec_point_invert(e, q) != 0
+		    || crypto_ec_point_cmp(e, p, q) != 0) {
+			break;
+		}
+
+		/* crypto_ec_point_to_bin() and crypto_ec_point_from_bin()
+		 * imbalanced interfaces? */
+		size_t prime_len = crypto_ec_prime_len(e);
+		if (prime_len * 2 > sizeof(bin))
+			break;
+		if (crypto_ec_point_to_bin(e, p, bin, bin+prime_len) != 0)
+			break;
+		struct crypto_ec_point *tmp = crypto_ec_point_from_bin(e, bin);
+		if (tmp == NULL)
+			break;
+		if (crypto_ec_point_cmp(e, p, tmp) != 0) {
+			crypto_ec_point_deinit(tmp, 0);
+			break;
+		}
+		crypto_ec_point_deinit(tmp, 0);
+
+		x = crypto_bignum_init();
+		y = crypto_bignum_init_set(bin+prime_len, prime_len);
+		if (x == NULL || y == NULL || crypto_ec_point_x(e, p, x) != 0)
+			break;
+		struct crypto_bignum *y2 = crypto_ec_point_compute_y_sqr(e, x);
+		if (y2 == NULL)
+			break;
+		if (crypto_bignum_sqrmod(y, crypto_ec_get_prime(e), y) != 0
+		    || crypto_bignum_cmp(y, y2) != 0) {
+			crypto_bignum_deinit(y2, 0);
+			break;
+		}
+		crypto_bignum_deinit(y2, 0);
+		crypto_bignum_deinit(x, 0);
+		crypto_bignum_deinit(y, 0);
+		x = NULL;
+		y = NULL;
+
+		x = crypto_bignum_init();
+		if (x == NULL)
+			break;
+		if (crypto_bignum_rand(x, crypto_ec_get_prime(e)) != 0)
+			break;
+		crypto_bignum_deinit(x, 0);
+		x = NULL;
+
+		crypto_ec_point_deinit(p, 0);
+		p = NULL;
+		crypto_ec_point_deinit(q, 0);
+		q = NULL;
+#endif /* CONFIG_DPP */
+		crypto_ec_deinit(e);
+		e = NULL;
+	}
+	if (i != ARRAY_SIZE(grps)) {
+		crypto_bignum_deinit(x, 0);
+		crypto_bignum_deinit(y, 0);
+		crypto_ec_point_deinit(p, 0);
+		crypto_ec_point_deinit(q, 0);
+		crypto_ec_deinit(e);
+		wpa_printf(MSG_INFO,
+		           "ECC test case failed tls_id:%d", grps[i]);
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO, "ECC test cases passed");
+#endif
+#endif /* !CONFIG_TLS_GNUTLS */
+#endif /* !CONFIG_TLS_INTERNAL */
+#endif /* CONFIG_ECC */
+	return 0;
+}
+
+
+static int test_ms_funcs(void)
+{
+#ifndef CONFIG_FIPS
+	/* Test vector from RFC2759 example */
+	char *username = "User";
+	char *password = "clientPass";
+	u8 auth_challenge[] = {
+		0x5B, 0x5D, 0x7C, 0x7D, 0x7B, 0x3F, 0x2F, 0x3E,
+		0x3C, 0x2C, 0x60, 0x21, 0x32, 0x26, 0x26, 0x28
+	};
+	u8 peer_challenge[] = {
+		0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A,
+		0x28, 0x29, 0x5F, 0x2B, 0x3A, 0x33, 0x7C, 0x7E
+	};
+	u8 password_hash[] = {
+		0x44, 0xEB, 0xBA, 0x8D, 0x53, 0x12, 0xB8, 0xD6,
+		0x11, 0x47, 0x44, 0x11, 0xF5, 0x69, 0x89, 0xAE
+	};
+	u8 nt_response[] = {
+		0x82, 0x30, 0x9E, 0xCD, 0x8D, 0x70, 0x8B, 0x5E,
+		0xA0, 0x8F, 0xAA, 0x39, 0x81, 0xCD, 0x83, 0x54,
+		0x42, 0x33, 0x11, 0x4A, 0x3D, 0x85, 0xD6, 0xDF
+	};
+	u8 password_hash_hash[] = {
+		0x41, 0xC0, 0x0C, 0x58, 0x4B, 0xD2, 0xD9, 0x1C,
+		0x40, 0x17, 0xA2, 0xA1, 0x2F, 0xA5, 0x9F, 0x3F
+	};
+	u8 authenticator_response[] = {
+		0x40, 0x7A, 0x55, 0x89, 0x11, 0x5F, 0xD0, 0xD6,
+		0x20, 0x9F, 0x51, 0x0F, 0xE9, 0xC0, 0x45, 0x66,
+		0x93, 0x2C, 0xDA, 0x56
+	};
+	u8 master_key[] = {
+		0xFD, 0xEC, 0xE3, 0x71, 0x7A, 0x8C, 0x83, 0x8C,
+		0xB3, 0x88, 0xE5, 0x27, 0xAE, 0x3C, 0xDD, 0x31
+	};
+	u8 send_start_key[] = {
+		0x8B, 0x7C, 0xDC, 0x14, 0x9B, 0x99, 0x3A, 0x1B,
+		0xA1, 0x18, 0xCB, 0x15, 0x3F, 0x56, 0xDC, 0xCB
+	};
+	u8 buf[32];
+	int errors = 0;
+
+	if (nt_password_hash((u8 *) password, os_strlen(password), buf) ||
+	    os_memcmp(password_hash, buf, sizeof(password_hash)) != 0) {
+		wpa_printf(MSG_ERROR, "nt_password_hash failed");
+		errors++;
+	}
+
+	if (generate_nt_response(auth_challenge, peer_challenge,
+				 (u8 *) username, os_strlen(username),
+				 (u8 *) password, os_strlen(password), buf) ||
+	    os_memcmp(nt_response, buf, sizeof(nt_response)) != 0) {
+		wpa_printf(MSG_ERROR, "generate_nt_response failed");
+		errors++;
+	}
+
+	if (hash_nt_password_hash(password_hash, buf) ||
+	    os_memcmp(password_hash_hash, buf,
+		      sizeof(password_hash_hash)) != 0) {
+		wpa_printf(MSG_ERROR, "hash_nt_password_hash failed");
+		errors++;
+	}
+
+	if (generate_authenticator_response((u8 *) password,
+					    os_strlen(password),
+					    peer_challenge, auth_challenge,
+					    (u8 *) username,
+					    os_strlen(username),
+					    nt_response, buf) ||
+	    os_memcmp(authenticator_response, buf,
+		      sizeof(authenticator_response)) != 0) {
+		wpa_printf(MSG_ERROR, "generate_authenticator_response failed");
+		errors++;
+	}
+
+	if (get_master_key(password_hash_hash, nt_response, buf) ||
+	    os_memcmp(master_key, buf, sizeof(master_key)) != 0) {
+		wpa_printf(MSG_ERROR, "get_master_key failed");
+		errors++;
+	}
+
+	if (get_asymetric_start_key(master_key, buf, sizeof(send_start_key),
+				    1, 1) ||
+	    os_memcmp(send_start_key, buf, sizeof(send_start_key)) != 0) {
+		wpa_printf(MSG_ERROR, "get_asymetric_start_key failed");
+		errors++;
+	}
+
+	if (errors)
+		wpa_printf(MSG_ERROR, "ms_funcs: %d errors", errors);
+	else
+		wpa_printf(MSG_INFO, "ms_funcs test cases passed");
+
+	return errors;
+#else /* CONFIG_FIPS */
+	wpa_printf(MSG_INFO, "ms_funcs test cases skipped due to CONFIG_FIPS");
+	return 0;
+#endif /* CONFIG_FIPS */
+}
+
+
+int crypto_module_tests(void)
+{
+	int ret = 0;
+
+	wpa_printf(MSG_INFO, "crypto module tests");
+	if (test_siv() ||
+	    test_omac1() ||
+	    test_eax() ||
+	    test_cbc() ||
+	    test_ecb() ||
+	    test_key_wrap() ||
+	    test_aes_ctr() ||
+	    test_md5() ||
+	    test_sha1() ||
+	    test_sha256() ||
+	    test_sha384() ||
+	    test_fips186_2_prf() ||
+	    test_extract_expand_hkdf() ||
+	    test_hpke() ||
+	    test_ecc() ||
+	    test_ms_funcs())
+		ret = -1;
+
+	return ret;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_nettle.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_nettle.c
new file mode 100644
index 0000000..d745027
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_nettle.c
@@ -0,0 +1,474 @@
+/*
+ * Wrapper functions for libnettle and libgmp
+ * Copyright (c) 2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <nettle/nettle-meta.h>
+#include <nettle/des.h>
+#undef des_encrypt
+#include <nettle/hmac.h>
+#include <nettle/aes.h>
+#undef aes_encrypt
+#undef aes_decrypt
+#include <nettle/arcfour.h>
+#include <nettle/bignum.h>
+
+#include "common.h"
+#include "md5.h"
+#include "sha1.h"
+#include "sha256.h"
+#include "sha384.h"
+#include "sha512.h"
+#include "crypto.h"
+
+
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+	struct des_ctx ctx;
+	u8 pkey[8], next, tmp;
+	int i;
+
+	/* Add parity bits to the key */
+	next = 0;
+	for (i = 0; i < 7; i++) {
+		tmp = key[i];
+		pkey[i] = (tmp >> i) | next | 1;
+		next = tmp << (7 - i);
+	}
+	pkey[i] = next | 1;
+
+	nettle_des_set_key(&ctx, pkey);
+	nettle_des_encrypt(&ctx, DES_BLOCK_SIZE, cypher, clear);
+	os_memset(&ctx, 0, sizeof(ctx));
+	return 0;
+}
+
+
+static int nettle_digest_vector(const struct nettle_hash *alg, size_t num_elem,
+				const u8 *addr[], const size_t *len, u8 *mac)
+{
+	void *ctx;
+	size_t i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	ctx = os_malloc(alg->context_size);
+	if (!ctx)
+		return -1;
+	alg->init(ctx);
+	for (i = 0; i < num_elem; i++)
+		alg->update(ctx, len[i], addr[i]);
+	alg->digest(ctx, alg->digest_size, mac);
+	bin_clear_free(ctx, alg->context_size);
+	return 0;
+}
+
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return nettle_digest_vector(&nettle_md4, num_elem, addr, len, mac);
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return nettle_digest_vector(&nettle_md5, num_elem, addr, len, mac);
+}
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return nettle_digest_vector(&nettle_sha1, num_elem, addr, len, mac);
+}
+
+
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return nettle_digest_vector(&nettle_sha256, num_elem, addr, len, mac);
+}
+
+
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return nettle_digest_vector(&nettle_sha384, num_elem, addr, len, mac);
+}
+
+
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return nettle_digest_vector(&nettle_sha512, num_elem, addr, len, mac);
+}
+
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+		    const u8 *addr[], const size_t *len, u8 *mac)
+{
+	struct hmac_md5_ctx ctx;
+	size_t i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	hmac_md5_set_key(&ctx, key_len, key);
+	for (i = 0; i < num_elem; i++)
+		hmac_md5_update(&ctx, len[i], addr[i]);
+	hmac_md5_digest(&ctx, MD5_DIGEST_SIZE, mac);
+	os_memset(&ctx, 0, sizeof(ctx));
+	return 0;
+}
+
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	     u8 *mac)
+{
+	return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	struct hmac_sha1_ctx ctx;
+	size_t i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	hmac_sha1_set_key(&ctx, key_len, key);
+	for (i = 0; i < num_elem; i++)
+		hmac_sha1_update(&ctx, len[i], addr[i]);
+	hmac_sha1_digest(&ctx, SHA1_DIGEST_SIZE, mac);
+	os_memset(&ctx, 0, sizeof(ctx));
+	return 0;
+}
+
+
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	       u8 *mac)
+{
+	return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+#ifdef CONFIG_SHA256
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	struct hmac_sha256_ctx ctx;
+	size_t i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	hmac_sha256_set_key(&ctx, key_len, key);
+	for (i = 0; i < num_elem; i++)
+		hmac_sha256_update(&ctx, len[i], addr[i]);
+	hmac_sha256_digest(&ctx, SHA256_DIGEST_SIZE, mac);
+	os_memset(&ctx, 0, sizeof(ctx));
+	return 0;
+}
+
+
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA256 */
+
+
+#ifdef CONFIG_SHA384
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	struct hmac_sha384_ctx ctx;
+	size_t i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	hmac_sha384_set_key(&ctx, key_len, key);
+	for (i = 0; i < num_elem; i++)
+		hmac_sha384_update(&ctx, len[i], addr[i]);
+	hmac_sha384_digest(&ctx, SHA384_DIGEST_SIZE, mac);
+	os_memset(&ctx, 0, sizeof(ctx));
+	return 0;
+}
+
+
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA384 */
+
+
+#ifdef CONFIG_SHA512
+
+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	struct hmac_sha512_ctx ctx;
+	size_t i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	hmac_sha512_set_key(&ctx, key_len, key);
+	for (i = 0; i < num_elem; i++)
+		hmac_sha512_update(&ctx, len[i], addr[i]);
+	hmac_sha512_digest(&ctx, SHA512_DIGEST_SIZE, mac);
+	os_memset(&ctx, 0, sizeof(ctx));
+	return 0;
+}
+
+
+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA512 */
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+	struct aes_ctx *ctx;
+
+	if (TEST_FAIL())
+		return NULL;
+	ctx = os_malloc(sizeof(*ctx));
+	if (!ctx)
+		return NULL;
+
+	nettle_aes_set_encrypt_key(ctx, len, key);
+
+	return ctx;
+}
+
+
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+	struct aes_ctx *actx = ctx;
+	nettle_aes_encrypt(actx, AES_BLOCK_SIZE, crypt, plain);
+	return 0;
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+	struct aes_ctx *actx = ctx;
+	bin_clear_free(actx, sizeof(*actx));
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+	struct aes_ctx *ctx;
+
+	if (TEST_FAIL())
+		return NULL;
+	ctx = os_malloc(sizeof(*ctx));
+	if (!ctx)
+		return NULL;
+
+	nettle_aes_set_decrypt_key(ctx, len, key);
+
+	return ctx;
+}
+
+
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+	struct aes_ctx *actx = ctx;
+	nettle_aes_decrypt(actx, AES_BLOCK_SIZE, plain, crypt);
+	return 0;
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+	struct aes_ctx *actx = ctx;
+	bin_clear_free(actx, sizeof(*actx));
+}
+
+
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+		   u8 *pubkey)
+{
+	size_t pubkey_len, pad;
+
+	if (os_get_random(privkey, prime_len) < 0)
+		return -1;
+	if (os_memcmp(privkey, prime, prime_len) > 0) {
+		/* Make sure private value is smaller than prime */
+		privkey[0] = 0;
+	}
+
+	pubkey_len = prime_len;
+	if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
+			   pubkey, &pubkey_len) < 0)
+		return -1;
+	if (pubkey_len < prime_len) {
+		pad = prime_len - pubkey_len;
+		os_memmove(pubkey + pad, pubkey, pubkey_len);
+		os_memset(pubkey, 0, pad);
+	}
+
+	return 0;
+}
+
+
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+			    const u8 *order, size_t order_len,
+			    const u8 *privkey, size_t privkey_len,
+			    const u8 *pubkey, size_t pubkey_len,
+			    u8 *secret, size_t *len)
+{
+	mpz_t pub;
+	int res = -1;
+
+	if (pubkey_len > prime_len ||
+	    (pubkey_len == prime_len &&
+	     os_memcmp(pubkey, prime, prime_len) >= 0))
+		return -1;
+
+	mpz_init(pub);
+	mpz_import(pub, pubkey_len, 1, 1, 1, 0, pubkey);
+	if (mpz_cmp_d(pub, 1) <= 0)
+		goto fail;
+
+	if (order) {
+		mpz_t p, q, tmp;
+		int failed;
+
+		/* verify: pubkey^q == 1 mod p */
+		mpz_inits(p, q, tmp, NULL);
+		mpz_import(p, prime_len, 1, 1, 1, 0, prime);
+		mpz_import(q, order_len, 1, 1, 1, 0, order);
+		mpz_powm(tmp, pub, q, p);
+		failed = mpz_cmp_d(tmp, 1) != 0;
+		mpz_clears(p, q, tmp, NULL);
+		if (failed)
+			goto fail;
+	}
+
+	res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+			     prime, prime_len, secret, len);
+fail:
+	mpz_clear(pub);
+	return res;
+}
+
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+		   const u8 *power, size_t power_len,
+		   const u8 *modulus, size_t modulus_len,
+		   u8 *result, size_t *result_len)
+{
+	mpz_t bn_base, bn_exp, bn_modulus, bn_result;
+	int ret = -1;
+	size_t len;
+
+	mpz_inits(bn_base, bn_exp, bn_modulus, bn_result, NULL);
+	mpz_import(bn_base, base_len, 1, 1, 1, 0, base);
+	mpz_import(bn_exp, power_len, 1, 1, 1, 0, power);
+	mpz_import(bn_modulus, modulus_len, 1, 1, 1, 0, modulus);
+
+	mpz_powm(bn_result, bn_base, bn_exp, bn_modulus);
+	len = mpz_sizeinbase(bn_result, 2);
+	len = (len + 7) / 8;
+	if (*result_len < len)
+		goto error;
+	mpz_export(result, result_len, 1, 1, 1, 0, bn_result);
+	ret = 0;
+
+error:
+	mpz_clears(bn_base, bn_exp, bn_modulus, bn_result, NULL);
+	return ret;
+}
+
+
+struct crypto_cipher {
+	enum crypto_cipher_alg alg;
+	union {
+		struct arcfour_ctx arcfour_ctx;
+	} u;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+					  const u8 *iv, const u8 *key,
+					  size_t key_len)
+{
+	struct crypto_cipher *ctx;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (!ctx)
+		return NULL;
+
+	ctx->alg = alg;
+
+	switch (alg) {
+	case CRYPTO_CIPHER_ALG_RC4:
+		nettle_arcfour_set_key(&ctx->u.arcfour_ctx, key_len, key);
+		break;
+	default:
+		os_free(ctx);
+		return NULL;
+	}
+
+	return ctx;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+			  u8 *crypt, size_t len)
+{
+	switch (ctx->alg) {
+	case CRYPTO_CIPHER_ALG_RC4:
+		nettle_arcfour_crypt(&ctx->u.arcfour_ctx, len, crypt, plain);
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+			  u8 *plain, size_t len)
+{
+	switch (ctx->alg) {
+	case CRYPTO_CIPHER_ALG_RC4:
+		nettle_arcfour_crypt(&ctx->u.arcfour_ctx, len, plain, crypt);
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+	bin_clear_free(ctx, sizeof(*ctx));
+}
+
+
+void crypto_unload(void)
+{
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_none.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_none.c
new file mode 100644
index 0000000..a0dc0f5
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_none.c
@@ -0,0 +1,29 @@
+/*
+ * WPA Supplicant / Empty template functions for crypto wrapper
+ * Copyright (c) 2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return 0;
+}
+
+
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+	return 0;
+}
+
+
+void crypto_unload(void)
+{
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_openssl.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_openssl.c
new file mode 100644
index 0000000..2d8ff60
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_openssl.c
@@ -0,0 +1,5584 @@
+/*
+ * Wrapper functions for OpenSSL libcrypto
+ * Copyright (c) 2004-2024, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <openssl/opensslv.h>
+#include <openssl/err.h>
+#include <openssl/des.h>
+#include <openssl/aes.h>
+#include <openssl/bn.h>
+#include <openssl/evp.h>
+#include <openssl/dh.h>
+#include <openssl/hmac.h>
+#include <openssl/rand.h>
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+#ifdef CONFIG_ECC
+#include <openssl/ec.h>
+#include <openssl/x509.h>
+#endif /* CONFIG_ECC */
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#include <openssl/provider.h>
+#include <openssl/core_names.h>
+#include <openssl/param_build.h>
+#include <openssl/encoder.h>
+#include <openssl/decoder.h>
+#else /* OpenSSL version >= 3.0 */
+#include <openssl/cmac.h>
+#endif /* OpenSSL version >= 3.0 */
+#ifdef CONFIG_DPP3
+#if OPENSSL_VERSION_NUMBER >= 0x30200000L
+#include <openssl/hpke.h>
+#endif
+#endif /* CONFIG_DPP3 */
+
+#include "common.h"
+#include "utils/const_time.h"
+#include "wpabuf.h"
+#include "dh_group5.h"
+#include "sha1.h"
+#include "sha256.h"
+#include "sha384.h"
+#include "sha512.h"
+#include "md5.h"
+#include "aes_wrap.h"
+#include "crypto.h"
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+/* Compatibility wrappers for older versions. */
+
+static HMAC_CTX * HMAC_CTX_new(void)
+{
+	HMAC_CTX *ctx;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx)
+		HMAC_CTX_init(ctx);
+	return ctx;
+}
+
+
+static void HMAC_CTX_free(HMAC_CTX *ctx)
+{
+	if (!ctx)
+		return;
+	HMAC_CTX_cleanup(ctx);
+	bin_clear_free(ctx, sizeof(*ctx));
+}
+
+
+static EVP_MD_CTX * EVP_MD_CTX_new(void)
+{
+	EVP_MD_CTX *ctx;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx)
+		EVP_MD_CTX_init(ctx);
+	return ctx;
+}
+
+
+static void EVP_MD_CTX_free(EVP_MD_CTX *ctx)
+{
+	if (!ctx)
+		return;
+	EVP_MD_CTX_cleanup(ctx);
+	bin_clear_free(ctx, sizeof(*ctx));
+}
+
+
+#ifdef CONFIG_ECC
+
+static EC_KEY * EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey)
+{
+	if (pkey->type != EVP_PKEY_EC)
+		return NULL;
+	return pkey->pkey.ec;
+}
+
+
+static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
+{
+	sig->r = r;
+	sig->s = s;
+	return 1;
+}
+
+
+static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr,
+			   const BIGNUM **ps)
+{
+	if (pr)
+		*pr = sig->r;
+	if (ps)
+		*ps = sig->s;
+}
+
+#endif /* CONFIG_ECC */
+
+static const unsigned char * ASN1_STRING_get0_data(const ASN1_STRING *x)
+{
+	return ASN1_STRING_data((ASN1_STRING *) x);
+}
+
+
+static const ASN1_TIME * X509_get0_notBefore(const X509 *x)
+{
+	return X509_get_notBefore(x);
+}
+
+
+static const ASN1_TIME * X509_get0_notAfter(const X509 *x)
+{
+	return X509_get_notAfter(x);
+}
+
+#endif /* OpenSSL version < 1.1.0 */
+
+
+#if OPENSSL_VERSION_NUMBER < 0x10101000L || \
+	(defined(LIBRESSL_VERSION_NUMBER) && \
+	 LIBRESSL_VERSION_NUMBER < 0x30400000L)
+
+static int EC_POINT_get_affine_coordinates(const EC_GROUP *group,
+					   const EC_POINT *point, BIGNUM *x,
+					   BIGNUM *y, BN_CTX *ctx)
+{
+	return EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx);
+}
+
+
+static int EC_POINT_set_affine_coordinates(const EC_GROUP *group,
+					   EC_POINT *point, const BIGNUM *x,
+					   const BIGNUM *y, BN_CTX *ctx)
+{
+	return EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx);
+}
+
+#endif /* OpenSSL version < 1.1.1 */
+
+
+#if OPENSSL_VERSION_NUMBER < 0x10101000L || \
+	defined(OPENSSL_IS_BORINGSSL) || \
+	(defined(LIBRESSL_VERSION_NUMBER) && \
+	 LIBRESSL_VERSION_NUMBER < 0x30400000L)
+
+static int EC_POINT_set_compressed_coordinates(const EC_GROUP *group,
+					       EC_POINT *point, const BIGNUM *x,
+					       int y_bit, BN_CTX *ctx)
+{
+	return EC_POINT_set_compressed_coordinates_GFp(group, point, x, y_bit,
+						       ctx);
+}
+
+
+static int EC_GROUP_get_curve(const EC_GROUP *group, BIGNUM *p, BIGNUM *a,
+			      BIGNUM *b, BN_CTX *ctx)
+{
+	return EC_GROUP_get_curve_GFp(group, p, a, b, ctx);
+}
+
+#endif /* OpenSSL version < 1.1.1 */
+
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+static OSSL_PROVIDER *openssl_legacy_provider = NULL;
+#endif /* OpenSSL version >= 3.0 */
+
+void openssl_load_legacy_provider(void)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	if (openssl_legacy_provider)
+		return;
+
+	openssl_legacy_provider = OSSL_PROVIDER_try_load(NULL, "legacy", 1);
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+static void openssl_unload_legacy_provider(void)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	if (openssl_legacy_provider) {
+		OSSL_PROVIDER_unload(openssl_legacy_provider);
+		openssl_legacy_provider = NULL;
+	}
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+
+static BIGNUM * get_group5_prime(void)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+	return BN_get_rfc3526_prime_1536(NULL);
+#elif !defined(OPENSSL_IS_BORINGSSL)
+	return get_rfc3526_prime_1536(NULL);
+#else
+	static const unsigned char RFC3526_PRIME_1536[] = {
+		0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2,
+		0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1,
+		0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74,0x02,0x0B,0xBE,0xA6,
+		0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD,
+		0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D,
+		0xF2,0x5F,0x14,0x37,0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45,
+		0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6,0xF4,0x4C,0x42,0xE9,
+		0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED,
+		0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11,
+		0x7C,0x4B,0x1F,0xE6,0x49,0x28,0x66,0x51,0xEC,0xE4,0x5B,0x3D,
+		0xC2,0x00,0x7C,0xB8,0xA1,0x63,0xBF,0x05,0x98,0xDA,0x48,0x36,
+		0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F,
+		0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56,
+		0x20,0x85,0x52,0xBB,0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D,
+		0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04,0xF1,0x74,0x6C,0x08,
+		0xCA,0x23,0x73,0x27,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+	};
+        return BN_bin2bn(RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536), NULL);
+#endif
+}
+
+
+static BIGNUM * get_group5_order(void)
+{
+	static const unsigned char RFC3526_ORDER_1536[] = {
+		0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE4,0x87,0xED,0x51,
+		0x10,0xB4,0x61,0x1A,0x62,0x63,0x31,0x45,0xC0,0x6E,0x0E,0x68,
+		0x94,0x81,0x27,0x04,0x45,0x33,0xE6,0x3A,0x01,0x05,0xDF,0x53,
+		0x1D,0x89,0xCD,0x91,0x28,0xA5,0x04,0x3C,0xC7,0x1A,0x02,0x6E,
+		0xF7,0xCA,0x8C,0xD9,0xE6,0x9D,0x21,0x8D,0x98,0x15,0x85,0x36,
+		0xF9,0x2F,0x8A,0x1B,0xA7,0xF0,0x9A,0xB6,0xB6,0xA8,0xE1,0x22,
+		0xF2,0x42,0xDA,0xBB,0x31,0x2F,0x3F,0x63,0x7A,0x26,0x21,0x74,
+		0xD3,0x1B,0xF6,0xB5,0x85,0xFF,0xAE,0x5B,0x7A,0x03,0x5B,0xF6,
+		0xF7,0x1C,0x35,0xFD,0xAD,0x44,0xCF,0xD2,0xD7,0x4F,0x92,0x08,
+		0xBE,0x25,0x8F,0xF3,0x24,0x94,0x33,0x28,0xF6,0x72,0x2D,0x9E,
+		0xE1,0x00,0x3E,0x5C,0x50,0xB1,0xDF,0x82,0xCC,0x6D,0x24,0x1B,
+		0x0E,0x2A,0xE9,0xCD,0x34,0x8B,0x1F,0xD4,0x7E,0x92,0x67,0xAF,
+		0xC1,0xB2,0xAE,0x91,0xEE,0x51,0xD6,0xCB,0x0E,0x31,0x79,0xAB,
+		0x10,0x42,0xA9,0x5D,0xCF,0x6A,0x94,0x83,0xB8,0x4B,0x4B,0x36,
+		0xB3,0x86,0x1A,0xA7,0x25,0x5E,0x4C,0x02,0x78,0xBA,0x36,0x04,
+		0x65,0x11,0xB9,0x93,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
+	};
+	return BN_bin2bn(RFC3526_ORDER_1536, sizeof(RFC3526_ORDER_1536), NULL);
+}
+
+#endif /* OpenSSL version < 3.0 */
+
+
+#ifdef OPENSSL_NO_SHA256
+#define NO_SHA256_WRAPPER
+#endif
+#ifdef OPENSSL_NO_SHA512
+#define NO_SHA384_WRAPPER
+#endif
+
+static int openssl_digest_vector(const EVP_MD *type, size_t num_elem,
+				 const u8 *addr[], const size_t *len, u8 *mac)
+{
+	EVP_MD_CTX *ctx;
+	size_t i;
+	unsigned int mac_len;
+
+	if (TEST_FAIL())
+		return -1;
+
+	ctx = EVP_MD_CTX_new();
+	if (!ctx)
+		return -1;
+	if (!EVP_DigestInit_ex(ctx, type, NULL)) {
+		wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestInit_ex failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		EVP_MD_CTX_free(ctx);
+		return -1;
+	}
+	for (i = 0; i < num_elem; i++) {
+		if (!EVP_DigestUpdate(ctx, addr[i], len[i])) {
+			wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestUpdate "
+				   "failed: %s",
+				   ERR_error_string(ERR_get_error(), NULL));
+			EVP_MD_CTX_free(ctx);
+			return -1;
+		}
+	}
+	if (!EVP_DigestFinal(ctx, mac, &mac_len)) {
+		wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestFinal failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		EVP_MD_CTX_free(ctx);
+		return -1;
+	}
+	EVP_MD_CTX_free(ctx);
+
+	return 0;
+}
+
+
+#ifndef CONFIG_FIPS
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	openssl_load_legacy_provider();
+	return openssl_digest_vector(EVP_md4(), num_elem, addr, len, mac);
+}
+
+
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+	u8 pkey[8], next, tmp;
+	int i, plen, ret = -1;
+	EVP_CIPHER_CTX *ctx;
+
+	openssl_load_legacy_provider();
+
+	/* Add parity bits to the key */
+	next = 0;
+	for (i = 0; i < 7; i++) {
+		tmp = key[i];
+		pkey[i] = (tmp >> i) | next | 1;
+		next = tmp << (7 - i);
+	}
+	pkey[i] = next | 1;
+
+	ctx = EVP_CIPHER_CTX_new();
+	if (ctx &&
+	    EVP_EncryptInit_ex(ctx, EVP_des_ecb(), NULL, pkey, NULL) == 1 &&
+	    EVP_CIPHER_CTX_set_padding(ctx, 0) == 1 &&
+	    EVP_EncryptUpdate(ctx, cypher, &plen, clear, 8) == 1 &&
+	    EVP_EncryptFinal_ex(ctx, &cypher[plen], &plen) == 1)
+		ret = 0;
+	else
+		wpa_printf(MSG_ERROR, "OpenSSL: DES encrypt failed");
+
+	if (ctx)
+		EVP_CIPHER_CTX_free(ctx);
+	return ret;
+}
+
+
+#ifndef CONFIG_NO_RC4
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+	     u8 *data, size_t data_len)
+{
+#ifdef OPENSSL_NO_RC4
+	return -1;
+#else /* OPENSSL_NO_RC4 */
+	EVP_CIPHER_CTX *ctx;
+	int outl;
+	int res = -1;
+	unsigned char skip_buf[16];
+
+	openssl_load_legacy_provider();
+
+	ctx = EVP_CIPHER_CTX_new();
+	if (!ctx ||
+	    !EVP_CipherInit_ex(ctx, EVP_rc4(), NULL, NULL, NULL, 1) ||
+	    !EVP_CIPHER_CTX_set_padding(ctx, 0) ||
+	    !EVP_CIPHER_CTX_set_key_length(ctx, keylen) ||
+	    !EVP_CipherInit_ex(ctx, NULL, NULL, key, NULL, 1))
+		goto out;
+
+	while (skip >= sizeof(skip_buf)) {
+		size_t len = skip;
+		if (len > sizeof(skip_buf))
+			len = sizeof(skip_buf);
+		if (!EVP_CipherUpdate(ctx, skip_buf, &outl, skip_buf, len))
+			goto out;
+		skip -= len;
+	}
+
+	if (EVP_CipherUpdate(ctx, data, &outl, data, data_len))
+		res = 0;
+
+out:
+	if (ctx)
+		EVP_CIPHER_CTX_free(ctx);
+	return res;
+#endif /* OPENSSL_NO_RC4 */
+}
+#endif /* CONFIG_NO_RC4 */
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_digest_vector(EVP_md5(), num_elem, addr, len, mac);
+}
+
+#endif /* CONFIG_FIPS */
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_digest_vector(EVP_sha1(), num_elem, addr, len, mac);
+}
+
+
+#ifndef NO_SHA256_WRAPPER
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	return openssl_digest_vector(EVP_sha256(), num_elem, addr, len, mac);
+}
+#endif /* NO_SHA256_WRAPPER */
+
+
+#ifndef NO_SHA384_WRAPPER
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	return openssl_digest_vector(EVP_sha384(), num_elem, addr, len, mac);
+}
+#endif /* NO_SHA384_WRAPPER */
+
+
+#ifndef NO_SHA512_WRAPPER
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	return openssl_digest_vector(EVP_sha512(), num_elem, addr, len, mac);
+}
+#endif /* NO_SHA512_WRAPPER */
+
+
+static const EVP_CIPHER * aes_get_evp_cipher(size_t keylen)
+{
+	switch (keylen) {
+	case 16:
+		return EVP_aes_128_ecb();
+	case 24:
+		return EVP_aes_192_ecb();
+	case 32:
+		return EVP_aes_256_ecb();
+	default:
+		return NULL;
+	}
+}
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+	EVP_CIPHER_CTX *ctx;
+	const EVP_CIPHER *type;
+
+	if (TEST_FAIL())
+		return NULL;
+
+	type = aes_get_evp_cipher(len);
+	if (!type) {
+		wpa_printf(MSG_INFO, "%s: Unsupported len=%u",
+			   __func__, (unsigned int) len);
+		return NULL;
+	}
+
+	ctx = EVP_CIPHER_CTX_new();
+	if (ctx == NULL)
+		return NULL;
+	if (EVP_EncryptInit_ex(ctx, type, NULL, key, NULL) != 1 ||
+	    EVP_CIPHER_CTX_set_padding(ctx, 0) != 1) {
+		EVP_CIPHER_CTX_free(ctx);
+		return NULL;
+	}
+	return ctx;
+}
+
+
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+	EVP_CIPHER_CTX *c = ctx;
+	int clen = 16;
+	if (EVP_EncryptUpdate(c, crypt, &clen, plain, 16) != 1) {
+		wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptUpdate failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		return -1;
+	}
+	return 0;
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+	EVP_CIPHER_CTX *c = ctx;
+	u8 buf[16];
+	int len = sizeof(buf);
+	if (EVP_EncryptFinal_ex(c, buf, &len) != 1) {
+		wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptFinal_ex failed: "
+			   "%s", ERR_error_string(ERR_get_error(), NULL));
+	}
+	if (len != 0) {
+		wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d "
+			   "in AES encrypt", len);
+	}
+	EVP_CIPHER_CTX_free(c);
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+	EVP_CIPHER_CTX *ctx;
+	const EVP_CIPHER *type;
+
+	if (TEST_FAIL())
+		return NULL;
+
+	type = aes_get_evp_cipher(len);
+	if (!type) {
+		wpa_printf(MSG_INFO, "%s: Unsupported len=%u",
+			   __func__, (unsigned int) len);
+		return NULL;
+	}
+
+	ctx = EVP_CIPHER_CTX_new();
+	if (ctx == NULL)
+		return NULL;
+	if (EVP_DecryptInit_ex(ctx, type, NULL, key, NULL) != 1 ||
+	    EVP_CIPHER_CTX_set_padding(ctx, 0) != 1) {
+		EVP_CIPHER_CTX_free(ctx);
+		return NULL;
+	}
+	return ctx;
+}
+
+
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+	EVP_CIPHER_CTX *c = ctx;
+	int plen = 16;
+	if (EVP_DecryptUpdate(c, plain, &plen, crypt, 16) != 1) {
+		wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptUpdate failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		return -1;
+	}
+	return 0;
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+	EVP_CIPHER_CTX *c = ctx;
+	u8 buf[16];
+	int len = sizeof(buf);
+	if (EVP_DecryptFinal_ex(c, buf, &len) != 1) {
+		wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptFinal_ex failed: "
+			   "%s", ERR_error_string(ERR_get_error(), NULL));
+	}
+	if (len != 0) {
+		wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d "
+			   "in AES decrypt", len);
+	}
+	EVP_CIPHER_CTX_free(c);
+}
+
+
+#ifndef CONFIG_FIPS
+#ifndef CONFIG_OPENSSL_INTERNAL_AES_WRAP
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+static const EVP_CIPHER * aes_get_evp_wrap_cipher(size_t keylen)
+{
+	switch (keylen) {
+	case 16:
+		return EVP_aes_128_wrap();
+	case 24:
+		return EVP_aes_192_wrap();
+	case 32:
+		return EVP_aes_256_wrap();
+	default:
+		return NULL;
+	}
+}
+#endif /* OpenSSL version >= 3.0 */
+
+
+int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_CIPHER_CTX *ctx;
+	const EVP_CIPHER *type;
+	int ret = -1, len;
+	u8 buf[16];
+
+	if (TEST_FAIL())
+		return -1;
+
+	type = aes_get_evp_wrap_cipher(kek_len);
+	if (!type)
+		return -1;
+
+	ctx = EVP_CIPHER_CTX_new();
+	if (!ctx)
+		return -1;
+
+	if (EVP_EncryptInit_ex(ctx, type, NULL, kek, NULL) == 1 &&
+	    EVP_CIPHER_CTX_set_padding(ctx, 0) == 1 &&
+	    EVP_EncryptUpdate(ctx, cipher, &len, plain, n * 8) == 1 &&
+	    len == (n + 1) * 8 &&
+	    EVP_EncryptFinal_ex(ctx, buf, &len) == 1)
+		ret = 0;
+
+	EVP_CIPHER_CTX_free(ctx);
+	return ret;
+#else /* OpenSSL version >= 3.0 */
+	AES_KEY actx;
+	int res;
+
+	if (TEST_FAIL())
+		return -1;
+	if (AES_set_encrypt_key(kek, kek_len << 3, &actx))
+		return -1;
+	res = AES_wrap_key(&actx, NULL, cipher, plain, n * 8);
+	OPENSSL_cleanse(&actx, sizeof(actx));
+	return res <= 0 ? -1 : 0;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
+	       u8 *plain)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_CIPHER_CTX *ctx;
+	const EVP_CIPHER *type;
+	int ret = -1, len;
+	u8 buf[16];
+
+	if (TEST_FAIL())
+		return -1;
+
+	type = aes_get_evp_wrap_cipher(kek_len);
+	if (!type)
+		return -1;
+
+	ctx = EVP_CIPHER_CTX_new();
+	if (!ctx)
+		return -1;
+
+	if (EVP_DecryptInit_ex(ctx, type, NULL, kek, NULL) == 1 &&
+	    EVP_CIPHER_CTX_set_padding(ctx, 0) == 1 &&
+	    EVP_DecryptUpdate(ctx, plain, &len, cipher, (n + 1) * 8) == 1 &&
+	    len == n * 8 &&
+	    EVP_DecryptFinal_ex(ctx, buf, &len) == 1)
+		ret = 0;
+
+	EVP_CIPHER_CTX_free(ctx);
+	return ret;
+#else /* OpenSSL version >= 3.0 */
+	AES_KEY actx;
+	int res;
+
+	if (TEST_FAIL())
+		return -1;
+	if (AES_set_decrypt_key(kek, kek_len << 3, &actx))
+		return -1;
+	res = AES_unwrap_key(&actx, NULL, plain, cipher, (n + 1) * 8);
+	OPENSSL_cleanse(&actx, sizeof(actx));
+	return res <= 0 ? -1 : 0;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+#endif /* CONFIG_OPENSSL_INTERNAL_AES_WRAP */
+#endif /* CONFIG_FIPS */
+
+
+int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+	EVP_CIPHER_CTX *ctx;
+	int clen, len;
+	u8 buf[16];
+	int res = -1;
+
+	if (TEST_FAIL())
+		return -1;
+
+	ctx = EVP_CIPHER_CTX_new();
+	if (!ctx)
+		return -1;
+	clen = data_len;
+	len = sizeof(buf);
+	if (EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv) == 1 &&
+	    EVP_CIPHER_CTX_set_padding(ctx, 0) == 1 &&
+	    EVP_EncryptUpdate(ctx, data, &clen, data, data_len) == 1 &&
+	    clen == (int) data_len &&
+	    EVP_EncryptFinal_ex(ctx, buf, &len) == 1 && len == 0)
+		res = 0;
+	EVP_CIPHER_CTX_free(ctx);
+
+	return res;
+}
+
+
+int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+	EVP_CIPHER_CTX *ctx;
+	int plen, len;
+	u8 buf[16];
+	int res = -1;
+
+	if (TEST_FAIL())
+		return -1;
+
+	ctx = EVP_CIPHER_CTX_new();
+	if (!ctx)
+		return -1;
+	plen = data_len;
+	len = sizeof(buf);
+	if (EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv) == 1 &&
+	    EVP_CIPHER_CTX_set_padding(ctx, 0) == 1 &&
+	    EVP_DecryptUpdate(ctx, data, &plen, data, data_len) == 1 &&
+	    plen == (int) data_len &&
+	    EVP_DecryptFinal_ex(ctx, buf, &len) == 1 && len == 0)
+		res = 0;
+	EVP_CIPHER_CTX_free(ctx);
+
+	return res;
+
+}
+
+
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+		   u8 *pubkey)
+{
+	size_t pubkey_len, pad;
+
+	if (os_get_random(privkey, prime_len) < 0)
+		return -1;
+	if (os_memcmp(privkey, prime, prime_len) > 0) {
+		/* Make sure private value is smaller than prime */
+		privkey[0] = 0;
+	}
+
+	pubkey_len = prime_len;
+	if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
+			   pubkey, &pubkey_len) < 0)
+		return -1;
+	if (pubkey_len < prime_len) {
+		pad = prime_len - pubkey_len;
+		os_memmove(pubkey + pad, pubkey, pubkey_len);
+		os_memset(pubkey, 0, pad);
+	}
+
+	return 0;
+}
+
+
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+			    const u8 *order, size_t order_len,
+			    const u8 *privkey, size_t privkey_len,
+			    const u8 *pubkey, size_t pubkey_len,
+			    u8 *secret, size_t *len)
+{
+	BIGNUM *pub, *p;
+	int res = -1;
+
+	pub = BN_bin2bn(pubkey, pubkey_len, NULL);
+	p = BN_bin2bn(prime, prime_len, NULL);
+	if (!pub || !p || BN_is_zero(pub) || BN_is_one(pub) ||
+	    BN_cmp(pub, p) >= 0)
+		goto fail;
+
+	if (order) {
+		BN_CTX *ctx;
+		BIGNUM *q, *tmp;
+		int failed;
+
+		/* verify: pubkey^q == 1 mod p */
+		q = BN_bin2bn(order, order_len, NULL);
+		ctx = BN_CTX_new();
+		tmp = BN_new();
+		failed = !q || !ctx || !tmp ||
+			!BN_mod_exp(tmp, pub, q, p, ctx) ||
+			!BN_is_one(tmp);
+		BN_clear_free(q);
+		BN_clear_free(tmp);
+		BN_CTX_free(ctx);
+		if (failed)
+			goto fail;
+	}
+
+	res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+			     prime, prime_len, secret, len);
+fail:
+	BN_clear_free(pub);
+	BN_clear_free(p);
+	return res;
+}
+
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+		   const u8 *power, size_t power_len,
+		   const u8 *modulus, size_t modulus_len,
+		   u8 *result, size_t *result_len)
+{
+	BIGNUM *bn_base, *bn_exp, *bn_modulus, *bn_result;
+	int ret = -1;
+	BN_CTX *ctx;
+
+	ctx = BN_CTX_new();
+	if (ctx == NULL)
+		return -1;
+
+	bn_base = BN_bin2bn(base, base_len, NULL);
+	bn_exp = BN_bin2bn(power, power_len, NULL);
+	bn_modulus = BN_bin2bn(modulus, modulus_len, NULL);
+	bn_result = BN_new();
+
+	if (bn_base == NULL || bn_exp == NULL || bn_modulus == NULL ||
+	    bn_result == NULL)
+		goto error;
+
+	if (BN_mod_exp_mont_consttime(bn_result, bn_base, bn_exp, bn_modulus,
+				      ctx, NULL) != 1)
+		goto error;
+
+	*result_len = BN_bn2bin(bn_result, result);
+	ret = 0;
+
+error:
+	BN_clear_free(bn_base);
+	BN_clear_free(bn_exp);
+	BN_clear_free(bn_modulus);
+	BN_clear_free(bn_result);
+	BN_CTX_free(ctx);
+	return ret;
+}
+
+
+struct crypto_cipher {
+	EVP_CIPHER_CTX *enc;
+	EVP_CIPHER_CTX *dec;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+					  const u8 *iv, const u8 *key,
+					  size_t key_len)
+{
+	struct crypto_cipher *ctx;
+	const EVP_CIPHER *cipher;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+
+	switch (alg) {
+#ifndef CONFIG_NO_RC4
+#ifndef OPENSSL_NO_RC4
+	case CRYPTO_CIPHER_ALG_RC4:
+		cipher = EVP_rc4();
+		break;
+#endif /* OPENSSL_NO_RC4 */
+#endif /* CONFIG_NO_RC4 */
+#ifndef OPENSSL_NO_AES
+	case CRYPTO_CIPHER_ALG_AES:
+		switch (key_len) {
+		case 16:
+			cipher = EVP_aes_128_cbc();
+			break;
+#ifndef OPENSSL_IS_BORINGSSL
+		case 24:
+			cipher = EVP_aes_192_cbc();
+			break;
+#endif /* OPENSSL_IS_BORINGSSL */
+		case 32:
+			cipher = EVP_aes_256_cbc();
+			break;
+		default:
+			os_free(ctx);
+			return NULL;
+		}
+		break;
+#endif /* OPENSSL_NO_AES */
+#ifndef OPENSSL_NO_DES
+	case CRYPTO_CIPHER_ALG_3DES:
+		cipher = EVP_des_ede3_cbc();
+		break;
+	case CRYPTO_CIPHER_ALG_DES:
+		cipher = EVP_des_cbc();
+		break;
+#endif /* OPENSSL_NO_DES */
+#ifndef OPENSSL_NO_RC2
+	case CRYPTO_CIPHER_ALG_RC2:
+		cipher = EVP_rc2_ecb();
+		break;
+#endif /* OPENSSL_NO_RC2 */
+	default:
+		os_free(ctx);
+		return NULL;
+	}
+
+	if (!(ctx->enc = EVP_CIPHER_CTX_new()) ||
+	    !EVP_EncryptInit_ex(ctx->enc, cipher, NULL, NULL, NULL) ||
+	    !EVP_CIPHER_CTX_set_padding(ctx->enc, 0) ||
+	    !EVP_CIPHER_CTX_set_key_length(ctx->enc, key_len) ||
+	    !EVP_EncryptInit_ex(ctx->enc, NULL, NULL, key, iv)) {
+		if (ctx->enc)
+			EVP_CIPHER_CTX_free(ctx->enc);
+		os_free(ctx);
+		return NULL;
+	}
+
+	if (!(ctx->dec = EVP_CIPHER_CTX_new()) ||
+	    !EVP_DecryptInit_ex(ctx->dec, cipher, NULL, NULL, NULL) ||
+	    !EVP_CIPHER_CTX_set_padding(ctx->dec, 0) ||
+	    !EVP_CIPHER_CTX_set_key_length(ctx->dec, key_len) ||
+	    !EVP_DecryptInit_ex(ctx->dec, NULL, NULL, key, iv)) {
+		EVP_CIPHER_CTX_free(ctx->enc);
+		if (ctx->dec)
+			EVP_CIPHER_CTX_free(ctx->dec);
+		os_free(ctx);
+		return NULL;
+	}
+
+	return ctx;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+			  u8 *crypt, size_t len)
+{
+	int outl;
+	if (!EVP_EncryptUpdate(ctx->enc, crypt, &outl, plain, len))
+		return -1;
+	return 0;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+			  u8 *plain, size_t len)
+{
+	int outl;
+	outl = len;
+	if (!EVP_DecryptUpdate(ctx->dec, plain, &outl, crypt, len))
+		return -1;
+	return 0;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+	EVP_CIPHER_CTX_free(ctx->enc);
+	EVP_CIPHER_CTX_free(ctx->dec);
+	os_free(ctx);
+}
+
+
+void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
+{
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+	DH *dh;
+	struct wpabuf *pubkey = NULL, *privkey = NULL;
+	size_t publen, privlen;
+
+	*priv = NULL;
+	wpabuf_free(*publ);
+	*publ = NULL;
+
+	dh = DH_new();
+	if (dh == NULL)
+		return NULL;
+
+	dh->g = BN_new();
+	if (dh->g == NULL || BN_set_word(dh->g, 2) != 1)
+		goto err;
+
+	dh->p = get_group5_prime();
+	if (dh->p == NULL)
+		goto err;
+
+	dh->q = get_group5_order();
+	if (!dh->q)
+		goto err;
+
+	if (DH_generate_key(dh) != 1)
+		goto err;
+
+	publen = BN_num_bytes(dh->pub_key);
+	pubkey = wpabuf_alloc(publen);
+	if (pubkey == NULL)
+		goto err;
+	privlen = BN_num_bytes(dh->priv_key);
+	privkey = wpabuf_alloc(privlen);
+	if (privkey == NULL)
+		goto err;
+
+	BN_bn2bin(dh->pub_key, wpabuf_put(pubkey, publen));
+	BN_bn2bin(dh->priv_key, wpabuf_put(privkey, privlen));
+
+	*priv = privkey;
+	*publ = pubkey;
+	return dh;
+
+err:
+	wpabuf_clear_free(pubkey);
+	wpabuf_clear_free(privkey);
+	DH_free(dh);
+	return NULL;
+#elif OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_PKEY *pkey = NULL;
+	OSSL_PARAM params[2];
+	size_t pub_len = OSSL_PARAM_UNMODIFIED;
+	size_t priv_len;
+	struct wpabuf *pubkey = NULL, *privkey = NULL;
+	BIGNUM *priv_bn = NULL;
+	EVP_PKEY_CTX *gctx;
+
+	*priv = NULL;
+	wpabuf_free(*publ);
+	*publ = NULL;
+
+	params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
+						     "modp_1536", 0);
+	params[1] = OSSL_PARAM_construct_end();
+
+	gctx = EVP_PKEY_CTX_new_from_name(NULL, "DH", NULL);
+	if (!gctx ||
+	    EVP_PKEY_keygen_init(gctx) != 1 ||
+	    EVP_PKEY_CTX_set_params(gctx, params) != 1 ||
+	    EVP_PKEY_generate(gctx, &pkey) != 1 ||
+	    EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY,
+				  &priv_bn) != 1 ||
+	    EVP_PKEY_get_octet_string_param(pkey,
+					    OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
+					    NULL, 0, &pub_len) < 0 ||
+	    pub_len == OSSL_PARAM_UNMODIFIED ||
+	    (priv_len = BN_num_bytes(priv_bn)) == 0 ||
+	    !(pubkey = wpabuf_alloc(pub_len)) ||
+	    !(privkey = wpabuf_alloc(priv_len)) ||
+	    EVP_PKEY_get_octet_string_param(pkey,
+					    OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
+					    wpabuf_put(pubkey, pub_len),
+					    pub_len, NULL) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL: failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		wpabuf_free(pubkey);
+		wpabuf_clear_free(privkey);
+		EVP_PKEY_free(pkey);
+		pkey = NULL;
+	} else {
+		BN_bn2bin(priv_bn, wpabuf_put(privkey, priv_len));
+
+		*priv = privkey;
+		*publ = pubkey;
+	}
+
+	BN_clear_free(priv_bn);
+	EVP_PKEY_CTX_free(gctx);
+	return pkey;
+#else
+	DH *dh;
+	struct wpabuf *pubkey = NULL, *privkey = NULL;
+	size_t publen, privlen;
+	BIGNUM *p, *g, *q;
+	const BIGNUM *priv_key = NULL, *pub_key = NULL;
+
+	*priv = NULL;
+	wpabuf_free(*publ);
+	*publ = NULL;
+
+	dh = DH_new();
+	if (dh == NULL)
+		return NULL;
+
+	g = BN_new();
+	p = get_group5_prime();
+	q = get_group5_order();
+	if (!g || BN_set_word(g, 2) != 1 || !p || !q ||
+	    DH_set0_pqg(dh, p, q, g) != 1)
+		goto err;
+	p = NULL;
+	q = NULL;
+	g = NULL;
+
+	if (DH_generate_key(dh) != 1)
+		goto err;
+
+	DH_get0_key(dh, &pub_key, &priv_key);
+	publen = BN_num_bytes(pub_key);
+	pubkey = wpabuf_alloc(publen);
+	if (!pubkey)
+		goto err;
+	privlen = BN_num_bytes(priv_key);
+	privkey = wpabuf_alloc(privlen);
+	if (!privkey)
+		goto err;
+
+	BN_bn2bin(pub_key, wpabuf_put(pubkey, publen));
+	BN_bn2bin(priv_key, wpabuf_put(privkey, privlen));
+
+	*priv = privkey;
+	*publ = pubkey;
+	return dh;
+
+err:
+	BN_free(p);
+	BN_free(q);
+	BN_free(g);
+	wpabuf_clear_free(pubkey);
+	wpabuf_clear_free(privkey);
+	DH_free(dh);
+	return NULL;
+#endif
+}
+
+
+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
+{
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+	DH *dh;
+
+	dh = DH_new();
+	if (dh == NULL)
+		return NULL;
+
+	dh->g = BN_new();
+	if (dh->g == NULL || BN_set_word(dh->g, 2) != 1)
+		goto err;
+
+	dh->p = get_group5_prime();
+	if (dh->p == NULL)
+		goto err;
+
+	dh->priv_key = BN_bin2bn(wpabuf_head(priv), wpabuf_len(priv), NULL);
+	if (dh->priv_key == NULL)
+		goto err;
+
+	dh->pub_key = BN_bin2bn(wpabuf_head(publ), wpabuf_len(publ), NULL);
+	if (dh->pub_key == NULL)
+		goto err;
+
+	if (DH_generate_key(dh) != 1)
+		goto err;
+
+	return dh;
+
+err:
+	DH_free(dh);
+	return NULL;
+#elif OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_PKEY *pkey = NULL;
+	OSSL_PARAM_BLD *bld;
+	OSSL_PARAM *params = NULL;
+	BIGNUM *priv_key, *pub_key;
+	EVP_PKEY_CTX *fctx;
+
+	fctx = EVP_PKEY_CTX_new_from_name(NULL, "DH", NULL);
+	priv_key = BN_bin2bn(wpabuf_head(priv), wpabuf_len(priv), NULL);
+	pub_key = BN_bin2bn(wpabuf_head(publ), wpabuf_len(publ), NULL);
+	bld = OSSL_PARAM_BLD_new();
+	if (!fctx || !priv_key || !pub_key || !bld ||
+	    OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME,
+					    "modp_1536", 0) != 1 ||
+	    OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY,
+				   priv_key) != 1 ||
+	    OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PUB_KEY,
+				   pub_key) != 1 ||
+	    !(params = OSSL_PARAM_BLD_to_param(bld)) ||
+	    EVP_PKEY_fromdata_init(fctx) != 1 ||
+	    EVP_PKEY_fromdata(fctx, &pkey, EVP_PKEY_KEYPAIR, params) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL: EVP_PKEY_fromdata failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		EVP_PKEY_free(pkey);
+		pkey = NULL;
+	}
+
+	BN_clear_free(priv_key);
+	BN_free(pub_key);
+	EVP_PKEY_CTX_free(fctx);
+	OSSL_PARAM_BLD_free(bld);
+	OSSL_PARAM_free(params);
+	return pkey;
+#else
+	DH *dh;
+	BIGNUM *p = NULL, *g, *priv_key = NULL, *pub_key = NULL;
+
+	dh = DH_new();
+	if (dh == NULL)
+		return NULL;
+
+	g = BN_new();
+	p = get_group5_prime();
+	if (!g || BN_set_word(g, 2) != 1 || !p ||
+	    DH_set0_pqg(dh, p, NULL, g) != 1)
+		goto err;
+	p = NULL;
+	g = NULL;
+
+	priv_key = BN_bin2bn(wpabuf_head(priv), wpabuf_len(priv), NULL);
+	pub_key = BN_bin2bn(wpabuf_head(publ), wpabuf_len(publ), NULL);
+	if (!priv_key || !pub_key || DH_set0_key(dh, pub_key, priv_key) != 1)
+		goto err;
+	pub_key = NULL;
+	priv_key = NULL;
+
+	if (DH_generate_key(dh) != 1)
+		goto err;
+
+	return dh;
+
+err:
+	BN_free(p);
+	BN_free(g);
+	BN_free(pub_key);
+	BN_clear_free(priv_key);
+	DH_free(dh);
+	return NULL;
+#endif
+}
+
+
+struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
+				  const struct wpabuf *own_private)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_PKEY *pkey = ctx;
+	EVP_PKEY *peer_pub;
+	size_t len;
+	struct wpabuf *res = NULL;
+	EVP_PKEY_CTX *dctx = NULL;
+
+	peer_pub = EVP_PKEY_new();
+	if (!pkey || !peer_pub ||
+	    EVP_PKEY_copy_parameters(peer_pub, pkey) != 1 ||
+	    EVP_PKEY_set1_encoded_public_key(peer_pub, wpabuf_head(peer_public),
+					     wpabuf_len(peer_public)) != 1 ||
+	    !(dctx = EVP_PKEY_CTX_new(pkey, NULL)) ||
+	    EVP_PKEY_derive_init(dctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(dctx, peer_pub) != 1 ||
+	    EVP_PKEY_derive(dctx, NULL, &len) != 1 ||
+	    !(res = wpabuf_alloc(len)) ||
+	    EVP_PKEY_derive(dctx, wpabuf_mhead(res), &len) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL: EVP_PKEY_derive failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		wpabuf_free(res);
+		res = NULL;
+	} else {
+		wpabuf_put(res, len);
+	}
+
+	EVP_PKEY_free(peer_pub);
+	EVP_PKEY_CTX_free(dctx);
+	return res;
+#else /* OpenSSL version >= 3.0 */
+	BIGNUM *pub_key;
+	struct wpabuf *res = NULL;
+	size_t rlen;
+	DH *dh = ctx;
+	int keylen;
+
+	if (ctx == NULL)
+		return NULL;
+
+	pub_key = BN_bin2bn(wpabuf_head(peer_public), wpabuf_len(peer_public),
+			    NULL);
+	if (pub_key == NULL)
+		return NULL;
+
+	rlen = DH_size(dh);
+	res = wpabuf_alloc(rlen);
+	if (res == NULL)
+		goto err;
+
+	keylen = DH_compute_key(wpabuf_mhead(res), pub_key, dh);
+	if (keylen < 0)
+		goto err;
+	wpabuf_put(res, keylen);
+	BN_clear_free(pub_key);
+
+	return res;
+
+err:
+	BN_clear_free(pub_key);
+	wpabuf_clear_free(res);
+	return NULL;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+void dh5_free(void *ctx)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_PKEY *pkey = ctx;
+
+	EVP_PKEY_free(pkey);
+#else /* OpenSSL version >= 3.0 */
+	DH *dh;
+	if (ctx == NULL)
+		return;
+	dh = ctx;
+	DH_free(dh);
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+struct crypto_hash {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_MAC_CTX *ctx;
+#else /* OpenSSL version >= 3.0 */
+	HMAC_CTX *ctx;
+#endif /* OpenSSL version >= 3.0 */
+	bool failed;
+};
+
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+				      size_t key_len)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	struct crypto_hash *ctx;
+	EVP_MAC *mac;
+	OSSL_PARAM params[2];
+	char *a = NULL;
+
+	switch (alg) {
+#ifndef OPENSSL_NO_MD5
+	case CRYPTO_HASH_ALG_HMAC_MD5:
+		a = "MD5";
+		break;
+#endif /* OPENSSL_NO_MD5 */
+#ifndef OPENSSL_NO_SHA
+	case CRYPTO_HASH_ALG_HMAC_SHA1:
+		a = "SHA1";
+		break;
+#endif /* OPENSSL_NO_SHA */
+#ifndef OPENSSL_NO_SHA256
+#ifdef CONFIG_SHA256
+	case CRYPTO_HASH_ALG_HMAC_SHA256:
+		a = "SHA256";
+		break;
+#endif /* CONFIG_SHA256 */
+#endif /* OPENSSL_NO_SHA256 */
+	default:
+		return NULL;
+	}
+
+	mac = EVP_MAC_fetch(NULL, "HMAC", NULL);
+	if (!mac)
+		return NULL;
+
+	params[0] = OSSL_PARAM_construct_utf8_string("digest", a, 0);
+	params[1] = OSSL_PARAM_construct_end();
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (!ctx)
+		goto fail;
+	ctx->ctx = EVP_MAC_CTX_new(mac);
+	if (!ctx->ctx) {
+		os_free(ctx);
+		ctx = NULL;
+		goto fail;
+	}
+
+	if (EVP_MAC_init(ctx->ctx, key, key_len, params) != 1) {
+		EVP_MAC_CTX_free(ctx->ctx);
+		bin_clear_free(ctx, sizeof(*ctx));
+		ctx = NULL;
+		goto fail;
+	}
+
+fail:
+	EVP_MAC_free(mac);
+	return ctx;
+#else /* OpenSSL version >= 3.0 */
+	struct crypto_hash *ctx;
+	const EVP_MD *md;
+
+	switch (alg) {
+#ifndef OPENSSL_NO_MD5
+	case CRYPTO_HASH_ALG_HMAC_MD5:
+		md = EVP_md5();
+		break;
+#endif /* OPENSSL_NO_MD5 */
+#ifndef OPENSSL_NO_SHA
+	case CRYPTO_HASH_ALG_HMAC_SHA1:
+		md = EVP_sha1();
+		break;
+#endif /* OPENSSL_NO_SHA */
+#ifndef OPENSSL_NO_SHA256
+#ifdef CONFIG_SHA256
+	case CRYPTO_HASH_ALG_HMAC_SHA256:
+		md = EVP_sha256();
+		break;
+#endif /* CONFIG_SHA256 */
+#endif /* OPENSSL_NO_SHA256 */
+	default:
+		return NULL;
+	}
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+	ctx->ctx = HMAC_CTX_new();
+	if (!ctx->ctx) {
+		os_free(ctx);
+		return NULL;
+	}
+
+	if (HMAC_Init_ex(ctx->ctx, key, key_len, md, NULL) != 1) {
+		HMAC_CTX_free(ctx->ctx);
+		bin_clear_free(ctx, sizeof(*ctx));
+		return NULL;
+	}
+
+	return ctx;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+	if (ctx == NULL)
+		return;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	if (!EVP_MAC_update(ctx->ctx, data, len))
+		ctx->failed = true;
+#else /* OpenSSL version >= 3.0 */
+	if (!HMAC_Update(ctx->ctx, data, len))
+		ctx->failed = true;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	size_t mdlen;
+	int res;
+	bool failed;
+
+	if (!ctx)
+		return -2;
+
+	if (!mac || !len) {
+		EVP_MAC_CTX_free(ctx->ctx);
+		bin_clear_free(ctx, sizeof(*ctx));
+		return 0;
+	}
+
+	res = EVP_MAC_final(ctx->ctx, NULL, &mdlen, 0);
+	if (res != 1) {
+		EVP_MAC_CTX_free(ctx->ctx);
+		bin_clear_free(ctx, sizeof(*ctx));
+		return -1;
+	}
+	res = EVP_MAC_final(ctx->ctx, mac, &mdlen, mdlen);
+	EVP_MAC_CTX_free(ctx->ctx);
+	failed = ctx->failed;
+	bin_clear_free(ctx, sizeof(*ctx));
+
+	if (TEST_FAIL())
+		return -1;
+
+	if (failed)
+		return -2;
+
+	if (res == 1) {
+		*len = mdlen;
+		return 0;
+	}
+
+	return -1;
+#else /* OpenSSL version >= 3.0 */
+	unsigned int mdlen;
+	int res;
+	bool failed;
+
+	if (ctx == NULL)
+		return -2;
+
+	if (mac == NULL || len == NULL) {
+		HMAC_CTX_free(ctx->ctx);
+		bin_clear_free(ctx, sizeof(*ctx));
+		return 0;
+	}
+
+	mdlen = *len;
+	res = HMAC_Final(ctx->ctx, mac, &mdlen);
+	HMAC_CTX_free(ctx->ctx);
+	failed = ctx->failed;
+	bin_clear_free(ctx, sizeof(*ctx));
+
+	if (TEST_FAIL())
+		return -1;
+
+	if (failed)
+		return -2;
+
+	if (res == 1) {
+		*len = mdlen;
+		return 0;
+	}
+
+	return -1;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+
+static int openssl_hmac_vector(char *digest, const u8 *key,
+			       size_t key_len, size_t num_elem,
+			       const u8 *addr[], const size_t *len, u8 *mac,
+			       unsigned int mdlen)
+{
+	EVP_MAC *hmac;
+	OSSL_PARAM params[2];
+	EVP_MAC_CTX *ctx;
+	size_t i, mlen;
+	int res;
+
+	if (TEST_FAIL())
+		return -1;
+
+	hmac = EVP_MAC_fetch(NULL, "HMAC", NULL);
+	if (!hmac)
+		return -1;
+
+	params[0] = OSSL_PARAM_construct_utf8_string("digest", digest, 0);
+	params[1] = OSSL_PARAM_construct_end();
+
+	ctx = EVP_MAC_CTX_new(hmac);
+	EVP_MAC_free(hmac);
+	if (!ctx)
+		return -1;
+
+	if (EVP_MAC_init(ctx, key, key_len, params) != 1)
+		goto fail;
+
+	for (i = 0; i < num_elem; i++) {
+		if (EVP_MAC_update(ctx, addr[i], len[i]) != 1)
+			goto fail;
+	}
+
+	res = EVP_MAC_final(ctx, mac, &mlen, mdlen);
+	EVP_MAC_CTX_free(ctx);
+
+	return res == 1 ? 0 : -1;
+fail:
+	EVP_MAC_CTX_free(ctx);
+	return -1;
+}
+
+
+#ifndef CONFIG_FIPS
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+		    const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_hmac_vector("MD5", key ,key_len, num_elem, addr, len,
+				   mac, 16);
+}
+
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	     u8 *mac)
+{
+	return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_FIPS */
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_hmac_vector("SHA1", key, key_len, num_elem, addr,
+				   len, mac, 20);
+}
+
+
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	       u8 *mac)
+{
+	return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+#ifdef CONFIG_SHA256
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_hmac_vector("SHA256", key, key_len, num_elem, addr,
+				   len, mac, 32);
+}
+
+
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA256 */
+
+
+#ifdef CONFIG_SHA384
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_hmac_vector("SHA384", key, key_len, num_elem, addr,
+				   len, mac, 48);
+}
+
+
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA384 */
+
+
+#ifdef CONFIG_SHA512
+
+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_hmac_vector("SHA512", key, key_len, num_elem, addr,
+				   len, mac, 64);
+}
+
+
+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA512 */
+
+#else /* OpenSSL version >= 3.0 */
+
+static int openssl_hmac_vector(const EVP_MD *type, const u8 *key,
+			       size_t key_len, size_t num_elem,
+			       const u8 *addr[], const size_t *len, u8 *mac,
+			       unsigned int mdlen)
+{
+	HMAC_CTX *ctx;
+	size_t i;
+	int res;
+
+	if (TEST_FAIL())
+		return -1;
+
+	ctx = HMAC_CTX_new();
+	if (!ctx)
+		return -1;
+	res = HMAC_Init_ex(ctx, key, key_len, type, NULL);
+	if (res != 1)
+		goto done;
+
+	for (i = 0; i < num_elem; i++)
+		HMAC_Update(ctx, addr[i], len[i]);
+
+	res = HMAC_Final(ctx, mac, &mdlen);
+done:
+	HMAC_CTX_free(ctx);
+
+	return res == 1 ? 0 : -1;
+}
+
+
+#ifndef CONFIG_FIPS
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+		    const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_hmac_vector(EVP_md5(), key ,key_len, num_elem, addr, len,
+				   mac, 16);
+}
+
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	     u8 *mac)
+{
+	return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_FIPS */
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_hmac_vector(EVP_sha1(), key, key_len, num_elem, addr,
+				   len, mac, 20);
+}
+
+
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	       u8 *mac)
+{
+	return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+#ifdef CONFIG_SHA256
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_hmac_vector(EVP_sha256(), key, key_len, num_elem, addr,
+				   len, mac, 32);
+}
+
+
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA256 */
+
+
+#ifdef CONFIG_SHA384
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_hmac_vector(EVP_sha384(), key, key_len, num_elem, addr,
+				   len, mac, 48);
+}
+
+
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA384 */
+
+
+#ifdef CONFIG_SHA512
+
+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_hmac_vector(EVP_sha512(), key, key_len, num_elem, addr,
+				   len, mac, 64);
+}
+
+
+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA512 */
+
+#endif /* OpenSSL version >= 3.0 */
+
+
+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
+		int iterations, u8 *buf, size_t buflen)
+{
+	if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), ssid,
+				   ssid_len, iterations, buflen, buf) != 1)
+		return -1;
+	return 0;
+}
+
+
+int crypto_get_random(void *buf, size_t len)
+{
+	if (RAND_bytes(buf, len) != 1)
+		return -1;
+	return 0;
+}
+
+
+int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_MAC_CTX *ctx = NULL;
+	EVP_MAC *emac;
+	int ret = -1;
+	size_t outlen, i;
+	OSSL_PARAM params[2];
+	char *cipher = NULL;
+
+	if (TEST_FAIL())
+		return -1;
+
+	emac = EVP_MAC_fetch(NULL, "CMAC", NULL);
+
+	if (key_len == 32)
+		cipher = "aes-256-cbc";
+	else if (key_len == 24)
+		cipher = "aes-192-cbc";
+	else if (key_len == 16)
+		cipher = "aes-128-cbc";
+
+	params[0] = OSSL_PARAM_construct_utf8_string("cipher", cipher, 0);
+	params[1] = OSSL_PARAM_construct_end();
+
+	if (!emac || !cipher ||
+	    !(ctx = EVP_MAC_CTX_new(emac)) ||
+	    EVP_MAC_init(ctx, key, key_len, params) != 1)
+		goto fail;
+
+	for (i = 0; i < num_elem; i++) {
+		if (!EVP_MAC_update(ctx, addr[i], len[i]))
+			goto fail;
+	}
+	if (EVP_MAC_final(ctx, mac, &outlen, 16) != 1 || outlen != 16)
+		goto fail;
+
+	ret = 0;
+fail:
+	EVP_MAC_CTX_free(ctx);
+	EVP_MAC_free(emac);
+	return ret;
+#else /* OpenSSL version >= 3.0 */
+	CMAC_CTX *ctx;
+	int ret = -1;
+	size_t outlen, i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	ctx = CMAC_CTX_new();
+	if (ctx == NULL)
+		return -1;
+
+	if (key_len == 32) {
+		if (!CMAC_Init(ctx, key, 32, EVP_aes_256_cbc(), NULL))
+			goto fail;
+	} else if (key_len == 24) {
+		if (!CMAC_Init(ctx, key, 24, EVP_aes_192_cbc(), NULL))
+			goto fail;
+	} else if (key_len == 16) {
+		if (!CMAC_Init(ctx, key, 16, EVP_aes_128_cbc(), NULL))
+			goto fail;
+	} else {
+		goto fail;
+	}
+	for (i = 0; i < num_elem; i++) {
+		if (!CMAC_Update(ctx, addr[i], len[i]))
+			goto fail;
+	}
+	if (!CMAC_Final(ctx, mac, &outlen) || outlen != 16)
+		goto fail;
+
+	ret = 0;
+fail:
+	CMAC_CTX_free(ctx);
+	return ret;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
+			 const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
+}
+
+
+int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+	return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
+}
+
+
+int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+	return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
+}
+
+
+struct crypto_bignum * crypto_bignum_init(void)
+{
+	if (TEST_FAIL())
+		return NULL;
+	return (struct crypto_bignum *) BN_new();
+}
+
+
+struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len)
+{
+	BIGNUM *bn;
+
+	if (TEST_FAIL())
+		return NULL;
+
+	bn = BN_bin2bn(buf, len, NULL);
+	return (struct crypto_bignum *) bn;
+}
+
+
+struct crypto_bignum * crypto_bignum_init_uint(unsigned int val)
+{
+	BIGNUM *bn;
+
+	if (TEST_FAIL())
+		return NULL;
+
+	bn = BN_new();
+	if (!bn)
+		return NULL;
+	if (BN_set_word(bn, val) != 1) {
+		BN_free(bn);
+		return NULL;
+	}
+	return (struct crypto_bignum *) bn;
+}
+
+
+void crypto_bignum_deinit(struct crypto_bignum *n, int clear)
+{
+	if (clear)
+		BN_clear_free((BIGNUM *) n);
+	else
+		BN_free((BIGNUM *) n);
+}
+
+
+int crypto_bignum_to_bin(const struct crypto_bignum *a,
+			 u8 *buf, size_t buflen, size_t padlen)
+{
+	int num_bytes, offset;
+
+	if (TEST_FAIL())
+		return -1;
+
+	if (padlen > buflen)
+		return -1;
+
+	if (padlen) {
+#ifdef OPENSSL_IS_BORINGSSL
+		if (BN_bn2bin_padded(buf, padlen, (const BIGNUM *) a) == 0)
+			return -1;
+		return padlen;
+#else /* OPENSSL_IS_BORINGSSL */
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
+		return BN_bn2binpad((const BIGNUM *) a, buf, padlen);
+#endif
+#endif
+	}
+
+	num_bytes = BN_num_bytes((const BIGNUM *) a);
+	if ((size_t) num_bytes > buflen)
+		return -1;
+	if (padlen > (size_t) num_bytes)
+		offset = padlen - num_bytes;
+	else
+		offset = 0;
+
+	os_memset(buf, 0, offset);
+	BN_bn2bin((const BIGNUM *) a, buf + offset);
+
+	return num_bytes + offset;
+}
+
+
+int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
+{
+	if (TEST_FAIL())
+		return -1;
+	return BN_rand_range((BIGNUM *) r, (const BIGNUM *) m) == 1 ? 0 : -1;
+}
+
+
+int crypto_bignum_add(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c)
+{
+	return BN_add((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ?
+		0 : -1;
+}
+
+
+int crypto_bignum_mod(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c)
+{
+	int res;
+	BN_CTX *bnctx;
+
+	bnctx = BN_CTX_new();
+	if (bnctx == NULL)
+		return -1;
+	res = BN_mod((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b,
+		     bnctx);
+	BN_CTX_free(bnctx);
+
+	return res ? 0 : -1;
+}
+
+
+int crypto_bignum_exptmod(const struct crypto_bignum *a,
+			  const struct crypto_bignum *b,
+			  const struct crypto_bignum *c,
+			  struct crypto_bignum *d)
+{
+	int res;
+	BN_CTX *bnctx;
+
+	if (TEST_FAIL())
+		return -1;
+
+	bnctx = BN_CTX_new();
+	if (bnctx == NULL)
+		return -1;
+	res = BN_mod_exp_mont_consttime((BIGNUM *) d, (const BIGNUM *) a,
+					(const BIGNUM *) b, (const BIGNUM *) c,
+					bnctx, NULL);
+	BN_CTX_free(bnctx);
+
+	return res ? 0 : -1;
+}
+
+
+int crypto_bignum_inverse(const struct crypto_bignum *a,
+			  const struct crypto_bignum *b,
+			  struct crypto_bignum *c)
+{
+	BIGNUM *res;
+	BN_CTX *bnctx;
+
+	if (TEST_FAIL())
+		return -1;
+	bnctx = BN_CTX_new();
+	if (bnctx == NULL)
+		return -1;
+#ifdef OPENSSL_IS_BORINGSSL
+	/* TODO: use BN_mod_inverse_blinded() ? */
+#else /* OPENSSL_IS_BORINGSSL */
+	BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME);
+#endif /* OPENSSL_IS_BORINGSSL */
+	res = BN_mod_inverse((BIGNUM *) c, (const BIGNUM *) a,
+			     (const BIGNUM *) b, bnctx);
+	BN_CTX_free(bnctx);
+
+	return res ? 0 : -1;
+}
+
+
+int crypto_bignum_sub(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c)
+{
+	if (TEST_FAIL())
+		return -1;
+	return BN_sub((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ?
+		0 : -1;
+}
+
+
+int crypto_bignum_div(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c)
+{
+	int res;
+
+	BN_CTX *bnctx;
+
+	if (TEST_FAIL())
+		return -1;
+
+	bnctx = BN_CTX_new();
+	if (bnctx == NULL)
+		return -1;
+#ifndef OPENSSL_IS_BORINGSSL
+	BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME);
+#endif /* OPENSSL_IS_BORINGSSL */
+	res = BN_div((BIGNUM *) c, NULL, (const BIGNUM *) a,
+		     (const BIGNUM *) b, bnctx);
+	BN_CTX_free(bnctx);
+
+	return res ? 0 : -1;
+}
+
+
+int crypto_bignum_addmod(const struct crypto_bignum *a,
+			 const struct crypto_bignum *b,
+			 const struct crypto_bignum *c,
+			 struct crypto_bignum *d)
+{
+	int res;
+	BN_CTX *bnctx;
+
+	if (TEST_FAIL())
+		return -1;
+
+	bnctx = BN_CTX_new();
+	if (!bnctx)
+		return -1;
+	res = BN_mod_add((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b,
+			 (const BIGNUM *) c, bnctx);
+	BN_CTX_free(bnctx);
+
+	return res ? 0 : -1;
+}
+
+
+int crypto_bignum_mulmod(const struct crypto_bignum *a,
+			 const struct crypto_bignum *b,
+			 const struct crypto_bignum *c,
+			 struct crypto_bignum *d)
+{
+	int res;
+
+	BN_CTX *bnctx;
+
+	if (TEST_FAIL())
+		return -1;
+
+	bnctx = BN_CTX_new();
+	if (bnctx == NULL)
+		return -1;
+	res = BN_mod_mul((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b,
+			 (const BIGNUM *) c, bnctx);
+	BN_CTX_free(bnctx);
+
+	return res ? 0 : -1;
+}
+
+
+int crypto_bignum_sqrmod(const struct crypto_bignum *a,
+			 const struct crypto_bignum *b,
+			 struct crypto_bignum *c)
+{
+	int res;
+	BN_CTX *bnctx;
+
+	if (TEST_FAIL())
+		return -1;
+
+	bnctx = BN_CTX_new();
+	if (!bnctx)
+		return -1;
+	res = BN_mod_sqr((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b,
+			 bnctx);
+	BN_CTX_free(bnctx);
+
+	return res ? 0 : -1;
+}
+
+
+int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
+			 struct crypto_bignum *r)
+{
+	return BN_rshift((BIGNUM *) r, (const BIGNUM *) a, n) == 1 ? 0 : -1;
+}
+
+
+int crypto_bignum_cmp(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b)
+{
+	return BN_cmp((const BIGNUM *) a, (const BIGNUM *) b);
+}
+
+
+int crypto_bignum_is_zero(const struct crypto_bignum *a)
+{
+	return BN_is_zero((const BIGNUM *) a);
+}
+
+
+int crypto_bignum_is_one(const struct crypto_bignum *a)
+{
+	return BN_is_one((const BIGNUM *) a);
+}
+
+
+int crypto_bignum_is_odd(const struct crypto_bignum *a)
+{
+	return BN_is_odd((const BIGNUM *) a);
+}
+
+
+int crypto_bignum_legendre(const struct crypto_bignum *a,
+			   const struct crypto_bignum *p)
+{
+	BN_CTX *bnctx;
+	BIGNUM *exp = NULL, *tmp = NULL;
+	int res = -2;
+	unsigned int mask;
+
+	if (TEST_FAIL())
+		return -2;
+
+	bnctx = BN_CTX_new();
+	if (bnctx == NULL)
+		return -2;
+
+	exp = BN_new();
+	tmp = BN_new();
+	if (!exp || !tmp ||
+	    /* exp = (p-1) / 2 */
+	    !BN_sub(exp, (const BIGNUM *) p, BN_value_one()) ||
+	    !BN_rshift1(exp, exp) ||
+	    !BN_mod_exp_mont_consttime(tmp, (const BIGNUM *) a, exp,
+				       (const BIGNUM *) p, bnctx, NULL))
+		goto fail;
+
+	/* Return 1 if tmp == 1, 0 if tmp == 0, or -1 otherwise. Need to use
+	 * constant time selection to avoid branches here. */
+	res = -1;
+	mask = const_time_eq(BN_is_word(tmp, 1), 1);
+	res = const_time_select_int(mask, 1, res);
+	mask = const_time_eq(BN_is_zero(tmp), 1);
+	res = const_time_select_int(mask, 0, res);
+
+fail:
+	BN_clear_free(tmp);
+	BN_clear_free(exp);
+	BN_CTX_free(bnctx);
+	return res;
+}
+
+
+#ifdef CONFIG_ECC
+
+struct crypto_ec {
+	EC_GROUP *group;
+	int nid;
+	int iana_group;
+	BN_CTX *bnctx;
+	BIGNUM *prime;
+	BIGNUM *order;
+	BIGNUM *a;
+	BIGNUM *b;
+};
+
+
+static int crypto_ec_group_2_nid(int group)
+{
+	/* Map from IANA registry for IKE D-H groups to OpenSSL NID */
+	switch (group) {
+	case 19:
+		return NID_X9_62_prime256v1;
+	case 20:
+		return NID_secp384r1;
+	case 21:
+		return NID_secp521r1;
+	case 25:
+		return NID_X9_62_prime192v1;
+	case 26:
+		return NID_secp224r1;
+#ifdef NID_brainpoolP224r1
+	case 27:
+		return NID_brainpoolP224r1;
+#endif /* NID_brainpoolP224r1 */
+#ifdef NID_brainpoolP256r1
+	case 28:
+		return NID_brainpoolP256r1;
+#endif /* NID_brainpoolP256r1 */
+#ifdef NID_brainpoolP384r1
+	case 29:
+		return NID_brainpoolP384r1;
+#endif /* NID_brainpoolP384r1 */
+#ifdef NID_brainpoolP512r1
+	case 30:
+		return NID_brainpoolP512r1;
+#endif /* NID_brainpoolP512r1 */
+	default:
+		return -1;
+	}
+}
+
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+static const char * crypto_ec_group_2_name(int group)
+{
+	/* Map from IANA registry for IKE D-H groups to OpenSSL group name */
+	switch (group) {
+	case 19:
+		return "prime256v1";
+	case 20:
+		return "secp384r1";
+	case 21:
+		return "secp521r1";
+	case 25:
+		return "prime192v1";
+	case 26:
+		return "secp224r1";
+#ifdef NID_brainpoolP224r1
+	case 27:
+		return "brainpoolP224r1";
+#endif /* NID_brainpoolP224r1 */
+#ifdef NID_brainpoolP256r1
+	case 28:
+		return "brainpoolP256r1";
+#endif /* NID_brainpoolP256r1 */
+#ifdef NID_brainpoolP384r1
+	case 29:
+		return "brainpoolP384r1";
+#endif /* NID_brainpoolP384r1 */
+#ifdef NID_brainpoolP512r1
+	case 30:
+		return "brainpoolP512r1";
+#endif /* NID_brainpoolP512r1 */
+	default:
+		return NULL;
+	}
+}
+#endif /* OpenSSL version >= 3.0 */
+
+
+struct crypto_ec * crypto_ec_init(int group)
+{
+	struct crypto_ec *e;
+	int nid;
+
+	nid = crypto_ec_group_2_nid(group);
+	if (nid < 0)
+		return NULL;
+
+	e = os_zalloc(sizeof(*e));
+	if (e == NULL)
+		return NULL;
+
+	e->nid = nid;
+	e->iana_group = group;
+	e->bnctx = BN_CTX_new();
+	e->group = EC_GROUP_new_by_curve_name(nid);
+	e->prime = BN_new();
+	e->order = BN_new();
+	e->a = BN_new();
+	e->b = BN_new();
+	if (e->group == NULL || e->bnctx == NULL || e->prime == NULL ||
+	    e->order == NULL || e->a == NULL || e->b == NULL ||
+	    !EC_GROUP_get_curve(e->group, e->prime, e->a, e->b, e->bnctx) ||
+	    !EC_GROUP_get_order(e->group, e->order, e->bnctx)) {
+		crypto_ec_deinit(e);
+		e = NULL;
+	}
+
+	return e;
+}
+
+
+void crypto_ec_deinit(struct crypto_ec *e)
+{
+	if (e == NULL)
+		return;
+	BN_clear_free(e->b);
+	BN_clear_free(e->a);
+	BN_clear_free(e->order);
+	BN_clear_free(e->prime);
+	EC_GROUP_free(e->group);
+	BN_CTX_free(e->bnctx);
+	os_free(e);
+}
+
+
+struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e)
+{
+	if (TEST_FAIL())
+		return NULL;
+	if (e == NULL)
+		return NULL;
+	return (struct crypto_ec_point *) EC_POINT_new(e->group);
+}
+
+
+size_t crypto_ec_prime_len(struct crypto_ec *e)
+{
+	return BN_num_bytes(e->prime);
+}
+
+
+size_t crypto_ec_prime_len_bits(struct crypto_ec *e)
+{
+	return BN_num_bits(e->prime);
+}
+
+
+size_t crypto_ec_order_len(struct crypto_ec *e)
+{
+	return BN_num_bytes(e->order);
+}
+
+
+const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e)
+{
+	return (const struct crypto_bignum *) e->prime;
+}
+
+
+const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e)
+{
+	return (const struct crypto_bignum *) e->order;
+}
+
+
+const struct crypto_bignum * crypto_ec_get_a(struct crypto_ec *e)
+{
+	return (const struct crypto_bignum *) e->a;
+}
+
+
+const struct crypto_bignum * crypto_ec_get_b(struct crypto_ec *e)
+{
+	return (const struct crypto_bignum *) e->b;
+}
+
+
+const struct crypto_ec_point * crypto_ec_get_generator(struct crypto_ec *e)
+{
+	return (const struct crypto_ec_point *)
+		EC_GROUP_get0_generator(e->group);
+}
+
+
+void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear)
+{
+	if (clear)
+		EC_POINT_clear_free((EC_POINT *) p);
+	else
+		EC_POINT_free((EC_POINT *) p);
+}
+
+
+int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
+		      struct crypto_bignum *x)
+{
+	return EC_POINT_get_affine_coordinates(e->group,
+					       (const EC_POINT *) p,
+					       (BIGNUM *) x, NULL,
+					       e->bnctx) == 1 ? 0 : -1;
+}
+
+
+int crypto_ec_point_to_bin(struct crypto_ec *e,
+			   const struct crypto_ec_point *point, u8 *x, u8 *y)
+{
+	BIGNUM *x_bn, *y_bn;
+	int ret = -1;
+	int len = BN_num_bytes(e->prime);
+
+	if (TEST_FAIL())
+		return -1;
+
+	x_bn = BN_new();
+	y_bn = BN_new();
+
+	if (x_bn && y_bn &&
+	    EC_POINT_get_affine_coordinates(e->group, (EC_POINT *) point,
+					    x_bn, y_bn, e->bnctx)) {
+		if (x) {
+			ret = crypto_bignum_to_bin(
+				(struct crypto_bignum *) x_bn, x, len, len);
+		}
+		if (ret >= 0 && y) {
+			ret = crypto_bignum_to_bin(
+				(struct crypto_bignum *) y_bn, y, len, len);
+		}
+
+		if (ret > 0)
+			ret = 0;
+	}
+
+	BN_clear_free(x_bn);
+	BN_clear_free(y_bn);
+	return ret;
+}
+
+
+struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
+						  const u8 *val)
+{
+	BIGNUM *x, *y;
+	EC_POINT *elem;
+	int len = BN_num_bytes(e->prime);
+
+	if (TEST_FAIL())
+		return NULL;
+
+	x = BN_bin2bn(val, len, NULL);
+	y = BN_bin2bn(val + len, len, NULL);
+	elem = EC_POINT_new(e->group);
+	if (x == NULL || y == NULL || elem == NULL) {
+		BN_clear_free(x);
+		BN_clear_free(y);
+		EC_POINT_clear_free(elem);
+		return NULL;
+	}
+
+	if (!EC_POINT_set_affine_coordinates(e->group, elem, x, y, e->bnctx)) {
+		EC_POINT_clear_free(elem);
+		elem = NULL;
+	}
+
+	BN_clear_free(x);
+	BN_clear_free(y);
+
+	return (struct crypto_ec_point *) elem;
+}
+
+
+int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
+			const struct crypto_ec_point *b,
+			struct crypto_ec_point *c)
+{
+	if (TEST_FAIL())
+		return -1;
+	return EC_POINT_add(e->group, (EC_POINT *) c, (const EC_POINT *) a,
+			    (const EC_POINT *) b, e->bnctx) ? 0 : -1;
+}
+
+
+int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
+			const struct crypto_bignum *b,
+			struct crypto_ec_point *res)
+{
+	if (TEST_FAIL())
+		return -1;
+	return EC_POINT_mul(e->group, (EC_POINT *) res, NULL,
+			    (const EC_POINT *) p, (const BIGNUM *) b, e->bnctx)
+		? 0 : -1;
+}
+
+
+int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
+{
+	if (TEST_FAIL())
+		return -1;
+	return EC_POINT_invert(e->group, (EC_POINT *) p, e->bnctx) ? 0 : -1;
+}
+
+
+struct crypto_bignum *
+crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
+			      const struct crypto_bignum *x)
+{
+	BIGNUM *tmp;
+
+	if (TEST_FAIL())
+		return NULL;
+
+	tmp = BN_new();
+
+	/* y^2 = x^3 + ax + b = (x^2 + a)x + b */
+	if (tmp &&
+	    BN_mod_sqr(tmp, (const BIGNUM *) x, e->prime, e->bnctx) &&
+	    BN_mod_add_quick(tmp, e->a, tmp, e->prime) &&
+	    BN_mod_mul(tmp, tmp, (const BIGNUM *) x, e->prime, e->bnctx) &&
+	    BN_mod_add_quick(tmp, tmp, e->b, e->prime))
+		return (struct crypto_bignum *) tmp;
+
+	BN_clear_free(tmp);
+	return NULL;
+}
+
+
+int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
+				   const struct crypto_ec_point *p)
+{
+	return EC_POINT_is_at_infinity(e->group, (const EC_POINT *) p);
+}
+
+
+int crypto_ec_point_is_on_curve(struct crypto_ec *e,
+				const struct crypto_ec_point *p)
+{
+	return EC_POINT_is_on_curve(e->group, (const EC_POINT *) p,
+				    e->bnctx) == 1;
+}
+
+
+int crypto_ec_point_cmp(const struct crypto_ec *e,
+			const struct crypto_ec_point *a,
+			const struct crypto_ec_point *b)
+{
+	return EC_POINT_cmp(e->group, (const EC_POINT *) a,
+			    (const EC_POINT *) b, e->bnctx);
+}
+
+
+void crypto_ec_point_debug_print(const struct crypto_ec *e,
+				 const struct crypto_ec_point *p,
+				 const char *title)
+{
+	BIGNUM *x, *y;
+	char *x_str = NULL, *y_str = NULL;
+
+	x = BN_new();
+	y = BN_new();
+	if (!x || !y ||
+	    EC_POINT_get_affine_coordinates(e->group, (const EC_POINT *) p,
+					    x, y, e->bnctx) != 1)
+		goto fail;
+
+	x_str = BN_bn2hex(x);
+	y_str = BN_bn2hex(y);
+	if (!x_str || !y_str)
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "%s (%s,%s)", title, x_str, y_str);
+
+fail:
+	OPENSSL_free(x_str);
+	OPENSSL_free(y_str);
+	BN_free(x);
+	BN_free(y);
+}
+
+
+struct crypto_ecdh {
+	struct crypto_ec *ec;
+	EVP_PKEY *pkey;
+};
+
+struct crypto_ecdh * crypto_ecdh_init(int group)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	struct crypto_ecdh *ecdh;
+	const char *name;
+
+	ecdh = os_zalloc(sizeof(*ecdh));
+	if (!ecdh)
+		goto fail;
+
+	ecdh->ec = crypto_ec_init(group);
+	if (!ecdh->ec)
+		goto fail;
+
+	name = OSSL_EC_curve_nid2name(ecdh->ec->nid);
+	if (!name)
+		goto fail;
+
+	ecdh->pkey = EVP_EC_gen(name);
+	if (!ecdh->pkey)
+		goto fail;
+
+done:
+	return ecdh;
+fail:
+	crypto_ecdh_deinit(ecdh);
+	ecdh = NULL;
+	goto done;
+#else /* OpenSSL version >= 3.0 */
+	struct crypto_ecdh *ecdh;
+	EVP_PKEY *params = NULL;
+	EC_KEY *ec_params = NULL;
+	EVP_PKEY_CTX *kctx = NULL;
+
+	ecdh = os_zalloc(sizeof(*ecdh));
+	if (!ecdh)
+		goto fail;
+
+	ecdh->ec = crypto_ec_init(group);
+	if (!ecdh->ec)
+		goto fail;
+
+	ec_params = EC_KEY_new_by_curve_name(ecdh->ec->nid);
+	if (!ec_params) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: Failed to generate EC_KEY parameters");
+		goto fail;
+	}
+	EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE);
+	params = EVP_PKEY_new();
+	if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: Failed to generate EVP_PKEY parameters");
+		goto fail;
+	}
+
+	kctx = EVP_PKEY_CTX_new(params, NULL);
+	if (!kctx)
+		goto fail;
+
+	if (EVP_PKEY_keygen_init(kctx) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: EVP_PKEY_keygen_init failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	if (EVP_PKEY_keygen(kctx, &ecdh->pkey) != 1) {
+		wpa_printf(MSG_ERROR, "OpenSSL: EVP_PKEY_keygen failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+done:
+	EC_KEY_free(ec_params);
+	EVP_PKEY_free(params);
+	EVP_PKEY_CTX_free(kctx);
+
+	return ecdh;
+fail:
+	crypto_ecdh_deinit(ecdh);
+	ecdh = NULL;
+	goto done;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+struct crypto_ecdh * crypto_ecdh_init2(int group, struct crypto_ec_key *own_key)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	struct crypto_ecdh *ecdh;
+
+	ecdh = os_zalloc(sizeof(*ecdh));
+	if (!ecdh)
+		goto fail;
+
+	ecdh->ec = crypto_ec_init(group);
+	if (!ecdh->ec)
+		goto fail;
+
+	ecdh->pkey = EVP_PKEY_dup((EVP_PKEY *) own_key);
+	if (!ecdh->pkey)
+		goto fail;
+
+	return ecdh;
+fail:
+	crypto_ecdh_deinit(ecdh);
+	return NULL;
+#else /* OpenSSL version >= 3.0 */
+	struct crypto_ecdh *ecdh;
+
+	ecdh = os_zalloc(sizeof(*ecdh));
+	if (!ecdh)
+		goto fail;
+
+	ecdh->ec = crypto_ec_init(group);
+	if (!ecdh->ec)
+		goto fail;
+
+	ecdh->pkey = EVP_PKEY_new();
+	if (!ecdh->pkey ||
+	    EVP_PKEY_assign_EC_KEY(ecdh->pkey,
+				   EVP_PKEY_get1_EC_KEY((EVP_PKEY *) own_key))
+	    != 1)
+		goto fail;
+
+	return ecdh;
+fail:
+	crypto_ecdh_deinit(ecdh);
+	return NULL;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	struct wpabuf *buf = NULL;
+	unsigned char *pub;
+	size_t len, exp_len;
+
+	len = EVP_PKEY_get1_encoded_public_key(ecdh->pkey, &pub);
+	if (len == 0)
+		return NULL;
+
+	/* Encoded using SECG SEC 1, Sec. 2.3.4 format */
+	exp_len = 1 + 2 * crypto_ec_prime_len(ecdh->ec);
+	if (len != exp_len) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL:%s: Unexpected encoded public key length %zu (expected %zu)",
+			   __func__, len, exp_len);
+		goto fail;
+	}
+	buf = wpabuf_alloc_copy(pub + 1, inc_y ? len - 1 : len / 2);
+fail:
+	OPENSSL_free(pub);
+	return buf;
+#else /* OpenSSL version >= 3.0 */
+	struct wpabuf *buf = NULL;
+	EC_KEY *eckey;
+	const EC_POINT *pubkey;
+	BIGNUM *x, *y = NULL;
+	int len = BN_num_bytes(ecdh->ec->prime);
+	int res;
+
+	eckey = EVP_PKEY_get1_EC_KEY(ecdh->pkey);
+	if (!eckey)
+		return NULL;
+
+	pubkey = EC_KEY_get0_public_key(eckey);
+	if (!pubkey)
+		return NULL;
+
+	x = BN_new();
+	if (inc_y) {
+		y = BN_new();
+		if (!y)
+			goto fail;
+	}
+	buf = wpabuf_alloc(inc_y ? 2 * len : len);
+	if (!x || !buf)
+		goto fail;
+
+	if (EC_POINT_get_affine_coordinates(ecdh->ec->group, pubkey,
+					    x, y, ecdh->ec->bnctx) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: EC_POINT_get_affine_coordinates failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	res = crypto_bignum_to_bin((struct crypto_bignum *) x,
+				   wpabuf_put(buf, len), len, len);
+	if (res < 0)
+		goto fail;
+
+	if (inc_y) {
+		res = crypto_bignum_to_bin((struct crypto_bignum *) y,
+					   wpabuf_put(buf, len), len, len);
+		if (res < 0)
+			goto fail;
+	}
+
+done:
+	BN_clear_free(x);
+	BN_clear_free(y);
+	EC_KEY_free(eckey);
+
+	return buf;
+fail:
+	wpabuf_free(buf);
+	buf = NULL;
+	goto done;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
+					const u8 *key, size_t len)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_PKEY *peerkey = EVP_PKEY_new();
+	EVP_PKEY_CTX *ctx;
+	size_t res_len;
+	struct wpabuf *res = NULL;
+	u8 *peer;
+
+	/* Encode using SECG SEC 1, Sec. 2.3.4 format */
+	peer = os_malloc(1 + len);
+	if (!peer) {
+		EVP_PKEY_free(peerkey);
+		return NULL;
+	}
+	peer[0] = inc_y ? 0x04 : 0x02;
+	os_memcpy(peer + 1, key, len);
+
+	if (!peerkey ||
+	    EVP_PKEY_copy_parameters(peerkey, ecdh->pkey) != 1 ||
+	    EVP_PKEY_set1_encoded_public_key(peerkey, peer, 1 + len) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL: EVP_PKEY_set1_encoded_public_key failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		EVP_PKEY_free(peerkey);
+		os_free(peer);
+		return NULL;
+	}
+	os_free(peer);
+
+	ctx = EVP_PKEY_CTX_new(ecdh->pkey, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, peerkey) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &res_len) != 1 ||
+	    !(res = wpabuf_alloc(res_len)) ||
+	    EVP_PKEY_derive(ctx, wpabuf_mhead(res), &res_len) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL: EVP_PKEY_derive failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		wpabuf_free(res);
+		res = NULL;
+	} else {
+		wpabuf_put(res, res_len);
+	}
+
+	EVP_PKEY_free(peerkey);
+	EVP_PKEY_CTX_free(ctx);
+	return res;
+#else /* OpenSSL version >= 3.0 */
+	BIGNUM *x, *y = NULL;
+	EVP_PKEY_CTX *ctx = NULL;
+	EVP_PKEY *peerkey = NULL;
+	struct wpabuf *secret = NULL;
+	size_t secret_len;
+	EC_POINT *pub;
+	EC_KEY *eckey = NULL;
+
+	x = BN_bin2bn(key, inc_y ? len / 2 : len, NULL);
+	pub = EC_POINT_new(ecdh->ec->group);
+	if (!x || !pub)
+		goto fail;
+
+	if (inc_y) {
+		y = BN_bin2bn(key + len / 2, len / 2, NULL);
+		if (!y)
+			goto fail;
+		if (!EC_POINT_set_affine_coordinates(ecdh->ec->group, pub,
+						     x, y, ecdh->ec->bnctx)) {
+			wpa_printf(MSG_ERROR,
+				   "OpenSSL: EC_POINT_set_affine_coordinates failed: %s",
+				   ERR_error_string(ERR_get_error(), NULL));
+			goto fail;
+		}
+	} else if (!EC_POINT_set_compressed_coordinates(ecdh->ec->group,
+							pub, x, 0,
+							ecdh->ec->bnctx)) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: EC_POINT_set_compressed_coordinates failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	if (!EC_POINT_is_on_curve(ecdh->ec->group, pub, ecdh->ec->bnctx)) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: ECDH peer public key is not on curve");
+		goto fail;
+	}
+
+	eckey = EC_KEY_new_by_curve_name(ecdh->ec->nid);
+	if (!eckey || EC_KEY_set_public_key(eckey, pub) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: EC_KEY_set_public_key failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	peerkey = EVP_PKEY_new();
+	if (!peerkey || EVP_PKEY_set1_EC_KEY(peerkey, eckey) != 1)
+		goto fail;
+
+	ctx = EVP_PKEY_CTX_new(ecdh->pkey, NULL);
+	if (!ctx || EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, peerkey) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &secret_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: EVP_PKEY_derive(1) failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	secret = wpabuf_alloc(secret_len);
+	if (!secret)
+		goto fail;
+	if (EVP_PKEY_derive(ctx, wpabuf_put(secret, 0), &secret_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: EVP_PKEY_derive(2) failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	if (secret->size != secret_len)
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: EVP_PKEY_derive(2) changed secret_len %d -> %d",
+			   (int) secret->size, (int) secret_len);
+	wpabuf_put(secret, secret_len);
+
+done:
+	BN_free(x);
+	BN_free(y);
+	EC_KEY_free(eckey);
+	EC_POINT_free(pub);
+	EVP_PKEY_CTX_free(ctx);
+	EVP_PKEY_free(peerkey);
+	return secret;
+fail:
+	wpabuf_free(secret);
+	secret = NULL;
+	goto done;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+void crypto_ecdh_deinit(struct crypto_ecdh *ecdh)
+{
+	if (ecdh) {
+		crypto_ec_deinit(ecdh->ec);
+		EVP_PKEY_free(ecdh->pkey);
+		os_free(ecdh);
+	}
+}
+
+
+size_t crypto_ecdh_prime_len(struct crypto_ecdh *ecdh)
+{
+	return crypto_ec_prime_len(ecdh->ec);
+}
+
+
+struct crypto_ec_key * crypto_ec_key_parse_priv(const u8 *der, size_t der_len)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_PKEY *pkey = NULL;
+	OSSL_DECODER_CTX *ctx;
+
+	ctx = OSSL_DECODER_CTX_new_for_pkey(
+		&pkey, "DER", NULL, "EC",
+		OSSL_KEYMGMT_SELECT_KEYPAIR |
+		OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
+		NULL, NULL);
+	if (!ctx ||
+	    OSSL_DECODER_from_data(ctx, &der, &der_len) != 1) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: Decoding EC private key (DER) failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		if (ctx)
+			OSSL_DECODER_CTX_free(ctx);
+		goto fail;
+	}
+
+	OSSL_DECODER_CTX_free(ctx);
+	return (struct crypto_ec_key *) pkey;
+fail:
+	crypto_ec_key_deinit((struct crypto_ec_key *) pkey);
+	return NULL;
+#else /* OpenSSL version >= 3.0 */
+	EVP_PKEY *pkey = NULL;
+	EC_KEY *eckey;
+
+	eckey = d2i_ECPrivateKey(NULL, &der, der_len);
+	if (!eckey) {
+		wpa_printf(MSG_INFO, "OpenSSL: d2i_ECPrivateKey() failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED);
+
+	pkey = EVP_PKEY_new();
+	if (!pkey || EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) {
+		EC_KEY_free(eckey);
+		goto fail;
+	}
+
+	return (struct crypto_ec_key *) pkey;
+fail:
+	crypto_ec_key_deinit((struct crypto_ec_key *) pkey);
+	return NULL;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+struct crypto_ec_key * crypto_ec_key_set_priv(int group,
+					      const u8 *raw, size_t raw_len)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	const char *group_name;
+	OSSL_PARAM params[4];
+	EVP_PKEY_CTX *ctx = NULL;
+	EVP_PKEY *pkey = NULL;
+	BIGNUM *priv;
+	EC_POINT *pub = NULL;
+	EC_GROUP *ec_group = NULL;
+	size_t len;
+	u8 *pub_bin = NULL;
+	u8 *priv_bin = NULL;
+	int priv_bin_len;
+
+	group_name = crypto_ec_group_2_name(group);
+	if (!group_name)
+		return NULL;
+
+	priv = BN_bin2bn(raw, raw_len, NULL);
+	if (!priv)
+		return NULL;
+	priv_bin = os_malloc(raw_len);
+	if (!priv_bin)
+		goto fail;
+	priv_bin_len = BN_bn2lebinpad(priv, priv_bin, raw_len);
+	if (priv_bin_len < 0)
+		goto fail;
+
+	ec_group = EC_GROUP_new_by_curve_name(crypto_ec_group_2_nid(group));
+	if (!ec_group)
+		goto fail;
+	pub = EC_POINT_new(ec_group);
+	if (!pub ||
+	    EC_POINT_mul(ec_group, pub, priv, NULL, NULL, NULL) != 1)
+		goto fail;
+	len = EC_POINT_point2oct(ec_group, pub, POINT_CONVERSION_UNCOMPRESSED,
+				 NULL, 0, NULL);
+	if (len == 0)
+		goto fail;
+	pub_bin = os_malloc(len);
+	if (!pub_bin)
+		goto fail;
+	len = EC_POINT_point2oct(ec_group, pub, POINT_CONVERSION_UNCOMPRESSED,
+				 pub_bin, len, NULL);
+	if (len == 0)
+		goto fail;
+
+	params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
+						     (char *) group_name, 0);
+	params[1] = OSSL_PARAM_construct_BN(OSSL_PKEY_PARAM_PRIV_KEY,
+					    priv_bin, priv_bin_len);
+	params[2] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY,
+						      pub_bin, len);
+	params[3] = OSSL_PARAM_construct_end();
+
+	ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
+	if (!ctx ||
+	    EVP_PKEY_fromdata_init(ctx) <= 0 ||
+	    EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_KEYPAIR, params) <= 0)
+		goto fail;
+
+out:
+	bin_clear_free(priv_bin, raw_len);
+	os_free(pub_bin);
+	BN_clear_free(priv);
+	EVP_PKEY_CTX_free(ctx);
+	EC_POINT_free(pub);
+	EC_GROUP_free(ec_group);
+	return (struct crypto_ec_key *) pkey;
+
+fail:
+	EVP_PKEY_free(pkey);
+	pkey = NULL;
+	goto out;
+#else /* OpenSSL version >= 3.0 */
+	EC_KEY *eckey = NULL;
+	EVP_PKEY *pkey = NULL;
+	BIGNUM *priv = NULL;
+	int nid;
+	const EC_GROUP *ec_group;
+	EC_POINT *pub = NULL;
+
+	nid = crypto_ec_group_2_nid(group);
+	if (nid < 0) {
+		wpa_printf(MSG_ERROR, "OpenSSL: Unsupported group %d", group);
+		return NULL;
+	}
+
+	eckey = EC_KEY_new_by_curve_name(nid);
+	priv = BN_bin2bn(raw, raw_len, NULL);
+	if (!eckey || !priv ||
+	    EC_KEY_set_private_key(eckey, priv) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: Failed to set EC_KEY: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	ec_group = EC_KEY_get0_group(eckey);
+	if (!ec_group)
+		goto fail;
+	pub = EC_POINT_new(ec_group);
+	if (!pub ||
+	    EC_POINT_mul(ec_group, pub, priv, NULL, NULL, NULL) != 1 ||
+	    EC_KEY_set_public_key(eckey, pub) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: Failed to set EC_KEY(pub): %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
+
+	pkey = EVP_PKEY_new();
+	if (!pkey || EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) {
+		wpa_printf(MSG_ERROR, "OpenSSL: Could not create EVP_PKEY");
+		goto fail;
+	}
+
+out:
+	BN_clear_free(priv);
+	EC_POINT_free(pub);
+	return (struct crypto_ec_key *) pkey;
+
+fail:
+	EC_KEY_free(eckey);
+	EVP_PKEY_free(pkey);
+	pkey = NULL;
+	goto out;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+struct crypto_ec_key * crypto_ec_key_parse_pub(const u8 *der, size_t der_len)
+{
+	EVP_PKEY *pkey;
+
+	pkey = d2i_PUBKEY(NULL, &der, der_len);
+	if (!pkey) {
+		wpa_printf(MSG_INFO, "OpenSSL: d2i_PUBKEY() failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	/* Ensure this is an EC key */
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	if (!EVP_PKEY_is_a(pkey, "EC"))
+		goto fail;
+#else /* OpenSSL version >= 3.0 */
+	if (!EVP_PKEY_get0_EC_KEY(pkey))
+		goto fail;
+#endif /* OpenSSL version >= 3.0 */
+	return (struct crypto_ec_key *) pkey;
+fail:
+	crypto_ec_key_deinit((struct crypto_ec_key *) pkey);
+	return NULL;
+}
+
+
+struct crypto_ec_key * crypto_ec_key_set_pub(int group, const u8 *buf_x,
+					     const u8 *buf_y, size_t len)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	const char *group_name;
+	OSSL_PARAM params[3];
+	u8 *pub;
+	EVP_PKEY_CTX *ctx;
+	EVP_PKEY *pkey = NULL;
+
+	group_name = crypto_ec_group_2_name(group);
+	if (!group_name)
+		return NULL;
+
+	pub = os_malloc(1 + len * 2);
+	if (!pub)
+		return NULL;
+	pub[0] = 0x04; /* uncompressed */
+	os_memcpy(pub + 1, buf_x, len);
+	os_memcpy(pub + 1 + len, buf_y, len);
+
+	params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
+						     (char *) group_name, 0);
+	params[1] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY,
+						      pub, 1 + len * 2);
+	params[2] = OSSL_PARAM_construct_end();
+
+	ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
+	if (!ctx) {
+		os_free(pub);
+		return NULL;
+	}
+	if (EVP_PKEY_fromdata_init(ctx) <= 0 ||
+	    EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) <= 0) {
+		os_free(pub);
+		EVP_PKEY_CTX_free(ctx);
+		return NULL;
+	}
+
+	os_free(pub);
+	EVP_PKEY_CTX_free(ctx);
+
+	return (struct crypto_ec_key *) pkey;
+#else /* OpenSSL version >= 3.0 */
+	EC_KEY *eckey = NULL;
+	EVP_PKEY *pkey = NULL;
+	EC_GROUP *ec_group = NULL;
+	BN_CTX *ctx;
+	EC_POINT *point = NULL;
+	BIGNUM *x = NULL, *y = NULL;
+	int nid;
+
+	if (!buf_x || !buf_y)
+		return NULL;
+
+	nid = crypto_ec_group_2_nid(group);
+	if (nid < 0) {
+		wpa_printf(MSG_ERROR, "OpenSSL: Unsupported group %d", group);
+		return NULL;
+	}
+
+	ctx = BN_CTX_new();
+	if (!ctx)
+		goto fail;
+
+	ec_group = EC_GROUP_new_by_curve_name(nid);
+	if (!ec_group)
+		goto fail;
+
+	x = BN_bin2bn(buf_x, len, NULL);
+	y = BN_bin2bn(buf_y, len, NULL);
+	point = EC_POINT_new(ec_group);
+	if (!x || !y || !point)
+		goto fail;
+
+	if (!EC_POINT_set_affine_coordinates(ec_group, point, x, y, ctx)) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: EC_POINT_set_affine_coordinates failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	if (!EC_POINT_is_on_curve(ec_group, point, ctx) ||
+	    EC_POINT_is_at_infinity(ec_group, point)) {
+		wpa_printf(MSG_ERROR, "OpenSSL: Invalid point");
+		goto fail;
+	}
+
+	eckey = EC_KEY_new();
+	if (!eckey ||
+	    EC_KEY_set_group(eckey, ec_group) != 1 ||
+	    EC_KEY_set_public_key(eckey, point) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: Failed to set EC_KEY: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
+
+	pkey = EVP_PKEY_new();
+	if (!pkey || EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) {
+		wpa_printf(MSG_ERROR, "OpenSSL: Could not create EVP_PKEY");
+		goto fail;
+	}
+
+out:
+	EC_GROUP_free(ec_group);
+	BN_free(x);
+	BN_free(y);
+	EC_POINT_free(point);
+	BN_CTX_free(ctx);
+	return (struct crypto_ec_key *) pkey;
+
+fail:
+	EC_KEY_free(eckey);
+	EVP_PKEY_free(pkey);
+	pkey = NULL;
+	goto out;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+struct crypto_ec_key *
+crypto_ec_key_set_pub_point(struct crypto_ec *ec,
+			    const struct crypto_ec_point *pub)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	int len = BN_num_bytes(ec->prime);
+	struct crypto_ec_key *key;
+	u8 *buf;
+
+	buf = os_malloc(2 * len);
+	if (!buf)
+		return NULL;
+	if (crypto_ec_point_to_bin(ec, pub, buf, buf + len) < 0) {
+		os_free(buf);
+		return NULL;
+	}
+
+	key = crypto_ec_key_set_pub(ec->iana_group, buf, buf + len, len);
+	os_free(buf);
+
+	return key;
+#else /* OpenSSL version >= 3.0 */
+	EC_KEY *eckey;
+	EVP_PKEY *pkey = NULL;
+
+	eckey = EC_KEY_new();
+	if (!eckey ||
+	    EC_KEY_set_group(eckey, ec->group) != 1 ||
+	    EC_KEY_set_public_key(eckey, (const EC_POINT *) pub) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: Failed to set EC_KEY: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
+
+	pkey = EVP_PKEY_new();
+	if (!pkey || EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) {
+		wpa_printf(MSG_ERROR, "OpenSSL: Could not create EVP_PKEY");
+		goto fail;
+	}
+
+out:
+	return (struct crypto_ec_key *) pkey;
+
+fail:
+	EVP_PKEY_free(pkey);
+	EC_KEY_free(eckey);
+	pkey = NULL;
+	goto out;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+struct crypto_ec_key * crypto_ec_key_gen(int group)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_PKEY_CTX *ctx;
+	OSSL_PARAM params[2];
+	const char *group_name;
+	EVP_PKEY *pkey = NULL;
+
+	group_name = crypto_ec_group_2_name(group);
+	if (!group_name)
+		return NULL;
+
+	params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
+						     (char *) group_name, 0);
+	params[1] = OSSL_PARAM_construct_end();
+
+	ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
+	if (!ctx ||
+	    EVP_PKEY_keygen_init(ctx) != 1 ||
+	    EVP_PKEY_CTX_set_params(ctx, params) != 1 ||
+	    EVP_PKEY_generate(ctx, &pkey) != 1) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: failed to generate EC keypair: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		pkey = NULL;
+	}
+
+	EVP_PKEY_CTX_free(ctx);
+
+	return (struct crypto_ec_key *) pkey;
+#else /* OpenSSL version >= 3.0 */
+	EVP_PKEY_CTX *kctx = NULL;
+	EC_KEY *ec_params = NULL, *eckey;
+	EVP_PKEY *params = NULL, *key = NULL;
+	int nid;
+
+	nid = crypto_ec_group_2_nid(group);
+	if (nid < 0) {
+		wpa_printf(MSG_ERROR, "OpenSSL: Unsupported group %d", group);
+		return NULL;
+	}
+
+	ec_params = EC_KEY_new_by_curve_name(nid);
+	if (!ec_params) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: Failed to generate EC_KEY parameters");
+		goto fail;
+	}
+	EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE);
+	params = EVP_PKEY_new();
+	if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: Failed to generate EVP_PKEY parameters");
+		goto fail;
+	}
+
+	kctx = EVP_PKEY_CTX_new(params, NULL);
+	if (!kctx ||
+	    EVP_PKEY_keygen_init(kctx) != 1 ||
+	    EVP_PKEY_keygen(kctx, &key) != 1) {
+		wpa_printf(MSG_ERROR, "OpenSSL: Failed to generate EC key");
+		key = NULL;
+		goto fail;
+	}
+
+	eckey = EVP_PKEY_get1_EC_KEY(key);
+	if (!eckey) {
+		key = NULL;
+		goto fail;
+	}
+	EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED);
+	EC_KEY_free(eckey);
+
+fail:
+	EC_KEY_free(ec_params);
+	EVP_PKEY_free(params);
+	EVP_PKEY_CTX_free(kctx);
+	return (struct crypto_ec_key *) key;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+void crypto_ec_key_deinit(struct crypto_ec_key *key)
+{
+	EVP_PKEY_free((EVP_PKEY *) key);
+}
+
+
+#ifdef OPENSSL_IS_BORINGSSL
+
+/* BoringSSL version of i2d_PUBKEY() always outputs public EC key using
+ * uncompressed form so define a custom function to export EC pubkey using
+ * the compressed format that is explicitly required for some protocols. */
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+
+typedef struct {
+	/* AlgorithmIdentifier ecPublicKey with optional parameters present
+	 * as an OID identifying the curve */
+	X509_ALGOR *alg;
+	/* Compressed format public key per ANSI X9.63 */
+	ASN1_BIT_STRING *pub_key;
+} EC_COMP_PUBKEY;
+
+ASN1_SEQUENCE(EC_COMP_PUBKEY) = {
+	ASN1_SIMPLE(EC_COMP_PUBKEY, alg, X509_ALGOR),
+	ASN1_SIMPLE(EC_COMP_PUBKEY, pub_key, ASN1_BIT_STRING)
+} ASN1_SEQUENCE_END(EC_COMP_PUBKEY);
+
+IMPLEMENT_ASN1_FUNCTIONS(EC_COMP_PUBKEY);
+
+#endif /* OPENSSL_IS_BORINGSSL */
+
+
+struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key)
+{
+	EVP_PKEY *pkey = (EVP_PKEY *) key;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	OSSL_ENCODER_CTX *ctx;
+	int selection;
+	unsigned char *pdata = NULL;
+	size_t pdata_len = 0;
+	EVP_PKEY *copy = NULL;
+	struct wpabuf *buf = NULL;
+
+	if (EVP_PKEY_get_ec_point_conv_form(pkey) !=
+	    POINT_CONVERSION_COMPRESSED) {
+		copy = EVP_PKEY_dup(pkey);
+		if (!copy)
+			return NULL;
+		if (EVP_PKEY_set_utf8_string_param(
+			    copy, OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT,
+			    OSSL_PKEY_EC_POINT_CONVERSION_FORMAT_COMPRESSED) !=
+		    1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set compressed format");
+			EVP_PKEY_free(copy);
+			return NULL;
+		}
+		pkey = copy;
+	}
+
+	selection = OSSL_KEYMGMT_SELECT_ALL_PARAMETERS |
+		OSSL_KEYMGMT_SELECT_PUBLIC_KEY;
+
+	ctx = OSSL_ENCODER_CTX_new_for_pkey(pkey, selection, "DER",
+					    "SubjectPublicKeyInfo",
+					    NULL);
+	if (!ctx || OSSL_ENCODER_to_data(ctx, &pdata, &pdata_len) != 1) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: Failed to encode SubjectPublicKeyInfo: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		pdata = NULL;
+	}
+	OSSL_ENCODER_CTX_free(ctx);
+	if (pdata) {
+		buf = wpabuf_alloc_copy(pdata, pdata_len);
+		OPENSSL_free(pdata);
+	}
+
+	EVP_PKEY_free(copy);
+
+	return buf;
+#else /* OpenSSL version >= 3.0 */
+#ifdef OPENSSL_IS_BORINGSSL
+	unsigned char *der = NULL;
+	int der_len;
+	const EC_KEY *eckey;
+	struct wpabuf *ret = NULL;
+	size_t len;
+	const EC_GROUP *group;
+	const EC_POINT *point;
+	BN_CTX *ctx;
+	EC_COMP_PUBKEY *pubkey = NULL;
+	int nid;
+
+	ctx = BN_CTX_new();
+	eckey = EVP_PKEY_get0_EC_KEY(pkey);
+	if (!ctx || !eckey)
+		goto fail;
+
+	group = EC_KEY_get0_group(eckey);
+	point = EC_KEY_get0_public_key(eckey);
+	if (!group || !point)
+		goto fail;
+	nid = EC_GROUP_get_curve_name(group);
+
+	pubkey = EC_COMP_PUBKEY_new();
+	if (!pubkey ||
+	    X509_ALGOR_set0(pubkey->alg, OBJ_nid2obj(EVP_PKEY_EC),
+			    V_ASN1_OBJECT, (void *) OBJ_nid2obj(nid)) != 1)
+		goto fail;
+
+	len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
+				 NULL, 0, ctx);
+	if (len == 0)
+		goto fail;
+
+	der = OPENSSL_malloc(len);
+	if (!der)
+		goto fail;
+	len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
+				 der, len, ctx);
+
+	OPENSSL_free(pubkey->pub_key->data);
+	pubkey->pub_key->data = der;
+	der = NULL;
+	pubkey->pub_key->length = len;
+	/* No unused bits */
+	pubkey->pub_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+	pubkey->pub_key->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+
+	der_len = i2d_EC_COMP_PUBKEY(pubkey, &der);
+	if (der_len <= 0) {
+		wpa_printf(MSG_ERROR,
+			   "BoringSSL: Failed to build DER encoded public key");
+		goto fail;
+	}
+
+	ret = wpabuf_alloc_copy(der, der_len);
+fail:
+	EC_COMP_PUBKEY_free(pubkey);
+	OPENSSL_free(der);
+	BN_CTX_free(ctx);
+	return ret;
+#else /* OPENSSL_IS_BORINGSSL */
+	unsigned char *der = NULL;
+	int der_len;
+	struct wpabuf *buf;
+	EC_KEY *eckey;
+
+	eckey = EVP_PKEY_get1_EC_KEY(pkey);
+	if (!eckey)
+		return NULL;
+
+	/* For now, all users expect COMPRESSED form */
+	EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED);
+
+	der_len = i2d_PUBKEY((EVP_PKEY *) key, &der);
+	EC_KEY_free(eckey);
+	if (der_len <= 0) {
+		wpa_printf(MSG_INFO, "OpenSSL: i2d_PUBKEY() failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		return NULL;
+	}
+
+	buf = wpabuf_alloc_copy(der, der_len);
+	OPENSSL_free(der);
+	return buf;
+#endif /* OPENSSL_IS_BORINGSSL */
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+struct wpabuf * crypto_ec_key_get_ecprivate_key(struct crypto_ec_key *key,
+						bool include_pub)
+{
+	EVP_PKEY *pkey = (EVP_PKEY *) key;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	OSSL_ENCODER_CTX *ctx;
+	int selection;
+	unsigned char *pdata = NULL;
+	size_t pdata_len = 0;
+	struct wpabuf *buf;
+	EVP_PKEY *copy = NULL;
+
+	selection = OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS |
+		OSSL_KEYMGMT_SELECT_PRIVATE_KEY;
+	if (include_pub) {
+		selection |= OSSL_KEYMGMT_SELECT_PUBLIC_KEY;
+	} else {
+		/* Not including OSSL_KEYMGMT_SELECT_PUBLIC_KEY does not seem
+		 * to really be sufficient, so clone the key and explicitly
+		 * mark it not to include the public key. */
+		copy = EVP_PKEY_dup(pkey);
+		if (!copy)
+			return NULL;
+		EVP_PKEY_set_int_param(copy, OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC,
+				       0);
+		pkey = copy;
+	}
+
+	ctx = OSSL_ENCODER_CTX_new_for_pkey(pkey, selection, "DER",
+					    "type-specific", NULL);
+	if (!ctx || OSSL_ENCODER_to_data(ctx, &pdata, &pdata_len) != 1) {
+		OSSL_ENCODER_CTX_free(ctx);
+		EVP_PKEY_free(copy);
+		return NULL;
+	}
+	OSSL_ENCODER_CTX_free(ctx);
+	buf = wpabuf_alloc_copy(pdata, pdata_len);
+	OPENSSL_free(pdata);
+	EVP_PKEY_free(copy);
+	return buf;
+#else /* OpenSSL version >= 3.0 */
+	EC_KEY *eckey;
+	unsigned char *der = NULL;
+	int der_len;
+	struct wpabuf *buf;
+	unsigned int key_flags;
+
+	eckey = EVP_PKEY_get1_EC_KEY(pkey);
+	if (!eckey)
+		return NULL;
+
+	key_flags = EC_KEY_get_enc_flags(eckey);
+	if (include_pub)
+		key_flags &= ~EC_PKEY_NO_PUBKEY;
+	else
+		key_flags |= EC_PKEY_NO_PUBKEY;
+	EC_KEY_set_enc_flags(eckey, key_flags);
+
+	EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED);
+
+	der_len = i2d_ECPrivateKey(eckey, &der);
+	EC_KEY_free(eckey);
+	if (der_len <= 0)
+		return NULL;
+	buf = wpabuf_alloc_copy(der, der_len);
+	OPENSSL_free(der);
+
+	return buf;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+struct wpabuf * crypto_ec_key_get_pubkey_point(struct crypto_ec_key *key,
+					       int prefix)
+{
+	EVP_PKEY *pkey = (EVP_PKEY *) key;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	struct wpabuf *buf;
+	unsigned char *pos;
+	size_t pub_len = OSSL_PARAM_UNMODIFIED;
+
+	buf = NULL;
+	if (!EVP_PKEY_is_a(pkey, "EC") ||
+	    EVP_PKEY_get_octet_string_param(pkey,
+					    OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
+					    NULL, 0, &pub_len) < 0 ||
+	    pub_len == OSSL_PARAM_UNMODIFIED ||
+	    !(buf = wpabuf_alloc(pub_len)) ||
+	    EVP_PKEY_get_octet_string_param(pkey,
+					    OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
+					    wpabuf_put(buf, pub_len),
+					    pub_len, NULL) != 1 ||
+	    wpabuf_head_u8(buf)[0] != 0x04) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: Failed to get encoded public key: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		wpabuf_free(buf);
+		return NULL;
+	}
+
+	if (!prefix) {
+		/* Remove 0x04 prefix if requested */
+		pos = wpabuf_mhead(buf);
+		os_memmove(pos, pos + 1, pub_len - 1);
+		buf->used--;
+	}
+
+	return buf;
+#else /* OpenSSL version >= 3.0 */
+	int len, res;
+	EC_KEY *eckey;
+	struct wpabuf *buf;
+	unsigned char *pos;
+
+	eckey = EVP_PKEY_get1_EC_KEY(pkey);
+	if (!eckey)
+		return NULL;
+	EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED);
+	len = i2o_ECPublicKey(eckey, NULL);
+	if (len <= 0) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: Failed to determine public key encoding length");
+		EC_KEY_free(eckey);
+		return NULL;
+	}
+
+	buf = wpabuf_alloc(len);
+	if (!buf) {
+		EC_KEY_free(eckey);
+		return NULL;
+	}
+
+	pos = wpabuf_put(buf, len);
+	res = i2o_ECPublicKey(eckey, &pos);
+	EC_KEY_free(eckey);
+	if (res != len) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: Failed to encode public key (res=%d/%d)",
+			   res, len);
+		wpabuf_free(buf);
+		return NULL;
+	}
+
+	if (!prefix) {
+		/* Remove 0x04 prefix if requested */
+		pos = wpabuf_mhead(buf);
+		os_memmove(pos, pos + 1, len - 1);
+		buf->used--;
+	}
+
+	return buf;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+struct crypto_ec_point *
+crypto_ec_key_get_public_key(struct crypto_ec_key *key)
+{
+	EVP_PKEY *pkey = (EVP_PKEY *) key;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	char group[64];
+	unsigned char pub[256];
+	size_t len;
+	EC_POINT *point = NULL;
+	EC_GROUP *grp;
+	int res = 0;
+	OSSL_PARAM params[2];
+
+	if (!EVP_PKEY_is_a(pkey, "EC") ||
+	    EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME,
+					   group, sizeof(group), &len) != 1 ||
+	    EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY,
+					    pub, sizeof(pub), &len) != 1)
+		return NULL;
+
+	params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
+						     group, 0);
+	params[1] = OSSL_PARAM_construct_end();
+	grp = EC_GROUP_new_from_params(params, NULL, NULL);
+	if (!grp)
+		goto fail;
+	point = EC_POINT_new(grp);
+	if (!point)
+		goto fail;
+	res = EC_POINT_oct2point(grp, point, pub, len, NULL);
+
+fail:
+	if (res != 1) {
+		EC_POINT_free(point);
+		point = NULL;
+	}
+
+	EC_GROUP_free(grp);
+
+	return (struct crypto_ec_point *) point;
+#else /* OpenSSL version >= 3.0 */
+	const EC_KEY *eckey;
+	const EC_POINT *point;
+	const EC_GROUP *group;
+
+	eckey = EVP_PKEY_get0_EC_KEY(pkey);
+	if (!eckey)
+		return NULL;
+	group = EC_KEY_get0_group(eckey);
+	if (!group)
+		return NULL;
+	point = EC_KEY_get0_public_key(eckey);
+	if (!point)
+		return NULL;
+	return (struct crypto_ec_point *) EC_POINT_dup(point, group);
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+struct crypto_bignum *
+crypto_ec_key_get_private_key(struct crypto_ec_key *key)
+{
+	EVP_PKEY *pkey = (EVP_PKEY *) key;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	BIGNUM *bn = NULL;
+
+	if (!EVP_PKEY_is_a(pkey, "EC") ||
+	    EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, &bn) != 1)
+		return NULL;
+	return (struct crypto_bignum *) bn;
+#else /* OpenSSL version >= 3.0 */
+	const EC_KEY *eckey;
+	const BIGNUM *bn;
+
+	eckey = EVP_PKEY_get0_EC_KEY(pkey);
+	if (!eckey)
+		return NULL;
+	bn = EC_KEY_get0_private_key(eckey);
+	if (!bn)
+		return NULL;
+	return (struct crypto_bignum *) BN_dup(bn);
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+struct wpabuf * crypto_ec_key_sign(struct crypto_ec_key *key, const u8 *data,
+				   size_t len)
+{
+	EVP_PKEY_CTX *pkctx;
+	struct wpabuf *sig_der;
+	size_t sig_len;
+
+	sig_len = EVP_PKEY_size((EVP_PKEY *) key);
+	sig_der = wpabuf_alloc(sig_len);
+	if (!sig_der)
+		return NULL;
+
+	pkctx = EVP_PKEY_CTX_new((EVP_PKEY *) key, NULL);
+	if (!pkctx ||
+	    EVP_PKEY_sign_init(pkctx) <= 0 ||
+	    EVP_PKEY_sign(pkctx, wpabuf_put(sig_der, 0), &sig_len,
+			  data, len) <= 0) {
+		wpabuf_free(sig_der);
+		sig_der = NULL;
+	} else {
+		wpabuf_put(sig_der, sig_len);
+	}
+
+	EVP_PKEY_CTX_free(pkctx);
+	return sig_der;
+}
+
+
+static int openssl_evp_pkey_ec_prime_len(struct crypto_ec_key *key)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	char gname[50];
+	int nid;
+	EC_GROUP *group;
+	BIGNUM *prime = NULL;
+	int prime_len = -1;
+
+	if (EVP_PKEY_get_group_name((EVP_PKEY *) key, gname, sizeof(gname),
+				    NULL) != 1)
+		return -1;
+	nid = OBJ_txt2nid(gname);
+	group = EC_GROUP_new_by_curve_name(nid);
+	prime = BN_new();
+	if (!group || !prime)
+		goto fail;
+	if (EC_GROUP_get_curve(group, prime, NULL, NULL, NULL) == 1)
+		prime_len = BN_num_bytes(prime);
+fail:
+	EC_GROUP_free(group);
+	BN_free(prime);
+	return prime_len;
+#else
+	const EC_GROUP *group;
+	const EC_KEY *eckey;
+	BIGNUM *prime = NULL;
+	int prime_len = -1;
+
+	eckey = EVP_PKEY_get0_EC_KEY((EVP_PKEY *) key);
+	if (!eckey)
+		goto fail;
+	group = EC_KEY_get0_group(eckey);
+	prime = BN_new();
+	if (!prime || !group ||
+	    !EC_GROUP_get_curve(group, prime, NULL, NULL, NULL))
+		goto fail;
+	prime_len = BN_num_bytes(prime);
+fail:
+	BN_free(prime);
+	return prime_len;
+#endif
+}
+
+
+struct wpabuf * crypto_ec_key_sign_r_s(struct crypto_ec_key *key,
+				       const u8 *data, size_t len)
+{
+	ECDSA_SIG *sig = NULL;
+	const BIGNUM *r, *s;
+	u8 *r_buf, *s_buf;
+	struct wpabuf *buf;
+	const unsigned char *p;
+	int prime_len;
+
+	prime_len = openssl_evp_pkey_ec_prime_len(key);
+	if (prime_len < 0)
+		return NULL;
+
+	buf = crypto_ec_key_sign(key, data, len);
+	if (!buf)
+		return NULL;
+
+	/* Extract (r,s) from Ecdsa-Sig-Value */
+
+	p = wpabuf_head(buf);
+	sig = d2i_ECDSA_SIG(NULL, &p, wpabuf_len(buf));
+	if (!sig)
+		goto fail;
+	ECDSA_SIG_get0(sig, &r, &s);
+
+	/* Re-use wpabuf returned by crypto_ec_key_sign() */
+	buf->used = 0;
+	r_buf = wpabuf_put(buf, prime_len);
+	s_buf = wpabuf_put(buf, prime_len);
+	if (crypto_bignum_to_bin((const struct crypto_bignum *) r, r_buf,
+				 prime_len, prime_len) < 0 ||
+	    crypto_bignum_to_bin((const struct crypto_bignum *) s, s_buf,
+				 prime_len, prime_len) < 0)
+		goto fail;
+
+out:
+	ECDSA_SIG_free(sig);
+	return buf;
+fail:
+	wpabuf_clear_free(buf);
+	buf = NULL;
+	goto out;
+}
+
+
+int crypto_ec_key_verify_signature(struct crypto_ec_key *key, const u8 *data,
+				   size_t len, const u8 *sig, size_t sig_len)
+{
+	EVP_PKEY_CTX *pkctx;
+	int ret;
+
+	pkctx = EVP_PKEY_CTX_new((EVP_PKEY *) key, NULL);
+	if (!pkctx || EVP_PKEY_verify_init(pkctx) <= 0) {
+		EVP_PKEY_CTX_free(pkctx);
+		return -1;
+	}
+
+	ret = EVP_PKEY_verify(pkctx, sig, sig_len, data, len);
+	EVP_PKEY_CTX_free(pkctx);
+	if (ret == 1)
+		return 1; /* signature ok */
+	if (ret == 0)
+		return 0; /* incorrect signature */
+	return -1;
+}
+
+
+int crypto_ec_key_verify_signature_r_s(struct crypto_ec_key *key,
+				       const u8 *data, size_t len,
+				       const u8 *r, size_t r_len,
+				       const u8 *s, size_t s_len)
+{
+	ECDSA_SIG *sig;
+	BIGNUM *r_bn, *s_bn;
+	unsigned char *der = NULL;
+	int der_len;
+	int ret = -1;
+
+	r_bn = BN_bin2bn(r, r_len, NULL);
+	s_bn = BN_bin2bn(s, s_len, NULL);
+	sig = ECDSA_SIG_new();
+	if (!r_bn || !s_bn || !sig || ECDSA_SIG_set0(sig, r_bn, s_bn) != 1)
+		goto fail;
+	r_bn = NULL;
+	s_bn = NULL;
+
+	der_len = i2d_ECDSA_SIG(sig, &der);
+	if (der_len <= 0) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Could not DER encode signature");
+		goto fail;
+	}
+
+	ret = crypto_ec_key_verify_signature(key, data, len, der, der_len);
+
+fail:
+	OPENSSL_free(der);
+	BN_free(r_bn);
+	BN_free(s_bn);
+	ECDSA_SIG_free(sig);
+	return ret;
+}
+
+
+int crypto_ec_key_group(struct crypto_ec_key *key)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	char gname[50];
+	int nid;
+
+	if (EVP_PKEY_get_group_name((EVP_PKEY *) key, gname, sizeof(gname),
+				    NULL) != 1)
+		return -1;
+	nid = OBJ_txt2nid(gname);
+#else
+	const EC_KEY *eckey;
+	const EC_GROUP *group;
+	int nid;
+
+	eckey = EVP_PKEY_get0_EC_KEY((EVP_PKEY *) key);
+	if (!eckey)
+		return -1;
+	group = EC_KEY_get0_group(eckey);
+	if (!group)
+		return -1;
+	nid = EC_GROUP_get_curve_name(group);
+#endif
+	switch (nid) {
+	case NID_X9_62_prime256v1:
+		return 19;
+	case NID_secp384r1:
+		return 20;
+	case NID_secp521r1:
+		return 21;
+#ifdef NID_brainpoolP256r1
+	case NID_brainpoolP256r1:
+		return 28;
+#endif /* NID_brainpoolP256r1 */
+#ifdef NID_brainpoolP384r1
+	case NID_brainpoolP384r1:
+		return 29;
+#endif /* NID_brainpoolP384r1 */
+#ifdef NID_brainpoolP512r1
+	case NID_brainpoolP512r1:
+		return 30;
+#endif /* NID_brainpoolP512r1 */
+	default:
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: Unsupported curve (nid=%d) in EC key",
+			   nid);
+		return -1;
+	}
+}
+
+
+int crypto_ec_key_cmp(struct crypto_ec_key *key1, struct crypto_ec_key *key2)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	if (EVP_PKEY_eq((EVP_PKEY *) key1, (EVP_PKEY *) key2) != 1)
+		return -1;
+#else
+	if (EVP_PKEY_cmp((EVP_PKEY *) key1, (EVP_PKEY *) key2) != 1)
+		return -1;
+#endif
+	return 0;
+}
+
+
+void crypto_ec_key_debug_print(const struct crypto_ec_key *key,
+			       const char *title)
+{
+	BIO *out;
+	size_t rlen;
+	char *txt;
+	int res;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return;
+
+	EVP_PKEY_print_private(out, (EVP_PKEY *) key, 0, NULL);
+	rlen = BIO_ctrl_pending(out);
+	txt = os_malloc(rlen + 1);
+	if (txt) {
+		res = BIO_read(out, txt, rlen);
+		if (res > 0) {
+			txt[res] = '\0';
+			wpa_printf(MSG_DEBUG, "%s: %s", title, txt);
+		}
+		os_free(txt);
+	}
+	BIO_free(out);
+}
+
+
+struct wpabuf * crypto_pkcs7_get_certificates(const struct wpabuf *pkcs7)
+{
+#ifdef OPENSSL_IS_BORINGSSL
+	CBS pkcs7_cbs;
+#else /* OPENSSL_IS_BORINGSSL */
+	PKCS7 *p7 = NULL;
+	const unsigned char *p = wpabuf_head(pkcs7);
+#endif /* OPENSSL_IS_BORINGSSL */
+	STACK_OF(X509) *certs;
+	int i, num;
+	BIO *out = NULL;
+	size_t rlen;
+	struct wpabuf *pem = NULL;
+	int res;
+
+#ifdef OPENSSL_IS_BORINGSSL
+	certs = sk_X509_new_null();
+	if (!certs)
+		goto fail;
+	CBS_init(&pkcs7_cbs, wpabuf_head(pkcs7), wpabuf_len(pkcs7));
+	if (!PKCS7_get_certificates(certs, &pkcs7_cbs)) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: Could not parse PKCS#7 object: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+#else /* OPENSSL_IS_BORINGSSL */
+	p7 = d2i_PKCS7(NULL, &p, wpabuf_len(pkcs7));
+	if (!p7) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: Could not parse PKCS#7 object: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	switch (OBJ_obj2nid(p7->type)) {
+	case NID_pkcs7_signed:
+		certs = p7->d.sign->cert;
+		break;
+	case NID_pkcs7_signedAndEnveloped:
+		certs = p7->d.signed_and_enveloped->cert;
+		break;
+	default:
+		certs = NULL;
+		break;
+	}
+#endif /* OPENSSL_IS_BORINGSSL */
+
+	if (!certs || ((num = sk_X509_num(certs)) == 0)) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: No certificates found in PKCS#7 object");
+		goto fail;
+	}
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		goto fail;
+
+	for (i = 0; i < num; i++) {
+		X509 *cert = sk_X509_value(certs, i);
+
+		PEM_write_bio_X509(out, cert);
+	}
+
+	rlen = BIO_ctrl_pending(out);
+	pem = wpabuf_alloc(rlen);
+	if (!pem)
+		goto fail;
+	res = BIO_read(out, wpabuf_put(pem, 0), rlen);
+	if (res <= 0) {
+		wpabuf_free(pem);
+		pem = NULL;
+		goto fail;
+	}
+	wpabuf_put(pem, res);
+
+fail:
+#ifdef OPENSSL_IS_BORINGSSL
+	if (certs)
+		sk_X509_pop_free(certs, X509_free);
+#else /* OPENSSL_IS_BORINGSSL */
+	PKCS7_free(p7);
+#endif /* OPENSSL_IS_BORINGSSL */
+	if (out)
+		BIO_free_all(out);
+
+	return pem;
+}
+
+
+struct crypto_csr * crypto_csr_init()
+{
+	return (struct crypto_csr *)X509_REQ_new();
+}
+
+
+struct crypto_csr * crypto_csr_verify(const struct wpabuf *req)
+{
+	X509_REQ *csr;
+	EVP_PKEY *pkey = NULL;
+	const u8 *der = wpabuf_head(req);
+
+	csr = d2i_X509_REQ(NULL, &der, wpabuf_len(req));
+	if (!csr)
+		return NULL;
+
+	pkey = X509_REQ_get_pubkey((X509_REQ *)csr);
+	if (!pkey)
+		goto fail;
+
+	if (X509_REQ_verify((X509_REQ *)csr, pkey) != 1)
+		goto fail;
+
+	return (struct crypto_csr *)csr;
+fail:
+	X509_REQ_free(csr);
+	return NULL;
+}
+
+
+void crypto_csr_deinit(struct crypto_csr *csr)
+{
+	X509_REQ_free((X509_REQ *)csr);
+}
+
+
+int crypto_csr_set_ec_public_key(struct crypto_csr *csr, struct crypto_ec_key *key)
+{
+	if (!X509_REQ_set_pubkey((X509_REQ *)csr, (EVP_PKEY *)key))
+		return -1;
+
+	return 0;
+}
+
+
+int crypto_csr_set_name(struct crypto_csr *csr, enum crypto_csr_name type,
+			const char *name)
+{
+	X509_NAME *n;
+	int nid;
+
+	switch (type) {
+	case CSR_NAME_CN:
+		nid = NID_commonName;
+		break;
+	case CSR_NAME_SN:
+		nid = NID_surname;
+		break;
+	case CSR_NAME_C:
+		nid = NID_countryName;
+		break;
+	case CSR_NAME_O:
+		nid = NID_organizationName;
+		break;
+	case CSR_NAME_OU:
+		nid = NID_organizationalUnitName;
+		break;
+	default:
+		return -1;
+	}
+
+	n = X509_REQ_get_subject_name((X509_REQ *) csr);
+	if (!n)
+		return -1;
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+	if (!X509_NAME_add_entry_by_NID(n, nid, MBSTRING_UTF8,
+					(unsigned char *) name,
+					os_strlen(name), -1, 0))
+		return -1;
+#else
+	if (!X509_NAME_add_entry_by_NID(n, nid, MBSTRING_UTF8,
+					(const unsigned char *) name,
+					os_strlen(name), -1, 0))
+		return -1;
+#endif
+
+	return 0;
+}
+
+
+int crypto_csr_set_attribute(struct crypto_csr *csr, enum crypto_csr_attr attr,
+			     int attr_type, const u8 *value, size_t len)
+{
+	int nid;
+
+	switch (attr) {
+	case CSR_ATTR_CHALLENGE_PASSWORD:
+		nid = NID_pkcs9_challengePassword;
+		break;
+	default:
+		return -1;
+	}
+
+	if (!X509_REQ_add1_attr_by_NID((X509_REQ *) csr, nid, attr_type, value,
+				       len))
+		return -1;
+
+	return 0;
+}
+
+
+const u8 * crypto_csr_get_attribute(struct crypto_csr *csr,
+				    enum crypto_csr_attr attr,
+				    size_t *len, int *type)
+{
+	X509_ATTRIBUTE *attrib;
+	ASN1_TYPE *attrib_type;
+	ASN1_STRING *data;
+	int loc;
+	int nid;
+
+	switch (attr) {
+	case CSR_ATTR_CHALLENGE_PASSWORD:
+		nid = NID_pkcs9_challengePassword;
+		break;
+	default:
+		return NULL;
+	}
+
+	loc = X509_REQ_get_attr_by_NID((X509_REQ *) csr, nid, -1);
+	if (loc < 0)
+		return NULL;
+
+	attrib = X509_REQ_get_attr((X509_REQ *) csr, loc);
+	if (!attrib)
+		return NULL;
+
+	attrib_type = X509_ATTRIBUTE_get0_type(attrib, 0);
+	if (!attrib_type)
+		return NULL;
+	*type = ASN1_TYPE_get(attrib_type);
+	data = X509_ATTRIBUTE_get0_data(attrib, 0, *type, NULL);
+	if (!data)
+		return NULL;
+	*len = ASN1_STRING_length(data);
+	return ASN1_STRING_get0_data(data);
+}
+
+
+struct wpabuf * crypto_csr_sign(struct crypto_csr *csr,
+				struct crypto_ec_key *key,
+				enum crypto_hash_alg algo)
+{
+	const EVP_MD *sign_md;
+	struct wpabuf *buf;
+	unsigned char *der = NULL;
+	int der_len;
+
+	switch (algo) {
+	case CRYPTO_HASH_ALG_SHA256:
+		sign_md = EVP_sha256();
+		break;
+	case CRYPTO_HASH_ALG_SHA384:
+		sign_md = EVP_sha384();
+		break;
+	case CRYPTO_HASH_ALG_SHA512:
+		sign_md = EVP_sha512();
+		break;
+	default:
+		return NULL;
+	}
+
+	if (!X509_REQ_sign((X509_REQ *) csr, (EVP_PKEY *) key, sign_md))
+		return NULL;
+
+	der_len = i2d_X509_REQ((X509_REQ *) csr, &der);
+	if (der_len < 0)
+		return NULL;
+
+	buf = wpabuf_alloc_copy(der, der_len);
+	OPENSSL_free(der);
+
+	return buf;
+}
+
+#endif /* CONFIG_ECC */
+
+
+static EVP_PKEY * crypto_rsa_key_read_public(FILE *f)
+{
+	EVP_PKEY *pkey;
+	X509 *x509;
+	const ASN1_TIME *not_before, *not_after;
+	int res_before, res_after;
+
+	pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
+	if (pkey)
+		return pkey;
+
+	rewind(f);
+	x509 = PEM_read_X509(f, NULL, NULL, NULL);
+	if (!x509)
+		return NULL;
+
+	not_before = X509_get0_notBefore(x509);
+	not_after = X509_get0_notAfter(x509);
+	if (!not_before || !not_after)
+		goto fail;
+	res_before = X509_cmp_current_time(not_before);
+	res_after = X509_cmp_current_time(not_after);
+	if (!res_before || !res_after)
+		goto fail;
+	if (res_before > 0 || res_after < 0) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: Certificate for RSA public key is not valid at this time (%d %d)",
+			   res_before, res_after);
+		goto fail;
+	}
+
+	pkey = X509_get_pubkey(x509);
+	X509_free(x509);
+
+	if (!pkey)
+		return NULL;
+	if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA) {
+		wpa_printf(MSG_INFO, "OpenSSL: No RSA public key found");
+		EVP_PKEY_free(pkey);
+		return NULL;
+	}
+
+	return pkey;
+fail:
+	X509_free(x509);
+	return NULL;
+}
+
+
+struct crypto_rsa_key * crypto_rsa_key_read(const char *file, bool private_key)
+{
+	FILE *f;
+	EVP_PKEY *pkey;
+
+	f = fopen(file, "r");
+	if (!f)
+		return NULL;
+	if (private_key)
+		pkey = PEM_read_PrivateKey(f, NULL, NULL, NULL);
+	else
+		pkey = crypto_rsa_key_read_public(f);
+	fclose(f);
+	return (struct crypto_rsa_key *) pkey;
+}
+
+
+#ifndef OPENSSL_NO_SHA256
+
+struct wpabuf * crypto_rsa_oaep_sha256_encrypt(struct crypto_rsa_key *key,
+					       const struct wpabuf *in)
+{
+#if !defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x30400000L
+	EVP_PKEY *pkey = (EVP_PKEY *) key;
+	EVP_PKEY_CTX *pkctx;
+	struct wpabuf *res = NULL;
+	size_t outlen;
+
+	pkctx = EVP_PKEY_CTX_new(pkey, NULL);
+	if (!pkctx)
+		goto fail;
+
+	if (EVP_PKEY_encrypt_init(pkctx) != 1 ||
+	    EVP_PKEY_CTX_set_rsa_padding(pkctx, RSA_PKCS1_OAEP_PADDING) <= 0 ||
+	    EVP_PKEY_CTX_set_rsa_oaep_md(pkctx, EVP_sha256()) <= 0 ||
+	    EVP_PKEY_encrypt(pkctx, NULL, &outlen, wpabuf_head(in),
+			     wpabuf_len(in)) != 1 ||
+	    !(res = wpabuf_alloc(outlen)) ||
+	    EVP_PKEY_encrypt(pkctx, wpabuf_put(res, 0), &outlen,
+			     wpabuf_head(in), wpabuf_len(in)) != 1) {
+		wpabuf_free(res);
+		res = NULL;
+		goto fail;
+	}
+	wpabuf_put(res, outlen);
+
+fail:
+	EVP_PKEY_CTX_free(pkctx);
+	return res;
+#else
+	wpa_printf(MSG_ERROR, "%s() not supported", __func__);
+	return NULL;
+#endif
+}
+
+
+struct wpabuf * crypto_rsa_oaep_sha256_decrypt(struct crypto_rsa_key *key,
+					       const struct wpabuf *in)
+{
+#if !defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x30400000L
+	EVP_PKEY *pkey = (EVP_PKEY *) key;
+	EVP_PKEY_CTX *pkctx;
+	struct wpabuf *res = NULL;
+	size_t outlen;
+
+	pkctx = EVP_PKEY_CTX_new(pkey, NULL);
+	if (!pkctx)
+		goto fail;
+
+	if (EVP_PKEY_decrypt_init(pkctx) != 1 ||
+	    EVP_PKEY_CTX_set_rsa_padding(pkctx, RSA_PKCS1_OAEP_PADDING) <= 0 ||
+	    EVP_PKEY_CTX_set_rsa_oaep_md(pkctx, EVP_sha256()) <= 0 ||
+	    EVP_PKEY_decrypt(pkctx, NULL, &outlen, wpabuf_head(in),
+			     wpabuf_len(in)) != 1 ||
+	    !(res = wpabuf_alloc(outlen)) ||
+	    EVP_PKEY_decrypt(pkctx, wpabuf_put(res, 0), &outlen,
+			     wpabuf_head(in), wpabuf_len(in)) != 1) {
+		wpabuf_free(res);
+		res = NULL;
+		goto fail;
+	}
+	wpabuf_put(res, outlen);
+
+fail:
+	EVP_PKEY_CTX_free(pkctx);
+	return res;
+#else
+	wpa_printf(MSG_ERROR, "%s() not supported", __func__);
+	return NULL;
+#endif
+}
+
+#endif /* OPENSSL_NO_SHA256 */
+
+
+void crypto_rsa_key_free(struct crypto_rsa_key *key)
+{
+	EVP_PKEY_free((EVP_PKEY *) key);
+}
+
+
+#ifdef CONFIG_DPP3
+
+#define HPKE_MAX_SHARED_SECRET_LEN 66
+#define HPKE_MAX_HASH_LEN 64
+#define HPKE_MAX_KEY_LEN 32
+#define HPKE_MAX_NONCE_LEN 12
+#define HPKE_MAX_PUB_LEN (1 + 2 * 66)
+
+struct hpke_context {
+	/* KEM */
+	enum hpke_kem_id kem_id;
+	int kem_nid;
+	int iana_group;
+	size_t n_pk;
+	size_t n_secret;
+	const EVP_MD *kem_h;
+	size_t kem_n_h;
+
+	/* KDF */
+	enum hpke_kdf_id kdf_id;
+	const EVP_MD *kdf_h;
+	size_t n_h;
+
+	/* AEAD */
+	enum hpke_aead_id aead_id;
+	const EVP_CIPHER *cipher;
+	size_t n_k;
+	size_t n_n;
+	size_t n_t;
+	u8 key[HPKE_MAX_KEY_LEN];
+	u8 base_nonce[HPKE_MAX_NONCE_LEN];
+};
+
+
+static void hpke_free_context(struct hpke_context *ctx)
+{
+	bin_clear_free(ctx, sizeof(*ctx));
+}
+
+
+static struct hpke_context * hpke_get_context(enum hpke_kem_id kem_id,
+					      enum hpke_kdf_id kdf_id,
+					      enum hpke_aead_id aead_id,
+					      struct crypto_ec_key *key)
+{
+	struct hpke_context *ctx;
+	int group;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (!ctx)
+		return NULL;
+
+	ctx->kem_id = kem_id;
+	switch (kem_id) {
+	case HPKE_DHKEM_P256_HKDF_SHA256:
+		ctx->kem_nid = NID_X9_62_prime256v1;
+		ctx->iana_group = 19;
+		ctx->n_pk = 65;
+		ctx->n_secret = 32;
+		ctx->kem_h = EVP_sha256();
+		ctx->kem_n_h = 32;
+		break;
+	case HPKE_DHKEM_P384_HKDF_SHA384:
+		ctx->kem_nid = NID_secp384r1;
+		ctx->iana_group = 20;
+		ctx->n_pk = 97;
+		ctx->n_secret = 48;
+		ctx->kem_h = EVP_sha384();
+		ctx->kem_n_h = 48;
+		break;
+	case HPKE_DHKEM_P521_HKDF_SHA512:
+		ctx->kem_nid = NID_secp521r1;
+		ctx->iana_group = 21;
+		ctx->n_pk = 133;
+		ctx->n_secret = 64;
+		ctx->kem_h = EVP_sha512();
+		ctx->kem_n_h = 64;
+		break;
+	default:
+		goto fail;
+	}
+
+	ctx->kdf_id = kdf_id;
+	switch (kdf_id) {
+	case HPKE_KDF_HKDF_SHA256:
+		ctx->kdf_h = EVP_sha256();
+		ctx->n_h = 32;
+		break;
+	case HPKE_KDF_HKDF_SHA384:
+		ctx->kdf_h = EVP_sha384();
+		ctx->n_h = 48;
+		break;
+	case HPKE_KDF_HKDF_SHA512:
+		ctx->kdf_h = EVP_sha512();
+		ctx->n_h = 64;
+		break;
+	default:
+		goto fail;
+	}
+
+	ctx->aead_id = aead_id;
+	switch (aead_id) {
+	case HPKE_AEAD_AES_128_GCM:
+		ctx->cipher = EVP_aes_128_gcm();
+		ctx->n_k = 16;
+		ctx->n_n = 12;
+		ctx->n_t = 16;
+		break;
+	case HPKE_AEAD_AES_256_GCM:
+		ctx->cipher = EVP_aes_256_gcm();
+		ctx->n_k = 32;
+		ctx->n_n = 12;
+		ctx->n_t = 16;
+		break;
+	default:
+		goto fail;
+	}
+
+	/* Convert BP-256/384/512 to P-256/384/521 for DPP */
+	group = crypto_ec_key_group(key);
+	if (group == 28 && ctx->iana_group == 19) {
+		ctx->iana_group = 28;
+	} else if (group == 29 && ctx->iana_group == 20) {
+		ctx->iana_group = 29;
+	} else if (group == 30 && ctx->iana_group == 21) {
+		ctx->iana_group = 30;
+		ctx->n_pk = 129;
+	}
+	if (group != ctx->iana_group) {
+		wpa_printf(MSG_INFO, "OpenSSL:%s:group mismatch (%d != %d)",
+			   __func__, group, ctx->iana_group);
+		goto fail;
+	}
+
+	return ctx;
+fail:
+	hpke_free_context(ctx);
+	return NULL;
+}
+
+
+static size_t hpke_suite_id(struct hpke_context *ctx, bool kem, u8 *suite_id)
+{
+	size_t suite_id_len;
+
+	if (kem) {
+		os_memcpy(suite_id, "KEM", 3);
+		WPA_PUT_BE16(&suite_id[3], ctx->kem_id);
+		suite_id_len = 5;
+	} else {
+		os_memcpy(suite_id, "HPKE", 4);
+		WPA_PUT_BE16(&suite_id[4], ctx->kem_id);
+		WPA_PUT_BE16(&suite_id[6], ctx->kdf_id);
+		WPA_PUT_BE16(&suite_id[8], ctx->aead_id);
+		suite_id_len = 10;
+	}
+	return suite_id_len;
+}
+
+
+static int hpke_labeled_extract(struct hpke_context *ctx, bool kem,
+				const u8 *salt, size_t salt_len,
+				const char *label,
+				const u8 *ikm, size_t ikm_len, u8 *prk)
+{
+	u8 zero[HPKE_MAX_HASH_LEN];
+	u8 suite_id[10];
+	size_t suite_id_len;
+	unsigned int mdlen = kem ? ctx->kem_n_h : ctx->n_h;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_MAC *hmac;
+	OSSL_PARAM params[2];
+	EVP_MAC_CTX *hctx;
+	size_t mlen;
+	int res;
+#else /* OpenSSL version >= 3.0 */
+	HMAC_CTX *hctx;
+	int res;
+#endif /* OpenSSL version >= 3.0 */
+
+	if (!salt || !salt_len) {
+		salt_len = mdlen;
+		os_memset(zero, 0, salt_len);
+		salt = zero;
+	}
+
+	suite_id_len = hpke_suite_id(ctx, kem, suite_id);
+
+	/* labeled_ikm = concat("HPKE-v1", suite_id, label, ikm)
+	 * return Extract(salt, labeled_ikm) */
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	hmac = EVP_MAC_fetch(NULL, "HMAC", NULL);
+	if (!hmac)
+		return -1;
+
+	params[0] = OSSL_PARAM_construct_utf8_string(
+		"digest",
+		(char *) EVP_MD_get0_name(kem ? ctx->kem_h : ctx->kdf_h), 0);
+	params[1] = OSSL_PARAM_construct_end();
+
+	hctx = EVP_MAC_CTX_new(hmac);
+	EVP_MAC_free(hmac);
+	if (!hctx)
+		return -1;
+
+	if (EVP_MAC_init(hctx, salt, salt_len, params) != 1)
+		goto fail;
+
+	if (EVP_MAC_update(hctx, (const unsigned char *) "HPKE-v1", 7) != 1 ||
+	    EVP_MAC_update(hctx, suite_id, suite_id_len) != 1 ||
+	    EVP_MAC_update(hctx, (const unsigned char *) label,
+			   os_strlen(label)) != 1 ||
+	    EVP_MAC_update(hctx, ikm, ikm_len) != 1)
+		goto fail;
+
+	res = EVP_MAC_final(hctx, prk, &mlen, mdlen);
+	EVP_MAC_CTX_free(hctx);
+
+	return res == 1 ? 0 : -1;
+fail:
+	EVP_MAC_CTX_free(hctx);
+	return -1;
+#else /* OpenSSL version >= 3.0 */
+	hctx = HMAC_CTX_new();
+	if (!hctx)
+		return -1;
+	res = HMAC_Init_ex(hctx, salt, salt_len, kem ? ctx->kem_h : ctx->kdf_h,
+			   NULL);
+	if (res != 1)
+		goto done;
+
+	HMAC_Update(hctx, (const unsigned char *) "HPKE-v1", 7);
+	HMAC_Update(hctx, suite_id, suite_id_len);
+	HMAC_Update(hctx, (const unsigned char *) label, os_strlen(label));
+	HMAC_Update(hctx, ikm, ikm_len);
+
+	res = HMAC_Final(hctx, prk, &mdlen);
+done:
+	HMAC_CTX_free(hctx);
+
+	return res == 1 ? 0 : -1;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+static int
+hpke_labeled_expand(struct hpke_context *ctx, bool kem, const u8 *prk,
+		    const char *label, const u8 *info, size_t info_len,
+		    u8 *out, size_t out_len)
+{
+	u8 suite_id[10];
+	size_t suite_id_len;
+	u8 hash[HPKE_MAX_HASH_LEN];
+	u8 iter = 0;
+	size_t label_len = os_strlen(label);
+	u8 *pos;
+	size_t left = out_len, clen;
+	int res = -1;
+	u8 *labeled_info;
+	size_t labeled_info_len;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_MAC *hmac;
+	OSSL_PARAM params[2];
+	EVP_MAC_CTX *hctx = NULL;
+	size_t mdlen;
+#else /* OpenSSL version >= 3.0 */
+	HMAC_CTX *hctx;
+	unsigned int mdlen;
+#endif /* OpenSSL version >= 3.0 */
+
+	/* labeled_info = concat(I2OSP(L, 2), "HPKE-v1", suite_id,
+	 *                       label, info)
+	 * return Expand(prk, labeled_info, L) */
+	suite_id_len = hpke_suite_id(ctx, kem, suite_id);
+	labeled_info_len = 2 + 7 + suite_id_len + label_len + info_len;
+	labeled_info = os_malloc(labeled_info_len);
+	if (!labeled_info)
+		return -1;
+	pos = labeled_info;
+	WPA_PUT_BE16(pos, out_len);
+	pos += 2;
+	os_memcpy(pos, "HPKE-v1", 7);
+	pos += 7;
+	os_memcpy(pos, suite_id, suite_id_len);
+	pos += suite_id_len;
+	os_memcpy(pos, label, label_len);
+	pos += label_len;
+	if (info && info_len)
+		os_memcpy(pos, info, info_len);
+
+	pos = out;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	hmac = EVP_MAC_fetch(NULL, "HMAC", NULL);
+	if (!hmac)
+		goto fail;
+
+	params[0] = OSSL_PARAM_construct_utf8_string(
+		"digest",
+		(char *) EVP_MD_get0_name(kem ? ctx->kem_h : ctx->kdf_h), 0);
+	params[1] = OSSL_PARAM_construct_end();
+#else /* OpenSSL version >= 3.0 */
+	hctx = HMAC_CTX_new();
+	if (!hctx)
+		goto fail;
+#endif /* OpenSSL version >= 3.0 */
+
+	while (left > 0) {
+		mdlen = kem ? ctx->kem_n_h : ctx->n_h;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+		EVP_MAC_CTX_free(hctx);
+		hctx = EVP_MAC_CTX_new(hmac);
+		if (!hctx)
+			goto fail;
+
+		if (EVP_MAC_init(hctx, prk, mdlen, params) != 1)
+			goto fail;
+
+		if (iter > 0 && EVP_MAC_update(hctx, hash, mdlen) != 1)
+			goto fail;
+		if (iter == 255)
+			goto fail;
+		iter++;
+
+		if (EVP_MAC_update(hctx, labeled_info, labeled_info_len) != 1 ||
+		    EVP_MAC_update(hctx, &iter, sizeof(iter)) != 1)
+			goto fail;
+
+		if (EVP_MAC_final(hctx, hash, &mdlen, mdlen) != 1)
+			goto fail;
+#else /* OpenSSL version >= 3.0 */
+		if (HMAC_Init_ex(hctx, prk, mdlen,
+				 kem ? ctx->kem_h : ctx->kdf_h,
+				 NULL) != 1)
+			goto fail;
+
+		if (iter > 0)
+			HMAC_Update(hctx, hash, mdlen);
+		if (iter == 255)
+			goto fail;
+		iter++;
+		HMAC_Update(hctx, labeled_info, labeled_info_len);
+		HMAC_Update(hctx, &iter, sizeof(iter));
+
+		if (HMAC_Final(hctx, hash, &mdlen) != 1)
+			goto fail;
+		HMAC_CTX_reset(hctx);
+#endif /* OpenSSL version >= 3.0 */
+
+		clen = left > mdlen ? mdlen : left;
+		os_memcpy(pos, hash, clen);
+		pos += clen;
+		left -= clen;
+	}
+	res = 0;
+fail:
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_MAC_free(hmac);
+	EVP_MAC_CTX_free(hctx);
+#else /* OpenSSL version >= 3.0 */
+	HMAC_CTX_free(hctx);
+#endif /* OpenSSL version >= 3.0 */
+	os_free(labeled_info);
+
+	return res;
+}
+
+
+static int hpke_extract_and_expand(struct hpke_context *ctx,
+				   const u8 *dhss, size_t dhss_len,
+				   const u8 *enc, size_t enc_len,
+				   const u8 *pk_rm, size_t pk_rm_len,
+				   u8 *shared_secret)
+{
+	u8 kem_context[2 * HPKE_MAX_PUB_LEN];
+	u8 eae_prk[HPKE_MAX_HASH_LEN];
+
+	/* eae_prk = LabeledExtract("", "eae_prk", dh) */
+	if (hpke_labeled_extract(ctx, true, NULL, 0, "eae_prk", dhss, dhss_len,
+				 eae_prk) < 0)
+		return -1;
+
+	if (enc_len > HPKE_MAX_PUB_LEN || pk_rm_len > HPKE_MAX_PUB_LEN)
+		return -1;
+	/* kem_context = concat(enc, pkRm) */
+	os_memcpy(kem_context, enc, enc_len);
+	os_memcpy(&kem_context[enc_len], pk_rm, pk_rm_len);
+
+	/* shared_secret = LabeledExpand(eae_prk, "shared_secret",
+	 *                               kem_context, Nsecret) */
+	if (hpke_labeled_expand(ctx, true, eae_prk, "shared_secret",
+				kem_context, enc_len + pk_rm_len,
+				shared_secret, ctx->n_secret) < 0)
+		return -1;
+
+	forced_memzero(eae_prk, sizeof(eae_prk));
+	return 0;
+}
+
+
+static int hpke_key_schedule(struct hpke_context *ctx, const u8 *shared_secret,
+			     const u8 *info, size_t info_len)
+{
+	u8 key_schedule_context[1 + 2 * HPKE_MAX_HASH_LEN];
+	u8 secret[HPKE_MAX_HASH_LEN];
+	int res = -1;
+
+	/* key_schedule_context = concat(mode, psk_id_hash, info_hash) */
+	key_schedule_context[0] = HPKE_MODE_BASE;
+
+	/* psk_id_hash = LabeledExtract("", "psk_id_hash", psk_id) */
+	if (hpke_labeled_extract(ctx, false, NULL, 0, "psk_id_hash",
+				 NULL, 0, &key_schedule_context[1]) < 0)
+		goto fail;
+
+	/* info_hash = LabeledExtract("", "info_hash", info) */
+	if (hpke_labeled_extract(ctx, false, NULL, 0, "info_hash",
+				 info, info_len,
+				 &key_schedule_context[1 + ctx->n_h]) < 0)
+		goto fail;
+
+	/* secret = LabeledExtract(shared_secret, "secret", psk) */
+	if (hpke_labeled_extract(ctx, false, shared_secret, ctx->n_secret,
+				 "secret", NULL, 0, secret) < 0)
+		goto fail;
+
+	/* key = LabeledExpand(secret, "key", key_schedule_context, Nk) */
+	if (hpke_labeled_expand(ctx, false, secret, "key",
+				key_schedule_context, 1 + 2 * ctx->n_h,
+				ctx->key, ctx->n_k) < 0)
+		goto fail;
+
+	/* base_nonce = LabeledExpand(secret, "base_nonce",
+	 *                            key_schedule_context, Nn) */
+	if (hpke_labeled_expand(ctx, false, secret, "base_nonce",
+				key_schedule_context, 1 + 2 * ctx->n_h,
+				ctx->base_nonce, ctx->n_n) < 0)
+		goto fail;
+	res = 0;
+fail:
+	forced_memzero(key_schedule_context, sizeof(key_schedule_context));
+	forced_memzero(secret, sizeof(secret));
+	return res;
+}
+
+
+static int hpke_encap(struct hpke_context *ctx, struct crypto_ec_key *pk_r,
+		      u8 *shared_secret, u8 *enc)
+{
+	EVP_PKEY_CTX *pctx = NULL;
+	struct crypto_ec_key *sk_e;
+	int res = -1;
+	u8 *dhss = NULL;
+	size_t dhss_len = 0;
+	struct wpabuf *enc_buf = NULL, *pk_rm = NULL;
+
+	/* skE, pkE = GenerateKeyPair() */
+	sk_e = crypto_ec_key_gen(ctx->iana_group);
+	if (!sk_e) {
+		wpa_printf(MSG_INFO, "OpenSSL:%s:Could not generate key pair",
+			   __func__);
+		goto fail;
+	}
+
+	/* dh = DH(skE, pkR) */
+	dhss_len = sizeof(dhss);
+	pctx = EVP_PKEY_CTX_new((EVP_PKEY *) sk_e, NULL);
+	if (!pctx ||
+	    EVP_PKEY_derive_init(pctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(pctx, (EVP_PKEY *) pk_r) != 1 ||
+	    EVP_PKEY_derive(pctx, NULL, &dhss_len) != 1 ||
+	    !(dhss = os_malloc(dhss_len)) ||
+	    EVP_PKEY_derive(pctx, dhss, &dhss_len) != 1 ||
+	    dhss_len > HPKE_MAX_SHARED_SECRET_LEN) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: hpke_encap: EVP_PKEY_derive failed (dhss_len=%zu): %s",
+			   dhss_len, ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	/* enc = SerializePublicKey(pkE) */
+	enc_buf = crypto_ec_key_get_pubkey_point(sk_e, 1);
+	if (!enc_buf)
+		goto fail;
+	os_memcpy(enc, wpabuf_head(enc_buf), wpabuf_len(enc_buf));
+
+	/* pkRm = SerializePublicKey(pkR) */
+	pk_rm = crypto_ec_key_get_pubkey_point(pk_r, 1);
+	if (!pk_rm)
+		goto fail;
+
+	/* kem_context = concat(enc, pkRm) */
+	/* shared_secret = ExtractAndExpand(dh, kem_context) */
+	/* return shared_secret, enc */
+	res = hpke_extract_and_expand(ctx, dhss, dhss_len, enc, ctx->n_pk,
+				      wpabuf_head(pk_rm),
+				      wpabuf_len(pk_rm), shared_secret);
+fail:
+	bin_clear_free(dhss, dhss_len);
+	crypto_ec_key_deinit(sk_e);
+	EVP_PKEY_CTX_free(pctx);
+	wpabuf_free(enc_buf);
+	wpabuf_free(pk_rm);
+	return res;
+}
+
+
+static struct wpabuf *
+hpke_aead_seal(struct hpke_context *ctx, const u8 *aad, size_t aad_len,
+	       const u8 *pt, size_t pt_len)
+{
+	EVP_CIPHER_CTX *cctx;
+	int len = 0;
+	struct wpabuf *ct = NULL;
+
+	/* No need to xor in sequence number since we support only the
+	 * single-shot API, i.e., base_nonce can be used as-is. */
+
+	cctx = EVP_CIPHER_CTX_new();
+	if (!cctx ||
+	    EVP_EncryptInit_ex(cctx, ctx->cipher, NULL, ctx->key,
+			       ctx->base_nonce) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_DecryptInit_ex failed",
+			   __func__);
+		goto fail;
+	}
+	if (aad && aad_len &&
+	    EVP_EncryptUpdate(cctx, NULL, &len, aad, aad_len) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_EncryptUpdate(AAD) failed",
+			   __func__);
+		goto fail;
+	}
+	ct = wpabuf_alloc(pt_len + AES_BLOCK_SIZE + ctx->n_t);
+	if (!ct)
+		goto fail;
+	if (EVP_EncryptUpdate(cctx, wpabuf_put(ct, 0), &len, pt, pt_len) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_EncryptUpdate failed",
+			   __func__);
+		goto fail;
+	}
+	wpabuf_put(ct, len);
+
+	if (EVP_EncryptFinal(cctx, wpabuf_put(ct, 0), &len) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_DecryptFinal failed",
+			   __func__);
+		wpabuf_free(ct);
+		ct = NULL;
+		goto fail;
+	}
+
+	if (EVP_CIPHER_CTX_ctrl(cctx, EVP_CTRL_AEAD_GET_TAG, ctx->n_t,
+				wpabuf_put(ct, ctx->n_t)) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL:%s:Could not get tag",
+			   __func__);
+		wpabuf_free(ct);
+		ct = NULL;
+		goto fail;
+	}
+fail:
+	EVP_CIPHER_CTX_free(cctx);
+	return ct;
+}
+
+
+static struct wpabuf * hpke_base_seal_int(enum hpke_kem_id kem_id,
+					  enum hpke_kdf_id kdf_id,
+					  enum hpke_aead_id aead_id,
+					  struct crypto_ec_key *peer_pub,
+					  const u8 *info, size_t info_len,
+					  const u8 *aad, size_t aad_len,
+					  const u8 *pt, size_t pt_len)
+{
+	struct hpke_context *ctx;
+	u8 shared_secret[HPKE_MAX_SHARED_SECRET_LEN];
+	u8 enc[1 + 2 * HPKE_MAX_PUB_LEN];
+	struct wpabuf *ct = NULL, *enc_ct = NULL;
+
+	ctx = hpke_get_context(kem_id, kdf_id, aead_id, peer_pub);
+	if (!ctx)
+		return NULL;
+
+	/* shared_secret, enc = Encap(pkR) */
+	if (hpke_encap(ctx, peer_pub, shared_secret, enc) < 0)
+		goto fail;
+
+	/* KeyScheduleS(mode_base, shared_secret, info,
+	 *              default_psk, default_psk_id) */
+	if (hpke_key_schedule(ctx, shared_secret, info, info_len) < 0)
+		goto fail;
+
+	/* ct = ctx.Seal(aad, pt) */
+	ct = hpke_aead_seal(ctx, aad, aad_len, pt, pt_len);
+	if (!ct)
+		goto fail;
+
+	/* return enc, ct */
+	enc_ct = wpabuf_alloc(ctx->n_pk + wpabuf_len(ct));
+	if (!enc_ct)
+		goto fail;
+	wpabuf_put_data(enc_ct, enc, ctx->n_pk);
+	wpabuf_put_buf(enc_ct, ct);
+
+fail:
+	forced_memzero(shared_secret, sizeof(shared_secret));
+	hpke_free_context(ctx);
+	wpabuf_free(ct);
+	return enc_ct;
+}
+
+
+static int hpke_decap(struct hpke_context *ctx, const u8 *enc,
+		      size_t enc_ct_len, struct crypto_ec_key *sk_r,
+		      u8 *shared_secret)
+{
+	EVP_PKEY_CTX *pctx = NULL;
+	struct wpabuf *pk_rm = NULL;
+	size_t len;
+	int res = -1;
+	struct crypto_ec_key *pk_e = NULL;
+	u8 *dhss = NULL;
+	size_t dhss_len = 0;
+
+	/* pkE = DeserializePublicKey(enc) */
+	if (enc_ct_len < ctx->n_pk)
+		return -1; /* not enough room for enc */
+	if (enc[0] != 0x04)
+		return -1; /* not in uncompressed form */
+	len = (ctx->n_pk - 1) / 2;
+	pk_e = crypto_ec_key_set_pub(ctx->iana_group, &enc[1],
+				     &enc[1 + len], len);
+	if (!pk_e)
+		return -1; /* invalid public key point */
+	/* dh = DH(skR, pkE) */
+	pctx = EVP_PKEY_CTX_new((EVP_PKEY *) sk_r, NULL);
+	if (!pctx ||
+	    EVP_PKEY_derive_init(pctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(pctx, (EVP_PKEY *) pk_e) != 1 ||
+	    EVP_PKEY_derive(pctx, NULL, &dhss_len) != 1 ||
+	    !(dhss = os_malloc(dhss_len)) ||
+	    EVP_PKEY_derive(pctx, dhss, &dhss_len) != 1 ||
+	    dhss_len > HPKE_MAX_SHARED_SECRET_LEN) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: hpke_decap: EVP_PKEY_derive failed (dhss_len=%zu): %s",
+			   dhss_len, ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	/* pkRm = SerializePublicKey(pk(skR)) */
+	pk_rm = crypto_ec_key_get_pubkey_point(sk_r, 1);
+	if (!pk_rm)
+		goto fail;
+
+	/* kem_context = concat(enc, pkRm) */
+	/* shared_secret = ExtractAndExpand(dh, kem_context) */
+	res = hpke_extract_and_expand(ctx, dhss, dhss_len, enc, ctx->n_pk,
+				      wpabuf_head(pk_rm),
+				      wpabuf_len(pk_rm), shared_secret);
+fail:
+	bin_clear_free(dhss, dhss_len);
+	crypto_ec_key_deinit(pk_e);
+	EVP_PKEY_CTX_free(pctx);
+	wpabuf_free(pk_rm);
+	return res;
+}
+
+
+static struct wpabuf *
+hpke_aead_open(struct hpke_context *ctx, const u8 *aad, size_t aad_len,
+	       const u8 *ct, size_t ct_len)
+{
+	EVP_CIPHER_CTX *cctx;
+	int len = 0;
+	const u8 *tag;
+	struct wpabuf *pt = NULL;
+
+	if (ct_len < ctx->n_t)
+		return NULL;
+	tag = ct + ct_len - ctx->n_t;
+	ct_len -= ctx->n_t;
+
+	/* No need to xor in sequence number since we support only the
+	 * single-shot API, i.e., base_nonce can be used as-is. */
+
+	cctx = EVP_CIPHER_CTX_new();
+	if (!cctx ||
+	    EVP_DecryptInit_ex(cctx, ctx->cipher, NULL, ctx->key,
+			       ctx->base_nonce) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_DecryptInit_ex failed",
+			   __func__);
+		goto fail;
+	}
+	if (aad && aad_len &&
+	    EVP_DecryptUpdate(cctx, NULL, &len, aad, aad_len) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_DecryptUpdate(AAD) failed",
+			   __func__);
+		goto fail;
+	}
+	pt = wpabuf_alloc(ct_len + AES_BLOCK_SIZE);
+	if (!pt)
+		goto fail;
+	if (EVP_DecryptUpdate(cctx, wpabuf_put(pt, 0), &len, ct, ct_len) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_DecryptUpdate failed",
+			   __func__);
+		goto fail;
+	}
+	wpabuf_put(pt, len);
+
+	if (EVP_CIPHER_CTX_ctrl(cctx, EVP_CTRL_AEAD_SET_TAG, ctx->n_t,
+				(void *) tag) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL:%s:Could not set tag",
+			   __func__);
+		wpabuf_free(pt);
+		pt = NULL;
+		goto fail;
+	}
+
+	if (EVP_DecryptFinal(cctx, wpabuf_put(pt, 0), &len) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_DecryptFinal failed",
+			   __func__);
+		wpabuf_free(pt);
+		pt = NULL;
+	}
+fail:
+	EVP_CIPHER_CTX_free(cctx);
+	return pt;
+}
+
+
+static struct wpabuf * hpke_base_open_int(enum hpke_kem_id kem_id,
+					  enum hpke_kdf_id kdf_id,
+					  enum hpke_aead_id aead_id,
+					  struct crypto_ec_key *own_priv,
+					  const u8 *info, size_t info_len,
+					  const u8 *aad, size_t aad_len,
+					  const u8 *enc_ct, size_t enc_ct_len)
+{
+	struct hpke_context *ctx;
+	u8 shared_secret[HPKE_MAX_SHARED_SECRET_LEN];
+	struct wpabuf *pt = NULL;
+
+	ctx = hpke_get_context(kem_id, kdf_id, aead_id, own_priv);
+	if (!ctx)
+		return NULL;
+
+	/* shared_secret = Decap(enc, skR) */
+	if (hpke_decap(ctx, enc_ct, enc_ct_len, own_priv, shared_secret) < 0)
+		goto fail;
+
+	/* KeyScheduleR(mode_base, shared_secret, info,
+	 *              default_psk, default_psk_id) */
+	if (hpke_key_schedule(ctx, shared_secret, info, info_len) < 0)
+		goto fail;
+
+	/* return ctx.Open(aad, ct) */
+	pt = hpke_aead_open(ctx, aad, aad_len,
+			    &enc_ct[ctx->n_pk], enc_ct_len - ctx->n_pk);
+
+fail:
+	forced_memzero(shared_secret, sizeof(shared_secret));
+	hpke_free_context(ctx);
+	return pt;
+}
+
+
+#if OPENSSL_VERSION_NUMBER >= 0x30200000L
+
+static bool hpke_set_suite(OSSL_HPKE_SUITE *suite,
+			   enum hpke_kem_id kem_id,
+			   enum hpke_kdf_id kdf_id,
+			   enum hpke_aead_id aead_id)
+{
+	os_memset(suite, 0, sizeof(*suite));
+
+	switch (kem_id) {
+	case HPKE_DHKEM_P256_HKDF_SHA256:
+		suite->kem_id = OSSL_HPKE_KEM_ID_P256;
+		break;
+	case HPKE_DHKEM_P384_HKDF_SHA384:
+		suite->kem_id = OSSL_HPKE_KEM_ID_P384;
+		break;
+	case HPKE_DHKEM_P521_HKDF_SHA512:
+		suite->kem_id = OSSL_HPKE_KEM_ID_P521;
+		break;
+	default:
+		return false;
+	}
+
+	switch (kdf_id) {
+	case HPKE_KDF_HKDF_SHA256:
+		suite->kdf_id = OSSL_HPKE_KDF_ID_HKDF_SHA256;
+		break;
+	case HPKE_KDF_HKDF_SHA384:
+		suite->kdf_id = OSSL_HPKE_KDF_ID_HKDF_SHA384;
+		break;
+	case HPKE_KDF_HKDF_SHA512:
+		suite->kdf_id = OSSL_HPKE_KDF_ID_HKDF_SHA512;
+		break;
+	default:
+		return false;
+	}
+
+	switch (aead_id) {
+	case HPKE_AEAD_AES_128_GCM:
+		suite->aead_id = OSSL_HPKE_AEAD_ID_AES_GCM_128;
+		break;
+	case HPKE_AEAD_AES_256_GCM:
+		suite->aead_id = OSSL_HPKE_AEAD_ID_AES_GCM_256;
+		break;
+	default:
+		return false;
+	}
+
+	if (!OSSL_HPKE_suite_check(*suite)) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: HPKE suite kem_id=%d kdf_id=%d aead_id=%d not supported",
+			   kem_id, kdf_id, aead_id);
+		return false;
+	}
+
+	return true;
+}
+
+
+struct wpabuf * hpke_base_seal(enum hpke_kem_id kem_id,
+			       enum hpke_kdf_id kdf_id,
+			       enum hpke_aead_id aead_id,
+			       struct crypto_ec_key *peer_pub,
+			       const u8 *info, size_t info_len,
+			       const u8 *aad, size_t aad_len,
+			       const u8 *pt, size_t pt_len)
+{
+	OSSL_HPKE_SUITE suite;
+	OSSL_HPKE_CTX *ctx = NULL;
+	struct wpabuf *res = NULL, *buf, *pub = NULL;
+	size_t enc_len, ct_len;
+	int group;
+
+	group = crypto_ec_key_group(peer_pub);
+	if (group == 28 || group == 29 || group == 30) {
+		/* Use the internal routines for the special DPP use case with
+		 * brainpool curves, */
+		return hpke_base_seal_int(kem_id, kdf_id, aead_id, peer_pub,
+					  info, info_len, aad, aad_len,
+					  pt, pt_len);
+	}
+
+
+	if (!hpke_set_suite(&suite, kem_id, kdf_id, aead_id))
+		return NULL;
+
+	enc_len = OSSL_HPKE_get_public_encap_size(suite);
+	ct_len = OSSL_HPKE_get_ciphertext_size(suite, pt_len);
+	buf = wpabuf_alloc(enc_len + ct_len);
+	if (!buf)
+		goto out;
+
+	pub = crypto_ec_key_get_pubkey_point(peer_pub, 1);
+	if (!pub)
+		goto out;
+
+	ctx = OSSL_HPKE_CTX_new(OSSL_HPKE_MODE_BASE, suite,
+				OSSL_HPKE_ROLE_SENDER, NULL, NULL);
+	if (!ctx)
+		goto out;
+
+	if (OSSL_HPKE_encap(ctx, wpabuf_put(buf, 0), &enc_len,
+			    wpabuf_head(pub), wpabuf_len(pub),
+			    info, info_len) != 1) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: OSSL_HPKE_encap failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto out;
+	}
+	wpabuf_put(buf, enc_len);
+
+	if (OSSL_HPKE_seal(ctx, wpabuf_put(buf, 0), &ct_len, aad, aad_len,
+			   pt, pt_len) != 1) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: OSSL_HPKE_seal failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto out;
+	}
+	wpabuf_put(buf, ct_len);
+	res = buf;
+	buf = NULL;
+
+out:
+	OSSL_HPKE_CTX_free(ctx);
+	wpabuf_free(buf);
+	wpabuf_free(pub);
+	return res;
+}
+
+
+struct wpabuf * hpke_base_open(enum hpke_kem_id kem_id,
+			       enum hpke_kdf_id kdf_id,
+			       enum hpke_aead_id aead_id,
+			       struct crypto_ec_key *own_priv,
+			       const u8 *info, size_t info_len,
+			       const u8 *aad, size_t aad_len,
+			       const u8 *enc_ct, size_t enc_ct_len)
+{
+	OSSL_HPKE_SUITE suite;
+	OSSL_HPKE_CTX *ctx;
+	struct wpabuf *buf = NULL, *res = NULL;
+	size_t len, enc_len;
+	int group;
+
+	group = crypto_ec_key_group(own_priv);
+	if (group == 28 || group == 29 || group == 30) {
+		/* Use the internal routines for the special DPP use case with
+		 * brainpool curves, */
+		return hpke_base_open_int(kem_id, kdf_id, aead_id, own_priv,
+					  info, info_len, aad, aad_len,
+					  enc_ct, enc_ct_len);
+	}
+
+	if (!hpke_set_suite(&suite, kem_id, kdf_id, aead_id))
+		return NULL;
+
+	enc_len = OSSL_HPKE_get_public_encap_size(suite);
+	if (enc_ct_len < enc_len) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Too short HPKE enc_ct data");
+		return NULL;
+	}
+
+	ctx = OSSL_HPKE_CTX_new(OSSL_HPKE_MODE_BASE, suite,
+				OSSL_HPKE_ROLE_RECEIVER, NULL, NULL);
+	if (!ctx)
+		goto out;
+
+	if (OSSL_HPKE_decap(ctx, enc_ct, enc_len, (EVP_PKEY *) own_priv,
+			    info, info_len) != 1) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: OSSL_HPKE_decap failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto out;
+	}
+
+	len = enc_ct_len;
+	buf = wpabuf_alloc(len);
+	if (!buf)
+		goto out;
+
+	if (OSSL_HPKE_open(ctx, wpabuf_put(buf, 0), &len, aad, aad_len,
+			   enc_ct + enc_len, enc_ct_len - enc_len) != 1) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: OSSL_HPKE_open failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto out;
+	}
+
+	wpabuf_put(buf, len);
+	res = buf;
+	buf = NULL;
+
+out:
+	OSSL_HPKE_CTX_free(ctx);
+	wpabuf_free(buf);
+	return res;
+}
+
+#else /* OpenSSL < 3.2 */
+
+struct wpabuf * hpke_base_seal(enum hpke_kem_id kem_id,
+			       enum hpke_kdf_id kdf_id,
+			       enum hpke_aead_id aead_id,
+			       struct crypto_ec_key *peer_pub,
+			       const u8 *info, size_t info_len,
+			       const u8 *aad, size_t aad_len,
+			       const u8 *pt, size_t pt_len)
+{
+	return hpke_base_seal_int(kem_id, kdf_id, aead_id, peer_pub,
+				  info, info_len, aad, aad_len, pt, pt_len);
+}
+
+
+struct wpabuf * hpke_base_open(enum hpke_kem_id kem_id,
+			       enum hpke_kdf_id kdf_id,
+			       enum hpke_aead_id aead_id,
+			       struct crypto_ec_key *own_priv,
+			       const u8 *info, size_t info_len,
+			       const u8 *aad, size_t aad_len,
+			       const u8 *enc_ct, size_t enc_ct_len)
+{
+	return hpke_base_open_int(kem_id, kdf_id, aead_id, own_priv,
+				  info, info_len, aad, aad_len,
+				  enc_ct, enc_ct_len);
+}
+
+#endif /* OpenSSL < 3.2 */
+
+#endif /* CONFIG_DPP3 */
+
+
+void crypto_unload(void)
+{
+	openssl_unload_legacy_provider();
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_wolfssl.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_wolfssl.c
new file mode 100644
index 0000000..2691743
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/crypto_wolfssl.c
@@ -0,0 +1,3560 @@
+/*
+ * Wrapper functions for libwolfssl
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+#include "tls/asn1.h"
+
+/* wolfSSL headers */
+#include <wolfssl/options.h> /* options.h needs to be included first */
+#include <wolfssl/version.h>
+#include <wolfssl/openssl/bn.h>
+#include <wolfssl/wolfcrypt/aes.h>
+#include <wolfssl/wolfcrypt/arc4.h>
+#include <wolfssl/wolfcrypt/asn_public.h>
+#include <wolfssl/wolfcrypt/cmac.h>
+#include <wolfssl/wolfcrypt/des3.h>
+#include <wolfssl/wolfcrypt/dh.h>
+#include <wolfssl/wolfcrypt/ecc.h>
+#include <wolfssl/wolfcrypt/error-crypt.h>
+#include <wolfssl/wolfcrypt/hmac.h>
+#include <wolfssl/wolfcrypt/md4.h>
+#include <wolfssl/wolfcrypt/md5.h>
+#include <wolfssl/wolfcrypt/pkcs7.h>
+#include <wolfssl/wolfcrypt/pwdbased.h>
+#include <wolfssl/wolfcrypt/sha.h>
+#include <wolfssl/wolfcrypt/sha256.h>
+#include <wolfssl/wolfcrypt/sha512.h>
+
+#ifdef CONFIG_FIPS
+#ifndef HAVE_FIPS
+#warning "You are compiling wpa_supplicant/hostapd in FIPS mode but wolfSSL is not configured for FIPS mode."
+#endif /* HAVE_FIPS */
+#endif /* CONFIG_FIPS */
+
+
+#ifdef CONFIG_FIPS
+#if !defined(HAVE_FIPS_VERSION) || HAVE_FIPS_VERSION <= 2
+#define WOLFSSL_OLD_FIPS
+#endif
+#endif
+
+#if LIBWOLFSSL_VERSION_HEX < 0x05004000
+static int wc_EccPublicKeyToDer_ex(ecc_key *key, byte *output,
+				   word32 inLen, int with_AlgCurve,
+				   int comp)
+{
+	return wc_EccPublicKeyToDer(key, output, inLen, with_AlgCurve);
+}
+#endif /* version < 5.4.0 */
+
+#define LOG_WOLF_ERROR_VA(msg, ...) \
+	wpa_printf(MSG_ERROR, "wolfSSL: %s:%d " msg, \
+		   __func__, __LINE__, __VA_ARGS__)
+
+#define LOG_WOLF_ERROR(msg) \
+	LOG_WOLF_ERROR_VA("%s", (msg))
+
+#define LOG_WOLF_ERROR_FUNC(func, err) \
+	LOG_WOLF_ERROR_VA(#func " failed with err: %d %s", \
+			  (err), wc_GetErrorString(err))
+
+#define LOG_WOLF_ERROR_FUNC_NULL(func) \
+	LOG_WOLF_ERROR(#func " failed with NULL return")
+
+#define LOG_INVALID_PARAMETERS() \
+	LOG_WOLF_ERROR("invalid input parameters")
+
+
+/* Helper functions to make type allocation uniform */
+
+static WC_RNG * wc_rng_init(void)
+{
+	WC_RNG *ret;
+
+#ifdef CONFIG_FIPS
+	ret = os_zalloc(sizeof(WC_RNG));
+	if (!ret) {
+		LOG_WOLF_ERROR_FUNC_NULL(os_zalloc);
+	} else {
+		int err;
+
+		err = wc_InitRng(ret);
+		if (err != 0) {
+			LOG_WOLF_ERROR_FUNC(wc_InitRng, err);
+			os_free(ret);
+			ret = NULL;
+		}
+	}
+#else /* CONFIG_FIPS */
+	ret = wc_rng_new(NULL, 0, NULL);
+	if (!ret)
+		LOG_WOLF_ERROR_FUNC_NULL(wc_rng_new);
+#endif /* CONFIG_FIPS */
+
+	return ret;
+}
+
+
+static void wc_rng_deinit(WC_RNG *rng)
+{
+#ifdef CONFIG_FIPS
+	wc_FreeRng(rng);
+	os_free(rng);
+#else /* CONFIG_FIPS */
+	wc_rng_free(rng);
+#endif /* CONFIG_FIPS */
+}
+
+
+static ecc_key * ecc_key_init(void)
+{
+	ecc_key *ret;
+#ifdef CONFIG_FIPS
+	int err;
+
+	ret = os_zalloc(sizeof(ecc_key));
+	if (!ret) {
+		LOG_WOLF_ERROR_FUNC_NULL(os_zalloc);
+	} else {
+		err = wc_ecc_init_ex(ret, NULL, INVALID_DEVID);
+		if (err != 0) {
+			LOG_WOLF_ERROR("wc_ecc_init_ex failed");
+			os_free(ret);
+			ret = NULL;
+		}
+	}
+#else /* CONFIG_FIPS */
+	ret = wc_ecc_key_new(NULL);
+	if (!ret)
+		LOG_WOLF_ERROR_FUNC_NULL(wc_ecc_key_new);
+#endif /* CONFIG_FIPS */
+
+	return ret;
+}
+
+
+static void ecc_key_deinit(ecc_key *key)
+{
+#ifdef CONFIG_FIPS
+	wc_ecc_free(key);
+	os_free(key);
+#else /* CONFIG_FIPS */
+	wc_ecc_key_free(key);
+#endif /* CONFIG_FIPS */
+}
+
+/* end of helper functions */
+
+
+#ifndef CONFIG_FIPS
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	Md4 md4;
+	size_t i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	wc_InitMd4(&md4);
+
+	for (i = 0; i < num_elem; i++)
+		wc_Md4Update(&md4, addr[i], len[i]);
+
+	wc_Md4Final(&md4, mac);
+
+	return 0;
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	wc_Md5 md5;
+	size_t i;
+	int err;
+	int ret = -1;
+
+	if (TEST_FAIL())
+		return -1;
+
+	err = wc_InitMd5(&md5);
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_InitMd5, err);
+		return -1;
+	}
+
+	for (i = 0; i < num_elem; i++) {
+		err = wc_Md5Update(&md5, addr[i], len[i]);
+		if (err != 0) {
+			LOG_WOLF_ERROR_FUNC(wc_Md5Update, err);
+			goto fail;
+		}
+	}
+
+	err = wc_Md5Final(&md5, mac);
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_Md5Final, err);
+		goto fail;
+	}
+
+	ret = 0;
+fail:
+	wc_Md5Free(&md5);
+	return ret;
+}
+
+#endif /* CONFIG_FIPS */
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	wc_Sha sha;
+	size_t i;
+	int err;
+	int ret = -1;
+
+	if (TEST_FAIL())
+		return -1;
+
+	err = wc_InitSha(&sha);
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_InitSha, err);
+		return -1;
+	}
+
+	for (i = 0; i < num_elem; i++) {
+		err = wc_ShaUpdate(&sha, addr[i], len[i]);
+		if (err != 0) {
+			LOG_WOLF_ERROR_FUNC(wc_ShaUpdate, err);
+			goto fail;
+		}
+	}
+
+	err = wc_ShaFinal(&sha, mac);
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_ShaFinal, err);
+		goto fail;
+	}
+
+	ret = 0;
+fail:
+	wc_ShaFree(&sha);
+	return ret;
+}
+
+
+#ifndef NO_SHA256_WRAPPER
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	wc_Sha256 sha256;
+	size_t i;
+	int err;
+	int ret = -1;
+
+	if (TEST_FAIL())
+		return -1;
+
+	err = wc_InitSha256(&sha256);
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_InitSha256, err);
+		return -1;
+	}
+
+	for (i = 0; i < num_elem; i++) {
+		err = wc_Sha256Update(&sha256, addr[i], len[i]);
+		if (err != 0) {
+			LOG_WOLF_ERROR_FUNC(wc_Sha256Update, err);
+			goto fail;
+		}
+	}
+
+	err = wc_Sha256Final(&sha256, mac);
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_Sha256Final, err);
+		goto fail;
+	}
+
+	ret = 0;
+fail:
+	wc_Sha256Free(&sha256);
+	return ret;
+}
+#endif /* NO_SHA256_WRAPPER */
+
+
+#ifdef CONFIG_SHA384
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	wc_Sha384 sha384;
+	size_t i;
+	int err;
+	int ret = -1;
+
+	if (TEST_FAIL())
+		return -1;
+
+	err = wc_InitSha384(&sha384);
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_InitSha384, err);
+		return -1;
+	}
+
+	for (i = 0; i < num_elem; i++) {
+		err = wc_Sha384Update(&sha384, addr[i], len[i]);
+		if (err != 0) {
+			LOG_WOLF_ERROR_FUNC(wc_Sha384Update, err);
+			goto fail;
+		}
+	}
+
+	err = wc_Sha384Final(&sha384, mac);
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_Sha384Final, err);
+		goto fail;
+	}
+
+	ret = 0;
+fail:
+	wc_Sha384Free(&sha384);
+	return ret;
+}
+#endif /* CONFIG_SHA384 */
+
+
+#ifdef CONFIG_SHA512
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	wc_Sha512 sha512;
+	size_t i;
+	int err;
+	int ret = -1;
+
+	if (TEST_FAIL())
+		return -1;
+
+	err = wc_InitSha512(&sha512);
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_InitSha512, err);
+		return -1;
+	}
+
+	for (i = 0; i < num_elem; i++) {
+		err = wc_Sha512Update(&sha512, addr[i], len[i]);
+		if (err != 0) {
+			LOG_WOLF_ERROR_FUNC(wc_Sha512Update, err);
+			goto fail;
+		}
+	}
+
+	err = wc_Sha512Final(&sha512, mac);
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_Sha512Final, err);
+		goto fail;
+	}
+
+	ret = 0;
+fail:
+	wc_Sha512Free(&sha512);
+	return ret;
+}
+#endif /* CONFIG_SHA512 */
+
+
+static int wolfssl_hmac_vector(int type, const u8 *key,
+			       size_t key_len, size_t num_elem,
+			       const u8 *addr[], const size_t *len, u8 *mac,
+			       unsigned int mdlen)
+{
+	Hmac hmac;
+	size_t i;
+	int err;
+	int ret = -1;
+
+	(void) mdlen;
+
+	if (TEST_FAIL())
+		return -1;
+
+	err = wc_HmacInit(&hmac, NULL, INVALID_DEVID);
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_HmacInit, err);
+		return -1;
+	}
+
+	err = wc_HmacSetKey(&hmac, type, key, (word32) key_len);
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_HmacSetKey, err);
+		goto fail;
+	}
+
+	for (i = 0; i < num_elem; i++) {
+		err = wc_HmacUpdate(&hmac, addr[i], len[i]);
+		if (err != 0) {
+			LOG_WOLF_ERROR_FUNC(wc_HmacUpdate, err);
+			goto fail;
+		}
+	}
+	err = wc_HmacFinal(&hmac, mac);
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_HmacFinal, err);
+		goto fail;
+	}
+
+	ret = 0;
+fail:
+	wc_HmacFree(&hmac);
+	return ret;
+}
+
+
+#ifndef CONFIG_FIPS
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+		    const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return wolfssl_hmac_vector(WC_MD5, key, key_len, num_elem, addr, len,
+				   mac, 16);
+}
+
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	     u8 *mac)
+{
+	return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_FIPS */
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return wolfssl_hmac_vector(WC_SHA, key, key_len, num_elem, addr, len,
+				   mac, 20);
+}
+
+
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	      u8 *mac)
+{
+	return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+#ifdef CONFIG_SHA256
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return wolfssl_hmac_vector(WC_SHA256, key, key_len, num_elem, addr, len,
+				   mac, 32);
+}
+
+
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA256 */
+
+
+#ifdef CONFIG_SHA384
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return wolfssl_hmac_vector(WC_SHA384, key, key_len, num_elem, addr, len,
+				   mac, 48);
+}
+
+
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA384 */
+
+
+#ifdef CONFIG_SHA512
+
+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return wolfssl_hmac_vector(WC_SHA512, key, key_len, num_elem, addr, len,
+				   mac, 64);
+}
+
+
+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA512 */
+
+
+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
+		int iterations, u8 *buf, size_t buflen)
+{
+	int ret;
+
+	ret = wc_PBKDF2(buf, (const byte *) passphrase, os_strlen(passphrase),
+			ssid, ssid_len, iterations, buflen, WC_SHA);
+	if (ret != 0) {
+		if (ret == HMAC_MIN_KEYLEN_E) {
+			LOG_WOLF_ERROR_VA("wolfSSL: Password is too short. Make sure your password is at least %d characters long. This is a requirement for FIPS builds.",
+					  HMAC_FIPS_MIN_KEY);
+		}
+		return -1;
+	}
+	return 0;
+}
+
+
+#ifdef CONFIG_DES
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+	Des des;
+	u8  pkey[8], next, tmp;
+	int i;
+
+	/* Add parity bits to the key */
+	next = 0;
+	for (i = 0; i < 7; i++) {
+		tmp = key[i];
+		pkey[i] = (tmp >> i) | next | 1;
+		next = tmp << (7 - i);
+	}
+	pkey[i] = next | 1;
+
+	wc_Des_SetKey(&des, pkey, NULL, DES_ENCRYPTION);
+	wc_Des_EcbEncrypt(&des, cypher, clear, DES_BLOCK_SIZE);
+
+	return 0;
+}
+#endif /* CONFIG_DES */
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+	Aes *aes;
+	int err;
+
+	if (TEST_FAIL())
+		return NULL;
+
+	aes = os_malloc(sizeof(Aes));
+	if (!aes) {
+		LOG_WOLF_ERROR_FUNC_NULL(os_malloc);
+		return NULL;
+	}
+
+	err = wc_AesSetKey(aes, key, len, NULL, AES_ENCRYPTION);
+	if (err < 0) {
+		LOG_WOLF_ERROR_FUNC(wc_AesSetKey, err);
+		os_free(aes);
+		return NULL;
+	}
+
+	return aes;
+}
+
+
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+#if defined(HAVE_FIPS) && \
+    (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION <= 2))
+	/* Old FIPS has void return on this API */
+	wc_AesEncryptDirect(ctx, crypt, plain);
+#else
+	int err = wc_AesEncryptDirect(ctx, crypt, plain);
+
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_AesEncryptDirect, err);
+		return -1;
+	}
+#endif
+	return 0;
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+	os_free(ctx);
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+	Aes *aes;
+	int err;
+
+	if (TEST_FAIL())
+		return NULL;
+
+	aes = os_malloc(sizeof(Aes));
+	if (!aes) {
+		LOG_WOLF_ERROR_FUNC_NULL(os_malloc);
+		return NULL;
+	}
+
+	err = wc_AesSetKey(aes, key, len, NULL, AES_DECRYPTION);
+	if (err < 0) {
+		LOG_WOLF_ERROR_FUNC(wc_AesSetKey, err);
+		os_free(aes);
+		return NULL;
+	}
+
+	return aes;
+}
+
+
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+#if defined(HAVE_FIPS) && \
+    (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION <= 2))
+	/* Old FIPS has void return on this API */
+	wc_AesDecryptDirect(ctx, plain, crypt);
+#else
+	int err = wc_AesDecryptDirect(ctx, plain, crypt);
+
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_AesDecryptDirect, err);
+		return -1;
+	}
+#endif
+	return 0;
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+	os_free(ctx);
+}
+
+
+int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+	Aes aes;
+	int ret;
+
+	if (TEST_FAIL())
+		return -1;
+
+	ret = wc_AesSetKey(&aes, key, 16, iv, AES_ENCRYPTION);
+	if (ret != 0)
+		return -1;
+
+	ret = wc_AesCbcEncrypt(&aes, data, data, data_len);
+	if (ret != 0)
+		return -1;
+	return 0;
+}
+
+
+int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+	Aes aes;
+	int ret;
+
+	if (TEST_FAIL())
+		return -1;
+
+	ret = wc_AesSetKey(&aes, key, 16, iv, AES_DECRYPTION);
+	if (ret != 0)
+		return -1;
+
+	ret = wc_AesCbcDecrypt(&aes, data, data, data_len);
+	if (ret != 0)
+		return -1;
+	return 0;
+}
+
+
+#ifndef CONFIG_FIPS
+#ifndef CONFIG_OPENSSL_INTERNAL_AES_WRAP
+int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
+{
+#ifdef HAVE_AES_KEYWRAP
+	int ret;
+
+	if (TEST_FAIL())
+		return -1;
+
+	ret = wc_AesKeyWrap(kek, kek_len, plain, n * 8, cipher, (n + 1) * 8,
+			    NULL);
+	return ret != (n + 1) * 8 ? -1 : 0;
+#else /* HAVE_AES_KEYWRAP */
+	return -1;
+#endif /* HAVE_AES_KEYWRAP */
+}
+
+
+int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
+	       u8 *plain)
+{
+#ifdef HAVE_AES_KEYWRAP
+	int ret;
+
+	if (TEST_FAIL())
+		return -1;
+
+	ret = wc_AesKeyUnWrap(kek, kek_len, cipher, (n + 1) * 8, plain, n * 8,
+			      NULL);
+	return ret != n * 8 ? -1 : 0;
+#else /* HAVE_AES_KEYWRAP */
+	return -1;
+#endif /* HAVE_AES_KEYWRAP */
+}
+#endif /* CONFIG_OPENSSL_INTERNAL_AES_WRAP */
+#endif /* CONFIG_FIPS */
+
+
+#ifndef CONFIG_NO_RC4
+int rc4_skip(const u8 *key, size_t keylen, size_t skip, u8 *data,
+	     size_t data_len)
+{
+#ifndef NO_RC4
+	Arc4 arc4;
+	unsigned char skip_buf[16];
+
+	wc_Arc4SetKey(&arc4, key, keylen);
+
+	while (skip >= sizeof(skip_buf)) {
+		size_t len = skip;
+
+		if (len > sizeof(skip_buf))
+			len = sizeof(skip_buf);
+		wc_Arc4Process(&arc4, skip_buf, skip_buf, len);
+		skip -= len;
+	}
+
+	wc_Arc4Process(&arc4, data, data, data_len);
+
+	return 0;
+#else /* NO_RC4 */
+	return -1;
+#endif /* NO_RC4 */
+}
+#endif /* CONFIG_NO_RC4 */
+
+
+#if defined(EAP_IKEV2) || defined(EAP_IKEV2_DYNAMIC) \
+		       || defined(EAP_SERVER_IKEV2)
+union wolfssl_cipher {
+	Aes aes;
+	Des3 des3;
+	Arc4 arc4;
+};
+
+struct crypto_cipher {
+	enum crypto_cipher_alg alg;
+	union wolfssl_cipher enc;
+	union wolfssl_cipher dec;
+};
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+					  const u8 *iv, const u8 *key,
+					  size_t key_len)
+{
+	struct crypto_cipher *ctx;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (!ctx)
+		return NULL;
+
+	switch (alg) {
+#ifndef CONFIG_NO_RC4
+#ifndef NO_RC4
+	case CRYPTO_CIPHER_ALG_RC4:
+		wc_Arc4SetKey(&ctx->enc.arc4, key, key_len);
+		wc_Arc4SetKey(&ctx->dec.arc4, key, key_len);
+		break;
+#endif /* NO_RC4 */
+#endif /* CONFIG_NO_RC4 */
+#ifndef NO_AES
+	case CRYPTO_CIPHER_ALG_AES:
+		switch (key_len) {
+		case 16:
+		case 24:
+		case 32:
+			break;
+		default:
+			os_free(ctx);
+			return NULL;
+		}
+		if (wc_AesSetKey(&ctx->enc.aes, key, key_len, iv,
+				 AES_ENCRYPTION) ||
+		    wc_AesSetKey(&ctx->dec.aes, key, key_len, iv,
+				 AES_DECRYPTION)) {
+			os_free(ctx);
+			return NULL;
+		}
+		break;
+#endif /* NO_AES */
+#ifndef NO_DES3
+	case CRYPTO_CIPHER_ALG_3DES:
+		if (key_len != DES3_KEYLEN ||
+		    wc_Des3_SetKey(&ctx->enc.des3, key, iv, DES_ENCRYPTION) ||
+		    wc_Des3_SetKey(&ctx->dec.des3, key, iv, DES_DECRYPTION)) {
+			os_free(ctx);
+			return NULL;
+		}
+		break;
+#endif /* NO_DES3 */
+	case CRYPTO_CIPHER_ALG_RC2:
+	case CRYPTO_CIPHER_ALG_DES:
+	default:
+		os_free(ctx);
+		return NULL;
+	}
+
+	ctx->alg = alg;
+
+	return ctx;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+			  u8 *crypt, size_t len)
+{
+	switch (ctx->alg) {
+#ifndef CONFIG_NO_RC4
+#ifndef NO_RC4
+	case CRYPTO_CIPHER_ALG_RC4:
+		wc_Arc4Process(&ctx->enc.arc4, crypt, plain, len);
+		return 0;
+#endif /* NO_RC4 */
+#endif /* CONFIG_NO_RC4 */
+#ifndef NO_AES
+	case CRYPTO_CIPHER_ALG_AES:
+		if (wc_AesCbcEncrypt(&ctx->enc.aes, crypt, plain, len) != 0)
+			return -1;
+		return 0;
+#endif /* NO_AES */
+#ifndef NO_DES3
+	case CRYPTO_CIPHER_ALG_3DES:
+		if (wc_Des3_CbcEncrypt(&ctx->enc.des3, crypt, plain, len) != 0)
+			return -1;
+		return 0;
+#endif /* NO_DES3 */
+	default:
+		return -1;
+	}
+	return -1;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+			  u8 *plain, size_t len)
+{
+	switch (ctx->alg) {
+#ifndef CONFIG_NO_RC4
+#ifndef NO_RC4
+	case CRYPTO_CIPHER_ALG_RC4:
+		wc_Arc4Process(&ctx->dec.arc4, plain, crypt, len);
+		return 0;
+#endif /* NO_RC4 */
+#endif /* CONFIG_NO_RC4 */
+#ifndef NO_AES
+	case CRYPTO_CIPHER_ALG_AES:
+		if (wc_AesCbcDecrypt(&ctx->dec.aes, plain, crypt, len) != 0)
+			return -1;
+		return 0;
+#endif /* NO_AES */
+#ifndef NO_DES3
+	case CRYPTO_CIPHER_ALG_3DES:
+		if (wc_Des3_CbcDecrypt(&ctx->dec.des3, plain, crypt, len) != 0)
+			return -1;
+		return 0;
+#endif /* NO_DES3 */
+	default:
+		return -1;
+	}
+	return -1;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+	os_free(ctx);
+}
+
+#endif
+
+
+#ifdef CONFIG_WPS
+
+static const unsigned char RFC3526_PRIME_1536[] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
+	0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+	0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
+	0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+	0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
+	0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+	0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
+	0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+	0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
+	0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+	0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36,
+	0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+	0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56,
+	0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+	0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08,
+	0xCA, 0x23, 0x73, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static const unsigned char RFC3526_GENERATOR_1536[] = {
+	0x02
+};
+
+#define RFC3526_LEN sizeof(RFC3526_PRIME_1536)
+
+
+void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
+{
+	WC_RNG rng;
+	DhKey *ret = NULL;
+	DhKey *dh = NULL;
+	struct wpabuf *privkey = NULL;
+	struct wpabuf *pubkey = NULL;
+	word32 priv_sz, pub_sz;
+
+	*priv = NULL;
+	wpabuf_free(*publ);
+	*publ = NULL;
+
+	dh = XMALLOC(sizeof(DhKey), NULL, DYNAMIC_TYPE_TMP_BUFFER);
+	if (!dh)
+		return NULL;
+	wc_InitDhKey(dh);
+
+	if (wc_InitRng(&rng) != 0) {
+		XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+		return NULL;
+	}
+
+	privkey = wpabuf_alloc(RFC3526_LEN);
+	pubkey = wpabuf_alloc(RFC3526_LEN);
+	if (!privkey || !pubkey)
+		goto done;
+
+	if (wc_DhSetKey(dh, RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536),
+			RFC3526_GENERATOR_1536, sizeof(RFC3526_GENERATOR_1536))
+	    != 0)
+		goto done;
+
+	priv_sz = pub_sz = RFC3526_LEN;
+	if (wc_DhGenerateKeyPair(dh, &rng, wpabuf_mhead(privkey), &priv_sz,
+				 wpabuf_mhead(pubkey), &pub_sz) != 0)
+		goto done;
+
+	wpabuf_put(privkey, priv_sz);
+	wpabuf_put(pubkey, pub_sz);
+
+	ret = dh;
+	*priv = privkey;
+	*publ = pubkey;
+	dh = NULL;
+	privkey = NULL;
+	pubkey = NULL;
+done:
+	wpabuf_clear_free(pubkey);
+	wpabuf_clear_free(privkey);
+	if (dh) {
+		wc_FreeDhKey(dh);
+		XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+	}
+	wc_FreeRng(&rng);
+	return ret;
+}
+
+
+#ifdef CONFIG_WPS_NFC
+
+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
+{
+	DhKey *ret = NULL;
+	DhKey *dh;
+	byte *secret;
+	word32 secret_sz;
+
+	dh = XMALLOC(sizeof(DhKey), NULL, DYNAMIC_TYPE_TMP_BUFFER);
+	if (!dh)
+		return NULL;
+	wc_InitDhKey(dh);
+
+	secret = XMALLOC(RFC3526_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+	if (!secret)
+		goto done;
+
+	if (wc_DhSetKey(dh, RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536),
+			RFC3526_GENERATOR_1536, sizeof(RFC3526_GENERATOR_1536))
+	    != 0)
+		goto done;
+
+	if (wc_DhAgree(dh, secret, &secret_sz, wpabuf_head(priv),
+		       wpabuf_len(priv), RFC3526_GENERATOR_1536,
+		       sizeof(RFC3526_GENERATOR_1536)) != 0)
+		goto done;
+
+	if (secret_sz != wpabuf_len(publ) ||
+	    os_memcmp(secret, wpabuf_head(publ), secret_sz) != 0)
+		goto done;
+
+	ret = dh;
+	dh = NULL;
+done:
+	if (dh) {
+		wc_FreeDhKey(dh);
+		XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+	}
+	XFREE(secret, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+	return ret;
+}
+
+#endif /* CONFIG_WPS_NFC */
+
+
+struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
+				  const struct wpabuf *own_private)
+{
+	struct wpabuf *ret = NULL;
+	struct wpabuf *secret;
+	word32 secret_sz;
+
+	secret = wpabuf_alloc(RFC3526_LEN);
+	if (!secret)
+		goto done;
+
+	if (wc_DhAgree(ctx, wpabuf_mhead(secret), &secret_sz,
+		       wpabuf_head(own_private), wpabuf_len(own_private),
+		       wpabuf_head(peer_public), wpabuf_len(peer_public)) != 0)
+		goto done;
+
+	wpabuf_put(secret, secret_sz);
+
+	ret = secret;
+	secret = NULL;
+done:
+	wpabuf_clear_free(secret);
+	return ret;
+}
+
+
+void dh5_free(void *ctx)
+{
+	if (!ctx)
+		return;
+
+	wc_FreeDhKey(ctx);
+	XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+}
+
+#endif /* CONFIG_WPS */
+
+
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+		   u8 *pubkey)
+{
+	int ret = -1;
+	WC_RNG rng;
+	DhKey *dh = NULL;
+	word32 priv_sz, pub_sz;
+
+	if (TEST_FAIL())
+		return -1;
+
+	dh = os_malloc(sizeof(DhKey));
+	if (!dh)
+		return -1;
+	wc_InitDhKey(dh);
+
+	if (wc_InitRng(&rng) != 0) {
+		os_free(dh);
+		return -1;
+	}
+
+	if (wc_DhSetKey(dh, prime, prime_len, &generator, 1) != 0)
+		goto done;
+
+	priv_sz = pub_sz = prime_len;
+	if (wc_DhGenerateKeyPair(dh, &rng, privkey, &priv_sz, pubkey, &pub_sz)
+	    != 0)
+		goto done;
+
+	if (priv_sz < prime_len) {
+		size_t pad_sz = prime_len - priv_sz;
+
+		os_memmove(privkey + pad_sz, privkey, priv_sz);
+		os_memset(privkey, 0, pad_sz);
+	}
+
+	if (pub_sz < prime_len) {
+		size_t pad_sz = prime_len - pub_sz;
+
+		os_memmove(pubkey + pad_sz, pubkey, pub_sz);
+		os_memset(pubkey, 0, pad_sz);
+	}
+	ret = 0;
+done:
+	wc_FreeDhKey(dh);
+	os_free(dh);
+	wc_FreeRng(&rng);
+	return ret;
+}
+
+
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+			    const u8 *order, size_t order_len,
+			    const u8 *privkey, size_t privkey_len,
+			    const u8 *pubkey, size_t pubkey_len,
+			    u8 *secret, size_t *len)
+{
+	int ret = -1;
+	DhKey *dh;
+	word32 secret_sz;
+
+	dh = os_malloc(sizeof(DhKey));
+	if (!dh)
+		return -1;
+	wc_InitDhKey(dh);
+
+	if (wc_DhSetKey(dh, prime, prime_len, &generator, 1) != 0)
+		goto done;
+
+	if (wc_DhAgree(dh, secret, &secret_sz, privkey, privkey_len, pubkey,
+		       pubkey_len) != 0)
+		goto done;
+
+	*len = secret_sz;
+	ret = 0;
+done:
+	wc_FreeDhKey(dh);
+	os_free(dh);
+	return ret;
+}
+
+
+#ifdef CONFIG_FIPS
+int crypto_get_random(void *buf, size_t len)
+{
+	int ret = 0;
+	WC_RNG rng;
+
+	if (wc_InitRng(&rng) != 0)
+		return -1;
+	if (wc_RNG_GenerateBlock(&rng, buf, len) != 0)
+		ret = -1;
+	wc_FreeRng(&rng);
+	return ret;
+}
+#endif /* CONFIG_FIPS */
+
+
+#if defined(EAP_PWD) || defined(EAP_SERVER_PWD)
+struct crypto_hash {
+	Hmac hmac;
+	int size;
+};
+
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+				      size_t key_len)
+{
+	struct crypto_hash *ret = NULL;
+	struct crypto_hash *hash;
+	int type;
+
+	hash = os_zalloc(sizeof(*hash));
+	if (!hash)
+		goto done;
+
+	switch (alg) {
+#ifndef NO_MD5
+	case CRYPTO_HASH_ALG_HMAC_MD5:
+		hash->size = 16;
+		type = WC_MD5;
+		break;
+#endif /* NO_MD5 */
+#ifndef NO_SHA
+	case CRYPTO_HASH_ALG_HMAC_SHA1:
+		type = WC_SHA;
+		hash->size = 20;
+		break;
+#endif /* NO_SHA */
+#ifdef CONFIG_SHA256
+#ifndef NO_SHA256
+	case CRYPTO_HASH_ALG_HMAC_SHA256:
+		type = WC_SHA256;
+		hash->size = 32;
+		break;
+#endif /* NO_SHA256 */
+#endif /* CONFIG_SHA256 */
+	default:
+		goto done;
+	}
+
+	if (wc_HmacInit(&hash->hmac, NULL, INVALID_DEVID) != 0 ||
+	    wc_HmacSetKey(&hash->hmac, type, key, key_len) != 0)
+		goto done;
+
+	ret = hash;
+	hash = NULL;
+done:
+	os_free(hash);
+	return ret;
+}
+
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+	if (!ctx)
+		return;
+	wc_HmacUpdate(&ctx->hmac, data, len);
+}
+
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+	int ret = 0;
+
+	if (!ctx)
+		return -2;
+
+	if (!mac || !len)
+		goto done;
+
+	if (wc_HmacFinal(&ctx->hmac, mac) != 0) {
+		ret = -1;
+		goto done;
+	}
+
+	*len = ctx->size;
+	ret = 0;
+done:
+	bin_clear_free(ctx, sizeof(*ctx));
+	if (TEST_FAIL())
+		return -1;
+	return ret;
+}
+
+#endif
+
+
+int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	Cmac cmac;
+	size_t i;
+	word32 sz;
+
+	if (TEST_FAIL())
+		return -1;
+
+	if (wc_InitCmac(&cmac, key, key_len, WC_CMAC_AES, NULL) != 0)
+		return -1;
+
+	for (i = 0; i < num_elem; i++)
+		if (wc_CmacUpdate(&cmac, addr[i], len[i]) != 0)
+			return -1;
+
+	sz = AES_BLOCK_SIZE;
+	if (wc_CmacFinal(&cmac, mac, &sz) != 0 || sz != AES_BLOCK_SIZE)
+		return -1;
+
+	return 0;
+}
+
+
+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
+			 const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
+}
+
+
+int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+	return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
+}
+
+
+int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+	return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
+}
+
+
+struct crypto_bignum * crypto_bignum_init(void)
+{
+	mp_int *a;
+
+	if (TEST_FAIL())
+		return NULL;
+
+	a = os_malloc(sizeof(*a));
+	if (!a || mp_init(a) != MP_OKAY) {
+		os_free(a);
+		a = NULL;
+	}
+
+	return (struct crypto_bignum *) a;
+}
+
+
+struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len)
+{
+	mp_int *a;
+
+	if (TEST_FAIL())
+		return NULL;
+
+	a = (mp_int *) crypto_bignum_init();
+	if (!a)
+		return NULL;
+
+	if (mp_read_unsigned_bin(a, buf, len) != MP_OKAY) {
+		os_free(a);
+		a = NULL;
+	}
+
+	return (struct crypto_bignum *) a;
+}
+
+
+struct crypto_bignum * crypto_bignum_init_uint(unsigned int val)
+{
+	mp_int *a;
+
+	if (TEST_FAIL())
+		return NULL;
+
+	a = (mp_int *) crypto_bignum_init();
+	if (!a)
+		return NULL;
+
+	if (mp_set_int(a, val) != MP_OKAY) {
+		os_free(a);
+		a = NULL;
+	}
+
+	return (struct crypto_bignum *) a;
+}
+
+
+void crypto_bignum_deinit(struct crypto_bignum *n, int clear)
+{
+	if (!n)
+		return;
+
+	if (clear)
+		mp_forcezero((mp_int *) n);
+	mp_clear((mp_int *) n);
+	os_free((mp_int *) n);
+}
+
+
+int crypto_bignum_to_bin(const struct crypto_bignum *a,
+			 u8 *buf, size_t buflen, size_t padlen)
+{
+	int num_bytes, offset;
+
+	if (TEST_FAIL())
+		return -1;
+
+	if (padlen > buflen)
+		return -1;
+
+	num_bytes = (mp_count_bits((mp_int *) a) + 7) / 8;
+	if ((size_t) num_bytes > buflen)
+		return -1;
+	if (padlen > (size_t) num_bytes)
+		offset = padlen - num_bytes;
+	else
+		offset = 0;
+
+	os_memset(buf, 0, offset);
+	mp_to_unsigned_bin((mp_int *) a, buf + offset);
+
+	return num_bytes + offset;
+}
+
+
+int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
+{
+	int ret = 0;
+	WC_RNG rng;
+	size_t len;
+	u8 *buf;
+
+	if (TEST_FAIL())
+		return -1;
+	if (wc_InitRng(&rng) != 0)
+		return -1;
+	len = (mp_count_bits((mp_int *) m) + 7) / 8;
+	buf = os_malloc(len);
+	if (!buf || wc_RNG_GenerateBlock(&rng, buf, len) != 0 ||
+	    mp_read_unsigned_bin((mp_int *) r, buf, len) != MP_OKAY ||
+	    mp_mod((mp_int *) r, (mp_int *) m, (mp_int *) r) != 0)
+		ret = -1;
+	wc_FreeRng(&rng);
+	bin_clear_free(buf, len);
+	return ret;
+}
+
+
+int crypto_bignum_add(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *r)
+{
+	return mp_add((mp_int *) a, (mp_int *) b,
+		      (mp_int *) r) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_bignum_mod(const struct crypto_bignum *a,
+		      const struct crypto_bignum *m,
+		      struct crypto_bignum *r)
+{
+	return mp_mod((mp_int *) a, (mp_int *) m,
+		      (mp_int *) r) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_bignum_exptmod(const struct crypto_bignum *b,
+			  const struct crypto_bignum *e,
+			  const struct crypto_bignum *m,
+			  struct crypto_bignum *r)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	return mp_exptmod((mp_int *) b, (mp_int *) e, (mp_int *) m,
+			  (mp_int *) r) == MP_OKAY ?  0 : -1;
+}
+
+
+int crypto_bignum_inverse(const struct crypto_bignum *a,
+			  const struct crypto_bignum *m,
+			  struct crypto_bignum *r)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	return mp_invmod((mp_int *) a, (mp_int *) m,
+			 (mp_int *) r) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_bignum_sub(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *r)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	return mp_sub((mp_int *) a, (mp_int *) b,
+		      (mp_int *) r) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_bignum_div(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *d)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	return mp_div((mp_int *) a, (mp_int *) b, (mp_int *) d,
+		      NULL) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_bignum_addmod(const struct crypto_bignum *a,
+			 const struct crypto_bignum *b,
+			 const struct crypto_bignum *c,
+			 struct crypto_bignum *d)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	return mp_addmod((mp_int *) a, (mp_int *) b, (mp_int *) c,
+			 (mp_int *) d) == MP_OKAY ?  0 : -1;
+}
+
+
+int crypto_bignum_mulmod(const struct crypto_bignum *a,
+			 const struct crypto_bignum *b,
+			 const struct crypto_bignum *m,
+			 struct crypto_bignum *d)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	return mp_mulmod((mp_int *) a, (mp_int *) b, (mp_int *) m,
+			 (mp_int *) d) == MP_OKAY ?  0 : -1;
+}
+
+
+int crypto_bignum_sqrmod(const struct crypto_bignum *a,
+			 const struct crypto_bignum *b,
+			 struct crypto_bignum *c)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	return mp_sqrmod((mp_int *) a, (mp_int *) b,
+			 (mp_int *) c) == MP_OKAY ?  0 : -1;
+}
+
+
+int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
+			 struct crypto_bignum *r)
+{
+	if (mp_copy((mp_int *) a, (mp_int *) r) != MP_OKAY)
+		return -1;
+	mp_rshb((mp_int *) r, n);
+	return 0;
+}
+
+
+int crypto_bignum_cmp(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b)
+{
+	return mp_cmp((mp_int *) a, (mp_int *) b);
+}
+
+
+int crypto_bignum_is_zero(const struct crypto_bignum *a)
+{
+	return mp_iszero((mp_int *) a);
+}
+
+
+int crypto_bignum_is_one(const struct crypto_bignum *a)
+{
+	return mp_isone((const mp_int *) a);
+}
+
+int crypto_bignum_is_odd(const struct crypto_bignum *a)
+{
+	return mp_isodd((mp_int *) a);
+}
+
+
+int crypto_bignum_legendre(const struct crypto_bignum *a,
+			   const struct crypto_bignum *p)
+{
+	mp_int t;
+	int ret;
+	int res = -2;
+
+	if (TEST_FAIL())
+		return -2;
+
+	if (mp_init(&t) != MP_OKAY)
+		return -2;
+
+	/* t = (p-1) / 2 */
+	ret = mp_sub_d((mp_int *) p, 1, &t);
+	if (ret == MP_OKAY)
+		mp_rshb(&t, 1);
+	if (ret == MP_OKAY)
+		ret = mp_exptmod((mp_int *) a, &t, (mp_int *) p, &t);
+	if (ret == MP_OKAY) {
+		if (mp_isone(&t))
+			res = 1;
+		else if (mp_iszero(&t))
+			res = 0;
+		else
+			res = -1;
+	}
+
+	mp_clear(&t);
+	return res;
+}
+
+
+#ifdef CONFIG_ECC
+
+static int crypto_ec_group_2_id(int group)
+{
+	switch (group) {
+	case 19:
+		return ECC_SECP256R1;
+	case 20:
+		return ECC_SECP384R1;
+	case 21:
+		return ECC_SECP521R1;
+	case 25:
+		return ECC_SECP192R1;
+	case 26:
+		return ECC_SECP224R1;
+#ifdef HAVE_ECC_BRAINPOOL
+	case 27:
+		return ECC_BRAINPOOLP224R1;
+	case 28:
+		return ECC_BRAINPOOLP256R1;
+	case 29:
+		return ECC_BRAINPOOLP384R1;
+	case 30:
+		return ECC_BRAINPOOLP512R1;
+#endif /* HAVE_ECC_BRAINPOOL */
+	default:
+		LOG_WOLF_ERROR_VA("Unsupported curve (id=%d) in EC key", group);
+		return ECC_CURVE_INVALID;
+	}
+}
+
+
+int ecc_map(ecc_point *, mp_int *, mp_digit);
+int ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R,
+			     mp_int *a, mp_int *modulus, mp_digit mp);
+
+struct crypto_ec {
+	ecc_key *key;
+#ifdef CONFIG_DPP
+	ecc_point *g; /* Only used in DPP for now */
+#endif /* CONFIG_DPP */
+	mp_int a;
+	mp_int prime;
+	mp_int order;
+	mp_digit mont_b;
+	mp_int b;
+	int curve_id;
+	bool own_key; /* Should we free the `key` */
+};
+
+
+struct crypto_ec * crypto_ec_init(int group)
+{
+	int built = 0;
+	struct crypto_ec *e;
+	int curve_id = crypto_ec_group_2_id(group);
+	int err;
+
+	if (curve_id == ECC_CURVE_INVALID) {
+		LOG_INVALID_PARAMETERS();
+		return NULL;
+	}
+
+	e = os_zalloc(sizeof(*e));
+	if (!e) {
+		LOG_WOLF_ERROR_FUNC_NULL(os_zalloc);
+		return NULL;
+	}
+
+	e->curve_id = curve_id;
+	e->own_key = true;
+	e->key = ecc_key_init();
+	if (!e->key) {
+		LOG_WOLF_ERROR_FUNC_NULL(ecc_key_init);
+		goto done;
+	}
+
+	err = wc_ecc_set_curve(e->key, 0, curve_id);
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_ecc_set_curve, err);
+		goto done;
+	}
+#ifdef CONFIG_DPP
+	e->g = wc_ecc_new_point();
+	if (!e->g) {
+		LOG_WOLF_ERROR_FUNC_NULL(wc_ecc_new_point);
+		goto done;
+	}
+#ifdef CONFIG_FIPS
+	/* Setup generator manually in FIPS mode */
+	if (!e->key->dp) {
+		LOG_WOLF_ERROR_FUNC_NULL(e->key->dp);
+		goto done;
+	}
+	err = mp_read_radix(e->g->x, e->key->dp->Gx, MP_RADIX_HEX);
+	if (err != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(mp_read_radix, err);
+		goto done;
+	}
+	err = mp_read_radix(e->g->y, e->key->dp->Gy, MP_RADIX_HEX);
+	if (err != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(mp_read_radix, err);
+		goto done;
+	}
+	err = mp_set(e->g->z, 1);
+	if (err != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(mp_set, err);
+		goto done;
+	}
+#else /* CONFIG_FIPS */
+	err = wc_ecc_get_generator(e->g, wc_ecc_get_curve_idx(curve_id));
+	if (err != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(wc_ecc_get_generator, err);
+		goto done;
+	}
+#endif /* CONFIG_FIPS */
+#endif /* CONFIG_DPP */
+	err = mp_init_multi(&e->a, &e->prime, &e->order, &e->b, NULL, NULL);
+	if (err != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(mp_init_multi, err);
+		goto done;
+	}
+	err = mp_read_radix(&e->a, e->key->dp->Af, 16);
+	if (err != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(mp_read_radix, err);
+		goto done;
+	}
+	err = mp_read_radix(&e->b, e->key->dp->Bf, 16);
+	if (err != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(mp_read_radix, err);
+		goto done;
+	}
+	err = mp_read_radix(&e->prime, e->key->dp->prime, 16);
+	if (err != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(mp_read_radix, err);
+		goto done;
+	}
+	err = mp_read_radix(&e->order, e->key->dp->order, 16);
+	if (err != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(mp_read_radix, err);
+		goto done;
+	}
+	err = mp_montgomery_setup(&e->prime, &e->mont_b);
+	if (err != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(mp_montgomery_setup, err);
+		goto done;
+	}
+
+	built = 1;
+done:
+	if (!built) {
+		crypto_ec_deinit(e);
+		e = NULL;
+	}
+	return e;
+}
+
+
+void crypto_ec_deinit(struct crypto_ec* e)
+{
+	if (!e)
+		return;
+
+	mp_clear(&e->b);
+	mp_clear(&e->order);
+	mp_clear(&e->prime);
+	mp_clear(&e->a);
+#ifdef CONFIG_DPP
+	wc_ecc_del_point(e->g);
+#endif /* CONFIG_DPP */
+	if (e->own_key)
+		ecc_key_deinit(e->key);
+	os_free(e);
+}
+
+
+struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e)
+{
+	if (TEST_FAIL())
+		return NULL;
+	if (!e)
+		return NULL;
+	return (struct crypto_ec_point *) wc_ecc_new_point();
+}
+
+
+size_t crypto_ec_prime_len(struct crypto_ec *e)
+{
+	return (mp_count_bits(&e->prime) + 7) / 8;
+}
+
+
+size_t crypto_ec_prime_len_bits(struct crypto_ec *e)
+{
+	return mp_count_bits(&e->prime);
+}
+
+
+size_t crypto_ec_order_len(struct crypto_ec *e)
+{
+	return (mp_count_bits(&e->order) + 7) / 8;
+}
+
+
+const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e)
+{
+	return (const struct crypto_bignum *) &e->prime;
+}
+
+
+const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e)
+{
+	return (const struct crypto_bignum *) &e->order;
+}
+
+
+const struct crypto_bignum * crypto_ec_get_a(struct crypto_ec *e)
+{
+	return (const struct crypto_bignum *) &e->a;
+}
+
+
+const struct crypto_bignum * crypto_ec_get_b(struct crypto_ec *e)
+{
+	return (const struct crypto_bignum *) &e->b;
+}
+
+
+void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear)
+{
+	ecc_point *point = (ecc_point *) p;
+
+	if (!p)
+		return;
+
+	if (clear) {
+#ifdef CONFIG_FIPS
+		mp_forcezero(point->x);
+		mp_forcezero(point->y);
+		mp_forcezero(point->z);
+#else /* CONFIG_FIPS */
+		wc_ecc_forcezero_point(point);
+#endif /* CONFIG_FIPS */
+	}
+	wc_ecc_del_point(point);
+}
+
+
+#ifdef CONFIG_DPP
+const struct crypto_ec_point * crypto_ec_get_generator(struct crypto_ec *e)
+{
+	return (const struct crypto_ec_point *) e->g;
+}
+#endif /* CONFIG_DPP */
+
+
+int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
+		      struct crypto_bignum *x)
+{
+	return mp_copy(((ecc_point *) p)->x, (mp_int *) x) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_ec_point_to_bin(struct crypto_ec *e,
+			   const struct crypto_ec_point *point, u8 *x, u8 *y)
+{
+	ecc_point *p = (ecc_point *) point;
+	int len;
+	int err;
+
+	if (TEST_FAIL())
+		return -1;
+
+	if (!mp_isone(p->z)) {
+		err = ecc_map(p, &e->prime, e->mont_b);
+		if (err != MP_OKAY) {
+			LOG_WOLF_ERROR_FUNC(ecc_map, err);
+			return -1;
+		}
+	}
+
+	len = wc_ecc_get_curve_size_from_id(e->curve_id);
+	if (len <= 0) {
+		LOG_WOLF_ERROR_FUNC(wc_ecc_get_curve_size_from_id, len);
+		LOG_WOLF_ERROR_VA("wc_ecc_get_curve_size_from_id error for curve_id %d", e->curve_id);
+		return -1;
+	}
+
+	if (x) {
+		if (crypto_bignum_to_bin((struct crypto_bignum *)p->x, x,
+					 (size_t) len, (size_t) len) <= 0) {
+			LOG_WOLF_ERROR_FUNC(crypto_bignum_to_bin, -1);
+			return -1;
+		}
+	}
+
+	if (y) {
+		if (crypto_bignum_to_bin((struct crypto_bignum *) p->y, y,
+					 (size_t) len, (size_t) len) <= 0) {
+			LOG_WOLF_ERROR_FUNC(crypto_bignum_to_bin, -1);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
+						  const u8 *val)
+{
+	ecc_point *point = NULL;
+	int loaded = 0;
+
+	if (TEST_FAIL())
+		return NULL;
+
+	point = wc_ecc_new_point();
+	if (!point)
+		goto done;
+
+	if (mp_read_unsigned_bin(point->x, val, e->key->dp->size) != MP_OKAY)
+		goto done;
+	val += e->key->dp->size;
+	if (mp_read_unsigned_bin(point->y, val, e->key->dp->size) != MP_OKAY)
+		goto done;
+	mp_set(point->z, 1);
+
+	loaded = 1;
+done:
+	if (!loaded) {
+		wc_ecc_del_point(point);
+		point = NULL;
+	}
+	return (struct crypto_ec_point *) point;
+}
+
+
+int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
+			const struct crypto_ec_point *b,
+			struct crypto_ec_point *c)
+{
+	mp_int mu;
+	ecc_point *ta = NULL, *tb = NULL;
+	ecc_point *pa = (ecc_point *) a, *pb = (ecc_point *) b;
+	mp_int *modulus = &e->prime;
+	int ret;
+
+	if (TEST_FAIL())
+		return -1;
+
+	ret = mp_init(&mu);
+	if (ret != MP_OKAY)
+		return -1;
+
+	ret = mp_montgomery_calc_normalization(&mu, modulus);
+	if (ret != MP_OKAY) {
+		mp_clear(&mu);
+		return -1;
+	}
+
+	if (!mp_isone(&mu)) {
+		ta = wc_ecc_new_point();
+		if (!ta) {
+			mp_clear(&mu);
+			return -1;
+		}
+		tb = wc_ecc_new_point();
+		if (!tb) {
+			wc_ecc_del_point(ta);
+			mp_clear(&mu);
+			return -1;
+		}
+
+		if (mp_mulmod(pa->x, &mu, modulus, ta->x) != MP_OKAY ||
+		    mp_mulmod(pa->y, &mu, modulus, ta->y) != MP_OKAY ||
+		    mp_mulmod(pa->z, &mu, modulus, ta->z) != MP_OKAY ||
+		    mp_mulmod(pb->x, &mu, modulus, tb->x) != MP_OKAY ||
+		    mp_mulmod(pb->y, &mu, modulus, tb->y) != MP_OKAY ||
+		    mp_mulmod(pb->z, &mu, modulus, tb->z) != MP_OKAY) {
+			ret = -1;
+			goto end;
+		}
+		pa = ta;
+		pb = tb;
+	}
+
+	ret = ecc_projective_add_point(pa, pb, (ecc_point *) c, &e->a,
+				       &e->prime, e->mont_b);
+	if (ret != 0) {
+		ret = -1;
+		goto end;
+	}
+
+	if (ecc_map((ecc_point *) c, &e->prime, e->mont_b) != MP_OKAY)
+		ret = -1;
+	else
+		ret = 0;
+end:
+	wc_ecc_del_point(tb);
+	wc_ecc_del_point(ta);
+	mp_clear(&mu);
+	return ret;
+}
+
+
+int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
+			const struct crypto_bignum *b,
+			struct crypto_ec_point *res)
+{
+	int ret;
+
+	if (TEST_FAIL())
+		return -1;
+
+	ret = wc_ecc_mulmod((mp_int *) b, (ecc_point *) p, (ecc_point *) res,
+			    &e->a, &e->prime, 1);
+	return ret == 0 ? 0 : -1;
+}
+
+
+int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
+{
+	ecc_point *point = (ecc_point *) p;
+
+	if (TEST_FAIL())
+		return -1;
+
+	if (mp_sub(&e->prime, point->y, point->y) != MP_OKAY)
+		return -1;
+
+	return 0;
+}
+
+
+struct crypto_bignum *
+crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
+			      const struct crypto_bignum *x)
+{
+	mp_int *y2;
+
+	if (TEST_FAIL())
+		return NULL;
+
+	/* y^2 = x^3 + ax + b = (x^2 + a)x + b */
+	y2 = (mp_int *) crypto_bignum_init();
+	if (!y2 ||
+	    mp_sqrmod((mp_int *) x, &e->prime, y2) != 0 ||
+	    mp_addmod(y2, &e->a, &e->prime, y2) != 0 ||
+	    mp_mulmod((mp_int *) x, y2, &e->prime, y2) != 0 ||
+	    mp_addmod(y2, &e->b, &e->prime, y2) != 0) {
+		mp_clear(y2);
+		os_free(y2);
+		y2 = NULL;
+	}
+
+	return (struct crypto_bignum *) y2;
+}
+
+
+int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
+				   const struct crypto_ec_point *p)
+{
+	return wc_ecc_point_is_at_infinity((ecc_point *) p);
+}
+
+
+int crypto_ec_point_is_on_curve(struct crypto_ec *e,
+				const struct crypto_ec_point *p)
+{
+	return wc_ecc_is_point((ecc_point *) p, &e->a, &e->b, &e->prime) ==
+		MP_OKAY;
+}
+
+
+int crypto_ec_point_cmp(const struct crypto_ec *e,
+			const struct crypto_ec_point *a,
+			const struct crypto_ec_point *b)
+{
+	return wc_ecc_cmp_point((ecc_point *) a, (ecc_point *) b);
+}
+
+struct crypto_ec_key {
+	ecc_key *eckey;
+	WC_RNG *rng; /* Needs to be initialized before use.
+		      * *NOT* initialized in crypto_ec_key_init */
+};
+
+
+struct crypto_ecdh {
+	struct crypto_ec *ec;
+	WC_RNG *rng;
+};
+
+static struct crypto_ecdh * _crypto_ecdh_init(int group)
+{
+	struct crypto_ecdh *ecdh = NULL;
+#if defined(ECC_TIMING_RESISTANT) && !defined(WOLFSSL_OLD_FIPS)
+	int ret;
+#endif /* ECC_TIMING_RESISTANT && !WOLFSSL_OLD_FIPS */
+
+	ecdh = os_zalloc(sizeof(*ecdh));
+	if (!ecdh) {
+		LOG_WOLF_ERROR_FUNC_NULL(os_zalloc);
+		return NULL;
+	}
+
+	ecdh->rng = wc_rng_init();
+	if (!ecdh->rng) {
+		LOG_WOLF_ERROR_FUNC_NULL(wc_rng_init);
+		goto fail;
+	}
+
+	ecdh->ec = crypto_ec_init(group);
+	if (!ecdh->ec) {
+		LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_init);
+		goto fail;
+	}
+
+#if defined(ECC_TIMING_RESISTANT) && !defined(WOLFSSL_OLD_FIPS)
+	ret = wc_ecc_set_rng(ecdh->ec->key, ecdh->rng);
+	if (ret != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_ecc_set_rng, ret);
+		goto fail;
+	}
+#endif /* ECC_TIMING_RESISTANT && !WOLFSSL_OLD_FIPS */
+
+	return ecdh;
+fail:
+	crypto_ecdh_deinit(ecdh);
+	return NULL;
+}
+
+
+struct crypto_ecdh * crypto_ecdh_init(int group)
+{
+	struct crypto_ecdh *ret = NULL;
+	int err;
+
+	ret = _crypto_ecdh_init(group);
+
+	if (!ret) {
+		LOG_WOLF_ERROR_FUNC_NULL(_crypto_ecdh_init);
+		return NULL;
+	}
+
+	err = wc_ecc_make_key_ex(ret->rng, 0, ret->ec->key,
+				 crypto_ec_group_2_id(group));
+	if (err != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(wc_ecc_make_key_ex, err);
+		crypto_ecdh_deinit(ret);
+		ret = NULL;
+	}
+
+	return ret;
+}
+
+
+struct crypto_ecdh * crypto_ecdh_init2(int group, struct crypto_ec_key *own_key)
+{
+	struct crypto_ecdh *ret = NULL;
+
+	if (!own_key || crypto_ec_key_group(own_key) != group) {
+		LOG_INVALID_PARAMETERS();
+		return NULL;
+	}
+
+	ret = _crypto_ecdh_init(group);
+	if (ret) {
+		/* Already init'ed to the right group. Enough to substitute the
+		 * key. */
+		ecc_key_deinit(ret->ec->key);
+		ret->ec->key = own_key->eckey;
+		ret->ec->own_key = false;
+#if defined(ECC_TIMING_RESISTANT) && !defined(WOLFSSL_OLD_FIPS)
+		if (!ret->ec->key->rng) {
+			int err = wc_ecc_set_rng(ret->ec->key, ret->rng);
+
+			if (err != 0)
+				LOG_WOLF_ERROR_FUNC(wc_ecc_set_rng, err);
+		}
+#endif /* ECC_TIMING_RESISTANT && !CONFIG_FIPS */
+	}
+
+	return ret;
+}
+
+
+void crypto_ecdh_deinit(struct crypto_ecdh *ecdh)
+{
+	if (ecdh) {
+#if defined(ECC_TIMING_RESISTANT) && !defined(WOLFSSL_OLD_FIPS)
+		/* Disassociate the rng */
+		if (ecdh->ec && ecdh->ec->key &&
+		    ecdh->ec->key->rng == ecdh->rng)
+			(void) wc_ecc_set_rng(ecdh->ec->key, NULL);
+#endif /* ECC_TIMING_RESISTANT && !WOLFSSL_OLD_FIPS */
+		crypto_ec_deinit(ecdh->ec);
+		wc_rng_deinit(ecdh->rng);
+		os_free(ecdh);
+	}
+}
+
+
+struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y)
+{
+	struct wpabuf *buf = NULL;
+	int ret;
+	int len = ecdh->ec->key->dp->size;
+
+	buf = wpabuf_alloc(inc_y ? 2 * len : len);
+	if (!buf)
+		goto fail;
+
+	ret = crypto_bignum_to_bin((struct crypto_bignum *)
+				   ecdh->ec->key->pubkey.x, wpabuf_put(buf, len),
+				   len, len);
+	if (ret < 0)
+		goto fail;
+	if (inc_y) {
+		ret = crypto_bignum_to_bin((struct crypto_bignum *)
+					   ecdh->ec->key->pubkey.y,
+					   wpabuf_put(buf, len), len, len);
+		if (ret < 0)
+			goto fail;
+	}
+
+done:
+	return buf;
+fail:
+	wpabuf_free(buf);
+	buf = NULL;
+	goto done;
+}
+
+
+struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
+					const u8 *key, size_t len)
+{
+	int ret;
+	struct wpabuf *pubkey = NULL;
+	struct wpabuf *secret = NULL;
+	word32 key_len = ecdh->ec->key->dp->size;
+	ecc_point *point = NULL;
+	size_t need_key_len = inc_y ? 2 * key_len : key_len;
+
+	if (len < need_key_len) {
+		LOG_WOLF_ERROR("key len too small");
+		goto fail;
+	}
+	pubkey = wpabuf_alloc(1 + 2 * key_len);
+	if (!pubkey) {
+		LOG_WOLF_ERROR_FUNC_NULL(wpabuf_alloc);
+		goto fail;
+	}
+	wpabuf_put_u8(pubkey, inc_y ? ECC_POINT_UNCOMP : ECC_POINT_COMP_EVEN);
+	wpabuf_put_data(pubkey, key, need_key_len);
+
+	point = wc_ecc_new_point();
+	if (!point) {
+		LOG_WOLF_ERROR_FUNC_NULL(wc_ecc_new_point);
+		goto fail;
+	}
+
+	ret = wc_ecc_import_point_der(wpabuf_mhead(pubkey), 1 + 2 * key_len,
+				      ecdh->ec->key->idx, point);
+	if (ret != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(wc_ecc_import_point_der, ret);
+		goto fail;
+	}
+
+	secret = wpabuf_alloc(key_len);
+	if (!secret) {
+		LOG_WOLF_ERROR_FUNC_NULL(wpabuf_alloc);
+		goto fail;
+	}
+
+	ret = wc_ecc_shared_secret_ex(ecdh->ec->key, point,
+				      wpabuf_put(secret, key_len), &key_len);
+	if (ret != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(wc_ecc_shared_secret_ex, ret);
+		goto fail;
+	}
+
+done:
+	wc_ecc_del_point(point);
+	wpabuf_free(pubkey);
+	return secret;
+fail:
+	wpabuf_free(secret);
+	secret = NULL;
+	goto done;
+}
+
+
+size_t crypto_ecdh_prime_len(struct crypto_ecdh *ecdh)
+{
+	return crypto_ec_prime_len(ecdh->ec);
+}
+
+static struct crypto_ec_key * crypto_ec_key_init(void)
+{
+	struct crypto_ec_key *key;
+
+	key = os_zalloc(sizeof(struct crypto_ec_key));
+	if (key) {
+		key->eckey = ecc_key_init();
+		/* Omit key->rng initialization because it seeds itself and thus
+		 * consumes entropy that may never be used. Lazy initialize when
+		 * necessary. */
+		if (!key->eckey) {
+			LOG_WOLF_ERROR_FUNC_NULL(ecc_key_init);
+			crypto_ec_key_deinit(key);
+			key = NULL;
+		}
+	}
+	return key;
+}
+
+
+void crypto_ec_key_deinit(struct crypto_ec_key *key)
+{
+	if (key) {
+		ecc_key_deinit(key->eckey);
+		wc_rng_deinit(key->rng);
+		os_free(key);
+	}
+}
+
+
+static WC_RNG * crypto_ec_key_init_rng(struct crypto_ec_key *key)
+{
+	if (!key->rng) {
+		/* Lazy init key->rng */
+		key->rng = wc_rng_init();
+		if (!key->rng)
+			LOG_WOLF_ERROR_FUNC_NULL(wc_rng_init);
+	}
+	return key->rng;
+}
+
+
+struct crypto_ec_key * crypto_ec_key_parse_priv(const u8 *der, size_t der_len)
+{
+	struct crypto_ec_key *ret;
+	word32 idx = 0;
+	int err;
+
+	ret = crypto_ec_key_init();
+	if (!ret) {
+		LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_key_init);
+		goto fail;
+	}
+
+	err = wc_EccPrivateKeyDecode(der, &idx, ret->eckey, (word32) der_len);
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_EccPrivateKeyDecode, err);
+		goto fail;
+	}
+
+	return ret;
+fail:
+	if (ret)
+		crypto_ec_key_deinit(ret);
+	return NULL;
+}
+
+
+int crypto_ec_key_group(struct crypto_ec_key *key)
+{
+
+	if (!key || !key->eckey || !key->eckey->dp) {
+		LOG_INVALID_PARAMETERS();
+		return -1;
+	}
+
+	switch (key->eckey->dp->id) {
+	case ECC_SECP256R1:
+		return 19;
+	case ECC_SECP384R1:
+		return 20;
+	case ECC_SECP521R1:
+		return 21;
+	case ECC_SECP192R1:
+		return 25;
+	case ECC_SECP224R1:
+		return 26;
+#ifdef HAVE_ECC_BRAINPOOL
+	case ECC_BRAINPOOLP224R1:
+		return 27;
+	case ECC_BRAINPOOLP256R1:
+		return 28;
+	case ECC_BRAINPOOLP384R1:
+		return 29;
+	case ECC_BRAINPOOLP512R1:
+		return 30;
+#endif /* HAVE_ECC_BRAINPOOL */
+	}
+
+	LOG_WOLF_ERROR_VA("Unsupported curve (id=%d) in EC key",
+			  key->eckey->dp->id);
+	return -1;
+}
+
+
+static int crypto_ec_key_gen_public_key(struct crypto_ec_key *key)
+{
+	int err;
+
+#ifdef WOLFSSL_OLD_FIPS
+	err = wc_ecc_make_pub(key->eckey, NULL);
+#else /* WOLFSSL_OLD_FIPS */
+	/* Have wolfSSL generate the public key to make it available for output
+	 */
+	if (!crypto_ec_key_init_rng(key)) {
+		LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_key_init_rng);
+		return -1;
+	}
+
+	err = wc_ecc_make_pub_ex(key->eckey, NULL, key->rng);
+#endif /* WOLFSSL_OLD_FIPS */
+
+	if (err != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(wc_ecc_make_pub_ex, err);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key)
+{
+	int der_len;
+	struct wpabuf *ret = NULL;
+	int err;
+
+	if (!key || !key->eckey) {
+		LOG_INVALID_PARAMETERS();
+		goto fail;
+	}
+
+#ifdef WOLFSSL_OLD_FIPS
+	if (key->eckey->type == ECC_PRIVATEKEY_ONLY &&
+	    crypto_ec_key_gen_public_key(key) != 0) {
+		LOG_WOLF_ERROR_FUNC(crypto_ec_key_gen_public_key, -1);
+		goto fail;
+	}
+#endif /* WOLFSSL_OLD_FIPS */
+
+	der_len = err = wc_EccPublicKeyToDer_ex(key->eckey, NULL, 0, 1, 1);
+	if (err == ECC_PRIVATEONLY_E) {
+		if (crypto_ec_key_gen_public_key(key) != 0) {
+			LOG_WOLF_ERROR_FUNC(crypto_ec_key_gen_public_key, -1);
+			goto fail;
+		}
+		der_len = err = wc_EccPublicKeyToDer_ex(key->eckey, NULL, 0, 1,
+							1);
+	}
+	if (err <= 0) {
+		LOG_WOLF_ERROR_FUNC(wc_EccPublicKeyDerSize, err);
+		goto fail;
+	}
+
+	ret = wpabuf_alloc(der_len);
+	if (!ret) {
+		LOG_WOLF_ERROR_FUNC_NULL(wpabuf_alloc);
+		goto fail;
+	}
+
+	err = wc_EccPublicKeyToDer_ex(key->eckey, wpabuf_mhead(ret), der_len, 1,
+				      1);
+	if (err == ECC_PRIVATEONLY_E) {
+		if (crypto_ec_key_gen_public_key(key) != 0) {
+			LOG_WOLF_ERROR_FUNC(crypto_ec_key_gen_public_key, -1);
+			goto fail;
+		}
+		err = wc_EccPublicKeyToDer_ex(key->eckey, wpabuf_mhead(ret),
+					      der_len, 1, 1);
+	}
+	if (err <= 0) {
+		LOG_WOLF_ERROR_FUNC(wc_EccPublicKeyToDer, err);
+		goto fail;
+	}
+	der_len = err;
+	wpabuf_put(ret, der_len);
+
+	return ret;
+
+fail:
+	wpabuf_free(ret);
+	return NULL;
+}
+
+
+struct crypto_ec_key * crypto_ec_key_parse_pub(const u8 *der, size_t der_len)
+{
+	word32 idx = 0;
+	struct crypto_ec_key *ret = NULL;
+	int err;
+
+	ret = crypto_ec_key_init();
+	if (!ret) {
+		LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_key_init);
+		goto fail;
+	}
+
+	err = wc_EccPublicKeyDecode(der, &idx, ret->eckey, (word32) der_len);
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_EccPublicKeyDecode, err);
+		goto fail;
+	}
+
+	return ret;
+fail:
+	crypto_ec_key_deinit(ret);
+	return NULL;
+}
+
+
+struct wpabuf * crypto_ec_key_sign(struct crypto_ec_key *key, const u8 *data,
+				   size_t len)
+{
+	int der_len;
+	int err;
+	word32 w32_der_len;
+	struct wpabuf *ret = NULL;
+
+	if (!key || !key->eckey || !data || len == 0) {
+		LOG_INVALID_PARAMETERS();
+		goto fail;
+	}
+
+	if (!crypto_ec_key_init_rng(key)) {
+		LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_key_init_rng);
+		goto fail;
+	}
+
+	der_len = wc_ecc_sig_size(key->eckey);
+	if (der_len <= 0) {
+		LOG_WOLF_ERROR_FUNC(wc_ecc_sig_size, der_len);
+		goto fail;
+	}
+
+	ret = wpabuf_alloc(der_len);
+	if (!ret) {
+		LOG_WOLF_ERROR_FUNC_NULL(wpabuf_alloc);
+		goto fail;
+	}
+
+	w32_der_len = (word32) der_len;
+	err = wc_ecc_sign_hash(data, len, wpabuf_mhead(ret), &w32_der_len,
+			       key->rng, key->eckey);
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_ecc_sign_hash, err);
+		goto fail;
+	}
+	wpabuf_put(ret, w32_der_len);
+
+	return ret;
+fail:
+	wpabuf_free(ret);
+	return NULL;
+}
+
+
+int crypto_ec_key_verify_signature(struct crypto_ec_key *key, const u8 *data,
+				   size_t len, const u8 *sig, size_t sig_len)
+{
+	int res = 0;
+
+	if (!key || !key->eckey || !data || len == 0 || !sig || sig_len == 0) {
+		LOG_INVALID_PARAMETERS();
+		return -1;
+	}
+
+	if (wc_ecc_verify_hash(sig, sig_len, data, len, &res, key->eckey) != 0)
+	{
+		LOG_WOLF_ERROR("wc_ecc_verify_hash failed");
+		return -1;
+	}
+
+	if (res != 1)
+		LOG_WOLF_ERROR("crypto_ec_key_verify_signature failed");
+
+	return res;
+}
+
+#endif /* CONFIG_ECC */
+
+#ifdef CONFIG_DPP
+
+struct wpabuf * crypto_ec_key_get_ecprivate_key(struct crypto_ec_key *key,
+						bool include_pub)
+{
+	int len;
+	int err;
+	struct wpabuf *ret = NULL;
+
+	if (!key || !key->eckey) {
+		LOG_INVALID_PARAMETERS();
+		return NULL;
+	}
+
+#ifdef WOLFSSL_OLD_FIPS
+	if (key->eckey->type != ECC_PRIVATEKEY &&
+	    key->eckey->type != ECC_PRIVATEKEY_ONLY) {
+		LOG_INVALID_PARAMETERS();
+		return NULL;
+	}
+#endif /* WOLFSSL_OLD_FIPS */
+
+	len = err = wc_EccKeyDerSize(key->eckey, include_pub);
+	if (err == ECC_PRIVATEONLY_E && include_pub) {
+		if (crypto_ec_key_gen_public_key(key) != 0) {
+			LOG_WOLF_ERROR_FUNC(crypto_ec_key_gen_public_key, -1);
+			return NULL;
+		}
+		len = err = wc_EccKeyDerSize(key->eckey, include_pub);
+	}
+	if (err <= 0) {
+		/* Exception for BAD_FUNC_ARG because higher levels blindly call
+		 * this function to determine if this is a private key or not.
+		 * BAD_FUNC_ARG most probably means that key->eckey is a public
+		 * key not private. */
+		if (err != BAD_FUNC_ARG)
+			LOG_WOLF_ERROR_FUNC(wc_EccKeyDerSize, err);
+		return NULL;
+	}
+
+	ret = wpabuf_alloc(len);
+	if (!ret) {
+		LOG_WOLF_ERROR_FUNC_NULL(wpabuf_alloc);
+		return NULL;
+	}
+
+	if (include_pub)
+		err = wc_EccKeyToDer(key->eckey, wpabuf_put(ret, len), len);
+	else
+		err = wc_EccPrivateKeyToDer(key->eckey, wpabuf_put(ret, len),
+					    len);
+
+	if (err != len) {
+		LOG_WOLF_ERROR_VA("%s failed with err: %d", include_pub ?
+				  "wc_EccKeyToDer" : "wc_EccPrivateKeyToDer",
+				  err);
+		wpabuf_free(ret);
+		ret = NULL;
+	}
+
+	return ret;
+}
+
+
+struct wpabuf * crypto_ec_key_get_pubkey_point(struct crypto_ec_key *key,
+					       int prefix)
+{
+	int err;
+	word32 len = 0;
+	struct wpabuf *ret = NULL;
+
+	if (!key || !key->eckey) {
+		LOG_INVALID_PARAMETERS();
+		return NULL;
+	}
+
+	err = wc_ecc_export_x963(key->eckey, NULL, &len);
+	if (err != LENGTH_ONLY_E) {
+		LOG_WOLF_ERROR_FUNC(wc_ecc_export_x963, err);
+		goto fail;
+	}
+
+	ret = wpabuf_alloc(len);
+	if (!ret) {
+		LOG_WOLF_ERROR_FUNC_NULL(wpabuf_alloc);
+		goto fail;
+	}
+
+	err = wc_ecc_export_x963(key->eckey, wpabuf_mhead(ret), &len);
+	if (err == ECC_PRIVATEONLY_E) {
+		if (crypto_ec_key_gen_public_key(key) != 0) {
+			LOG_WOLF_ERROR_FUNC(crypto_ec_key_gen_public_key, -1);
+			goto fail;
+		}
+		err = wc_ecc_export_x963(key->eckey, wpabuf_mhead(ret), &len);
+	}
+	if (err != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(wc_ecc_export_x963, err);
+		goto fail;
+	}
+
+	if (!prefix)
+		os_memmove(wpabuf_mhead(ret), wpabuf_mhead_u8(ret) + 1,
+			   (size_t)--len);
+	wpabuf_put(ret, len);
+
+	return ret;
+
+fail:
+	wpabuf_free(ret);
+	return NULL;
+}
+
+
+struct crypto_ec_key * crypto_ec_key_set_pub(int group, const u8 *x,
+					     const u8 *y, size_t len)
+{
+	struct crypto_ec_key *ret = NULL;
+	int curve_id = crypto_ec_group_2_id(group);
+	int err;
+
+	if (!x || !y || len == 0 || curve_id == ECC_CURVE_INVALID ||
+	    wc_ecc_get_curve_size_from_id(curve_id) != (int) len) {
+		LOG_INVALID_PARAMETERS();
+		return NULL;
+	}
+
+	ret = crypto_ec_key_init();
+	if (!ret) {
+		LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_key_init);
+		return NULL;
+	}
+
+	/* Cast necessary for FIPS API */
+	err = wc_ecc_import_unsigned(ret->eckey, (u8 *) x, (u8 *) y, NULL,
+				     curve_id);
+	if (err != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(wc_ecc_import_unsigned, err);
+		crypto_ec_key_deinit(ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+
+int crypto_ec_key_cmp(struct crypto_ec_key *key1, struct crypto_ec_key *key2)
+{
+	int ret;
+	struct wpabuf *key1_buf = crypto_ec_key_get_subject_public_key(key1);
+	struct wpabuf *key2_buf = crypto_ec_key_get_subject_public_key(key2);
+
+	if ((key1 && !key1_buf) || (key2 && !key2_buf)) {
+		LOG_WOLF_ERROR("crypto_ec_key_get_subject_public_key failed");
+		return -1;
+	}
+
+	ret = wpabuf_cmp(key1_buf, key2_buf);
+	if (ret != 0)
+		ret = -1; /* Default to -1 for different keys */
+
+	wpabuf_clear_free(key1_buf);
+	wpabuf_clear_free(key2_buf);
+	return ret;
+}
+
+
+/* wolfSSL doesn't have a pretty print function for keys so just print out the
+ * PEM of the private key. */
+void crypto_ec_key_debug_print(const struct crypto_ec_key *key,
+			       const char *title)
+{
+	struct wpabuf * key_buf;
+	struct wpabuf * out = NULL;
+	int err;
+	int pem_len;
+
+	if (!key || !key->eckey) {
+		LOG_INVALID_PARAMETERS();
+		return;
+	}
+
+	if (key->eckey->type == ECC_PUBLICKEY)
+		key_buf = crypto_ec_key_get_subject_public_key(
+			(struct crypto_ec_key *) key);
+	else
+		key_buf = crypto_ec_key_get_ecprivate_key(
+			(struct crypto_ec_key *) key, 1);
+
+	if (!key_buf) {
+		LOG_WOLF_ERROR_VA("%s has returned NULL",
+				  key->eckey->type == ECC_PUBLICKEY ?
+				  "crypto_ec_key_get_subject_public_key" :
+				  "crypto_ec_key_get_ecprivate_key");
+		goto fail;
+	}
+
+	if (!title)
+		title = "";
+
+	err = wc_DerToPem(wpabuf_head(key_buf), wpabuf_len(key_buf), NULL, 0,
+			  ECC_TYPE);
+	if (err <= 0) {
+		LOG_WOLF_ERROR_FUNC(wc_DerToPem, err);
+		goto fail;
+	}
+	pem_len = err;
+
+	out = wpabuf_alloc(pem_len + 1);
+	if (!out) {
+		LOG_WOLF_ERROR_FUNC_NULL(wc_DerToPem);
+		goto fail;
+	}
+
+	err = wc_DerToPem(wpabuf_head(key_buf), wpabuf_len(key_buf),
+			  wpabuf_mhead(out), pem_len, ECC_TYPE);
+	if (err <= 0) {
+		LOG_WOLF_ERROR_FUNC(wc_DerToPem, err);
+		goto fail;
+	}
+
+	wpabuf_mhead_u8(out)[err] = '\0';
+	wpabuf_put(out, err + 1);
+	wpa_printf(MSG_DEBUG, "%s:\n%s", title,
+		   (const char *) wpabuf_head(out));
+
+fail:
+	wpabuf_clear_free(key_buf);
+	wpabuf_clear_free(out);
+}
+
+
+void crypto_ec_point_debug_print(const struct crypto_ec *e,
+				 const struct crypto_ec_point *p,
+				 const char *title)
+{
+	u8 x[ECC_MAXSIZE];
+	u8 y[ECC_MAXSIZE];
+	int coord_size;
+	int err;
+
+	if (!p || !e) {
+		LOG_INVALID_PARAMETERS();
+		return;
+	}
+
+	coord_size = e->key->dp->size;
+
+	if (!title)
+		title = "";
+
+	err = crypto_ec_point_to_bin((struct crypto_ec *)e, p, x, y);
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(crypto_ec_point_to_bin, err);
+		return;
+	}
+
+	wpa_hexdump(MSG_DEBUG, title, x, coord_size);
+	wpa_hexdump(MSG_DEBUG, title, y, coord_size);
+}
+
+
+struct crypto_ec_key * crypto_ec_key_gen(int group)
+{
+	int curve_id = crypto_ec_group_2_id(group);
+	int err;
+	struct crypto_ec_key * ret = NULL;
+
+	if (curve_id == ECC_CURVE_INVALID) {
+		LOG_INVALID_PARAMETERS();
+		return NULL;
+	}
+
+	ret = crypto_ec_key_init();
+	if (!ret) {
+		LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_key_init);
+		return NULL;
+	}
+
+	if (!crypto_ec_key_init_rng(ret)) {
+		LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_key_init_rng);
+		goto fail;
+	}
+
+	err = wc_ecc_make_key_ex(ret->rng, 0, ret->eckey, curve_id);
+	if (err != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(wc_ecc_make_key_ex, err);
+		goto fail;
+	}
+
+	return ret;
+fail:
+	crypto_ec_key_deinit(ret);
+	return NULL;
+}
+
+
+int crypto_ec_key_verify_signature_r_s(struct crypto_ec_key *key,
+				       const u8 *data, size_t len,
+				       const u8 *r, size_t r_len,
+				       const u8 *s, size_t s_len)
+{
+	int err;
+	u8 sig[ECC_MAX_SIG_SIZE];
+	word32 sig_len = ECC_MAX_SIG_SIZE;
+
+	if (!key || !key->eckey || !data || !len || !r || !r_len ||
+	    !s || !s_len) {
+		LOG_INVALID_PARAMETERS();
+		return -1;
+	}
+
+	err = wc_ecc_rs_raw_to_sig(r, r_len, s, s_len, sig, &sig_len);
+	if (err != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(wc_ecc_rs_raw_to_sig, err);
+		return -1;
+	}
+
+	return crypto_ec_key_verify_signature(key, data, len, sig, sig_len);
+}
+
+
+struct crypto_ec_point * crypto_ec_key_get_public_key(struct crypto_ec_key *key)
+{
+	ecc_point *point = NULL;
+	int err;
+	u8 *der = NULL;
+	word32 der_len = 0;
+
+	if (!key || !key->eckey || !key->eckey->dp) {
+		LOG_INVALID_PARAMETERS();
+		goto fail;
+	}
+
+	err = wc_ecc_export_x963(key->eckey, NULL, &der_len);
+	if (err != LENGTH_ONLY_E) {
+		LOG_WOLF_ERROR_FUNC(wc_ecc_export_x963, err);
+		goto fail;
+	}
+
+	der = os_malloc(der_len);
+	if (!der) {
+		LOG_WOLF_ERROR_FUNC_NULL(os_malloc);
+		goto fail;
+	}
+
+	err = wc_ecc_export_x963(key->eckey, der, &der_len);
+	if (err == ECC_PRIVATEONLY_E) {
+		if (crypto_ec_key_gen_public_key(key) != 0) {
+			LOG_WOLF_ERROR_FUNC(crypto_ec_key_gen_public_key, -1);
+			goto fail;
+		}
+		err = wc_ecc_export_x963(key->eckey, der, &der_len);
+	}
+	if (err != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(wc_ecc_export_x963, err);
+		goto fail;
+	}
+
+	point = wc_ecc_new_point();
+	if (!point) {
+		LOG_WOLF_ERROR_FUNC_NULL(wc_ecc_new_point);
+		goto fail;
+	}
+
+	err = wc_ecc_import_point_der(der, der_len, key->eckey->idx, point);
+	if (err != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(wc_ecc_import_point_der, err);
+		goto fail;
+	}
+
+	os_free(der);
+	return (struct crypto_ec_point *) point;
+
+fail:
+	os_free(der);
+	if (point)
+		wc_ecc_del_point(point);
+	return NULL;
+}
+
+
+struct crypto_bignum * crypto_ec_key_get_private_key(struct crypto_ec_key *key)
+{
+	u8 priv[ECC_MAXSIZE];
+	word32 priv_len = ECC_MAXSIZE;
+#ifdef WOLFSSL_OLD_FIPS
+	/* Needed to be compliant with the old API */
+	u8 qx[ECC_MAXSIZE];
+	word32 qx_len = ECC_MAXSIZE;
+	u8 qy[ECC_MAXSIZE];
+	word32 qy_len = ECC_MAXSIZE;
+#endif /* WOLFSSL_OLD_FIPS */
+	struct crypto_bignum *ret = NULL;
+	int err;
+
+	if (!key || !key->eckey) {
+		LOG_INVALID_PARAMETERS();
+		return NULL;
+	}
+
+#ifndef WOLFSSL_OLD_FIPS
+	err = wc_ecc_export_private_raw(key->eckey, NULL, NULL, NULL, NULL,
+					priv, &priv_len);
+#else /* WOLFSSL_OLD_FIPS */
+	err = wc_ecc_export_private_raw(key->eckey, qx, &qx_len, qy, &qy_len,
+					priv, &priv_len);
+#endif /* WOLFSSL_OLD_FIPS */
+	if (err != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(wc_ecc_export_private_raw, err);
+		return NULL;
+	}
+
+	ret = crypto_bignum_init_set(priv, priv_len);
+	forced_memzero(priv, priv_len);
+	return ret;
+}
+
+
+struct wpabuf * crypto_ec_key_sign_r_s(struct crypto_ec_key *key,
+				       const u8 *data, size_t len)
+{
+	int err;
+	u8 success = 0;
+	mp_int r;
+	mp_int s;
+	u8 rs_init = 0;
+	int sz;
+	struct wpabuf * ret = NULL;
+
+	if (!key || !key->eckey || !key->eckey->dp || !data || !len) {
+		LOG_INVALID_PARAMETERS();
+		return NULL;
+	}
+
+	sz = key->eckey->dp->size;
+
+	if (!crypto_ec_key_init_rng(key)) {
+		LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_key_init_rng);
+		goto fail;
+	}
+
+	err = mp_init_multi(&r, &s, NULL, NULL, NULL, NULL);
+	if (err != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(mp_init_multi, err);
+		goto fail;
+	}
+	rs_init = 1;
+
+	err = wc_ecc_sign_hash_ex(data, len, key->rng, key->eckey, &r, &s);
+	if (err != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(wc_ecc_sign_hash_ex, err);
+		goto fail;
+	}
+
+	if (mp_unsigned_bin_size(&r) > sz || mp_unsigned_bin_size(&s) > sz) {
+		LOG_WOLF_ERROR_VA("Unexpected size of r or s (%d %d %d)", sz,
+				  mp_unsigned_bin_size(&r),
+				  mp_unsigned_bin_size(&s));
+		goto fail;
+	}
+
+	ret = wpabuf_alloc(2 * sz);
+	if (!ret) {
+		LOG_WOLF_ERROR_FUNC_NULL(wpabuf_alloc);
+		goto fail;
+	}
+
+	err = mp_to_unsigned_bin_len(&r, wpabuf_put(ret, sz), sz);
+	if (err == MP_OKAY)
+		err = mp_to_unsigned_bin_len(&s, wpabuf_put(ret, sz), sz);
+	if (err != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(wc_ecc_sign_hash_ex, err);
+		goto fail;
+	}
+
+	success = 1;
+fail:
+	if (rs_init) {
+		mp_free(&r);
+		mp_free(&s);
+	}
+	if (!success) {
+		wpabuf_free(ret);
+		ret = NULL;
+	}
+
+	return ret;
+}
+
+
+struct crypto_ec_key *
+crypto_ec_key_set_pub_point(struct crypto_ec *e,
+			    const struct crypto_ec_point *pub)
+{
+	struct crypto_ec_key  *ret = NULL;
+	int err;
+	byte *buf = NULL;
+	word32 buf_len = 0;
+
+	if (!e || !pub) {
+		LOG_INVALID_PARAMETERS();
+		return NULL;
+	}
+
+	/* Export to DER to not mess with wolfSSL internals */
+	err = wc_ecc_export_point_der(wc_ecc_get_curve_idx(e->curve_id),
+				      (ecc_point *) pub, NULL, &buf_len);
+	if (err != LENGTH_ONLY_E || !buf_len) {
+		LOG_WOLF_ERROR_FUNC(wc_ecc_export_point_der, err);
+		goto fail;
+	}
+
+	buf = os_malloc(buf_len);
+	if (!buf) {
+		LOG_WOLF_ERROR_FUNC_NULL(os_malloc);
+		goto fail;
+	}
+
+	err = wc_ecc_export_point_der(wc_ecc_get_curve_idx(e->curve_id),
+			(ecc_point *) pub, buf, &buf_len);
+	if (err != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(wc_ecc_export_point_der, err);
+		goto fail;
+	}
+
+	ret = crypto_ec_key_init();
+	if (!ret) {
+		LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_key_init);
+		goto fail;
+	}
+
+	err = wc_ecc_import_x963_ex(buf, buf_len, ret->eckey, e->curve_id);
+	if (err != MP_OKAY) {
+		LOG_WOLF_ERROR_FUNC(wc_ecc_import_x963_ex, err);
+		goto fail;
+	}
+
+	os_free(buf);
+	return ret;
+
+fail:
+	os_free(buf);
+	crypto_ec_key_deinit(ret);
+	return NULL;
+}
+
+
+struct wpabuf * crypto_pkcs7_get_certificates(const struct wpabuf *pkcs7)
+{
+	PKCS7 *p7 = NULL;
+	struct wpabuf *ret = NULL;
+	int err = 0;
+	int total_sz = 0;
+	int i;
+
+	if (!pkcs7) {
+		LOG_INVALID_PARAMETERS();
+		return NULL;
+	}
+
+	p7 = wc_PKCS7_New(NULL, INVALID_DEVID);
+	if (!p7) {
+		LOG_WOLF_ERROR_FUNC_NULL(wc_PKCS7_New);
+		return NULL;
+	}
+
+	err = wc_PKCS7_VerifySignedData(p7, (byte *) wpabuf_head(pkcs7),
+					wpabuf_len(pkcs7));
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_PKCS7_VerifySignedData, err);
+		wc_PKCS7_Free(p7);
+		goto fail;
+	}
+
+	/* Need to access p7 members directly */
+	for (i = 0; i < MAX_PKCS7_CERTS; i++) {
+		if (p7->certSz[i] == 0)
+			continue;
+		err = wc_DerToPem(p7->cert[i], p7->certSz[i], NULL, 0,
+				  CERT_TYPE);
+		if (err > 0) {
+			total_sz += err;
+		} else {
+			LOG_WOLF_ERROR_FUNC(wc_DerToPem, err);
+			goto fail;
+		}
+	}
+
+	if (total_sz == 0) {
+		LOG_WOLF_ERROR("No certificates found in PKCS7 input");
+		goto fail;
+	}
+
+	ret = wpabuf_alloc(total_sz);
+	if (!ret) {
+		LOG_WOLF_ERROR_FUNC_NULL(wpabuf_alloc);
+		goto fail;
+	}
+
+	/* Need to access p7 members directly */
+	for (i = 0; i < MAX_PKCS7_CERTS; i++) {
+		if (p7->certSz[i] == 0)
+			continue;
+		/* Not using wpabuf_put() here so that wpabuf_overflow() isn't
+		 * called in case of a size mismatch. wc_DerToPem() checks if
+		 * the output is large enough internally. */
+		err = wc_DerToPem(p7->cert[i], p7->certSz[i],
+				  wpabuf_mhead_u8(ret) + wpabuf_len(ret),
+				  wpabuf_tailroom(ret),
+				  CERT_TYPE);
+		if (err > 0) {
+			wpabuf_put(ret, err);
+		} else {
+			LOG_WOLF_ERROR_FUNC(wc_DerToPem, err);
+			wpabuf_free(ret);
+			ret = NULL;
+			goto fail;
+		}
+	}
+
+fail:
+	if (p7)
+		wc_PKCS7_Free(p7);
+	return ret;
+}
+
+
+/* BEGIN Certificate Signing Request (CSR) APIs */
+
+enum cert_type {
+	cert_type_none = 0,
+	cert_type_decoded_cert,
+	cert_type_cert,
+};
+
+struct crypto_csr {
+	union {
+		/* For parsed csr should be read-only for higher levels */
+		DecodedCert dc;
+		Cert c; /* For generating a csr */
+	} req;
+	enum cert_type type;
+	struct crypto_ec_key *pubkey;
+};
+
+
+/* Helper function to make sure that the correct type is initialized */
+static void crypto_csr_init_type(struct crypto_csr *csr, enum cert_type type,
+				 const byte *source, word32 in_sz)
+{
+	int err;
+
+	if (csr->type == type)
+		return; /* Already correct type */
+
+	switch (csr->type) {
+	case cert_type_decoded_cert:
+		wc_FreeDecodedCert(&csr->req.dc);
+		break;
+	case cert_type_cert:
+#ifdef WOLFSSL_CERT_GEN_CACHE
+		wc_SetCert_Free(&csr->req.c);
+#endif /* WOLFSSL_CERT_GEN_CACHE */
+		break;
+	case cert_type_none:
+		break;
+	}
+
+	switch (type) {
+	case cert_type_decoded_cert:
+		wc_InitDecodedCert(&csr->req.dc, source, in_sz, NULL);
+		break;
+	case cert_type_cert:
+		err = wc_InitCert(&csr->req.c);
+		if (err != 0)
+			LOG_WOLF_ERROR_FUNC(wc_InitCert, err);
+		break;
+	case cert_type_none:
+		break;
+	}
+
+	csr->type = type;
+}
+
+
+struct crypto_csr * crypto_csr_init(void)
+{
+	struct crypto_csr *ret = os_malloc(sizeof(struct crypto_csr));
+
+	if (!ret) {
+		LOG_WOLF_ERROR_FUNC_NULL(os_malloc);
+		return NULL;
+	}
+
+	ret->type = cert_type_none;
+	crypto_csr_init_type(ret, cert_type_cert, NULL, 0);
+	ret->pubkey = NULL;
+
+	return ret;
+}
+
+
+void crypto_csr_deinit(struct crypto_csr *csr)
+{
+	if (csr) {
+		crypto_csr_init_type(csr, cert_type_none, NULL, 0);
+		crypto_ec_key_deinit(csr->pubkey);
+		os_free(csr);
+	}
+}
+
+
+int crypto_csr_set_ec_public_key(struct crypto_csr *csr,
+				 struct crypto_ec_key *key)
+{
+	struct wpabuf *der = NULL;
+
+	if (!csr || !key || !key->eckey) {
+		LOG_INVALID_PARAMETERS();
+		return -1;
+	}
+
+	if (csr->pubkey) {
+		crypto_ec_key_deinit(csr->pubkey);
+		csr->pubkey = NULL;
+	}
+
+	/* Create copy of key to mitigate use-after-free errors */
+	der = crypto_ec_key_get_subject_public_key(key);
+	if (!der) {
+		LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_key_get_subject_public_key);
+		return -1;
+	}
+
+	csr->pubkey = crypto_ec_key_parse_pub(wpabuf_head(der),
+					      wpabuf_len(der));
+	wpabuf_free(der);
+	if (!csr->pubkey) {
+		LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_key_parse_pub);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int crypto_csr_set_name(struct crypto_csr *csr, enum crypto_csr_name type,
+			const char *name)
+{
+	int name_len;
+	char *dest;
+
+	if (!csr || !name) {
+		LOG_INVALID_PARAMETERS();
+		return -1;
+	}
+
+	if (csr->type != cert_type_cert) {
+		LOG_WOLF_ERROR_VA("csr is incorrect type (%d)", csr->type);
+		return -1;
+	}
+
+	name_len = os_strlen(name);
+	if (name_len >= CTC_NAME_SIZE) {
+		LOG_WOLF_ERROR("name input too long");
+		return -1;
+	}
+
+	switch (type) {
+	case CSR_NAME_CN:
+		dest = csr->req.c.subject.commonName;
+		break;
+	case CSR_NAME_SN:
+		dest = csr->req.c.subject.sur;
+		break;
+	case CSR_NAME_C:
+		dest = csr->req.c.subject.country;
+		break;
+	case CSR_NAME_O:
+		dest = csr->req.c.subject.org;
+		break;
+	case CSR_NAME_OU:
+		dest = csr->req.c.subject.unit;
+		break;
+	default:
+		LOG_INVALID_PARAMETERS();
+		return -1;
+	}
+
+	os_memcpy(dest, name, name_len);
+	dest[name_len] = '\0';
+
+	return 0;
+}
+
+
+int crypto_csr_set_attribute(struct crypto_csr *csr, enum crypto_csr_attr attr,
+			     int attr_type, const u8 *value, size_t len)
+{
+	if (!csr || attr_type != ASN1_TAG_UTF8STRING || !value ||
+	    len >= CTC_NAME_SIZE) {
+		LOG_INVALID_PARAMETERS();
+		return -1;
+	}
+
+	if (csr->type != cert_type_cert) {
+		LOG_WOLF_ERROR_VA("csr is incorrect type (%d)", csr->type);
+		return -1;
+	}
+
+	switch (attr) {
+	case CSR_ATTR_CHALLENGE_PASSWORD:
+		os_memcpy(csr->req.c.challengePw, value, len);
+		csr->req.c.challengePw[len] = '\0';
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+
+const u8 * crypto_csr_get_attribute(struct crypto_csr *csr,
+				    enum crypto_csr_attr attr,
+				    size_t *len, int *type)
+{
+	if (!csr || !len || !type) {
+		LOG_INVALID_PARAMETERS();
+		return NULL;;
+	}
+
+	switch (attr) {
+	case CSR_ATTR_CHALLENGE_PASSWORD:
+		switch (csr->type) {
+		case cert_type_decoded_cert:
+			*type = ASN1_TAG_UTF8STRING;
+			*len = csr->req.dc.cPwdLen;
+			return (const u8 *) csr->req.dc.cPwd;
+		case cert_type_cert:
+			*type = ASN1_TAG_UTF8STRING;
+			*len = os_strlen(csr->req.c.challengePw);
+			return (const u8 *) csr->req.c.challengePw;
+		case cert_type_none:
+			return NULL;
+		}
+		break;
+	}
+	return NULL;
+}
+
+
+struct wpabuf * crypto_csr_sign(struct crypto_csr *csr,
+				struct crypto_ec_key *key,
+				enum crypto_hash_alg algo)
+{
+	int err;
+	int len;
+	u8 *buf = NULL;
+	int buf_len;
+	struct wpabuf *ret = NULL;
+
+	if (!csr || !key || !key->eckey) {
+		LOG_INVALID_PARAMETERS();
+		return NULL;
+	}
+
+	if (csr->type != cert_type_cert) {
+		LOG_WOLF_ERROR_VA("csr is incorrect type (%d)", csr->type);
+		return NULL;
+	}
+
+	if (!crypto_ec_key_init_rng(key)) {
+		LOG_WOLF_ERROR_FUNC_NULL(crypto_ec_key_init_rng);
+		return NULL;
+	}
+
+	switch (algo) {
+	case CRYPTO_HASH_ALG_SHA256:
+		csr->req.c.sigType = CTC_SHA256wECDSA;
+		break;
+	case CRYPTO_HASH_ALG_SHA384:
+		csr->req.c.sigType = CTC_SHA384wECDSA;
+		break;
+	case CRYPTO_HASH_ALG_SHA512:
+		csr->req.c.sigType = CTC_SHA512wECDSA;
+		break;
+	default:
+		LOG_INVALID_PARAMETERS();
+		return NULL;
+	}
+
+	/* Pass in large value that is guaranteed to be larger than the
+	 * necessary buffer */
+	err = wc_MakeCertReq(&csr->req.c, NULL, 100000, NULL,
+			     csr->pubkey->eckey);
+	if (err <= 0) {
+		LOG_WOLF_ERROR_FUNC(wc_MakeCertReq, err);
+		goto fail;
+	}
+	len = err;
+
+	buf_len = len + MAX_SEQ_SZ * 2 + MAX_ENCODED_SIG_SZ;
+	buf = os_malloc(buf_len);
+	if (!buf) {
+		LOG_WOLF_ERROR_FUNC_NULL(os_malloc);
+		goto fail;
+	}
+
+	err = wc_MakeCertReq(&csr->req.c, buf, buf_len, NULL,
+			     csr->pubkey->eckey);
+	if (err <= 0) {
+		LOG_WOLF_ERROR_FUNC(wc_MakeCertReq, err);
+		goto fail;
+	}
+	len = err;
+
+	err = wc_SignCert(len, csr->req.c.sigType, buf, buf_len, NULL,
+			  key->eckey, key->rng);
+	if (err <= 0) {
+		LOG_WOLF_ERROR_FUNC(wc_SignCert, err);
+		goto fail;
+	}
+	len = err;
+
+	ret = wpabuf_alloc_copy(buf, len);
+	if (!ret) {
+		LOG_WOLF_ERROR_FUNC_NULL(wpabuf_alloc_copy);
+		goto fail;
+	}
+
+fail:
+	os_free(buf);
+	return ret;
+}
+
+
+struct crypto_csr * crypto_csr_verify(const struct wpabuf *req)
+{
+	struct crypto_csr *csr = NULL;
+	int err;
+
+	if (!req) {
+		LOG_INVALID_PARAMETERS();
+		return NULL;
+	}
+
+	csr = crypto_csr_init();
+	if (!csr) {
+		LOG_WOLF_ERROR_FUNC_NULL(crypto_csr_init);
+		goto fail;
+	}
+
+	crypto_csr_init_type(csr, cert_type_decoded_cert,
+			     wpabuf_head(req), wpabuf_len(req));
+	err = wc_ParseCert(&csr->req.dc, CERTREQ_TYPE, VERIFY, NULL);
+	if (err != 0) {
+		LOG_WOLF_ERROR_FUNC(wc_ParseCert, err);
+		goto fail;
+	}
+
+	return csr;
+fail:
+	crypto_csr_deinit(csr);
+	return NULL;
+}
+
+/* END Certificate Signing Request (CSR) APIs */
+
+#endif /* CONFIG_DPP */
+
+
+void crypto_unload(void)
+{
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/des-internal.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/des-internal.c
new file mode 100644
index 0000000..4ed6957
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/des-internal.c
@@ -0,0 +1,494 @@
+/*
+ * DES and 3DES-EDE ciphers
+ *
+ * Modifications to LibTomCrypt implementation:
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+#include "des_i.h"
+
+/*
+ * This implementation is based on a DES implementation included in
+ * LibTomCrypt. The version here is modified to fit in wpa_supplicant/hostapd
+ * coding style.
+ */
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtomcrypt.com
+ */
+
+/**
+  DES code submitted by Dobes Vandermeer
+*/
+
+#define ROLc(x, y) \
+	((((unsigned long) (x) << (unsigned long) ((y) & 31)) | \
+	  (((unsigned long) (x) & 0xFFFFFFFFUL) >> \
+	   (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL)
+#define RORc(x, y) \
+	(((((unsigned long) (x) & 0xFFFFFFFFUL) >> \
+	   (unsigned long) ((y) & 31)) | \
+	  ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & \
+	 0xFFFFFFFFUL)
+
+
+static const u32 bytebit[8] =
+{
+	0200, 0100, 040, 020, 010, 04, 02, 01
+};
+
+static const u32 bigbyte[24] =
+{
+	0x800000UL,  0x400000UL,  0x200000UL,  0x100000UL,
+	0x80000UL,   0x40000UL,   0x20000UL,   0x10000UL,
+	0x8000UL,    0x4000UL,    0x2000UL,    0x1000UL,
+	0x800UL,     0x400UL,     0x200UL,     0x100UL,
+	0x80UL,      0x40UL,      0x20UL,      0x10UL,
+	0x8UL,       0x4UL,       0x2UL,       0x1L
+};
+
+/* Use the key schedule specific in the standard (ANSI X3.92-1981) */
+
+static const u8 pc1[56] = {
+	56, 48, 40, 32, 24, 16,  8,  0, 57, 49, 41, 33, 25, 17,
+	 9,  1, 58, 50, 42, 34, 26, 18, 10,  2, 59, 51, 43, 35,
+	62, 54, 46, 38, 30, 22, 14,  6, 61, 53, 45, 37, 29, 21,
+	13,  5, 60, 52, 44, 36, 28, 20, 12,  4, 27, 19, 11,  3
+};
+
+static const u8 totrot[16] = {
+	1,   2,  4,  6,
+	8,  10, 12, 14,
+	15, 17, 19, 21,
+	23, 25, 27, 28
+};
+
+static const u8 pc2[48] = {
+	13, 16, 10, 23,  0,  4,      2, 27, 14,  5, 20,  9,
+	22, 18, 11,  3, 25,  7,     15,  6, 26, 19, 12,  1,
+	40, 51, 30, 36, 46, 54,     29, 39, 50, 44, 32, 47,
+	43, 48, 38, 55, 33, 52,     45, 41, 49, 35, 28, 31
+};
+
+
+static const u32 SP1[64] =
+{
+	0x01010400UL, 0x00000000UL, 0x00010000UL, 0x01010404UL,
+	0x01010004UL, 0x00010404UL, 0x00000004UL, 0x00010000UL,
+	0x00000400UL, 0x01010400UL, 0x01010404UL, 0x00000400UL,
+	0x01000404UL, 0x01010004UL, 0x01000000UL, 0x00000004UL,
+	0x00000404UL, 0x01000400UL, 0x01000400UL, 0x00010400UL,
+	0x00010400UL, 0x01010000UL, 0x01010000UL, 0x01000404UL,
+	0x00010004UL, 0x01000004UL, 0x01000004UL, 0x00010004UL,
+	0x00000000UL, 0x00000404UL, 0x00010404UL, 0x01000000UL,
+	0x00010000UL, 0x01010404UL, 0x00000004UL, 0x01010000UL,
+	0x01010400UL, 0x01000000UL, 0x01000000UL, 0x00000400UL,
+	0x01010004UL, 0x00010000UL, 0x00010400UL, 0x01000004UL,
+	0x00000400UL, 0x00000004UL, 0x01000404UL, 0x00010404UL,
+	0x01010404UL, 0x00010004UL, 0x01010000UL, 0x01000404UL,
+	0x01000004UL, 0x00000404UL, 0x00010404UL, 0x01010400UL,
+	0x00000404UL, 0x01000400UL, 0x01000400UL, 0x00000000UL,
+	0x00010004UL, 0x00010400UL, 0x00000000UL, 0x01010004UL
+};
+
+static const u32 SP2[64] =
+{
+	0x80108020UL, 0x80008000UL, 0x00008000UL, 0x00108020UL,
+	0x00100000UL, 0x00000020UL, 0x80100020UL, 0x80008020UL,
+	0x80000020UL, 0x80108020UL, 0x80108000UL, 0x80000000UL,
+	0x80008000UL, 0x00100000UL, 0x00000020UL, 0x80100020UL,
+	0x00108000UL, 0x00100020UL, 0x80008020UL, 0x00000000UL,
+	0x80000000UL, 0x00008000UL, 0x00108020UL, 0x80100000UL,
+	0x00100020UL, 0x80000020UL, 0x00000000UL, 0x00108000UL,
+	0x00008020UL, 0x80108000UL, 0x80100000UL, 0x00008020UL,
+	0x00000000UL, 0x00108020UL, 0x80100020UL, 0x00100000UL,
+	0x80008020UL, 0x80100000UL, 0x80108000UL, 0x00008000UL,
+	0x80100000UL, 0x80008000UL, 0x00000020UL, 0x80108020UL,
+	0x00108020UL, 0x00000020UL, 0x00008000UL, 0x80000000UL,
+	0x00008020UL, 0x80108000UL, 0x00100000UL, 0x80000020UL,
+	0x00100020UL, 0x80008020UL, 0x80000020UL, 0x00100020UL,
+	0x00108000UL, 0x00000000UL, 0x80008000UL, 0x00008020UL,
+	0x80000000UL, 0x80100020UL, 0x80108020UL, 0x00108000UL
+};
+
+static const u32 SP3[64] =
+{
+	0x00000208UL, 0x08020200UL, 0x00000000UL, 0x08020008UL,
+	0x08000200UL, 0x00000000UL, 0x00020208UL, 0x08000200UL,
+	0x00020008UL, 0x08000008UL, 0x08000008UL, 0x00020000UL,
+	0x08020208UL, 0x00020008UL, 0x08020000UL, 0x00000208UL,
+	0x08000000UL, 0x00000008UL, 0x08020200UL, 0x00000200UL,
+	0x00020200UL, 0x08020000UL, 0x08020008UL, 0x00020208UL,
+	0x08000208UL, 0x00020200UL, 0x00020000UL, 0x08000208UL,
+	0x00000008UL, 0x08020208UL, 0x00000200UL, 0x08000000UL,
+	0x08020200UL, 0x08000000UL, 0x00020008UL, 0x00000208UL,
+	0x00020000UL, 0x08020200UL, 0x08000200UL, 0x00000000UL,
+	0x00000200UL, 0x00020008UL, 0x08020208UL, 0x08000200UL,
+	0x08000008UL, 0x00000200UL, 0x00000000UL, 0x08020008UL,
+	0x08000208UL, 0x00020000UL, 0x08000000UL, 0x08020208UL,
+	0x00000008UL, 0x00020208UL, 0x00020200UL, 0x08000008UL,
+	0x08020000UL, 0x08000208UL, 0x00000208UL, 0x08020000UL,
+	0x00020208UL, 0x00000008UL, 0x08020008UL, 0x00020200UL
+};
+
+static const u32 SP4[64] =
+{
+	0x00802001UL, 0x00002081UL, 0x00002081UL, 0x00000080UL,
+	0x00802080UL, 0x00800081UL, 0x00800001UL, 0x00002001UL,
+	0x00000000UL, 0x00802000UL, 0x00802000UL, 0x00802081UL,
+	0x00000081UL, 0x00000000UL, 0x00800080UL, 0x00800001UL,
+	0x00000001UL, 0x00002000UL, 0x00800000UL, 0x00802001UL,
+	0x00000080UL, 0x00800000UL, 0x00002001UL, 0x00002080UL,
+	0x00800081UL, 0x00000001UL, 0x00002080UL, 0x00800080UL,
+	0x00002000UL, 0x00802080UL, 0x00802081UL, 0x00000081UL,
+	0x00800080UL, 0x00800001UL, 0x00802000UL, 0x00802081UL,
+	0x00000081UL, 0x00000000UL, 0x00000000UL, 0x00802000UL,
+	0x00002080UL, 0x00800080UL, 0x00800081UL, 0x00000001UL,
+	0x00802001UL, 0x00002081UL, 0x00002081UL, 0x00000080UL,
+	0x00802081UL, 0x00000081UL, 0x00000001UL, 0x00002000UL,
+	0x00800001UL, 0x00002001UL, 0x00802080UL, 0x00800081UL,
+	0x00002001UL, 0x00002080UL, 0x00800000UL, 0x00802001UL,
+	0x00000080UL, 0x00800000UL, 0x00002000UL, 0x00802080UL
+};
+
+static const u32 SP5[64] =
+{
+	0x00000100UL, 0x02080100UL, 0x02080000UL, 0x42000100UL,
+	0x00080000UL, 0x00000100UL, 0x40000000UL, 0x02080000UL,
+	0x40080100UL, 0x00080000UL, 0x02000100UL, 0x40080100UL,
+	0x42000100UL, 0x42080000UL, 0x00080100UL, 0x40000000UL,
+	0x02000000UL, 0x40080000UL, 0x40080000UL, 0x00000000UL,
+	0x40000100UL, 0x42080100UL, 0x42080100UL, 0x02000100UL,
+	0x42080000UL, 0x40000100UL, 0x00000000UL, 0x42000000UL,
+	0x02080100UL, 0x02000000UL, 0x42000000UL, 0x00080100UL,
+	0x00080000UL, 0x42000100UL, 0x00000100UL, 0x02000000UL,
+	0x40000000UL, 0x02080000UL, 0x42000100UL, 0x40080100UL,
+	0x02000100UL, 0x40000000UL, 0x42080000UL, 0x02080100UL,
+	0x40080100UL, 0x00000100UL, 0x02000000UL, 0x42080000UL,
+	0x42080100UL, 0x00080100UL, 0x42000000UL, 0x42080100UL,
+	0x02080000UL, 0x00000000UL, 0x40080000UL, 0x42000000UL,
+	0x00080100UL, 0x02000100UL, 0x40000100UL, 0x00080000UL,
+	0x00000000UL, 0x40080000UL, 0x02080100UL, 0x40000100UL
+};
+
+static const u32 SP6[64] =
+{
+	0x20000010UL, 0x20400000UL, 0x00004000UL, 0x20404010UL,
+	0x20400000UL, 0x00000010UL, 0x20404010UL, 0x00400000UL,
+	0x20004000UL, 0x00404010UL, 0x00400000UL, 0x20000010UL,
+	0x00400010UL, 0x20004000UL, 0x20000000UL, 0x00004010UL,
+	0x00000000UL, 0x00400010UL, 0x20004010UL, 0x00004000UL,
+	0x00404000UL, 0x20004010UL, 0x00000010UL, 0x20400010UL,
+	0x20400010UL, 0x00000000UL, 0x00404010UL, 0x20404000UL,
+	0x00004010UL, 0x00404000UL, 0x20404000UL, 0x20000000UL,
+	0x20004000UL, 0x00000010UL, 0x20400010UL, 0x00404000UL,
+	0x20404010UL, 0x00400000UL, 0x00004010UL, 0x20000010UL,
+	0x00400000UL, 0x20004000UL, 0x20000000UL, 0x00004010UL,
+	0x20000010UL, 0x20404010UL, 0x00404000UL, 0x20400000UL,
+	0x00404010UL, 0x20404000UL, 0x00000000UL, 0x20400010UL,
+	0x00000010UL, 0x00004000UL, 0x20400000UL, 0x00404010UL,
+	0x00004000UL, 0x00400010UL, 0x20004010UL, 0x00000000UL,
+	0x20404000UL, 0x20000000UL, 0x00400010UL, 0x20004010UL
+};
+
+static const u32 SP7[64] =
+{
+	0x00200000UL, 0x04200002UL, 0x04000802UL, 0x00000000UL,
+	0x00000800UL, 0x04000802UL, 0x00200802UL, 0x04200800UL,
+	0x04200802UL, 0x00200000UL, 0x00000000UL, 0x04000002UL,
+	0x00000002UL, 0x04000000UL, 0x04200002UL, 0x00000802UL,
+	0x04000800UL, 0x00200802UL, 0x00200002UL, 0x04000800UL,
+	0x04000002UL, 0x04200000UL, 0x04200800UL, 0x00200002UL,
+	0x04200000UL, 0x00000800UL, 0x00000802UL, 0x04200802UL,
+	0x00200800UL, 0x00000002UL, 0x04000000UL, 0x00200800UL,
+	0x04000000UL, 0x00200800UL, 0x00200000UL, 0x04000802UL,
+	0x04000802UL, 0x04200002UL, 0x04200002UL, 0x00000002UL,
+	0x00200002UL, 0x04000000UL, 0x04000800UL, 0x00200000UL,
+	0x04200800UL, 0x00000802UL, 0x00200802UL, 0x04200800UL,
+	0x00000802UL, 0x04000002UL, 0x04200802UL, 0x04200000UL,
+	0x00200800UL, 0x00000000UL, 0x00000002UL, 0x04200802UL,
+	0x00000000UL, 0x00200802UL, 0x04200000UL, 0x00000800UL,
+	0x04000002UL, 0x04000800UL, 0x00000800UL, 0x00200002UL
+};
+
+static const u32 SP8[64] =
+{
+	0x10001040UL, 0x00001000UL, 0x00040000UL, 0x10041040UL,
+	0x10000000UL, 0x10001040UL, 0x00000040UL, 0x10000000UL,
+	0x00040040UL, 0x10040000UL, 0x10041040UL, 0x00041000UL,
+	0x10041000UL, 0x00041040UL, 0x00001000UL, 0x00000040UL,
+	0x10040000UL, 0x10000040UL, 0x10001000UL, 0x00001040UL,
+	0x00041000UL, 0x00040040UL, 0x10040040UL, 0x10041000UL,
+	0x00001040UL, 0x00000000UL, 0x00000000UL, 0x10040040UL,
+	0x10000040UL, 0x10001000UL, 0x00041040UL, 0x00040000UL,
+	0x00041040UL, 0x00040000UL, 0x10041000UL, 0x00001000UL,
+	0x00000040UL, 0x10040040UL, 0x00001000UL, 0x00041040UL,
+	0x10001000UL, 0x00000040UL, 0x10000040UL, 0x10040000UL,
+	0x10040040UL, 0x10000000UL, 0x00040000UL, 0x10001040UL,
+	0x00000000UL, 0x10041040UL, 0x00040040UL, 0x10000040UL,
+	0x10040000UL, 0x10001000UL, 0x10001040UL, 0x00000000UL,
+	0x10041040UL, 0x00041000UL, 0x00041000UL, 0x00001040UL,
+	0x00001040UL, 0x00040040UL, 0x10000000UL, 0x10041000UL
+};
+
+
+static void cookey(const u32 *raw1, u32 *keyout)
+{
+	u32 *cook;
+	const u32 *raw0;
+	u32 dough[32];
+	int i;
+
+	cook = dough;
+	for (i = 0; i < 16; i++, raw1++) {
+		raw0 = raw1++;
+		*cook    = (*raw0 & 0x00fc0000L) << 6;
+		*cook   |= (*raw0 & 0x00000fc0L) << 10;
+		*cook   |= (*raw1 & 0x00fc0000L) >> 10;
+		*cook++ |= (*raw1 & 0x00000fc0L) >> 6;
+		*cook    = (*raw0 & 0x0003f000L) << 12;
+		*cook   |= (*raw0 & 0x0000003fL) << 16;
+		*cook   |= (*raw1 & 0x0003f000L) >> 4;
+		*cook++ |= (*raw1 & 0x0000003fL);
+	}
+
+	os_memcpy(keyout, dough, sizeof(dough));
+}
+
+
+static void deskey(const u8 *key, int decrypt, u32 *keyout)
+{
+	u32 i, j, l, m, n, kn[32];
+	u8 pc1m[56], pcr[56];
+
+	for (j = 0; j < 56; j++) {
+		l = (u32) pc1[j];
+		m = l & 7;
+		pc1m[j] = (u8)
+			((key[l >> 3U] & bytebit[m]) == bytebit[m] ? 1 : 0);
+	}
+
+	for (i = 0; i < 16; i++) {
+		if (decrypt)
+			m = (15 - i) << 1;
+		else
+			m = i << 1;
+		n = m + 1;
+		kn[m] = kn[n] = 0L;
+		for (j = 0; j < 28; j++) {
+			l = j + (u32) totrot[i];
+			if (l < 28)
+				pcr[j] = pc1m[l];
+			else
+				pcr[j] = pc1m[l - 28];
+		}
+		for (/* j = 28 */; j < 56; j++) {
+			l = j + (u32) totrot[i];
+			if (l < 56)
+				pcr[j] = pc1m[l];
+			else
+				pcr[j] = pc1m[l - 28];
+		}
+		for (j = 0; j < 24; j++) {
+			if ((int) pcr[(int) pc2[j]] != 0)
+				kn[m] |= bigbyte[j];
+			if ((int) pcr[(int) pc2[j + 24]] != 0)
+				kn[n] |= bigbyte[j];
+		}
+	}
+
+	cookey(kn, keyout);
+}
+
+
+static void desfunc(u32 *block, const u32 *keys)
+{
+	u32 work, right, leftt;
+	int cur_round;
+
+	leftt = block[0];
+	right = block[1];
+
+	work = ((leftt >> 4)  ^ right) & 0x0f0f0f0fL;
+	right ^= work;
+	leftt ^= (work << 4);
+
+	work = ((leftt >> 16) ^ right) & 0x0000ffffL;
+	right ^= work;
+	leftt ^= (work << 16);
+
+	work = ((right >> 2)  ^ leftt) & 0x33333333L;
+	leftt ^= work;
+	right ^= (work << 2);
+
+	work = ((right >> 8)  ^ leftt) & 0x00ff00ffL;
+	leftt ^= work;
+	right ^= (work << 8);
+
+	right = ROLc(right, 1);
+	work = (leftt ^ right) & 0xaaaaaaaaL;
+
+	leftt ^= work;
+	right ^= work;
+	leftt = ROLc(leftt, 1);
+
+	for (cur_round = 0; cur_round < 8; cur_round++) {
+		work  = RORc(right, 4) ^ *keys++;
+		leftt ^= SP7[work        & 0x3fL]
+			^ SP5[(work >>  8) & 0x3fL]
+			^ SP3[(work >> 16) & 0x3fL]
+			^ SP1[(work >> 24) & 0x3fL];
+		work  = right ^ *keys++;
+		leftt ^= SP8[ work        & 0x3fL]
+			^  SP6[(work >>  8) & 0x3fL]
+			^  SP4[(work >> 16) & 0x3fL]
+			^  SP2[(work >> 24) & 0x3fL];
+
+		work = RORc(leftt, 4) ^ *keys++;
+		right ^= SP7[ work        & 0x3fL]
+			^  SP5[(work >>  8) & 0x3fL]
+			^  SP3[(work >> 16) & 0x3fL]
+			^  SP1[(work >> 24) & 0x3fL];
+		work  = leftt ^ *keys++;
+		right ^= SP8[ work        & 0x3fL]
+			^  SP6[(work >>  8) & 0x3fL]
+			^  SP4[(work >> 16) & 0x3fL]
+			^  SP2[(work >> 24) & 0x3fL];
+	}
+
+	right = RORc(right, 1);
+	work = (leftt ^ right) & 0xaaaaaaaaL;
+	leftt ^= work;
+	right ^= work;
+	leftt = RORc(leftt, 1);
+	work = ((leftt >> 8) ^ right) & 0x00ff00ffL;
+	right ^= work;
+	leftt ^= (work << 8);
+	/* -- */
+	work = ((leftt >> 2) ^ right) & 0x33333333L;
+	right ^= work;
+	leftt ^= (work << 2);
+	work = ((right >> 16) ^ leftt) & 0x0000ffffL;
+	leftt ^= work;
+	right ^= (work << 16);
+	work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL;
+	leftt ^= work;
+	right ^= (work << 4);
+
+	block[0] = right;
+	block[1] = leftt;
+}
+
+
+/* wpa_supplicant/hostapd specific wrapper */
+
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+	u8 pkey[8], next, tmp;
+	int i;
+	u32 ek[32], work[2];
+
+	/* Add parity bits to the key */
+	next = 0;
+	for (i = 0; i < 7; i++) {
+		tmp = key[i];
+		pkey[i] = (tmp >> i) | next | 1;
+		next = tmp << (7 - i);
+	}
+	pkey[i] = next | 1;
+
+	deskey(pkey, 0, ek);
+
+	work[0] = WPA_GET_BE32(clear);
+	work[1] = WPA_GET_BE32(clear + 4);
+	desfunc(work, ek);
+	WPA_PUT_BE32(cypher, work[0]);
+	WPA_PUT_BE32(cypher + 4, work[1]);
+
+	os_memset(pkey, 0, sizeof(pkey));
+	os_memset(ek, 0, sizeof(ek));
+	return 0;
+}
+
+
+void des_key_setup(const u8 *key, u32 *ek, u32 *dk)
+{
+	deskey(key, 0, ek);
+	deskey(key, 1, dk);
+}
+
+
+void des_block_encrypt(const u8 *plain, const u32 *ek, u8 *crypt)
+{
+	u32 work[2];
+	work[0] = WPA_GET_BE32(plain);
+	work[1] = WPA_GET_BE32(plain + 4);
+	desfunc(work, ek);
+	WPA_PUT_BE32(crypt, work[0]);
+	WPA_PUT_BE32(crypt + 4, work[1]);
+}
+
+
+void des_block_decrypt(const u8 *crypt, const u32 *dk, u8 *plain)
+{
+	u32 work[2];
+	work[0] = WPA_GET_BE32(crypt);
+	work[1] = WPA_GET_BE32(crypt + 4);
+	desfunc(work, dk);
+	WPA_PUT_BE32(plain, work[0]);
+	WPA_PUT_BE32(plain + 4, work[1]);
+}
+
+
+void des3_key_setup(const u8 *key, struct des3_key_s *dkey)
+{
+	deskey(key, 0, dkey->ek[0]);
+	deskey(key + 8, 1, dkey->ek[1]);
+	deskey(key + 16, 0, dkey->ek[2]);
+
+	deskey(key, 1, dkey->dk[2]);
+	deskey(key + 8, 0, dkey->dk[1]);
+	deskey(key + 16, 1, dkey->dk[0]);
+}
+
+
+void des3_encrypt(const u8 *plain, const struct des3_key_s *key, u8 *crypt)
+{
+	u32 work[2];
+
+	work[0] = WPA_GET_BE32(plain);
+	work[1] = WPA_GET_BE32(plain + 4);
+	desfunc(work, key->ek[0]);
+	desfunc(work, key->ek[1]);
+	desfunc(work, key->ek[2]);
+	WPA_PUT_BE32(crypt, work[0]);
+	WPA_PUT_BE32(crypt + 4, work[1]);
+}
+
+
+void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain)
+{
+	u32 work[2];
+
+	work[0] = WPA_GET_BE32(crypt);
+	work[1] = WPA_GET_BE32(crypt + 4);
+	desfunc(work, key->dk[0]);
+	desfunc(work, key->dk[1]);
+	desfunc(work, key->dk[2]);
+	WPA_PUT_BE32(plain, work[0]);
+	WPA_PUT_BE32(plain + 4, work[1]);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/des_i.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/des_i.h
new file mode 100644
index 0000000..c9563d2
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/des_i.h
@@ -0,0 +1,25 @@
+/*
+ * DES and 3DES-EDE ciphers
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DES_I_H
+#define DES_I_H
+
+struct des3_key_s {
+	u32 ek[3][32];
+	u32 dk[3][32];
+};
+
+void des_key_setup(const u8 *key, u32 *ek, u32 *dk);
+void des_block_encrypt(const u8 *plain, const u32 *ek, u8 *crypt);
+void des_block_decrypt(const u8 *crypt, const u32 *dk, u8 *plain);
+
+void des3_key_setup(const u8 *key, struct des3_key_s *dkey);
+void des3_encrypt(const u8 *plain, const struct des3_key_s *key, u8 *crypt);
+void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain);
+
+#endif /* DES_I_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/dh_group5.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/dh_group5.c
new file mode 100644
index 0000000..425c848
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/dh_group5.c
@@ -0,0 +1,41 @@
+/*
+ * Diffie-Hellman group 5 operations
+ * Copyright (c) 2009, 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "dh_groups.h"
+#include "dh_group5.h"
+
+
+void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
+{
+	wpabuf_free(*publ);
+	*publ = dh_init(dh_groups_get(5), priv);
+	if (*publ == NULL)
+		return NULL;
+	return (void *) 1;
+}
+
+
+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
+{
+	return (void *) 1;
+}
+
+
+struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
+				  const struct wpabuf *own_private)
+{
+	return dh_derive_shared(peer_public, own_private, dh_groups_get(5));
+}
+
+
+void dh5_free(void *ctx)
+{
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/dh_group5.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/dh_group5.h
new file mode 100644
index 0000000..abee8ea
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/dh_group5.h
@@ -0,0 +1,18 @@
+/*
+ * Diffie-Hellman group 5 operations
+ * Copyright (c) 2009, 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DH_GROUP5_H
+#define DH_GROUP5_H
+
+void * dh5_init(struct wpabuf **priv, struct wpabuf **publ);
+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ);
+struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
+				  const struct wpabuf *own_private);
+void dh5_free(void *ctx);
+
+#endif /* DH_GROUP5_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/dh_groups.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/dh_groups.c
new file mode 100644
index 0000000..5e421b2
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/dh_groups.c
@@ -0,0 +1,1266 @@
+/*
+ * Diffie-Hellman groups
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+#include "random.h"
+#include "dh_groups.h"
+
+
+#ifdef ALL_DH_GROUPS
+
+/* RFC 4306, B.1. Group 1 - 768 Bit MODP
+ * Generator: 2
+ * Prime: 2^768 - 2 ^704 - 1 + 2^64 * { [2^638 pi] + 149686 }
+ */
+static const u8 dh_group1_generator[1] = { 0x02 };
+static const u8 dh_group1_prime[96] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+	0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+	0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+	0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+	0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+	0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+	0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+	0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+	0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+	0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x3A, 0x36, 0x20,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+static const u8 dh_group1_order[96] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1D, 0x1B, 0x10,
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/* RFC 4306, B.2. Group 2 - 1024 Bit MODP
+ * Generator: 2
+ * Prime: 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }
+ */
+static const u8 dh_group2_generator[1] = { 0x02 };
+static const u8 dh_group2_prime[128] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+	0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+	0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+	0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+	0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+	0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+	0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+	0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+	0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+	0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+	0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+	0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+	0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+	0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+static const u8 dh_group2_order[128] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x73, 0x29, 0xC0,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+#endif /* ALL_DH_GROUPS */
+
+/* RFC 3526, 2. Group 5 - 1536 Bit MODP
+ * Generator: 2
+ * Prime: 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 }
+ */
+static const u8 dh_group5_generator[1] = { 0x02 };
+static const u8 dh_group5_prime[192] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+	0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+	0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+	0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+	0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+	0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+	0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+	0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+	0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+	0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+	0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+	0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+	0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+	0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+	0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+	0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+	0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+	0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+	0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+	0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+	0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+	0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x23, 0x73, 0x27,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+static const u8 dh_group5_order[192] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x11, 0xB9, 0x93,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+#ifdef ALL_DH_GROUPS
+
+/* RFC 3526, 3. Group 14 - 2048 Bit MODP
+ * Generator: 2
+ * Prime: 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 }
+ */
+static const u8 dh_group14_generator[1] = { 0x02 };
+static const u8 dh_group14_prime[256] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+	0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+	0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+	0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+	0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+	0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+	0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+	0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+	0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+	0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+	0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+	0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+	0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+	0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+	0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+	0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+	0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+	0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+	0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+	0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+	0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+	0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+	0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+	0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+	0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+	0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+	0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+	0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+	0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+	0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+static const u8 dh_group14_order[256] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
+	0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
+	0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
+	0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
+	0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
+	0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
+	0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
+	0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
+	0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x56, 0x55, 0x34,
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/* RFC 3526, 4. Group 15 - 3072 Bit MODP
+ * Generator: 2
+ * Prime: 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 }
+ */
+static const u8 dh_group15_generator[1] = { 0x02 };
+static const u8 dh_group15_prime[384] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+	0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+	0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+	0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+	0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+	0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+	0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+	0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+	0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+	0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+	0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+	0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+	0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+	0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+	0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+	0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+	0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+	0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+	0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+	0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+	0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+	0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+	0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+	0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+	0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+	0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+	0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+	0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+	0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+	0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+	0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+	0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+	0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+	0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+	0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+	0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+	0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+	0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+	0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+	0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+	0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+	0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+	0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+	0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+	0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+	0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x3A, 0xD2, 0xCA,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+static const u8 dh_group15_order[384] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
+	0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
+	0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
+	0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
+	0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
+	0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
+	0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
+	0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
+	0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16,
+	0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19,
+	0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32,
+	0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85,
+	0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E,
+	0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63,
+	0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB,
+	0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE,
+	0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35,
+	0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32,
+	0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32,
+	0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06,
+	0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6,
+	0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71,
+	0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98,
+	0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47,
+	0x25, 0xC1, 0x68, 0x90, 0x54, 0x9D, 0x69, 0x65,
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/* RFC 3526, 5. Group 16 - 4096 Bit MODP
+ * Generator: 2
+ * Prime: 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 }
+ */
+static const u8 dh_group16_generator[1] = { 0x02 };
+static const u8 dh_group16_prime[512] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+	0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+	0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+	0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+	0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+	0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+	0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+	0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+	0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+	0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+	0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+	0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+	0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+	0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+	0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+	0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+	0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+	0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+	0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+	0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+	0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+	0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+	0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+	0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+	0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+	0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+	0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+	0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+	0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+	0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+	0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+	0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+	0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+	0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+	0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+	0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+	0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+	0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+	0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+	0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+	0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+	0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+	0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+	0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+	0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+	0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,
+	0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
+	0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,
+	0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,
+	0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
+	0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,
+	0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,
+	0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
+	0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,
+	0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,
+	0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
+	0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,
+	0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,
+	0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
+	0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,
+	0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,
+	0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+static const u8 dh_group16_order[512] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
+	0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
+	0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
+	0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
+	0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
+	0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
+	0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
+	0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
+	0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16,
+	0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19,
+	0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32,
+	0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85,
+	0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E,
+	0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63,
+	0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB,
+	0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE,
+	0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35,
+	0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32,
+	0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32,
+	0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06,
+	0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6,
+	0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71,
+	0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98,
+	0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47,
+	0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00,
+	0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B,
+	0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93,
+	0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E,
+	0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED,
+	0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74,
+	0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C,
+	0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53,
+	0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E,
+	0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1,
+	0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6,
+	0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7,
+	0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E,
+	0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54,
+	0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0,
+	0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47,
+	0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x03, 0x18, 0xCC,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/* RFC 3526, 6. Group 17 - 6144 Bit MODP
+ * Generator: 2
+ * Prime: 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 }
+ */
+static const u8 dh_group17_generator[1] = { 0x02 };
+static const u8 dh_group17_prime[768] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+	0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+	0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+	0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+	0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+	0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+	0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+	0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+	0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+	0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+	0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+	0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+	0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+	0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+	0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+	0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+	0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+	0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+	0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+	0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+	0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+	0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+	0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+	0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+	0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+	0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+	0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+	0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+	0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+	0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+	0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+	0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+	0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+	0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+	0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+	0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+	0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+	0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+	0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+	0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+	0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+	0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+	0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+	0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+	0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+	0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,
+	0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
+	0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,
+	0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,
+	0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
+	0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,
+	0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,
+	0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
+	0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,
+	0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,
+	0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
+	0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,
+	0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,
+	0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
+	0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,
+	0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,
+	0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x02, 0x84, 0x92,
+	0x36, 0xC3, 0xFA, 0xB4, 0xD2, 0x7C, 0x70, 0x26,
+	0xC1, 0xD4, 0xDC, 0xB2, 0x60, 0x26, 0x46, 0xDE,
+	0xC9, 0x75, 0x1E, 0x76, 0x3D, 0xBA, 0x37, 0xBD,
+	0xF8, 0xFF, 0x94, 0x06, 0xAD, 0x9E, 0x53, 0x0E,
+	0xE5, 0xDB, 0x38, 0x2F, 0x41, 0x30, 0x01, 0xAE,
+	0xB0, 0x6A, 0x53, 0xED, 0x90, 0x27, 0xD8, 0x31,
+	0x17, 0x97, 0x27, 0xB0, 0x86, 0x5A, 0x89, 0x18,
+	0xDA, 0x3E, 0xDB, 0xEB, 0xCF, 0x9B, 0x14, 0xED,
+	0x44, 0xCE, 0x6C, 0xBA, 0xCE, 0xD4, 0xBB, 0x1B,
+	0xDB, 0x7F, 0x14, 0x47, 0xE6, 0xCC, 0x25, 0x4B,
+	0x33, 0x20, 0x51, 0x51, 0x2B, 0xD7, 0xAF, 0x42,
+	0x6F, 0xB8, 0xF4, 0x01, 0x37, 0x8C, 0xD2, 0xBF,
+	0x59, 0x83, 0xCA, 0x01, 0xC6, 0x4B, 0x92, 0xEC,
+	0xF0, 0x32, 0xEA, 0x15, 0xD1, 0x72, 0x1D, 0x03,
+	0xF4, 0x82, 0xD7, 0xCE, 0x6E, 0x74, 0xFE, 0xF6,
+	0xD5, 0x5E, 0x70, 0x2F, 0x46, 0x98, 0x0C, 0x82,
+	0xB5, 0xA8, 0x40, 0x31, 0x90, 0x0B, 0x1C, 0x9E,
+	0x59, 0xE7, 0xC9, 0x7F, 0xBE, 0xC7, 0xE8, 0xF3,
+	0x23, 0xA9, 0x7A, 0x7E, 0x36, 0xCC, 0x88, 0xBE,
+	0x0F, 0x1D, 0x45, 0xB7, 0xFF, 0x58, 0x5A, 0xC5,
+	0x4B, 0xD4, 0x07, 0xB2, 0x2B, 0x41, 0x54, 0xAA,
+	0xCC, 0x8F, 0x6D, 0x7E, 0xBF, 0x48, 0xE1, 0xD8,
+	0x14, 0xCC, 0x5E, 0xD2, 0x0F, 0x80, 0x37, 0xE0,
+	0xA7, 0x97, 0x15, 0xEE, 0xF2, 0x9B, 0xE3, 0x28,
+	0x06, 0xA1, 0xD5, 0x8B, 0xB7, 0xC5, 0xDA, 0x76,
+	0xF5, 0x50, 0xAA, 0x3D, 0x8A, 0x1F, 0xBF, 0xF0,
+	0xEB, 0x19, 0xCC, 0xB1, 0xA3, 0x13, 0xD5, 0x5C,
+	0xDA, 0x56, 0xC9, 0xEC, 0x2E, 0xF2, 0x96, 0x32,
+	0x38, 0x7F, 0xE8, 0xD7, 0x6E, 0x3C, 0x04, 0x68,
+	0x04, 0x3E, 0x8F, 0x66, 0x3F, 0x48, 0x60, 0xEE,
+	0x12, 0xBF, 0x2D, 0x5B, 0x0B, 0x74, 0x74, 0xD6,
+	0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xCC, 0x40, 0x24,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+static const u8 dh_group17_order[768] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
+	0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
+	0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
+	0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
+	0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
+	0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
+	0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
+	0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
+	0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16,
+	0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19,
+	0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32,
+	0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85,
+	0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E,
+	0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63,
+	0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB,
+	0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE,
+	0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35,
+	0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32,
+	0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32,
+	0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06,
+	0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6,
+	0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71,
+	0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98,
+	0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47,
+	0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00,
+	0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B,
+	0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93,
+	0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E,
+	0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED,
+	0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74,
+	0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C,
+	0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53,
+	0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E,
+	0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1,
+	0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6,
+	0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7,
+	0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E,
+	0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54,
+	0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0,
+	0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47,
+	0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x01, 0x42, 0x49,
+	0x1B, 0x61, 0xFD, 0x5A, 0x69, 0x3E, 0x38, 0x13,
+	0x60, 0xEA, 0x6E, 0x59, 0x30, 0x13, 0x23, 0x6F,
+	0x64, 0xBA, 0x8F, 0x3B, 0x1E, 0xDD, 0x1B, 0xDE,
+	0xFC, 0x7F, 0xCA, 0x03, 0x56, 0xCF, 0x29, 0x87,
+	0x72, 0xED, 0x9C, 0x17, 0xA0, 0x98, 0x00, 0xD7,
+	0x58, 0x35, 0x29, 0xF6, 0xC8, 0x13, 0xEC, 0x18,
+	0x8B, 0xCB, 0x93, 0xD8, 0x43, 0x2D, 0x44, 0x8C,
+	0x6D, 0x1F, 0x6D, 0xF5, 0xE7, 0xCD, 0x8A, 0x76,
+	0xA2, 0x67, 0x36, 0x5D, 0x67, 0x6A, 0x5D, 0x8D,
+	0xED, 0xBF, 0x8A, 0x23, 0xF3, 0x66, 0x12, 0xA5,
+	0x99, 0x90, 0x28, 0xA8, 0x95, 0xEB, 0xD7, 0xA1,
+	0x37, 0xDC, 0x7A, 0x00, 0x9B, 0xC6, 0x69, 0x5F,
+	0xAC, 0xC1, 0xE5, 0x00, 0xE3, 0x25, 0xC9, 0x76,
+	0x78, 0x19, 0x75, 0x0A, 0xE8, 0xB9, 0x0E, 0x81,
+	0xFA, 0x41, 0x6B, 0xE7, 0x37, 0x3A, 0x7F, 0x7B,
+	0x6A, 0xAF, 0x38, 0x17, 0xA3, 0x4C, 0x06, 0x41,
+	0x5A, 0xD4, 0x20, 0x18, 0xC8, 0x05, 0x8E, 0x4F,
+	0x2C, 0xF3, 0xE4, 0xBF, 0xDF, 0x63, 0xF4, 0x79,
+	0x91, 0xD4, 0xBD, 0x3F, 0x1B, 0x66, 0x44, 0x5F,
+	0x07, 0x8E, 0xA2, 0xDB, 0xFF, 0xAC, 0x2D, 0x62,
+	0xA5, 0xEA, 0x03, 0xD9, 0x15, 0xA0, 0xAA, 0x55,
+	0x66, 0x47, 0xB6, 0xBF, 0x5F, 0xA4, 0x70, 0xEC,
+	0x0A, 0x66, 0x2F, 0x69, 0x07, 0xC0, 0x1B, 0xF0,
+	0x53, 0xCB, 0x8A, 0xF7, 0x79, 0x4D, 0xF1, 0x94,
+	0x03, 0x50, 0xEA, 0xC5, 0xDB, 0xE2, 0xED, 0x3B,
+	0x7A, 0xA8, 0x55, 0x1E, 0xC5, 0x0F, 0xDF, 0xF8,
+	0x75, 0x8C, 0xE6, 0x58, 0xD1, 0x89, 0xEA, 0xAE,
+	0x6D, 0x2B, 0x64, 0xF6, 0x17, 0x79, 0x4B, 0x19,
+	0x1C, 0x3F, 0xF4, 0x6B, 0xB7, 0x1E, 0x02, 0x34,
+	0x02, 0x1F, 0x47, 0xB3, 0x1F, 0xA4, 0x30, 0x77,
+	0x09, 0x5F, 0x96, 0xAD, 0x85, 0xBA, 0x3A, 0x6B,
+	0x73, 0x4A, 0x7C, 0x8F, 0x36, 0xE6, 0x20, 0x12,
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/* RFC 3526, 7. Group 18 - 8192 Bit MODP
+ * Generator: 2
+ * Prime: 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 }
+ */
+static const u8 dh_group18_generator[1] = { 0x02 };
+static const u8 dh_group18_prime[1024] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+	0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+	0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+	0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+	0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+	0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+	0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+	0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+	0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+	0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+	0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+	0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+	0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+	0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+	0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+	0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+	0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+	0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+	0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+	0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+	0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+	0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+	0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+	0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+	0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+	0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+	0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+	0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+	0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+	0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+	0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+	0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+	0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+	0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+	0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+	0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+	0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+	0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+	0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+	0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+	0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+	0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+	0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+	0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+	0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+	0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,
+	0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
+	0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,
+	0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,
+	0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
+	0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,
+	0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,
+	0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
+	0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,
+	0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,
+	0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
+	0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,
+	0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,
+	0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
+	0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,
+	0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,
+	0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x02, 0x84, 0x92,
+	0x36, 0xC3, 0xFA, 0xB4, 0xD2, 0x7C, 0x70, 0x26,
+	0xC1, 0xD4, 0xDC, 0xB2, 0x60, 0x26, 0x46, 0xDE,
+	0xC9, 0x75, 0x1E, 0x76, 0x3D, 0xBA, 0x37, 0xBD,
+	0xF8, 0xFF, 0x94, 0x06, 0xAD, 0x9E, 0x53, 0x0E,
+	0xE5, 0xDB, 0x38, 0x2F, 0x41, 0x30, 0x01, 0xAE,
+	0xB0, 0x6A, 0x53, 0xED, 0x90, 0x27, 0xD8, 0x31,
+	0x17, 0x97, 0x27, 0xB0, 0x86, 0x5A, 0x89, 0x18,
+	0xDA, 0x3E, 0xDB, 0xEB, 0xCF, 0x9B, 0x14, 0xED,
+	0x44, 0xCE, 0x6C, 0xBA, 0xCE, 0xD4, 0xBB, 0x1B,
+	0xDB, 0x7F, 0x14, 0x47, 0xE6, 0xCC, 0x25, 0x4B,
+	0x33, 0x20, 0x51, 0x51, 0x2B, 0xD7, 0xAF, 0x42,
+	0x6F, 0xB8, 0xF4, 0x01, 0x37, 0x8C, 0xD2, 0xBF,
+	0x59, 0x83, 0xCA, 0x01, 0xC6, 0x4B, 0x92, 0xEC,
+	0xF0, 0x32, 0xEA, 0x15, 0xD1, 0x72, 0x1D, 0x03,
+	0xF4, 0x82, 0xD7, 0xCE, 0x6E, 0x74, 0xFE, 0xF6,
+	0xD5, 0x5E, 0x70, 0x2F, 0x46, 0x98, 0x0C, 0x82,
+	0xB5, 0xA8, 0x40, 0x31, 0x90, 0x0B, 0x1C, 0x9E,
+	0x59, 0xE7, 0xC9, 0x7F, 0xBE, 0xC7, 0xE8, 0xF3,
+	0x23, 0xA9, 0x7A, 0x7E, 0x36, 0xCC, 0x88, 0xBE,
+	0x0F, 0x1D, 0x45, 0xB7, 0xFF, 0x58, 0x5A, 0xC5,
+	0x4B, 0xD4, 0x07, 0xB2, 0x2B, 0x41, 0x54, 0xAA,
+	0xCC, 0x8F, 0x6D, 0x7E, 0xBF, 0x48, 0xE1, 0xD8,
+	0x14, 0xCC, 0x5E, 0xD2, 0x0F, 0x80, 0x37, 0xE0,
+	0xA7, 0x97, 0x15, 0xEE, 0xF2, 0x9B, 0xE3, 0x28,
+	0x06, 0xA1, 0xD5, 0x8B, 0xB7, 0xC5, 0xDA, 0x76,
+	0xF5, 0x50, 0xAA, 0x3D, 0x8A, 0x1F, 0xBF, 0xF0,
+	0xEB, 0x19, 0xCC, 0xB1, 0xA3, 0x13, 0xD5, 0x5C,
+	0xDA, 0x56, 0xC9, 0xEC, 0x2E, 0xF2, 0x96, 0x32,
+	0x38, 0x7F, 0xE8, 0xD7, 0x6E, 0x3C, 0x04, 0x68,
+	0x04, 0x3E, 0x8F, 0x66, 0x3F, 0x48, 0x60, 0xEE,
+	0x12, 0xBF, 0x2D, 0x5B, 0x0B, 0x74, 0x74, 0xD6,
+	0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xBE, 0x11, 0x59,
+	0x74, 0xA3, 0x92, 0x6F, 0x12, 0xFE, 0xE5, 0xE4,
+	0x38, 0x77, 0x7C, 0xB6, 0xA9, 0x32, 0xDF, 0x8C,
+	0xD8, 0xBE, 0xC4, 0xD0, 0x73, 0xB9, 0x31, 0xBA,
+	0x3B, 0xC8, 0x32, 0xB6, 0x8D, 0x9D, 0xD3, 0x00,
+	0x74, 0x1F, 0xA7, 0xBF, 0x8A, 0xFC, 0x47, 0xED,
+	0x25, 0x76, 0xF6, 0x93, 0x6B, 0xA4, 0x24, 0x66,
+	0x3A, 0xAB, 0x63, 0x9C, 0x5A, 0xE4, 0xF5, 0x68,
+	0x34, 0x23, 0xB4, 0x74, 0x2B, 0xF1, 0xC9, 0x78,
+	0x23, 0x8F, 0x16, 0xCB, 0xE3, 0x9D, 0x65, 0x2D,
+	0xE3, 0xFD, 0xB8, 0xBE, 0xFC, 0x84, 0x8A, 0xD9,
+	0x22, 0x22, 0x2E, 0x04, 0xA4, 0x03, 0x7C, 0x07,
+	0x13, 0xEB, 0x57, 0xA8, 0x1A, 0x23, 0xF0, 0xC7,
+	0x34, 0x73, 0xFC, 0x64, 0x6C, 0xEA, 0x30, 0x6B,
+	0x4B, 0xCB, 0xC8, 0x86, 0x2F, 0x83, 0x85, 0xDD,
+	0xFA, 0x9D, 0x4B, 0x7F, 0xA2, 0xC0, 0x87, 0xE8,
+	0x79, 0x68, 0x33, 0x03, 0xED, 0x5B, 0xDD, 0x3A,
+	0x06, 0x2B, 0x3C, 0xF5, 0xB3, 0xA2, 0x78, 0xA6,
+	0x6D, 0x2A, 0x13, 0xF8, 0x3F, 0x44, 0xF8, 0x2D,
+	0xDF, 0x31, 0x0E, 0xE0, 0x74, 0xAB, 0x6A, 0x36,
+	0x45, 0x97, 0xE8, 0x99, 0xA0, 0x25, 0x5D, 0xC1,
+	0x64, 0xF3, 0x1C, 0xC5, 0x08, 0x46, 0x85, 0x1D,
+	0xF9, 0xAB, 0x48, 0x19, 0x5D, 0xED, 0x7E, 0xA1,
+	0xB1, 0xD5, 0x10, 0xBD, 0x7E, 0xE7, 0x4D, 0x73,
+	0xFA, 0xF3, 0x6B, 0xC3, 0x1E, 0xCF, 0xA2, 0x68,
+	0x35, 0x90, 0x46, 0xF4, 0xEB, 0x87, 0x9F, 0x92,
+	0x40, 0x09, 0x43, 0x8B, 0x48, 0x1C, 0x6C, 0xD7,
+	0x88, 0x9A, 0x00, 0x2E, 0xD5, 0xEE, 0x38, 0x2B,
+	0xC9, 0x19, 0x0D, 0xA6, 0xFC, 0x02, 0x6E, 0x47,
+	0x95, 0x58, 0xE4, 0x47, 0x56, 0x77, 0xE9, 0xAA,
+	0x9E, 0x30, 0x50, 0xE2, 0x76, 0x56, 0x94, 0xDF,
+	0xC8, 0x1F, 0x56, 0xE8, 0x80, 0xB9, 0x6E, 0x71,
+	0x60, 0xC9, 0x80, 0xDD, 0x98, 0xED, 0xD3, 0xDF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+static const u8 dh_group18_order[1024] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
+	0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
+	0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
+	0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
+	0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
+	0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
+	0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
+	0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
+	0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16,
+	0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19,
+	0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32,
+	0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85,
+	0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E,
+	0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63,
+	0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB,
+	0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE,
+	0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35,
+	0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32,
+	0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32,
+	0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06,
+	0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6,
+	0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71,
+	0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98,
+	0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47,
+	0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00,
+	0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B,
+	0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93,
+	0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E,
+	0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED,
+	0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74,
+	0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C,
+	0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53,
+	0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E,
+	0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1,
+	0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6,
+	0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7,
+	0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E,
+	0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54,
+	0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0,
+	0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47,
+	0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x01, 0x42, 0x49,
+	0x1B, 0x61, 0xFD, 0x5A, 0x69, 0x3E, 0x38, 0x13,
+	0x60, 0xEA, 0x6E, 0x59, 0x30, 0x13, 0x23, 0x6F,
+	0x64, 0xBA, 0x8F, 0x3B, 0x1E, 0xDD, 0x1B, 0xDE,
+	0xFC, 0x7F, 0xCA, 0x03, 0x56, 0xCF, 0x29, 0x87,
+	0x72, 0xED, 0x9C, 0x17, 0xA0, 0x98, 0x00, 0xD7,
+	0x58, 0x35, 0x29, 0xF6, 0xC8, 0x13, 0xEC, 0x18,
+	0x8B, 0xCB, 0x93, 0xD8, 0x43, 0x2D, 0x44, 0x8C,
+	0x6D, 0x1F, 0x6D, 0xF5, 0xE7, 0xCD, 0x8A, 0x76,
+	0xA2, 0x67, 0x36, 0x5D, 0x67, 0x6A, 0x5D, 0x8D,
+	0xED, 0xBF, 0x8A, 0x23, 0xF3, 0x66, 0x12, 0xA5,
+	0x99, 0x90, 0x28, 0xA8, 0x95, 0xEB, 0xD7, 0xA1,
+	0x37, 0xDC, 0x7A, 0x00, 0x9B, 0xC6, 0x69, 0x5F,
+	0xAC, 0xC1, 0xE5, 0x00, 0xE3, 0x25, 0xC9, 0x76,
+	0x78, 0x19, 0x75, 0x0A, 0xE8, 0xB9, 0x0E, 0x81,
+	0xFA, 0x41, 0x6B, 0xE7, 0x37, 0x3A, 0x7F, 0x7B,
+	0x6A, 0xAF, 0x38, 0x17, 0xA3, 0x4C, 0x06, 0x41,
+	0x5A, 0xD4, 0x20, 0x18, 0xC8, 0x05, 0x8E, 0x4F,
+	0x2C, 0xF3, 0xE4, 0xBF, 0xDF, 0x63, 0xF4, 0x79,
+	0x91, 0xD4, 0xBD, 0x3F, 0x1B, 0x66, 0x44, 0x5F,
+	0x07, 0x8E, 0xA2, 0xDB, 0xFF, 0xAC, 0x2D, 0x62,
+	0xA5, 0xEA, 0x03, 0xD9, 0x15, 0xA0, 0xAA, 0x55,
+	0x66, 0x47, 0xB6, 0xBF, 0x5F, 0xA4, 0x70, 0xEC,
+	0x0A, 0x66, 0x2F, 0x69, 0x07, 0xC0, 0x1B, 0xF0,
+	0x53, 0xCB, 0x8A, 0xF7, 0x79, 0x4D, 0xF1, 0x94,
+	0x03, 0x50, 0xEA, 0xC5, 0xDB, 0xE2, 0xED, 0x3B,
+	0x7A, 0xA8, 0x55, 0x1E, 0xC5, 0x0F, 0xDF, 0xF8,
+	0x75, 0x8C, 0xE6, 0x58, 0xD1, 0x89, 0xEA, 0xAE,
+	0x6D, 0x2B, 0x64, 0xF6, 0x17, 0x79, 0x4B, 0x19,
+	0x1C, 0x3F, 0xF4, 0x6B, 0xB7, 0x1E, 0x02, 0x34,
+	0x02, 0x1F, 0x47, 0xB3, 0x1F, 0xA4, 0x30, 0x77,
+	0x09, 0x5F, 0x96, 0xAD, 0x85, 0xBA, 0x3A, 0x6B,
+	0x73, 0x4A, 0x7C, 0x8F, 0x36, 0xDF, 0x08, 0xAC,
+	0xBA, 0x51, 0xC9, 0x37, 0x89, 0x7F, 0x72, 0xF2,
+	0x1C, 0x3B, 0xBE, 0x5B, 0x54, 0x99, 0x6F, 0xC6,
+	0x6C, 0x5F, 0x62, 0x68, 0x39, 0xDC, 0x98, 0xDD,
+	0x1D, 0xE4, 0x19, 0x5B, 0x46, 0xCE, 0xE9, 0x80,
+	0x3A, 0x0F, 0xD3, 0xDF, 0xC5, 0x7E, 0x23, 0xF6,
+	0x92, 0xBB, 0x7B, 0x49, 0xB5, 0xD2, 0x12, 0x33,
+	0x1D, 0x55, 0xB1, 0xCE, 0x2D, 0x72, 0x7A, 0xB4,
+	0x1A, 0x11, 0xDA, 0x3A, 0x15, 0xF8, 0xE4, 0xBC,
+	0x11, 0xC7, 0x8B, 0x65, 0xF1, 0xCE, 0xB2, 0x96,
+	0xF1, 0xFE, 0xDC, 0x5F, 0x7E, 0x42, 0x45, 0x6C,
+	0x91, 0x11, 0x17, 0x02, 0x52, 0x01, 0xBE, 0x03,
+	0x89, 0xF5, 0xAB, 0xD4, 0x0D, 0x11, 0xF8, 0x63,
+	0x9A, 0x39, 0xFE, 0x32, 0x36, 0x75, 0x18, 0x35,
+	0xA5, 0xE5, 0xE4, 0x43, 0x17, 0xC1, 0xC2, 0xEE,
+	0xFD, 0x4E, 0xA5, 0xBF, 0xD1, 0x60, 0x43, 0xF4,
+	0x3C, 0xB4, 0x19, 0x81, 0xF6, 0xAD, 0xEE, 0x9D,
+	0x03, 0x15, 0x9E, 0x7A, 0xD9, 0xD1, 0x3C, 0x53,
+	0x36, 0x95, 0x09, 0xFC, 0x1F, 0xA2, 0x7C, 0x16,
+	0xEF, 0x98, 0x87, 0x70, 0x3A, 0x55, 0xB5, 0x1B,
+	0x22, 0xCB, 0xF4, 0x4C, 0xD0, 0x12, 0xAE, 0xE0,
+	0xB2, 0x79, 0x8E, 0x62, 0x84, 0x23, 0x42, 0x8E,
+	0xFC, 0xD5, 0xA4, 0x0C, 0xAE, 0xF6, 0xBF, 0x50,
+	0xD8, 0xEA, 0x88, 0x5E, 0xBF, 0x73, 0xA6, 0xB9,
+	0xFD, 0x79, 0xB5, 0xE1, 0x8F, 0x67, 0xD1, 0x34,
+	0x1A, 0xC8, 0x23, 0x7A, 0x75, 0xC3, 0xCF, 0xC9,
+	0x20, 0x04, 0xA1, 0xC5, 0xA4, 0x0E, 0x36, 0x6B,
+	0xC4, 0x4D, 0x00, 0x17, 0x6A, 0xF7, 0x1C, 0x15,
+	0xE4, 0x8C, 0x86, 0xD3, 0x7E, 0x01, 0x37, 0x23,
+	0xCA, 0xAC, 0x72, 0x23, 0xAB, 0x3B, 0xF4, 0xD5,
+	0x4F, 0x18, 0x28, 0x71, 0x3B, 0x2B, 0x4A, 0x6F,
+	0xE4, 0x0F, 0xAB, 0x74, 0x40, 0x5C, 0xB7, 0x38,
+	0xB0, 0x64, 0xC0, 0x6E, 0xCC, 0x76, 0xE9, 0xEF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/*
+ * RFC 5114, 2.1.
+ * Group 22 - 1024-bit MODP Group with 160-bit Prime Order Subgroup
+ */
+static const u8 dh_group22_generator[] = {
+	0xA4, 0xD1, 0xCB, 0xD5, 0xC3, 0xFD, 0x34, 0x12,
+	0x67, 0x65, 0xA4, 0x42, 0xEF, 0xB9, 0x99, 0x05,
+	0xF8, 0x10, 0x4D, 0xD2, 0x58, 0xAC, 0x50, 0x7F,
+	0xD6, 0x40, 0x6C, 0xFF, 0x14, 0x26, 0x6D, 0x31,
+	0x26, 0x6F, 0xEA, 0x1E, 0x5C, 0x41, 0x56, 0x4B,
+	0x77, 0x7E, 0x69, 0x0F, 0x55, 0x04, 0xF2, 0x13,
+	0x16, 0x02, 0x17, 0xB4, 0xB0, 0x1B, 0x88, 0x6A,
+	0x5E, 0x91, 0x54, 0x7F, 0x9E, 0x27, 0x49, 0xF4,
+	0xD7, 0xFB, 0xD7, 0xD3, 0xB9, 0xA9, 0x2E, 0xE1,
+	0x90, 0x9D, 0x0D, 0x22, 0x63, 0xF8, 0x0A, 0x76,
+	0xA6, 0xA2, 0x4C, 0x08, 0x7A, 0x09, 0x1F, 0x53,
+	0x1D, 0xBF, 0x0A, 0x01, 0x69, 0xB6, 0xA2, 0x8A,
+	0xD6, 0x62, 0xA4, 0xD1, 0x8E, 0x73, 0xAF, 0xA3,
+	0x2D, 0x77, 0x9D, 0x59, 0x18, 0xD0, 0x8B, 0xC8,
+	0x85, 0x8F, 0x4D, 0xCE, 0xF9, 0x7C, 0x2A, 0x24,
+	0x85, 0x5E, 0x6E, 0xEB, 0x22, 0xB3, 0xB2, 0xE5
+};
+static const u8 dh_group22_prime[] = {
+	0xB1, 0x0B, 0x8F, 0x96, 0xA0, 0x80, 0xE0, 0x1D,
+	0xDE, 0x92, 0xDE, 0x5E, 0xAE, 0x5D, 0x54, 0xEC,
+	0x52, 0xC9, 0x9F, 0xBC, 0xFB, 0x06, 0xA3, 0xC6,
+	0x9A, 0x6A, 0x9D, 0xCA, 0x52, 0xD2, 0x3B, 0x61,
+	0x60, 0x73, 0xE2, 0x86, 0x75, 0xA2, 0x3D, 0x18,
+	0x98, 0x38, 0xEF, 0x1E, 0x2E, 0xE6, 0x52, 0xC0,
+	0x13, 0xEC, 0xB4, 0xAE, 0xA9, 0x06, 0x11, 0x23,
+	0x24, 0x97, 0x5C, 0x3C, 0xD4, 0x9B, 0x83, 0xBF,
+	0xAC, 0xCB, 0xDD, 0x7D, 0x90, 0xC4, 0xBD, 0x70,
+	0x98, 0x48, 0x8E, 0x9C, 0x21, 0x9A, 0x73, 0x72,
+	0x4E, 0xFF, 0xD6, 0xFA, 0xE5, 0x64, 0x47, 0x38,
+	0xFA, 0xA3, 0x1A, 0x4F, 0xF5, 0x5B, 0xCC, 0xC0,
+	0xA1, 0x51, 0xAF, 0x5F, 0x0D, 0xC8, 0xB4, 0xBD,
+	0x45, 0xBF, 0x37, 0xDF, 0x36, 0x5C, 0x1A, 0x65,
+	0xE6, 0x8C, 0xFD, 0xA7, 0x6D, 0x4D, 0xA7, 0x08,
+	0xDF, 0x1F, 0xB2, 0xBC, 0x2E, 0x4A, 0x43, 0x71
+};
+static const u8 dh_group22_order[] = {
+	0xF5, 0x18, 0xAA, 0x87, 0x81, 0xA8, 0xDF, 0x27,
+	0x8A, 0xBA, 0x4E, 0x7D, 0x64, 0xB7, 0xCB, 0x9D,
+	0x49, 0x46, 0x23, 0x53
+};
+
+/*
+ * RFC 5114, 2.2.
+ * Group 23 - 2048-bit MODP Group with 224-bit Prime Order Subgroup
+ */
+static const u8 dh_group23_generator[] = {
+	0xAC, 0x40, 0x32, 0xEF, 0x4F, 0x2D, 0x9A, 0xE3,
+	0x9D, 0xF3, 0x0B, 0x5C, 0x8F, 0xFD, 0xAC, 0x50,
+	0x6C, 0xDE, 0xBE, 0x7B, 0x89, 0x99, 0x8C, 0xAF,
+	0x74, 0x86, 0x6A, 0x08, 0xCF, 0xE4, 0xFF, 0xE3,
+	0xA6, 0x82, 0x4A, 0x4E, 0x10, 0xB9, 0xA6, 0xF0,
+	0xDD, 0x92, 0x1F, 0x01, 0xA7, 0x0C, 0x4A, 0xFA,
+	0xAB, 0x73, 0x9D, 0x77, 0x00, 0xC2, 0x9F, 0x52,
+	0xC5, 0x7D, 0xB1, 0x7C, 0x62, 0x0A, 0x86, 0x52,
+	0xBE, 0x5E, 0x90, 0x01, 0xA8, 0xD6, 0x6A, 0xD7,
+	0xC1, 0x76, 0x69, 0x10, 0x19, 0x99, 0x02, 0x4A,
+	0xF4, 0xD0, 0x27, 0x27, 0x5A, 0xC1, 0x34, 0x8B,
+	0xB8, 0xA7, 0x62, 0xD0, 0x52, 0x1B, 0xC9, 0x8A,
+	0xE2, 0x47, 0x15, 0x04, 0x22, 0xEA, 0x1E, 0xD4,
+	0x09, 0x93, 0x9D, 0x54, 0xDA, 0x74, 0x60, 0xCD,
+	0xB5, 0xF6, 0xC6, 0xB2, 0x50, 0x71, 0x7C, 0xBE,
+	0xF1, 0x80, 0xEB, 0x34, 0x11, 0x8E, 0x98, 0xD1,
+	0x19, 0x52, 0x9A, 0x45, 0xD6, 0xF8, 0x34, 0x56,
+	0x6E, 0x30, 0x25, 0xE3, 0x16, 0xA3, 0x30, 0xEF,
+	0xBB, 0x77, 0xA8, 0x6F, 0x0C, 0x1A, 0xB1, 0x5B,
+	0x05, 0x1A, 0xE3, 0xD4, 0x28, 0xC8, 0xF8, 0xAC,
+	0xB7, 0x0A, 0x81, 0x37, 0x15, 0x0B, 0x8E, 0xEB,
+	0x10, 0xE1, 0x83, 0xED, 0xD1, 0x99, 0x63, 0xDD,
+	0xD9, 0xE2, 0x63, 0xE4, 0x77, 0x05, 0x89, 0xEF,
+	0x6A, 0xA2, 0x1E, 0x7F, 0x5F, 0x2F, 0xF3, 0x81,
+	0xB5, 0x39, 0xCC, 0xE3, 0x40, 0x9D, 0x13, 0xCD,
+	0x56, 0x6A, 0xFB, 0xB4, 0x8D, 0x6C, 0x01, 0x91,
+	0x81, 0xE1, 0xBC, 0xFE, 0x94, 0xB3, 0x02, 0x69,
+	0xED, 0xFE, 0x72, 0xFE, 0x9B, 0x6A, 0xA4, 0xBD,
+	0x7B, 0x5A, 0x0F, 0x1C, 0x71, 0xCF, 0xFF, 0x4C,
+	0x19, 0xC4, 0x18, 0xE1, 0xF6, 0xEC, 0x01, 0x79,
+	0x81, 0xBC, 0x08, 0x7F, 0x2A, 0x70, 0x65, 0xB3,
+	0x84, 0xB8, 0x90, 0xD3, 0x19, 0x1F, 0x2B, 0xFA
+};
+static const u8 dh_group23_prime[] = {
+	0xAD, 0x10, 0x7E, 0x1E, 0x91, 0x23, 0xA9, 0xD0,
+	0xD6, 0x60, 0xFA, 0xA7, 0x95, 0x59, 0xC5, 0x1F,
+	0xA2, 0x0D, 0x64, 0xE5, 0x68, 0x3B, 0x9F, 0xD1,
+	0xB5, 0x4B, 0x15, 0x97, 0xB6, 0x1D, 0x0A, 0x75,
+	0xE6, 0xFA, 0x14, 0x1D, 0xF9, 0x5A, 0x56, 0xDB,
+	0xAF, 0x9A, 0x3C, 0x40, 0x7B, 0xA1, 0xDF, 0x15,
+	0xEB, 0x3D, 0x68, 0x8A, 0x30, 0x9C, 0x18, 0x0E,
+	0x1D, 0xE6, 0xB8, 0x5A, 0x12, 0x74, 0xA0, 0xA6,
+	0x6D, 0x3F, 0x81, 0x52, 0xAD, 0x6A, 0xC2, 0x12,
+	0x90, 0x37, 0xC9, 0xED, 0xEF, 0xDA, 0x4D, 0xF8,
+	0xD9, 0x1E, 0x8F, 0xEF, 0x55, 0xB7, 0x39, 0x4B,
+	0x7A, 0xD5, 0xB7, 0xD0, 0xB6, 0xC1, 0x22, 0x07,
+	0xC9, 0xF9, 0x8D, 0x11, 0xED, 0x34, 0xDB, 0xF6,
+	0xC6, 0xBA, 0x0B, 0x2C, 0x8B, 0xBC, 0x27, 0xBE,
+	0x6A, 0x00, 0xE0, 0xA0, 0xB9, 0xC4, 0x97, 0x08,
+	0xB3, 0xBF, 0x8A, 0x31, 0x70, 0x91, 0x88, 0x36,
+	0x81, 0x28, 0x61, 0x30, 0xBC, 0x89, 0x85, 0xDB,
+	0x16, 0x02, 0xE7, 0x14, 0x41, 0x5D, 0x93, 0x30,
+	0x27, 0x82, 0x73, 0xC7, 0xDE, 0x31, 0xEF, 0xDC,
+	0x73, 0x10, 0xF7, 0x12, 0x1F, 0xD5, 0xA0, 0x74,
+	0x15, 0x98, 0x7D, 0x9A, 0xDC, 0x0A, 0x48, 0x6D,
+	0xCD, 0xF9, 0x3A, 0xCC, 0x44, 0x32, 0x83, 0x87,
+	0x31, 0x5D, 0x75, 0xE1, 0x98, 0xC6, 0x41, 0xA4,
+	0x80, 0xCD, 0x86, 0xA1, 0xB9, 0xE5, 0x87, 0xE8,
+	0xBE, 0x60, 0xE6, 0x9C, 0xC9, 0x28, 0xB2, 0xB9,
+	0xC5, 0x21, 0x72, 0xE4, 0x13, 0x04, 0x2E, 0x9B,
+	0x23, 0xF1, 0x0B, 0x0E, 0x16, 0xE7, 0x97, 0x63,
+	0xC9, 0xB5, 0x3D, 0xCF, 0x4B, 0xA8, 0x0A, 0x29,
+	0xE3, 0xFB, 0x73, 0xC1, 0x6B, 0x8E, 0x75, 0xB9,
+	0x7E, 0xF3, 0x63, 0xE2, 0xFF, 0xA3, 0x1F, 0x71,
+	0xCF, 0x9D, 0xE5, 0x38, 0x4E, 0x71, 0xB8, 0x1C,
+	0x0A, 0xC4, 0xDF, 0xFE, 0x0C, 0x10, 0xE6, 0x4F
+};
+static const u8 dh_group23_order[] = {
+	0x80, 0x1C, 0x0D, 0x34, 0xC5, 0x8D, 0x93, 0xFE,
+	0x99, 0x71, 0x77, 0x10, 0x1F, 0x80, 0x53, 0x5A,
+	0x47, 0x38, 0xCE, 0xBC, 0xBF, 0x38, 0x9A, 0x99,
+	0xB3, 0x63, 0x71, 0xEB
+};
+
+/*
+ * RFC 5114, 2.3.
+ * Group 24 - 2048-bit MODP Group with 256-bit Prime Order Subgroup
+ */
+static const u8 dh_group24_generator[] = {
+	0x3F, 0xB3, 0x2C, 0x9B, 0x73, 0x13, 0x4D, 0x0B,
+	0x2E, 0x77, 0x50, 0x66, 0x60, 0xED, 0xBD, 0x48,
+	0x4C, 0xA7, 0xB1, 0x8F, 0x21, 0xEF, 0x20, 0x54,
+	0x07, 0xF4, 0x79, 0x3A, 0x1A, 0x0B, 0xA1, 0x25,
+	0x10, 0xDB, 0xC1, 0x50, 0x77, 0xBE, 0x46, 0x3F,
+	0xFF, 0x4F, 0xED, 0x4A, 0xAC, 0x0B, 0xB5, 0x55,
+	0xBE, 0x3A, 0x6C, 0x1B, 0x0C, 0x6B, 0x47, 0xB1,
+	0xBC, 0x37, 0x73, 0xBF, 0x7E, 0x8C, 0x6F, 0x62,
+	0x90, 0x12, 0x28, 0xF8, 0xC2, 0x8C, 0xBB, 0x18,
+	0xA5, 0x5A, 0xE3, 0x13, 0x41, 0x00, 0x0A, 0x65,
+	0x01, 0x96, 0xF9, 0x31, 0xC7, 0x7A, 0x57, 0xF2,
+	0xDD, 0xF4, 0x63, 0xE5, 0xE9, 0xEC, 0x14, 0x4B,
+	0x77, 0x7D, 0xE6, 0x2A, 0xAA, 0xB8, 0xA8, 0x62,
+	0x8A, 0xC3, 0x76, 0xD2, 0x82, 0xD6, 0xED, 0x38,
+	0x64, 0xE6, 0x79, 0x82, 0x42, 0x8E, 0xBC, 0x83,
+	0x1D, 0x14, 0x34, 0x8F, 0x6F, 0x2F, 0x91, 0x93,
+	0xB5, 0x04, 0x5A, 0xF2, 0x76, 0x71, 0x64, 0xE1,
+	0xDF, 0xC9, 0x67, 0xC1, 0xFB, 0x3F, 0x2E, 0x55,
+	0xA4, 0xBD, 0x1B, 0xFF, 0xE8, 0x3B, 0x9C, 0x80,
+	0xD0, 0x52, 0xB9, 0x85, 0xD1, 0x82, 0xEA, 0x0A,
+	0xDB, 0x2A, 0x3B, 0x73, 0x13, 0xD3, 0xFE, 0x14,
+	0xC8, 0x48, 0x4B, 0x1E, 0x05, 0x25, 0x88, 0xB9,
+	0xB7, 0xD2, 0xBB, 0xD2, 0xDF, 0x01, 0x61, 0x99,
+	0xEC, 0xD0, 0x6E, 0x15, 0x57, 0xCD, 0x09, 0x15,
+	0xB3, 0x35, 0x3B, 0xBB, 0x64, 0xE0, 0xEC, 0x37,
+	0x7F, 0xD0, 0x28, 0x37, 0x0D, 0xF9, 0x2B, 0x52,
+	0xC7, 0x89, 0x14, 0x28, 0xCD, 0xC6, 0x7E, 0xB6,
+	0x18, 0x4B, 0x52, 0x3D, 0x1D, 0xB2, 0x46, 0xC3,
+	0x2F, 0x63, 0x07, 0x84, 0x90, 0xF0, 0x0E, 0xF8,
+	0xD6, 0x47, 0xD1, 0x48, 0xD4, 0x79, 0x54, 0x51,
+	0x5E, 0x23, 0x27, 0xCF, 0xEF, 0x98, 0xC5, 0x82,
+	0x66, 0x4B, 0x4C, 0x0F, 0x6C, 0xC4, 0x16, 0x59
+};
+static const u8 dh_group24_prime[] = {
+	0x87, 0xA8, 0xE6, 0x1D, 0xB4, 0xB6, 0x66, 0x3C,
+	0xFF, 0xBB, 0xD1, 0x9C, 0x65, 0x19, 0x59, 0x99,
+	0x8C, 0xEE, 0xF6, 0x08, 0x66, 0x0D, 0xD0, 0xF2,
+	0x5D, 0x2C, 0xEE, 0xD4, 0x43, 0x5E, 0x3B, 0x00,
+	0xE0, 0x0D, 0xF8, 0xF1, 0xD6, 0x19, 0x57, 0xD4,
+	0xFA, 0xF7, 0xDF, 0x45, 0x61, 0xB2, 0xAA, 0x30,
+	0x16, 0xC3, 0xD9, 0x11, 0x34, 0x09, 0x6F, 0xAA,
+	0x3B, 0xF4, 0x29, 0x6D, 0x83, 0x0E, 0x9A, 0x7C,
+	0x20, 0x9E, 0x0C, 0x64, 0x97, 0x51, 0x7A, 0xBD,
+	0x5A, 0x8A, 0x9D, 0x30, 0x6B, 0xCF, 0x67, 0xED,
+	0x91, 0xF9, 0xE6, 0x72, 0x5B, 0x47, 0x58, 0xC0,
+	0x22, 0xE0, 0xB1, 0xEF, 0x42, 0x75, 0xBF, 0x7B,
+	0x6C, 0x5B, 0xFC, 0x11, 0xD4, 0x5F, 0x90, 0x88,
+	0xB9, 0x41, 0xF5, 0x4E, 0xB1, 0xE5, 0x9B, 0xB8,
+	0xBC, 0x39, 0xA0, 0xBF, 0x12, 0x30, 0x7F, 0x5C,
+	0x4F, 0xDB, 0x70, 0xC5, 0x81, 0xB2, 0x3F, 0x76,
+	0xB6, 0x3A, 0xCA, 0xE1, 0xCA, 0xA6, 0xB7, 0x90,
+	0x2D, 0x52, 0x52, 0x67, 0x35, 0x48, 0x8A, 0x0E,
+	0xF1, 0x3C, 0x6D, 0x9A, 0x51, 0xBF, 0xA4, 0xAB,
+	0x3A, 0xD8, 0x34, 0x77, 0x96, 0x52, 0x4D, 0x8E,
+	0xF6, 0xA1, 0x67, 0xB5, 0xA4, 0x18, 0x25, 0xD9,
+	0x67, 0xE1, 0x44, 0xE5, 0x14, 0x05, 0x64, 0x25,
+	0x1C, 0xCA, 0xCB, 0x83, 0xE6, 0xB4, 0x86, 0xF6,
+	0xB3, 0xCA, 0x3F, 0x79, 0x71, 0x50, 0x60, 0x26,
+	0xC0, 0xB8, 0x57, 0xF6, 0x89, 0x96, 0x28, 0x56,
+	0xDE, 0xD4, 0x01, 0x0A, 0xBD, 0x0B, 0xE6, 0x21,
+	0xC3, 0xA3, 0x96, 0x0A, 0x54, 0xE7, 0x10, 0xC3,
+	0x75, 0xF2, 0x63, 0x75, 0xD7, 0x01, 0x41, 0x03,
+	0xA4, 0xB5, 0x43, 0x30, 0xC1, 0x98, 0xAF, 0x12,
+	0x61, 0x16, 0xD2, 0x27, 0x6E, 0x11, 0x71, 0x5F,
+	0x69, 0x38, 0x77, 0xFA, 0xD7, 0xEF, 0x09, 0xCA,
+	0xDB, 0x09, 0x4A, 0xE9, 0x1E, 0x1A, 0x15, 0x97
+};
+static const u8 dh_group24_order[] = {
+	0x8C, 0xF8, 0x36, 0x42, 0xA7, 0x09, 0xA0, 0x97,
+	0xB4, 0x47, 0x99, 0x76, 0x40, 0x12, 0x9D, 0xA2,
+	0x99, 0xB1, 0xA4, 0x7D, 0x1E, 0xB3, 0x75, 0x0B,
+	0xA3, 0x08, 0xB0, 0xFE, 0x64, 0xF5, 0xFB, 0xD3
+};
+
+#endif /* ALL_DH_GROUPS */
+
+
+#define DH_GROUP(id,safe) \
+{ id, dh_group ## id ## _generator, sizeof(dh_group ## id ## _generator), \
+dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime), \
+dh_group ## id ## _order, sizeof(dh_group ## id ## _order), safe }
+
+
+static const struct dh_group dh_groups[] = {
+	DH_GROUP(5, 1),
+#ifdef ALL_DH_GROUPS
+	DH_GROUP(1, 1),
+	DH_GROUP(2, 1),
+	DH_GROUP(14, 1),
+	DH_GROUP(15, 1),
+	DH_GROUP(16, 1),
+	DH_GROUP(17, 1),
+	DH_GROUP(18, 1),
+	DH_GROUP(22, 0),
+	DH_GROUP(23, 0),
+	DH_GROUP(24, 0)
+#endif /* ALL_DH_GROUPS */
+};
+
+#define NUM_DH_GROUPS ARRAY_SIZE(dh_groups)
+
+
+const struct dh_group * dh_groups_get(int id)
+{
+	size_t i;
+
+	for (i = 0; i < NUM_DH_GROUPS; i++) {
+		if (dh_groups[i].id == id)
+			return &dh_groups[i];
+	}
+	return NULL;
+}
+
+
+/**
+ * dh_init - Initialize Diffie-Hellman handshake
+ * @dh: Selected Diffie-Hellman group
+ * @priv: Pointer for returning Diffie-Hellman private key
+ * Returns: Diffie-Hellman public value
+ */
+struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv)
+{
+	struct wpabuf *pv;
+	size_t pv_len;
+
+	if (dh == NULL)
+		return NULL;
+
+	wpabuf_clear_free(*priv);
+	*priv = wpabuf_alloc(dh->prime_len);
+	if (*priv == NULL)
+		return NULL;
+
+	pv_len = dh->prime_len;
+	pv = wpabuf_alloc(pv_len);
+	if (pv == NULL) {
+		wpabuf_clear_free(*priv);
+		*priv = NULL;
+		return NULL;
+	}
+	if (crypto_dh_init(*dh->generator, dh->prime, dh->prime_len,
+			   wpabuf_mhead(*priv), wpabuf_mhead(pv)) < 0) {
+		wpabuf_clear_free(pv);
+		wpa_printf(MSG_INFO, "DH: crypto_dh_init failed");
+		wpabuf_clear_free(*priv);
+		*priv = NULL;
+		return NULL;
+	}
+	wpabuf_put(*priv, dh->prime_len);
+	wpabuf_put(pv, dh->prime_len);
+	wpa_hexdump_buf_key(MSG_DEBUG, "DH: private value", *priv);
+	wpa_hexdump_buf(MSG_DEBUG, "DH: public value", pv);
+
+	return pv;
+}
+
+
+/**
+ * dh_derive_shared - Derive shared Diffie-Hellman key
+ * @peer_public: Diffie-Hellman public value from peer
+ * @own_private: Diffie-Hellman private key from dh_init()
+ * @dh: Selected Diffie-Hellman group
+ * Returns: Diffie-Hellman shared key
+ */
+struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public,
+				 const struct wpabuf *own_private,
+				 const struct dh_group *dh)
+{
+	struct wpabuf *shared;
+	size_t shared_len;
+
+	if (dh == NULL || peer_public == NULL || own_private == NULL)
+		return NULL;
+
+	shared_len = dh->prime_len;
+	shared = wpabuf_alloc(shared_len);
+	if (shared == NULL)
+		return NULL;
+	if (crypto_dh_derive_secret(*dh->generator, dh->prime, dh->prime_len,
+				    dh->order, dh->order_len,
+				    wpabuf_head(own_private),
+				    wpabuf_len(own_private),
+				    wpabuf_head(peer_public),
+				    wpabuf_len(peer_public),
+				    wpabuf_mhead(shared), &shared_len) < 0) {
+		wpabuf_clear_free(shared);
+		wpa_printf(MSG_INFO, "DH: crypto_dh_derive_secret failed");
+		return NULL;
+	}
+	wpabuf_put(shared, shared_len);
+	wpa_hexdump_buf_key(MSG_DEBUG, "DH: shared key", shared);
+
+	return shared;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/dh_groups.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/dh_groups.h
new file mode 100644
index 0000000..d0e74b9
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/dh_groups.h
@@ -0,0 +1,29 @@
+/*
+ * Diffie-Hellman groups
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DH_GROUPS_H
+#define DH_GROUPS_H
+
+struct dh_group {
+	int id;
+	const u8 *generator;
+	size_t generator_len;
+	const u8 *prime;
+	size_t prime_len;
+	const u8 *order;
+	size_t order_len;
+	unsigned int safe_prime:1;
+};
+
+const struct dh_group * dh_groups_get(int id);
+struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv);
+struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public,
+				 const struct wpabuf *own_private,
+				 const struct dh_group *dh);
+
+#endif /* DH_GROUPS_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/fips_prf_internal.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/fips_prf_internal.c
new file mode 100644
index 0000000..f9347d0
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/fips_prf_internal.c
@@ -0,0 +1,66 @@
+/*
+ * FIPS 186-2 PRF for internal crypto implementation
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+#include "sha1_i.h"
+#include "crypto.h"
+
+
+int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
+{
+	u8 xkey[64];
+	u32 _t[5];
+	int i, j, m, k;
+	u8 *xpos = x;
+	u32 carry;
+	struct SHA1Context ctx;
+
+	if (seed_len < sizeof(xkey))
+		os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len);
+	else
+		seed_len = sizeof(xkey);
+
+	/* FIPS 186-2 + change notice 1 */
+
+	os_memcpy(xkey, seed, seed_len);
+	SHA1Init(&ctx);
+
+	m = xlen / 40;
+	for (j = 0; j < m; j++) {
+		/* XSEED_j = 0 */
+		for (i = 0; i < 2; i++) {
+			/* XVAL = (XKEY + XSEED_j) mod 2^b */
+
+			/* w_i = G(t, XVAL) */
+			os_memcpy(_t, ctx.state, 20);
+			SHA1Transform(_t, xkey);
+			_t[0] = host_to_be32(_t[0]);
+			_t[1] = host_to_be32(_t[1]);
+			_t[2] = host_to_be32(_t[2]);
+			_t[3] = host_to_be32(_t[3]);
+			_t[4] = host_to_be32(_t[4]);
+			os_memcpy(xpos, _t, 20);
+
+			/* XKEY = (1 + XKEY + w_i) mod 2^b */
+			carry = 1;
+			for (k = 19; k >= 0; k--) {
+				carry += xkey[k] + xpos[k];
+				xkey[k] = carry & 0xff;
+				carry >>= 8;
+			}
+
+			xpos += SHA1_MAC_LEN;
+		}
+		/* x_j = w_0|w_1 */
+	}
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/fips_prf_openssl.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/fips_prf_openssl.c
new file mode 100644
index 0000000..484f772
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/fips_prf_openssl.c
@@ -0,0 +1,114 @@
+/*
+ * FIPS 186-2 PRF for libcrypto
+ * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <openssl/opensslv.h>
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+
+/* OpenSSL 3.0 has deprecated the low-level SHA1 functions and does not
+ * include an upper layer interface that could be used to use the
+ * SHA1_Transform() function. Use the internal SHA-1 implementation instead
+ * as a workaround. */
+#include "sha1-internal.c"
+#include "fips_prf_internal.c"
+
+#else /* OpenSSL version >= 3.0 */
+
+#include <openssl/sha.h>
+
+#include "common.h"
+#include "crypto.h"
+
+
+static void sha1_transform(u32 *state, const u8 data[64])
+{
+	SHA_CTX context;
+	os_memset(&context, 0, sizeof(context));
+#if defined(OPENSSL_IS_BORINGSSL) && !defined(ANDROID)
+	context.h[0] = state[0];
+	context.h[1] = state[1];
+	context.h[2] = state[2];
+	context.h[3] = state[3];
+	context.h[4] = state[4];
+	SHA1_Transform(&context, data);
+	state[0] = context.h[0];
+	state[1] = context.h[1];
+	state[2] = context.h[2];
+	state[3] = context.h[3];
+	state[4] = context.h[4];
+#else
+	context.h0 = state[0];
+	context.h1 = state[1];
+	context.h2 = state[2];
+	context.h3 = state[3];
+	context.h4 = state[4];
+	SHA1_Transform(&context, data);
+	state[0] = context.h0;
+	state[1] = context.h1;
+	state[2] = context.h2;
+	state[3] = context.h3;
+	state[4] = context.h4;
+#endif
+}
+
+
+int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
+{
+	u8 xkey[64];
+	u32 t[5], _t[5];
+	int i, j, m, k;
+	u8 *xpos = x;
+	u32 carry;
+
+	if (seed_len < sizeof(xkey))
+		os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len);
+	else
+		seed_len = sizeof(xkey);
+
+	/* FIPS 186-2 + change notice 1 */
+
+	os_memcpy(xkey, seed, seed_len);
+	t[0] = 0x67452301;
+	t[1] = 0xEFCDAB89;
+	t[2] = 0x98BADCFE;
+	t[3] = 0x10325476;
+	t[4] = 0xC3D2E1F0;
+
+	m = xlen / 40;
+	for (j = 0; j < m; j++) {
+		/* XSEED_j = 0 */
+		for (i = 0; i < 2; i++) {
+			/* XVAL = (XKEY + XSEED_j) mod 2^b */
+
+			/* w_i = G(t, XVAL) */
+			os_memcpy(_t, t, 20);
+			sha1_transform(_t, xkey);
+			WPA_PUT_BE32(xpos, _t[0]);
+			WPA_PUT_BE32(xpos + 4, _t[1]);
+			WPA_PUT_BE32(xpos + 8, _t[2]);
+			WPA_PUT_BE32(xpos + 12, _t[3]);
+			WPA_PUT_BE32(xpos + 16, _t[4]);
+
+			/* XKEY = (1 + XKEY + w_i) mod 2^b */
+			carry = 1;
+			for (k = 19; k >= 0; k--) {
+				carry += xkey[k] + xpos[k];
+				xkey[k] = carry & 0xff;
+				carry >>= 8;
+			}
+
+			xpos += 20;
+		}
+		/* x_j = w_0|w_1 */
+	}
+
+	return 0;
+}
+
+#endif /* OpenSSL version >= 3.0 */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/fips_prf_wolfssl.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/fips_prf_wolfssl.c
new file mode 100644
index 0000000..feb39db
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/fips_prf_wolfssl.c
@@ -0,0 +1,87 @@
+/*
+ * FIPS 186-2 PRF for libcrypto
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <wolfssl/options.h>
+#include <wolfssl/wolfcrypt/sha.h>
+
+#include "common.h"
+#include "crypto.h"
+
+
+static void sha1_transform(u32 *state, const u8 data[64])
+{
+	wc_Sha sha;
+
+	os_memset(&sha, 0, sizeof(sha));
+	sha.digest[0] = state[0];
+	sha.digest[1] = state[1];
+	sha.digest[2] = state[2];
+	sha.digest[3] = state[3];
+	sha.digest[4] = state[4];
+	wc_ShaUpdate(&sha, data, 64);
+	state[0] = sha.digest[0];
+	state[1] = sha.digest[1];
+	state[2] = sha.digest[2];
+	state[3] = sha.digest[3];
+	state[4] = sha.digest[4];
+}
+
+
+int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
+{
+	u8 xkey[64];
+	u32 t[5], _t[5];
+	int i, j, m, k;
+	u8 *xpos = x;
+	u32 carry;
+
+	if (seed_len < sizeof(xkey))
+		os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len);
+	else
+		seed_len = sizeof(xkey);
+
+	/* FIPS 186-2 + change notice 1 */
+
+	os_memcpy(xkey, seed, seed_len);
+	t[0] = 0x67452301;
+	t[1] = 0xEFCDAB89;
+	t[2] = 0x98BADCFE;
+	t[3] = 0x10325476;
+	t[4] = 0xC3D2E1F0;
+
+	m = xlen / 40;
+	for (j = 0; j < m; j++) {
+		/* XSEED_j = 0 */
+		for (i = 0; i < 2; i++) {
+			/* XVAL = (XKEY + XSEED_j) mod 2^b */
+
+			/* w_i = G(t, XVAL) */
+			os_memcpy(_t, t, 20);
+			sha1_transform(_t, xkey);
+			WPA_PUT_BE32(xpos, _t[0]);
+			WPA_PUT_BE32(xpos + 4, _t[1]);
+			WPA_PUT_BE32(xpos + 8, _t[2]);
+			WPA_PUT_BE32(xpos + 12, _t[3]);
+			WPA_PUT_BE32(xpos + 16, _t[4]);
+
+			/* XKEY = (1 + XKEY + w_i) mod 2^b */
+			carry = 1;
+			for (k = 19; k >= 0; k--) {
+				carry += xkey[k] + xpos[k];
+				xkey[k] = carry & 0xff;
+				carry >>= 8;
+			}
+
+			xpos += 20;
+		}
+		/* x_j = w_0|w_1 */
+	}
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/md4-internal.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/md4-internal.c
new file mode 100644
index 0000000..cf408e8
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/md4-internal.c
@@ -0,0 +1,275 @@
+/*
+ * MD4 hash implementation
+ * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+
+#define	MD4_BLOCK_LENGTH		64
+#define	MD4_DIGEST_LENGTH		16
+
+typedef struct MD4Context {
+	u32 state[4];			/* state */
+	u64 count;			/* number of bits, mod 2^64 */
+	u8 buffer[MD4_BLOCK_LENGTH];	/* input buffer */
+} MD4_CTX;
+
+
+static void MD4Init(MD4_CTX *ctx);
+static void MD4Update(MD4_CTX *ctx, const unsigned char *input, size_t len);
+static void MD4Final(unsigned char digest[MD4_DIGEST_LENGTH], MD4_CTX *ctx);
+
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	MD4_CTX ctx;
+	size_t i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	MD4Init(&ctx);
+	for (i = 0; i < num_elem; i++)
+		MD4Update(&ctx, addr[i], len[i]);
+	MD4Final(mac, &ctx);
+	return 0;
+}
+
+
+/* ===== start - public domain MD4 implementation ===== */
+/*	$OpenBSD: md4.c,v 1.7 2005/08/08 08:05:35 espie Exp $	*/
+
+/*
+ * This code implements the MD4 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.	This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ * Todd C. Miller modified the MD5 code to do MD4 based on RFC 1186.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD4Context structure, pass it to MD4Init, call MD4Update as
+ * needed on buffers full of bytes, and then call MD4Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#define	MD4_DIGEST_STRING_LENGTH	(MD4_DIGEST_LENGTH * 2 + 1)
+
+
+static void
+MD4Transform(u32 state[4], const u8 block[MD4_BLOCK_LENGTH]);
+
+#define PUT_64BIT_LE(cp, value) do {					\
+	(cp)[7] = (value) >> 56;					\
+	(cp)[6] = (value) >> 48;					\
+	(cp)[5] = (value) >> 40;					\
+	(cp)[4] = (value) >> 32;					\
+	(cp)[3] = (value) >> 24;					\
+	(cp)[2] = (value) >> 16;					\
+	(cp)[1] = (value) >> 8;						\
+	(cp)[0] = (value); } while (0)
+
+#define PUT_32BIT_LE(cp, value) do {					\
+	(cp)[3] = (value) >> 24;					\
+	(cp)[2] = (value) >> 16;					\
+	(cp)[1] = (value) >> 8;						\
+	(cp)[0] = (value); } while (0)
+
+static const u8 PADDING[MD4_BLOCK_LENGTH] = {
+	0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/*
+ * Start MD4 accumulation.
+ * Set bit count to 0 and buffer to mysterious initialization constants.
+ */
+static void MD4Init(MD4_CTX *ctx)
+{
+	ctx->count = 0;
+	ctx->state[0] = 0x67452301;
+	ctx->state[1] = 0xefcdab89;
+	ctx->state[2] = 0x98badcfe;
+	ctx->state[3] = 0x10325476;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+static void MD4Update(MD4_CTX *ctx, const unsigned char *input, size_t len)
+{
+	size_t have, need;
+
+	/* Check how many bytes we already have and how many more we need. */
+	have = (size_t)((ctx->count >> 3) & (MD4_BLOCK_LENGTH - 1));
+	need = MD4_BLOCK_LENGTH - have;
+
+	/* Update bitcount */
+	ctx->count += (u64)len << 3;
+
+	if (len >= need) {
+		if (have != 0) {
+			os_memcpy(ctx->buffer + have, input, need);
+			MD4Transform(ctx->state, ctx->buffer);
+			input += need;
+			len -= need;
+			have = 0;
+		}
+
+		/* Process data in MD4_BLOCK_LENGTH-byte chunks. */
+		while (len >= MD4_BLOCK_LENGTH) {
+			MD4Transform(ctx->state, input);
+			input += MD4_BLOCK_LENGTH;
+			len -= MD4_BLOCK_LENGTH;
+		}
+	}
+
+	/* Handle any remaining bytes of data. */
+	if (len != 0)
+		os_memcpy(ctx->buffer + have, input, len);
+}
+
+/*
+ * Pad pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+static void MD4Pad(MD4_CTX *ctx)
+{
+	u8 count[8];
+	size_t padlen;
+
+	/* Convert count to 8 bytes in little endian order. */
+	PUT_64BIT_LE(count, ctx->count);
+
+	/* Pad out to 56 mod 64. */
+	padlen = MD4_BLOCK_LENGTH -
+	    ((ctx->count >> 3) & (MD4_BLOCK_LENGTH - 1));
+	if (padlen < 1 + 8)
+		padlen += MD4_BLOCK_LENGTH;
+	MD4Update(ctx, PADDING, padlen - 8);		/* padlen - 8 <= 64 */
+	MD4Update(ctx, count, 8);
+}
+
+/*
+ * Final wrapup--call MD4Pad, fill in digest and zero out ctx.
+ */
+static void MD4Final(unsigned char digest[MD4_DIGEST_LENGTH], MD4_CTX *ctx)
+{
+	int i;
+
+	MD4Pad(ctx);
+	if (digest != NULL) {
+		for (i = 0; i < 4; i++)
+			PUT_32BIT_LE(digest + i * 4, ctx->state[i]);
+		os_memset(ctx, 0, sizeof(*ctx));
+	}
+}
+
+
+/* The three core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) ((x & y) | (x & z) | (y & z))
+#define F3(x, y, z) (x ^ y ^ z)
+
+/* This is the central step in the MD4 algorithm. */
+#define MD4STEP(f, w, x, y, z, data, s) \
+	( w += f(x, y, z) + data,  w = w<<s | w>>(32-s) )
+
+/*
+ * The core of the MD4 algorithm, this alters an existing MD4 hash to
+ * reflect the addition of 16 longwords of new data.  MD4Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void
+MD4Transform(u32 state[4], const u8 block[MD4_BLOCK_LENGTH])
+{
+	u32 a, b, c, d, in[MD4_BLOCK_LENGTH / 4];
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+	os_memcpy(in, block, sizeof(in));
+#else
+	for (a = 0; a < MD4_BLOCK_LENGTH / 4; a++) {
+		in[a] = (u32)(
+		    (u32)(block[a * 4 + 0]) |
+		    (u32)(block[a * 4 + 1]) <<  8 |
+		    (u32)(block[a * 4 + 2]) << 16 |
+		    (u32)(block[a * 4 + 3]) << 24);
+	}
+#endif
+
+	a = state[0];
+	b = state[1];
+	c = state[2];
+	d = state[3];
+
+	MD4STEP(F1, a, b, c, d, in[ 0],  3);
+	MD4STEP(F1, d, a, b, c, in[ 1],  7);
+	MD4STEP(F1, c, d, a, b, in[ 2], 11);
+	MD4STEP(F1, b, c, d, a, in[ 3], 19);
+	MD4STEP(F1, a, b, c, d, in[ 4],  3);
+	MD4STEP(F1, d, a, b, c, in[ 5],  7);
+	MD4STEP(F1, c, d, a, b, in[ 6], 11);
+	MD4STEP(F1, b, c, d, a, in[ 7], 19);
+	MD4STEP(F1, a, b, c, d, in[ 8],  3);
+	MD4STEP(F1, d, a, b, c, in[ 9],  7);
+	MD4STEP(F1, c, d, a, b, in[10], 11);
+	MD4STEP(F1, b, c, d, a, in[11], 19);
+	MD4STEP(F1, a, b, c, d, in[12],  3);
+	MD4STEP(F1, d, a, b, c, in[13],  7);
+	MD4STEP(F1, c, d, a, b, in[14], 11);
+	MD4STEP(F1, b, c, d, a, in[15], 19);
+
+	MD4STEP(F2, a, b, c, d, in[ 0] + 0x5a827999,  3);
+	MD4STEP(F2, d, a, b, c, in[ 4] + 0x5a827999,  5);
+	MD4STEP(F2, c, d, a, b, in[ 8] + 0x5a827999,  9);
+	MD4STEP(F2, b, c, d, a, in[12] + 0x5a827999, 13);
+	MD4STEP(F2, a, b, c, d, in[ 1] + 0x5a827999,  3);
+	MD4STEP(F2, d, a, b, c, in[ 5] + 0x5a827999,  5);
+	MD4STEP(F2, c, d, a, b, in[ 9] + 0x5a827999,  9);
+	MD4STEP(F2, b, c, d, a, in[13] + 0x5a827999, 13);
+	MD4STEP(F2, a, b, c, d, in[ 2] + 0x5a827999,  3);
+	MD4STEP(F2, d, a, b, c, in[ 6] + 0x5a827999,  5);
+	MD4STEP(F2, c, d, a, b, in[10] + 0x5a827999,  9);
+	MD4STEP(F2, b, c, d, a, in[14] + 0x5a827999, 13);
+	MD4STEP(F2, a, b, c, d, in[ 3] + 0x5a827999,  3);
+	MD4STEP(F2, d, a, b, c, in[ 7] + 0x5a827999,  5);
+	MD4STEP(F2, c, d, a, b, in[11] + 0x5a827999,  9);
+	MD4STEP(F2, b, c, d, a, in[15] + 0x5a827999, 13);
+
+	MD4STEP(F3, a, b, c, d, in[ 0] + 0x6ed9eba1,  3);
+	MD4STEP(F3, d, a, b, c, in[ 8] + 0x6ed9eba1,  9);
+	MD4STEP(F3, c, d, a, b, in[ 4] + 0x6ed9eba1, 11);
+	MD4STEP(F3, b, c, d, a, in[12] + 0x6ed9eba1, 15);
+	MD4STEP(F3, a, b, c, d, in[ 2] + 0x6ed9eba1,  3);
+	MD4STEP(F3, d, a, b, c, in[10] + 0x6ed9eba1,  9);
+	MD4STEP(F3, c, d, a, b, in[ 6] + 0x6ed9eba1, 11);
+	MD4STEP(F3, b, c, d, a, in[14] + 0x6ed9eba1, 15);
+	MD4STEP(F3, a, b, c, d, in[ 1] + 0x6ed9eba1,  3);
+	MD4STEP(F3, d, a, b, c, in[ 9] + 0x6ed9eba1,  9);
+	MD4STEP(F3, c, d, a, b, in[ 5] + 0x6ed9eba1, 11);
+	MD4STEP(F3, b, c, d, a, in[13] + 0x6ed9eba1, 15);
+	MD4STEP(F3, a, b, c, d, in[ 3] + 0x6ed9eba1,  3);
+	MD4STEP(F3, d, a, b, c, in[11] + 0x6ed9eba1,  9);
+	MD4STEP(F3, c, d, a, b, in[ 7] + 0x6ed9eba1, 11);
+	MD4STEP(F3, b, c, d, a, in[15] + 0x6ed9eba1, 15);
+
+	state[0] += a;
+	state[1] += b;
+	state[2] += c;
+	state[3] += d;
+}
+/* ===== end - public domain MD4 implementation ===== */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/md5-internal.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/md5-internal.c
new file mode 100644
index 0000000..944698a
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/md5-internal.c
@@ -0,0 +1,290 @@
+/*
+ * MD5 hash implementation and interface functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "md5.h"
+#include "md5_i.h"
+#include "crypto.h"
+
+
+static void MD5Transform(u32 buf[4], u32 const in[16]);
+
+
+typedef struct MD5Context MD5_CTX;
+
+
+/**
+ * md5_vector - MD5 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 of failure
+ */
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	MD5_CTX ctx;
+	size_t i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	MD5Init(&ctx);
+	for (i = 0; i < num_elem; i++)
+		MD5Update(&ctx, addr[i], len[i]);
+	MD5Final(mac, &ctx);
+	return 0;
+}
+
+
+/* ===== start - public domain MD5 implementation ===== */
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#ifndef WORDS_BIGENDIAN
+#define byteReverse(buf, len)	/* Nothing */
+#else
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void byteReverse(unsigned char *buf, unsigned longs)
+{
+    u32 t;
+    do {
+	t = (u32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+	    ((unsigned) buf[1] << 8 | buf[0]);
+	*(u32 *) buf = t;
+	buf += 4;
+    } while (--longs);
+}
+#endif
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+    ctx->buf[0] = 0x67452301;
+    ctx->buf[1] = 0xefcdab89;
+    ctx->buf[2] = 0x98badcfe;
+    ctx->buf[3] = 0x10325476;
+
+    ctx->bits[0] = 0;
+    ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+    u32 t;
+
+    /* Update bitcount */
+
+    t = ctx->bits[0];
+    if ((ctx->bits[0] = t + ((u32) len << 3)) < t)
+	ctx->bits[1]++;		/* Carry from low to high */
+    ctx->bits[1] += len >> 29;
+
+    t = (t >> 3) & 0x3f;	/* Bytes already in shsInfo->data */
+
+    /* Handle any leading odd-sized chunks */
+
+    if (t) {
+	unsigned char *p = (unsigned char *) ctx->in + t;
+
+	t = 64 - t;
+	if (len < t) {
+	    os_memcpy(p, buf, len);
+	    return;
+	}
+	os_memcpy(p, buf, t);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (u32 *) ctx->in);
+	buf += t;
+	len -= t;
+    }
+    /* Process data in 64-byte chunks */
+
+    while (len >= 64) {
+	os_memcpy(ctx->in, buf, 64);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (u32 *) ctx->in);
+	buf += 64;
+	len -= 64;
+    }
+
+    /* Handle any remaining bytes of data. */
+
+    os_memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+    unsigned count;
+    unsigned char *p;
+
+    /* Compute number of bytes mod 64 */
+    count = (ctx->bits[0] >> 3) & 0x3F;
+
+    /* Set the first char of padding to 0x80.  This is safe since there is
+       always at least one byte free */
+    p = ctx->in + count;
+    *p++ = 0x80;
+
+    /* Bytes of padding needed to make 64 bytes */
+    count = 64 - 1 - count;
+
+    /* Pad out to 56 mod 64 */
+    if (count < 8) {
+	/* Two lots of padding:  Pad the first block to 64 bytes */
+	os_memset(p, 0, count);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (u32 *) ctx->in);
+
+	/* Now fill the next block with 56 bytes */
+	os_memset(ctx->in, 0, 56);
+    } else {
+	/* Pad block to 56 bytes */
+	os_memset(p, 0, count - 8);
+    }
+    byteReverse(ctx->in, 14);
+
+    /* Append length in bits and transform */
+    ((u32 *) aliasing_hide_typecast(ctx->in, u32))[14] = ctx->bits[0];
+    ((u32 *) aliasing_hide_typecast(ctx->in, u32))[15] = ctx->bits[1];
+
+    MD5Transform(ctx->buf, (u32 *) ctx->in);
+    byteReverse((unsigned char *) ctx->buf, 4);
+    os_memcpy(digest, ctx->buf, 16);
+    os_memset(ctx, 0, sizeof(*ctx));	/* In case it's sensitive */
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+	( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void MD5Transform(u32 buf[4], u32 const in[16])
+{
+    register u32 a, b, c, d;
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+    MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
+/* ===== end - public domain MD5 implementation ===== */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/md5.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/md5.c
new file mode 100644
index 0000000..f64dfd3
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/md5.c
@@ -0,0 +1,109 @@
+/*
+ * MD5 hash implementation and interface functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "md5.h"
+#include "crypto.h"
+
+
+/**
+ * hmac_md5_vector - HMAC-MD5 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (16 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+		    const u8 *addr[], const size_t *len, u8 *mac)
+{
+	u8 k_pad[64]; /* padding - key XORd with ipad/opad */
+	u8 tk[16];
+	const u8 *_addr[6];
+	size_t i, _len[6];
+	int res;
+
+	if (num_elem > 5) {
+		/*
+		 * Fixed limit on the number of fragments to avoid having to
+		 * allocate memory (which could fail).
+		 */
+		return -1;
+	}
+
+        /* if key is longer than 64 bytes reset it to key = MD5(key) */
+        if (key_len > 64) {
+		if (md5_vector(1, &key, &key_len, tk))
+			return -1;
+		key = tk;
+		key_len = 16;
+        }
+
+	/* the HMAC_MD5 transform looks like:
+	 *
+	 * MD5(K XOR opad, MD5(K XOR ipad, text))
+	 *
+	 * where K is an n byte key
+	 * ipad is the byte 0x36 repeated 64 times
+	 * opad is the byte 0x5c repeated 64 times
+	 * and text is the data being protected */
+
+	/* start out by storing key in ipad */
+	os_memset(k_pad, 0, sizeof(k_pad));
+	os_memcpy(k_pad, key, key_len);
+
+	/* XOR key with ipad values */
+	for (i = 0; i < 64; i++)
+		k_pad[i] ^= 0x36;
+
+	/* perform inner MD5 */
+	_addr[0] = k_pad;
+	_len[0] = 64;
+	for (i = 0; i < num_elem; i++) {
+		_addr[i + 1] = addr[i];
+		_len[i + 1] = len[i];
+	}
+	if (md5_vector(1 + num_elem, _addr, _len, mac))
+		return -1;
+
+	os_memset(k_pad, 0, sizeof(k_pad));
+	os_memcpy(k_pad, key, key_len);
+	/* XOR key with opad values */
+	for (i = 0; i < 64; i++)
+		k_pad[i] ^= 0x5c;
+
+	/* perform outer MD5 */
+	_addr[0] = k_pad;
+	_len[0] = 64;
+	_addr[1] = mac;
+	_len[1] = MD5_MAC_LEN;
+	res = md5_vector(2, _addr, _len, mac);
+	os_memset(k_pad, 0, sizeof(k_pad));
+	os_memset(tk, 0, sizeof(tk));
+	return res;
+}
+
+
+/**
+ * hmac_md5 - HMAC-MD5 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (16 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	      u8 *mac)
+{
+	return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/md5.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/md5.h
new file mode 100644
index 0000000..33f8426
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/md5.h
@@ -0,0 +1,19 @@
+/*
+ * MD5 hash implementation and interface functions
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MD5_H
+#define MD5_H
+
+#define MD5_MAC_LEN 16
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+		    const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	     u8 *mac);
+
+#endif /* MD5_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/md5_i.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/md5_i.h
new file mode 100644
index 0000000..7dfc100
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/md5_i.h
@@ -0,0 +1,23 @@
+/*
+ * MD5 internal definitions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MD5_I_H
+#define MD5_I_H
+
+struct MD5Context {
+	u32 buf[4];
+	u32 bits[2];
+	u8 in[64];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, unsigned char const *buf,
+	       unsigned len);
+void MD5Final(unsigned char digest[16], struct MD5Context *context);
+
+#endif /* MD5_I_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/milenage.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/milenage.c
new file mode 100644
index 0000000..6edea57
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/milenage.c
@@ -0,0 +1,323 @@
+/*
+ * 3GPP AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208)
+ * Copyright (c) 2006-2007 <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file implements an example authentication algorithm defined for 3GPP
+ * AKA. This can be used to implement a simple HLR/AuC into hlr_auc_gw to allow
+ * EAP-AKA to be tested properly with real USIM cards.
+ *
+ * This implementations assumes that the r1..r5 and c1..c5 constants defined in
+ * TS 35.206 are used, i.e., r1=64, r2=0, r3=32, r4=64, r5=96, c1=00..00,
+ * c2=00..01, c3=00..02, c4=00..04, c5=00..08. The block cipher is assumed to
+ * be AES (Rijndael).
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "milenage.h"
+
+
+/**
+ * milenage_f1 - Milenage f1 and f1* algorithms
+ * @opc: OPc = 128-bit value derived from OP and K
+ * @k: K = 128-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @sqn: SQN = 48-bit sequence number
+ * @amf: AMF = 16-bit authentication management field
+ * @mac_a: Buffer for MAC-A = 64-bit network authentication code, or %NULL
+ * @mac_s: Buffer for MAC-S = 64-bit resync authentication code, or %NULL
+ * Returns: 0 on success, -1 on failure
+ */
+int milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand,
+		const u8 *sqn, const u8 *amf, u8 *mac_a, u8 *mac_s)
+{
+	u8 tmp1[16], tmp2[16], tmp3[16];
+	int i;
+
+	/* tmp1 = TEMP = E_K(RAND XOR OP_C) */
+	for (i = 0; i < 16; i++)
+		tmp1[i] = _rand[i] ^ opc[i];
+	if (aes_128_encrypt_block(k, tmp1, tmp1))
+		return -1;
+
+	/* tmp2 = IN1 = SQN || AMF || SQN || AMF */
+	os_memcpy(tmp2, sqn, 6);
+	os_memcpy(tmp2 + 6, amf, 2);
+	os_memcpy(tmp2 + 8, tmp2, 8);
+
+	/* OUT1 = E_K(TEMP XOR rot(IN1 XOR OP_C, r1) XOR c1) XOR OP_C */
+
+	/* rotate (tmp2 XOR OP_C) by r1 (= 0x40 = 8 bytes) */
+	for (i = 0; i < 16; i++)
+		tmp3[(i + 8) % 16] = tmp2[i] ^ opc[i];
+	/* XOR with TEMP = E_K(RAND XOR OP_C) */
+	for (i = 0; i < 16; i++)
+		tmp3[i] ^= tmp1[i];
+	/* XOR with c1 (= ..00, i.e., NOP) */
+
+	/* f1 || f1* = E_K(tmp3) XOR OP_c */
+	if (aes_128_encrypt_block(k, tmp3, tmp1))
+		return -1;
+	for (i = 0; i < 16; i++)
+		tmp1[i] ^= opc[i];
+	if (mac_a)
+		os_memcpy(mac_a, tmp1, 8); /* f1 */
+	if (mac_s)
+		os_memcpy(mac_s, tmp1 + 8, 8); /* f1* */
+	return 0;
+}
+
+
+/**
+ * milenage_f2345 - Milenage f2, f3, f4, f5, f5* algorithms
+ * @opc: OPc = 128-bit value derived from OP and K
+ * @k: K = 128-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @res: Buffer for RES = 64-bit signed response (f2), or %NULL
+ * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL
+ * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL
+ * @ak: Buffer for AK = 48-bit anonymity key (f5), or %NULL
+ * @akstar: Buffer for AK = 48-bit anonymity key (f5*), or %NULL
+ * Returns: 0 on success, -1 on failure
+ */
+int milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand,
+		   u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar)
+{
+	u8 tmp1[16], tmp2[16], tmp3[16];
+	int i;
+
+	/* tmp2 = TEMP = E_K(RAND XOR OP_C) */
+	for (i = 0; i < 16; i++)
+		tmp1[i] = _rand[i] ^ opc[i];
+	if (aes_128_encrypt_block(k, tmp1, tmp2))
+		return -1;
+
+	/* OUT2 = E_K(rot(TEMP XOR OP_C, r2) XOR c2) XOR OP_C */
+	/* OUT3 = E_K(rot(TEMP XOR OP_C, r3) XOR c3) XOR OP_C */
+	/* OUT4 = E_K(rot(TEMP XOR OP_C, r4) XOR c4) XOR OP_C */
+	/* OUT5 = E_K(rot(TEMP XOR OP_C, r5) XOR c5) XOR OP_C */
+
+	/* f2 and f5 */
+	/* rotate by r2 (= 0, i.e., NOP) */
+	for (i = 0; i < 16; i++)
+		tmp1[i] = tmp2[i] ^ opc[i];
+	tmp1[15] ^= 1; /* XOR c2 (= ..01) */
+	/* f5 || f2 = E_K(tmp1) XOR OP_c */
+	if (aes_128_encrypt_block(k, tmp1, tmp3))
+		return -1;
+	for (i = 0; i < 16; i++)
+		tmp3[i] ^= opc[i];
+	if (res)
+		os_memcpy(res, tmp3 + 8, 8); /* f2 */
+	if (ak)
+		os_memcpy(ak, tmp3, 6); /* f5 */
+
+	/* f3 */
+	if (ck) {
+		/* rotate by r3 = 0x20 = 4 bytes */
+		for (i = 0; i < 16; i++)
+			tmp1[(i + 12) % 16] = tmp2[i] ^ opc[i];
+		tmp1[15] ^= 2; /* XOR c3 (= ..02) */
+		if (aes_128_encrypt_block(k, tmp1, ck))
+			return -1;
+		for (i = 0; i < 16; i++)
+			ck[i] ^= opc[i];
+	}
+
+	/* f4 */
+	if (ik) {
+		/* rotate by r4 = 0x40 = 8 bytes */
+		for (i = 0; i < 16; i++)
+			tmp1[(i + 8) % 16] = tmp2[i] ^ opc[i];
+		tmp1[15] ^= 4; /* XOR c4 (= ..04) */
+		if (aes_128_encrypt_block(k, tmp1, ik))
+			return -1;
+		for (i = 0; i < 16; i++)
+			ik[i] ^= opc[i];
+	}
+
+	/* f5* */
+	if (akstar) {
+		/* rotate by r5 = 0x60 = 12 bytes */
+		for (i = 0; i < 16; i++)
+			tmp1[(i + 4) % 16] = tmp2[i] ^ opc[i];
+		tmp1[15] ^= 8; /* XOR c5 (= ..08) */
+		if (aes_128_encrypt_block(k, tmp1, tmp1))
+			return -1;
+		for (i = 0; i < 6; i++)
+			akstar[i] = tmp1[i] ^ opc[i];
+	}
+
+	return 0;
+}
+
+
+/**
+ * milenage_generate - Generate AKA AUTN,IK,CK,RES
+ * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.)
+ * @amf: AMF = 16-bit authentication management field
+ * @k: K = 128-bit subscriber key
+ * @sqn: SQN = 48-bit sequence number
+ * @_rand: RAND = 128-bit random challenge
+ * @autn: Buffer for AUTN = 128-bit authentication token
+ * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL
+ * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL
+ * @res: Buffer for RES = 64-bit signed response (f2), or %NULL
+ * @res_len: Max length for res; set to used length or 0 on failure
+ */
+void milenage_generate(const u8 *opc, const u8 *amf, const u8 *k,
+		       const u8 *sqn, const u8 *_rand, u8 *autn, u8 *ik,
+		       u8 *ck, u8 *res, size_t *res_len)
+{
+	int i;
+	u8 mac_a[8], ak[6];
+
+	if (*res_len < 8) {
+		*res_len = 0;
+		return;
+	}
+	if (milenage_f1(opc, k, _rand, sqn, amf, mac_a, NULL) ||
+	    milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL)) {
+		*res_len = 0;
+		return;
+	}
+	*res_len = 8;
+
+	/* AUTN = (SQN ^ AK) || AMF || MAC */
+	for (i = 0; i < 6; i++)
+		autn[i] = sqn[i] ^ ak[i];
+	os_memcpy(autn + 6, amf, 2);
+	os_memcpy(autn + 8, mac_a, 8);
+}
+
+
+/**
+ * milenage_auts - Milenage AUTS validation
+ * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.)
+ * @k: K = 128-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @auts: AUTS = 112-bit authentication token from client
+ * @sqn: Buffer for SQN = 48-bit sequence number
+ * Returns: 0 = success (sqn filled), -1 on failure
+ */
+int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts,
+		  u8 *sqn)
+{
+	u8 amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */
+	u8 ak[6], mac_s[8];
+	int i;
+
+	if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak))
+		return -1;
+	for (i = 0; i < 6; i++)
+		sqn[i] = auts[i] ^ ak[i];
+	if (milenage_f1(opc, k, _rand, sqn, amf, NULL, mac_s) ||
+	    os_memcmp_const(mac_s, auts + 6, 8) != 0)
+		return -1;
+	return 0;
+}
+
+
+/**
+ * gsm_milenage - Generate GSM-Milenage (3GPP TS 55.205) authentication triplet
+ * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.)
+ * @k: K = 128-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @sres: Buffer for SRES = 32-bit SRES
+ * @kc: Buffer for Kc = 64-bit Kc
+ * Returns: 0 on success, -1 on failure
+ */
+int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, u8 *kc)
+{
+	u8 res[8], ck[16], ik[16];
+	int i;
+
+	if (milenage_f2345(opc, k, _rand, res, ck, ik, NULL, NULL))
+		return -1;
+
+	for (i = 0; i < 8; i++)
+		kc[i] = ck[i] ^ ck[i + 8] ^ ik[i] ^ ik[i + 8];
+
+#ifdef GSM_MILENAGE_ALT_SRES
+	os_memcpy(sres, res, 4);
+#else /* GSM_MILENAGE_ALT_SRES */
+	for (i = 0; i < 4; i++)
+		sres[i] = res[i] ^ res[i + 4];
+#endif /* GSM_MILENAGE_ALT_SRES */
+	return 0;
+}
+
+
+/**
+ * milenage_generate - Generate AKA AUTN,IK,CK,RES
+ * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.)
+ * @k: K = 128-bit subscriber key
+ * @sqn: SQN = 48-bit sequence number
+ * @_rand: RAND = 128-bit random challenge
+ * @autn: AUTN = 128-bit authentication token
+ * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL
+ * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL
+ * @res: Buffer for RES = 64-bit signed response (f2), or %NULL
+ * @res_len: Variable that will be set to RES length
+ * @auts: 112-bit buffer for AUTS
+ * Returns: 0 on success, -1 on failure, or -2 on synchronization failure
+ */
+int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand,
+		   const u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len,
+		   u8 *auts)
+{
+	int i;
+	u8 mac_a[8], ak[6], rx_sqn[6];
+	const u8 *amf;
+
+	wpa_hexdump(MSG_DEBUG, "Milenage: AUTN", autn, 16);
+	wpa_hexdump(MSG_DEBUG, "Milenage: RAND", _rand, 16);
+
+	if (milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL))
+		return -1;
+
+	*res_len = 8;
+	wpa_hexdump_key(MSG_DEBUG, "Milenage: RES", res, *res_len);
+	wpa_hexdump_key(MSG_DEBUG, "Milenage: CK", ck, 16);
+	wpa_hexdump_key(MSG_DEBUG, "Milenage: IK", ik, 16);
+	wpa_hexdump_key(MSG_DEBUG, "Milenage: AK", ak, 6);
+
+	/* AUTN = (SQN ^ AK) || AMF || MAC */
+	for (i = 0; i < 6; i++)
+		rx_sqn[i] = autn[i] ^ ak[i];
+	wpa_hexdump(MSG_DEBUG, "Milenage: SQN", rx_sqn, 6);
+
+	if (os_memcmp(rx_sqn, sqn, 6) <= 0) {
+		u8 auts_amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */
+		if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak))
+			return -1;
+		wpa_hexdump_key(MSG_DEBUG, "Milenage: AK*", ak, 6);
+		for (i = 0; i < 6; i++)
+			auts[i] = sqn[i] ^ ak[i];
+		if (milenage_f1(opc, k, _rand, sqn, auts_amf, NULL, auts + 6))
+			return -1;
+		wpa_hexdump(MSG_DEBUG, "Milenage: AUTS", auts, 14);
+		return -2;
+	}
+
+	amf = autn + 6;
+	wpa_hexdump(MSG_DEBUG, "Milenage: AMF", amf, 2);
+	if (milenage_f1(opc, k, _rand, rx_sqn, amf, mac_a, NULL))
+		return -1;
+
+	wpa_hexdump(MSG_DEBUG, "Milenage: MAC_A", mac_a, 8);
+
+	if (os_memcmp_const(mac_a, autn + 8, 8) != 0) {
+		wpa_printf(MSG_DEBUG, "Milenage: MAC mismatch");
+		wpa_hexdump(MSG_DEBUG, "Milenage: Received MAC_A",
+			    autn + 8, 8);
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/milenage.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/milenage.h
new file mode 100644
index 0000000..62137d9
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/milenage.h
@@ -0,0 +1,27 @@
+/*
+ * UMTS AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208)
+ * Copyright (c) 2006-2007 <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MILENAGE_H
+#define MILENAGE_H
+
+void milenage_generate(const u8 *opc, const u8 *amf, const u8 *k,
+		       const u8 *sqn, const u8 *_rand, u8 *autn, u8 *ik,
+		       u8 *ck, u8 *res, size_t *res_len);
+int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts,
+		  u8 *sqn);
+int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres,
+		 u8 *kc);
+int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand,
+		   const u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len,
+		   u8 *auts);
+int milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand,
+		const u8 *sqn, const u8 *amf, u8 *mac_a, u8 *mac_s);
+int milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand,
+		   u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar);
+
+#endif /* MILENAGE_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/ms_funcs.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/ms_funcs.c
new file mode 100644
index 0000000..aff7d33
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/ms_funcs.c
@@ -0,0 +1,531 @@
+/*
+ * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+#include "ms_funcs.h"
+#include "crypto.h"
+
+/**
+ * utf8_to_ucs2 - Convert UTF-8 string to UCS-2 encoding
+ * @utf8_string: UTF-8 string (IN)
+ * @utf8_string_len: Length of utf8_string (IN)
+ * @ucs2_buffer: UCS-2 buffer (OUT)
+ * @ucs2_buffer_size: Length of UCS-2 buffer (IN)
+ * @ucs2_string_size: Number of 2-byte words in the resulting UCS-2 string
+ * Returns: 0 on success, -1 on failure
+ */
+static int utf8_to_ucs2(const u8 *utf8_string, size_t utf8_string_len,
+                        u8 *ucs2_buffer, size_t ucs2_buffer_size,
+                        size_t *ucs2_string_size)
+{
+	size_t i, j;
+
+	for (i = 0, j = 0; i < utf8_string_len; i++) {
+		u8 c = utf8_string[i];
+		if (j >= ucs2_buffer_size) {
+			/* input too long */
+			return -1;
+		}
+		if (c <= 0x7F) {
+			WPA_PUT_LE16(ucs2_buffer + j, c);
+			j += 2;
+		} else if (i == utf8_string_len - 1 ||
+			   j >= ucs2_buffer_size - 1) {
+			/* incomplete surrogate */
+			return -1;
+		} else {
+			u8 c2 = utf8_string[++i];
+			if ((c & 0xE0) == 0xC0) {
+				/* two-byte encoding */
+				WPA_PUT_LE16(ucs2_buffer + j,
+					     ((c & 0x1F) << 6) | (c2 & 0x3F));
+				j += 2;
+			} else if (i == utf8_string_len - 1 ||
+				   j >= ucs2_buffer_size - 1) {
+				/* incomplete surrogate */
+				return -1;
+			} else {
+				/* three-byte encoding */
+				u8 c3 = utf8_string[++i];
+				WPA_PUT_LE16(ucs2_buffer + j,
+					     ((c & 0xF) << 12) |
+					     ((c2 & 0x3F) << 6) | (c3 & 0x3F));
+				j += 2;
+			}
+		}
+	}
+
+	if (ucs2_string_size)
+		*ucs2_string_size = j / 2;
+	return 0;
+}
+
+
+/**
+ * challenge_hash - ChallengeHash() - RFC 2759, Sect. 8.2
+ * @peer_challenge: 16-octet PeerChallenge (IN)
+ * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
+ * @username: 0-to-256-char UserName (IN)
+ * @username_len: Length of username
+ * @challenge: 8-octet Challenge (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge,
+		   const u8 *username, size_t username_len, u8 *challenge)
+{
+	u8 hash[SHA1_MAC_LEN];
+	const unsigned char *addr[3];
+	size_t len[3];
+
+	addr[0] = peer_challenge;
+	len[0] = 16;
+	addr[1] = auth_challenge;
+	len[1] = 16;
+	addr[2] = username;
+	len[2] = username_len;
+
+	if (sha1_vector(3, addr, len, hash))
+		return -1;
+	os_memcpy(challenge, hash, 8);
+	return 0;
+}
+
+
+/**
+ * nt_password_hash - NtPasswordHash() - RFC 2759, Sect. 8.3
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
+ * @password_len: Length of password
+ * @password_hash: 16-octet PasswordHash (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int nt_password_hash(const u8 *password, size_t password_len,
+		      u8 *password_hash)
+{
+	u8 buf[512], *pos;
+	size_t len, max_len;
+
+	max_len = sizeof(buf);
+	if (utf8_to_ucs2(password, password_len, buf, max_len, &len) < 0)
+		return -1;
+
+	len *= 2;
+	pos = buf;
+	return md4_vector(1, (const u8 **) &pos, &len, password_hash);
+}
+
+
+/**
+ * hash_nt_password_hash - HashNtPasswordHash() - RFC 2759, Sect. 8.4
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @password_hash_hash: 16-octet PasswordHashHash (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash)
+{
+	size_t len = 16;
+	return md4_vector(1, &password_hash, &len, password_hash_hash);
+}
+
+
+/**
+ * challenge_response - ChallengeResponse() - RFC 2759, Sect. 8.5
+ * @challenge: 8-octet Challenge (IN)
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @response: 24-octet Response (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int challenge_response(const u8 *challenge, const u8 *password_hash,
+		       u8 *response)
+{
+	u8 zpwd[7];
+
+	if (des_encrypt(challenge, password_hash, response) < 0 ||
+	    des_encrypt(challenge, password_hash + 7, response + 8) < 0)
+		return -1;
+	zpwd[0] = password_hash[14];
+	zpwd[1] = password_hash[15];
+	os_memset(zpwd + 2, 0, 5);
+	return des_encrypt(challenge, zpwd, response + 16);
+}
+
+
+/**
+ * generate_nt_response - GenerateNTResponse() - RFC 2759, Sect. 8.1
+ * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
+ * @peer_challenge: 16-octet PeerChallenge (IN)
+ * @username: 0-to-256-char UserName (IN)
+ * @username_len: Length of username
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
+ * @password_len: Length of password
+ * @response: 24-octet Response (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge,
+			 const u8 *username, size_t username_len,
+			 const u8 *password, size_t password_len,
+			 u8 *response)
+{
+	u8 challenge[8];
+	u8 password_hash[16];
+
+	if (challenge_hash(peer_challenge, auth_challenge, username,
+			   username_len, challenge) ||
+	    nt_password_hash(password, password_len, password_hash) ||
+	    challenge_response(challenge, password_hash, response))
+		return -1;
+	return 0;
+}
+
+
+/**
+ * generate_nt_response_pwhash - GenerateNTResponse() - RFC 2759, Sect. 8.1
+ * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
+ * @peer_challenge: 16-octet PeerChallenge (IN)
+ * @username: 0-to-256-char UserName (IN)
+ * @username_len: Length of username
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @response: 24-octet Response (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int generate_nt_response_pwhash(const u8 *auth_challenge,
+				const u8 *peer_challenge,
+				const u8 *username, size_t username_len,
+				const u8 *password_hash,
+				u8 *response)
+{
+	u8 challenge[8];
+
+	if (challenge_hash(peer_challenge, auth_challenge,
+			   username, username_len,
+			   challenge) ||
+	    challenge_response(challenge, password_hash, response))
+		return -1;
+	return 0;
+}
+
+
+/**
+ * generate_authenticator_response_pwhash - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @nt_response: 24-octet NT-Response (IN)
+ * @peer_challenge: 16-octet PeerChallenge (IN)
+ * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
+ * @username: 0-to-256-char UserName (IN)
+ * @username_len: Length of username
+ * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually
+ * encoded as a 42-octet ASCII string (S=hexdump_of_response)
+ * Returns: 0 on success, -1 on failure
+ */
+int generate_authenticator_response_pwhash(
+	const u8 *password_hash,
+	const u8 *peer_challenge, const u8 *auth_challenge,
+	const u8 *username, size_t username_len,
+	const u8 *nt_response, u8 *response)
+{
+	static const u8 magic1[39] = {
+		0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
+		0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
+		0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
+		0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74
+	};
+	static const u8 magic2[41] = {
+		0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
+		0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
+		0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
+		0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
+		0x6E
+	};
+
+	u8 password_hash_hash[16], challenge[8];
+	const unsigned char *addr1[3];
+	const size_t len1[3] = { 16, 24, sizeof(magic1) };
+	const unsigned char *addr2[3];
+	const size_t len2[3] = { SHA1_MAC_LEN, 8, sizeof(magic2) };
+
+	addr1[0] = password_hash_hash;
+	addr1[1] = nt_response;
+	addr1[2] = magic1;
+
+	addr2[0] = response;
+	addr2[1] = challenge;
+	addr2[2] = magic2;
+
+	if (hash_nt_password_hash(password_hash, password_hash_hash) ||
+	    sha1_vector(3, addr1, len1, response) ||
+	    challenge_hash(peer_challenge, auth_challenge, username,
+			   username_len, challenge))
+		return -1;
+	return sha1_vector(3, addr2, len2, response);
+}
+
+
+/**
+ * generate_authenticator_response - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
+ * @password_len: Length of password
+ * @nt_response: 24-octet NT-Response (IN)
+ * @peer_challenge: 16-octet PeerChallenge (IN)
+ * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
+ * @username: 0-to-256-char UserName (IN)
+ * @username_len: Length of username
+ * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually
+ * encoded as a 42-octet ASCII string (S=hexdump_of_response)
+ * Returns: 0 on success, -1 on failure
+ */
+int generate_authenticator_response(const u8 *password, size_t password_len,
+				    const u8 *peer_challenge,
+				    const u8 *auth_challenge,
+				    const u8 *username, size_t username_len,
+				    const u8 *nt_response, u8 *response)
+{
+	u8 password_hash[16];
+	if (nt_password_hash(password, password_len, password_hash))
+		return -1;
+	return generate_authenticator_response_pwhash(
+		password_hash, peer_challenge, auth_challenge,
+		username, username_len, nt_response, response);
+}
+
+
+/**
+ * nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5
+ * @challenge: 8-octet Challenge (IN)
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
+ * @password_len: Length of password
+ * @response: 24-octet Response (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int nt_challenge_response(const u8 *challenge, const u8 *password,
+			  size_t password_len, u8 *response)
+{
+	u8 password_hash[16];
+
+	if (nt_password_hash(password, password_len, password_hash) ||
+	    challenge_response(challenge, password_hash, response))
+		return -1;
+	return 0;
+}
+
+
+/**
+ * get_master_key - GetMasterKey() - RFC 3079, Sect. 3.4
+ * @password_hash_hash: 16-octet PasswordHashHash (IN)
+ * @nt_response: 24-octet NTResponse (IN)
+ * @master_key: 16-octet MasterKey (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int get_master_key(const u8 *password_hash_hash, const u8 *nt_response,
+		   u8 *master_key)
+{
+	static const u8 magic1[27] = {
+		0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
+		0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
+		0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79
+	};
+	const unsigned char *addr[3];
+	const size_t len[3] = { 16, 24, sizeof(magic1) };
+	u8 hash[SHA1_MAC_LEN];
+
+	addr[0] = password_hash_hash;
+	addr[1] = nt_response;
+	addr[2] = magic1;
+
+	if (sha1_vector(3, addr, len, hash))
+		return -1;
+	os_memcpy(master_key, hash, 16);
+	return 0;
+}
+
+
+/**
+ * get_asymetric_start_key - GetAsymetricStartKey() - RFC 3079, Sect. 3.4
+ * @master_key: 16-octet MasterKey (IN)
+ * @session_key: 8-to-16 octet SessionKey (OUT)
+ * @session_key_len: SessionKeyLength (Length of session_key) (IN)
+ * @is_send: IsSend (IN, BOOLEAN)
+ * @is_server: IsServer (IN, BOOLEAN)
+ * Returns: 0 on success, -1 on failure
+ */
+int get_asymetric_start_key(const u8 *master_key, u8 *session_key,
+			    size_t session_key_len, int is_send,
+			    int is_server)
+{
+	static const u8 magic2[84] = {
+		0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+		0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+		0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+		0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
+		0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
+		0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
+		0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+		0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+		0x6b, 0x65, 0x79, 0x2e
+	};
+	static const u8 magic3[84] = {
+		0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+		0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+		0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+		0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+		0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
+		0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
+		0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
+		0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
+		0x6b, 0x65, 0x79, 0x2e
+	};
+	static const u8 shs_pad1[40] = {
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+	};
+
+	static const u8 shs_pad2[40] = {
+		0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+		0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+		0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+		0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2
+	};
+	u8 digest[SHA1_MAC_LEN];
+	const unsigned char *addr[4];
+	const size_t len[4] = { 16, 40, 84, 40 };
+
+	addr[0] = master_key;
+	addr[1] = shs_pad1;
+	if (is_send) {
+		addr[2] = is_server ? magic3 : magic2;
+	} else {
+		addr[2] = is_server ? magic2 : magic3;
+	}
+	addr[3] = shs_pad2;
+
+	if (sha1_vector(4, addr, len, digest))
+		return -1;
+
+	if (session_key_len > SHA1_MAC_LEN)
+		session_key_len = SHA1_MAC_LEN;
+	os_memcpy(session_key, digest, session_key_len);
+	return 0;
+}
+
+
+#ifndef CONFIG_NO_RC4
+
+#define PWBLOCK_LEN 516
+
+/**
+ * encrypt_pw_block_with_password_hash - EncryptPwBlockWithPasswordHash() - RFC 2759, Sect. 8.10
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
+ * @password_len: Length of password
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @pw_block: 516-byte PwBlock (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int encrypt_pw_block_with_password_hash(
+	const u8 *password, size_t password_len,
+	const u8 *password_hash, u8 *pw_block)
+{
+	size_t ucs2_len, offset;
+	u8 *pos;
+
+	os_memset(pw_block, 0, PWBLOCK_LEN);
+
+	if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0
+	    || ucs2_len > 256)
+		return -1;
+
+	offset = (256 - ucs2_len) * 2;
+	if (offset != 0) {
+		os_memmove(pw_block + offset, pw_block, ucs2_len * 2);
+		if (os_get_random(pw_block, offset) < 0)
+			return -1;
+	}
+	/*
+	 * PasswordLength is 4 octets, but since the maximum password length is
+	 * 256, only first two (in little endian byte order) can be non-zero.
+	 */
+	pos = &pw_block[2 * 256];
+	WPA_PUT_LE16(pos, password_len * 2);
+	rc4_skip(password_hash, 16, 0, pw_block, PWBLOCK_LEN);
+	return 0;
+}
+
+
+/**
+ * new_password_encrypted_with_old_nt_password_hash - NewPasswordEncryptedWithOldNtPasswordHash() - RFC 2759, Sect. 8.9
+ * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8)
+ * @new_password_len: Length of new_password
+ * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8)
+ * @old_password_len: Length of old_password
+ * @encrypted_pw_block: 516-octet EncryptedPwBlock (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int new_password_encrypted_with_old_nt_password_hash(
+	const u8 *new_password, size_t new_password_len,
+	const u8 *old_password, size_t old_password_len,
+	u8 *encrypted_pw_block)
+{
+	u8 password_hash[16];
+
+	if (nt_password_hash(old_password, old_password_len, password_hash))
+		return -1;
+	if (encrypt_pw_block_with_password_hash(new_password, new_password_len,
+						password_hash,
+						encrypted_pw_block))
+		return -1;
+	return 0;
+}
+
+#endif /* CONFIG_NO_RC4 */
+
+
+/**
+ * nt_password_hash_encrypted_with_block - NtPasswordHashEncryptedWithBlock() - RFC 2759, Sect 8.13
+ * @password_hash: 16-octer PasswordHash (IN)
+ * @block: 16-octet Block (IN)
+ * @cypher: 16-octer Cypher (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int nt_password_hash_encrypted_with_block(const u8 *password_hash,
+					  const u8 *block, u8 *cypher)
+{
+	if (des_encrypt(password_hash, block, cypher) < 0 ||
+	    des_encrypt(password_hash + 8, block + 7, cypher + 8) < 0)
+		return -1;
+	return 0;
+}
+
+
+/**
+ * old_nt_password_hash_encrypted_with_new_nt_password_hash - OldNtPasswordHashEncryptedWithNewNtPasswordHash() - RFC 2759, Sect. 8.12
+ * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8)
+ * @new_password_len: Length of new_password
+ * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8)
+ * @old_password_len: Length of old_password
+ * @encrypted_password_hash: 16-octet EncryptedPasswordHash (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int old_nt_password_hash_encrypted_with_new_nt_password_hash(
+	const u8 *new_password, size_t new_password_len,
+	const u8 *old_password, size_t old_password_len,
+	u8 *encrypted_password_hash)
+{
+	u8 old_password_hash[16], new_password_hash[16];
+
+	if (nt_password_hash(old_password, old_password_len,
+			     old_password_hash) ||
+	    nt_password_hash(new_password, new_password_len,
+			     new_password_hash) ||
+	    nt_password_hash_encrypted_with_block(old_password_hash,
+						  new_password_hash,
+						  encrypted_password_hash))
+		return -1;
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/ms_funcs.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/ms_funcs.h
new file mode 100644
index 0000000..b8d55f0
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/ms_funcs.h
@@ -0,0 +1,60 @@
+/*
+ * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MS_FUNCS_H
+#define MS_FUNCS_H
+
+int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge,
+			 const u8 *username, size_t username_len,
+			 const u8 *password, size_t password_len,
+			 u8 *response);
+int generate_nt_response_pwhash(const u8 *auth_challenge,
+				const u8 *peer_challenge,
+				const u8 *username, size_t username_len,
+				const u8 *password_hash,
+				u8 *response);
+int generate_authenticator_response(const u8 *password, size_t password_len,
+				    const u8 *peer_challenge,
+				    const u8 *auth_challenge,
+				    const u8 *username, size_t username_len,
+				    const u8 *nt_response, u8 *response);
+int generate_authenticator_response_pwhash(
+	const u8 *password_hash,
+	const u8 *peer_challenge, const u8 *auth_challenge,
+	const u8 *username, size_t username_len,
+	const u8 *nt_response, u8 *response);
+int nt_challenge_response(const u8 *challenge, const u8 *password,
+			  size_t password_len, u8 *response);
+
+int challenge_response(const u8 *challenge, const u8 *password_hash,
+		       u8 *response);
+int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge,
+		   const u8 *username, size_t username_len, u8 *challenge);
+int nt_password_hash(const u8 *password, size_t password_len,
+		     u8 *password_hash);
+int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash);
+int get_master_key(const u8 *password_hash_hash, const u8 *nt_response,
+		   u8 *master_key);
+int get_asymetric_start_key(const u8 *master_key, u8 *session_key,
+			    size_t session_key_len, int is_send,
+			    int is_server);
+int __must_check encrypt_pw_block_with_password_hash(
+	const u8 *password, size_t password_len,
+	const u8 *password_hash, u8 *pw_block);
+int __must_check new_password_encrypted_with_old_nt_password_hash(
+	const u8 *new_password, size_t new_password_len,
+	const u8 *old_password, size_t old_password_len,
+	u8 *encrypted_pw_block);
+int nt_password_hash_encrypted_with_block(const u8 *password_hash,
+					  const u8 *block, u8 *cypher);
+int old_nt_password_hash_encrypted_with_new_nt_password_hash(
+	const u8 *new_password, size_t new_password_len,
+	const u8 *old_password, size_t old_password_len,
+	u8 *encrypted_password_hash);
+
+#endif /* MS_FUNCS_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/random.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/random.c
new file mode 100644
index 0000000..548b60d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/random.c
@@ -0,0 +1,478 @@
+/*
+ * Random number generator
+ * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This random number generator is used to provide additional entropy to the
+ * one provided by the operating system (os_get_random()) for session key
+ * generation. The os_get_random() output is expected to be secure and the
+ * implementation here is expected to provide only limited protection against
+ * cases where os_get_random() cannot provide strong randomness. This
+ * implementation shall not be assumed to be secure as the sole source of
+ * randomness. The random_get_bytes() function mixes in randomness from
+ * os_get_random() and as such, calls to os_get_random() can be replaced with
+ * calls to random_get_bytes() without reducing security.
+ *
+ * The design here follows partially the design used in the Linux
+ * drivers/char/random.c, but the implementation here is simpler and not as
+ * strong. This is a compromise to reduce duplicated CPU effort and to avoid
+ * extra code/memory size. As pointed out above, os_get_random() needs to be
+ * guaranteed to be secure for any of the security assumptions to hold.
+ */
+
+#include "utils/includes.h"
+#ifdef __linux__
+#include <fcntl.h>
+#ifdef CONFIG_GETRANDOM
+#include <sys/random.h>
+#endif /* CONFIG_GETRANDOM */
+#endif /* __linux__ */
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/crypto.h"
+#include "sha1.h"
+#include "random.h"
+
+#define POOL_WORDS 32
+#define POOL_WORDS_MASK (POOL_WORDS - 1)
+#define POOL_TAP1 26
+#define POOL_TAP2 20
+#define POOL_TAP3 14
+#define POOL_TAP4 7
+#define POOL_TAP5 1
+#define EXTRACT_LEN 16
+#define MIN_READY_MARK 2
+
+static u32 pool[POOL_WORDS];
+static unsigned int input_rotate = 0;
+static unsigned int pool_pos = 0;
+static u8 stub_key[20];
+#ifdef __linux__
+static size_t stub_key_avail = 0;
+static int random_fd = -1;
+#endif /* __linux__ */
+static unsigned int own_pool_ready = 0;
+#define RANDOM_ENTROPY_SIZE 20
+static char *random_entropy_file = NULL;
+
+#define MIN_COLLECT_ENTROPY 1000
+static unsigned int entropy = 0;
+static unsigned int total_collected = 0;
+
+
+static void random_write_entropy(void);
+
+
+static u32 __ROL32(u32 x, u32 y)
+{
+	if (y == 0)
+		return x;
+
+	return (x << (y & 31)) | (x >> (32 - (y & 31)));
+}
+
+
+static void random_mix_pool(const void *buf, size_t len)
+{
+	static const u32 twist[8] = {
+		0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
+		0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278
+	};
+	const u8 *pos = buf;
+	u32 w;
+
+	wpa_hexdump_key(MSG_EXCESSIVE, "random_mix_pool", buf, len);
+
+	while (len--) {
+		w = __ROL32(*pos++, input_rotate & 31);
+		input_rotate += pool_pos ? 7 : 14;
+		pool_pos = (pool_pos - 1) & POOL_WORDS_MASK;
+		w ^= pool[pool_pos];
+		w ^= pool[(pool_pos + POOL_TAP1) & POOL_WORDS_MASK];
+		w ^= pool[(pool_pos + POOL_TAP2) & POOL_WORDS_MASK];
+		w ^= pool[(pool_pos + POOL_TAP3) & POOL_WORDS_MASK];
+		w ^= pool[(pool_pos + POOL_TAP4) & POOL_WORDS_MASK];
+		w ^= pool[(pool_pos + POOL_TAP5) & POOL_WORDS_MASK];
+		pool[pool_pos] = (w >> 3) ^ twist[w & 7];
+	}
+}
+
+
+static void random_extract(u8 *out)
+{
+	unsigned int i;
+	u8 hash[SHA1_MAC_LEN];
+	u32 *hash_ptr;
+	u32 buf[POOL_WORDS / 2];
+
+	/* First, add hash back to pool to make backtracking more difficult. */
+	hmac_sha1(stub_key, sizeof(stub_key), (const u8 *) pool,
+		  sizeof(pool), hash);
+	random_mix_pool(hash, sizeof(hash));
+	/* Hash half the pool to extra data */
+	for (i = 0; i < POOL_WORDS / 2; i++)
+		buf[i] = pool[(pool_pos - i) & POOL_WORDS_MASK];
+	hmac_sha1(stub_key, sizeof(stub_key), (const u8 *) buf,
+		  sizeof(buf), hash);
+	/*
+	 * Fold the hash to further reduce any potential output pattern.
+	 * Though, compromise this to reduce CPU use for the most common output
+	 * length (32) and return 16 bytes from instead of only half.
+	 */
+	hash_ptr = (u32 *) hash;
+	hash_ptr[0] ^= hash_ptr[4];
+	os_memcpy(out, hash, EXTRACT_LEN);
+}
+
+
+void random_add_randomness(const void *buf, size_t len)
+{
+	struct os_time t;
+	static unsigned int count = 0;
+
+	count++;
+	if (entropy > MIN_COLLECT_ENTROPY && (count & 0x3ff) != 0) {
+		/*
+		 * No need to add more entropy at this point, so save CPU and
+		 * skip the update.
+		 */
+		return;
+	}
+	wpa_printf(MSG_EXCESSIVE, "Add randomness: count=%u entropy=%u",
+		   count, entropy);
+
+	os_get_time(&t);
+	wpa_hexdump_key(MSG_EXCESSIVE, "random pool",
+			(const u8 *) pool, sizeof(pool));
+	random_mix_pool(&t, sizeof(t));
+	random_mix_pool(buf, len);
+	wpa_hexdump_key(MSG_EXCESSIVE, "random pool",
+			(const u8 *) pool, sizeof(pool));
+	entropy++;
+	total_collected++;
+}
+
+
+int random_get_bytes(void *buf, size_t len)
+{
+	int ret;
+	u8 *bytes = buf;
+	size_t left;
+
+	wpa_printf(MSG_MSGDUMP, "Get randomness: len=%u entropy=%u",
+		   (unsigned int) len, entropy);
+
+	/* Start with assumed strong randomness from OS */
+	ret = os_get_random(buf, len);
+	wpa_hexdump_key(MSG_EXCESSIVE, "random from os_get_random",
+			buf, len);
+
+	/* Mix in additional entropy extracted from the internal pool */
+	left = len;
+	while (left) {
+		size_t siz, i;
+		u8 tmp[EXTRACT_LEN];
+		random_extract(tmp);
+		wpa_hexdump_key(MSG_EXCESSIVE, "random from internal pool",
+				tmp, sizeof(tmp));
+		siz = left > EXTRACT_LEN ? EXTRACT_LEN : left;
+		for (i = 0; i < siz; i++)
+			*bytes++ ^= tmp[i];
+		left -= siz;
+	}
+
+#ifdef CONFIG_FIPS
+	/* Mix in additional entropy from the crypto module */
+	bytes = buf;
+	left = len;
+	while (left) {
+		size_t siz, i;
+		u8 tmp[EXTRACT_LEN];
+		if (crypto_get_random(tmp, sizeof(tmp)) < 0) {
+			wpa_printf(MSG_ERROR, "random: No entropy available "
+				   "for generating strong random bytes");
+			return -1;
+		}
+		wpa_hexdump_key(MSG_EXCESSIVE, "random from crypto module",
+				tmp, sizeof(tmp));
+		siz = left > EXTRACT_LEN ? EXTRACT_LEN : left;
+		for (i = 0; i < siz; i++)
+			*bytes++ ^= tmp[i];
+		left -= siz;
+	}
+#endif /* CONFIG_FIPS */
+
+	wpa_hexdump_key(MSG_EXCESSIVE, "mixed random", buf, len);
+
+	if (entropy < len)
+		entropy = 0;
+	else
+		entropy -= len;
+
+	return ret;
+}
+
+
+int random_pool_ready(void)
+{
+#ifdef __linux__
+	int fd;
+	ssize_t res;
+
+	/*
+	 * Make sure that there is reasonable entropy available before allowing
+	 * some key derivation operations to proceed.
+	 */
+
+	if (stub_key_avail == sizeof(stub_key))
+		return 1; /* Already initialized - good to continue */
+
+	/*
+	 * Try to fetch some more data from the kernel high quality RNG.
+	 * There may not be enough data available at this point,
+	 * so use non-blocking read to avoid blocking the application
+	 * completely.
+	 */
+
+#ifdef CONFIG_GETRANDOM
+	res = getrandom(stub_key + stub_key_avail,
+			sizeof(stub_key) - stub_key_avail, GRND_NONBLOCK);
+	if (res < 0) {
+		if (errno == ENOSYS) {
+			wpa_printf(MSG_DEBUG,
+				   "random: getrandom() not supported, falling back to /dev/random");
+		} else {
+			wpa_printf(MSG_INFO,
+				   "random: no data from getrandom(): %s",
+				   strerror(errno));
+			res = 0;
+		}
+	}
+#else /* CONFIG_GETRANDOM */
+	res = -1;
+#endif /* CONFIG_GETRANDOM */
+	if (res < 0) {
+		fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
+		if (fd < 0) {
+			wpa_printf(MSG_ERROR,
+				   "random: Cannot open /dev/random: %s",
+				   strerror(errno));
+			return -1;
+		}
+
+		res = read(fd, stub_key + stub_key_avail,
+			   sizeof(stub_key) - stub_key_avail);
+		if (res < 0) {
+			wpa_printf(MSG_ERROR,
+				   "random: Cannot read from /dev/random: %s",
+				   strerror(errno));
+			res = 0;
+		}
+		close(fd);
+	}
+
+	wpa_printf(MSG_DEBUG, "random: Got %u/%u random bytes", (unsigned) res,
+		   (unsigned) (sizeof(stub_key) - stub_key_avail));
+	stub_key_avail += res;
+
+	if (stub_key_avail == sizeof(stub_key)) {
+		if (own_pool_ready < MIN_READY_MARK)
+			own_pool_ready = MIN_READY_MARK;
+		random_write_entropy();
+		return 1;
+	}
+
+	wpa_printf(MSG_INFO, "random: Only %u/%u bytes of strong "
+		   "random data available",
+		   (unsigned) stub_key_avail, (unsigned) sizeof(stub_key));
+
+	if (own_pool_ready >= MIN_READY_MARK ||
+	    total_collected + 10 * own_pool_ready > MIN_COLLECT_ENTROPY) {
+		wpa_printf(MSG_INFO, "random: Allow operation to proceed "
+			   "based on internal entropy");
+		return 1;
+	}
+
+	wpa_printf(MSG_INFO, "random: Not enough entropy pool available for "
+		   "secure operations");
+	return 0;
+#else /* __linux__ */
+	/* TODO: could do similar checks on non-Linux platforms */
+	return 1;
+#endif /* __linux__ */
+}
+
+
+void random_mark_pool_ready(void)
+{
+	own_pool_ready++;
+	wpa_printf(MSG_DEBUG, "random: Mark internal entropy pool to be "
+		   "ready (count=%u/%u)", own_pool_ready, MIN_READY_MARK);
+	random_write_entropy();
+}
+
+
+#ifdef __linux__
+
+static void random_close_fd(void)
+{
+	if (random_fd >= 0) {
+		eloop_unregister_read_sock(random_fd);
+		close(random_fd);
+		random_fd = -1;
+	}
+}
+
+
+static void random_read_fd(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	ssize_t res;
+
+	if (stub_key_avail == sizeof(stub_key)) {
+		random_close_fd();
+		return;
+	}
+
+	res = read(sock, stub_key + stub_key_avail,
+		   sizeof(stub_key) - stub_key_avail);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: "
+			   "%s", strerror(errno));
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from /dev/random",
+		   (unsigned) res,
+		   (unsigned) (sizeof(stub_key) - stub_key_avail));
+	stub_key_avail += res;
+
+	if (stub_key_avail == sizeof(stub_key)) {
+		random_close_fd();
+		if (own_pool_ready < MIN_READY_MARK)
+			own_pool_ready = MIN_READY_MARK;
+		random_write_entropy();
+	}
+}
+
+#endif /* __linux__ */
+
+
+static void random_read_entropy(void)
+{
+	char *buf;
+	size_t len;
+
+	if (!random_entropy_file)
+		return;
+
+	buf = os_readfile(random_entropy_file, &len);
+	if (buf == NULL)
+		return; /* entropy file not yet available */
+
+	if (len != 1 + RANDOM_ENTROPY_SIZE) {
+		wpa_printf(MSG_DEBUG, "random: Invalid entropy file %s",
+			   random_entropy_file);
+		os_free(buf);
+		return;
+	}
+
+	own_pool_ready = (u8) buf[0];
+	random_add_randomness(buf + 1, RANDOM_ENTROPY_SIZE);
+	os_free(buf);
+	wpa_printf(MSG_DEBUG, "random: Added entropy from %s "
+		   "(own_pool_ready=%u)",
+		   random_entropy_file, own_pool_ready);
+}
+
+
+static void random_write_entropy(void)
+{
+	char buf[RANDOM_ENTROPY_SIZE];
+	FILE *f;
+	u8 opr;
+	int fail = 0;
+
+	if (!random_entropy_file)
+		return;
+
+	if (random_get_bytes(buf, RANDOM_ENTROPY_SIZE) < 0)
+		return;
+
+	f = fopen(random_entropy_file, "wb");
+	if (f == NULL) {
+		wpa_printf(MSG_ERROR, "random: Could not open entropy file %s "
+			   "for writing", random_entropy_file);
+		return;
+	}
+
+	opr = own_pool_ready > 0xff ? 0xff : own_pool_ready;
+	if (fwrite(&opr, 1, 1, f) != 1 ||
+	    fwrite(buf, RANDOM_ENTROPY_SIZE, 1, f) != 1)
+		fail = 1;
+	fclose(f);
+	if (fail) {
+		wpa_printf(MSG_ERROR, "random: Could not write entropy data "
+			   "to %s", random_entropy_file);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "random: Updated entropy file %s "
+		   "(own_pool_ready=%u)",
+		   random_entropy_file, own_pool_ready);
+}
+
+
+void random_init(const char *entropy_file)
+{
+	os_free(random_entropy_file);
+	if (entropy_file)
+		random_entropy_file = os_strdup(entropy_file);
+	else
+		random_entropy_file = NULL;
+	random_read_entropy();
+
+#ifdef __linux__
+	if (random_fd >= 0)
+		return;
+
+#ifdef CONFIG_GETRANDOM
+	{
+		u8 stub;
+
+		if (getrandom(&stub, 0, GRND_NONBLOCK) == 0 ||
+		    errno != ENOSYS) {
+			wpa_printf(MSG_DEBUG,
+				   "random: getrandom() support available");
+			return;
+		}
+	}
+#endif /* CONFIG_GETRANDOM */
+
+	random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
+	if (random_fd < 0) {
+		wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
+			   strerror(errno));
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "random: Trying to read entropy from "
+		   "/dev/random");
+
+	eloop_register_read_sock(random_fd, random_read_fd, NULL, NULL);
+#endif /* __linux__ */
+
+	random_write_entropy();
+}
+
+
+void random_deinit(void)
+{
+#ifdef __linux__
+	random_close_fd();
+#endif /* __linux__ */
+	random_write_entropy();
+	os_free(random_entropy_file);
+	random_entropy_file = NULL;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/random.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/random.h
new file mode 100644
index 0000000..d13e1c4
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/random.h
@@ -0,0 +1,28 @@
+/*
+ * Random number generator
+ * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef RANDOM_H
+#define RANDOM_H
+
+#ifdef CONFIG_NO_RANDOM_POOL
+#define random_init(e) do { } while (0)
+#define random_deinit() do { } while (0)
+#define random_add_randomness(b, l) do { } while (0)
+#define random_get_bytes(b, l) os_get_random((b), (l))
+#define random_pool_ready() 1
+#define random_mark_pool_ready() do { } while (0)
+#else /* CONFIG_NO_RANDOM_POOL */
+void random_init(const char *entropy_file);
+void random_deinit(void);
+void random_add_randomness(const void *buf, size_t len);
+int random_get_bytes(void *buf, size_t len);
+int random_pool_ready(void);
+void random_mark_pool_ready(void);
+#endif /* CONFIG_NO_RANDOM_POOL */
+
+#endif /* RANDOM_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/rc4.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/rc4.c
new file mode 100644
index 0000000..98ae269
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/rc4.c
@@ -0,0 +1,54 @@
+/*
+ * RC4 stream cipher
+ * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+
+#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
+
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+	     u8 *data, size_t data_len)
+{
+	u32 i, j, k;
+	u8 S[256], *pos;
+	size_t kpos;
+
+	/* Setup RC4 state */
+	for (i = 0; i < 256; i++)
+		S[i] = i;
+	j = 0;
+	kpos = 0;
+	for (i = 0; i < 256; i++) {
+		j = (j + S[i] + key[kpos]) & 0xff;
+		kpos++;
+		if (kpos >= keylen)
+			kpos = 0;
+		S_SWAP(i, j);
+	}
+
+	/* Skip the start of the stream */
+	i = j = 0;
+	for (k = 0; k < skip; k++) {
+		i = (i + 1) & 0xff;
+		j = (j + S[i]) & 0xff;
+		S_SWAP(i, j);
+	}
+
+	/* Apply RC4 to data */
+	pos = data;
+	for (k = 0; k < data_len; k++) {
+		i = (i + 1) & 0xff;
+		j = (j + S[i]) & 0xff;
+		S_SWAP(i, j);
+		*pos++ ^= S[(S[i] + S[j]) & 0xff];
+	}
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha1-internal.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha1-internal.c
new file mode 100644
index 0000000..ffa04df
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha1-internal.c
@@ -0,0 +1,306 @@
+/*
+ * SHA1 hash implementation and interface functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+#include "sha1_i.h"
+#include "md5.h"
+#include "crypto.h"
+
+typedef struct SHA1Context SHA1_CTX;
+
+void SHA1Transform(u32 state[5], const unsigned char buffer[64]);
+
+
+#ifdef CONFIG_CRYPTO_INTERNAL
+/**
+ * sha1_vector - SHA-1 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 of failure
+ */
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	SHA1_CTX ctx;
+	size_t i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	SHA1Init(&ctx);
+	for (i = 0; i < num_elem; i++)
+		SHA1Update(&ctx, addr[i], len[i]);
+	SHA1Final(mac, &ctx);
+	return 0;
+}
+#endif /* CONFIG_CRYPTO_INTERNAL */
+
+
+/* ===== start - public domain SHA1 implementation ===== */
+
+/*
+SHA-1 in C
+By Steve Reid <sreid@sea-to-sky.net>
+100% Public Domain
+
+-----------------
+Modified 7/98
+By James H. Brown <jbrown@burgoyne.com>
+Still 100% Public Domain
+
+Corrected a problem which generated improper hash values on 16 bit machines
+Routine SHA1Update changed from
+	void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
+len)
+to
+	void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
+long len)
+
+The 'len' parameter was declared an int which works fine on 32 bit machines.
+However, on 16 bit machines an int is too small for the shifts being done
+against
+it.  This caused the hash function to generate incorrect values if len was
+greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
+
+Since the file IO in main() reads 16K at a time, any file 8K or larger would
+be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
+"a"s).
+
+I also changed the declaration of variables i & j in SHA1Update to
+unsigned long from unsigned int for the same reason.
+
+These changes should make no difference to any 32 bit implementations since
+an
+int and a long are the same size in those environments.
+
+--
+I also corrected a few compiler warnings generated by Borland C.
+1. Added #include <process.h> for exit() prototype
+2. Removed unused variable 'j' in SHA1Final
+3. Changed exit(0) to return(0) at end of main.
+
+ALL changes I made can be located by searching for comments containing 'JHB'
+-----------------
+Modified 8/98
+By Steve Reid <sreid@sea-to-sky.net>
+Still 100% public domain
+
+1- Removed #include <process.h> and used return() instead of exit()
+2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
+3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
+
+-----------------
+Modified 4/01
+By Saul Kravitz <Saul.Kravitz@celera.com>
+Still 100% PD
+Modified to run on Compaq Alpha hardware.
+
+-----------------
+Modified 4/01
+By Jouni Malinen <j@w1.fi>
+Minor changes to match the coding style used in Dynamics.
+
+Modified September 24, 2004
+By Jouni Malinen <j@w1.fi>
+Fixed alignment issue in SHA1Transform when SHA1HANDSOFF is defined.
+
+*/
+
+/*
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+  A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+  84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+  34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+#define SHA1HANDSOFF
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#ifndef WORDS_BIGENDIAN
+#define blk0(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | \
+	(rol(block->l[i], 8) & 0x00FF00FF))
+#else
+#define blk0(i) block->l[i]
+#endif
+#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ \
+	block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) \
+	z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \
+	w = rol(w, 30);
+#define R1(v,w,x,y,z,i) \
+	z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \
+	w = rol(w, 30);
+#define R2(v,w,x,y,z,i) \
+	z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); w = rol(w, 30);
+#define R3(v,w,x,y,z,i) \
+	z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \
+	w = rol(w, 30);
+#define R4(v,w,x,y,z,i) \
+	z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
+	w=rol(w, 30);
+
+
+#ifdef VERBOSE  /* SAK */
+void SHAPrintContext(SHA1_CTX *context, char *msg)
+{
+	printf("%s (%d,%d) %x %x %x %x %x\n",
+	       msg,
+	       context->count[0], context->count[1],
+	       context->state[0],
+	       context->state[1],
+	       context->state[2],
+	       context->state[3],
+	       context->state[4]);
+}
+#endif
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+void SHA1Transform(u32 state[5], const unsigned char buffer[64])
+{
+	u32 a, b, c, d, e;
+	typedef union {
+		unsigned char c[64];
+		u32 l[16];
+	} CHAR64LONG16;
+	CHAR64LONG16* block;
+#ifdef SHA1HANDSOFF
+	CHAR64LONG16 workspace;
+	block = &workspace;
+	os_memcpy(block, buffer, 64);
+#else
+	block = (CHAR64LONG16 *) buffer;
+#endif
+	/* Copy context->state[] to working vars */
+	a = state[0];
+	b = state[1];
+	c = state[2];
+	d = state[3];
+	e = state[4];
+	/* 4 rounds of 20 operations each. Loop unrolled. */
+	R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+	R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+	R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+	R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+	R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+	R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+	R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+	R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+	R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+	R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+	R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+	R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+	R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+	R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+	R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+	R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+	R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+	R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+	R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+	R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+	/* Add the working vars back into context.state[] */
+	state[0] += a;
+	state[1] += b;
+	state[2] += c;
+	state[3] += d;
+	state[4] += e;
+	/* Wipe variables */
+	a = b = c = d = e = 0;
+#ifdef SHA1HANDSOFF
+	forced_memzero(block, 64);
+#endif
+}
+
+
+/* SHA1Init - Initialize new context */
+
+void SHA1Init(SHA1_CTX* context)
+{
+	/* SHA1 initialization constants */
+	context->state[0] = 0x67452301;
+	context->state[1] = 0xEFCDAB89;
+	context->state[2] = 0x98BADCFE;
+	context->state[3] = 0x10325476;
+	context->state[4] = 0xC3D2E1F0;
+	context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+void SHA1Update(SHA1_CTX* context, const void *_data, u32 len)
+{
+	u32 i, j;
+	const unsigned char *data = _data;
+
+#ifdef VERBOSE
+	SHAPrintContext(context, "before");
+#endif
+	j = (context->count[0] >> 3) & 63;
+	if ((context->count[0] += len << 3) < (len << 3))
+		context->count[1]++;
+	context->count[1] += (len >> 29);
+	if ((j + len) > 63) {
+		os_memcpy(&context->buffer[j], data, (i = 64-j));
+		SHA1Transform(context->state, context->buffer);
+		for ( ; i + 63 < len; i += 64) {
+			SHA1Transform(context->state, &data[i]);
+		}
+		j = 0;
+	}
+	else i = 0;
+	os_memcpy(&context->buffer[j], &data[i], len - i);
+#ifdef VERBOSE
+	SHAPrintContext(context, "after ");
+#endif
+}
+
+
+/* Add padding and return the message digest. */
+
+void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
+{
+	u32 i;
+	unsigned char finalcount[8];
+
+	for (i = 0; i < 8; i++) {
+		finalcount[i] = (unsigned char)
+			((context->count[(i >= 4 ? 0 : 1)] >>
+			  ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
+	}
+	SHA1Update(context, (unsigned char *) "\200", 1);
+	while ((context->count[0] & 504) != 448) {
+		SHA1Update(context, (unsigned char *) "\0", 1);
+	}
+	SHA1Update(context, finalcount, 8);  /* Should cause a SHA1Transform()
+					      */
+	for (i = 0; i < 20; i++) {
+		digest[i] = (unsigned char)
+			((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) &
+			 255);
+	}
+	/* Wipe variables */
+	os_memset(context->buffer, 0, 64);
+	os_memset(context->state, 0, 20);
+	os_memset(context->count, 0, 8);
+	forced_memzero(finalcount, sizeof(finalcount));
+}
+
+/* ===== end - public domain SHA1 implementation ===== */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha1-pbkdf2.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha1-pbkdf2.c
new file mode 100644
index 0000000..d2bdc95
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha1-pbkdf2.c
@@ -0,0 +1,95 @@
+/*
+ * SHA1-based key derivation function (PBKDF2) for IEEE 802.11i
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+
+static int pbkdf2_sha1_f(const char *passphrase, const u8 *ssid,
+			 size_t ssid_len, int iterations, unsigned int count,
+			 u8 *digest)
+{
+	unsigned char tmp[SHA1_MAC_LEN], tmp2[SHA1_MAC_LEN];
+	int i, j;
+	unsigned char count_buf[4];
+	const u8 *addr[2];
+	size_t len[2];
+	size_t passphrase_len = os_strlen(passphrase);
+
+	addr[0] = ssid;
+	len[0] = ssid_len;
+	addr[1] = count_buf;
+	len[1] = 4;
+
+	/* F(P, S, c, i) = U1 xor U2 xor ... Uc
+	 * U1 = PRF(P, S || i)
+	 * U2 = PRF(P, U1)
+	 * Uc = PRF(P, Uc-1)
+	 */
+
+	count_buf[0] = (count >> 24) & 0xff;
+	count_buf[1] = (count >> 16) & 0xff;
+	count_buf[2] = (count >> 8) & 0xff;
+	count_buf[3] = count & 0xff;
+	if (hmac_sha1_vector((u8 *) passphrase, passphrase_len, 2, addr, len,
+			     tmp))
+		return -1;
+	os_memcpy(digest, tmp, SHA1_MAC_LEN);
+
+	for (i = 1; i < iterations; i++) {
+		if (hmac_sha1((u8 *) passphrase, passphrase_len, tmp,
+			      SHA1_MAC_LEN, tmp2))
+			return -1;
+		os_memcpy(tmp, tmp2, SHA1_MAC_LEN);
+		for (j = 0; j < SHA1_MAC_LEN; j++)
+			digest[j] ^= tmp2[j];
+	}
+	forced_memzero(tmp, SHA1_MAC_LEN);
+	forced_memzero(tmp2, SHA1_MAC_LEN);
+
+	return 0;
+}
+
+
+/**
+ * pbkdf2_sha1 - SHA1-based key derivation function (PBKDF2) for IEEE 802.11i
+ * @passphrase: ASCII passphrase
+ * @ssid: SSID
+ * @ssid_len: SSID length in bytes
+ * @iterations: Number of iterations to run
+ * @buf: Buffer for the generated key
+ * @buflen: Length of the buffer in bytes
+ * Returns: 0 on success, -1 of failure
+ *
+ * This function is used to derive PSK for WPA-PSK. For this protocol,
+ * iterations is set to 4096 and buflen to 32. This function is described in
+ * IEEE Std 802.11-2004, Clause H.4. The main construction is from PKCS#5 v2.0.
+ */
+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
+		int iterations, u8 *buf, size_t buflen)
+{
+	unsigned int count = 0;
+	unsigned char *pos = buf;
+	size_t left = buflen, plen;
+	unsigned char digest[SHA1_MAC_LEN];
+
+	while (left > 0) {
+		count++;
+		if (pbkdf2_sha1_f(passphrase, ssid, ssid_len, iterations,
+				  count, digest))
+			return -1;
+		plen = left > SHA1_MAC_LEN ? SHA1_MAC_LEN : left;
+		os_memcpy(pos, digest, plen);
+		pos += plen;
+		left -= plen;
+	}
+	forced_memzero(digest, SHA1_MAC_LEN);
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha1-prf.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha1-prf.c
new file mode 100644
index 0000000..1385149
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha1-prf.c
@@ -0,0 +1,67 @@
+/*
+ * SHA1-based PRF
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+#include "crypto.h"
+
+
+/**
+ * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1)
+ * @key: Key for PRF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 of failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key (e.g., PMK in IEEE 802.11i).
+ */
+int sha1_prf(const u8 *key, size_t key_len, const char *label,
+	     const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+	u8 counter = 0;
+	size_t pos, plen;
+	u8 hash[SHA1_MAC_LEN];
+	size_t label_len = os_strlen(label) + 1;
+	const unsigned char *addr[3];
+	size_t len[3];
+
+	addr[0] = (u8 *) label;
+	len[0] = label_len;
+	addr[1] = data;
+	len[1] = data_len;
+	addr[2] = &counter;
+	len[2] = 1;
+
+	pos = 0;
+	while (pos < buf_len) {
+		plen = buf_len - pos;
+		if (plen >= SHA1_MAC_LEN) {
+			if (hmac_sha1_vector(key, key_len, 3, addr, len,
+					     &buf[pos]))
+				return -1;
+			pos += SHA1_MAC_LEN;
+		} else {
+			if (hmac_sha1_vector(key, key_len, 3, addr, len,
+					     hash))
+				return -1;
+			os_memcpy(&buf[pos], hash, plen);
+			break;
+		}
+		counter++;
+	}
+	forced_memzero(hash, sizeof(hash));
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha1-tlsprf.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha1-tlsprf.c
new file mode 100644
index 0000000..5e8d159
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha1-tlsprf.c
@@ -0,0 +1,101 @@
+/*
+ * TLS PRF (SHA1 + MD5)
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+#include "md5.h"
+
+
+/**
+ * tls_prf_sha1_md5 - Pseudo-Random Function for TLS (TLS-PRF, RFC 2246)
+ * @secret: Key for PRF
+ * @secret_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @out: Buffer for the generated pseudo-random key
+ * @outlen: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key in TLS. This PRF is defined in RFC 2246, Chapter 5.
+ */
+int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label,
+		     const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
+{
+	size_t L_S1, L_S2, i;
+	const u8 *S1, *S2;
+	u8 A_MD5[MD5_MAC_LEN], A_SHA1[SHA1_MAC_LEN];
+	u8 P_MD5[MD5_MAC_LEN], P_SHA1[SHA1_MAC_LEN];
+	int MD5_pos, SHA1_pos;
+	const u8 *MD5_addr[3];
+	size_t MD5_len[3];
+	const unsigned char *SHA1_addr[3];
+	size_t SHA1_len[3];
+
+	MD5_addr[0] = A_MD5;
+	MD5_len[0] = MD5_MAC_LEN;
+	MD5_addr[1] = (unsigned char *) label;
+	MD5_len[1] = os_strlen(label);
+	MD5_addr[2] = seed;
+	MD5_len[2] = seed_len;
+
+	SHA1_addr[0] = A_SHA1;
+	SHA1_len[0] = SHA1_MAC_LEN;
+	SHA1_addr[1] = (unsigned char *) label;
+	SHA1_len[1] = os_strlen(label);
+	SHA1_addr[2] = seed;
+	SHA1_len[2] = seed_len;
+
+	/* RFC 2246, Chapter 5
+	 * A(0) = seed, A(i) = HMAC(secret, A(i-1))
+	 * P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + ..
+	 * PRF = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed)
+	 */
+
+	L_S1 = L_S2 = (secret_len + 1) / 2;
+	S1 = secret;
+	S2 = secret + L_S1;
+	if (secret_len & 1) {
+		/* The last byte of S1 will be shared with S2 */
+		S2--;
+	}
+
+	hmac_md5_vector(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1], A_MD5);
+	hmac_sha1_vector(S2, L_S2, 2, &SHA1_addr[1], &SHA1_len[1], A_SHA1);
+
+	MD5_pos = MD5_MAC_LEN;
+	SHA1_pos = SHA1_MAC_LEN;
+	for (i = 0; i < outlen; i++) {
+		if (MD5_pos == MD5_MAC_LEN) {
+			hmac_md5_vector(S1, L_S1, 3, MD5_addr, MD5_len, P_MD5);
+			MD5_pos = 0;
+			hmac_md5(S1, L_S1, A_MD5, MD5_MAC_LEN, A_MD5);
+		}
+		if (SHA1_pos == SHA1_MAC_LEN) {
+			hmac_sha1_vector(S2, L_S2, 3, SHA1_addr, SHA1_len,
+					 P_SHA1);
+			SHA1_pos = 0;
+			hmac_sha1(S2, L_S2, A_SHA1, SHA1_MAC_LEN, A_SHA1);
+		}
+
+		out[i] = P_MD5[MD5_pos] ^ P_SHA1[SHA1_pos];
+
+		MD5_pos++;
+		SHA1_pos++;
+	}
+
+	forced_memzero(A_MD5, MD5_MAC_LEN);
+	forced_memzero(P_MD5, MD5_MAC_LEN);
+	forced_memzero(A_SHA1, SHA1_MAC_LEN);
+	forced_memzero(P_SHA1, SHA1_MAC_LEN);
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha1-tprf.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha1-tprf.c
new file mode 100644
index 0000000..c3acf19
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha1-tprf.c
@@ -0,0 +1,72 @@
+/*
+ * SHA1 T-PRF for EAP-FAST
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+#include "crypto.h"
+
+/**
+ * sha1_t_prf - EAP-FAST Pseudo-Random Function (T-PRF)
+ * @key: Key for PRF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 of failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key for EAP-FAST. T-PRF is defined in RFC 4851, Section 5.5.
+ */
+int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
+	       const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len)
+{
+	unsigned char counter = 0;
+	size_t pos, plen;
+	u8 hash[SHA1_MAC_LEN];
+	size_t label_len = os_strlen(label);
+	u8 output_len[2];
+	const unsigned char *addr[5];
+	size_t len[5];
+
+	addr[0] = hash;
+	len[0] = 0;
+	addr[1] = (unsigned char *) label;
+	len[1] = label_len + 1;
+	addr[2] = seed;
+	len[2] = seed_len;
+	addr[3] = output_len;
+	len[3] = 2;
+	addr[4] = &counter;
+	len[4] = 1;
+
+	output_len[0] = (buf_len >> 8) & 0xff;
+	output_len[1] = buf_len & 0xff;
+	pos = 0;
+	while (pos < buf_len) {
+		counter++;
+		plen = buf_len - pos;
+		if (hmac_sha1_vector(key, key_len, 5, addr, len, hash))
+			return -1;
+		if (plen >= SHA1_MAC_LEN) {
+			os_memcpy(&buf[pos], hash, SHA1_MAC_LEN);
+			pos += SHA1_MAC_LEN;
+		} else {
+			os_memcpy(&buf[pos], hash, plen);
+			break;
+		}
+		len[0] = SHA1_MAC_LEN;
+	}
+
+	forced_memzero(hash, SHA1_MAC_LEN);
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha1.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha1.c
new file mode 100644
index 0000000..76d7a68
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha1.c
@@ -0,0 +1,108 @@
+/*
+ * SHA1 hash implementation and interface functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+#include "crypto.h"
+
+
+/**
+ * hmac_sha1_vector - HMAC-SHA1 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (20 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */
+	unsigned char tk[20];
+	const u8 *_addr[6];
+	size_t _len[6], i;
+	int ret;
+
+	if (num_elem > 5) {
+		/*
+		 * Fixed limit on the number of fragments to avoid having to
+		 * allocate memory (which could fail).
+		 */
+		return -1;
+	}
+
+        /* if key is longer than 64 bytes reset it to key = SHA1(key) */
+        if (key_len > 64) {
+		if (sha1_vector(1, &key, &key_len, tk))
+			return -1;
+		key = tk;
+		key_len = 20;
+        }
+
+	/* the HMAC_SHA1 transform looks like:
+	 *
+	 * SHA1(K XOR opad, SHA1(K XOR ipad, text))
+	 *
+	 * where K is an n byte key
+	 * ipad is the byte 0x36 repeated 64 times
+	 * opad is the byte 0x5c repeated 64 times
+	 * and text is the data being protected */
+
+	/* start out by storing key in ipad */
+	os_memset(k_pad, 0, sizeof(k_pad));
+	os_memcpy(k_pad, key, key_len);
+	/* XOR key with ipad values */
+	for (i = 0; i < 64; i++)
+		k_pad[i] ^= 0x36;
+
+	/* perform inner SHA1 */
+	_addr[0] = k_pad;
+	_len[0] = 64;
+	for (i = 0; i < num_elem; i++) {
+		_addr[i + 1] = addr[i];
+		_len[i + 1] = len[i];
+	}
+	if (sha1_vector(1 + num_elem, _addr, _len, mac))
+		return -1;
+
+	os_memset(k_pad, 0, sizeof(k_pad));
+	os_memcpy(k_pad, key, key_len);
+	/* XOR key with opad values */
+	for (i = 0; i < 64; i++)
+		k_pad[i] ^= 0x5c;
+
+	/* perform outer SHA1 */
+	_addr[0] = k_pad;
+	_len[0] = 64;
+	_addr[1] = mac;
+	_len[1] = SHA1_MAC_LEN;
+	ret = sha1_vector(2, _addr, _len, mac);
+	forced_memzero(k_pad, sizeof(k_pad));
+	forced_memzero(tk, sizeof(tk));
+	return ret;
+}
+
+
+/**
+ * hmac_sha1 - HMAC-SHA1 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (20 bytes)
+ * Returns: 0 on success, -1 of failure
+ */
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	       u8 *mac)
+{
+	return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha1.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha1.h
new file mode 100644
index 0000000..933cd81
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha1.h
@@ -0,0 +1,27 @@
+/*
+ * SHA1 hash implementation and interface functions
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA1_H
+#define SHA1_H
+
+#define SHA1_MAC_LEN 20
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	       u8 *mac);
+int sha1_prf(const u8 *key, size_t key_len, const char *label,
+	     const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
+	       const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len);
+int __must_check tls_prf_sha1_md5(const u8 *secret, size_t secret_len,
+				  const char *label, const u8 *seed,
+				  size_t seed_len, u8 *out, size_t outlen);
+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
+		int iterations, u8 *buf, size_t buflen);
+#endif /* SHA1_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha1_i.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha1_i.h
new file mode 100644
index 0000000..344387e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha1_i.h
@@ -0,0 +1,23 @@
+/*
+ * SHA1 internal definitions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA1_I_H
+#define SHA1_I_H
+
+struct SHA1Context {
+	u32 state[5];
+	u32 count[2];
+	unsigned char buffer[64];
+};
+
+void SHA1Init(struct SHA1Context *context);
+void SHA1Update(struct SHA1Context *context, const void *data, u32 len);
+void SHA1Final(unsigned char digest[20], struct SHA1Context *context);
+void SHA1Transform(u32 state[5], const unsigned char buffer[64]);
+
+#endif /* SHA1_I_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha256-internal.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha256-internal.c
new file mode 100644
index 0000000..ff1e2ba
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha256-internal.c
@@ -0,0 +1,229 @@
+/*
+ * SHA-256 hash implementation and interface functions
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha256.h"
+#include "sha256_i.h"
+#include "crypto.h"
+
+
+/**
+ * sha256_vector - SHA256 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 of failure
+ */
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	struct sha256_state ctx;
+	size_t i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	sha256_init(&ctx);
+	for (i = 0; i < num_elem; i++)
+		if (sha256_process(&ctx, addr[i], len[i]))
+			return -1;
+	if (sha256_done(&ctx, mac))
+		return -1;
+	return 0;
+}
+
+
+/* ===== start - public domain SHA256 implementation ===== */
+
+/* This is based on SHA256 implementation in LibTomCrypt that was released into
+ * public domain by Tom St Denis. */
+
+/* the K array */
+static const unsigned long K[64] = {
+	0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL,
+	0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL,
+	0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL,
+	0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+	0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL,
+	0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL,
+	0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL,
+	0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+	0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL,
+	0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL,
+	0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL,
+	0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+	0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+
+
+/* Various logical functions */
+#define RORc(x, y) \
+( ((((unsigned long) (x) & 0xFFFFFFFFUL) >> (unsigned long) ((y) & 31)) | \
+   ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL)
+#define Ch(x,y,z)       (z ^ (x & (y ^ z)))
+#define Maj(x,y,z)      (((x | y) & z) | (x & y))
+#define S(x, n)         RORc((x), (n))
+#define R(x, n)         (((x)&0xFFFFFFFFUL)>>(n))
+#define Sigma0(x)       (S(x, 2) ^ S(x, 13) ^ S(x, 22))
+#define Sigma1(x)       (S(x, 6) ^ S(x, 11) ^ S(x, 25))
+#define Gamma0(x)       (S(x, 7) ^ S(x, 18) ^ R(x, 3))
+#define Gamma1(x)       (S(x, 17) ^ S(x, 19) ^ R(x, 10))
+#ifndef MIN
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+#endif
+
+/* compress 512-bits */
+static int sha256_compress(struct sha256_state *md, unsigned char *buf)
+{
+	u32 S[8], W[64], t0, t1;
+	u32 t;
+	int i;
+
+	/* copy state into S */
+	for (i = 0; i < 8; i++) {
+		S[i] = md->state[i];
+	}
+
+	/* copy the state into 512-bits into W[0..15] */
+	for (i = 0; i < 16; i++)
+		W[i] = WPA_GET_BE32(buf + (4 * i));
+
+	/* fill W[16..63] */
+	for (i = 16; i < 64; i++) {
+		W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) +
+			W[i - 16];
+	}
+
+	/* Compress */
+#define RND(a,b,c,d,e,f,g,h,i)                          \
+	t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i];	\
+	t1 = Sigma0(a) + Maj(a, b, c);			\
+	d += t0;					\
+	h  = t0 + t1;
+
+	for (i = 0; i < 64; ++i) {
+		RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i);
+		t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4];
+		S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t;
+	}
+
+	/* feedback */
+	for (i = 0; i < 8; i++) {
+		md->state[i] = md->state[i] + S[i];
+	}
+	return 0;
+}
+
+
+/* Initialize the hash state */
+void sha256_init(struct sha256_state *md)
+{
+	md->curlen = 0;
+	md->length = 0;
+	md->state[0] = 0x6A09E667UL;
+	md->state[1] = 0xBB67AE85UL;
+	md->state[2] = 0x3C6EF372UL;
+	md->state[3] = 0xA54FF53AUL;
+	md->state[4] = 0x510E527FUL;
+	md->state[5] = 0x9B05688CUL;
+	md->state[6] = 0x1F83D9ABUL;
+	md->state[7] = 0x5BE0CD19UL;
+}
+
+/**
+   Process a block of memory though the hash
+   @param md     The hash state
+   @param in     The data to hash
+   @param inlen  The length of the data (octets)
+   @return CRYPT_OK if successful
+*/
+int sha256_process(struct sha256_state *md, const unsigned char *in,
+		   unsigned long inlen)
+{
+	unsigned long n;
+
+	if (md->curlen >= sizeof(md->buf))
+		return -1;
+
+	while (inlen > 0) {
+		if (md->curlen == 0 && inlen >= SHA256_BLOCK_SIZE) {
+			if (sha256_compress(md, (unsigned char *) in) < 0)
+				return -1;
+			md->length += SHA256_BLOCK_SIZE * 8;
+			in += SHA256_BLOCK_SIZE;
+			inlen -= SHA256_BLOCK_SIZE;
+		} else {
+			n = MIN(inlen, (SHA256_BLOCK_SIZE - md->curlen));
+			os_memcpy(md->buf + md->curlen, in, n);
+			md->curlen += n;
+			in += n;
+			inlen -= n;
+			if (md->curlen == SHA256_BLOCK_SIZE) {
+				if (sha256_compress(md, md->buf) < 0)
+					return -1;
+				md->length += 8 * SHA256_BLOCK_SIZE;
+				md->curlen = 0;
+			}
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+   Terminate the hash to get the digest
+   @param md  The hash state
+   @param out [out] The destination of the hash (32 bytes)
+   @return CRYPT_OK if successful
+*/
+int sha256_done(struct sha256_state *md, unsigned char *out)
+{
+	int i;
+
+	if (md->curlen >= sizeof(md->buf))
+		return -1;
+
+	/* increase the length of the message */
+	md->length += md->curlen * 8;
+
+	/* append the '1' bit */
+	md->buf[md->curlen++] = (unsigned char) 0x80;
+
+	/* if the length is currently above 56 bytes we append zeros
+	 * then compress.  Then we can fall back to padding zeros and length
+	 * encoding like normal.
+	 */
+	if (md->curlen > 56) {
+		while (md->curlen < SHA256_BLOCK_SIZE) {
+			md->buf[md->curlen++] = (unsigned char) 0;
+		}
+		sha256_compress(md, md->buf);
+		md->curlen = 0;
+	}
+
+	/* pad up to 56 bytes of zeroes */
+	while (md->curlen < 56) {
+		md->buf[md->curlen++] = (unsigned char) 0;
+	}
+
+	/* store length */
+	WPA_PUT_BE64(md->buf + 56, md->length);
+	sha256_compress(md, md->buf);
+
+	/* copy output */
+	for (i = 0; i < 8; i++)
+		WPA_PUT_BE32(out + (4 * i), md->state[i]);
+
+	return 0;
+}
+
+/* ===== end - public domain SHA256 implementation ===== */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha256-kdf.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha256-kdf.c
new file mode 100644
index 0000000..5a6b744
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha256-kdf.c
@@ -0,0 +1,87 @@
+/*
+ * HMAC-SHA256 KDF (RFC 5295) and HKDF-Expand(SHA256) (RFC 5869)
+ * Copyright (c) 2014-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha256.h"
+
+
+/**
+ * hmac_sha256_kdf - HMAC-SHA256 based KDF (RFC 5295)
+ * @secret: Key for KDF
+ * @secret_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the KDF or %NULL to select
+ *	RFC 5869 HKDF-Expand() with arbitrary seed (= info)
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @out: Buffer for the generated pseudo-random key
+ * @outlen: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used
+ * with label = NULL and seed = info, this matches HKDF-Expand() defined in
+ * RFC 5869, Chapter 2.3.
+ */
+int hmac_sha256_kdf(const u8 *secret, size_t secret_len,
+		    const char *label, const u8 *seed, size_t seed_len,
+		    u8 *out, size_t outlen)
+{
+	u8 T[SHA256_MAC_LEN];
+	u8 iter = 1;
+	const unsigned char *addr[4];
+	size_t len[4];
+	size_t pos, clen;
+
+	addr[0] = T;
+	len[0] = SHA256_MAC_LEN;
+	if (label) {
+		addr[1] = (const unsigned char *) label;
+		len[1] = os_strlen(label) + 1;
+	} else {
+		addr[1] = (const u8 *) "";
+		len[1] = 0;
+	}
+	addr[2] = seed;
+	len[2] = seed_len;
+	addr[3] = &iter;
+	len[3] = 1;
+
+	if (hmac_sha256_vector(secret, secret_len, 3, &addr[1], &len[1], T) < 0)
+		return -1;
+
+	pos = 0;
+	for (;;) {
+		clen = outlen - pos;
+		if (clen > SHA256_MAC_LEN)
+			clen = SHA256_MAC_LEN;
+		os_memcpy(out + pos, T, clen);
+		pos += clen;
+
+		if (pos == outlen)
+			break;
+
+		if (iter == 255) {
+			os_memset(out, 0, outlen);
+			forced_memzero(T, SHA256_MAC_LEN);
+			return -1;
+		}
+		iter++;
+
+		if (hmac_sha256_vector(secret, secret_len, 4, addr, len, T) < 0)
+		{
+			os_memset(out, 0, outlen);
+			forced_memzero(T, SHA256_MAC_LEN);
+			return -1;
+		}
+	}
+
+	forced_memzero(T, SHA256_MAC_LEN);
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha256-prf.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha256-prf.c
new file mode 100644
index 0000000..d665a99
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha256-prf.c
@@ -0,0 +1,108 @@
+/*
+ * SHA256-based PRF (IEEE 802.11r)
+ * Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha256.h"
+#include "crypto.h"
+
+
+/**
+ * sha256_prf - SHA256-based Pseudo-Random Function (IEEE 802.11r, 8.5.1.5.2)
+ * @key: Key for PRF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key.
+ */
+int sha256_prf(const u8 *key, size_t key_len, const char *label,
+		const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+	return sha256_prf_bits(key, key_len, label, data, data_len, buf,
+			       buf_len * 8);
+}
+
+
+/**
+ * sha256_prf_bits - IEEE Std 802.11-2012, 11.6.1.7.2 Key derivation function
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bits of key to generate
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key. If the requested buf_len is not divisible by eight, the least
+ * significant 1-7 bits of the last octet in the output are not part of the
+ * requested output.
+ */
+int sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
+		    const u8 *data, size_t data_len, u8 *buf,
+		    size_t buf_len_bits)
+{
+	u16 counter = 1;
+	size_t pos, plen;
+	u8 hash[SHA256_MAC_LEN];
+	const u8 *addr[4];
+	size_t len[4];
+	u8 counter_le[2], length_le[2];
+	size_t buf_len = (buf_len_bits + 7) / 8;
+
+	addr[0] = counter_le;
+	len[0] = 2;
+	addr[1] = (u8 *) label;
+	len[1] = os_strlen(label);
+	addr[2] = data;
+	len[2] = data_len;
+	addr[3] = length_le;
+	len[3] = sizeof(length_le);
+
+	WPA_PUT_LE16(length_le, buf_len_bits);
+	pos = 0;
+	while (pos < buf_len) {
+		plen = buf_len - pos;
+		WPA_PUT_LE16(counter_le, counter);
+		if (plen >= SHA256_MAC_LEN) {
+			if (hmac_sha256_vector(key, key_len, 4, addr, len,
+					       &buf[pos]) < 0)
+				return -1;
+			pos += SHA256_MAC_LEN;
+		} else {
+			if (hmac_sha256_vector(key, key_len, 4, addr, len,
+					       hash) < 0)
+				return -1;
+			os_memcpy(&buf[pos], hash, plen);
+			pos += plen;
+			break;
+		}
+		counter++;
+	}
+
+	/*
+	 * Mask out unused bits in the last octet if it does not use all the
+	 * bits.
+	 */
+	if (buf_len_bits % 8) {
+		u8 mask = 0xff << (8 - buf_len_bits % 8);
+		buf[pos - 1] &= mask;
+	}
+
+	forced_memzero(hash, sizeof(hash));
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha256-tlsprf.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha256-tlsprf.c
new file mode 100644
index 0000000..9045cd3
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha256-tlsprf.c
@@ -0,0 +1,71 @@
+/*
+ * TLS PRF P_SHA256
+ * Copyright (c) 2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha256.h"
+
+
+/**
+ * tls_prf_sha256 - Pseudo-Random Function for TLS v1.2 (P_SHA256, RFC 5246)
+ * @secret: Key for PRF
+ * @secret_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @out: Buffer for the generated pseudo-random key
+ * @outlen: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key in TLS. This PRF is defined in RFC 2246, Chapter 5.
+ */
+int tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label,
+		   const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
+{
+	size_t clen;
+	u8 A[SHA256_MAC_LEN];
+	u8 P[SHA256_MAC_LEN];
+	size_t pos;
+	const unsigned char *addr[3];
+	size_t len[3];
+
+	addr[0] = A;
+	len[0] = SHA256_MAC_LEN;
+	addr[1] = (unsigned char *) label;
+	len[1] = os_strlen(label);
+	addr[2] = seed;
+	len[2] = seed_len;
+
+	/*
+	 * RFC 5246, Chapter 5
+	 * A(0) = seed, A(i) = HMAC(secret, A(i-1))
+	 * P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + ..
+	 * PRF(secret, label, seed) = P_SHA256(secret, label + seed)
+	 */
+
+	if (hmac_sha256_vector(secret, secret_len, 2, &addr[1], &len[1], A) < 0)
+		return -1;
+
+	pos = 0;
+	while (pos < outlen) {
+		if (hmac_sha256_vector(secret, secret_len, 3, addr, len, P) <
+		    0 ||
+		    hmac_sha256(secret, secret_len, A, SHA256_MAC_LEN, A) < 0)
+			return -1;
+
+		clen = outlen - pos;
+		if (clen > SHA256_MAC_LEN)
+			clen = SHA256_MAC_LEN;
+		os_memcpy(out + pos, P, clen);
+		pos += clen;
+	}
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha256.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha256.c
new file mode 100644
index 0000000..1e8a122
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha256.c
@@ -0,0 +1,113 @@
+/*
+ * SHA-256 hash implementation and interface functions
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha256.h"
+#include "crypto.h"
+
+
+/**
+ * hmac_sha256_vector - HMAC-SHA256 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (32 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */
+	unsigned char tk[32];
+	const u8 *_addr[HMAC_VECTOR_MAX_ELEM + 1];
+	size_t _len[HMAC_VECTOR_MAX_ELEM + 1], i;
+	int ret;
+
+	if (num_elem > HMAC_VECTOR_MAX_ELEM) {
+		/*
+		 * Fixed limit on the number of fragments to avoid having to
+		 * allocate memory (which could fail).
+		 */
+		return -1;
+	}
+
+        /* if key is longer than 64 bytes reset it to key = SHA256(key) */
+        if (key_len > 64) {
+		if (sha256_vector(1, &key, &key_len, tk) < 0)
+			return -1;
+		key = tk;
+		key_len = 32;
+        }
+
+	/* the HMAC_SHA256 transform looks like:
+	 *
+	 * SHA256(K XOR opad, SHA256(K XOR ipad, text))
+	 *
+	 * where K is an n byte key
+	 * ipad is the byte 0x36 repeated 64 times
+	 * opad is the byte 0x5c repeated 64 times
+	 * and text is the data being protected */
+
+	/* start out by storing key in ipad */
+	os_memset(k_pad, 0, sizeof(k_pad));
+	os_memcpy(k_pad, key, key_len);
+	/* XOR key with ipad values */
+	for (i = 0; i < 64; i++)
+		k_pad[i] ^= 0x36;
+
+	/* perform inner SHA256 */
+	_addr[0] = k_pad;
+	_len[0] = 64;
+	for (i = 0; i < num_elem; i++) {
+		_addr[i + 1] = addr[i];
+		_len[i + 1] = len[i];
+	}
+	ret = sha256_vector(1 + num_elem, _addr, _len, mac);
+	if (ret < 0)
+		goto fail;
+
+	os_memset(k_pad, 0, sizeof(k_pad));
+	os_memcpy(k_pad, key, key_len);
+	/* XOR key with opad values */
+	for (i = 0; i < 64; i++)
+		k_pad[i] ^= 0x5c;
+
+	/* perform outer SHA256 */
+	_addr[0] = k_pad;
+	_len[0] = 64;
+	_addr[1] = mac;
+	_len[1] = SHA256_MAC_LEN;
+
+	ret = sha256_vector(2, _addr, _len, mac);
+
+fail:
+	forced_memzero(k_pad, sizeof(k_pad));
+	forced_memzero(tk, sizeof(tk));
+
+	return ret;
+}
+
+
+/**
+ * hmac_sha256 - HMAC-SHA256 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (32 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha256.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha256.h
new file mode 100644
index 0000000..8054bbe
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha256.h
@@ -0,0 +1,30 @@
+/*
+ * SHA256 hash implementation and interface functions
+ * Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA256_H
+#define SHA256_H
+
+#define SHA256_MAC_LEN 32
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac);
+int sha256_prf(const u8 *key, size_t key_len, const char *label,
+	       const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+int sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
+		    const u8 *data, size_t data_len, u8 *buf,
+		    size_t buf_len_bits);
+int tls_prf_sha256(const u8 *secret, size_t secret_len,
+		   const char *label, const u8 *seed, size_t seed_len,
+		   u8 *out, size_t outlen);
+int hmac_sha256_kdf(const u8 *secret, size_t secret_len,
+		    const char *label, const u8 *seed, size_t seed_len,
+		    u8 *out, size_t outlen);
+
+#endif /* SHA256_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha256_i.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha256_i.h
new file mode 100644
index 0000000..a502d2b
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha256_i.h
@@ -0,0 +1,25 @@
+/*
+ * SHA-256 internal definitions
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA256_I_H
+#define SHA256_I_H
+
+#define SHA256_BLOCK_SIZE 64
+
+struct sha256_state {
+	u64 length;
+	u32 state[8], curlen;
+	u8 buf[SHA256_BLOCK_SIZE];
+};
+
+void sha256_init(struct sha256_state *md);
+int sha256_process(struct sha256_state *md, const unsigned char *in,
+		   unsigned long inlen);
+int sha256_done(struct sha256_state *md, unsigned char *out);
+
+#endif /* SHA256_I_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha384-internal.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha384-internal.c
new file mode 100644
index 0000000..646f729
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha384-internal.c
@@ -0,0 +1,92 @@
+/*
+ * SHA-384 hash implementation and interface functions
+ * Copyright (c) 2015, Pali Rohár <pali.rohar@gmail.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha384_i.h"
+#include "crypto.h"
+
+
+/**
+ * sha384_vector - SHA384 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 of failure
+ */
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	struct sha384_state ctx;
+	size_t i;
+
+	sha384_init(&ctx);
+	for (i = 0; i < num_elem; i++)
+		if (sha384_process(&ctx, addr[i], len[i]))
+			return -1;
+	if (sha384_done(&ctx, mac))
+		return -1;
+	return 0;
+}
+
+
+/* ===== start - public domain SHA384 implementation ===== */
+
+/* This is based on SHA384 implementation in LibTomCrypt that was released into
+ * public domain by Tom St Denis. */
+
+#define CONST64(n) n ## ULL
+
+/**
+   Initialize the hash state
+   @param md   The hash state you wish to initialize
+   @return CRYPT_OK if successful
+*/
+void sha384_init(struct sha384_state *md)
+{
+	md->curlen = 0;
+	md->length = 0;
+	md->state[0] = CONST64(0xcbbb9d5dc1059ed8);
+	md->state[1] = CONST64(0x629a292a367cd507);
+	md->state[2] = CONST64(0x9159015a3070dd17);
+	md->state[3] = CONST64(0x152fecd8f70e5939);
+	md->state[4] = CONST64(0x67332667ffc00b31);
+	md->state[5] = CONST64(0x8eb44a8768581511);
+	md->state[6] = CONST64(0xdb0c2e0d64f98fa7);
+	md->state[7] = CONST64(0x47b5481dbefa4fa4);
+}
+
+int sha384_process(struct sha384_state *md, const unsigned char *in,
+		   unsigned long inlen)
+{
+	return sha512_process(md, in, inlen);
+}
+
+/**
+   Terminate the hash to get the digest
+   @param md  The hash state
+   @param out [out] The destination of the hash (48 bytes)
+   @return CRYPT_OK if successful
+*/
+int sha384_done(struct sha384_state *md, unsigned char *out)
+{
+	unsigned char buf[64];
+
+	if (md->curlen >= sizeof(md->buf))
+		return -1;
+
+	if (sha512_done(md, buf) != 0)
+		return -1;
+
+	os_memcpy(out, buf, 48);
+	return 0;
+}
+
+/* ===== end - public domain SHA384 implementation ===== */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha384-kdf.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha384-kdf.c
new file mode 100644
index 0000000..babcb9e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha384-kdf.c
@@ -0,0 +1,87 @@
+/*
+ * HMAC-SHA384 KDF (RFC 5295) and HKDF-Expand(SHA384) (RFC 5869)
+ * Copyright (c) 2014-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha384.h"
+
+
+/**
+ * hmac_sha384_kdf - HMAC-SHA384 based KDF (RFC 5295)
+ * @secret: Key for KDF
+ * @secret_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the KDF or %NULL to select
+ *	RFC 5869 HKDF-Expand() with arbitrary seed (= info)
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @out: Buffer for the generated pseudo-random key
+ * @outlen: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used
+ * with label = NULL and seed = info, this matches HKDF-Expand() defined in
+ * RFC 5869, Chapter 2.3.
+ */
+int hmac_sha384_kdf(const u8 *secret, size_t secret_len,
+		    const char *label, const u8 *seed, size_t seed_len,
+		    u8 *out, size_t outlen)
+{
+	u8 T[SHA384_MAC_LEN];
+	u8 iter = 1;
+	const unsigned char *addr[4];
+	size_t len[4];
+	size_t pos, clen;
+
+	addr[0] = T;
+	len[0] = SHA384_MAC_LEN;
+	if (label) {
+		addr[1] = (const unsigned char *) label;
+		len[1] = os_strlen(label) + 1;
+	} else {
+		addr[1] = (const u8 *) "";
+		len[1] = 0;
+	}
+	addr[2] = seed;
+	len[2] = seed_len;
+	addr[3] = &iter;
+	len[3] = 1;
+
+	if (hmac_sha384_vector(secret, secret_len, 3, &addr[1], &len[1], T) < 0)
+		return -1;
+
+	pos = 0;
+	for (;;) {
+		clen = outlen - pos;
+		if (clen > SHA384_MAC_LEN)
+			clen = SHA384_MAC_LEN;
+		os_memcpy(out + pos, T, clen);
+		pos += clen;
+
+		if (pos == outlen)
+			break;
+
+		if (iter == 255) {
+			os_memset(out, 0, outlen);
+			forced_memzero(T, SHA384_MAC_LEN);
+			return -1;
+		}
+		iter++;
+
+		if (hmac_sha384_vector(secret, secret_len, 4, addr, len, T) < 0)
+		{
+			os_memset(out, 0, outlen);
+			forced_memzero(T, SHA384_MAC_LEN);
+			return -1;
+		}
+	}
+
+	forced_memzero(T, SHA384_MAC_LEN);
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha384-prf.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha384-prf.c
new file mode 100644
index 0000000..420e78c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha384-prf.c
@@ -0,0 +1,108 @@
+/*
+ * SHA384-based KDF (IEEE 802.11ac)
+ * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha384.h"
+#include "crypto.h"
+
+
+/**
+ * sha384_prf - SHA384-based Key derivation function (IEEE 802.11ac, 11.6.1.7.2)
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key.
+ */
+int sha384_prf(const u8 *key, size_t key_len, const char *label,
+	       const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+	return sha384_prf_bits(key, key_len, label, data, data_len, buf,
+			       buf_len * 8);
+}
+
+
+/**
+ * sha384_prf_bits - IEEE Std 802.11ac-2013, 11.6.1.7.2 Key derivation function
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bits of key to generate
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key. If the requested buf_len is not divisible by eight, the least
+ * significant 1-7 bits of the last octet in the output are not part of the
+ * requested output.
+ */
+int sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
+		    const u8 *data, size_t data_len, u8 *buf,
+		    size_t buf_len_bits)
+{
+	u16 counter = 1;
+	size_t pos, plen;
+	u8 hash[SHA384_MAC_LEN];
+	const u8 *addr[4];
+	size_t len[4];
+	u8 counter_le[2], length_le[2];
+	size_t buf_len = (buf_len_bits + 7) / 8;
+
+	addr[0] = counter_le;
+	len[0] = 2;
+	addr[1] = (u8 *) label;
+	len[1] = os_strlen(label);
+	addr[2] = data;
+	len[2] = data_len;
+	addr[3] = length_le;
+	len[3] = sizeof(length_le);
+
+	WPA_PUT_LE16(length_le, buf_len_bits);
+	pos = 0;
+	while (pos < buf_len) {
+		plen = buf_len - pos;
+		WPA_PUT_LE16(counter_le, counter);
+		if (plen >= SHA384_MAC_LEN) {
+			if (hmac_sha384_vector(key, key_len, 4, addr, len,
+					       &buf[pos]) < 0)
+				return -1;
+			pos += SHA384_MAC_LEN;
+		} else {
+			if (hmac_sha384_vector(key, key_len, 4, addr, len,
+					       hash) < 0)
+				return -1;
+			os_memcpy(&buf[pos], hash, plen);
+			pos += plen;
+			break;
+		}
+		counter++;
+	}
+
+	/*
+	 * Mask out unused bits in the last octet if it does not use all the
+	 * bits.
+	 */
+	if (buf_len_bits % 8) {
+		u8 mask = 0xff << (8 - buf_len_bits % 8);
+		buf[pos - 1] &= mask;
+	}
+
+	forced_memzero(hash, sizeof(hash));
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha384-tlsprf.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha384-tlsprf.c
new file mode 100644
index 0000000..9ff96ac
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha384-tlsprf.c
@@ -0,0 +1,71 @@
+/*
+ * TLS PRF P_SHA384
+ * Copyright (c) 2011-2019, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha384.h"
+
+
+/**
+ * tls_prf_sha384 - Pseudo-Random Function for TLS v1.2 (P_SHA384, RFC 5246)
+ * @secret: Key for PRF
+ * @secret_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @out: Buffer for the generated pseudo-random key
+ * @outlen: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key in TLS. This PRF is defined in RFC 5246, Chapter 5.
+ */
+int tls_prf_sha384(const u8 *secret, size_t secret_len, const char *label,
+		   const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
+{
+	size_t clen;
+	u8 A[SHA384_MAC_LEN];
+	u8 P[SHA384_MAC_LEN];
+	size_t pos;
+	const unsigned char *addr[3];
+	size_t len[3];
+
+	addr[0] = A;
+	len[0] = SHA384_MAC_LEN;
+	addr[1] = (unsigned char *) label;
+	len[1] = os_strlen(label);
+	addr[2] = seed;
+	len[2] = seed_len;
+
+	/*
+	 * RFC 5246, Chapter 5
+	 * A(0) = seed, A(i) = HMAC(secret, A(i-1))
+	 * P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + ..
+	 * PRF(secret, label, seed) = P_SHA384(secret, label + seed)
+	 */
+
+	if (hmac_sha384_vector(secret, secret_len, 2, &addr[1], &len[1], A) < 0)
+		return -1;
+
+	pos = 0;
+	while (pos < outlen) {
+		if (hmac_sha384_vector(secret, secret_len, 3, addr, len, P) <
+		    0 ||
+		    hmac_sha384(secret, secret_len, A, SHA384_MAC_LEN, A) < 0)
+			return -1;
+
+		clen = outlen - pos;
+		if (clen > SHA384_MAC_LEN)
+			clen = SHA384_MAC_LEN;
+		os_memcpy(out + pos, P, clen);
+		pos += clen;
+	}
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha384.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha384.c
new file mode 100644
index 0000000..be07e9c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha384.c
@@ -0,0 +1,104 @@
+/*
+ * SHA-384 hash implementation and interface functions
+ * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha384.h"
+#include "crypto.h"
+
+
+/**
+ * hmac_sha384_vector - HMAC-SHA384 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (48 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	unsigned char k_pad[128]; /* padding - key XORd with ipad/opad */
+	unsigned char tk[48];
+	const u8 *_addr[HMAC_VECTOR_MAX_ELEM + 1];
+	size_t _len[HMAC_VECTOR_MAX_ELEM + 1], i;
+
+	if (num_elem > HMAC_VECTOR_MAX_ELEM) {
+		/*
+		 * Fixed limit on the number of fragments to avoid having to
+		 * allocate memory (which could fail).
+		 */
+		return -1;
+	}
+
+	/* if key is longer than 128 bytes reset it to key = SHA384(key) */
+	if (key_len > 128) {
+		if (sha384_vector(1, &key, &key_len, tk) < 0)
+			return -1;
+		key = tk;
+		key_len = 48;
+	}
+
+	/* the HMAC_SHA384 transform looks like:
+	 *
+	 * SHA384(K XOR opad, SHA384(K XOR ipad, text))
+	 *
+	 * where K is an n byte key
+	 * ipad is the byte 0x36 repeated 128 times
+	 * opad is the byte 0x5c repeated 128 times
+	 * and text is the data being protected */
+
+	/* start out by storing key in ipad */
+	os_memset(k_pad, 0, sizeof(k_pad));
+	os_memcpy(k_pad, key, key_len);
+	/* XOR key with ipad values */
+	for (i = 0; i < 128; i++)
+		k_pad[i] ^= 0x36;
+
+	/* perform inner SHA384 */
+	_addr[0] = k_pad;
+	_len[0] = 128;
+	for (i = 0; i < num_elem; i++) {
+		_addr[i + 1] = addr[i];
+		_len[i + 1] = len[i];
+	}
+	if (sha384_vector(1 + num_elem, _addr, _len, mac) < 0)
+		return -1;
+
+	os_memset(k_pad, 0, sizeof(k_pad));
+	os_memcpy(k_pad, key, key_len);
+	/* XOR key with opad values */
+	for (i = 0; i < 128; i++)
+		k_pad[i] ^= 0x5c;
+
+	/* perform outer SHA384 */
+	_addr[0] = k_pad;
+	_len[0] = 128;
+	_addr[1] = mac;
+	_len[1] = SHA384_MAC_LEN;
+	return sha384_vector(2, _addr, _len, mac);
+}
+
+
+/**
+ * hmac_sha384 - HMAC-SHA384 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (48 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha384.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha384.h
new file mode 100644
index 0000000..d946907
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha384.h
@@ -0,0 +1,30 @@
+/*
+ * SHA384 hash implementation and interface functions
+ * Copyright (c) 2015-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA384_H
+#define SHA384_H
+
+#define SHA384_MAC_LEN 48
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac);
+int sha384_prf(const u8 *key, size_t key_len, const char *label,
+	       const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+int sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
+		    const u8 *data, size_t data_len, u8 *buf,
+		    size_t buf_len_bits);
+int tls_prf_sha384(const u8 *secret, size_t secret_len,
+		   const char *label, const u8 *seed, size_t seed_len,
+		   u8 *out, size_t outlen);
+int hmac_sha384_kdf(const u8 *secret, size_t secret_len,
+		    const char *label, const u8 *seed, size_t seed_len,
+		    u8 *out, size_t outlen);
+
+#endif /* SHA384_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha384_i.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha384_i.h
new file mode 100644
index 0000000..a00253f
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha384_i.h
@@ -0,0 +1,23 @@
+/*
+ * SHA-384 internal definitions
+ * Copyright (c) 2015, Pali Rohár <pali.rohar@gmail.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA384_I_H
+#define SHA384_I_H
+
+#include "sha512_i.h"
+
+#define SHA384_BLOCK_SIZE SHA512_BLOCK_SIZE
+
+#define sha384_state sha512_state
+
+void sha384_init(struct sha384_state *md);
+int sha384_process(struct sha384_state *md, const unsigned char *in,
+		   unsigned long inlen);
+int sha384_done(struct sha384_state *md, unsigned char *out);
+
+#endif /* SHA384_I_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha512-internal.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha512-internal.c
new file mode 100644
index 0000000..c026394
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha512-internal.c
@@ -0,0 +1,270 @@
+/*
+ * SHA-512 hash implementation and interface functions
+ * Copyright (c) 2015, Pali Rohár <pali.rohar@gmail.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha512_i.h"
+#include "crypto.h"
+
+
+/**
+ * sha512_vector - SHA512 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 of failure
+ */
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	struct sha512_state ctx;
+	size_t i;
+
+	sha512_init(&ctx);
+	for (i = 0; i < num_elem; i++)
+		if (sha512_process(&ctx, addr[i], len[i]))
+			return -1;
+	if (sha512_done(&ctx, mac))
+		return -1;
+	return 0;
+}
+
+
+/* ===== start - public domain SHA512 implementation ===== */
+
+/* This is based on SHA512 implementation in LibTomCrypt that was released into
+ * public domain by Tom St Denis. */
+
+#define CONST64(n) n ## ULL
+
+/* the K array */
+static const u64 K[80] = {
+	CONST64(0x428a2f98d728ae22), CONST64(0x7137449123ef65cd),
+	CONST64(0xb5c0fbcfec4d3b2f), CONST64(0xe9b5dba58189dbbc),
+	CONST64(0x3956c25bf348b538), CONST64(0x59f111f1b605d019),
+	CONST64(0x923f82a4af194f9b), CONST64(0xab1c5ed5da6d8118),
+	CONST64(0xd807aa98a3030242), CONST64(0x12835b0145706fbe),
+	CONST64(0x243185be4ee4b28c), CONST64(0x550c7dc3d5ffb4e2),
+	CONST64(0x72be5d74f27b896f), CONST64(0x80deb1fe3b1696b1),
+	CONST64(0x9bdc06a725c71235), CONST64(0xc19bf174cf692694),
+	CONST64(0xe49b69c19ef14ad2), CONST64(0xefbe4786384f25e3),
+	CONST64(0x0fc19dc68b8cd5b5), CONST64(0x240ca1cc77ac9c65),
+	CONST64(0x2de92c6f592b0275), CONST64(0x4a7484aa6ea6e483),
+	CONST64(0x5cb0a9dcbd41fbd4), CONST64(0x76f988da831153b5),
+	CONST64(0x983e5152ee66dfab), CONST64(0xa831c66d2db43210),
+	CONST64(0xb00327c898fb213f), CONST64(0xbf597fc7beef0ee4),
+	CONST64(0xc6e00bf33da88fc2), CONST64(0xd5a79147930aa725),
+	CONST64(0x06ca6351e003826f), CONST64(0x142929670a0e6e70),
+	CONST64(0x27b70a8546d22ffc), CONST64(0x2e1b21385c26c926),
+	CONST64(0x4d2c6dfc5ac42aed), CONST64(0x53380d139d95b3df),
+	CONST64(0x650a73548baf63de), CONST64(0x766a0abb3c77b2a8),
+	CONST64(0x81c2c92e47edaee6), CONST64(0x92722c851482353b),
+	CONST64(0xa2bfe8a14cf10364), CONST64(0xa81a664bbc423001),
+	CONST64(0xc24b8b70d0f89791), CONST64(0xc76c51a30654be30),
+	CONST64(0xd192e819d6ef5218), CONST64(0xd69906245565a910),
+	CONST64(0xf40e35855771202a), CONST64(0x106aa07032bbd1b8),
+	CONST64(0x19a4c116b8d2d0c8), CONST64(0x1e376c085141ab53),
+	CONST64(0x2748774cdf8eeb99), CONST64(0x34b0bcb5e19b48a8),
+	CONST64(0x391c0cb3c5c95a63), CONST64(0x4ed8aa4ae3418acb),
+	CONST64(0x5b9cca4f7763e373), CONST64(0x682e6ff3d6b2b8a3),
+	CONST64(0x748f82ee5defb2fc), CONST64(0x78a5636f43172f60),
+	CONST64(0x84c87814a1f0ab72), CONST64(0x8cc702081a6439ec),
+	CONST64(0x90befffa23631e28), CONST64(0xa4506cebde82bde9),
+	CONST64(0xbef9a3f7b2c67915), CONST64(0xc67178f2e372532b),
+	CONST64(0xca273eceea26619c), CONST64(0xd186b8c721c0c207),
+	CONST64(0xeada7dd6cde0eb1e), CONST64(0xf57d4f7fee6ed178),
+	CONST64(0x06f067aa72176fba), CONST64(0x0a637dc5a2c898a6),
+	CONST64(0x113f9804bef90dae), CONST64(0x1b710b35131c471b),
+	CONST64(0x28db77f523047d84), CONST64(0x32caab7b40c72493),
+	CONST64(0x3c9ebe0a15c9bebc), CONST64(0x431d67c49c100d4c),
+	CONST64(0x4cc5d4becb3e42b6), CONST64(0x597f299cfc657e2a),
+	CONST64(0x5fcb6fab3ad6faec), CONST64(0x6c44198c4a475817)
+};
+
+/* Various logical functions */
+#define Ch(x,y,z)       (z ^ (x & (y ^ z)))
+#define Maj(x,y,z)      (((x | y) & z) | (x & y))
+#define S(x, n)         ROR64c(x, n)
+#define R(x, n)         (((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((u64) n))
+#define Sigma0(x)       (S(x, 28) ^ S(x, 34) ^ S(x, 39))
+#define Sigma1(x)       (S(x, 14) ^ S(x, 18) ^ S(x, 41))
+#define Gamma0(x)       (S(x, 1) ^ S(x, 8) ^ R(x, 7))
+#define Gamma1(x)       (S(x, 19) ^ S(x, 61) ^ R(x, 6))
+#ifndef MIN
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+#endif
+
+#define ROR64c(x, y) \
+    ( ((((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((u64) (y) & CONST64(63))) | \
+      ((x) << ((u64) (64 - ((y) & CONST64(63)))))) & \
+      CONST64(0xFFFFFFFFFFFFFFFF))
+
+/* compress 1024-bits */
+static int sha512_compress(struct sha512_state *md, unsigned char *buf)
+{
+	u64 S[8], t0, t1;
+	u64 *W;
+	int i;
+
+	W = os_malloc(80 * sizeof(u64));
+	if (!W)
+		return -1;
+
+	/* copy state into S */
+	for (i = 0; i < 8; i++) {
+		S[i] = md->state[i];
+	}
+
+	/* copy the state into 1024-bits into W[0..15] */
+	for (i = 0; i < 16; i++)
+		W[i] = WPA_GET_BE64(buf + (8 * i));
+
+	/* fill W[16..79] */
+	for (i = 16; i < 80; i++) {
+		W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) +
+			W[i - 16];
+	}
+
+	/* Compress */
+	for (i = 0; i < 80; i++) {
+		t0 = S[7] + Sigma1(S[4]) + Ch(S[4], S[5], S[6]) + K[i] + W[i];
+		t1 = Sigma0(S[0]) + Maj(S[0], S[1], S[2]);
+		S[7] = S[6];
+		S[6] = S[5];
+		S[5] = S[4];
+		S[4] = S[3] + t0;
+		S[3] = S[2];
+		S[2] = S[1];
+		S[1] = S[0];
+		S[0] = t0 + t1;
+	}
+
+	/* feedback */
+	for (i = 0; i < 8; i++) {
+		md->state[i] = md->state[i] + S[i];
+	}
+
+	os_free(W);
+	return 0;
+}
+
+
+/**
+   Initialize the hash state
+   @param md   The hash state you wish to initialize
+   @return CRYPT_OK if successful
+*/
+void sha512_init(struct sha512_state *md)
+{
+	md->curlen = 0;
+	md->length = 0;
+	md->state[0] = CONST64(0x6a09e667f3bcc908);
+	md->state[1] = CONST64(0xbb67ae8584caa73b);
+	md->state[2] = CONST64(0x3c6ef372fe94f82b);
+	md->state[3] = CONST64(0xa54ff53a5f1d36f1);
+	md->state[4] = CONST64(0x510e527fade682d1);
+	md->state[5] = CONST64(0x9b05688c2b3e6c1f);
+	md->state[6] = CONST64(0x1f83d9abfb41bd6b);
+	md->state[7] = CONST64(0x5be0cd19137e2179);
+}
+
+
+/**
+   Process a block of memory though the hash
+   @param md     The hash state
+   @param in     The data to hash
+   @param inlen  The length of the data (octets)
+   @return CRYPT_OK if successful
+*/
+int sha512_process(struct sha512_state *md, const unsigned char *in,
+		   unsigned long inlen)
+{
+	unsigned long n;
+
+	if (md->curlen >= sizeof(md->buf))
+		return -1;
+
+	while (inlen > 0) {
+		if (md->curlen == 0 && inlen >= SHA512_BLOCK_SIZE) {
+			if (sha512_compress(md, (unsigned char *) in) < 0)
+				return -1;
+			md->length += SHA512_BLOCK_SIZE * 8;
+			in += SHA512_BLOCK_SIZE;
+			inlen -= SHA512_BLOCK_SIZE;
+		} else {
+			n = MIN(inlen, (SHA512_BLOCK_SIZE - md->curlen));
+			os_memcpy(md->buf + md->curlen, in, n);
+			md->curlen += n;
+			in += n;
+			inlen -= n;
+			if (md->curlen == SHA512_BLOCK_SIZE) {
+				if (sha512_compress(md, md->buf) < 0)
+					return -1;
+				md->length += 8 * SHA512_BLOCK_SIZE;
+				md->curlen = 0;
+			}
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+   Terminate the hash to get the digest
+   @param md  The hash state
+   @param out [out] The destination of the hash (64 bytes)
+   @return CRYPT_OK if successful
+*/
+int sha512_done(struct sha512_state *md, unsigned char *out)
+{
+	int i;
+
+	if (md->curlen >= sizeof(md->buf))
+		return -1;
+
+	/* increase the length of the message */
+	md->length += md->curlen * CONST64(8);
+
+	/* append the '1' bit */
+	md->buf[md->curlen++] = (unsigned char) 0x80;
+
+	/* if the length is currently above 112 bytes we append zeros
+	 * then compress.  Then we can fall back to padding zeros and length
+	 * encoding like normal.
+	 */
+	if (md->curlen > 112) {
+		while (md->curlen < 128) {
+			md->buf[md->curlen++] = (unsigned char) 0;
+		}
+		sha512_compress(md, md->buf);
+		md->curlen = 0;
+	}
+
+	/* pad up to 120 bytes of zeroes
+	 * note: that from 112 to 120 is the 64 MSB of the length.  We assume
+	 * that you won't hash > 2^64 bits of data... :-)
+	 */
+	while (md->curlen < 120) {
+		md->buf[md->curlen++] = (unsigned char) 0;
+	}
+
+	/* store length */
+	WPA_PUT_BE64(md->buf + 120, md->length);
+	sha512_compress(md, md->buf);
+
+	/* copy output */
+	for (i = 0; i < 8; i++)
+		WPA_PUT_BE64(out + (8 * i), md->state[i]);
+
+	return 0;
+}
+
+/* ===== end - public domain SHA512 implementation ===== */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha512-kdf.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha512-kdf.c
new file mode 100644
index 0000000..5bde664
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha512-kdf.c
@@ -0,0 +1,87 @@
+/*
+ * HMAC-SHA512 KDF (RFC 5295) and HKDF-Expand(SHA512) (RFC 5869)
+ * Copyright (c) 2014-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha512.h"
+
+
+/**
+ * hmac_sha512_kdf - HMAC-SHA512 based KDF (RFC 5295)
+ * @secret: Key for KDF
+ * @secret_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the KDF or %NULL to select
+ *	RFC 5869 HKDF-Expand() with arbitrary seed (= info)
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @out: Buffer for the generated pseudo-random key
+ * @outlen: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used
+ * with label = NULL and seed = info, this matches HKDF-Expand() defined in
+ * RFC 5869, Chapter 2.3.
+ */
+int hmac_sha512_kdf(const u8 *secret, size_t secret_len,
+		    const char *label, const u8 *seed, size_t seed_len,
+		    u8 *out, size_t outlen)
+{
+	u8 T[SHA512_MAC_LEN];
+	u8 iter = 1;
+	const unsigned char *addr[4];
+	size_t len[4];
+	size_t pos, clen;
+
+	addr[0] = T;
+	len[0] = SHA512_MAC_LEN;
+	if (label) {
+		addr[1] = (const unsigned char *) label;
+		len[1] = os_strlen(label) + 1;
+	} else {
+		addr[1] = (const u8 *) "";
+		len[1] = 0;
+	}
+	addr[2] = seed;
+	len[2] = seed_len;
+	addr[3] = &iter;
+	len[3] = 1;
+
+	if (hmac_sha512_vector(secret, secret_len, 3, &addr[1], &len[1], T) < 0)
+		return -1;
+
+	pos = 0;
+	for (;;) {
+		clen = outlen - pos;
+		if (clen > SHA512_MAC_LEN)
+			clen = SHA512_MAC_LEN;
+		os_memcpy(out + pos, T, clen);
+		pos += clen;
+
+		if (pos == outlen)
+			break;
+
+		if (iter == 255) {
+			os_memset(out, 0, outlen);
+			forced_memzero(T, SHA512_MAC_LEN);
+			return -1;
+		}
+		iter++;
+
+		if (hmac_sha512_vector(secret, secret_len, 4, addr, len, T) < 0)
+		{
+			os_memset(out, 0, outlen);
+			forced_memzero(T, SHA512_MAC_LEN);
+			return -1;
+		}
+	}
+
+	forced_memzero(T, SHA512_MAC_LEN);
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha512-prf.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha512-prf.c
new file mode 100644
index 0000000..e48cf5f
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha512-prf.c
@@ -0,0 +1,108 @@
+/*
+ * SHA512-based KDF (IEEE 802.11ac)
+ * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha512.h"
+#include "crypto.h"
+
+
+/**
+ * sha512_prf - SHA512-based Key derivation function (IEEE 802.11ac, 11.6.1.7.2)
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key.
+ */
+int sha512_prf(const u8 *key, size_t key_len, const char *label,
+	       const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+	return sha512_prf_bits(key, key_len, label, data, data_len, buf,
+			       buf_len * 8);
+}
+
+
+/**
+ * sha512_prf_bits - IEEE Std 802.11ac-2013, 11.6.1.7.2 Key derivation function
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bits of key to generate
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key. If the requested buf_len is not divisible by eight, the least
+ * significant 1-7 bits of the last octet in the output are not part of the
+ * requested output.
+ */
+int sha512_prf_bits(const u8 *key, size_t key_len, const char *label,
+		    const u8 *data, size_t data_len, u8 *buf,
+		    size_t buf_len_bits)
+{
+	u16 counter = 1;
+	size_t pos, plen;
+	u8 hash[SHA512_MAC_LEN];
+	const u8 *addr[4];
+	size_t len[4];
+	u8 counter_le[2], length_le[2];
+	size_t buf_len = (buf_len_bits + 7) / 8;
+
+	addr[0] = counter_le;
+	len[0] = 2;
+	addr[1] = (u8 *) label;
+	len[1] = os_strlen(label);
+	addr[2] = data;
+	len[2] = data_len;
+	addr[3] = length_le;
+	len[3] = sizeof(length_le);
+
+	WPA_PUT_LE16(length_le, buf_len_bits);
+	pos = 0;
+	while (pos < buf_len) {
+		plen = buf_len - pos;
+		WPA_PUT_LE16(counter_le, counter);
+		if (plen >= SHA512_MAC_LEN) {
+			if (hmac_sha512_vector(key, key_len, 4, addr, len,
+					       &buf[pos]) < 0)
+				return -1;
+			pos += SHA512_MAC_LEN;
+		} else {
+			if (hmac_sha512_vector(key, key_len, 4, addr, len,
+					       hash) < 0)
+				return -1;
+			os_memcpy(&buf[pos], hash, plen);
+			pos += plen;
+			break;
+		}
+		counter++;
+	}
+
+	/*
+	 * Mask out unused bits in the last octet if it does not use all the
+	 * bits.
+	 */
+	if (buf_len_bits % 8) {
+		u8 mask = 0xff << (8 - buf_len_bits % 8);
+		buf[pos - 1] &= mask;
+	}
+
+	forced_memzero(hash, sizeof(hash));
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha512.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha512.c
new file mode 100644
index 0000000..73b54c7
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha512.c
@@ -0,0 +1,104 @@
+/*
+ * SHA-512 hash implementation and interface functions
+ * Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha512.h"
+#include "crypto.h"
+
+
+/**
+ * hmac_sha512_vector - HMAC-SHA512 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (64 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	unsigned char k_pad[128]; /* padding - key XORd with ipad/opad */
+	unsigned char tk[64];
+	const u8 *_addr[HMAC_VECTOR_MAX_ELEM + 1];
+	size_t _len[HMAC_VECTOR_MAX_ELEM + 1], i;
+
+	if (num_elem > HMAC_VECTOR_MAX_ELEM) {
+		/*
+		 * Fixed limit on the number of fragments to avoid having to
+		 * allocate memory (which could fail).
+		 */
+		return -1;
+	}
+
+	/* if key is longer than 128 bytes reset it to key = SHA512(key) */
+	if (key_len > 128) {
+		if (sha512_vector(1, &key, &key_len, tk) < 0)
+			return -1;
+		key = tk;
+		key_len = 64;
+	}
+
+	/* the HMAC_SHA512 transform looks like:
+	 *
+	 * SHA512(K XOR opad, SHA512(K XOR ipad, text))
+	 *
+	 * where K is an n byte key
+	 * ipad is the byte 0x36 repeated 128 times
+	 * opad is the byte 0x5c repeated 128 times
+	 * and text is the data being protected */
+
+	/* start out by storing key in ipad */
+	os_memset(k_pad, 0, sizeof(k_pad));
+	os_memcpy(k_pad, key, key_len);
+	/* XOR key with ipad values */
+	for (i = 0; i < 128; i++)
+		k_pad[i] ^= 0x36;
+
+	/* perform inner SHA512 */
+	_addr[0] = k_pad;
+	_len[0] = 128;
+	for (i = 0; i < num_elem; i++) {
+		_addr[i + 1] = addr[i];
+		_len[i + 1] = len[i];
+	}
+	if (sha512_vector(1 + num_elem, _addr, _len, mac) < 0)
+		return -1;
+
+	os_memset(k_pad, 0, sizeof(k_pad));
+	os_memcpy(k_pad, key, key_len);
+	/* XOR key with opad values */
+	for (i = 0; i < 128; i++)
+		k_pad[i] ^= 0x5c;
+
+	/* perform outer SHA512 */
+	_addr[0] = k_pad;
+	_len[0] = 128;
+	_addr[1] = mac;
+	_len[1] = SHA512_MAC_LEN;
+	return sha512_vector(2, _addr, _len, mac);
+}
+
+
+/**
+ * hmac_sha512 - HMAC-SHA512 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (64 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha512.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha512.h
new file mode 100644
index 0000000..8e64c8b
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha512.h
@@ -0,0 +1,27 @@
+/*
+ * SHA512 hash implementation and interface functions
+ * Copyright (c) 2015-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA512_H
+#define SHA512_H
+
+#define SHA512_MAC_LEN 64
+
+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac);
+int sha512_prf(const u8 *key, size_t key_len, const char *label,
+	       const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+int sha512_prf_bits(const u8 *key, size_t key_len, const char *label,
+		    const u8 *data, size_t data_len, u8 *buf,
+		    size_t buf_len_bits);
+int hmac_sha512_kdf(const u8 *secret, size_t secret_len,
+		    const char *label, const u8 *seed, size_t seed_len,
+		    u8 *out, size_t outlen);
+
+#endif /* SHA512_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha512_i.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha512_i.h
new file mode 100644
index 0000000..1089589
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/sha512_i.h
@@ -0,0 +1,25 @@
+/*
+ * SHA-512 internal definitions
+ * Copyright (c) 2015, Pali Rohár <pali.rohar@gmail.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA512_I_H
+#define SHA512_I_H
+
+#define SHA512_BLOCK_SIZE 128
+
+struct sha512_state {
+	u64 length, state[8];
+	u32 curlen;
+	u8 buf[SHA512_BLOCK_SIZE];
+};
+
+void sha512_init(struct sha512_state *md);
+int sha512_process(struct sha512_state *md, const unsigned char *in,
+		   unsigned long inlen);
+int sha512_done(struct sha512_state *md, unsigned char *out);
+
+#endif /* SHA512_I_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls.h
new file mode 100644
index 0000000..f839f9d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls.h
@@ -0,0 +1,693 @@
+/*
+ * SSL/TLS interface definition
+ * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TLS_H
+#define TLS_H
+
+struct tls_connection;
+
+struct tls_random {
+	const u8 *client_random;
+	size_t client_random_len;
+	const u8 *server_random;
+	size_t server_random_len;
+};
+
+enum tls_event {
+	TLS_CERT_CHAIN_SUCCESS,
+	TLS_CERT_CHAIN_FAILURE,
+	TLS_PEER_CERTIFICATE,
+	TLS_ALERT,
+	TLS_UNSAFE_RENEGOTIATION_DISABLED,
+};
+
+/*
+ * Note: These are used as identifier with external programs and as such, the
+ * values must not be changed.
+ */
+enum tls_fail_reason {
+	TLS_FAIL_UNSPECIFIED = 0,
+	TLS_FAIL_UNTRUSTED = 1,
+	TLS_FAIL_REVOKED = 2,
+	TLS_FAIL_NOT_YET_VALID = 3,
+	TLS_FAIL_EXPIRED = 4,
+	TLS_FAIL_SUBJECT_MISMATCH = 5,
+	TLS_FAIL_ALTSUBJECT_MISMATCH = 6,
+	TLS_FAIL_BAD_CERTIFICATE = 7,
+	TLS_FAIL_SERVER_CHAIN_PROBE = 8,
+	TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9,
+	TLS_FAIL_DOMAIN_MISMATCH = 10,
+	TLS_FAIL_INSUFFICIENT_KEY_LEN = 11,
+	TLS_FAIL_DN_MISMATCH = 12,
+};
+
+
+#define TLS_MAX_ALT_SUBJECT 10
+
+struct tls_cert_data {
+	int depth;
+	const char *subject;
+	const struct wpabuf *cert;
+	const u8 *hash;
+	size_t hash_len;
+	const char *altsubject[TLS_MAX_ALT_SUBJECT];
+	int num_altsubject;
+	const char *serial_num;
+	int tod;
+};
+
+union tls_event_data {
+	struct {
+		int depth;
+		const char *subject;
+		enum tls_fail_reason reason;
+		const char *reason_txt;
+		const struct wpabuf *cert;
+	} cert_fail;
+
+	struct tls_cert_data peer_cert;
+
+	struct {
+		int is_local;
+		const char *type;
+		const char *description;
+	} alert;
+};
+
+struct tls_config {
+#ifndef CONFIG_OPENSC_ENGINE_PATH
+	const char *opensc_engine_path;
+#endif /* CONFIG_OPENSC_ENGINE_PATH */
+#ifndef CONFIG_PKCS11_ENGINE_PATH
+	const char *pkcs11_engine_path;
+#endif /* CONFIG_PKCS11_ENGINE_PATH */
+#ifndef CONFIG_PKCS11_MODULE_PATH
+	const char *pkcs11_module_path;
+#endif /* CONFIG_PKCS11_MODULE_PATH */
+	int fips_mode;
+	int cert_in_cb;
+	const char *openssl_ciphers;
+	unsigned int tls_session_lifetime;
+	unsigned int crl_reload_interval;
+	unsigned int tls_flags;
+
+	void (*event_cb)(void *ctx, enum tls_event ev,
+			 union tls_event_data *data);
+	void *cb_ctx;
+};
+
+#define TLS_CONN_ALLOW_SIGN_RSA_MD5 BIT(0)
+#define TLS_CONN_DISABLE_TIME_CHECKS BIT(1)
+#define TLS_CONN_DISABLE_SESSION_TICKET BIT(2)
+#define TLS_CONN_REQUEST_OCSP BIT(3)
+#define TLS_CONN_REQUIRE_OCSP BIT(4)
+#define TLS_CONN_DISABLE_TLSv1_1 BIT(5)
+#define TLS_CONN_DISABLE_TLSv1_2 BIT(6)
+#define TLS_CONN_EAP_FAST BIT(7)
+#define TLS_CONN_DISABLE_TLSv1_0 BIT(8)
+#define TLS_CONN_EXT_CERT_CHECK BIT(9)
+#define TLS_CONN_REQUIRE_OCSP_ALL BIT(10)
+#define TLS_CONN_SUITEB BIT(11)
+#define TLS_CONN_SUITEB_NO_ECDH BIT(12)
+#define TLS_CONN_DISABLE_TLSv1_3 BIT(13)
+#define TLS_CONN_ENABLE_TLSv1_0 BIT(14)
+#define TLS_CONN_ENABLE_TLSv1_1 BIT(15)
+#define TLS_CONN_ENABLE_TLSv1_2 BIT(16)
+#define TLS_CONN_TEAP_ANON_DH BIT(17)
+#define TLS_CONN_ALLOW_UNSAFE_RENEGOTIATION BIT(18)
+
+/**
+ * struct tls_connection_params - Parameters for TLS connection
+ * @ca_cert: File or reference name for CA X.509 certificate in PEM or DER
+ * format
+ * @ca_cert_blob: ca_cert as inlined data or %NULL if not used
+ * @ca_cert_blob_len: ca_cert_blob length
+ * @ca_path: Path to CA certificates (OpenSSL specific)
+ * @subject_match: String to match in the subject of the peer certificate or
+ * %NULL to allow all subjects
+ * @altsubject_match: String to match in the alternative subject of the peer
+ * certificate or %NULL to allow all alternative subjects
+ * @suffix_match: Semicolon deliminated string of values to suffix match against
+ * the dNSName or CN of the peer certificate or %NULL to allow all domain names.
+ * This may allow subdomains and wildcard certificates. Each domain name label
+ * must have a full case-insensitive match.
+ * @domain_match: String to match in the dNSName or CN of the peer
+ * certificate or %NULL to allow all domain names. This requires a full,
+ * case-insensitive match.
+ *
+ * More than one match string can be provided by using semicolons to
+ * separate the strings (e.g., example.org;example.com). When multiple
+ * strings are specified, a match with any one of the values is
+ * considered a sufficient match for the certificate, i.e., the
+ * conditions are ORed together.
+ * @client_cert: File or reference name for client X.509 certificate in PEM or
+ * DER format
+ * @client_cert_blob: client_cert as inlined data or %NULL if not used
+ * @client_cert_blob_len: client_cert_blob length
+ * @private_key: File or reference name for client private key in PEM or DER
+ * format (traditional format (RSA PRIVATE KEY) or PKCS#8 (PRIVATE KEY)
+ * @private_key_blob: private_key as inlined data or %NULL if not used
+ * @private_key_blob_len: private_key_blob length
+ * @private_key_passwd: Passphrase for decrypted private key, %NULL if no
+ * passphrase is used.
+ * @dh_file: File name for DH/DSA data in PEM format, or %NULL if not used
+ * @engine: 1 = use engine (e.g., a smartcard) for private key operations
+ * (this is OpenSSL specific for now)
+ * @engine_id: engine id string (this is OpenSSL specific for now)
+ * @ppin: pointer to the pin variable in the configuration
+ * (this is OpenSSL specific for now)
+ * @key_id: the private key's id when using engine (this is OpenSSL
+ * specific for now)
+ * @cert_id: the certificate's id when using engine
+ * @ca_cert_id: the CA certificate's id when using engine
+ * @openssl_ciphers: OpenSSL cipher configuration
+ * @openssl_ecdh_curves: OpenSSL ECDH curve configuration. %NULL for auto if
+ *	supported, empty string to disable, or a colon-separated curve list.
+ * @flags: Parameter options (TLS_CONN_*)
+ * @ocsp_stapling_response: DER encoded file with cached OCSP stapling response
+ *	or %NULL if OCSP is not enabled
+ * @ocsp_stapling_response_multi: DER encoded file with cached OCSP stapling
+ *	response list (OCSPResponseList for ocsp_multi in RFC 6961) or %NULL if
+ *	ocsp_multi is not enabled
+ * @check_cert_subject: Client certificate subject name matching string
+ *
+ * TLS connection parameters to be configured with tls_connection_set_params()
+ * and tls_global_set_params().
+ *
+ * Certificates and private key can be configured either as a reference name
+ * (file path or reference to certificate store) or by providing the same data
+ * as a pointer to the data in memory. Only one option will be used for each
+ * field.
+ */
+struct tls_connection_params {
+	const char *ca_cert;
+	const u8 *ca_cert_blob;
+	size_t ca_cert_blob_len;
+	const char *ca_path;
+	const char *subject_match;
+	const char *altsubject_match;
+	const char *suffix_match;
+	const char *domain_match;
+	const char *client_cert;
+	const char *client_cert2;
+	const u8 *client_cert_blob;
+	size_t client_cert_blob_len;
+	const char *private_key;
+	const char *private_key2;
+	const u8 *private_key_blob;
+	size_t private_key_blob_len;
+	const char *private_key_passwd;
+	const char *private_key_passwd2;
+	const char *dh_file;
+
+	/* OpenSSL specific variables */
+	int engine;
+	const char *engine_id;
+	const char *pin;
+	const char *key_id;
+	const char *cert_id;
+	const char *ca_cert_id;
+	const char *openssl_ciphers;
+	const char *openssl_ecdh_curves;
+
+	unsigned int flags;
+	const char *ocsp_stapling_response;
+	const char *ocsp_stapling_response_multi;
+	const char *check_cert_subject;
+};
+
+
+/**
+ * tls_init - Initialize TLS library
+ * @conf: Configuration data for TLS library
+ * Returns: Context data to be used as tls_ctx in calls to other functions,
+ * or %NULL on failure.
+ *
+ * Called once during program startup and once for each RSN pre-authentication
+ * session. In other words, there can be two concurrent TLS contexts. If global
+ * library initialization is needed (i.e., one that is shared between both
+ * authentication types), the TLS library wrapper should maintain a reference
+ * counter and do global initialization only when moving from 0 to 1 reference.
+ */
+void * tls_init(const struct tls_config *conf);
+
+/**
+ * tls_deinit - Deinitialize TLS library
+ * @tls_ctx: TLS context data from tls_init()
+ *
+ * Called once during program shutdown and once for each RSN pre-authentication
+ * session. If global library deinitialization is needed (i.e., one that is
+ * shared between both authentication types), the TLS library wrapper should
+ * maintain a reference counter and do global deinitialization only when moving
+ * from 1 to 0 references.
+ */
+void tls_deinit(void *tls_ctx);
+
+/**
+ * tls_get_errors - Process pending errors
+ * @tls_ctx: TLS context data from tls_init()
+ * Returns: Number of found error, 0 if no errors detected.
+ *
+ * Process all pending TLS errors.
+ */
+int tls_get_errors(void *tls_ctx);
+
+/**
+ * tls_connection_init - Initialize a new TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * Returns: Connection context data, conn for other function calls
+ */
+struct tls_connection * tls_connection_init(void *tls_ctx);
+
+/**
+ * tls_connection_deinit - Free TLS connection data
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ *
+ * Release all resources allocated for TLS connection.
+ */
+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_established - Has the TLS connection been completed?
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 1 if TLS connection has been completed, 0 if not.
+ */
+int tls_connection_established(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_peer_serial_num - Fetch peer certificate serial number
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: Allocated string buffer containing the peer certificate serial
+ * number or %NULL on error.
+ *
+ * The caller is responsible for freeing the returned buffer with os_free().
+ */
+char * tls_connection_peer_serial_num(void *tls_ctx,
+				      struct tls_connection *conn);
+
+/**
+ * tls_connection_shutdown - Shutdown TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * Shutdown current TLS connection without releasing all resources. New
+ * connection can be started by using the same conn without having to call
+ * tls_connection_init() or setting certificates etc. again. The new
+ * connection should try to use session resumption.
+ */
+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn);
+
+enum {
+	TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN = -4,
+	TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED = -3,
+	TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED = -2
+};
+
+/**
+ * tls_connection_set_params - Set TLS connection parameters
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @params: Connection parameters
+ * Returns: 0 on success, -1 on failure,
+ * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on error causing PKCS#11 engine
+ * failure, or
+ * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the
+ * PKCS#11 engine private key, or
+ * TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN (-4) on PIN error causing PKCS#11 engine
+ * failure.
+ */
+int __must_check
+tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+			  const struct tls_connection_params *params);
+
+/**
+ * tls_global_set_params - Set TLS parameters for all TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @params: Global TLS parameters
+ * Returns: 0 on success, -1 on failure,
+ * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on error causing PKCS#11 engine
+ * failure, or
+ * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the
+ * PKCS#11 engine private key, or
+ * TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN (-4) on PIN error causing PKCS#11 engine
+ * failure.
+ */
+int __must_check tls_global_set_params(
+	void *tls_ctx, const struct tls_connection_params *params);
+
+/**
+ * tls_global_set_verify - Set global certificate verification options
+ * @tls_ctx: TLS context data from tls_init()
+ * @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate,
+ * 2 = verify CRL for all certificates
+ * @strict: 0 = allow CRL time errors, 1 = do not allow CRL time errors
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_global_set_verify(void *tls_ctx, int check_crl,
+				       int strict);
+
+/**
+ * tls_connection_set_verify - Set certificate verification options
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @verify_peer: 0 = do not verify peer certificate, 1 = verify peer
+ *	certificate (require it to be provided), 2 = verify peer certificate if
+ *	provided
+ * @flags: Connection flags (TLS_CONN_*)
+ * @session_ctx: Session caching context or %NULL to use default
+ * @session_ctx_len: Length of @session_ctx in bytes.
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_connection_set_verify(void *tls_ctx,
+					   struct tls_connection *conn,
+					   int verify_peer,
+					   unsigned int flags,
+					   const u8 *session_ctx,
+					   size_t session_ctx_len);
+
+/**
+ * tls_connection_get_random - Get random data from TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @data: Structure of client/server random data (filled on success)
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_connection_get_random(void *tls_ctx,
+					 struct tls_connection *conn,
+					 struct tls_random *data);
+
+/**
+ * tls_connection_export_key - Derive keying material from a TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @label: Label (e.g., description of the key) for PRF
+ * @context: Optional extra upper-layer context (max len 2^16)
+ * @context_len: The length of the context value
+ * @out: Buffer for output data from TLS-PRF
+ * @out_len: Length of the output buffer
+ * Returns: 0 on success, -1 on failure
+ *
+ * Exports keying material using the mechanism described in RFC 5705. If
+ * context is %NULL, context is not provided; otherwise, context is provided
+ * (including the case of empty context with context_len == 0).
+ */
+int __must_check tls_connection_export_key(void *tls_ctx,
+					   struct tls_connection *conn,
+					   const char *label,
+					   const u8 *context,
+					   size_t context_len,
+					   u8 *out, size_t out_len);
+
+/**
+ * tls_connection_get_eap_fast_key - Derive key material for EAP-FAST
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @out: Buffer for output data from TLS-PRF
+ * @out_len: Length of the output buffer
+ * Returns: 0 on success, -1 on failure
+ *
+ * Exports key material after the normal TLS key block for use with
+ * EAP-FAST. Most callers will want tls_connection_export_key(), but EAP-FAST
+ * uses a different legacy mechanism.
+ */
+int __must_check tls_connection_get_eap_fast_key(void *tls_ctx,
+						 struct tls_connection *conn,
+						 u8 *out, size_t out_len);
+
+/**
+ * tls_connection_handshake - Process TLS handshake (client side)
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Input data from TLS server
+ * @appl_data: Pointer to application data pointer, or %NULL if dropped
+ * Returns: Output data, %NULL on failure
+ *
+ * The caller is responsible for freeing the returned output data. If the final
+ * handshake message includes application data, this is decrypted and
+ * appl_data (if not %NULL) is set to point this data. The caller is
+ * responsible for freeing appl_data.
+ *
+ * This function is used during TLS handshake. The first call is done with
+ * in_data == %NULL and the library is expected to return ClientHello packet.
+ * This packet is then send to the server and a response from server is given
+ * to TLS library by calling this function again with in_data pointing to the
+ * TLS message from the server.
+ *
+ * If the TLS handshake fails, this function may return %NULL. However, if the
+ * TLS library has a TLS alert to send out, that should be returned as the
+ * output data. In this case, tls_connection_get_failed() must return failure
+ * (> 0).
+ *
+ * tls_connection_established() should return 1 once the TLS handshake has been
+ * completed successfully.
+ */
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+					 struct tls_connection *conn,
+					 const struct wpabuf *in_data,
+					 struct wpabuf **appl_data);
+
+struct wpabuf * tls_connection_handshake2(void *tls_ctx,
+					  struct tls_connection *conn,
+					  const struct wpabuf *in_data,
+					  struct wpabuf **appl_data,
+					  int *more_data_needed);
+
+/**
+ * tls_connection_server_handshake - Process TLS handshake (server side)
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Input data from TLS peer
+ * @appl_data: Pointer to application data pointer, or %NULL if dropped
+ * Returns: Output data, %NULL on failure
+ *
+ * The caller is responsible for freeing the returned output data.
+ */
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+						struct tls_connection *conn,
+						const struct wpabuf *in_data,
+						struct wpabuf **appl_data);
+
+/**
+ * tls_connection_encrypt - Encrypt data into TLS tunnel
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Plaintext data to be encrypted
+ * Returns: Encrypted TLS data or %NULL on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * send data in the encrypted tunnel. The caller is responsible for freeing the
+ * returned output data.
+ */
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data);
+
+/**
+ * tls_connection_decrypt - Decrypt data from TLS tunnel
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Encrypted TLS data
+ * Returns: Decrypted TLS data or %NULL on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * receive data from the encrypted tunnel. The caller is responsible for
+ * freeing the returned output data.
+ */
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data);
+
+struct wpabuf * tls_connection_decrypt2(void *tls_ctx,
+					struct tls_connection *conn,
+					const struct wpabuf *in_data,
+					int *more_data_needed);
+
+/**
+ * tls_connection_resumed - Was session resumption used
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 1 if current session used session resumption, 0 if not
+ */
+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn);
+
+enum {
+	TLS_CIPHER_NONE,
+	TLS_CIPHER_RC4_SHA /* 0x0005 */,
+	TLS_CIPHER_AES128_SHA /* 0x002f */,
+	TLS_CIPHER_RSA_DHE_AES128_SHA /* 0x0031 */,
+	TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */,
+	TLS_CIPHER_RSA_DHE_AES256_SHA /* 0x0039 */,
+	TLS_CIPHER_AES256_SHA /* 0x0035 */,
+};
+
+/**
+ * tls_connection_set_cipher_list - Configure acceptable cipher suites
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers
+ * (TLS_CIPHER_*).
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_connection_set_cipher_list(void *tls_ctx,
+						struct tls_connection *conn,
+						u8 *ciphers);
+
+/**
+ * tls_get_version - Get the current TLS version number
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @buf: Buffer for returning the TLS version number
+ * @buflen: buf size
+ * Returns: 0 on success, -1 on failure
+ *
+ * Get the currently used TLS version number.
+ */
+int __must_check tls_get_version(void *tls_ctx, struct tls_connection *conn,
+				 char *buf, size_t buflen);
+
+/**
+ * tls_get_cipher - Get current cipher name
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @buf: Buffer for the cipher name
+ * @buflen: buf size
+ * Returns: 0 on success, -1 on failure
+ *
+ * Get the name of the currently used cipher.
+ */
+int __must_check tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
+				char *buf, size_t buflen);
+
+/**
+ * tls_connection_enable_workaround - Enable TLS workaround options
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to enable connection-specific workaround options for
+ * buffer SSL/TLS implementations.
+ */
+int __must_check tls_connection_enable_workaround(void *tls_ctx,
+						  struct tls_connection *conn);
+
+/**
+ * tls_connection_client_hello_ext - Set TLS extension for ClientHello
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @ext_type: Extension type
+ * @data: Extension payload (%NULL to remove extension)
+ * @data_len: Extension payload length
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_connection_client_hello_ext(void *tls_ctx,
+						 struct tls_connection *conn,
+						 int ext_type, const u8 *data,
+						 size_t data_len);
+
+/**
+ * tls_connection_get_failed - Get connection failure status
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ *
+ * Returns >0 if connection has failed, 0 if not.
+ */
+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_get_read_alerts - Get connection read alert status
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: Number of times a fatal read (remote end reported error) has
+ * happened during this connection.
+ */
+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_get_write_alerts - Get connection write alert status
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: Number of times a fatal write (locally detected error) has happened
+ * during this connection.
+ */
+int tls_connection_get_write_alerts(void *tls_ctx,
+				    struct tls_connection *conn);
+
+typedef int (*tls_session_ticket_cb)
+(void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
+ const u8 *server_random, u8 *master_secret);
+
+int __must_check  tls_connection_set_session_ticket_cb(
+	void *tls_ctx, struct tls_connection *conn,
+	tls_session_ticket_cb cb, void *ctx);
+
+void tls_connection_set_log_cb(struct tls_connection *conn,
+			       void (*log_cb)(void *ctx, const char *msg),
+			       void *ctx);
+
+#define TLS_BREAK_VERIFY_DATA BIT(0)
+#define TLS_BREAK_SRV_KEY_X_HASH BIT(1)
+#define TLS_BREAK_SRV_KEY_X_SIGNATURE BIT(2)
+#define TLS_DHE_PRIME_511B BIT(3)
+#define TLS_DHE_PRIME_767B BIT(4)
+#define TLS_DHE_PRIME_15 BIT(5)
+#define TLS_DHE_PRIME_58B BIT(6)
+#define TLS_DHE_NON_PRIME BIT(7)
+
+void tls_connection_set_test_flags(struct tls_connection *conn, u32 flags);
+
+int tls_get_library_version(char *buf, size_t buf_len);
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+				     struct wpabuf *data);
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn);
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn);
+
+void tls_connection_remove_session(struct tls_connection *conn);
+
+/**
+ * tls_get_tls_unique - Fetch "tls-unique" for channel binding
+ * @conn: Connection context data from tls_connection_init()
+ * @buf: Buffer for returning the value
+ * @max_len: Maximum length of the buffer in bytes
+ * Returns: Number of bytes written to buf or -1 on error
+ *
+ * This function can be used to fetch "tls-unique" (RFC 5929, Section 3) which
+ * is the first TLS Finished message sent in the most recent TLS handshake of
+ * the TLS connection.
+ */
+int tls_get_tls_unique(struct tls_connection *conn, u8 *buf, size_t max_len);
+
+/**
+ * tls_connection_get_cipher_suite - Get current TLS cipher suite
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: TLS cipher suite of the current connection or 0 on error
+ */
+u16 tls_connection_get_cipher_suite(struct tls_connection *conn);
+
+/**
+ * tls_connection_get_peer_subject - Get peer subject
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: Peer subject or %NULL if not authenticated or not available
+ */
+const char * tls_connection_get_peer_subject(struct tls_connection *conn);
+
+/**
+ * tls_connection_get_own_cert_used - Was own certificate used
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: true if own certificate was used during authentication
+ */
+bool tls_connection_get_own_cert_used(struct tls_connection *conn);
+
+#endif /* TLS_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls_gnutls.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls_gnutls.c
new file mode 100644
index 0000000..e3f5b5a
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls_gnutls.c
@@ -0,0 +1,1787 @@
+/*
+ * SSL/TLS interface functions for GnuTLS
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#ifdef PKCS12_FUNCS
+#include <gnutls/pkcs12.h>
+#endif /* PKCS12_FUNCS */
+#if GNUTLS_VERSION_NUMBER >= 0x030103
+#include <gnutls/ocsp.h>
+#endif /* 3.1.3 */
+
+#include "common.h"
+#include "crypto/crypto.h"
+#include "tls.h"
+
+
+static int tls_gnutls_ref_count = 0;
+
+struct tls_global {
+	/* Data for session resumption */
+	void *session_data;
+	size_t session_data_size;
+
+	int server;
+
+	int params_set;
+	gnutls_certificate_credentials_t xcred;
+
+	void (*event_cb)(void *ctx, enum tls_event ev,
+			 union tls_event_data *data);
+	void *cb_ctx;
+	int cert_in_cb;
+
+	char *ocsp_stapling_response;
+};
+
+struct tls_connection {
+	struct tls_global *global;
+	gnutls_session_t session;
+	int read_alerts, write_alerts, failed;
+
+	u8 *pre_shared_secret;
+	size_t pre_shared_secret_len;
+	int established;
+	int verify_peer;
+	unsigned int disable_time_checks:1;
+
+	struct wpabuf *push_buf;
+	struct wpabuf *pull_buf;
+	const u8 *pull_buf_offset;
+
+	int params_set;
+	gnutls_certificate_credentials_t xcred;
+
+	char *suffix_match;
+	char *domain_match;
+	unsigned int flags;
+};
+
+
+static int tls_connection_verify_peer(gnutls_session_t session);
+
+
+static void tls_log_func(int level, const char *msg)
+{
+	char *s, *pos;
+	if (level == 6 || level == 7) {
+		/* These levels seem to be mostly I/O debug and msg dumps */
+		return;
+	}
+
+	s = os_strdup(msg);
+	if (s == NULL)
+		return;
+
+	pos = s;
+	while (*pos != '\0') {
+		if (*pos == '\n') {
+			*pos = '\0';
+			break;
+		}
+		pos++;
+	}
+	wpa_printf(level > 3 ? MSG_MSGDUMP : MSG_DEBUG,
+		   "gnutls<%d> %s", level, s);
+	os_free(s);
+}
+
+
+void * tls_init(const struct tls_config *conf)
+{
+	struct tls_global *global;
+
+	if (tls_gnutls_ref_count == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: Library version %s (runtime) - %s (build)",
+			   gnutls_check_version(NULL), GNUTLS_VERSION);
+	}
+
+	global = os_zalloc(sizeof(*global));
+	if (global == NULL)
+		return NULL;
+
+	if (tls_gnutls_ref_count == 0 && gnutls_global_init() < 0) {
+		os_free(global);
+		return NULL;
+	}
+	tls_gnutls_ref_count++;
+
+	gnutls_global_set_log_function(tls_log_func);
+	if (wpa_debug_show_keys)
+		gnutls_global_set_log_level(11);
+
+	if (conf) {
+		global->event_cb = conf->event_cb;
+		global->cb_ctx = conf->cb_ctx;
+		global->cert_in_cb = conf->cert_in_cb;
+	}
+
+	return global;
+}
+
+
+void tls_deinit(void *ssl_ctx)
+{
+	struct tls_global *global = ssl_ctx;
+	if (global) {
+		if (global->params_set)
+			gnutls_certificate_free_credentials(global->xcred);
+		os_free(global->session_data);
+		os_free(global->ocsp_stapling_response);
+		os_free(global);
+	}
+
+	tls_gnutls_ref_count--;
+	if (tls_gnutls_ref_count == 0)
+		gnutls_global_deinit();
+}
+
+
+int tls_get_errors(void *ssl_ctx)
+{
+	return 0;
+}
+
+
+static ssize_t tls_pull_func(gnutls_transport_ptr_t ptr, void *buf,
+			     size_t len)
+{
+	struct tls_connection *conn = (struct tls_connection *) ptr;
+	const u8 *end;
+	if (conn->pull_buf == NULL) {
+		errno = EWOULDBLOCK;
+		return -1;
+	}
+
+	end = wpabuf_head_u8(conn->pull_buf) + wpabuf_len(conn->pull_buf);
+	if ((size_t) (end - conn->pull_buf_offset) < len)
+		len = end - conn->pull_buf_offset;
+	os_memcpy(buf, conn->pull_buf_offset, len);
+	conn->pull_buf_offset += len;
+	if (conn->pull_buf_offset == end) {
+		wpa_printf(MSG_DEBUG, "%s - pull_buf consumed", __func__);
+		wpabuf_free(conn->pull_buf);
+		conn->pull_buf = NULL;
+		conn->pull_buf_offset = NULL;
+	} else {
+		wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in pull_buf",
+			   __func__,
+			   (unsigned long) (end - conn->pull_buf_offset));
+	}
+	return len;
+}
+
+
+static ssize_t tls_push_func(gnutls_transport_ptr_t ptr, const void *buf,
+			     size_t len)
+{
+	struct tls_connection *conn = (struct tls_connection *) ptr;
+
+	if (wpabuf_resize(&conn->push_buf, len) < 0) {
+		errno = ENOMEM;
+		return -1;
+	}
+	wpabuf_put_data(conn->push_buf, buf, len);
+
+	return len;
+}
+
+
+static int tls_gnutls_init_session(struct tls_global *global,
+				   struct tls_connection *conn)
+{
+	const char *err;
+	int ret;
+
+	ret = gnutls_init(&conn->session,
+			  global->server ? GNUTLS_SERVER : GNUTLS_CLIENT);
+	if (ret < 0) {
+		wpa_printf(MSG_INFO, "TLS: Failed to initialize new TLS "
+			   "connection: %s", gnutls_strerror(ret));
+		return -1;
+	}
+
+	ret = gnutls_set_default_priority(conn->session);
+	if (ret < 0)
+		goto fail;
+
+	ret = gnutls_priority_set_direct(conn->session, "NORMAL:-VERS-SSL3.0",
+					 &err);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "GnuTLS: Priority string failure at "
+			   "'%s'", err);
+		goto fail;
+	}
+
+	gnutls_transport_set_pull_function(conn->session, tls_pull_func);
+	gnutls_transport_set_push_function(conn->session, tls_push_func);
+	gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr_t) conn);
+	gnutls_session_set_ptr(conn->session, conn);
+
+	return 0;
+
+fail:
+	wpa_printf(MSG_INFO, "TLS: Failed to setup new TLS connection: %s",
+		   gnutls_strerror(ret));
+	gnutls_deinit(conn->session);
+	return -1;
+}
+
+
+struct tls_connection * tls_connection_init(void *ssl_ctx)
+{
+	struct tls_global *global = ssl_ctx;
+	struct tls_connection *conn;
+	int ret;
+
+	conn = os_zalloc(sizeof(*conn));
+	if (conn == NULL)
+		return NULL;
+	conn->global = global;
+
+	if (tls_gnutls_init_session(global, conn)) {
+		os_free(conn);
+		return NULL;
+	}
+
+	if (global->params_set) {
+		ret = gnutls_credentials_set(conn->session,
+					     GNUTLS_CRD_CERTIFICATE,
+					     global->xcred);
+		if (ret < 0) {
+			wpa_printf(MSG_INFO, "Failed to configure "
+				   "credentials: %s", gnutls_strerror(ret));
+			os_free(conn);
+			return NULL;
+		}
+	}
+
+	if (gnutls_certificate_allocate_credentials(&conn->xcred)) {
+		os_free(conn);
+		return NULL;
+	}
+
+	return conn;
+}
+
+
+void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
+{
+	if (conn == NULL)
+		return;
+
+	gnutls_certificate_free_credentials(conn->xcred);
+	gnutls_deinit(conn->session);
+	os_free(conn->pre_shared_secret);
+	wpabuf_free(conn->push_buf);
+	wpabuf_free(conn->pull_buf);
+	os_free(conn->suffix_match);
+	os_free(conn->domain_match);
+	os_free(conn);
+}
+
+
+int tls_connection_established(void *ssl_ctx, struct tls_connection *conn)
+{
+	return conn ? conn->established : 0;
+}
+
+
+char * tls_connection_peer_serial_num(void *tls_ctx,
+				      struct tls_connection *conn)
+{
+	/* TODO */
+	return NULL;
+}
+
+
+int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
+{
+	struct tls_global *global = ssl_ctx;
+	int ret;
+
+	if (conn == NULL)
+		return -1;
+
+	/* Shutdown previous TLS connection without notifying the peer
+	 * because the connection was already terminated in practice
+	 * and "close notify" shutdown alert would confuse AS. */
+	gnutls_bye(conn->session, GNUTLS_SHUT_RDWR);
+	wpabuf_free(conn->push_buf);
+	conn->push_buf = NULL;
+	conn->established = 0;
+
+	gnutls_deinit(conn->session);
+	if (tls_gnutls_init_session(global, conn)) {
+		wpa_printf(MSG_INFO, "GnuTLS: Failed to preparare new session "
+			   "for session resumption use");
+		return -1;
+	}
+
+	ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE,
+				     conn->params_set ? conn->xcred :
+				     global->xcred);
+	if (ret < 0) {
+		wpa_printf(MSG_INFO, "GnuTLS: Failed to configure credentials "
+			   "for session resumption: %s", gnutls_strerror(ret));
+		return -1;
+	}
+
+	if (global->session_data) {
+		ret = gnutls_session_set_data(conn->session,
+					      global->session_data,
+					      global->session_data_size);
+		if (ret < 0) {
+			wpa_printf(MSG_INFO, "GnuTLS: Failed to set session "
+				   "data: %s", gnutls_strerror(ret));
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+			      const struct tls_connection_params *params)
+{
+	int ret;
+	const char *err;
+	char prio_buf[100];
+	const char *prio = NULL;
+
+	if (conn == NULL || params == NULL)
+		return -1;
+
+	if (params->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
+		wpa_printf(MSG_INFO,
+			   "GnuTLS: ocsp=3 not supported");
+		return -1;
+	}
+
+	if (params->flags & TLS_CONN_EXT_CERT_CHECK) {
+		wpa_printf(MSG_INFO,
+			   "GnuTLS: tls_ext_cert_check=1 not supported");
+		return -1;
+	}
+
+	if (params->subject_match) {
+		wpa_printf(MSG_INFO, "GnuTLS: subject_match not supported");
+		return -1;
+	}
+
+	if (params->altsubject_match) {
+		wpa_printf(MSG_INFO, "GnuTLS: altsubject_match not supported");
+		return -1;
+	}
+
+	os_free(conn->suffix_match);
+	conn->suffix_match = NULL;
+	if (params->suffix_match) {
+		conn->suffix_match = os_strdup(params->suffix_match);
+		if (conn->suffix_match == NULL)
+			return -1;
+	}
+
+#if GNUTLS_VERSION_NUMBER >= 0x030300
+	os_free(conn->domain_match);
+	conn->domain_match = NULL;
+	if (params->domain_match) {
+		conn->domain_match = os_strdup(params->domain_match);
+		if (conn->domain_match == NULL)
+			return -1;
+	}
+#else /* < 3.3.0 */
+	if (params->domain_match) {
+		wpa_printf(MSG_INFO, "GnuTLS: domain_match not supported");
+		return -1;
+	}
+#endif /* >= 3.3.0 */
+
+	conn->flags = params->flags;
+
+	if (params->flags & (TLS_CONN_DISABLE_TLSv1_0 |
+			     TLS_CONN_DISABLE_TLSv1_1 |
+			     TLS_CONN_DISABLE_TLSv1_2)) {
+		os_snprintf(prio_buf, sizeof(prio_buf),
+			    "NORMAL:-VERS-SSL3.0%s%s%s",
+			    params->flags & TLS_CONN_DISABLE_TLSv1_0 ?
+			    ":-VERS-TLS1.0" : "",
+			    params->flags & TLS_CONN_DISABLE_TLSv1_1 ?
+			    ":-VERS-TLS1.1" : "",
+			    params->flags & TLS_CONN_DISABLE_TLSv1_2 ?
+			    ":-VERS-TLS1.2" : "");
+		prio = prio_buf;
+	}
+
+	if (params->openssl_ciphers) {
+		if (os_strcmp(params->openssl_ciphers, "SUITEB128") == 0) {
+			prio = "SUITEB128";
+		} else if (os_strcmp(params->openssl_ciphers,
+				     "SUITEB192") == 0) {
+			prio = "SUITEB192";
+		} else if ((params->flags & TLS_CONN_SUITEB) &&
+			   os_strcmp(params->openssl_ciphers,
+				     "ECDHE-RSA-AES256-GCM-SHA384") == 0) {
+			prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL";
+		} else if (os_strcmp(params->openssl_ciphers,
+				     "ECDHE-RSA-AES256-GCM-SHA384") == 0) {
+			prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL";
+		} else if (os_strcmp(params->openssl_ciphers,
+				     "DHE-RSA-AES256-GCM-SHA384") == 0) {
+			prio = "NONE:+VERS-TLS1.2:+AEAD:+DHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL:%PROFILE_HIGH";
+		} else if (os_strcmp(params->openssl_ciphers,
+				     "ECDHE-ECDSA-AES256-GCM-SHA384") == 0) {
+			prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-ECDSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL";
+		} else {
+			wpa_printf(MSG_INFO,
+				   "GnuTLS: openssl_ciphers not supported");
+			return -1;
+		}
+	} else if (params->flags & TLS_CONN_SUITEB) {
+		prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-ECDSA:+ECDHE-RSA:+DHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL:%PROFILE_HIGH";
+	}
+
+	if (prio) {
+		wpa_printf(MSG_DEBUG, "GnuTLS: Set priority string: %s", prio);
+		ret = gnutls_priority_set_direct(conn->session, prio, &err);
+		if (ret < 0) {
+			wpa_printf(MSG_ERROR,
+				   "GnuTLS: Priority string failure at '%s'",
+				   err);
+			return -1;
+		}
+	}
+
+	if (params->openssl_ecdh_curves) {
+		wpa_printf(MSG_INFO,
+			   "GnuTLS: openssl_ecdh_curves not supported");
+		return -1;
+	}
+
+	/* TODO: gnutls_certificate_set_verify_flags(xcred, flags);
+	 * to force peer validation(?) */
+
+	if (params->ca_cert) {
+		wpa_printf(MSG_DEBUG, "GnuTLS: Try to parse %s in DER format",
+			   params->ca_cert);
+		ret = gnutls_certificate_set_x509_trust_file(
+			conn->xcred, params->ca_cert, GNUTLS_X509_FMT_DER);
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "GnuTLS: Failed to read CA cert '%s' in DER format (%s) - try in PEM format",
+				   params->ca_cert,
+				   gnutls_strerror(ret));
+			ret = gnutls_certificate_set_x509_trust_file(
+				conn->xcred, params->ca_cert,
+				GNUTLS_X509_FMT_PEM);
+			if (ret < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "Failed to read CA cert '%s' in PEM format: %s",
+					   params->ca_cert,
+					   gnutls_strerror(ret));
+				return -1;
+			}
+			wpa_printf(MSG_DEBUG,
+				   "GnuTLS: Successfully read CA cert '%s' in PEM format",
+				   params->ca_cert);
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "GnuTLS: Successfully read CA cert '%s' in DER format",
+				   params->ca_cert);
+		}
+	} else if (params->ca_cert_blob) {
+		gnutls_datum_t ca;
+
+		ca.data = (unsigned char *) params->ca_cert_blob;
+		ca.size = params->ca_cert_blob_len;
+
+		ret = gnutls_certificate_set_x509_trust_mem(
+			conn->xcred, &ca, GNUTLS_X509_FMT_DER);
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "Failed to parse CA cert in DER format: %s",
+				   gnutls_strerror(ret));
+			ret = gnutls_certificate_set_x509_trust_mem(
+				conn->xcred, &ca, GNUTLS_X509_FMT_PEM);
+			if (ret < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "Failed to parse CA cert in PEM format: %s",
+					   gnutls_strerror(ret));
+				return -1;
+			}
+		}
+	} else if (params->ca_path) {
+		wpa_printf(MSG_INFO, "GnuTLS: ca_path not supported");
+		return -1;
+	}
+
+	conn->disable_time_checks = 0;
+	if (params->ca_cert || params->ca_cert_blob) {
+		conn->verify_peer = 1;
+		gnutls_certificate_set_verify_function(
+			conn->xcred, tls_connection_verify_peer);
+
+		if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) {
+			gnutls_certificate_set_verify_flags(
+				conn->xcred, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
+		}
+
+		if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
+			conn->disable_time_checks = 1;
+			gnutls_certificate_set_verify_flags(
+				conn->xcred,
+				GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
+		}
+	}
+
+	if (params->client_cert && params->private_key) {
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: Try to parse client cert '%s' and key '%s' in DER format",
+			   params->client_cert, params->private_key);
+#if GNUTLS_VERSION_NUMBER >= 0x03010b
+		ret = gnutls_certificate_set_x509_key_file2(
+			conn->xcred, params->client_cert, params->private_key,
+			GNUTLS_X509_FMT_DER, params->private_key_passwd, 0);
+#else
+		/* private_key_passwd not (easily) supported here */
+		ret = gnutls_certificate_set_x509_key_file(
+			conn->xcred, params->client_cert, params->private_key,
+			GNUTLS_X509_FMT_DER);
+#endif
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "GnuTLS: Failed to read client cert/key in DER format (%s) - try in PEM format",
+				   gnutls_strerror(ret));
+#if GNUTLS_VERSION_NUMBER >= 0x03010b
+			ret = gnutls_certificate_set_x509_key_file2(
+				conn->xcred, params->client_cert,
+				params->private_key, GNUTLS_X509_FMT_PEM,
+				params->private_key_passwd, 0);
+#else
+			ret = gnutls_certificate_set_x509_key_file(
+				conn->xcred, params->client_cert,
+				params->private_key, GNUTLS_X509_FMT_PEM);
+#endif
+			if (ret < 0) {
+				wpa_printf(MSG_DEBUG, "Failed to read client "
+					   "cert/key in PEM format: %s",
+					   gnutls_strerror(ret));
+				return ret;
+			}
+			wpa_printf(MSG_DEBUG,
+				   "GnuTLS: Successfully read client cert/key in PEM format");
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "GnuTLS: Successfully read client cert/key in DER format");
+		}
+	} else if (params->private_key) {
+		int pkcs12_ok = 0;
+#ifdef PKCS12_FUNCS
+		/* Try to load in PKCS#12 format */
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: Try to parse client cert/key '%s'in PKCS#12 DER format",
+			   params->private_key);
+		ret = gnutls_certificate_set_x509_simple_pkcs12_file(
+			conn->xcred, params->private_key, GNUTLS_X509_FMT_DER,
+			params->private_key_passwd);
+		if (ret != 0) {
+			wpa_printf(MSG_DEBUG, "Failed to load private_key in "
+				   "PKCS#12 format: %s", gnutls_strerror(ret));
+			return -1;
+		} else
+			pkcs12_ok = 1;
+#endif /* PKCS12_FUNCS */
+
+		if (!pkcs12_ok) {
+			wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not "
+				   "included");
+			return -1;
+		}
+	} else if (params->client_cert_blob && params->private_key_blob) {
+		gnutls_datum_t cert, key;
+
+		cert.data = (unsigned char *) params->client_cert_blob;
+		cert.size = params->client_cert_blob_len;
+		key.data = (unsigned char *) params->private_key_blob;
+		key.size = params->private_key_blob_len;
+
+#if GNUTLS_VERSION_NUMBER >= 0x03010b
+		ret = gnutls_certificate_set_x509_key_mem2(
+			conn->xcred, &cert, &key, GNUTLS_X509_FMT_DER,
+			params->private_key_passwd, 0);
+#else
+		/* private_key_passwd not (easily) supported here */
+		ret = gnutls_certificate_set_x509_key_mem(
+			conn->xcred, &cert, &key, GNUTLS_X509_FMT_DER);
+#endif
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
+				   "in DER format: %s", gnutls_strerror(ret));
+#if GNUTLS_VERSION_NUMBER >= 0x03010b
+			ret = gnutls_certificate_set_x509_key_mem2(
+				conn->xcred, &cert, &key, GNUTLS_X509_FMT_PEM,
+				params->private_key_passwd, 0);
+#else
+			/* private_key_passwd not (easily) supported here */
+			ret = gnutls_certificate_set_x509_key_mem(
+				conn->xcred, &cert, &key, GNUTLS_X509_FMT_PEM);
+#endif
+			if (ret < 0) {
+				wpa_printf(MSG_DEBUG, "Failed to read client "
+					   "cert/key in PEM format: %s",
+					   gnutls_strerror(ret));
+				return ret;
+			}
+		}
+	} else if (params->private_key_blob) {
+#ifdef PKCS12_FUNCS
+		gnutls_datum_t key;
+
+		key.data = (unsigned char *) params->private_key_blob;
+		key.size = params->private_key_blob_len;
+
+		/* Try to load in PKCS#12 format */
+		ret = gnutls_certificate_set_x509_simple_pkcs12_mem(
+			conn->xcred, &key, GNUTLS_X509_FMT_DER,
+			params->private_key_passwd);
+		if (ret != 0) {
+			wpa_printf(MSG_DEBUG, "Failed to load private_key in "
+				   "PKCS#12 format: %s", gnutls_strerror(ret));
+			return -1;
+		}
+#else /* PKCS12_FUNCS */
+		wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not included");
+		return -1;
+#endif /* PKCS12_FUNCS */
+	}
+
+#if GNUTLS_VERSION_NUMBER >= 0x030103
+	if (params->flags & (TLS_CONN_REQUEST_OCSP | TLS_CONN_REQUIRE_OCSP)) {
+		ret = gnutls_ocsp_status_request_enable_client(conn->session,
+							       NULL, 0, NULL);
+		if (ret != GNUTLS_E_SUCCESS) {
+			wpa_printf(MSG_INFO,
+				   "GnuTLS: Failed to enable OCSP client");
+			return -1;
+		}
+	}
+#else /* 3.1.3 */
+	if (params->flags & TLS_CONN_REQUIRE_OCSP) {
+		wpa_printf(MSG_INFO,
+			   "GnuTLS: OCSP not supported by this version of GnuTLS");
+		return -1;
+	}
+#endif /* 3.1.3 */
+
+	conn->params_set = 1;
+
+	ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE,
+				     conn->xcred);
+	if (ret < 0) {
+		wpa_printf(MSG_INFO, "Failed to configure credentials: %s",
+			   gnutls_strerror(ret));
+	}
+
+	return ret;
+}
+
+
+#if GNUTLS_VERSION_NUMBER >= 0x030103
+static int server_ocsp_status_req(gnutls_session_t session, void *ptr,
+				  gnutls_datum_t *resp)
+{
+	struct tls_global *global = ptr;
+	char *cached;
+	size_t len;
+
+	if (!global->ocsp_stapling_response) {
+		wpa_printf(MSG_DEBUG, "GnuTLS: OCSP status callback - no response configured");
+		return GNUTLS_E_NO_CERTIFICATE_STATUS;
+	}
+
+	cached = os_readfile(global->ocsp_stapling_response, &len);
+	if (!cached) {
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: OCSP status callback - could not read response file (%s)",
+			   global->ocsp_stapling_response);
+		return GNUTLS_E_NO_CERTIFICATE_STATUS;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "GnuTLS: OCSP status callback - send cached response");
+	resp->data = gnutls_malloc(len);
+	if (!resp->data) {
+		os_free(resp);
+		return GNUTLS_E_MEMORY_ERROR;
+	}
+
+	os_memcpy(resp->data, cached, len);
+	resp->size = len;
+	os_free(cached);
+
+	return GNUTLS_E_SUCCESS;
+}
+#endif /* 3.1.3 */
+
+
+int tls_global_set_params(void *tls_ctx,
+			  const struct tls_connection_params *params)
+{
+	struct tls_global *global = tls_ctx;
+	int ret;
+
+	if (params->check_cert_subject)
+		return -1; /* not yet supported */
+
+	/* Currently, global parameters are only set when running in server
+	 * mode. */
+	global->server = 1;
+
+	if (global->params_set) {
+		gnutls_certificate_free_credentials(global->xcred);
+		global->params_set = 0;
+	}
+
+	ret = gnutls_certificate_allocate_credentials(&global->xcred);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "Failed to allocate global credentials "
+			   "%s", gnutls_strerror(ret));
+		return -1;
+	}
+
+	if (params->ca_cert) {
+		ret = gnutls_certificate_set_x509_trust_file(
+			global->xcred, params->ca_cert, GNUTLS_X509_FMT_DER);
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' "
+				   "in DER format: %s", params->ca_cert,
+				   gnutls_strerror(ret));
+			ret = gnutls_certificate_set_x509_trust_file(
+				global->xcred, params->ca_cert,
+				GNUTLS_X509_FMT_PEM);
+			if (ret < 0) {
+				wpa_printf(MSG_DEBUG, "Failed to read CA cert "
+					   "'%s' in PEM format: %s",
+					   params->ca_cert,
+					   gnutls_strerror(ret));
+				goto fail;
+			}
+		}
+
+		if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) {
+			gnutls_certificate_set_verify_flags(
+				global->xcred,
+				GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
+		}
+
+		if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
+			gnutls_certificate_set_verify_flags(
+				global->xcred,
+				GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
+		}
+	}
+
+	if (params->client_cert && params->private_key) {
+		/* TODO: private_key_passwd? */
+		ret = gnutls_certificate_set_x509_key_file(
+			global->xcred, params->client_cert,
+			params->private_key, GNUTLS_X509_FMT_DER);
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
+				   "in DER format: %s", gnutls_strerror(ret));
+			ret = gnutls_certificate_set_x509_key_file(
+				global->xcred, params->client_cert,
+				params->private_key, GNUTLS_X509_FMT_PEM);
+			if (ret < 0) {
+				wpa_printf(MSG_DEBUG, "Failed to read client "
+					   "cert/key in PEM format: %s",
+					   gnutls_strerror(ret));
+				goto fail;
+			}
+		}
+	} else if (params->private_key) {
+		int pkcs12_ok = 0;
+#ifdef PKCS12_FUNCS
+		/* Try to load in PKCS#12 format */
+		ret = gnutls_certificate_set_x509_simple_pkcs12_file(
+			global->xcred, params->private_key,
+			GNUTLS_X509_FMT_DER, params->private_key_passwd);
+		if (ret != 0) {
+			wpa_printf(MSG_DEBUG, "Failed to load private_key in "
+				   "PKCS#12 format: %s", gnutls_strerror(ret));
+			goto fail;
+		} else
+			pkcs12_ok = 1;
+#endif /* PKCS12_FUNCS */
+
+		if (!pkcs12_ok) {
+			wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not "
+				   "included");
+			goto fail;
+		}
+	}
+
+#if GNUTLS_VERSION_NUMBER >= 0x030103
+	os_free(global->ocsp_stapling_response);
+	if (params->ocsp_stapling_response)
+		global->ocsp_stapling_response =
+			os_strdup(params->ocsp_stapling_response);
+	else
+		global->ocsp_stapling_response = NULL;
+	gnutls_certificate_set_ocsp_status_request_function(
+		global->xcred, server_ocsp_status_req, global);
+#endif /* 3.1.3 */
+
+	global->params_set = 1;
+
+	return 0;
+
+fail:
+	gnutls_certificate_free_credentials(global->xcred);
+	return -1;
+}
+
+
+int tls_global_set_verify(void *ssl_ctx, int check_crl, int strict)
+{
+	/* TODO */
+	return 0;
+}
+
+
+int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
+			      int verify_peer, unsigned int flags,
+			      const u8 *session_ctx, size_t session_ctx_len)
+{
+	if (conn == NULL || conn->session == NULL)
+		return -1;
+
+	conn->verify_peer = verify_peer;
+	gnutls_certificate_server_set_request(conn->session,
+					      verify_peer ? GNUTLS_CERT_REQUIRE
+					      : GNUTLS_CERT_REQUEST);
+
+	return 0;
+}
+
+
+int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn,
+			    struct tls_random *keys)
+{
+#if GNUTLS_VERSION_NUMBER >= 0x030012
+	gnutls_datum_t client, server;
+
+	if (conn == NULL || conn->session == NULL || keys == NULL)
+		return -1;
+
+	os_memset(keys, 0, sizeof(*keys));
+	gnutls_session_get_random(conn->session, &client, &server);
+	keys->client_random = client.data;
+	keys->server_random = server.data;
+	keys->client_random_len = client.size;
+	keys->server_random_len = client.size;
+
+	return 0;
+#else /* 3.0.18 */
+	return -1;
+#endif /* 3.0.18 */
+}
+
+
+int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
+			      const char *label, const u8 *context,
+			      size_t context_len, u8 *out, size_t out_len)
+{
+	if (conn == NULL || conn->session == NULL)
+		return -1;
+
+#if GNUTLS_VERSION_NUMBER >= 0x030404
+	return gnutls_prf_rfc5705(conn->session, os_strlen(label), label,
+				  context_len, (const char *) context,
+				  out_len, (char *) out);
+#else /* 3.4.4 */
+	if (context)
+		return -1;
+	return gnutls_prf(conn->session, os_strlen(label), label,
+			  0 /* client_random first */, 0, NULL, out_len,
+			  (char *) out);
+#endif /* 3.4.4 */
+}
+
+
+int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
+				    u8 *out, size_t out_len)
+{
+	return -1;
+}
+
+
+static void gnutls_tls_fail_event(struct tls_connection *conn,
+				  const gnutls_datum_t *cert, int depth,
+				  const char *subject, const char *err_str,
+				  enum tls_fail_reason reason)
+{
+	union tls_event_data ev;
+	struct tls_global *global = conn->global;
+	struct wpabuf *cert_buf = NULL;
+
+	if (global->event_cb == NULL)
+		return;
+
+	os_memset(&ev, 0, sizeof(ev));
+	ev.cert_fail.depth = depth;
+	ev.cert_fail.subject = subject ? subject : "";
+	ev.cert_fail.reason = reason;
+	ev.cert_fail.reason_txt = err_str;
+	if (cert) {
+		cert_buf = wpabuf_alloc_copy(cert->data, cert->size);
+		ev.cert_fail.cert = cert_buf;
+	}
+	global->event_cb(global->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
+	wpabuf_free(cert_buf);
+}
+
+
+#if GNUTLS_VERSION_NUMBER < 0x030300
+static int server_eku_purpose(gnutls_x509_crt_t cert)
+{
+	unsigned int i;
+
+	for (i = 0; ; i++) {
+		char oid[128];
+		size_t oid_size = sizeof(oid);
+		int res;
+
+		res = gnutls_x509_crt_get_key_purpose_oid(cert, i, oid,
+							  &oid_size, NULL);
+		if (res == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+			if (i == 0) {
+				/* No EKU - assume any use allowed */
+				return 1;
+			}
+			break;
+		}
+
+		if (res < 0) {
+			wpa_printf(MSG_INFO, "GnuTLS: Failed to get EKU");
+			return 0;
+		}
+
+		wpa_printf(MSG_DEBUG, "GnuTLS: Certificate purpose: %s", oid);
+		if (os_strcmp(oid, GNUTLS_KP_TLS_WWW_SERVER) == 0 ||
+		    os_strcmp(oid, GNUTLS_KP_ANY) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+#endif /* < 3.3.0 */
+
+
+static int check_ocsp(struct tls_connection *conn, gnutls_session_t session,
+		      gnutls_alert_description_t *err)
+{
+#if GNUTLS_VERSION_NUMBER >= 0x030103
+	gnutls_datum_t response, buf;
+	gnutls_ocsp_resp_t resp;
+	unsigned int cert_status;
+	int res;
+
+	if (!(conn->flags & (TLS_CONN_REQUEST_OCSP | TLS_CONN_REQUIRE_OCSP)))
+		return 0;
+
+	if (!gnutls_ocsp_status_request_is_checked(session, 0)) {
+		if (conn->flags & TLS_CONN_REQUIRE_OCSP) {
+			wpa_printf(MSG_INFO,
+				   "GnuTLS: No valid OCSP response received");
+			goto ocsp_error;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: Valid OCSP response was not received - continue since OCSP was not required");
+		return 0;
+	}
+
+	/*
+	 * GnuTLS has already verified the OCSP response in
+	 * check_ocsp_response() and rejected handshake if the certificate was
+	 * found to be revoked. However, if the response indicates that the
+	 * status is unknown, handshake continues and reaches here. We need to
+	 * re-import the OCSP response to check for unknown certificate status,
+	 * but we do not need to repeat gnutls_ocsp_resp_check_crt() and
+	 * gnutls_ocsp_resp_verify_direct() calls.
+	 */
+
+	res = gnutls_ocsp_status_request_get(session, &response);
+	if (res != GNUTLS_E_SUCCESS) {
+		wpa_printf(MSG_INFO,
+			   "GnuTLS: OCSP response was received, but it was not valid");
+		goto ocsp_error;
+	}
+
+	if (gnutls_ocsp_resp_init(&resp) != GNUTLS_E_SUCCESS)
+		goto ocsp_error;
+
+	res = gnutls_ocsp_resp_import(resp, &response);
+	if (res != GNUTLS_E_SUCCESS) {
+		wpa_printf(MSG_INFO,
+			   "GnuTLS: Could not parse received OCSP response: %s",
+			   gnutls_strerror(res));
+		gnutls_ocsp_resp_deinit(resp);
+		goto ocsp_error;
+	}
+
+	res = gnutls_ocsp_resp_print(resp, GNUTLS_OCSP_PRINT_FULL, &buf);
+	if (res == GNUTLS_E_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "GnuTLS: %s", buf.data);
+		gnutls_free(buf.data);
+	}
+
+	res = gnutls_ocsp_resp_get_single(resp, 0, NULL, NULL, NULL,
+					  NULL, &cert_status, NULL,
+					  NULL, NULL, NULL);
+	gnutls_ocsp_resp_deinit(resp);
+	if (res != GNUTLS_E_SUCCESS) {
+		wpa_printf(MSG_INFO,
+			   "GnuTLS: Failed to extract OCSP information: %s",
+			   gnutls_strerror(res));
+		goto ocsp_error;
+	}
+
+	if (cert_status == GNUTLS_OCSP_CERT_GOOD) {
+		wpa_printf(MSG_DEBUG, "GnuTLS: OCSP cert status: good");
+	} else if (cert_status == GNUTLS_OCSP_CERT_REVOKED) {
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: OCSP cert status: revoked");
+		goto ocsp_error;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: OCSP cert status: unknown");
+		if (conn->flags & TLS_CONN_REQUIRE_OCSP)
+			goto ocsp_error;
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: OCSP was not required, so allow connection to continue");
+	}
+
+	return 0;
+
+ocsp_error:
+	gnutls_tls_fail_event(conn, NULL, 0, NULL,
+			      "bad certificate status response",
+			      TLS_FAIL_REVOKED);
+	*err = GNUTLS_A_CERTIFICATE_REVOKED;
+	return -1;
+#else /* GnuTLS 3.1.3 or newer */
+	return 0;
+#endif /* GnuTLS 3.1.3 or newer */
+}
+
+
+static int tls_match_suffix_helper(gnutls_x509_crt_t cert, const char *match,
+				   int full)
+{
+	int res = -1;
+
+#if GNUTLS_VERSION_NUMBER >= 0x030300
+	if (full)
+		res = gnutls_x509_crt_check_hostname2(
+			cert, match,
+			GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS);
+#endif /* >= 3.3.0 */
+	if (res == -1)
+		res = gnutls_x509_crt_check_hostname(cert, match);
+
+	wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s --> res=%d",
+		   full ? "": "suffix ", match, res);
+	return res;
+}
+
+
+static int tls_match_suffix(gnutls_x509_crt_t cert, const char *match,
+			    int full)
+{
+	char *values, *token, *context = NULL;
+	int ret = 0;
+
+	if (!os_strchr(match, ';'))
+		return tls_match_suffix_helper(cert, match, full);
+
+	values = os_strdup(match);
+	if (!values)
+		return 0;
+
+	/* Process each match alternative separately until a match is found */
+	while ((token = str_token(values, ";", &context))) {
+		if (tls_match_suffix_helper(cert, token, full)) {
+			ret = 1;
+			break;
+		}
+	}
+
+	os_free(values);
+	return ret;
+}
+
+
+static int tls_connection_verify_peer(gnutls_session_t session)
+{
+	struct tls_connection *conn;
+	unsigned int status, num_certs, i;
+	struct os_time now;
+	const gnutls_datum_t *certs;
+	gnutls_x509_crt_t cert;
+	gnutls_alert_description_t err;
+	int res;
+
+	conn = gnutls_session_get_ptr(session);
+	if (!conn->verify_peer) {
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: No peer certificate verification enabled");
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "GnuTSL: Verifying peer certificate");
+
+#if GNUTLS_VERSION_NUMBER >= 0x030300
+	{
+		gnutls_typed_vdata_st data[1];
+		unsigned int elements = 0;
+
+		os_memset(data, 0, sizeof(data));
+		if (!conn->global->server) {
+			data[elements].type = GNUTLS_DT_KEY_PURPOSE_OID;
+			data[elements].data = (void *) GNUTLS_KP_TLS_WWW_SERVER;
+			elements++;
+		}
+		res = gnutls_certificate_verify_peers(session, data, 1,
+						      &status);
+	}
+#else /* < 3.3.0 */
+	res = gnutls_certificate_verify_peers2(session, &status);
+#endif
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "TLS: Failed to verify peer "
+			   "certificate chain");
+		err = GNUTLS_A_INTERNAL_ERROR;
+		goto out;
+	}
+
+#if GNUTLS_VERSION_NUMBER >= 0x030104
+	{
+		gnutls_datum_t info;
+		int ret, type;
+
+		type = gnutls_certificate_type_get(session);
+		ret = gnutls_certificate_verification_status_print(status, type,
+								   &info, 0);
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "GnuTLS: Failed to print verification status");
+			err = GNUTLS_A_INTERNAL_ERROR;
+			goto out;
+		}
+		wpa_printf(MSG_DEBUG, "GnuTLS: %s", info.data);
+		gnutls_free(info.data);
+	}
+#endif /* GnuTLS 3.1.4 or newer */
+
+	certs = gnutls_certificate_get_peers(session, &num_certs);
+	if (certs == NULL || num_certs == 0) {
+		wpa_printf(MSG_INFO, "TLS: No peer certificate chain received");
+		err = GNUTLS_A_UNKNOWN_CA;
+		goto out;
+	}
+
+	if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) {
+		wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted");
+		if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
+			wpa_printf(MSG_INFO, "TLS: Certificate uses insecure "
+				   "algorithm");
+			gnutls_tls_fail_event(conn, NULL, 0, NULL,
+					      "certificate uses insecure algorithm",
+					      TLS_FAIL_BAD_CERTIFICATE);
+			err = GNUTLS_A_INSUFFICIENT_SECURITY;
+			goto out;
+		}
+		if (status & GNUTLS_CERT_NOT_ACTIVATED) {
+			wpa_printf(MSG_INFO, "TLS: Certificate not yet "
+				   "activated");
+			gnutls_tls_fail_event(conn, NULL, 0, NULL,
+					      "certificate not yet valid",
+					      TLS_FAIL_NOT_YET_VALID);
+			err = GNUTLS_A_CERTIFICATE_EXPIRED;
+			goto out;
+		}
+		if (status & GNUTLS_CERT_EXPIRED) {
+			wpa_printf(MSG_INFO, "TLS: Certificate expired");
+			gnutls_tls_fail_event(conn, NULL, 0, NULL,
+					      "certificate has expired",
+					      TLS_FAIL_EXPIRED);
+			err = GNUTLS_A_CERTIFICATE_EXPIRED;
+			goto out;
+		}
+		gnutls_tls_fail_event(conn, NULL, 0, NULL,
+				      "untrusted certificate",
+				      TLS_FAIL_UNTRUSTED);
+		err = GNUTLS_A_INTERNAL_ERROR;
+		goto out;
+	}
+
+	if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
+		wpa_printf(MSG_INFO, "TLS: Peer certificate does not have a "
+			   "known issuer");
+		gnutls_tls_fail_event(conn, NULL, 0, NULL, "signed not found",
+				      TLS_FAIL_UNTRUSTED);
+		err = GNUTLS_A_UNKNOWN_CA;
+		goto out;
+	}
+
+	if (status & GNUTLS_CERT_REVOKED) {
+		wpa_printf(MSG_INFO, "TLS: Peer certificate has been revoked");
+		gnutls_tls_fail_event(conn, NULL, 0, NULL,
+				      "certificate revoked",
+				      TLS_FAIL_REVOKED);
+		err = GNUTLS_A_CERTIFICATE_REVOKED;
+		goto out;
+	}
+
+	if (status != 0) {
+		wpa_printf(MSG_INFO, "TLS: Unknown verification status: %d",
+			   status);
+		err = GNUTLS_A_INTERNAL_ERROR;
+		goto out;
+	}
+
+	if (check_ocsp(conn, session, &err))
+		goto out;
+
+	os_get_time(&now);
+
+	for (i = 0; i < num_certs; i++) {
+		char *buf;
+		size_t len;
+		if (gnutls_x509_crt_init(&cert) < 0) {
+			wpa_printf(MSG_INFO, "TLS: Certificate initialization "
+				   "failed");
+			err = GNUTLS_A_BAD_CERTIFICATE;
+			goto out;
+		}
+
+		if (gnutls_x509_crt_import(cert, &certs[i],
+					   GNUTLS_X509_FMT_DER) < 0) {
+			wpa_printf(MSG_INFO, "TLS: Could not parse peer "
+				   "certificate %d/%d", i + 1, num_certs);
+			gnutls_x509_crt_deinit(cert);
+			err = GNUTLS_A_BAD_CERTIFICATE;
+			goto out;
+		}
+
+		gnutls_x509_crt_get_dn(cert, NULL, &len);
+		len++;
+		buf = os_malloc(len + 1);
+		if (buf) {
+			buf[0] = buf[len] = '\0';
+			gnutls_x509_crt_get_dn(cert, buf, &len);
+		}
+		wpa_printf(MSG_DEBUG, "TLS: Peer cert chain %d/%d: %s",
+			   i + 1, num_certs, buf);
+
+		if (conn->global->event_cb) {
+			struct wpabuf *cert_buf = NULL;
+			union tls_event_data ev;
+#ifdef CONFIG_SHA256
+			u8 hash[32];
+			const u8 *_addr[1];
+			size_t _len[1];
+#endif /* CONFIG_SHA256 */
+
+			os_memset(&ev, 0, sizeof(ev));
+			if (conn->global->cert_in_cb) {
+				cert_buf = wpabuf_alloc_copy(certs[i].data,
+							     certs[i].size);
+				ev.peer_cert.cert = cert_buf;
+			}
+#ifdef CONFIG_SHA256
+			_addr[0] = certs[i].data;
+			_len[0] = certs[i].size;
+			if (sha256_vector(1, _addr, _len, hash) == 0) {
+				ev.peer_cert.hash = hash;
+				ev.peer_cert.hash_len = sizeof(hash);
+			}
+#endif /* CONFIG_SHA256 */
+			ev.peer_cert.depth = i;
+			ev.peer_cert.subject = buf;
+			conn->global->event_cb(conn->global->cb_ctx,
+					       TLS_PEER_CERTIFICATE, &ev);
+			wpabuf_free(cert_buf);
+		}
+
+		if (i == 0) {
+			if (conn->suffix_match &&
+			    !tls_match_suffix(cert, conn->suffix_match, 0)) {
+				wpa_printf(MSG_WARNING,
+					   "TLS: Domain suffix match '%s' not found",
+					   conn->suffix_match);
+				gnutls_tls_fail_event(
+					conn, &certs[i], i, buf,
+					"Domain suffix mismatch",
+					TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
+				err = GNUTLS_A_BAD_CERTIFICATE;
+				gnutls_x509_crt_deinit(cert);
+				os_free(buf);
+				goto out;
+			}
+
+#if GNUTLS_VERSION_NUMBER >= 0x030300
+			if (conn->domain_match &&
+			    !tls_match_suffix(cert, conn->domain_match, 1)) {
+				wpa_printf(MSG_WARNING,
+					   "TLS: Domain match '%s' not found",
+					   conn->domain_match);
+				gnutls_tls_fail_event(
+					conn, &certs[i], i, buf,
+					"Domain mismatch",
+					TLS_FAIL_DOMAIN_MISMATCH);
+				err = GNUTLS_A_BAD_CERTIFICATE;
+				gnutls_x509_crt_deinit(cert);
+				os_free(buf);
+				goto out;
+			}
+#endif /* >= 3.3.0 */
+
+			/* TODO: validate altsubject_match.
+			 * For now, any such configuration is rejected in
+			 * tls_connection_set_params() */
+
+#if GNUTLS_VERSION_NUMBER < 0x030300
+			/*
+			 * gnutls_certificate_verify_peers() not available, so
+			 * need to check EKU separately.
+			 */
+			if (!conn->global->server &&
+			    !server_eku_purpose(cert)) {
+				wpa_printf(MSG_WARNING,
+					   "GnuTLS: No server EKU");
+				gnutls_tls_fail_event(
+					conn, &certs[i], i, buf,
+					"No server EKU",
+					TLS_FAIL_BAD_CERTIFICATE);
+				err = GNUTLS_A_BAD_CERTIFICATE;
+				gnutls_x509_crt_deinit(cert);
+				os_free(buf);
+				goto out;
+			}
+#endif /* < 3.3.0 */
+		}
+
+		if (!conn->disable_time_checks &&
+		    (gnutls_x509_crt_get_expiration_time(cert) < now.sec ||
+		     gnutls_x509_crt_get_activation_time(cert) > now.sec)) {
+			wpa_printf(MSG_INFO, "TLS: Peer certificate %d/%d is "
+				   "not valid at this time",
+				   i + 1, num_certs);
+			gnutls_tls_fail_event(
+				conn, &certs[i], i, buf,
+				"Certificate is not valid at this time",
+				TLS_FAIL_EXPIRED);
+			gnutls_x509_crt_deinit(cert);
+			os_free(buf);
+			err = GNUTLS_A_CERTIFICATE_EXPIRED;
+			goto out;
+		}
+
+		os_free(buf);
+
+		gnutls_x509_crt_deinit(cert);
+	}
+
+	if (conn->global->event_cb != NULL)
+		conn->global->event_cb(conn->global->cb_ctx,
+				       TLS_CERT_CHAIN_SUCCESS, NULL);
+
+	return 0;
+
+out:
+	conn->failed++;
+	gnutls_alert_send(session, GNUTLS_AL_FATAL, err);
+	return GNUTLS_E_CERTIFICATE_ERROR;
+}
+
+
+static struct wpabuf * gnutls_get_appl_data(struct tls_connection *conn)
+{
+	int res;
+	struct wpabuf *ad;
+	wpa_printf(MSG_DEBUG, "GnuTLS: Check for possible Application Data");
+	ad = wpabuf_alloc((wpabuf_len(conn->pull_buf) + 500) * 3);
+	if (ad == NULL)
+		return NULL;
+
+	res = gnutls_record_recv(conn->session, wpabuf_mhead(ad),
+				 wpabuf_size(ad));
+	wpa_printf(MSG_DEBUG, "GnuTLS: gnutls_record_recv: %d", res);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d "
+			   "(%s)", __func__, (int) res,
+			   gnutls_strerror(res));
+		wpabuf_free(ad);
+		return NULL;
+	}
+
+	wpabuf_put(ad, res);
+	wpa_printf(MSG_DEBUG, "GnuTLS: Received %d bytes of Application Data",
+		   res);
+	return ad;
+}
+
+
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+					 struct tls_connection *conn,
+					 const struct wpabuf *in_data,
+					 struct wpabuf **appl_data)
+{
+	struct tls_global *global = tls_ctx;
+	struct wpabuf *out_data;
+	int ret;
+
+	if (appl_data)
+		*appl_data = NULL;
+
+	if (in_data && wpabuf_len(in_data) > 0) {
+		if (conn->pull_buf) {
+			wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
+				   "pull_buf", __func__,
+				   (unsigned long) wpabuf_len(conn->pull_buf));
+			wpabuf_free(conn->pull_buf);
+		}
+		conn->pull_buf = wpabuf_dup(in_data);
+		if (conn->pull_buf == NULL)
+			return NULL;
+		conn->pull_buf_offset = wpabuf_head(conn->pull_buf);
+	}
+
+	ret = gnutls_handshake(conn->session);
+	if (ret < 0) {
+		gnutls_alert_description_t alert;
+		union tls_event_data ev;
+
+		switch (ret) {
+		case GNUTLS_E_AGAIN:
+			if (global->server && conn->established &&
+			    conn->push_buf == NULL) {
+				/* Need to return something to trigger
+				 * completion of EAP-TLS. */
+				conn->push_buf = wpabuf_alloc(0);
+			}
+			break;
+		case GNUTLS_E_DH_PRIME_UNACCEPTABLE:
+			wpa_printf(MSG_DEBUG, "GnuTLS: Unacceptable DH prime");
+			if (conn->global->event_cb) {
+				os_memset(&ev, 0, sizeof(ev));
+				ev.alert.is_local = 1;
+				ev.alert.type = "fatal";
+				ev.alert.description = "insufficient security";
+				conn->global->event_cb(conn->global->cb_ctx,
+						       TLS_ALERT, &ev);
+			}
+			/*
+			 * Could send a TLS Alert to the server, but for now,
+			 * simply terminate handshake.
+			 */
+			conn->failed++;
+			conn->write_alerts++;
+			break;
+		case GNUTLS_E_FATAL_ALERT_RECEIVED:
+			alert = gnutls_alert_get(conn->session);
+			wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert",
+				   __func__, gnutls_alert_get_name(alert));
+			conn->read_alerts++;
+			if (conn->global->event_cb != NULL) {
+				os_memset(&ev, 0, sizeof(ev));
+				ev.alert.is_local = 0;
+				ev.alert.type = gnutls_alert_get_name(alert);
+				ev.alert.description = ev.alert.type;
+				conn->global->event_cb(conn->global->cb_ctx,
+						       TLS_ALERT, &ev);
+			}
+			/* continue */
+		default:
+			wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed "
+				   "-> %s", __func__, gnutls_strerror(ret));
+			conn->failed++;
+		}
+	} else {
+		size_t size;
+
+		wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully");
+
+#if GNUTLS_VERSION_NUMBER >= 0x03010a
+		{
+			char *desc;
+
+			desc = gnutls_session_get_desc(conn->session);
+			if (desc) {
+				wpa_printf(MSG_DEBUG, "GnuTLS: %s", desc);
+				gnutls_free(desc);
+			}
+		}
+#endif /* GnuTLS 3.1.10 or newer */
+
+		conn->established = 1;
+		if (conn->push_buf == NULL) {
+			/* Need to return something to get final TLS ACK. */
+			conn->push_buf = wpabuf_alloc(0);
+		}
+
+		gnutls_session_get_data(conn->session, NULL, &size);
+		if (global->session_data == NULL ||
+		    global->session_data_size < size) {
+			os_free(global->session_data);
+			global->session_data = os_malloc(size);
+		}
+		if (global->session_data) {
+			global->session_data_size = size;
+			gnutls_session_get_data(conn->session,
+						global->session_data,
+						&global->session_data_size);
+		}
+
+		if (conn->pull_buf && appl_data)
+			*appl_data = gnutls_get_appl_data(conn);
+	}
+
+	out_data = conn->push_buf;
+	conn->push_buf = NULL;
+	return out_data;
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+						struct tls_connection *conn,
+						const struct wpabuf *in_data,
+						struct wpabuf **appl_data)
+{
+	return tls_connection_handshake(tls_ctx, conn, in_data, appl_data);
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data)
+{
+	ssize_t res;
+	struct wpabuf *buf;
+
+	res = gnutls_record_send(conn->session, wpabuf_head(in_data),
+				 wpabuf_len(in_data));
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "%s: Encryption failed: %s",
+			   __func__, gnutls_strerror(res));
+		return NULL;
+	}
+
+	buf = conn->push_buf;
+	conn->push_buf = NULL;
+	return buf;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data)
+{
+	ssize_t res;
+	struct wpabuf *out;
+
+	if (conn->pull_buf) {
+		wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
+			   "pull_buf", __func__,
+			   (unsigned long) wpabuf_len(conn->pull_buf));
+		wpabuf_free(conn->pull_buf);
+	}
+	conn->pull_buf = wpabuf_dup(in_data);
+	if (conn->pull_buf == NULL)
+		return NULL;
+	conn->pull_buf_offset = wpabuf_head(conn->pull_buf);
+
+	/*
+	 * Even though we try to disable TLS compression, it is possible that
+	 * this cannot be done with all TLS libraries. Add extra buffer space
+	 * to handle the possibility of the decrypted data being longer than
+	 * input data.
+	 */
+	out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
+	if (out == NULL)
+		return NULL;
+
+	res = gnutls_record_recv(conn->session, wpabuf_mhead(out),
+				 wpabuf_size(out));
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d "
+			   "(%s)", __func__, (int) res, gnutls_strerror(res));
+		wpabuf_free(out);
+		return NULL;
+	}
+	wpabuf_put(out, res);
+
+	return out;
+}
+
+
+int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn)
+{
+	if (conn == NULL)
+		return 0;
+	return gnutls_session_is_resumed(conn->session);
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+				   u8 *ciphers)
+{
+	/* TODO */
+	return -1;
+}
+
+
+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
+		    char *buf, size_t buflen)
+{
+	gnutls_protocol_t ver;
+
+	ver = gnutls_protocol_get_version(conn->session);
+	if (ver == GNUTLS_TLS1_0)
+		os_strlcpy(buf, "TLSv1", buflen);
+	else if (ver == GNUTLS_TLS1_1)
+		os_strlcpy(buf, "TLSv1.1", buflen);
+	else if (ver == GNUTLS_TLS1_2)
+		os_strlcpy(buf, "TLSv1.2", buflen);
+	else
+		return -1;
+	return 0;
+}
+
+
+int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
+		   char *buf, size_t buflen)
+{
+	gnutls_cipher_algorithm_t cipher;
+	gnutls_kx_algorithm_t kx;
+	gnutls_mac_algorithm_t mac;
+	const char *kx_str, *cipher_str, *mac_str;
+	int res;
+
+	cipher = gnutls_cipher_get(conn->session);
+	cipher_str = gnutls_cipher_get_name(cipher);
+	if (!cipher_str)
+		cipher_str = "";
+
+	kx = gnutls_kx_get(conn->session);
+	kx_str = gnutls_kx_get_name(kx);
+	if (!kx_str)
+		kx_str = "";
+
+	mac = gnutls_mac_get(conn->session);
+	mac_str = gnutls_mac_get_name(mac);
+	if (!mac_str)
+		mac_str = "";
+
+	if (kx == GNUTLS_KX_RSA)
+		res = os_snprintf(buf, buflen, "%s-%s", cipher_str, mac_str);
+	else
+		res = os_snprintf(buf, buflen, "%s-%s-%s",
+				  kx_str, cipher_str, mac_str);
+	if (os_snprintf_error(buflen, res))
+		return -1;
+
+	return 0;
+}
+
+
+int tls_connection_enable_workaround(void *ssl_ctx,
+				     struct tls_connection *conn)
+{
+	gnutls_record_disable_padding(conn->session);
+	return 0;
+}
+
+
+int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
+				    int ext_type, const u8 *data,
+				    size_t data_len)
+{
+	/* TODO */
+	return -1;
+}
+
+
+int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn)
+{
+	if (conn == NULL)
+		return -1;
+	return conn->failed;
+}
+
+
+int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn)
+{
+	if (conn == NULL)
+		return -1;
+	return conn->read_alerts;
+}
+
+
+int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn)
+{
+	if (conn == NULL)
+		return -1;
+	return conn->write_alerts;
+}
+
+
+int tls_connection_set_session_ticket_cb(void *tls_ctx,
+					 struct tls_connection *conn,
+					 tls_session_ticket_cb cb, void *ctx)
+{
+	return -1;
+}
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+	return os_snprintf(buf, buf_len, "GnuTLS build=%s run=%s",
+			   GNUTLS_VERSION, gnutls_check_version(NULL));
+}
+
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+				     struct wpabuf *data)
+{
+	wpabuf_free(data);
+}
+
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
+{
+}
+
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn)
+{
+	return NULL;
+}
+
+
+void tls_connection_remove_session(struct tls_connection *conn)
+{
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls_internal.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls_internal.c
new file mode 100644
index 0000000..f3e05ce
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls_internal.c
@@ -0,0 +1,804 @@
+/*
+ * TLS interface functions and an internal TLS implementation
+ * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file interface functions for hostapd/wpa_supplicant to use the
+ * integrated TLSv1 implementation.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "tls.h"
+#include "tls/tlsv1_client.h"
+#include "tls/tlsv1_server.h"
+
+
+static int tls_ref_count = 0;
+
+struct tls_global {
+	int server;
+	struct tlsv1_credentials *server_cred;
+	int check_crl;
+
+	void (*event_cb)(void *ctx, enum tls_event ev,
+			 union tls_event_data *data);
+	void *cb_ctx;
+	int cert_in_cb;
+};
+
+struct tls_connection {
+	struct tlsv1_client *client;
+	struct tlsv1_server *server;
+	struct tls_global *global;
+};
+
+
+void * tls_init(const struct tls_config *conf)
+{
+	struct tls_global *global;
+
+	if (tls_ref_count == 0) {
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+		if (tlsv1_client_global_init())
+			return NULL;
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+		if (tlsv1_server_global_init())
+			return NULL;
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	}
+	tls_ref_count++;
+
+	global = os_zalloc(sizeof(*global));
+	if (global == NULL)
+		return NULL;
+	if (conf) {
+		global->event_cb = conf->event_cb;
+		global->cb_ctx = conf->cb_ctx;
+		global->cert_in_cb = conf->cert_in_cb;
+	}
+
+	return global;
+}
+
+void tls_deinit(void *ssl_ctx)
+{
+	struct tls_global *global = ssl_ctx;
+	tls_ref_count--;
+	if (tls_ref_count == 0) {
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+		tlsv1_client_global_deinit();
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+		tlsv1_server_global_deinit();
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	}
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	tlsv1_cred_free(global->server_cred);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	os_free(global);
+}
+
+
+int tls_get_errors(void *tls_ctx)
+{
+	return 0;
+}
+
+
+struct tls_connection * tls_connection_init(void *tls_ctx)
+{
+	struct tls_connection *conn;
+	struct tls_global *global = tls_ctx;
+
+	conn = os_zalloc(sizeof(*conn));
+	if (conn == NULL)
+		return NULL;
+	conn->global = global;
+
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (!global->server) {
+		conn->client = tlsv1_client_init();
+		if (conn->client == NULL) {
+			os_free(conn);
+			return NULL;
+		}
+		tlsv1_client_set_cb(conn->client, global->event_cb,
+				    global->cb_ctx, global->cert_in_cb);
+	}
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (global->server) {
+		conn->server = tlsv1_server_init(global->server_cred);
+		if (conn->server == NULL) {
+			os_free(conn);
+			return NULL;
+		}
+	}
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+
+	return conn;
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+void tls_connection_set_test_flags(struct tls_connection *conn, u32 flags)
+{
+	if (conn->server)
+		tlsv1_server_set_test_flags(conn->server, flags);
+}
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+void tls_connection_set_log_cb(struct tls_connection *conn,
+			       void (*log_cb)(void *ctx, const char *msg),
+			       void *ctx)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server)
+		tlsv1_server_set_log_cb(conn->server, log_cb, ctx);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+}
+
+
+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
+{
+	if (conn == NULL)
+		return;
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client)
+		tlsv1_client_deinit(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server)
+		tlsv1_server_deinit(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	os_free(conn);
+}
+
+
+int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client)
+		return tlsv1_client_established(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server)
+		return tlsv1_server_established(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return 0;
+}
+
+
+char * tls_connection_peer_serial_num(void *tls_ctx,
+				      struct tls_connection *conn)
+{
+	/* TODO */
+	return NULL;
+}
+
+
+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client)
+		return tlsv1_client_shutdown(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server)
+		return tlsv1_server_shutdown(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return -1;
+}
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+			      const struct tls_connection_params *params)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	struct tlsv1_credentials *cred;
+
+	if (conn->client == NULL)
+		return -1;
+
+	if (params->flags & TLS_CONN_EXT_CERT_CHECK) {
+		wpa_printf(MSG_INFO,
+			   "TLS: tls_ext_cert_check=1 not supported");
+		return -1;
+	}
+
+	cred = tlsv1_cred_alloc();
+	if (cred == NULL)
+		return -1;
+
+	if (params->subject_match) {
+		wpa_printf(MSG_INFO, "TLS: subject_match not supported");
+		tlsv1_cred_free(cred);
+		return -1;
+	}
+
+	if (params->altsubject_match) {
+		wpa_printf(MSG_INFO, "TLS: altsubject_match not supported");
+		tlsv1_cred_free(cred);
+		return -1;
+	}
+
+	if (params->suffix_match) {
+		wpa_printf(MSG_INFO, "TLS: suffix_match not supported");
+		tlsv1_cred_free(cred);
+		return -1;
+	}
+
+	if (params->domain_match) {
+		wpa_printf(MSG_INFO, "TLS: domain_match not supported");
+		tlsv1_cred_free(cred);
+		return -1;
+	}
+
+	if (params->openssl_ciphers) {
+		wpa_printf(MSG_INFO, "TLS: openssl_ciphers not supported");
+		tlsv1_cred_free(cred);
+		return -1;
+	}
+
+	if (params->openssl_ecdh_curves) {
+		wpa_printf(MSG_INFO, "TLS: openssl_ecdh_curves not supported");
+		tlsv1_cred_free(cred);
+		return -1;
+	}
+
+	if (tlsv1_set_ca_cert(cred, params->ca_cert,
+			      params->ca_cert_blob, params->ca_cert_blob_len,
+			      params->ca_path)) {
+		wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA "
+			   "certificates");
+		tlsv1_cred_free(cred);
+		return -1;
+	}
+
+	if (tlsv1_set_cert(cred, params->client_cert,
+			   params->client_cert_blob,
+			   params->client_cert_blob_len)) {
+		wpa_printf(MSG_INFO, "TLS: Failed to configure client "
+			   "certificate");
+		tlsv1_cred_free(cred);
+		return -1;
+	}
+
+	if (tlsv1_set_private_key(cred, params->private_key,
+				  params->private_key_passwd,
+				  params->private_key_blob,
+				  params->private_key_blob_len)) {
+		wpa_printf(MSG_INFO, "TLS: Failed to load private key");
+		tlsv1_cred_free(cred);
+		return -1;
+	}
+
+	if (tlsv1_client_set_cred(conn->client, cred) < 0) {
+		tlsv1_cred_free(cred);
+		return -1;
+	}
+
+	tlsv1_client_set_flags(conn->client, params->flags);
+
+	return 0;
+#else /* CONFIG_TLS_INTERNAL_CLIENT */
+	return -1;
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+}
+
+
+int tls_global_set_params(void *tls_ctx,
+			  const struct tls_connection_params *params)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	struct tls_global *global = tls_ctx;
+	struct tlsv1_credentials *cred;
+
+	if (params->check_cert_subject)
+		return -1; /* not yet supported */
+
+	/* Currently, global parameters are only set when running in server
+	 * mode. */
+	global->server = 1;
+	tlsv1_cred_free(global->server_cred);
+	global->server_cred = cred = tlsv1_cred_alloc();
+	if (cred == NULL)
+		return -1;
+
+	if (tlsv1_set_ca_cert(cred, params->ca_cert, params->ca_cert_blob,
+			      params->ca_cert_blob_len, params->ca_path)) {
+		wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA "
+			   "certificates");
+		return -1;
+	}
+
+	if (tlsv1_set_cert(cred, params->client_cert, params->client_cert_blob,
+			   params->client_cert_blob_len)) {
+		wpa_printf(MSG_INFO, "TLS: Failed to configure server "
+			   "certificate");
+		return -1;
+	}
+
+	if (tlsv1_set_private_key(cred, params->private_key,
+				  params->private_key_passwd,
+				  params->private_key_blob,
+				  params->private_key_blob_len)) {
+		wpa_printf(MSG_INFO, "TLS: Failed to load private key");
+		return -1;
+	}
+
+	if (tlsv1_set_dhparams(cred, params->dh_file, NULL, 0)) {
+		wpa_printf(MSG_INFO, "TLS: Failed to load DH parameters");
+		return -1;
+	}
+
+	if (params->ocsp_stapling_response)
+		cred->ocsp_stapling_response =
+			os_strdup(params->ocsp_stapling_response);
+	if (params->ocsp_stapling_response_multi)
+		cred->ocsp_stapling_response_multi =
+			os_strdup(params->ocsp_stapling_response_multi);
+
+	return 0;
+#else /* CONFIG_TLS_INTERNAL_SERVER */
+	return -1;
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+}
+
+
+int tls_global_set_verify(void *tls_ctx, int check_crl, int strict)
+{
+	struct tls_global *global = tls_ctx;
+	global->check_crl = check_crl;
+	return 0;
+}
+
+
+int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
+			      int verify_peer, unsigned int flags,
+			      const u8 *session_ctx, size_t session_ctx_len)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server)
+		return tlsv1_server_set_verify(conn->server, verify_peer);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return -1;
+}
+
+
+int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
+			      struct tls_random *data)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client)
+		return tlsv1_client_get_random(conn->client, data);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server)
+		return tlsv1_server_get_random(conn->server, data);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return -1;
+}
+
+
+static int tls_get_keyblock_size(struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client)
+		return tlsv1_client_get_keyblock_size(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server)
+		return tlsv1_server_get_keyblock_size(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return -1;
+}
+
+
+static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+			      const char *label, const u8 *context,
+			      size_t context_len, int server_random_first,
+			      int skip_keyblock, u8 *out, size_t out_len)
+{
+	int ret = -1, skip = 0;
+	u8 *tmp_out = NULL;
+	u8 *_out = out;
+
+	if (skip_keyblock) {
+		skip = tls_get_keyblock_size(conn);
+		if (skip < 0)
+			return -1;
+		tmp_out = os_malloc(skip + out_len);
+		if (!tmp_out)
+			return -1;
+		_out = tmp_out;
+	}
+
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client) {
+		ret = tlsv1_client_prf(conn->client, label, context,
+				       context_len, server_random_first,
+				       _out, skip + out_len);
+	}
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server) {
+		ret = tlsv1_server_prf(conn->server, label, context,
+				       context_len, server_random_first,
+				       _out, skip + out_len);
+	}
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	if (ret == 0 && skip_keyblock)
+		os_memcpy(out, _out + skip, out_len);
+	bin_clear_free(tmp_out, skip);
+
+	return ret;
+}
+
+
+int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
+			      const char *label, const u8 *context,
+			      size_t context_len, u8 *out, size_t out_len)
+{
+	return tls_connection_prf(tls_ctx, conn, label, context, context_len,
+				  0, 0, out, out_len);
+}
+
+
+int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
+				    u8 *out, size_t out_len)
+{
+	return tls_connection_prf(tls_ctx, conn, "key expansion", NULL, 0,
+				  1, 1, out, out_len);
+}
+
+
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+					 struct tls_connection *conn,
+					 const struct wpabuf *in_data,
+					 struct wpabuf **appl_data)
+{
+	return tls_connection_handshake2(tls_ctx, conn, in_data, appl_data,
+					 NULL);
+}
+
+
+struct wpabuf * tls_connection_handshake2(void *tls_ctx,
+					  struct tls_connection *conn,
+					  const struct wpabuf *in_data,
+					  struct wpabuf **appl_data,
+					  int *need_more_data)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	u8 *res, *ad;
+	size_t res_len, ad_len;
+	struct wpabuf *out;
+
+	if (conn->client == NULL)
+		return NULL;
+
+	ad = NULL;
+	res = tlsv1_client_handshake(conn->client,
+				     in_data ? wpabuf_head(in_data) : NULL,
+				     in_data ? wpabuf_len(in_data) : 0,
+				     &res_len, &ad, &ad_len, need_more_data);
+	if (res == NULL)
+		return NULL;
+	out = wpabuf_alloc_ext_data(res, res_len);
+	if (out == NULL) {
+		os_free(res);
+		os_free(ad);
+		return NULL;
+	}
+	if (appl_data) {
+		if (ad) {
+			*appl_data = wpabuf_alloc_ext_data(ad, ad_len);
+			if (*appl_data == NULL)
+				os_free(ad);
+		} else
+			*appl_data = NULL;
+	} else
+		os_free(ad);
+
+	return out;
+#else /* CONFIG_TLS_INTERNAL_CLIENT */
+	return NULL;
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+						struct tls_connection *conn,
+						const struct wpabuf *in_data,
+						struct wpabuf **appl_data)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	u8 *res;
+	size_t res_len;
+	struct wpabuf *out;
+
+	if (conn->server == NULL)
+		return NULL;
+
+	if (appl_data)
+		*appl_data = NULL;
+
+	res = tlsv1_server_handshake(conn->server, wpabuf_head(in_data),
+				     wpabuf_len(in_data), &res_len);
+	if (res == NULL && tlsv1_server_established(conn->server))
+		return wpabuf_alloc(0);
+	if (res == NULL)
+		return NULL;
+	out = wpabuf_alloc_ext_data(res, res_len);
+	if (out == NULL) {
+		os_free(res);
+		return NULL;
+	}
+
+	return out;
+#else /* CONFIG_TLS_INTERNAL_SERVER */
+	return NULL;
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client) {
+		struct wpabuf *buf;
+		int res;
+		buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
+		if (buf == NULL)
+			return NULL;
+		res = tlsv1_client_encrypt(conn->client, wpabuf_head(in_data),
+					   wpabuf_len(in_data),
+					   wpabuf_mhead(buf),
+					   wpabuf_size(buf));
+		if (res < 0) {
+			wpabuf_free(buf);
+			return NULL;
+		}
+		wpabuf_put(buf, res);
+		return buf;
+	}
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server) {
+		struct wpabuf *buf;
+		int res;
+		buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
+		if (buf == NULL)
+			return NULL;
+		res = tlsv1_server_encrypt(conn->server, wpabuf_head(in_data),
+					   wpabuf_len(in_data),
+					   wpabuf_mhead(buf),
+					   wpabuf_size(buf));
+		if (res < 0) {
+			wpabuf_free(buf);
+			return NULL;
+		}
+		wpabuf_put(buf, res);
+		return buf;
+	}
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return NULL;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data)
+{
+	return tls_connection_decrypt2(tls_ctx, conn, in_data, NULL);
+}
+
+
+struct wpabuf * tls_connection_decrypt2(void *tls_ctx,
+					struct tls_connection *conn,
+					const struct wpabuf *in_data,
+					int *need_more_data)
+{
+	if (need_more_data)
+		*need_more_data = 0;
+
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client) {
+		return tlsv1_client_decrypt(conn->client, wpabuf_head(in_data),
+					    wpabuf_len(in_data),
+					    need_more_data);
+	}
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server) {
+		struct wpabuf *buf;
+		int res;
+		buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
+		if (buf == NULL)
+			return NULL;
+		res = tlsv1_server_decrypt(conn->server, wpabuf_head(in_data),
+					   wpabuf_len(in_data),
+					   wpabuf_mhead(buf),
+					   wpabuf_size(buf));
+		if (res < 0) {
+			wpabuf_free(buf);
+			return NULL;
+		}
+		wpabuf_put(buf, res);
+		return buf;
+	}
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return NULL;
+}
+
+
+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client)
+		return tlsv1_client_resumed(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server)
+		return tlsv1_server_resumed(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return -1;
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+				   u8 *ciphers)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client)
+		return tlsv1_client_set_cipher_list(conn->client, ciphers);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server)
+		return tlsv1_server_set_cipher_list(conn->server, ciphers);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return -1;
+}
+
+
+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
+		    char *buf, size_t buflen)
+{
+	if (conn == NULL)
+		return -1;
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client)
+		return tlsv1_client_get_version(conn->client, buf, buflen);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+	return -1;
+}
+
+
+int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
+		   char *buf, size_t buflen)
+{
+	if (conn == NULL)
+		return -1;
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client)
+		return tlsv1_client_get_cipher(conn->client, buf, buflen);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server)
+		return tlsv1_server_get_cipher(conn->server, buf, buflen);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return -1;
+}
+
+
+int tls_connection_enable_workaround(void *tls_ctx,
+				     struct tls_connection *conn)
+{
+	return -1;
+}
+
+
+int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
+				    int ext_type, const u8 *data,
+				    size_t data_len)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client) {
+		return tlsv1_client_hello_ext(conn->client, ext_type,
+					      data, data_len);
+	}
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+	return -1;
+}
+
+
+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server)
+		return tlsv1_server_get_failed(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return 0;
+}
+
+
+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server)
+		return tlsv1_server_get_read_alerts(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return 0;
+}
+
+
+int tls_connection_get_write_alerts(void *tls_ctx,
+				    struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server)
+		return tlsv1_server_get_write_alerts(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return 0;
+}
+
+
+int tls_connection_set_session_ticket_cb(void *tls_ctx,
+					 struct tls_connection *conn,
+					 tls_session_ticket_cb cb,
+					 void *ctx)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client) {
+		tlsv1_client_set_session_ticket_cb(conn->client, cb, ctx);
+		return 0;
+	}
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server) {
+		tlsv1_server_set_session_ticket_cb(conn->server, cb, ctx);
+		return 0;
+	}
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return -1;
+}
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+	return os_snprintf(buf, buf_len, "internal");
+}
+
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+				     struct wpabuf *data)
+{
+	wpabuf_free(data);
+}
+
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
+{
+}
+
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn)
+{
+	return NULL;
+}
+
+
+void tls_connection_remove_session(struct tls_connection *conn)
+{
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls_mbedtls.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls_mbedtls.c
new file mode 100644
index 0000000..d83a3db
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls_mbedtls.c
@@ -0,0 +1,3313 @@
+/*
+ * SSL/TLS interface functions for mbed TLS
+ *
+ * SPDX-FileCopyrightText: 2022 Glenn Strauss <gstrauss@gluelogic.com>
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * template:  src/crypto/tls_none.c
+ * reference: src/crypto/tls_*.c
+ *
+ * Known Limitations:
+ * - no TLSv1.3 (not available in mbedtls 2.x; experimental in mbedtls 3.x)
+ * - no OCSP (not yet available in mbedtls)
+ * - mbedtls does not support all certificate encodings used by hwsim tests
+ *   PCKS#5 v1.5
+ *   PCKS#12
+ *   DH DSA
+ * - EAP-FAST, EAP-TEAP session ticket support not implemented in tls_mbedtls.c
+ * - mbedtls does not currently provide way to set an attribute in a CSR
+ *     https://github.com/Mbed-TLS/mbedtls/issues/4886
+ *   so tests/hwsim dpp_enterprise tests fail
+ * - DPP2 not supported
+ *   PKCS#7 parsing is not supported in mbedtls
+ *   See crypto_mbedtls.c:crypto_pkcs7_get_certificates() comments
+ * - DPP3 not supported
+ *   hpke_base_seal() and hpke_base_seal() not implemented in crypto_mbedtls.c
+ *
+ * Status:
+ * - code written to be compatible with mbedtls 2.x and mbedtls 3.x
+ *   (currently requires mbedtls >= 2.27.0 for mbedtls_mpi_random())
+ *   (currently requires mbedtls >= 2.18.0 for mbedtls_ssl_tls_prf())
+ * - builds with tests/build/build-wpa_supplicant-mbedtls.config
+ * - passes all tests/ crypto module tests (incomplete coverage)
+ *   ($ cd tests; make clean; make -j 4 run-tests CONFIG_TLS=mbedtls)
+ * - passes almost all tests/hwsim tests
+ *   (hwsim tests skipped for missing features)
+ *
+ * RFE:
+ * - EAP-FAST, EAP-TEAP session ticket support not implemented in tls_mbedtls.c
+ * - client/server session resumption, and/or save client session ticket
+ */
+
+#include "includes.h"
+#include "common.h"
+
+#include <mbedtls/version.h>
+#include <mbedtls/ctr_drbg.h>
+#include <mbedtls/error.h>
+#include <mbedtls/oid.h>
+#include <mbedtls/pem.h>
+#include <mbedtls/platform.h> /* mbedtls_calloc() mbedtls_free() */
+#include <mbedtls/platform_util.h> /* mbedtls_platform_zeroize() */
+#include <mbedtls/ssl.h>
+#include <mbedtls/ssl_ticket.h>
+#include <mbedtls/x509.h>
+#include <mbedtls/x509_crt.h>
+
+#if MBEDTLS_VERSION_NUMBER >= 0x02040000 /* mbedtls 2.4.0 */
+#include <mbedtls/net_sockets.h>
+#else
+#include <mbedtls/net.h>
+#endif
+
+#ifndef MBEDTLS_PRIVATE
+#define MBEDTLS_PRIVATE(x) x
+#endif
+
+#if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
+#define mbedtls_ssl_get_ciphersuite_id_from_ssl(ssl) \
+        ((ssl)->MBEDTLS_PRIVATE(session) \
+        ?(ssl)->MBEDTLS_PRIVATE(session)->MBEDTLS_PRIVATE(ciphersuite) \
+        : 0)
+#define mbedtls_ssl_ciphersuite_get_name(info) \
+        (info)->MBEDTLS_PRIVATE(name)
+#endif
+
+#include "crypto.h"     /* sha256_vector() */
+#include "tls.h"
+
+#ifndef SHA256_DIGEST_LENGTH
+#define SHA256_DIGEST_LENGTH 32
+#endif
+
+#ifndef MBEDTLS_EXPKEY_FIXED_SECRET_LEN
+#define MBEDTLS_EXPKEY_FIXED_SECRET_LEN 48
+#endif
+
+#ifndef MBEDTLS_EXPKEY_RAND_LEN
+#define MBEDTLS_EXPKEY_RAND_LEN 32
+#endif
+
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
+static mbedtls_ssl_export_keys_t tls_connection_export_keys_cb;
+#elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
+static mbedtls_ssl_export_keys_ext_t tls_connection_export_keys_cb;
+#else /*(not implemented; return error)*/
+#define mbedtls_ssl_tls_prf(a,b,c,d,e,f,g,h) (-1)
+typedef mbedtls_tls_prf_types int;
+#endif
+
+
+/* hostapd/wpa_supplicant provides forced_memzero(),
+ * but prefer mbedtls_platform_zeroize() */
+#define forced_memzero(ptr,sz) mbedtls_platform_zeroize(ptr,sz)
+
+
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) \
+ || defined(EAP_TEAP) || defined(EAP_SERVER_TEAP)
+#ifdef MBEDTLS_SSL_SESSION_TICKETS
+#ifdef MBEDTLS_SSL_TICKET_C
+#define TLS_MBEDTLS_SESSION_TICKETS
+#if defined(EAP_TEAP) || defined(EAP_SERVER_TEAP)
+#define TLS_MBEDTLS_EAP_TEAP
+#endif
+#if !defined(CONFIG_FIPS) /* EAP-FAST keys cannot be exported in FIPS mode */
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+#define TLS_MBEDTLS_EAP_FAST
+#endif
+#endif
+#endif
+#endif
+#endif
+
+
+struct tls_conf {
+	mbedtls_ssl_config conf;
+
+	unsigned int verify_peer:1;
+	unsigned int verify_depth0_only:1;
+	unsigned int check_crl:2;           /*(needs :2 bits for 0, 1, 2)*/
+	unsigned int check_crl_strict:1;    /*(needs :1 bit  for 0, 1)*/
+	unsigned int ca_cert_probe:1;
+	unsigned int has_ca_cert:1;
+	unsigned int has_client_cert:1;
+	unsigned int has_private_key:1;
+	unsigned int suiteb128:1;
+	unsigned int suiteb192:1;
+	mbedtls_x509_crl *crl;
+	mbedtls_x509_crt ca_cert;
+	mbedtls_x509_crt client_cert;
+	mbedtls_pk_context private_key;
+
+	uint32_t refcnt;
+
+	unsigned int flags;
+	char *subject_match;
+	char *altsubject_match;
+	char *suffix_match;
+	char *domain_match;
+	char *check_cert_subject;
+	u8 ca_cert_hash[SHA256_DIGEST_LENGTH];
+
+	int *ciphersuites;  /* list of ciphersuite ids for mbedtls_ssl_config */
+#if MBEDTLS_VERSION_NUMBER < 0x03010000 /* mbedtls 3.1.0 */
+	mbedtls_ecp_group_id *curves;
+#else
+	uint16_t *curves;   /* list of curve ids for mbedtls_ssl_config */
+#endif
+};
+
+
+struct tls_global {
+	struct tls_conf *tls_conf;
+	char *ocsp_stapling_response;
+	mbedtls_ctr_drbg_context *ctr_drbg; /*(see crypto_mbedtls.c)*/
+  #ifdef MBEDTLS_SSL_SESSION_TICKETS
+	mbedtls_ssl_ticket_context ticket_ctx;
+  #endif
+	char *ca_cert_file;
+	struct os_reltime crl_reload_previous;
+	unsigned int crl_reload_interval;
+	uint32_t refcnt;
+	struct tls_config init_conf;
+};
+
+static struct tls_global tls_ctx_global;
+
+
+struct tls_connection {
+	struct tls_conf *tls_conf;
+	struct wpabuf *push_buf;
+	struct wpabuf *pull_buf;
+	size_t pull_buf_offset;
+
+	unsigned int established:1;
+	unsigned int resumed:1;
+	unsigned int verify_peer:1;
+	unsigned int is_server:1;
+
+	mbedtls_ssl_context ssl;
+
+	mbedtls_tls_prf_types tls_prf_type;
+	size_t expkey_keyblock_size;
+	size_t expkey_secret_len;
+  #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */
+	unsigned char expkey_secret[MBEDTLS_EXPKEY_FIXED_SECRET_LEN];
+  #else
+	unsigned char expkey_secret[MBEDTLS_MD_MAX_SIZE];
+  #endif
+	unsigned char expkey_randbytes[MBEDTLS_EXPKEY_RAND_LEN*2];
+
+	int read_alerts, write_alerts, failed;
+
+  #ifdef TLS_MBEDTLS_SESSION_TICKETS
+	tls_session_ticket_cb session_ticket_cb;
+	void *session_ticket_cb_ctx;
+	unsigned char *clienthello_session_ticket;
+	size_t clienthello_session_ticket_len;
+  #endif
+	char *peer_subject; /* peer subject info for authenticated peer */
+	struct wpabuf *success_data;
+};
+
+
+#ifndef __has_attribute
+#define __has_attribute(x) 0
+#endif
+
+#ifndef __GNUC_PREREQ
+#define __GNUC_PREREQ(maj,min) 0
+#endif
+
+#ifndef __attribute_cold__
+#if __has_attribute(cold) \
+ || __GNUC_PREREQ(4,3)
+#define __attribute_cold__  __attribute__((__cold__))
+#else
+#define __attribute_cold__
+#endif
+#endif
+
+#ifndef __attribute_noinline__
+#if __has_attribute(noinline) \
+ || __GNUC_PREREQ(3,1)
+#define __attribute_noinline__  __attribute__((__noinline__))
+#else
+#define __attribute_noinline__
+#endif
+#endif
+
+
+__attribute_cold__
+__attribute_noinline__
+static void emsg(int level, const char * const msg)
+{
+	wpa_printf(level, "MTLS: %s", msg);
+}
+
+
+__attribute_cold__
+__attribute_noinline__
+static void emsgrc(int level, const char * const msg, int rc)
+{
+  #ifdef MBEDTLS_ERROR_C
+	/* error logging convenience function that decodes mbedtls result codes */
+	char buf[256];
+	mbedtls_strerror(rc, buf, sizeof(buf));
+	wpa_printf(level, "MTLS: %s: %s (-0x%04x)", msg, buf, -rc);
+  #else
+	wpa_printf(level, "MTLS: %s: (-0x%04x)", msg, -rc);
+  #endif
+}
+
+
+#define elog(rc, msg) emsgrc(MSG_ERROR, (msg), (rc))
+#define ilog(rc, msg) emsgrc(MSG_INFO,  (msg), (rc))
+
+
+struct tls_conf * tls_conf_init(void *tls_ctx)
+{
+	struct tls_conf *tls_conf = os_zalloc(sizeof(*tls_conf));
+	if (tls_conf == NULL)
+		return NULL;
+	tls_conf->refcnt = 1;
+
+	mbedtls_ssl_config_init(&tls_conf->conf);
+	mbedtls_ssl_conf_rng(&tls_conf->conf,
+			     mbedtls_ctr_drbg_random, tls_ctx_global.ctr_drbg);
+	mbedtls_x509_crt_init(&tls_conf->ca_cert);
+	mbedtls_x509_crt_init(&tls_conf->client_cert);
+	mbedtls_pk_init(&tls_conf->private_key);
+
+	return tls_conf;
+}
+
+
+void tls_conf_deinit(struct tls_conf *tls_conf)
+{
+	if (tls_conf == NULL || --tls_conf->refcnt != 0)
+		return;
+
+	mbedtls_x509_crt_free(&tls_conf->ca_cert);
+	mbedtls_x509_crt_free(&tls_conf->client_cert);
+	if (tls_conf->crl) {
+		mbedtls_x509_crl_free(tls_conf->crl);
+		os_free(tls_conf->crl);
+	}
+	mbedtls_pk_free(&tls_conf->private_key);
+	mbedtls_ssl_config_free(&tls_conf->conf);
+	os_free(tls_conf->curves);
+	os_free(tls_conf->ciphersuites);
+	os_free(tls_conf->subject_match);
+	os_free(tls_conf->altsubject_match);
+	os_free(tls_conf->suffix_match);
+	os_free(tls_conf->domain_match);
+	os_free(tls_conf->check_cert_subject);
+	os_free(tls_conf);
+}
+
+
+mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void); /*(not in header)*/
+
+__attribute_cold__
+void * tls_init(const struct tls_config *conf)
+{
+	/* RFE: review struct tls_config *conf (different from tls_conf) */
+
+	if (++tls_ctx_global.refcnt > 1)
+		return &tls_ctx_global;
+
+	tls_ctx_global.ctr_drbg = crypto_mbedtls_ctr_drbg();
+  #ifdef MBEDTLS_SSL_SESSION_TICKETS
+	mbedtls_ssl_ticket_init(&tls_ctx_global.ticket_ctx);
+	mbedtls_ssl_ticket_setup(&tls_ctx_global.ticket_ctx,
+	                         mbedtls_ctr_drbg_random,
+	                         tls_ctx_global.ctr_drbg,
+	                         MBEDTLS_CIPHER_AES_256_GCM,
+	                         43200); /* ticket timeout: 12 hours */
+  #endif
+	/* copy struct for future use */
+	tls_ctx_global.init_conf = *conf;
+	if (conf->openssl_ciphers)
+		tls_ctx_global.init_conf.openssl_ciphers =
+		  os_strdup(conf->openssl_ciphers);
+
+	tls_ctx_global.crl_reload_interval = conf->crl_reload_interval;
+	os_get_reltime(&tls_ctx_global.crl_reload_previous);
+
+	return &tls_ctx_global;
+}
+
+
+__attribute_cold__
+void tls_deinit(void *tls_ctx)
+{
+	if (tls_ctx == NULL || --tls_ctx_global.refcnt != 0)
+		return;
+
+	tls_conf_deinit(tls_ctx_global.tls_conf);
+	os_free(tls_ctx_global.ca_cert_file);
+	os_free(tls_ctx_global.ocsp_stapling_response);
+	char *openssl_ciphers; /*(allocated in tls_init())*/
+	*(const char **)&openssl_ciphers =
+	  tls_ctx_global.init_conf.openssl_ciphers;
+	os_free(openssl_ciphers);
+  #ifdef MBEDTLS_SSL_SESSION_TICKETS
+	mbedtls_ssl_ticket_free(&tls_ctx_global.ticket_ctx);
+  #endif
+	os_memset(&tls_ctx_global, 0, sizeof(tls_ctx_global));
+}
+
+
+int tls_get_errors(void *tls_ctx)
+{
+	return 0;
+}
+
+
+static void tls_connection_deinit_expkey(struct tls_connection *conn)
+{
+	conn->tls_prf_type = 0; /* MBEDTLS_SSL_TLS_PRF_NONE; */
+	conn->expkey_keyblock_size = 0;
+	conn->expkey_secret_len = 0;
+	forced_memzero(conn->expkey_secret, sizeof(conn->expkey_secret));
+	forced_memzero(conn->expkey_randbytes, sizeof(conn->expkey_randbytes));
+}
+
+
+#ifdef TLS_MBEDTLS_SESSION_TICKETS
+void tls_connection_deinit_clienthello_session_ticket(struct tls_connection *conn)
+{
+	if (conn->clienthello_session_ticket) {
+		mbedtls_platform_zeroize(conn->clienthello_session_ticket,
+		                         conn->clienthello_session_ticket_len);
+		mbedtls_free(conn->clienthello_session_ticket);
+		conn->clienthello_session_ticket = NULL;
+		conn->clienthello_session_ticket_len = 0;
+	}
+}
+#endif
+
+
+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
+{
+	if (conn == NULL)
+		return;
+
+  #if 0 /*(good intention, but never sent since we destroy self below)*/
+	if (conn->established)
+		mbedtls_ssl_close_notify(&conn->ssl);
+  #endif
+
+	if (conn->tls_prf_type)
+		tls_connection_deinit_expkey(conn);
+
+  #ifdef TLS_MBEDTLS_SESSION_TICKETS
+	if (conn->clienthello_session_ticket)
+		tls_connection_deinit_clienthello_session_ticket(conn);
+  #endif
+
+	os_free(conn->peer_subject);
+	wpabuf_free(conn->success_data);
+	wpabuf_free(conn->push_buf);
+	wpabuf_free(conn->pull_buf);
+	mbedtls_ssl_free(&conn->ssl);
+	tls_conf_deinit(conn->tls_conf);
+	os_free(conn);
+}
+
+
+static void tls_mbedtls_refresh_crl(void);
+static int tls_mbedtls_ssl_setup(struct tls_connection *conn);
+
+struct tls_connection * tls_connection_init(void *tls_ctx)
+{
+	struct tls_connection *conn = os_zalloc(sizeof(*conn));
+	if (conn == NULL)
+		return NULL;
+
+	mbedtls_ssl_init(&conn->ssl);
+
+	conn->tls_conf = tls_ctx_global.tls_conf; /*(inherit global conf, if set)*/
+	if (conn->tls_conf) {
+		++conn->tls_conf->refcnt;
+		/* check for CRL refresh if inheriting from global config */
+		tls_mbedtls_refresh_crl();
+
+		conn->verify_peer = conn->tls_conf->verify_peer;
+		if (tls_mbedtls_ssl_setup(conn) != 0) {
+			tls_connection_deinit(&tls_ctx_global, conn);
+			return NULL;
+		}
+	}
+
+	return conn;
+}
+
+
+int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
+{
+	return conn ? conn->established : 0;
+}
+
+
+__attribute_noinline__
+char * tls_mbedtls_peer_serial_num(const mbedtls_x509_crt *crt, char *serial_num, size_t len)
+{
+	/* mbedtls_x509_serial_gets() inefficiently formats to hex separated by
+	 * colons, so generate the hex serial number here.  The func
+	 * wpa_snprintf_hex_uppercase() is similarly inefficient. */
+	size_t i = 0; /* skip leading 0's per Distinguished Encoding Rules (DER) */
+	while (i < crt->serial.len && crt->serial.p[i] == 0) ++i;
+	if (i == crt->serial.len) --i;
+
+	const unsigned char *s = crt->serial.p + i;
+	const size_t e = (crt->serial.len - i) * 2;
+	if (e >= len)
+		return NULL;
+  #if 0
+	wpa_snprintf_hex_uppercase(serial_num, len, s, crt->serial.len-i);
+  #else
+	for (i = 0; i < e; i+=2, ++s) {
+		serial_num[i+0] = "0123456789ABCDEF"[(*s >>  4)];
+		serial_num[i+1] = "0123456789ABCDEF"[(*s & 0xF)];
+	}
+	serial_num[e] = '\0';
+  #endif
+	return serial_num;
+}
+
+
+char * tls_connection_peer_serial_num(void *tls_ctx,
+				      struct tls_connection *conn)
+{
+	const mbedtls_x509_crt *crt = mbedtls_ssl_get_peer_cert(&conn->ssl);
+	if (crt == NULL)
+		return NULL;
+	size_t len = crt->serial.len * 2 + 1;
+	char *serial_num = os_malloc(len);
+	if (!serial_num)
+		return NULL;
+	return tls_mbedtls_peer_serial_num(crt, serial_num, len);
+}
+
+
+static void tls_pull_buf_reset(struct tls_connection *conn);
+
+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
+{
+	/* Note: this function called from eap_peer_tls_reauth_init()
+	 * for session resumption, not for connection shutdown */
+
+	if (conn == NULL)
+		return -1;
+
+	tls_pull_buf_reset(conn);
+	wpabuf_free(conn->push_buf);
+	conn->push_buf = NULL;
+	conn->established = 0;
+	conn->resumed = 0;
+	if (conn->tls_prf_type)
+		tls_connection_deinit_expkey(conn);
+
+	/* RFE: prepare for session resumption? (see doc in crypto/tls.h) */
+
+	return mbedtls_ssl_session_reset(&conn->ssl);
+}
+
+
+static int tls_wpabuf_resize_put_data(struct wpabuf **buf,
+                                      const unsigned char *data, size_t dlen)
+{
+	if (wpabuf_resize(buf, dlen) < 0)
+		return 0;
+	wpabuf_put_data(*buf, data, dlen);
+	return 1;
+}
+
+
+static int tls_pull_buf_append(struct tls_connection *conn,
+                               const struct wpabuf *in_data)
+{
+	/*(interface does not lend itself to move semantics)*/
+	return tls_wpabuf_resize_put_data(&conn->pull_buf,
+	                                  wpabuf_head(in_data),
+	                                  wpabuf_len(in_data));
+}
+
+
+static void tls_pull_buf_reset(struct tls_connection *conn)
+{
+	/*(future: might consider reusing conn->pull_buf)*/
+	wpabuf_free(conn->pull_buf);
+	conn->pull_buf = NULL;
+	conn->pull_buf_offset = 0;
+}
+
+
+__attribute_cold__
+static void tls_pull_buf_discard(struct tls_connection *conn, const char *func)
+{
+	size_t discard = wpabuf_len(conn->pull_buf) - conn->pull_buf_offset;
+	if (discard)
+		wpa_printf(MSG_DEBUG,
+			   "%s - %zu bytes remaining in pull_buf; discarding",
+			   func, discard);
+	tls_pull_buf_reset(conn);
+}
+
+
+static int tls_pull_func(void *ptr, unsigned char *buf, size_t len)
+{
+	struct tls_connection *conn = (struct tls_connection *) ptr;
+	if (conn->pull_buf == NULL)
+		return MBEDTLS_ERR_SSL_WANT_READ;
+	const size_t dlen = wpabuf_len(conn->pull_buf) - conn->pull_buf_offset;
+	if (dlen == 0)
+		return MBEDTLS_ERR_SSL_WANT_READ;
+
+	if (len > dlen)
+		len = dlen;
+	os_memcpy(buf, wpabuf_head(conn->pull_buf)+conn->pull_buf_offset, len);
+
+	if (len == dlen) {
+		tls_pull_buf_reset(conn);
+		/*wpa_printf(MSG_DEBUG, "%s - emptied pull_buf", __func__);*/
+	}
+	else {
+		conn->pull_buf_offset += len;
+		/*wpa_printf(MSG_DEBUG, "%s - %zu bytes remaining in pull_buf",
+			   __func__, dlen - len);*/
+	}
+	return (int)len;
+}
+
+
+static int tls_push_func(void *ptr, const unsigned char *buf, size_t len)
+{
+	struct tls_connection *conn = (struct tls_connection *) ptr;
+	return tls_wpabuf_resize_put_data(&conn->push_buf, buf, len)
+	  ? (int)len
+	  : MBEDTLS_ERR_SSL_ALLOC_FAILED;
+}
+
+
+static int
+tls_mbedtls_verify_cb (void *arg, mbedtls_x509_crt *crt, int depth, uint32_t *flags);
+
+
+static int tls_mbedtls_ssl_setup(struct tls_connection *conn)
+{
+  #if 0
+	/* mbedtls_ssl_setup() must be called only once */
+	/* If this func might be called multiple times (e.g. via set_params),
+	 * then we should set a flag in conn that ssl was initialized */
+	if (conn->ssl_is_init) {
+		mbedtls_ssl_free(&conn->ssl);
+		mbedtls_ssl_init(&conn->ssl);
+	}
+  #endif
+
+	int ret = mbedtls_ssl_setup(&conn->ssl, &conn->tls_conf->conf);
+	if (ret != 0) {
+		elog(ret, "mbedtls_ssl_setup");
+		return -1;
+	}
+
+	mbedtls_ssl_set_bio(&conn->ssl, conn, tls_push_func, tls_pull_func, NULL);
+  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
+	mbedtls_ssl_set_export_keys_cb(
+	    &conn->ssl, tls_connection_export_keys_cb, conn);
+  #elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
+	mbedtls_ssl_conf_export_keys_ext_cb(
+	    &conn->tls_conf->conf, tls_connection_export_keys_cb, conn);
+  #endif
+	if (conn->verify_peer)
+		mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
+
+	return 0;
+}
+
+
+static int tls_mbedtls_data_is_pem(const u8 *data)
+{
+    return (NULL != os_strstr((char *)data, "-----"));
+}
+
+
+static void tls_mbedtls_set_allowed_tls_vers(struct tls_conf *tls_conf,
+                                             mbedtls_ssl_config *conf)
+{
+  #if !defined(MBEDTLS_SSL_PROTO_TLS1_3)
+	tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_3;
+  #endif
+
+	/* unconditionally require TLSv1.2+ for TLS_CONN_SUITEB */
+	if (tls_conf->flags & TLS_CONN_SUITEB) {
+		tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_0;
+		tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_1;
+	}
+
+	const unsigned int flags = tls_conf->flags;
+
+	/* attempt to map flags to min and max TLS protocol version */
+
+	int min = (flags & TLS_CONN_DISABLE_TLSv1_0)
+		? (flags & TLS_CONN_DISABLE_TLSv1_1)
+		? (flags & TLS_CONN_DISABLE_TLSv1_2)
+		? (flags & TLS_CONN_DISABLE_TLSv1_3)
+		? 4
+		: 3
+		: 2
+		: 1
+		: 0;
+
+	int max = (flags & TLS_CONN_DISABLE_TLSv1_3)
+		? (flags & TLS_CONN_DISABLE_TLSv1_2)
+		? (flags & TLS_CONN_DISABLE_TLSv1_1)
+		? (flags & TLS_CONN_DISABLE_TLSv1_0)
+		? -1
+		: 0
+		: 1
+		: 2
+		: 3;
+
+	if ((flags & TLS_CONN_ENABLE_TLSv1_2) && min > 2) min = 2;
+	if ((flags & TLS_CONN_ENABLE_TLSv1_1) && min > 1) min = 1;
+	if ((flags & TLS_CONN_ENABLE_TLSv1_0) && min > 0) min = 0;
+	if (max < min) {
+		emsg(MSG_ERROR, "invalid tls_disable_tlsv* params; ignoring");
+		return;
+	}
+  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
+	/* mbed TLS 3.0.0 removes support for protocols < TLSv1.2 */
+	if (min < 2 || max < 2) {
+		emsg(MSG_ERROR, "invalid tls_disable_tlsv* params; ignoring");
+		if (min < 2) min = 2;
+		if (max < 2) max = 2;
+	}
+  #endif
+
+  #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
+	/* MBEDTLS_SSL_VERSION_TLS1_2 = 0x0303 *//*!< (D)TLS 1.2 */
+	/* MBEDTLS_SSL_VERSION_TLS1_3 = 0x0304 *//*!< (D)TLS 1.3 */
+	min = (min == 2) ? MBEDTLS_SSL_VERSION_TLS1_2 : MBEDTLS_SSL_VERSION_TLS1_3;
+	max = (max == 2) ? MBEDTLS_SSL_VERSION_TLS1_2 : MBEDTLS_SSL_VERSION_TLS1_3;
+	mbedtls_ssl_conf_min_tls_version(conf, min);
+	mbedtls_ssl_conf_max_tls_version(conf, max);
+  #else
+   #ifndef MBEDTLS_SSL_MINOR_VERSION_4
+	if (min == 3) min = 2;
+	if (max == 3) max = 2;
+   #endif
+	/* MBEDTLS_SSL_MINOR_VERSION_0  0 *//*!< SSL v3.0 */
+	/* MBEDTLS_SSL_MINOR_VERSION_1  1 *//*!< TLS v1.0 */
+	/* MBEDTLS_SSL_MINOR_VERSION_2  2 *//*!< TLS v1.1 */
+	/* MBEDTLS_SSL_MINOR_VERSION_3  3 *//*!< TLS v1.2 */
+	/* MBEDTLS_SSL_MINOR_VERSION_4  4 *//*!< TLS v1.3 */
+	mbedtls_ssl_conf_min_version(conf, MBEDTLS_SSL_MAJOR_VERSION_3, min+1);
+	mbedtls_ssl_conf_max_version(conf, MBEDTLS_SSL_MAJOR_VERSION_3, max+1);
+  #endif
+}
+
+
+__attribute_noinline__
+static int tls_mbedtls_readfile(const char *path, u8 **buf, size_t *n);
+
+
+static int
+tls_mbedtls_set_dhparams(struct tls_conf *tls_conf, const char *dh_file)
+{
+    size_t len;
+    u8 *data;
+    if (tls_mbedtls_readfile(dh_file, &data, &len))
+        return 0;
+
+    /* parse only if DH parameters if in PEM format */
+    if (tls_mbedtls_data_is_pem(data)
+        && NULL == os_strstr((char *)data, "-----BEGIN DH PARAMETERS-----")) {
+        if (os_strstr((char *)data, "-----BEGIN DSA PARAMETERS-----"))
+            wpa_printf(MSG_WARNING, "DSA parameters not handled (%s)", dh_file);
+        else
+            wpa_printf(MSG_WARNING, "unexpected DH param content (%s)",dh_file);
+        forced_memzero(data, len);
+        os_free(data);
+        return 0;
+    }
+
+    /* mbedtls_dhm_parse_dhm() expects "-----BEGIN DH PARAMETERS-----" if PEM */
+    mbedtls_dhm_context dhm;
+    mbedtls_dhm_init(&dhm);
+    int rc = mbedtls_dhm_parse_dhm(&dhm, data, len);
+    if (0 == rc)
+        rc = mbedtls_ssl_conf_dh_param_ctx(&tls_conf->conf, &dhm);
+    if (0 != rc)
+        elog(rc, dh_file);
+    mbedtls_dhm_free(&dhm);
+
+    forced_memzero(data, len);
+    os_free(data);
+    return (0 == rc);
+}
+
+
+/* reference: lighttpd src/mod_mbedtls.c:mod_mbedtls_ssl_append_curve()
+ * (same author: gstrauss@gluelogic.com; same license: BSD-3-Clause) */
+#if MBEDTLS_VERSION_NUMBER < 0x03010000 /* mbedtls 3.1.0 */
+static int
+tls_mbedtls_append_curve (mbedtls_ecp_group_id *ids, int nids, int idsz, const mbedtls_ecp_group_id id)
+{
+    if (1 >= idsz - (nids + 1)) {
+        emsg(MSG_ERROR, "error: too many curves during list expand");
+        return -1;
+    }
+    ids[++nids] = id;
+    return nids;
+}
+
+
+static int
+tls_mbedtls_set_curves(struct tls_conf *tls_conf, const char *curvelist)
+{
+    mbedtls_ecp_group_id ids[512];
+    int nids = -1;
+    const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
+    const mbedtls_ecp_curve_info * const curve_info = mbedtls_ecp_curve_list();
+
+    for (const char *e = curvelist-1; e; ) {
+        const char * const n = e+1;
+        e = os_strchr(n, ':');
+        size_t len = e ? (size_t)(e - n) : os_strlen(n);
+        mbedtls_ecp_group_id grp_id = MBEDTLS_ECP_DP_NONE;
+        switch (len) {
+          case 5:
+            if (0 == os_memcmp("P-521", n, 5))
+                grp_id = MBEDTLS_ECP_DP_SECP521R1;
+            else if (0 == os_memcmp("P-384", n, 5))
+                grp_id = MBEDTLS_ECP_DP_SECP384R1;
+            else if (0 == os_memcmp("P-256", n, 5))
+                grp_id = MBEDTLS_ECP_DP_SECP256R1;
+            break;
+          case 6:
+            if (0 == os_memcmp("BP-521", n, 6))
+                grp_id = MBEDTLS_ECP_DP_BP512R1;
+            else if (0 == os_memcmp("BP-384", n, 6))
+                grp_id = MBEDTLS_ECP_DP_BP384R1;
+            else if (0 == os_memcmp("BP-256", n, 6))
+                grp_id = MBEDTLS_ECP_DP_BP256R1;
+            break;
+          default:
+            break;
+        }
+        if (grp_id != MBEDTLS_ECP_DP_NONE) {
+            nids = tls_mbedtls_append_curve(ids, nids, idsz, grp_id);
+            if (-1 == nids) return 0;
+            continue;
+        }
+        /* similar to mbedtls_ecp_curve_info_from_name() */
+        const mbedtls_ecp_curve_info *info;
+        for (info = curve_info; info->grp_id != MBEDTLS_ECP_DP_NONE; ++info) {
+            if (0 == os_strncmp(info->name, n, len) && info->name[len] == '\0')
+                break;
+        }
+        if (info->grp_id == MBEDTLS_ECP_DP_NONE) {
+            wpa_printf(MSG_ERROR, "MTLS: unrecognized curve: %.*s",(int)len,n);
+            return 0;
+        }
+
+        nids = tls_mbedtls_append_curve(ids, nids, idsz, info->grp_id);
+        if (-1 == nids) return 0;
+    }
+
+    /* mod_openssl configures "prime256v1" if curve list not specified,
+     * but mbedtls provides a list of supported curves if not explicitly set */
+    if (-1 == nids) return 1; /* empty list; no-op */
+
+    ids[++nids] = MBEDTLS_ECP_DP_NONE; /* terminate list */
+    ++nids;
+
+    /* curves list must be persistent for lifetime of mbedtls_ssl_config */
+    tls_conf->curves = os_malloc(nids * sizeof(mbedtls_ecp_group_id));
+    if (tls_conf->curves == NULL)
+        return 0;
+    os_memcpy(tls_conf->curves, ids, nids * sizeof(mbedtls_ecp_group_id));
+
+    mbedtls_ssl_conf_curves(&tls_conf->conf, tls_conf->curves);
+    return 1;
+}
+#else
+static int
+tls_mbedtls_append_curve (uint16_t *ids, int nids, int idsz, const uint16_t id)
+{
+    if (1 >= idsz - (nids + 1)) {
+        emsg(MSG_ERROR, "error: too many curves during list expand");
+        return -1;
+    }
+    ids[++nids] = id;
+    return nids;
+}
+
+
+static int
+tls_mbedtls_set_curves(struct tls_conf *tls_conf, const char *curvelist)
+{
+    /* TLS Supported Groups (renamed from "EC Named Curve Registry")
+     * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
+     */
+    uint16_t ids[512];
+    int nids = -1;
+    const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
+    const mbedtls_ecp_curve_info * const curve_info = mbedtls_ecp_curve_list();
+
+    for (const char *e = curvelist-1; e; ) {
+        const char * const n = e+1;
+        e = os_strchr(n, ':');
+        size_t len = e ? (size_t)(e - n) : os_strlen(n);
+        uint16_t tls_id = 0;
+        switch (len) {
+          case 5:
+            if (0 == os_memcmp("P-521", n, 5))
+                tls_id = 25; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP521R1 */
+            else if (0 == os_memcmp("P-384", n, 5))
+                tls_id = 24; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP384R1 */
+            else if (0 == os_memcmp("P-256", n, 5))
+                tls_id = 23; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP256R1 */
+            break;
+          case 6:
+            if (0 == os_memcmp("BP-521", n, 6))
+                tls_id = 28; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP512R1 */
+            else if (0 == os_memcmp("BP-384", n, 6))
+                tls_id = 27; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP384R1 */
+            else if (0 == os_memcmp("BP-256", n, 6))
+                tls_id = 26; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP256R1 */
+            break;
+          default:
+            break;
+        }
+        if (tls_id != 0) {
+            nids = tls_mbedtls_append_curve(ids, nids, idsz, tls_id);
+            if (-1 == nids) return 0;
+            continue;
+        }
+        /* similar to mbedtls_ecp_curve_info_from_name() */
+        const mbedtls_ecp_curve_info *info;
+        for (info = curve_info; info->tls_id != 0; ++info) {
+            if (0 == os_strncmp(info->name, n, len) && info->name[len] == '\0')
+                break;
+        }
+        if (info->tls_id == 0) {
+            wpa_printf(MSG_ERROR, "MTLS: unrecognized curve: %.*s",(int)len,n);
+            return 0;
+        }
+
+        nids = tls_mbedtls_append_curve(ids, nids, idsz, info->tls_id);
+        if (-1 == nids) return 0;
+    }
+
+    /* mod_openssl configures "prime256v1" if curve list not specified,
+     * but mbedtls provides a list of supported curves if not explicitly set */
+    if (-1 == nids) return 1; /* empty list; no-op */
+
+    ids[++nids] = 0; /* terminate list */
+    ++nids;
+
+    /* curves list must be persistent for lifetime of mbedtls_ssl_config */
+    tls_conf->curves = os_malloc(nids * sizeof(uint16_t));
+    if (tls_conf->curves == NULL)
+        return 0;
+    os_memcpy(tls_conf->curves, ids, nids * sizeof(uint16_t));
+
+    mbedtls_ssl_conf_groups(&tls_conf->conf, tls_conf->curves);
+    return 1;
+}
+#endif /* MBEDTLS_VERSION_NUMBER >= 0x03010000 */ /* mbedtls 3.1.0 */
+
+
+/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
+static const int suite_AES_256_ephemeral[] = {
+    /* All AES-256 ephemeral suites */
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM,
+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM,
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,
+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8
+};
+
+/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
+static const int suite_AES_128_ephemeral[] = {
+    /* All AES-128 ephemeral suites */
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM,
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8
+};
+
+/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */
+/* HIGH cipher list (mapped from openssl list to mbedtls) */
+static const int suite_HIGH[] = {
+    MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
+    MBEDTLS_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM,
+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM,
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,
+    MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8,
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384,
+    MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384,
+    MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256,
+    MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384,
+    MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384,
+    MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384,
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM,
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
+    MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8,
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256,
+    MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
+    MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
+    MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256,
+    MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256,
+    MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256,
+    MBEDTLS_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
+    MBEDTLS_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
+    MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,
+    MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM,
+    MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384,
+    MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384,
+    MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA,
+    MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
+    MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
+    MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
+    MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM_8,
+    MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384,
+    MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256,
+    MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM,
+    MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
+    MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
+    MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
+    MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA,
+    MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
+    MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
+    MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM_8,
+    MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256,
+    MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384,
+    MBEDTLS_TLS_RSA_WITH_AES_256_CCM,
+    MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256,
+    MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA,
+    MBEDTLS_TLS_RSA_WITH_AES_256_CCM_8,
+    MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256,
+    MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,
+    MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384,
+    MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256,
+    MBEDTLS_TLS_RSA_WITH_AES_128_CCM,
+    MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256,
+    MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA,
+    MBEDTLS_TLS_RSA_WITH_AES_128_CCM_8,
+    MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256,
+    MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA,
+    MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256,
+    MBEDTLS_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256,
+    MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384,
+    MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
+    MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
+    MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384,
+    MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384,
+    MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256,
+    MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
+    MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
+    MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256,
+    MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256,
+    MBEDTLS_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256,
+    MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384,
+    MBEDTLS_TLS_PSK_WITH_AES_256_CCM,
+    MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384,
+    MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA,
+    MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384,
+    MBEDTLS_TLS_PSK_WITH_AES_256_CCM_8,
+    MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384,
+    MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256,
+    MBEDTLS_TLS_PSK_WITH_AES_128_CCM,
+    MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256,
+    MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA,
+    MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256,
+    MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8,
+    MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256
+};
+
+
+__attribute_noinline__
+static int
+tls_mbedtls_append_ciphersuite (int *ids, int nids, int idsz, const int *x, int xsz)
+{
+    if (xsz >= idsz - (nids + 1)) {
+        emsg(MSG_ERROR, "error: too many ciphers during list expand");
+        return -1;
+    }
+
+    for (int i = 0; i < xsz; ++i)
+        ids[++nids] = x[i];
+
+    return nids;
+}
+
+
+static int
+tls_mbedtls_translate_ciphername(int id, char *buf, size_t buflen)
+{
+    const mbedtls_ssl_ciphersuite_t *info =
+      mbedtls_ssl_ciphersuite_from_id(id);
+    if (info == NULL)
+        return 0;
+    const char *name = mbedtls_ssl_ciphersuite_get_name(info);
+    const size_t len = os_strlen(name);
+    if (len == 7 && 0 == os_memcmp(name, "unknown", 7))
+        return 0;
+    if (len >= buflen)
+        return 0;
+    os_strlcpy(buf, name, buflen);
+
+    /* attempt to translate mbedtls string to openssl string
+     * (some heuristics; incomplete) */
+    size_t i = 0, j = 0;
+    if (buf[0] == 'T') {
+        if (os_strncmp(buf, "TLS1-3-", 7) == 0) {
+            buf[3] = '-';
+            j = 4; /* remove "1-3" from "TLS1-3-" prefix */
+            i = 7;
+        }
+        else if (os_strncmp(buf, "TLS-", 4) == 0)
+            i = 4; /* remove "TLS-" prefix */
+    }
+    for (; buf[i]; ++i) {
+        if (buf[i] == '-') {
+            if (i >= 3) {
+                if (0 == os_memcmp(buf+i-3, "AES", 3))
+                    continue; /* "AES-" -> "AES" */
+            }
+            if (i >= 4) {
+                if (0 == os_memcmp(buf+i-4, "WITH", 4)) {
+                    j -= 4;   /* remove "WITH-" */
+                    continue;
+                }
+            }
+        }
+        buf[j++] = buf[i];
+    }
+    buf[j] = '\0';
+
+    return j;
+}
+
+
+__attribute_noinline__
+static int
+tls_mbedtls_set_ciphersuites(struct tls_conf *tls_conf, int *ids, int nids)
+{
+    /* ciphersuites list must be persistent for lifetime of mbedtls_ssl_config*/
+    os_free(tls_conf->ciphersuites);
+    tls_conf->ciphersuites = os_malloc(nids * sizeof(int));
+    if (tls_conf->ciphersuites == NULL)
+        return 0;
+    os_memcpy(tls_conf->ciphersuites, ids, nids * sizeof(int));
+    mbedtls_ssl_conf_ciphersuites(&tls_conf->conf, tls_conf->ciphersuites);
+    return 1;
+}
+
+
+static int
+tls_mbedtls_set_ciphers(struct tls_conf *tls_conf, const char *ciphers)
+{
+    char buf[64];
+    int ids[512];
+    int nids = -1;
+    const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1);
+    const char *next;
+    size_t blen, clen;
+    do {
+        next = os_strchr(ciphers, ':');
+        clen = next ? (size_t)(next - ciphers) : os_strlen(ciphers);
+        if (!clen)
+            continue;
+
+        /* special-case a select set of openssl group names for hwsim tests */
+	/* (review; remove excess code if tests are not run for non-OpenSSL?) */
+        if (clen == 9 && os_memcmp(ciphers, "SUITEB192", 9) == 0) {
+            static int ssl_preset_suiteb192_ciphersuites[] = {
+                MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+                0
+            };
+            return tls_mbedtls_set_ciphersuites(tls_conf,
+                                                ssl_preset_suiteb192_ciphersuites,
+                                                2);
+        }
+        if (clen == 9 && os_memcmp(ciphers, "SUITEB128", 9) == 0) {
+            static int ssl_preset_suiteb128_ciphersuites[] = {
+                MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+                0
+            };
+            return tls_mbedtls_set_ciphersuites(tls_conf,
+                                                ssl_preset_suiteb128_ciphersuites,
+                                                2);
+        }
+        if (clen == 7 && os_memcmp(ciphers, "DEFAULT", 7) == 0)
+            continue;
+        if (clen == 6 && os_memcmp(ciphers, "AES128", 6) == 0) {
+            nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz,
+                     suite_AES_128_ephemeral,
+                     (int)ARRAY_SIZE(suite_AES_128_ephemeral));
+            if (nids == -1)
+                return 0;
+            continue;
+        }
+        if (clen == 6 && os_memcmp(ciphers, "AES256", 6) == 0) {
+            nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz,
+                     suite_AES_256_ephemeral,
+                     (int)ARRAY_SIZE(suite_AES_256_ephemeral));
+            if (nids == -1)
+                return 0;
+            continue;
+        }
+        if (clen == 4 && os_memcmp(ciphers, "HIGH", 4) == 0) {
+            nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz, suite_HIGH,
+                                                  (int)ARRAY_SIZE(suite_HIGH));
+            if (nids == -1)
+                return 0;
+            continue;
+        }
+        /* ignore anonymous cipher group names (?not supported by mbedtls?) */
+        if (clen == 4 && os_memcmp(ciphers, "!ADH", 4) == 0)
+            continue;
+        if (clen == 6 && os_memcmp(ciphers, "-aECDH", 6) == 0)
+            continue;
+        if (clen == 7 && os_memcmp(ciphers, "-aECDSA", 7) == 0)
+            continue;
+
+        /* attempt to match mbedtls cipher names
+         * nb: does not support openssl group names or list manipulation syntax
+         *   (alt: could copy almost 1200 lines (!!!) of lighttpd mod_mbedtls.c
+         *    mod_mbedtls_ssl_conf_ciphersuites() to translate strings)
+         * note: not efficient to rewrite list for each ciphers entry,
+         *       but this code is expected to run only at startup
+         */
+        const int *list = mbedtls_ssl_list_ciphersuites();
+        for (; *list; ++list) {
+            blen = tls_mbedtls_translate_ciphername(*list,buf,sizeof(buf));
+            if (!blen)
+                continue;
+
+            /* matching heuristics additional to translate_ciphername above */
+            if (blen == clen+4) {
+                char *cbc = os_strstr(buf, "CBC-");
+                if (cbc) {
+                    os_memmove(cbc, cbc+4, blen-(cbc+4-buf)+1); /*(w/ '\0')*/
+                    blen -= 4;
+                }
+            }
+            if (blen >= clen && os_memcmp(ciphers, buf, clen) == 0
+                && (blen == clen
+                    || (blen == clen+7 && os_memcmp(buf+clen, "-SHA256", 7)))) {
+                if (1 >= idsz - (nids + 1)) {
+                    emsg(MSG_ERROR,
+                         "error: too many ciphers during list expand");
+                    return 0;
+                }
+                ids[++nids] = *list;
+                break;
+            }
+        }
+        if (*list == 0) {
+            wpa_printf(MSG_ERROR,
+                       "MTLS: unrecognized cipher: %.*s", (int)clen, ciphers);
+            return 0;
+        }
+    } while ((ciphers = next ? next+1 : NULL));
+
+    if (-1 == nids) return 1; /* empty list; no-op */
+
+    ids[++nids] = 0; /* terminate list */
+    ++nids;
+
+    return tls_mbedtls_set_ciphersuites(tls_conf, ids, nids);
+}
+
+
+__attribute_noinline__
+static int tls_mbedtls_set_item(char **config_item, const char *item)
+{
+	os_free(*config_item);
+	*config_item = NULL;
+	return item ? (*config_item = os_strdup(item)) != NULL : 1;
+}
+
+
+static int tls_connection_set_subject_match(struct tls_conf *tls_conf,
+                                            const struct tls_connection_params *params)
+{
+	int rc = 1;
+	rc &= tls_mbedtls_set_item(&tls_conf->subject_match,
+	                              params->subject_match);
+	rc &= tls_mbedtls_set_item(&tls_conf->altsubject_match,
+	                              params->altsubject_match);
+	rc &= tls_mbedtls_set_item(&tls_conf->suffix_match,
+	                              params->suffix_match);
+	rc &= tls_mbedtls_set_item(&tls_conf->domain_match,
+	                              params->domain_match);
+	rc &= tls_mbedtls_set_item(&tls_conf->check_cert_subject,
+	                              params->check_cert_subject);
+	return rc;
+}
+
+
+/* duplicated in crypto_mbedtls.c:crypto_mbedtls_readfile()*/
+__attribute_noinline__
+static int tls_mbedtls_readfile(const char *path, u8 **buf, size_t *n)
+{
+  #if 0 /* #ifdef MBEDTLS_FS_IO */
+	/*(includes +1 for '\0' needed by mbedtls PEM parsing funcs)*/
+	if (mbedtls_pk_load_file(path, (unsigned char **)buf, n) != 0) {
+		wpa_printf(MSG_ERROR, "error: mbedtls_pk_load_file %s", path);
+		return -1;
+	}
+  #else
+	/*(use os_readfile() so that we can use os_free()
+	 *(if we use mbedtls_pk_load_file() above, macros prevent calling free()
+	 * directly #if defined(OS_REJECT_C_LIB_FUNCTIONS) and calling os_free()
+	 * on buf aborts in tests if buf not allocated via os_malloc())*/
+	*buf = (u8 *)os_readfile(path, n);
+	if (!*buf) {
+		wpa_printf(MSG_ERROR, "error: os_readfile %s", path);
+		return -1;
+	}
+	u8 *buf0 = os_realloc(*buf, *n+1);
+	if (!buf0) {
+		bin_clear_free(*buf, *n);
+		*buf = NULL;
+		return -1;
+	}
+	buf0[(*n)++] = '\0';
+	*buf = buf0;
+  #endif
+	return 0;
+}
+
+
+static int tls_mbedtls_set_crl(struct tls_conf *tls_conf, const u8 *data, size_t len)
+{
+	/* do not use mbedtls_x509_crl_parse() on PEM unless it contains CRL */
+	if (len && data[len-1] == '\0'
+	    && NULL == os_strstr((const char *)data,"-----BEGIN X509 CRL-----")
+	    && tls_mbedtls_data_is_pem(data))
+		return 0;
+
+	mbedtls_x509_crl crl;
+	mbedtls_x509_crl_init(&crl);
+	int rc = mbedtls_x509_crl_parse(&crl, data, len);
+	if (rc < 0) {
+		mbedtls_x509_crl_free(&crl);
+		return rc == MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT ? 0 : rc;
+	}
+
+	mbedtls_x509_crl *crl_new = os_malloc(sizeof(crl));
+	if (crl_new == NULL) {
+		mbedtls_x509_crl_free(&crl);
+		return MBEDTLS_ERR_X509_ALLOC_FAILED;
+	}
+	os_memcpy(crl_new, &crl, sizeof(crl));
+
+	mbedtls_x509_crl *crl_old = tls_conf->crl;
+	tls_conf->crl = crl_new;
+	if (crl_old) {
+		mbedtls_x509_crl_free(crl_old);
+		os_free(crl_old);
+	}
+	return 0;
+}
+
+
+static int tls_mbedtls_set_ca(struct tls_conf *tls_conf, u8 *data, size_t len)
+{
+	/* load crt struct onto stack and then copy into tls_conf in
+	 * order to preserve existing tls_conf value if error occurs
+	 *
+	 * hostapd is not threaded, or else should allocate memory and swap in
+	 * pointer reduce race condition.  (If threaded, would also need to
+	 * keep reference count of use to avoid freeing while still in use.) */
+
+	mbedtls_x509_crt crt;
+	mbedtls_x509_crt_init(&crt);
+	int rc = mbedtls_x509_crt_parse(&crt, data, len);
+	if (rc < 0) {
+		mbedtls_x509_crt_free(&crt);
+		return rc;
+	}
+
+	mbedtls_x509_crt_free(&tls_conf->ca_cert);
+	os_memcpy(&tls_conf->ca_cert, &crt, sizeof(crt));
+	return 0;
+}
+
+
+static int tls_mbedtls_set_ca_and_crl(struct tls_conf *tls_conf, const char *ca_cert_file)
+{
+	size_t len;
+	u8 *data;
+	if (tls_mbedtls_readfile(ca_cert_file, &data, &len))
+		return -1;
+
+	int rc;
+	if (0 == (rc = tls_mbedtls_set_ca(tls_conf, data, len))
+	    && (!tls_mbedtls_data_is_pem(data) /*skip parse for CRL if not PEM*/
+	        || 0 == (rc = tls_mbedtls_set_crl(tls_conf, data, len)))) {
+		mbedtls_ssl_conf_ca_chain(&tls_conf->conf,
+		                          &tls_conf->ca_cert,
+		                          tls_conf->crl);
+	}
+	else {
+		elog(rc, __func__);
+		emsg(MSG_ERROR, ca_cert_file);
+	}
+
+	forced_memzero(data, len);
+	os_free(data);
+	return rc;
+}
+
+
+static void tls_mbedtls_refresh_crl(void)
+{
+	/* check for CRL refresh
+	 * continue even if error occurs; continue with previous cert, CRL */
+	unsigned int crl_reload_interval = tls_ctx_global.crl_reload_interval;
+	const char *ca_cert_file = tls_ctx_global.ca_cert_file;
+	if (!crl_reload_interval || !ca_cert_file)
+		return;
+
+	struct os_reltime *previous = &tls_ctx_global.crl_reload_previous;
+	struct os_reltime now;
+	if (os_get_reltime(&now) != 0
+	    || !os_reltime_expired(&now, previous, crl_reload_interval))
+		return;
+
+	/* Note: modifying global state is not thread-safe
+	 *       if in use by existing connections
+	 *
+	 * src/utils/os.h does not provide a portable stat()
+	 * or else it would be a good idea to check mtime and size,
+	 * and avoid reloading if file has not changed */
+
+	if (tls_mbedtls_set_ca_and_crl(tls_ctx_global.tls_conf, ca_cert_file) == 0)
+		*previous = now;
+}
+
+
+static int tls_mbedtls_set_ca_cert(struct tls_conf *tls_conf,
+				   const struct tls_connection_params *params)
+{
+	if (params->ca_cert) {
+		if (os_strncmp(params->ca_cert, "probe://", 8) == 0) {
+			tls_conf->ca_cert_probe = 1;
+			tls_conf->has_ca_cert = 1;
+			return 0;
+		}
+
+		if (os_strncmp(params->ca_cert, "hash://", 7) == 0) {
+			const char *pos = params->ca_cert + 7;
+			if (os_strncmp(pos, "server/sha256/", 14) != 0) {
+				emsg(MSG_ERROR, "unsupported ca_cert hash value");
+				return -1;
+			}
+			pos += 14;
+			if (os_strlen(pos) != SHA256_DIGEST_LENGTH*2) {
+				emsg(MSG_ERROR, "unexpected ca_cert hash length");
+				return -1;
+			}
+			if (hexstr2bin(pos, tls_conf->ca_cert_hash,
+			               SHA256_DIGEST_LENGTH) < 0) {
+				emsg(MSG_ERROR, "invalid ca_cert hash value");
+				return -1;
+			}
+			emsg(MSG_DEBUG, "checking only server certificate match");
+			tls_conf->verify_depth0_only = 1;
+			tls_conf->has_ca_cert = 1;
+			return 0;
+		}
+
+		if (tls_mbedtls_set_ca_and_crl(tls_conf, params->ca_cert) != 0)
+			return -1;
+	}
+	if (params->ca_cert_blob) {
+		size_t len = params->ca_cert_blob_len;
+		int is_pem = tls_mbedtls_data_is_pem(params->ca_cert_blob);
+		if (len && params->ca_cert_blob[len-1] != '\0' && is_pem)
+			++len; /*(include '\0' in len for PEM)*/
+		int ret = mbedtls_x509_crt_parse(&tls_conf->ca_cert,
+		                                 params->ca_cert_blob, len);
+		if (ret != 0) {
+			elog(ret, "mbedtls_x509_crt_parse");
+			return -1;
+		}
+		if (is_pem) { /*(ca_cert_blob in DER format contains ca cert only)*/
+			ret = tls_mbedtls_set_crl(tls_conf, params->ca_cert_blob, len);
+			if (ret != 0) {
+				elog(ret, "mbedtls_x509_crl_parse");
+				return -1;
+			}
+		}
+	}
+
+	if (mbedtls_x509_time_is_future(&tls_conf->ca_cert.valid_from)
+	    || mbedtls_x509_time_is_past(&tls_conf->ca_cert.valid_to)) {
+		emsg(MSG_WARNING, "ca_cert expired or not yet valid");
+		if (params->ca_cert)
+			emsg(MSG_WARNING, params->ca_cert);
+	}
+
+	tls_conf->has_ca_cert = 1;
+	return 0;
+}
+
+
+static int tls_mbedtls_set_certs(struct tls_conf *tls_conf,
+				 const struct tls_connection_params *params)
+{
+	int ret;
+
+	if (params->ca_cert || params->ca_cert_blob) {
+		if (tls_mbedtls_set_ca_cert(tls_conf, params) != 0)
+			return -1;
+	}
+	else if (params->ca_path) {
+		emsg(MSG_INFO, "ca_path support not implemented");
+		return -1;
+	}
+
+	if (!tls_conf->has_ca_cert)
+		mbedtls_ssl_conf_authmode(&tls_conf->conf, MBEDTLS_SSL_VERIFY_NONE);
+	else {
+		/* Initial setting: REQUIRED for client, OPTIONAL for server
+		 *   (see also tls_connection_set_verify()) */
+		tls_conf->verify_peer = (tls_ctx_global.tls_conf == NULL);
+		int authmode = tls_conf->verify_peer
+		  ? MBEDTLS_SSL_VERIFY_REQUIRED
+		  : MBEDTLS_SSL_VERIFY_OPTIONAL;
+		mbedtls_ssl_conf_authmode(&tls_conf->conf, authmode);
+		mbedtls_ssl_conf_ca_chain(&tls_conf->conf,
+		                          &tls_conf->ca_cert,
+		                          tls_conf->crl);
+
+		if (!tls_connection_set_subject_match(tls_conf, params))
+			return -1;
+	}
+
+	if (params->client_cert2) /*(yes, server_cert2 in msg below)*/
+		emsg(MSG_INFO, "server_cert2 support not implemented");
+
+	if (params->client_cert) {
+		size_t len;
+		u8 *data;
+		if (tls_mbedtls_readfile(params->client_cert, &data, &len))
+			return -1;
+		ret = mbedtls_x509_crt_parse(&tls_conf->client_cert, data, len);
+		forced_memzero(data, len);
+		os_free(data);
+	}
+	if (params->client_cert_blob) {
+		size_t len = params->client_cert_blob_len;
+		if (len && params->client_cert_blob[len-1] != '\0'
+		    && tls_mbedtls_data_is_pem(params->client_cert_blob))
+			++len; /*(include '\0' in len for PEM)*/
+		ret = mbedtls_x509_crt_parse(&tls_conf->client_cert,
+		                             params->client_cert_blob, len);
+	}
+	if (params->client_cert || params->client_cert_blob) {
+		if (ret < 0) {
+			elog(ret, "mbedtls_x509_crt_parse");
+			if (params->client_cert)
+				emsg(MSG_ERROR, params->client_cert);
+			return -1;
+		}
+		if (mbedtls_x509_time_is_future(&tls_conf->client_cert.valid_from)
+		    || mbedtls_x509_time_is_past(&tls_conf->client_cert.valid_to)) {
+			emsg(MSG_WARNING, "cert expired or not yet valid");
+			if (params->client_cert)
+				emsg(MSG_WARNING, params->client_cert);
+		}
+		tls_conf->has_client_cert = 1;
+	}
+
+	if (params->private_key || params->private_key_blob) {
+		size_t len = params->private_key_blob_len;
+		u8 *data;
+		*(const u8 **)&data = params->private_key_blob;
+		if (len && data[len-1] != '\0' && tls_mbedtls_data_is_pem(data))
+			++len; /*(include '\0' in len for PEM)*/
+		if (params->private_key
+		    && tls_mbedtls_readfile(params->private_key, &data, &len)) {
+			return -1;
+		}
+		const char *pwd = params->private_key_passwd;
+	  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
+		ret = mbedtls_pk_parse_key(&tls_conf->private_key,
+			data, len,
+			(const unsigned char *)pwd,
+			pwd ? os_strlen(pwd) : 0,
+			mbedtls_ctr_drbg_random,
+			tls_ctx_global.ctr_drbg);
+	  #else
+		ret = mbedtls_pk_parse_key(&tls_conf->private_key,
+			data, len,
+			(const unsigned char *)pwd,
+			pwd ? os_strlen(pwd) : 0);
+	  #endif
+		if (params->private_key) {
+			forced_memzero(data, len);
+			os_free(data);
+		}
+		if (ret < 0) {
+			elog(ret, "mbedtls_pk_parse_key");
+			return -1;
+		}
+		tls_conf->has_private_key = 1;
+	}
+
+	if (tls_conf->has_client_cert && tls_conf->has_private_key) {
+		ret = mbedtls_ssl_conf_own_cert(
+		    &tls_conf->conf, &tls_conf->client_cert, &tls_conf->private_key);
+		if (ret < 0) {
+			elog(ret, "mbedtls_ssl_conf_own_cert");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+/* mbedtls_x509_crt_profile_suiteb plus rsa_min_bitlen 2048 */
+/* (reference: see also mbedtls_x509_crt_profile_next) */
+/* ??? should permit SHA-512, too, and additional curves ??? */
+static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb128 =
+{
+    /* Only SHA-256 and 384 */
+    MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA256 ) |
+    MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
+    /* Only ECDSA */
+    MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ) |
+    MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECKEY ),
+#if defined(MBEDTLS_ECP_C)
+    /* Only NIST P-256 and P-384 */
+    MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP256R1 ) |
+    MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
+#else
+    0,
+#endif
+    2048,
+};
+
+
+/* stricter than mbedtls_x509_crt_profile_suiteb */
+/* (reference: see also mbedtls_x509_crt_profile_next) */
+/* ??? should permit SHA-512, too, and additional curves ??? */
+static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb192 =
+{
+    /* Only SHA-384 */
+    MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
+    /* Only ECDSA */
+    MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ) |
+    MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECKEY ),
+#if defined(MBEDTLS_ECP_C)
+    /* Only NIST P-384 */
+    MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
+#else
+    0,
+#endif
+    3072,
+};
+
+
+/* stricter than mbedtls_x509_crt_profile_suiteb except allow any PK alg */
+/* (reference: see also mbedtls_x509_crt_profile_next) */
+/* ??? should permit SHA-512, too, and additional curves ??? */
+static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb192_anypk =
+{
+    /* Only SHA-384 */
+    MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
+    0xFFFFFFF, /* Any PK alg    */
+#if defined(MBEDTLS_ECP_C)
+    /* Only NIST P-384 */
+    MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ),
+#else
+    0,
+#endif
+    3072,
+};
+
+
+static int tls_mbedtls_set_params(struct tls_conf *tls_conf,
+				  const struct tls_connection_params *params)
+{
+	tls_conf->flags = params->flags;
+
+	if (tls_conf->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
+		emsg(MSG_INFO, "ocsp=3 not supported");
+		return -1;
+	}
+
+	if (tls_conf->flags & TLS_CONN_REQUIRE_OCSP) {
+		emsg(MSG_INFO, "ocsp not supported");
+		return -1;
+	}
+
+	int suiteb128 = 0;
+	int suiteb192 = 0;
+	if (params->openssl_ciphers) {
+		if (os_strcmp(params->openssl_ciphers, "SUITEB192") == 0) {
+			suiteb192 = 1;
+			tls_conf->flags |= TLS_CONN_SUITEB;
+		}
+		if (os_strcmp(params->openssl_ciphers, "SUITEB128") == 0) {
+			suiteb128 = 1;
+			tls_conf->flags |= TLS_CONN_SUITEB;
+		}
+	}
+
+	int ret = mbedtls_ssl_config_defaults(
+	    &tls_conf->conf, tls_ctx_global.tls_conf ? MBEDTLS_SSL_IS_SERVER
+	                                             : MBEDTLS_SSL_IS_CLIENT,
+	    MBEDTLS_SSL_TRANSPORT_STREAM,
+	    (tls_conf->flags & TLS_CONN_SUITEB) ? MBEDTLS_SSL_PRESET_SUITEB
+	                                        : MBEDTLS_SSL_PRESET_DEFAULT);
+	if (ret != 0) {
+		elog(ret, "mbedtls_ssl_config_defaults");
+		return -1;
+	}
+
+	if (suiteb128) {
+		mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
+		                              &tls_mbedtls_crt_profile_suiteb128);
+		mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 2048);
+	}
+	else if (suiteb192) {
+		mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
+		                              &tls_mbedtls_crt_profile_suiteb192);
+		mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 3072);
+	}
+	else if (tls_conf->flags & TLS_CONN_SUITEB) {
+		/* treat as suiteb192 while allowing any PK algorithm */
+		mbedtls_ssl_conf_cert_profile(&tls_conf->conf,
+		                              &tls_mbedtls_crt_profile_suiteb192_anypk);
+		mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 3072);
+	}
+
+	tls_mbedtls_set_allowed_tls_vers(tls_conf, &tls_conf->conf);
+	ret = tls_mbedtls_set_certs(tls_conf, params);
+	if (ret != 0)
+		return -1;
+
+	if (params->dh_file
+	    && !tls_mbedtls_set_dhparams(tls_conf, params->dh_file)) {
+		return -1;
+	}
+
+	if (params->openssl_ecdh_curves
+	    && !tls_mbedtls_set_curves(tls_conf, params->openssl_ecdh_curves)) {
+		return -1;
+	}
+
+	if (params->openssl_ciphers) {
+		if (!tls_mbedtls_set_ciphers(tls_conf, params->openssl_ciphers))
+			return -1;
+	}
+	else if (tls_conf->flags & TLS_CONN_SUITEB) {
+		/* special-case a select set of ciphers for hwsim tests */
+		if (!tls_mbedtls_set_ciphers(tls_conf,
+		        (tls_conf->flags & TLS_CONN_SUITEB_NO_ECDH)
+		          ? "DHE-RSA-AES256-GCM-SHA384"
+		          : "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384"))
+			return -1;
+	}
+
+	return 0;
+}
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+			      const struct tls_connection_params *params)
+{
+	if (conn == NULL || params == NULL)
+		return -1;
+
+	tls_conf_deinit(conn->tls_conf);
+	struct tls_conf *tls_conf = conn->tls_conf = tls_conf_init(tls_ctx);
+	if (tls_conf == NULL)
+		return -1;
+
+	if (tls_ctx_global.tls_conf) {
+		tls_conf->check_crl = tls_ctx_global.tls_conf->check_crl;
+		tls_conf->check_crl_strict = tls_ctx_global.tls_conf->check_crl_strict;
+		/*(tls_openssl.c inherits check_cert_subject from global conf)*/
+		if (tls_ctx_global.tls_conf->check_cert_subject) {
+			tls_conf->check_cert_subject =
+			  os_strdup(tls_ctx_global.tls_conf->check_cert_subject);
+			if (tls_conf->check_cert_subject == NULL)
+				return -1;
+		}
+	}
+
+	if (tls_mbedtls_set_params(tls_conf, params) != 0)
+		return -1;
+	conn->verify_peer = tls_conf->verify_peer;
+
+	return tls_mbedtls_ssl_setup(conn);
+}
+
+
+#ifdef TLS_MBEDTLS_SESSION_TICKETS
+
+static int tls_mbedtls_clienthello_session_ticket_prep (struct tls_connection *conn,
+                                                        const u8 *data, size_t len)
+{
+	if (conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)
+		return -1;
+	if (conn->clienthello_session_ticket)
+		tls_connection_deinit_clienthello_session_ticket(conn);
+	if (len) {
+		conn->clienthello_session_ticket = mbedtls_calloc(1, len);
+		if (conn->clienthello_session_ticket == NULL)
+			return -1;
+		conn->clienthello_session_ticket_len = len;
+		os_memcpy(conn->clienthello_session_ticket, data, len);
+	}
+	return 0;
+}
+
+
+static void tls_mbedtls_clienthello_session_ticket_set (struct tls_connection *conn)
+{
+	mbedtls_ssl_session *sess = conn->ssl.MBEDTLS_PRIVATE(session_negotiate);
+	if (sess->MBEDTLS_PRIVATE(ticket)) {
+		mbedtls_platform_zeroize(sess->MBEDTLS_PRIVATE(ticket),
+		                         sess->MBEDTLS_PRIVATE(ticket_len));
+		mbedtls_free(sess->MBEDTLS_PRIVATE(ticket));
+	}
+	sess->MBEDTLS_PRIVATE(ticket) = conn->clienthello_session_ticket;
+	sess->MBEDTLS_PRIVATE(ticket_len) = conn->clienthello_session_ticket_len;
+	sess->MBEDTLS_PRIVATE(ticket_lifetime) = 86400;/* XXX: can hint be 0? */
+
+	conn->clienthello_session_ticket = NULL;
+	conn->clienthello_session_ticket_len = 0;
+}
+
+
+static int tls_mbedtls_ssl_ticket_write(void *p_ticket,
+                                        const mbedtls_ssl_session *session,
+                                        unsigned char *start,
+                                        const unsigned char *end,
+                                        size_t *tlen,
+                                        uint32_t *lifetime)
+{
+	struct tls_connection *conn = p_ticket;
+	if (conn && conn->session_ticket_cb) {
+		/* see tls_mbedtls_clienthello_session_ticket_prep() */
+		/* see tls_mbedtls_clienthello_session_ticket_set() */
+		return 0;
+	}
+
+	return mbedtls_ssl_ticket_write(&tls_ctx_global.ticket_ctx,
+	                                session, start, end, tlen, lifetime);
+}
+
+
+static int tls_mbedtls_ssl_ticket_parse(void *p_ticket,
+                                        mbedtls_ssl_session *session,
+                                        unsigned char *buf,
+                                        size_t len)
+{
+	/* XXX: TODO: not implemented in client;
+	 * mbedtls_ssl_conf_session_tickets_cb() callbacks only for TLS server*/
+
+	if (len == 0)
+		return MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+
+	struct tls_connection *conn = p_ticket;
+	if (conn && conn->session_ticket_cb) {
+		/* XXX: have random and secret been initialized yet?
+		 *      or must keys first be exported?
+		 *      EAP-FAST uses all args, EAP-TEAP only uses secret */
+		struct tls_random data;
+		if (tls_connection_get_random(NULL, conn, &data) != 0)
+			return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
+		int ret =
+		  conn->session_ticket_cb(conn->session_ticket_cb_ctx,
+		                          buf, len,
+		                          data.client_random,
+		                          data.server_random,
+		                          conn->expkey_secret);
+		if (ret == 1) {
+			conn->resumed = 1;
+			return 0;
+		}
+		emsg(MSG_ERROR, "EAP session ticket ext not implemented");
+		return MBEDTLS_ERR_SSL_INVALID_MAC;
+		/*(non-zero return used for mbedtls debug logging)*/
+	}
+
+	/* XXX: TODO always use tls_mbedtls_ssl_ticket_parse() for callback? */
+	int rc = mbedtls_ssl_ticket_parse(&tls_ctx_global.ticket_ctx,
+	                                  session, buf, len);
+	if (conn)
+		conn->resumed = (rc == 0);
+	return rc;
+}
+
+#endif /* TLS_MBEDTLS_SESSION_TICKETS */
+
+
+__attribute_cold__
+int tls_global_set_params(void *tls_ctx,
+			  const struct tls_connection_params *params)
+{
+	/* XXX: why might global_set_params be called more than once? */
+	if (tls_ctx_global.tls_conf)
+		tls_conf_deinit(tls_ctx_global.tls_conf);
+	tls_ctx_global.tls_conf = tls_conf_init(tls_ctx);
+	if (tls_ctx_global.tls_conf == NULL)
+		return -1;
+
+  #ifdef MBEDTLS_SSL_SESSION_TICKETS
+  #ifdef MBEDTLS_SSL_TICKET_C
+	if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET))
+	  #ifdef TLS_MBEDTLS_SESSION_TICKETS
+		mbedtls_ssl_conf_session_tickets_cb(&tls_ctx_global.tls_conf->conf,
+		                                    tls_mbedtls_ssl_ticket_write,
+		                                    tls_mbedtls_ssl_ticket_parse,
+		                                    NULL);
+	  #else
+		mbedtls_ssl_conf_session_tickets_cb(&tls_ctx_global.tls_conf->conf,
+		                                    mbedtls_ssl_ticket_write,
+		                                    mbedtls_ssl_ticket_parse,
+		                                    &tls_ctx_global.ticket_ctx);
+	  #endif
+  #endif
+  #endif
+
+	os_free(tls_ctx_global.ocsp_stapling_response);
+	tls_ctx_global.ocsp_stapling_response = NULL;
+	if (params->ocsp_stapling_response)
+		tls_ctx_global.ocsp_stapling_response =
+			os_strdup(params->ocsp_stapling_response);
+
+	os_free(tls_ctx_global.ca_cert_file);
+	tls_ctx_global.ca_cert_file = NULL;
+	if (params->ca_cert)
+		tls_ctx_global.ca_cert_file = os_strdup(params->ca_cert);
+	return tls_mbedtls_set_params(tls_ctx_global.tls_conf, params);
+}
+
+
+int tls_global_set_verify(void *tls_ctx, int check_crl, int strict)
+{
+	tls_ctx_global.tls_conf->check_crl = check_crl;
+	tls_ctx_global.tls_conf->check_crl_strict = strict; /*(time checks)*/
+	return 0;
+}
+
+
+int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
+			      int verify_peer, unsigned int flags,
+			      const u8 *session_ctx, size_t session_ctx_len)
+{
+	/*(EAP server-side calls this from eap_server_tls_ssl_init())*/
+	if (conn == NULL)
+		return -1;
+
+	conn->tls_conf->flags |= flags;/* TODO: reprocess flags, if necessary */
+
+	int authmode;
+	switch (verify_peer) {
+	case 2:  authmode = MBEDTLS_SSL_VERIFY_OPTIONAL; break;/*(eap_teap_init())*/
+	case 1:  authmode = MBEDTLS_SSL_VERIFY_REQUIRED; break;
+	default: authmode = MBEDTLS_SSL_VERIFY_NONE;     break;
+	}
+	mbedtls_ssl_set_hs_authmode(&conn->ssl, authmode);
+
+	if ((conn->verify_peer = (authmode != MBEDTLS_SSL_VERIFY_NONE)))
+		mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
+	else
+		mbedtls_ssl_set_verify(&conn->ssl, NULL, NULL);
+
+	return 0;
+}
+
+
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
+static void tls_connection_export_keys_cb(
+    void *p_expkey, mbedtls_ssl_key_export_type secret_type,
+    const unsigned char *secret, size_t secret_len,
+    const unsigned char client_random[MBEDTLS_EXPKEY_RAND_LEN],
+    const unsigned char server_random[MBEDTLS_EXPKEY_RAND_LEN],
+    mbedtls_tls_prf_types tls_prf_type)
+{
+	struct tls_connection *conn = p_expkey;
+	conn->tls_prf_type = tls_prf_type;
+	if (!tls_prf_type)
+		return;
+	if (secret_len > sizeof(conn->expkey_secret)) {
+		emsg(MSG_ERROR, "tls_connection_export_keys_cb secret too long");
+		conn->tls_prf_type = MBEDTLS_SSL_TLS_PRF_NONE; /* 0 */
+		return;
+	}
+	conn->expkey_secret_len = secret_len;
+	os_memcpy(conn->expkey_secret, secret, secret_len);
+	os_memcpy(conn->expkey_randbytes,
+	          client_random, MBEDTLS_EXPKEY_RAND_LEN);
+	os_memcpy(conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
+	          server_random, MBEDTLS_EXPKEY_RAND_LEN);
+}
+#elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
+static int tls_connection_export_keys_cb(
+    void *p_expkey,
+    const unsigned char *ms,
+    const unsigned char *kb,
+    size_t maclen,
+    size_t keylen,
+    size_t ivlen,
+    const unsigned char client_random[MBEDTLS_EXPKEY_RAND_LEN],
+    const unsigned char server_random[MBEDTLS_EXPKEY_RAND_LEN],
+    mbedtls_tls_prf_types tls_prf_type )
+{
+	struct tls_connection *conn = p_expkey;
+	conn->tls_prf_type = tls_prf_type;
+	if (!tls_prf_type)
+		return -1; /*(return value ignored by mbedtls)*/
+	conn->expkey_keyblock_size = maclen + keylen + ivlen;
+	conn->expkey_secret_len = MBEDTLS_EXPKEY_FIXED_SECRET_LEN;
+	os_memcpy(conn->expkey_secret, ms, MBEDTLS_EXPKEY_FIXED_SECRET_LEN);
+	os_memcpy(conn->expkey_randbytes,
+	          client_random, MBEDTLS_EXPKEY_RAND_LEN);
+	os_memcpy(conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
+	          server_random, MBEDTLS_EXPKEY_RAND_LEN);
+	return 0;
+}
+#endif
+
+
+int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
+			      struct tls_random *data)
+{
+	if (!conn || !conn->tls_prf_type)
+		return -1;
+	data->client_random = conn->expkey_randbytes;
+	data->client_random_len = MBEDTLS_EXPKEY_RAND_LEN;
+	data->server_random = conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN;
+	data->server_random_len = MBEDTLS_EXPKEY_RAND_LEN;
+	return 0;
+}
+
+
+int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
+			      const char *label, const u8 *context,
+			      size_t context_len, u8 *out, size_t out_len)
+{
+	/* (EAP-PEAP EAP-TLS EAP-TTLS) */
+  #if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
+	return (conn && conn->established && conn->tls_prf_type)
+	  ? mbedtls_ssl_tls_prf(conn->tls_prf_type,
+				conn->expkey_secret, conn->expkey_secret_len, label,
+				conn->expkey_randbytes,
+				sizeof(conn->expkey_randbytes), out, out_len)
+	  : -1;
+  #else
+	/* not implemented here for mbedtls < 2.18.0 */
+	return -1;
+  #endif
+}
+
+
+#ifdef TLS_MBEDTLS_EAP_FAST
+
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
+/* keyblock size info is not exposed in mbed TLS 3.0.0 */
+/* extracted from mbedtls library/ssl_tls.c:ssl_tls12_populate_transform() */
+#include <mbedtls/ssl_ciphersuites.h>
+#include <mbedtls/cipher.h>
+static size_t tls_mbedtls_ssl_keyblock_size (mbedtls_ssl_context *ssl)
+{
+  #if !defined(MBEDTLS_USE_PSA_CRYPTO) /* XXX: (not extracted for PSA crypto) */
+  #if defined(MBEDTLS_SSL_PROTO_TLS1_3)
+    if (mbedtls_ssl_get_version_number(ssl) == MBEDTLS_SSL_VERSION_TLS1_3)
+        return 0; /* (calculation not extracted) */
+  #endif /* MBEDTLS_SSL_PROTO_TLS1_3 */
+
+    int ciphersuite = mbedtls_ssl_get_ciphersuite_id_from_ssl(ssl);
+    const mbedtls_ssl_ciphersuite_t *ciphersuite_info =
+      mbedtls_ssl_ciphersuite_from_id(ciphersuite);
+    if (ciphersuite_info == NULL)
+        return 0;
+
+    const mbedtls_cipher_info_t *cipher_info =
+      mbedtls_cipher_info_from_type(ciphersuite_info->MBEDTLS_PRIVATE(cipher));
+    if (cipher_info == NULL)
+        return 0;
+
+  #if MBEDTLS_VERSION_NUMBER >= 0x03010000 /* mbedtls 3.1.0 */
+    size_t keylen = mbedtls_cipher_info_get_key_bitlen(cipher_info) / 8;
+    mbedtls_cipher_mode_t mode = mbedtls_cipher_info_get_mode(cipher_info);
+  #else
+    size_t keylen = cipher_info->MBEDTLS_PRIVATE(key_bitlen) / 8;
+    mbedtls_cipher_mode_t mode = cipher_info->MBEDTLS_PRIVATE(mode);
+  #endif
+  #if defined(MBEDTLS_GCM_C) || \
+      defined(MBEDTLS_CCM_C) || \
+      defined(MBEDTLS_CHACHAPOLY_C)
+    if (mode == MBEDTLS_MODE_GCM || mode == MBEDTLS_MODE_CCM)
+        return keylen + 4;
+    else if (mode == MBEDTLS_MODE_CHACHAPOLY)
+        return keylen + 12;
+    else
+  #endif /* MBEDTLS_GCM_C || MBEDTLS_CCM_C || MBEDTLS_CHACHAPOLY_C */
+  #if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC)
+    {
+        const mbedtls_md_info_t *md_info =
+          mbedtls_md_info_from_type(ciphersuite_info->MBEDTLS_PRIVATE(mac));
+        if (md_info == NULL)
+            return 0;
+        size_t mac_key_len = mbedtls_md_get_size(md_info);
+        size_t ivlen = mbedtls_cipher_info_get_iv_size(cipher_info);
+        return keylen + mac_key_len + ivlen;
+    }
+  #endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */
+  #endif /* !MBEDTLS_USE_PSA_CRYPTO *//* (not extracted for PSA crypto) */
+    return 0;
+}
+#endif /* MBEDTLS_VERSION_NUMBER >= 0x03000000 *//* mbedtls 3.0.0 */
+
+
+int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
+				    u8 *out, size_t out_len)
+{
+	/* XXX: has export keys callback been run? */
+	if (!conn || !conn->tls_prf_type)
+		return -1;
+
+  #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
+	conn->expkey_keyblock_size = tls_mbedtls_ssl_keyblock_size(&conn->ssl);
+	if (conn->expkey_keyblock_size == 0)
+		return -1;
+  #endif
+	size_t skip = conn->expkey_keyblock_size * 2;
+	unsigned char *tmp_out = os_malloc(skip + out_len);
+	if (!tmp_out)
+		return -1;
+
+	/* server_random and then client_random */
+	unsigned char seed[MBEDTLS_EXPKEY_RAND_LEN*2];
+	os_memcpy(seed, conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN,
+	          MBEDTLS_EXPKEY_RAND_LEN);
+	os_memcpy(seed + MBEDTLS_EXPKEY_RAND_LEN, conn->expkey_randbytes,
+	          MBEDTLS_EXPKEY_RAND_LEN);
+
+  #if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
+	int ret = mbedtls_ssl_tls_prf(conn->tls_prf_type,
+				      conn->expkey_secret, conn->expkey_secret_len,
+				      "key expansion", seed, sizeof(seed),
+				      tmp_out, skip + out_len);
+	if (ret == 0)
+		os_memcpy(out, tmp_out + skip, out_len);
+  #else
+	int ret = -1; /*(not reached if not impl; return -1 at top of func)*/
+  #endif
+
+	bin_clear_free(tmp_out, skip + out_len);
+	forced_memzero(seed, sizeof(seed));
+	return ret;
+}
+
+#endif /* TLS_MBEDTLS_EAP_FAST */
+
+
+__attribute_cold__
+static void tls_mbedtls_suiteb_handshake_alert (struct tls_connection *conn)
+{
+	/* tests/hwsim/test_suite_b.py test_suite_b_192_rsa_insufficient_dh */
+	if (!(conn->tls_conf->flags & TLS_CONN_SUITEB))
+		return;
+	if (tls_ctx_global.tls_conf) /*(is server; want issue event on client)*/
+		return;
+  #if 0
+	/*(info not available on client;
+         * mbed TLS library enforces dhm min bitlen in ServerKeyExchange)*/
+	if (MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ==
+	  #if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
+	          mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl)
+	  #else
+	          mbedtls_ssl_get_ciphersuite_id(
+	            mbedtls_ssl_get_ciphersuite(&conn->ssl))
+	  #endif
+	    && mbedtls_mpi_size(&conn->tls_conf->conf.MBEDTLS_PRIVATE(dhm_P))
+	         < 384 /*(3072/8)*/)
+  #endif
+	{
+		struct tls_config *init_conf = &tls_ctx_global.init_conf;
+		if (init_conf->event_cb) {
+			union tls_event_data ev;
+			os_memset(&ev, 0, sizeof(ev));
+			ev.alert.is_local = 1;
+			ev.alert.type = "fatal";
+			/*"internal error" string for tests/hwsim/test_suiteb.py */
+			ev.alert.description = "internal error: handshake failure";
+			/*ev.alert.description = "insufficient security";*/
+			init_conf->event_cb(init_conf->cb_ctx, TLS_ALERT, &ev);
+		}
+	}
+}
+
+
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+					 struct tls_connection *conn,
+					 const struct wpabuf *in_data,
+					 struct wpabuf **appl_data)
+{
+	if (appl_data)
+		*appl_data = NULL;
+
+	if (in_data && wpabuf_len(in_data)) {
+		/*(unsure why tls_gnutls.c discards buffer contents; skip here)*/
+		if (conn->pull_buf && 0) /* disable; appears unwise */
+			tls_pull_buf_discard(conn, __func__);
+		if (!tls_pull_buf_append(conn, in_data))
+			return NULL;
+	}
+
+	if (conn->tls_conf == NULL) {
+		struct tls_connection_params params;
+		os_memset(&params, 0, sizeof(params));
+		params.openssl_ciphers =
+		  tls_ctx_global.init_conf.openssl_ciphers;
+		params.flags = tls_ctx_global.tls_conf->flags;
+		if (tls_connection_set_params(tls_ctx, conn, &params) != 0)
+			return NULL;
+	}
+
+	if (conn->verify_peer) /*(call here might be redundant; nbd)*/
+		mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn);
+
+  #ifdef TLS_MBEDTLS_SESSION_TICKETS
+	if (conn->clienthello_session_ticket)
+		/*(starting handshake for EAP-FAST and EAP-TEAP)*/
+		tls_mbedtls_clienthello_session_ticket_set(conn);
+
+	/* (not thread-safe due to need to set userdata 'conn' for callback) */
+	/* (unable to use mbedtls_ssl_set_user_data_p() with mbedtls 3.2.0+
+	 *  since ticket write and parse callbacks take (mbedtls_ssl_session *)
+	 *  param instead of (mbedtls_ssl_context *) param) */
+	if (conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)
+		mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
+		                                    NULL, NULL, NULL);
+	else
+		mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
+		                                    tls_mbedtls_ssl_ticket_write,
+		                                    tls_mbedtls_ssl_ticket_parse,
+		                                    conn);
+  #endif
+
+  #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */
+	int ret = mbedtls_ssl_handshake(&conn->ssl);
+  #else
+	int ret = 0;
+	while (conn->ssl.MBEDTLS_PRIVATE(state) != MBEDTLS_SSL_HANDSHAKE_OVER) {
+		ret = mbedtls_ssl_handshake_step(&conn->ssl);
+		if (ret != 0)
+			break;
+	}
+  #endif
+
+  #ifdef TLS_MBEDTLS_SESSION_TICKETS
+	mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf,
+	                                    tls_mbedtls_ssl_ticket_write,
+	                                    tls_mbedtls_ssl_ticket_parse,
+	                                    NULL);
+  #endif
+
+	switch (ret) {
+	case 0:
+		conn->established = 1;
+		if (conn->push_buf == NULL)
+			/* Need to return something to get final TLS ACK. */
+			conn->push_buf = wpabuf_alloc(0);
+
+		if (appl_data /*&& conn->pull_buf && wpabuf_len(conn->pull_buf)*/)
+			*appl_data = NULL; /* RFE: check for application data */
+		break;
+	case MBEDTLS_ERR_SSL_WANT_WRITE:
+	case MBEDTLS_ERR_SSL_WANT_READ:
+	case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS:
+	case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS:
+		if (tls_ctx_global.tls_conf /*(is server)*/
+		    && conn->established && conn->push_buf == NULL)
+			/* Need to return something to trigger completion of EAP-TLS. */
+			conn->push_buf = wpabuf_alloc(0);
+		break;
+	default:
+		++conn->failed;
+		switch (ret) {
+		case MBEDTLS_ERR_SSL_CLIENT_RECONNECT:
+		case MBEDTLS_ERR_NET_CONN_RESET:
+		case MBEDTLS_ERR_NET_SEND_FAILED:
+			++conn->write_alerts;
+			break;
+	      #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */
+		case MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE:
+	      #else
+		case MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE:
+	      #endif
+			tls_mbedtls_suiteb_handshake_alert(conn);
+			/* fall through */
+		case MBEDTLS_ERR_NET_RECV_FAILED:
+		case MBEDTLS_ERR_SSL_CONN_EOF:
+		case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
+		case MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE:
+			++conn->read_alerts;
+			break;
+		default:
+			break;
+		}
+
+		ilog(ret, "mbedtls_ssl_handshake");
+		break;
+	}
+
+	struct wpabuf *out_data = conn->push_buf;
+	conn->push_buf = NULL;
+	return out_data;
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+						struct tls_connection *conn,
+						const struct wpabuf *in_data,
+						struct wpabuf **appl_data)
+{
+	conn->is_server = 1;
+	return tls_connection_handshake(tls_ctx, conn, in_data, appl_data);
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data)
+{
+	int res = mbedtls_ssl_write(&conn->ssl,
+	                            wpabuf_head_u8(in_data), wpabuf_len(in_data));
+	if (res < 0) {
+		elog(res, "mbedtls_ssl_write");
+		return NULL;
+	}
+
+	struct wpabuf *buf = conn->push_buf;
+	conn->push_buf = NULL;
+	return buf;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data)
+{
+	int res;
+	struct wpabuf *out;
+
+	/*assert(in_data != NULL);*/
+	if (!tls_pull_buf_append(conn, in_data))
+		return NULL;
+
+  #if defined(MBEDTLS_ZLIB_SUPPORT) /* removed in mbedtls 3.x */
+	/* Add extra buffer space to handle the possibility of decrypted
+	 * data being longer than input data due to TLS compression. */
+	out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
+  #else /* TLS compression is disabled in mbedtls 3.x */
+	out = wpabuf_alloc(wpabuf_len(in_data));
+  #endif
+	if (out == NULL)
+		return NULL;
+
+	res = mbedtls_ssl_read(&conn->ssl, wpabuf_mhead(out), wpabuf_size(out));
+	if (res < 0) {
+	  #if 1 /*(seems like a different error if wpabuf_len(in_data) == 0)*/
+		if (res == MBEDTLS_ERR_SSL_WANT_READ)
+			return out;
+	  #endif
+		elog(res, "mbedtls_ssl_read");
+		wpabuf_free(out);
+		return NULL;
+	}
+	wpabuf_put(out, res);
+
+	return out;
+}
+
+
+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
+{
+	/* XXX: might need to detect if session resumed from TLS session ticket
+	 * even if not special session ticket handling for EAP-FAST, EAP-TEAP */
+	/* (?ssl->handshake->resume during session ticket validation?) */
+	return conn && conn->resumed;
+}
+
+
+#ifdef TLS_MBEDTLS_EAP_FAST
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+				   u8 *ciphers)
+{
+	/* ciphers is list of TLS_CIPHER_* from hostap/src/crypto/tls.h */
+	int ids[7];
+	const int idsz = (int)sizeof(ids);
+	int nids = -1, id;
+	for ( ; *ciphers != TLS_CIPHER_NONE; ++ciphers) {
+		switch (*ciphers) {
+		case TLS_CIPHER_RC4_SHA:
+		  #ifdef MBEDTLS_TLS_RSA_WITH_RC4_128_SHA
+			id = MBEDTLS_TLS_RSA_WITH_RC4_128_SHA;
+			break;
+		  #else
+			continue; /*(not supported in mbedtls 3.x; ignore)*/
+		  #endif
+		case TLS_CIPHER_AES128_SHA:
+			id = MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA;
+			break;
+		case TLS_CIPHER_RSA_DHE_AES128_SHA:
+			id = MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
+			break;
+		case TLS_CIPHER_ANON_DH_AES128_SHA:
+			continue; /*(not supported in mbedtls; ignore)*/
+		case TLS_CIPHER_RSA_DHE_AES256_SHA:
+			id = MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
+			break;
+		case TLS_CIPHER_AES256_SHA:
+			id = MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA;
+			break;
+		default:
+			return -1; /* should not happen */
+		}
+		if (++nids == idsz)
+			return -1; /* should not happen */
+		ids[nids] = id;
+	}
+	if (nids < 0)
+		return 0; /* nothing to do */
+	if (++nids == idsz)
+		return -1; /* should not happen */
+	ids[nids] = 0; /* terminate list */
+	++nids;
+
+	return tls_mbedtls_set_ciphersuites(conn->tls_conf, ids, nids) ? 0 : -1;
+}
+#endif
+
+
+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
+		    char *buf, size_t buflen)
+{
+	if (conn == NULL)
+		return -1;
+	os_strlcpy(buf, mbedtls_ssl_get_version(&conn->ssl), buflen);
+	return buf[0] != 'u' ? 0 : -1; /*(-1 if "unknown")*/
+}
+
+
+#ifdef TLS_MBEDTLS_EAP_TEAP
+u16 tls_connection_get_cipher_suite(struct tls_connection *conn)
+{
+	if (conn == NULL)
+		return 0;
+	return (u16)mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl);
+}
+#endif
+
+
+int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
+		   char *buf, size_t buflen)
+{
+	if (conn == NULL)
+		return -1;
+	const int id = mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl);
+	return tls_mbedtls_translate_ciphername(id, buf, buflen) ? 0 : -1;
+}
+
+
+#ifdef TLS_MBEDTLS_SESSION_TICKETS
+
+int tls_connection_enable_workaround(void *tls_ctx,
+				     struct tls_connection *conn)
+{
+	/* (see comment in src/eap_peer/eap_fast.c:eap_fast_init()) */
+	/* XXX: is there a relevant setting for this in mbed TLS? */
+	/* (do we even care that much about older CBC ciphers?) */
+	return 0;
+}
+
+
+int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
+				    int ext_type, const u8 *data,
+				    size_t data_len)
+{
+	/* (EAP-FAST and EAP-TEAP) */
+	if (ext_type == MBEDTLS_TLS_EXT_SESSION_TICKET) /*(ext_type == 35)*/
+		return tls_mbedtls_clienthello_session_ticket_prep(conn, data,
+		                                                   data_len);
+
+	return -1;
+}
+
+#endif /* TLS_MBEDTLS_SESSION_TICKETS */
+
+
+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
+{
+	return conn ? conn->failed : -1;
+}
+
+
+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
+{
+	return conn ? conn->read_alerts : -1;
+}
+
+
+int tls_connection_get_write_alerts(void *tls_ctx,
+				    struct tls_connection *conn)
+{
+	return conn ? conn->write_alerts : -1;
+}
+
+
+#ifdef TLS_MBEDTLS_SESSION_TICKETS
+int tls_connection_set_session_ticket_cb(
+	void *tls_ctx, struct tls_connection *conn,
+	tls_session_ticket_cb cb, void *ctx)
+{
+	if (!(conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)) {
+		/* (EAP-FAST and EAP-TEAP) */
+		conn->session_ticket_cb = cb;
+		conn->session_ticket_cb_ctx = ctx;
+		return 0;
+	}
+	return -1;
+}
+#endif
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+  #ifndef MBEDTLS_VERSION_C
+	const char * const ver = "n/a";
+  #else
+	char ver[9];
+	mbedtls_version_get_string(ver);
+  #endif
+	return os_snprintf(buf, buf_len,
+	                   "mbed TLS build=" MBEDTLS_VERSION_STRING " run=%s", ver);
+}
+
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+				     struct wpabuf *data)
+{
+	wpabuf_free(conn->success_data);
+	conn->success_data = data;
+}
+
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
+{
+}
+
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn)
+{
+	return conn->success_data;
+}
+
+
+void tls_connection_remove_session(struct tls_connection *conn)
+{
+}
+
+
+#ifdef TLS_MBEDTLS_EAP_TEAP
+int tls_get_tls_unique(struct tls_connection *conn, u8 *buf, size_t max_len)
+{
+  #if defined(MBEDTLS_SSL_RENEGOTIATION) /* XXX: renegotiation or resumption? */
+	/* data from TLS handshake Finished message */
+	size_t verify_len = conn->ssl.MBEDTLS_PRIVATE(verify_data_len);
+	char *verify_data = (conn->is_server ^ conn->resumed)
+	  ? conn->ssl.MBEDTLS_PRIVATE(peer_verify_data)
+	  : conn->ssl.MBEDTLS_PRIVATE(own_verify_data);
+	if (verify_len && verify_len <= max_len) {
+		os_memcpy(buf, verify_data, verify_len);
+		return (int)verify_len;
+	}
+  #endif
+	return -1;
+}
+#endif
+
+
+__attribute_noinline__
+static void tls_mbedtls_set_peer_subject(struct tls_connection *conn, const mbedtls_x509_crt *crt)
+{
+	if (conn->peer_subject)
+		return;
+	char buf[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
+	int buflen = mbedtls_x509_dn_gets(buf, sizeof(buf), &crt->subject);
+	if (buflen >= 0 && (conn->peer_subject = os_malloc((size_t)buflen+1)))
+		os_memcpy(conn->peer_subject, buf, (size_t)buflen+1);
+}
+
+
+#ifdef TLS_MBEDTLS_EAP_TEAP
+const char * tls_connection_get_peer_subject(struct tls_connection *conn)
+{
+	if (!conn)
+		return NULL;
+	if (!conn->peer_subject) { /*(if not set during cert verify)*/
+		const mbedtls_x509_crt *peer_cert =
+		  mbedtls_ssl_get_peer_cert(&conn->ssl);
+		if (peer_cert)
+			tls_mbedtls_set_peer_subject(conn, peer_cert);
+	}
+	return conn->peer_subject;
+}
+#endif
+
+
+#ifdef TLS_MBEDTLS_EAP_TEAP
+bool tls_connection_get_own_cert_used(struct tls_connection *conn)
+{
+	/* XXX: availability of cert does not necessary mean that client
+	 * received certificate request from server and then sent cert.
+	 * ? step handshake in tls_connection_handshake() looking for
+	 *   MBEDTLS_SSL_CERTIFICATE_REQUEST ? */
+	const struct tls_conf * const tls_conf = conn->tls_conf;
+	return (tls_conf->has_client_cert && tls_conf->has_private_key);
+}
+#endif
+
+
+#if defined(CONFIG_FIPS)
+#define TLS_MBEDTLS_CONFIG_FIPS
+#endif
+
+#if defined(CONFIG_SHA256)
+#define TLS_MBEDTLS_TLS_PRF_SHA256
+#endif
+
+#if defined(CONFIG_SHA384)
+#define TLS_MBEDTLS_TLS_PRF_SHA384
+#endif
+
+
+#ifndef TLS_MBEDTLS_CONFIG_FIPS
+#if defined(CONFIG_MODULE_TESTS)
+/* unused with CONFIG_TLS=mbedtls except in crypto_module_tests.c */
+#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */ \
+ && MBEDTLS_VERSION_NUMBER <  0x03000000 /* mbedtls 3.0.0 */
+/* sha1-tlsprf.c */
+#include "sha1.h"
+int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label,
+		     const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
+{
+	return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_TLS1,
+				   secret, secret_len, label,
+				   seed, seed_len, out, outlen) ? -1 : 0;
+}
+#else
+#include "sha1-tlsprf.c" /* pull in hostap local implementation */
+#endif
+#endif
+#endif
+
+#ifdef TLS_MBEDTLS_TLS_PRF_SHA256
+/* sha256-tlsprf.c */
+#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
+#include "sha256.h"
+int tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label,
+		   const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
+{
+	return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_SHA256,
+				   secret, secret_len, label,
+				   seed, seed_len, out, outlen) ? -1 : 0;
+}
+#else
+#include "sha256-tlsprf.c" /* pull in hostap local implementation */
+#endif
+#endif
+
+#ifdef TLS_MBEDTLS_TLS_PRF_SHA384
+/* sha384-tlsprf.c */
+#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */
+#include "sha384.h"
+int tls_prf_sha384(const u8 *secret, size_t secret_len, const char *label,
+		   const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
+{
+	return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_SHA384,
+				   secret, secret_len, label,
+				   seed, seed_len, out, outlen) ? -1 : 0;
+}
+#else
+#include "sha384-tlsprf.c" /* pull in hostap local implementation */
+#endif
+#endif
+
+
+#if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */
+#define mbedtls_x509_crt_has_ext_type(crt, ext_type) \
+        ((crt)->MBEDTLS_PRIVATE(ext_types) & (ext_type))
+#endif
+
+struct mlist { const char *p; size_t n; };
+
+
+static int
+tls_mbedtls_match_altsubject(mbedtls_x509_crt *crt, const char *match)
+{
+	/* RFE: this could be pre-parsed into structured data at config time */
+	struct mlist list[256]; /*(much larger than expected)*/
+	int nlist = 0;
+	if (   os_strncmp(match, "EMAIL:", 6) != 0
+	    && os_strncmp(match, "DNS:",   4) != 0
+	    && os_strncmp(match, "URI:",   4) != 0   ) {
+		wpa_printf(MSG_INFO, "MTLS: Invalid altSubjectName match '%s'", match);
+		return 0;
+	}
+	for (const char *s = match, *tok; *s; s = tok ? tok+1 : "") {
+		do { } while ((tok = os_strchr(s, ';'))
+		              && os_strncmp(tok+1, "EMAIL:", 6) != 0
+		              && os_strncmp(tok+1, "DNS:",   4) != 0
+		              && os_strncmp(tok+1, "URI:",   4) != 0);
+		list[nlist].p = s;
+		list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
+		if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
+			wpa_printf(MSG_INFO, "MTLS: excessive altSubjectName match '%s'",
+			           match);
+			break; /* truncate huge list and continue */
+		}
+	}
+
+	if (!mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME))
+		return 0;
+
+	const mbedtls_x509_sequence *cur = &crt->subject_alt_names;
+	for (; cur != NULL; cur = cur->next) {
+		const unsigned char san_type = (unsigned char)cur->buf.tag
+		                             & MBEDTLS_ASN1_TAG_VALUE_MASK;
+		char t;
+		size_t step = 4;
+		switch (san_type) {             /* "EMAIL:" or "DNS:" or "URI:" */
+		case MBEDTLS_X509_SAN_RFC822_NAME:       step = 6; t = 'E'; break;
+		case MBEDTLS_X509_SAN_DNS_NAME:                    t = 'D'; break;
+		case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: t = 'U'; break;
+		default: continue;
+		}
+
+		for (int i = 0; i < nlist; ++i) {
+			/* step over "EMAIL:" or "DNS:" or "URI:" in list[i].p */
+			/* Note: v is not '\0'-terminated, but is a known length vlen,
+			 * so okay to pass to os_strncasecmp() even though not z-string */
+			if (cur->buf.len == list[i].n - step && t == *list[i].p
+			    && 0 == os_strncasecmp((char *)cur->buf.p,
+			                           list[i].p+step, cur->buf.len)) {
+				return 1; /* match */
+			}
+		}
+	}
+	return 0; /* no match */
+}
+
+
+static int
+tls_mbedtls_match_suffix(const char *v, size_t vlen,
+                         const struct mlist *list, int nlist, int full)
+{
+	/* Note: v is not '\0'-terminated, but is a known length vlen,
+	 * so okay to pass to os_strncasecmp() even though not z-string */
+	for (int i = 0; i < nlist; ++i) {
+		size_t n = list[i].n;
+		if ((n == vlen || (n < vlen && v[vlen-n-1] == '.' && !full))
+		    && 0 == os_strncasecmp(v+vlen-n, list[i].p, n))
+			return 1; /* match */
+	}
+	return 0; /* no match */
+}
+
+
+static int
+tls_mbedtls_match_suffixes(mbedtls_x509_crt *crt, const char *match, int full)
+{
+	/* RFE: this could be pre-parsed into structured data at config time */
+	struct mlist list[256]; /*(much larger than expected)*/
+	int nlist = 0;
+	for (const char *s = match, *tok; *s; s = tok ? tok+1 : "") {
+		tok = os_strchr(s, ';');
+		list[nlist].p = s;
+		list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
+		if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
+			wpa_printf(MSG_INFO, "MTLS: excessive suffix match '%s'", match);
+			break; /* truncate huge list and continue */
+		}
+	}
+
+	/* check subjectAltNames */
+	if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME)) {
+		const mbedtls_x509_sequence *cur = &crt->subject_alt_names;
+		for (; cur != NULL; cur = cur->next) {
+			const unsigned char san_type = (unsigned char)cur->buf.tag
+			                             & MBEDTLS_ASN1_TAG_VALUE_MASK;
+			if (san_type == MBEDTLS_X509_SAN_DNS_NAME
+			    && tls_mbedtls_match_suffix((char *)cur->buf.p,
+			                                cur->buf.len,
+			                                list, nlist, full)) {
+				return 1; /* match */
+			}
+		}
+	}
+
+	/* check subject CN */
+	const mbedtls_x509_name *name = &crt->subject;
+	for (; name != NULL; name = name->next) {
+		if (name->oid.p && MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &name->oid) == 0)
+			break;
+	}
+	if (name && tls_mbedtls_match_suffix((char *)name->val.p, name->val.len,
+	                                     list, nlist, full)) {
+		return 1; /* match */
+	}
+
+	return 0; /* no match */
+}
+
+
+static int
+tls_mbedtls_match_dn_field(mbedtls_x509_crt *crt, const char *match)
+{
+	/* RFE: this could be pre-parsed into structured data at config time */
+	struct mlistoid { const char *p; size_t n;
+	                  const char *oid; size_t olen;
+	                  int prefix; };
+	struct mlistoid list[32]; /*(much larger than expected)*/
+	int nlist = 0;
+	for (const char *s = match, *tok, *e; *s; s = tok ? tok+1 : "") {
+		tok = os_strchr(s, '/');
+		list[nlist].oid = NULL;
+		list[nlist].olen = 0;
+		list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s);
+		e = memchr(s, '=', list[nlist].n);
+		if (e == NULL) {
+			if (list[nlist].n == 0)
+				continue; /* skip consecutive, repeated '/' */
+			if (list[nlist].n == 1 && *s == '*') {
+				/* special-case "*" to match any OID and value */
+				s = e = "=*";
+				list[nlist].n = 2;
+				list[nlist].oid = "";
+			}
+			else {
+				wpa_printf(MSG_INFO,
+				           "MTLS: invalid check_cert_subject '%s' missing '='",
+				           match);
+				return 0;
+			}
+		}
+		switch (e - s) {
+		case 1:
+			if (*s == 'C') {
+				list[nlist].oid  = MBEDTLS_OID_AT_COUNTRY;
+				list[nlist].olen = sizeof(MBEDTLS_OID_AT_COUNTRY)-1;
+			}
+			else if (*s == 'L') {
+				list[nlist].oid  = MBEDTLS_OID_AT_LOCALITY;
+				list[nlist].olen = sizeof(MBEDTLS_OID_AT_LOCALITY)-1;
+			}
+			else if (*s == 'O') {
+				list[nlist].oid  = MBEDTLS_OID_AT_ORGANIZATION;
+				list[nlist].olen = sizeof(MBEDTLS_OID_AT_ORGANIZATION)-1;
+			}
+			break;
+		case 2:
+			if (s[0] == 'C' && s[1] == 'N') {
+				list[nlist].oid  = MBEDTLS_OID_AT_CN;
+				list[nlist].olen = sizeof(MBEDTLS_OID_AT_CN)-1;
+			}
+			else if (s[0] == 'S' && s[1] == 'T') {
+				list[nlist].oid  = MBEDTLS_OID_AT_STATE;
+				list[nlist].olen = sizeof(MBEDTLS_OID_AT_STATE)-1;
+			}
+			else if (s[0] == 'O' && s[1] == 'U') {
+				list[nlist].oid  = MBEDTLS_OID_AT_ORG_UNIT;
+				list[nlist].olen = sizeof(MBEDTLS_OID_AT_ORG_UNIT)-1;
+			}
+			break;
+		case 12:
+			if (os_memcmp(s, "emailAddress", 12) == 0) {
+				list[nlist].oid  = MBEDTLS_OID_PKCS9_EMAIL;
+				list[nlist].olen = sizeof(MBEDTLS_OID_PKCS9_EMAIL)-1;
+			}
+			break;
+		default:
+			break;
+		}
+		if (list[nlist].oid == NULL) {
+			wpa_printf(MSG_INFO,
+			           "MTLS: Unknown field in check_cert_subject '%s'",
+			           match);
+			return 0;
+		}
+		list[nlist].n -= (size_t)(++e - s);
+		list[nlist].p = e;
+		if (list[nlist].n && e[list[nlist].n-1] == '*') {
+			--list[nlist].n;
+			list[nlist].prefix = 1;
+		}
+		/*(could easily add support for suffix matches if value begins with '*',
+		 * but suffix match is not currently supported by other TLS modules)*/
+
+		if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) {
+			wpa_printf(MSG_INFO,
+			           "MTLS: excessive check_cert_subject match '%s'",
+			           match);
+			break; /* truncate huge list and continue */
+		}
+	}
+
+	/* each component in match string must match cert Subject in order listed
+	 * The behavior below preserves ordering but is slightly different than
+	 * the grossly inefficient contortions implemented in tls_openssl.c */
+	const mbedtls_x509_name *name = &crt->subject;
+	for (int i = 0; i < nlist; ++i) {
+		int found = 0;
+		for (; name != NULL && !found; name = name->next) {
+			if (!name->oid.p)
+				continue;
+			/* special-case "*" to match any OID and value */
+			if (list[i].olen == 0) {
+				found = 1;
+				continue;
+			}
+			/* perform equalent of !MBEDTLS_OID_CMP() with oid ptr and len */
+			if (list[i].olen != name->oid.len
+			    || os_memcmp(list[i].oid, name->oid.p, name->oid.len) != 0)
+				continue;
+			/* Note: v is not '\0'-terminated, but is a known length vlen,
+			 * so okay to pass to os_strncasecmp() even though not z-string */
+			if ((list[i].prefix
+			      ? list[i].n <= name->val.len  /* prefix match */
+			      : list[i].n == name->val.len) /* full match */
+			    && 0 == os_strncasecmp((char *)name->val.p,
+			                           list[i].p, list[i].n)) {
+				found = 1;
+				continue;
+			}
+		}
+		if (!found)
+			return 0; /* no match */
+	}
+	return 1; /* match */
+}
+
+
+__attribute_cold__
+static void
+tls_mbedtls_verify_fail_event (mbedtls_x509_crt *crt, int depth,
+                               const char *errmsg, enum tls_fail_reason reason)
+{
+	struct tls_config *init_conf = &tls_ctx_global.init_conf;
+	if (init_conf->event_cb == NULL)
+		return;
+
+	struct wpabuf *certbuf = wpabuf_alloc_copy(crt->raw.p, crt->raw.len);
+	char subject[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
+	if (mbedtls_x509_dn_gets(subject, sizeof(subject), &crt->subject) < 0)
+		subject[0] = '\0';
+	union tls_event_data ev;
+	os_memset(&ev, 0, sizeof(ev));
+	ev.cert_fail.reason = reason;
+	ev.cert_fail.depth = depth;
+	ev.cert_fail.subject = subject;
+	ev.cert_fail.reason_txt = errmsg;
+	ev.cert_fail.cert = certbuf;
+
+	init_conf->event_cb(init_conf->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
+
+	wpabuf_free(certbuf);
+}
+
+
+__attribute_noinline__
+static void
+tls_mbedtls_verify_cert_event (struct tls_connection *conn,
+                               mbedtls_x509_crt *crt, int depth)
+{
+	struct tls_config *init_conf = &tls_ctx_global.init_conf;
+	if (init_conf->event_cb == NULL)
+		return;
+
+	struct wpabuf *certbuf = NULL;
+	union tls_event_data ev;
+	os_memset(&ev, 0, sizeof(ev));
+
+  #ifdef MBEDTLS_SHA256_C
+	u8 hash[SHA256_DIGEST_LENGTH];
+	const u8 *addr[] = { (u8 *)crt->raw.p };
+	if (sha256_vector(1, addr, &crt->raw.len, hash) == 0) {
+		ev.peer_cert.hash = hash;
+		ev.peer_cert.hash_len = sizeof(hash);
+	}
+  #endif
+	ev.peer_cert.depth = depth;
+	char subject[MBEDTLS_X509_MAX_DN_NAME_SIZE*2];
+	if (depth == 0)
+		ev.peer_cert.subject = conn->peer_subject;
+	if (ev.peer_cert.subject == NULL) {
+		ev.peer_cert.subject = subject;
+		if (mbedtls_x509_dn_gets(subject, sizeof(subject), &crt->subject) < 0)
+			subject[0] = '\0';
+	}
+
+	char serial_num[128+1];
+	ev.peer_cert.serial_num =
+	  tls_mbedtls_peer_serial_num(crt, serial_num, sizeof(serial_num));
+
+	const mbedtls_x509_sequence *cur;
+
+	cur = NULL;
+	if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME))
+		cur = &crt->subject_alt_names;
+	for (; cur != NULL; cur = cur->next) {
+		const unsigned char san_type = (unsigned char)cur->buf.tag
+		                             & MBEDTLS_ASN1_TAG_VALUE_MASK;
+		size_t prelen = 4;
+		const char *pre;
+		switch (san_type) {
+		case MBEDTLS_X509_SAN_RFC822_NAME:     prelen = 6; pre = "EMAIL:";break;
+		case MBEDTLS_X509_SAN_DNS_NAME:                    pre = "DNS:";  break;
+		case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: pre = "URI:";  break;
+		default: continue;
+		}
+
+		char *pos = os_malloc(prelen + cur->buf.len + 1);
+		if (pos == NULL)
+			break;
+		ev.peer_cert.altsubject[ev.peer_cert.num_altsubject] = pos;
+		os_memcpy(pos, pre, prelen);
+		/* data should be properly backslash-escaped if needed,
+		 * so code below does not re-escape, but does replace CTLs */
+		/*os_memcpy(pos+prelen, cur->buf.p, cur->buf.len);*/
+		/*pos[prelen+cur->buf.len] = '\0';*/
+		pos += prelen;
+		for (size_t i = 0; i < cur->buf.len; ++i) {
+			unsigned char c = cur->buf.p[i];
+			*pos++ = (c >= 32 && c != 127) ? c : '?';
+		}
+		*pos = '\0';
+
+		if (++ev.peer_cert.num_altsubject == TLS_MAX_ALT_SUBJECT)
+			break;
+	}
+
+	cur = NULL;
+	if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_CERTIFICATE_POLICIES))
+		cur = &crt->certificate_policies;
+	for (; cur != NULL; cur = cur->next) {
+		if (cur->buf.len != 11) /* len of OID_TOD_STRICT or OID_TOD_TOFU */
+			continue;
+		/* TOD-STRICT "1.3.6.1.4.1.40808.1.3.1" */
+		/* TOD-TOFU   "1.3.6.1.4.1.40808.1.3.2" */
+		#define OID_TOD_STRICT "\x2b\x06\x01\x04\x01\x82\xbe\x68\x01\x03\x01"
+		#define OID_TOD_TOFU   "\x2b\x06\x01\x04\x01\x82\xbe\x68\x01\x03\x02"
+		if (os_memcmp(cur->buf.p,
+		              OID_TOD_STRICT, sizeof(OID_TOD_STRICT)-1) == 0) {
+			ev.peer_cert.tod = 1; /* TOD-STRICT */
+			break;
+		}
+		if (os_memcmp(cur->buf.p,
+		              OID_TOD_TOFU, sizeof(OID_TOD_TOFU)-1) == 0) {
+			ev.peer_cert.tod = 2; /* TOD-TOFU */
+			break;
+		}
+	}
+
+	struct tls_conf *tls_conf = conn->tls_conf;
+	if (tls_conf->ca_cert_probe || (tls_conf->flags & TLS_CONN_EXT_CERT_CHECK)
+	    || init_conf->cert_in_cb) {
+		certbuf = wpabuf_alloc_copy(crt->raw.p, crt->raw.len);
+		ev.peer_cert.cert = certbuf;
+	}
+
+	init_conf->event_cb(init_conf->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
+
+	wpabuf_free(certbuf);
+	char **altsubject;
+	*(const char ***)&altsubject = ev.peer_cert.altsubject;
+	for (size_t i = 0; i < ev.peer_cert.num_altsubject; ++i)
+		os_free(altsubject[i]);
+}
+
+
+static int
+tls_mbedtls_verify_cb (void *arg, mbedtls_x509_crt *crt, int depth, uint32_t *flags)
+{
+	/* XXX: N.B. verify code not carefully tested besides hwsim tests
+	 *
+	 * RFE: mbedtls_x509_crt_verify_info() and enhance log trace messages
+	 * RFE: review and add support for additional TLS_CONN_* flags
+	 * not handling OCSP (not available in mbedtls)
+	 * ... */
+
+	struct tls_connection *conn = (struct tls_connection *)arg;
+	struct tls_conf *tls_conf = conn->tls_conf;
+	uint32_t flags_in = *flags;
+
+	if (depth > 8) { /*(depth 8 picked as arbitrary limit)*/
+		emsg(MSG_WARNING, "client cert chain too long");
+		*flags |= MBEDTLS_X509_BADCERT_OTHER; /* cert chain too long */
+		tls_mbedtls_verify_fail_event(crt, depth,
+			                      "client cert chain too long",
+		                              TLS_FAIL_BAD_CERTIFICATE);
+	}
+	else if (tls_conf->verify_depth0_only) {
+		if (depth > 0)
+			*flags = 0;
+		else {
+		  #ifdef MBEDTLS_SHA256_C
+			u8 hash[SHA256_DIGEST_LENGTH];
+			const u8 *addr[] = { (u8 *)crt->raw.p };
+			if (sha256_vector(1, addr, &crt->raw.len, hash) < 0
+			    || os_memcmp(tls_conf->ca_cert_hash, hash, sizeof(hash)) != 0) {
+				*flags |= MBEDTLS_X509_BADCERT_NOT_TRUSTED;
+				tls_mbedtls_verify_fail_event(crt, depth,
+			                                      "cert hash mismatch",
+				                              TLS_FAIL_UNTRUSTED);
+			}
+			else /* hash matches; ignore other issues *except* if revoked)*/
+				*flags &= MBEDTLS_X509_BADCERT_REVOKED;
+		  #endif
+		}
+	}
+	else if (depth == 0) {
+		if (!conn->peer_subject)
+			tls_mbedtls_set_peer_subject(conn, crt);
+		/*(use same labels to tls_mbedtls_verify_fail_event() as used in
+		 * other TLS modules so that hwsim tests find exact string match)*/
+		if (!conn->peer_subject) { /* error copying subject string */
+			*flags |= MBEDTLS_X509_BADCERT_OTHER;
+			tls_mbedtls_verify_fail_event(crt, depth,
+			                              "internal error",
+			                              TLS_FAIL_UNSPECIFIED);
+		}
+		/*(use os_strstr() for subject match as is done in tls_mbedtls.c
+		 * to follow the same behavior, even though a suffix match would
+		 * make more sense.  Also, note that strstr match does not
+		 * normalize whitespace (between components) for comparison)*/
+		else if (tls_conf->subject_match
+		         && os_strstr(conn->peer_subject,
+		                      tls_conf->subject_match) == NULL) {
+			wpa_printf(MSG_WARNING,
+			           "MTLS: Subject '%s' did not match with '%s'",
+			           conn->peer_subject, tls_conf->subject_match);
+			*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
+			tls_mbedtls_verify_fail_event(crt, depth,
+			                              "Subject mismatch",
+			                              TLS_FAIL_SUBJECT_MISMATCH);
+		}
+		if (tls_conf->altsubject_match
+		    && !tls_mbedtls_match_altsubject(crt, tls_conf->altsubject_match)) {
+			wpa_printf(MSG_WARNING,
+				   "MTLS: altSubjectName match '%s' not found",
+			           tls_conf->altsubject_match);
+			*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
+			tls_mbedtls_verify_fail_event(crt, depth,
+			                              "AltSubject mismatch",
+			                              TLS_FAIL_ALTSUBJECT_MISMATCH);
+		}
+		if (tls_conf->suffix_match
+		    && !tls_mbedtls_match_suffixes(crt, tls_conf->suffix_match, 0)) {
+			wpa_printf(MSG_WARNING,
+			           "MTLS: Domain suffix match '%s' not found",
+				   tls_conf->suffix_match);
+			*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
+			tls_mbedtls_verify_fail_event(crt, depth,
+			                              "Domain suffix mismatch",
+			                              TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
+		}
+		if (tls_conf->domain_match
+		    && !tls_mbedtls_match_suffixes(crt, tls_conf->domain_match, 1)) {
+			wpa_printf(MSG_WARNING,
+			           "MTLS: Domain match '%s' not found",
+				   tls_conf->domain_match);
+			*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
+			tls_mbedtls_verify_fail_event(crt, depth,
+			                              "Domain mismatch",
+			                              TLS_FAIL_DOMAIN_MISMATCH);
+		}
+		if (tls_conf->check_cert_subject
+		    && !tls_mbedtls_match_dn_field(crt, tls_conf->check_cert_subject)) {
+			*flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH;
+			tls_mbedtls_verify_fail_event(crt, depth,
+			                              "Distinguished Name",
+			                              TLS_FAIL_DN_MISMATCH);
+		}
+		if (tls_conf->flags & TLS_CONN_SUITEB) {
+			/* check RSA modulus size (public key bitlen) */
+			const mbedtls_pk_type_t pk_alg = mbedtls_pk_get_type(&crt->pk);
+			if ((pk_alg == MBEDTLS_PK_RSA || pk_alg == MBEDTLS_PK_RSASSA_PSS)
+			    && mbedtls_pk_get_bitlen(&crt->pk) < 3072) {
+				/* hwsim suite_b RSA tests expect 3072
+				 *   suite_b_192_rsa_ecdhe_radius_rsa2048_client
+				 *   suite_b_192_rsa_dhe_radius_rsa2048_client */
+				*flags |= MBEDTLS_X509_BADCERT_BAD_KEY;
+				tls_mbedtls_verify_fail_event(crt, depth,
+				                              "Insufficient RSA modulus size",
+				                              TLS_FAIL_INSUFFICIENT_KEY_LEN);
+			}
+		}
+		if (tls_conf->check_crl && tls_conf->crl == NULL) {
+			/* see tests/hwsim test_ap_eap.py ap_wpa2_eap_tls_check_crl */
+			emsg(MSG_WARNING, "check_crl set but no CRL loaded; reject all?");
+			*flags |= MBEDTLS_X509_BADCERT_OTHER;
+			tls_mbedtls_verify_fail_event(crt, depth,
+				                      "check_crl set but no CRL loaded; "
+			                              "reject all?",
+			                              TLS_FAIL_BAD_CERTIFICATE);
+		}
+	}
+	else {
+		if (tls_conf->check_crl != 2) /* 2 == verify CRLs for all certs */
+			*flags &= ~MBEDTLS_X509_BADCERT_REVOKED;
+	}
+
+	if (!tls_conf->check_crl_strict) {
+		*flags &= ~MBEDTLS_X509_BADCRL_EXPIRED;
+		*flags &= ~MBEDTLS_X509_BADCRL_FUTURE;
+	}
+
+	if (tls_conf->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
+		*flags &= ~MBEDTLS_X509_BADCERT_EXPIRED;
+		*flags &= ~MBEDTLS_X509_BADCERT_FUTURE;
+	}
+
+	tls_mbedtls_verify_cert_event(conn, crt, depth);
+
+	if (*flags) {
+		if (*flags & (MBEDTLS_X509_BADCERT_NOT_TRUSTED
+		             |MBEDTLS_X509_BADCERT_CN_MISMATCH
+		             |MBEDTLS_X509_BADCERT_REVOKED)) {
+			emsg(MSG_WARNING, "client cert not trusted");
+		}
+		/* report event if flags set but no additional flags set above */
+		/* (could translate flags to more detailed TLS_FAIL_* if needed) */
+		if (!(*flags & ~flags_in)) {
+			enum tls_fail_reason reason = TLS_FAIL_UNSPECIFIED;
+			const char *errmsg = "cert verify fail unspecified";
+			if (*flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED) {
+				reason = TLS_FAIL_UNTRUSTED;
+				errmsg = "certificate not trusted";
+			}
+			if (*flags & MBEDTLS_X509_BADCERT_REVOKED) {
+				reason = TLS_FAIL_REVOKED;
+				errmsg = "certificate has been revoked";
+			}
+			if (*flags & MBEDTLS_X509_BADCERT_FUTURE) {
+				reason = TLS_FAIL_NOT_YET_VALID;
+				errmsg = "certificate not yet valid";
+			}
+			if (*flags & MBEDTLS_X509_BADCERT_EXPIRED) {
+				reason = TLS_FAIL_EXPIRED;
+				errmsg = "certificate has expired";
+			}
+			if (*flags & MBEDTLS_X509_BADCERT_BAD_MD) {
+				reason = TLS_FAIL_BAD_CERTIFICATE;
+				errmsg = "certificate uses insecure algorithm";
+			}
+			tls_mbedtls_verify_fail_event(crt, depth, errmsg, reason);
+		}
+	  #if 0
+		/* ??? send (again) cert events for all certs in chain ???
+		 * (should already have been called for greater depths) */
+		/* tls_openssl.c:tls_verify_cb() sends cert events for all certs
+		 * in chain if certificate validation fails, but sends all events
+		 * with depth set to 0 (might be a bug) */
+		if (depth > 0) {
+			int pdepth = depth + 1;
+			for (mbedtls_x509_crt *pcrt; (pcrt = crt->next); ++pdepth) {
+				tls_mbedtls_verify_cert_event(conn, pcrt, pdepth);
+			}
+		}
+	  #endif
+		/*(do not preserve subject if verification failed but was optional)*/
+		if (depth == 0 && conn->peer_subject) {
+			os_free(conn->peer_subject);
+			conn->peer_subject = NULL;
+		}
+	}
+	else if (depth == 0) {
+		struct tls_config *init_conf = &tls_ctx_global.init_conf;
+		if (tls_conf->ca_cert_probe) {
+			/* reject server certificate on probe-only run */
+			*flags |= MBEDTLS_X509_BADCERT_OTHER;
+			tls_mbedtls_verify_fail_event(crt, depth,
+			                              "server chain probe",
+			                              TLS_FAIL_SERVER_CHAIN_PROBE);
+		}
+		else if (init_conf->event_cb) {
+			/* ??? send event as soon as depth == 0 is verified ???
+			 * What about rest of chain?
+			 * Follows tls_mbedtls.c behavior: */
+			init_conf->event_cb(init_conf->cb_ctx,
+			                    TLS_CERT_CHAIN_SUCCESS, NULL);
+		}
+	}
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls_none.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls_none.c
new file mode 100644
index 0000000..87f45f8
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls_none.c
@@ -0,0 +1,233 @@
+/*
+ * SSL/TLS interface functions for no TLS case
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "tls.h"
+
+void * tls_init(const struct tls_config *conf)
+{
+	return (void *) 1;
+}
+
+
+void tls_deinit(void *ssl_ctx)
+{
+}
+
+
+int tls_get_errors(void *tls_ctx)
+{
+	return 0;
+}
+
+
+struct tls_connection * tls_connection_init(void *tls_ctx)
+{
+	return NULL;
+}
+
+
+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
+{
+}
+
+
+int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
+{
+	return -1;
+}
+
+
+char * tls_connection_peer_serial_num(void *tls_ctx,
+				      struct tls_connection *conn)
+{
+	return NULL;
+}
+
+
+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
+{
+	return -1;
+}
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+			      const struct tls_connection_params *params)
+{
+	return -1;
+}
+
+
+int tls_global_set_params(void *tls_ctx,
+			  const struct tls_connection_params *params)
+{
+	return -1;
+}
+
+
+int tls_global_set_verify(void *tls_ctx, int check_crl, int strict)
+{
+	return -1;
+}
+
+
+int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
+			      int verify_peer, unsigned int flags,
+			      const u8 *session_ctx, size_t session_ctx_len)
+{
+	return -1;
+}
+
+
+int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
+			      struct tls_random *data)
+{
+	return -1;
+}
+
+
+int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
+			      const char *label, const u8 *context,
+			      size_t context_len, u8 *out, size_t out_len)
+{
+	return -1;
+}
+
+
+int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
+				    u8 *out, size_t out_len)
+{
+	return -1;
+}
+
+
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+					 struct tls_connection *conn,
+					 const struct wpabuf *in_data,
+					 struct wpabuf **appl_data)
+{
+	return NULL;
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+						struct tls_connection *conn,
+						const struct wpabuf *in_data,
+						struct wpabuf **appl_data)
+{
+	return NULL;
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data)
+{
+	return NULL;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data)
+{
+	return NULL;
+}
+
+
+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
+{
+	return 0;
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+				   u8 *ciphers)
+{
+	return -1;
+}
+
+
+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
+		    char *buf, size_t buflen)
+{
+	return -1;
+}
+
+
+int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
+		   char *buf, size_t buflen)
+{
+	return -1;
+}
+
+
+int tls_connection_enable_workaround(void *tls_ctx,
+				     struct tls_connection *conn)
+{
+	return -1;
+}
+
+
+int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
+				    int ext_type, const u8 *data,
+				    size_t data_len)
+{
+	return -1;
+}
+
+
+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
+{
+	return 0;
+}
+
+
+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
+{
+	return 0;
+}
+
+
+int tls_connection_get_write_alerts(void *tls_ctx,
+				    struct tls_connection *conn)
+{
+	return 0;
+}
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+	return os_snprintf(buf, buf_len, "none");
+}
+
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+				     struct wpabuf *data)
+{
+	wpabuf_free(data);
+}
+
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
+{
+}
+
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn)
+{
+	return NULL;
+}
+
+
+void tls_connection_remove_session(struct tls_connection *conn)
+{
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls_openssl.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls_openssl.c
new file mode 100644
index 0000000..17283f9
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls_openssl.c
@@ -0,0 +1,6029 @@
+/*
+ * SSL/TLS interface functions for OpenSSL
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#ifdef CONFIG_TESTING_OPTIONS
+#include <fcntl.h>
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifndef CONFIG_SMARTCARD
+#ifndef OPENSSL_NO_ENGINE
+#ifndef ANDROID
+#define OPENSSL_NO_ENGINE
+#endif
+#endif
+#endif
+
+#ifndef OPENSSL_NO_ENGINE
+/* OpenSSL 3.0 has moved away from the engine API */
+#define OPENSSL_SUPPRESS_DEPRECATED
+#include <openssl/engine.h>
+#endif /* OPENSSL_NO_ENGINE */
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/opensslv.h>
+#include <openssl/pkcs12.h>
+#include <openssl/x509v3.h>
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#include <openssl/core_names.h>
+#include <openssl/decoder.h>
+#include <openssl/param_build.h>
+#else /* OpenSSL version >= 3.0 */
+#ifndef OPENSSL_NO_DSA
+#include <openssl/dsa.h>
+#endif
+#ifndef OPENSSL_NO_DH
+#include <openssl/dh.h>
+#endif
+#endif /* OpenSSL version >= 3.0 */
+
+#include "common.h"
+#include "utils/list.h"
+#include "crypto.h"
+#include "sha1.h"
+#include "sha256.h"
+#include "tls.h"
+#include "tls_openssl.h"
+
+#if !defined(CONFIG_FIPS) &&                             \
+    (defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) ||   \
+     defined(EAP_SERVER_FAST))
+#define OPENSSL_NEED_EAP_FAST_PRF
+#endif
+
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || \
+	defined(EAP_SERVER_FAST) || defined(EAP_TEAP) || \
+	defined(EAP_SERVER_TEAP)
+#define EAP_FAST_OR_TEAP
+#endif
+
+
+#if defined(OPENSSL_IS_BORINGSSL)
+/* stack_index_t is the return type of OpenSSL's sk_XXX_num() functions. */
+typedef size_t stack_index_t;
+#else
+typedef int stack_index_t;
+#endif
+
+#ifdef SSL_set_tlsext_status_type
+#ifndef OPENSSL_NO_TLSEXT
+#define HAVE_OCSP
+#include <openssl/ocsp.h>
+#endif /* OPENSSL_NO_TLSEXT */
+#endif /* SSL_set_tlsext_status_type */
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L && \
+    !defined(BORINGSSL_API_VERSION)
+/*
+ * SSL_get_client_random() and SSL_get_server_random() were added in OpenSSL
+ * 1.1.0 and newer BoringSSL revisions. Provide compatibility wrappers for
+ * older versions.
+ */
+
+static size_t SSL_get_client_random(const SSL *ssl, unsigned char *out,
+				    size_t outlen)
+{
+	if (!ssl->s3 || outlen < SSL3_RANDOM_SIZE)
+		return 0;
+	os_memcpy(out, ssl->s3->client_random, SSL3_RANDOM_SIZE);
+	return SSL3_RANDOM_SIZE;
+}
+
+
+static size_t SSL_get_server_random(const SSL *ssl, unsigned char *out,
+				    size_t outlen)
+{
+	if (!ssl->s3 || outlen < SSL3_RANDOM_SIZE)
+		return 0;
+	os_memcpy(out, ssl->s3->server_random, SSL3_RANDOM_SIZE);
+	return SSL3_RANDOM_SIZE;
+}
+
+
+#ifdef OPENSSL_NEED_EAP_FAST_PRF
+static size_t SSL_SESSION_get_master_key(const SSL_SESSION *session,
+					 unsigned char *out, size_t outlen)
+{
+	if (!session || session->master_key_length < 0 ||
+	    (size_t) session->master_key_length > outlen)
+		return 0;
+	if ((size_t) session->master_key_length < outlen)
+		outlen = session->master_key_length;
+	os_memcpy(out, session->master_key, outlen);
+	return outlen;
+}
+#endif /* OPENSSL_NEED_EAP_FAST_PRF */
+
+#endif
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+static const unsigned char * ASN1_STRING_get0_data(const ASN1_STRING *x)
+{
+	return ASN1_STRING_data((ASN1_STRING *) x);
+}
+#endif
+
+#ifdef ANDROID
+#include <openssl/pem.h>
+#include <keystore/keystore_get.h>
+
+static BIO * BIO_from_keystore(const char *key)
+{
+	BIO *bio = NULL;
+	uint8_t *value = NULL;
+	int length = keystore_get(key, strlen(key), &value);
+	if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL)
+		BIO_write(bio, value, length);
+	free(value);
+	return bio;
+}
+
+
+static int tls_add_ca_from_keystore(X509_STORE *ctx, const char *key_alias)
+{
+	BIO *bio = BIO_from_keystore(key_alias);
+	STACK_OF(X509_INFO) *stack = NULL;
+	stack_index_t i;
+
+	if (bio) {
+		stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
+		BIO_free(bio);
+	}
+
+	if (!stack) {
+		wpa_printf(MSG_WARNING, "TLS: Failed to parse certificate: %s",
+			   key_alias);
+		return -1;
+	}
+
+	for (i = 0; i < sk_X509_INFO_num(stack); ++i) {
+		X509_INFO *info = sk_X509_INFO_value(stack, i);
+
+		if (info->x509)
+			X509_STORE_add_cert(ctx, info->x509);
+		if (info->crl)
+			X509_STORE_add_crl(ctx, info->crl);
+	}
+
+	sk_X509_INFO_pop_free(stack, X509_INFO_free);
+
+	return 0;
+}
+
+
+static int tls_add_ca_from_keystore_encoded(X509_STORE *ctx,
+					    const char *encoded_key_alias)
+{
+	int rc = -1;
+	int len = os_strlen(encoded_key_alias);
+	unsigned char *decoded_alias;
+
+	if (len & 1) {
+		wpa_printf(MSG_WARNING, "Invalid hex-encoded alias: %s",
+			   encoded_key_alias);
+		return rc;
+	}
+
+	decoded_alias = os_malloc(len / 2 + 1);
+	if (decoded_alias) {
+		if (!hexstr2bin(encoded_key_alias, decoded_alias, len / 2)) {
+			decoded_alias[len / 2] = '\0';
+			rc = tls_add_ca_from_keystore(
+				ctx, (const char *) decoded_alias);
+		}
+		os_free(decoded_alias);
+	}
+
+	return rc;
+}
+
+#endif /* ANDROID */
+
+static int tls_openssl_ref_count = 0;
+static int tls_ex_idx_session = -1;
+
+struct tls_session_data {
+	struct dl_list list;
+	struct wpabuf *buf;
+};
+
+struct tls_context {
+	void (*event_cb)(void *ctx, enum tls_event ev,
+			 union tls_event_data *data);
+	void *cb_ctx;
+	int cert_in_cb;
+	char *ocsp_stapling_response;
+	struct dl_list sessions; /* struct tls_session_data */
+};
+
+static struct tls_context *tls_global = NULL;
+
+
+struct tls_data {
+	SSL_CTX *ssl;
+	unsigned int tls_session_lifetime;
+	int check_crl;
+	int check_crl_strict;
+	char *ca_cert;
+	unsigned int crl_reload_interval;
+	struct os_reltime crl_last_reload;
+	char *check_cert_subject;
+	char *openssl_ciphers;
+};
+
+struct tls_connection {
+	struct tls_context *context;
+	struct tls_data *data;
+	SSL_CTX *ssl_ctx;
+	SSL *ssl;
+	BIO *ssl_in, *ssl_out;
+#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE)
+	ENGINE *engine;        /* functional reference to the engine */
+	EVP_PKEY *private_key; /* the private key if using engine */
+#endif /* OPENSSL_NO_ENGINE */
+	char *subject_match, *altsubject_match, *suffix_match, *domain_match;
+	char *check_cert_subject;
+	int read_alerts, write_alerts, failed;
+
+	tls_session_ticket_cb session_ticket_cb;
+	void *session_ticket_cb_ctx;
+
+	/* SessionTicket received from OpenSSL hello_extension_cb (server) */
+	u8 *session_ticket;
+	size_t session_ticket_len;
+
+	unsigned int ca_cert_verify:1;
+	unsigned int cert_probe:1;
+	unsigned int server_cert_only:1;
+	unsigned int invalid_hb_used:1;
+	unsigned int success_data:1;
+	unsigned int client_hello_generated:1;
+	unsigned int server:1;
+
+	u8 srv_cert_hash[32];
+
+	unsigned int flags;
+
+	X509 *peer_cert;
+	X509 *peer_issuer;
+	X509 *peer_issuer_issuer;
+	char *peer_subject; /* peer subject info for authenticated peer */
+
+	unsigned char client_random[SSL3_RANDOM_SIZE];
+	unsigned char server_random[SSL3_RANDOM_SIZE];
+
+	u16 cipher_suite;
+	int server_dh_prime_len;
+};
+
+
+static struct tls_context * tls_context_new(const struct tls_config *conf)
+{
+	struct tls_context *context = os_zalloc(sizeof(*context));
+	if (context == NULL)
+		return NULL;
+	dl_list_init(&context->sessions);
+	if (conf) {
+		context->event_cb = conf->event_cb;
+		context->cb_ctx = conf->cb_ctx;
+		context->cert_in_cb = conf->cert_in_cb;
+	}
+	return context;
+}
+
+
+#ifdef CONFIG_NO_STDOUT_DEBUG
+
+static void _tls_show_errors(void)
+{
+	unsigned long err;
+
+	while ((err = ERR_get_error())) {
+		/* Just ignore the errors, since stdout is disabled */
+	}
+}
+#define tls_show_errors(l, f, t) _tls_show_errors()
+
+#else /* CONFIG_NO_STDOUT_DEBUG */
+
+static void tls_show_errors(int level, const char *func, const char *txt)
+{
+	unsigned long err;
+
+	wpa_printf(level, "OpenSSL: %s - %s %s",
+		   func, txt, ERR_error_string(ERR_get_error(), NULL));
+
+	while ((err = ERR_get_error())) {
+		wpa_printf(MSG_INFO, "OpenSSL: pending error: %s",
+			   ERR_error_string(err, NULL));
+	}
+}
+
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+static X509_STORE * tls_crl_cert_reload(const char *ca_cert, int check_crl)
+{
+	int flags;
+	X509_STORE *store;
+
+	store = X509_STORE_new();
+	if (!store) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: %s - failed to allocate new certificate store",
+			   __func__);
+		return NULL;
+	}
+
+	if (ca_cert && X509_STORE_load_locations(store, ca_cert, NULL) != 1) {
+		tls_show_errors(MSG_WARNING, __func__,
+				"Failed to load root certificates");
+		X509_STORE_free(store);
+		return NULL;
+	}
+
+	flags = check_crl ? X509_V_FLAG_CRL_CHECK : 0;
+	if (check_crl == 2)
+		flags |= X509_V_FLAG_CRL_CHECK_ALL;
+
+	X509_STORE_set_flags(store, flags);
+
+	return store;
+}
+
+
+#ifdef CONFIG_NATIVE_WINDOWS
+
+/* Windows CryptoAPI and access to certificate stores */
+#include <wincrypt.h>
+
+#ifdef __MINGW32_VERSION
+/*
+ * MinGW does not yet include all the needed definitions for CryptoAPI, so
+ * define here whatever extra is needed.
+ */
+#define CERT_SYSTEM_STORE_CURRENT_USER (1 << 16)
+#define CERT_STORE_READONLY_FLAG 0x00008000
+#define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000
+
+#endif /* __MINGW32_VERSION */
+
+
+struct cryptoapi_rsa_data {
+	const CERT_CONTEXT *cert;
+	HCRYPTPROV crypt_prov;
+	DWORD key_spec;
+	BOOL free_crypt_prov;
+};
+
+
+static void cryptoapi_error(const char *msg)
+{
+	wpa_printf(MSG_INFO, "CryptoAPI: %s; err=%u",
+		   msg, (unsigned int) GetLastError());
+}
+
+
+static int cryptoapi_rsa_pub_enc(int flen, const unsigned char *from,
+				 unsigned char *to, RSA *rsa, int padding)
+{
+	wpa_printf(MSG_DEBUG, "%s - not implemented", __func__);
+	return 0;
+}
+
+
+static int cryptoapi_rsa_pub_dec(int flen, const unsigned char *from,
+				 unsigned char *to, RSA *rsa, int padding)
+{
+	wpa_printf(MSG_DEBUG, "%s - not implemented", __func__);
+	return 0;
+}
+
+
+static int cryptoapi_rsa_priv_enc(int flen, const unsigned char *from,
+				  unsigned char *to, RSA *rsa, int padding)
+{
+	struct cryptoapi_rsa_data *priv =
+		(struct cryptoapi_rsa_data *) rsa->meth->app_data;
+	HCRYPTHASH hash;
+	DWORD hash_size, len, i;
+	unsigned char *buf = NULL;
+	int ret = 0;
+
+	if (priv == NULL) {
+		RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
+		       ERR_R_PASSED_NULL_PARAMETER);
+		return 0;
+	}
+
+	if (padding != RSA_PKCS1_PADDING) {
+		RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
+		       RSA_R_UNKNOWN_PADDING_TYPE);
+		return 0;
+	}
+
+	if (flen != 16 /* MD5 */ + 20 /* SHA-1 */) {
+		wpa_printf(MSG_INFO, "%s - only MD5-SHA1 hash supported",
+			   __func__);
+		RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
+		       RSA_R_INVALID_MESSAGE_LENGTH);
+		return 0;
+	}
+
+	if (!CryptCreateHash(priv->crypt_prov, CALG_SSL3_SHAMD5, 0, 0, &hash))
+	{
+		cryptoapi_error("CryptCreateHash failed");
+		return 0;
+	}
+
+	len = sizeof(hash_size);
+	if (!CryptGetHashParam(hash, HP_HASHSIZE, (BYTE *) &hash_size, &len,
+			       0)) {
+		cryptoapi_error("CryptGetHashParam failed");
+		goto err;
+	}
+
+	if ((int) hash_size != flen) {
+		wpa_printf(MSG_INFO, "CryptoAPI: Invalid hash size (%u != %d)",
+			   (unsigned) hash_size, flen);
+		RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
+		       RSA_R_INVALID_MESSAGE_LENGTH);
+		goto err;
+	}
+	if (!CryptSetHashParam(hash, HP_HASHVAL, (BYTE * ) from, 0)) {
+		cryptoapi_error("CryptSetHashParam failed");
+		goto err;
+	}
+
+	len = RSA_size(rsa);
+	buf = os_malloc(len);
+	if (buf == NULL) {
+		RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, ERR_R_MALLOC_FAILURE);
+		goto err;
+	}
+
+	if (!CryptSignHash(hash, priv->key_spec, NULL, 0, buf, &len)) {
+		cryptoapi_error("CryptSignHash failed");
+		goto err;
+	}
+
+	for (i = 0; i < len; i++)
+		to[i] = buf[len - i - 1];
+	ret = len;
+
+err:
+	os_free(buf);
+	CryptDestroyHash(hash);
+
+	return ret;
+}
+
+
+static int cryptoapi_rsa_priv_dec(int flen, const unsigned char *from,
+				  unsigned char *to, RSA *rsa, int padding)
+{
+	wpa_printf(MSG_DEBUG, "%s - not implemented", __func__);
+	return 0;
+}
+
+
+static void cryptoapi_free_data(struct cryptoapi_rsa_data *priv)
+{
+	if (priv == NULL)
+		return;
+	if (priv->crypt_prov && priv->free_crypt_prov)
+		CryptReleaseContext(priv->crypt_prov, 0);
+	if (priv->cert)
+		CertFreeCertificateContext(priv->cert);
+	os_free(priv);
+}
+
+
+static int cryptoapi_finish(RSA *rsa)
+{
+	cryptoapi_free_data((struct cryptoapi_rsa_data *) rsa->meth->app_data);
+	os_free((void *) rsa->meth);
+	rsa->meth = NULL;
+	return 1;
+}
+
+
+static const CERT_CONTEXT * cryptoapi_find_cert(const char *name, DWORD store)
+{
+	HCERTSTORE cs;
+	const CERT_CONTEXT *ret = NULL;
+
+	cs = CertOpenStore((LPCSTR) CERT_STORE_PROV_SYSTEM, 0, 0,
+			   store | CERT_STORE_OPEN_EXISTING_FLAG |
+			   CERT_STORE_READONLY_FLAG, L"MY");
+	if (cs == NULL) {
+		cryptoapi_error("Failed to open 'My system store'");
+		return NULL;
+	}
+
+	if (strncmp(name, "cert://", 7) == 0) {
+		unsigned short wbuf[255];
+		MultiByteToWideChar(CP_ACP, 0, name + 7, -1, wbuf, 255);
+		ret = CertFindCertificateInStore(cs, X509_ASN_ENCODING |
+						 PKCS_7_ASN_ENCODING,
+						 0, CERT_FIND_SUBJECT_STR,
+						 wbuf, NULL);
+	} else if (strncmp(name, "hash://", 7) == 0) {
+		CRYPT_HASH_BLOB blob;
+		int len;
+		const char *hash = name + 7;
+		unsigned char *buf;
+
+		len = os_strlen(hash) / 2;
+		buf = os_malloc(len);
+		if (buf && hexstr2bin(hash, buf, len) == 0) {
+			blob.cbData = len;
+			blob.pbData = buf;
+			ret = CertFindCertificateInStore(cs,
+							 X509_ASN_ENCODING |
+							 PKCS_7_ASN_ENCODING,
+							 0, CERT_FIND_HASH,
+							 &blob, NULL);
+		}
+		os_free(buf);
+	}
+
+	CertCloseStore(cs, 0);
+
+	return ret;
+}
+
+
+static int tls_cryptoapi_cert(SSL *ssl, const char *name)
+{
+	X509 *cert = NULL;
+	RSA *rsa = NULL, *pub_rsa;
+	struct cryptoapi_rsa_data *priv;
+	RSA_METHOD *rsa_meth;
+
+	if (name == NULL ||
+	    (strncmp(name, "cert://", 7) != 0 &&
+	     strncmp(name, "hash://", 7) != 0))
+		return -1;
+
+	priv = os_zalloc(sizeof(*priv));
+	rsa_meth = os_zalloc(sizeof(*rsa_meth));
+	if (priv == NULL || rsa_meth == NULL) {
+		wpa_printf(MSG_WARNING, "CryptoAPI: Failed to allocate memory "
+			   "for CryptoAPI RSA method");
+		os_free(priv);
+		os_free(rsa_meth);
+		return -1;
+	}
+
+	priv->cert = cryptoapi_find_cert(name, CERT_SYSTEM_STORE_CURRENT_USER);
+	if (priv->cert == NULL) {
+		priv->cert = cryptoapi_find_cert(
+			name, CERT_SYSTEM_STORE_LOCAL_MACHINE);
+	}
+	if (priv->cert == NULL) {
+		wpa_printf(MSG_INFO, "CryptoAPI: Could not find certificate "
+			   "'%s'", name);
+		goto err;
+	}
+
+	cert = d2i_X509(NULL,
+			(const unsigned char **) &priv->cert->pbCertEncoded,
+			priv->cert->cbCertEncoded);
+	if (cert == NULL) {
+		wpa_printf(MSG_INFO, "CryptoAPI: Could not process X509 DER "
+			   "encoding");
+		goto err;
+	}
+
+	if (!CryptAcquireCertificatePrivateKey(priv->cert,
+					       CRYPT_ACQUIRE_COMPARE_KEY_FLAG,
+					       NULL, &priv->crypt_prov,
+					       &priv->key_spec,
+					       &priv->free_crypt_prov)) {
+		cryptoapi_error("Failed to acquire a private key for the "
+				"certificate");
+		goto err;
+	}
+
+	rsa_meth->name = "Microsoft CryptoAPI RSA Method";
+	rsa_meth->rsa_pub_enc = cryptoapi_rsa_pub_enc;
+	rsa_meth->rsa_pub_dec = cryptoapi_rsa_pub_dec;
+	rsa_meth->rsa_priv_enc = cryptoapi_rsa_priv_enc;
+	rsa_meth->rsa_priv_dec = cryptoapi_rsa_priv_dec;
+	rsa_meth->finish = cryptoapi_finish;
+	rsa_meth->flags = RSA_METHOD_FLAG_NO_CHECK;
+	rsa_meth->app_data = (char *) priv;
+
+	rsa = RSA_new();
+	if (rsa == NULL) {
+		SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE,
+		       ERR_R_MALLOC_FAILURE);
+		goto err;
+	}
+
+	if (!SSL_use_certificate(ssl, cert)) {
+		RSA_free(rsa);
+		rsa = NULL;
+		goto err;
+	}
+	pub_rsa = cert->cert_info->key->pkey->pkey.rsa;
+	X509_free(cert);
+	cert = NULL;
+
+	rsa->n = BN_dup(pub_rsa->n);
+	rsa->e = BN_dup(pub_rsa->e);
+	if (!RSA_set_method(rsa, rsa_meth))
+		goto err;
+
+	if (!SSL_use_RSAPrivateKey(ssl, rsa))
+		goto err;
+	RSA_free(rsa);
+
+	return 0;
+
+err:
+	if (cert)
+		X509_free(cert);
+	if (rsa)
+		RSA_free(rsa);
+	else {
+		os_free(rsa_meth);
+		cryptoapi_free_data(priv);
+	}
+	return -1;
+}
+
+
+static int tls_cryptoapi_ca_cert(SSL_CTX *ssl_ctx, SSL *ssl, const char *name)
+{
+	HCERTSTORE cs;
+	PCCERT_CONTEXT ctx = NULL;
+	X509 *cert;
+	char buf[128];
+	const char *store;
+#ifdef UNICODE
+	WCHAR *wstore;
+#endif /* UNICODE */
+
+	if (name == NULL || strncmp(name, "cert_store://", 13) != 0)
+		return -1;
+
+	store = name + 13;
+#ifdef UNICODE
+	wstore = os_malloc((os_strlen(store) + 1) * sizeof(WCHAR));
+	if (wstore == NULL)
+		return -1;
+	wsprintf(wstore, L"%S", store);
+	cs = CertOpenSystemStore(0, wstore);
+	os_free(wstore);
+#else /* UNICODE */
+	cs = CertOpenSystemStore(0, store);
+#endif /* UNICODE */
+	if (cs == NULL) {
+		wpa_printf(MSG_DEBUG, "%s: failed to open system cert store "
+			   "'%s': error=%d", __func__, store,
+			   (int) GetLastError());
+		return -1;
+	}
+
+	while ((ctx = CertEnumCertificatesInStore(cs, ctx))) {
+		cert = d2i_X509(NULL,
+				(const unsigned char **) &ctx->pbCertEncoded,
+				ctx->cbCertEncoded);
+		if (cert == NULL) {
+			wpa_printf(MSG_INFO, "CryptoAPI: Could not process "
+				   "X509 DER encoding for CA cert");
+			continue;
+		}
+
+		X509_NAME_oneline(X509_get_subject_name(cert), buf,
+				  sizeof(buf));
+		wpa_printf(MSG_DEBUG, "OpenSSL: Loaded CA certificate for "
+			   "system certificate store: subject='%s'", buf);
+
+		if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx),
+					 cert)) {
+			tls_show_errors(MSG_WARNING, __func__,
+					"Failed to add ca_cert to OpenSSL "
+					"certificate store");
+		}
+
+		X509_free(cert);
+	}
+
+	if (!CertCloseStore(cs, 0)) {
+		wpa_printf(MSG_DEBUG, "%s: failed to close system cert store "
+			   "'%s': error=%d", __func__, name + 13,
+			   (int) GetLastError());
+	}
+
+	return 0;
+}
+
+
+#else /* CONFIG_NATIVE_WINDOWS */
+
+static int tls_cryptoapi_cert(SSL *ssl, const char *name)
+{
+	return -1;
+}
+
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+static void ssl_info_cb(const SSL *ssl, int where, int ret)
+{
+	const char *str;
+	int w;
+
+	wpa_printf(MSG_DEBUG, "SSL: (where=0x%x ret=0x%x)", where, ret);
+	w = where & ~SSL_ST_MASK;
+	if (w & SSL_ST_CONNECT)
+		str = "SSL_connect";
+	else if (w & SSL_ST_ACCEPT)
+		str = "SSL_accept";
+	else
+		str = "undefined";
+
+	if (where & SSL_CB_LOOP) {
+		wpa_printf(MSG_DEBUG, "SSL: %s:%s",
+			   str, SSL_state_string_long(ssl));
+	} else if (where & SSL_CB_ALERT) {
+		struct tls_connection *conn = SSL_get_app_data((SSL *) ssl);
+		wpa_printf(MSG_INFO, "SSL: SSL3 alert: %s:%s:%s",
+			   where & SSL_CB_READ ?
+			   "read (remote end reported an error)" :
+			   "write (local SSL3 detected an error)",
+			   SSL_alert_type_string_long(ret),
+			   SSL_alert_desc_string_long(ret));
+		if ((ret >> 8) == SSL3_AL_FATAL) {
+			if (where & SSL_CB_READ)
+				conn->read_alerts++;
+			else
+				conn->write_alerts++;
+		}
+		if (conn->context->event_cb != NULL) {
+			union tls_event_data ev;
+			struct tls_context *context = conn->context;
+			os_memset(&ev, 0, sizeof(ev));
+			ev.alert.is_local = !(where & SSL_CB_READ);
+			ev.alert.type = SSL_alert_type_string_long(ret);
+			ev.alert.description = SSL_alert_desc_string_long(ret);
+			context->event_cb(context->cb_ctx, TLS_ALERT, &ev);
+		}
+	} else if (where & SSL_CB_EXIT && ret <= 0) {
+		wpa_printf(MSG_DEBUG, "SSL: %s:%s in %s",
+			   str, ret == 0 ? "failed" : "error",
+			   SSL_state_string_long(ssl));
+	}
+}
+
+
+#ifndef OPENSSL_NO_ENGINE
+/**
+ * tls_engine_load_dynamic_generic - load any openssl engine
+ * @pre: an array of commands and values that load an engine initialized
+ *       in the engine specific function
+ * @post: an array of commands and values that initialize an already loaded
+ *        engine (or %NULL if not required)
+ * @id: the engine id of the engine to load (only required if post is not %NULL
+ *
+ * This function is a generic function that loads any openssl engine.
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+static int tls_engine_load_dynamic_generic(const char *pre[],
+					   const char *post[], const char *id)
+{
+	ENGINE *engine;
+	const char *dynamic_id = "dynamic";
+
+	engine = ENGINE_by_id(id);
+	if (engine) {
+		wpa_printf(MSG_DEBUG, "ENGINE: engine '%s' is already "
+			   "available", id);
+		/*
+		 * If it was auto-loaded by ENGINE_by_id() we might still
+		 * need to tell it which PKCS#11 module to use in legacy
+		 * (non-p11-kit) environments. Do so now; even if it was
+		 * properly initialised before, setting it again will be
+		 * harmless.
+		 */
+		goto found;
+	}
+	ERR_clear_error();
+
+	engine = ENGINE_by_id(dynamic_id);
+	if (engine == NULL) {
+		wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]",
+			   dynamic_id,
+			   ERR_error_string(ERR_get_error(), NULL));
+		return -1;
+	}
+
+	/* Perform the pre commands. This will load the engine. */
+	while (pre && pre[0]) {
+		wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", pre[0], pre[1]);
+		if (ENGINE_ctrl_cmd_string(engine, pre[0], pre[1], 0) == 0) {
+			wpa_printf(MSG_INFO, "ENGINE: ctrl cmd_string failed: "
+				   "%s %s [%s]", pre[0], pre[1],
+				   ERR_error_string(ERR_get_error(), NULL));
+			ENGINE_free(engine);
+			return -1;
+		}
+		pre += 2;
+	}
+
+	/*
+	 * Free the reference to the "dynamic" engine. The loaded engine can
+	 * now be looked up using ENGINE_by_id().
+	 */
+	ENGINE_free(engine);
+
+	engine = ENGINE_by_id(id);
+	if (engine == NULL) {
+		wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]",
+			   id, ERR_error_string(ERR_get_error(), NULL));
+		return -1;
+	}
+ found:
+	while (post && post[0]) {
+		wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", post[0], post[1]);
+		if (ENGINE_ctrl_cmd_string(engine, post[0], post[1], 0) == 0) {
+			wpa_printf(MSG_DEBUG, "ENGINE: ctrl cmd_string failed:"
+				" %s %s [%s]", post[0], post[1],
+				   ERR_error_string(ERR_get_error(), NULL));
+			ENGINE_remove(engine);
+			ENGINE_free(engine);
+			return -1;
+		}
+		post += 2;
+	}
+	ENGINE_free(engine);
+
+	return 0;
+}
+
+
+/**
+ * tls_engine_load_dynamic_pkcs11 - load the pkcs11 engine provided by opensc
+ * @pkcs11_so_path: pksc11_so_path from the configuration
+ * @pcks11_module_path: pkcs11_module_path from the configuration
+ */
+static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path,
+					  const char *pkcs11_module_path)
+{
+	char *engine_id = "pkcs11";
+	const char *pre_cmd[] = {
+		"SO_PATH", NULL /* pkcs11_so_path */,
+		"ID", NULL /* engine_id */,
+		"LIST_ADD", "1",
+		/* "NO_VCHECK", "1", */
+		"LOAD", NULL,
+		NULL, NULL
+	};
+	const char *post_cmd[] = {
+		"MODULE_PATH", NULL /* pkcs11_module_path */,
+		NULL, NULL
+	};
+
+	if (!pkcs11_so_path)
+		return 0;
+
+	pre_cmd[1] = pkcs11_so_path;
+	pre_cmd[3] = engine_id;
+	if (pkcs11_module_path)
+		post_cmd[1] = pkcs11_module_path;
+	else
+		post_cmd[0] = NULL;
+
+	wpa_printf(MSG_DEBUG, "ENGINE: Loading pkcs11 Engine from %s",
+		   pkcs11_so_path);
+
+	return tls_engine_load_dynamic_generic(pre_cmd, post_cmd, engine_id);
+}
+
+
+/**
+ * tls_engine_load_dynamic_opensc - load the opensc engine provided by opensc
+ * @opensc_so_path: opensc_so_path from the configuration
+ */
+static int tls_engine_load_dynamic_opensc(const char *opensc_so_path)
+{
+	char *engine_id = "opensc";
+	const char *pre_cmd[] = {
+		"SO_PATH", NULL /* opensc_so_path */,
+		"ID", NULL /* engine_id */,
+		"LIST_ADD", "1",
+		"LOAD", NULL,
+		NULL, NULL
+	};
+
+	if (!opensc_so_path)
+		return 0;
+
+	pre_cmd[1] = opensc_so_path;
+	pre_cmd[3] = engine_id;
+
+	wpa_printf(MSG_DEBUG, "ENGINE: Loading OpenSC Engine from %s",
+		   opensc_so_path);
+
+	return tls_engine_load_dynamic_generic(pre_cmd, NULL, engine_id);
+}
+#endif /* OPENSSL_NO_ENGINE */
+
+
+static struct tls_session_data * get_session_data(struct tls_context *context,
+						  const struct wpabuf *buf)
+{
+	struct tls_session_data *data;
+
+	dl_list_for_each(data, &context->sessions, struct tls_session_data,
+			 list) {
+		if (data->buf == buf)
+			return data;
+	}
+
+	return NULL;
+}
+
+
+static void remove_session_cb(SSL_CTX *ctx, SSL_SESSION *sess)
+{
+	struct wpabuf *buf;
+	struct tls_context *context;
+	struct tls_session_data *found;
+
+	wpa_printf(MSG_DEBUG,
+		   "OpenSSL: Remove session %p (tls_ex_idx_session=%d)", sess,
+		   tls_ex_idx_session);
+
+	if (tls_ex_idx_session < 0)
+		return;
+	buf = SSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+	if (!buf)
+		return;
+
+	context = SSL_CTX_get_app_data(ctx);
+	SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, NULL);
+	found = get_session_data(context, buf);
+	if (!found) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Do not free application session data %p (sess %p)",
+			   buf, sess);
+		return;
+	}
+
+	dl_list_del(&found->list);
+	os_free(found);
+	wpa_printf(MSG_DEBUG,
+		   "OpenSSL: Free application session data %p (sess %p)",
+		   buf, sess);
+	wpabuf_free(buf);
+}
+
+
+void * tls_init(const struct tls_config *conf)
+{
+	struct tls_data *data;
+	SSL_CTX *ssl;
+	struct tls_context *context;
+	const char *ciphers;
+#ifndef OPENSSL_NO_ENGINE
+#ifdef CONFIG_OPENSC_ENGINE_PATH
+	char const * const opensc_engine_path = CONFIG_OPENSC_ENGINE_PATH;
+#else /* CONFIG_OPENSC_ENGINE_PATH */
+	char const * const opensc_engine_path =
+		conf ? conf->opensc_engine_path : NULL;
+#endif /* CONFIG_OPENSC_ENGINE_PATH */
+#ifdef CONFIG_PKCS11_ENGINE_PATH
+	char const * const pkcs11_engine_path = CONFIG_PKCS11_ENGINE_PATH;
+#else /* CONFIG_PKCS11_ENGINE_PATH */
+	char const * const pkcs11_engine_path =
+		conf ? conf->pkcs11_engine_path : NULL;
+#endif /* CONFIG_PKCS11_ENGINE_PATH */
+#ifdef CONFIG_PKCS11_MODULE_PATH
+	char const * const pkcs11_module_path = CONFIG_PKCS11_MODULE_PATH;
+#else /* CONFIG_PKCS11_MODULE_PATH */
+	char const * const pkcs11_module_path =
+		conf ? conf->pkcs11_module_path : NULL;
+#endif /* CONFIG_PKCS11_MODULE_PATH */
+#endif /* OPENSSL_NO_ENGINE */
+
+	if (tls_openssl_ref_count == 0) {
+		void openssl_load_legacy_provider(void);
+
+		openssl_load_legacy_provider();
+
+		tls_global = context = tls_context_new(conf);
+		if (context == NULL)
+			return NULL;
+#ifdef CONFIG_FIPS
+#ifdef OPENSSL_FIPS
+		if (conf && conf->fips_mode) {
+			static int fips_enabled = 0;
+
+			if (!fips_enabled && !FIPS_mode_set(1)) {
+				wpa_printf(MSG_ERROR, "Failed to enable FIPS "
+					   "mode");
+				ERR_load_crypto_strings();
+				ERR_print_errors_fp(stderr);
+				os_free(tls_global);
+				tls_global = NULL;
+				return NULL;
+			} else {
+				wpa_printf(MSG_INFO, "Running in FIPS mode");
+				fips_enabled = 1;
+			}
+		}
+#else /* OPENSSL_FIPS */
+		if (conf && conf->fips_mode) {
+			wpa_printf(MSG_ERROR, "FIPS mode requested, but not "
+				   "supported");
+			os_free(tls_global);
+			tls_global = NULL;
+			return NULL;
+		}
+#endif /* OPENSSL_FIPS */
+#endif /* CONFIG_FIPS */
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+		SSL_load_error_strings();
+		SSL_library_init();
+#ifndef OPENSSL_NO_SHA256
+		EVP_add_digest(EVP_sha256());
+#endif /* OPENSSL_NO_SHA256 */
+		/* TODO: if /dev/urandom is available, PRNG is seeded
+		 * automatically. If this is not the case, random data should
+		 * be added here. */
+
+#ifdef PKCS12_FUNCS
+#ifndef OPENSSL_NO_RC2
+		/*
+		 * 40-bit RC2 is commonly used in PKCS#12 files, so enable it.
+		 * This is enabled by PKCS12_PBE_add() in OpenSSL 0.9.8
+		 * versions, but it looks like OpenSSL 1.0.0 does not do that
+		 * anymore.
+		 */
+		EVP_add_cipher(EVP_rc2_40_cbc());
+#endif /* OPENSSL_NO_RC2 */
+		PKCS12_PBE_add();
+#endif  /* PKCS12_FUNCS */
+#endif /* < 1.1.0 */
+	} else {
+		context = tls_context_new(conf);
+		if (context == NULL)
+			return NULL;
+	}
+	tls_openssl_ref_count++;
+
+	data = os_zalloc(sizeof(*data));
+	if (data)
+		ssl = SSL_CTX_new(SSLv23_method());
+	else
+		ssl = NULL;
+	if (ssl == NULL) {
+		tls_openssl_ref_count--;
+		if (context != tls_global)
+			os_free(context);
+		if (tls_openssl_ref_count == 0) {
+			os_free(tls_global);
+			tls_global = NULL;
+		}
+		os_free(data);
+		return NULL;
+	}
+	data->ssl = ssl;
+	if (conf) {
+		data->tls_session_lifetime = conf->tls_session_lifetime;
+		data->crl_reload_interval = conf->crl_reload_interval;
+	}
+
+	SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2);
+	SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3);
+
+	SSL_CTX_set_mode(ssl, SSL_MODE_AUTO_RETRY);
+
+#ifdef SSL_MODE_NO_AUTO_CHAIN
+	/* Number of deployed use cases assume the default OpenSSL behavior of
+	 * auto chaining the local certificate is in use. BoringSSL removed this
+	 * functionality by default, so we need to restore it here to avoid
+	 * breaking existing use cases. */
+	SSL_CTX_clear_mode(ssl, SSL_MODE_NO_AUTO_CHAIN);
+#endif /* SSL_MODE_NO_AUTO_CHAIN */
+
+	SSL_CTX_set_info_callback(ssl, ssl_info_cb);
+	SSL_CTX_set_app_data(ssl, context);
+	if (data->tls_session_lifetime > 0) {
+		SSL_CTX_set_quiet_shutdown(ssl, 1);
+		/*
+		 * Set default context here. In practice, this will be replaced
+		 * by the per-EAP method context in tls_connection_set_verify().
+		 */
+		SSL_CTX_set_session_id_context(ssl, (u8 *) "hostapd", 7);
+		SSL_CTX_set_session_cache_mode(ssl, SSL_SESS_CACHE_SERVER);
+		SSL_CTX_set_timeout(ssl, data->tls_session_lifetime);
+		SSL_CTX_sess_set_remove_cb(ssl, remove_session_cb);
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && \
+	!defined(LIBRESSL_VERSION_NUMBER) && \
+	!defined(OPENSSL_IS_BORINGSSL)
+		/* One session ticket is sufficient for EAP-TLS */
+		SSL_CTX_set_num_tickets(ssl, 1);
+#endif
+	} else {
+		SSL_CTX_set_session_cache_mode(ssl, SSL_SESS_CACHE_OFF);
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && \
+	!defined(LIBRESSL_VERSION_NUMBER) && \
+	!defined(OPENSSL_IS_BORINGSSL)
+		SSL_CTX_set_num_tickets(ssl, 0);
+#endif
+	}
+
+	if (tls_ex_idx_session < 0) {
+		tls_ex_idx_session = SSL_SESSION_get_ex_new_index(
+			0, NULL, NULL, NULL, NULL);
+		if (tls_ex_idx_session < 0) {
+			tls_deinit(data);
+			return NULL;
+		}
+	}
+
+#ifndef OPENSSL_NO_ENGINE
+	wpa_printf(MSG_DEBUG, "ENGINE: Loading builtin engines");
+	ENGINE_load_builtin_engines();
+
+	if (opensc_engine_path || pkcs11_engine_path || pkcs11_module_path) {
+		if (tls_engine_load_dynamic_opensc(opensc_engine_path) ||
+		    tls_engine_load_dynamic_pkcs11(pkcs11_engine_path,
+						   pkcs11_module_path)) {
+			tls_deinit(data);
+			return NULL;
+		}
+	}
+#endif /* OPENSSL_NO_ENGINE */
+
+	if (conf && conf->openssl_ciphers)
+		ciphers = conf->openssl_ciphers;
+	else
+		ciphers = TLS_DEFAULT_CIPHERS;
+	if (SSL_CTX_set_cipher_list(ssl, ciphers) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: Failed to set cipher string '%s'",
+			   ciphers);
+		tls_deinit(data);
+		return NULL;
+	}
+
+	return data;
+}
+
+
+void tls_deinit(void *ssl_ctx)
+{
+	struct tls_data *data = ssl_ctx;
+	SSL_CTX *ssl = data->ssl;
+	struct tls_context *context = SSL_CTX_get_app_data(ssl);
+	struct tls_session_data *sess_data;
+
+	if (data->tls_session_lifetime > 0) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Flush sessions");
+		SSL_CTX_flush_sessions(ssl, 0);
+		wpa_printf(MSG_DEBUG, "OpenSSL: Flush sessions - done");
+	}
+	while ((sess_data = dl_list_first(&context->sessions,
+					  struct tls_session_data, list))) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Freeing not-flushed session data %p",
+			   sess_data->buf);
+		wpabuf_free(sess_data->buf);
+		dl_list_del(&sess_data->list);
+		os_free(sess_data);
+	}
+	if (context != tls_global)
+		os_free(context);
+	os_free(data->ca_cert);
+	SSL_CTX_free(ssl);
+
+	tls_openssl_ref_count--;
+	if (tls_openssl_ref_count == 0) {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#ifndef OPENSSL_NO_ENGINE
+		ENGINE_cleanup();
+#endif /* OPENSSL_NO_ENGINE */
+		CRYPTO_cleanup_all_ex_data();
+		ERR_remove_thread_state(NULL);
+		ERR_free_strings();
+		EVP_cleanup();
+#endif /* < 1.1.0 */
+		os_free(tls_global->ocsp_stapling_response);
+		tls_global->ocsp_stapling_response = NULL;
+		os_free(tls_global);
+		tls_global = NULL;
+	}
+
+	os_free(data->check_cert_subject);
+	os_free(data->openssl_ciphers);
+	os_free(data);
+}
+
+
+#ifndef OPENSSL_NO_ENGINE
+
+/* Cryptoki return values */
+#define CKR_PIN_INCORRECT 0x000000a0
+#define CKR_PIN_INVALID 0x000000a1
+#define CKR_PIN_LEN_RANGE 0x000000a2
+
+/* libp11 */
+#define ERR_LIB_PKCS11	ERR_LIB_USER
+
+static int tls_is_pin_error(unsigned int err)
+{
+	return ERR_GET_LIB(err) == ERR_LIB_PKCS11 &&
+		(ERR_GET_REASON(err) == CKR_PIN_INCORRECT ||
+		 ERR_GET_REASON(err) == CKR_PIN_INVALID ||
+		 ERR_GET_REASON(err) == CKR_PIN_LEN_RANGE);
+}
+
+#endif /* OPENSSL_NO_ENGINE */
+
+
+#ifdef ANDROID
+/* EVP_PKEY_from_keystore comes from system/security/keystore-engine. */
+EVP_PKEY * EVP_PKEY_from_keystore(const char *key_id);
+#endif /* ANDROID */
+
+static int tls_engine_init(struct tls_connection *conn, const char *engine_id,
+			   const char *pin, const char *key_id,
+			   const char *cert_id, const char *ca_cert_id)
+{
+#if defined(ANDROID) && defined(OPENSSL_IS_BORINGSSL)
+#if !defined(OPENSSL_NO_ENGINE)
+#error "This code depends on OPENSSL_NO_ENGINE being defined by BoringSSL."
+#endif
+	if (!key_id)
+		return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+	conn->engine = NULL;
+	conn->private_key = EVP_PKEY_from_keystore(key_id);
+	if (!conn->private_key) {
+		wpa_printf(MSG_ERROR,
+			   "ENGINE: cannot load private key with id '%s' [%s]",
+			   key_id,
+			   ERR_error_string(ERR_get_error(), NULL));
+		return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+	}
+#endif /* ANDROID && OPENSSL_IS_BORINGSSL */
+
+#ifndef OPENSSL_NO_ENGINE
+	int ret = -1;
+	if (engine_id == NULL) {
+		wpa_printf(MSG_ERROR, "ENGINE: Engine ID not set");
+		return -1;
+	}
+
+	ERR_clear_error();
+#ifdef ANDROID
+	ENGINE_load_dynamic();
+#endif
+	conn->engine = ENGINE_by_id(engine_id);
+	if (!conn->engine) {
+		wpa_printf(MSG_ERROR, "ENGINE: engine %s not available [%s]",
+			   engine_id, ERR_error_string(ERR_get_error(), NULL));
+		goto err;
+	}
+	if (ENGINE_init(conn->engine) != 1) {
+		wpa_printf(MSG_ERROR, "ENGINE: engine init failed "
+			   "(engine: %s) [%s]", engine_id,
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto err;
+	}
+	wpa_printf(MSG_DEBUG, "ENGINE: engine initialized");
+
+#ifndef ANDROID
+	if (pin && ENGINE_ctrl_cmd_string(conn->engine, "PIN", pin, 0) == 0) {
+		wpa_printf(MSG_ERROR, "ENGINE: cannot set pin [%s]",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto err;
+	}
+#endif
+	if (key_id) {
+		/*
+		 * Ensure that the ENGINE does not attempt to use the OpenSSL
+		 * UI system to obtain a PIN, if we didn't provide one.
+		 */
+		struct {
+			const void *password;
+			const char *prompt_info;
+		} key_cb = { "", NULL };
+
+		/* load private key first in-case PIN is required for cert */
+		conn->private_key = ENGINE_load_private_key(conn->engine,
+							    key_id, NULL,
+							    &key_cb);
+		if (!conn->private_key) {
+			unsigned long err = ERR_get_error();
+
+			wpa_printf(MSG_ERROR,
+				   "ENGINE: cannot load private key with id '%s' [%s]",
+				   key_id,
+				   ERR_error_string(err, NULL));
+			if (tls_is_pin_error(err))
+				ret = TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN;
+			else
+				ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+			goto err;
+		}
+	}
+
+	/* handle a certificate and/or CA certificate */
+	if (cert_id || ca_cert_id) {
+		const char *cmd_name = "LOAD_CERT_CTRL";
+
+		/* test if the engine supports a LOAD_CERT_CTRL */
+		if (!ENGINE_ctrl(conn->engine, ENGINE_CTRL_GET_CMD_FROM_NAME,
+				 0, (void *)cmd_name, NULL)) {
+			wpa_printf(MSG_ERROR, "ENGINE: engine does not support"
+				   " loading certificates");
+			ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+			goto err;
+		}
+	}
+
+	return 0;
+
+err:
+	if (conn->engine) {
+		ENGINE_free(conn->engine);
+		conn->engine = NULL;
+	}
+
+	if (conn->private_key) {
+		EVP_PKEY_free(conn->private_key);
+		conn->private_key = NULL;
+	}
+
+	return ret;
+#else /* OPENSSL_NO_ENGINE */
+	return 0;
+#endif /* OPENSSL_NO_ENGINE */
+}
+
+
+static void tls_engine_deinit(struct tls_connection *conn)
+{
+#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE)
+	wpa_printf(MSG_DEBUG, "ENGINE: engine deinit");
+	if (conn->private_key) {
+		EVP_PKEY_free(conn->private_key);
+		conn->private_key = NULL;
+	}
+	if (conn->engine) {
+#if !defined(OPENSSL_IS_BORINGSSL)
+		ENGINE_finish(conn->engine);
+#endif /* !OPENSSL_IS_BORINGSSL */
+		conn->engine = NULL;
+	}
+#endif /* ANDROID || !OPENSSL_NO_ENGINE */
+}
+
+
+int tls_get_errors(void *ssl_ctx)
+{
+	int count = 0;
+	unsigned long err;
+
+	while ((err = ERR_get_error())) {
+		wpa_printf(MSG_INFO, "TLS - SSL error: %s",
+			   ERR_error_string(err, NULL));
+		count++;
+	}
+
+	return count;
+}
+
+
+static const char * openssl_content_type(int content_type)
+{
+	switch (content_type) {
+	case 20:
+		return "change cipher spec";
+	case 21:
+		return "alert";
+	case 22:
+		return "handshake";
+	case 23:
+		return "application data";
+	case 24:
+		return "heartbeat";
+	case 256:
+		return "TLS header info"; /* pseudo content type */
+	case 257:
+		return "inner content type"; /* pseudo content type */
+	default:
+		return "?";
+	}
+}
+
+
+static const char * openssl_handshake_type(int content_type, const u8 *buf,
+					   size_t len)
+{
+	if (content_type == 257 && buf && len == 1)
+		return openssl_content_type(buf[0]);
+	if (content_type != 22 || !buf || len == 0)
+		return "";
+	switch (buf[0]) {
+	case 0:
+		return "hello request";
+	case 1:
+		return "client hello";
+	case 2:
+		return "server hello";
+	case 3:
+		return "hello verify request";
+	case 4:
+		return "new session ticket";
+	case 5:
+		return "end of early data";
+	case 6:
+		return "hello retry request";
+	case 8:
+		return "encrypted extensions";
+	case 11:
+		return "certificate";
+	case 12:
+		return "server key exchange";
+	case 13:
+		return "certificate request";
+	case 14:
+		return "server hello done";
+	case 15:
+		return "certificate verify";
+	case 16:
+		return "client key exchange";
+	case 20:
+		return "finished";
+	case 21:
+		return "certificate url";
+	case 22:
+		return "certificate status";
+	case 23:
+		return "supplemental data";
+	case 24:
+		return "key update";
+	case 254:
+		return "message hash";
+	default:
+		return "?";
+	}
+}
+
+
+#ifdef CONFIG_SUITEB
+
+static void check_server_hello(struct tls_connection *conn,
+			       const u8 *pos, const u8 *end)
+{
+	size_t payload_len, id_len;
+
+	/*
+	 * Parse ServerHello to get the selected cipher suite since OpenSSL does
+	 * not make it cleanly available during handshake and we need to know
+	 * whether DHE was selected.
+	 */
+
+	if (end - pos < 3)
+		return;
+	payload_len = WPA_GET_BE24(pos);
+	pos += 3;
+
+	if ((size_t) (end - pos) < payload_len)
+		return;
+	end = pos + payload_len;
+
+	/* Skip Version and Random */
+	if (end - pos < 2 + SSL3_RANDOM_SIZE)
+		return;
+	pos += 2 + SSL3_RANDOM_SIZE;
+
+	/* Skip Session ID */
+	if (end - pos < 1)
+		return;
+	id_len = *pos++;
+	if ((size_t) (end - pos) < id_len)
+		return;
+	pos += id_len;
+
+	if (end - pos < 2)
+		return;
+	conn->cipher_suite = WPA_GET_BE16(pos);
+	wpa_printf(MSG_DEBUG, "OpenSSL: Server selected cipher suite 0x%x",
+		   conn->cipher_suite);
+}
+
+
+static void check_server_key_exchange(SSL *ssl, struct tls_connection *conn,
+				      const u8 *pos, const u8 *end)
+{
+	size_t payload_len;
+	u16 dh_len;
+	BIGNUM *p;
+	int bits;
+
+	if (!(conn->flags & TLS_CONN_SUITEB))
+		return;
+
+	/* DHE is enabled only with DHE-RSA-AES256-GCM-SHA384 */
+	if (conn->cipher_suite != 0x9f)
+		return;
+
+	if (end - pos < 3)
+		return;
+	payload_len = WPA_GET_BE24(pos);
+	pos += 3;
+
+	if ((size_t) (end - pos) < payload_len)
+		return;
+	end = pos + payload_len;
+
+	if (end - pos < 2)
+		return;
+	dh_len = WPA_GET_BE16(pos);
+	pos += 2;
+
+	if ((size_t) (end - pos) < dh_len)
+		return;
+	p = BN_bin2bn(pos, dh_len, NULL);
+	if (!p)
+		return;
+
+	bits = BN_num_bits(p);
+	BN_free(p);
+
+	conn->server_dh_prime_len = bits;
+	wpa_printf(MSG_DEBUG, "OpenSSL: Server DH prime length: %d bits",
+		   conn->server_dh_prime_len);
+}
+
+#endif /* CONFIG_SUITEB */
+
+
+static void tls_msg_cb(int write_p, int version, int content_type,
+		       const void *buf, size_t len, SSL *ssl, void *arg)
+{
+	struct tls_connection *conn = arg;
+	const u8 *pos = buf;
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	if ((SSL_version(ssl) == TLS1_VERSION ||
+	     SSL_version(ssl) == TLS1_1_VERSION) &&
+	    SSL_get_security_level(ssl) > 0) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Drop security level to 0 to allow TLS 1.0/1.1 use of MD5-SHA1 signature algorithm");
+		SSL_set_security_level(ssl, 0);
+	}
+#endif /* OpenSSL version >= 3.0 */
+	if (write_p == 2) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: session ver=0x%x content_type=%d",
+			   version, content_type);
+		wpa_hexdump_key(MSG_MSGDUMP, "OpenSSL: Data", buf, len);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: %s ver=0x%x content_type=%d (%s/%s)",
+		   write_p ? "TX" : "RX", version, content_type,
+		   openssl_content_type(content_type),
+		   openssl_handshake_type(content_type, buf, len));
+	wpa_hexdump_key(MSG_MSGDUMP, "OpenSSL: Message", buf, len);
+	if (content_type == 24 && len >= 3 && pos[0] == 1) {
+		size_t payload_len = WPA_GET_BE16(pos + 1);
+		if (payload_len + 3 > len) {
+			wpa_printf(MSG_ERROR, "OpenSSL: Heartbeat attack detected");
+			conn->invalid_hb_used = 1;
+		}
+	}
+
+#ifdef CONFIG_SUITEB
+	/*
+	 * Need to parse these handshake messages to be able to check DH prime
+	 * length since OpenSSL does not expose the new cipher suite and DH
+	 * parameters during handshake (e.g., for cert_cb() callback).
+	 */
+	if (content_type == 22 && pos && len > 0 && pos[0] == 2)
+		check_server_hello(conn, pos + 1, pos + len);
+	if (content_type == 22 && pos && len > 0 && pos[0] == 12)
+		check_server_key_exchange(ssl, conn, pos + 1, pos + len);
+#endif /* CONFIG_SUITEB */
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
+/*
+ * By setting the environment variable SSLKEYLOGFILE to a filename keying
+ * material will be exported that you may use with Wireshark to decode any
+ * TLS flows. Please see the following for more details:
+ *
+ *	https://gitlab.com/wireshark/wireshark/-/wikis/TLS#tls-decryption
+ *
+ * Example logging sessions are (you should delete the file on each run):
+ *
+ *	rm -f /tmp/sslkey.log
+ *	env SSLKEYLOGFILE=/tmp/sslkey.log hostapd ...
+ *
+ *	rm -f /tmp/sslkey.log
+ *	env SSLKEYLOGFILE=/tmp/sslkey.log wpa_supplicant ...
+ *
+ *	rm -f /tmp/sslkey.log
+ *	env SSLKEYLOGFILE=/tmp/sslkey.log eapol_test ...
+ */
+static void tls_keylog_cb(const SSL *ssl, const char *line)
+{
+	int fd;
+	const char *filename;
+	struct iovec iov[2];
+
+	filename = getenv("SSLKEYLOGFILE");
+	if (!filename)
+		return;
+
+	fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR);
+	if (fd < 0) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: Failed to open keylog file %s: %s",
+			   filename, strerror(errno));
+		return;
+	}
+
+	/* Assume less than _POSIX_PIPE_BUF (512) where writes are guaranteed
+	 * to be atomic for O_APPEND. */
+	iov[0].iov_base = (void *) line;
+	iov[0].iov_len = os_strlen(line);
+	iov[1].iov_base = "\n";
+	iov[1].iov_len = 1;
+
+	if (writev(fd, iov, ARRAY_SIZE(iov)) < 01) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Failed to write to keylog file %s: %s",
+			   filename, strerror(errno));
+	}
+
+	close(fd);
+}
+#endif
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+struct tls_connection * tls_connection_init(void *ssl_ctx)
+{
+	struct tls_data *data = ssl_ctx;
+	SSL_CTX *ssl = data->ssl;
+	struct tls_connection *conn;
+	long options;
+	X509_STORE *new_cert_store;
+	struct os_reltime now;
+	struct tls_context *context = SSL_CTX_get_app_data(ssl);
+
+	/* Replace X509 store if it is time to update CRL. */
+	if (data->crl_reload_interval > 0 && os_get_reltime(&now) == 0 &&
+	    os_reltime_expired(&now, &data->crl_last_reload,
+			       data->crl_reload_interval)) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: Flushing X509 store with ca_cert file");
+		new_cert_store = tls_crl_cert_reload(data->ca_cert,
+						     data->check_crl);
+		if (!new_cert_store) {
+			wpa_printf(MSG_ERROR,
+				   "OpenSSL: Error replacing X509 store with ca_cert file");
+		} else {
+			/* Replace old store */
+			SSL_CTX_set_cert_store(ssl, new_cert_store);
+			data->crl_last_reload = now;
+		}
+	}
+
+	conn = os_zalloc(sizeof(*conn));
+	if (conn == NULL)
+		return NULL;
+	conn->data = data;
+	conn->ssl_ctx = ssl;
+	conn->ssl = SSL_new(ssl);
+	if (conn->ssl == NULL) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Failed to initialize new SSL connection");
+		os_free(conn);
+		return NULL;
+	}
+
+	conn->context = context;
+	SSL_set_app_data(conn->ssl, conn);
+	SSL_set_msg_callback(conn->ssl, tls_msg_cb);
+	SSL_set_msg_callback_arg(conn->ssl, conn);
+	options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
+		SSL_OP_SINGLE_DH_USE;
+#ifdef SSL_OP_NO_COMPRESSION
+	options |= SSL_OP_NO_COMPRESSION;
+#endif /* SSL_OP_NO_COMPRESSION */
+	SSL_set_options(conn->ssl, options);
+#ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT
+	/* Hopefully there is no need for middlebox compatibility mechanisms
+	 * when going through EAP authentication. */
+	SSL_clear_options(conn->ssl, SSL_OP_ENABLE_MIDDLEBOX_COMPAT);
+#endif
+
+#ifdef CONFIG_TESTING_OPTIONS
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
+	/* Set the keylog file if the admin requested it. */
+	if (getenv("SSLKEYLOGFILE"))
+		SSL_CTX_set_keylog_callback(conn->ssl_ctx, tls_keylog_cb);
+#endif
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	conn->ssl_in = BIO_new(BIO_s_mem());
+	if (!conn->ssl_in) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Failed to create a new BIO for ssl_in");
+		SSL_free(conn->ssl);
+		os_free(conn);
+		return NULL;
+	}
+
+	conn->ssl_out = BIO_new(BIO_s_mem());
+	if (!conn->ssl_out) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Failed to create a new BIO for ssl_out");
+		SSL_free(conn->ssl);
+		BIO_free(conn->ssl_in);
+		os_free(conn);
+		return NULL;
+	}
+
+	SSL_set_bio(conn->ssl, conn->ssl_in, conn->ssl_out);
+
+	return conn;
+}
+
+
+void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
+{
+	if (conn == NULL)
+		return;
+	if (conn->success_data) {
+		/*
+		 * Make sure ssl_clear_bad_session() does not remove this
+		 * session.
+		 */
+		SSL_set_quiet_shutdown(conn->ssl, 1);
+		SSL_shutdown(conn->ssl);
+	}
+	SSL_free(conn->ssl);
+	tls_engine_deinit(conn);
+	os_free(conn->subject_match);
+	os_free(conn->altsubject_match);
+	os_free(conn->suffix_match);
+	os_free(conn->domain_match);
+	os_free(conn->check_cert_subject);
+	os_free(conn->session_ticket);
+	os_free(conn->peer_subject);
+	os_free(conn);
+}
+
+
+int tls_connection_established(void *ssl_ctx, struct tls_connection *conn)
+{
+	return conn ? SSL_is_init_finished(conn->ssl) : 0;
+}
+
+
+char * tls_connection_peer_serial_num(void *tls_ctx,
+				      struct tls_connection *conn)
+{
+	ASN1_INTEGER *ser;
+	char *serial_num;
+	size_t len;
+
+	if (!conn->peer_cert)
+		return NULL;
+
+	ser = X509_get_serialNumber(conn->peer_cert);
+	if (!ser)
+		return NULL;
+
+	len = ASN1_STRING_length(ser) * 2 + 1;
+	serial_num = os_malloc(len);
+	if (!serial_num)
+		return NULL;
+	wpa_snprintf_hex_uppercase(serial_num, len,
+				   ASN1_STRING_get0_data(ser),
+				   ASN1_STRING_length(ser));
+	return serial_num;
+}
+
+
+int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
+{
+	if (conn == NULL)
+		return -1;
+
+	/* Shutdown previous TLS connection without notifying the peer
+	 * because the connection was already terminated in practice
+	 * and "close notify" shutdown alert would confuse AS. */
+	SSL_set_quiet_shutdown(conn->ssl, 1);
+	SSL_shutdown(conn->ssl);
+	return SSL_clear(conn->ssl) == 1 ? 0 : -1;
+}
+
+
+static int tls_match_altsubject_component(X509 *cert, int type,
+					  const char *value, size_t len)
+{
+	GENERAL_NAME *gen;
+	void *ext;
+	int found = 0;
+	stack_index_t i;
+
+	ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+
+	for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
+		gen = sk_GENERAL_NAME_value(ext, i);
+		if (gen->type != type)
+			continue;
+		if (os_strlen((char *) gen->d.ia5->data) == len &&
+		    os_memcmp(value, gen->d.ia5->data, len) == 0)
+			found++;
+	}
+
+	sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
+
+	return found;
+}
+
+
+static int tls_match_altsubject(X509 *cert, const char *match)
+{
+	int type;
+	const char *pos, *end;
+	size_t len;
+
+	pos = match;
+	do {
+		if (os_strncmp(pos, "EMAIL:", 6) == 0) {
+			type = GEN_EMAIL;
+			pos += 6;
+		} else if (os_strncmp(pos, "DNS:", 4) == 0) {
+			type = GEN_DNS;
+			pos += 4;
+		} else if (os_strncmp(pos, "URI:", 4) == 0) {
+			type = GEN_URI;
+			pos += 4;
+		} else {
+			wpa_printf(MSG_INFO, "TLS: Invalid altSubjectName "
+				   "match '%s'", pos);
+			return 0;
+		}
+		end = os_strchr(pos, ';');
+		while (end) {
+			if (os_strncmp(end + 1, "EMAIL:", 6) == 0 ||
+			    os_strncmp(end + 1, "DNS:", 4) == 0 ||
+			    os_strncmp(end + 1, "URI:", 4) == 0)
+				break;
+			end = os_strchr(end + 1, ';');
+		}
+		if (end)
+			len = end - pos;
+		else
+			len = os_strlen(pos);
+		if (tls_match_altsubject_component(cert, type, pos, len) > 0)
+			return 1;
+		pos = end + 1;
+	} while (end);
+
+	return 0;
+}
+
+
+#ifndef CONFIG_NATIVE_WINDOWS
+static int domain_suffix_match(const u8 *val, size_t len, const char *match,
+			       size_t match_len, int full)
+{
+	size_t i;
+
+	/* Check for embedded nuls that could mess up suffix matching */
+	for (i = 0; i < len; i++) {
+		if (val[i] == '\0') {
+			wpa_printf(MSG_DEBUG, "TLS: Embedded null in a string - reject");
+			return 0;
+		}
+	}
+
+	if (match_len > len || (full && match_len != len))
+		return 0;
+
+	if (os_strncasecmp((const char *) val + len - match_len, match,
+			   match_len) != 0)
+		return 0; /* no match */
+
+	if (match_len == len)
+		return 1; /* exact match */
+
+	if (val[len - match_len - 1] == '.')
+		return 1; /* full label match completes suffix match */
+
+	wpa_printf(MSG_DEBUG, "TLS: Reject due to incomplete label match");
+	return 0;
+}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+struct tls_dn_field_order_cnt {
+	u8 cn;
+	u8 c;
+	u8 l;
+	u8 st;
+	u8 o;
+	u8 ou;
+	u8 email;
+};
+
+
+static int get_dn_field_index(const struct tls_dn_field_order_cnt *dn_cnt,
+			      int nid)
+{
+	switch (nid) {
+	case NID_commonName:
+		return dn_cnt->cn;
+	case NID_countryName:
+		return dn_cnt->c;
+	case NID_localityName:
+		return dn_cnt->l;
+	case NID_stateOrProvinceName:
+		return dn_cnt->st;
+	case NID_organizationName:
+		return dn_cnt->o;
+	case NID_organizationalUnitName:
+		return dn_cnt->ou;
+	case NID_pkcs9_emailAddress:
+		return dn_cnt->email;
+	default:
+		wpa_printf(MSG_ERROR,
+			   "TLS: Unknown NID '%d' in check_cert_subject",
+			   nid);
+		return -1;
+	}
+}
+
+
+/**
+ * match_dn_field - Match configuration DN field against Certificate DN field
+ * @cert: Certificate
+ * @nid: NID of DN field
+ * @field: Field name
+ * @value DN field value which is passed from configuration
+ *	e.g., if configuration have C=US and this argument will point to US.
+ * @dn_cnt: DN matching context
+ * Returns: 1 on success and 0 on failure
+ */
+static int match_dn_field(const X509 *cert, int nid, const char *field,
+			  const char *value,
+			  const struct tls_dn_field_order_cnt *dn_cnt)
+{
+	int i, ret = 0, len, config_dn_field_index, match_index = 0;
+	X509_NAME *name;
+
+	len = os_strlen(value);
+	name = X509_get_subject_name((X509 *) cert);
+
+	/* Assign incremented cnt for every field of DN to check DN field in
+	 * right order */
+	config_dn_field_index = get_dn_field_index(dn_cnt, nid);
+	if (config_dn_field_index < 0)
+		return 0;
+
+	/* Fetch value based on NID */
+	for (i = -1; (i = X509_NAME_get_index_by_NID(name, nid, i)) > -1;) {
+		X509_NAME_ENTRY *e;
+		ASN1_STRING *cn;
+
+		e = X509_NAME_get_entry(name, i);
+		if (!e)
+			continue;
+
+		cn = X509_NAME_ENTRY_get_data(e);
+		if (!cn)
+			continue;
+
+		match_index++;
+
+		/* check for more than one DN field with same name */
+		if (match_index != config_dn_field_index)
+			continue;
+
+		/* Check wildcard at the right end side */
+		/* E.g., if OU=develop* mentioned in configuration, allow 'OU'
+		 * of the subject in the client certificate to start with
+		 * 'develop' */
+		if (len > 0 && value[len - 1] == '*') {
+			/* Compare actual certificate DN field value with
+			 * configuration DN field value up to the specified
+			 * length. */
+			ret = ASN1_STRING_length(cn) >= len - 1 &&
+				os_memcmp(ASN1_STRING_get0_data(cn), value,
+					  len - 1) == 0;
+		} else {
+			/* Compare actual certificate DN field value with
+			 * configuration DN field value */
+			ret = ASN1_STRING_length(cn) == len &&
+				os_memcmp(ASN1_STRING_get0_data(cn), value,
+					  len) == 0;
+		}
+		if (!ret) {
+			wpa_printf(MSG_ERROR,
+				   "OpenSSL: Failed to match %s '%s' with certificate DN field value '%s'",
+				   field, value, ASN1_STRING_get0_data(cn));
+		}
+		break;
+	}
+
+	return ret;
+}
+
+
+/**
+ * get_value_from_field - Get value from DN field
+ * @cert: Certificate
+ * @field_str: DN field string which is passed from configuration file (e.g.,
+ *	 C=US)
+ * @dn_cnt: DN matching context
+ * Returns: 1 on success and 0 on failure
+ */
+static int get_value_from_field(const X509 *cert, char *field_str,
+				struct tls_dn_field_order_cnt *dn_cnt)
+{
+	int nid;
+	char *context = NULL, *name, *value;
+
+	if (os_strcmp(field_str, "*") == 0)
+		return 1; /* wildcard matches everything */
+
+	name = str_token(field_str, "=", &context);
+	if (!name)
+		return 0;
+
+	/* Compare all configured DN fields and assign nid based on that to
+	 * fetch correct value from certificate subject */
+	if (os_strcmp(name, "CN") == 0) {
+		nid = NID_commonName;
+		dn_cnt->cn++;
+	} else if(os_strcmp(name, "C") == 0) {
+		nid = NID_countryName;
+		dn_cnt->c++;
+	} else if (os_strcmp(name, "L") == 0) {
+		nid = NID_localityName;
+		dn_cnt->l++;
+	} else if (os_strcmp(name, "ST") == 0) {
+		nid = NID_stateOrProvinceName;
+		dn_cnt->st++;
+	} else if (os_strcmp(name, "O") == 0) {
+		nid = NID_organizationName;
+		dn_cnt->o++;
+	} else if (os_strcmp(name, "OU") == 0) {
+		nid = NID_organizationalUnitName;
+		dn_cnt->ou++;
+	} else if (os_strcmp(name, "emailAddress") == 0) {
+		nid = NID_pkcs9_emailAddress;
+		dn_cnt->email++;
+	} else {
+		wpa_printf(MSG_ERROR,
+			"TLS: Unknown field '%s' in check_cert_subject", name);
+		return 0;
+	}
+
+	value = str_token(field_str, "=", &context);
+	if (!value) {
+		wpa_printf(MSG_ERROR,
+			   "TLS: Distinguished Name field '%s' value is not defined in check_cert_subject",
+			   name);
+		return 0;
+	}
+
+	return match_dn_field(cert, nid, name, value, dn_cnt);
+}
+
+
+/**
+ * tls_match_dn_field - Match subject DN field with check_cert_subject
+ * @cert: Certificate
+ * @match: check_cert_subject string
+ * Returns: Return 1 on success and 0 on failure
+*/
+static int tls_match_dn_field(X509 *cert, const char *match)
+{
+	const char *token, *last = NULL;
+	char field[256];
+	struct tls_dn_field_order_cnt dn_cnt;
+
+	os_memset(&dn_cnt, 0, sizeof(dn_cnt));
+
+	/* Maximum length of each DN field is 255 characters */
+
+	/* Process each '/' delimited field */
+	while ((token = cstr_token(match, "/", &last))) {
+		if (last - token >= (int) sizeof(field)) {
+			wpa_printf(MSG_ERROR,
+				   "OpenSSL: Too long DN matching field value in '%s'",
+				   match);
+			return 0;
+		}
+		os_memcpy(field, token, last - token);
+		field[last - token] = '\0';
+
+		if (!get_value_from_field(cert, field, &dn_cnt)) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: No match for DN '%s'",
+				   field);
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+
+#ifndef CONFIG_NATIVE_WINDOWS
+static int tls_match_suffix_helper(X509 *cert, const char *match,
+				   size_t match_len, int full)
+{
+	GENERAL_NAME *gen;
+	void *ext;
+	int i;
+	stack_index_t j;
+	int dns_name = 0;
+	X509_NAME *name;
+
+	wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s",
+		   full ? "": "suffix ", match);
+
+	ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+
+	for (j = 0; ext && j < sk_GENERAL_NAME_num(ext); j++) {
+		gen = sk_GENERAL_NAME_value(ext, j);
+		if (gen->type != GEN_DNS)
+			continue;
+		dns_name++;
+		wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName",
+				  gen->d.dNSName->data,
+				  gen->d.dNSName->length);
+		if (domain_suffix_match(gen->d.dNSName->data,
+					gen->d.dNSName->length,
+					match, match_len, full) == 1) {
+			wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found",
+				   full ? "Match" : "Suffix match");
+			sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
+			return 1;
+		}
+	}
+	sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
+
+	if (dns_name) {
+		wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched");
+		return 0;
+	}
+
+	name = X509_get_subject_name(cert);
+	i = -1;
+	for (;;) {
+		X509_NAME_ENTRY *e;
+		ASN1_STRING *cn;
+
+		i = X509_NAME_get_index_by_NID(name, NID_commonName, i);
+		if (i == -1)
+			break;
+		e = X509_NAME_get_entry(name, i);
+		if (e == NULL)
+			continue;
+		cn = X509_NAME_ENTRY_get_data(e);
+		if (cn == NULL)
+			continue;
+		wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName",
+				  cn->data, cn->length);
+		if (domain_suffix_match(cn->data, cn->length,
+					match, match_len, full) == 1) {
+			wpa_printf(MSG_DEBUG, "TLS: %s in commonName found",
+				   full ? "Match" : "Suffix match");
+			return 1;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found",
+		   full ? "": "suffix ");
+	return 0;
+}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+static int tls_match_suffix(X509 *cert, const char *match, int full)
+{
+#ifdef CONFIG_NATIVE_WINDOWS
+	/* wincrypt.h has conflicting X509_NAME definition */
+	return -1;
+#else /* CONFIG_NATIVE_WINDOWS */
+	const char *token, *last = NULL;
+
+	/* Process each match alternative separately until a match is found */
+	while ((token = cstr_token(match, ";", &last))) {
+		if (tls_match_suffix_helper(cert, token, last - token, full))
+			return 1;
+	}
+
+	return 0;
+#endif /* CONFIG_NATIVE_WINDOWS */
+}
+
+
+static enum tls_fail_reason openssl_tls_fail_reason(int err)
+{
+	switch (err) {
+	case X509_V_ERR_CERT_REVOKED:
+		return TLS_FAIL_REVOKED;
+	case X509_V_ERR_CERT_NOT_YET_VALID:
+	case X509_V_ERR_CRL_NOT_YET_VALID:
+		return TLS_FAIL_NOT_YET_VALID;
+	case X509_V_ERR_CERT_HAS_EXPIRED:
+	case X509_V_ERR_CRL_HAS_EXPIRED:
+		return TLS_FAIL_EXPIRED;
+	case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+	case X509_V_ERR_UNABLE_TO_GET_CRL:
+	case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:
+	case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+	case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+	case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+	case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+	case X509_V_ERR_CERT_CHAIN_TOO_LONG:
+	case X509_V_ERR_PATH_LENGTH_EXCEEDED:
+	case X509_V_ERR_INVALID_CA:
+		return TLS_FAIL_UNTRUSTED;
+	case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+	case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+	case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+	case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+	case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+	case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+	case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+	case X509_V_ERR_CERT_UNTRUSTED:
+	case X509_V_ERR_CERT_REJECTED:
+		return TLS_FAIL_BAD_CERTIFICATE;
+	default:
+		return TLS_FAIL_UNSPECIFIED;
+	}
+}
+
+
+static struct wpabuf * get_x509_cert(X509 *cert)
+{
+	struct wpabuf *buf;
+	u8 *tmp;
+
+	int cert_len = i2d_X509(cert, NULL);
+	if (cert_len <= 0)
+		return NULL;
+
+	buf = wpabuf_alloc(cert_len);
+	if (buf == NULL)
+		return NULL;
+
+	tmp = wpabuf_put(buf, cert_len);
+	i2d_X509(cert, &tmp);
+	return buf;
+}
+
+
+static void openssl_tls_fail_event(struct tls_connection *conn,
+				   X509 *err_cert, int err, int depth,
+				   const char *subject, const char *err_str,
+				   enum tls_fail_reason reason)
+{
+	union tls_event_data ev;
+	struct wpabuf *cert = NULL;
+	struct tls_context *context = conn->context;
+
+	if (context->event_cb == NULL)
+		return;
+
+	cert = get_x509_cert(err_cert);
+	os_memset(&ev, 0, sizeof(ev));
+	ev.cert_fail.reason = reason != TLS_FAIL_UNSPECIFIED ?
+		reason : openssl_tls_fail_reason(err);
+	ev.cert_fail.depth = depth;
+	ev.cert_fail.subject = subject;
+	ev.cert_fail.reason_txt = err_str;
+	ev.cert_fail.cert = cert;
+	context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
+	wpabuf_free(cert);
+}
+
+
+static int openssl_cert_tod(X509 *cert)
+{
+	CERTIFICATEPOLICIES *ext;
+	stack_index_t i;
+	char buf[100];
+	int res;
+	int tod = 0;
+
+	ext = X509_get_ext_d2i(cert, NID_certificate_policies, NULL, NULL);
+	if (!ext)
+		return 0;
+
+	for (i = 0; i < sk_POLICYINFO_num(ext); i++) {
+		POLICYINFO *policy;
+
+		policy = sk_POLICYINFO_value(ext, i);
+		res = OBJ_obj2txt(buf, sizeof(buf), policy->policyid, 0);
+		if (res < 0 || (size_t) res >= sizeof(buf))
+			continue;
+		wpa_printf(MSG_DEBUG, "OpenSSL: Certificate Policy %s", buf);
+		if (os_strcmp(buf, "1.3.6.1.4.1.40808.1.3.1") == 0)
+			tod = 1; /* TOD-STRICT */
+		else if (os_strcmp(buf, "1.3.6.1.4.1.40808.1.3.2") == 0 && !tod)
+			tod = 2; /* TOD-TOFU */
+	}
+	sk_POLICYINFO_pop_free(ext, POLICYINFO_free);
+
+	return tod;
+}
+
+
+static void openssl_tls_cert_event(struct tls_connection *conn,
+				   X509 *err_cert, int depth,
+				   const char *subject)
+{
+	struct wpabuf *cert = NULL;
+	union tls_event_data ev;
+	struct tls_context *context = conn->context;
+	char *altsubject[TLS_MAX_ALT_SUBJECT];
+	int alt, num_altsubject = 0;
+	GENERAL_NAME *gen;
+	void *ext;
+	stack_index_t i;
+	ASN1_INTEGER *ser;
+	char serial_num[128];
+#ifdef CONFIG_SHA256
+	u8 hash[32];
+#endif /* CONFIG_SHA256 */
+
+	if (context->event_cb == NULL)
+		return;
+
+	os_memset(&ev, 0, sizeof(ev));
+	if (conn->cert_probe || (conn->flags & TLS_CONN_EXT_CERT_CHECK) ||
+	    context->cert_in_cb) {
+		cert = get_x509_cert(err_cert);
+		ev.peer_cert.cert = cert;
+	}
+#ifdef CONFIG_SHA256
+	if (cert) {
+		const u8 *addr[1];
+		size_t len[1];
+		addr[0] = wpabuf_head(cert);
+		len[0] = wpabuf_len(cert);
+		if (sha256_vector(1, addr, len, hash) == 0) {
+			ev.peer_cert.hash = hash;
+			ev.peer_cert.hash_len = sizeof(hash);
+		}
+	}
+#endif /* CONFIG_SHA256 */
+	ev.peer_cert.depth = depth;
+	ev.peer_cert.subject = subject;
+
+	ser = X509_get_serialNumber(err_cert);
+	if (ser) {
+		wpa_snprintf_hex_uppercase(serial_num, sizeof(serial_num),
+					   ASN1_STRING_get0_data(ser),
+					   ASN1_STRING_length(ser));
+		ev.peer_cert.serial_num = serial_num;
+	}
+
+	ext = X509_get_ext_d2i(err_cert, NID_subject_alt_name, NULL, NULL);
+	for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
+		char *pos;
+
+		if (num_altsubject == TLS_MAX_ALT_SUBJECT)
+			break;
+		gen = sk_GENERAL_NAME_value(ext, i);
+		if (gen->type != GEN_EMAIL &&
+		    gen->type != GEN_DNS &&
+		    gen->type != GEN_URI)
+			continue;
+
+		pos = os_malloc(10 + gen->d.ia5->length + 1);
+		if (pos == NULL)
+			break;
+		altsubject[num_altsubject++] = pos;
+
+		switch (gen->type) {
+		case GEN_EMAIL:
+			os_memcpy(pos, "EMAIL:", 6);
+			pos += 6;
+			break;
+		case GEN_DNS:
+			os_memcpy(pos, "DNS:", 4);
+			pos += 4;
+			break;
+		case GEN_URI:
+			os_memcpy(pos, "URI:", 4);
+			pos += 4;
+			break;
+		}
+
+		os_memcpy(pos, gen->d.ia5->data, gen->d.ia5->length);
+		pos += gen->d.ia5->length;
+		*pos = '\0';
+	}
+	sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
+
+	for (alt = 0; alt < num_altsubject; alt++)
+		ev.peer_cert.altsubject[alt] = altsubject[alt];
+	ev.peer_cert.num_altsubject = num_altsubject;
+
+	ev.peer_cert.tod = openssl_cert_tod(err_cert);
+
+	context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
+	wpabuf_free(cert);
+	for (alt = 0; alt < num_altsubject; alt++)
+		os_free(altsubject[alt]);
+}
+
+
+static void debug_print_cert(X509 *cert, const char *title)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+	BIO *out;
+	size_t rlen;
+	char *txt;
+	int res;
+
+	if (wpa_debug_level > MSG_DEBUG)
+		return;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return;
+
+	X509_print(out, cert);
+	rlen = BIO_ctrl_pending(out);
+	txt = os_malloc(rlen + 1);
+	if (txt) {
+		res = BIO_read(out, txt, rlen);
+		if (res > 0) {
+			txt[res] = '\0';
+			wpa_printf(MSG_DEBUG, "OpenSSL: %s\n%s", title, txt);
+		}
+		os_free(txt);
+	}
+
+	BIO_free(out);
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+}
+
+
+static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
+{
+	char buf[256];
+	X509 *err_cert;
+	int err, depth;
+	SSL *ssl;
+	struct tls_connection *conn;
+	struct tls_context *context;
+	char *match, *altmatch, *suffix_match, *domain_match;
+	const char *check_cert_subject;
+	const char *err_str;
+
+	err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
+	if (!err_cert)
+		return 0;
+
+	err = X509_STORE_CTX_get_error(x509_ctx);
+	depth = X509_STORE_CTX_get_error_depth(x509_ctx);
+	ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
+					 SSL_get_ex_data_X509_STORE_CTX_idx());
+	os_snprintf(buf, sizeof(buf), "Peer certificate - depth %d", depth);
+	debug_print_cert(err_cert, buf);
+	X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
+
+	conn = SSL_get_app_data(ssl);
+	if (conn == NULL)
+		return 0;
+
+	if (depth == 0)
+		conn->peer_cert = err_cert;
+	else if (depth == 1)
+		conn->peer_issuer = err_cert;
+	else if (depth == 2)
+		conn->peer_issuer_issuer = err_cert;
+
+	context = conn->context;
+	match = conn->subject_match;
+	altmatch = conn->altsubject_match;
+	suffix_match = conn->suffix_match;
+	domain_match = conn->domain_match;
+
+	if (!preverify_ok && !conn->ca_cert_verify)
+		preverify_ok = 1;
+	if (!preverify_ok && depth > 0 && conn->server_cert_only)
+		preverify_ok = 1;
+	if (!preverify_ok && (conn->flags & TLS_CONN_DISABLE_TIME_CHECKS) &&
+	    (err == X509_V_ERR_CERT_HAS_EXPIRED ||
+	     err == X509_V_ERR_CERT_NOT_YET_VALID)) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Ignore certificate validity "
+			   "time mismatch");
+		preverify_ok = 1;
+	}
+	if (!preverify_ok && !conn->data->check_crl_strict &&
+	    (err == X509_V_ERR_CRL_HAS_EXPIRED ||
+	     err == X509_V_ERR_CRL_NOT_YET_VALID)) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Ignore certificate validity CRL time mismatch");
+		preverify_ok = 1;
+	}
+
+	err_str = X509_verify_cert_error_string(err);
+
+#ifdef CONFIG_SHA256
+	/*
+	 * Do not require preverify_ok so we can explicity allow otherwise
+	 * invalid pinned server certificates.
+	 */
+	if (depth == 0 && conn->server_cert_only) {
+		struct wpabuf *cert;
+		cert = get_x509_cert(err_cert);
+		if (!cert) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: Could not fetch "
+				   "server certificate data");
+			preverify_ok = 0;
+		} else {
+			u8 hash[32];
+			const u8 *addr[1];
+			size_t len[1];
+			addr[0] = wpabuf_head(cert);
+			len[0] = wpabuf_len(cert);
+			if (sha256_vector(1, addr, len, hash) < 0 ||
+			    os_memcmp(conn->srv_cert_hash, hash, 32) != 0) {
+				err_str = "Server certificate mismatch";
+				err = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
+				preverify_ok = 0;
+			} else if (!preverify_ok) {
+				/*
+				 * Certificate matches pinned certificate, allow
+				 * regardless of other problems.
+				 */
+				wpa_printf(MSG_DEBUG,
+					   "OpenSSL: Ignore validation issues for a pinned server certificate");
+				preverify_ok = 1;
+			}
+			wpabuf_free(cert);
+		}
+	}
+#endif /* CONFIG_SHA256 */
+
+	openssl_tls_cert_event(conn, err_cert, depth, buf);
+
+	if (!preverify_ok) {
+		if (depth > 0) {
+			/* Send cert event for the peer certificate so that
+			 * the upper layers get information about it even if
+			 * validation of a CA certificate fails. */
+			STACK_OF(X509) *chain;
+
+			chain = X509_STORE_CTX_get1_chain(x509_ctx);
+			if (chain && sk_X509_num(chain) > 0) {
+				char buf2[256];
+				X509 *cert;
+
+				cert = sk_X509_value(chain, 0);
+				X509_NAME_oneline(X509_get_subject_name(cert),
+						  buf2, sizeof(buf2));
+
+				openssl_tls_cert_event(conn, cert, 0, buf2);
+			}
+			if (chain)
+				sk_X509_pop_free(chain, X509_free);
+		}
+
+		wpa_printf(MSG_WARNING, "TLS: Certificate verification failed,"
+			   " error %d (%s) depth %d for '%s'", err, err_str,
+			   depth, buf);
+		openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       err_str, TLS_FAIL_UNSPECIFIED);
+		return preverify_ok;
+	}
+
+	wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - preverify_ok=%d "
+		   "err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'",
+		   preverify_ok, err, err_str,
+		   conn->ca_cert_verify, depth, buf);
+	check_cert_subject = conn->check_cert_subject;
+	if (!check_cert_subject)
+		check_cert_subject = conn->data->check_cert_subject;
+	if (check_cert_subject) {
+		if (depth == 0 &&
+		    !tls_match_dn_field(err_cert, check_cert_subject)) {
+			preverify_ok = 0;
+			openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+					       "Distinguished Name",
+					       TLS_FAIL_DN_MISMATCH);
+		}
+	}
+	if (depth == 0 && match && os_strstr(buf, match) == NULL) {
+		wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not "
+			   "match with '%s'", buf, match);
+		preverify_ok = 0;
+		openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       "Subject mismatch",
+				       TLS_FAIL_SUBJECT_MISMATCH);
+	} else if (depth == 0 && altmatch &&
+		   !tls_match_altsubject(err_cert, altmatch)) {
+		wpa_printf(MSG_WARNING, "TLS: altSubjectName match "
+			   "'%s' not found", altmatch);
+		preverify_ok = 0;
+		openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       "AltSubject mismatch",
+				       TLS_FAIL_ALTSUBJECT_MISMATCH);
+	} else if (depth == 0 && suffix_match &&
+		   !tls_match_suffix(err_cert, suffix_match, 0)) {
+		wpa_printf(MSG_WARNING, "TLS: Domain suffix match '%s' not found",
+			   suffix_match);
+		preverify_ok = 0;
+		openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       "Domain suffix mismatch",
+				       TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
+	} else if (depth == 0 && domain_match &&
+		   !tls_match_suffix(err_cert, domain_match, 1)) {
+		wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found",
+			   domain_match);
+		preverify_ok = 0;
+		openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       "Domain mismatch",
+				       TLS_FAIL_DOMAIN_MISMATCH);
+	}
+
+	if (conn->cert_probe && preverify_ok && depth == 0) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Reject server certificate "
+			   "on probe-only run");
+		preverify_ok = 0;
+		openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       "Server certificate chain probe",
+				       TLS_FAIL_SERVER_CHAIN_PROBE);
+	}
+
+#ifdef CONFIG_SUITEB
+	if (conn->flags & TLS_CONN_SUITEB) {
+		EVP_PKEY *pk;
+		int len = -1;
+
+		pk = X509_get_pubkey(err_cert);
+		if (pk) {
+			len = EVP_PKEY_bits(pk);
+			EVP_PKEY_free(pk);
+		}
+
+		if (len >= 0) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: RSA modulus size: %d bits", len);
+			if (len < 3072) {
+				preverify_ok = 0;
+				openssl_tls_fail_event(
+					conn, err_cert, err,
+					depth, buf,
+					"Insufficient RSA modulus size",
+					TLS_FAIL_INSUFFICIENT_KEY_LEN);
+			}
+		}
+	}
+#endif /* CONFIG_SUITEB */
+
+#ifdef OPENSSL_IS_BORINGSSL
+	if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) &&
+	    preverify_ok) {
+		enum ocsp_result res;
+
+		res = check_ocsp_resp(conn->ssl_ctx, conn->ssl, err_cert,
+				      conn->peer_issuer,
+				      conn->peer_issuer_issuer);
+		if (res == OCSP_REVOKED) {
+			preverify_ok = 0;
+			openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+					       "certificate revoked",
+					       TLS_FAIL_REVOKED);
+			if (err == X509_V_OK)
+				X509_STORE_CTX_set_error(
+					x509_ctx, X509_V_ERR_CERT_REVOKED);
+		} else if (res != OCSP_GOOD &&
+			   (conn->flags & TLS_CONN_REQUIRE_OCSP)) {
+			preverify_ok = 0;
+			openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+					       "bad certificate status response",
+					       TLS_FAIL_UNSPECIFIED);
+		}
+	}
+#endif /* OPENSSL_IS_BORINGSSL */
+
+	if (depth == 0 && preverify_ok && context->event_cb != NULL)
+		context->event_cb(context->cb_ctx,
+				  TLS_CERT_CHAIN_SUCCESS, NULL);
+
+	if (depth == 0 && preverify_ok) {
+		os_free(conn->peer_subject);
+		conn->peer_subject = os_strdup(buf);
+	}
+
+	return preverify_ok;
+}
+
+
+#ifndef OPENSSL_NO_STDIO
+static int tls_load_ca_der(struct tls_data *data, const char *ca_cert)
+{
+	SSL_CTX *ssl_ctx = data->ssl;
+	X509_LOOKUP *lookup;
+	int ret = 0;
+
+	lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(ssl_ctx),
+				       X509_LOOKUP_file());
+	if (lookup == NULL) {
+		tls_show_errors(MSG_WARNING, __func__,
+				"Failed add lookup for X509 store");
+		return -1;
+	}
+
+	if (!X509_LOOKUP_load_file(lookup, ca_cert, X509_FILETYPE_ASN1)) {
+		unsigned long err = ERR_peek_error();
+		tls_show_errors(MSG_WARNING, __func__,
+				"Failed load CA in DER format");
+		if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
+		    ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring "
+				   "cert already in hash table error",
+				   __func__);
+		} else
+			ret = -1;
+	}
+
+	return ret;
+}
+#endif /* OPENSSL_NO_STDIO */
+
+
+static int tls_connection_ca_cert(struct tls_data *data,
+				  struct tls_connection *conn,
+				  const char *ca_cert, const u8 *ca_cert_blob,
+				  size_t ca_cert_blob_len, const char *ca_path)
+{
+	SSL_CTX *ssl_ctx = data->ssl;
+	X509_STORE *store;
+
+	/*
+	 * Remove previously configured trusted CA certificates before adding
+	 * new ones.
+	 */
+	store = X509_STORE_new();
+	if (store == NULL) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new "
+			   "certificate store", __func__);
+		return -1;
+	}
+	SSL_CTX_set_cert_store(ssl_ctx, store);
+
+	SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
+	conn->ca_cert_verify = 1;
+
+	if (ca_cert && os_strncmp(ca_cert, "probe://", 8) == 0) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Probe for server certificate "
+			   "chain");
+		conn->cert_probe = 1;
+		conn->ca_cert_verify = 0;
+		return 0;
+	}
+
+	if (ca_cert && os_strncmp(ca_cert, "hash://", 7) == 0) {
+#ifdef CONFIG_SHA256
+		const char *pos = ca_cert + 7;
+		if (os_strncmp(pos, "server/sha256/", 14) != 0) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: Unsupported ca_cert "
+				   "hash value '%s'", ca_cert);
+			return -1;
+		}
+		pos += 14;
+		if (os_strlen(pos) != 32 * 2) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: Unexpected SHA256 "
+				   "hash length in ca_cert '%s'", ca_cert);
+			return -1;
+		}
+		if (hexstr2bin(pos, conn->srv_cert_hash, 32) < 0) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: Invalid SHA256 hash "
+				   "value in ca_cert '%s'", ca_cert);
+			return -1;
+		}
+		conn->server_cert_only = 1;
+		wpa_printf(MSG_DEBUG, "OpenSSL: Checking only server "
+			   "certificate match");
+		return 0;
+#else /* CONFIG_SHA256 */
+		wpa_printf(MSG_INFO, "No SHA256 included in the build - "
+			   "cannot validate server certificate hash");
+		return -1;
+#endif /* CONFIG_SHA256 */
+	}
+
+	if (ca_cert_blob) {
+		X509 *cert = d2i_X509(NULL,
+				      (const unsigned char **) &ca_cert_blob,
+				      ca_cert_blob_len);
+		if (cert == NULL) {
+			BIO *bio = BIO_new_mem_buf(ca_cert_blob,
+						   ca_cert_blob_len);
+
+			if (bio) {
+				cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+				BIO_free(bio);
+			}
+
+			if (!cert) {
+				tls_show_errors(MSG_WARNING, __func__,
+						"Failed to parse ca_cert_blob");
+				return -1;
+			}
+
+			while (ERR_get_error()) {
+				/* Ignore errors from DER conversion. */
+			}
+		}
+
+		if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx),
+					 cert)) {
+			unsigned long err = ERR_peek_error();
+			tls_show_errors(MSG_WARNING, __func__,
+					"Failed to add ca_cert_blob to "
+					"certificate store");
+			if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
+			    ERR_GET_REASON(err) ==
+			    X509_R_CERT_ALREADY_IN_HASH_TABLE) {
+				wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring "
+					   "cert already in hash table error",
+					   __func__);
+			} else {
+				X509_free(cert);
+				return -1;
+			}
+		}
+		X509_free(cert);
+		wpa_printf(MSG_DEBUG, "OpenSSL: %s - added ca_cert_blob "
+			   "to certificate store", __func__);
+		return 0;
+	}
+
+#ifdef ANDROID
+	/* Single alias */
+	if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) {
+		if (tls_add_ca_from_keystore(SSL_CTX_get_cert_store(ssl_ctx),
+					     &ca_cert[11]) < 0)
+			return -1;
+		SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
+		return 0;
+	}
+
+	/* Multiple aliases separated by space */
+	if (ca_cert && os_strncmp("keystores://", ca_cert, 12) == 0) {
+		char *aliases = os_strdup(&ca_cert[12]);
+		const char *delim = " ";
+		int rc = 0;
+		char *savedptr;
+		char *alias;
+
+		if (!aliases)
+			return -1;
+		alias = strtok_r(aliases, delim, &savedptr);
+		for (; alias; alias = strtok_r(NULL, delim, &savedptr)) {
+			if (tls_add_ca_from_keystore_encoded(
+				    SSL_CTX_get_cert_store(ssl_ctx), alias)) {
+				wpa_printf(MSG_WARNING,
+					   "OpenSSL: %s - Failed to add ca_cert %s from keystore",
+					   __func__, alias);
+				rc = -1;
+				break;
+			}
+		}
+		os_free(aliases);
+		if (rc)
+			return rc;
+
+		SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
+		return 0;
+	}
+#endif /* ANDROID */
+
+#ifdef CONFIG_NATIVE_WINDOWS
+	if (ca_cert && tls_cryptoapi_ca_cert(ssl_ctx, conn->ssl, ca_cert) ==
+	    0) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Added CA certificates from "
+			   "system certificate store");
+		return 0;
+	}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+	if (ca_cert || ca_path) {
+#ifndef OPENSSL_NO_STDIO
+		if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, ca_path) !=
+		    1) {
+			tls_show_errors(MSG_WARNING, __func__,
+					"Failed to load root certificates");
+			if (ca_cert &&
+			    tls_load_ca_der(data, ca_cert) == 0) {
+				wpa_printf(MSG_DEBUG, "OpenSSL: %s - loaded "
+					   "DER format CA certificate",
+					   __func__);
+			} else
+				return -1;
+		} else {
+			wpa_printf(MSG_DEBUG, "TLS: Trusted root "
+				   "certificate(s) loaded");
+			tls_get_errors(data);
+		}
+#else /* OPENSSL_NO_STDIO */
+		wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO",
+			   __func__);
+		return -1;
+#endif /* OPENSSL_NO_STDIO */
+	} else {
+		/* No ca_cert configured - do not try to verify server
+		 * certificate */
+		conn->ca_cert_verify = 0;
+	}
+
+	return 0;
+}
+
+
+static int tls_global_ca_cert(struct tls_data *data, const char *ca_cert)
+{
+	SSL_CTX *ssl_ctx = data->ssl;
+
+	if (ca_cert) {
+		if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1)
+		{
+			tls_show_errors(MSG_WARNING, __func__,
+					"Failed to load root certificates");
+			return -1;
+		}
+
+		wpa_printf(MSG_DEBUG, "TLS: Trusted root "
+			   "certificate(s) loaded");
+
+#ifndef OPENSSL_NO_STDIO
+		/* Add the same CAs to the client certificate requests */
+		SSL_CTX_set_client_CA_list(ssl_ctx,
+					   SSL_load_client_CA_file(ca_cert));
+#endif /* OPENSSL_NO_STDIO */
+
+		os_free(data->ca_cert);
+		data->ca_cert = os_strdup(ca_cert);
+	}
+
+	return 0;
+}
+
+
+int tls_global_set_verify(void *ssl_ctx, int check_crl, int strict)
+{
+	int flags;
+
+	if (check_crl) {
+		struct tls_data *data = ssl_ctx;
+		X509_STORE *cs = SSL_CTX_get_cert_store(data->ssl);
+		if (cs == NULL) {
+			tls_show_errors(MSG_INFO, __func__, "Failed to get "
+					"certificate store when enabling "
+					"check_crl");
+			return -1;
+		}
+		flags = X509_V_FLAG_CRL_CHECK;
+		if (check_crl == 2)
+			flags |= X509_V_FLAG_CRL_CHECK_ALL;
+		X509_STORE_set_flags(cs, flags);
+
+		data->check_crl = check_crl;
+		data->check_crl_strict = strict;
+		os_get_reltime(&data->crl_last_reload);
+	}
+	return 0;
+}
+
+
+static int tls_connection_set_subject_match(struct tls_connection *conn,
+					    const char *subject_match,
+					    const char *altsubject_match,
+					    const char *suffix_match,
+					    const char *domain_match,
+					    const char *check_cert_subject)
+{
+	os_free(conn->subject_match);
+	conn->subject_match = NULL;
+	if (subject_match) {
+		conn->subject_match = os_strdup(subject_match);
+		if (conn->subject_match == NULL)
+			return -1;
+	}
+
+	os_free(conn->altsubject_match);
+	conn->altsubject_match = NULL;
+	if (altsubject_match) {
+		conn->altsubject_match = os_strdup(altsubject_match);
+		if (conn->altsubject_match == NULL)
+			return -1;
+	}
+
+	os_free(conn->suffix_match);
+	conn->suffix_match = NULL;
+	if (suffix_match) {
+		conn->suffix_match = os_strdup(suffix_match);
+		if (conn->suffix_match == NULL)
+			return -1;
+	}
+
+	os_free(conn->domain_match);
+	conn->domain_match = NULL;
+	if (domain_match) {
+		conn->domain_match = os_strdup(domain_match);
+		if (conn->domain_match == NULL)
+			return -1;
+	}
+
+	os_free(conn->check_cert_subject);
+	conn->check_cert_subject = NULL;
+	if (check_cert_subject) {
+		conn->check_cert_subject = os_strdup(check_cert_subject);
+		if (!conn->check_cert_subject)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+#ifdef CONFIG_SUITEB
+static int suiteb_cert_cb(SSL *ssl, void *arg)
+{
+	struct tls_connection *conn = arg;
+
+	/*
+	 * This cert_cb() is not really the best location for doing a
+	 * constraint check for the ServerKeyExchange message, but this seems to
+	 * be the only place where the current OpenSSL sequence can be
+	 * terminated cleanly with an TLS alert going out to the server.
+	 */
+
+	if (!(conn->flags & TLS_CONN_SUITEB))
+		return 1;
+
+	/* DHE is enabled only with DHE-RSA-AES256-GCM-SHA384 */
+	if (conn->cipher_suite != 0x9f)
+		return 1;
+
+	if (conn->server_dh_prime_len >= 3072)
+		return 1;
+
+	wpa_printf(MSG_DEBUG,
+		   "OpenSSL: Server DH prime length (%d bits) not sufficient for Suite B RSA - reject handshake",
+		   conn->server_dh_prime_len);
+	return 0;
+}
+#endif /* CONFIG_SUITEB */
+
+
+static int tls_set_conn_flags(struct tls_connection *conn, unsigned int flags,
+			      const char *openssl_ciphers)
+{
+	SSL *ssl = conn->ssl;
+
+#ifdef SSL_OP_NO_TICKET
+	if (flags & TLS_CONN_DISABLE_SESSION_TICKET)
+		SSL_set_options(ssl, SSL_OP_NO_TICKET);
+	else
+		SSL_clear_options(ssl, SSL_OP_NO_TICKET);
+#endif /* SSL_OP_NO_TICKET */
+
+#ifdef SSL_OP_LEGACY_SERVER_CONNECT
+	if (flags & TLS_CONN_ALLOW_UNSAFE_RENEGOTIATION)
+		SSL_set_options(ssl, SSL_OP_LEGACY_SERVER_CONNECT);
+#endif /* SSL_OP_LEGACY_SERVER_CONNECT */
+
+#ifdef SSL_OP_NO_TLSv1
+	if (flags & TLS_CONN_DISABLE_TLSv1_0)
+		SSL_set_options(ssl, SSL_OP_NO_TLSv1);
+	else
+		SSL_clear_options(ssl, SSL_OP_NO_TLSv1);
+#endif /* SSL_OP_NO_TLSv1 */
+#ifdef SSL_OP_NO_TLSv1_1
+	if (flags & TLS_CONN_DISABLE_TLSv1_1)
+		SSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
+	else
+		SSL_clear_options(ssl, SSL_OP_NO_TLSv1_1);
+#endif /* SSL_OP_NO_TLSv1_1 */
+#ifdef SSL_OP_NO_TLSv1_2
+	if (flags & TLS_CONN_DISABLE_TLSv1_2)
+		SSL_set_options(ssl, SSL_OP_NO_TLSv1_2);
+	else
+		SSL_clear_options(ssl, SSL_OP_NO_TLSv1_2);
+#endif /* SSL_OP_NO_TLSv1_2 */
+#ifdef SSL_OP_NO_TLSv1_3
+	if (flags & TLS_CONN_DISABLE_TLSv1_3)
+		SSL_set_options(ssl, SSL_OP_NO_TLSv1_3);
+	else
+		SSL_clear_options(ssl, SSL_OP_NO_TLSv1_3);
+#endif /* SSL_OP_NO_TLSv1_3 */
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+	if (flags & (TLS_CONN_ENABLE_TLSv1_0 |
+		     TLS_CONN_ENABLE_TLSv1_1 |
+		     TLS_CONN_ENABLE_TLSv1_2)) {
+		int version = 0;
+
+		/* Explicit request to enable TLS versions even if needing to
+		 * override systemwide policies. */
+		if (flags & TLS_CONN_ENABLE_TLSv1_0)
+			version = TLS1_VERSION;
+		else if (flags & TLS_CONN_ENABLE_TLSv1_1)
+			version = TLS1_1_VERSION;
+		else if (flags & TLS_CONN_ENABLE_TLSv1_2)
+			version = TLS1_2_VERSION;
+		if (!version) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: Invalid TLS version configuration");
+			return -1;
+		}
+
+		if (SSL_set_min_proto_version(ssl, version) != 1) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: Failed to set minimum TLS version");
+			return -1;
+		}
+	}
+#endif /* >= 1.1.0 */
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
+	!defined(LIBRESSL_VERSION_NUMBER) && \
+	!defined(OPENSSL_IS_BORINGSSL)
+	{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+		int need_level = 0;
+#else
+		int need_level = 1;
+#endif
+
+		if ((flags &
+		     (TLS_CONN_ENABLE_TLSv1_0 | TLS_CONN_ENABLE_TLSv1_1)) &&
+		    SSL_get_security_level(ssl) > need_level) {
+			/*
+			 * Need to drop to security level 1 (or 0  with OpenSSL
+			 * 3.0) to allow TLS versions older than 1.2 to be used
+			 * when explicitly enabled in configuration.
+			 */
+			SSL_set_security_level(conn->ssl, need_level);
+		}
+	}
+#endif
+
+	if (!openssl_ciphers)
+		openssl_ciphers = conn->data->openssl_ciphers;
+
+#ifdef CONFIG_SUITEB
+#ifdef OPENSSL_IS_BORINGSSL
+	/* Start with defaults from BoringSSL */
+	SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, NULL, 0);
+#endif /* OPENSSL_IS_BORINGSSL */
+	if (flags & TLS_CONN_SUITEB_NO_ECDH) {
+		const char *ciphers = "DHE-RSA-AES256-GCM-SHA384";
+
+		if (openssl_ciphers) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: Override ciphers for Suite B (no ECDH): %s",
+				   openssl_ciphers);
+			ciphers = openssl_ciphers;
+		}
+		if (SSL_set_cipher_list(ssl, ciphers) != 1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set Suite B ciphers");
+			return -1;
+		}
+	} else if (flags & TLS_CONN_SUITEB) {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+		EC_KEY *ecdh;
+#endif
+		const char *ciphers =
+			"ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384";
+		int nid[1] = { NID_secp384r1 };
+
+		if (openssl_ciphers) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: Override ciphers for Suite B: %s",
+				   openssl_ciphers);
+			ciphers = openssl_ciphers;
+		}
+		if (SSL_set_cipher_list(ssl, ciphers) != 1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set Suite B ciphers");
+			return -1;
+		}
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+		if (SSL_set1_groups(ssl, nid, 1) != 1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set Suite B groups");
+			return -1;
+		}
+
+#else
+		if (SSL_set1_curves(ssl, nid, 1) != 1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set Suite B curves");
+			return -1;
+		}
+
+		ecdh = EC_KEY_new_by_curve_name(NID_secp384r1);
+		if (!ecdh || SSL_set_tmp_ecdh(ssl, ecdh) != 1) {
+			EC_KEY_free(ecdh);
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set ECDH parameter");
+			return -1;
+		}
+		EC_KEY_free(ecdh);
+#endif
+	}
+	if (flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) {
+#ifdef OPENSSL_IS_BORINGSSL
+		uint16_t sigalgs[3] = { SSL_SIGN_RSA_PKCS1_SHA384 };
+		int num = 1;
+
+		if (!(flags & TLS_CONN_DISABLE_TLSv1_3)) {
+#ifdef SSL_SIGN_ECDSA_SECP384R1_SHA384
+			sigalgs[num++] = SSL_SIGN_ECDSA_SECP384R1_SHA384;
+#endif
+#ifdef SSL_SIGN_RSA_PSS_RSAE_SHA384
+			sigalgs[num++] = SSL_SIGN_RSA_PSS_RSAE_SHA384;
+#endif
+		}
+
+		if (SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, sigalgs,
+						       num) != 1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set Suite B sigalgs");
+			return -1;
+		}
+#else /* OPENSSL_IS_BORINGSSL */
+		/* ECDSA+SHA384 if need to add EC support here */
+		const char *algs = "RSA+SHA384";
+
+		if (!(flags & TLS_CONN_DISABLE_TLSv1_3))
+			algs = "RSA+SHA384:ecdsa_secp384r1_sha384:rsa_pss_rsae_sha384";
+		if (SSL_set1_sigalgs_list(ssl, algs) != 1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set Suite B sigalgs");
+			return -1;
+		}
+#endif /* OPENSSL_IS_BORINGSSL */
+
+		SSL_set_options(ssl, SSL_OP_NO_TLSv1);
+		SSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
+		SSL_set_cert_cb(ssl, suiteb_cert_cb, conn);
+	}
+
+#ifdef OPENSSL_IS_BORINGSSL
+	if (openssl_ciphers && os_strcmp(openssl_ciphers, "SUITEB192") == 0) {
+		uint16_t sigalgs[1] = { SSL_SIGN_ECDSA_SECP384R1_SHA384 };
+		int nid[1] = { NID_secp384r1 };
+
+		if (SSL_set1_curves(ssl, nid, 1) != 1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set Suite B curves");
+			return -1;
+		}
+
+		if (SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, sigalgs,
+						       1) != 1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set Suite B sigalgs");
+			return -1;
+		}
+	}
+#else /* OPENSSL_IS_BORINGSSL */
+	if (!(flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) &&
+	    openssl_ciphers && SSL_set_cipher_list(ssl, openssl_ciphers) != 1) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: Failed to set openssl_ciphers '%s'",
+			   openssl_ciphers);
+		return -1;
+	}
+#endif /* OPENSSL_IS_BORINGSSL */
+#else /* CONFIG_SUITEB */
+	if (openssl_ciphers && SSL_set_cipher_list(ssl, openssl_ciphers) != 1) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: Failed to set openssl_ciphers '%s'",
+			   openssl_ciphers);
+		return -1;
+	}
+#endif /* CONFIG_SUITEB */
+
+	if (flags & TLS_CONN_TEAP_ANON_DH) {
+#ifndef TEAP_DH_ANON_CS
+#define TEAP_DH_ANON_CS \
+	"ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:" \
+	"ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:" \
+	"ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:" \
+	"DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:" \
+	"DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:" \
+	"DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:" \
+	"ADH-AES256-GCM-SHA384:ADH-AES128-GCM-SHA256:" \
+	"ADH-AES256-SHA256:ADH-AES128-SHA256:ADH-AES256-SHA:ADH-AES128-SHA"
+#endif
+		static const char *cs = TEAP_DH_ANON_CS;
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
+	!defined(LIBRESSL_VERSION_NUMBER) && \
+	!defined(OPENSSL_IS_BORINGSSL)
+		/*
+		 * Need to drop to security level 0 to allow anonymous
+		 * cipher suites for EAP-TEAP.
+		 */
+		SSL_set_security_level(conn->ssl, 0);
+#endif
+
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Enable cipher suites for anonymous EAP-TEAP provisioning: %s",
+			   cs);
+		if (SSL_set_cipher_list(conn->ssl, cs) != 1) {
+			tls_show_errors(MSG_INFO, __func__,
+					"Cipher suite configuration failed");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
+			      int verify_peer, unsigned int flags,
+			      const u8 *session_ctx, size_t session_ctx_len)
+{
+	static int counter = 0;
+	struct tls_data *data = ssl_ctx;
+
+	if (conn == NULL)
+		return -1;
+
+	if (verify_peer == 2) {
+		conn->ca_cert_verify = 1;
+		SSL_set_verify(conn->ssl, SSL_VERIFY_PEER |
+			       SSL_VERIFY_CLIENT_ONCE, tls_verify_cb);
+	} else if (verify_peer) {
+		conn->ca_cert_verify = 1;
+		SSL_set_verify(conn->ssl, SSL_VERIFY_PEER |
+			       SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
+			       SSL_VERIFY_CLIENT_ONCE, tls_verify_cb);
+	} else {
+		conn->ca_cert_verify = 0;
+		SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
+	}
+
+	if (tls_set_conn_flags(conn, flags, NULL) < 0)
+		return -1;
+	conn->flags = flags;
+
+	SSL_set_accept_state(conn->ssl);
+
+	if (data->tls_session_lifetime == 0) {
+		/*
+		 * Set session id context to a unique value to make sure
+		 * session resumption cannot be used either through session
+		 * caching or TLS ticket extension.
+		 */
+		counter++;
+		SSL_set_session_id_context(conn->ssl,
+					   (const unsigned char *) &counter,
+					   sizeof(counter));
+	} else if (session_ctx) {
+		SSL_set_session_id_context(conn->ssl, session_ctx,
+					   session_ctx_len);
+	}
+
+	return 0;
+}
+
+
+static int tls_connection_client_cert(struct tls_connection *conn,
+				      const char *client_cert,
+				      const u8 *client_cert_blob,
+				      size_t client_cert_blob_len)
+{
+	if (client_cert == NULL && client_cert_blob == NULL)
+		return 0;
+
+#ifdef PKCS12_FUNCS
+#ifdef LIBRESSL_VERSION_NUMBER
+	/*
+	 * Clear previously set extra chain certificates, if any, from PKCS#12
+	 * processing in tls_parse_pkcs12() to allow LibreSSL to build a new
+	 * chain properly.
+	 */
+	SSL_CTX_clear_extra_chain_certs(conn->ssl_ctx);
+#endif /* LIBRESSL_VERSION_NUMBER */
+#endif /* PKCS12_FUNCS */
+
+	if (client_cert_blob &&
+	    SSL_use_certificate_ASN1(conn->ssl, (u8 *) client_cert_blob,
+				     client_cert_blob_len) == 1) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_ASN1 --> "
+			   "OK");
+		return 0;
+	} else if (client_cert_blob) {
+#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20901000L
+		tls_show_errors(MSG_DEBUG, __func__,
+				"SSL_use_certificate_ASN1 failed");
+#else
+		BIO *bio;
+		X509 *x509;
+
+		tls_show_errors(MSG_DEBUG, __func__,
+				"SSL_use_certificate_ASN1 failed");
+		bio = BIO_new(BIO_s_mem());
+		if (!bio)
+			return -1;
+		BIO_write(bio, client_cert_blob, client_cert_blob_len);
+		x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+		if (!x509 || SSL_use_certificate(conn->ssl, x509) != 1) {
+			X509_free(x509);
+			BIO_free(bio);
+			return -1;
+		}
+		X509_free(x509);
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Found PEM encoded certificate from blob");
+		while ((x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL))) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: Added an additional certificate into the chain");
+			SSL_add0_chain_cert(conn->ssl, x509);
+		}
+		BIO_free(bio);
+		return 0;
+#endif
+	}
+
+	if (client_cert == NULL)
+		return -1;
+
+#ifdef ANDROID
+	if (os_strncmp("keystore://", client_cert, 11) == 0) {
+		BIO *bio = BIO_from_keystore(&client_cert[11]);
+		X509 *x509 = NULL;
+		int ret = -1;
+		if (bio) {
+			x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+		}
+		if (x509) {
+			if (SSL_use_certificate(conn->ssl, x509) == 1)
+				ret = 0;
+			X509_free(x509);
+		}
+
+		/* Read additional certificates into the chain. */
+		while (bio) {
+			x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+			if (x509) {
+				/* Takes ownership of x509 */
+				SSL_add0_chain_cert(conn->ssl, x509);
+			} else {
+				BIO_free(bio);
+				bio = NULL;
+			}
+		}
+		return ret;
+	}
+#endif /* ANDROID */
+
+#ifndef OPENSSL_NO_STDIO
+	if (SSL_use_certificate_file(conn->ssl, client_cert,
+				     SSL_FILETYPE_ASN1) == 1) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (DER)"
+			   " --> OK");
+		return 0;
+	}
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
+	!defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_IS_BORINGSSL)
+	if (SSL_use_certificate_chain_file(conn->ssl, client_cert) == 1) {
+		ERR_clear_error();
+		wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_chain_file"
+			   " --> OK");
+		return 0;
+	}
+#else
+	if (SSL_use_certificate_file(conn->ssl, client_cert,
+				     SSL_FILETYPE_PEM) == 1) {
+		ERR_clear_error();
+		wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (PEM)"
+			   " --> OK");
+		return 0;
+	}
+#endif
+
+	tls_show_errors(MSG_DEBUG, __func__,
+			"SSL_use_certificate_file failed");
+#else /* OPENSSL_NO_STDIO */
+	wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__);
+#endif /* OPENSSL_NO_STDIO */
+
+	return -1;
+}
+
+
+static int tls_global_client_cert(struct tls_data *data,
+				  const char *client_cert)
+{
+#ifndef OPENSSL_NO_STDIO
+	SSL_CTX *ssl_ctx = data->ssl;
+
+	if (client_cert == NULL)
+		return 0;
+
+	if (SSL_CTX_use_certificate_file(ssl_ctx, client_cert,
+					 SSL_FILETYPE_ASN1) != 1 &&
+	    SSL_CTX_use_certificate_chain_file(ssl_ctx, client_cert) != 1 &&
+	    SSL_CTX_use_certificate_file(ssl_ctx, client_cert,
+					 SSL_FILETYPE_PEM) != 1) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Failed to load client certificate");
+		return -1;
+	}
+	return 0;
+#else /* OPENSSL_NO_STDIO */
+	if (client_cert == NULL)
+		return 0;
+	wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__);
+	return -1;
+#endif /* OPENSSL_NO_STDIO */
+}
+
+
+#ifdef PKCS12_FUNCS
+static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12,
+			    const char *passwd)
+{
+	EVP_PKEY *pkey;
+	X509 *cert;
+	STACK_OF(X509) *certs;
+	int res = 0;
+	char buf[256];
+
+	pkey = NULL;
+	cert = NULL;
+	certs = NULL;
+	if (!passwd)
+		passwd = "";
+	if (!PKCS12_parse(p12, passwd, &pkey, &cert, &certs)) {
+		tls_show_errors(MSG_DEBUG, __func__,
+				"Failed to parse PKCS12 file");
+		PKCS12_free(p12);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "TLS: Successfully parsed PKCS12 data");
+
+	if (cert) {
+		X509_NAME_oneline(X509_get_subject_name(cert), buf,
+				  sizeof(buf));
+		wpa_printf(MSG_DEBUG, "TLS: Got certificate from PKCS12: "
+			   "subject='%s'", buf);
+		if (ssl) {
+			if (SSL_use_certificate(ssl, cert) != 1)
+				res = -1;
+		} else {
+			if (SSL_CTX_use_certificate(data->ssl, cert) != 1)
+				res = -1;
+		}
+		X509_free(cert);
+	}
+
+	if (pkey) {
+		wpa_printf(MSG_DEBUG, "TLS: Got private key from PKCS12");
+		if (ssl) {
+			if (SSL_use_PrivateKey(ssl, pkey) != 1)
+				res = -1;
+		} else {
+			if (SSL_CTX_use_PrivateKey(data->ssl, pkey) != 1)
+				res = -1;
+		}
+		EVP_PKEY_free(pkey);
+	}
+
+	if (certs) {
+#ifndef LIBRESSL_VERSION_NUMBER
+		if (ssl)
+			SSL_clear_chain_certs(ssl);
+		else
+			SSL_CTX_clear_chain_certs(data->ssl);
+		while ((cert = sk_X509_pop(certs)) != NULL) {
+			X509_NAME_oneline(X509_get_subject_name(cert), buf,
+					  sizeof(buf));
+			wpa_printf(MSG_DEBUG, "TLS: additional certificate"
+				   " from PKCS12: subject='%s'", buf);
+			if ((ssl && SSL_add1_chain_cert(ssl, cert) != 1) ||
+			    (!ssl && SSL_CTX_add1_chain_cert(data->ssl,
+							     cert) != 1)) {
+				tls_show_errors(MSG_DEBUG, __func__,
+						"Failed to add additional certificate");
+				res = -1;
+				X509_free(cert);
+				break;
+			}
+			X509_free(cert);
+		}
+		if (!res) {
+			/* Try to continue anyway */
+		}
+		sk_X509_pop_free(certs, X509_free);
+#ifndef OPENSSL_IS_BORINGSSL
+		if (ssl)
+			res = SSL_build_cert_chain(
+				ssl,
+				SSL_BUILD_CHAIN_FLAG_CHECK |
+				SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR);
+		else
+			res = SSL_CTX_build_cert_chain(
+				data->ssl,
+				SSL_BUILD_CHAIN_FLAG_CHECK |
+				SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR);
+		if (!res) {
+			tls_show_errors(MSG_DEBUG, __func__,
+					"Failed to build certificate chain");
+		} else if (res == 2) {
+			wpa_printf(MSG_DEBUG,
+				   "TLS: Ignore certificate chain verification error when building chain with PKCS#12 extra certificates");
+		}
+#endif /* OPENSSL_IS_BORINGSSL */
+		/*
+		 * Try to continue regardless of result since it is possible for
+		 * the extra certificates not to be required.
+		 */
+		res = 0;
+#else /* LIBRESSL_VERSION_NUMBER */
+		SSL_CTX_clear_extra_chain_certs(data->ssl);
+		while ((cert = sk_X509_pop(certs)) != NULL) {
+			X509_NAME_oneline(X509_get_subject_name(cert), buf,
+					  sizeof(buf));
+			wpa_printf(MSG_DEBUG, "TLS: additional certificate"
+				   " from PKCS12: subject='%s'", buf);
+			/*
+			 * There is no SSL equivalent for the chain cert - so
+			 * always add it to the context...
+			 */
+			if (SSL_CTX_add_extra_chain_cert(data->ssl, cert) != 1)
+			{
+				X509_free(cert);
+				res = -1;
+				break;
+			}
+		}
+		sk_X509_pop_free(certs, X509_free);
+#endif /* LIBRSESSL_VERSION_NUMBER */
+	}
+
+	PKCS12_free(p12);
+
+	if (res < 0)
+		tls_get_errors(data);
+
+	return res;
+}
+#endif  /* PKCS12_FUNCS */
+
+
+static int tls_read_pkcs12(struct tls_data *data, SSL *ssl,
+			   const char *private_key, const char *passwd)
+{
+#ifdef PKCS12_FUNCS
+	FILE *f;
+	PKCS12 *p12;
+
+	f = fopen(private_key, "rb");
+	if (f == NULL)
+		return -1;
+
+	p12 = d2i_PKCS12_fp(f, NULL);
+	fclose(f);
+
+	if (p12 == NULL) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Failed to use PKCS#12 file");
+		return -1;
+	}
+
+	return tls_parse_pkcs12(data, ssl, p12, passwd);
+
+#else /* PKCS12_FUNCS */
+	wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot read "
+		   "p12/pfx files");
+	return -1;
+#endif  /* PKCS12_FUNCS */
+}
+
+
+static int tls_read_pkcs12_blob(struct tls_data *data, SSL *ssl,
+				const u8 *blob, size_t len, const char *passwd)
+{
+#ifdef PKCS12_FUNCS
+	PKCS12 *p12;
+
+	p12 = d2i_PKCS12(NULL, (const unsigned char **) &blob, len);
+	if (p12 == NULL) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Failed to use PKCS#12 blob");
+		return -1;
+	}
+
+	return tls_parse_pkcs12(data, ssl, p12, passwd);
+
+#else /* PKCS12_FUNCS */
+	wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot parse "
+		   "p12/pfx blobs");
+	return -1;
+#endif  /* PKCS12_FUNCS */
+}
+
+
+#ifndef OPENSSL_NO_ENGINE
+static int tls_engine_get_cert(struct tls_connection *conn,
+			       const char *cert_id,
+			       X509 **cert)
+{
+	/* this runs after the private key is loaded so no PIN is required */
+	struct {
+		const char *cert_id;
+		X509 *cert;
+	} params;
+	params.cert_id = cert_id;
+	params.cert = NULL;
+
+	if (!ENGINE_ctrl_cmd(conn->engine, "LOAD_CERT_CTRL",
+			     0, &params, NULL, 1)) {
+		unsigned long err = ERR_get_error();
+
+		wpa_printf(MSG_ERROR, "ENGINE: cannot load client cert with id"
+			   " '%s' [%s]", cert_id,
+			   ERR_error_string(err, NULL));
+		if (tls_is_pin_error(err))
+			return TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN;
+		return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+	}
+	if (!params.cert) {
+		wpa_printf(MSG_ERROR, "ENGINE: did not properly cert with id"
+			   " '%s'", cert_id);
+		return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+	}
+	*cert = params.cert;
+	return 0;
+}
+#endif /* OPENSSL_NO_ENGINE */
+
+
+static int tls_connection_engine_client_cert(struct tls_connection *conn,
+					     const char *cert_id)
+{
+#ifndef OPENSSL_NO_ENGINE
+	X509 *cert;
+
+	if (tls_engine_get_cert(conn, cert_id, &cert))
+		return -1;
+
+	if (!SSL_use_certificate(conn->ssl, cert)) {
+		tls_show_errors(MSG_ERROR, __func__,
+				"SSL_use_certificate failed");
+                X509_free(cert);
+		return -1;
+	}
+	X509_free(cert);
+	wpa_printf(MSG_DEBUG, "ENGINE: SSL_use_certificate --> "
+		   "OK");
+	return 0;
+
+#else /* OPENSSL_NO_ENGINE */
+	return -1;
+#endif /* OPENSSL_NO_ENGINE */
+}
+
+
+static int tls_connection_engine_ca_cert(struct tls_data *data,
+					 struct tls_connection *conn,
+					 const char *ca_cert_id)
+{
+#ifndef OPENSSL_NO_ENGINE
+	X509 *cert;
+	SSL_CTX *ssl_ctx = data->ssl;
+	X509_STORE *store;
+
+	if (tls_engine_get_cert(conn, ca_cert_id, &cert))
+		return -1;
+
+	/* start off the same as tls_connection_ca_cert */
+	store = X509_STORE_new();
+	if (store == NULL) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new "
+			   "certificate store", __func__);
+		X509_free(cert);
+		return -1;
+	}
+	SSL_CTX_set_cert_store(ssl_ctx, store);
+	if (!X509_STORE_add_cert(store, cert)) {
+		unsigned long err = ERR_peek_error();
+		tls_show_errors(MSG_WARNING, __func__,
+				"Failed to add CA certificate from engine "
+				"to certificate store");
+		if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
+		    ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring cert"
+				   " already in hash table error",
+				   __func__);
+		} else {
+			X509_free(cert);
+			return -1;
+		}
+	}
+	X509_free(cert);
+	wpa_printf(MSG_DEBUG, "OpenSSL: %s - added CA certificate from engine "
+		   "to certificate store", __func__);
+	SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
+	conn->ca_cert_verify = 1;
+
+	return 0;
+
+#else /* OPENSSL_NO_ENGINE */
+	return -1;
+#endif /* OPENSSL_NO_ENGINE */
+}
+
+
+static int tls_connection_engine_private_key(struct tls_connection *conn)
+{
+#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE)
+	if (SSL_use_PrivateKey(conn->ssl, conn->private_key) != 1) {
+		tls_show_errors(MSG_ERROR, __func__,
+				"ENGINE: cannot use private key for TLS");
+		return -1;
+	}
+	if (!SSL_check_private_key(conn->ssl)) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Private key failed verification");
+		return -1;
+	}
+	return 0;
+#else /* OPENSSL_NO_ENGINE */
+	wpa_printf(MSG_ERROR, "SSL: Configuration uses engine, but "
+		   "engine support was not compiled in");
+	return -1;
+#endif /* OPENSSL_NO_ENGINE */
+}
+
+
+#ifndef OPENSSL_NO_STDIO
+static int tls_passwd_cb(char *buf, int size, int rwflag, void *password)
+{
+	if (!password)
+		return 0;
+	os_strlcpy(buf, (const char *) password, size);
+	return os_strlen(buf);
+}
+#endif /* OPENSSL_NO_STDIO */
+
+
+static int tls_use_private_key_file(struct tls_data *data, SSL *ssl,
+				    const char *private_key,
+				    const char *private_key_passwd)
+{
+#ifndef OPENSSL_NO_STDIO
+	BIO *bio;
+	EVP_PKEY *pkey;
+	int ret;
+
+	/* First try ASN.1 (DER). */
+	bio = BIO_new_file(private_key, "r");
+	if (!bio)
+		return -1;
+	pkey = d2i_PrivateKey_bio(bio, NULL);
+	BIO_free(bio);
+
+	if (pkey) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: %s (DER) --> loaded", __func__);
+	} else {
+		/* Try PEM with the provided password. */
+		bio = BIO_new_file(private_key, "r");
+		if (!bio)
+			return -1;
+		pkey = PEM_read_bio_PrivateKey(bio, NULL, tls_passwd_cb,
+					       (void *) private_key_passwd);
+		BIO_free(bio);
+		if (!pkey)
+			return -1;
+		wpa_printf(MSG_DEBUG, "OpenSSL: %s (PEM) --> loaded", __func__);
+		/* Clear errors from the previous failed load. */
+		ERR_clear_error();
+	}
+
+	if (ssl)
+		ret = SSL_use_PrivateKey(ssl, pkey);
+	else
+		ret = SSL_CTX_use_PrivateKey(data->ssl, pkey);
+
+	EVP_PKEY_free(pkey);
+	return ret == 1 ? 0 : -1;
+#else /* OPENSSL_NO_STDIO */
+	wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__);
+	return -1;
+#endif /* OPENSSL_NO_STDIO */
+}
+
+
+static int tls_connection_private_key(struct tls_data *data,
+				      struct tls_connection *conn,
+				      const char *private_key,
+				      const char *private_key_passwd,
+				      const u8 *private_key_blob,
+				      size_t private_key_blob_len)
+{
+	BIO *bio;
+	int ok;
+
+	if (private_key == NULL && private_key_blob == NULL)
+		return 0;
+
+	ok = 0;
+	while (private_key_blob) {
+		if (SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA, conn->ssl,
+					    (u8 *) private_key_blob,
+					    private_key_blob_len) == 1) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_"
+				   "ASN1(EVP_PKEY_RSA) --> OK");
+			ok = 1;
+			break;
+		}
+
+		if (SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA, conn->ssl,
+					    (u8 *) private_key_blob,
+					    private_key_blob_len) == 1) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_"
+				   "ASN1(EVP_PKEY_DSA) --> OK");
+			ok = 1;
+			break;
+		}
+
+#ifndef OPENSSL_NO_EC
+		if (SSL_use_PrivateKey_ASN1(EVP_PKEY_EC, conn->ssl,
+					    (u8 *) private_key_blob,
+					    private_key_blob_len) == 1) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: SSL_use_PrivateKey_ASN1(EVP_PKEY_EC) --> OK");
+			ok = 1;
+			break;
+		}
+#endif /* OPENSSL_NO_EC */
+
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+		if (SSL_use_RSAPrivateKey_ASN1(conn->ssl,
+					       (u8 *) private_key_blob,
+					       private_key_blob_len) == 1) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: "
+				   "SSL_use_RSAPrivateKey_ASN1 --> OK");
+			ok = 1;
+			break;
+		}
+#endif
+
+		bio = BIO_new_mem_buf((u8 *) private_key_blob,
+				      private_key_blob_len);
+		if (bio) {
+			EVP_PKEY *pkey;
+
+			pkey = PEM_read_bio_PrivateKey(
+				bio, NULL, tls_passwd_cb,
+				(void *) private_key_passwd);
+			if (pkey) {
+				if (SSL_use_PrivateKey(conn->ssl, pkey) == 1) {
+					wpa_printf(MSG_DEBUG,
+						   "OpenSSL: SSL_use_PrivateKey --> OK");
+					ok = 1;
+					EVP_PKEY_free(pkey);
+					BIO_free(bio);
+					break;
+				}
+				EVP_PKEY_free(pkey);
+			}
+			BIO_free(bio);
+		}
+
+		if (tls_read_pkcs12_blob(data, conn->ssl, private_key_blob,
+					 private_key_blob_len,
+					 private_key_passwd) == 0) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: PKCS#12 as blob --> "
+				   "OK");
+			ok = 1;
+			break;
+		}
+
+		break;
+	}
+
+	while (!ok && private_key) {
+		if (tls_use_private_key_file(data, conn->ssl, private_key,
+					     private_key_passwd) == 0) {
+			ok = 1;
+			break;
+		}
+
+		if (tls_read_pkcs12(data, conn->ssl, private_key,
+				    private_key_passwd) == 0) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file "
+				   "--> OK");
+			ok = 1;
+			break;
+		}
+
+		if (tls_cryptoapi_cert(conn->ssl, private_key) == 0) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: Using CryptoAPI to "
+				   "access certificate store --> OK");
+			ok = 1;
+			break;
+		}
+
+		break;
+	}
+
+	if (!ok) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Failed to load private key");
+		return -1;
+	}
+	ERR_clear_error();
+
+	if (!SSL_check_private_key(conn->ssl)) {
+		tls_show_errors(MSG_INFO, __func__, "Private key failed "
+				"verification");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "SSL: Private key loaded successfully");
+	return 0;
+}
+
+
+static int tls_global_private_key(struct tls_data *data,
+				  const char *private_key,
+				  const char *private_key_passwd)
+{
+	SSL_CTX *ssl_ctx = data->ssl;
+
+	if (private_key == NULL)
+		return 0;
+
+	if (tls_use_private_key_file(data, NULL, private_key,
+				     private_key_passwd) &&
+	    tls_read_pkcs12(data, NULL, private_key, private_key_passwd)) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Failed to load private key");
+		ERR_clear_error();
+		return -1;
+	}
+	ERR_clear_error();
+
+	if (!SSL_CTX_check_private_key(ssl_ctx)) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Private key failed verification");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#ifndef OPENSSL_NO_DH
+#ifndef OPENSSL_NO_DSA
+/* This is needed to replace the deprecated DSA_dup_DH() function */
+static EVP_PKEY * openssl_dsa_to_dh(EVP_PKEY *dsa)
+{
+	OSSL_PARAM_BLD *bld = NULL;
+	OSSL_PARAM *params = NULL;
+	BIGNUM *p = NULL, *q = NULL, *g = NULL;
+	EVP_PKEY_CTX *ctx = NULL;
+	EVP_PKEY *pkey = NULL;
+
+	if (!EVP_PKEY_get_bn_param(dsa, OSSL_PKEY_PARAM_FFC_P, &p) ||
+	    !EVP_PKEY_get_bn_param(dsa, OSSL_PKEY_PARAM_FFC_Q, &q) ||
+	    !EVP_PKEY_get_bn_param(dsa, OSSL_PKEY_PARAM_FFC_G, &g) ||
+	    !(bld = OSSL_PARAM_BLD_new()) ||
+	    !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_P, p) ||
+	    !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_Q, q) ||
+	    !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_G, g) ||
+	    !(params = OSSL_PARAM_BLD_to_param(bld)) ||
+	    !(ctx = EVP_PKEY_CTX_new_from_name(NULL, "DHX", NULL)) ||
+	    EVP_PKEY_fromdata_init(ctx) != 1 ||
+	    EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_KEY_PARAMETERS,
+			      params) != 1)
+		wpa_printf(MSG_INFO,
+			   "TLS: Failed to convert DSA parameters to DH parameters");
+
+	EVP_PKEY_CTX_free(ctx);
+	OSSL_PARAM_free(params);
+	OSSL_PARAM_BLD_free(bld);
+	BN_free(p);
+	BN_free(q);
+	BN_free(g);
+	return pkey;
+}
+#endif /* !OPENSSL_NO_DSA */
+#endif /* OPENSSL_NO_DH */
+#endif /* OpenSSL version >= 3.0 */
+
+static int tls_global_dh(struct tls_data *data, const char *dh_file)
+{
+#ifdef OPENSSL_NO_DH
+	if (dh_file == NULL)
+		return 0;
+	wpa_printf(MSG_ERROR, "TLS: openssl does not include DH support, but "
+		   "dh_file specified");
+	return -1;
+#else /* OPENSSL_NO_DH */
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	SSL_CTX *ssl_ctx = data->ssl;
+	BIO *bio;
+	OSSL_DECODER_CTX *ctx = NULL;
+	EVP_PKEY *pkey = NULL, *tmpkey = NULL;
+	bool dsa = false;
+
+	if (!ssl_ctx)
+		return -1;
+	if (!dh_file) {
+		SSL_CTX_set_dh_auto(ssl_ctx, 1);
+		return 0;
+	}
+
+	bio = BIO_new_file(dh_file, "r");
+	if (!bio) {
+		wpa_printf(MSG_INFO, "TLS: Failed to open DH file '%s': %s",
+			   dh_file, ERR_error_string(ERR_get_error(), NULL));
+		return -1;
+	}
+	ctx = OSSL_DECODER_CTX_new_for_pkey(
+		&tmpkey, "PEM", NULL, NULL,
+		OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS, NULL, NULL);
+	if (!ctx ||
+	    OSSL_DECODER_from_bio(ctx, bio) != 1) {
+		wpa_printf(MSG_INFO,
+			   "TLS: Failed to decode domain parameters from '%s': %s",
+			   dh_file, ERR_error_string(ERR_get_error(), NULL));
+		BIO_free(bio);
+		OSSL_DECODER_CTX_free(ctx);
+		return -1;
+	}
+	OSSL_DECODER_CTX_free(ctx);
+	BIO_free(bio);
+
+	if (!tmpkey) {
+		wpa_printf(MSG_INFO, "TLS: Failed to load domain parameters");
+		return -1;
+	}
+
+#ifndef OPENSSL_NO_DSA
+	if (EVP_PKEY_is_a(tmpkey, "DSA")) {
+		pkey = openssl_dsa_to_dh(tmpkey);
+		EVP_PKEY_free(tmpkey);
+		if (!pkey)
+			return -1;
+		dsa = true;
+	}
+#endif /* !OPENSSL_NO_DSA */
+	if (!dsa) {
+		if (EVP_PKEY_is_a(tmpkey, "DH") ||
+		    EVP_PKEY_is_a(tmpkey, "DHX")) {
+		} else {
+			wpa_printf(MSG_INFO,
+				   "TLS: No DH parameters found in %s",
+				   dh_file);
+			EVP_PKEY_free(tmpkey);
+			return -1;
+		}
+		pkey = tmpkey;
+		tmpkey = NULL;
+	}
+
+	if (SSL_CTX_set0_tmp_dh_pkey(ssl_ctx, pkey) != 1) {
+		wpa_printf(MSG_INFO,
+			   "TLS: Failed to set DH params from '%s': %s",
+			   dh_file, ERR_error_string(ERR_get_error(), NULL));
+		EVP_PKEY_free(pkey);
+		return -1;
+	}
+	return 0;
+#else /* OpenSSL version >= 3.0 */
+	SSL_CTX *ssl_ctx = data->ssl;
+	DH *dh;
+	BIO *bio;
+
+	if (!ssl_ctx)
+		return -1;
+	if (!dh_file) {
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL)
+		SSL_CTX_set_dh_auto(ssl_ctx, 1);
+#endif
+		return 0;
+	}
+
+	bio = BIO_new_file(dh_file, "r");
+	if (bio == NULL) {
+		wpa_printf(MSG_INFO, "TLS: Failed to open DH file '%s': %s",
+			   dh_file, ERR_error_string(ERR_get_error(), NULL));
+		return -1;
+	}
+	dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+	BIO_free(bio);
+#ifndef OPENSSL_NO_DSA
+	while (dh == NULL) {
+		DSA *dsa;
+		wpa_printf(MSG_DEBUG, "TLS: Failed to parse DH file '%s': %s -"
+			   " trying to parse as DSA params", dh_file,
+			   ERR_error_string(ERR_get_error(), NULL));
+		bio = BIO_new_file(dh_file, "r");
+		if (bio == NULL)
+			break;
+		dsa = PEM_read_bio_DSAparams(bio, NULL, NULL, NULL);
+		BIO_free(bio);
+		if (!dsa) {
+			wpa_printf(MSG_DEBUG, "TLS: Failed to parse DSA file "
+				   "'%s': %s", dh_file,
+				   ERR_error_string(ERR_get_error(), NULL));
+			break;
+		}
+
+		wpa_printf(MSG_DEBUG, "TLS: DH file in DSA param format");
+		dh = DSA_dup_DH(dsa);
+		DSA_free(dsa);
+		if (dh == NULL) {
+			wpa_printf(MSG_INFO, "TLS: Failed to convert DSA "
+				   "params into DH params");
+			break;
+		}
+		break;
+	}
+#endif /* !OPENSSL_NO_DSA */
+	if (dh == NULL) {
+		wpa_printf(MSG_INFO, "TLS: Failed to read/parse DH/DSA file "
+			   "'%s'", dh_file);
+		return -1;
+	}
+
+	if (SSL_CTX_set_tmp_dh(ssl_ctx, dh) != 1) {
+		wpa_printf(MSG_INFO, "TLS: Failed to set DH params from '%s': "
+			   "%s", dh_file,
+			   ERR_error_string(ERR_get_error(), NULL));
+		DH_free(dh);
+		return -1;
+	}
+	DH_free(dh);
+	return 0;
+#endif /* OpenSSL version >= 3.0 */
+#endif /* OPENSSL_NO_DH */
+}
+
+
+int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn,
+			      struct tls_random *keys)
+{
+	SSL *ssl;
+
+	if (conn == NULL || keys == NULL)
+		return -1;
+	ssl = conn->ssl;
+	if (ssl == NULL)
+		return -1;
+
+	os_memset(keys, 0, sizeof(*keys));
+	keys->client_random = conn->client_random;
+	keys->client_random_len = SSL_get_client_random(
+		ssl, conn->client_random, sizeof(conn->client_random));
+	keys->server_random = conn->server_random;
+	keys->server_random_len = SSL_get_server_random(
+		ssl, conn->server_random, sizeof(conn->server_random));
+
+	return 0;
+}
+
+
+#ifdef OPENSSL_NEED_EAP_FAST_PRF
+static int openssl_get_keyblock_size(SSL *ssl)
+{
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+	const EVP_CIPHER *c;
+	const EVP_MD *h;
+	int md_size;
+
+	if (ssl->enc_read_ctx == NULL || ssl->enc_read_ctx->cipher == NULL ||
+	    ssl->read_hash == NULL)
+		return -1;
+
+	c = ssl->enc_read_ctx->cipher;
+	h = EVP_MD_CTX_md(ssl->read_hash);
+	if (h)
+		md_size = EVP_MD_size(h);
+	else if (ssl->s3)
+		md_size = ssl->s3->tmp.new_mac_secret_size;
+	else
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: keyblock size: key_len=%d MD_size=%d "
+		   "IV_len=%d", EVP_CIPHER_key_length(c), md_size,
+		   EVP_CIPHER_iv_length(c));
+	return 2 * (EVP_CIPHER_key_length(c) +
+		    md_size +
+		    EVP_CIPHER_iv_length(c));
+#else
+	const SSL_CIPHER *ssl_cipher;
+	int cipher, digest;
+	const EVP_CIPHER *c;
+	const EVP_MD *h;
+	int mac_key_len, enc_key_len, fixed_iv_len;
+
+	ssl_cipher = SSL_get_current_cipher(ssl);
+	if (!ssl_cipher)
+		return -1;
+	cipher = SSL_CIPHER_get_cipher_nid(ssl_cipher);
+	digest = SSL_CIPHER_get_digest_nid(ssl_cipher);
+	wpa_printf(MSG_DEBUG, "OpenSSL: cipher nid %d digest nid %d",
+		   cipher, digest);
+	if (cipher < 0 || digest < 0)
+		return -1;
+	if (cipher == NID_undef) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: no cipher in use?!");
+		return -1;
+	}
+	c = EVP_get_cipherbynid(cipher);
+	if (!c)
+		return -1;
+	enc_key_len = EVP_CIPHER_key_length(c);
+	if (EVP_CIPHER_mode(c) == EVP_CIPH_GCM_MODE ||
+	    EVP_CIPHER_mode(c) == EVP_CIPH_CCM_MODE)
+		fixed_iv_len = 4; /* only part of IV from PRF */
+	else
+		fixed_iv_len = EVP_CIPHER_iv_length(c);
+	if (digest == NID_undef) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: no digest in use (e.g., AEAD)");
+		mac_key_len = 0;
+	} else {
+		h = EVP_get_digestbynid(digest);
+		if (!h)
+			return -1;
+		mac_key_len = EVP_MD_size(h);
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "OpenSSL: keyblock size: mac_key_len=%d enc_key_len=%d fixed_iv_len=%d",
+		   mac_key_len, enc_key_len, fixed_iv_len);
+	return 2 * (mac_key_len + enc_key_len + fixed_iv_len);
+#endif
+}
+#endif /* OPENSSL_NEED_EAP_FAST_PRF */
+
+
+int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
+			      const char *label, const u8 *context,
+			      size_t context_len, u8 *out, size_t out_len)
+{
+	if (!conn ||
+	    SSL_export_keying_material(conn->ssl, out, out_len, label,
+				       os_strlen(label), context, context_len,
+				       context != NULL) != 1)
+		return -1;
+	return 0;
+}
+
+
+int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
+				    u8 *out, size_t out_len)
+{
+#ifdef OPENSSL_NEED_EAP_FAST_PRF
+	SSL *ssl;
+	SSL_SESSION *sess;
+	u8 *rnd;
+	int ret = -1;
+	int skip = 0;
+	u8 *tmp_out = NULL;
+	u8 *_out = out;
+	unsigned char client_random[SSL3_RANDOM_SIZE];
+	unsigned char server_random[SSL3_RANDOM_SIZE];
+	unsigned char master_key[64];
+	size_t master_key_len;
+	const char *ver;
+
+	/*
+	 * TLS library did not support EAP-FAST key generation, so get the
+	 * needed TLS session parameters and use an internal implementation of
+	 * TLS PRF to derive the key.
+	 */
+
+	if (conn == NULL)
+		return -1;
+	ssl = conn->ssl;
+	if (ssl == NULL)
+		return -1;
+	ver = SSL_get_version(ssl);
+	sess = SSL_get_session(ssl);
+	if (!ver || !sess)
+		return -1;
+
+	skip = openssl_get_keyblock_size(ssl);
+	if (skip < 0)
+		return -1;
+	tmp_out = os_malloc(skip + out_len);
+	if (!tmp_out)
+		return -1;
+	_out = tmp_out;
+
+	rnd = os_malloc(2 * SSL3_RANDOM_SIZE);
+	if (!rnd) {
+		os_free(tmp_out);
+		return -1;
+	}
+
+	SSL_get_client_random(ssl, client_random, sizeof(client_random));
+	SSL_get_server_random(ssl, server_random, sizeof(server_random));
+	master_key_len = SSL_SESSION_get_master_key(sess, master_key,
+						    sizeof(master_key));
+
+	os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE);
+	os_memcpy(rnd + SSL3_RANDOM_SIZE, client_random, SSL3_RANDOM_SIZE);
+
+	if (os_strcmp(ver, "TLSv1.2") == 0) {
+		tls_prf_sha256(master_key, master_key_len,
+			       "key expansion", rnd, 2 * SSL3_RANDOM_SIZE,
+			       _out, skip + out_len);
+		ret = 0;
+	} else if (tls_prf_sha1_md5(master_key, master_key_len,
+				    "key expansion", rnd, 2 * SSL3_RANDOM_SIZE,
+				    _out, skip + out_len) == 0) {
+		ret = 0;
+	}
+	forced_memzero(master_key, sizeof(master_key));
+	os_free(rnd);
+	if (ret == 0)
+		os_memcpy(out, _out + skip, out_len);
+	bin_clear_free(tmp_out, skip);
+
+	return ret;
+#else /* OPENSSL_NEED_EAP_FAST_PRF */
+	wpa_printf(MSG_ERROR,
+		   "OpenSSL: EAP-FAST keys cannot be exported in FIPS mode");
+	return -1;
+#endif /* OPENSSL_NEED_EAP_FAST_PRF */
+}
+
+
+static struct wpabuf *
+openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data)
+{
+	struct tls_context *context = conn->context;
+	int res;
+	struct wpabuf *out_data;
+
+	/*
+	 * Give TLS handshake data from the server (if available) to OpenSSL
+	 * for processing.
+	 */
+	if (in_data && wpabuf_len(in_data) > 0 &&
+	    BIO_write(conn->ssl_in, wpabuf_head(in_data), wpabuf_len(in_data))
+	    < 0) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Handshake failed - BIO_write");
+		return NULL;
+	}
+
+	/* Initiate TLS handshake or continue the existing handshake */
+	if (conn->server)
+		res = SSL_accept(conn->ssl);
+	else
+		res = SSL_connect(conn->ssl);
+	if (res != 1) {
+		int err = SSL_get_error(conn->ssl, res);
+		if (err == SSL_ERROR_WANT_READ)
+			wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want "
+				   "more data");
+		else if (err == SSL_ERROR_WANT_WRITE)
+			wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want to "
+				   "write");
+		else {
+			unsigned long error = ERR_peek_last_error();
+
+			tls_show_errors(MSG_INFO, __func__, "SSL_connect");
+
+			if (context->event_cb &&
+			    ERR_GET_LIB(error) == ERR_LIB_SSL &&
+			    ERR_GET_REASON(error) ==
+			    SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED) {
+				context->event_cb(
+					context->cb_ctx,
+					TLS_UNSAFE_RENEGOTIATION_DISABLED,
+					NULL);
+			}
+			conn->failed++;
+			if (!conn->server && !conn->client_hello_generated) {
+				/* The server would not understand TLS Alert
+				 * before ClientHello, so simply terminate
+				 * handshake on this type of error case caused
+				 * by a likely internal error like no ciphers
+				 * available. */
+				wpa_printf(MSG_DEBUG,
+					   "OpenSSL: Could not generate ClientHello");
+				conn->write_alerts++;
+				return NULL;
+			}
+		}
+	}
+
+	if (!conn->server && !conn->failed)
+		conn->client_hello_generated = 1;
+
+#ifdef CONFIG_SUITEB
+	if ((conn->flags & TLS_CONN_SUITEB) && !conn->server &&
+	    os_strncmp(SSL_get_cipher(conn->ssl), "DHE-", 4) == 0 &&
+	    conn->server_dh_prime_len < 3072) {
+		/*
+		 * This should not be reached since earlier cert_cb should have
+		 * terminated the handshake. Keep this check here for extra
+		 * protection if anything goes wrong with the more low-level
+		 * checks based on having to parse the TLS handshake messages.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Server DH prime length: %d bits",
+			   conn->server_dh_prime_len);
+
+		if (context->event_cb) {
+			union tls_event_data ev;
+
+			os_memset(&ev, 0, sizeof(ev));
+			ev.alert.is_local = 1;
+			ev.alert.type = "fatal";
+			ev.alert.description = "insufficient security";
+			context->event_cb(context->cb_ctx, TLS_ALERT, &ev);
+		}
+		/*
+		 * Could send a TLS Alert to the server, but for now, simply
+		 * terminate handshake.
+		 */
+		conn->failed++;
+		conn->write_alerts++;
+		return NULL;
+	}
+#endif /* CONFIG_SUITEB */
+
+	/* Get the TLS handshake data to be sent to the server */
+	res = BIO_ctrl_pending(conn->ssl_out);
+	wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res);
+	out_data = wpabuf_alloc(res);
+	if (out_data == NULL) {
+		wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for "
+			   "handshake output (%d bytes)", res);
+		if (BIO_reset(conn->ssl_out) < 0) {
+			tls_show_errors(MSG_INFO, __func__,
+					"BIO_reset failed");
+		}
+		return NULL;
+	}
+	res = res == 0 ? 0 : BIO_read(conn->ssl_out, wpabuf_mhead(out_data),
+				      res);
+	if (res < 0) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Handshake failed - BIO_read");
+		if (BIO_reset(conn->ssl_out) < 0) {
+			tls_show_errors(MSG_INFO, __func__,
+					"BIO_reset failed");
+		}
+		wpabuf_free(out_data);
+		return NULL;
+	}
+	wpabuf_put(out_data, res);
+
+	return out_data;
+}
+
+
+static struct wpabuf *
+openssl_get_appl_data(struct tls_connection *conn, size_t max_len)
+{
+	struct wpabuf *appl_data;
+	int res;
+
+	appl_data = wpabuf_alloc(max_len + 100);
+	if (appl_data == NULL)
+		return NULL;
+
+	res = SSL_read(conn->ssl, wpabuf_mhead(appl_data),
+		       wpabuf_size(appl_data));
+	if (res < 0) {
+		int err = SSL_get_error(conn->ssl, res);
+		if (err == SSL_ERROR_WANT_READ ||
+		    err == SSL_ERROR_WANT_WRITE) {
+			wpa_printf(MSG_DEBUG, "SSL: No Application Data "
+				   "included");
+		} else {
+			tls_show_errors(MSG_INFO, __func__,
+					"Failed to read possible "
+					"Application Data");
+		}
+		wpabuf_free(appl_data);
+		return NULL;
+	}
+
+	wpabuf_put(appl_data, res);
+	wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application Data in Finished "
+			    "message", appl_data);
+
+	return appl_data;
+}
+
+
+static struct wpabuf *
+openssl_connection_handshake(struct tls_connection *conn,
+			     const struct wpabuf *in_data,
+			     struct wpabuf **appl_data)
+{
+	struct wpabuf *out_data;
+
+	if (appl_data)
+		*appl_data = NULL;
+
+	out_data = openssl_handshake(conn, in_data);
+	if (out_data == NULL)
+		return NULL;
+	if (conn->invalid_hb_used) {
+		wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response");
+		wpabuf_free(out_data);
+		return NULL;
+	}
+
+	if (SSL_is_init_finished(conn->ssl)) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Handshake finished - resumed=%d",
+			   tls_connection_resumed(conn->ssl_ctx, conn));
+		if (conn->server) {
+			char *buf;
+			size_t buflen = 2000;
+
+			buf = os_malloc(buflen);
+			if (buf) {
+				if (SSL_get_shared_ciphers(conn->ssl, buf,
+							   buflen)) {
+					buf[buflen - 1] = '\0';
+					wpa_printf(MSG_DEBUG,
+						   "OpenSSL: Shared ciphers: %s",
+						   buf);
+				}
+				os_free(buf);
+			}
+		}
+		if (appl_data && in_data)
+			*appl_data = openssl_get_appl_data(conn,
+							   wpabuf_len(in_data));
+	}
+
+	if (conn->invalid_hb_used) {
+		wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response");
+		if (appl_data) {
+			wpabuf_free(*appl_data);
+			*appl_data = NULL;
+		}
+		wpabuf_free(out_data);
+		return NULL;
+	}
+
+	return out_data;
+}
+
+
+struct wpabuf *
+tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn,
+			 const struct wpabuf *in_data,
+			 struct wpabuf **appl_data)
+{
+	return openssl_connection_handshake(conn, in_data, appl_data);
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+						struct tls_connection *conn,
+						const struct wpabuf *in_data,
+						struct wpabuf **appl_data)
+{
+	conn->server = 1;
+	return openssl_connection_handshake(conn, in_data, appl_data);
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data)
+{
+	int res;
+	struct wpabuf *buf;
+
+	if (conn == NULL)
+		return NULL;
+
+	/* Give plaintext data for OpenSSL to encrypt into the TLS tunnel. */
+	if ((res = BIO_reset(conn->ssl_in)) < 0 ||
+	    (res = BIO_reset(conn->ssl_out)) < 0) {
+		tls_show_errors(MSG_INFO, __func__, "BIO_reset failed");
+		return NULL;
+	}
+	res = SSL_write(conn->ssl, wpabuf_head(in_data), wpabuf_len(in_data));
+	if (res < 0) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Encryption failed - SSL_write");
+		return NULL;
+	}
+
+	/* Read encrypted data to be sent to the server */
+	buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
+	if (buf == NULL)
+		return NULL;
+	res = BIO_read(conn->ssl_out, wpabuf_mhead(buf), wpabuf_size(buf));
+	if (res < 0) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Encryption failed - BIO_read");
+		wpabuf_free(buf);
+		return NULL;
+	}
+	wpabuf_put(buf, res);
+
+	return buf;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data)
+{
+	int res;
+	struct wpabuf *buf;
+
+	/* Give encrypted data from TLS tunnel for OpenSSL to decrypt. */
+	res = BIO_write(conn->ssl_in, wpabuf_head(in_data),
+			wpabuf_len(in_data));
+	if (res < 0) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Decryption failed - BIO_write");
+		return NULL;
+	}
+	if (BIO_reset(conn->ssl_out) < 0) {
+		tls_show_errors(MSG_INFO, __func__, "BIO_reset failed");
+		return NULL;
+	}
+
+	/* Read decrypted data for further processing */
+	/*
+	 * Even though we try to disable TLS compression, it is possible that
+	 * this cannot be done with all TLS libraries. Add extra buffer space
+	 * to handle the possibility of the decrypted data being longer than
+	 * input data.
+	 */
+	buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
+	if (buf == NULL)
+		return NULL;
+	res = SSL_read(conn->ssl, wpabuf_mhead(buf), wpabuf_size(buf));
+	if (res < 0) {
+		int err = SSL_get_error(conn->ssl, res);
+
+		if (err == SSL_ERROR_WANT_READ) {
+			wpa_printf(MSG_DEBUG,
+				   "SSL: SSL_connect - want more data");
+			res = 0;
+		} else {
+			tls_show_errors(MSG_INFO, __func__,
+					"Decryption failed - SSL_read");
+			wpabuf_free(buf);
+			return NULL;
+		}
+	}
+	wpabuf_put(buf, res);
+
+	if (conn->invalid_hb_used) {
+		wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response");
+		wpabuf_free(buf);
+		return NULL;
+	}
+
+	return buf;
+}
+
+
+int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn)
+{
+	return conn ? SSL_session_reused(conn->ssl) : 0;
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+				   u8 *ciphers)
+{
+	char buf[500], *pos, *end;
+	u8 *c;
+	int ret;
+
+	if (conn == NULL || conn->ssl == NULL || ciphers == NULL)
+		return -1;
+
+	buf[0] = '\0';
+	pos = buf;
+	end = pos + sizeof(buf);
+
+	c = ciphers;
+	while (*c != TLS_CIPHER_NONE) {
+		const char *suite;
+
+		switch (*c) {
+		case TLS_CIPHER_RC4_SHA:
+			suite = "RC4-SHA";
+			break;
+		case TLS_CIPHER_AES128_SHA:
+			suite = "AES128-SHA";
+			break;
+		case TLS_CIPHER_RSA_DHE_AES128_SHA:
+			suite = "DHE-RSA-AES128-SHA";
+			break;
+		case TLS_CIPHER_ANON_DH_AES128_SHA:
+			suite = "ADH-AES128-SHA";
+			break;
+		case TLS_CIPHER_RSA_DHE_AES256_SHA:
+			suite = "DHE-RSA-AES256-SHA";
+			break;
+		case TLS_CIPHER_AES256_SHA:
+			suite = "AES256-SHA";
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "TLS: Unsupported "
+				   "cipher selection: %d", *c);
+			return -1;
+		}
+		ret = os_snprintf(pos, end - pos, ":%s", suite);
+		if (os_snprintf_error(end - pos, ret))
+			break;
+		pos += ret;
+
+		c++;
+	}
+	if (!buf[0]) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: No ciphers listed");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1);
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
+#ifdef EAP_FAST_OR_TEAP
+	if (os_strstr(buf, ":ADH-")) {
+		/*
+		 * Need to drop to security level 0 to allow anonymous
+		 * cipher suites for EAP-FAST.
+		 */
+		SSL_set_security_level(conn->ssl, 0);
+	} else if (SSL_get_security_level(conn->ssl) == 0) {
+		/* Force at least security level 1 */
+		SSL_set_security_level(conn->ssl, 1);
+	}
+#endif /* EAP_FAST_OR_TEAP */
+#endif
+
+	if (SSL_set_cipher_list(conn->ssl, buf + 1) != 1) {
+		tls_show_errors(MSG_INFO, __func__,
+				"Cipher suite configuration failed");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
+		    char *buf, size_t buflen)
+{
+	const char *name;
+	if (conn == NULL || conn->ssl == NULL)
+		return -1;
+
+	name = SSL_get_version(conn->ssl);
+	if (name == NULL)
+		return -1;
+
+	os_strlcpy(buf, name, buflen);
+	return 0;
+}
+
+
+int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
+		   char *buf, size_t buflen)
+{
+	const char *name;
+	if (conn == NULL || conn->ssl == NULL)
+		return -1;
+
+	name = SSL_get_cipher(conn->ssl);
+	if (name == NULL)
+		return -1;
+
+	os_strlcpy(buf, name, buflen);
+	return 0;
+}
+
+
+int tls_connection_enable_workaround(void *ssl_ctx,
+				     struct tls_connection *conn)
+{
+	SSL_set_options(conn->ssl, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
+
+	return 0;
+}
+
+
+#ifdef EAP_FAST_OR_TEAP
+/* ClientHello TLS extensions require a patch to openssl, so this function is
+ * commented out unless explicitly needed for EAP-FAST in order to be able to
+ * build this file with unmodified openssl. */
+int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
+				    int ext_type, const u8 *data,
+				    size_t data_len)
+{
+	if (conn == NULL || conn->ssl == NULL || ext_type != 35)
+		return -1;
+
+	if (SSL_set_session_ticket_ext(conn->ssl, (void *) data,
+				       data_len) != 1)
+		return -1;
+
+	return 0;
+}
+#endif /* EAP_FAST_OR_TEAP */
+
+
+int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn)
+{
+	if (conn == NULL)
+		return -1;
+	return conn->failed;
+}
+
+
+int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn)
+{
+	if (conn == NULL)
+		return -1;
+	return conn->read_alerts;
+}
+
+
+int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn)
+{
+	if (conn == NULL)
+		return -1;
+	return conn->write_alerts;
+}
+
+
+#ifdef HAVE_OCSP
+
+static void ocsp_debug_print_resp(OCSP_RESPONSE *rsp)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+	BIO *out;
+	size_t rlen;
+	char *txt;
+	int res;
+
+	if (wpa_debug_level > MSG_DEBUG)
+		return;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return;
+
+	OCSP_RESPONSE_print(out, rsp, 0);
+	rlen = BIO_ctrl_pending(out);
+	txt = os_malloc(rlen + 1);
+	if (!txt) {
+		BIO_free(out);
+		return;
+	}
+
+	res = BIO_read(out, txt, rlen);
+	if (res > 0) {
+		txt[res] = '\0';
+		wpa_printf(MSG_DEBUG, "OpenSSL: OCSP Response\n%s", txt);
+	}
+	os_free(txt);
+	BIO_free(out);
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+}
+
+
+static int ocsp_resp_cb(SSL *s, void *arg)
+{
+	struct tls_connection *conn = arg;
+	const unsigned char *p;
+	int len, status, reason, res;
+	OCSP_RESPONSE *rsp;
+	OCSP_BASICRESP *basic;
+	OCSP_CERTID *id;
+	ASN1_GENERALIZEDTIME *produced_at, *this_update, *next_update;
+	X509_STORE *store;
+	STACK_OF(X509) *certs = NULL;
+
+	len = SSL_get_tlsext_status_ocsp_resp(s, &p);
+	if (!p) {
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L
+#if !defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x30400000L
+		if (SSL_version(s) == TLS1_3_VERSION && SSL_session_reused(s)) {
+			/* TLS 1.3 sends the OCSP response with the server
+			 * Certificate message. Since that Certificate message
+			 * is not sent when resuming a session, there can be no
+			 * new OCSP response. Allow this since the OCSP response
+			 * was validated when checking the initial certificate
+			 * exchange. */
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: Allow no OCSP response when using TLS 1.3 and a resumed session");
+			return 1;
+		}
+#endif
+#endif
+		wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received");
+		return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", p, len);
+
+	rsp = d2i_OCSP_RESPONSE(NULL, &p, len);
+	if (!rsp) {
+		wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSP response");
+		return 0;
+	}
+
+	ocsp_debug_print_resp(rsp);
+
+	status = OCSP_response_status(rsp);
+	if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+		wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d (%s)",
+			   status, OCSP_response_status_str(status));
+		return 0;
+	}
+
+	basic = OCSP_response_get1_basic(rsp);
+	if (!basic) {
+		wpa_printf(MSG_INFO, "OpenSSL: Could not find BasicOCSPResponse");
+		return 0;
+	}
+
+	store = SSL_CTX_get_cert_store(conn->ssl_ctx);
+	if (conn->peer_issuer) {
+		debug_print_cert(conn->peer_issuer, "Add OCSP issuer");
+
+		if (X509_STORE_add_cert(store, conn->peer_issuer) != 1) {
+			tls_show_errors(MSG_INFO, __func__,
+					"OpenSSL: Could not add issuer to certificate store");
+		}
+		certs = sk_X509_new_null();
+		if (certs) {
+			X509 *cert;
+			cert = X509_dup(conn->peer_issuer);
+			if (cert && !sk_X509_push(certs, cert)) {
+				tls_show_errors(
+					MSG_INFO, __func__,
+					"OpenSSL: Could not add issuer to OCSP responder trust store");
+				X509_free(cert);
+				sk_X509_free(certs);
+				certs = NULL;
+			}
+			if (certs && conn->peer_issuer_issuer) {
+				cert = X509_dup(conn->peer_issuer_issuer);
+				if (cert && !sk_X509_push(certs, cert)) {
+					tls_show_errors(
+						MSG_INFO, __func__,
+						"OpenSSL: Could not add issuer's issuer to OCSP responder trust store");
+					X509_free(cert);
+				}
+			}
+		}
+	}
+
+	status = OCSP_basic_verify(basic, certs, store, OCSP_TRUSTOTHER);
+	sk_X509_pop_free(certs, X509_free);
+	if (status <= 0) {
+		tls_show_errors(MSG_INFO, __func__,
+				"OpenSSL: OCSP response failed verification");
+		OCSP_BASICRESP_free(basic);
+		OCSP_RESPONSE_free(rsp);
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response verification succeeded");
+
+	if (!conn->peer_cert) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Peer certificate not available for OCSP status check");
+		OCSP_BASICRESP_free(basic);
+		OCSP_RESPONSE_free(rsp);
+		return 0;
+	}
+
+	if (!conn->peer_issuer) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Peer issuer certificate not available for OCSP status check");
+		OCSP_BASICRESP_free(basic);
+		OCSP_RESPONSE_free(rsp);
+		return 0;
+	}
+
+	id = OCSP_cert_to_id(EVP_sha256(), conn->peer_cert, conn->peer_issuer);
+	if (!id) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Could not create OCSP certificate identifier (SHA256)");
+		OCSP_BASICRESP_free(basic);
+		OCSP_RESPONSE_free(rsp);
+		return 0;
+	}
+
+	res = OCSP_resp_find_status(basic, id, &status, &reason, &produced_at,
+				    &this_update, &next_update);
+	if (!res) {
+		OCSP_CERTID_free(id);
+		id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer);
+		if (!id) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: Could not create OCSP certificate identifier (SHA1)");
+			OCSP_BASICRESP_free(basic);
+			OCSP_RESPONSE_free(rsp);
+			return 0;
+		}
+
+		res = OCSP_resp_find_status(basic, id, &status, &reason,
+					    &produced_at, &this_update,
+					    &next_update);
+	}
+
+	if (!res) {
+		wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s",
+			   (conn->flags & TLS_CONN_REQUIRE_OCSP) ? "" :
+			   " (OCSP not required)");
+		OCSP_CERTID_free(id);
+		OCSP_BASICRESP_free(basic);
+		OCSP_RESPONSE_free(rsp);
+		return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1;
+	}
+	OCSP_CERTID_free(id);
+
+	if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) {
+		tls_show_errors(MSG_INFO, __func__,
+				"OpenSSL: OCSP status times invalid");
+		OCSP_BASICRESP_free(basic);
+		OCSP_RESPONSE_free(rsp);
+		return 0;
+	}
+
+	OCSP_BASICRESP_free(basic);
+	OCSP_RESPONSE_free(rsp);
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status for server certificate: %s",
+		   OCSP_cert_status_str(status));
+
+	if (status == V_OCSP_CERTSTATUS_GOOD)
+		return 1;
+	if (status == V_OCSP_CERTSTATUS_REVOKED)
+		return 0;
+	if (conn->flags & TLS_CONN_REQUIRE_OCSP) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP required");
+		return 0;
+	}
+	wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue");
+	return 1;
+}
+
+
+static int ocsp_status_cb(SSL *s, void *arg)
+{
+	char *tmp;
+	char *resp;
+	size_t len;
+
+	if (tls_global->ocsp_stapling_response == NULL) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - no response configured");
+		return SSL_TLSEXT_ERR_OK;
+	}
+
+	resp = os_readfile(tls_global->ocsp_stapling_response, &len);
+	if (resp == NULL) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - could not read response file");
+		/* TODO: Build OCSPResponse with responseStatus = internalError
+		 */
+		return SSL_TLSEXT_ERR_OK;
+	}
+	wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - send cached response");
+	tmp = OPENSSL_malloc(len);
+	if (tmp == NULL) {
+		os_free(resp);
+		return SSL_TLSEXT_ERR_ALERT_FATAL;
+	}
+
+	os_memcpy(tmp, resp, len);
+	os_free(resp);
+	SSL_set_tlsext_status_ocsp_resp(s, tmp, len);
+
+	return SSL_TLSEXT_ERR_OK;
+}
+
+#endif /* HAVE_OCSP */
+
+
+static size_t max_str_len(const char **lines)
+{
+	const char **p;
+	size_t max_len = 0;
+
+	for (p = lines; *p; p++) {
+		size_t len = os_strlen(*p);
+
+		if (len > max_len)
+			max_len = len;
+	}
+
+	return max_len;
+}
+
+
+static int match_lines_in_file(const char *path, const char **lines)
+{
+	FILE *f;
+	char *buf;
+	size_t bufsize;
+	int found = 0, is_linestart = 1;
+
+	bufsize = max_str_len(lines) + sizeof("\r\n");
+	buf = os_malloc(bufsize);
+	if (!buf)
+		return 0;
+
+	f = fopen(path, "r");
+	if (!f) {
+		os_free(buf);
+		return 0;
+	}
+
+	while (!found && fgets(buf, bufsize, f)) {
+		int is_lineend;
+		size_t len;
+		const char **p;
+
+		len = strcspn(buf, "\r\n");
+		is_lineend = buf[len] != '\0';
+		buf[len] = '\0';
+
+		if (is_linestart && is_lineend) {
+			for (p = lines; !found && *p; p++)
+				found = os_strcmp(buf, *p) == 0;
+		}
+		is_linestart = is_lineend;
+	}
+
+	fclose(f);
+	bin_clear_free(buf, bufsize);
+
+	return found;
+}
+
+
+static int is_tpm2_key(const char *path)
+{
+	/* Check both new and old format of TPM2 PEM guard tag */
+	static const char *tpm2_tags[] = {
+		"-----BEGIN TSS2 PRIVATE KEY-----",
+		"-----BEGIN TSS2 KEY BLOB-----",
+		NULL
+	};
+
+	return match_lines_in_file(path, tpm2_tags);
+}
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+			      const struct tls_connection_params *params)
+{
+	struct tls_data *data = tls_ctx;
+	int ret;
+	unsigned long err;
+	int can_pkcs11 = 0;
+	const char *key_id = params->key_id;
+	const char *cert_id = params->cert_id;
+	const char *ca_cert_id = params->ca_cert_id;
+	const char *engine_id = params->engine ? params->engine_id : NULL;
+	const char *ciphers;
+
+	if (conn == NULL)
+		return -1;
+
+	if (params->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: ocsp=3 not supported");
+		return -1;
+	}
+
+	/*
+	 * If the engine isn't explicitly configured, and any of the
+	 * cert/key fields are actually PKCS#11 URIs, then automatically
+	 * use the PKCS#11 ENGINE.
+	 */
+	if (!engine_id || os_strcmp(engine_id, "pkcs11") == 0)
+		can_pkcs11 = 1;
+
+	if (!key_id && params->private_key && can_pkcs11 &&
+	    os_strncmp(params->private_key, "pkcs11:", 7) == 0) {
+		can_pkcs11 = 2;
+		key_id = params->private_key;
+	}
+
+	if (!cert_id && params->client_cert && can_pkcs11 &&
+	    os_strncmp(params->client_cert, "pkcs11:", 7) == 0) {
+		can_pkcs11 = 2;
+		cert_id = params->client_cert;
+	}
+
+	if (!ca_cert_id && params->ca_cert && can_pkcs11 &&
+	    os_strncmp(params->ca_cert, "pkcs11:", 7) == 0) {
+		can_pkcs11 = 2;
+		ca_cert_id = params->ca_cert;
+	}
+
+	/* If we need to automatically enable the PKCS#11 ENGINE, do so. */
+	if (can_pkcs11 == 2 && !engine_id)
+		engine_id = "pkcs11";
+
+	/* If private_key points to a TPM2-wrapped key, automatically enable
+	 * tpm2 engine and use it to unwrap the key. */
+	if (params->private_key &&
+	    (!engine_id || os_strcmp(engine_id, "tpm2") == 0) &&
+	    is_tpm2_key(params->private_key)) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Found TPM2 wrapped key %s",
+			   params->private_key);
+		key_id = key_id ? key_id : params->private_key;
+		engine_id = engine_id ? engine_id : "tpm2";
+	}
+
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+	if (params->flags & TLS_CONN_EAP_FAST) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Use TLSv1_method() for EAP-FAST");
+		if (SSL_set_ssl_method(conn->ssl, TLSv1_method()) != 1) {
+			tls_show_errors(MSG_INFO, __func__,
+					"Failed to set TLSv1_method() for EAP-FAST");
+			return -1;
+		}
+	}
+#endif
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L
+#ifdef SSL_OP_NO_TLSv1_3
+	if (params->flags & TLS_CONN_EAP_FAST) {
+		/* Need to disable TLS v1.3 at least for now since OpenSSL 1.1.1
+		 * refuses to start the handshake with the modified ciphersuite
+		 * list (no TLS v1.3 ciphersuites included) for EAP-FAST. */
+		wpa_printf(MSG_DEBUG, "OpenSSL: Disable TLSv1.3 for EAP-FAST");
+		SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_3);
+	}
+#endif /* SSL_OP_NO_TLSv1_3 */
+#endif
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+
+	while ((err = ERR_get_error())) {
+		wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s",
+			   __func__, ERR_error_string(err, NULL));
+	}
+
+	if (tls_set_conn_flags(conn, params->flags,
+			       params->openssl_ciphers) < 0)
+		return -1;
+
+	if (engine_id) {
+		wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine %s",
+			   engine_id);
+		ret = tls_engine_init(conn, engine_id, params->pin,
+				      key_id, cert_id, ca_cert_id);
+		if (ret)
+			return ret;
+	}
+	if (tls_connection_set_subject_match(conn,
+					     params->subject_match,
+					     params->altsubject_match,
+					     params->suffix_match,
+					     params->domain_match,
+					     params->check_cert_subject))
+		return -1;
+
+	if (engine_id && ca_cert_id) {
+		if (tls_connection_engine_ca_cert(data, conn, ca_cert_id))
+			return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
+	} else if (tls_connection_ca_cert(data, conn, params->ca_cert,
+					  params->ca_cert_blob,
+					  params->ca_cert_blob_len,
+					  params->ca_path))
+		return -1;
+
+	if (engine_id && cert_id) {
+		if (tls_connection_engine_client_cert(conn, cert_id))
+			return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
+	} else if (tls_connection_client_cert(conn, params->client_cert,
+					      params->client_cert_blob,
+					      params->client_cert_blob_len))
+		return -1;
+
+	if (engine_id && key_id) {
+		wpa_printf(MSG_DEBUG, "TLS: Using private key from engine");
+		if (tls_connection_engine_private_key(conn))
+			return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
+	} else if (tls_connection_private_key(data, conn,
+					      params->private_key,
+					      params->private_key_passwd,
+					      params->private_key_blob,
+					      params->private_key_blob_len)) {
+		wpa_printf(MSG_INFO, "TLS: Failed to load private key '%s'",
+			   params->private_key);
+		return -1;
+	}
+
+	ciphers = params->openssl_ciphers;
+#ifdef CONFIG_SUITEB
+#ifdef OPENSSL_IS_BORINGSSL
+	if (ciphers && os_strcmp(ciphers, "SUITEB192") == 0) {
+		/* BoringSSL removed support for SUITEB192, so need to handle
+		 * this with hardcoded ciphersuite and additional checks for
+		 * other parameters. */
+		ciphers = "ECDHE-ECDSA-AES256-GCM-SHA384";
+	}
+#endif /* OPENSSL_IS_BORINGSSL */
+#endif /* CONFIG_SUITEB */
+	if (ciphers && SSL_set_cipher_list(conn->ssl, ciphers) != 1) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: Failed to set cipher string '%s'",
+			   ciphers);
+		return -1;
+	}
+
+	if (!params->openssl_ecdh_curves) {
+#ifndef OPENSSL_IS_BORINGSSL
+#ifndef OPENSSL_NO_EC
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+		if (SSL_set_ecdh_auto(conn->ssl, 1) != 1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set ECDH curves to auto");
+			return -1;
+		}
+#endif /* < 1.1.0 */
+#endif /* OPENSSL_NO_EC */
+#endif /* OPENSSL_IS_BORINGSSL */
+	} else if (params->openssl_ecdh_curves[0]) {
+#ifdef OPENSSL_IS_BORINGSSL
+		wpa_printf(MSG_INFO,
+			"OpenSSL: ECDH configuration not supported");
+		return -1;
+#else /* !OPENSSL_IS_BORINGSSL */
+#ifndef OPENSSL_NO_EC
+		if (SSL_set1_curves_list(conn->ssl,
+					 params->openssl_ecdh_curves) != 1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set ECDH curves '%s'",
+				   params->openssl_ecdh_curves);
+			return -1;
+		}
+#else /* OPENSSL_NO_EC */
+		wpa_printf(MSG_INFO, "OpenSSL: ECDH not supported");
+		return -1;
+#endif /* OPENSSL_NO_EC */
+#endif /* OPENSSL_IS_BORINGSSL */
+	}
+
+#ifdef OPENSSL_IS_BORINGSSL
+	if (params->flags & TLS_CONN_REQUEST_OCSP) {
+		SSL_enable_ocsp_stapling(conn->ssl);
+	}
+#else /* OPENSSL_IS_BORINGSSL */
+#ifdef HAVE_OCSP
+	if (params->flags & TLS_CONN_REQUEST_OCSP) {
+		SSL_CTX *ssl_ctx = data->ssl;
+		SSL_set_tlsext_status_type(conn->ssl, TLSEXT_STATUSTYPE_ocsp);
+		SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_resp_cb);
+		SSL_CTX_set_tlsext_status_arg(ssl_ctx, conn);
+	}
+#else /* HAVE_OCSP */
+	if (params->flags & TLS_CONN_REQUIRE_OCSP) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: No OCSP support included - reject configuration");
+		return -1;
+	}
+	if (params->flags & TLS_CONN_REQUEST_OCSP) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: No OCSP support included - allow optional OCSP case to continue");
+	}
+#endif /* HAVE_OCSP */
+#endif /* OPENSSL_IS_BORINGSSL */
+
+	conn->flags = params->flags;
+
+	tls_get_errors(data);
+
+	return 0;
+}
+
+
+static void openssl_debug_dump_cipher_list(SSL_CTX *ssl_ctx)
+{
+	SSL *ssl;
+	int i;
+
+	ssl = SSL_new(ssl_ctx);
+	if (!ssl)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "OpenSSL: Enabled cipher suites in priority order");
+	for (i = 0; ; i++) {
+		const char *cipher;
+
+		cipher = SSL_get_cipher_list(ssl, i);
+		if (!cipher)
+			break;
+		wpa_printf(MSG_DEBUG, "Cipher %d: %s", i, cipher);
+	}
+
+	SSL_free(ssl);
+}
+
+
+#if !defined(LIBRESSL_VERSION_NUMBER) && !defined(BORINGSSL_API_VERSION)
+
+static const char * openssl_pkey_type_str(const EVP_PKEY *pkey)
+{
+	if (!pkey)
+		return "NULL";
+	switch (EVP_PKEY_type(EVP_PKEY_id(pkey))) {
+	case EVP_PKEY_RSA:
+		return "RSA";
+	case EVP_PKEY_DSA:
+		return "DSA";
+	case EVP_PKEY_DH:
+		return "DH";
+	case EVP_PKEY_EC:
+		return "EC";
+	default:
+		return "?";
+	}
+}
+
+
+static void openssl_debug_dump_certificate(int i, X509 *cert)
+{
+	char buf[256];
+	EVP_PKEY *pkey;
+	ASN1_INTEGER *ser;
+	char serial_num[128];
+
+	if (!cert)
+		return;
+
+	X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
+
+	ser = X509_get_serialNumber(cert);
+	if (ser)
+		wpa_snprintf_hex_uppercase(serial_num, sizeof(serial_num),
+					   ASN1_STRING_get0_data(ser),
+					   ASN1_STRING_length(ser));
+	else
+		serial_num[0] = '\0';
+
+	pkey = X509_get_pubkey(cert);
+	wpa_printf(MSG_DEBUG, "%d: %s (%s) %s", i, buf,
+		   openssl_pkey_type_str(pkey), serial_num);
+	EVP_PKEY_free(pkey);
+}
+
+
+static void openssl_debug_dump_certificates(SSL_CTX *ssl_ctx)
+{
+	STACK_OF(X509) *certs;
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: Configured certificate chain");
+	if (SSL_CTX_get0_chain_certs(ssl_ctx, &certs) == 1) {
+		int i;
+
+		for (i = sk_X509_num(certs); i > 0; i--)
+			openssl_debug_dump_certificate(i, sk_X509_value(certs,
+									i - 1));
+	}
+	openssl_debug_dump_certificate(0, SSL_CTX_get0_certificate(ssl_ctx));
+}
+
+#endif
+
+
+static void openssl_debug_dump_certificate_chains(SSL_CTX *ssl_ctx)
+{
+#if !defined(LIBRESSL_VERSION_NUMBER) && !defined(BORINGSSL_API_VERSION)
+	int res;
+
+	for (res = SSL_CTX_set_current_cert(ssl_ctx, SSL_CERT_SET_FIRST);
+	     res == 1;
+	     res = SSL_CTX_set_current_cert(ssl_ctx, SSL_CERT_SET_NEXT))
+		openssl_debug_dump_certificates(ssl_ctx);
+
+	SSL_CTX_set_current_cert(ssl_ctx, SSL_CERT_SET_FIRST);
+#endif
+}
+
+
+static void openssl_debug_dump_ctx(SSL_CTX *ssl_ctx)
+{
+	openssl_debug_dump_cipher_list(ssl_ctx);
+	openssl_debug_dump_certificate_chains(ssl_ctx);
+}
+
+
+int tls_global_set_params(void *tls_ctx,
+			  const struct tls_connection_params *params)
+{
+	struct tls_data *data = tls_ctx;
+	SSL_CTX *ssl_ctx = data->ssl;
+	unsigned long err;
+
+	while ((err = ERR_get_error())) {
+		wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s",
+			   __func__, ERR_error_string(err, NULL));
+	}
+
+	os_free(data->check_cert_subject);
+	data->check_cert_subject = NULL;
+	if (params->check_cert_subject) {
+		data->check_cert_subject =
+			os_strdup(params->check_cert_subject);
+		if (!data->check_cert_subject)
+			return -1;
+	}
+
+	if (tls_global_ca_cert(data, params->ca_cert) ||
+	    tls_global_client_cert(data, params->client_cert) ||
+	    tls_global_private_key(data, params->private_key,
+				   params->private_key_passwd) ||
+	    tls_global_client_cert(data, params->client_cert2) ||
+	    tls_global_private_key(data, params->private_key2,
+				   params->private_key_passwd2) ||
+	    tls_global_dh(data, params->dh_file)) {
+		wpa_printf(MSG_INFO, "TLS: Failed to set global parameters");
+		return -1;
+	}
+
+	os_free(data->openssl_ciphers);
+	if (params->openssl_ciphers) {
+		data->openssl_ciphers = os_strdup(params->openssl_ciphers);
+		if (!data->openssl_ciphers)
+			return -1;
+	} else {
+		data->openssl_ciphers = NULL;
+	}
+	if (params->openssl_ciphers &&
+	    SSL_CTX_set_cipher_list(ssl_ctx, params->openssl_ciphers) != 1) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: Failed to set cipher string '%s'",
+			   params->openssl_ciphers);
+		return -1;
+	}
+
+	if (!params->openssl_ecdh_curves) {
+#ifndef OPENSSL_IS_BORINGSSL
+#ifndef OPENSSL_NO_EC
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+		if (SSL_CTX_set_ecdh_auto(ssl_ctx, 1) != 1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set ECDH curves to auto");
+			return -1;
+		}
+#endif /* < 1.1.0 */
+#endif /* OPENSSL_NO_EC */
+#endif /* OPENSSL_IS_BORINGSSL */
+	} else if (params->openssl_ecdh_curves[0]) {
+#ifdef OPENSSL_IS_BORINGSSL
+		wpa_printf(MSG_INFO,
+			"OpenSSL: ECDH configuration not supported");
+		return -1;
+#else /* !OPENSSL_IS_BORINGSSL */
+#ifndef OPENSSL_NO_EC
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+		SSL_CTX_set_ecdh_auto(ssl_ctx, 1);
+#endif
+		if (SSL_CTX_set1_curves_list(ssl_ctx,
+					     params->openssl_ecdh_curves) !=
+		    1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set ECDH curves '%s'",
+				   params->openssl_ecdh_curves);
+			return -1;
+		}
+#else /* OPENSSL_NO_EC */
+		wpa_printf(MSG_INFO, "OpenSSL: ECDH not supported");
+		return -1;
+#endif /* OPENSSL_NO_EC */
+#endif /* OPENSSL_IS_BORINGSSL */
+	}
+
+#ifdef SSL_OP_NO_TICKET
+	if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET)
+		SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET);
+	else
+		SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TICKET);
+#endif /*  SSL_OP_NO_TICKET */
+
+#ifdef HAVE_OCSP
+	SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_status_cb);
+	SSL_CTX_set_tlsext_status_arg(ssl_ctx, ssl_ctx);
+	os_free(tls_global->ocsp_stapling_response);
+	if (params->ocsp_stapling_response)
+		tls_global->ocsp_stapling_response =
+			os_strdup(params->ocsp_stapling_response);
+	else
+		tls_global->ocsp_stapling_response = NULL;
+#endif /* HAVE_OCSP */
+
+	openssl_debug_dump_ctx(ssl_ctx);
+
+	return 0;
+}
+
+
+#ifdef EAP_FAST_OR_TEAP
+/* Pre-shared secred requires a patch to openssl, so this function is
+ * commented out unless explicitly needed for EAP-FAST in order to be able to
+ * build this file with unmodified openssl. */
+
+#if (defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER)
+static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len,
+			   STACK_OF(SSL_CIPHER) *peer_ciphers,
+			   const SSL_CIPHER **cipher, void *arg)
+#else /* OPENSSL_IS_BORINGSSL */
+static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len,
+			   STACK_OF(SSL_CIPHER) *peer_ciphers,
+			   SSL_CIPHER **cipher, void *arg)
+#endif /* OPENSSL_IS_BORINGSSL */
+{
+	struct tls_connection *conn = arg;
+	int ret;
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+	if (conn == NULL || conn->session_ticket_cb == NULL)
+		return 0;
+
+	ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx,
+				      conn->session_ticket,
+				      conn->session_ticket_len,
+				      s->s3->client_random,
+				      s->s3->server_random, secret);
+#else
+	unsigned char client_random[SSL3_RANDOM_SIZE];
+	unsigned char server_random[SSL3_RANDOM_SIZE];
+
+	if (conn == NULL || conn->session_ticket_cb == NULL)
+		return 0;
+
+	SSL_get_client_random(s, client_random, sizeof(client_random));
+	SSL_get_server_random(s, server_random, sizeof(server_random));
+
+	ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx,
+				      conn->session_ticket,
+				      conn->session_ticket_len,
+				      client_random,
+				      server_random, secret);
+#endif
+
+	os_free(conn->session_ticket);
+	conn->session_ticket = NULL;
+
+	if (ret <= 0)
+		return 0;
+
+	*secret_len = SSL_MAX_MASTER_KEY_LENGTH;
+	return 1;
+}
+
+
+static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data,
+				     int len, void *arg)
+{
+	struct tls_connection *conn = arg;
+
+	if (conn == NULL || conn->session_ticket_cb == NULL)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: %s: length=%d", __func__, len);
+
+	os_free(conn->session_ticket);
+	conn->session_ticket = NULL;
+
+	wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket "
+		    "extension", data, len);
+
+	conn->session_ticket = os_memdup(data, len);
+	if (conn->session_ticket == NULL)
+		return 0;
+
+	conn->session_ticket_len = len;
+
+	return 1;
+}
+#endif /* EAP_FAST_OR_TEAP */
+
+
+int tls_connection_set_session_ticket_cb(void *tls_ctx,
+					 struct tls_connection *conn,
+					 tls_session_ticket_cb cb,
+					 void *ctx)
+{
+#ifdef EAP_FAST_OR_TEAP
+	conn->session_ticket_cb = cb;
+	conn->session_ticket_cb_ctx = ctx;
+
+	if (cb) {
+		if (SSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb,
+					      conn) != 1)
+			return -1;
+		SSL_set_session_ticket_ext_cb(conn->ssl,
+					      tls_session_ticket_ext_cb, conn);
+	} else {
+		if (SSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1)
+			return -1;
+		SSL_set_session_ticket_ext_cb(conn->ssl, NULL, NULL);
+	}
+
+	return 0;
+#else /* EAP_FAST_OR_TEAP */
+	return -1;
+#endif /* EAP_FAST_OR_TEAP */
+}
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
+	return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s",
+			   OPENSSL_VERSION_TEXT,
+			   OpenSSL_version(OPENSSL_VERSION));
+#else
+	return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s",
+			   OPENSSL_VERSION_TEXT,
+			   SSLeay_version(SSLEAY_VERSION));
+#endif
+}
+
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+				     struct wpabuf *data)
+{
+	SSL_SESSION *sess;
+	struct wpabuf *old;
+	struct tls_session_data *sess_data = NULL;
+
+	if (tls_ex_idx_session < 0)
+		goto fail;
+	sess = SSL_get_session(conn->ssl);
+	if (!sess)
+		goto fail;
+	old = SSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+	if (old) {
+		struct tls_session_data *found;
+
+		found = get_session_data(conn->context, old);
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Replacing old success data %p (sess %p)%s",
+			   old, sess, found ? "" : " (not freeing)");
+		if (found) {
+			dl_list_del(&found->list);
+			os_free(found);
+			wpabuf_free(old);
+		}
+	}
+
+	sess_data = os_zalloc(sizeof(*sess_data));
+	if (!sess_data ||
+	    SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, data) != 1)
+		goto fail;
+
+	sess_data->buf = data;
+	dl_list_add(&conn->context->sessions, &sess_data->list);
+	wpa_printf(MSG_DEBUG, "OpenSSL: Stored success data %p (sess %p)",
+		   data, sess);
+	conn->success_data = 1;
+	return;
+
+fail:
+	wpa_printf(MSG_INFO, "OpenSSL: Failed to store success data");
+	wpabuf_free(data);
+	os_free(sess_data);
+}
+
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
+{
+	wpa_printf(MSG_DEBUG,
+		   "OpenSSL: Success data accepted for resumed session");
+	conn->success_data = 1;
+}
+
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn)
+{
+	SSL_SESSION *sess;
+
+	if (tls_ex_idx_session < 0 ||
+	    !(sess = SSL_get_session(conn->ssl)))
+		return NULL;
+	return SSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+}
+
+
+void tls_connection_remove_session(struct tls_connection *conn)
+{
+	SSL_SESSION *sess;
+
+	sess = SSL_get_session(conn->ssl);
+	if (!sess)
+		return;
+
+	if (SSL_CTX_remove_session(conn->ssl_ctx, sess) != 1)
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Session was not cached");
+	else
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Removed cached session to disable session resumption");
+}
+
+
+int tls_get_tls_unique(struct tls_connection *conn, u8 *buf, size_t max_len)
+{
+	size_t len;
+	int reused;
+
+	reused = SSL_session_reused(conn->ssl);
+	if ((conn->server && !reused) || (!conn->server && reused))
+		len = SSL_get_peer_finished(conn->ssl, buf, max_len);
+	else
+		len = SSL_get_finished(conn->ssl, buf, max_len);
+
+	if (len == 0 || len > max_len)
+		return -1;
+
+	return len;
+}
+
+
+u16 tls_connection_get_cipher_suite(struct tls_connection *conn)
+{
+	const SSL_CIPHER *cipher;
+
+	cipher = SSL_get_current_cipher(conn->ssl);
+	if (!cipher)
+		return 0;
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
+	return SSL_CIPHER_get_protocol_id(cipher);
+#else
+	return SSL_CIPHER_get_id(cipher) & 0xFFFF;
+#endif
+}
+
+
+const char * tls_connection_get_peer_subject(struct tls_connection *conn)
+{
+	if (conn)
+		return conn->peer_subject;
+	return NULL;
+}
+
+
+bool tls_connection_get_own_cert_used(struct tls_connection *conn)
+{
+	if (conn)
+		return SSL_get_certificate(conn->ssl) != NULL;
+	return false;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls_openssl.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls_openssl.h
new file mode 100644
index 0000000..2a62d5c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls_openssl.h
@@ -0,0 +1,19 @@
+/*
+ * SSL/TLS interface functions for OpenSSL
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TLS_OPENSSL_H
+#define TLS_OPENSSL_H
+
+enum ocsp_result {
+	OCSP_GOOD, OCSP_REVOKED, OCSP_NO_RESPONSE, OCSP_INVALID
+};
+
+enum ocsp_result check_ocsp_resp(SSL_CTX *ssl_ctx, SSL *ssl, X509 *cert,
+				 X509 *issuer, X509 *issuer_issuer);
+
+#endif /* TLS_OPENSSL_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls_openssl_ocsp.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls_openssl_ocsp.c
new file mode 100644
index 0000000..4e4c2ac
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls_openssl_ocsp.c
@@ -0,0 +1,842 @@
+/*
+ * SSL/TLS interface functions for OpenSSL - BoringSSL OCSP
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/x509v3.h>
+#ifdef OPENSSL_IS_BORINGSSL
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#endif /* OPENSSL_IS_BORINGSSL */
+
+#include "common.h"
+#include "tls_openssl.h"
+
+
+#ifdef OPENSSL_IS_BORINGSSL
+
+static void tls_show_errors(int level, const char *func, const char *txt)
+{
+	unsigned long err;
+
+	wpa_printf(level, "OpenSSL: %s - %s %s",
+		   func, txt, ERR_error_string(ERR_get_error(), NULL));
+
+	while ((err = ERR_get_error())) {
+		wpa_printf(MSG_INFO, "OpenSSL: pending error: %s",
+			   ERR_error_string(err, NULL));
+	}
+}
+
+
+/*
+ * CertID ::= SEQUENCE {
+ *     hashAlgorithm      AlgorithmIdentifier,
+ *     issuerNameHash     OCTET STRING, -- Hash of Issuer's DN
+ *     issuerKeyHash      OCTET STRING, -- Hash of Issuer's public key
+ *     serialNumber       CertificateSerialNumber }
+ */
+typedef struct {
+	X509_ALGOR *hashAlgorithm;
+	ASN1_OCTET_STRING *issuerNameHash;
+	ASN1_OCTET_STRING *issuerKeyHash;
+	ASN1_INTEGER *serialNumber;
+} CertID;
+
+/*
+ * ResponseBytes ::=       SEQUENCE {
+ *     responseType   OBJECT IDENTIFIER,
+ *     response       OCTET STRING }
+ */
+typedef struct {
+	ASN1_OBJECT *responseType;
+	ASN1_OCTET_STRING *response;
+} ResponseBytes;
+
+/*
+ * OCSPResponse ::= SEQUENCE {
+ *    responseStatus         OCSPResponseStatus,
+ *    responseBytes          [0] EXPLICIT ResponseBytes OPTIONAL }
+ */
+typedef struct {
+	ASN1_ENUMERATED *responseStatus;
+	ResponseBytes *responseBytes;
+} OCSPResponse;
+
+ASN1_SEQUENCE(ResponseBytes) = {
+	ASN1_SIMPLE(ResponseBytes, responseType, ASN1_OBJECT),
+	ASN1_SIMPLE(ResponseBytes, response, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(ResponseBytes);
+
+ASN1_SEQUENCE(OCSPResponse) = {
+	ASN1_SIMPLE(OCSPResponse, responseStatus, ASN1_ENUMERATED),
+	ASN1_EXP_OPT(OCSPResponse, responseBytes, ResponseBytes, 0)
+} ASN1_SEQUENCE_END(OCSPResponse);
+
+IMPLEMENT_ASN1_FUNCTIONS(OCSPResponse);
+
+/*
+ * ResponderID ::= CHOICE {
+ *    byName               [1] Name,
+ *    byKey                [2] KeyHash }
+ */
+typedef struct {
+	int type;
+	union {
+		X509_NAME *byName;
+		ASN1_OCTET_STRING *byKey;
+	} value;
+} ResponderID;
+
+/*
+ * RevokedInfo ::= SEQUENCE {
+ *     revocationTime              GeneralizedTime,
+ *     revocationReason    [0]     EXPLICIT CRLReason OPTIONAL }
+ */
+typedef struct {
+	ASN1_GENERALIZEDTIME *revocationTime;
+	ASN1_ENUMERATED *revocationReason;
+} RevokedInfo;
+
+/*
+ * CertStatus ::= CHOICE {
+ *     good        [0]     IMPLICIT NULL,
+ *     revoked     [1]     IMPLICIT RevokedInfo,
+ *     unknown     [2]     IMPLICIT UnknownInfo }
+ */
+typedef struct {
+	int type;
+	union {
+		ASN1_NULL *good;
+		RevokedInfo *revoked;
+		ASN1_NULL *unknown;
+	} value;
+} CertStatus;
+
+/*
+ * SingleResponse ::= SEQUENCE {
+ *    certID                       CertID,
+ *    certStatus                   CertStatus,
+ *    thisUpdate                   GeneralizedTime,
+ *    nextUpdate         [0]       EXPLICIT GeneralizedTime OPTIONAL,
+ *    singleExtensions   [1]       EXPLICIT Extensions OPTIONAL }
+ */
+typedef struct {
+	CertID *certID;
+	CertStatus *certStatus;
+	ASN1_GENERALIZEDTIME *thisUpdate;
+	ASN1_GENERALIZEDTIME *nextUpdate;
+	STACK_OF(X509_EXTENSION) *singleExtensions;
+} SingleResponse;
+
+/*
+ * ResponseData ::= SEQUENCE {
+ *   version              [0] EXPLICIT Version DEFAULT v1,
+ *   responderID              ResponderID,
+ *   producedAt               GeneralizedTime,
+ *   responses                SEQUENCE OF SingleResponse,
+ *   responseExtensions   [1] EXPLICIT Extensions OPTIONAL }
+ */
+typedef struct {
+	ASN1_INTEGER *version;
+	ResponderID *responderID;
+	ASN1_GENERALIZEDTIME *producedAt;
+	STACK_OF(SingleResponse) *responses;
+	STACK_OF(X509_EXTENSION) *responseExtensions;
+} ResponseData;
+
+/*
+ * BasicOCSPResponse       ::= SEQUENCE {
+ *   tbsResponseData      ResponseData,
+ *   signatureAlgorithm   AlgorithmIdentifier,
+ *   signature            BIT STRING,
+ *   certs                [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+ */
+typedef struct {
+	ResponseData *tbsResponseData;
+	X509_ALGOR *signatureAlgorithm;
+	ASN1_BIT_STRING *signature;
+	STACK_OF(X509) *certs;
+} BasicOCSPResponse;
+
+ASN1_SEQUENCE(CertID) = {
+	ASN1_SIMPLE(CertID, hashAlgorithm, X509_ALGOR),
+	ASN1_SIMPLE(CertID, issuerNameHash, ASN1_OCTET_STRING),
+	ASN1_SIMPLE(CertID, issuerKeyHash, ASN1_OCTET_STRING),
+	ASN1_SIMPLE(CertID, serialNumber, ASN1_INTEGER)
+} ASN1_SEQUENCE_END(CertID);
+
+ASN1_CHOICE(ResponderID) = {
+	ASN1_EXP(ResponderID, value.byName, X509_NAME, 1),
+	ASN1_EXP(ResponderID, value.byKey, ASN1_OCTET_STRING, 2)
+} ASN1_CHOICE_END(ResponderID);
+
+ASN1_SEQUENCE(RevokedInfo) = {
+	ASN1_SIMPLE(RevokedInfo, revocationTime, ASN1_GENERALIZEDTIME),
+	ASN1_EXP_OPT(RevokedInfo, revocationReason, ASN1_ENUMERATED, 0)
+} ASN1_SEQUENCE_END(RevokedInfo);
+
+ASN1_CHOICE(CertStatus) = {
+	ASN1_IMP(CertStatus, value.good, ASN1_NULL, 0),
+	ASN1_IMP(CertStatus, value.revoked, RevokedInfo, 1),
+	ASN1_IMP(CertStatus, value.unknown, ASN1_NULL, 2)
+} ASN1_CHOICE_END(CertStatus);
+
+ASN1_SEQUENCE(SingleResponse) = {
+	ASN1_SIMPLE(SingleResponse, certID, CertID),
+	ASN1_SIMPLE(SingleResponse, certStatus, CertStatus),
+	ASN1_SIMPLE(SingleResponse, thisUpdate, ASN1_GENERALIZEDTIME),
+	ASN1_EXP_OPT(SingleResponse, nextUpdate, ASN1_GENERALIZEDTIME, 0),
+	ASN1_EXP_SEQUENCE_OF_OPT(SingleResponse, singleExtensions,
+				 X509_EXTENSION, 1)
+} ASN1_SEQUENCE_END(SingleResponse);
+
+ASN1_SEQUENCE(ResponseData) = {
+	ASN1_EXP_OPT(ResponseData, version, ASN1_INTEGER, 0),
+	ASN1_SIMPLE(ResponseData, responderID, ResponderID),
+	ASN1_SIMPLE(ResponseData, producedAt, ASN1_GENERALIZEDTIME),
+	ASN1_SEQUENCE_OF(ResponseData, responses, SingleResponse),
+	ASN1_EXP_SEQUENCE_OF_OPT(ResponseData, responseExtensions,
+				 X509_EXTENSION, 1)
+} ASN1_SEQUENCE_END(ResponseData);
+
+ASN1_SEQUENCE(BasicOCSPResponse) = {
+	ASN1_SIMPLE(BasicOCSPResponse, tbsResponseData, ResponseData),
+	ASN1_SIMPLE(BasicOCSPResponse, signatureAlgorithm, X509_ALGOR),
+	ASN1_SIMPLE(BasicOCSPResponse, signature, ASN1_BIT_STRING),
+	ASN1_EXP_SEQUENCE_OF_OPT(BasicOCSPResponse, certs, X509, 0)
+} ASN1_SEQUENCE_END(BasicOCSPResponse);
+
+IMPLEMENT_ASN1_FUNCTIONS(BasicOCSPResponse);
+
+DEFINE_STACK_OF(SingleResponse)
+
+static char * mem_bio_to_str(BIO *out)
+{
+	char *txt;
+	size_t rlen;
+	int res;
+
+	rlen = BIO_ctrl_pending(out);
+	txt = os_malloc(rlen + 1);
+	if (!txt) {
+		BIO_free(out);
+		return NULL;
+	}
+
+	res = BIO_read(out, txt, rlen);
+	BIO_free(out);
+	if (res < 0) {
+		os_free(txt);
+		return NULL;
+	}
+
+	txt[res] = '\0';
+	return txt;
+}
+
+
+static char * generalizedtime_str(ASN1_GENERALIZEDTIME *t)
+{
+	BIO *out;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return NULL;
+
+	if (!ASN1_GENERALIZEDTIME_print(out, t)) {
+		BIO_free(out);
+		return NULL;
+	}
+
+	return mem_bio_to_str(out);
+}
+
+
+static char * responderid_str(ResponderID *rid)
+{
+	BIO *out;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return NULL;
+
+	switch (rid->type) {
+	case 0:
+		X509_NAME_print_ex(out, rid->value.byName, 0, XN_FLAG_ONELINE);
+		break;
+	case 1:
+		i2a_ASN1_STRING(out, rid->value.byKey, V_ASN1_OCTET_STRING);
+		break;
+	default:
+		BIO_free(out);
+		return NULL;
+	}
+
+	return mem_bio_to_str(out);
+}
+
+
+static char * octet_string_str(ASN1_OCTET_STRING *o)
+{
+	BIO *out;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return NULL;
+
+	i2a_ASN1_STRING(out, o, V_ASN1_OCTET_STRING);
+	return mem_bio_to_str(out);
+}
+
+
+static char * integer_str(ASN1_INTEGER *i)
+{
+	BIO *out;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return NULL;
+
+	i2a_ASN1_INTEGER(out, i);
+	return mem_bio_to_str(out);
+}
+
+
+static char * algor_str(X509_ALGOR *alg)
+{
+	BIO *out;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return NULL;
+
+	i2a_ASN1_OBJECT(out, alg->algorithm);
+	return mem_bio_to_str(out);
+}
+
+
+static char * extensions_str(const char *title, STACK_OF(X509_EXTENSION) *ext)
+{
+	BIO *out;
+
+	if (!ext)
+		return NULL;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return NULL;
+
+	if (!X509V3_extensions_print(out, title, ext, 0, 0)) {
+		BIO_free(out);
+		return NULL;
+	}
+	return mem_bio_to_str(out);
+}
+
+
+static int ocsp_resp_valid(ASN1_GENERALIZEDTIME *thisupd,
+			   ASN1_GENERALIZEDTIME *nextupd)
+{
+	time_t now, tmp;
+
+	if (!ASN1_GENERALIZEDTIME_check(thisupd)) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Invalid OCSP response thisUpdate");
+		return 0;
+	}
+
+	time(&now);
+	tmp = now + 5 * 60; /* allow five minute clock difference */
+	if (X509_cmp_time(thisupd, &tmp) > 0) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response not yet valid");
+		return 0;
+	}
+
+	if (!nextupd)
+		return 1; /* OK - no limit on response age */
+
+	if (!ASN1_GENERALIZEDTIME_check(nextupd)) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Invalid OCSP response nextUpdate");
+		return 0;
+	}
+
+	tmp = now - 5 * 60; /* allow five minute clock difference */
+	if (X509_cmp_time(nextupd, &tmp) < 0) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response expired");
+		return 0;
+	}
+
+	if (ASN1_STRING_cmp(nextupd, thisupd) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: OCSP response nextUpdate before thisUpdate");
+		return 0;
+	}
+
+	/* Both thisUpdate and nextUpdate are valid */
+	return -1;
+}
+
+
+static int issuer_match(X509 *cert, X509 *issuer, CertID *certid)
+{
+	X509_NAME *iname;
+	ASN1_BIT_STRING *ikey;
+	const EVP_MD *dgst;
+	unsigned int len;
+	unsigned char md[EVP_MAX_MD_SIZE];
+	ASN1_OCTET_STRING *hash;
+	char *txt;
+
+	dgst = EVP_get_digestbyobj(certid->hashAlgorithm->algorithm);
+	if (!dgst) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Could not find matching hash algorithm for OCSP");
+		return -1;
+	}
+
+	iname = X509_get_issuer_name(cert);
+	if (!X509_NAME_digest(iname, dgst, md, &len))
+		return -1;
+	hash = ASN1_OCTET_STRING_new();
+	if (!hash)
+		return -1;
+	if (!ASN1_OCTET_STRING_set(hash, md, len)) {
+		ASN1_OCTET_STRING_free(hash);
+		return -1;
+	}
+
+	txt = octet_string_str(hash);
+	if (txt) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: calculated issuerNameHash: %s",
+			   txt);
+		os_free(txt);
+	}
+
+	if (ASN1_OCTET_STRING_cmp(certid->issuerNameHash, hash)) {
+		ASN1_OCTET_STRING_free(hash);
+		return -1;
+	}
+
+	ikey = X509_get0_pubkey_bitstr(issuer);
+	if (!ikey ||
+	    !EVP_Digest(ikey->data, ikey->length, md, &len, dgst, NULL) ||
+	    !ASN1_OCTET_STRING_set(hash, md, len)) {
+		ASN1_OCTET_STRING_free(hash);
+		return -1;
+	}
+
+	txt = octet_string_str(hash);
+	if (txt) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: calculated issuerKeyHash: %s",
+			   txt);
+		os_free(txt);
+	}
+
+	if (ASN1_OCTET_STRING_cmp(certid->issuerKeyHash, hash)) {
+		ASN1_OCTET_STRING_free(hash);
+		return -1;
+	}
+
+	ASN1_OCTET_STRING_free(hash);
+	return 0;
+}
+
+
+static X509 * ocsp_find_signer(STACK_OF(X509) *certs, ResponderID *rid)
+{
+	unsigned int i;
+	unsigned char hash[SHA_DIGEST_LENGTH];
+
+	if (rid->type == 0) {
+		/* byName */
+		return X509_find_by_subject(certs, rid->value.byName);
+	}
+
+	/* byKey */
+	if (rid->value.byKey->length != SHA_DIGEST_LENGTH)
+		return NULL;
+	for (i = 0; i < sk_X509_num(certs); i++) {
+		X509 *x = sk_X509_value(certs, i);
+
+		X509_pubkey_digest(x, EVP_sha1(), hash, NULL);
+		if (os_memcmp(rid->value.byKey->data, hash,
+			      SHA_DIGEST_LENGTH) == 0)
+			return x;
+	}
+
+	return NULL;
+}
+
+
+enum ocsp_result check_ocsp_resp(SSL_CTX *ssl_ctx, SSL *ssl, X509 *cert,
+				 X509 *issuer, X509 *issuer_issuer)
+{
+	const uint8_t *resp_data;
+	size_t resp_len;
+	OCSPResponse *resp;
+	int status;
+	ResponseBytes *bytes;
+	const u8 *basic_data;
+	size_t basic_len;
+	BasicOCSPResponse *basic;
+	ResponseData *rd;
+	char *txt;
+	int i, num;
+	unsigned int j, num_resp;
+	SingleResponse *matching_resp = NULL, *cmp_sresp;
+	enum ocsp_result result = OCSP_INVALID;
+	X509_STORE *store;
+	STACK_OF(X509) *untrusted = NULL, *certs = NULL, *chain = NULL;
+	X509_STORE_CTX *ctx = NULL;
+	X509 *signer, *tmp_cert;
+	int signer_trusted = 0;
+	EVP_PKEY *skey;
+	int ret;
+	char buf[256];
+
+	txt = integer_str(X509_get_serialNumber(cert));
+	if (txt) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Searching OCSP response for peer certificate serialNumber: %s", txt);
+		os_free(txt);
+	}
+
+	SSL_get0_ocsp_response(ssl, &resp_data, &resp_len);
+	if (resp_data == NULL || resp_len == 0) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received");
+		return OCSP_NO_RESPONSE;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", resp_data, resp_len);
+
+	resp = d2i_OCSPResponse(NULL, &resp_data, resp_len);
+	if (!resp) {
+		wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSPResponse");
+		return OCSP_INVALID;
+	}
+
+	status = ASN1_ENUMERATED_get(resp->responseStatus);
+	if (status != 0) {
+		wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d",
+			   status);
+		return OCSP_INVALID;
+	}
+
+	bytes = resp->responseBytes;
+
+	if (!bytes ||
+	    OBJ_obj2nid(bytes->responseType) != NID_id_pkix_OCSP_basic) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: Could not find BasicOCSPResponse");
+		return OCSP_INVALID;
+	}
+
+	basic_data = ASN1_STRING_data(bytes->response);
+	basic_len = ASN1_STRING_length(bytes->response);
+	wpa_hexdump(MSG_DEBUG, "OpenSSL: BasicOCSPResponse",
+		    basic_data, basic_len);
+
+	basic = d2i_BasicOCSPResponse(NULL, &basic_data, basic_len);
+	if (!basic) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: Could not parse BasicOCSPResponse");
+		OCSPResponse_free(resp);
+		return OCSP_INVALID;
+	}
+
+	rd = basic->tbsResponseData;
+
+	if (basic->certs) {
+		untrusted = sk_X509_dup(basic->certs);
+		if (!untrusted)
+			goto fail;
+
+		num = sk_X509_num(basic->certs);
+		for (i = 0; i < num; i++) {
+			X509 *extra_cert;
+
+			extra_cert = sk_X509_value(basic->certs, i);
+			X509_NAME_oneline(X509_get_subject_name(extra_cert),
+					  buf, sizeof(buf));
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: BasicOCSPResponse cert %s", buf);
+
+			if (!sk_X509_push(untrusted, extra_cert)) {
+				wpa_printf(MSG_DEBUG,
+					   "OpenSSL: Could not add certificate to the untrusted stack");
+			}
+		}
+	}
+
+	store = SSL_CTX_get_cert_store(ssl_ctx);
+	if (issuer) {
+		if (X509_STORE_add_cert(store, issuer) != 1) {
+			tls_show_errors(MSG_INFO, __func__,
+					"OpenSSL: Could not add issuer to certificate store");
+		}
+		certs = sk_X509_new_null();
+		if (certs) {
+			tmp_cert = X509_dup(issuer);
+			if (tmp_cert && !sk_X509_push(certs, tmp_cert)) {
+				tls_show_errors(
+					MSG_INFO, __func__,
+					"OpenSSL: Could not add issuer to OCSP responder trust store");
+				X509_free(tmp_cert);
+				sk_X509_free(certs);
+				certs = NULL;
+			}
+			if (certs && issuer_issuer) {
+				tmp_cert = X509_dup(issuer_issuer);
+				if (tmp_cert &&
+				    !sk_X509_push(certs, tmp_cert)) {
+					tls_show_errors(
+						MSG_INFO, __func__,
+						"OpenSSL: Could not add issuer's issuer to OCSP responder trust store");
+					X509_free(tmp_cert);
+				}
+			}
+		}
+	}
+
+	signer = ocsp_find_signer(certs, rd->responderID);
+	if (!signer)
+		signer = ocsp_find_signer(untrusted, rd->responderID);
+	else
+		signer_trusted = 1;
+	if (!signer) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Could not find OCSP signer certificate");
+		goto fail;
+	}
+
+	skey = X509_get_pubkey(signer);
+	if (!skey) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Could not get OCSP signer public key");
+		goto fail;
+	}
+	if (ASN1_item_verify(ASN1_ITEM_rptr(ResponseData),
+			     basic->signatureAlgorithm, basic->signature,
+			     basic->tbsResponseData, skey) <= 0) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: BasicOCSPResponse signature is invalid");
+		goto fail;
+	}
+
+	X509_NAME_oneline(X509_get_subject_name(signer), buf, sizeof(buf));
+	wpa_printf(MSG_DEBUG,
+		   "OpenSSL: Found OCSP signer certificate %s and verified BasicOCSPResponse signature",
+		   buf);
+
+	ctx = X509_STORE_CTX_new();
+	if (!ctx || !X509_STORE_CTX_init(ctx, store, signer, untrusted))
+		goto fail;
+	X509_STORE_CTX_set_purpose(ctx, X509_PURPOSE_OCSP_HELPER);
+	ret = X509_verify_cert(ctx);
+	chain = X509_STORE_CTX_get1_chain(ctx);
+	X509_STORE_CTX_cleanup(ctx);
+	if (ret <= 0) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Could not validate OCSP signer certificate");
+		goto fail;
+	}
+
+	if (!chain || sk_X509_num(chain) <= 0) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP signer chain found");
+		goto fail;
+	}
+
+	if (!signer_trusted) {
+		X509_check_purpose(signer, -1, 0);
+		if ((X509_get_extension_flags(signer) & EXFLAG_XKUSAGE) &&
+		    (X509_get_extended_key_usage(signer) & XKU_OCSP_SIGN)) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: OCSP signer certificate delegation OK");
+		} else {
+			tmp_cert = sk_X509_value(chain, sk_X509_num(chain) - 1);
+			if (X509_check_trust(tmp_cert, NID_OCSP_sign, 0) !=
+			    X509_TRUST_TRUSTED) {
+				wpa_printf(MSG_DEBUG,
+					   "OpenSSL: OCSP signer certificate not trusted");
+				result = OCSP_NO_RESPONSE;
+				goto fail;
+			}
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: OCSP version: %lu",
+		   ASN1_INTEGER_get(rd->version));
+
+	txt = responderid_str(rd->responderID);
+	if (txt) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: OCSP responderID: %s",
+			   txt);
+		os_free(txt);
+	}
+
+	txt = generalizedtime_str(rd->producedAt);
+	if (txt) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: OCSP producedAt: %s",
+			   txt);
+		os_free(txt);
+	}
+
+	num_resp = sk_SingleResponse_num(rd->responses);
+	if (num_resp == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: No OCSP SingleResponse within BasicOCSPResponse");
+		result = OCSP_NO_RESPONSE;
+		goto fail;
+	}
+	cmp_sresp = sk_SingleResponse_value(rd->responses, 0);
+	for (j = 0; j < num_resp; j++) {
+		SingleResponse *sresp;
+		CertID *cid1, *cid2;
+
+		sresp = sk_SingleResponse_value(rd->responses, j);
+		wpa_printf(MSG_DEBUG, "OpenSSL: OCSP SingleResponse %u/%u",
+			   j + 1, num_resp);
+
+		txt = algor_str(sresp->certID->hashAlgorithm);
+		if (txt) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: certID hashAlgorithm: %s", txt);
+			os_free(txt);
+		}
+
+		txt = octet_string_str(sresp->certID->issuerNameHash);
+		if (txt) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: certID issuerNameHash: %s", txt);
+			os_free(txt);
+		}
+
+		txt = octet_string_str(sresp->certID->issuerKeyHash);
+		if (txt) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: certID issuerKeyHash: %s", txt);
+			os_free(txt);
+		}
+
+		txt = integer_str(sresp->certID->serialNumber);
+		if (txt) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: certID serialNumber: %s", txt);
+			os_free(txt);
+		}
+
+		switch (sresp->certStatus->type) {
+		case 0:
+			wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: good");
+			break;
+		case 1:
+			wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: revoked");
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: unknown");
+			break;
+		}
+
+		txt = generalizedtime_str(sresp->thisUpdate);
+		if (txt) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: thisUpdate: %s", txt);
+			os_free(txt);
+		}
+
+		if (sresp->nextUpdate) {
+			txt = generalizedtime_str(sresp->nextUpdate);
+			if (txt) {
+				wpa_printf(MSG_DEBUG, "OpenSSL: nextUpdate: %s",
+					   txt);
+				os_free(txt);
+			}
+		}
+
+		txt = extensions_str("singleExtensions",
+				     sresp->singleExtensions);
+		if (txt) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: %s", txt);
+			os_free(txt);
+		}
+
+		cid1 = cmp_sresp->certID;
+		cid2 = sresp->certID;
+		if (j > 0 &&
+		    (OBJ_cmp(cid1->hashAlgorithm->algorithm,
+			     cid2->hashAlgorithm->algorithm) != 0 ||
+		     ASN1_OCTET_STRING_cmp(cid1->issuerNameHash,
+					   cid2->issuerNameHash) != 0 ||
+		     ASN1_OCTET_STRING_cmp(cid1->issuerKeyHash,
+					   cid2->issuerKeyHash) != 0)) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: Different OCSP response issuer information between SingleResponse values within BasicOCSPResponse");
+			goto fail;
+		}
+
+		if (!matching_resp && issuer &&
+		    ASN1_INTEGER_cmp(sresp->certID->serialNumber,
+				     X509_get_serialNumber(cert)) == 0 &&
+		    issuer_match(cert, issuer, sresp->certID) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: This response matches peer certificate");
+			matching_resp = sresp;
+		}
+	}
+
+	txt = extensions_str("responseExtensions", rd->responseExtensions);
+	if (txt) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: %s", txt);
+		os_free(txt);
+	}
+
+	if (!matching_resp) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Could not find OCSP response that matches the peer certificate");
+		result = OCSP_NO_RESPONSE;
+		goto fail;
+	}
+
+	if (!ocsp_resp_valid(matching_resp->thisUpdate,
+			     matching_resp->nextUpdate)) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: OCSP response not valid at this time");
+		goto fail;
+	}
+
+	if (matching_resp->certStatus->type == 1) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: OCSP response indicated that the peer certificate has been revoked");
+		result = OCSP_REVOKED;
+		goto fail;
+	}
+
+	if (matching_resp->certStatus->type != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: OCSP response did not indicate good status");
+		result = OCSP_NO_RESPONSE;
+		goto fail;
+	}
+
+	/* OCSP response indicated the certificate is good. */
+	result = OCSP_GOOD;
+fail:
+	sk_X509_pop_free(chain, X509_free);
+	sk_X509_free(untrusted);
+	sk_X509_pop_free(certs, X509_free);
+	BasicOCSPResponse_free(basic);
+	OCSPResponse_free(resp);
+	X509_STORE_CTX_free(ctx);
+
+	return result;
+}
+
+#endif /* OPENSSL_IS_BORINGSSL */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls_wolfssl.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls_wolfssl.c
new file mode 100644
index 0000000..0b2947d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/crypto/tls_wolfssl.c
@@ -0,0 +1,2314 @@
+/*
+ * SSL/TLS interface functions for wolfSSL TLS case
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "tls.h"
+
+/* wolfSSL includes */
+#include <wolfssl/options.h>
+#include <wolfssl/ssl.h>
+#include <wolfssl/error-ssl.h>
+#include <wolfssl/wolfcrypt/asn.h>
+#include <wolfssl/openssl/x509v3.h>
+
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+#define HAVE_AESGCM
+#include <wolfssl/wolfcrypt/aes.h>
+#endif
+
+#ifdef CONFIG_FIPS
+#include <wolfssl/wolfcrypt/fips_test.h>
+#endif /* CONFIG_FIPS */
+
+#if !defined(CONFIG_FIPS) &&                             \
+    (defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) ||   \
+     defined(EAP_SERVER_FAST))
+#define WOLFSSL_NEED_EAP_FAST_PRF
+#endif
+
+#define SECRET_LEN          48
+#define RAN_LEN             32
+#define SESSION_TICKET_LEN  256
+
+static int tls_ref_count = 0;
+
+static int tls_ex_idx_session = 0;
+
+
+/* tls input data for wolfSSL Read Callback */
+struct tls_in_data {
+	const struct wpabuf *in_data;
+	size_t consumed; /* how many bytes have we used already */
+};
+
+/* tls output data for wolfSSL Write Callback */
+struct tls_out_data {
+	struct wpabuf *out_data;
+};
+
+struct tls_context {
+	void (*event_cb)(void *ctx, enum tls_event ev,
+			 union tls_event_data *data);
+	void *cb_ctx;
+	int cert_in_cb;
+	char *ocsp_stapling_response;
+	unsigned int tls_session_lifetime;
+};
+
+static struct tls_context *tls_global = NULL;
+
+/* wolfssl tls_connection */
+struct tls_connection {
+	struct tls_context *context;
+	WOLFSSL *ssl;
+	int read_alerts;
+	int write_alerts;
+	int failed;
+	struct tls_in_data input;
+	struct tls_out_data output;
+	char *subject_match;
+	char *alt_subject_match;
+	char *suffix_match;
+	char *domain_match;
+
+	u8 srv_cert_hash[32];
+
+	unsigned char client_random[RAN_LEN];
+	unsigned char server_random[RAN_LEN];
+	unsigned int flags;
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+	tls_session_ticket_cb session_ticket_cb;
+	void *session_ticket_cb_ctx;
+	byte session_ticket[SESSION_TICKET_LEN];
+#endif
+	unsigned int ca_cert_verify:1;
+	unsigned int cert_probe:1;
+	unsigned int server_cert_only:1;
+	unsigned int success_data:1;
+
+	WOLFSSL_X509 *peer_cert;
+	WOLFSSL_X509 *peer_issuer;
+	WOLFSSL_X509 *peer_issuer_issuer;
+	char *peer_subject; /* peer subject info for authenticated peer */
+};
+
+
+static struct tls_context * tls_context_new(const struct tls_config *conf)
+{
+	struct tls_context *context = os_zalloc(sizeof(*context));
+
+	if (!context)
+		return NULL;
+
+	if (conf) {
+		context->event_cb = conf->event_cb;
+		context->cb_ctx = conf->cb_ctx;
+		context->cert_in_cb = conf->cert_in_cb;
+	}
+
+	return context;
+}
+
+
+static void wolfssl_reset_in_data(struct tls_in_data *in,
+				  const struct wpabuf *buf)
+{
+	/* old one not owned by us so don't free */
+	in->in_data = buf;
+	in->consumed = 0;
+}
+
+
+static void wolfssl_reset_out_data(struct tls_out_data *out)
+{
+	/* old one not owned by us so don't free */
+	out->out_data = wpabuf_alloc_copy("", 0);
+}
+
+
+/* wolfSSL I/O Receive CallBack */
+static int wolfssl_receive_cb(WOLFSSL *ssl, char *buf, int sz, void *ctx)
+{
+	size_t get = sz;
+	struct tls_in_data *data = ctx;
+
+	if (!data)
+		return -1;
+
+	if (get > (wpabuf_len(data->in_data) - data->consumed))
+		get = wpabuf_len(data->in_data) - data->consumed;
+
+	os_memcpy(buf, wpabuf_head_u8(data->in_data) + data->consumed, get);
+	data->consumed += get;
+
+	if (get == 0)
+		return -2; /* WANT_READ */
+
+	return (int) get;
+}
+
+
+/* wolfSSL I/O Send CallBack */
+static int wolfssl_send_cb(WOLFSSL *ssl, char *buf, int sz, void *ctx)
+{
+	struct wpabuf *tmp;
+	struct tls_out_data *data = ctx;
+
+	if (!data)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "SSL: adding %d bytes", sz);
+
+	tmp = wpabuf_alloc_copy(buf, sz);
+	if (!tmp)
+		return -1;
+	data->out_data = wpabuf_concat(data->out_data, tmp);
+	if (!data->out_data)
+		return -1;
+
+	return sz;
+}
+
+
+static void remove_session_cb(WOLFSSL_CTX *ctx, WOLFSSL_SESSION *sess)
+{
+	struct wpabuf *buf;
+
+	buf = wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+	if (!buf)
+		return;
+	wpa_printf(MSG_DEBUG,
+		   "wolfSSL: Free application session data %p (sess %p)",
+		   buf, sess);
+	wpabuf_free(buf);
+
+	wolfSSL_SESSION_set_ex_data(sess, tls_ex_idx_session, NULL);
+}
+
+
+#if defined(CONFIG_FIPS) && defined(HAVE_FIPS)
+static void wcFipsCb(int ok, int err, const char *hash)
+{
+	wpa_printf(MSG_INFO,
+		   "wolfFIPS: wolfCrypt Fips error callback, ok = %d, err = %d",
+		   ok, err);
+	wpa_printf(MSG_INFO, "wolfFIPS: message = %s", wc_GetErrorString(err));
+	wpa_printf(MSG_INFO, "wolfFIPS: hash = %s", hash);
+	if (err == IN_CORE_FIPS_E) {
+		wpa_printf(MSG_ERROR,
+			   "wolfFIPS: In core integrity hash check failure, copy above hash");
+		wpa_printf(MSG_ERROR, "wolfFIPS: into verifyCore[] in fips_test.c and rebuild");
+	}
+}
+#endif /* CONFIG_FIPS && HAVE_FIPS */
+
+
+#ifdef DEBUG_WOLFSSL
+static void wolfSSL_logging_cb(const int log_level,
+			       const char * const log_message)
+{
+	(void) log_level;
+	wpa_printf(MSG_DEBUG, "wolfSSL log:%s", log_message);
+}
+#endif /* DEBUG_WOLFSSL */
+
+
+void * tls_init(const struct tls_config *conf)
+{
+	WOLFSSL_CTX *ssl_ctx;
+	struct tls_context *context;
+	const char *ciphers;
+
+#ifdef DEBUG_WOLFSSL
+	wolfSSL_SetLoggingCb(wolfSSL_logging_cb);
+	wolfSSL_Debugging_ON();
+#endif /* DEBUG_WOLFSSL */
+
+	context = tls_context_new(conf);
+	if (!context)
+		return NULL;
+
+	if (tls_ref_count == 0) {
+		tls_global = context;
+
+		if (wolfSSL_Init() < 0)
+			return NULL;
+#if defined(CONFIG_FIPS) && defined(HAVE_FIPS)
+		wolfCrypt_SetCb_fips(wcFipsCb);
+#endif /* CONFIG_FIPS && HAVE_FIPS */
+	}
+
+	tls_ref_count++;
+
+	/* start as client */
+	ssl_ctx = wolfSSL_CTX_new(wolfSSLv23_client_method());
+	if (!ssl_ctx) {
+		tls_ref_count--;
+		if (context != tls_global)
+			os_free(context);
+		if (tls_ref_count == 0) {
+			os_free(tls_global);
+			tls_global = NULL;
+		}
+	}
+	wolfSSL_SetIORecv(ssl_ctx, wolfssl_receive_cb);
+	wolfSSL_SetIOSend(ssl_ctx, wolfssl_send_cb);
+	context->tls_session_lifetime = conf->tls_session_lifetime;
+	wolfSSL_CTX_set_ex_data(ssl_ctx, 0, context);
+
+	if (conf->tls_session_lifetime > 0) {
+		wolfSSL_CTX_set_session_id_context(ssl_ctx,
+						   (const unsigned char *)
+						   "hostapd", 7);
+		wolfSSL_CTX_set_quiet_shutdown(ssl_ctx, 1);
+		wolfSSL_CTX_set_session_cache_mode(ssl_ctx,
+						   WOLFSSL_SESS_CACHE_SERVER);
+		wolfSSL_CTX_set_timeout(ssl_ctx, conf->tls_session_lifetime);
+		wolfSSL_CTX_sess_set_remove_cb(ssl_ctx, remove_session_cb);
+	} else {
+		wolfSSL_CTX_set_session_cache_mode(ssl_ctx,
+						   WOLFSSL_SESS_CACHE_OFF);
+	}
+
+	if (conf && conf->openssl_ciphers)
+		ciphers = conf->openssl_ciphers;
+	else
+		ciphers = "ALL";
+	wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s", ciphers);
+	if (wolfSSL_CTX_set_cipher_list(ssl_ctx, ciphers) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "wolfSSL: Failed to set cipher string '%s'",
+			   ciphers);
+		tls_deinit(ssl_ctx);
+		return NULL;
+	}
+
+	return ssl_ctx;
+}
+
+
+void tls_deinit(void *ssl_ctx)
+{
+	struct tls_context *context = wolfSSL_CTX_get_ex_data(ssl_ctx, 0);
+
+	if (context != tls_global)
+		os_free(context);
+
+	wolfSSL_CTX_free((WOLFSSL_CTX *) ssl_ctx);
+
+	tls_ref_count--;
+	if (tls_ref_count == 0) {
+		wolfSSL_Cleanup();
+		os_free(tls_global);
+		tls_global = NULL;
+	}
+}
+
+
+int tls_get_errors(void *tls_ctx)
+{
+#ifdef DEBUG_WOLFSSL
+#if 0
+	unsigned long err;
+
+	err = wolfSSL_ERR_peek_last_error_line(NULL, NULL);
+	if (err != 0) {
+		wpa_printf(MSG_INFO, "TLS - SSL error: %s",
+			   wolfSSL_ERR_error_string(err, NULL));
+		return 1;
+	}
+#endif
+#endif /* DEBUG_WOLFSSL */
+	return 0;
+}
+
+
+struct tls_connection * tls_connection_init(void *tls_ctx)
+{
+	WOLFSSL_CTX *ssl_ctx = tls_ctx;
+	struct tls_connection *conn;
+
+	wpa_printf(MSG_DEBUG, "SSL: connection init");
+
+	conn = os_zalloc(sizeof(*conn));
+	if (!conn)
+		return NULL;
+	conn->ssl = wolfSSL_new(ssl_ctx);
+	if (!conn->ssl) {
+		os_free(conn);
+		return NULL;
+	}
+
+	wolfSSL_SetIOReadCtx(conn->ssl,  &conn->input);
+	wolfSSL_SetIOWriteCtx(conn->ssl, &conn->output);
+	wolfSSL_set_ex_data(conn->ssl, 0, conn);
+	conn->context = wolfSSL_CTX_get_ex_data(ssl_ctx, 0);
+
+	/* Need randoms post-hanshake for EAP-FAST, export key and deriving
+	 * session ID in EAP methods. */
+	wolfSSL_KeepArrays(conn->ssl);
+	wolfSSL_KeepHandshakeResources(conn->ssl);
+	wolfSSL_UseClientSuites(conn->ssl);
+
+	return conn;
+}
+
+
+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
+{
+	if (!conn)
+		return;
+
+	wpa_printf(MSG_DEBUG, "SSL: connection deinit");
+
+	/* parts */
+	wolfSSL_free(conn->ssl);
+	os_free(conn->subject_match);
+	os_free(conn->alt_subject_match);
+	os_free(conn->suffix_match);
+	os_free(conn->domain_match);
+	os_free(conn->peer_subject);
+
+	/* self */
+	os_free(conn);
+}
+
+
+int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
+{
+	return conn ? wolfSSL_is_init_finished(conn->ssl) : 0;
+}
+
+
+char * tls_connection_peer_serial_num(void *tls_ctx,
+				      struct tls_connection *conn)
+{
+	/* TODO */
+	return NULL;
+}
+
+
+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
+{
+	WOLFSSL_SESSION *session;
+
+	if (!conn)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "SSL: connection shutdown");
+
+	/* Set quiet as OpenSSL does */
+	wolfSSL_set_quiet_shutdown(conn->ssl, 1);
+	wolfSSL_shutdown(conn->ssl);
+
+	session = wolfSSL_get1_session(conn->ssl);
+	if (wolfSSL_clear(conn->ssl) != 1) {
+		wolfSSL_SESSION_free(session);
+		return -1;
+	}
+	wolfSSL_set_session(conn->ssl, session);
+	wolfSSL_SESSION_free(session);
+
+	return 0;
+}
+
+
+static int tls_connection_set_subject_match(struct tls_connection *conn,
+					    const char *subject_match,
+					    const char *alt_subject_match,
+					    const char *suffix_match,
+					    const char *domain_match)
+{
+	os_free(conn->subject_match);
+	conn->subject_match = NULL;
+	if (subject_match) {
+		conn->subject_match = os_strdup(subject_match);
+		if (!conn->subject_match)
+			return -1;
+	}
+
+	os_free(conn->alt_subject_match);
+	conn->alt_subject_match = NULL;
+	if (alt_subject_match) {
+		conn->alt_subject_match = os_strdup(alt_subject_match);
+		if (!conn->alt_subject_match)
+			return -1;
+	}
+
+	os_free(conn->suffix_match);
+	conn->suffix_match = NULL;
+	if (suffix_match) {
+		conn->suffix_match = os_strdup(suffix_match);
+		if (!conn->suffix_match)
+			return -1;
+	}
+
+	os_free(conn->domain_match);
+	conn->domain_match = NULL;
+	if (domain_match) {
+		conn->domain_match = os_strdup(domain_match);
+		if (!conn->domain_match)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+static int tls_connection_client_cert(struct tls_connection *conn,
+				      const char *client_cert,
+				      const u8 *client_cert_blob,
+				      size_t blob_len)
+{
+	if (!client_cert && !client_cert_blob)
+		return 0;
+
+	if (client_cert_blob) {
+		if (wolfSSL_use_certificate_chain_buffer_format(
+			    conn->ssl, client_cert_blob, blob_len,
+			    SSL_FILETYPE_ASN1) != SSL_SUCCESS) {
+			wpa_printf(MSG_INFO,
+				   "SSL: use client cert DER blob failed");
+			if (wolfSSL_use_certificate_chain_buffer_format(
+				    conn->ssl, client_cert_blob, blob_len,
+				    SSL_FILETYPE_PEM) != SSL_SUCCESS) {
+				wpa_printf(MSG_INFO,
+					   "SSL: use client cert PEM blob failed");
+				return -1;
+			}
+		}
+		wpa_printf(MSG_DEBUG, "SSL: use client cert blob OK");
+		return 0;
+	}
+
+	if (client_cert) {
+		if (wolfSSL_use_certificate_chain_file(
+			    conn->ssl, client_cert) != SSL_SUCCESS) {
+			wpa_printf(MSG_INFO,
+				   "SSL: use client cert PEM file failed");
+			if (wolfSSL_use_certificate_chain_file_format(
+				    conn->ssl, client_cert,
+				    SSL_FILETYPE_ASN1) != SSL_SUCCESS) {
+				wpa_printf(MSG_INFO,
+					   "SSL: use client cert DER file failed");
+				return -1;
+			}
+		}
+		wpa_printf(MSG_DEBUG, "SSL: use client cert file OK");
+		return 0;
+	}
+
+	return 0;
+}
+
+
+static int tls_passwd_cb(char *buf, int size, int rwflag, void *password)
+{
+	if (!password)
+		return 0;
+	os_strlcpy(buf, (char *) password, size);
+	return os_strlen(buf);
+}
+
+
+static int tls_connection_private_key(void *tls_ctx,
+				      struct tls_connection *conn,
+				      const char *private_key,
+				      const char *private_key_passwd,
+				      const u8 *private_key_blob,
+				      size_t blob_len)
+{
+	WOLFSSL_CTX *ctx = tls_ctx;
+	char *passwd = NULL;
+	int ok = 0;
+
+	if (!private_key && !private_key_blob)
+		return 0;
+
+	if (private_key_passwd) {
+		passwd = os_strdup(private_key_passwd);
+		if (!passwd)
+			return -1;
+	}
+
+	wolfSSL_CTX_set_default_passwd_cb(ctx, tls_passwd_cb);
+	wolfSSL_CTX_set_default_passwd_cb_userdata(ctx, passwd);
+
+	if (private_key_blob) {
+		if (wolfSSL_use_PrivateKey_buffer(conn->ssl,
+						  private_key_blob, blob_len,
+						  SSL_FILETYPE_ASN1) !=
+		    SSL_SUCCESS) {
+			wpa_printf(MSG_INFO,
+				   "SSL: use private DER blob failed");
+			if (wolfSSL_use_PrivateKey_buffer(
+				    conn->ssl,
+				    private_key_blob, blob_len,
+				    SSL_FILETYPE_PEM) != SSL_SUCCESS) {
+				wpa_printf(MSG_INFO,
+					   "SSL: use private PEM blob failed");
+			} else {
+				ok = 1;
+			}
+		} else {
+			ok = 1;
+		}
+		if (ok)
+			wpa_printf(MSG_DEBUG, "SSL: use private key blob OK");
+	}
+
+	if (!ok && private_key) {
+		if (wolfSSL_use_PrivateKey_file(conn->ssl, private_key,
+						SSL_FILETYPE_PEM) !=
+		    SSL_SUCCESS) {
+			wpa_printf(MSG_INFO,
+				   "SSL: use private key PEM file failed");
+			if (wolfSSL_use_PrivateKey_file(conn->ssl, private_key,
+							SSL_FILETYPE_ASN1) !=
+			    SSL_SUCCESS) {
+				wpa_printf(MSG_INFO,
+					   "SSL: use private key DER file failed");
+			} else {
+				ok = 1;
+			}
+		} else {
+			ok = 1;
+		}
+
+		if (ok)
+			wpa_printf(MSG_DEBUG, "SSL: use private key file OK");
+	}
+
+	wolfSSL_CTX_set_default_passwd_cb(ctx, NULL);
+	os_free(passwd);
+
+	if (!ok)
+		return -1;
+
+	return 0;
+}
+
+
+static int tls_match_alt_subject_component(WOLFSSL_X509 *cert, int type,
+					   const char *value, size_t len)
+{
+	WOLFSSL_GENERAL_NAME *gen;
+	void *ext;
+	int found = 0;
+	int i;
+
+	ext = wolfSSL_X509_get_ext_d2i(cert, ALT_NAMES_OID, NULL, NULL);
+
+	for (i = 0; ext && i < wolfSSL_sk_num(ext); i++) {
+		gen = wolfSSL_sk_value(ext, i);
+		if (!gen || gen->type != type)
+			continue;
+		if ((size_t) wolfSSL_ASN1_STRING_length(gen->d.ia5) == len &&
+		    os_memcmp(value, wolfSSL_ASN1_STRING_data(gen->d.ia5),
+			      len) == 0)
+			found++;
+	}
+
+	wolfSSL_sk_GENERAL_NAME_free(ext);
+
+	return found;
+}
+
+
+static int tls_match_alt_subject(WOLFSSL_X509 *cert, const char *match)
+{
+	int type;
+	const char *pos, *end;
+	size_t len;
+
+	pos = match;
+	do {
+		if (os_strncmp(pos, "EMAIL:", 6) == 0) {
+			type = GEN_EMAIL;
+			pos += 6;
+		} else if (os_strncmp(pos, "DNS:", 4) == 0) {
+			type = GEN_DNS;
+			pos += 4;
+		} else if (os_strncmp(pos, "URI:", 4) == 0) {
+			type = GEN_URI;
+			pos += 4;
+		} else {
+			wpa_printf(MSG_INFO,
+				   "TLS: Invalid altSubjectName match '%s'",
+				   pos);
+			return 0;
+		}
+		end = os_strchr(pos, ';');
+		while (end) {
+			if (os_strncmp(end + 1, "EMAIL:", 6) == 0 ||
+			    os_strncmp(end + 1, "DNS:", 4) == 0 ||
+			    os_strncmp(end + 1, "URI:", 4) == 0)
+				break;
+			end = os_strchr(end + 1, ';');
+		}
+		if (end)
+			len = end - pos;
+		else
+			len = os_strlen(pos);
+		if (tls_match_alt_subject_component(cert, type, pos, len) > 0)
+			return 1;
+		pos = end + 1;
+	} while (end);
+
+	return 0;
+}
+
+
+static int domain_suffix_match(const char *val, size_t len, const char *match,
+			       size_t match_len, int full)
+{
+	size_t i;
+
+	/* Check for embedded nuls that could mess up suffix matching */
+	for (i = 0; i < len; i++) {
+		if (val[i] == '\0') {
+			wpa_printf(MSG_DEBUG,
+				   "TLS: Embedded null in a string - reject");
+			return 0;
+		}
+	}
+
+	if (match_len > len || (full && match_len != len))
+		return 0;
+
+	if (os_strncasecmp(val + len - match_len, match, match_len) != 0)
+		return 0; /* no match */
+
+	if (match_len == len)
+		return 1; /* exact match */
+
+	if (val[len - match_len - 1] == '.')
+		return 1; /* full label match completes suffix match */
+
+	wpa_printf(MSG_DEBUG, "TLS: Reject due to incomplete label match");
+	return 0;
+}
+
+
+static int tls_match_suffix_helper(WOLFSSL_X509 *cert, const char *match,
+				   size_t match_len, int full)
+{
+	WOLFSSL_GENERAL_NAME *gen;
+	void *ext;
+	int i;
+	int j;
+	int dns_name = 0;
+	WOLFSSL_X509_NAME *name;
+
+	wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s",
+		   full ? "" : "suffix ", match);
+
+	ext = wolfSSL_X509_get_ext_d2i(cert, ALT_NAMES_OID, NULL, NULL);
+
+	for (j = 0; ext && j < wolfSSL_sk_num(ext); j++) {
+		gen = wolfSSL_sk_value(ext, j);
+		if (!gen || gen->type != ASN_DNS_TYPE)
+			continue;
+		dns_name++;
+		wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName",
+				  wolfSSL_ASN1_STRING_data(gen->d.ia5),
+				  wolfSSL_ASN1_STRING_length(gen->d.ia5));
+		if (domain_suffix_match(
+			    (const char *) wolfSSL_ASN1_STRING_data(gen->d.ia5),
+			    wolfSSL_ASN1_STRING_length(gen->d.ia5), match,
+			    match_len, full) == 1) {
+			wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found",
+				   full ? "Match" : "Suffix match");
+			wolfSSL_sk_ASN1_OBJECT_free(ext);
+			return 1;
+		}
+	}
+	wolfSSL_sk_GENERAL_NAME_free(ext);
+
+	if (dns_name) {
+		wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched");
+		return 0;
+	}
+
+	name = wolfSSL_X509_get_subject_name(cert);
+	i = -1;
+	for (;;) {
+		WOLFSSL_X509_NAME_ENTRY *e;
+		WOLFSSL_ASN1_STRING *cn;
+
+		i = wolfSSL_X509_NAME_get_index_by_NID(name, NID_commonName, i);
+		if (i == -1)
+			break;
+		e = wolfSSL_X509_NAME_get_entry(name, i);
+		if (!e)
+			continue;
+		cn = wolfSSL_X509_NAME_ENTRY_get_data(e);
+		if (!cn)
+			continue;
+		wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName",
+				  cn->data, cn->length);
+		if (domain_suffix_match(cn->data, cn->length,
+					match, match_len, full) == 1) {
+			wpa_printf(MSG_DEBUG, "TLS: %s in commonName found",
+				   full ? "Match" : "Suffix match");
+			return 1;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found",
+		   full ? "" : "suffix ");
+	return 0;
+}
+
+
+static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full)
+{
+	const char *token, *last = NULL;
+
+	/* Process each match alternative separately until a match is found */
+	while ((token = cstr_token(match, ";", &last))) {
+		if (tls_match_suffix_helper(cert, token, last - token, full))
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static enum tls_fail_reason wolfssl_tls_fail_reason(int err)
+{
+	switch (err) {
+	case X509_V_ERR_CERT_REVOKED:
+		return TLS_FAIL_REVOKED;
+	case ASN_BEFORE_DATE_E:
+	case X509_V_ERR_CERT_NOT_YET_VALID:
+	case X509_V_ERR_CRL_NOT_YET_VALID:
+		return TLS_FAIL_NOT_YET_VALID;
+	case ASN_AFTER_DATE_E:
+	case X509_V_ERR_CERT_HAS_EXPIRED:
+	case X509_V_ERR_CRL_HAS_EXPIRED:
+		return TLS_FAIL_EXPIRED;
+	case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+	case X509_V_ERR_UNABLE_TO_GET_CRL:
+	case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:
+	case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+	case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+	case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+	case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+	case X509_V_ERR_CERT_CHAIN_TOO_LONG:
+	case X509_V_ERR_PATH_LENGTH_EXCEEDED:
+	case X509_V_ERR_INVALID_CA:
+		return TLS_FAIL_UNTRUSTED;
+	case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+	case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+	case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+	case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+	case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+	case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+	case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+	case X509_V_ERR_CERT_UNTRUSTED:
+	case X509_V_ERR_CERT_REJECTED:
+		return TLS_FAIL_BAD_CERTIFICATE;
+	default:
+		return TLS_FAIL_UNSPECIFIED;
+	}
+}
+
+
+static const char * wolfssl_tls_err_string(int err, const char *err_str)
+{
+	switch (err) {
+	case ASN_BEFORE_DATE_E:
+		return "certificate is not yet valid";
+	case ASN_AFTER_DATE_E:
+		return "certificate has expired";
+	default:
+		return err_str;
+	}
+}
+
+
+static struct wpabuf * get_x509_cert(WOLFSSL_X509 *cert)
+{
+	struct wpabuf *buf = NULL;
+	const u8 *data;
+	int cert_len;
+
+	data = wolfSSL_X509_get_der(cert, &cert_len);
+	if (!data)
+		buf = wpabuf_alloc_copy(data, cert_len);
+
+	return buf;
+}
+
+
+static void wolfssl_tls_fail_event(struct tls_connection *conn,
+				   WOLFSSL_X509 *err_cert, int err, int depth,
+				   const char *subject, const char *err_str,
+				   enum tls_fail_reason reason)
+{
+	union tls_event_data ev;
+	struct wpabuf *cert = NULL;
+	struct tls_context *context = conn->context;
+
+	if (!context->event_cb)
+		return;
+
+	cert = get_x509_cert(err_cert);
+	os_memset(&ev, 0, sizeof(ev));
+	ev.cert_fail.reason = reason != TLS_FAIL_UNSPECIFIED ?
+		reason : wolfssl_tls_fail_reason(err);
+	ev.cert_fail.depth = depth;
+	ev.cert_fail.subject = subject;
+	ev.cert_fail.reason_txt = wolfssl_tls_err_string(err, err_str);
+	ev.cert_fail.cert = cert;
+	context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
+	wpabuf_free(cert);
+}
+
+
+static void wolfssl_tls_cert_event(struct tls_connection *conn,
+				   WOLFSSL_X509 *err_cert, int depth,
+				   const char *subject)
+{
+	struct wpabuf *cert = NULL;
+	union tls_event_data ev;
+	struct tls_context *context = conn->context;
+	char *alt_subject[TLS_MAX_ALT_SUBJECT];
+	int alt, num_alt_subject = 0;
+	WOLFSSL_GENERAL_NAME *gen;
+	void *ext;
+	int i;
+#ifdef CONFIG_SHA256
+	u8 hash[32];
+#endif /* CONFIG_SHA256 */
+
+	if (!context->event_cb)
+		return;
+
+	os_memset(&ev, 0, sizeof(ev));
+	if (conn->cert_probe || (conn->flags & TLS_CONN_EXT_CERT_CHECK) ||
+	    context->cert_in_cb) {
+		cert = get_x509_cert(err_cert);
+		ev.peer_cert.cert = cert;
+	}
+
+#ifdef CONFIG_SHA256
+	if (cert) {
+		const u8 *addr[1];
+		size_t len[1];
+
+		addr[0] = wpabuf_head(cert);
+		len[0] = wpabuf_len(cert);
+		if (sha256_vector(1, addr, len, hash) == 0) {
+			ev.peer_cert.hash = hash;
+			ev.peer_cert.hash_len = sizeof(hash);
+		}
+	}
+#endif /* CONFIG_SHA256 */
+
+	ev.peer_cert.depth = depth;
+	ev.peer_cert.subject = subject;
+
+	ext = wolfSSL_X509_get_ext_d2i(err_cert, ALT_NAMES_OID, NULL, NULL);
+	for (i = 0; ext && i < wolfSSL_sk_num(ext); i++) {
+		char *pos;
+
+		if (num_alt_subject == TLS_MAX_ALT_SUBJECT)
+			break;
+		gen = wolfSSL_sk_value((void *) ext, i);
+		if (!gen ||
+		    (gen->type != GEN_EMAIL &&
+		     gen->type != GEN_DNS &&
+		     gen->type != GEN_URI))
+			continue;
+
+		pos = os_malloc(10 + wolfSSL_ASN1_STRING_length(gen->d.ia5) +
+				1);
+		if (!pos)
+			break;
+		alt_subject[num_alt_subject++] = pos;
+
+		switch (gen->type) {
+		case GEN_EMAIL:
+			os_memcpy(pos, "EMAIL:", 6);
+			pos += 6;
+			break;
+		case GEN_DNS:
+			os_memcpy(pos, "DNS:", 4);
+			pos += 4;
+			break;
+		case GEN_URI:
+			os_memcpy(pos, "URI:", 4);
+			pos += 4;
+			break;
+		}
+
+		os_memcpy(pos, wolfSSL_ASN1_STRING_data(gen->d.ia5),
+			  wolfSSL_ASN1_STRING_length(gen->d.ia5));
+		pos += wolfSSL_ASN1_STRING_length(gen->d.ia5);
+		*pos = '\0';
+	}
+	wolfSSL_sk_GENERAL_NAME_free(ext);
+
+	for (alt = 0; alt < num_alt_subject; alt++)
+		ev.peer_cert.altsubject[alt] = alt_subject[alt];
+	ev.peer_cert.num_altsubject = num_alt_subject;
+
+	context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
+	wpabuf_free(cert);
+	for (alt = 0; alt < num_alt_subject; alt++)
+		os_free(alt_subject[alt]);
+}
+
+
+static int tls_verify_cb(int preverify_ok, WOLFSSL_X509_STORE_CTX *x509_ctx)
+{
+	char buf[256];
+	WOLFSSL_X509 *err_cert;
+	int err, depth;
+	WOLFSSL *ssl;
+	struct tls_connection *conn;
+	struct tls_context *context;
+	char *match, *altmatch, *suffix_match, *domain_match;
+	const char *err_str;
+
+	err_cert = wolfSSL_X509_STORE_CTX_get_current_cert(x509_ctx);
+	if (!err_cert) {
+		wpa_printf(MSG_DEBUG, "wolfSSL: No Cert");
+		return 0;
+	}
+
+	err = wolfSSL_X509_STORE_CTX_get_error(x509_ctx);
+	depth = wolfSSL_X509_STORE_CTX_get_error_depth(x509_ctx);
+	ssl = wolfSSL_X509_STORE_CTX_get_ex_data(
+		x509_ctx, wolfSSL_get_ex_data_X509_STORE_CTX_idx());
+	wolfSSL_X509_NAME_oneline(wolfSSL_X509_get_subject_name(err_cert), buf,
+				  sizeof(buf));
+
+	conn = wolfSSL_get_ex_data(ssl, 0);
+	if (!conn) {
+		wpa_printf(MSG_DEBUG, "wolfSSL: No ex_data");
+		return 0;
+	}
+
+	if (depth == 0)
+		conn->peer_cert = err_cert;
+	else if (depth == 1)
+		conn->peer_issuer = err_cert;
+	else if (depth == 2)
+		conn->peer_issuer_issuer = err_cert;
+
+	context = conn->context;
+	match = conn->subject_match;
+	altmatch = conn->alt_subject_match;
+	suffix_match = conn->suffix_match;
+	domain_match = conn->domain_match;
+
+	if (!preverify_ok && !conn->ca_cert_verify)
+		preverify_ok = 1;
+	if (!preverify_ok && depth > 0 && conn->server_cert_only)
+		preverify_ok = 1;
+	if (!preverify_ok && (conn->flags & TLS_CONN_DISABLE_TIME_CHECKS) &&
+	    (err == X509_V_ERR_CERT_HAS_EXPIRED ||
+	     err == ASN_AFTER_DATE_E || err == ASN_BEFORE_DATE_E ||
+	     err == X509_V_ERR_CERT_NOT_YET_VALID)) {
+		wpa_printf(MSG_DEBUG,
+			   "wolfSSL: Ignore certificate validity time mismatch");
+		preverify_ok = 1;
+	}
+
+	err_str = wolfSSL_X509_verify_cert_error_string(err);
+
+#ifdef CONFIG_SHA256
+	/*
+	 * Do not require preverify_ok so we can explicity allow otherwise
+	 * invalid pinned server certificates.
+	 */
+	if (depth == 0 && conn->server_cert_only) {
+		struct wpabuf *cert;
+
+		cert = get_x509_cert(err_cert);
+		if (!cert) {
+			wpa_printf(MSG_DEBUG,
+				   "wolfSSL: Could not fetch server certificate data");
+			preverify_ok = 0;
+		} else {
+			u8 hash[32];
+			const u8 *addr[1];
+			size_t len[1];
+
+			addr[0] = wpabuf_head(cert);
+			len[0] = wpabuf_len(cert);
+			if (sha256_vector(1, addr, len, hash) < 0 ||
+			    os_memcmp(conn->srv_cert_hash, hash, 32) != 0) {
+				err_str = "Server certificate mismatch";
+				err = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
+				preverify_ok = 0;
+			} else if (!preverify_ok) {
+				/*
+				 * Certificate matches pinned certificate, allow
+				 * regardless of other problems.
+				 */
+				wpa_printf(MSG_DEBUG,
+					   "wolfSSL: Ignore validation issues for a pinned server certificate");
+				preverify_ok = 1;
+			}
+			wpabuf_free(cert);
+		}
+	}
+#endif /* CONFIG_SHA256 */
+
+	if (!preverify_ok) {
+		wpa_printf(MSG_WARNING,
+			   "TLS: Certificate verification failed, error %d (%s) depth %d for '%s'",
+			   err, err_str, depth, buf);
+		wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       err_str, TLS_FAIL_UNSPECIFIED);
+		return preverify_ok;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "TLS: %s - preverify_ok=%d err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'",
+		   __func__, preverify_ok, err, err_str,
+		   conn->ca_cert_verify, depth, buf);
+	if (depth == 0 && match && os_strstr(buf, match) == NULL) {
+		wpa_printf(MSG_WARNING,
+			   "TLS: Subject '%s' did not match with '%s'",
+			   buf, match);
+		preverify_ok = 0;
+		wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       "Subject mismatch",
+				       TLS_FAIL_SUBJECT_MISMATCH);
+	} else if (depth == 0 && altmatch &&
+		   !tls_match_alt_subject(err_cert, altmatch)) {
+		wpa_printf(MSG_WARNING,
+			   "TLS: altSubjectName match '%s' not found",
+			   altmatch);
+		preverify_ok = 0;
+		wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       "AltSubject mismatch",
+				       TLS_FAIL_ALTSUBJECT_MISMATCH);
+	} else if (depth == 0 && suffix_match &&
+		   !tls_match_suffix(err_cert, suffix_match, 0)) {
+		wpa_printf(MSG_WARNING,
+			   "TLS: Domain suffix match '%s' not found",
+			   suffix_match);
+		preverify_ok = 0;
+		wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       "Domain suffix mismatch",
+				       TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
+	} else if (depth == 0 && domain_match &&
+		   !tls_match_suffix(err_cert, domain_match, 1)) {
+		wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found",
+			   domain_match);
+		preverify_ok = 0;
+		wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       "Domain mismatch",
+				       TLS_FAIL_DOMAIN_MISMATCH);
+	} else {
+		wolfssl_tls_cert_event(conn, err_cert, depth, buf);
+	}
+
+	if (conn->cert_probe && preverify_ok && depth == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "wolfSSL: Reject server certificate on probe-only run");
+		preverify_ok = 0;
+		wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       "Server certificate chain probe",
+				       TLS_FAIL_SERVER_CHAIN_PROBE);
+	}
+
+#ifdef HAVE_OCSP_WOLFSSL
+	if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) &&
+	    preverify_ok) {
+		enum ocsp_result res;
+
+		res = check_ocsp_resp(conn->ssl_ctx, conn->ssl, err_cert,
+				      conn->peer_issuer,
+				      conn->peer_issuer_issuer);
+		if (res == OCSP_REVOKED) {
+			preverify_ok = 0;
+			wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+					       "certificate revoked",
+					       TLS_FAIL_REVOKED);
+			if (err == X509_V_OK)
+				X509_STORE_CTX_set_error(
+					x509_ctx, X509_V_ERR_CERT_REVOKED);
+		} else if (res != OCSP_GOOD &&
+			   (conn->flags & TLS_CONN_REQUIRE_OCSP)) {
+			preverify_ok = 0;
+			wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+					       "bad certificate status response",
+					       TLS_FAIL_UNSPECIFIED);
+		}
+	}
+#endif /* HAVE_OCSP_WOLFSSL */
+	if (depth == 0 && preverify_ok && context->event_cb != NULL)
+		context->event_cb(context->cb_ctx,
+				  TLS_CERT_CHAIN_SUCCESS, NULL);
+
+	if (depth == 0 && preverify_ok) {
+		os_free(conn->peer_subject);
+		conn->peer_subject = os_strdup(buf);
+	}
+
+	return preverify_ok;
+}
+
+
+static int tls_connection_ca_cert(void *tls_ctx, struct tls_connection *conn,
+				  const char *ca_cert,
+				  const u8 *ca_cert_blob, size_t blob_len,
+				  const char *ca_path)
+{
+	WOLFSSL_CTX *ctx = tls_ctx;
+
+	wolfSSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
+	conn->ca_cert_verify = 1;
+
+	if (ca_cert && os_strncmp(ca_cert, "probe://", 8) == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "wolfSSL: Probe for server certificate chain");
+		conn->cert_probe = 1;
+		conn->ca_cert_verify = 0;
+		return 0;
+	}
+
+	if (ca_cert && os_strncmp(ca_cert, "hash://", 7) == 0) {
+#ifdef CONFIG_SHA256
+		const char *pos = ca_cert + 7;
+
+		if (os_strncmp(pos, "server/sha256/", 14) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "wolfSSL: Unsupported ca_cert hash value '%s'",
+				   ca_cert);
+			return -1;
+		}
+		pos += 14;
+		if (os_strlen(pos) != 32 * 2) {
+			wpa_printf(MSG_DEBUG,
+				   "wolfSSL: Unexpected SHA256 hash length in ca_cert '%s'",
+				   ca_cert);
+			return -1;
+		}
+		if (hexstr2bin(pos, conn->srv_cert_hash, 32) < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "wolfSSL: Invalid SHA256 hash value in ca_cert '%s'",
+				   ca_cert);
+			return -1;
+		}
+		conn->server_cert_only = 1;
+		wpa_printf(MSG_DEBUG,
+			   "wolfSSL: Checking only server certificate match");
+		return 0;
+#else /* CONFIG_SHA256 */
+		wpa_printf(MSG_INFO,
+			   "No SHA256 included in the build - cannot validate server certificate hash");
+		return -1;
+#endif /* CONFIG_SHA256 */
+	}
+
+	if (ca_cert_blob) {
+		if (wolfSSL_CTX_load_verify_buffer(ctx, ca_cert_blob, blob_len,
+						   SSL_FILETYPE_ASN1) !=
+		    SSL_SUCCESS) {
+			wpa_printf(MSG_INFO, "SSL: failed to load DER CA blob");
+			if (wolfSSL_CTX_load_verify_buffer(
+				    ctx, ca_cert_blob, blob_len,
+				    SSL_FILETYPE_PEM) != SSL_SUCCESS) {
+				wpa_printf(MSG_INFO,
+					   "SSL: failed to load PEM CA blob");
+				return -1;
+			}
+		}
+		wpa_printf(MSG_DEBUG, "SSL: use CA cert blob OK");
+		return 0;
+	}
+
+	if (ca_cert || ca_path) {
+		WOLFSSL_X509_STORE *cm = wolfSSL_X509_STORE_new();
+
+		if (!cm) {
+			wpa_printf(MSG_INFO,
+				   "SSL: failed to create certificate store");
+			return -1;
+		}
+		wolfSSL_CTX_set_cert_store(ctx, cm);
+
+		if (wolfSSL_CTX_load_verify_locations(ctx, ca_cert, ca_path) !=
+		    SSL_SUCCESS) {
+			wpa_printf(MSG_INFO,
+				   "SSL: failed to load ca_cert as PEM");
+
+			if (!ca_cert)
+				return -1;
+
+			if (wolfSSL_CTX_der_load_verify_locations(
+				    ctx, ca_cert, SSL_FILETYPE_ASN1) !=
+			    SSL_SUCCESS) {
+				wpa_printf(MSG_INFO,
+					   "SSL: failed to load ca_cert as DER");
+				return -1;
+			}
+		}
+		return 0;
+	}
+
+	conn->ca_cert_verify = 0;
+	return 0;
+}
+
+
+static void tls_set_conn_flags(WOLFSSL *ssl, unsigned int flags)
+{
+#ifdef HAVE_SESSION_TICKET
+	if (!(flags & TLS_CONN_DISABLE_SESSION_TICKET))
+		wolfSSL_UseSessionTicket(ssl);
+#endif /* HAVE_SESSION_TICKET */
+
+	if (flags & TLS_CONN_DISABLE_TLSv1_0)
+		wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1);
+	if (flags & TLS_CONN_DISABLE_TLSv1_1)
+		wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
+	if (flags & TLS_CONN_DISABLE_TLSv1_2)
+		wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_2);
+	if (flags & TLS_CONN_DISABLE_TLSv1_3)
+		wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_3);
+}
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+			      const struct tls_connection_params *params)
+{
+	wpa_printf(MSG_DEBUG, "SSL: set params");
+
+	if (tls_connection_set_subject_match(conn, params->subject_match,
+					     params->altsubject_match,
+					     params->suffix_match,
+					     params->domain_match) < 0) {
+		wpa_printf(MSG_INFO, "Error setting subject match");
+		return -1;
+	}
+
+	if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert,
+				   params->ca_cert_blob,
+				   params->ca_cert_blob_len,
+				   params->ca_path) < 0) {
+		wpa_printf(MSG_INFO, "Error setting CA cert");
+		return -1;
+	}
+
+	if (tls_connection_client_cert(conn, params->client_cert,
+				       params->client_cert_blob,
+				       params->client_cert_blob_len) < 0) {
+		wpa_printf(MSG_INFO, "Error setting client cert");
+		return -1;
+	}
+
+	if (tls_connection_private_key(tls_ctx, conn, params->private_key,
+				       params->private_key_passwd,
+				       params->private_key_blob,
+				       params->private_key_blob_len) < 0) {
+		wpa_printf(MSG_INFO, "Error setting private key");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s",
+		   params->openssl_ciphers ? params->openssl_ciphers : "N/A");
+	if (params->openssl_ciphers &&
+	    wolfSSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) {
+		wpa_printf(MSG_INFO,
+			   "wolfSSL: Failed to set cipher string '%s'",
+			   params->openssl_ciphers);
+		return -1;
+	}
+
+	tls_set_conn_flags(conn->ssl, params->flags);
+
+#ifdef HAVE_CERTIFICATE_STATUS_REQUEST
+	if (params->flags & TLS_CONN_REQUEST_OCSP) {
+		if (wolfSSL_UseOCSPStapling(conn->ssl, WOLFSSL_CSR_OCSP,
+					    WOLFSSL_CSR_OCSP_USE_NONCE) !=
+		    SSL_SUCCESS)
+			return -1;
+		if (wolfSSL_EnableOCSPStapling(conn->ssl) != SSL_SUCCESS)
+			return -1;
+	}
+#endif /* HAVE_CERTIFICATE_STATUS_REQUEST */
+#ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2
+	if (params->flags & TLS_CONN_REQUEST_OCSP) {
+		if (wolfSSL_UseOCSPStaplingV2(conn->ssl,
+					      WOLFSSL_CSR2_OCSP_MULTI, 0) !=
+		    SSL_SUCCESS)
+			return -1;
+		if (wolfSSL_EnableOCSPStapling(conn->ssl) != SSL_SUCCESS)
+			return -1;
+	}
+#endif /* HAVE_CERTIFICATE_STATUS_REQUEST_V2 */
+#if !defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \
+    !defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
+#ifdef HAVE_OCSP
+	if (params->flags & TLS_CONN_REQUEST_OCSP)
+		wolfSSL_CTX_EnableOCSP(ctx, 0);
+#else /* HAVE_OCSP */
+	if (params->flags & TLS_CONN_REQUIRE_OCSP) {
+		wpa_printf(MSG_INFO,
+			   "wolfSSL: No OCSP support included - reject configuration");
+		return -1;
+	}
+	if (params->flags & TLS_CONN_REQUEST_OCSP) {
+		wpa_printf(MSG_DEBUG,
+			   "wolfSSL: No OCSP support included - allow optional OCSP case to continue");
+	}
+#endif /* HAVE_OCSP */
+#endif /* !HAVE_CERTIFICATE_STATUS_REQUEST &&
+	* !HAVE_CERTIFICATE_STATUS_REQUEST_V2 */
+
+	conn->flags = params->flags;
+
+	return 0;
+}
+
+
+static int tls_global_ca_cert(void *ssl_ctx, const char *ca_cert)
+{
+	WOLFSSL_CTX *ctx = ssl_ctx;
+
+	if (ca_cert) {
+		if (wolfSSL_CTX_load_verify_locations(ctx, ca_cert, NULL) != 1)
+		{
+			wpa_printf(MSG_WARNING,
+				   "Failed to load root certificates");
+			return -1;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "TLS: Trusted root certificate(s) loaded");
+	}
+
+	return 0;
+}
+
+
+static int tls_global_client_cert(void *ssl_ctx, const char *client_cert)
+{
+	WOLFSSL_CTX *ctx = ssl_ctx;
+
+	if (!client_cert)
+		return 0;
+
+	if (wolfSSL_CTX_use_certificate_chain_file_format(ctx, client_cert,
+							  SSL_FILETYPE_ASN1) !=
+	    SSL_SUCCESS &&
+	    wolfSSL_CTX_use_certificate_chain_file(ctx, client_cert) !=
+	    SSL_SUCCESS) {
+		wpa_printf(MSG_INFO, "Failed to load client certificate");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "SSL: Loaded global client certificate: %s",
+		   client_cert);
+
+	return 0;
+}
+
+
+static int tls_global_private_key(void *ssl_ctx, const char *private_key,
+				  const char *private_key_passwd)
+{
+	WOLFSSL_CTX *ctx = ssl_ctx;
+	char *passwd = NULL;
+	int ret = 0;
+
+	if (!private_key)
+		return 0;
+
+	if (private_key_passwd) {
+		passwd = os_strdup(private_key_passwd);
+		if (!passwd)
+			return -1;
+	}
+
+	wolfSSL_CTX_set_default_passwd_cb(ctx, tls_passwd_cb);
+	wolfSSL_CTX_set_default_passwd_cb_userdata(ctx, passwd);
+
+	if (wolfSSL_CTX_use_PrivateKey_file(ctx, private_key,
+					    SSL_FILETYPE_ASN1) != 1 &&
+	    wolfSSL_CTX_use_PrivateKey_file(ctx, private_key,
+					    SSL_FILETYPE_PEM) != 1) {
+		wpa_printf(MSG_INFO, "Failed to load private key");
+		ret = -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "SSL: Loaded global private key");
+
+	os_free(passwd);
+	wolfSSL_CTX_set_default_passwd_cb(ctx, NULL);
+
+	return ret;
+}
+
+
+static int tls_global_dh(void *ssl_ctx, const char *dh_file)
+{
+	WOLFSSL_CTX *ctx = ssl_ctx;
+
+	if (dh_file) {
+		if (wolfSSL_CTX_SetTmpDH_file(ctx, dh_file, SSL_FILETYPE_PEM) <
+		    0) {
+			wpa_printf(MSG_INFO,
+				   "SSL: global use DH PEM file failed");
+			if (wolfSSL_CTX_SetTmpDH_file(ctx, dh_file,
+						      SSL_FILETYPE_ASN1) < 0) {
+				wpa_printf(MSG_INFO,
+					   "SSL: global use DH DER file failed");
+				return -1;
+			}
+		}
+		wpa_printf(MSG_DEBUG, "SSL: global use DH file OK");
+		return 0;
+	}
+
+	return 0;
+}
+
+
+#ifdef HAVE_OCSP
+
+int ocsp_status_cb(void *unused, const char *url, int url_sz,
+		   unsigned char *request, int request_sz,
+		   unsigned char **response)
+{
+	size_t len;
+
+	(void) unused;
+
+	if (!url) {
+		wpa_printf(MSG_DEBUG,
+			   "wolfSSL: OCSP status callback - no response configured");
+		*response = NULL;
+		return 0;
+	}
+
+	*response = (unsigned char *) os_readfile(url, &len);
+	if (!*response) {
+		wpa_printf(MSG_DEBUG,
+			   "wolfSSL: OCSP status callback - could not read response file");
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG,
+		   "wolfSSL: OCSP status callback - send cached response");
+	return len;
+}
+
+
+void ocsp_resp_free_cb(void *ocsp_stapling_response, unsigned char *response)
+{
+	os_free(response);
+}
+
+#endif /* HAVE_OCSP */
+
+
+int tls_global_set_params(void *tls_ctx,
+			  const struct tls_connection_params *params)
+{
+	wpa_printf(MSG_DEBUG, "SSL: global set params");
+
+	if (params->check_cert_subject)
+		return -1; /* not yet supported */
+
+	if (tls_global_ca_cert(tls_ctx, params->ca_cert) < 0) {
+		wpa_printf(MSG_INFO, "SSL: Failed to load ca cert file '%s'",
+			   params->ca_cert);
+		return -1;
+	}
+
+	if (tls_global_client_cert(tls_ctx, params->client_cert) < 0) {
+		wpa_printf(MSG_INFO,
+			   "SSL: Failed to load client cert file '%s'",
+			   params->client_cert);
+		return -1;
+	}
+
+	if (tls_global_private_key(tls_ctx, params->private_key,
+				   params->private_key_passwd) < 0) {
+		wpa_printf(MSG_INFO,
+			   "SSL: Failed to load private key file '%s'",
+			   params->private_key);
+		return -1;
+	}
+
+	if (tls_global_dh(tls_ctx, params->dh_file) < 0) {
+		wpa_printf(MSG_INFO, "SSL: Failed to load DH file '%s'",
+			   params->dh_file);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s",
+		   params->openssl_ciphers ? params->openssl_ciphers : "N/A");
+	if (params->openssl_ciphers &&
+	    wolfSSL_CTX_set_cipher_list(tls_ctx,
+					params->openssl_ciphers) != 1) {
+		wpa_printf(MSG_INFO,
+			   "wolfSSL: Failed to set cipher string '%s'",
+			   params->openssl_ciphers);
+		return -1;
+	}
+
+	if (params->openssl_ecdh_curves) {
+		wpa_printf(MSG_INFO,
+			   "wolfSSL: openssl_ecdh_curves not supported");
+		return -1;
+	}
+
+#ifdef HAVE_SESSION_TICKET
+	/* Session ticket is off by default - can't disable once on. */
+	if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET))
+		wolfSSL_CTX_UseSessionTicket(tls_ctx);
+#endif /* HAVE_SESSION_TICKET */
+
+#ifdef HAVE_OCSP
+	if (params->ocsp_stapling_response) {
+		wolfSSL_CTX_SetOCSP_OverrideURL(tls_ctx,
+						params->ocsp_stapling_response);
+		wolfSSL_CTX_SetOCSP_Cb(tls_ctx, ocsp_status_cb,
+				       ocsp_resp_free_cb, NULL);
+	}
+#endif /* HAVE_OCSP */
+
+	return 0;
+}
+
+
+int tls_global_set_verify(void *tls_ctx, int check_crl, int strict)
+{
+	wpa_printf(MSG_DEBUG, "SSL: global set verify: %d", check_crl);
+
+	if (check_crl) {
+		/* Hack to Enable CRLs. */
+		wolfSSL_CTX_LoadCRLBuffer(tls_ctx, NULL, 0, SSL_FILETYPE_PEM);
+	}
+
+	return 0;
+}
+
+
+int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
+			      int verify_peer, unsigned int flags,
+			      const u8 *session_ctx, size_t session_ctx_len)
+{
+	static int counter = 0;
+	struct tls_context *context;
+
+	if (!conn)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "SSL: set verify: %d", verify_peer);
+
+	if (verify_peer) {
+		conn->ca_cert_verify = 1;
+		wolfSSL_set_verify(conn->ssl, SSL_VERIFY_PEER |
+				   SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+				   tls_verify_cb);
+	} else {
+		conn->ca_cert_verify = 0;
+		wolfSSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
+	}
+
+	wolfSSL_set_accept_state(conn->ssl);
+
+	context = wolfSSL_CTX_get_ex_data((WOLFSSL_CTX *) ssl_ctx, 0);
+	if (context && context->tls_session_lifetime == 0) {
+		/*
+		 * Set session id context to a unique value to make sure
+		 * session resumption cannot be used either through session
+		 * caching or TLS ticket extension.
+		 */
+		counter++;
+		wolfSSL_set_session_id_context(conn->ssl,
+					       (const unsigned char *) &counter,
+					       sizeof(counter));
+	} else {
+		wolfSSL_set_session_id_context(conn->ssl, session_ctx,
+					       session_ctx_len);
+	}
+
+	/* TODO: do we need to fake a session like OpenSSL does here? */
+
+	return 0;
+}
+
+
+static struct wpabuf * wolfssl_handshake(struct tls_connection *conn,
+					 const struct wpabuf *in_data,
+					 int server)
+{
+	int res;
+
+	wolfssl_reset_out_data(&conn->output);
+
+	/* Initiate TLS handshake or continue the existing handshake */
+	if (server) {
+		wolfSSL_set_accept_state(conn->ssl);
+		res = wolfSSL_accept(conn->ssl);
+		wpa_printf(MSG_DEBUG, "SSL: wolfSSL_accept: %d", res);
+	} else {
+		wolfSSL_set_connect_state(conn->ssl);
+		res = wolfSSL_connect(conn->ssl);
+		wpa_printf(MSG_DEBUG, "SSL: wolfSSL_connect: %d", res);
+	}
+
+	if (res != WOLFSSL_SUCCESS) {
+		int err = wolfSSL_get_error(conn->ssl, res);
+
+		if (err == WOLFSSL_ERROR_NONE) {
+			wpa_printf(MSG_DEBUG,
+				   "SSL: %s - WOLFSSL_ERROR_NONE (%d)",
+				   server ? "wolfSSL_accept" :
+				   "wolfSSL_connect", res);
+		} else if (err == WOLFSSL_ERROR_WANT_READ) {
+			wpa_printf(MSG_DEBUG,
+				   "SSL: %s - want more data",
+				   server ? "wolfSSL_accept" :
+				   "wolfSSL_connect");
+		} else if (err == WOLFSSL_ERROR_WANT_WRITE) {
+			wpa_printf(MSG_DEBUG,
+				   "SSL: %s - want to write",
+				   server ? "wolfSSL_accept" :
+				   "wolfSSL_connect");
+		} else {
+			char msg[80];
+
+			wpa_printf(MSG_DEBUG,
+				   "SSL: %s - failed %s",
+				   server ? "wolfSSL_accept" :
+				   "wolfSSL_connect",
+				   wolfSSL_ERR_error_string(err, msg));
+			conn->failed++;
+		}
+	}
+
+	return conn->output.out_data;
+}
+
+
+static struct wpabuf * wolfssl_get_appl_data(struct tls_connection *conn,
+					     size_t max_len)
+{
+	int res;
+	struct wpabuf *appl_data = wpabuf_alloc(max_len + 100);
+
+	if (!appl_data)
+		return NULL;
+
+	res = wolfSSL_read(conn->ssl, wpabuf_mhead(appl_data),
+			   wpabuf_size(appl_data));
+	if (res < 0) {
+		int err = wolfSSL_get_error(conn->ssl, res);
+
+		if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
+			wpa_printf(MSG_DEBUG,
+				   "SSL: No Application Data included");
+		} else {
+			char msg[80];
+
+			wpa_printf(MSG_DEBUG,
+				   "Failed to read possible Application Data %s",
+				   wolfSSL_ERR_error_string(err, msg));
+		}
+
+		wpabuf_free(appl_data);
+		return NULL;
+	}
+
+	wpabuf_put(appl_data, res);
+	wpa_hexdump_buf_key(MSG_MSGDUMP,
+			    "SSL: Application Data in Finished message",
+			    appl_data);
+	return appl_data;
+}
+
+
+static struct wpabuf *
+wolfssl_connection_handshake(struct tls_connection *conn,
+			     const struct wpabuf *in_data,
+			     struct wpabuf **appl_data, int server)
+{
+	struct wpabuf *out_data;
+
+	wolfssl_reset_in_data(&conn->input, in_data);
+
+	if (appl_data)
+		*appl_data = NULL;
+
+	out_data = wolfssl_handshake(conn, in_data, server);
+	if (!out_data)
+		return NULL;
+
+	if (wolfSSL_is_init_finished(conn->ssl)) {
+		wpa_printf(MSG_DEBUG,
+			   "wolfSSL: Handshake finished - resumed=%d",
+			   tls_connection_resumed(NULL, conn));
+		if (appl_data && in_data)
+			*appl_data = wolfssl_get_appl_data(conn,
+							   wpabuf_len(in_data));
+	}
+
+	return out_data;
+}
+
+
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+					 struct tls_connection *conn,
+					 const struct wpabuf *in_data,
+					 struct wpabuf **appl_data)
+{
+	return wolfssl_connection_handshake(conn, in_data, appl_data, 0);
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+						struct tls_connection *conn,
+						const struct wpabuf *in_data,
+						struct wpabuf **appl_data)
+{
+	return wolfssl_connection_handshake(conn, in_data, appl_data, 1);
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data)
+{
+	int res;
+
+	if (!conn)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "SSL: encrypt: %zu bytes", wpabuf_len(in_data));
+
+	wolfssl_reset_out_data(&conn->output);
+
+	res = wolfSSL_write(conn->ssl, wpabuf_head(in_data),
+			    wpabuf_len(in_data));
+	if (res < 0) {
+		int  err = wolfSSL_get_error(conn->ssl, res);
+		char msg[80];
+
+		wpa_printf(MSG_INFO, "Encryption failed - SSL_write: %s",
+			   wolfSSL_ERR_error_string(err, msg));
+		return NULL;
+	}
+
+	return conn->output.out_data;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data)
+{
+	int res;
+	struct wpabuf *buf;
+
+	if (!conn)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "SSL: decrypt");
+
+	wolfssl_reset_in_data(&conn->input, in_data);
+
+	/* Read decrypted data for further processing */
+	/*
+	 * Even though we try to disable TLS compression, it is possible that
+	 * this cannot be done with all TLS libraries. Add extra buffer space
+	 * to handle the possibility of the decrypted data being longer than
+	 * input data.
+	 */
+	buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
+	if (!buf)
+		return NULL;
+	res = wolfSSL_read(conn->ssl, wpabuf_mhead(buf), wpabuf_size(buf));
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "Decryption failed - SSL_read");
+		wpabuf_free(buf);
+		return NULL;
+	}
+	wpabuf_put(buf, res);
+
+	wpa_printf(MSG_DEBUG, "SSL: decrypt: %zu bytes", wpabuf_len(buf));
+
+	return buf;
+}
+
+
+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
+{
+	return conn ? wolfSSL_session_reused(conn->ssl) : 0;
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+				   u8 *ciphers)
+{
+	char buf[128], *pos, *end;
+	u8 *c;
+	int ret;
+
+	if (!conn || !conn->ssl || !ciphers)
+		return -1;
+
+	buf[0] = '\0';
+	pos = buf;
+	end = pos + sizeof(buf);
+
+	c = ciphers;
+	while (*c != TLS_CIPHER_NONE) {
+		const char *suite;
+
+		switch (*c) {
+		case TLS_CIPHER_RC4_SHA:
+			suite = "RC4-SHA";
+			break;
+		case TLS_CIPHER_AES128_SHA:
+			suite = "AES128-SHA";
+			break;
+		case TLS_CIPHER_RSA_DHE_AES128_SHA:
+			suite = "DHE-RSA-AES128-SHA";
+			break;
+		case TLS_CIPHER_ANON_DH_AES128_SHA:
+			suite = "ADH-AES128-SHA";
+			break;
+		case TLS_CIPHER_RSA_DHE_AES256_SHA:
+			suite = "DHE-RSA-AES256-SHA";
+			break;
+		case TLS_CIPHER_AES256_SHA:
+			suite = "AES256-SHA";
+			break;
+		default:
+			wpa_printf(MSG_DEBUG,
+				   "TLS: Unsupported cipher selection: %d", *c);
+			return -1;
+		}
+		ret = os_snprintf(pos, end - pos, ":%s", suite);
+		if (os_snprintf_error(end - pos, ret))
+			break;
+		pos += ret;
+
+		c++;
+	}
+
+	wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s", buf + 1);
+
+	if (wolfSSL_set_cipher_list(conn->ssl, buf + 1) != 1) {
+		wpa_printf(MSG_DEBUG, "Cipher suite configuration failed");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
+		   char *buf, size_t buflen)
+{
+	WOLFSSL_CIPHER *cipher;
+	const char *name;
+
+	if (!conn || !conn->ssl)
+		return -1;
+
+	cipher = wolfSSL_get_current_cipher(conn->ssl);
+	if (!cipher)
+		return -1;
+
+	name = wolfSSL_CIPHER_get_name(cipher);
+	if (!name)
+		return -1;
+
+	if (os_strcmp(name, "SSL_RSA_WITH_RC4_128_SHA") == 0)
+		os_strlcpy(buf, "RC4-SHA", buflen);
+	else if (os_strcmp(name, "TLS_RSA_WITH_AES_128_CBC_SHA") == 0)
+		os_strlcpy(buf, "AES128-SHA", buflen);
+	else if (os_strcmp(name, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA") == 0)
+		os_strlcpy(buf, "DHE-RSA-AES128-SHA", buflen);
+	else if (os_strcmp(name, "TLS_DH_anon_WITH_AES_128_CBC_SHA") == 0)
+		os_strlcpy(buf, "ADH-AES128-SHA", buflen);
+	else if (os_strcmp(name, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA") == 0)
+		os_strlcpy(buf, "DHE-RSA-AES256-SHA", buflen);
+	else if (os_strcmp(name, "TLS_RSA_WITH_AES_256_CBC_SHA") == 0)
+		os_strlcpy(buf, "AES256-SHA", buflen);
+	else
+		os_strlcpy(buf, name, buflen);
+
+	return 0;
+}
+
+
+int tls_connection_enable_workaround(void *tls_ctx,
+				     struct tls_connection *conn)
+{
+	/* no empty fragments in wolfSSL for now */
+	return 0;
+}
+
+
+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
+{
+	if (!conn)
+		return -1;
+
+	return conn->failed;
+}
+
+
+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
+{
+	if (!conn)
+		return -1;
+
+	/* TODO: this is not incremented anywhere */
+	return conn->read_alerts;
+}
+
+
+int tls_connection_get_write_alerts(void *tls_ctx,
+				    struct tls_connection *conn)
+{
+	if (!conn)
+		return -1;
+
+	/* TODO: this is not incremented anywhere */
+	return conn->write_alerts;
+}
+
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+	return os_snprintf(buf, buf_len, "wolfSSL build=%s run=%s",
+			   WOLFSSL_VERSION, wolfSSL_lib_version());
+}
+
+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
+		    char *buf, size_t buflen)
+{
+	const char *name;
+
+	if (!conn || !conn->ssl)
+		return -1;
+
+	name = wolfSSL_get_version(conn->ssl);
+	if (!name)
+		return -1;
+
+	os_strlcpy(buf, name, buflen);
+	return 0;
+}
+
+
+int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn,
+			      struct tls_random *keys)
+{
+	WOLFSSL *ssl;
+
+	if (!conn || !keys)
+		return -1;
+	ssl = conn->ssl;
+	if (!ssl)
+		return -1;
+
+	os_memset(keys, 0, sizeof(*keys));
+	keys->client_random = conn->client_random;
+	keys->client_random_len = wolfSSL_get_client_random(
+		ssl, conn->client_random, sizeof(conn->client_random));
+	keys->server_random = conn->server_random;
+	keys->server_random_len = wolfSSL_get_server_random(
+		ssl, conn->server_random, sizeof(conn->server_random));
+
+	return 0;
+}
+
+
+int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
+			      const char *label, const u8 *context,
+			      size_t context_len, u8 *out, size_t out_len)
+{
+	if (!conn)
+		return -1;
+#if LIBWOLFSSL_VERSION_HEX >= 0x04007000
+	if (wolfSSL_export_keying_material(conn->ssl, out, out_len,
+					   label, os_strlen(label),
+					   context, context_len,
+					   context != NULL) != WOLFSSL_SUCCESS)
+		return -1;
+	return 0;
+#else
+	if (context ||
+	    wolfSSL_make_eap_keys(conn->ssl, out, out_len, label) != 0)
+		return -1;
+#endif
+	return 0;
+}
+
+
+#define SEED_LEN	(RAN_LEN + RAN_LEN)
+
+int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
+				    u8 *out, size_t out_len)
+{
+	byte seed[SEED_LEN];
+	int ret = -1;
+	WOLFSSL *ssl;
+	byte *tmp_out;
+	byte *_out;
+	int skip = 0;
+	byte *master_key;
+	unsigned int master_key_len;
+	byte *server_random;
+	unsigned int server_len;
+	byte *client_random;
+	unsigned int client_len;
+
+	if (!conn || !conn->ssl)
+		return -1;
+	ssl = conn->ssl;
+
+	skip = 2 * (wolfSSL_GetKeySize(ssl) + wolfSSL_GetHmacSize(ssl) +
+		    wolfSSL_GetIVSize(ssl));
+
+	tmp_out = os_malloc(skip + out_len);
+	if (!tmp_out)
+		return -1;
+	_out = tmp_out;
+
+	wolfSSL_get_keys(ssl, &master_key, &master_key_len, &server_random,
+			 &server_len, &client_random, &client_len);
+	os_memcpy(seed, server_random, RAN_LEN);
+	os_memcpy(seed + RAN_LEN, client_random, RAN_LEN);
+
+	if (wolfSSL_GetVersion(ssl) == WOLFSSL_TLSV1_2) {
+		tls_prf_sha256(master_key, master_key_len,
+			       "key expansion", seed, sizeof(seed),
+			       _out, skip + out_len);
+		ret = 0;
+	} else {
+#ifdef CONFIG_FIPS
+		wpa_printf(MSG_ERROR,
+			   "wolfSSL: Can't use sha1_md5 in FIPS build");
+		ret = -1;
+#else /* CONFIG_FIPS */
+		ret = tls_prf_sha1_md5(master_key, master_key_len,
+				       "key expansion", seed, sizeof(seed),
+				       _out, skip + out_len);
+#endif /* CONFIG_FIPS */
+	}
+
+	forced_memzero(master_key, master_key_len);
+	if (ret == 0)
+		os_memcpy(out, _out + skip, out_len);
+	bin_clear_free(tmp_out, skip + out_len);
+
+	return ret;
+}
+
+
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+
+int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
+				    int ext_type, const u8 *data,
+				    size_t data_len)
+{
+	(void) ssl_ctx;
+
+	if (!conn || !conn->ssl || ext_type != 35)
+		return -1;
+
+	if (wolfSSL_set_SessionTicket(conn->ssl, data,
+				      (unsigned int) data_len) != 1)
+		return -1;
+
+	return 0;
+}
+
+
+static int tls_sess_sec_cb(WOLFSSL *s, void *secret, int *secret_len, void *arg)
+{
+	struct tls_connection *conn = arg;
+	int ret;
+	unsigned char client_random[RAN_LEN];
+	unsigned char server_random[RAN_LEN];
+	word32 ticket_len = sizeof(conn->session_ticket);
+
+	if (!conn || !conn->session_ticket_cb)
+		return 1;
+
+	if (wolfSSL_get_client_random(s, client_random,
+				      sizeof(client_random)) == 0 ||
+	    wolfSSL_get_server_random(s, server_random,
+				      sizeof(server_random)) == 0 ||
+	    wolfSSL_get_SessionTicket(s, conn->session_ticket,
+				      &ticket_len) != 1)
+		return 1;
+
+	if (ticket_len == 0)
+		return 0;
+
+	ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx,
+				      conn->session_ticket, ticket_len,
+				      client_random, server_random, secret);
+	if (ret <= 0)
+		return 1;
+
+	*secret_len = SECRET_LEN;
+	return 0;
+}
+
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+
+
+int tls_connection_set_session_ticket_cb(void *tls_ctx,
+					 struct tls_connection *conn,
+					 tls_session_ticket_cb cb,
+					 void *ctx)
+{
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+	conn->session_ticket_cb = cb;
+	conn->session_ticket_cb_ctx = ctx;
+
+	if (cb) {
+		if (wolfSSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb,
+						  conn) != 1)
+			return -1;
+	} else {
+		if (wolfSSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1)
+			return -1;
+	}
+
+	return 0;
+#else /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+	return -1;
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+}
+
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
+{
+	wpa_printf(MSG_DEBUG,
+		   "wolfSSL: Success data accepted for resumed session");
+}
+
+
+void tls_connection_remove_session(struct tls_connection *conn)
+{
+	WOLFSSL_SESSION *sess;
+
+	sess = wolfSSL_get_session(conn->ssl);
+	if (!sess)
+		return;
+
+	wolfSSL_SSL_SESSION_set_timeout(sess, 0);
+	wpa_printf(MSG_DEBUG,
+		   "wolfSSL: Removed cached session to disable session resumption");
+}
+
+
+int tls_get_tls_unique(struct tls_connection *conn, u8 *buf, size_t max_len)
+{
+	size_t len;
+	int reused;
+
+	reused = wolfSSL_session_reused(conn->ssl);
+	if ((wolfSSL_is_server(conn->ssl) && !reused) ||
+	    (!wolfSSL_is_server(conn->ssl) && reused))
+		len = wolfSSL_get_peer_finished(conn->ssl, buf, max_len);
+	else
+		len = wolfSSL_get_finished(conn->ssl, buf, max_len);
+
+	if (len == 0 || len > max_len)
+		return -1;
+
+	return len;
+}
+
+
+u16 tls_connection_get_cipher_suite(struct tls_connection *conn)
+{
+	return (u16) wolfSSL_get_current_cipher_suite(conn->ssl);
+}
+
+
+const char * tls_connection_get_peer_subject(struct tls_connection *conn)
+{
+	if (conn)
+		return conn->peer_subject;
+	return NULL;
+}
+
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+				     struct wpabuf *data)
+{
+	WOLFSSL_SESSION *sess;
+	struct wpabuf *old;
+
+	wpa_printf(MSG_DEBUG, "wolfSSL: Set success data");
+
+	sess = wolfSSL_get_session(conn->ssl);
+	if (!sess) {
+		wpa_printf(MSG_DEBUG,
+			   "wolfSSL: No session found for success data");
+		goto fail;
+	}
+
+	old = wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+	if (old) {
+		wpa_printf(MSG_DEBUG, "wolfSSL: Replacing old success data %p",
+			   old);
+		wpabuf_free(old);
+	}
+	if (wolfSSL_SESSION_set_ex_data(sess, tls_ex_idx_session, data) != 1)
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "wolfSSL: Stored success data %p", data);
+	conn->success_data = 1;
+	return;
+
+fail:
+	wpa_printf(MSG_INFO, "wolfSSL: Failed to store success data");
+	wpabuf_free(data);
+}
+
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn)
+{
+	WOLFSSL_SESSION *sess;
+
+	wpa_printf(MSG_DEBUG, "wolfSSL: Get success data");
+
+	sess = wolfSSL_get_session(conn->ssl);
+	if (!sess)
+		return NULL;
+	return wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+}
+
+
+bool tls_connection_get_own_cert_used(struct tls_connection *conn)
+{
+	if (conn)
+		return wolfSSL_get_certificate(conn->ssl) != NULL;
+	return false;
+}