ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/Makefile b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/Makefile
new file mode 100644
index 0000000..e974a41
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/Makefile
@@ -0,0 +1,36 @@
+LIB_OBJS= asn1.o
+
+ifneq ($(CONFIG_TLS),gnutls)
+ifneq ($(CONFIG_TLS),mbedtls)
+ifneq ($(CONFIG_TLS),openssl)
+ifneq ($(CONFIG_TLS),wolfssl)
+
+CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+CFLAGS += -DCONFIG_CRYPTO_INTERNAL
+CFLAGS += -DCONFIG_TLSV11
+CFLAGS += -DCONFIG_TLSV12
+
+LIB_OBJS= \
+	asn1.o \
+	bignum.o \
+	pkcs1.o \
+	pkcs5.o \
+	pkcs8.o \
+	rsa.o \
+	tlsv1_client.o \
+	tlsv1_client_read.o \
+	tlsv1_client_write.o \
+	tlsv1_client_ocsp.o \
+	tlsv1_common.o \
+	tlsv1_cred.o \
+	tlsv1_record.o \
+	tlsv1_server.o \
+	tlsv1_server_read.o \
+	tlsv1_server_write.o \
+	x509v3.o
+endif
+endif
+endif
+endif
+
+include ../lib.rules
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/asn1.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/asn1.c
new file mode 100644
index 0000000..04d5320
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/asn1.c
@@ -0,0 +1,650 @@
+/*
+ * ASN.1 DER parsing
+ * Copyright (c) 2006-2014, 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 "utils/wpabuf.h"
+#include "asn1.h"
+
+const struct asn1_oid asn1_sha1_oid = {
+	.oid = { 1, 3, 14, 3, 2, 26 },
+	.len = 6
+};
+
+const struct asn1_oid asn1_sha256_oid = {
+	.oid = { 2, 16, 840, 1, 101, 3, 4, 2, 1 },
+	.len = 9
+};
+
+const struct asn1_oid asn1_ec_public_key_oid = {
+	.oid = { 1, 2, 840, 10045, 2, 1 },
+	.len = 6
+};
+
+const struct asn1_oid asn1_prime256v1_oid = {
+	.oid = { 1, 2, 840, 10045, 3, 1, 7 },
+	.len = 7
+};
+
+const struct asn1_oid asn1_secp384r1_oid = {
+	.oid = { 1, 3, 132, 0, 34 },
+	.len = 5
+};
+
+const struct asn1_oid asn1_secp521r1_oid = {
+	.oid = { 1, 3, 132, 0, 35 },
+	.len = 5
+};
+
+const struct asn1_oid asn1_brainpoolP256r1_oid = {
+	.oid = { 1, 3, 36, 3, 3, 2, 8, 1, 1, 7 },
+	.len = 10
+};
+
+const struct asn1_oid asn1_brainpoolP384r1_oid = {
+	.oid = { 1, 3, 36, 3, 3, 2, 8, 1, 1, 11 },
+	.len = 10
+};
+
+const struct asn1_oid asn1_brainpoolP512r1_oid = {
+	.oid = { 1, 3, 36, 3, 3, 2, 8, 1, 1, 13 },
+	.len = 10
+};
+
+const struct asn1_oid asn1_aes_siv_cmac_aead_256_oid = {
+	.oid = { 1, 2, 840, 113549, 1, 9, 16, 3, 22 },
+	.len = 9
+};
+
+const struct asn1_oid asn1_aes_siv_cmac_aead_384_oid = {
+	.oid = { 1, 2, 840, 113549, 1, 9, 16, 3, 23 },
+	.len = 9
+};
+
+const struct asn1_oid asn1_aes_siv_cmac_aead_512_oid = {
+	.oid = { 1, 2, 840, 113549, 1, 9, 16, 3, 24 },
+	.len = 9
+};
+
+const struct asn1_oid asn1_pbkdf2_oid = {
+	.oid = { 1, 2, 840, 113549, 1, 5, 12 },
+	.len = 7
+};
+
+const struct asn1_oid asn1_pbkdf2_hmac_sha256_oid = {
+	.oid = { 1, 2, 840, 113549, 2, 9 },
+	.len = 6
+};
+
+const struct asn1_oid asn1_pbkdf2_hmac_sha384_oid = {
+	.oid = { 1, 2, 840, 113549, 2, 10 },
+	.len = 6
+};
+
+const struct asn1_oid asn1_pbkdf2_hmac_sha512_oid = {
+	.oid = { 1, 2, 840, 113549, 2, 11 },
+	.len = 6
+};
+
+const struct asn1_oid asn1_dpp_config_params_oid = {
+	.oid = { 1, 3, 6, 1, 4, 1, 40808, 1, 2, 1 },
+	.len = 10
+};
+
+const struct asn1_oid asn1_dpp_asymmetric_key_package_oid = {
+	.oid = { 1, 3, 6, 1, 4, 1, 40808, 1, 2, 2 },
+	.len = 10
+};
+
+
+static int asn1_valid_der_boolean(struct asn1_hdr *hdr)
+{
+	/* Enforce DER requirements for a single way of encoding a BOOLEAN */
+	if (hdr->length != 1) {
+		wpa_printf(MSG_DEBUG, "ASN.1: Unexpected BOOLEAN length (%u)",
+			   hdr->length);
+		return 0;
+	}
+
+	if (hdr->payload[0] != 0 && hdr->payload[0] != 0xff) {
+		wpa_printf(MSG_DEBUG,
+			   "ASN.1: Invalid BOOLEAN value 0x%x (DER requires 0 or 0xff)",
+			   hdr->payload[0]);
+		return 0;
+	}
+
+	return 1;
+}
+
+
+static int asn1_valid_der(struct asn1_hdr *hdr)
+{
+	if (hdr->class != ASN1_CLASS_UNIVERSAL)
+		return 1;
+	if (hdr->tag == ASN1_TAG_BOOLEAN && !asn1_valid_der_boolean(hdr))
+		return 0;
+	if (hdr->tag == ASN1_TAG_NULL && hdr->length != 0)
+		return 0;
+
+	/* Check for allowed primitive/constructed values */
+	if (hdr->constructed &&
+	    (hdr->tag == ASN1_TAG_BOOLEAN ||
+	     hdr->tag == ASN1_TAG_INTEGER ||
+	     hdr->tag == ASN1_TAG_NULL ||
+	     hdr->tag == ASN1_TAG_OID ||
+	     hdr->tag == ANS1_TAG_RELATIVE_OID ||
+	     hdr->tag == ASN1_TAG_REAL ||
+	     hdr->tag == ASN1_TAG_ENUMERATED ||
+	     hdr->tag == ASN1_TAG_BITSTRING ||
+	     hdr->tag == ASN1_TAG_OCTETSTRING ||
+	     hdr->tag == ASN1_TAG_NUMERICSTRING ||
+	     hdr->tag == ASN1_TAG_PRINTABLESTRING ||
+	     hdr->tag == ASN1_TAG_T61STRING ||
+	     hdr->tag == ASN1_TAG_VIDEOTEXSTRING ||
+	     hdr->tag == ASN1_TAG_VISIBLESTRING ||
+	     hdr->tag == ASN1_TAG_IA5STRING ||
+	     hdr->tag == ASN1_TAG_GRAPHICSTRING ||
+	     hdr->tag == ASN1_TAG_GENERALSTRING ||
+	     hdr->tag == ASN1_TAG_UNIVERSALSTRING ||
+	     hdr->tag == ASN1_TAG_UTF8STRING ||
+	     hdr->tag == ASN1_TAG_BMPSTRING ||
+	     hdr->tag == ASN1_TAG_CHARACTERSTRING ||
+	     hdr->tag == ASN1_TAG_UTCTIME ||
+	     hdr->tag == ASN1_TAG_GENERALIZEDTIME ||
+	     hdr->tag == ASN1_TAG_TIME))
+		return 0;
+	if (!hdr->constructed &&
+	    (hdr->tag == ASN1_TAG_SEQUENCE ||
+	     hdr->tag == ASN1_TAG_SET))
+		return 0;
+
+	return 1;
+}
+
+
+int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr)
+{
+	const u8 *pos, *end;
+	u8 tmp;
+
+	os_memset(hdr, 0, sizeof(*hdr));
+	pos = buf;
+	end = buf + len;
+
+	if (pos >= end) {
+		wpa_printf(MSG_DEBUG, "ASN.1: No room for Identifier");
+		return -1;
+	}
+	hdr->identifier = *pos++;
+	hdr->class = hdr->identifier >> 6;
+	hdr->constructed = !!(hdr->identifier & (1 << 5));
+
+	if ((hdr->identifier & 0x1f) == 0x1f) {
+		size_t ext_len = 0;
+
+		hdr->tag = 0;
+		if (pos == end || (*pos & 0x7f) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "ASN.1: Invalid extended tag (first octet has to be included with at least one nonzero bit for the tag value)");
+			return -1;
+		}
+		do {
+			if (pos >= end) {
+				wpa_printf(MSG_DEBUG, "ASN.1: Identifier "
+					   "underflow");
+				return -1;
+			}
+			ext_len++;
+			tmp = *pos++;
+			wpa_printf(MSG_MSGDUMP, "ASN.1: Extended tag data: "
+				   "0x%02x", tmp);
+			hdr->tag = (hdr->tag << 7) | (tmp & 0x7f);
+		} while (tmp & 0x80);
+		wpa_printf(MSG_MSGDUMP, "ASN.1: Extended Tag: 0x%x (len=%zu)",
+			   hdr->tag, ext_len);
+		if ((hdr->class != ASN1_CLASS_PRIVATE && hdr->tag < 31) ||
+		    ext_len * 7 > sizeof(hdr->tag) * 8) {
+			wpa_printf(MSG_DEBUG,
+				   "ASN.1: Invalid or unsupported (too large) extended Tag: 0x%x (len=%zu)",
+				   hdr->tag, ext_len);
+			return -1;
+		}
+	} else
+		hdr->tag = hdr->identifier & 0x1f;
+
+	if (pos >= end) {
+		wpa_printf(MSG_DEBUG, "ASN.1: No room for Length");
+		return -1;
+	}
+	tmp = *pos++;
+	if (tmp & 0x80) {
+		if (tmp == 0xff) {
+			wpa_printf(MSG_DEBUG, "ASN.1: Reserved length "
+				   "value 0xff used");
+			return -1;
+		}
+		tmp &= 0x7f; /* number of subsequent octets */
+		hdr->length = 0;
+		if (tmp == 0 || pos == end || *pos == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "ASN.1: Definite long form of the length does not start with a nonzero value");
+			return -1;
+		}
+		if (tmp > 4) {
+			wpa_printf(MSG_DEBUG, "ASN.1: Too long length field");
+			return -1;
+		}
+		while (tmp--) {
+			if (pos >= end) {
+				wpa_printf(MSG_DEBUG, "ASN.1: Length "
+					   "underflow");
+				return -1;
+			}
+			hdr->length = (hdr->length << 8) | *pos++;
+		}
+		if (hdr->length < 128) {
+			wpa_printf(MSG_DEBUG,
+				   "ASN.1: Definite long form of the length used with too short length");
+			return -1;
+		}
+	} else {
+		/* Short form - length 0..127 in one octet */
+		hdr->length = tmp;
+	}
+
+	if (end < pos || hdr->length > (unsigned int) (end - pos)) {
+		wpa_printf(MSG_DEBUG, "ASN.1: Contents underflow");
+		return -1;
+	}
+
+	hdr->payload = pos;
+
+	if (!asn1_valid_der(hdr)) {
+		asn1_print_hdr(hdr, "ASN.1: Invalid DER encoding: ");
+		return -1;
+	}
+	return 0;
+}
+
+
+void asn1_print_hdr(const struct asn1_hdr *hdr, const char *title)
+{
+	wpa_printf(MSG_DEBUG, "%sclass %d constructed %d tag 0x%x",
+		   title, hdr->class, hdr->constructed, hdr->tag);
+}
+
+
+void asn1_unexpected(const struct asn1_hdr *hdr, const char *title)
+{
+	wpa_printf(MSG_DEBUG, "%s - found class %d constructed %d tag 0x%x",
+		   title, hdr->class, hdr->constructed, hdr->tag);
+}
+
+
+int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid)
+{
+	const u8 *pos, *end;
+	unsigned long val;
+	u8 tmp;
+
+	os_memset(oid, 0, sizeof(*oid));
+
+	pos = buf;
+	end = buf + len;
+
+	while (pos < end) {
+		val = 0;
+
+		do {
+			if (pos >= end)
+				return -1;
+			tmp = *pos++;
+			val = (val << 7) | (tmp & 0x7f);
+		} while (tmp & 0x80);
+
+		if (oid->len >= ASN1_MAX_OID_LEN) {
+			wpa_printf(MSG_DEBUG, "ASN.1: Too long OID value");
+			return -1;
+		}
+		if (oid->len == 0) {
+			/*
+			 * The first octet encodes the first two object
+			 * identifier components in (X*40) + Y formula.
+			 * X = 0..2.
+			 */
+			oid->oid[0] = val / 40;
+			if (oid->oid[0] > 2)
+				oid->oid[0] = 2;
+			oid->oid[1] = val - oid->oid[0] * 40;
+			oid->len = 2;
+		} else
+			oid->oid[oid->len++] = val;
+	}
+
+	return 0;
+}
+
+
+int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid,
+		 const u8 **next)
+{
+	struct asn1_hdr hdr;
+
+	if (asn1_get_next(buf, len, &hdr) < 0 || hdr.length == 0 ||
+	    !asn1_is_oid(&hdr)) {
+		asn1_unexpected(&hdr, "ASN.1: Expected OID");
+		return -1;
+	}
+
+	*next = hdr.payload + hdr.length;
+
+	return asn1_parse_oid(hdr.payload, hdr.length, oid);
+}
+
+
+void asn1_oid_to_str(const struct asn1_oid *oid, char *buf, size_t len)
+{
+	char *pos = buf;
+	size_t i;
+	int ret;
+
+	if (len == 0)
+		return;
+
+	buf[0] = '\0';
+
+	for (i = 0; i < oid->len; i++) {
+		ret = os_snprintf(pos, buf + len - pos,
+				  "%s%lu",
+				  i == 0 ? "" : ".", oid->oid[i]);
+		if (os_snprintf_error(buf + len - pos, ret))
+			break;
+		pos += ret;
+	}
+	buf[len - 1] = '\0';
+}
+
+
+static u8 rotate_bits(u8 octet)
+{
+	int i;
+	u8 res;
+
+	res = 0;
+	for (i = 0; i < 8; i++) {
+		res <<= 1;
+		if (octet & 1)
+			res |= 1;
+		octet >>= 1;
+	}
+
+	return res;
+}
+
+
+unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len)
+{
+	unsigned long val = 0;
+	const u8 *pos = buf;
+
+	/* BER requires that unused bits are zero, so we can ignore the number
+	 * of unused bits */
+	pos++;
+
+	if (len >= 2)
+		val |= rotate_bits(*pos++);
+	if (len >= 3)
+		val |= ((unsigned long) rotate_bits(*pos++)) << 8;
+	if (len >= 4)
+		val |= ((unsigned long) rotate_bits(*pos++)) << 16;
+	if (len >= 5)
+		val |= ((unsigned long) rotate_bits(*pos++)) << 24;
+	if (len >= 6)
+		wpa_printf(MSG_DEBUG, "X509: %s - some bits ignored "
+			   "(BIT STRING length %lu)",
+			   __func__, (unsigned long) len);
+
+	return val;
+}
+
+
+int asn1_oid_equal(const struct asn1_oid *a, const struct asn1_oid *b)
+{
+	size_t i;
+
+	if (a->len != b->len)
+		return 0;
+
+	for (i = 0; i < a->len; i++) {
+		if (a->oid[i] != b->oid[i])
+			return 0;
+	}
+
+	return 1;
+}
+
+
+int asn1_get_integer(const u8 *buf, size_t len, int *integer, const u8 **next)
+{
+	struct asn1_hdr hdr;
+	size_t left;
+	const u8 *pos;
+	int value;
+
+	if (asn1_get_next(buf, len, &hdr) < 0 || hdr.length == 0 ||
+	    !asn1_is_integer(&hdr)) {
+		asn1_unexpected(&hdr, "ASN.1: Expected INTEGER");
+		return -1;
+	}
+
+	*next = hdr.payload + hdr.length;
+	pos = hdr.payload;
+	left = hdr.length;
+	if (left > sizeof(value)) {
+		wpa_printf(MSG_DEBUG, "ASN.1: Too large INTEGER (len %u)",
+			   hdr.length);
+		return -1;
+	}
+	value = 0;
+	while (left) {
+		value <<= 8;
+		value |= *pos++;
+		left--;
+	}
+
+	*integer = value;
+	return 0;
+}
+
+
+int asn1_get_sequence(const u8 *buf, size_t len, struct asn1_hdr *hdr,
+		      const u8 **next)
+{
+	if (asn1_get_next(buf, len, hdr) < 0 || !asn1_is_sequence(hdr)) {
+		asn1_unexpected(hdr, "ASN.1: Expected SEQUENCE");
+		return -1;
+	}
+
+	if (next)
+		*next = hdr->payload + hdr->length;
+	return 0;
+}
+
+
+int asn1_get_alg_id(const u8 *buf, size_t len, struct asn1_oid *oid,
+		    const u8 **params, size_t *params_len, const u8 **next)
+{
+	const u8 *pos = buf, *end = buf + len;
+	struct asn1_hdr hdr;
+
+	/*
+	 * AlgorithmIdentifier ::= SEQUENCE {
+	 *     algorithm            OBJECT IDENTIFIER,
+	 *     parameters           ANY DEFINED BY algorithm OPTIONAL}
+	 */
+	if (asn1_get_sequence(pos, end - pos, &hdr, next) < 0 ||
+	    asn1_get_oid(hdr.payload, hdr.length, oid, &pos) < 0)
+		return -1;
+
+	if (params && params_len) {
+		*params = pos;
+		*params_len = hdr.payload + hdr.length - pos;
+	}
+
+	return 0;
+}
+
+
+void asn1_put_integer(struct wpabuf *buf, int val)
+{
+	u8 bin[4];
+	int zeros;
+
+	WPA_PUT_BE32(bin, val);
+	zeros = 0;
+	while (zeros < 3 && bin[zeros] == 0)
+		zeros++;
+	wpabuf_put_u8(buf, ASN1_TAG_INTEGER);
+	wpabuf_put_u8(buf, 4 - zeros);
+	wpabuf_put_data(buf, &bin[zeros], 4 - zeros);
+}
+
+
+static void asn1_put_len(struct wpabuf *buf, size_t len)
+{
+	if (len <= 0x7f) {
+		wpabuf_put_u8(buf, len);
+	} else if (len <= 0xff) {
+		wpabuf_put_u8(buf, 0x80 | 1);
+		wpabuf_put_u8(buf, len);
+	} else if (len <= 0xffff) {
+		wpabuf_put_u8(buf, 0x80 | 2);
+		wpabuf_put_be16(buf, len);
+	} else if (len <= 0xffffff) {
+		wpabuf_put_u8(buf, 0x80 | 3);
+		wpabuf_put_be24(buf, len);
+	} else {
+		wpabuf_put_u8(buf, 0x80 | 4);
+		wpabuf_put_be32(buf, len);
+	}
+}
+
+
+void asn1_put_octet_string(struct wpabuf *buf, const struct wpabuf *val)
+{
+	wpabuf_put_u8(buf, ASN1_TAG_OCTETSTRING);
+	asn1_put_len(buf, wpabuf_len(val));
+	wpabuf_put_buf(buf, val);
+}
+
+
+void asn1_put_oid(struct wpabuf *buf, const struct asn1_oid *oid)
+{
+	u8 *len;
+	size_t i;
+
+	if (oid->len < 2)
+		return;
+	wpabuf_put_u8(buf, ASN1_TAG_OID);
+	len = wpabuf_put(buf, 1);
+	wpabuf_put_u8(buf, 40 * oid->oid[0] + oid->oid[1]);
+	for (i = 2; i < oid->len; i++) {
+		unsigned long val = oid->oid[i];
+		u8 bytes[8];
+		int idx = 0;
+
+		while (val) {
+			bytes[idx] = (idx ? 0x80 : 0x00) | (val & 0x7f);
+			idx++;
+			val >>= 7;
+		}
+		if (idx == 0) {
+			bytes[idx] = 0;
+			idx = 1;
+		}
+		while (idx > 0) {
+			idx--;
+			wpabuf_put_u8(buf, bytes[idx]);
+		}
+	}
+	*len = (u8 *) wpabuf_put(buf, 0) - len - 1;
+}
+
+
+void asn1_put_hdr(struct wpabuf *buf, u8 class, int constructed, u8 tag,
+		  size_t len)
+{
+	wpabuf_put_u8(buf, class << 6 | (constructed ? 0x20 : 0x00) | tag);
+	asn1_put_len(buf, len);
+}
+
+
+void asn1_put_sequence(struct wpabuf *buf, const struct wpabuf *payload)
+{
+	asn1_put_hdr(buf, ASN1_CLASS_UNIVERSAL, 1, ASN1_TAG_SEQUENCE,
+		     wpabuf_len(payload));
+	wpabuf_put_buf(buf, payload);
+}
+
+
+void asn1_put_set(struct wpabuf *buf, const struct wpabuf *payload)
+{
+	asn1_put_hdr(buf, ASN1_CLASS_UNIVERSAL, 1, ASN1_TAG_SET,
+		     wpabuf_len(payload));
+	wpabuf_put_buf(buf, payload);
+}
+
+
+void asn1_put_utf8string(struct wpabuf *buf, const char *val)
+{
+	asn1_put_hdr(buf, ASN1_CLASS_UNIVERSAL, 0, ASN1_TAG_UTF8STRING,
+		     os_strlen(val));
+	wpabuf_put_str(buf, val);
+}
+
+
+struct wpabuf * asn1_build_alg_id(const struct asn1_oid *oid,
+				  const struct wpabuf *params)
+{
+	struct wpabuf *buf;
+	size_t len;
+
+	/*
+	 * AlgorithmIdentifier ::= SEQUENCE {
+	 *    algorithm		OBJECT IDENTIFIER,
+	 *    parameters	ANY DEFINED BY algorithm OPTIONAL}
+	 */
+
+	len = 100;
+	if (params)
+		len += wpabuf_len(params);
+	buf = wpabuf_alloc(len);
+	if (!buf)
+		return NULL;
+	asn1_put_oid(buf, oid);
+	if (params)
+		wpabuf_put_buf(buf, params);
+	return asn1_encaps(buf, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+}
+
+
+struct wpabuf * asn1_encaps(struct wpabuf *buf, u8 class, u8 tag)
+{
+	struct wpabuf *res;
+
+	if (!buf)
+		return NULL;
+	res = wpabuf_alloc(10 + wpabuf_len(buf));
+	if (res) {
+		asn1_put_hdr(res, class, 1, tag, wpabuf_len(buf));
+		wpabuf_put_buf(res, buf);
+	}
+	wpabuf_clear_free(buf);
+	return res;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/asn1.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/asn1.h
new file mode 100644
index 0000000..a4d1be4
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/asn1.h
@@ -0,0 +1,212 @@
+/*
+ * ASN.1 DER parsing
+ * 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.
+ */
+
+#ifndef ASN1_H
+#define ASN1_H
+
+#define ASN1_TAG_EOC		0x00 /* not used with DER */
+#define ASN1_TAG_BOOLEAN	0x01
+#define ASN1_TAG_INTEGER	0x02
+#define ASN1_TAG_BITSTRING	0x03
+#define ASN1_TAG_OCTETSTRING	0x04
+#define ASN1_TAG_NULL		0x05
+#define ASN1_TAG_OID		0x06
+#define ASN1_TAG_OBJECT_DESCRIPTOR	0x07 /* not yet parsed */
+#define ASN1_TAG_EXTERNAL	0x08 /* not yet parsed */
+#define ASN1_TAG_REAL		0x09 /* not yet parsed */
+#define ASN1_TAG_ENUMERATED	0x0A /* not yet parsed */
+#define ASN1_TAG_EMBEDDED_PDV	0x0B /* not yet parsed */
+#define ASN1_TAG_UTF8STRING	0x0C /* not yet parsed */
+#define ANS1_TAG_RELATIVE_OID	0x0D
+#define ASN1_TAG_TIME		0x0E
+#define ASN1_TAG_SEQUENCE	0x10 /* shall be constructed */
+#define ASN1_TAG_SET		0x11
+#define ASN1_TAG_NUMERICSTRING	0x12 /* not yet parsed */
+#define ASN1_TAG_PRINTABLESTRING	0x13
+#define ASN1_TAG_T61STRING	0x14 /* not yet parsed */
+#define ASN1_TAG_VIDEOTEXSTRING	0x15 /* not yet parsed */
+#define ASN1_TAG_IA5STRING	0x16
+#define ASN1_TAG_UTCTIME	0x17
+#define ASN1_TAG_GENERALIZEDTIME	0x18 /* not yet parsed */
+#define ASN1_TAG_GRAPHICSTRING	0x19 /* not yet parsed */
+#define ASN1_TAG_VISIBLESTRING	0x1A
+#define ASN1_TAG_GENERALSTRING	0x1B /* not yet parsed */
+#define ASN1_TAG_UNIVERSALSTRING	0x1C /* not yet parsed */
+#define ASN1_TAG_CHARACTERSTRING	0x1D /* not yet parsed */
+#define ASN1_TAG_BMPSTRING	0x1E /* not yet parsed */
+
+#define ASN1_CLASS_UNIVERSAL		0
+#define ASN1_CLASS_APPLICATION		1
+#define ASN1_CLASS_CONTEXT_SPECIFIC	2
+#define ASN1_CLASS_PRIVATE		3
+
+
+struct asn1_hdr {
+	const u8 *payload;
+	u8 identifier, class, constructed;
+	unsigned int tag, length;
+};
+
+#define ASN1_MAX_OID_LEN 20
+struct asn1_oid {
+	unsigned long oid[ASN1_MAX_OID_LEN];
+	size_t len;
+};
+
+
+int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr);
+void asn1_print_hdr(const struct asn1_hdr *hdr, const char *title);
+void asn1_unexpected(const struct asn1_hdr *hdr, const char *title);
+int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid);
+int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid,
+		 const u8 **next);
+void asn1_oid_to_str(const struct asn1_oid *oid, char *buf, size_t len);
+unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len);
+int asn1_oid_equal(const struct asn1_oid *a, const struct asn1_oid *b);
+int asn1_get_integer(const u8 *buf, size_t len, int *integer, const u8 **next);
+int asn1_get_sequence(const u8 *buf, size_t len, struct asn1_hdr *hdr,
+		      const u8 **next);
+int asn1_get_alg_id(const u8 *buf, size_t len, struct asn1_oid *oid,
+		    const u8 **params, size_t *params_len, const u8 **next);
+void asn1_put_integer(struct wpabuf *buf, int val);
+void asn1_put_octet_string(struct wpabuf *buf, const struct wpabuf *val);
+void asn1_put_oid(struct wpabuf *buf, const struct asn1_oid *oid);
+void asn1_put_hdr(struct wpabuf *buf, u8 class, int constructed, u8 tag,
+		  size_t len);
+void asn1_put_sequence(struct wpabuf *buf, const struct wpabuf *payload);
+void asn1_put_set(struct wpabuf *buf, const struct wpabuf *payload);
+void asn1_put_utf8string(struct wpabuf *buf, const char *val);
+struct wpabuf * asn1_build_alg_id(const struct asn1_oid *oid,
+				  const struct wpabuf *params);
+struct wpabuf * asn1_encaps(struct wpabuf *buf, u8 class, u8 tag);
+
+static inline bool asn1_is_oid(const struct asn1_hdr *hdr)
+{
+	return hdr->class == ASN1_CLASS_UNIVERSAL &&
+		hdr->tag == ASN1_TAG_OID;
+}
+
+static inline bool asn1_is_boolean(const struct asn1_hdr *hdr)
+{
+	return hdr->class == ASN1_CLASS_UNIVERSAL &&
+		hdr->tag == ASN1_TAG_BOOLEAN;
+}
+
+static inline bool asn1_is_integer(const struct asn1_hdr *hdr)
+{
+	return hdr->class == ASN1_CLASS_UNIVERSAL &&
+		hdr->tag == ASN1_TAG_INTEGER;
+}
+
+static inline bool asn1_is_enumerated(const struct asn1_hdr *hdr)
+{
+	return hdr->class == ASN1_CLASS_UNIVERSAL &&
+		hdr->tag == ASN1_TAG_ENUMERATED;
+}
+
+static inline bool asn1_is_sequence(const struct asn1_hdr *hdr)
+{
+	return hdr->class == ASN1_CLASS_UNIVERSAL &&
+		hdr->tag == ASN1_TAG_SEQUENCE;
+}
+
+static inline bool asn1_is_set(const struct asn1_hdr *hdr)
+{
+	return hdr->class == ASN1_CLASS_UNIVERSAL &&
+		hdr->tag == ASN1_TAG_SET;
+}
+
+static inline bool asn1_is_octetstring(const struct asn1_hdr *hdr)
+{
+	return hdr->class == ASN1_CLASS_UNIVERSAL &&
+		hdr->tag == ASN1_TAG_OCTETSTRING;
+}
+
+static inline bool asn1_is_bitstring(const struct asn1_hdr *hdr)
+{
+	return hdr->class == ASN1_CLASS_UNIVERSAL &&
+		hdr->tag == ASN1_TAG_BITSTRING;
+}
+
+static inline bool asn1_is_utctime(const struct asn1_hdr *hdr)
+{
+	return hdr->class == ASN1_CLASS_UNIVERSAL &&
+		hdr->tag == ASN1_TAG_UTCTIME;
+}
+
+static inline bool asn1_is_generalizedtime(const struct asn1_hdr *hdr)
+{
+	return hdr->class == ASN1_CLASS_UNIVERSAL &&
+		hdr->tag == ASN1_TAG_GENERALIZEDTIME;
+}
+
+static inline bool asn1_is_string_type(const struct asn1_hdr *hdr)
+{
+	if (hdr->class != ASN1_CLASS_UNIVERSAL || hdr->constructed)
+		return false;
+	return hdr->tag == ASN1_TAG_UTF8STRING ||
+		hdr->tag == ASN1_TAG_NUMERICSTRING ||
+		hdr->tag == ASN1_TAG_PRINTABLESTRING ||
+		hdr->tag == ASN1_TAG_T61STRING ||
+		hdr->tag == ASN1_TAG_VIDEOTEXSTRING ||
+		hdr->tag == ASN1_TAG_IA5STRING ||
+		hdr->tag == ASN1_TAG_GRAPHICSTRING ||
+		hdr->tag == ASN1_TAG_VISIBLESTRING ||
+		hdr->tag == ASN1_TAG_GENERALSTRING ||
+		hdr->tag == ASN1_TAG_UNIVERSALSTRING ||
+		hdr->tag == ASN1_TAG_CHARACTERSTRING ||
+		hdr->tag == ASN1_TAG_BMPSTRING;
+}
+
+static inline bool asn1_is_bmpstring(const struct asn1_hdr *hdr)
+{
+	return hdr->class == ASN1_CLASS_UNIVERSAL &&
+		hdr->tag == ASN1_TAG_BMPSTRING;
+}
+
+static inline bool asn1_is_utf8string(const struct asn1_hdr *hdr)
+{
+	return hdr->class == ASN1_CLASS_UNIVERSAL &&
+		hdr->tag == ASN1_TAG_UTF8STRING;
+}
+
+static inline bool asn1_is_null(const struct asn1_hdr *hdr)
+{
+	return hdr->class == ASN1_CLASS_UNIVERSAL &&
+		hdr->tag == ASN1_TAG_NULL;
+}
+
+static inline bool asn1_is_cs_tag(const struct asn1_hdr *hdr, unsigned int tag)
+{
+	return hdr->class == ASN1_CLASS_CONTEXT_SPECIFIC &&
+		hdr->tag == tag;
+}
+
+extern const struct asn1_oid asn1_sha1_oid;
+extern const struct asn1_oid asn1_sha256_oid;
+extern const struct asn1_oid asn1_ec_public_key_oid;
+extern const struct asn1_oid asn1_prime256v1_oid;
+extern const struct asn1_oid asn1_secp384r1_oid;
+extern const struct asn1_oid asn1_secp521r1_oid;
+extern const struct asn1_oid asn1_brainpoolP256r1_oid;
+extern const struct asn1_oid asn1_brainpoolP384r1_oid;
+extern const struct asn1_oid asn1_brainpoolP512r1_oid;
+extern const struct asn1_oid asn1_aes_siv_cmac_aead_256_oid;
+extern const struct asn1_oid asn1_aes_siv_cmac_aead_384_oid;
+extern const struct asn1_oid asn1_aes_siv_cmac_aead_512_oid;
+extern const struct asn1_oid asn1_aes_siv_cmac_aead_256_oid;
+extern const struct asn1_oid asn1_aes_siv_cmac_aead_384_oid;
+extern const struct asn1_oid asn1_aes_siv_cmac_aead_512_oid;
+extern const struct asn1_oid asn1_pbkdf2_oid;
+extern const struct asn1_oid asn1_pbkdf2_hmac_sha256_oid;
+extern const struct asn1_oid asn1_pbkdf2_hmac_sha384_oid;
+extern const struct asn1_oid asn1_pbkdf2_hmac_sha512_oid;
+extern const struct asn1_oid asn1_dpp_config_params_oid;
+extern const struct asn1_oid asn1_dpp_asymmetric_key_package_oid;
+
+#endif /* ASN1_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/bignum.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/bignum.c
new file mode 100644
index 0000000..1a87c82
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/bignum.c
@@ -0,0 +1,224 @@
+/*
+ * Big number math
+ * 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 "bignum.h"
+
+#ifdef CONFIG_INTERNAL_LIBTOMMATH
+#include "libtommath.c"
+#else /* CONFIG_INTERNAL_LIBTOMMATH */
+#include <tommath.h>
+#endif /* CONFIG_INTERNAL_LIBTOMMATH */
+
+
+/*
+ * The current version is just a wrapper for LibTomMath library, so
+ * struct bignum is just typecast to mp_int.
+ */
+
+/**
+ * bignum_init - Allocate memory for bignum
+ * Returns: Pointer to allocated bignum or %NULL on failure
+ */
+struct bignum * bignum_init(void)
+{
+	struct bignum *n = os_zalloc(sizeof(mp_int));
+	if (n == NULL)
+		return NULL;
+	if (mp_init((mp_int *) n) != MP_OKAY) {
+		os_free(n);
+		n = NULL;
+	}
+	return n;
+}
+
+
+/**
+ * bignum_deinit - Free bignum
+ * @n: Bignum from bignum_init()
+ */
+void bignum_deinit(struct bignum *n)
+{
+	if (n) {
+		mp_clear((mp_int *) n);
+		os_free(n);
+	}
+}
+
+
+/**
+ * bignum_get_unsigned_bin - Get length of bignum as an unsigned binary buffer
+ * @n: Bignum from bignum_init()
+ * Returns: Length of n if written to a binary buffer
+ */
+size_t bignum_get_unsigned_bin_len(struct bignum *n)
+{
+	return mp_unsigned_bin_size((mp_int *) n);
+}
+
+
+/**
+ * bignum_get_unsigned_bin - Set binary buffer to unsigned bignum
+ * @n: Bignum from bignum_init()
+ * @buf: Buffer for the binary number
+ * @len: Length of the buffer, can be %NULL if buffer is known to be long
+ * enough. Set to used buffer length on success if not %NULL.
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_get_unsigned_bin(const struct bignum *n, u8 *buf, size_t *len)
+{
+	size_t need = mp_unsigned_bin_size((mp_int *) n);
+	if (len && need > *len) {
+		*len = need;
+		return -1;
+	}
+	if (mp_to_unsigned_bin((mp_int *) n, buf) != MP_OKAY) {
+		wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+		return -1;
+	}
+	if (len)
+		*len = need;
+	return 0;
+}
+
+
+/**
+ * bignum_set_unsigned_bin - Set bignum based on unsigned binary buffer
+ * @n: Bignum from bignum_init(); to be set to the given value
+ * @buf: Buffer with unsigned binary value
+ * @len: Length of buf in octets
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_set_unsigned_bin(struct bignum *n, const u8 *buf, size_t len)
+{
+	if (mp_read_unsigned_bin((mp_int *) n, (u8 *) buf, len) != MP_OKAY) {
+		wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+		return -1;
+	}
+	return 0;
+}
+
+
+/**
+ * bignum_cmp - Signed comparison
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_cmp(const struct bignum *a, const struct bignum *b)
+{
+	return mp_cmp((mp_int *) a, (mp_int *) b);
+}
+
+
+/**
+ * bignum_cmp_d - Compare bignum to standard integer
+ * @a: Bignum from bignum_init()
+ * @b: Small integer
+ * Returns: -1 if a < b, 0 if a == b, 1 if a > b
+ */
+int bignum_cmp_d(const struct bignum *a, unsigned long b)
+{
+	return mp_cmp_d((mp_int *) a, b);
+}
+
+
+/**
+ * bignum_add - c = a + b
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * @c: Bignum from bignum_init(); used to store the result of a + b
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_add(const struct bignum *a, const struct bignum *b,
+	       struct bignum *c)
+{
+	if (mp_add((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) {
+		wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+		return -1;
+	}
+	return 0;
+}
+
+
+/**
+ * bignum_sub - c = a - b
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * @c: Bignum from bignum_init(); used to store the result of a - b
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_sub(const struct bignum *a, const struct bignum *b,
+	       struct bignum *c)
+{
+	if (mp_sub((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) {
+		wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+		return -1;
+	}
+	return 0;
+}
+
+
+/**
+ * bignum_mul - c = a * b
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * @c: Bignum from bignum_init(); used to store the result of a * b
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_mul(const struct bignum *a, const struct bignum *b,
+	       struct bignum *c)
+{
+	if (mp_mul((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) {
+		wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+		return -1;
+	}
+	return 0;
+}
+
+
+/**
+ * bignum_mulmod - d = a * b (mod c)
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * @c: Bignum from bignum_init(); modulus
+ * @d: Bignum from bignum_init(); used to store the result of a * b (mod c)
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_mulmod(const struct bignum *a, const struct bignum *b,
+		  const struct bignum *c, struct bignum *d)
+{
+	if (mp_mulmod((mp_int *) a, (mp_int *) b, (mp_int *) c, (mp_int *) d)
+	    != MP_OKAY) {
+		wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+		return -1;
+	}
+	return 0;
+}
+
+
+/**
+ * bignum_exptmod - Modular exponentiation: d = a^b (mod c)
+ * @a: Bignum from bignum_init(); base
+ * @b: Bignum from bignum_init(); exponent
+ * @c: Bignum from bignum_init(); modulus
+ * @d: Bignum from bignum_init(); used to store the result of a^b (mod c)
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_exptmod(const struct bignum *a, const struct bignum *b,
+		   const struct bignum *c, struct bignum *d)
+{
+	if (mp_exptmod((mp_int *) a, (mp_int *) b, (mp_int *) c, (mp_int *) d)
+	    != MP_OKAY) {
+		wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+		return -1;
+	}
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/bignum.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/bignum.h
new file mode 100644
index 0000000..24acdce
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/bignum.h
@@ -0,0 +1,32 @@
+/*
+ * Big number math
+ * 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.
+ */
+
+#ifndef BIGNUM_H
+#define BIGNUM_H
+
+struct bignum;
+
+struct bignum * bignum_init(void);
+void bignum_deinit(struct bignum *n);
+size_t bignum_get_unsigned_bin_len(struct bignum *n);
+int bignum_get_unsigned_bin(const struct bignum *n, u8 *buf, size_t *len);
+int bignum_set_unsigned_bin(struct bignum *n, const u8 *buf, size_t len);
+int bignum_cmp(const struct bignum *a, const struct bignum *b);
+int bignum_cmp_d(const struct bignum *a, unsigned long b);
+int bignum_add(const struct bignum *a, const struct bignum *b,
+	       struct bignum *c);
+int bignum_sub(const struct bignum *a, const struct bignum *b,
+	       struct bignum *c);
+int bignum_mul(const struct bignum *a, const struct bignum *b,
+	       struct bignum *c);
+int bignum_mulmod(const struct bignum *a, const struct bignum *b,
+		  const struct bignum *c, struct bignum *d);
+int bignum_exptmod(const struct bignum *a, const struct bignum *b,
+		   const struct bignum *c, struct bignum *d);
+
+#endif /* BIGNUM_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/libtommath.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/libtommath.c
new file mode 100644
index 0000000..7156744
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/libtommath.c
@@ -0,0 +1,3401 @@
+/*
+ * Minimal code for RSA support from LibTomMath 0.41
+ * http://libtom.org/
+ * http://libtom.org/files/ltm-0.41.tar.bz2
+ * This library was released in public domain by Tom St Denis.
+ *
+ * The combination in this file may not use all of the optimized algorithms
+ * from LibTomMath and may be considerable slower than the LibTomMath with its
+ * default settings. The main purpose of having this version here is to make it
+ * easier to build bignum.c wrapper without having to install and build an
+ * external library.
+ *
+ * If CONFIG_INTERNAL_LIBTOMMATH is defined, bignum.c includes this
+ * libtommath.c file instead of using the external LibTomMath library.
+ */
+
+#ifndef CHAR_BIT
+#define CHAR_BIT 8
+#endif
+
+#define BN_MP_INVMOD_C
+#define BN_S_MP_EXPTMOD_C /* Note: #undef in tommath_superclass.h; this would
+			   * require BN_MP_EXPTMOD_FAST_C instead */
+#define BN_S_MP_MUL_DIGS_C
+#define BN_MP_INVMOD_SLOW_C
+#define BN_S_MP_SQR_C
+#define BN_S_MP_MUL_HIGH_DIGS_C /* Note: #undef in tommath_superclass.h; this
+				 * would require other than mp_reduce */
+
+#ifdef LTM_FAST
+
+/* Use faster div at the cost of about 1 kB */
+#define BN_MP_MUL_D_C
+
+/* Include faster exptmod (Montgomery) at the cost of about 2.5 kB in code */
+#define BN_MP_EXPTMOD_FAST_C
+#define BN_MP_MONTGOMERY_SETUP_C
+#define BN_FAST_MP_MONTGOMERY_REDUCE_C
+#define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C
+#define BN_MP_MUL_2_C
+
+/* Include faster sqr at the cost of about 0.5 kB in code */
+#define BN_FAST_S_MP_SQR_C
+
+/* About 0.25 kB of code, but ~1.7kB of stack space! */
+#define BN_FAST_S_MP_MUL_DIGS_C
+
+#else /* LTM_FAST */
+
+#define BN_MP_DIV_SMALL
+#define BN_MP_INIT_MULTI_C
+#define BN_MP_CLEAR_MULTI_C
+#define BN_MP_ABS_C
+#endif /* LTM_FAST */
+
+/* Current uses do not require support for negative exponent in exptmod, so we
+ * can save about 1.5 kB in leaving out invmod. */
+#define LTM_NO_NEG_EXP
+
+/* from tommath.h */
+
+#ifndef MIN
+   #define MIN(x,y) ((x)<(y)?(x):(y))
+#endif
+
+#ifndef MAX
+   #define MAX(x,y) ((x)>(y)?(x):(y))
+#endif
+
+#define  OPT_CAST(x)
+
+#ifdef __x86_64__
+typedef unsigned long mp_digit;
+typedef unsigned long mp_word __attribute__((mode(TI)));
+
+#define DIGIT_BIT 60
+#define MP_64BIT
+#else
+typedef unsigned long mp_digit;
+typedef u64 mp_word;
+
+#define DIGIT_BIT          28
+#define MP_28BIT
+#endif
+
+
+#define XMALLOC  os_malloc
+#define XFREE    os_free
+#define XREALLOC os_realloc
+
+
+#define MP_MASK          ((((mp_digit)1)<<((mp_digit)DIGIT_BIT))-((mp_digit)1))
+
+#define MP_LT        -1   /* less than */
+#define MP_EQ         0   /* equal to */
+#define MP_GT         1   /* greater than */
+
+#define MP_ZPOS       0   /* positive integer */
+#define MP_NEG        1   /* negative */
+
+#define MP_OKAY       0   /* ok result */
+#define MP_MEM        -2  /* out of mem */
+#define MP_VAL        -3  /* invalid input */
+
+#define MP_YES        1   /* yes response */
+#define MP_NO         0   /* no response */
+
+typedef int           mp_err;
+
+/* define this to use lower memory usage routines (exptmods mostly) */
+#define MP_LOW_MEM
+
+/* default precision */
+#ifndef MP_PREC
+   #ifndef MP_LOW_MEM
+      #define MP_PREC                 32     /* default digits of precision */
+   #else
+      #define MP_PREC                 8      /* default digits of precision */
+   #endif
+#endif
+
+/* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */
+#define MP_WARRAY               (1 << (sizeof(mp_word) * CHAR_BIT - 2 * DIGIT_BIT + 1))
+
+/* the infamous mp_int structure */
+typedef struct  {
+    int used, alloc, sign;
+    mp_digit *dp;
+} mp_int;
+
+
+/* ---> Basic Manipulations <--- */
+#define mp_iszero(a) (((a)->used == 0) ? MP_YES : MP_NO)
+#define mp_iseven(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 0)) ? MP_YES : MP_NO)
+#define mp_isodd(a)  (((a)->used > 0 && (((a)->dp[0] & 1) == 1)) ? MP_YES : MP_NO)
+
+
+/* prototypes for copied functions */
+#define s_mp_mul(a, b, c) s_mp_mul_digs(a, b, c, (a)->used + (b)->used + 1)
+static int s_mp_exptmod(mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode);
+static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs);
+static int s_mp_sqr(mp_int * a, mp_int * b);
+static int s_mp_mul_high_digs(mp_int * a, mp_int * b, mp_int * c, int digs);
+
+#ifdef BN_FAST_S_MP_MUL_DIGS_C
+static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs);
+#endif
+
+#ifdef BN_MP_INIT_MULTI_C
+static int mp_init_multi(mp_int *mp, ...);
+#endif
+#ifdef BN_MP_CLEAR_MULTI_C
+static void mp_clear_multi(mp_int *mp, ...);
+#endif
+static int mp_lshd(mp_int * a, int b);
+static void mp_set(mp_int * a, mp_digit b);
+static void mp_clamp(mp_int * a);
+static void mp_exch(mp_int * a, mp_int * b);
+static void mp_rshd(mp_int * a, int b);
+static void mp_zero(mp_int * a);
+static int mp_mod_2d(mp_int * a, int b, mp_int * c);
+static int mp_div_2d(mp_int * a, int b, mp_int * c, mp_int * d);
+static int mp_init_copy(mp_int * a, mp_int * b);
+static int mp_mul_2d(mp_int * a, int b, mp_int * c);
+#ifndef LTM_NO_NEG_EXP
+static int mp_div_2(mp_int * a, mp_int * b);
+static int mp_invmod(mp_int * a, mp_int * b, mp_int * c);
+static int mp_invmod_slow(mp_int * a, mp_int * b, mp_int * c);
+#endif /* LTM_NO_NEG_EXP */
+static int mp_copy(mp_int * a, mp_int * b);
+static int mp_count_bits(mp_int * a);
+static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d);
+static int mp_mod(mp_int * a, mp_int * b, mp_int * c);
+static int mp_grow(mp_int * a, int size);
+static int mp_cmp_mag(mp_int * a, mp_int * b);
+#ifdef BN_MP_ABS_C
+static int mp_abs(mp_int * a, mp_int * b);
+#endif
+static int mp_sqr(mp_int * a, mp_int * b);
+static int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d);
+static int mp_reduce_2k_setup_l(mp_int *a, mp_int *d);
+static int mp_2expt(mp_int * a, int b);
+static int mp_reduce_setup(mp_int * a, mp_int * b);
+static int mp_reduce(mp_int * x, mp_int * m, mp_int * mu);
+static int mp_init_size(mp_int * a, int size);
+#ifdef BN_MP_EXPTMOD_FAST_C
+static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode);
+#endif /* BN_MP_EXPTMOD_FAST_C */
+#ifdef BN_FAST_S_MP_SQR_C
+static int fast_s_mp_sqr (mp_int * a, mp_int * b);
+#endif /* BN_FAST_S_MP_SQR_C */
+#ifdef BN_MP_MUL_D_C
+static int mp_mul_d (mp_int * a, mp_digit b, mp_int * c);
+#endif /* BN_MP_MUL_D_C */
+
+
+
+/* functions from bn_<func name>.c */
+
+
+/* reverse an array, used for radix code */
+static void bn_reverse (unsigned char *s, int len)
+{
+  int     ix, iy;
+  unsigned char t;
+
+  ix = 0;
+  iy = len - 1;
+  while (ix < iy) {
+    t     = s[ix];
+    s[ix] = s[iy];
+    s[iy] = t;
+    ++ix;
+    --iy;
+  }
+}
+
+
+/* low level addition, based on HAC pp.594, Algorithm 14.7 */
+static int s_mp_add (mp_int * a, mp_int * b, mp_int * c)
+{
+  mp_int *x;
+  int     olduse, res, min, max;
+
+  /* find sizes, we let |a| <= |b| which means we have to sort
+   * them.  "x" will point to the input with the most digits
+   */
+  if (a->used > b->used) {
+    min = b->used;
+    max = a->used;
+    x = a;
+  } else {
+    min = a->used;
+    max = b->used;
+    x = b;
+  }
+
+  /* init result */
+  if (c->alloc < max + 1) {
+    if ((res = mp_grow (c, max + 1)) != MP_OKAY) {
+      return res;
+    }
+  }
+
+  /* get old used digit count and set new one */
+  olduse = c->used;
+  c->used = max + 1;
+
+  {
+    register mp_digit u, *tmpa, *tmpb, *tmpc;
+    register int i;
+
+    /* alias for digit pointers */
+
+    /* first input */
+    tmpa = a->dp;
+
+    /* second input */
+    tmpb = b->dp;
+
+    /* destination */
+    tmpc = c->dp;
+
+    /* zero the carry */
+    u = 0;
+    for (i = 0; i < min; i++) {
+      /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */
+      *tmpc = *tmpa++ + *tmpb++ + u;
+
+      /* U = carry bit of T[i] */
+      u = *tmpc >> ((mp_digit)DIGIT_BIT);
+
+      /* take away carry bit from T[i] */
+      *tmpc++ &= MP_MASK;
+    }
+
+    /* now copy higher words if any, that is in A+B
+     * if A or B has more digits add those in
+     */
+    if (min != max) {
+      for (; i < max; i++) {
+        /* T[i] = X[i] + U */
+        *tmpc = x->dp[i] + u;
+
+        /* U = carry bit of T[i] */
+        u = *tmpc >> ((mp_digit)DIGIT_BIT);
+
+        /* take away carry bit from T[i] */
+        *tmpc++ &= MP_MASK;
+      }
+    }
+
+    /* add carry */
+    *tmpc++ = u;
+
+    /* clear digits above oldused */
+    for (i = c->used; i < olduse; i++) {
+      *tmpc++ = 0;
+    }
+  }
+
+  mp_clamp (c);
+  return MP_OKAY;
+}
+
+
+/* low level subtraction (assumes |a| > |b|), HAC pp.595 Algorithm 14.9 */
+static int s_mp_sub (mp_int * a, mp_int * b, mp_int * c)
+{
+  int     olduse, res, min, max;
+
+  /* find sizes */
+  min = b->used;
+  max = a->used;
+
+  /* init result */
+  if (c->alloc < max) {
+    if ((res = mp_grow (c, max)) != MP_OKAY) {
+      return res;
+    }
+  }
+  olduse = c->used;
+  c->used = max;
+
+  {
+    register mp_digit u, *tmpa, *tmpb, *tmpc;
+    register int i;
+
+    /* alias for digit pointers */
+    tmpa = a->dp;
+    tmpb = b->dp;
+    tmpc = c->dp;
+
+    /* set carry to zero */
+    u = 0;
+    for (i = 0; i < min; i++) {
+      /* T[i] = A[i] - B[i] - U */
+      *tmpc = *tmpa++ - *tmpb++ - u;
+
+      /* U = carry bit of T[i]
+       * Note this saves performing an AND operation since
+       * if a carry does occur it will propagate all the way to the
+       * MSB.  As a result a single shift is enough to get the carry
+       */
+      u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1));
+
+      /* Clear carry from T[i] */
+      *tmpc++ &= MP_MASK;
+    }
+
+    /* now copy higher words if any, e.g. if A has more digits than B  */
+    for (; i < max; i++) {
+      /* T[i] = A[i] - U */
+      *tmpc = *tmpa++ - u;
+
+      /* U = carry bit of T[i] */
+      u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1));
+
+      /* Clear carry from T[i] */
+      *tmpc++ &= MP_MASK;
+    }
+
+    /* clear digits above used (since we may not have grown result above) */
+    for (i = c->used; i < olduse; i++) {
+      *tmpc++ = 0;
+    }
+  }
+
+  mp_clamp (c);
+  return MP_OKAY;
+}
+
+
+/* init a new mp_int */
+static int mp_init (mp_int * a)
+{
+  int i;
+
+  /* allocate memory required and clear it */
+  a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * MP_PREC);
+  if (a->dp == NULL) {
+    return MP_MEM;
+  }
+
+  /* set the digits to zero */
+  for (i = 0; i < MP_PREC; i++) {
+      a->dp[i] = 0;
+  }
+
+  /* set the used to zero, allocated digits to the default precision
+   * and sign to positive */
+  a->used  = 0;
+  a->alloc = MP_PREC;
+  a->sign  = MP_ZPOS;
+
+  return MP_OKAY;
+}
+
+
+/* clear one (frees)  */
+static void mp_clear (mp_int * a)
+{
+  int i;
+
+  /* only do anything if a hasn't been freed previously */
+  if (a->dp != NULL) {
+    /* first zero the digits */
+    for (i = 0; i < a->used; i++) {
+        a->dp[i] = 0;
+    }
+
+    /* free ram */
+    XFREE(a->dp);
+
+    /* reset members to make debugging easier */
+    a->dp    = NULL;
+    a->alloc = a->used = 0;
+    a->sign  = MP_ZPOS;
+  }
+}
+
+
+/* high level addition (handles signs) */
+static int mp_add (mp_int * a, mp_int * b, mp_int * c)
+{
+  int     sa, sb, res;
+
+  /* get sign of both inputs */
+  sa = a->sign;
+  sb = b->sign;
+
+  /* handle two cases, not four */
+  if (sa == sb) {
+    /* both positive or both negative */
+    /* add their magnitudes, copy the sign */
+    c->sign = sa;
+    res = s_mp_add (a, b, c);
+  } else {
+    /* one positive, the other negative */
+    /* subtract the one with the greater magnitude from */
+    /* the one of the lesser magnitude.  The result gets */
+    /* the sign of the one with the greater magnitude. */
+    if (mp_cmp_mag (a, b) == MP_LT) {
+      c->sign = sb;
+      res = s_mp_sub (b, a, c);
+    } else {
+      c->sign = sa;
+      res = s_mp_sub (a, b, c);
+    }
+  }
+  return res;
+}
+
+
+/* high level subtraction (handles signs) */
+static int mp_sub (mp_int * a, mp_int * b, mp_int * c)
+{
+  int     sa, sb, res;
+
+  sa = a->sign;
+  sb = b->sign;
+
+  if (sa != sb) {
+    /* subtract a negative from a positive, OR */
+    /* subtract a positive from a negative. */
+    /* In either case, ADD their magnitudes, */
+    /* and use the sign of the first number. */
+    c->sign = sa;
+    res = s_mp_add (a, b, c);
+  } else {
+    /* subtract a positive from a positive, OR */
+    /* subtract a negative from a negative. */
+    /* First, take the difference between their */
+    /* magnitudes, then... */
+    if (mp_cmp_mag (a, b) != MP_LT) {
+      /* Copy the sign from the first */
+      c->sign = sa;
+      /* The first has a larger or equal magnitude */
+      res = s_mp_sub (a, b, c);
+    } else {
+      /* The result has the *opposite* sign from */
+      /* the first number. */
+      c->sign = (sa == MP_ZPOS) ? MP_NEG : MP_ZPOS;
+      /* The second has a larger magnitude */
+      res = s_mp_sub (b, a, c);
+    }
+  }
+  return res;
+}
+
+
+/* high level multiplication (handles sign) */
+static int mp_mul (mp_int * a, mp_int * b, mp_int * c)
+{
+  int     res, neg;
+  neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG;
+
+  /* use Toom-Cook? */
+#ifdef BN_MP_TOOM_MUL_C
+  if (MIN (a->used, b->used) >= TOOM_MUL_CUTOFF) {
+    res = mp_toom_mul(a, b, c);
+  } else
+#endif
+#ifdef BN_MP_KARATSUBA_MUL_C
+  /* use Karatsuba? */
+  if (MIN (a->used, b->used) >= KARATSUBA_MUL_CUTOFF) {
+    res = mp_karatsuba_mul (a, b, c);
+  } else
+#endif
+  {
+    /* can we use the fast multiplier?
+     *
+     * The fast multiplier can be used if the output will
+     * have less than MP_WARRAY digits and the number of
+     * digits won't affect carry propagation
+     */
+#ifdef BN_FAST_S_MP_MUL_DIGS_C
+    int     digs = a->used + b->used + 1;
+
+    if ((digs < MP_WARRAY) &&
+        MIN(a->used, b->used) <=
+        (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
+      res = fast_s_mp_mul_digs (a, b, c, digs);
+    } else
+#endif
+#ifdef BN_S_MP_MUL_DIGS_C
+      res = s_mp_mul (a, b, c); /* uses s_mp_mul_digs */
+#else
+#error mp_mul could fail
+      res = MP_VAL;
+#endif
+
+  }
+  c->sign = (c->used > 0) ? neg : MP_ZPOS;
+  return res;
+}
+
+
+/* d = a * b (mod c) */
+static int mp_mulmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d)
+{
+  int     res;
+  mp_int  t;
+
+  if ((res = mp_init (&t)) != MP_OKAY) {
+    return res;
+  }
+
+  if ((res = mp_mul (a, b, &t)) != MP_OKAY) {
+    mp_clear (&t);
+    return res;
+  }
+  res = mp_mod (&t, c, d);
+  mp_clear (&t);
+  return res;
+}
+
+
+/* c = a mod b, 0 <= c < b */
+static int mp_mod (mp_int * a, mp_int * b, mp_int * c)
+{
+  mp_int  t;
+  int     res;
+
+  if ((res = mp_init (&t)) != MP_OKAY) {
+    return res;
+  }
+
+  if ((res = mp_div (a, b, NULL, &t)) != MP_OKAY) {
+    mp_clear (&t);
+    return res;
+  }
+
+  if (t.sign != b->sign) {
+    res = mp_add (b, &t, c);
+  } else {
+    res = MP_OKAY;
+    mp_exch (&t, c);
+  }
+
+  mp_clear (&t);
+  return res;
+}
+
+
+/* this is a shell function that calls either the normal or Montgomery
+ * exptmod functions.  Originally the call to the montgomery code was
+ * embedded in the normal function but that wasted a lot of stack space
+ * for nothing (since 99% of the time the Montgomery code would be called)
+ */
+static int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y)
+{
+  int dr;
+
+  /* modulus P must be positive */
+  if (P->sign == MP_NEG) {
+     return MP_VAL;
+  }
+
+  /* if exponent X is negative we have to recurse */
+  if (X->sign == MP_NEG) {
+#ifdef LTM_NO_NEG_EXP
+        return MP_VAL;
+#else /* LTM_NO_NEG_EXP */
+#ifdef BN_MP_INVMOD_C
+     mp_int tmpG, tmpX;
+     int err;
+
+     /* first compute 1/G mod P */
+     if ((err = mp_init(&tmpG)) != MP_OKAY) {
+        return err;
+     }
+     if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) {
+        mp_clear(&tmpG);
+        return err;
+     }
+
+     /* now get |X| */
+     if ((err = mp_init(&tmpX)) != MP_OKAY) {
+        mp_clear(&tmpG);
+        return err;
+     }
+     if ((err = mp_abs(X, &tmpX)) != MP_OKAY) {
+        mp_clear_multi(&tmpG, &tmpX, NULL);
+        return err;
+     }
+
+     /* and now compute (1/G)**|X| instead of G**X [X < 0] */
+     err = mp_exptmod(&tmpG, &tmpX, P, Y);
+     mp_clear_multi(&tmpG, &tmpX, NULL);
+     return err;
+#else
+#error mp_exptmod would always fail
+     /* no invmod */
+     return MP_VAL;
+#endif
+#endif /* LTM_NO_NEG_EXP */
+  }
+
+/* modified diminished radix reduction */
+#if defined(BN_MP_REDUCE_IS_2K_L_C) && defined(BN_MP_REDUCE_2K_L_C) && defined(BN_S_MP_EXPTMOD_C)
+  if (mp_reduce_is_2k_l(P) == MP_YES) {
+     return s_mp_exptmod(G, X, P, Y, 1);
+  }
+#endif
+
+#ifdef BN_MP_DR_IS_MODULUS_C
+  /* is it a DR modulus? */
+  dr = mp_dr_is_modulus(P);
+#else
+  /* default to no */
+  dr = 0;
+#endif
+
+#ifdef BN_MP_REDUCE_IS_2K_C
+  /* if not, is it a unrestricted DR modulus? */
+  if (dr == 0) {
+     dr = mp_reduce_is_2k(P) << 1;
+  }
+#endif
+
+  /* if the modulus is odd or dr != 0 use the montgomery method */
+#ifdef BN_MP_EXPTMOD_FAST_C
+  if (mp_isodd (P) == 1 || dr !=  0) {
+    return mp_exptmod_fast (G, X, P, Y, dr);
+  } else {
+#endif
+#ifdef BN_S_MP_EXPTMOD_C
+    /* otherwise use the generic Barrett reduction technique */
+    return s_mp_exptmod (G, X, P, Y, 0);
+#else
+#error mp_exptmod could fail
+    /* no exptmod for evens */
+    return MP_VAL;
+#endif
+#ifdef BN_MP_EXPTMOD_FAST_C
+  }
+#endif
+  if (dr == 0) {
+    /* avoid compiler warnings about possibly unused variable */
+  }
+}
+
+
+/* compare two ints (signed)*/
+static int mp_cmp (mp_int * a, mp_int * b)
+{
+  /* compare based on sign */
+  if (a->sign != b->sign) {
+     if (a->sign == MP_NEG) {
+        return MP_LT;
+     } else {
+        return MP_GT;
+     }
+  }
+
+  /* compare digits */
+  if (a->sign == MP_NEG) {
+     /* if negative compare opposite direction */
+     return mp_cmp_mag(b, a);
+  } else {
+     return mp_cmp_mag(a, b);
+  }
+}
+
+
+/* compare a digit */
+static int mp_cmp_d(mp_int * a, mp_digit b)
+{
+  /* compare based on sign */
+  if (a->sign == MP_NEG) {
+    return MP_LT;
+  }
+
+  /* compare based on magnitude */
+  if (a->used > 1) {
+    return MP_GT;
+  }
+
+  /* compare the only digit of a to b */
+  if (a->dp[0] > b) {
+    return MP_GT;
+  } else if (a->dp[0] < b) {
+    return MP_LT;
+  } else {
+    return MP_EQ;
+  }
+}
+
+
+#ifndef LTM_NO_NEG_EXP
+/* hac 14.61, pp608 */
+static int mp_invmod (mp_int * a, mp_int * b, mp_int * c)
+{
+  /* b cannot be negative */
+  if (b->sign == MP_NEG || mp_iszero(b) == 1) {
+    return MP_VAL;
+  }
+
+#ifdef BN_FAST_MP_INVMOD_C
+  /* if the modulus is odd we can use a faster routine instead */
+  if (mp_isodd (b) == 1) {
+    return fast_mp_invmod (a, b, c);
+  }
+#endif
+
+#ifdef BN_MP_INVMOD_SLOW_C
+  return mp_invmod_slow(a, b, c);
+#endif
+
+#ifndef BN_FAST_MP_INVMOD_C
+#ifndef BN_MP_INVMOD_SLOW_C
+#error mp_invmod would always fail
+#endif
+#endif
+  return MP_VAL;
+}
+#endif /* LTM_NO_NEG_EXP */
+
+
+/* get the size for an unsigned equivalent */
+static int mp_unsigned_bin_size (mp_int * a)
+{
+  int     size = mp_count_bits (a);
+  return (size / 8 + ((size & 7) != 0 ? 1 : 0));
+}
+
+
+#ifndef LTM_NO_NEG_EXP
+/* hac 14.61, pp608 */
+static int mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c)
+{
+  mp_int  x, y, u, v, A, B, C, D;
+  int     res;
+
+  /* b cannot be negative */
+  if (b->sign == MP_NEG || mp_iszero(b) == 1) {
+    return MP_VAL;
+  }
+
+  /* init temps */
+  if ((res = mp_init_multi(&x, &y, &u, &v,
+                           &A, &B, &C, &D, NULL)) != MP_OKAY) {
+     return res;
+  }
+
+  /* x = a, y = b */
+  if ((res = mp_mod(a, b, &x)) != MP_OKAY) {
+      goto LBL_ERR;
+  }
+  if ((res = mp_copy (b, &y)) != MP_OKAY) {
+    goto LBL_ERR;
+  }
+
+  /* 2. [modified] if x,y are both even then return an error! */
+  if (mp_iseven (&x) == 1 && mp_iseven (&y) == 1) {
+    res = MP_VAL;
+    goto LBL_ERR;
+  }
+
+  /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */
+  if ((res = mp_copy (&x, &u)) != MP_OKAY) {
+    goto LBL_ERR;
+  }
+  if ((res = mp_copy (&y, &v)) != MP_OKAY) {
+    goto LBL_ERR;
+  }
+  mp_set (&A, 1);
+  mp_set (&D, 1);
+
+top:
+  /* 4.  while u is even do */
+  while (mp_iseven (&u) == 1) {
+    /* 4.1 u = u/2 */
+    if ((res = mp_div_2 (&u, &u)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+    /* 4.2 if A or B is odd then */
+    if (mp_isodd (&A) == 1 || mp_isodd (&B) == 1) {
+      /* A = (A+y)/2, B = (B-x)/2 */
+      if ((res = mp_add (&A, &y, &A)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+      if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+    }
+    /* A = A/2, B = B/2 */
+    if ((res = mp_div_2 (&A, &A)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+    if ((res = mp_div_2 (&B, &B)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+  }
+
+  /* 5.  while v is even do */
+  while (mp_iseven (&v) == 1) {
+    /* 5.1 v = v/2 */
+    if ((res = mp_div_2 (&v, &v)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+    /* 5.2 if C or D is odd then */
+    if (mp_isodd (&C) == 1 || mp_isodd (&D) == 1) {
+      /* C = (C+y)/2, D = (D-x)/2 */
+      if ((res = mp_add (&C, &y, &C)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+      if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+    }
+    /* C = C/2, D = D/2 */
+    if ((res = mp_div_2 (&C, &C)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+    if ((res = mp_div_2 (&D, &D)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+  }
+
+  /* 6.  if u >= v then */
+  if (mp_cmp (&u, &v) != MP_LT) {
+    /* u = u - v, A = A - C, B = B - D */
+    if ((res = mp_sub (&u, &v, &u)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+
+    if ((res = mp_sub (&A, &C, &A)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+
+    if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+  } else {
+    /* v - v - u, C = C - A, D = D - B */
+    if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+
+    if ((res = mp_sub (&C, &A, &C)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+
+    if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+  }
+
+  /* if not zero goto step 4 */
+  if (mp_iszero (&u) == 0)
+    goto top;
+
+  /* now a = C, b = D, gcd == g*v */
+
+  /* if v != 1 then there is no inverse */
+  if (mp_cmp_d (&v, 1) != MP_EQ) {
+    res = MP_VAL;
+    goto LBL_ERR;
+  }
+
+  /* if its too low */
+  while (mp_cmp_d(&C, 0) == MP_LT) {
+      if ((res = mp_add(&C, b, &C)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+  }
+
+  /* too big */
+  while (mp_cmp_mag(&C, b) != MP_LT) {
+      if ((res = mp_sub(&C, b, &C)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+  }
+
+  /* C is now the inverse */
+  mp_exch (&C, c);
+  res = MP_OKAY;
+LBL_ERR:mp_clear_multi (&x, &y, &u, &v, &A, &B, &C, &D, NULL);
+  return res;
+}
+#endif /* LTM_NO_NEG_EXP */
+
+
+/* compare maginitude of two ints (unsigned) */
+static int mp_cmp_mag (mp_int * a, mp_int * b)
+{
+  int     n;
+  mp_digit *tmpa, *tmpb;
+
+  /* compare based on # of non-zero digits */
+  if (a->used > b->used) {
+    return MP_GT;
+  }
+
+  if (a->used < b->used) {
+    return MP_LT;
+  }
+
+  /* alias for a */
+  tmpa = a->dp + (a->used - 1);
+
+  /* alias for b */
+  tmpb = b->dp + (a->used - 1);
+
+  /* compare based on digits  */
+  for (n = 0; n < a->used; ++n, --tmpa, --tmpb) {
+    if (*tmpa > *tmpb) {
+      return MP_GT;
+    }
+
+    if (*tmpa < *tmpb) {
+      return MP_LT;
+    }
+  }
+  return MP_EQ;
+}
+
+
+/* reads a unsigned char array, assumes the msb is stored first [big endian] */
+static int mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c)
+{
+  int     res;
+
+  /* make sure there are at least two digits */
+  if (a->alloc < 2) {
+     if ((res = mp_grow(a, 2)) != MP_OKAY) {
+        return res;
+     }
+  }
+
+  /* zero the int */
+  mp_zero (a);
+
+  /* read the bytes in */
+  while (c-- > 0) {
+    if ((res = mp_mul_2d (a, 8, a)) != MP_OKAY) {
+      return res;
+    }
+
+#ifndef MP_8BIT
+      a->dp[0] |= *b++;
+      a->used += 1;
+#else
+      a->dp[0] = (*b & MP_MASK);
+      a->dp[1] |= ((*b++ >> 7U) & 1);
+      a->used += 2;
+#endif
+  }
+  mp_clamp (a);
+  return MP_OKAY;
+}
+
+
+/* store in unsigned [big endian] format */
+static int mp_to_unsigned_bin (mp_int * a, unsigned char *b)
+{
+  int     x, res;
+  mp_int  t;
+
+  if ((res = mp_init_copy (&t, a)) != MP_OKAY) {
+    return res;
+  }
+
+  x = 0;
+  while (mp_iszero (&t) == 0) {
+#ifndef MP_8BIT
+      b[x++] = (unsigned char) (t.dp[0] & 255);
+#else
+      b[x++] = (unsigned char) (t.dp[0] | ((t.dp[1] & 0x01) << 7));
+#endif
+    if ((res = mp_div_2d (&t, 8, &t, NULL)) != MP_OKAY) {
+      mp_clear (&t);
+      return res;
+    }
+  }
+  bn_reverse (b, x);
+  mp_clear (&t);
+  return MP_OKAY;
+}
+
+
+/* shift right by a certain bit count (store quotient in c, optional remainder in d) */
+static int mp_div_2d (mp_int * a, int b, mp_int * c, mp_int * d)
+{
+  mp_digit D, r, rr;
+  int     x, res;
+  mp_int  t;
+
+
+  /* if the shift count is <= 0 then we do no work */
+  if (b <= 0) {
+    res = mp_copy (a, c);
+    if (d != NULL) {
+      mp_zero (d);
+    }
+    return res;
+  }
+
+  if ((res = mp_init (&t)) != MP_OKAY) {
+    return res;
+  }
+
+  /* get the remainder */
+  if (d != NULL) {
+    if ((res = mp_mod_2d (a, b, &t)) != MP_OKAY) {
+      mp_clear (&t);
+      return res;
+    }
+  }
+
+  /* copy */
+  if ((res = mp_copy (a, c)) != MP_OKAY) {
+    mp_clear (&t);
+    return res;
+  }
+
+  /* shift by as many digits in the bit count */
+  if (b >= (int)DIGIT_BIT) {
+    mp_rshd (c, b / DIGIT_BIT);
+  }
+
+  /* shift any bit count < DIGIT_BIT */
+  D = (mp_digit) (b % DIGIT_BIT);
+  if (D != 0) {
+    register mp_digit *tmpc, mask, shift;
+
+    /* mask */
+    mask = (((mp_digit)1) << D) - 1;
+
+    /* shift for lsb */
+    shift = DIGIT_BIT - D;
+
+    /* alias */
+    tmpc = c->dp + (c->used - 1);
+
+    /* carry */
+    r = 0;
+    for (x = c->used - 1; x >= 0; x--) {
+      /* get the lower  bits of this word in a temp */
+      rr = *tmpc & mask;
+
+      /* shift the current word and mix in the carry bits from the previous word */
+      *tmpc = (*tmpc >> D) | (r << shift);
+      --tmpc;
+
+      /* set the carry to the carry bits of the current word found above */
+      r = rr;
+    }
+  }
+  mp_clamp (c);
+  if (d != NULL) {
+    mp_exch (&t, d);
+  }
+  mp_clear (&t);
+  return MP_OKAY;
+}
+
+
+static int mp_init_copy (mp_int * a, mp_int * b)
+{
+  int     res;
+
+  if ((res = mp_init (a)) != MP_OKAY) {
+    return res;
+  }
+  return mp_copy (b, a);
+}
+
+
+/* set to zero */
+static void mp_zero (mp_int * a)
+{
+  int       n;
+  mp_digit *tmp;
+
+  a->sign = MP_ZPOS;
+  a->used = 0;
+
+  tmp = a->dp;
+  for (n = 0; n < a->alloc; n++) {
+     *tmp++ = 0;
+  }
+}
+
+
+/* copy, b = a */
+static int mp_copy (mp_int * a, mp_int * b)
+{
+  int     res, n;
+
+  /* if dst == src do nothing */
+  if (a == b) {
+    return MP_OKAY;
+  }
+
+  /* grow dest */
+  if (b->alloc < a->used) {
+     if ((res = mp_grow (b, a->used)) != MP_OKAY) {
+        return res;
+     }
+  }
+
+  /* zero b and copy the parameters over */
+  {
+    register mp_digit *tmpa, *tmpb;
+
+    /* pointer aliases */
+
+    /* source */
+    tmpa = a->dp;
+
+    /* destination */
+    tmpb = b->dp;
+
+    /* copy all the digits */
+    for (n = 0; n < a->used; n++) {
+      *tmpb++ = *tmpa++;
+    }
+
+    /* clear high digits */
+    for (; n < b->used; n++) {
+      *tmpb++ = 0;
+    }
+  }
+
+  /* copy used count and sign */
+  b->used = a->used;
+  b->sign = a->sign;
+  return MP_OKAY;
+}
+
+
+/* shift right a certain amount of digits */
+static void mp_rshd (mp_int * a, int b)
+{
+  int     x;
+
+  /* if b <= 0 then ignore it */
+  if (b <= 0) {
+    return;
+  }
+
+  /* if b > used then simply zero it and return */
+  if (a->used <= b) {
+    mp_zero (a);
+    return;
+  }
+
+  {
+    register mp_digit *bottom, *top;
+
+    /* shift the digits down */
+
+    /* bottom */
+    bottom = a->dp;
+
+    /* top [offset into digits] */
+    top = a->dp + b;
+
+    /* this is implemented as a sliding window where
+     * the window is b-digits long and digits from
+     * the top of the window are copied to the bottom
+     *
+     * e.g.
+
+     b-2 | b-1 | b0 | b1 | b2 | ... | bb |   ---->
+                 /\                   |      ---->
+                  \-------------------/      ---->
+     */
+    for (x = 0; x < (a->used - b); x++) {
+      *bottom++ = *top++;
+    }
+
+    /* zero the top digits */
+    for (; x < a->used; x++) {
+      *bottom++ = 0;
+    }
+  }
+
+  /* remove excess digits */
+  a->used -= b;
+}
+
+
+/* swap the elements of two integers, for cases where you can't simply swap the
+ * mp_int pointers around
+ */
+static void mp_exch (mp_int * a, mp_int * b)
+{
+  mp_int  t;
+
+  t  = *a;
+  *a = *b;
+  *b = t;
+}
+
+
+/* trim unused digits
+ *
+ * This is used to ensure that leading zero digits are
+ * trimed and the leading "used" digit will be non-zero
+ * Typically very fast.  Also fixes the sign if there
+ * are no more leading digits
+ */
+static void mp_clamp (mp_int * a)
+{
+  /* decrease used while the most significant digit is
+   * zero.
+   */
+  while (a->used > 0 && a->dp[a->used - 1] == 0) {
+    --(a->used);
+  }
+
+  /* reset the sign flag if used == 0 */
+  if (a->used == 0) {
+    a->sign = MP_ZPOS;
+  }
+}
+
+
+/* grow as required */
+static int mp_grow (mp_int * a, int size)
+{
+  int     i;
+  mp_digit *tmp;
+
+  /* if the alloc size is smaller alloc more ram */
+  if (a->alloc < size) {
+    /* ensure there are always at least MP_PREC digits extra on top */
+    size += (MP_PREC * 2) - (size % MP_PREC);
+
+    /* reallocate the array a->dp
+     *
+     * We store the return in a temporary variable
+     * in case the operation failed we don't want
+     * to overwrite the dp member of a.
+     */
+    tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * size);
+    if (tmp == NULL) {
+      /* reallocation failed but "a" is still valid [can be freed] */
+      return MP_MEM;
+    }
+
+    /* reallocation succeeded so set a->dp */
+    a->dp = tmp;
+
+    /* zero excess digits */
+    i        = a->alloc;
+    a->alloc = size;
+    for (; i < a->alloc; i++) {
+      a->dp[i] = 0;
+    }
+  }
+  return MP_OKAY;
+}
+
+
+#ifdef BN_MP_ABS_C
+/* b = |a|
+ *
+ * Simple function copies the input and fixes the sign to positive
+ */
+static int mp_abs (mp_int * a, mp_int * b)
+{
+  int     res;
+
+  /* copy a to b */
+  if (a != b) {
+     if ((res = mp_copy (a, b)) != MP_OKAY) {
+       return res;
+     }
+  }
+
+  /* force the sign of b to positive */
+  b->sign = MP_ZPOS;
+
+  return MP_OKAY;
+}
+#endif
+
+
+/* set to a digit */
+static void mp_set (mp_int * a, mp_digit b)
+{
+  mp_zero (a);
+  a->dp[0] = b & MP_MASK;
+  a->used  = (a->dp[0] != 0) ? 1 : 0;
+}
+
+
+#ifndef LTM_NO_NEG_EXP
+/* b = a/2 */
+static int mp_div_2(mp_int * a, mp_int * b)
+{
+  int     x, res, oldused;
+
+  /* copy */
+  if (b->alloc < a->used) {
+    if ((res = mp_grow (b, a->used)) != MP_OKAY) {
+      return res;
+    }
+  }
+
+  oldused = b->used;
+  b->used = a->used;
+  {
+    register mp_digit r, rr, *tmpa, *tmpb;
+
+    /* source alias */
+    tmpa = a->dp + b->used - 1;
+
+    /* dest alias */
+    tmpb = b->dp + b->used - 1;
+
+    /* carry */
+    r = 0;
+    for (x = b->used - 1; x >= 0; x--) {
+      /* get the carry for the next iteration */
+      rr = *tmpa & 1;
+
+      /* shift the current digit, add in carry and store */
+      *tmpb-- = (*tmpa-- >> 1) | (r << (DIGIT_BIT - 1));
+
+      /* forward carry to next iteration */
+      r = rr;
+    }
+
+    /* zero excess digits */
+    tmpb = b->dp + b->used;
+    for (x = b->used; x < oldused; x++) {
+      *tmpb++ = 0;
+    }
+  }
+  b->sign = a->sign;
+  mp_clamp (b);
+  return MP_OKAY;
+}
+#endif /* LTM_NO_NEG_EXP */
+
+
+/* shift left by a certain bit count */
+static int mp_mul_2d (mp_int * a, int b, mp_int * c)
+{
+  mp_digit d;
+  int      res;
+
+  /* copy */
+  if (a != c) {
+     if ((res = mp_copy (a, c)) != MP_OKAY) {
+       return res;
+     }
+  }
+
+  if (c->alloc < (int)(c->used + b/DIGIT_BIT + 1)) {
+     if ((res = mp_grow (c, c->used + b / DIGIT_BIT + 1)) != MP_OKAY) {
+       return res;
+     }
+  }
+
+  /* shift by as many digits in the bit count */
+  if (b >= (int)DIGIT_BIT) {
+    if ((res = mp_lshd (c, b / DIGIT_BIT)) != MP_OKAY) {
+      return res;
+    }
+  }
+
+  /* shift any bit count < DIGIT_BIT */
+  d = (mp_digit) (b % DIGIT_BIT);
+  if (d != 0) {
+    register mp_digit *tmpc, shift, mask, r, rr;
+    register int x;
+
+    /* bitmask for carries */
+    mask = (((mp_digit)1) << d) - 1;
+
+    /* shift for msbs */
+    shift = DIGIT_BIT - d;
+
+    /* alias */
+    tmpc = c->dp;
+
+    /* carry */
+    r    = 0;
+    for (x = 0; x < c->used; x++) {
+      /* get the higher bits of the current word */
+      rr = (*tmpc >> shift) & mask;
+
+      /* shift the current word and OR in the carry */
+      *tmpc = ((*tmpc << d) | r) & MP_MASK;
+      ++tmpc;
+
+      /* set the carry to the carry bits of the current word */
+      r = rr;
+    }
+
+    /* set final carry */
+    if (r != 0) {
+       c->dp[(c->used)++] = r;
+    }
+  }
+  mp_clamp (c);
+  return MP_OKAY;
+}
+
+
+#ifdef BN_MP_INIT_MULTI_C
+static int mp_init_multi(mp_int *mp, ...)
+{
+    mp_err res = MP_OKAY;      /* Assume ok until proven otherwise */
+    int n = 0;                 /* Number of ok inits */
+    mp_int* cur_arg = mp;
+    va_list args;
+
+    va_start(args, mp);        /* init args to next argument from caller */
+    while (cur_arg != NULL) {
+        if (mp_init(cur_arg) != MP_OKAY) {
+            /* Oops - error! Back-track and mp_clear what we already
+               succeeded in init-ing, then return error.
+            */
+            va_list clean_args;
+
+            /* end the current list */
+            va_end(args);
+
+            /* now start cleaning up */
+            cur_arg = mp;
+            va_start(clean_args, mp);
+            while (n--) {
+                mp_clear(cur_arg);
+                cur_arg = va_arg(clean_args, mp_int*);
+            }
+            va_end(clean_args);
+            return MP_MEM;
+        }
+        n++;
+        cur_arg = va_arg(args, mp_int*);
+    }
+    va_end(args);
+    return res;                /* Assumed ok, if error flagged above. */
+}
+#endif
+
+
+#ifdef BN_MP_CLEAR_MULTI_C
+static void mp_clear_multi(mp_int *mp, ...)
+{
+    mp_int* next_mp = mp;
+    va_list args;
+    va_start(args, mp);
+    while (next_mp != NULL) {
+        mp_clear(next_mp);
+        next_mp = va_arg(args, mp_int*);
+    }
+    va_end(args);
+}
+#endif
+
+
+/* shift left a certain amount of digits */
+static int mp_lshd (mp_int * a, int b)
+{
+  int     x, res;
+
+  /* if its less than zero return */
+  if (b <= 0) {
+    return MP_OKAY;
+  }
+
+  /* grow to fit the new digits */
+  if (a->alloc < a->used + b) {
+     if ((res = mp_grow (a, a->used + b)) != MP_OKAY) {
+       return res;
+     }
+  }
+
+  {
+    register mp_digit *top, *bottom;
+
+    /* increment the used by the shift amount then copy upwards */
+    a->used += b;
+
+    /* top */
+    top = a->dp + a->used - 1;
+
+    /* base */
+    bottom = a->dp + a->used - 1 - b;
+
+    /* much like mp_rshd this is implemented using a sliding window
+     * except the window goes the otherway around.  Copying from
+     * the bottom to the top.  see bn_mp_rshd.c for more info.
+     */
+    for (x = a->used - 1; x >= b; x--) {
+      *top-- = *bottom--;
+    }
+
+    /* zero the lower digits */
+    top = a->dp;
+    for (x = 0; x < b; x++) {
+      *top++ = 0;
+    }
+  }
+  return MP_OKAY;
+}
+
+
+/* returns the number of bits in an int */
+static int mp_count_bits (mp_int * a)
+{
+  int     r;
+  mp_digit q;
+
+  /* shortcut */
+  if (a->used == 0) {
+    return 0;
+  }
+
+  /* get number of digits and add that */
+  r = (a->used - 1) * DIGIT_BIT;
+
+  /* take the last digit and count the bits in it */
+  q = a->dp[a->used - 1];
+  while (q > ((mp_digit) 0)) {
+    ++r;
+    q >>= ((mp_digit) 1);
+  }
+  return r;
+}
+
+
+/* calc a value mod 2**b */
+static int mp_mod_2d (mp_int * a, int b, mp_int * c)
+{
+  int     x, res;
+
+  /* if b is <= 0 then zero the int */
+  if (b <= 0) {
+    mp_zero (c);
+    return MP_OKAY;
+  }
+
+  /* if the modulus is larger than the value than return */
+  if (b >= (int) (a->used * DIGIT_BIT)) {
+    res = mp_copy (a, c);
+    return res;
+  }
+
+  /* copy */
+  if ((res = mp_copy (a, c)) != MP_OKAY) {
+    return res;
+  }
+
+  /* zero digits above the last digit of the modulus */
+  for (x = (b / DIGIT_BIT) + ((b % DIGIT_BIT) == 0 ? 0 : 1); x < c->used; x++) {
+    c->dp[x] = 0;
+  }
+  /* clear the digit that is not completely outside/inside the modulus */
+  c->dp[b / DIGIT_BIT] &=
+    (mp_digit) ((((mp_digit) 1) << (((mp_digit) b) % DIGIT_BIT)) - ((mp_digit) 1));
+  mp_clamp (c);
+  return MP_OKAY;
+}
+
+
+#ifdef BN_MP_DIV_SMALL
+
+/* slower bit-bang division... also smaller */
+static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d)
+{
+   mp_int ta, tb, tq, q;
+   int    res, n, n2;
+
+  /* is divisor zero ? */
+  if (mp_iszero (b) == 1) {
+    return MP_VAL;
+  }
+
+  /* if a < b then q=0, r = a */
+  if (mp_cmp_mag (a, b) == MP_LT) {
+    if (d != NULL) {
+      res = mp_copy (a, d);
+    } else {
+      res = MP_OKAY;
+    }
+    if (c != NULL) {
+      mp_zero (c);
+    }
+    return res;
+  }
+
+  /* init our temps */
+  if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL)) != MP_OKAY) {
+     return res;
+  }
+
+
+  mp_set(&tq, 1);
+  n = mp_count_bits(a) - mp_count_bits(b);
+  if (((res = mp_abs(a, &ta)) != MP_OKAY) ||
+      ((res = mp_abs(b, &tb)) != MP_OKAY) ||
+      ((res = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) ||
+      ((res = mp_mul_2d(&tq, n, &tq)) != MP_OKAY)) {
+      goto LBL_ERR;
+  }
+
+  while (n-- >= 0) {
+     if (mp_cmp(&tb, &ta) != MP_GT) {
+        if (((res = mp_sub(&ta, &tb, &ta)) != MP_OKAY) ||
+            ((res = mp_add(&q, &tq, &q)) != MP_OKAY)) {
+           goto LBL_ERR;
+        }
+     }
+     if (((res = mp_div_2d(&tb, 1, &tb, NULL)) != MP_OKAY) ||
+         ((res = mp_div_2d(&tq, 1, &tq, NULL)) != MP_OKAY)) {
+           goto LBL_ERR;
+     }
+  }
+
+  /* now q == quotient and ta == remainder */
+  n  = a->sign;
+  n2 = (a->sign == b->sign ? MP_ZPOS : MP_NEG);
+  if (c != NULL) {
+     mp_exch(c, &q);
+     c->sign  = (mp_iszero(c) == MP_YES) ? MP_ZPOS : n2;
+  }
+  if (d != NULL) {
+     mp_exch(d, &ta);
+     d->sign = (mp_iszero(d) == MP_YES) ? MP_ZPOS : n;
+  }
+LBL_ERR:
+   mp_clear_multi(&ta, &tb, &tq, &q, NULL);
+   return res;
+}
+
+#else
+
+/* integer signed division.
+ * c*b + d == a [e.g. a/b, c=quotient, d=remainder]
+ * HAC pp.598 Algorithm 14.20
+ *
+ * Note that the description in HAC is horribly
+ * incomplete.  For example, it doesn't consider
+ * the case where digits are removed from 'x' in
+ * the inner loop.  It also doesn't consider the
+ * case that y has fewer than three digits, etc..
+ *
+ * The overall algorithm is as described as
+ * 14.20 from HAC but fixed to treat these cases.
+*/
+static int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d)
+{
+  mp_int  q, x, y, t1, t2;
+  int     res, n, t, i, norm, neg;
+
+  /* is divisor zero ? */
+  if (mp_iszero (b) == 1) {
+    return MP_VAL;
+  }
+
+  /* if a < b then q=0, r = a */
+  if (mp_cmp_mag (a, b) == MP_LT) {
+    if (d != NULL) {
+      res = mp_copy (a, d);
+    } else {
+      res = MP_OKAY;
+    }
+    if (c != NULL) {
+      mp_zero (c);
+    }
+    return res;
+  }
+
+  if ((res = mp_init_size (&q, a->used + 2)) != MP_OKAY) {
+    return res;
+  }
+  q.used = a->used + 2;
+
+  if ((res = mp_init (&t1)) != MP_OKAY) {
+    goto LBL_Q;
+  }
+
+  if ((res = mp_init (&t2)) != MP_OKAY) {
+    goto LBL_T1;
+  }
+
+  if ((res = mp_init_copy (&x, a)) != MP_OKAY) {
+    goto LBL_T2;
+  }
+
+  if ((res = mp_init_copy (&y, b)) != MP_OKAY) {
+    goto LBL_X;
+  }
+
+  /* fix the sign */
+  neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG;
+  x.sign = y.sign = MP_ZPOS;
+
+  /* normalize both x and y, ensure that y >= b/2, [b == 2**DIGIT_BIT] */
+  norm = mp_count_bits(&y) % DIGIT_BIT;
+  if (norm < (int)(DIGIT_BIT-1)) {
+     norm = (DIGIT_BIT-1) - norm;
+     if ((res = mp_mul_2d (&x, norm, &x)) != MP_OKAY) {
+       goto LBL_Y;
+     }
+     if ((res = mp_mul_2d (&y, norm, &y)) != MP_OKAY) {
+       goto LBL_Y;
+     }
+  } else {
+     norm = 0;
+  }
+
+  /* note hac does 0 based, so if used==5 then its 0,1,2,3,4, e.g. use 4 */
+  n = x.used - 1;
+  t = y.used - 1;
+
+  /* while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } */
+  if ((res = mp_lshd (&y, n - t)) != MP_OKAY) { /* y = y*b**{n-t} */
+    goto LBL_Y;
+  }
+
+  while (mp_cmp (&x, &y) != MP_LT) {
+    ++(q.dp[n - t]);
+    if ((res = mp_sub (&x, &y, &x)) != MP_OKAY) {
+      goto LBL_Y;
+    }
+  }
+
+  /* reset y by shifting it back down */
+  mp_rshd (&y, n - t);
+
+  /* step 3. for i from n down to (t + 1) */
+  for (i = n; i >= (t + 1); i--) {
+    if (i > x.used) {
+      continue;
+    }
+
+    /* step 3.1 if xi == yt then set q{i-t-1} to b-1,
+     * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */
+    if (x.dp[i] == y.dp[t]) {
+      q.dp[i - t - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1);
+    } else {
+      mp_word tmp;
+      tmp = ((mp_word) x.dp[i]) << ((mp_word) DIGIT_BIT);
+      tmp |= ((mp_word) x.dp[i - 1]);
+      tmp /= ((mp_word) y.dp[t]);
+      if (tmp > (mp_word) MP_MASK)
+        tmp = MP_MASK;
+      q.dp[i - t - 1] = (mp_digit) (tmp & (mp_word) (MP_MASK));
+    }
+
+    /* while (q{i-t-1} * (yt * b + y{t-1})) >
+             xi * b**2 + xi-1 * b + xi-2
+
+       do q{i-t-1} -= 1;
+    */
+    q.dp[i - t - 1] = (q.dp[i - t - 1] + 1) & MP_MASK;
+    do {
+      q.dp[i - t - 1] = (q.dp[i - t - 1] - 1) & MP_MASK;
+
+      /* find left hand */
+      mp_zero (&t1);
+      t1.dp[0] = (t - 1 < 0) ? 0 : y.dp[t - 1];
+      t1.dp[1] = y.dp[t];
+      t1.used = 2;
+      if ((res = mp_mul_d (&t1, q.dp[i - t - 1], &t1)) != MP_OKAY) {
+        goto LBL_Y;
+      }
+
+      /* find right hand */
+      t2.dp[0] = (i - 2 < 0) ? 0 : x.dp[i - 2];
+      t2.dp[1] = (i - 1 < 0) ? 0 : x.dp[i - 1];
+      t2.dp[2] = x.dp[i];
+      t2.used = 3;
+    } while (mp_cmp_mag(&t1, &t2) == MP_GT);
+
+    /* step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} */
+    if ((res = mp_mul_d (&y, q.dp[i - t - 1], &t1)) != MP_OKAY) {
+      goto LBL_Y;
+    }
+
+    if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) {
+      goto LBL_Y;
+    }
+
+    if ((res = mp_sub (&x, &t1, &x)) != MP_OKAY) {
+      goto LBL_Y;
+    }
+
+    /* if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } */
+    if (x.sign == MP_NEG) {
+      if ((res = mp_copy (&y, &t1)) != MP_OKAY) {
+        goto LBL_Y;
+      }
+      if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) {
+        goto LBL_Y;
+      }
+      if ((res = mp_add (&x, &t1, &x)) != MP_OKAY) {
+        goto LBL_Y;
+      }
+
+      q.dp[i - t - 1] = (q.dp[i - t - 1] - 1UL) & MP_MASK;
+    }
+  }
+
+  /* now q is the quotient and x is the remainder
+   * [which we have to normalize]
+   */
+
+  /* get sign before writing to c */
+  x.sign = x.used == 0 ? MP_ZPOS : a->sign;
+
+  if (c != NULL) {
+    mp_clamp (&q);
+    mp_exch (&q, c);
+    c->sign = neg;
+  }
+
+  if (d != NULL) {
+    mp_div_2d (&x, norm, &x, NULL);
+    mp_exch (&x, d);
+  }
+
+  res = MP_OKAY;
+
+LBL_Y:mp_clear (&y);
+LBL_X:mp_clear (&x);
+LBL_T2:mp_clear (&t2);
+LBL_T1:mp_clear (&t1);
+LBL_Q:mp_clear (&q);
+  return res;
+}
+
+#endif
+
+
+#ifdef MP_LOW_MEM
+   #define TAB_SIZE 32
+#else
+   #define TAB_SIZE 256
+#endif
+
+static int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode)
+{
+  mp_int  M[TAB_SIZE], res, mu;
+  mp_digit buf;
+  int     err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize;
+  int (*redux)(mp_int*,mp_int*,mp_int*);
+
+  /* find window size */
+  x = mp_count_bits (X);
+  if (x <= 7) {
+    winsize = 2;
+  } else if (x <= 36) {
+    winsize = 3;
+  } else if (x <= 140) {
+    winsize = 4;
+  } else if (x <= 450) {
+    winsize = 5;
+  } else if (x <= 1303) {
+    winsize = 6;
+  } else if (x <= 3529) {
+    winsize = 7;
+  } else {
+    winsize = 8;
+  }
+
+#ifdef MP_LOW_MEM
+    if (winsize > 5) {
+       winsize = 5;
+    }
+#endif
+
+  /* init M array */
+  /* init first cell */
+  if ((err = mp_init(&M[1])) != MP_OKAY) {
+     return err;
+  }
+
+  /* now init the second half of the array */
+  for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
+    if ((err = mp_init(&M[x])) != MP_OKAY) {
+      for (y = 1<<(winsize-1); y < x; y++) {
+        mp_clear (&M[y]);
+      }
+      mp_clear(&M[1]);
+      return err;
+    }
+  }
+
+  /* create mu, used for Barrett reduction */
+  if ((err = mp_init (&mu)) != MP_OKAY) {
+    goto LBL_M;
+  }
+
+  if (redmode == 0) {
+     if ((err = mp_reduce_setup (&mu, P)) != MP_OKAY) {
+        goto LBL_MU;
+     }
+     redux = mp_reduce;
+  } else {
+     if ((err = mp_reduce_2k_setup_l (P, &mu)) != MP_OKAY) {
+        goto LBL_MU;
+     }
+     redux = mp_reduce_2k_l;
+  }
+
+  /* create M table
+   *
+   * The M table contains powers of the base,
+   * e.g. M[x] = G**x mod P
+   *
+   * The first half of the table is not
+   * computed though accept for M[0] and M[1]
+   */
+  if ((err = mp_mod (G, P, &M[1])) != MP_OKAY) {
+    goto LBL_MU;
+  }
+
+  /* compute the value at M[1<<(winsize-1)] by squaring
+   * M[1] (winsize-1) times
+   */
+  if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) {
+    goto LBL_MU;
+  }
+
+  for (x = 0; x < (winsize - 1); x++) {
+    /* square it */
+    if ((err = mp_sqr (&M[1 << (winsize - 1)],
+                       &M[1 << (winsize - 1)])) != MP_OKAY) {
+      goto LBL_MU;
+    }
+
+    /* reduce modulo P */
+    if ((err = redux (&M[1 << (winsize - 1)], P, &mu)) != MP_OKAY) {
+      goto LBL_MU;
+    }
+  }
+
+  /* create upper table, that is M[x] = M[x-1] * M[1] (mod P)
+   * for x = (2**(winsize - 1) + 1) to (2**winsize - 1)
+   */
+  for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) {
+    if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) {
+      goto LBL_MU;
+    }
+    if ((err = redux (&M[x], P, &mu)) != MP_OKAY) {
+      goto LBL_MU;
+    }
+  }
+
+  /* setup result */
+  if ((err = mp_init (&res)) != MP_OKAY) {
+    goto LBL_MU;
+  }
+  mp_set (&res, 1);
+
+  /* set initial mode and bit cnt */
+  mode   = 0;
+  bitcnt = 1;
+  buf    = 0;
+  digidx = X->used - 1;
+  bitcpy = 0;
+  bitbuf = 0;
+
+  for (;;) {
+    /* grab next digit as required */
+    if (--bitcnt == 0) {
+      /* if digidx == -1 we are out of digits */
+      if (digidx == -1) {
+        break;
+      }
+      /* read next digit and reset the bitcnt */
+      buf    = X->dp[digidx--];
+      bitcnt = (int) DIGIT_BIT;
+    }
+
+    /* grab the next msb from the exponent */
+    y     = (buf >> (mp_digit)(DIGIT_BIT - 1)) & 1;
+    buf <<= (mp_digit)1;
+
+    /* if the bit is zero and mode == 0 then we ignore it
+     * These represent the leading zero bits before the first 1 bit
+     * in the exponent.  Technically this opt is not required but it
+     * does lower the # of trivial squaring/reductions used
+     */
+    if (mode == 0 && y == 0) {
+      continue;
+    }
+
+    /* if the bit is zero and mode == 1 then we square */
+    if (mode == 1 && y == 0) {
+      if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+      if ((err = redux (&res, P, &mu)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+      continue;
+    }
+
+    /* else we add it to the window */
+    bitbuf |= (y << (winsize - ++bitcpy));
+    mode    = 2;
+
+    if (bitcpy == winsize) {
+      /* ok window is filled so square as required and multiply  */
+      /* square first */
+      for (x = 0; x < winsize; x++) {
+        if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
+          goto LBL_RES;
+        }
+        if ((err = redux (&res, P, &mu)) != MP_OKAY) {
+          goto LBL_RES;
+        }
+      }
+
+      /* then multiply */
+      if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+      if ((err = redux (&res, P, &mu)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+
+      /* empty window and reset */
+      bitcpy = 0;
+      bitbuf = 0;
+      mode   = 1;
+    }
+  }
+
+  /* if bits remain then square/multiply */
+  if (mode == 2 && bitcpy > 0) {
+    /* square then multiply if the bit is set */
+    for (x = 0; x < bitcpy; x++) {
+      if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+      if ((err = redux (&res, P, &mu)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+
+      bitbuf <<= 1;
+      if ((bitbuf & (1 << winsize)) != 0) {
+        /* then multiply */
+        if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) {
+          goto LBL_RES;
+        }
+        if ((err = redux (&res, P, &mu)) != MP_OKAY) {
+          goto LBL_RES;
+        }
+      }
+    }
+  }
+
+  mp_exch (&res, Y);
+  err = MP_OKAY;
+LBL_RES:mp_clear (&res);
+LBL_MU:mp_clear (&mu);
+LBL_M:
+  mp_clear(&M[1]);
+  for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
+    mp_clear (&M[x]);
+  }
+  return err;
+}
+
+
+/* computes b = a*a */
+static int mp_sqr (mp_int * a, mp_int * b)
+{
+  int     res;
+
+#ifdef BN_MP_TOOM_SQR_C
+  /* use Toom-Cook? */
+  if (a->used >= TOOM_SQR_CUTOFF) {
+    res = mp_toom_sqr(a, b);
+  /* Karatsuba? */
+  } else
+#endif
+#ifdef BN_MP_KARATSUBA_SQR_C
+if (a->used >= KARATSUBA_SQR_CUTOFF) {
+    res = mp_karatsuba_sqr (a, b);
+  } else
+#endif
+  {
+#ifdef BN_FAST_S_MP_SQR_C
+    /* can we use the fast comba multiplier? */
+    if ((a->used * 2 + 1) < MP_WARRAY &&
+         a->used <
+         (1 << (sizeof(mp_word) * CHAR_BIT - 2*DIGIT_BIT - 1))) {
+      res = fast_s_mp_sqr (a, b);
+    } else
+#endif
+#ifdef BN_S_MP_SQR_C
+      res = s_mp_sqr (a, b);
+#else
+#error mp_sqr could fail
+      res = MP_VAL;
+#endif
+  }
+  b->sign = MP_ZPOS;
+  return res;
+}
+
+
+/* reduces a modulo n where n is of the form 2**p - d
+   This differs from reduce_2k since "d" can be larger
+   than a single digit.
+*/
+static int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d)
+{
+   mp_int q;
+   int    p, res;
+
+   if ((res = mp_init(&q)) != MP_OKAY) {
+      return res;
+   }
+
+   p = mp_count_bits(n);
+top:
+   /* q = a/2**p, a = a mod 2**p */
+   if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) {
+      goto ERR;
+   }
+
+   /* q = q * d */
+   if ((res = mp_mul(&q, d, &q)) != MP_OKAY) {
+      goto ERR;
+   }
+
+   /* a = a + q */
+   if ((res = s_mp_add(a, &q, a)) != MP_OKAY) {
+      goto ERR;
+   }
+
+   if (mp_cmp_mag(a, n) != MP_LT) {
+      s_mp_sub(a, n, a);
+      goto top;
+   }
+
+ERR:
+   mp_clear(&q);
+   return res;
+}
+
+
+/* determines the setup value */
+static int mp_reduce_2k_setup_l(mp_int *a, mp_int *d)
+{
+   int    res;
+   mp_int tmp;
+
+   if ((res = mp_init(&tmp)) != MP_OKAY) {
+      return res;
+   }
+
+   if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) {
+      goto ERR;
+   }
+
+   if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) {
+      goto ERR;
+   }
+
+ERR:
+   mp_clear(&tmp);
+   return res;
+}
+
+
+/* computes a = 2**b
+ *
+ * Simple algorithm which zeroes the int, grows it then just sets one bit
+ * as required.
+ */
+static int mp_2expt (mp_int * a, int b)
+{
+  int     res;
+
+  /* zero a as per default */
+  mp_zero (a);
+
+  /* grow a to accommodate the single bit */
+  if ((res = mp_grow (a, b / DIGIT_BIT + 1)) != MP_OKAY) {
+    return res;
+  }
+
+  /* set the used count of where the bit will go */
+  a->used = b / DIGIT_BIT + 1;
+
+  /* put the single bit in its place */
+  a->dp[b / DIGIT_BIT] = ((mp_digit)1) << (b % DIGIT_BIT);
+
+  return MP_OKAY;
+}
+
+
+/* pre-calculate the value required for Barrett reduction
+ * For a given modulus "b" it calulates the value required in "a"
+ */
+static int mp_reduce_setup (mp_int * a, mp_int * b)
+{
+  int     res;
+
+  if ((res = mp_2expt (a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) {
+    return res;
+  }
+  return mp_div (a, b, a, NULL);
+}
+
+
+/* reduces x mod m, assumes 0 < x < m**2, mu is
+ * precomputed via mp_reduce_setup.
+ * From HAC pp.604 Algorithm 14.42
+ */
+static int mp_reduce (mp_int * x, mp_int * m, mp_int * mu)
+{
+  mp_int  q;
+  int     res, um = m->used;
+
+  /* q = x */
+  if ((res = mp_init_copy (&q, x)) != MP_OKAY) {
+    return res;
+  }
+
+  /* q1 = x / b**(k-1)  */
+  mp_rshd (&q, um - 1);
+
+  /* according to HAC this optimization is ok */
+  if (((unsigned long) um) > (((mp_digit)1) << (DIGIT_BIT - 1))) {
+    if ((res = mp_mul (&q, mu, &q)) != MP_OKAY) {
+      goto CLEANUP;
+    }
+  } else {
+#ifdef BN_S_MP_MUL_HIGH_DIGS_C
+    if ((res = s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) {
+      goto CLEANUP;
+    }
+#elif defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C)
+    if ((res = fast_s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) {
+      goto CLEANUP;
+    }
+#else
+    {
+#error mp_reduce would always fail
+      res = MP_VAL;
+      goto CLEANUP;
+    }
+#endif
+  }
+
+  /* q3 = q2 / b**(k+1) */
+  mp_rshd (&q, um + 1);
+
+  /* x = x mod b**(k+1), quick (no division) */
+  if ((res = mp_mod_2d (x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) {
+    goto CLEANUP;
+  }
+
+  /* q = q * m mod b**(k+1), quick (no division) */
+  if ((res = s_mp_mul_digs (&q, m, &q, um + 1)) != MP_OKAY) {
+    goto CLEANUP;
+  }
+
+  /* x = x - q */
+  if ((res = mp_sub (x, &q, x)) != MP_OKAY) {
+    goto CLEANUP;
+  }
+
+  /* If x < 0, add b**(k+1) to it */
+  if (mp_cmp_d (x, 0) == MP_LT) {
+    mp_set (&q, 1);
+    if ((res = mp_lshd (&q, um + 1)) != MP_OKAY) {
+      goto CLEANUP;
+    }
+    if ((res = mp_add (x, &q, x)) != MP_OKAY) {
+      goto CLEANUP;
+    }
+  }
+
+  /* Back off if it's too big */
+  while (mp_cmp (x, m) != MP_LT) {
+    if ((res = s_mp_sub (x, m, x)) != MP_OKAY) {
+      goto CLEANUP;
+    }
+  }
+
+CLEANUP:
+  mp_clear (&q);
+
+  return res;
+}
+
+
+/* multiplies |a| * |b| and only computes up to digs digits of result
+ * HAC pp. 595, Algorithm 14.12  Modified so you can control how
+ * many digits of output are created.
+ */
+static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
+{
+  mp_int  t;
+  int     res, pa, pb, ix, iy;
+  mp_digit u;
+  mp_word r;
+  mp_digit tmpx, *tmpt, *tmpy;
+
+#ifdef BN_FAST_S_MP_MUL_DIGS_C
+  /* can we use the fast multiplier? */
+  if (((digs) < MP_WARRAY) &&
+      MIN (a->used, b->used) <
+          (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
+    return fast_s_mp_mul_digs (a, b, c, digs);
+  }
+#endif
+
+  if ((res = mp_init_size (&t, digs)) != MP_OKAY) {
+    return res;
+  }
+  t.used = digs;
+
+  /* compute the digits of the product directly */
+  pa = a->used;
+  for (ix = 0; ix < pa; ix++) {
+    /* set the carry to zero */
+    u = 0;
+
+    /* limit ourselves to making digs digits of output */
+    pb = MIN (b->used, digs - ix);
+
+    /* setup some aliases */
+    /* copy of the digit from a used within the nested loop */
+    tmpx = a->dp[ix];
+
+    /* an alias for the destination shifted ix places */
+    tmpt = t.dp + ix;
+
+    /* an alias for the digits of b */
+    tmpy = b->dp;
+
+    /* compute the columns of the output and propagate the carry */
+    for (iy = 0; iy < pb; iy++) {
+      /* compute the column as a mp_word */
+      r       = ((mp_word)*tmpt) +
+                ((mp_word)tmpx) * ((mp_word)*tmpy++) +
+                ((mp_word) u);
+
+      /* the new column is the lower part of the result */
+      *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK));
+
+      /* get the carry word from the result */
+      u       = (mp_digit) (r >> ((mp_word) DIGIT_BIT));
+    }
+    /* set carry if it is placed below digs */
+    if (ix + iy < digs) {
+      *tmpt = u;
+    }
+  }
+
+  mp_clamp (&t);
+  mp_exch (&t, c);
+
+  mp_clear (&t);
+  return MP_OKAY;
+}
+
+
+#ifdef BN_FAST_S_MP_MUL_DIGS_C
+/* Fast (comba) multiplier
+ *
+ * This is the fast column-array [comba] multiplier.  It is
+ * designed to compute the columns of the product first
+ * then handle the carries afterwards.  This has the effect
+ * of making the nested loops that compute the columns very
+ * simple and schedulable on super-scalar processors.
+ *
+ * This has been modified to produce a variable number of
+ * digits of output so if say only a half-product is required
+ * you don't have to compute the upper half (a feature
+ * required for fast Barrett reduction).
+ *
+ * Based on Algorithm 14.12 on pp.595 of HAC.
+ *
+ */
+static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
+{
+  int     olduse, res, pa, ix, iz;
+  mp_digit W[MP_WARRAY];
+  register mp_word  _W;
+
+  /* grow the destination as required */
+  if (c->alloc < digs) {
+    if ((res = mp_grow (c, digs)) != MP_OKAY) {
+      return res;
+    }
+  }
+
+  /* number of output digits to produce */
+  pa = MIN(digs, a->used + b->used);
+
+  /* clear the carry */
+  _W = 0;
+  os_memset(W, 0, sizeof(W));
+  for (ix = 0; ix < pa; ix++) {
+      int      tx, ty;
+      int      iy;
+      mp_digit *tmpx, *tmpy;
+
+      /* get offsets into the two bignums */
+      ty = MIN(b->used-1, ix);
+      tx = ix - ty;
+
+      /* setup temp aliases */
+      tmpx = a->dp + tx;
+      tmpy = b->dp + ty;
+
+      /* this is the number of times the loop will iterrate, essentially
+         while (tx++ < a->used && ty-- >= 0) { ... }
+       */
+      iy = MIN(a->used-tx, ty+1);
+
+      /* execute loop */
+      for (iz = 0; iz < iy; ++iz) {
+         _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--);
+
+      }
+
+      /* store term */
+      W[ix] = ((mp_digit)_W) & MP_MASK;
+
+      /* make next carry */
+      _W = _W >> ((mp_word)DIGIT_BIT);
+ }
+
+  /* setup dest */
+  olduse  = c->used;
+  c->used = pa;
+
+  {
+    register mp_digit *tmpc;
+    tmpc = c->dp;
+    for (ix = 0; ix < pa+1; ix++) {
+      /* now extract the previous digit [below the carry] */
+      *tmpc++ = W[ix];
+    }
+
+    /* clear unused digits [that existed in the old copy of c] */
+    for (; ix < olduse; ix++) {
+      *tmpc++ = 0;
+    }
+  }
+  mp_clamp (c);
+  return MP_OKAY;
+}
+#endif /* BN_FAST_S_MP_MUL_DIGS_C */
+
+
+/* init an mp_init for a given size */
+static int mp_init_size (mp_int * a, int size)
+{
+  int x;
+
+  /* pad size so there are always extra digits */
+  size += (MP_PREC * 2) - (size % MP_PREC);
+
+  /* alloc mem */
+  a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * size);
+  if (a->dp == NULL) {
+    return MP_MEM;
+  }
+
+  /* set the members */
+  a->used  = 0;
+  a->alloc = size;
+  a->sign  = MP_ZPOS;
+
+  /* zero the digits */
+  for (x = 0; x < size; x++) {
+      a->dp[x] = 0;
+  }
+
+  return MP_OKAY;
+}
+
+
+/* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */
+static int s_mp_sqr (mp_int * a, mp_int * b)
+{
+  mp_int  t;
+  int     res, ix, iy, pa;
+  mp_word r;
+  mp_digit u, tmpx, *tmpt;
+
+  pa = a->used;
+  if ((res = mp_init_size (&t, 2*pa + 1)) != MP_OKAY) {
+    return res;
+  }
+
+  /* default used is maximum possible size */
+  t.used = 2*pa + 1;
+
+  for (ix = 0; ix < pa; ix++) {
+    /* first calculate the digit at 2*ix */
+    /* calculate double precision result */
+    r = ((mp_word) t.dp[2*ix]) +
+        ((mp_word)a->dp[ix])*((mp_word)a->dp[ix]);
+
+    /* store lower part in result */
+    t.dp[ix+ix] = (mp_digit) (r & ((mp_word) MP_MASK));
+
+    /* get the carry */
+    u           = (mp_digit)(r >> ((mp_word) DIGIT_BIT));
+
+    /* left hand side of A[ix] * A[iy] */
+    tmpx        = a->dp[ix];
+
+    /* alias for where to store the results */
+    tmpt        = t.dp + (2*ix + 1);
+
+    for (iy = ix + 1; iy < pa; iy++) {
+      /* first calculate the product */
+      r       = ((mp_word)tmpx) * ((mp_word)a->dp[iy]);
+
+      /* now calculate the double precision result, note we use
+       * addition instead of *2 since it's easier to optimize
+       */
+      r       = ((mp_word) *tmpt) + r + r + ((mp_word) u);
+
+      /* store lower part */
+      *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK));
+
+      /* get carry */
+      u       = (mp_digit)(r >> ((mp_word) DIGIT_BIT));
+    }
+    /* propagate upwards */
+    while (u != ((mp_digit) 0)) {
+      r       = ((mp_word) *tmpt) + ((mp_word) u);
+      *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK));
+      u       = (mp_digit)(r >> ((mp_word) DIGIT_BIT));
+    }
+  }
+
+  mp_clamp (&t);
+  mp_exch (&t, b);
+  mp_clear (&t);
+  return MP_OKAY;
+}
+
+
+/* multiplies |a| * |b| and does not compute the lower digs digits
+ * [meant to get the higher part of the product]
+ */
+static int s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
+{
+  mp_int  t;
+  int     res, pa, pb, ix, iy;
+  mp_digit u;
+  mp_word r;
+  mp_digit tmpx, *tmpt, *tmpy;
+
+  /* can we use the fast multiplier? */
+#ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C
+  if (((a->used + b->used + 1) < MP_WARRAY)
+      && MIN (a->used, b->used) < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
+    return fast_s_mp_mul_high_digs (a, b, c, digs);
+  }
+#endif
+
+  if ((res = mp_init_size (&t, a->used + b->used + 1)) != MP_OKAY) {
+    return res;
+  }
+  t.used = a->used + b->used + 1;
+
+  pa = a->used;
+  pb = b->used;
+  for (ix = 0; ix < pa; ix++) {
+    /* clear the carry */
+    u = 0;
+
+    /* left hand side of A[ix] * B[iy] */
+    tmpx = a->dp[ix];
+
+    /* alias to the address of where the digits will be stored */
+    tmpt = &(t.dp[digs]);
+
+    /* alias for where to read the right hand side from */
+    tmpy = b->dp + (digs - ix);
+
+    for (iy = digs - ix; iy < pb; iy++) {
+      /* calculate the double precision result */
+      r       = ((mp_word)*tmpt) +
+                ((mp_word)tmpx) * ((mp_word)*tmpy++) +
+                ((mp_word) u);
+
+      /* get the lower part */
+      *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK));
+
+      /* carry the carry */
+      u       = (mp_digit) (r >> ((mp_word) DIGIT_BIT));
+    }
+    *tmpt = u;
+  }
+  mp_clamp (&t);
+  mp_exch (&t, c);
+  mp_clear (&t);
+  return MP_OKAY;
+}
+
+
+#ifdef BN_MP_MONTGOMERY_SETUP_C
+/* setups the montgomery reduction stuff */
+static int
+mp_montgomery_setup (mp_int * n, mp_digit * rho)
+{
+  mp_digit x, b;
+
+/* fast inversion mod 2**k
+ *
+ * Based on the fact that
+ *
+ * XA = 1 (mod 2**n)  =>  (X(2-XA)) A = 1 (mod 2**2n)
+ *                    =>  2*X*A - X*X*A*A = 1
+ *                    =>  2*(1) - (1)     = 1
+ */
+  b = n->dp[0];
+
+  if ((b & 1) == 0) {
+    return MP_VAL;
+  }
+
+  x = (((b + 2) & 4) << 1) + b; /* here x*a==1 mod 2**4 */
+  x *= 2 - b * x;               /* here x*a==1 mod 2**8 */
+#if !defined(MP_8BIT)
+  x *= 2 - b * x;               /* here x*a==1 mod 2**16 */
+#endif
+#if defined(MP_64BIT) || !(defined(MP_8BIT) || defined(MP_16BIT))
+  x *= 2 - b * x;               /* here x*a==1 mod 2**32 */
+#endif
+#ifdef MP_64BIT
+  x *= 2 - b * x;               /* here x*a==1 mod 2**64 */
+#endif
+
+  /* rho = -1/m mod b */
+  *rho = (unsigned long)(((mp_word)1 << ((mp_word) DIGIT_BIT)) - x) & MP_MASK;
+
+  return MP_OKAY;
+}
+#endif
+
+
+#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C
+/* computes xR**-1 == x (mod N) via Montgomery Reduction
+ *
+ * This is an optimized implementation of montgomery_reduce
+ * which uses the comba method to quickly calculate the columns of the
+ * reduction.
+ *
+ * Based on Algorithm 14.32 on pp.601 of HAC.
+*/
+static int fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho)
+{
+  int     ix, res, olduse;
+  mp_word W[MP_WARRAY];
+
+  /* get old used count */
+  olduse = x->used;
+
+  /* grow a as required */
+  if (x->alloc < n->used + 1) {
+    if ((res = mp_grow (x, n->used + 1)) != MP_OKAY) {
+      return res;
+    }
+  }
+
+  /* first we have to get the digits of the input into
+   * an array of double precision words W[...]
+   */
+  {
+    register mp_word *_W;
+    register mp_digit *tmpx;
+
+    /* alias for the W[] array */
+    _W   = W;
+
+    /* alias for the digits of  x*/
+    tmpx = x->dp;
+
+    /* copy the digits of a into W[0..a->used-1] */
+    for (ix = 0; ix < x->used; ix++) {
+      *_W++ = *tmpx++;
+    }
+
+    /* zero the high words of W[a->used..m->used*2] */
+    for (; ix < n->used * 2 + 1; ix++) {
+      *_W++ = 0;
+    }
+  }
+
+  /* now we proceed to zero successive digits
+   * from the least significant upwards
+   */
+  for (ix = 0; ix < n->used; ix++) {
+    /* mu = ai * m' mod b
+     *
+     * We avoid a double precision multiplication (which isn't required)
+     * by casting the value down to a mp_digit.  Note this requires
+     * that W[ix-1] have  the carry cleared (see after the inner loop)
+     */
+    register mp_digit mu;
+    mu = (mp_digit) (((W[ix] & MP_MASK) * rho) & MP_MASK);
+
+    /* a = a + mu * m * b**i
+     *
+     * This is computed in place and on the fly.  The multiplication
+     * by b**i is handled by offseting which columns the results
+     * are added to.
+     *
+     * Note the comba method normally doesn't handle carries in the
+     * inner loop In this case we fix the carry from the previous
+     * column since the Montgomery reduction requires digits of the
+     * result (so far) [see above] to work.  This is
+     * handled by fixing up one carry after the inner loop.  The
+     * carry fixups are done in order so after these loops the
+     * first m->used words of W[] have the carries fixed
+     */
+    {
+      register int iy;
+      register mp_digit *tmpn;
+      register mp_word *_W;
+
+      /* alias for the digits of the modulus */
+      tmpn = n->dp;
+
+      /* Alias for the columns set by an offset of ix */
+      _W = W + ix;
+
+      /* inner loop */
+      for (iy = 0; iy < n->used; iy++) {
+          *_W++ += ((mp_word)mu) * ((mp_word)*tmpn++);
+      }
+    }
+
+    /* now fix carry for next digit, W[ix+1] */
+    W[ix + 1] += W[ix] >> ((mp_word) DIGIT_BIT);
+  }
+
+  /* now we have to propagate the carries and
+   * shift the words downward [all those least
+   * significant digits we zeroed].
+   */
+  {
+    register mp_digit *tmpx;
+    register mp_word *_W, *_W1;
+
+    /* nox fix rest of carries */
+
+    /* alias for current word */
+    _W1 = W + ix;
+
+    /* alias for next word, where the carry goes */
+    _W = W + ++ix;
+
+    for (; ix <= n->used * 2 + 1; ix++) {
+      *_W++ += *_W1++ >> ((mp_word) DIGIT_BIT);
+    }
+
+    /* copy out, A = A/b**n
+     *
+     * The result is A/b**n but instead of converting from an
+     * array of mp_word to mp_digit than calling mp_rshd
+     * we just copy them in the right order
+     */
+
+    /* alias for destination word */
+    tmpx = x->dp;
+
+    /* alias for shifted double precision result */
+    _W = W + n->used;
+
+    for (ix = 0; ix < n->used + 1; ix++) {
+      *tmpx++ = (mp_digit)(*_W++ & ((mp_word) MP_MASK));
+    }
+
+    /* zero oldused digits, if the input a was larger than
+     * m->used+1 we'll have to clear the digits
+     */
+    for (; ix < olduse; ix++) {
+      *tmpx++ = 0;
+    }
+  }
+
+  /* set the max used and clamp */
+  x->used = n->used + 1;
+  mp_clamp (x);
+
+  /* if A >= m then A = A - m */
+  if (mp_cmp_mag (x, n) != MP_LT) {
+    return s_mp_sub (x, n, x);
+  }
+  return MP_OKAY;
+}
+#endif
+
+
+#ifdef BN_MP_MUL_2_C
+/* b = a*2 */
+static int mp_mul_2(mp_int * a, mp_int * b)
+{
+  int     x, res, oldused;
+
+  /* grow to accommodate result */
+  if (b->alloc < a->used + 1) {
+    if ((res = mp_grow (b, a->used + 1)) != MP_OKAY) {
+      return res;
+    }
+  }
+
+  oldused = b->used;
+  b->used = a->used;
+
+  {
+    register mp_digit r, rr, *tmpa, *tmpb;
+
+    /* alias for source */
+    tmpa = a->dp;
+
+    /* alias for dest */
+    tmpb = b->dp;
+
+    /* carry */
+    r = 0;
+    for (x = 0; x < a->used; x++) {
+
+      /* get what will be the *next* carry bit from the
+       * MSB of the current digit
+       */
+      rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1));
+
+      /* now shift up this digit, add in the carry [from the previous] */
+      *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK;
+
+      /* copy the carry that would be from the source
+       * digit into the next iteration
+       */
+      r = rr;
+    }
+
+    /* new leading digit? */
+    if (r != 0) {
+      /* add a MSB which is always 1 at this point */
+      *tmpb = 1;
+      ++(b->used);
+    }
+
+    /* now zero any excess digits on the destination
+     * that we didn't write to
+     */
+    tmpb = b->dp + b->used;
+    for (x = b->used; x < oldused; x++) {
+      *tmpb++ = 0;
+    }
+  }
+  b->sign = a->sign;
+  return MP_OKAY;
+}
+#endif
+
+
+#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C
+/*
+ * shifts with subtractions when the result is greater than b.
+ *
+ * The method is slightly modified to shift B unconditionally up to just under
+ * the leading bit of b.  This saves a lot of multiple precision shifting.
+ */
+static int mp_montgomery_calc_normalization (mp_int * a, mp_int * b)
+{
+  int     x, bits, res;
+
+  /* how many bits of last digit does b use */
+  bits = mp_count_bits (b) % DIGIT_BIT;
+
+  if (b->used > 1) {
+     if ((res = mp_2expt (a, (b->used - 1) * DIGIT_BIT + bits - 1)) != MP_OKAY) {
+        return res;
+     }
+  } else {
+     mp_set(a, 1);
+     bits = 1;
+  }
+
+
+  /* now compute C = A * B mod b */
+  for (x = bits - 1; x < (int)DIGIT_BIT; x++) {
+    if ((res = mp_mul_2 (a, a)) != MP_OKAY) {
+      return res;
+    }
+    if (mp_cmp_mag (a, b) != MP_LT) {
+      if ((res = s_mp_sub (a, b, a)) != MP_OKAY) {
+        return res;
+      }
+    }
+  }
+
+  return MP_OKAY;
+}
+#endif
+
+
+#ifdef BN_MP_EXPTMOD_FAST_C
+/* computes Y == G**X mod P, HAC pp.616, Algorithm 14.85
+ *
+ * Uses a left-to-right k-ary sliding window to compute the modular exponentiation.
+ * The value of k changes based on the size of the exponent.
+ *
+ * Uses Montgomery or Diminished Radix reduction [whichever appropriate]
+ */
+
+static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode)
+{
+  mp_int  M[TAB_SIZE], res;
+  mp_digit buf, mp;
+  int     err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize;
+
+  /* use a pointer to the reduction algorithm.  This allows us to use
+   * one of many reduction algorithms without modding the guts of
+   * the code with if statements everywhere.
+   */
+  int     (*redux)(mp_int*,mp_int*,mp_digit);
+
+  /* find window size */
+  x = mp_count_bits (X);
+  if (x <= 7) {
+    winsize = 2;
+  } else if (x <= 36) {
+    winsize = 3;
+  } else if (x <= 140) {
+    winsize = 4;
+  } else if (x <= 450) {
+    winsize = 5;
+  } else if (x <= 1303) {
+    winsize = 6;
+  } else if (x <= 3529) {
+    winsize = 7;
+  } else {
+    winsize = 8;
+  }
+
+#ifdef MP_LOW_MEM
+  if (winsize > 5) {
+     winsize = 5;
+  }
+#endif
+
+  /* init M array */
+  /* init first cell */
+  if ((err = mp_init(&M[1])) != MP_OKAY) {
+     return err;
+  }
+
+  /* now init the second half of the array */
+  for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
+    if ((err = mp_init(&M[x])) != MP_OKAY) {
+      for (y = 1<<(winsize-1); y < x; y++) {
+        mp_clear (&M[y]);
+      }
+      mp_clear(&M[1]);
+      return err;
+    }
+  }
+
+  /* determine and setup reduction code */
+  if (redmode == 0) {
+#ifdef BN_MP_MONTGOMERY_SETUP_C
+     /* now setup montgomery  */
+     if ((err = mp_montgomery_setup (P, &mp)) != MP_OKAY) {
+        goto LBL_M;
+     }
+#else
+     err = MP_VAL;
+     goto LBL_M;
+#endif
+
+     /* automatically pick the comba one if available (saves quite a few calls/ifs) */
+#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C
+     if (((P->used * 2 + 1) < MP_WARRAY) &&
+          P->used < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
+        redux = fast_mp_montgomery_reduce;
+     } else
+#endif
+     {
+#ifdef BN_MP_MONTGOMERY_REDUCE_C
+        /* use slower baseline Montgomery method */
+        redux = mp_montgomery_reduce;
+#else
+        err = MP_VAL;
+        goto LBL_M;
+#endif
+     }
+  } else if (redmode == 1) {
+#if defined(BN_MP_DR_SETUP_C) && defined(BN_MP_DR_REDUCE_C)
+     /* setup DR reduction for moduli of the form B**k - b */
+     mp_dr_setup(P, &mp);
+     redux = mp_dr_reduce;
+#else
+     err = MP_VAL;
+     goto LBL_M;
+#endif
+  } else {
+#if defined(BN_MP_REDUCE_2K_SETUP_C) && defined(BN_MP_REDUCE_2K_C)
+     /* setup DR reduction for moduli of the form 2**k - b */
+     if ((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) {
+        goto LBL_M;
+     }
+     redux = mp_reduce_2k;
+#else
+     err = MP_VAL;
+     goto LBL_M;
+#endif
+  }
+
+  /* setup result */
+  if ((err = mp_init (&res)) != MP_OKAY) {
+    goto LBL_M;
+  }
+
+  /* create M table
+   *
+
+   *
+   * The first half of the table is not computed though accept for M[0] and M[1]
+   */
+
+  if (redmode == 0) {
+#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C
+     /* now we need R mod m */
+     if ((err = mp_montgomery_calc_normalization (&res, P)) != MP_OKAY) {
+       goto LBL_RES;
+     }
+#else
+     err = MP_VAL;
+     goto LBL_RES;
+#endif
+
+     /* now set M[1] to G * R mod m */
+     if ((err = mp_mulmod (G, &res, P, &M[1])) != MP_OKAY) {
+       goto LBL_RES;
+     }
+  } else {
+     mp_set(&res, 1);
+     if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) {
+        goto LBL_RES;
+     }
+  }
+
+  /* compute the value at M[1<<(winsize-1)] by squaring M[1] (winsize-1) times */
+  if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) {
+    goto LBL_RES;
+  }
+
+  for (x = 0; x < (winsize - 1); x++) {
+    if ((err = mp_sqr (&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) {
+      goto LBL_RES;
+    }
+    if ((err = redux (&M[1 << (winsize - 1)], P, mp)) != MP_OKAY) {
+      goto LBL_RES;
+    }
+  }
+
+  /* create upper table */
+  for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) {
+    if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) {
+      goto LBL_RES;
+    }
+    if ((err = redux (&M[x], P, mp)) != MP_OKAY) {
+      goto LBL_RES;
+    }
+  }
+
+  /* set initial mode and bit cnt */
+  mode   = 0;
+  bitcnt = 1;
+  buf    = 0;
+  digidx = X->used - 1;
+  bitcpy = 0;
+  bitbuf = 0;
+
+  for (;;) {
+    /* grab next digit as required */
+    if (--bitcnt == 0) {
+      /* if digidx == -1 we are out of digits so break */
+      if (digidx == -1) {
+        break;
+      }
+      /* read next digit and reset bitcnt */
+      buf    = X->dp[digidx--];
+      bitcnt = (int)DIGIT_BIT;
+    }
+
+    /* grab the next msb from the exponent */
+    y     = (mp_digit)(buf >> (DIGIT_BIT - 1)) & 1;
+    buf <<= (mp_digit)1;
+
+    /* if the bit is zero and mode == 0 then we ignore it
+     * These represent the leading zero bits before the first 1 bit
+     * in the exponent.  Technically this opt is not required but it
+     * does lower the # of trivial squaring/reductions used
+     */
+    if (mode == 0 && y == 0) {
+      continue;
+    }
+
+    /* if the bit is zero and mode == 1 then we square */
+    if (mode == 1 && y == 0) {
+      if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+      if ((err = redux (&res, P, mp)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+      continue;
+    }
+
+    /* else we add it to the window */
+    bitbuf |= (y << (winsize - ++bitcpy));
+    mode    = 2;
+
+    if (bitcpy == winsize) {
+      /* ok window is filled so square as required and multiply  */
+      /* square first */
+      for (x = 0; x < winsize; x++) {
+        if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
+          goto LBL_RES;
+        }
+        if ((err = redux (&res, P, mp)) != MP_OKAY) {
+          goto LBL_RES;
+        }
+      }
+
+      /* then multiply */
+      if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+      if ((err = redux (&res, P, mp)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+
+      /* empty window and reset */
+      bitcpy = 0;
+      bitbuf = 0;
+      mode   = 1;
+    }
+  }
+
+  /* if bits remain then square/multiply */
+  if (mode == 2 && bitcpy > 0) {
+    /* square then multiply if the bit is set */
+    for (x = 0; x < bitcpy; x++) {
+      if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+      if ((err = redux (&res, P, mp)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+
+      /* get next bit of the window */
+      bitbuf <<= 1;
+      if ((bitbuf & (1 << winsize)) != 0) {
+        /* then multiply */
+        if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) {
+          goto LBL_RES;
+        }
+        if ((err = redux (&res, P, mp)) != MP_OKAY) {
+          goto LBL_RES;
+        }
+      }
+    }
+  }
+
+  if (redmode == 0) {
+     /* fixup result if Montgomery reduction is used
+      * recall that any value in a Montgomery system is
+      * actually multiplied by R mod n.  So we have
+      * to reduce one more time to cancel out the factor
+      * of R.
+      */
+     if ((err = redux(&res, P, mp)) != MP_OKAY) {
+       goto LBL_RES;
+     }
+  }
+
+  /* swap res with Y */
+  mp_exch (&res, Y);
+  err = MP_OKAY;
+LBL_RES:mp_clear (&res);
+LBL_M:
+  mp_clear(&M[1]);
+  for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
+    mp_clear (&M[x]);
+  }
+  return err;
+}
+#endif
+
+
+#ifdef BN_FAST_S_MP_SQR_C
+/* the jist of squaring...
+ * you do like mult except the offset of the tmpx [one that
+ * starts closer to zero] can't equal the offset of tmpy.
+ * So basically you set up iy like before then you min it with
+ * (ty-tx) so that it never happens.  You double all those
+ * you add in the inner loop
+
+After that loop you do the squares and add them in.
+*/
+
+static int fast_s_mp_sqr (mp_int * a, mp_int * b)
+{
+  int       olduse, res, pa, ix, iz;
+  mp_digit   W[MP_WARRAY], *tmpx;
+  mp_word   W1;
+
+  /* grow the destination as required */
+  pa = a->used + a->used;
+  if (b->alloc < pa) {
+    if ((res = mp_grow (b, pa)) != MP_OKAY) {
+      return res;
+    }
+  }
+
+  /* number of output digits to produce */
+  W1 = 0;
+  for (ix = 0; ix < pa; ix++) {
+      int      tx, ty, iy;
+      mp_word  _W;
+      mp_digit *tmpy;
+
+      /* clear counter */
+      _W = 0;
+
+      /* get offsets into the two bignums */
+      ty = MIN(a->used-1, ix);
+      tx = ix - ty;
+
+      /* setup temp aliases */
+      tmpx = a->dp + tx;
+      tmpy = a->dp + ty;
+
+      /* this is the number of times the loop will iterrate, essentially
+         while (tx++ < a->used && ty-- >= 0) { ... }
+       */
+      iy = MIN(a->used-tx, ty+1);
+
+      /* now for squaring tx can never equal ty
+       * we halve the distance since they approach at a rate of 2x
+       * and we have to round because odd cases need to be executed
+       */
+      iy = MIN(iy, (ty-tx+1)>>1);
+
+      /* execute loop */
+      for (iz = 0; iz < iy; iz++) {
+         _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--);
+      }
+
+      /* double the inner product and add carry */
+      _W = _W + _W + W1;
+
+      /* even columns have the square term in them */
+      if ((ix&1) == 0) {
+         _W += ((mp_word)a->dp[ix>>1])*((mp_word)a->dp[ix>>1]);
+      }
+
+      /* store it */
+      W[ix] = (mp_digit)(_W & MP_MASK);
+
+      /* make next carry */
+      W1 = _W >> ((mp_word)DIGIT_BIT);
+  }
+
+  /* setup dest */
+  olduse  = b->used;
+  b->used = a->used+a->used;
+
+  {
+    mp_digit *tmpb;
+    tmpb = b->dp;
+    for (ix = 0; ix < pa; ix++) {
+      *tmpb++ = W[ix] & MP_MASK;
+    }
+
+    /* clear unused digits [that existed in the old copy of c] */
+    for (; ix < olduse; ix++) {
+      *tmpb++ = 0;
+    }
+  }
+  mp_clamp (b);
+  return MP_OKAY;
+}
+#endif
+
+
+#ifdef BN_MP_MUL_D_C
+/* multiply by a digit */
+static int
+mp_mul_d (mp_int * a, mp_digit b, mp_int * c)
+{
+  mp_digit u, *tmpa, *tmpc;
+  mp_word  r;
+  int      ix, res, olduse;
+
+  /* make sure c is big enough to hold a*b */
+  if (c->alloc < a->used + 1) {
+    if ((res = mp_grow (c, a->used + 1)) != MP_OKAY) {
+      return res;
+    }
+  }
+
+  /* get the original destinations used count */
+  olduse = c->used;
+
+  /* set the sign */
+  c->sign = a->sign;
+
+  /* alias for a->dp [source] */
+  tmpa = a->dp;
+
+  /* alias for c->dp [dest] */
+  tmpc = c->dp;
+
+  /* zero carry */
+  u = 0;
+
+  /* compute columns */
+  for (ix = 0; ix < a->used; ix++) {
+    /* compute product and carry sum for this term */
+    r       = ((mp_word) u) + ((mp_word)*tmpa++) * ((mp_word)b);
+
+    /* mask off higher bits to get a single digit */
+    *tmpc++ = (mp_digit) (r & ((mp_word) MP_MASK));
+
+    /* send carry into next iteration */
+    u       = (mp_digit) (r >> ((mp_word) DIGIT_BIT));
+  }
+
+  /* store final carry [if any] and increment ix offset  */
+  *tmpc++ = u;
+  ++ix;
+
+  /* now zero digits above the top */
+  while (ix++ < olduse) {
+     *tmpc++ = 0;
+  }
+
+  /* set used count */
+  c->used = a->used + 1;
+  mp_clamp(c);
+
+  return MP_OKAY;
+}
+#endif
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/pkcs1.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/pkcs1.c
new file mode 100644
index 0000000..49e439d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/pkcs1.c
@@ -0,0 +1,339 @@
+/*
+ * PKCS #1 (RSA Encryption)
+ * Copyright (c) 2006-2014, 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/crypto.h"
+#include "rsa.h"
+#include "asn1.h"
+#include "pkcs1.h"
+
+
+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;
+}
+
+
+int pkcs1_encrypt(int block_type, struct crypto_rsa_key *key,
+		  int use_private, const u8 *in, size_t inlen,
+		  u8 *out, size_t *outlen)
+{
+	size_t modlen;
+
+	modlen = crypto_rsa_get_modulus_len(key);
+
+	if (pkcs1_generate_encryption_block(block_type, modlen, in, inlen,
+					    out, outlen) < 0)
+		return -1;
+
+	return crypto_rsa_exptmod(out, modlen, out, outlen, key, use_private);
+}
+
+
+int pkcs1_v15_private_key_decrypt(struct crypto_rsa_key *key,
+				  const u8 *in, size_t inlen,
+				  u8 *out, size_t *outlen)
+{
+	int res;
+	u8 *pos, *end;
+
+	res = crypto_rsa_exptmod(in, inlen, out, outlen, key, 1);
+	if (res)
+		return res;
+
+	if (*outlen < 2 || out[0] != 0 || out[1] != 2)
+		return -1;
+
+	/* Skip PS (pseudorandom non-zero octets) */
+	pos = out + 2;
+	end = out + *outlen;
+	while (*pos && pos < end)
+		pos++;
+	if (pos == end)
+		return -1;
+	if (pos - out - 2 < 8) {
+		/* PKCS #1 v1.5, 8.1: At least eight octets long PS */
+		wpa_printf(MSG_INFO, "LibTomCrypt: Too short padding");
+		return -1;
+	}
+	pos++;
+
+	*outlen -= pos - out;
+
+	/* Strip PKCS #1 header */
+	os_memmove(out, pos, *outlen);
+
+	return 0;
+}
+
+
+int pkcs1_decrypt_public_key(struct crypto_rsa_key *key,
+			     const u8 *crypt, size_t crypt_len,
+			     u8 *plain, size_t *plain_len)
+{
+	size_t len;
+	u8 *pos;
+
+	len = *plain_len;
+	if (crypto_rsa_exptmod(crypt, crypt_len, plain, &len, key, 0) < 0)
+		return -1;
+
+	/*
+	 * PKCS #1 v1.5, 8.1:
+	 *
+	 * EB = 00 || BT || PS || 00 || D
+	 * BT = 00 or 01
+	 * PS = k-3-||D|| times (00 if BT=00) or (FF if BT=01)
+	 * k = length of modulus in octets
+	 *
+	 * Based on 10.1.3, "The block type shall be 01" for a signature.
+	 */
+
+	if (len < 3 + 8 + 16 /* min hash len */ ||
+	    plain[0] != 0x00 || plain[1] != 0x01) {
+		wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
+			   "structure");
+		wpa_hexdump_key(MSG_DEBUG, "Signature EB", plain, len);
+		return -1;
+	}
+
+	pos = plain + 3;
+	/* BT = 01 */
+	if (plain[2] != 0xff) {
+		wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature "
+			   "PS (BT=01)");
+		wpa_hexdump_key(MSG_DEBUG, "Signature EB", plain, len);
+		return -1;
+	}
+	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");
+		wpa_hexdump_key(MSG_DEBUG, "Signature EB", plain, len);
+		return -1;
+	}
+
+	if (pos + 16 /* min hash len */ >= plain + len || *pos != 0x00) {
+		wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
+			   "structure (2)");
+		wpa_hexdump_key(MSG_DEBUG, "Signature EB", plain, len);
+		return -1;
+	}
+	pos++;
+	len -= pos - plain;
+
+	/* Strip PKCS #1 header */
+	os_memmove(plain, pos, len);
+	*plain_len = len;
+
+	return 0;
+}
+
+
+int pkcs1_v15_sig_ver(struct crypto_public_key *pk,
+		      const u8 *s, size_t s_len,
+		      const struct asn1_oid *hash_alg,
+		      const u8 *hash, size_t hash_len)
+{
+	int res;
+	u8 *decrypted;
+	size_t decrypted_len;
+	const u8 *pos, *end, *next, *da_end;
+	struct asn1_hdr hdr;
+	struct asn1_oid oid;
+
+	decrypted = os_malloc(s_len);
+	if (decrypted == NULL)
+		return -1;
+	decrypted_len = s_len;
+	res = crypto_public_key_decrypt_pkcs1(pk, s, s_len, decrypted,
+					      &decrypted_len);
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "PKCS #1: RSA decrypt failed");
+		os_free(decrypted);
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "Decrypted(S)", decrypted, decrypted_len);
+
+	/*
+	 * PKCS #1 v1.5, 10.1.2:
+	 *
+	 * DigestInfo ::= SEQUENCE {
+	 *     digestAlgorithm DigestAlgorithmIdentifier,
+	 *     digest Digest
+	 * }
+	 *
+	 * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
+	 *
+	 * Digest ::= OCTET STRING
+	 *
+	 */
+	if (asn1_get_next(decrypted, decrypted_len, &hdr) < 0 ||
+	    !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #1: Expected SEQUENCE (DigestInfo)");
+		os_free(decrypted);
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "PKCS #1: DigestInfo",
+		    hdr.payload, hdr.length);
+
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	/*
+	 * X.509:
+	 * AlgorithmIdentifier ::= SEQUENCE {
+	 *     algorithm            OBJECT IDENTIFIER,
+	 *     parameters           ANY DEFINED BY algorithm OPTIONAL
+	 * }
+	 */
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #1: Expected SEQUENCE (AlgorithmIdentifier)");
+		os_free(decrypted);
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "PKCS #1: DigestAlgorithmIdentifier",
+		    hdr.payload, hdr.length);
+	da_end = hdr.payload + hdr.length;
+
+	if (asn1_get_oid(hdr.payload, hdr.length, &oid, &next)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #1: Failed to parse digestAlgorithm");
+		os_free(decrypted);
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "PKCS #1: Digest algorithm parameters",
+		    next, da_end - next);
+
+	/*
+	 * RFC 5754: The correct encoding for the SHA2 algorithms would be to
+	 * omit the parameters, but there are implementation that encode these
+	 * as a NULL element. Allow these two cases and reject anything else.
+	 */
+	if (da_end > next &&
+	    (asn1_get_next(next, da_end - next, &hdr) < 0 ||
+	     !asn1_is_null(&hdr) ||
+	     hdr.payload + hdr.length != da_end)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #1: Unexpected digest algorithm parameters");
+		os_free(decrypted);
+		return -1;
+	}
+
+	if (!asn1_oid_equal(&oid, hash_alg)) {
+		char txt[100], txt2[100];
+		asn1_oid_to_str(&oid, txt, sizeof(txt));
+		asn1_oid_to_str(hash_alg, txt2, sizeof(txt2));
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #1: Hash alg OID mismatch: was %s, expected %s",
+			   txt, txt2);
+		os_free(decrypted);
+		return -1;
+	}
+
+	/* Digest ::= OCTET STRING */
+	pos = da_end;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_octetstring(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #1: Expected OCTETSTRING (Digest)");
+		os_free(decrypted);
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "PKCS #1: Decrypted Digest",
+		    hdr.payload, hdr.length);
+
+	if (hdr.length != hash_len ||
+	    os_memcmp_const(hdr.payload, hash, hdr.length) != 0) {
+		wpa_printf(MSG_INFO, "PKCS #1: Digest value does not match calculated hash");
+		os_free(decrypted);
+		return -1;
+	}
+
+	os_free(decrypted);
+
+	if (hdr.payload + hdr.length != decrypted + decrypted_len) {
+		wpa_printf(MSG_INFO,
+			   "PKCS #1: Extra data after signature - reject");
+
+		wpa_hexdump(MSG_DEBUG, "PKCS #1: Extra data",
+			    hdr.payload + hdr.length,
+			    decrypted + decrypted_len - hdr.payload -
+			    hdr.length);
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/pkcs1.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/pkcs1.h
new file mode 100644
index 0000000..f37ebf3
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/pkcs1.h
@@ -0,0 +1,29 @@
+/*
+ * PKCS #1 (RSA Encryption)
+ * 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 PKCS1_H
+#define PKCS1_H
+
+struct crypto_public_key;
+struct asn1_oid;
+
+int pkcs1_encrypt(int block_type, struct crypto_rsa_key *key,
+		  int use_private, const u8 *in, size_t inlen,
+		  u8 *out, size_t *outlen);
+int pkcs1_v15_private_key_decrypt(struct crypto_rsa_key *key,
+				  const u8 *in, size_t inlen,
+				  u8 *out, size_t *outlen);
+int pkcs1_decrypt_public_key(struct crypto_rsa_key *key,
+			     const u8 *crypt, size_t crypt_len,
+			     u8 *plain, size_t *plain_len);
+int pkcs1_v15_sig_ver(struct crypto_public_key *pk,
+		      const u8 *s, size_t s_len,
+		      const struct asn1_oid *hash_alg,
+		      const u8 *hash, size_t hash_len);
+
+#endif /* PKCS1_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/pkcs5.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/pkcs5.c
new file mode 100644
index 0000000..7bef89b
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/pkcs5.c
@@ -0,0 +1,628 @@
+/*
+ * PKCS #5 (Password-based Encryption)
+ * Copyright (c) 2009-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 "common.h"
+#include "crypto/crypto.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "asn1.h"
+#include "pkcs5.h"
+
+
+struct pkcs5_params {
+	enum pkcs5_alg {
+		PKCS5_ALG_UNKNOWN,
+		PKCS5_ALG_MD5_DES_CBC,
+		PKCS5_ALG_PBES2,
+		PKCS5_ALG_SHA1_3DES_CBC,
+	} alg;
+	u8 salt[64];
+	size_t salt_len;
+	unsigned int iter_count;
+	enum pbes2_enc_alg {
+		PBES2_ENC_ALG_UNKNOWN,
+		PBES2_ENC_ALG_DES_EDE3_CBC,
+	} enc_alg;
+	u8 iv[8];
+	size_t iv_len;
+};
+
+
+static int oid_is_rsadsi(struct asn1_oid *oid)
+{
+	return oid->len >= 4 &&
+		oid->oid[0] == 1 /* iso */ &&
+		oid->oid[1] == 2 /* member-body */ &&
+		oid->oid[2] == 840 /* us */ &&
+		oid->oid[3] == 113549 /* rsadsi */;
+}
+
+
+static int pkcs5_is_oid(struct asn1_oid *oid, unsigned long alg)
+{
+	return oid->len == 7 &&
+		oid_is_rsadsi(oid) &&
+		oid->oid[4] == 1 /* pkcs */ &&
+		oid->oid[5] == 5 /* pkcs-5 */ &&
+		oid->oid[6] == alg;
+}
+
+
+static int enc_alg_is_oid(struct asn1_oid *oid, unsigned long alg)
+{
+	return oid->len == 6 &&
+		oid_is_rsadsi(oid) &&
+		oid->oid[4] == 3 /* encryptionAlgorithm */ &&
+		oid->oid[5] == alg;
+}
+
+
+static int pkcs12_is_pbe_oid(struct asn1_oid *oid, unsigned long alg)
+{
+	return oid->len == 8 &&
+		oid_is_rsadsi(oid) &&
+		oid->oid[4] == 1 /* pkcs */ &&
+		oid->oid[5] == 12 /* pkcs-12 */ &&
+		oid->oid[6] == 1 /* pkcs-12PbeIds */ &&
+		oid->oid[7] == alg;
+}
+
+
+static enum pkcs5_alg pkcs5_get_alg(struct asn1_oid *oid)
+{
+	if (pkcs5_is_oid(oid, 3)) /* pbeWithMD5AndDES-CBC (PBES1) */
+		return PKCS5_ALG_MD5_DES_CBC;
+	if (pkcs12_is_pbe_oid(oid, 3)) /* pbeWithSHAAnd3-KeyTripleDES-CBC */
+		return PKCS5_ALG_SHA1_3DES_CBC;
+	if (pkcs5_is_oid(oid, 13)) /* id-PBES2 (PBES2) */
+		return PKCS5_ALG_PBES2;
+	return PKCS5_ALG_UNKNOWN;
+}
+
+
+static int pkcs5_get_params_pbes2(struct pkcs5_params *params, const u8 *pos,
+				  const u8 *enc_alg_end)
+{
+	struct asn1_hdr hdr;
+	const u8 *end, *kdf_end;
+	struct asn1_oid oid;
+	char obuf[80];
+
+	/*
+	 * RFC 2898, Ch. A.4
+	 *
+	 * PBES2-params ::= SEQUENCE {
+	 *     keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}},
+	 *     encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} }
+	 *
+	 * PBES2-KDFs ALGORITHM-IDENTIFIER ::=
+	 *     { {PBKDF2-params IDENTIFIED BY id-PBKDF2}, ... }
+	 */
+
+	if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 ||
+	    !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #5: Expected SEQUENCE (PBES2-params)");
+		return -1;
+	}
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #5: Expected SEQUENCE (keyDerivationFunc)");
+		return -1;
+	}
+
+	pos = hdr.payload;
+	kdf_end = end = hdr.payload + hdr.length;
+
+	if (asn1_get_oid(pos, end - pos, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #5: Failed to parse OID (keyDerivationFunc algorithm)");
+		return -1;
+	}
+
+	asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+	wpa_printf(MSG_DEBUG, "PKCS #5: PBES2 keyDerivationFunc algorithm %s",
+		   obuf);
+	if (!pkcs5_is_oid(&oid, 12)) /* id-PBKDF2 */ {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #5: Unsupported PBES2 keyDerivationFunc algorithm %s",
+			   obuf);
+		return -1;
+	}
+
+	/*
+	 * RFC 2898, C.
+	 *
+	 * PBKDF2-params ::= SEQUENCE {
+	 *     salt CHOICE {
+	 *       specified OCTET STRING,
+	 *       otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}}
+	 *     },
+	 *     iterationCount INTEGER (1..MAX),
+	 *     keyLength INTEGER (1..MAX) OPTIONAL,
+	 *     prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT
+	 *     algid-hmacWithSHA1
+	 * }
+	 */
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #5: Expected SEQUENCE (PBKDF2-params)");
+		return -1;
+	}
+
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	/* For now, only support the salt CHOICE specified (OCTET STRING) */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_octetstring(&hdr) ||
+	    hdr.length > sizeof(params->salt)) {
+		asn1_unexpected(&hdr,
+				"PKCS #5: Expected OCTET STRING (salt.specified)");
+		return -1;
+	}
+	pos = hdr.payload + hdr.length;
+	os_memcpy(params->salt, hdr.payload, hdr.length);
+	params->salt_len = hdr.length;
+	wpa_hexdump(MSG_DEBUG, "PKCS #5: salt", params->salt, params->salt_len);
+
+	/* iterationCount INTEGER */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 || !asn1_is_integer(&hdr)) {
+		asn1_unexpected(&hdr, "PKCS #5: Expected INTEGER");
+		return -1;
+	}
+	if (hdr.length == 1) {
+		params->iter_count = *hdr.payload;
+	} else if (hdr.length == 2) {
+		params->iter_count = WPA_GET_BE16(hdr.payload);
+	} else if (hdr.length == 4) {
+		params->iter_count = WPA_GET_BE32(hdr.payload);
+	} else {
+		wpa_hexdump(MSG_DEBUG,
+			    "PKCS #5: Unsupported INTEGER value (iterationCount)",
+			    hdr.payload, hdr.length);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "PKCS #5: iterationCount=0x%x",
+		   params->iter_count);
+	if (params->iter_count == 0 || params->iter_count > 0xffff) {
+		wpa_printf(MSG_INFO, "PKCS #5: Unsupported iterationCount=0x%x",
+			   params->iter_count);
+		return -1;
+	}
+
+	/* For now, ignore optional keyLength and prf */
+
+	pos = kdf_end;
+
+	/* encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} */
+
+	if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 ||
+	    !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #5: Expected SEQUENCE (encryptionScheme)");
+		return -1;
+	}
+
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	if (asn1_get_oid(pos, end - pos, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #5: Failed to parse OID (encryptionScheme algorithm)");
+		return -1;
+	}
+
+	asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+	wpa_printf(MSG_DEBUG, "PKCS #5: PBES2 encryptionScheme algorithm %s",
+		   obuf);
+	if (enc_alg_is_oid(&oid, 7)) {
+		params->enc_alg = PBES2_ENC_ALG_DES_EDE3_CBC;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #5: Unsupported PBES2 encryptionScheme algorithm %s",
+			   obuf);
+		return -1;
+	}
+
+	/*
+	 * RFC 2898, B.2.2:
+	 * The parameters field associated with this OID in an
+	 * AlgorithmIdentifier shall have type OCTET STRING (SIZE(8)),
+	 * specifying the initialization vector for CBC mode.
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_octetstring(&hdr) || hdr.length != 8) {
+		asn1_unexpected(&hdr,
+				"PKCS #5: Expected OCTET STRING (SIZE(8)) (IV)");
+		return -1;
+	}
+	os_memcpy(params->iv, hdr.payload, hdr.length);
+	params->iv_len = hdr.length;
+	wpa_hexdump(MSG_DEBUG, "PKCS #5: IV", params->iv, params->iv_len);
+
+	return 0;
+}
+
+
+static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len,
+			    struct pkcs5_params *params)
+{
+	struct asn1_hdr hdr;
+	const u8 *enc_alg_end, *pos, *end;
+	struct asn1_oid oid;
+	char obuf[80];
+
+	/* AlgorithmIdentifier */
+
+	enc_alg_end = enc_alg + enc_alg_len;
+
+	os_memset(params, 0, sizeof(*params));
+
+	if (asn1_get_oid(enc_alg, enc_alg_end - enc_alg, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG, "PKCS #5: Failed to parse OID "
+			   "(algorithm)");
+		return -1;
+	}
+
+	asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+	wpa_printf(MSG_DEBUG, "PKCS #5: encryption algorithm %s", obuf);
+	params->alg = pkcs5_get_alg(&oid);
+	if (params->alg == PKCS5_ALG_UNKNOWN) {
+		wpa_printf(MSG_INFO, "PKCS #5: unsupported encryption "
+			   "algorithm %s", obuf);
+		return -1;
+	}
+
+	if (params->alg == PKCS5_ALG_PBES2)
+		return pkcs5_get_params_pbes2(params, pos, enc_alg_end);
+
+	/* PBES1 */
+
+	/*
+	 * PKCS#5, Section 8
+	 * PBEParameter ::= SEQUENCE {
+	 *   salt OCTET STRING SIZE(8),
+	 *   iterationCount INTEGER }
+	 *
+	 * Note: The same implementation can be used to parse the PKCS #12
+	 * version described in RFC 7292, C:
+	 * pkcs-12PbeParams ::= SEQUENCE {
+	 *     salt        OCTET STRING,
+	 *     iterations  INTEGER
+	 * }
+	 */
+
+	if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 ||
+	    !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #5: Expected SEQUENCE (PBEParameter)");
+		return -1;
+	}
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	/* salt OCTET STRING SIZE(8) (PKCS #5) or OCTET STRING (PKCS #12) */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_octetstring(&hdr) || hdr.length > sizeof(params->salt)) {
+		asn1_unexpected(&hdr,
+				"PKCS #5: Expected OCTETSTRING SIZE(8) (salt)");
+		return -1;
+	}
+	pos = hdr.payload + hdr.length;
+	os_memcpy(params->salt, hdr.payload, hdr.length);
+	params->salt_len = hdr.length;
+	wpa_hexdump(MSG_DEBUG, "PKCS #5: salt",
+		    params->salt, params->salt_len);
+
+	/* iterationCount INTEGER */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_integer(&hdr)) {
+		asn1_unexpected(&hdr, "PKCS #5: Expected INTEGER");
+		return -1;
+	}
+	if (hdr.length == 1)
+		params->iter_count = *hdr.payload;
+	else if (hdr.length == 2)
+		params->iter_count = WPA_GET_BE16(hdr.payload);
+	else if (hdr.length == 4)
+		params->iter_count = WPA_GET_BE32(hdr.payload);
+	else {
+		wpa_hexdump(MSG_DEBUG, "PKCS #5: Unsupported INTEGER value "
+			    " (iterationCount)",
+			    hdr.payload, hdr.length);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "PKCS #5: iterationCount=0x%x",
+		   params->iter_count);
+	if (params->iter_count == 0 || params->iter_count > 0xffff) {
+		wpa_printf(MSG_INFO, "PKCS #5: Unsupported "
+			   "iterationCount=0x%x", params->iter_count);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static struct crypto_cipher *
+pkcs5_crypto_init_pbes2(struct pkcs5_params *params, const char *passwd)
+{
+	u8 key[24];
+
+	if (params->enc_alg != PBES2_ENC_ALG_DES_EDE3_CBC ||
+	    params->iv_len != 8)
+		return NULL;
+
+	wpa_hexdump_ascii_key(MSG_DEBUG, "PKCS #5: PBES2 password for PBKDF2",
+			      passwd, os_strlen(passwd));
+	wpa_hexdump(MSG_DEBUG, "PKCS #5: PBES2 salt for PBKDF2",
+		    params->salt, params->salt_len);
+	wpa_printf(MSG_DEBUG, "PKCS #5: PBES2 PBKDF2 iterations: %u",
+		   params->iter_count);
+	if (pbkdf2_sha1(passwd, params->salt, params->salt_len,
+			params->iter_count, key, sizeof(key)) < 0)
+		return NULL;
+	wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES EDE3 key", key, sizeof(key));
+	wpa_hexdump(MSG_DEBUG, "PKCS #5: DES IV", params->iv, params->iv_len);
+
+	return crypto_cipher_init(CRYPTO_CIPHER_ALG_3DES, params->iv,
+				  key, sizeof(key));
+}
+
+
+static void add_byte_array_mod(u8 *a, const u8 *b, size_t len)
+{
+	size_t i;
+	unsigned int carry = 0;
+
+	for (i = len - 1; i < len; i--) {
+		carry = carry + a[i] + b[i];
+		a[i] = carry & 0xff;
+		carry >>= 8;
+	}
+}
+
+
+static int pkcs12_key_gen(const u8 *pw, size_t pw_len, const u8 *salt,
+			  size_t salt_len, u8 id, unsigned int iter,
+			  size_t out_len, u8 *out)
+{
+	unsigned int u, v, S_len, P_len, i;
+	u8 *D = NULL, *I = NULL, *B = NULL, *pos;
+	int res = -1;
+
+	/* RFC 7292, B.2 */
+	u = SHA1_MAC_LEN;
+	v = 64;
+
+	/* D = copies of ID */
+	D = os_malloc(v);
+	if (!D)
+		goto done;
+	os_memset(D, id, v);
+
+	/* S = copies of salt; P = copies of password, I = S || P */
+	S_len = v * ((salt_len + v - 1) / v);
+	P_len = v * ((pw_len + v - 1) / v);
+	I = os_malloc(S_len + P_len);
+	if (!I)
+		goto done;
+	pos = I;
+	if (salt_len) {
+		for (i = 0; i < S_len; i++)
+			*pos++ = salt[i % salt_len];
+	}
+	if (pw_len) {
+		for (i = 0; i < P_len; i++)
+			*pos++ = pw[i % pw_len];
+	}
+
+	B = os_malloc(v);
+	if (!B)
+		goto done;
+
+	for (;;) {
+		u8 hash[SHA1_MAC_LEN];
+		const u8 *addr[2];
+		size_t len[2];
+
+		addr[0] = D;
+		len[0] = v;
+		addr[1] = I;
+		len[1] = S_len + P_len;
+		if (sha1_vector(2, addr, len, hash) < 0)
+			goto done;
+
+		addr[0] = hash;
+		len[0] = SHA1_MAC_LEN;
+		for (i = 1; i < iter; i++) {
+			if (sha1_vector(1, addr, len, hash) < 0)
+				goto done;
+		}
+
+		if (out_len <= u) {
+			os_memcpy(out, hash, out_len);
+			res = 0;
+			goto done;
+		}
+
+		os_memcpy(out, hash, u);
+		out += u;
+		out_len -= u;
+
+		/* I_j = (I_j + B + 1) mod 2^(v*8) */
+		/* B = copies of Ai (final hash value) */
+		for (i = 0; i < v; i++)
+			B[i] = hash[i % u];
+		inc_byte_array(B, v);
+		for (i = 0; i < S_len + P_len; i += v)
+			add_byte_array_mod(&I[i], B, v);
+	}
+
+done:
+	os_free(B);
+	os_free(I);
+	os_free(D);
+	return res;
+}
+
+
+#define PKCS12_ID_ENC 1
+#define PKCS12_ID_IV 2
+#define PKCS12_ID_MAC 3
+
+static struct crypto_cipher *
+pkcs12_crypto_init_sha1(struct pkcs5_params *params, const char *passwd)
+{
+	unsigned int i;
+	u8 *pw;
+	size_t pw_len;
+	u8 key[24];
+	u8 iv[8];
+
+	if (params->alg != PKCS5_ALG_SHA1_3DES_CBC)
+		return NULL;
+
+	pw_len = passwd ? os_strlen(passwd) : 0;
+	pw = os_malloc(2 * (pw_len + 1));
+	if (!pw)
+		return NULL;
+	if (pw_len) {
+		for (i = 0; i <= pw_len; i++)
+			WPA_PUT_BE16(&pw[2 * i], passwd[i]);
+		pw_len = 2 * (pw_len + 1);
+	}
+
+	if (pkcs12_key_gen(pw, pw_len, params->salt, params->salt_len,
+			   PKCS12_ID_ENC, params->iter_count,
+			   sizeof(key), key) < 0 ||
+	    pkcs12_key_gen(pw, pw_len, params->salt, params->salt_len,
+			   PKCS12_ID_IV, params->iter_count,
+			   sizeof(iv), iv) < 0) {
+		os_free(pw);
+		return NULL;
+	}
+
+	os_free(pw);
+
+	wpa_hexdump_key(MSG_DEBUG, "PKCS #12: DES key", key, sizeof(key));
+	wpa_hexdump_key(MSG_DEBUG, "PKCS #12: DES IV", iv, sizeof(iv));
+
+	return crypto_cipher_init(CRYPTO_CIPHER_ALG_3DES, iv, key, sizeof(key));
+}
+
+
+static struct crypto_cipher * pkcs5_crypto_init(struct pkcs5_params *params,
+						const char *passwd)
+{
+	unsigned int i;
+	u8 hash[MD5_MAC_LEN];
+	const u8 *addr[2];
+	size_t len[2];
+
+	if (params->alg == PKCS5_ALG_PBES2)
+		return pkcs5_crypto_init_pbes2(params, passwd);
+
+	if (params->alg == PKCS5_ALG_SHA1_3DES_CBC)
+		return pkcs12_crypto_init_sha1(params, passwd);
+
+	if (params->alg != PKCS5_ALG_MD5_DES_CBC)
+		return NULL;
+
+	addr[0] = (const u8 *) passwd;
+	len[0] = os_strlen(passwd);
+	addr[1] = params->salt;
+	len[1] = params->salt_len;
+	if (md5_vector(2, addr, len, hash) < 0)
+		return NULL;
+	addr[0] = hash;
+	len[0] = MD5_MAC_LEN;
+	for (i = 1; i < params->iter_count; i++) {
+		if (md5_vector(1, addr, len, hash) < 0)
+			return NULL;
+	}
+	/* TODO: DES key parity bits(?) */
+	wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES key", hash, 8);
+	wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES IV", hash + 8, 8);
+
+	return crypto_cipher_init(CRYPTO_CIPHER_ALG_DES, hash + 8, hash, 8);
+}
+
+
+u8 * pkcs5_decrypt(const u8 *enc_alg, size_t enc_alg_len,
+		   const u8 *enc_data, size_t enc_data_len,
+		   const char *passwd, size_t *data_len)
+{
+	struct crypto_cipher *ctx;
+	u8 *eb, pad;
+	struct pkcs5_params params;
+	unsigned int i;
+
+	if (pkcs5_get_params(enc_alg, enc_alg_len, &params) < 0) {
+		wpa_printf(MSG_DEBUG, "PKCS #5: Unsupported parameters");
+		return NULL;
+	}
+
+	ctx = pkcs5_crypto_init(&params, passwd);
+	if (ctx == NULL) {
+		wpa_printf(MSG_DEBUG, "PKCS #5: Failed to initialize crypto");
+		return NULL;
+	}
+
+	/* PKCS #5, Section 7 - Decryption process */
+	if (enc_data_len < 16 || enc_data_len % 8) {
+		wpa_printf(MSG_INFO, "PKCS #5: invalid length of ciphertext "
+			   "%d", (int) enc_data_len);
+		crypto_cipher_deinit(ctx);
+		return NULL;
+	}
+
+	eb = os_malloc(enc_data_len);
+	if (eb == NULL) {
+		crypto_cipher_deinit(ctx);
+		return NULL;
+	}
+
+	if (crypto_cipher_decrypt(ctx, enc_data, eb, enc_data_len) < 0) {
+		wpa_printf(MSG_DEBUG, "PKCS #5: Failed to decrypt EB");
+		crypto_cipher_deinit(ctx);
+		os_free(eb);
+		return NULL;
+	}
+	crypto_cipher_deinit(ctx);
+
+	pad = eb[enc_data_len - 1];
+	if (pad > 8) {
+		wpa_printf(MSG_INFO, "PKCS #5: Invalid PS octet 0x%x", pad);
+		os_free(eb);
+		return NULL;
+	}
+	for (i = enc_data_len - pad; i < enc_data_len; i++) {
+		if (eb[i] != pad) {
+			wpa_hexdump(MSG_INFO, "PKCS #5: Invalid PS",
+				    eb + enc_data_len - pad, pad);
+			os_free(eb);
+			return NULL;
+		}
+	}
+
+	wpa_hexdump_key(MSG_MSGDUMP, "PKCS #5: message M (encrypted key)",
+			eb, enc_data_len - pad);
+
+	*data_len = enc_data_len - pad;
+	return eb;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/pkcs5.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/pkcs5.h
new file mode 100644
index 0000000..20ddadc
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/pkcs5.h
@@ -0,0 +1,16 @@
+/*
+ * PKCS #5 (Password-based Encryption)
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PKCS5_H
+#define PKCS5_H
+
+u8 * pkcs5_decrypt(const u8 *enc_alg, size_t enc_alg_len,
+		   const u8 *enc_data, size_t enc_data_len,
+		   const char *passwd, size_t *data_len);
+
+#endif /* PKCS5_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/pkcs8.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/pkcs8.c
new file mode 100644
index 0000000..75bbd12
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/pkcs8.c
@@ -0,0 +1,168 @@
+/*
+ * PKCS #8 (Private-key information syntax)
+ * 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 "asn1.h"
+#include "bignum.h"
+#include "rsa.h"
+#include "pkcs5.h"
+#include "pkcs8.h"
+
+
+struct crypto_private_key * pkcs8_key_import(const u8 *buf, size_t len)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+	struct bignum *zero;
+	struct asn1_oid oid;
+	char obuf[80];
+
+	/* PKCS #8, Chapter 6 */
+
+	/* PrivateKeyInfo ::= SEQUENCE */
+	if (asn1_get_next(buf, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #8: Does not start with PKCS #8 header (SEQUENCE)");
+		return NULL;
+	}
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	/* version Version (Version ::= INTEGER) */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 || !asn1_is_integer(&hdr)) {
+		asn1_unexpected(&hdr, "PKCS #8: Expected INTEGER");
+		return NULL;
+	}
+
+	zero = bignum_init();
+	if (zero == NULL)
+		return NULL;
+
+	if (bignum_set_unsigned_bin(zero, hdr.payload, hdr.length) < 0) {
+		wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse INTEGER");
+		bignum_deinit(zero);
+		return NULL;
+	}
+	pos = hdr.payload + hdr.length;
+
+	if (bignum_cmp_d(zero, 0) != 0) {
+		wpa_printf(MSG_DEBUG, "PKCS #8: Expected zero INTEGER in the "
+			   "beginning of private key; not found; assume "
+			   "PKCS #8 not used");
+		bignum_deinit(zero);
+		return NULL;
+	}
+	bignum_deinit(zero);
+
+	/* privateKeyAlgorithm PrivateKeyAlgorithmIdentifier
+	 * (PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier) */
+	if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #8: Expected SEQUENCE (AlgorithmIdentifier); assume PKCS #8 not used");
+		return NULL;
+	}
+
+	if (asn1_get_oid(hdr.payload, hdr.length, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse OID "
+			   "(algorithm); assume PKCS #8 not used");
+		return NULL;
+	}
+
+	asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+	wpa_printf(MSG_DEBUG, "PKCS #8: algorithm=%s", obuf);
+
+	if (oid.len != 7 ||
+	    oid.oid[0] != 1 /* iso */ ||
+	    oid.oid[1] != 2 /* member-body */ ||
+	    oid.oid[2] != 840 /* us */ ||
+	    oid.oid[3] != 113549 /* rsadsi */ ||
+	    oid.oid[4] != 1 /* pkcs */ ||
+	    oid.oid[5] != 1 /* pkcs-1 */ ||
+	    oid.oid[6] != 1 /* rsaEncryption */) {
+		wpa_printf(MSG_DEBUG, "PKCS #8: Unsupported private key "
+			   "algorithm %s", obuf);
+		return NULL;
+	}
+
+	pos = hdr.payload + hdr.length;
+
+	/* privateKey PrivateKey (PrivateKey ::= OCTET STRING) */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_octetstring(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #8: Expected OCTETSTRING (privateKey)");
+		return NULL;
+	}
+	wpa_printf(MSG_DEBUG, "PKCS #8: Try to parse RSAPrivateKey");
+
+	return (struct crypto_private_key *)
+		crypto_rsa_import_private_key(hdr.payload, hdr.length);
+}
+
+
+struct crypto_private_key *
+pkcs8_enc_key_import(const u8 *buf, size_t len, const char *passwd)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end, *enc_alg;
+	size_t enc_alg_len;
+	u8 *data;
+	size_t data_len;
+
+	if (passwd == NULL)
+		return NULL;
+
+	/*
+	 * PKCS #8, Chapter 7
+	 * EncryptedPrivateKeyInfo ::= SEQUENCE {
+	 *   encryptionAlgorithm EncryptionAlgorithmIdentifier,
+	 *   encryptedData EncryptedData }
+	 * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
+	 * EncryptedData ::= OCTET STRING
+	 */
+
+	if (asn1_get_next(buf, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #8: Does not start with PKCS #8 header (SEQUENCE); assume encrypted PKCS #8 not used");
+		return NULL;
+	}
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	/* encryptionAlgorithm EncryptionAlgorithmIdentifier */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #8: Expected SEQUENCE (AlgorithmIdentifier); assume encrypted PKCS #8 not used");
+		return NULL;
+	}
+	enc_alg = hdr.payload;
+	enc_alg_len = hdr.length;
+	pos = hdr.payload + hdr.length;
+
+	/* encryptedData EncryptedData */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_octetstring(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #8: Expected OCTETSTRING (encryptedData)");
+		return NULL;
+	}
+
+	data = pkcs5_decrypt(enc_alg, enc_alg_len, hdr.payload, hdr.length,
+			     passwd, &data_len);
+	if (data) {
+		struct crypto_private_key *key;
+		key = pkcs8_key_import(data, data_len);
+		os_free(data);
+		return key;
+	}
+
+	return NULL;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/pkcs8.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/pkcs8.h
new file mode 100644
index 0000000..bebf840
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/pkcs8.h
@@ -0,0 +1,16 @@
+/*
+ * PKCS #8 (Private-key information syntax)
+ * 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 PKCS8_H
+#define PKCS8_H
+
+struct crypto_private_key * pkcs8_key_import(const u8 *buf, size_t len);
+struct crypto_private_key *
+pkcs8_enc_key_import(const u8 *buf, size_t len, const char *passwd);
+
+#endif /* PKCS8_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/rsa.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/rsa.c
new file mode 100644
index 0000000..56ae7d7
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/rsa.c
@@ -0,0 +1,366 @@
+/*
+ * RSA
+ * Copyright (c) 2006-2014, 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 "asn1.h"
+#include "bignum.h"
+#include "rsa.h"
+
+
+struct crypto_rsa_key {
+	int private_key; /* whether private key is set */
+	struct bignum *n; /* modulus (p * q) */
+	struct bignum *e; /* public exponent */
+	/* The following parameters are available only if private_key is set */
+	struct bignum *d; /* private exponent */
+	struct bignum *p; /* prime p (factor of n) */
+	struct bignum *q; /* prime q (factor of n) */
+	struct bignum *dmp1; /* d mod (p - 1); CRT exponent */
+	struct bignum *dmq1; /* d mod (q - 1); CRT exponent */
+	struct bignum *iqmp; /* 1 / q mod p; CRT coefficient */
+};
+
+
+static const u8 * crypto_rsa_parse_integer(const u8 *pos, const u8 *end,
+					   struct bignum *num)
+{
+	struct asn1_hdr hdr;
+
+	if (pos == NULL)
+		return NULL;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_integer(&hdr)) {
+		asn1_unexpected(&hdr, "RSA: Expected INTEGER");
+		return NULL;
+	}
+
+	if (bignum_set_unsigned_bin(num, hdr.payload, hdr.length) < 0) {
+		wpa_printf(MSG_DEBUG, "RSA: Failed to parse INTEGER");
+		return NULL;
+	}
+
+	return hdr.payload + hdr.length;
+}
+
+
+/**
+ * crypto_rsa_import_public_key - Import an RSA public key
+ * @buf: Key buffer (DER encoded RSA public key)
+ * @len: Key buffer length in bytes
+ * Returns: Pointer to the public key or %NULL on failure
+ */
+struct crypto_rsa_key *
+crypto_rsa_import_public_key(const u8 *buf, size_t len)
+{
+	struct crypto_rsa_key *key;
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+
+	key = os_zalloc(sizeof(*key));
+	if (key == NULL)
+		return NULL;
+
+	key->n = bignum_init();
+	key->e = bignum_init();
+	if (key->n == NULL || key->e == NULL) {
+		crypto_rsa_free(key);
+		return NULL;
+	}
+
+	/*
+	 * PKCS #1, 7.1:
+	 * RSAPublicKey ::= SEQUENCE {
+	 *     modulus INTEGER, -- n
+	 *     publicExponent INTEGER -- e
+	 * }
+	 */
+
+	if (asn1_get_next(buf, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr, "RSA: Expected SEQUENCE (public key)");
+		goto error;
+	}
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	pos = crypto_rsa_parse_integer(pos, end, key->n);
+	pos = crypto_rsa_parse_integer(pos, end, key->e);
+
+	if (pos == NULL)
+		goto error;
+
+	if (pos != end) {
+		wpa_hexdump(MSG_DEBUG,
+			    "RSA: Extra data in public key SEQUENCE",
+			    pos, end - pos);
+		goto error;
+	}
+
+	return key;
+
+error:
+	crypto_rsa_free(key);
+	return NULL;
+}
+
+
+struct crypto_rsa_key *
+crypto_rsa_import_public_key_parts(const u8 *n, size_t n_len,
+				   const u8 *e, size_t e_len)
+{
+	struct crypto_rsa_key *key;
+
+	key = os_zalloc(sizeof(*key));
+	if (key == NULL)
+		return NULL;
+
+	key->n = bignum_init();
+	key->e = bignum_init();
+	if (key->n == NULL || key->e == NULL ||
+	    bignum_set_unsigned_bin(key->n, n, n_len) < 0 ||
+	    bignum_set_unsigned_bin(key->e, e, e_len) < 0) {
+		crypto_rsa_free(key);
+		return NULL;
+	}
+
+	return key;
+}
+
+
+/**
+ * crypto_rsa_import_private_key - Import an RSA private key
+ * @buf: Key buffer (DER encoded RSA private key)
+ * @len: Key buffer length in bytes
+ * Returns: Pointer to the private key or %NULL on failure
+ */
+struct crypto_rsa_key *
+crypto_rsa_import_private_key(const u8 *buf, size_t len)
+{
+	struct crypto_rsa_key *key;
+	struct bignum *zero;
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+
+	key = os_zalloc(sizeof(*key));
+	if (key == NULL)
+		return NULL;
+
+	key->private_key = 1;
+
+	key->n = bignum_init();
+	key->e = bignum_init();
+	key->d = bignum_init();
+	key->p = bignum_init();
+	key->q = bignum_init();
+	key->dmp1 = bignum_init();
+	key->dmq1 = bignum_init();
+	key->iqmp = bignum_init();
+
+	if (key->n == NULL || key->e == NULL || key->d == NULL ||
+	    key->p == NULL || key->q == NULL || key->dmp1 == NULL ||
+	    key->dmq1 == NULL || key->iqmp == NULL) {
+		crypto_rsa_free(key);
+		return NULL;
+	}
+
+	/*
+	 * PKCS #1, 7.2:
+	 * RSAPrivateKey ::= SEQUENCE {
+	 *    version Version,
+	 *    modulus INTEGER, -- n
+	 *    publicExponent INTEGER, -- e
+	 *    privateExponent INTEGER, -- d
+	 *    prime1 INTEGER, -- p
+	 *    prime2 INTEGER, -- q
+	 *    exponent1 INTEGER, -- d mod (p-1)
+	 *    exponent2 INTEGER, -- d mod (q-1)
+	 *    coefficient INTEGER -- (inverse of q) mod p
+	 * }
+	 *
+	 * Version ::= INTEGER -- shall be 0 for this version of the standard
+	 */
+	if (asn1_get_next(buf, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr, "RSA: Expected SEQUENCE (public key)");
+		goto error;
+	}
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	zero = bignum_init();
+	if (zero == NULL)
+		goto error;
+	pos = crypto_rsa_parse_integer(pos, end, zero);
+	if (pos == NULL || bignum_cmp_d(zero, 0) != 0) {
+		wpa_printf(MSG_DEBUG, "RSA: Expected zero INTEGER in the "
+			   "beginning of private key; not found");
+		bignum_deinit(zero);
+		goto error;
+	}
+	bignum_deinit(zero);
+
+	pos = crypto_rsa_parse_integer(pos, end, key->n);
+	pos = crypto_rsa_parse_integer(pos, end, key->e);
+	pos = crypto_rsa_parse_integer(pos, end, key->d);
+	pos = crypto_rsa_parse_integer(pos, end, key->p);
+	pos = crypto_rsa_parse_integer(pos, end, key->q);
+	pos = crypto_rsa_parse_integer(pos, end, key->dmp1);
+	pos = crypto_rsa_parse_integer(pos, end, key->dmq1);
+	pos = crypto_rsa_parse_integer(pos, end, key->iqmp);
+
+	if (pos == NULL)
+		goto error;
+
+	if (pos != end) {
+		wpa_hexdump(MSG_DEBUG,
+			    "RSA: Extra data in public key SEQUENCE",
+			    pos, end - pos);
+		goto error;
+	}
+
+	return key;
+
+error:
+	crypto_rsa_free(key);
+	return NULL;
+}
+
+
+/**
+ * crypto_rsa_get_modulus_len - Get the modulus length of the RSA key
+ * @key: RSA key
+ * Returns: Modulus length of the key
+ */
+size_t crypto_rsa_get_modulus_len(struct crypto_rsa_key *key)
+{
+	return bignum_get_unsigned_bin_len(key->n);
+}
+
+
+/**
+ * crypto_rsa_exptmod - RSA modular exponentiation
+ * @in: Input data
+ * @inlen: Input data length
+ * @out: Buffer for output data
+ * @outlen: Maximum size of the output buffer and used size on success
+ * @key: RSA key
+ * @use_private: 1 = Use RSA private key, 0 = Use RSA public key
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_rsa_exptmod(const u8 *in, size_t inlen, u8 *out, size_t *outlen,
+		       struct crypto_rsa_key *key, int use_private)
+{
+	struct bignum *tmp, *a = NULL, *b = NULL;
+	int ret = -1;
+	size_t modlen;
+
+	if (use_private && !key->private_key)
+		return -1;
+
+	tmp = bignum_init();
+	if (tmp == NULL)
+		return -1;
+
+	if (bignum_set_unsigned_bin(tmp, in, inlen) < 0)
+		goto error;
+	if (bignum_cmp(key->n, tmp) < 0) {
+		/* Too large input value for the RSA key modulus */
+		goto error;
+	}
+
+	if (use_private) {
+		/*
+		 * Decrypt (or sign) using Chinese remainder theorem to speed
+		 * up calculation. This is equivalent to tmp = tmp^d mod n
+		 * (which would require more CPU to calculate directly).
+		 *
+		 * dmp1 = (1/e) mod (p-1)
+		 * dmq1 = (1/e) mod (q-1)
+		 * iqmp = (1/q) mod p, where p > q
+		 * m1 = c^dmp1 mod p
+		 * m2 = c^dmq1 mod q
+		 * h = q^-1 (m1 - m2) mod p
+		 * m = m2 + hq
+		 */
+		a = bignum_init();
+		b = bignum_init();
+		if (a == NULL || b == NULL)
+			goto error;
+
+		/* a = tmp^dmp1 mod p */
+		if (bignum_exptmod(tmp, key->dmp1, key->p, a) < 0)
+			goto error;
+
+		/* b = tmp^dmq1 mod q */
+		if (bignum_exptmod(tmp, key->dmq1, key->q, b) < 0)
+			goto error;
+
+		/* tmp = (a - b) * (1/q mod p) (mod p) */
+		if (bignum_sub(a, b, tmp) < 0 ||
+		    bignum_mulmod(tmp, key->iqmp, key->p, tmp) < 0)
+			goto error;
+
+		/* tmp = b + q * tmp */
+		if (bignum_mul(tmp, key->q, tmp) < 0 ||
+		    bignum_add(tmp, b, tmp) < 0)
+			goto error;
+	} else {
+		/* Encrypt (or verify signature) */
+		/* tmp = tmp^e mod N */
+		if (bignum_exptmod(tmp, key->e, key->n, tmp) < 0)
+			goto error;
+	}
+
+	modlen = crypto_rsa_get_modulus_len(key);
+	if (modlen > *outlen) {
+		*outlen = modlen;
+		goto error;
+	}
+
+	if (bignum_get_unsigned_bin_len(tmp) > modlen)
+		goto error; /* should never happen */
+
+	*outlen = modlen;
+	os_memset(out, 0, modlen);
+	if (bignum_get_unsigned_bin(
+		    tmp, out +
+		    (modlen - bignum_get_unsigned_bin_len(tmp)), NULL) < 0)
+		goto error;
+
+	ret = 0;
+
+error:
+	bignum_deinit(tmp);
+	bignum_deinit(a);
+	bignum_deinit(b);
+	return ret;
+}
+
+
+/**
+ * crypto_rsa_free - Free RSA key
+ * @key: RSA key to be freed
+ *
+ * This function frees an RSA key imported with either
+ * crypto_rsa_import_public_key() or crypto_rsa_import_private_key().
+ */
+void crypto_rsa_free(struct crypto_rsa_key *key)
+{
+	if (key) {
+		bignum_deinit(key->n);
+		bignum_deinit(key->e);
+		bignum_deinit(key->d);
+		bignum_deinit(key->p);
+		bignum_deinit(key->q);
+		bignum_deinit(key->dmp1);
+		bignum_deinit(key->dmq1);
+		bignum_deinit(key->iqmp);
+		os_free(key);
+	}
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/rsa.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/rsa.h
new file mode 100644
index 0000000..b65818e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/rsa.h
@@ -0,0 +1,26 @@
+/*
+ * RSA
+ * 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.
+ */
+
+#ifndef RSA_H
+#define RSA_H
+
+struct crypto_rsa_key;
+
+struct crypto_rsa_key *
+crypto_rsa_import_public_key(const u8 *buf, size_t len);
+struct crypto_rsa_key *
+crypto_rsa_import_public_key_parts(const u8 *n, size_t n_len,
+				   const u8 *e, size_t e_len);
+struct crypto_rsa_key *
+crypto_rsa_import_private_key(const u8 *buf, size_t len);
+size_t crypto_rsa_get_modulus_len(struct crypto_rsa_key *key);
+int crypto_rsa_exptmod(const u8 *in, size_t inlen, u8 *out, size_t *outlen,
+		       struct crypto_rsa_key *key, int use_private);
+void crypto_rsa_free(struct crypto_rsa_key *key);
+
+#endif /* RSA_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_client.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_client.c
new file mode 100644
index 0000000..486da16
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_client.c
@@ -0,0 +1,918 @@
+/*
+ * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246)
+ * Copyright (c) 2006-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 "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "x509v3.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+#include "tlsv1_client.h"
+#include "tlsv1_client_i.h"
+
+/* TODO:
+ * Support for a message fragmented across several records (RFC 2246, 6.2.1)
+ */
+
+
+void tls_alert(struct tlsv1_client *conn, u8 level, u8 description)
+{
+	conn->alert_level = level;
+	conn->alert_description = description;
+}
+
+
+void tlsv1_client_free_dh(struct tlsv1_client *conn)
+{
+	os_free(conn->dh_p);
+	os_free(conn->dh_g);
+	os_free(conn->dh_ys);
+	conn->dh_p = conn->dh_g = conn->dh_ys = NULL;
+}
+
+
+u16 tls_client_highest_ver(struct tlsv1_client *conn)
+{
+	u16 tls_version = TLS_VERSION;
+
+	/* Pick the highest locally enabled TLS version */
+#ifdef CONFIG_TLSV12
+	if ((conn->flags & TLS_CONN_DISABLE_TLSv1_2) &&
+	    tls_version == TLS_VERSION_1_2)
+		tls_version = TLS_VERSION_1_1;
+#endif /* CONFIG_TLSV12 */
+#ifdef CONFIG_TLSV11
+	if ((conn->flags & TLS_CONN_DISABLE_TLSv1_1) &&
+	    tls_version == TLS_VERSION_1_1)
+		tls_version = TLS_VERSION_1;
+#endif /* CONFIG_TLSV11 */
+	if ((conn->flags & TLS_CONN_DISABLE_TLSv1_0) &&
+	    tls_version == TLS_VERSION_1)
+		return 0;
+
+	return tls_version;
+}
+
+
+int tls_derive_pre_master_secret(struct tlsv1_client *conn,
+				 u8 *pre_master_secret)
+{
+	WPA_PUT_BE16(pre_master_secret, tls_client_highest_ver(conn));
+	if (os_get_random(pre_master_secret + 2,
+			  TLS_PRE_MASTER_SECRET_LEN - 2))
+		return -1;
+	return 0;
+}
+
+
+int tls_derive_keys(struct tlsv1_client *conn,
+		    const u8 *pre_master_secret, size_t pre_master_secret_len)
+{
+	u8 seed[2 * TLS_RANDOM_LEN];
+	u8 key_block[TLS_MAX_KEY_BLOCK_LEN];
+	u8 *pos;
+	size_t key_block_len;
+
+	if (pre_master_secret) {
+		wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret",
+				pre_master_secret, pre_master_secret_len);
+		os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
+		os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
+			  TLS_RANDOM_LEN);
+		if (tls_prf(conn->rl.tls_version,
+			    pre_master_secret, pre_master_secret_len,
+			    "master secret", seed, 2 * TLS_RANDOM_LEN,
+			    conn->master_secret, TLS_MASTER_SECRET_LEN)) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive "
+				   "master_secret");
+			return -1;
+		}
+		wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret",
+				conn->master_secret, TLS_MASTER_SECRET_LEN);
+	}
+
+	os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
+	os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN);
+	key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len);
+	if (conn->rl.tls_version == TLS_VERSION_1)
+		key_block_len += 2 * conn->rl.iv_size;
+	if (tls_prf(conn->rl.tls_version,
+		    conn->master_secret, TLS_MASTER_SECRET_LEN,
+		    "key expansion", seed, 2 * TLS_RANDOM_LEN,
+		    key_block, key_block_len)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block");
+		return -1;
+	}
+	wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block",
+			key_block, key_block_len);
+
+	pos = key_block;
+
+	/* client_write_MAC_secret */
+	os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size);
+	pos += conn->rl.hash_size;
+	/* server_write_MAC_secret */
+	os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size);
+	pos += conn->rl.hash_size;
+
+	/* client_write_key */
+	os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len);
+	pos += conn->rl.key_material_len;
+	/* server_write_key */
+	os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len);
+	pos += conn->rl.key_material_len;
+
+	if (conn->rl.tls_version == TLS_VERSION_1) {
+		/* client_write_IV */
+		os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size);
+		pos += conn->rl.iv_size;
+		/* server_write_IV */
+		os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size);
+	} else {
+		/*
+		 * Use IV field to set the mask value for TLS v1.1. A fixed
+		 * mask of zero is used per the RFC 4346, 6.2.3.2 CBC Block
+		 * Cipher option 2a.
+		 */
+		os_memset(conn->rl.write_iv, 0, conn->rl.iv_size);
+	}
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_client_handshake - Process TLS handshake
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @in_data: Input data from TLS peer
+ * @in_len: Input data length
+ * @out_len: Length of the output buffer.
+ * @appl_data: Pointer to application data pointer, or %NULL if dropped
+ * @appl_data_len: Pointer to variable that is set to appl_data length
+ * @need_more_data: Set to 1 if more data would be needed to complete
+ *	processing
+ * Returns: Pointer to output data, %NULL on failure
+ */
+u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
+			    const u8 *in_data, size_t in_len,
+			    size_t *out_len, u8 **appl_data,
+			    size_t *appl_data_len, int *need_more_data)
+{
+	const u8 *pos, *end;
+	u8 *msg = NULL, *in_msg = NULL, *in_pos, *in_end, alert, ct;
+	size_t in_msg_len;
+	int no_appl_data;
+	int used;
+
+	if (need_more_data)
+		*need_more_data = 0;
+
+	if (conn->state == CLIENT_HELLO) {
+		if (in_len)
+			return NULL;
+		return tls_send_client_hello(conn, out_len);
+	}
+
+	if (conn->partial_input) {
+		if (wpabuf_resize(&conn->partial_input, in_len) < 0) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+				   "memory for pending record");
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				  TLS_ALERT_INTERNAL_ERROR);
+			goto failed;
+		}
+		wpabuf_put_data(conn->partial_input, in_data, in_len);
+		in_data = wpabuf_head(conn->partial_input);
+		in_len = wpabuf_len(conn->partial_input);
+	}
+
+	if (in_data == NULL || in_len == 0)
+		return NULL;
+
+	pos = in_data;
+	end = in_data + in_len;
+	in_msg = os_malloc(in_len);
+	if (in_msg == NULL)
+		return NULL;
+
+	/* Each received packet may include multiple records */
+	while (pos < end) {
+		in_msg_len = in_len;
+		used = tlsv1_record_receive(&conn->rl, pos, end - pos,
+					    in_msg, &in_msg_len, &alert);
+		if (used < 0) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Processing received "
+				   "record failed");
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+			goto failed;
+		}
+		if (used == 0) {
+			struct wpabuf *partial;
+			wpa_printf(MSG_DEBUG, "TLSv1: Need more data");
+			partial = wpabuf_alloc_copy(pos, end - pos);
+			wpabuf_free(conn->partial_input);
+			conn->partial_input = partial;
+			if (conn->partial_input == NULL) {
+				wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
+					   "allocate memory for pending "
+					   "record");
+				tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					  TLS_ALERT_INTERNAL_ERROR);
+				goto failed;
+			}
+			os_free(in_msg);
+			if (need_more_data)
+				*need_more_data = 1;
+			return NULL;
+		}
+		ct = pos[0];
+
+		in_pos = in_msg;
+		in_end = in_msg + in_msg_len;
+
+		/* Each received record may include multiple messages of the
+		 * same ContentType. */
+		while (in_pos < in_end) {
+			in_msg_len = in_end - in_pos;
+			if (tlsv1_client_process_handshake(conn, ct, in_pos,
+							   &in_msg_len,
+							   appl_data,
+							   appl_data_len) < 0)
+				goto failed;
+			in_pos += in_msg_len;
+		}
+
+		pos += used;
+	}
+
+	os_free(in_msg);
+	in_msg = NULL;
+
+	no_appl_data = appl_data == NULL || *appl_data == NULL;
+	msg = tlsv1_client_handshake_write(conn, out_len, no_appl_data);
+
+failed:
+	os_free(in_msg);
+	if (conn->alert_level) {
+		wpabuf_free(conn->partial_input);
+		conn->partial_input = NULL;
+		conn->state = FAILED;
+		os_free(msg);
+		msg = tlsv1_client_send_alert(conn, conn->alert_level,
+					      conn->alert_description,
+					      out_len);
+	} else if (msg == NULL) {
+		msg = os_zalloc(1);
+		*out_len = 0;
+	}
+
+	if (need_more_data == NULL || !(*need_more_data)) {
+		wpabuf_free(conn->partial_input);
+		conn->partial_input = NULL;
+	}
+
+	return msg;
+}
+
+
+/**
+ * tlsv1_client_encrypt - Encrypt data into TLS tunnel
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @in_data: Pointer to plaintext data to be encrypted
+ * @in_len: Input buffer length
+ * @out_data: Pointer to output buffer (encrypted TLS data)
+ * @out_len: Maximum out_data length
+ * Returns: Number of bytes written to out_data, -1 on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * send data in the encrypted tunnel.
+ */
+int tlsv1_client_encrypt(struct tlsv1_client *conn,
+			 const u8 *in_data, size_t in_len,
+			 u8 *out_data, size_t out_len)
+{
+	size_t rlen;
+
+	wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
+			in_data, in_len);
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
+			      out_data, out_len, in_data, in_len, &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	return rlen;
+}
+
+
+/**
+ * tlsv1_client_decrypt - Decrypt data from TLS tunnel
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @in_data: Pointer to input buffer (encrypted TLS data)
+ * @in_len: Input buffer length
+ * @need_more_data: Set to 1 if more data would be needed to complete
+ *	processing
+ * Returns: Decrypted data or %NULL on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * receive data from the encrypted tunnel.
+ */
+struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn,
+				     const u8 *in_data, size_t in_len,
+				     int *need_more_data)
+{
+	const u8 *in_end, *pos;
+	int used;
+	u8 alert, *out_pos, ct;
+	size_t olen;
+	struct wpabuf *buf = NULL;
+
+	if (need_more_data)
+		*need_more_data = 0;
+
+	if (conn->partial_input) {
+		if (wpabuf_resize(&conn->partial_input, in_len) < 0) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+				   "memory for pending record");
+			alert = TLS_ALERT_INTERNAL_ERROR;
+			goto fail;
+		}
+		wpabuf_put_data(conn->partial_input, in_data, in_len);
+		in_data = wpabuf_head(conn->partial_input);
+		in_len = wpabuf_len(conn->partial_input);
+	}
+
+	pos = in_data;
+	in_end = in_data + in_len;
+
+	while (pos < in_end) {
+		ct = pos[0];
+		if (wpabuf_resize(&buf, in_end - pos) < 0) {
+			alert = TLS_ALERT_INTERNAL_ERROR;
+			goto fail;
+		}
+		out_pos = wpabuf_put(buf, 0);
+		olen = wpabuf_tailroom(buf);
+		used = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
+					    out_pos, &olen, &alert);
+		if (used < 0) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
+				   "failed");
+			goto fail;
+		}
+		if (used == 0) {
+			struct wpabuf *partial;
+			wpa_printf(MSG_DEBUG, "TLSv1: Need more data");
+			partial = wpabuf_alloc_copy(pos, in_end - pos);
+			wpabuf_free(conn->partial_input);
+			conn->partial_input = partial;
+			if (conn->partial_input == NULL) {
+				wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
+					   "allocate memory for pending "
+					   "record");
+				alert = TLS_ALERT_INTERNAL_ERROR;
+				goto fail;
+			}
+			if (need_more_data)
+				*need_more_data = 1;
+			return buf;
+		}
+
+		if (ct == TLS_CONTENT_TYPE_ALERT) {
+			if (olen < 2) {
+				wpa_printf(MSG_DEBUG, "TLSv1: Alert "
+					   "underflow");
+				alert = TLS_ALERT_DECODE_ERROR;
+				goto fail;
+			}
+			wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
+				   out_pos[0], out_pos[1]);
+			if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) {
+				/* Continue processing */
+				pos += used;
+				continue;
+			}
+
+			alert = out_pos[1];
+			goto fail;
+		}
+
+		if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
+				   "0x%x when decrypting application data",
+				   pos[0]);
+			alert = TLS_ALERT_UNEXPECTED_MESSAGE;
+			goto fail;
+		}
+
+		wpabuf_put(buf, olen);
+
+		pos += used;
+	}
+
+	wpabuf_free(conn->partial_input);
+	conn->partial_input = NULL;
+	return buf;
+
+fail:
+	wpabuf_free(buf);
+	wpabuf_free(conn->partial_input);
+	conn->partial_input = NULL;
+	tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+	return NULL;
+}
+
+
+/**
+ * tlsv1_client_global_init - Initialize TLSv1 client
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function must be called before using any other TLSv1 client functions.
+ */
+int tlsv1_client_global_init(void)
+{
+	return crypto_global_init();
+}
+
+
+/**
+ * tlsv1_client_global_deinit - Deinitialize TLSv1 client
+ *
+ * This function can be used to deinitialize the TLSv1 client that was
+ * initialized by calling tlsv1_client_global_init(). No TLSv1 client functions
+ * can be called after this before calling tlsv1_client_global_init() again.
+ */
+void tlsv1_client_global_deinit(void)
+{
+	crypto_global_deinit();
+}
+
+
+/**
+ * tlsv1_client_init - Initialize TLSv1 client connection
+ * Returns: Pointer to TLSv1 client connection data or %NULL on failure
+ */
+struct tlsv1_client * tlsv1_client_init(void)
+{
+	struct tlsv1_client *conn;
+	size_t count;
+	u16 *suites;
+
+	conn = os_zalloc(sizeof(*conn));
+	if (conn == NULL)
+		return NULL;
+
+	conn->state = CLIENT_HELLO;
+
+	if (tls_verify_hash_init(&conn->verify) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify "
+			   "hash");
+		os_free(conn);
+		return NULL;
+	}
+
+	count = 0;
+	suites = conn->cipher_suites;
+	suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA256;
+	suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256;
+	suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
+	suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+	suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA256;
+	suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
+	suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
+	suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+	suites[count++] = TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA;
+	suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
+	suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
+	suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
+	conn->num_cipher_suites = count;
+
+	conn->rl.tls_version = TLS_VERSION;
+
+	return conn;
+}
+
+
+/**
+ * tlsv1_client_deinit - Deinitialize TLSv1 client connection
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ */
+void tlsv1_client_deinit(struct tlsv1_client *conn)
+{
+	crypto_public_key_free(conn->server_rsa_key);
+	tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
+	tlsv1_record_change_write_cipher(&conn->rl);
+	tlsv1_record_change_read_cipher(&conn->rl);
+	tls_verify_hash_free(&conn->verify);
+	os_free(conn->client_hello_ext);
+	tlsv1_client_free_dh(conn);
+	tlsv1_cred_free(conn->cred);
+	wpabuf_free(conn->partial_input);
+	x509_certificate_chain_free(conn->server_cert);
+	os_free(conn);
+}
+
+
+/**
+ * tlsv1_client_established - Check whether connection has been established
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * Returns: 1 if connection is established, 0 if not
+ */
+int tlsv1_client_established(struct tlsv1_client *conn)
+{
+	return conn->state == ESTABLISHED;
+}
+
+
+/**
+ * tlsv1_client_prf - Use TLS-PRF to derive keying material
+ * @conn: TLSv1 client connection data from tlsv1_client_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
+ * @server_random_first: seed is 0 = client_random|server_random,
+ * 1 = server_random|client_random
+ * @out: Buffer for output data from TLS-PRF
+ * @out_len: Length of the output buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_prf(struct tlsv1_client *conn, const char *label,
+		     const u8 *context, size_t context_len,
+		     int server_random_first, u8 *out, size_t out_len)
+{
+	u8 *seed, *pos;
+	size_t seed_len = 2 * TLS_RANDOM_LEN;
+	int res;
+
+	if (conn->state != ESTABLISHED)
+		return -1;
+
+	if (context_len > 65535)
+		return -1;
+
+	if (context)
+		seed_len += 2 + context_len;
+
+	seed = os_malloc(seed_len);
+	if (!seed)
+		return -1;
+
+	if (server_random_first) {
+		os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
+		os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random,
+			  TLS_RANDOM_LEN);
+	} else {
+		os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
+		os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
+			  TLS_RANDOM_LEN);
+	}
+
+	if (context) {
+		pos = seed + 2 * TLS_RANDOM_LEN;
+		WPA_PUT_BE16(pos, context_len);
+		pos += 2;
+		os_memcpy(pos, context, context_len);
+	}
+
+	res = tls_prf(conn->rl.tls_version,
+		      conn->master_secret, TLS_MASTER_SECRET_LEN,
+		      label, seed, seed_len, out, out_len);
+	os_free(seed);
+	return res;
+}
+
+
+/**
+ * tlsv1_client_get_cipher - Get current cipher name
+ * @conn: TLSv1 client connection data from tlsv1_client_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 tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf,
+			    size_t buflen)
+{
+	char *cipher;
+
+	switch (conn->rl.cipher_suite) {
+	case TLS_RSA_WITH_RC4_128_MD5:
+		cipher = "RC4-MD5";
+		break;
+	case TLS_RSA_WITH_RC4_128_SHA:
+		cipher = "RC4-SHA";
+		break;
+	case TLS_RSA_WITH_DES_CBC_SHA:
+		cipher = "DES-CBC-SHA";
+		break;
+	case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+		cipher = "DES-CBC3-SHA";
+		break;
+	case TLS_DHE_RSA_WITH_DES_CBC_SHA:
+		cipher = "DHE-RSA-DES-CBC-SHA";
+		break;
+	case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+		cipher = "DHE-RSA-DES-CBC3-SHA";
+		break;
+	case TLS_DH_anon_WITH_RC4_128_MD5:
+		cipher = "ADH-RC4-MD5";
+		break;
+	case TLS_DH_anon_WITH_DES_CBC_SHA:
+		cipher = "ADH-DES-SHA";
+		break;
+	case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA:
+		cipher = "ADH-DES-CBC3-SHA";
+		break;
+	case TLS_RSA_WITH_AES_128_CBC_SHA:
+		cipher = "AES-128-SHA";
+		break;
+	case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+		cipher = "DHE-RSA-AES-128-SHA";
+		break;
+	case TLS_DH_anon_WITH_AES_128_CBC_SHA:
+		cipher = "ADH-AES-128-SHA";
+		break;
+	case TLS_RSA_WITH_AES_256_CBC_SHA:
+		cipher = "AES-256-SHA";
+		break;
+	case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+		cipher = "DHE-RSA-AES-256-SHA";
+		break;
+	case TLS_DH_anon_WITH_AES_256_CBC_SHA:
+		cipher = "ADH-AES-256-SHA";
+		break;
+	case TLS_RSA_WITH_AES_128_CBC_SHA256:
+		cipher = "AES-128-SHA256";
+		break;
+	case TLS_RSA_WITH_AES_256_CBC_SHA256:
+		cipher = "AES-256-SHA256";
+		break;
+	case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+		cipher = "DHE-RSA-AES-128-SHA256";
+		break;
+	case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+		cipher = "DHE-RSA-AES-256-SHA256";
+		break;
+	case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
+		cipher = "ADH-AES-128-SHA256";
+		break;
+	case TLS_DH_anon_WITH_AES_256_CBC_SHA256:
+		cipher = "ADH-AES-256-SHA256";
+		break;
+	default:
+		return -1;
+	}
+
+	if (os_strlcpy(buf, cipher, buflen) >= buflen)
+		return -1;
+	return 0;
+}
+
+
+/**
+ * tlsv1_client_shutdown - Shutdown TLS connection
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_shutdown(struct tlsv1_client *conn)
+{
+	conn->state = CLIENT_HELLO;
+
+	if (tls_verify_hash_init(&conn->verify) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify "
+			   "hash");
+		return -1;
+	}
+
+	tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
+	tlsv1_record_change_write_cipher(&conn->rl);
+	tlsv1_record_change_read_cipher(&conn->rl);
+
+	conn->certificate_requested = 0;
+	crypto_public_key_free(conn->server_rsa_key);
+	conn->server_rsa_key = NULL;
+	conn->session_resumed = 0;
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_client_resumed - Was session resumption used
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * Returns: 1 if current session used session resumption, 0 if not
+ */
+int tlsv1_client_resumed(struct tlsv1_client *conn)
+{
+	return !!conn->session_resumed;
+}
+
+
+/**
+ * tlsv1_client_hello_ext - Set TLS extension for ClientHello
+ * @conn: TLSv1 client connection data from tlsv1_client_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 tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type,
+			   const u8 *data, size_t data_len)
+{
+	u8 *pos;
+
+	conn->session_ticket_included = 0;
+	os_free(conn->client_hello_ext);
+	conn->client_hello_ext = NULL;
+	conn->client_hello_ext_len = 0;
+
+	if (data == NULL || data_len == 0)
+		return 0;
+
+	pos = conn->client_hello_ext = os_malloc(4 + data_len);
+	if (pos == NULL)
+		return -1;
+
+	WPA_PUT_BE16(pos, ext_type);
+	pos += 2;
+	WPA_PUT_BE16(pos, data_len);
+	pos += 2;
+	os_memcpy(pos, data, data_len);
+	conn->client_hello_ext_len = 4 + data_len;
+
+	if (ext_type == TLS_EXT_PAC_OPAQUE) {
+		conn->session_ticket_included = 1;
+		wpa_printf(MSG_DEBUG, "TLSv1: Using session ticket");
+	}
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_client_get_random - Get random data from TLS connection
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @keys: Structure of random data (filled on success)
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_get_random(struct tlsv1_client *conn, struct tls_random *keys)
+{
+	os_memset(keys, 0, sizeof(*keys));
+	if (conn->state == CLIENT_HELLO)
+		return -1;
+
+	keys->client_random = conn->client_random;
+	keys->client_random_len = TLS_RANDOM_LEN;
+
+	if (conn->state != SERVER_HELLO) {
+		keys->server_random = conn->server_random;
+		keys->server_random_len = TLS_RANDOM_LEN;
+	}
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_client_get_keyblock_size - Get TLS key_block size
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * Returns: Size of the key_block for the negotiated cipher suite or -1 on
+ * failure
+ */
+int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn)
+{
+	if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO)
+		return -1;
+
+	return 2 * (conn->rl.hash_size + conn->rl.key_material_len +
+		    conn->rl.iv_size);
+}
+
+
+/**
+ * tlsv1_client_set_cipher_list - Configure acceptable cipher suites
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers
+ * (TLS_CIPHER_*).
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers)
+{
+	size_t count;
+	u16 *suites;
+
+	/* TODO: implement proper configuration of cipher suites */
+	if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) {
+		count = 0;
+		suites = conn->cipher_suites;
+		suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA256;
+		suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA;
+		suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA256;
+		suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
+		suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
+		suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5;
+		suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA;
+
+		/*
+		 * Cisco AP (at least 350 and 1200 series) local authentication
+		 * server does not know how to search cipher suites from the
+		 * list and seem to require that the last entry in the list is
+		 * the one that it wants to use. However, TLS specification
+		 * requires the list to be in the client preference order. As a
+		 * workaround, add anon-DH AES-128-SHA1 again at the end of the
+		 * list to allow the Cisco code to find it.
+		 */
+		suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
+		conn->num_cipher_suites = count;
+	}
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_client_set_cred - Set client credentials
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @cred: Credentials from tlsv1_cred_alloc()
+ * Returns: 0 on success, -1 on failure
+ *
+ * On success, the client takes ownership of the credentials block and caller
+ * must not free it. On failure, caller is responsible for freeing the
+ * credential block.
+ */
+int tlsv1_client_set_cred(struct tlsv1_client *conn,
+			  struct tlsv1_credentials *cred)
+{
+	tlsv1_cred_free(conn->cred);
+	conn->cred = cred;
+	return 0;
+}
+
+
+/**
+ * tlsv1_client_set_flags - Set connection flags
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @flags: TLS_CONN_* bitfield
+ */
+void tlsv1_client_set_flags(struct tlsv1_client *conn, unsigned int flags)
+{
+	conn->flags = flags;
+	conn->rl.tls_version = tls_client_highest_ver(conn);
+}
+
+
+void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn,
+					tlsv1_client_session_ticket_cb cb,
+					void *ctx)
+{
+	wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)",
+		   cb, ctx);
+	conn->session_ticket_cb = cb;
+	conn->session_ticket_cb_ctx = ctx;
+}
+
+
+void tlsv1_client_set_cb(struct tlsv1_client *conn,
+			 void (*event_cb)(void *ctx, enum tls_event ev,
+					  union tls_event_data *data),
+			 void *cb_ctx,
+			 int cert_in_cb)
+{
+	conn->event_cb = event_cb;
+	conn->cb_ctx = cb_ctx;
+	conn->cert_in_cb = !!cert_in_cb;
+}
+
+
+int tlsv1_client_get_version(struct tlsv1_client *conn, char *buf,
+			     size_t buflen)
+{
+	if (!conn)
+		return -1;
+	switch (conn->rl.tls_version) {
+	case TLS_VERSION_1:
+		os_strlcpy(buf, "TLSv1", buflen);
+		break;
+	case TLS_VERSION_1_1:
+		os_strlcpy(buf, "TLSv1.1", buflen);
+		break;
+	case TLS_VERSION_1_2:
+		os_strlcpy(buf, "TLSv1.2", buflen);
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_client.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_client.h
new file mode 100644
index 0000000..7fcc256
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_client.h
@@ -0,0 +1,63 @@
+/*
+ * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246)
+ * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TLSV1_CLIENT_H
+#define TLSV1_CLIENT_H
+
+#include "tlsv1_cred.h"
+
+struct tlsv1_client;
+
+int tlsv1_client_global_init(void);
+void tlsv1_client_global_deinit(void);
+struct tlsv1_client * tlsv1_client_init(void);
+void tlsv1_client_deinit(struct tlsv1_client *conn);
+int tlsv1_client_established(struct tlsv1_client *conn);
+int tlsv1_client_prf(struct tlsv1_client *conn, const char *label,
+		     const u8 *context, size_t context_len,
+		     int server_random_first, u8 *out, size_t out_len);
+u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
+			    const u8 *in_data, size_t in_len,
+			    size_t *out_len, u8 **appl_data,
+			    size_t *appl_data_len, int *need_more_data);
+int tlsv1_client_encrypt(struct tlsv1_client *conn,
+			 const u8 *in_data, size_t in_len,
+			 u8 *out_data, size_t out_len);
+struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn,
+				     const u8 *in_data, size_t in_len,
+				     int *need_more_data);
+int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf,
+			    size_t buflen);
+int tlsv1_client_shutdown(struct tlsv1_client *conn);
+int tlsv1_client_resumed(struct tlsv1_client *conn);
+int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type,
+			   const u8 *data, size_t data_len);
+int tlsv1_client_get_random(struct tlsv1_client *conn, struct tls_random *data);
+int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn);
+int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers);
+int tlsv1_client_set_cred(struct tlsv1_client *conn,
+			  struct tlsv1_credentials *cred);
+void tlsv1_client_set_flags(struct tlsv1_client *conn, unsigned int flags);
+
+typedef int (*tlsv1_client_session_ticket_cb)
+(void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
+ const u8 *server_random, u8 *master_secret);
+
+void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn,
+					tlsv1_client_session_ticket_cb cb,
+					void *ctx);
+
+void tlsv1_client_set_cb(struct tlsv1_client *conn,
+			 void (*event_cb)(void *ctx, enum tls_event ev,
+					  union tls_event_data *data),
+			 void *cb_ctx,
+			 int cert_in_cb);
+int tlsv1_client_get_version(struct tlsv1_client *conn, char *buf,
+			     size_t buflen);
+
+#endif /* TLSV1_CLIENT_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_client_i.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_client_i.h
new file mode 100644
index 0000000..ccb2e15
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_client_i.h
@@ -0,0 +1,102 @@
+/*
+ * TLSv1 client - internal structures
+ * 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.
+ */
+
+#ifndef TLSV1_CLIENT_I_H
+#define TLSV1_CLIENT_I_H
+
+struct tlsv1_client {
+	enum {
+		CLIENT_HELLO, SERVER_HELLO, SERVER_CERTIFICATE,
+		SERVER_KEY_EXCHANGE, SERVER_CERTIFICATE_REQUEST,
+		SERVER_HELLO_DONE, CLIENT_KEY_EXCHANGE, CHANGE_CIPHER_SPEC,
+		SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, ACK_FINISHED,
+		ESTABLISHED, FAILED
+	} state;
+
+	struct tlsv1_record_layer rl;
+
+	u8 session_id[TLS_SESSION_ID_MAX_LEN];
+	size_t session_id_len;
+	u8 client_random[TLS_RANDOM_LEN];
+	u8 server_random[TLS_RANDOM_LEN];
+	u8 master_secret[TLS_MASTER_SECRET_LEN];
+
+	u8 alert_level;
+	u8 alert_description;
+
+	unsigned int flags; /* TLS_CONN_* bitfield */
+
+	unsigned int certificate_requested:1;
+	unsigned int session_resumed:1;
+	unsigned int session_ticket_included:1;
+	unsigned int use_session_ticket:1;
+	unsigned int cert_in_cb:1;
+	unsigned int ocsp_resp_received:1;
+
+	struct crypto_public_key *server_rsa_key;
+
+	struct tls_verify_hash verify;
+
+#define MAX_CIPHER_COUNT 30
+	u16 cipher_suites[MAX_CIPHER_COUNT];
+	size_t num_cipher_suites;
+
+	u16 prev_cipher_suite;
+
+	u8 *client_hello_ext;
+	size_t client_hello_ext_len;
+
+	/* The prime modulus used for Diffie-Hellman */
+	u8 *dh_p;
+	size_t dh_p_len;
+	/* The generator used for Diffie-Hellman */
+	u8 *dh_g;
+	size_t dh_g_len;
+	/* The server's Diffie-Hellman public value */
+	u8 *dh_ys;
+	size_t dh_ys_len;
+
+	struct tlsv1_credentials *cred;
+
+	tlsv1_client_session_ticket_cb session_ticket_cb;
+	void *session_ticket_cb_ctx;
+
+	struct wpabuf *partial_input;
+
+	void (*event_cb)(void *ctx, enum tls_event ev,
+			 union tls_event_data *data);
+	void *cb_ctx;
+
+	struct x509_certificate *server_cert;
+};
+
+
+void tls_alert(struct tlsv1_client *conn, u8 level, u8 description);
+void tlsv1_client_free_dh(struct tlsv1_client *conn);
+u16 tls_client_highest_ver(struct tlsv1_client *conn);
+int tls_derive_pre_master_secret(struct tlsv1_client *conn,
+				 u8 *pre_master_secret);
+int tls_derive_keys(struct tlsv1_client *conn,
+		    const u8 *pre_master_secret, size_t pre_master_secret_len);
+u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len);
+u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level,
+			     u8 description, size_t *out_len);
+u8 * tlsv1_client_handshake_write(struct tlsv1_client *conn, size_t *out_len,
+				  int no_appl_data);
+int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct,
+				   const u8 *buf, size_t *len,
+				   u8 **out_data, size_t *out_len);
+
+enum tls_ocsp_result {
+	TLS_OCSP_NO_RESPONSE, TLS_OCSP_INVALID, TLS_OCSP_GOOD, TLS_OCSP_REVOKED
+};
+
+enum tls_ocsp_result tls_process_ocsp_response(struct tlsv1_client *conn,
+					       const u8 *resp, size_t len);
+
+#endif /* TLSV1_CLIENT_I_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_client_ocsp.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_client_ocsp.c
new file mode 100644
index 0000000..128f4b5
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_client_ocsp.c
@@ -0,0 +1,759 @@
+/*
+ * TLSv1 client - OCSP
+ * Copyright (c) 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 "common.h"
+#include "crypto/tls.h"
+#include "crypto/sha1.h"
+#include "asn1.h"
+#include "x509v3.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+#include "tlsv1_client.h"
+#include "tlsv1_client_i.h"
+
+
+/* RFC 6960, 4.2.1: OCSPResponseStatus ::= ENUMERATED */
+enum ocsp_response_status {
+	OCSP_RESP_STATUS_SUCCESSFUL = 0,
+	OCSP_RESP_STATUS_MALFORMED_REQ = 1,
+	OCSP_RESP_STATUS_INT_ERROR = 2,
+	OCSP_RESP_STATUS_TRY_LATER = 3,
+	/* 4 not used */
+	OCSP_RESP_STATUS_SIG_REQUIRED = 5,
+	OCSP_RESP_STATUS_UNAUTHORIZED = 6,
+};
+
+
+static int is_oid_basic_ocsp_resp(struct asn1_oid *oid)
+{
+	return oid->len == 10 &&
+		oid->oid[0] == 1 /* iso */ &&
+		oid->oid[1] == 3 /* identified-organization */ &&
+		oid->oid[2] == 6 /* dod */ &&
+		oid->oid[3] == 1 /* internet */ &&
+		oid->oid[4] == 5 /* security */ &&
+		oid->oid[5] == 5 /* mechanisms */ &&
+		oid->oid[6] == 7 /* id-pkix */ &&
+		oid->oid[7] == 48 /* id-ad */ &&
+		oid->oid[8] == 1 /* id-pkix-ocsp */ &&
+		oid->oid[9] == 1 /* id-pkix-ocsp-basic */;
+}
+
+
+static int ocsp_responder_id_match(struct x509_certificate *signer,
+				   struct x509_name *name, const u8 *key_hash)
+{
+	if (key_hash) {
+		u8 hash[SHA1_MAC_LEN];
+		const u8 *addr[1] = { signer->public_key };
+		size_t len[1] = { signer->public_key_len };
+
+		if (sha1_vector(1, addr, len, hash) < 0)
+			return 0;
+		return os_memcmp(hash, key_hash, SHA1_MAC_LEN) == 0;
+	}
+
+	return x509_name_compare(&signer->subject, name) == 0;
+}
+
+
+static unsigned int ocsp_hash_data(struct asn1_oid *alg, const u8 *data,
+				   size_t data_len, u8 *hash)
+{
+	const u8 *addr[1] = { data };
+	size_t len[1] = { data_len };
+	char buf[100];
+
+	if (x509_sha1_oid(alg)) {
+		if (sha1_vector(1, addr, len, hash) < 0)
+			return 0;
+		wpa_hexdump(MSG_MSGDUMP, "OCSP: Hash (SHA1)", hash, 20);
+		return 20;
+	}
+
+	if (x509_sha256_oid(alg)) {
+		if (sha256_vector(1, addr, len, hash) < 0)
+			return 0;
+		wpa_hexdump(MSG_MSGDUMP, "OCSP: Hash (SHA256)", hash, 32);
+		return 32;
+	}
+
+	if (x509_sha384_oid(alg)) {
+		if (sha384_vector(1, addr, len, hash) < 0)
+			return 0;
+		wpa_hexdump(MSG_MSGDUMP, "OCSP: Hash (SHA384)", hash, 48);
+		return 48;
+	}
+
+	if (x509_sha512_oid(alg)) {
+		if (sha512_vector(1, addr, len, hash) < 0)
+			return 0;
+		wpa_hexdump(MSG_MSGDUMP, "OCSP: Hash (SHA512)", hash, 64);
+		return 64;
+	}
+
+
+	asn1_oid_to_str(alg, buf, sizeof(buf));
+	wpa_printf(MSG_DEBUG, "OCSP: Could not calculate hash with alg %s",
+		   buf);
+	return 0;
+}
+
+
+static int tls_process_ocsp_single_response(struct tlsv1_client *conn,
+					    struct x509_certificate *cert,
+					    struct x509_certificate *issuer,
+					    const u8 *resp, size_t len,
+					    enum tls_ocsp_result *res)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+	struct x509_algorithm_identifier alg;
+	const u8 *name_hash, *key_hash;
+	size_t name_hash_len, key_hash_len;
+	const u8 *serial_number;
+	size_t serial_number_len;
+	u8 hash[64];
+	unsigned int hash_len;
+	unsigned int cert_status;
+	os_time_t update;
+	struct os_time now;
+
+	wpa_hexdump(MSG_MSGDUMP, "OCSP: SingleResponse", resp, len);
+
+	/*
+	 * SingleResponse ::= SEQUENCE {
+	 *    certID                       CertID,
+	 *    certStatus                   CertStatus,
+	 *    thisUpdate                   GeneralizedTime,
+	 *    nextUpdate         [0]       EXPLICIT GeneralizedTime OPTIONAL,
+	 *    singleExtensions   [1]       EXPLICIT Extensions OPTIONAL }
+	 */
+
+	/* CertID ::= SEQUENCE */
+	if (asn1_get_next(resp, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr, "OCSP: Expected SEQUENCE (CertID)");
+		return -1;
+	}
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	/*
+	 * CertID ::= SEQUENCE {
+	 *    hashAlgorithm           AlgorithmIdentifier,
+	 *    issuerNameHash          OCTET STRING,
+	 *    issuerKeyHash           OCTET STRING,
+	 *    serialNumber            CertificateSerialNumber }
+	 */
+
+	/* hashAlgorithm  AlgorithmIdentifier */
+	if (x509_parse_algorithm_identifier(pos, end - pos, &alg, &pos))
+		return -1;
+
+	/* issuerNameHash  OCTET STRING */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_octetstring(&hdr)) {
+		asn1_unexpected(&hdr,
+				"OCSP: Expected OCTET STRING (issuerNameHash)");
+		return -1;
+	}
+	name_hash = hdr.payload;
+	name_hash_len = hdr.length;
+	wpa_hexdump(MSG_DEBUG, "OCSP: issuerNameHash",
+		    name_hash, name_hash_len);
+	pos = hdr.payload + hdr.length;
+
+	wpa_hexdump(MSG_DEBUG, "OCSP: Issuer subject DN",
+		    issuer->subject_dn, issuer->subject_dn_len);
+	hash_len = ocsp_hash_data(&alg.oid, issuer->subject_dn,
+				  issuer->subject_dn_len, hash);
+	if (hash_len == 0 || name_hash_len != hash_len ||
+	    os_memcmp(name_hash, hash, hash_len) != 0) {
+		wpa_printf(MSG_DEBUG, "OCSP: issuerNameHash mismatch");
+		wpa_hexdump(MSG_DEBUG, "OCSP: Calculated issuerNameHash",
+			    hash, hash_len);
+		return -1;
+	}
+
+	/* issuerKeyHash  OCTET STRING */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_octetstring(&hdr)) {
+		asn1_unexpected(&hdr,
+				"OCSP: Expected OCTET STRING (issuerKeyHash)");
+		return -1;
+	}
+	key_hash = hdr.payload;
+	key_hash_len = hdr.length;
+	wpa_hexdump(MSG_DEBUG, "OCSP: issuerKeyHash", key_hash, key_hash_len);
+	pos = hdr.payload + hdr.length;
+
+	hash_len = ocsp_hash_data(&alg.oid, issuer->public_key,
+				  issuer->public_key_len, hash);
+	if (hash_len == 0 || key_hash_len != hash_len ||
+	    os_memcmp(key_hash, hash, hash_len) != 0) {
+		wpa_printf(MSG_DEBUG, "OCSP: issuerKeyHash mismatch");
+		wpa_hexdump(MSG_DEBUG, "OCSP: Calculated issuerKeyHash",
+			    hash, hash_len);
+		return -1;
+	}
+
+	/* serialNumber CertificateSerialNumber ::= INTEGER */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_integer(&hdr) ||
+	    hdr.length < 1 || hdr.length > X509_MAX_SERIAL_NUM_LEN) {
+		asn1_unexpected(&hdr,
+				"OCSP: No INTEGER tag found for serialNumber");
+		return -1;
+	}
+	serial_number = hdr.payload;
+	serial_number_len = hdr.length;
+	while (serial_number_len > 0 && serial_number[0] == 0) {
+		serial_number++;
+		serial_number_len--;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "OCSP: serialNumber", serial_number,
+		    serial_number_len);
+
+	if (serial_number_len != cert->serial_number_len ||
+	    os_memcmp(serial_number, cert->serial_number,
+		      serial_number_len) != 0) {
+		wpa_printf(MSG_DEBUG, "OCSP: serialNumber mismatch");
+		return -1;
+	}
+
+	pos = end;
+	end = resp + len;
+
+	/* certStatus CertStatus ::= CHOICE
+	 *
+	 * CertStatus ::= CHOICE {
+	 *     good        [0]     IMPLICIT NULL,
+	 *     revoked     [1]     IMPLICIT RevokedInfo,
+	 *     unknown     [2]     IMPLICIT UnknownInfo }
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
+		asn1_unexpected(&hdr, "OCSP: Expected CHOICE (CertStatus)");
+		return -1;
+	}
+	cert_status = hdr.tag;
+	wpa_printf(MSG_DEBUG, "OCSP: certStatus=%u", cert_status);
+	wpa_hexdump(MSG_DEBUG, "OCSP: CertStatus additional data",
+		    hdr.payload, hdr.length);
+	pos = hdr.payload + hdr.length;
+
+	os_get_time(&now);
+	/* thisUpdate  GeneralizedTime */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_generalizedtime(&hdr) ||
+	    x509_parse_time(hdr.payload, hdr.length, hdr.tag, &update) < 0) {
+		wpa_printf(MSG_DEBUG, "OCSP: Failed to parse thisUpdate");
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "OCSP: thisUpdate %lu", (unsigned long) update);
+	pos = hdr.payload + hdr.length;
+	if ((unsigned long) now.sec < (unsigned long) update) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: thisUpdate time in the future (response not yet valid)");
+		return -1;
+	}
+
+	/* nextUpdate  [0]  EXPLICIT GeneralizedTime OPTIONAL */
+	if (pos < end) {
+		if (asn1_get_next(pos, end - pos, &hdr) < 0)
+			return -1;
+		if (asn1_is_cs_tag(&hdr, 0) && hdr.constructed) {
+			const u8 *next = hdr.payload + hdr.length;
+
+			if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 ||
+			    !asn1_is_generalizedtime(&hdr) ||
+			    x509_parse_time(hdr.payload, hdr.length, hdr.tag,
+					    &update) < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "OCSP: Failed to parse nextUpdate");
+				return -1;
+			}
+			wpa_printf(MSG_DEBUG, "OCSP: nextUpdate %lu",
+				   (unsigned long) update);
+			pos = next;
+			if ((unsigned long) now.sec > (unsigned long) update) {
+				wpa_printf(MSG_DEBUG, "OCSP: nextUpdate time in the past (response has expired)");
+				return -1;
+			}
+		}
+	}
+
+	/* singleExtensions  [1]  EXPLICIT Extensions OPTIONAL */
+	if (pos < end) {
+		wpa_hexdump(MSG_MSGDUMP, "OCSP: singleExtensions",
+			    pos, end - pos);
+		/* Ignore for now */
+	}
+
+	if (cert_status == 0 /* good */)
+		*res = TLS_OCSP_GOOD;
+	else if (cert_status == 1 /* revoked */)
+		*res = TLS_OCSP_REVOKED;
+	else
+		return -1;
+	return 0;
+}
+
+
+static enum tls_ocsp_result
+tls_process_ocsp_responses(struct tlsv1_client *conn,
+			   struct x509_certificate *cert,
+			   struct x509_certificate *issuer, const u8 *resp,
+			   size_t len)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+	enum tls_ocsp_result res;
+
+	pos = resp;
+	end = resp + len;
+	while (pos < end) {
+		/* SingleResponse ::= SEQUENCE */
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    !asn1_is_sequence(&hdr)) {
+			asn1_unexpected(&hdr,
+					"OCSP: Expected SEQUENCE (SingleResponse)");
+			return TLS_OCSP_INVALID;
+		}
+		if (tls_process_ocsp_single_response(conn, cert, issuer,
+						     hdr.payload, hdr.length,
+						     &res) == 0)
+			return res;
+		pos = hdr.payload + hdr.length;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "OCSP: Did not find a response matching the server certificate");
+	return TLS_OCSP_NO_RESPONSE;
+}
+
+
+static enum tls_ocsp_result
+tls_process_basic_ocsp_response(struct tlsv1_client *conn,
+				struct x509_certificate *srv_cert,
+				const u8 *resp, size_t len)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+	const u8 *resp_data, *sign_value, *key_hash = NULL, *responses;
+	const u8 *resp_data_signed;
+	size_t resp_data_len, sign_value_len, responses_len;
+	size_t resp_data_signed_len;
+	struct x509_algorithm_identifier alg;
+	struct x509_certificate *certs = NULL, *last_cert = NULL;
+	struct x509_certificate *issuer, *signer;
+	struct x509_name name; /* used if key_hash == NULL */
+	char buf[100];
+	os_time_t produced_at;
+	enum tls_ocsp_result res;
+
+	wpa_hexdump(MSG_MSGDUMP, "OCSP: BasicOCSPResponse", resp, len);
+
+	os_memset(&name, 0, sizeof(name));
+
+	/*
+	 * RFC 6960, 4.2.1:
+	 * BasicOCSPResponse       ::= SEQUENCE {
+	 *    tbsResponseData      ResponseData,
+	 *    signatureAlgorithm   AlgorithmIdentifier,
+	 *    signature            BIT STRING,
+	 *    certs            [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+	 */
+
+	if (asn1_get_next(resp, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"OCSP: Expected SEQUENCE (BasicOCSPResponse)");
+		return TLS_OCSP_INVALID;
+	}
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	/* ResponseData ::= SEQUENCE */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"OCSP: Expected SEQUENCE (ResponseData)");
+		return TLS_OCSP_INVALID;
+	}
+	resp_data = hdr.payload;
+	resp_data_len = hdr.length;
+	resp_data_signed = pos;
+	pos = hdr.payload + hdr.length;
+	resp_data_signed_len = pos - resp_data_signed;
+
+	/* signatureAlgorithm  AlgorithmIdentifier */
+	if (x509_parse_algorithm_identifier(pos, end - pos, &alg, &pos))
+		return TLS_OCSP_INVALID;
+
+	/* signature  BIT STRING */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_bitstring(&hdr)) {
+		asn1_unexpected(&hdr,
+				"OCSP: Expected BITSTRING (signature)");
+		return TLS_OCSP_INVALID;
+	}
+	if (hdr.length < 1)
+		return TLS_OCSP_INVALID;
+	pos = hdr.payload;
+	if (*pos) {
+		wpa_printf(MSG_DEBUG, "OCSP: BITSTRING - %d unused bits", *pos);
+		/* PKCS #1 v1.5 10.2.1:
+		 * It is an error if the length in bits of the signature S is
+		 * not a multiple of eight.
+		 */
+		return TLS_OCSP_INVALID;
+	}
+	sign_value = pos + 1;
+	sign_value_len = hdr.length - 1;
+	pos += hdr.length;
+	wpa_hexdump(MSG_MSGDUMP, "OCSP: signature", sign_value, sign_value_len);
+
+	/* certs  [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL */
+	if (pos < end) {
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    !hdr.constructed || !asn1_is_cs_tag(&hdr, 0)) {
+			asn1_unexpected(&hdr,
+					"OCSP: Expected [0] EXPLICIT (certs)");
+			return TLS_OCSP_INVALID;
+		}
+		wpa_hexdump(MSG_MSGDUMP, "OCSP: certs",
+			    hdr.payload, hdr.length);
+		pos = hdr.payload;
+		end = hdr.payload + hdr.length;
+		while (pos < end) {
+			struct x509_certificate *cert;
+
+			if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+			    !asn1_is_sequence(&hdr)) {
+				asn1_unexpected(&hdr,
+						"OCSP: Expected SEQUENCE (Certificate)");
+				goto fail;
+			}
+
+			cert = x509_certificate_parse(hdr.payload, hdr.length);
+			if (!cert)
+				goto fail;
+			if (last_cert) {
+				last_cert->next = cert;
+				last_cert = cert;
+			} else {
+				last_cert = certs = cert;
+			}
+			pos = hdr.payload + hdr.length;
+		}
+	}
+
+	/*
+	 * ResponseData ::= SEQUENCE {
+	 *    version              [0] EXPLICIT Version DEFAULT v1,
+	 *    responderID              ResponderID,
+	 *    producedAt               GeneralizedTime,
+	 *    responses                SEQUENCE OF SingleResponse,
+	 *    responseExtensions   [1] EXPLICIT Extensions OPTIONAL }
+	 */
+	pos = resp_data;
+	end = resp_data + resp_data_len;
+	wpa_hexdump(MSG_MSGDUMP, "OCSP: ResponseData", pos, end - pos);
+
+	/*
+	 * version [0] EXPLICIT Version DEFAULT v1
+	 * Version ::= INTEGER { v1(0) }
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) == 0 && hdr.constructed &&
+	    asn1_is_cs_tag(&hdr, 0)) {
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    !asn1_is_integer(&hdr) || hdr.length != 1) {
+			asn1_unexpected(&hdr,
+					"OCSP: No INTEGER (len=1) tag found for version field");
+			goto fail;
+		}
+		wpa_printf(MSG_DEBUG, "OCSP: ResponseData version %u",
+			   hdr.payload[0]);
+		if (hdr.payload[0] != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "OCSP: Unsupported ResponseData version %u",
+				   hdr.payload[0]);
+			goto no_resp;
+		}
+		pos = hdr.payload + hdr.length;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Default ResponseData version (v1)");
+	}
+
+	/*
+	 * ResponderID ::= CHOICE {
+	 *    byName              [1] Name,
+	 *    byKey               [2] KeyHash }
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
+		asn1_unexpected(&hdr, "OCSP: Expected CHOICE (ResponderID)");
+		goto fail;
+	}
+
+	if (hdr.tag == 1) {
+		/* Name */
+		if (x509_parse_name(hdr.payload, hdr.length, &name, &pos) < 0)
+			goto fail;
+		x509_name_string(&name, buf, sizeof(buf));
+		wpa_printf(MSG_DEBUG, "OCSP: ResponderID byName Name: %s", buf);
+	} else if (hdr.tag == 2) {
+		/* KeyHash ::= OCTET STRING */
+		if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 ||
+		    !asn1_is_octetstring(&hdr)) {
+			asn1_unexpected(&hdr,
+					"OCSP: Expected OCTET STRING (KeyHash)");
+			goto fail;
+		}
+		key_hash = hdr.payload;
+		wpa_hexdump(MSG_DEBUG, "OCSP: ResponderID byKey KeyHash",
+			    key_hash, hdr.length);
+		if (hdr.length != SHA1_MAC_LEN) {
+			wpa_printf(MSG_DEBUG,
+				   "OCSP: Unexpected byKey KeyHash length %u - expected %u for SHA-1",
+				   hdr.length, SHA1_MAC_LEN);
+			goto fail;
+		}
+		pos = hdr.payload + hdr.length;
+	} else {
+		wpa_printf(MSG_DEBUG, "OCSP: Unexpected ResponderID CHOICE %u",
+			   hdr.tag);
+		goto fail;
+	}
+
+	/* producedAt  GeneralizedTime */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_generalizedtime(&hdr) ||
+	    x509_parse_time(hdr.payload, hdr.length, hdr.tag,
+			    &produced_at) < 0) {
+		wpa_printf(MSG_DEBUG, "OCSP: Failed to parse producedAt");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "OCSP: producedAt %lu",
+		   (unsigned long) produced_at);
+	pos = hdr.payload + hdr.length;
+
+	/* responses  SEQUENCE OF SingleResponse */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"OCSP: Expected SEQUENCE (responses)");
+		goto fail;
+	}
+	responses = hdr.payload;
+	responses_len = hdr.length;
+	wpa_hexdump(MSG_MSGDUMP, "OCSP: responses", responses, responses_len);
+	pos = hdr.payload + hdr.length;
+
+	if (pos < end) {
+		/* responseExtensions  [1] EXPLICIT Extensions OPTIONAL */
+		wpa_hexdump(MSG_MSGDUMP, "OCSP: responseExtensions",
+			    pos, end - pos);
+		/* Ignore for now. */
+	}
+
+	if (!srv_cert) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Server certificate not known - cannot check OCSP response");
+		goto no_resp;
+	}
+
+	if (srv_cert->next) {
+		/* Issuer has already been verified in the chain */
+		issuer = srv_cert->next;
+	} else {
+		/* Find issuer from the set of trusted certificates */
+		for (issuer = conn->cred ? conn->cred->trusted_certs : NULL;
+		     issuer; issuer = issuer->next) {
+			if (x509_name_compare(&srv_cert->issuer,
+					      &issuer->subject) == 0)
+				break;
+		}
+	}
+	if (!issuer) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Server certificate issuer not known - cannot check OCSP response");
+		goto no_resp;
+	}
+
+	if (ocsp_responder_id_match(issuer, &name, key_hash)) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Server certificate issuer certificate matches ResponderID");
+		signer = issuer;
+	} else {
+		for (signer = certs; signer; signer = signer->next) {
+			if (!ocsp_responder_id_match(signer, &name, key_hash) ||
+			    x509_name_compare(&srv_cert->issuer,
+					      &issuer->subject) != 0 ||
+			    !(signer->ext_key_usage &
+			      X509_EXT_KEY_USAGE_OCSP) ||
+			    x509_certificate_check_signature(issuer, signer) <
+			    0)
+				continue;
+			wpa_printf(MSG_DEBUG,
+				   "OCSP: An extra certificate from the response matches ResponderID and is trusted as an OCSP signer");
+			break;
+		}
+		if (!signer) {
+			wpa_printf(MSG_DEBUG,
+				   "OCSP: Could not find OCSP signer certificate");
+			goto no_resp;
+		}
+	}
+
+	x509_free_name(&name);
+	os_memset(&name, 0, sizeof(name));
+	x509_certificate_chain_free(certs);
+	certs = NULL;
+
+	if (x509_check_signature(signer, &alg, sign_value, sign_value_len,
+				 resp_data_signed, resp_data_signed_len) < 0) {
+		    wpa_printf(MSG_DEBUG, "OCSP: Invalid signature");
+		    return TLS_OCSP_INVALID;
+	}
+
+	res = tls_process_ocsp_responses(conn, srv_cert, issuer,
+					 responses, responses_len);
+	if (res == TLS_OCSP_REVOKED)
+		srv_cert->ocsp_revoked = 1;
+	else if (res == TLS_OCSP_GOOD)
+		srv_cert->ocsp_good = 1;
+	return res;
+
+no_resp:
+	x509_free_name(&name);
+	x509_certificate_chain_free(certs);
+	return TLS_OCSP_NO_RESPONSE;
+
+fail:
+	x509_free_name(&name);
+	x509_certificate_chain_free(certs);
+	return TLS_OCSP_INVALID;
+}
+
+
+enum tls_ocsp_result tls_process_ocsp_response(struct tlsv1_client *conn,
+					       const u8 *resp, size_t len)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+	u8 resp_status;
+	struct asn1_oid oid;
+	char obuf[80];
+	struct x509_certificate *cert;
+	enum tls_ocsp_result res = TLS_OCSP_NO_RESPONSE;
+	enum tls_ocsp_result res_first = res;
+
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: OCSPResponse", resp, len);
+
+	/*
+	 * RFC 6960, 4.2.1:
+	 * OCSPResponse ::= SEQUENCE {
+	 *    responseStatus  OCSPResponseStatus,
+	 *    responseBytes   [0] EXPLICIT ResponseBytes OPTIONAL }
+	 */
+
+	if (asn1_get_next(resp, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"OCSP: Expected SEQUENCE (OCSPResponse)");
+		return TLS_OCSP_INVALID;
+	}
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	/* OCSPResponseStatus ::= ENUMERATED */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_enumerated(&hdr) || hdr.length != 1) {
+		asn1_unexpected(&hdr,
+				"OCSP: Expected ENUMERATED (responseStatus)");
+		return TLS_OCSP_INVALID;
+	}
+	resp_status = hdr.payload[0];
+	wpa_printf(MSG_DEBUG, "OCSP: responseStatus %u", resp_status);
+	pos = hdr.payload + hdr.length;
+	if (resp_status != OCSP_RESP_STATUS_SUCCESSFUL) {
+		wpa_printf(MSG_DEBUG, "OCSP: No stapling result");
+		return TLS_OCSP_NO_RESPONSE;
+	}
+
+	/* responseBytes   [0] EXPLICIT ResponseBytes OPTIONAL */
+	if (pos == end)
+		return TLS_OCSP_NO_RESPONSE;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 || !hdr.constructed ||
+	    !asn1_is_cs_tag(&hdr, 0)) {
+		asn1_unexpected(&hdr,
+				"OCSP: Expected [0] EXPLICIT (responseBytes)");
+		return TLS_OCSP_INVALID;
+	}
+
+	/*
+	 * ResponseBytes ::= SEQUENCE {
+	 *     responseType   OBJECT IDENTIFIER,
+	 *     response       OCTET STRING }
+	 */
+
+	if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 ||
+	    !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"OCSP: Expected SEQUENCE (ResponseBytes)");
+		return TLS_OCSP_INVALID;
+	}
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	/* responseType   OBJECT IDENTIFIER */
+	if (asn1_get_oid(pos, end - pos, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Failed to parse OID (responseType)");
+		return TLS_OCSP_INVALID;
+	}
+	asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+	wpa_printf(MSG_DEBUG, "OCSP: responseType %s", obuf);
+	if (!is_oid_basic_ocsp_resp(&oid)) {
+		wpa_printf(MSG_DEBUG, "OCSP: Ignore unsupported response type");
+		return TLS_OCSP_NO_RESPONSE;
+	}
+
+	/* response       OCTET STRING */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_octetstring(&hdr)) {
+		asn1_unexpected(&hdr, "OCSP: Expected OCTET STRING (response)");
+		return TLS_OCSP_INVALID;
+	}
+
+	cert = conn->server_cert;
+	while (cert) {
+		if (!cert->ocsp_good && !cert->ocsp_revoked) {
+			char sbuf[128];
+
+			x509_name_string(&cert->subject, sbuf, sizeof(sbuf));
+			wpa_printf(MSG_DEBUG,
+				   "OCSP: Trying to find certificate status for %s",
+				   sbuf);
+
+			res = tls_process_basic_ocsp_response(conn, cert,
+							      hdr.payload,
+							      hdr.length);
+			if (cert == conn->server_cert)
+				res_first = res;
+		}
+		if (res == TLS_OCSP_REVOKED || cert->issuer_trusted)
+			break;
+		cert = cert->next;
+	}
+	return res == TLS_OCSP_REVOKED ? res : res_first;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_client_read.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_client_read.c
new file mode 100644
index 0000000..9df56c2
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_client_read.c
@@ -0,0 +1,1562 @@
+/*
+ * TLSv1 client - read handshake message
+ * Copyright (c) 2006-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 "common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/tls.h"
+#include "x509v3.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+#include "tlsv1_client.h"
+#include "tlsv1_client_i.h"
+
+static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct,
+					   const u8 *in_data, size_t *in_len);
+static int tls_process_certificate_request(struct tlsv1_client *conn, u8 ct,
+					   const u8 *in_data, size_t *in_len);
+static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct,
+					 const u8 *in_data, size_t *in_len);
+
+
+static int tls_version_disabled(struct tlsv1_client *conn, u16 ver)
+{
+	return (((conn->flags & TLS_CONN_DISABLE_TLSv1_0) &&
+		 ver == TLS_VERSION_1) ||
+		((conn->flags & TLS_CONN_DISABLE_TLSv1_1) &&
+		 ver == TLS_VERSION_1_1) ||
+		((conn->flags & TLS_CONN_DISABLE_TLSv1_2) &&
+		 ver == TLS_VERSION_1_2));
+}
+
+
+static int tls_process_server_hello_extensions(struct tlsv1_client *conn,
+					       const u8 *pos, size_t len)
+{
+	const u8 *end = pos + len;
+
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerHello extensions",
+		    pos, len);
+	while (pos < end) {
+		u16 ext, elen;
+
+		if (end - pos < 4) {
+			wpa_printf(MSG_INFO, "TLSv1: Truncated ServerHello extension header");
+			return -1;
+		}
+
+		ext = WPA_GET_BE16(pos);
+		pos += 2;
+		elen = WPA_GET_BE16(pos);
+		pos += 2;
+
+		if (elen > end - pos) {
+			wpa_printf(MSG_INFO, "TLSv1: Truncated ServerHello extension");
+			return -1;
+		}
+
+		wpa_printf(MSG_DEBUG, "TLSv1: ServerHello ExtensionType %u",
+			   ext);
+		wpa_hexdump(MSG_DEBUG, "TLSv1: ServerHello extension data",
+			    pos, elen);
+
+		pos += elen;
+	}
+
+	return 0;
+}
+
+
+static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct,
+				    const u8 *in_data, size_t *in_len)
+{
+	const u8 *pos, *end;
+	size_t left, len, i;
+	u16 cipher_suite;
+	u16 tls_version;
+
+	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+			   "received content type 0x%x", ct);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 4)
+		goto decode_error;
+
+	/* HandshakeType msg_type */
+	if (*pos != TLS_HANDSHAKE_TYPE_SERVER_HELLO) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+			   "message %d (expected ServerHello)", *pos);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHello");
+	pos++;
+	/* uint24 length */
+	len = WPA_GET_BE24(pos);
+	pos += 3;
+	left -= 4;
+
+	if (len > left)
+		goto decode_error;
+
+	/* body - ServerHello */
+
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerHello", pos, len);
+	end = pos + len;
+
+	/* ProtocolVersion server_version */
+	if (end - pos < 2)
+		goto decode_error;
+	tls_version = WPA_GET_BE16(pos);
+	if (!tls_version_ok(tls_version) ||
+	    tls_version_disabled(conn, tls_version)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in "
+			   "ServerHello %u.%u", pos[0], pos[1]);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_PROTOCOL_VERSION);
+		return -1;
+	}
+	pos += 2;
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s",
+		   tls_version_str(tls_version));
+	conn->rl.tls_version = tls_version;
+
+	/* Random random */
+	if (end - pos < TLS_RANDOM_LEN)
+		goto decode_error;
+
+	os_memcpy(conn->server_random, pos, TLS_RANDOM_LEN);
+	pos += TLS_RANDOM_LEN;
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: server_random",
+		    conn->server_random, TLS_RANDOM_LEN);
+
+	/* SessionID session_id */
+	if (end - pos < 1)
+		goto decode_error;
+	if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN)
+		goto decode_error;
+	if (conn->session_id_len && conn->session_id_len == *pos &&
+	    os_memcmp(conn->session_id, pos + 1, conn->session_id_len) == 0) {
+		pos += 1 + conn->session_id_len;
+		wpa_printf(MSG_DEBUG, "TLSv1: Resuming old session");
+		conn->session_resumed = 1;
+	} else {
+		conn->session_id_len = *pos;
+		pos++;
+		os_memcpy(conn->session_id, pos, conn->session_id_len);
+		pos += conn->session_id_len;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: session_id",
+		    conn->session_id, conn->session_id_len);
+
+	/* CipherSuite cipher_suite */
+	if (end - pos < 2)
+		goto decode_error;
+	cipher_suite = WPA_GET_BE16(pos);
+	pos += 2;
+	for (i = 0; i < conn->num_cipher_suites; i++) {
+		if (cipher_suite == conn->cipher_suites[i])
+			break;
+	}
+	if (i == conn->num_cipher_suites) {
+		wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected "
+			   "cipher suite 0x%04x", cipher_suite);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_ILLEGAL_PARAMETER);
+		return -1;
+	}
+
+	if (conn->session_resumed && cipher_suite != conn->prev_cipher_suite) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Server selected a different "
+			   "cipher suite for a resumed connection (0x%04x != "
+			   "0x%04x)", cipher_suite, conn->prev_cipher_suite);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_ILLEGAL_PARAMETER);
+		return -1;
+	}
+
+	if (tlsv1_record_set_cipher_suite(&conn->rl, cipher_suite) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to set CipherSuite for "
+			   "record layer");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	conn->prev_cipher_suite = cipher_suite;
+
+	/* CompressionMethod compression_method */
+	if (end - pos < 1)
+		goto decode_error;
+	if (*pos != TLS_COMPRESSION_NULL) {
+		wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected "
+			   "compression 0x%02x", *pos);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_ILLEGAL_PARAMETER);
+		return -1;
+	}
+	pos++;
+
+	if (end - pos >= 2) {
+		u16 ext_len;
+
+		ext_len = WPA_GET_BE16(pos);
+		pos += 2;
+		if (end - pos < ext_len) {
+			wpa_printf(MSG_INFO,
+				   "TLSv1: Invalid ServerHello extension length: %u (left: %u)",
+				   ext_len, (unsigned int) (end - pos));
+			goto decode_error;
+		}
+
+		if (tls_process_server_hello_extensions(conn, pos, ext_len))
+			goto decode_error;
+		pos += ext_len;
+	}
+
+	if (end != pos) {
+		wpa_hexdump(MSG_DEBUG, "TLSv1: Unexpected extra data in the "
+			    "end of ServerHello", pos, end - pos);
+		goto decode_error;
+	}
+
+	if (conn->session_ticket_included && conn->session_ticket_cb) {
+		/* TODO: include SessionTicket extension if one was included in
+		 * ServerHello */
+		int res = conn->session_ticket_cb(
+			conn->session_ticket_cb_ctx, NULL, 0,
+			conn->client_random, conn->server_random,
+			conn->master_secret);
+		if (res < 0) {
+			wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback "
+				   "indicated failure");
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				  TLS_ALERT_HANDSHAKE_FAILURE);
+			return -1;
+		}
+		conn->use_session_ticket = !!res;
+	}
+
+	if ((conn->session_resumed || conn->use_session_ticket) &&
+	    tls_derive_keys(conn, NULL, 0)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	*in_len = end - in_data;
+
+	conn->state = (conn->session_resumed || conn->use_session_ticket) ?
+		SERVER_CHANGE_CIPHER_SPEC : SERVER_CERTIFICATE;
+
+	return 0;
+
+decode_error:
+	wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ServerHello");
+	tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+	return -1;
+}
+
+
+static void tls_peer_cert_event(struct tlsv1_client *conn, int depth,
+				struct x509_certificate *cert)
+{
+	union tls_event_data ev;
+	struct wpabuf *cert_buf = NULL;
+#ifdef CONFIG_SHA256
+	u8 hash[32];
+#endif /* CONFIG_SHA256 */
+	char subject[128];
+
+	if (!conn->event_cb)
+		return;
+
+	os_memset(&ev, 0, sizeof(ev));
+	if ((conn->cred && conn->cred->cert_probe) || conn->cert_in_cb) {
+		cert_buf = wpabuf_alloc_copy(cert->cert_start,
+					     cert->cert_len);
+		ev.peer_cert.cert = cert_buf;
+	}
+#ifdef CONFIG_SHA256
+	if (cert_buf) {
+		const u8 *addr[1];
+		size_t len[1];
+		addr[0] = wpabuf_head(cert_buf);
+		len[0] = wpabuf_len(cert_buf);
+		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;
+	x509_name_string(&cert->subject, subject, sizeof(subject));
+	ev.peer_cert.subject = subject;
+
+	if (cert->extensions_present & X509_EXT_CERTIFICATE_POLICY) {
+		if (cert->certificate_policy & X509_EXT_CERT_POLICY_TOD_STRICT)
+			ev.peer_cert.tod = 1;
+		else if (cert->certificate_policy &
+			 X509_EXT_CERT_POLICY_TOD_TOFU)
+			ev.peer_cert.tod = 2;
+	}
+
+	conn->event_cb(conn->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
+	wpabuf_free(cert_buf);
+}
+
+
+static void tls_cert_chain_failure_event(struct tlsv1_client *conn, int depth,
+					 struct x509_certificate *cert,
+					 enum tls_fail_reason reason,
+					 const char *reason_txt)
+{
+	struct wpabuf *cert_buf = NULL;
+	union tls_event_data ev;
+	char subject[128];
+
+	if (!conn->event_cb || !cert)
+		return;
+
+	os_memset(&ev, 0, sizeof(ev));
+	ev.cert_fail.depth = depth;
+	x509_name_string(&cert->subject, subject, sizeof(subject));
+	ev.peer_cert.subject = subject;
+	ev.cert_fail.reason = reason;
+	ev.cert_fail.reason_txt = reason_txt;
+	cert_buf = wpabuf_alloc_copy(cert->cert_start,
+				     cert->cert_len);
+	ev.cert_fail.cert = cert_buf;
+	conn->event_cb(conn->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
+	wpabuf_free(cert_buf);
+}
+
+
+static int tls_process_certificate(struct tlsv1_client *conn, u8 ct,
+				   const u8 *in_data, size_t *in_len)
+{
+	const u8 *pos, *end;
+	size_t left, len, list_len, cert_len, idx;
+	u8 type;
+	struct x509_certificate *chain = NULL, *last = NULL, *cert;
+	int reason;
+
+	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+			   "received content type 0x%x", ct);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 4) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message "
+			   "(len=%lu)", (unsigned long) left);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	type = *pos++;
+	len = WPA_GET_BE24(pos);
+	pos += 3;
+	left -= 4;
+
+	if (len > left) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message "
+			   "length (len=%lu != left=%lu)",
+			   (unsigned long) len, (unsigned long) left);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	if (type == TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE)
+		return tls_process_server_key_exchange(conn, ct, in_data,
+						       in_len);
+	if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST)
+		return tls_process_certificate_request(conn, ct, in_data,
+						       in_len);
+	if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE)
+		return tls_process_server_hello_done(conn, ct, in_data,
+						     in_len);
+	if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+			   "message %d (expected Certificate/"
+			   "ServerKeyExchange/CertificateRequest/"
+			   "ServerHelloDone)", type);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "TLSv1: Received Certificate (certificate_list len %lu)",
+		   (unsigned long) len);
+
+	/*
+	 * opaque ASN.1Cert<2^24-1>;
+	 *
+	 * struct {
+	 *     ASN.1Cert certificate_list<1..2^24-1>;
+	 * } Certificate;
+	 */
+
+	end = pos + len;
+
+	if (end - pos < 3) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate "
+			   "(left=%lu)", (unsigned long) left);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	list_len = WPA_GET_BE24(pos);
+	pos += 3;
+
+	if ((size_t) (end - pos) != list_len) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate_list "
+			   "length (len=%lu left=%lu)",
+			   (unsigned long) list_len,
+			   (unsigned long) (end - pos));
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	idx = 0;
+	while (pos < end) {
+		if (end - pos < 3) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+				   "certificate_list");
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				  TLS_ALERT_DECODE_ERROR);
+			x509_certificate_chain_free(chain);
+			return -1;
+		}
+
+		cert_len = WPA_GET_BE24(pos);
+		pos += 3;
+
+		if ((size_t) (end - pos) < cert_len) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate "
+				   "length (len=%lu left=%lu)",
+				   (unsigned long) cert_len,
+				   (unsigned long) (end - pos));
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				  TLS_ALERT_DECODE_ERROR);
+			x509_certificate_chain_free(chain);
+			return -1;
+		}
+
+		wpa_printf(MSG_DEBUG, "TLSv1: Certificate %lu (len %lu)",
+			   (unsigned long) idx, (unsigned long) cert_len);
+
+		if (idx == 0) {
+			crypto_public_key_free(conn->server_rsa_key);
+			if (tls_parse_cert(pos, cert_len,
+					   &conn->server_rsa_key)) {
+				wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+					   "the certificate");
+				tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					  TLS_ALERT_BAD_CERTIFICATE);
+				x509_certificate_chain_free(chain);
+				return -1;
+			}
+		}
+
+		cert = x509_certificate_parse(pos, cert_len);
+		if (cert == NULL) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+				   "the certificate");
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				  TLS_ALERT_BAD_CERTIFICATE);
+			x509_certificate_chain_free(chain);
+			return -1;
+		}
+
+		tls_peer_cert_event(conn, idx, cert);
+
+		if (last == NULL)
+			chain = cert;
+		else
+			last->next = cert;
+		last = cert;
+
+		idx++;
+		pos += cert_len;
+	}
+
+	if (conn->cred && conn->cred->server_cert_only && chain) {
+		u8 hash[SHA256_MAC_LEN];
+		char buf[128];
+
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Validate server certificate hash");
+		x509_name_string(&chain->subject, buf, sizeof(buf));
+		wpa_printf(MSG_DEBUG, "TLSv1: 0: %s", buf);
+		if (sha256_vector(1, &chain->cert_start, &chain->cert_len,
+				  hash) < 0 ||
+		    os_memcmp(conn->cred->srv_cert_hash, hash,
+			      SHA256_MAC_LEN) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "TLSv1: Server certificate hash mismatch");
+			wpa_hexdump(MSG_MSGDUMP, "TLSv1: SHA256 hash",
+				    hash, SHA256_MAC_LEN);
+			if (conn->event_cb) {
+				union tls_event_data ev;
+
+				os_memset(&ev, 0, sizeof(ev));
+				ev.cert_fail.reason = TLS_FAIL_UNSPECIFIED;
+				ev.cert_fail.reason_txt =
+					"Server certificate mismatch";
+				ev.cert_fail.subject = buf;
+				conn->event_cb(conn->cb_ctx,
+					       TLS_CERT_CHAIN_FAILURE, &ev);
+			}
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				  TLS_ALERT_BAD_CERTIFICATE);
+			x509_certificate_chain_free(chain);
+			return -1;
+		}
+	} else if (conn->cred && conn->cred->cert_probe) {
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Reject server certificate on probe-only run");
+		if (conn->event_cb) {
+			union tls_event_data ev;
+			char buf[128];
+
+			os_memset(&ev, 0, sizeof(ev));
+			ev.cert_fail.reason = TLS_FAIL_SERVER_CHAIN_PROBE;
+			ev.cert_fail.reason_txt =
+				"Server certificate chain probe";
+			if (chain) {
+				x509_name_string(&chain->subject, buf,
+						 sizeof(buf));
+				ev.cert_fail.subject = buf;
+			}
+			conn->event_cb(conn->cb_ctx, TLS_CERT_CHAIN_FAILURE,
+				       &ev);
+		}
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_BAD_CERTIFICATE);
+		x509_certificate_chain_free(chain);
+		return -1;
+	} else if (conn->cred && conn->cred->ca_cert_verify &&
+		   x509_certificate_chain_validate(
+			   conn->cred->trusted_certs, chain, &reason,
+			   !!(conn->flags & TLS_CONN_DISABLE_TIME_CHECKS))
+		   < 0) {
+		int tls_reason;
+		wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain "
+			   "validation failed (reason=%d)", reason);
+		switch (reason) {
+		case X509_VALIDATE_BAD_CERTIFICATE:
+			tls_reason = TLS_ALERT_BAD_CERTIFICATE;
+			tls_cert_chain_failure_event(
+				conn, 0, chain, TLS_FAIL_BAD_CERTIFICATE,
+				"bad certificate");
+			break;
+		case X509_VALIDATE_UNSUPPORTED_CERTIFICATE:
+			tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE;
+			break;
+		case X509_VALIDATE_CERTIFICATE_REVOKED:
+			tls_reason = TLS_ALERT_CERTIFICATE_REVOKED;
+			tls_cert_chain_failure_event(
+				conn, 0, chain, TLS_FAIL_REVOKED,
+				"certificate revoked");
+			break;
+		case X509_VALIDATE_CERTIFICATE_EXPIRED:
+			tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED;
+			tls_cert_chain_failure_event(
+				conn, 0, chain, TLS_FAIL_EXPIRED,
+				"certificate has expired or is not yet valid");
+			break;
+		case X509_VALIDATE_CERTIFICATE_UNKNOWN:
+			tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN;
+			break;
+		case X509_VALIDATE_UNKNOWN_CA:
+			tls_reason = TLS_ALERT_UNKNOWN_CA;
+			tls_cert_chain_failure_event(
+				conn, 0, chain, TLS_FAIL_UNTRUSTED,
+				"unknown CA");
+			break;
+		default:
+			tls_reason = TLS_ALERT_BAD_CERTIFICATE;
+			break;
+		}
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, tls_reason);
+		x509_certificate_chain_free(chain);
+		return -1;
+	}
+
+	if (conn->cred && !conn->cred->server_cert_only && chain &&
+	    (chain->extensions_present & X509_EXT_EXT_KEY_USAGE) &&
+	    !(chain->ext_key_usage &
+	      (X509_EXT_KEY_USAGE_ANY | X509_EXT_KEY_USAGE_SERVER_AUTH))) {
+		tls_cert_chain_failure_event(
+			conn, 0, chain, TLS_FAIL_BAD_CERTIFICATE,
+			"certificate not allowed for server authentication");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_BAD_CERTIFICATE);
+		x509_certificate_chain_free(chain);
+		return -1;
+	}
+
+	if (conn->flags & TLS_CONN_REQUEST_OCSP) {
+		x509_certificate_chain_free(conn->server_cert);
+		conn->server_cert = chain;
+	} else {
+		x509_certificate_chain_free(chain);
+	}
+
+	*in_len = end - in_data;
+
+	conn->state = SERVER_KEY_EXCHANGE;
+
+	return 0;
+}
+
+
+static unsigned int count_bits(const u8 *val, size_t len)
+{
+	size_t i;
+	unsigned int bits;
+	u8 tmp;
+
+	for (i = 0; i < len; i++) {
+		if (val[i])
+			break;
+	}
+	if (i == len)
+		return 0;
+
+	bits = (len - i - 1) * 8;
+	tmp = val[i];
+	while (tmp) {
+		bits++;
+		tmp >>= 1;
+	}
+
+	return bits;
+}
+
+
+static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
+					const u8 *buf, size_t len,
+					tls_key_exchange key_exchange)
+{
+	const u8 *pos, *end, *server_params, *server_params_end;
+	u8 alert;
+	unsigned int bits;
+	u16 val;
+
+	tlsv1_client_free_dh(conn);
+
+	pos = buf;
+	end = buf + len;
+
+	if (end - pos < 3)
+		goto fail;
+	server_params = pos;
+	val = WPA_GET_BE16(pos);
+	pos += 2;
+	if (val == 0 || val > (size_t) (end - pos)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Invalid dh_p length %u", val);
+		goto fail;
+	}
+	conn->dh_p_len = val;
+	bits = count_bits(pos, conn->dh_p_len);
+	if (bits < 768) {
+		wpa_printf(MSG_INFO, "TLSv1: Reject under 768-bit DH prime (insecure; only %u bits)",
+			   bits);
+		wpa_hexdump(MSG_DEBUG, "TLSv1: Rejected DH prime",
+			    pos, conn->dh_p_len);
+		goto fail;
+	}
+	conn->dh_p = os_memdup(pos, conn->dh_p_len);
+	if (conn->dh_p == NULL)
+		goto fail;
+	pos += conn->dh_p_len;
+	wpa_hexdump(MSG_DEBUG, "TLSv1: DH p (prime)",
+		    conn->dh_p, conn->dh_p_len);
+
+	if (end - pos < 3)
+		goto fail;
+	val = WPA_GET_BE16(pos);
+	pos += 2;
+	if (val == 0 || val > (size_t) (end - pos))
+		goto fail;
+	conn->dh_g_len = val;
+	conn->dh_g = os_memdup(pos, conn->dh_g_len);
+	if (conn->dh_g == NULL)
+		goto fail;
+	pos += conn->dh_g_len;
+	wpa_hexdump(MSG_DEBUG, "TLSv1: DH g (generator)",
+		    conn->dh_g, conn->dh_g_len);
+	if (conn->dh_g_len == 1 && conn->dh_g[0] < 2)
+		goto fail;
+
+	if (end - pos < 3)
+		goto fail;
+	val = WPA_GET_BE16(pos);
+	pos += 2;
+	if (val == 0 || val > (size_t) (end - pos))
+		goto fail;
+	conn->dh_ys_len = val;
+	conn->dh_ys = os_memdup(pos, conn->dh_ys_len);
+	if (conn->dh_ys == NULL)
+		goto fail;
+	pos += conn->dh_ys_len;
+	wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)",
+		    conn->dh_ys, conn->dh_ys_len);
+	server_params_end = pos;
+
+	if (key_exchange == TLS_KEY_X_DHE_RSA) {
+		u8 hash[64];
+		int hlen;
+
+		if (conn->rl.tls_version == TLS_VERSION_1_2) {
+#ifdef CONFIG_TLSV12
+			/*
+			 * RFC 5246, 4.7:
+			 * TLS v1.2 adds explicit indication of the used
+			 * signature and hash algorithms.
+			 *
+			 * struct {
+			 *   HashAlgorithm hash;
+			 *   SignatureAlgorithm signature;
+			 * } SignatureAndHashAlgorithm;
+			 */
+			if (end - pos < 2)
+				goto fail;
+			if ((pos[0] != TLS_HASH_ALG_SHA256 &&
+			     pos[0] != TLS_HASH_ALG_SHA384 &&
+			     pos[0] != TLS_HASH_ALG_SHA512) ||
+			    pos[1] != TLS_SIGN_ALG_RSA) {
+				wpa_printf(MSG_DEBUG, "TLSv1.2: Unsupported hash(%u)/signature(%u) algorithm",
+					   pos[0], pos[1]);
+				goto fail;
+			}
+
+			hlen = tlsv12_key_x_server_params_hash(
+				conn->rl.tls_version, pos[0],
+				conn->client_random,
+				conn->server_random, server_params,
+				server_params_end - server_params, hash);
+			pos += 2;
+#else /* CONFIG_TLSV12 */
+			goto fail;
+#endif /* CONFIG_TLSV12 */
+		} else {
+			hlen = tls_key_x_server_params_hash(
+				conn->rl.tls_version, conn->client_random,
+				conn->server_random, server_params,
+				server_params_end - server_params, hash,
+				sizeof(hash));
+		}
+
+		if (hlen < 0)
+			goto fail;
+		wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerKeyExchange hash",
+			    hash, hlen);
+
+		if (tls_verify_signature(conn->rl.tls_version,
+					 conn->server_rsa_key,
+					 hash, hlen, pos, end - pos,
+					 &alert) < 0)
+			goto fail;
+	}
+
+	return 0;
+
+fail:
+	wpa_printf(MSG_DEBUG, "TLSv1: Processing DH params failed");
+	tlsv1_client_free_dh(conn);
+	return -1;
+}
+
+
+static enum tls_ocsp_result
+tls_process_certificate_status_ocsp_response(struct tlsv1_client *conn,
+					     const u8 *pos, size_t len)
+{
+	const u8 *end = pos + len;
+	u32 ocsp_resp_len;
+
+	/* opaque OCSPResponse<1..2^24-1>; */
+	if (end - pos < 3) {
+		wpa_printf(MSG_INFO, "TLSv1: Too short OCSPResponse");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return TLS_OCSP_INVALID;
+	}
+	ocsp_resp_len = WPA_GET_BE24(pos);
+	pos += 3;
+	if (end - pos < ocsp_resp_len) {
+		wpa_printf(MSG_INFO, "TLSv1: Truncated OCSPResponse");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return TLS_OCSP_INVALID;
+	}
+
+	return tls_process_ocsp_response(conn, pos, ocsp_resp_len);
+}
+
+
+static int tls_process_certificate_status(struct tlsv1_client *conn, u8 ct,
+					   const u8 *in_data, size_t *in_len)
+{
+	const u8 *pos, *end;
+	size_t left, len;
+	u8 type, status_type;
+	enum tls_ocsp_result res;
+	struct x509_certificate *cert;
+	int depth;
+
+	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Expected Handshake; received content type 0x%x",
+			   ct);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 4) {
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Too short CertificateStatus (left=%lu)",
+			   (unsigned long) left);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	type = *pos++;
+	len = WPA_GET_BE24(pos);
+	pos += 3;
+	left -= 4;
+
+	if (len > left) {
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Mismatch in CertificateStatus length (len=%lu != left=%lu)",
+			   (unsigned long) len, (unsigned long) left);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	end = pos + len;
+
+	if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS) {
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Received unexpected handshake message %d (expected CertificateStatus)",
+			   type);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateStatus");
+
+	/*
+	 * struct {
+	 *     CertificateStatusType status_type;
+	 *     select (status_type) {
+	 *         case ocsp: OCSPResponse;
+	 *         case ocsp_multi: OCSPResponseList;
+	 *     } response;
+	 * } CertificateStatus;
+	 */
+	if (end - pos < 1) {
+		wpa_printf(MSG_INFO, "TLSv1: Too short CertificateStatus");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+	status_type = *pos++;
+	wpa_printf(MSG_DEBUG, "TLSv1: CertificateStatus status_type %u",
+		   status_type);
+
+	if (status_type == 1 /* ocsp */) {
+		res = tls_process_certificate_status_ocsp_response(
+			conn, pos, end - pos);
+	} else if (status_type == 2 /* ocsp_multi */) {
+		int good = 0, revoked = 0;
+		u32 resp_len;
+
+		res = TLS_OCSP_NO_RESPONSE;
+
+		/*
+		 * opaque OCSPResponse<0..2^24-1>;
+		 *
+		 * struct {
+		 *   OCSPResponse ocsp_response_list<1..2^24-1>;
+		 * } OCSPResponseList;
+		 */
+		if (end - pos < 3) {
+			wpa_printf(MSG_DEBUG,
+				   "TLSv1: Truncated OCSPResponseList");
+			res = TLS_OCSP_INVALID;
+			goto done;
+		}
+		resp_len = WPA_GET_BE24(pos);
+		pos += 3;
+		if (end - pos < resp_len) {
+			wpa_printf(MSG_DEBUG,
+				   "TLSv1: Truncated OCSPResponseList(len=%u)",
+				   resp_len);
+			res = TLS_OCSP_INVALID;
+			goto done;
+		}
+		end = pos + resp_len;
+
+		while (end - pos >= 3) {
+			resp_len = WPA_GET_BE24(pos);
+			pos += 3;
+			if (resp_len > end - pos) {
+				wpa_printf(MSG_DEBUG,
+					   "TLSv1: Truncated OCSPResponse(len=%u; left=%d) in ocsp_multi",
+					   resp_len, (int) (end - pos));
+				res = TLS_OCSP_INVALID;
+				break;
+			}
+			if (!resp_len)
+				continue; /* Skip an empty response */
+			res = tls_process_certificate_status_ocsp_response(
+				conn, pos - 3, resp_len + 3);
+			if (res == TLS_OCSP_REVOKED)
+				revoked++;
+			else if (res == TLS_OCSP_GOOD)
+				good++;
+			pos += resp_len;
+		}
+
+		if (revoked)
+			res = TLS_OCSP_REVOKED;
+		else if (good)
+			res = TLS_OCSP_GOOD;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Ignore unsupported CertificateStatus");
+		goto skip;
+	}
+
+done:
+	if (res == TLS_OCSP_REVOKED) {
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_CERTIFICATE_REVOKED);
+		for (cert = conn->server_cert, depth = 0; cert;
+		     cert = cert->next, depth++) {
+			if (cert->ocsp_revoked) {
+				tls_cert_chain_failure_event(
+					conn, depth, cert, TLS_FAIL_REVOKED,
+					"certificate revoked");
+			}
+		}
+		return -1;
+	}
+
+	if (conn->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
+		/*
+		 * Verify that each certificate on the chain that is not part
+		 * of the trusted certificates has a good status. If not,
+		 * terminate handshake.
+		 */
+		for (cert = conn->server_cert, depth = 0; cert;
+		     cert = cert->next, depth++) {
+			if (!cert->ocsp_good) {
+				tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					  TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE);
+				tls_cert_chain_failure_event(
+					conn, depth, cert,
+					TLS_FAIL_UNSPECIFIED,
+					"bad certificate status response");
+				return -1;
+			}
+			if (cert->issuer_trusted)
+				break;
+		}
+	}
+
+	if ((conn->flags & TLS_CONN_REQUIRE_OCSP) && res != TLS_OCSP_GOOD) {
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  res == TLS_OCSP_INVALID ? TLS_ALERT_DECODE_ERROR :
+			  TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE);
+		if (conn->server_cert)
+			tls_cert_chain_failure_event(
+				conn, 0, conn->server_cert,
+				TLS_FAIL_UNSPECIFIED,
+				"bad certificate status response");
+		return -1;
+	}
+
+	conn->ocsp_resp_received = 1;
+
+skip:
+	*in_len = end - in_data;
+
+	conn->state = SERVER_KEY_EXCHANGE;
+
+	return 0;
+}
+
+
+static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct,
+					   const u8 *in_data, size_t *in_len)
+{
+	const u8 *pos, *end;
+	size_t left, len;
+	u8 type;
+	const struct tls_cipher_suite *suite;
+
+	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+			   "received content type 0x%x", ct);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 4) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerKeyExchange "
+			   "(Left=%lu)", (unsigned long) left);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	type = *pos++;
+	len = WPA_GET_BE24(pos);
+	pos += 3;
+	left -= 4;
+
+	if (len > left) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerKeyExchange "
+			   "length (len=%lu != left=%lu)",
+			   (unsigned long) len, (unsigned long) left);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	end = pos + len;
+
+	if ((conn->flags & TLS_CONN_REQUEST_OCSP) &&
+	    type == TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS)
+		return tls_process_certificate_status(conn, ct, in_data,
+						      in_len);
+	if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST)
+		return tls_process_certificate_request(conn, ct, in_data,
+						       in_len);
+	if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE)
+		return tls_process_server_hello_done(conn, ct, in_data,
+						     in_len);
+	if (type != TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+			   "message %d (expected ServerKeyExchange/"
+			   "CertificateRequest/ServerHelloDone%s)", type,
+			   (conn->flags & TLS_CONN_REQUEST_OCSP) ?
+			   "/CertificateStatus" : "");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Received ServerKeyExchange");
+
+	if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not allowed "
+			   "with the selected cipher suite");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "TLSv1: ServerKeyExchange", pos, len);
+	suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+	if (suite && (suite->key_exchange == TLS_KEY_X_DH_anon ||
+		      suite->key_exchange == TLS_KEY_X_DHE_RSA)) {
+		if (tlsv1_process_diffie_hellman(conn, pos, len,
+						 suite->key_exchange) < 0) {
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				  TLS_ALERT_DECODE_ERROR);
+			return -1;
+		}
+	} else {
+		wpa_printf(MSG_DEBUG, "TLSv1: UnexpectedServerKeyExchange");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	*in_len = end - in_data;
+
+	conn->state = SERVER_CERTIFICATE_REQUEST;
+
+	return 0;
+}
+
+
+static int tls_process_certificate_request(struct tlsv1_client *conn, u8 ct,
+					   const u8 *in_data, size_t *in_len)
+{
+	const u8 *pos, *end;
+	size_t left, len;
+	u8 type;
+
+	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+			   "received content type 0x%x", ct);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 4) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateRequest "
+			   "(left=%lu)", (unsigned long) left);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	type = *pos++;
+	len = WPA_GET_BE24(pos);
+	pos += 3;
+	left -= 4;
+
+	if (len > left) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in CertificateRequest "
+			   "length (len=%lu != left=%lu)",
+			   (unsigned long) len, (unsigned long) left);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	end = pos + len;
+
+	if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE)
+		return tls_process_server_hello_done(conn, ct, in_data,
+						     in_len);
+	if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+			   "message %d (expected CertificateRequest/"
+			   "ServerHelloDone)", type);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateRequest");
+
+	conn->certificate_requested = 1;
+
+	*in_len = end - in_data;
+
+	conn->state = SERVER_HELLO_DONE;
+
+	return 0;
+}
+
+
+static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct,
+					 const u8 *in_data, size_t *in_len)
+{
+	const u8 *pos, *end;
+	size_t left, len;
+	u8 type;
+
+	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+			   "received content type 0x%x", ct);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 4) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerHelloDone "
+			   "(left=%lu)", (unsigned long) left);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	type = *pos++;
+	len = WPA_GET_BE24(pos);
+	pos += 3;
+	left -= 4;
+
+	if (len > left) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerHelloDone "
+			   "length (len=%lu != left=%lu)",
+			   (unsigned long) len, (unsigned long) left);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+	end = pos + len;
+
+	if (type != TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+			   "message %d (expected ServerHelloDone)", type);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHelloDone");
+
+	if ((conn->flags & TLS_CONN_REQUIRE_OCSP) &&
+	    !conn->ocsp_resp_received) {
+		wpa_printf(MSG_INFO,
+			   "TLSv1: No OCSP response received - reject handshake");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE);
+		return -1;
+	}
+
+	*in_len = end - in_data;
+
+	conn->state = CLIENT_KEY_EXCHANGE;
+
+	return 0;
+}
+
+
+static int tls_process_server_change_cipher_spec(struct tlsv1_client *conn,
+						 u8 ct, const u8 *in_data,
+						 size_t *in_len)
+{
+	const u8 *pos;
+	size_t left;
+
+	if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
+			   "received content type 0x%x", ct);
+		if (conn->use_session_ticket) {
+			int res;
+			wpa_printf(MSG_DEBUG, "TLSv1: Server may have "
+				   "rejected SessionTicket");
+			conn->use_session_ticket = 0;
+
+			/* Notify upper layers that SessionTicket failed */
+			res = conn->session_ticket_cb(
+				conn->session_ticket_cb_ctx, NULL, 0, NULL,
+				NULL, NULL);
+			if (res < 0) {
+				wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket "
+					   "callback indicated failure");
+				tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					  TLS_ALERT_HANDSHAKE_FAILURE);
+				return -1;
+			}
+
+			conn->state = SERVER_CERTIFICATE;
+			return tls_process_certificate(conn, ct, in_data,
+						       in_len);
+		}
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 1) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	if (*pos != TLS_CHANGE_CIPHER_SPEC) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
+			   "received data 0x%x", *pos);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec");
+	if (tlsv1_record_change_read_cipher(&conn->rl) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher "
+			   "for record layer");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	*in_len = pos + 1 - in_data;
+
+	conn->state = SERVER_FINISHED;
+
+	return 0;
+}
+
+
+static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct,
+				       const u8 *in_data, size_t *in_len)
+{
+	const u8 *pos, *end;
+	size_t left, len, hlen;
+	u8 verify_data[TLS_VERIFY_DATA_LEN];
+	u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+
+	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; "
+			   "received content type 0x%x", ct);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 4) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for "
+			   "Finished",
+			   (unsigned long) left);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	if (pos[0] != TLS_HANDSHAKE_TYPE_FINISHED) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; received "
+			   "type 0x%x", pos[0]);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	len = WPA_GET_BE24(pos + 1);
+
+	pos += 4;
+	left -= 4;
+
+	if (len > left) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Too short buffer for Finished "
+			   "(len=%lu > left=%lu)",
+			   (unsigned long) len, (unsigned long) left);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+	end = pos + len;
+	if (len != TLS_VERIFY_DATA_LEN) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected verify_data length "
+			   "in Finished: %lu (expected %d)",
+			   (unsigned long) len, TLS_VERIFY_DATA_LEN);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished",
+		    pos, TLS_VERIFY_DATA_LEN);
+
+#ifdef CONFIG_TLSV12
+	if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+		hlen = SHA256_MAC_LEN;
+		if (conn->verify.sha256_server == NULL ||
+		    crypto_hash_finish(conn->verify.sha256_server, hash, &hlen)
+		    < 0) {
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				  TLS_ALERT_INTERNAL_ERROR);
+			conn->verify.sha256_server = NULL;
+			return -1;
+		}
+		conn->verify.sha256_server = NULL;
+	} else {
+#endif /* CONFIG_TLSV12 */
+
+	hlen = MD5_MAC_LEN;
+	if (conn->verify.md5_server == NULL ||
+	    crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) {
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		conn->verify.md5_server = NULL;
+		crypto_hash_finish(conn->verify.sha1_server, NULL, NULL);
+		conn->verify.sha1_server = NULL;
+		return -1;
+	}
+	conn->verify.md5_server = NULL;
+	hlen = SHA1_MAC_LEN;
+	if (conn->verify.sha1_server == NULL ||
+	    crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN,
+			       &hlen) < 0) {
+		conn->verify.sha1_server = NULL;
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	conn->verify.sha1_server = NULL;
+	hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+	}
+#endif /* CONFIG_TLSV12 */
+
+	if (tls_prf(conn->rl.tls_version,
+		    conn->master_secret, TLS_MASTER_SECRET_LEN,
+		    "server finished", hash, hlen,
+		    verify_data, TLS_VERIFY_DATA_LEN)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_DECRYPT_ERROR);
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)",
+			verify_data, TLS_VERIFY_DATA_LEN);
+
+	if (os_memcmp_const(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
+		wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_DECRYPT_ERROR);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Received Finished");
+
+	*in_len = end - in_data;
+
+	conn->state = (conn->session_resumed || conn->use_session_ticket) ?
+		CHANGE_CIPHER_SPEC : ACK_FINISHED;
+
+	return 0;
+}
+
+
+static int tls_process_application_data(struct tlsv1_client *conn, u8 ct,
+					const u8 *in_data, size_t *in_len,
+					u8 **out_data, size_t *out_len)
+{
+	const u8 *pos;
+	size_t left;
+
+	if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Expected Application Data; "
+			   "received content type 0x%x", ct);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	wpa_hexdump(MSG_DEBUG, "TLSv1: Application Data included in Handshake",
+		    pos, left);
+
+	*out_data = os_malloc(left);
+	if (*out_data) {
+		os_memcpy(*out_data, pos, left);
+		*out_len = left;
+	}
+
+	return 0;
+}
+
+
+int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct,
+				   const u8 *buf, size_t *len,
+				   u8 **out_data, size_t *out_len)
+{
+	if (ct == TLS_CONTENT_TYPE_ALERT) {
+		if (*len < 2) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow");
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				  TLS_ALERT_DECODE_ERROR);
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
+			   buf[0], buf[1]);
+		*len = 2;
+		conn->state = FAILED;
+		return -1;
+	}
+
+	if (ct == TLS_CONTENT_TYPE_HANDSHAKE && *len >= 4 &&
+	    buf[0] == TLS_HANDSHAKE_TYPE_HELLO_REQUEST) {
+		size_t hr_len = WPA_GET_BE24(buf + 1);
+		if (hr_len > *len - 4) {
+			wpa_printf(MSG_DEBUG, "TLSv1: HelloRequest underflow");
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				  TLS_ALERT_DECODE_ERROR);
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG, "TLSv1: Ignored HelloRequest");
+		*len = 4 + hr_len;
+		return 0;
+	}
+
+	switch (conn->state) {
+	case SERVER_HELLO:
+		if (tls_process_server_hello(conn, ct, buf, len))
+			return -1;
+		break;
+	case SERVER_CERTIFICATE:
+		if (tls_process_certificate(conn, ct, buf, len))
+			return -1;
+		break;
+	case SERVER_KEY_EXCHANGE:
+		if (tls_process_server_key_exchange(conn, ct, buf, len))
+			return -1;
+		break;
+	case SERVER_CERTIFICATE_REQUEST:
+		if (tls_process_certificate_request(conn, ct, buf, len))
+			return -1;
+		break;
+	case SERVER_HELLO_DONE:
+		if (tls_process_server_hello_done(conn, ct, buf, len))
+			return -1;
+		break;
+	case SERVER_CHANGE_CIPHER_SPEC:
+		if (tls_process_server_change_cipher_spec(conn, ct, buf, len))
+			return -1;
+		break;
+	case SERVER_FINISHED:
+		if (tls_process_server_finished(conn, ct, buf, len))
+			return -1;
+		break;
+	case ACK_FINISHED:
+		if (out_data &&
+		    tls_process_application_data(conn, ct, buf, len, out_data,
+						 out_len))
+			return -1;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d "
+			   "while processing received message",
+			   conn->state);
+		return -1;
+	}
+
+	if (ct == TLS_CONTENT_TYPE_HANDSHAKE)
+		tls_verify_hash_add(&conn->verify, buf, *len);
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_client_write.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_client_write.c
new file mode 100644
index 0000000..9b12618
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_client_write.c
@@ -0,0 +1,997 @@
+/*
+ * TLSv1 client - write handshake message
+ * Copyright (c) 2006-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 "common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/tls.h"
+#include "crypto/random.h"
+#include "x509v3.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+#include "tlsv1_client.h"
+#include "tlsv1_client_i.h"
+
+
+static size_t tls_client_cert_chain_der_len(struct tlsv1_client *conn)
+{
+	size_t len = 0;
+	struct x509_certificate *cert;
+
+	if (conn->cred == NULL)
+		return 0;
+
+	cert = conn->cred->cert;
+	while (cert) {
+		len += 3 + cert->cert_len;
+		if (x509_certificate_self_signed(cert))
+			break;
+		cert = x509_certificate_get_subject(conn->cred->trusted_certs,
+						    &cert->issuer);
+	}
+
+	return len;
+}
+
+
+u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len)
+{
+	u8 *hello, *end, *pos, *hs_length, *hs_start, *rhdr;
+	struct os_time now;
+	size_t len, i;
+	u8 *ext_start;
+	u16 tls_version = tls_client_highest_ver(conn);
+
+	if (!tls_version) {
+		wpa_printf(MSG_INFO, "TLSv1: No TLS version allowed");
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello (ver %s)",
+		   tls_version_str(tls_version));
+	*out_len = 0;
+
+	os_get_time(&now);
+#ifdef TEST_FUZZ
+	now.sec = 0xfffefdfc;
+#endif /* TEST_FUZZ */
+	WPA_PUT_BE32(conn->client_random, now.sec);
+	if (random_get_bytes(conn->client_random + 4, TLS_RANDOM_LEN - 4)) {
+		wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
+			   "client_random");
+		return NULL;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random",
+		    conn->client_random, TLS_RANDOM_LEN);
+
+	len = 150 + conn->num_cipher_suites * 2 + conn->client_hello_ext_len;
+	hello = os_malloc(len);
+	if (hello == NULL)
+		return NULL;
+	end = hello + len;
+
+	rhdr = hello;
+	pos = rhdr + TLS_RECORD_HEADER_LEN;
+
+	/* opaque fragment[TLSPlaintext.length] */
+
+	/* Handshake */
+	hs_start = pos;
+	/* HandshakeType msg_type */
+	*pos++ = TLS_HANDSHAKE_TYPE_CLIENT_HELLO;
+	/* uint24 length (to be filled) */
+	hs_length = pos;
+	pos += 3;
+	/* body - ClientHello */
+	/* ProtocolVersion client_version */
+	WPA_PUT_BE16(pos, tls_version);
+	pos += 2;
+	/* Random random: uint32 gmt_unix_time, opaque random_bytes */
+	os_memcpy(pos, conn->client_random, TLS_RANDOM_LEN);
+	pos += TLS_RANDOM_LEN;
+	/* SessionID session_id */
+	*pos++ = conn->session_id_len;
+	os_memcpy(pos, conn->session_id, conn->session_id_len);
+	pos += conn->session_id_len;
+	/* CipherSuite cipher_suites<2..2^16-1> */
+	WPA_PUT_BE16(pos, 2 * conn->num_cipher_suites);
+	pos += 2;
+	for (i = 0; i < conn->num_cipher_suites; i++) {
+		WPA_PUT_BE16(pos, conn->cipher_suites[i]);
+		pos += 2;
+	}
+	/* CompressionMethod compression_methods<1..2^8-1> */
+	*pos++ = 1;
+	*pos++ = TLS_COMPRESSION_NULL;
+
+	/* Extension */
+	ext_start = pos;
+	pos += 2;
+
+#ifdef CONFIG_TLSV12
+	if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+		/*
+		 * Add signature_algorithms extension since we support only
+		 * SHA256 (and not the default SHA1) with TLSv1.2.
+		 */
+		/* ExtensionsType extension_type = signature_algorithms(13) */
+		WPA_PUT_BE16(pos, TLS_EXT_SIGNATURE_ALGORITHMS);
+		pos += 2;
+		/* opaque extension_data<0..2^16-1> length */
+		WPA_PUT_BE16(pos, 8);
+		pos += 2;
+		/* supported_signature_algorithms<2..2^16-2> length */
+		WPA_PUT_BE16(pos, 6);
+		pos += 2;
+		/* supported_signature_algorithms */
+		*pos++ = TLS_HASH_ALG_SHA512;
+		*pos++ = TLS_SIGN_ALG_RSA;
+		*pos++ = TLS_HASH_ALG_SHA384;
+		*pos++ = TLS_SIGN_ALG_RSA;
+		*pos++ = TLS_HASH_ALG_SHA256;
+		*pos++ = TLS_SIGN_ALG_RSA;
+	}
+#endif /* CONFIG_TLSV12 */
+
+	if (conn->client_hello_ext) {
+		os_memcpy(pos, conn->client_hello_ext,
+			  conn->client_hello_ext_len);
+		pos += conn->client_hello_ext_len;
+	}
+
+	if (conn->flags & TLS_CONN_REQUEST_OCSP) {
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Add status_request extension for OCSP stapling");
+		/* ExtensionsType extension_type = status_request(5) */
+		WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST);
+		pos += 2;
+		/* opaque extension_data<0..2^16-1> length */
+		WPA_PUT_BE16(pos, 5);
+		pos += 2;
+
+		/*
+		 * RFC 6066, 8:
+		 * struct {
+		 *     CertificateStatusType status_type;
+		 *     select (status_type) {
+		 *         case ocsp: OCSPStatusRequest;
+		 *     } request;
+		 * } CertificateStatusRequest;
+		 *
+		 * enum { ocsp(1), (255) } CertificateStatusType;
+		 */
+		*pos++ = 1; /* status_type = ocsp(1) */
+
+		/*
+		 * struct {
+		 *     ResponderID responder_id_list<0..2^16-1>;
+		 *     Extensions  request_extensions;
+		 * } OCSPStatusRequest;
+		 *
+		 * opaque ResponderID<1..2^16-1>;
+		 * opaque Extensions<0..2^16-1>;
+		 */
+		WPA_PUT_BE16(pos, 0); /* responder_id_list(empty) */
+		pos += 2;
+		WPA_PUT_BE16(pos, 0); /* request_extensions(empty) */
+		pos += 2;
+
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Add status_request_v2 extension for OCSP stapling");
+		/* ExtensionsType extension_type = status_request_v2(17) */
+		WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST_V2);
+		pos += 2;
+		/* opaque extension_data<0..2^16-1> length */
+		WPA_PUT_BE16(pos, 7);
+		pos += 2;
+
+		/*
+		 * RFC 6961, 2.2:
+		 * struct {
+		 *     CertificateStatusType status_type;
+		 *     uint16 request_length;
+		 *     select (status_type) {
+		 *         case ocsp: OCSPStatusRequest;
+		 *         case ocsp_multi: OCSPStatusRequest;
+		 *     } request;
+		 * } CertificateStatusRequestItemV2;
+		 *
+		 * enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType;
+		 *
+		 * struct {
+		 * CertificateStatusRequestItemV2
+		 *     certificate_status_req_list<1..2^16-1>;
+		 * } CertificateStatusRequestListV2;
+		 */
+
+		/* certificate_status_req_list<1..2^16-1> */
+		WPA_PUT_BE16(pos, 5);
+		pos += 2;
+
+		/* CertificateStatusRequestItemV2 */
+		*pos++ = 2; /* status_type = ocsp_multi(2) */
+		/* OCSPStatusRequest as shown above for v1 */
+		WPA_PUT_BE16(pos, 0); /* responder_id_list(empty) */
+		pos += 2;
+		WPA_PUT_BE16(pos, 0); /* request_extensions(empty) */
+		pos += 2;
+	}
+
+	if (pos == ext_start + 2)
+		pos -= 2; /* no extensions */
+	else
+		WPA_PUT_BE16(ext_start, pos - ext_start - 2);
+
+	WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+			      rhdr, end - rhdr, hs_start, pos - hs_start,
+			      out_len) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		os_free(hello);
+		return NULL;
+	}
+
+	conn->state = SERVER_HELLO;
+
+	return hello;
+}
+
+
+static int tls_write_client_certificate(struct tlsv1_client *conn,
+					u8 **msgpos, u8 *end)
+{
+	u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start;
+	size_t rlen;
+	struct x509_certificate *cert;
+
+	pos = *msgpos;
+	if (TLS_RECORD_HEADER_LEN + 1 + 3 + 3 > end - pos) {
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate");
+	rhdr = pos;
+	pos += TLS_RECORD_HEADER_LEN;
+
+	/* opaque fragment[TLSPlaintext.length] */
+
+	/* Handshake */
+	hs_start = pos;
+	/* HandshakeType msg_type */
+	*pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE;
+	/* uint24 length (to be filled) */
+	hs_length = pos;
+	pos += 3;
+	/* body - Certificate */
+	/* uint24 length (to be filled) */
+	cert_start = pos;
+	pos += 3;
+	cert = conn->cred ? conn->cred->cert : NULL;
+	while (cert) {
+		if (3 + cert->cert_len > (size_t) (end - pos)) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space "
+				   "for Certificate (cert_len=%lu left=%lu)",
+				   (unsigned long) cert->cert_len,
+				   (unsigned long) (end - pos));
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				  TLS_ALERT_INTERNAL_ERROR);
+			return -1;
+		}
+		WPA_PUT_BE24(pos, cert->cert_len);
+		pos += 3;
+		os_memcpy(pos, cert->cert_start, cert->cert_len);
+		pos += cert->cert_len;
+
+		if (x509_certificate_self_signed(cert))
+			break;
+		cert = x509_certificate_get_subject(conn->cred->trusted_certs,
+						    &cert->issuer);
+	}
+	if (conn->cred == NULL || cert == conn->cred->cert || cert == NULL) {
+		/*
+		 * Client was not configured with all the needed certificates
+		 * to form a full certificate chain. The server may fail to
+		 * validate the chain unless it is configured with all the
+		 * missing CA certificates.
+		 */
+		wpa_printf(MSG_DEBUG, "TLSv1: Full client certificate chain "
+			   "not configured - validation may fail");
+	}
+	WPA_PUT_BE24(cert_start, pos - cert_start - 3);
+
+	WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+			      rhdr, end - rhdr, hs_start, pos - hs_start,
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	pos = rhdr + rlen;
+
+	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+	*msgpos = pos;
+
+	return 0;
+}
+
+
+static int tlsv1_key_x_dh(struct tlsv1_client *conn, u8 **pos, u8 *end)
+{
+	/* ClientDiffieHellmanPublic */
+	u8 *csecret, *csecret_start, *dh_yc, *shared;
+	size_t csecret_len, dh_yc_len, shared_len;
+
+	csecret_len = conn->dh_p_len;
+	csecret = os_malloc(csecret_len);
+	if (csecret == NULL) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+			   "memory for Yc (Diffie-Hellman)");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	if (random_get_bytes(csecret, csecret_len)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
+			   "data for Diffie-Hellman");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		os_free(csecret);
+		return -1;
+	}
+
+	if (os_memcmp(csecret, conn->dh_p, csecret_len) > 0)
+		csecret[0] = 0; /* make sure Yc < p */
+
+	csecret_start = csecret;
+	while (csecret_len > 1 && *csecret_start == 0) {
+		csecret_start++;
+		csecret_len--;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "TLSv1: DH client's secret value",
+			csecret_start, csecret_len);
+
+	/* Yc = g^csecret mod p */
+	dh_yc_len = conn->dh_p_len;
+	dh_yc = os_malloc(dh_yc_len);
+	if (dh_yc == NULL) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+			   "memory for Diffie-Hellman");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		os_free(csecret);
+		return -1;
+	}
+	if (crypto_mod_exp(conn->dh_g, conn->dh_g_len,
+			   csecret_start, csecret_len,
+			   conn->dh_p, conn->dh_p_len,
+			   dh_yc, &dh_yc_len)) {
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		os_free(csecret);
+		os_free(dh_yc);
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)",
+		    dh_yc, dh_yc_len);
+
+	if (end - *pos < 2) {
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		os_free(csecret);
+		os_free(dh_yc);
+		return -1;
+	}
+	WPA_PUT_BE16(*pos, dh_yc_len);
+	*pos += 2;
+	if (dh_yc_len > (size_t) (end - *pos)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Not enough room in the "
+			   "message buffer for Yc");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		os_free(csecret);
+		os_free(dh_yc);
+		return -1;
+	}
+	os_memcpy(*pos, dh_yc, dh_yc_len);
+	*pos += dh_yc_len;
+	os_free(dh_yc);
+
+	shared_len = conn->dh_p_len;
+	shared = os_malloc(shared_len);
+	if (shared == NULL) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for "
+			   "DH");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		os_free(csecret);
+		return -1;
+	}
+
+	/* shared = Ys^csecret mod p */
+	if (crypto_mod_exp(conn->dh_ys, conn->dh_ys_len,
+			   csecret_start, csecret_len,
+			   conn->dh_p, conn->dh_p_len,
+			   shared, &shared_len)) {
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		os_free(csecret);
+		os_free(shared);
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "TLSv1: Shared secret from DH key exchange",
+			shared, shared_len);
+
+	os_memset(csecret_start, 0, csecret_len);
+	os_free(csecret);
+	if (tls_derive_keys(conn, shared, shared_len)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		os_free(shared);
+		return -1;
+	}
+	os_memset(shared, 0, shared_len);
+	os_free(shared);
+	tlsv1_client_free_dh(conn);
+	return 0;
+}
+
+
+static int tlsv1_key_x_rsa(struct tlsv1_client *conn, u8 **pos, u8 *end)
+{
+	u8 pre_master_secret[TLS_PRE_MASTER_SECRET_LEN];
+	size_t clen;
+	int res;
+
+	if (tls_derive_pre_master_secret(conn, pre_master_secret) < 0 ||
+	    tls_derive_keys(conn, pre_master_secret,
+			    TLS_PRE_MASTER_SECRET_LEN)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	/* EncryptedPreMasterSecret */
+	if (conn->server_rsa_key == NULL) {
+		wpa_printf(MSG_DEBUG, "TLSv1: No server RSA key to "
+			   "use for encrypting pre-master secret");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	/* RSA encrypted value is encoded with PKCS #1 v1.5 block type 2. */
+	*pos += 2;
+	clen = end - *pos;
+	res = crypto_public_key_encrypt_pkcs1_v15(
+		conn->server_rsa_key,
+		pre_master_secret, TLS_PRE_MASTER_SECRET_LEN,
+		*pos, &clen);
+	os_memset(pre_master_secret, 0, TLS_PRE_MASTER_SECRET_LEN);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: RSA encryption failed");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	WPA_PUT_BE16(*pos - 2, clen);
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: Encrypted pre_master_secret",
+		    *pos, clen);
+	*pos += clen;
+
+	return 0;
+}
+
+
+static int tls_write_client_key_exchange(struct tlsv1_client *conn,
+					 u8 **msgpos, u8 *end)
+{
+	u8 *pos, *rhdr, *hs_start, *hs_length;
+	size_t rlen;
+	tls_key_exchange keyx;
+	const struct tls_cipher_suite *suite;
+
+	suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+	if (suite == NULL)
+		keyx = TLS_KEY_X_NULL;
+	else
+		keyx = suite->key_exchange;
+
+	pos = *msgpos;
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Send ClientKeyExchange");
+
+	rhdr = pos;
+	pos += TLS_RECORD_HEADER_LEN;
+
+	/* opaque fragment[TLSPlaintext.length] */
+
+	/* Handshake */
+	hs_start = pos;
+	/* HandshakeType msg_type */
+	*pos++ = TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE;
+	/* uint24 length (to be filled) */
+	hs_length = pos;
+	pos += 3;
+	/* body - ClientKeyExchange */
+	if (keyx == TLS_KEY_X_DH_anon || keyx == TLS_KEY_X_DHE_RSA) {
+		if (tlsv1_key_x_dh(conn, &pos, end) < 0)
+			return -1;
+	} else {
+		if (tlsv1_key_x_rsa(conn, &pos, end) < 0)
+			return -1;
+	}
+
+	WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+			      rhdr, end - rhdr, hs_start, pos - hs_start,
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	pos = rhdr + rlen;
+	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+	*msgpos = pos;
+
+	return 0;
+}
+
+
+static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
+					       u8 **msgpos, u8 *end)
+{
+	u8 *pos, *rhdr, *hs_start, *hs_length, *signed_start;
+	size_t rlen, hlen, clen;
+	u8 hash[100], *hpos;
+
+	pos = *msgpos;
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateVerify");
+	rhdr = pos;
+	pos += TLS_RECORD_HEADER_LEN;
+
+	/* Handshake */
+	hs_start = pos;
+	/* HandshakeType msg_type */
+	*pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY;
+	/* uint24 length (to be filled) */
+	hs_length = pos;
+	pos += 3;
+
+	/*
+	 * RFC 2246: 7.4.3 and 7.4.8:
+	 * Signature signature
+	 *
+	 * RSA:
+	 * digitally-signed struct {
+	 *     opaque md5_hash[16];
+	 *     opaque sha_hash[20];
+	 * };
+	 *
+	 * DSA:
+	 * digitally-signed struct {
+	 *     opaque sha_hash[20];
+	 * };
+	 *
+	 * The hash values are calculated over all handshake messages sent or
+	 * received starting at ClientHello up to, but not including, this
+	 * CertificateVerify message, including the type and length fields of
+	 * the handshake messages.
+	 */
+
+	hpos = hash;
+
+#ifdef CONFIG_TLSV12
+	if (conn->rl.tls_version == TLS_VERSION_1_2) {
+		hlen = SHA256_MAC_LEN;
+		if (conn->verify.sha256_cert == NULL ||
+		    crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) <
+		    0) {
+			conn->verify.sha256_cert = NULL;
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				  TLS_ALERT_INTERNAL_ERROR);
+			return -1;
+		}
+		conn->verify.sha256_cert = NULL;
+
+		/*
+		 * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5
+		 *
+		 * DigestInfo ::= SEQUENCE {
+		 *   digestAlgorithm DigestAlgorithm,
+		 *   digest OCTET STRING
+		 * }
+		 *
+		 * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11}
+		 *
+		 * DER encoded DigestInfo for SHA256 per RFC 3447:
+		 * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 ||
+		 * H
+		 */
+		os_memmove(hash + 19, hash, hlen);
+		hlen += 19;
+		os_memcpy(hash, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65"
+			  "\x03\x04\x02\x01\x05\x00\x04\x20", 19);
+	} else {
+#endif /* CONFIG_TLSV12 */
+
+	hlen = MD5_MAC_LEN;
+	if (conn->verify.md5_cert == NULL ||
+	    crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) {
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		conn->verify.md5_cert = NULL;
+		crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL);
+		conn->verify.sha1_cert = NULL;
+		return -1;
+	}
+	hpos += MD5_MAC_LEN;
+
+	conn->verify.md5_cert = NULL;
+	hlen = SHA1_MAC_LEN;
+	if (conn->verify.sha1_cert == NULL ||
+	    crypto_hash_finish(conn->verify.sha1_cert, hpos, &hlen) < 0) {
+		conn->verify.sha1_cert = NULL;
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	conn->verify.sha1_cert = NULL;
+
+	hlen += MD5_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+	}
+#endif /* CONFIG_TLSV12 */
+
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen);
+
+#ifdef CONFIG_TLSV12
+	if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+		/*
+		 * RFC 5246, 4.7:
+		 * TLS v1.2 adds explicit indication of the used signature and
+		 * hash algorithms.
+		 *
+		 * struct {
+		 *   HashAlgorithm hash;
+		 *   SignatureAlgorithm signature;
+		 * } SignatureAndHashAlgorithm;
+		 */
+		*pos++ = TLS_HASH_ALG_SHA256;
+		*pos++ = TLS_SIGN_ALG_RSA;
+	}
+#endif /* CONFIG_TLSV12 */
+
+	/*
+	 * RFC 2246, 4.7:
+	 * In digital signing, one-way hash functions are used as input for a
+	 * signing algorithm. A digitally-signed element is encoded as an
+	 * opaque vector <0..2^16-1>, where the length is specified by the
+	 * signing algorithm and key.
+	 *
+	 * In RSA signing, a 36-byte structure of two hashes (one SHA and one
+	 * MD5) is signed (encrypted with the private key). It is encoded with
+	 * PKCS #1 block type 0 or type 1 as described in [PKCS1].
+	 */
+	signed_start = pos; /* length to be filled */
+	pos += 2;
+	clen = end - pos;
+	if (conn->cred == NULL ||
+	    crypto_private_key_sign_pkcs1(conn->cred->key, hash, hlen,
+					  pos, &clen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to sign hash (PKCS #1)");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	WPA_PUT_BE16(signed_start, clen);
+
+	pos += clen;
+
+	WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+			      rhdr, end - rhdr, hs_start, pos - hs_start,
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	pos = rhdr + rlen;
+
+	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+	*msgpos = pos;
+
+	return 0;
+}
+
+
+static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn,
+					       u8 **msgpos, u8 *end)
+{
+	size_t rlen;
+	u8 payload[1];
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec");
+
+	payload[0] = TLS_CHANGE_CIPHER_SPEC;
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC,
+			      *msgpos, end - *msgpos, payload, sizeof(payload),
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	if (tlsv1_record_change_write_cipher(&conn->rl) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for "
+			   "record layer");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	*msgpos += rlen;
+
+	return 0;
+}
+
+
+static int tls_write_client_finished(struct tlsv1_client *conn,
+				     u8 **msgpos, u8 *end)
+{
+	u8 *pos, *hs_start;
+	size_t rlen, hlen;
+	u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN];
+	u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Send Finished");
+
+	/* Encrypted Handshake Message: Finished */
+
+#ifdef CONFIG_TLSV12
+	if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+		hlen = SHA256_MAC_LEN;
+		if (conn->verify.sha256_client == NULL ||
+		    crypto_hash_finish(conn->verify.sha256_client, hash, &hlen)
+		    < 0) {
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				  TLS_ALERT_INTERNAL_ERROR);
+			conn->verify.sha256_client = NULL;
+			return -1;
+		}
+		conn->verify.sha256_client = NULL;
+	} else {
+#endif /* CONFIG_TLSV12 */
+
+	hlen = MD5_MAC_LEN;
+	if (conn->verify.md5_client == NULL ||
+	    crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) {
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		conn->verify.md5_client = NULL;
+		crypto_hash_finish(conn->verify.sha1_client, NULL, NULL);
+		conn->verify.sha1_client = NULL;
+		return -1;
+	}
+	conn->verify.md5_client = NULL;
+	hlen = SHA1_MAC_LEN;
+	if (conn->verify.sha1_client == NULL ||
+	    crypto_hash_finish(conn->verify.sha1_client, hash + MD5_MAC_LEN,
+			       &hlen) < 0) {
+		conn->verify.sha1_client = NULL;
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	conn->verify.sha1_client = NULL;
+	hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+	}
+#endif /* CONFIG_TLSV12 */
+
+	if (tls_prf(conn->rl.tls_version,
+		    conn->master_secret, TLS_MASTER_SECRET_LEN,
+		    "client finished", hash, hlen,
+		    verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)",
+			verify_data + 1 + 3, TLS_VERIFY_DATA_LEN);
+
+	/* Handshake */
+	pos = hs_start = verify_data;
+	/* HandshakeType msg_type */
+	*pos++ = TLS_HANDSHAKE_TYPE_FINISHED;
+	/* uint24 length */
+	WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN);
+	pos += 3;
+	pos += TLS_VERIFY_DATA_LEN; /* verify_data already in place */
+	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+			      *msgpos, end - *msgpos, hs_start, pos - hs_start,
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	*msgpos += rlen;
+
+	return 0;
+}
+
+
+static u8 * tls_send_client_key_exchange(struct tlsv1_client *conn,
+					 size_t *out_len)
+{
+	u8 *msg, *end, *pos;
+	size_t msglen;
+
+	*out_len = 0;
+
+	msglen = 2000;
+	if (conn->certificate_requested)
+		msglen += tls_client_cert_chain_der_len(conn);
+
+	msg = os_malloc(msglen);
+	if (msg == NULL)
+		return NULL;
+
+	pos = msg;
+	end = msg + msglen;
+
+	if (conn->certificate_requested) {
+		if (tls_write_client_certificate(conn, &pos, end) < 0) {
+			os_free(msg);
+			return NULL;
+		}
+	}
+
+	if (tls_write_client_key_exchange(conn, &pos, end) < 0 ||
+	    (conn->certificate_requested && conn->cred && conn->cred->key &&
+	     tls_write_client_certificate_verify(conn, &pos, end) < 0) ||
+	    tls_write_client_change_cipher_spec(conn, &pos, end) < 0 ||
+	    tls_write_client_finished(conn, &pos, end) < 0) {
+		os_free(msg);
+		return NULL;
+	}
+
+	*out_len = pos - msg;
+
+	conn->state = SERVER_CHANGE_CIPHER_SPEC;
+
+	return msg;
+}
+
+
+static u8 * tls_send_change_cipher_spec(struct tlsv1_client *conn,
+					size_t *out_len)
+{
+	u8 *msg, *end, *pos;
+
+	*out_len = 0;
+
+	msg = os_malloc(1000);
+	if (msg == NULL)
+		return NULL;
+
+	pos = msg;
+	end = msg + 1000;
+
+	if (tls_write_client_change_cipher_spec(conn, &pos, end) < 0 ||
+	    tls_write_client_finished(conn, &pos, end) < 0) {
+		os_free(msg);
+		return NULL;
+	}
+
+	*out_len = pos - msg;
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Session resumption completed "
+		   "successfully");
+	if (!conn->session_resumed && conn->use_session_ticket)
+		conn->session_resumed = 1;
+	conn->state = ESTABLISHED;
+
+	return msg;
+}
+
+
+u8 * tlsv1_client_handshake_write(struct tlsv1_client *conn, size_t *out_len,
+				  int no_appl_data)
+{
+	switch (conn->state) {
+	case CLIENT_KEY_EXCHANGE:
+		return tls_send_client_key_exchange(conn, out_len);
+	case CHANGE_CIPHER_SPEC:
+		return tls_send_change_cipher_spec(conn, out_len);
+	case ACK_FINISHED:
+		wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed "
+			   "successfully");
+		conn->state = ESTABLISHED;
+		*out_len = 0;
+		if (no_appl_data) {
+			/* Need to return something to get final TLS ACK. */
+			return os_malloc(1);
+		}
+		return NULL;
+	default:
+		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while "
+			   "generating reply", conn->state);
+		return NULL;
+	}
+}
+
+
+u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level,
+			     u8 description, size_t *out_len)
+{
+	u8 *alert, *pos, *length;
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description);
+	*out_len = 0;
+
+	alert = os_malloc(10);
+	if (alert == NULL)
+		return NULL;
+
+	pos = alert;
+
+	/* TLSPlaintext */
+	/* ContentType type */
+	*pos++ = TLS_CONTENT_TYPE_ALERT;
+	/* ProtocolVersion version */
+	WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version :
+		     TLS_VERSION);
+	pos += 2;
+	/* uint16 length (to be filled) */
+	length = pos;
+	pos += 2;
+	/* opaque fragment[TLSPlaintext.length] */
+
+	/* Alert */
+	/* AlertLevel level */
+	*pos++ = level;
+	/* AlertDescription description */
+	*pos++ = description;
+
+	WPA_PUT_BE16(length, pos - length - 2);
+	*out_len = pos - alert;
+
+	return alert;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_common.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_common.c
new file mode 100644
index 0000000..0dd8e27
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_common.c
@@ -0,0 +1,525 @@
+/*
+ * TLSv1 common routines
+ * Copyright (c) 2006-2014, 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/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "x509v3.h"
+#include "tlsv1_common.h"
+
+
+/*
+ * TODO:
+ * RFC 2246 Section 9: Mandatory to implement TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
+ * Add support for commonly used cipher suites; don't bother with exportable
+ * suites.
+ */
+
+static const struct tls_cipher_suite tls_cipher_suites[] = {
+	{ TLS_NULL_WITH_NULL_NULL, TLS_KEY_X_NULL, TLS_CIPHER_NULL,
+	  TLS_HASH_NULL },
+	{ TLS_RSA_WITH_RC4_128_MD5, TLS_KEY_X_RSA, TLS_CIPHER_RC4_128,
+	  TLS_HASH_MD5 },
+	{ TLS_RSA_WITH_RC4_128_SHA, TLS_KEY_X_RSA, TLS_CIPHER_RC4_128,
+	  TLS_HASH_SHA },
+	{ TLS_RSA_WITH_DES_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_DES_CBC,
+	  TLS_HASH_SHA },
+	{ TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_RSA,
+	  TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA },
+	{ TLS_DHE_RSA_WITH_DES_CBC_SHA, TLS_KEY_X_DHE_RSA, TLS_CIPHER_DES_CBC,
+	  TLS_HASH_SHA},
+	{ TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_DHE_RSA,
+	  TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA },
+ 	{ TLS_DH_anon_WITH_RC4_128_MD5, TLS_KEY_X_DH_anon,
+	  TLS_CIPHER_RC4_128, TLS_HASH_MD5 },
+ 	{ TLS_DH_anon_WITH_DES_CBC_SHA, TLS_KEY_X_DH_anon,
+	  TLS_CIPHER_DES_CBC, TLS_HASH_SHA },
+ 	{ TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_DH_anon,
+	  TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA },
+	{ TLS_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_128_CBC,
+	  TLS_HASH_SHA },
+	{ TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_X_DHE_RSA,
+	  TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA },
+	{ TLS_DH_anon_WITH_AES_128_CBC_SHA, TLS_KEY_X_DH_anon,
+	  TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA },
+	{ TLS_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_256_CBC,
+	  TLS_HASH_SHA },
+	{ TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_DHE_RSA,
+	  TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA },
+	{ TLS_DH_anon_WITH_AES_256_CBC_SHA, TLS_KEY_X_DH_anon,
+	  TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA },
+	{ TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_X_RSA,
+	  TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 },
+	{ TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_X_RSA,
+	  TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 },
+	{ TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_X_DHE_RSA,
+	  TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 },
+	{ TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_X_DHE_RSA,
+	  TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 },
+	{ TLS_DH_anon_WITH_AES_128_CBC_SHA256, TLS_KEY_X_DH_anon,
+	  TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 },
+	{ TLS_DH_anon_WITH_AES_256_CBC_SHA256, TLS_KEY_X_DH_anon,
+	  TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 }
+};
+
+#define NUM_TLS_CIPHER_SUITES ARRAY_SIZE(tls_cipher_suites)
+
+
+static const struct tls_cipher_data tls_ciphers[] = {
+	{ TLS_CIPHER_NULL,         TLS_CIPHER_STREAM,  0,  0,  0,
+	  CRYPTO_CIPHER_NULL },
+	{ TLS_CIPHER_IDEA_CBC,     TLS_CIPHER_BLOCK,  16, 16,  8,
+	  CRYPTO_CIPHER_NULL },
+	{ TLS_CIPHER_RC2_CBC_40,   TLS_CIPHER_BLOCK,   5, 16,  0,
+	  CRYPTO_CIPHER_ALG_RC2 },
+	{ TLS_CIPHER_RC4_40,       TLS_CIPHER_STREAM,  5, 16,  0,
+	  CRYPTO_CIPHER_ALG_RC4 },
+	{ TLS_CIPHER_RC4_128,      TLS_CIPHER_STREAM, 16, 16,  0,
+	  CRYPTO_CIPHER_ALG_RC4 },
+	{ TLS_CIPHER_DES40_CBC,    TLS_CIPHER_BLOCK,   5,  8,  8,
+	  CRYPTO_CIPHER_ALG_DES },
+	{ TLS_CIPHER_DES_CBC,      TLS_CIPHER_BLOCK,   8,  8,  8,
+	  CRYPTO_CIPHER_ALG_DES },
+	{ TLS_CIPHER_3DES_EDE_CBC, TLS_CIPHER_BLOCK,  24, 24,  8,
+	  CRYPTO_CIPHER_ALG_3DES },
+	{ TLS_CIPHER_AES_128_CBC,  TLS_CIPHER_BLOCK,  16, 16, 16,
+	  CRYPTO_CIPHER_ALG_AES },
+	{ TLS_CIPHER_AES_256_CBC,  TLS_CIPHER_BLOCK,  32, 32, 16,
+	  CRYPTO_CIPHER_ALG_AES }
+};
+
+#define NUM_TLS_CIPHER_DATA ARRAY_SIZE(tls_ciphers)
+
+
+/**
+ * tls_get_cipher_suite - Get TLS cipher suite
+ * @suite: Cipher suite identifier
+ * Returns: Pointer to the cipher data or %NULL if not found
+ */
+const struct tls_cipher_suite * tls_get_cipher_suite(u16 suite)
+{
+	size_t i;
+	for (i = 0; i < NUM_TLS_CIPHER_SUITES; i++)
+		if (tls_cipher_suites[i].suite == suite)
+			return &tls_cipher_suites[i];
+	return NULL;
+}
+
+
+const struct tls_cipher_data * tls_get_cipher_data(tls_cipher cipher)
+{
+	size_t i;
+	for (i = 0; i < NUM_TLS_CIPHER_DATA; i++)
+		if (tls_ciphers[i].cipher == cipher)
+			return &tls_ciphers[i];
+	return NULL;
+}
+
+
+int tls_server_key_exchange_allowed(tls_cipher cipher)
+{
+	const struct tls_cipher_suite *suite;
+
+	/* RFC 2246, Section 7.4.3 */
+	suite = tls_get_cipher_suite(cipher);
+	if (suite == NULL)
+		return 0;
+
+	switch (suite->key_exchange) {
+	case TLS_KEY_X_DHE_DSS:
+	case TLS_KEY_X_DHE_DSS_EXPORT:
+	case TLS_KEY_X_DHE_RSA:
+	case TLS_KEY_X_DHE_RSA_EXPORT:
+	case TLS_KEY_X_DH_anon_EXPORT:
+	case TLS_KEY_X_DH_anon:
+		return 1;
+	case TLS_KEY_X_RSA_EXPORT:
+		return 1 /* FIX: public key len > 512 bits */;
+	default:
+		return 0;
+	}
+}
+
+
+/**
+ * tls_parse_cert - Parse DER encoded X.509 certificate and get public key
+ * @buf: ASN.1 DER encoded certificate
+ * @len: Length of the buffer
+ * @pk: Buffer for returning the allocated public key
+ * Returns: 0 on success, -1 on failure
+ *
+ * This functions parses an ASN.1 DER encoded X.509 certificate and retrieves
+ * the public key from it. The caller is responsible for freeing the public key
+ * by calling crypto_public_key_free().
+ */
+int tls_parse_cert(const u8 *buf, size_t len, struct crypto_public_key **pk)
+{
+	struct x509_certificate *cert;
+
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: Parse ASN.1 DER certificate",
+		    buf, len);
+
+	*pk = crypto_public_key_from_cert(buf, len);
+	if (*pk)
+		return 0;
+
+	cert = x509_certificate_parse(buf, len);
+	if (cert == NULL) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse X.509 "
+			   "certificate");
+		return -1;
+	}
+
+	/* TODO
+	 * verify key usage (must allow encryption)
+	 *
+	 * All certificate profiles, key and cryptographic formats are
+	 * defined by the IETF PKIX working group [PKIX]. When a key
+	 * usage extension is present, the digitalSignature bit must be
+	 * set for the key to be eligible for signing, as described
+	 * above, and the keyEncipherment bit must be present to allow
+	 * encryption, as described above. The keyAgreement bit must be
+	 * set on Diffie-Hellman certificates. (PKIX: RFC 3280)
+	 */
+
+	*pk = crypto_public_key_import(cert->public_key, cert->public_key_len);
+	x509_certificate_free(cert);
+
+	if (*pk == NULL) {
+		wpa_printf(MSG_ERROR, "TLSv1: Failed to import "
+			   "server public key");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int tls_verify_hash_init(struct tls_verify_hash *verify)
+{
+	tls_verify_hash_free(verify);
+	verify->md5_client = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
+	verify->md5_server = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
+	verify->md5_cert = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
+	verify->sha1_client = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
+	verify->sha1_server = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
+	verify->sha1_cert = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
+	if (verify->md5_client == NULL || verify->md5_server == NULL ||
+	    verify->md5_cert == NULL || verify->sha1_client == NULL ||
+	    verify->sha1_server == NULL || verify->sha1_cert == NULL) {
+		tls_verify_hash_free(verify);
+		return -1;
+	}
+#ifdef CONFIG_TLSV12
+	verify->sha256_client = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL,
+						 0);
+	verify->sha256_server = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL,
+						 0);
+	verify->sha256_cert = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL,
+					       0);
+	if (verify->sha256_client == NULL || verify->sha256_server == NULL ||
+	    verify->sha256_cert == NULL) {
+		tls_verify_hash_free(verify);
+		return -1;
+	}
+#endif /* CONFIG_TLSV12 */
+	return 0;
+}
+
+
+void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf,
+			 size_t len)
+{
+	if (verify->md5_client && verify->sha1_client) {
+		crypto_hash_update(verify->md5_client, buf, len);
+		crypto_hash_update(verify->sha1_client, buf, len);
+	}
+	if (verify->md5_server && verify->sha1_server) {
+		crypto_hash_update(verify->md5_server, buf, len);
+		crypto_hash_update(verify->sha1_server, buf, len);
+	}
+	if (verify->md5_cert && verify->sha1_cert) {
+		crypto_hash_update(verify->md5_cert, buf, len);
+		crypto_hash_update(verify->sha1_cert, buf, len);
+	}
+#ifdef CONFIG_TLSV12
+	if (verify->sha256_client)
+		crypto_hash_update(verify->sha256_client, buf, len);
+	if (verify->sha256_server)
+		crypto_hash_update(verify->sha256_server, buf, len);
+	if (verify->sha256_cert)
+		crypto_hash_update(verify->sha256_cert, buf, len);
+#endif /* CONFIG_TLSV12 */
+}
+
+
+void tls_verify_hash_free(struct tls_verify_hash *verify)
+{
+	crypto_hash_finish(verify->md5_client, NULL, NULL);
+	crypto_hash_finish(verify->md5_server, NULL, NULL);
+	crypto_hash_finish(verify->md5_cert, NULL, NULL);
+	crypto_hash_finish(verify->sha1_client, NULL, NULL);
+	crypto_hash_finish(verify->sha1_server, NULL, NULL);
+	crypto_hash_finish(verify->sha1_cert, NULL, NULL);
+	verify->md5_client = NULL;
+	verify->md5_server = NULL;
+	verify->md5_cert = NULL;
+	verify->sha1_client = NULL;
+	verify->sha1_server = NULL;
+	verify->sha1_cert = NULL;
+#ifdef CONFIG_TLSV12
+	crypto_hash_finish(verify->sha256_client, NULL, NULL);
+	crypto_hash_finish(verify->sha256_server, NULL, NULL);
+	crypto_hash_finish(verify->sha256_cert, NULL, NULL);
+	verify->sha256_client = NULL;
+	verify->sha256_server = NULL;
+	verify->sha256_cert = NULL;
+#endif /* CONFIG_TLSV12 */
+}
+
+
+int tls_version_ok(u16 ver)
+{
+	if (ver == TLS_VERSION_1)
+		return 1;
+#ifdef CONFIG_TLSV11
+	if (ver == TLS_VERSION_1_1)
+		return 1;
+#endif /* CONFIG_TLSV11 */
+#ifdef CONFIG_TLSV12
+	if (ver == TLS_VERSION_1_2)
+		return 1;
+#endif /* CONFIG_TLSV12 */
+
+	return 0;
+}
+
+
+const char * tls_version_str(u16 ver)
+{
+	switch (ver) {
+	case TLS_VERSION_1:
+		return "1.0";
+	case TLS_VERSION_1_1:
+		return "1.1";
+	case TLS_VERSION_1_2:
+		return "1.2";
+	}
+
+	return "?";
+}
+
+
+int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label,
+	    const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
+{
+#ifdef CONFIG_TLSV12
+	if (ver >= TLS_VERSION_1_2) {
+		tls_prf_sha256(secret, secret_len, label, seed, seed_len,
+			       out, outlen);
+		return 0;
+	}
+#endif /* CONFIG_TLSV12 */
+
+	return tls_prf_sha1_md5(secret, secret_len, label, seed, seed_len, out,
+				outlen);
+}
+
+
+#ifdef CONFIG_TLSV12
+int tlsv12_key_x_server_params_hash(u16 tls_version, u8 hash_alg,
+				    const u8 *client_random,
+				    const u8 *server_random,
+				    const u8 *server_params,
+				    size_t server_params_len, u8 *hash)
+{
+	size_t hlen;
+	struct crypto_hash *ctx;
+	enum crypto_hash_alg alg;
+
+	switch (hash_alg) {
+	case TLS_HASH_ALG_SHA256:
+		alg = CRYPTO_HASH_ALG_SHA256;
+		hlen = SHA256_MAC_LEN;
+		break;
+	case TLS_HASH_ALG_SHA384:
+		alg = CRYPTO_HASH_ALG_SHA384;
+		hlen = 48;
+		break;
+	case TLS_HASH_ALG_SHA512:
+		alg = CRYPTO_HASH_ALG_SHA512;
+		hlen = 64;
+		break;
+	default:
+		return -1;
+	}
+	ctx = crypto_hash_init(alg, NULL, 0);
+	if (ctx == NULL)
+		return -1;
+	crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN);
+	crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN);
+	crypto_hash_update(ctx, server_params, server_params_len);
+	if (crypto_hash_finish(ctx, hash, &hlen) < 0)
+		return -1;
+
+	return hlen;
+}
+#endif /* CONFIG_TLSV12 */
+
+
+int tls_key_x_server_params_hash(u16 tls_version, const u8 *client_random,
+				 const u8 *server_random,
+				 const u8 *server_params,
+				 size_t server_params_len, u8 *hash, size_t hsz)
+{
+	u8 *hpos;
+	size_t hlen;
+	struct crypto_hash *ctx;
+
+	hpos = hash;
+
+	ctx = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
+	if (ctx == NULL)
+		return -1;
+	crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN);
+	crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN);
+	crypto_hash_update(ctx, server_params, server_params_len);
+	hlen = MD5_MAC_LEN;
+	if (hsz < hlen)
+		return -1;
+	if (crypto_hash_finish(ctx, hash, &hlen) < 0)
+		return -1;
+	hpos += hlen;
+
+	ctx = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
+	if (ctx == NULL)
+		return -1;
+	crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN);
+	crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN);
+	crypto_hash_update(ctx, server_params, server_params_len);
+	hlen = hsz - hlen;
+	if (crypto_hash_finish(ctx, hpos, &hlen) < 0)
+		return -1;
+	hpos += hlen;
+	return hpos - hash;
+}
+
+
+int tls_verify_signature(u16 tls_version, struct crypto_public_key *pk,
+			 const u8 *data, size_t data_len,
+			 const u8 *pos, size_t len, u8 *alert)
+{
+	u8 *buf;
+	const u8 *end = pos + len;
+	const u8 *decrypted;
+	u16 slen;
+	size_t buflen;
+
+	if (end - pos < 2) {
+		*alert = TLS_ALERT_DECODE_ERROR;
+		return -1;
+	}
+	slen = WPA_GET_BE16(pos);
+	pos += 2;
+	if (end - pos < slen) {
+		*alert = TLS_ALERT_DECODE_ERROR;
+		return -1;
+	}
+	if (end - pos > slen) {
+		wpa_hexdump(MSG_MSGDUMP, "Additional data after Signature",
+			    pos + slen, end - pos - slen);
+		end = pos + slen;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: Signature", pos, end - pos);
+	if (pk == NULL) {
+		wpa_printf(MSG_DEBUG, "TLSv1: No public key to verify signature");
+		*alert = TLS_ALERT_INTERNAL_ERROR;
+		return -1;
+	}
+
+	buflen = end - pos;
+	buf = os_malloc(end - pos);
+	if (buf == NULL) {
+		*alert = TLS_ALERT_INTERNAL_ERROR;
+		return -1;
+	}
+	if (crypto_public_key_decrypt_pkcs1(pk, pos, end - pos, buf, &buflen) <
+	    0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt signature");
+		os_free(buf);
+		*alert = TLS_ALERT_DECRYPT_ERROR;
+		return -1;
+	}
+	decrypted = buf;
+
+	wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature",
+			decrypted, buflen);
+
+#ifdef CONFIG_TLSV12
+	if (tls_version >= TLS_VERSION_1_2) {
+		/*
+		 * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5
+		 *
+		 * DigestInfo ::= SEQUENCE {
+		 *   digestAlgorithm DigestAlgorithm,
+		 *   digest OCTET STRING
+		 * }
+		 *
+		 * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11}
+		 *
+		 * DER encoded DigestInfo for SHA256 per RFC 3447:
+		 * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 ||
+		 * H
+		 */
+		if (buflen >= 19 + 32 &&
+		    os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01"
+			      "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0)
+		{
+			wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithm = SHA-256");
+			decrypted = buf + 19;
+			buflen -= 19;
+		} else if (buflen >= 19 + 48 &&
+		    os_memcmp(buf, "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01"
+			      "\x65\x03\x04\x02\x02\x05\x00\x04\x30", 19) == 0)
+		{
+			wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithm = SHA-384");
+			decrypted = buf + 19;
+			buflen -= 19;
+		} else if (buflen >= 19 + 64 &&
+		    os_memcmp(buf, "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01"
+			      "\x65\x03\x04\x02\x03\x05\x00\x04\x40", 19) == 0)
+		{
+			wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithm = SHA-512");
+			decrypted = buf + 19;
+			buflen -= 19;
+
+		} else {
+			wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized DigestInfo");
+			os_free(buf);
+			*alert = TLS_ALERT_DECRYPT_ERROR;
+			return -1;
+		}
+	}
+#endif /* CONFIG_TLSV12 */
+
+	if (buflen != data_len ||
+	    os_memcmp_const(decrypted, data, data_len) != 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in CertificateVerify - did not match calculated hash");
+		os_free(buf);
+		*alert = TLS_ALERT_DECRYPT_ERROR;
+		return -1;
+	}
+
+	os_free(buf);
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_common.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_common.h
new file mode 100644
index 0000000..4cfdc2d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_common.h
@@ -0,0 +1,276 @@
+/*
+ * TLSv1 common definitions
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TLSV1_COMMON_H
+#define TLSV1_COMMON_H
+
+#include "crypto/crypto.h"
+
+#define TLS_VERSION_1 0x0301 /* TLSv1 */
+#define TLS_VERSION_1_1 0x0302 /* TLSv1.1 */
+#define TLS_VERSION_1_2 0x0303 /* TLSv1.2 */
+#ifdef CONFIG_TLSV12
+#define TLS_VERSION TLS_VERSION_1_2
+#else /* CONFIG_TLSV12 */
+#ifdef CONFIG_TLSV11
+#define TLS_VERSION TLS_VERSION_1_1
+#else /* CONFIG_TLSV11 */
+#define TLS_VERSION TLS_VERSION_1
+#endif /* CONFIG_TLSV11 */
+#endif /* CONFIG_TLSV12 */
+#define TLS_RANDOM_LEN 32
+#define TLS_PRE_MASTER_SECRET_LEN 48
+#define TLS_MASTER_SECRET_LEN 48
+#define TLS_SESSION_ID_MAX_LEN 32
+#define TLS_VERIFY_DATA_LEN 12
+
+/* HandshakeType */
+enum {
+	TLS_HANDSHAKE_TYPE_HELLO_REQUEST = 0,
+	TLS_HANDSHAKE_TYPE_CLIENT_HELLO = 1,
+	TLS_HANDSHAKE_TYPE_SERVER_HELLO = 2,
+	TLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET = 4 /* RFC 4507 */,
+	TLS_HANDSHAKE_TYPE_CERTIFICATE = 11,
+	TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE = 12,
+	TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST = 13,
+	TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE = 14,
+	TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY = 15,
+	TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE = 16,
+	TLS_HANDSHAKE_TYPE_FINISHED = 20,
+	TLS_HANDSHAKE_TYPE_CERTIFICATE_URL = 21 /* RFC 4366 */,
+	TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS = 22 /* RFC 4366 */
+};
+
+/* CipherSuite */
+#define TLS_NULL_WITH_NULL_NULL			0x0000 /* RFC 2246 */
+#define TLS_RSA_WITH_NULL_MD5			0x0001 /* RFC 2246 */
+#define TLS_RSA_WITH_NULL_SHA			0x0002 /* RFC 2246 */
+#define TLS_RSA_EXPORT_WITH_RC4_40_MD5		0x0003 /* RFC 2246 */
+#define TLS_RSA_WITH_RC4_128_MD5		0x0004 /* RFC 2246 */
+#define TLS_RSA_WITH_RC4_128_SHA		0x0005 /* RFC 2246 */
+#define TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5	0x0006 /* RFC 2246 */
+#define TLS_RSA_WITH_IDEA_CBC_SHA		0x0007 /* RFC 2246 */
+#define TLS_RSA_EXPORT_WITH_DES40_CBC_SHA	0x0008 /* RFC 2246 */
+#define TLS_RSA_WITH_DES_CBC_SHA		0x0009 /* RFC 2246 */
+#define TLS_RSA_WITH_3DES_EDE_CBC_SHA		0x000A /* RFC 2246 */
+#define TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA	0x000B /* RFC 2246 */
+#define TLS_DH_DSS_WITH_DES_CBC_SHA		0x000C /* RFC 2246 */
+#define TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA	0x000D /* RFC 2246 */
+#define TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA	0x000E /* RFC 2246 */
+#define TLS_DH_RSA_WITH_DES_CBC_SHA		0x000F /* RFC 2246 */
+#define TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA	0x0010 /* RFC 2246 */
+#define TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA	0x0011 /* RFC 2246 */
+#define TLS_DHE_DSS_WITH_DES_CBC_SHA		0x0012 /* RFC 2246 */
+#define TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA	0x0013 /* RFC 2246 */
+#define TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA	0x0014 /* RFC 2246 */
+#define TLS_DHE_RSA_WITH_DES_CBC_SHA		0x0015 /* RFC 2246 */
+#define TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA	0x0016 /* RFC 2246 */
+#define TLS_DH_anon_EXPORT_WITH_RC4_40_MD5	0x0017 /* RFC 2246 */
+#define TLS_DH_anon_WITH_RC4_128_MD5		0x0018 /* RFC 2246 */
+#define TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA	0x0019 /* RFC 2246 */
+#define TLS_DH_anon_WITH_DES_CBC_SHA		0x001A /* RFC 2246 */
+#define TLS_DH_anon_WITH_3DES_EDE_CBC_SHA	0x001B /* RFC 2246 */
+#define TLS_RSA_WITH_AES_128_CBC_SHA		0x002F /* RFC 3268 */
+#define TLS_DH_DSS_WITH_AES_128_CBC_SHA		0x0030 /* RFC 3268 */
+#define TLS_DH_RSA_WITH_AES_128_CBC_SHA		0x0031 /* RFC 3268 */
+#define TLS_DHE_DSS_WITH_AES_128_CBC_SHA	0x0032 /* RFC 3268 */
+#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA	0x0033 /* RFC 3268 */
+#define TLS_DH_anon_WITH_AES_128_CBC_SHA	0x0034 /* RFC 3268 */
+#define TLS_RSA_WITH_AES_256_CBC_SHA		0x0035 /* RFC 3268 */
+#define TLS_DH_DSS_WITH_AES_256_CBC_SHA		0x0036 /* RFC 3268 */
+#define TLS_DH_RSA_WITH_AES_256_CBC_SHA		0x0037 /* RFC 3268 */
+#define TLS_DHE_DSS_WITH_AES_256_CBC_SHA	0x0038 /* RFC 3268 */
+#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA	0x0039 /* RFC 3268 */
+#define TLS_DH_anon_WITH_AES_256_CBC_SHA	0x003A /* RFC 3268 */
+#define TLS_RSA_WITH_NULL_SHA256		0x003B /* RFC 5246 */
+#define TLS_RSA_WITH_AES_128_CBC_SHA256		0x003C /* RFC 5246 */
+#define TLS_RSA_WITH_AES_256_CBC_SHA256		0x003D /* RFC 5246 */
+#define TLS_DH_DSS_WITH_AES_128_CBC_SHA256	0x003E /* RFC 5246 */
+#define TLS_DH_RSA_WITH_AES_128_CBC_SHA256	0x003F /* RFC 5246 */
+#define TLS_DHE_DSS_WITH_AES_128_CBC_SHA256	0x0040 /* RFC 5246 */
+#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA256	0x0067 /* RFC 5246 */
+#define TLS_DH_DSS_WITH_AES_256_CBC_SHA256	0x0068 /* RFC 5246 */
+#define TLS_DH_RSA_WITH_AES_256_CBC_SHA256	0x0069 /* RFC 5246 */
+#define TLS_DHE_DSS_WITH_AES_256_CBC_SHA256	0x006A /* RFC 5246 */
+#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA256	0x006B /* RFC 5246 */
+#define TLS_DH_anon_WITH_AES_128_CBC_SHA256	0x006C /* RFC 5246 */
+#define TLS_DH_anon_WITH_AES_256_CBC_SHA256	0x006D /* RFC 5246 */
+
+/* CompressionMethod */
+#define TLS_COMPRESSION_NULL 0
+
+/* HashAlgorithm */
+enum {
+	TLS_HASH_ALG_NONE = 0,
+	TLS_HASH_ALG_MD5 = 1,
+	TLS_HASH_ALG_SHA1 = 2,
+	TLS_HASH_ALG_SHA224 = 3,
+	TLS_HASH_ALG_SHA256 = 4,
+	TLS_HASH_ALG_SHA384 = 5,
+	TLS_HASH_ALG_SHA512 = 6
+};
+
+/* SignatureAlgorithm */
+enum {
+	TLS_SIGN_ALG_ANONYMOUS = 0,
+	TLS_SIGN_ALG_RSA = 1,
+	TLS_SIGN_ALG_DSA = 2,
+	TLS_SIGN_ALG_ECDSA = 3,
+};
+
+/* AlertLevel */
+#define TLS_ALERT_LEVEL_WARNING 1
+#define TLS_ALERT_LEVEL_FATAL 2
+
+/* AlertDescription */
+#define TLS_ALERT_CLOSE_NOTIFY			0
+#define TLS_ALERT_UNEXPECTED_MESSAGE		10
+#define TLS_ALERT_BAD_RECORD_MAC		20
+#define TLS_ALERT_DECRYPTION_FAILED		21
+#define TLS_ALERT_RECORD_OVERFLOW		22
+#define TLS_ALERT_DECOMPRESSION_FAILURE		30
+#define TLS_ALERT_HANDSHAKE_FAILURE		40
+#define TLS_ALERT_BAD_CERTIFICATE		42
+#define TLS_ALERT_UNSUPPORTED_CERTIFICATE	43
+#define TLS_ALERT_CERTIFICATE_REVOKED		44
+#define TLS_ALERT_CERTIFICATE_EXPIRED		45
+#define TLS_ALERT_CERTIFICATE_UNKNOWN		46
+#define TLS_ALERT_ILLEGAL_PARAMETER		47
+#define TLS_ALERT_UNKNOWN_CA			48
+#define TLS_ALERT_ACCESS_DENIED			49
+#define TLS_ALERT_DECODE_ERROR			50
+#define TLS_ALERT_DECRYPT_ERROR			51
+#define TLS_ALERT_EXPORT_RESTRICTION		60
+#define TLS_ALERT_PROTOCOL_VERSION		70
+#define TLS_ALERT_INSUFFICIENT_SECURITY		71
+#define TLS_ALERT_INTERNAL_ERROR		80
+#define TLS_ALERT_USER_CANCELED			90
+#define TLS_ALERT_NO_RENEGOTIATION		100
+#define TLS_ALERT_UNSUPPORTED_EXTENSION		110 /* RFC 4366 */
+#define TLS_ALERT_CERTIFICATE_UNOBTAINABLE	111 /* RFC 4366 */
+#define TLS_ALERT_UNRECOGNIZED_NAME		112 /* RFC 4366 */
+#define TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE	113 /* RFC 4366 */
+#define TLS_ALERT_BAD_CERTIFICATE_HASH_VALUE	114 /* RFC 4366 */
+
+/* ChangeCipherSpec */
+enum {
+	TLS_CHANGE_CIPHER_SPEC = 1
+};
+
+/* TLS Extensions */
+#define TLS_EXT_SERVER_NAME			0 /* RFC 4366 */
+#define TLS_EXT_MAX_FRAGMENT_LENGTH		1 /* RFC 4366 */
+#define TLS_EXT_CLIENT_CERTIFICATE_URL		2 /* RFC 4366 */
+#define TLS_EXT_TRUSTED_CA_KEYS			3 /* RFC 4366 */
+#define TLS_EXT_TRUNCATED_HMAC			4 /* RFC 4366 */
+#define TLS_EXT_STATUS_REQUEST			5 /* RFC 4366 */
+#define TLS_EXT_SIGNATURE_ALGORITHMS		13 /* RFC 5246 */
+#define TLS_EXT_STATUS_REQUEST_V2		17 /* RFC 6961 */
+#define TLS_EXT_SESSION_TICKET			35 /* RFC 4507 */
+
+#define TLS_EXT_PAC_OPAQUE TLS_EXT_SESSION_TICKET /* EAP-FAST terminology */
+
+
+typedef enum {
+	TLS_KEY_X_NULL,
+	TLS_KEY_X_RSA,
+	TLS_KEY_X_RSA_EXPORT,
+	TLS_KEY_X_DH_DSS_EXPORT,
+	TLS_KEY_X_DH_DSS,
+	TLS_KEY_X_DH_RSA_EXPORT,
+	TLS_KEY_X_DH_RSA,
+	TLS_KEY_X_DHE_DSS_EXPORT,
+	TLS_KEY_X_DHE_DSS,
+	TLS_KEY_X_DHE_RSA_EXPORT,
+	TLS_KEY_X_DHE_RSA,
+	TLS_KEY_X_DH_anon_EXPORT,
+	TLS_KEY_X_DH_anon
+} tls_key_exchange;
+
+typedef enum {
+	TLS_CIPHER_NULL,
+	TLS_CIPHER_RC4_40,
+	TLS_CIPHER_RC4_128,
+	TLS_CIPHER_RC2_CBC_40,
+	TLS_CIPHER_IDEA_CBC,
+	TLS_CIPHER_DES40_CBC,
+	TLS_CIPHER_DES_CBC,
+	TLS_CIPHER_3DES_EDE_CBC,
+	TLS_CIPHER_AES_128_CBC,
+	TLS_CIPHER_AES_256_CBC
+} tls_cipher;
+
+typedef enum {
+	TLS_HASH_NULL,
+	TLS_HASH_MD5,
+	TLS_HASH_SHA,
+	TLS_HASH_SHA256
+} tls_hash;
+
+struct tls_cipher_suite {
+	u16 suite;
+	tls_key_exchange key_exchange;
+	tls_cipher cipher;
+	tls_hash hash;
+};
+
+typedef enum {
+	TLS_CIPHER_STREAM,
+	TLS_CIPHER_BLOCK
+} tls_cipher_type;
+
+struct tls_cipher_data {
+	tls_cipher cipher;
+	tls_cipher_type type;
+	size_t key_material;
+	size_t expanded_key_material;
+	size_t block_size; /* also iv_size */
+	enum crypto_cipher_alg alg;
+};
+
+
+struct tls_verify_hash {
+	struct crypto_hash *md5_client;
+	struct crypto_hash *sha1_client;
+	struct crypto_hash *sha256_client;
+	struct crypto_hash *md5_server;
+	struct crypto_hash *sha1_server;
+	struct crypto_hash *sha256_server;
+	struct crypto_hash *md5_cert;
+	struct crypto_hash *sha1_cert;
+	struct crypto_hash *sha256_cert;
+};
+
+
+const struct tls_cipher_suite * tls_get_cipher_suite(u16 suite);
+const struct tls_cipher_data * tls_get_cipher_data(tls_cipher cipher);
+int tls_server_key_exchange_allowed(tls_cipher cipher);
+int tls_parse_cert(const u8 *buf, size_t len, struct crypto_public_key **pk);
+int tls_verify_hash_init(struct tls_verify_hash *verify);
+void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf,
+			 size_t len);
+void tls_verify_hash_free(struct tls_verify_hash *verify);
+int tls_version_ok(u16 ver);
+const char * tls_version_str(u16 ver);
+int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label,
+	    const u8 *seed, size_t seed_len, u8 *out, size_t outlen);
+int tlsv12_key_x_server_params_hash(u16 tls_version, u8 hash_Alg,
+				    const u8 *client_random,
+				    const u8 *server_random,
+				    const u8 *server_params,
+				    size_t server_params_len, u8 *hash);
+int tls_key_x_server_params_hash(u16 tls_version, const u8 *client_random,
+				 const u8 *server_random,
+				 const u8 *server_params,
+				 size_t server_params_len,
+				 u8 *hash, size_t hsz);
+int tls_verify_signature(u16 tls_version, struct crypto_public_key *pk,
+			 const u8 *data, size_t data_len,
+			 const u8 *pos, size_t len, u8 *alert);
+
+#endif /* TLSV1_COMMON_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_cred.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_cred.c
new file mode 100644
index 0000000..1310f4e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_cred.c
@@ -0,0 +1,1212 @@
+/*
+ * TLSv1 credentials
+ * Copyright (c) 2006-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 "common.h"
+#include "base64.h"
+#include "crypto/crypto.h"
+#include "crypto/sha1.h"
+#include "pkcs5.h"
+#include "pkcs8.h"
+#include "x509v3.h"
+#include "tlsv1_cred.h"
+
+
+struct tlsv1_credentials * tlsv1_cred_alloc(void)
+{
+	struct tlsv1_credentials *cred;
+	cred = os_zalloc(sizeof(*cred));
+	return cred;
+}
+
+
+void tlsv1_cred_free(struct tlsv1_credentials *cred)
+{
+	if (cred == NULL)
+		return;
+
+	x509_certificate_chain_free(cred->trusted_certs);
+	x509_certificate_chain_free(cred->cert);
+	crypto_private_key_free(cred->key);
+	os_free(cred->dh_p);
+	os_free(cred->dh_g);
+	os_free(cred->ocsp_stapling_response);
+	os_free(cred->ocsp_stapling_response_multi);
+	os_free(cred);
+}
+
+
+static int tlsv1_add_cert_der(struct x509_certificate **chain,
+			      const u8 *buf, size_t len)
+{
+	struct x509_certificate *cert, *p;
+	char name[128];
+
+	cert = x509_certificate_parse(buf, len);
+	if (cert == NULL) {
+		wpa_printf(MSG_INFO, "TLSv1: %s - failed to parse certificate",
+			   __func__);
+		return -1;
+	}
+
+	p = *chain;
+	while (p && p->next)
+		p = p->next;
+	if (p && x509_name_compare(&cert->subject, &p->issuer) == 0) {
+		/*
+		 * The new certificate is the issuer of the last certificate in
+		 * the chain - add the new certificate to the end.
+		 */
+		p->next = cert;
+	} else {
+		/* Add to the beginning of the chain */
+		cert->next = *chain;
+		*chain = cert;
+	}
+
+	x509_name_string(&cert->subject, name, sizeof(name));
+	wpa_printf(MSG_DEBUG, "TLSv1: Added certificate: %s", name);
+
+	return 0;
+}
+
+
+static const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----";
+static const char *pem_cert_end = "-----END CERTIFICATE-----";
+static const char *pem_key_begin = "-----BEGIN RSA PRIVATE KEY-----";
+static const char *pem_key_end = "-----END RSA PRIVATE KEY-----";
+static const char *pem_key2_begin = "-----BEGIN PRIVATE KEY-----";
+static const char *pem_key2_end = "-----END PRIVATE KEY-----";
+static const char *pem_key_enc_begin = "-----BEGIN ENCRYPTED PRIVATE KEY-----";
+static const char *pem_key_enc_end = "-----END ENCRYPTED PRIVATE KEY-----";
+
+
+static const u8 * search_tag(const char *tag, const u8 *buf, size_t len)
+{
+	size_t i, plen;
+
+	plen = os_strlen(tag);
+	if (len < plen)
+		return NULL;
+
+	for (i = 0; i < len - plen; i++) {
+		if (os_memcmp(buf + i, tag, plen) == 0)
+			return buf + i;
+	}
+
+	return NULL;
+}
+
+
+static int tlsv1_add_cert(struct x509_certificate **chain,
+			  const u8 *buf, size_t len)
+{
+	const u8 *pos, *end;
+	unsigned char *der;
+	size_t der_len;
+
+	pos = search_tag(pem_cert_begin, buf, len);
+	if (!pos) {
+		wpa_printf(MSG_DEBUG, "TLSv1: No PEM certificate tag found - "
+			   "assume DER format");
+		return tlsv1_add_cert_der(chain, buf, len);
+	}
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format certificate into "
+		   "DER format");
+
+	while (pos) {
+		pos += os_strlen(pem_cert_begin);
+		end = search_tag(pem_cert_end, pos, buf + len - pos);
+		if (end == NULL) {
+			wpa_printf(MSG_INFO, "TLSv1: Could not find PEM "
+				   "certificate end tag (%s)", pem_cert_end);
+			return -1;
+		}
+
+		der = base64_decode((const char *) pos, end - pos, &der_len);
+		if (der == NULL) {
+			wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM "
+				   "certificate");
+			return -1;
+		}
+
+		if (tlsv1_add_cert_der(chain, der, der_len) < 0) {
+			wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM "
+				   "certificate after DER conversion");
+			os_free(der);
+			return -1;
+		}
+
+		os_free(der);
+
+		end += os_strlen(pem_cert_end);
+		pos = search_tag(pem_cert_begin, end, buf + len - end);
+	}
+
+	return 0;
+}
+
+
+static int tlsv1_set_cert_chain(struct x509_certificate **chain,
+				const char *cert, const u8 *cert_blob,
+				size_t cert_blob_len)
+{
+	if (cert_blob)
+		return tlsv1_add_cert(chain, cert_blob, cert_blob_len);
+
+	if (cert) {
+		u8 *buf;
+		size_t len;
+		int ret;
+
+		buf = (u8 *) os_readfile(cert, &len);
+		if (buf == NULL) {
+			wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
+				   cert);
+			return -1;
+		}
+
+		ret = tlsv1_add_cert(chain, buf, len);
+		os_free(buf);
+		return ret;
+	}
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_set_ca_cert - Set trusted CA certificate(s)
+ * @cred: TLSv1 credentials from tlsv1_cred_alloc()
+ * @cert: File or reference name for X.509 certificate in PEM or DER format
+ * @cert_blob: cert as inlined data or %NULL if not used
+ * @cert_blob_len: ca_cert_blob length
+ * @path: Path to CA certificates (not yet supported)
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert,
+		      const u8 *cert_blob, size_t cert_blob_len,
+		      const char *path)
+{
+	if (cert && os_strncmp(cert, "hash://", 7) == 0) {
+		const char *pos = cert + 7;
+		if (os_strncmp(pos, "server/sha256/", 14) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "TLSv1: Unsupported ca_cert hash value '%s'",
+				   cert);
+			return -1;
+		}
+		pos += 14;
+		if (os_strlen(pos) != 32 * 2) {
+			wpa_printf(MSG_DEBUG,
+				   "TLSv1: Unexpected SHA256 hash length in ca_cert '%s'",
+				   cert);
+			return -1;
+		}
+		if (hexstr2bin(pos, cred->srv_cert_hash, 32) < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "TLSv1: Invalid SHA256 hash value in ca_cert '%s'",
+				   cert);
+			return -1;
+		}
+		cred->server_cert_only = 1;
+		cred->ca_cert_verify = 0;
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Checking only server certificate match");
+		return 0;
+	}
+
+	if (cert && os_strncmp(cert, "probe://", 8) == 0) {
+		cred->cert_probe = 1;
+		cred->ca_cert_verify = 0;
+		wpa_printf(MSG_DEBUG, "TLSv1: Only probe server certificate");
+		return 0;
+	}
+
+	cred->ca_cert_verify = cert || cert_blob || path;
+
+	if (tlsv1_set_cert_chain(&cred->trusted_certs, cert,
+				 cert_blob, cert_blob_len) < 0)
+		return -1;
+
+	if (path) {
+		/* TODO: add support for reading number of certificate files */
+		wpa_printf(MSG_INFO, "TLSv1: Use of CA certificate directory "
+			   "not yet supported");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_set_cert - Set certificate
+ * @cred: TLSv1 credentials from tlsv1_cred_alloc()
+ * @cert: File or reference name for X.509 certificate in PEM or DER format
+ * @cert_blob: cert as inlined data or %NULL if not used
+ * @cert_blob_len: cert_blob length
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert,
+		   const u8 *cert_blob, size_t cert_blob_len)
+{
+	return tlsv1_set_cert_chain(&cred->cert, cert,
+				    cert_blob, cert_blob_len);
+}
+
+
+static struct crypto_private_key * tlsv1_set_key_pem(const u8 *key, size_t len)
+{
+	const u8 *pos, *end;
+	unsigned char *der;
+	size_t der_len;
+	struct crypto_private_key *pkey;
+
+	pos = search_tag(pem_key_begin, key, len);
+	if (!pos) {
+		pos = search_tag(pem_key2_begin, key, len);
+		if (!pos)
+			return NULL;
+		pos += os_strlen(pem_key2_begin);
+		end = search_tag(pem_key2_end, pos, key + len - pos);
+		if (!end)
+			return NULL;
+	} else {
+		const u8 *pos2;
+		pos += os_strlen(pem_key_begin);
+		end = search_tag(pem_key_end, pos, key + len - pos);
+		if (!end)
+			return NULL;
+		pos2 = search_tag("Proc-Type: 4,ENCRYPTED", pos, end - pos);
+		if (pos2) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Unsupported private key "
+				   "format (Proc-Type/DEK-Info)");
+			return NULL;
+		}
+	}
+
+	der = base64_decode((const char *) pos, end - pos, &der_len);
+	if (!der)
+		return NULL;
+	pkey = crypto_private_key_import(der, der_len, NULL);
+	os_free(der);
+	return pkey;
+}
+
+
+static struct crypto_private_key * tlsv1_set_key_enc_pem(const u8 *key,
+							 size_t len,
+							 const char *passwd)
+{
+	const u8 *pos, *end;
+	unsigned char *der;
+	size_t der_len;
+	struct crypto_private_key *pkey;
+
+	if (passwd == NULL)
+		return NULL;
+	pos = search_tag(pem_key_enc_begin, key, len);
+	if (!pos)
+		return NULL;
+	pos += os_strlen(pem_key_enc_begin);
+	end = search_tag(pem_key_enc_end, pos, key + len - pos);
+	if (!end)
+		return NULL;
+
+	der = base64_decode((const char *) pos, end - pos, &der_len);
+	if (!der)
+		return NULL;
+	pkey = crypto_private_key_import(der, der_len, passwd);
+	os_free(der);
+	return pkey;
+}
+
+
+#ifdef PKCS12_FUNCS
+
+static int oid_is_rsadsi(struct asn1_oid *oid)
+{
+	return oid->len >= 4 &&
+		oid->oid[0] == 1 /* iso */ &&
+		oid->oid[1] == 2 /* member-body */ &&
+		oid->oid[2] == 840 /* us */ &&
+		oid->oid[3] == 113549 /* rsadsi */;
+}
+
+
+static int pkcs12_is_bagtype_oid(struct asn1_oid *oid, unsigned long type)
+{
+	return oid->len == 9 &&
+		oid_is_rsadsi(oid) &&
+		oid->oid[4] == 1 /* pkcs */ &&
+		oid->oid[5] == 12 /* pkcs-12 */ &&
+		oid->oid[6] == 10 &&
+		oid->oid[7] == 1 /* bagtypes */ &&
+		oid->oid[8] == type;
+}
+
+
+static int is_oid_pkcs7(struct asn1_oid *oid)
+{
+	return oid->len == 7 &&
+		oid->oid[0] == 1 /* iso */ &&
+		oid->oid[1] == 2 /* member-body */ &&
+		oid->oid[2] == 840 /* us */ &&
+		oid->oid[3] == 113549 /* rsadsi */ &&
+		oid->oid[4] == 1 /* pkcs */ &&
+		oid->oid[5] == 7 /* pkcs-7 */;
+}
+
+
+static int is_oid_pkcs7_data(struct asn1_oid *oid)
+{
+	return is_oid_pkcs7(oid) && oid->oid[6] == 1 /* data */;
+}
+
+
+static int is_oid_pkcs7_enc_data(struct asn1_oid *oid)
+{
+	return is_oid_pkcs7(oid) && oid->oid[6] == 6 /* encryptedData */;
+}
+
+
+static int is_oid_pkcs9(struct asn1_oid *oid)
+{
+	return oid->len >= 6 &&
+		oid->oid[0] == 1 /* iso */ &&
+		oid->oid[1] == 2 /* member-body */ &&
+		oid->oid[2] == 840 /* us */ &&
+		oid->oid[3] == 113549 /* rsadsi */ &&
+		oid->oid[4] == 1 /* pkcs */ &&
+		oid->oid[5] == 9 /* pkcs-9 */;
+}
+
+
+static int is_oid_pkcs9_friendly_name(struct asn1_oid *oid)
+{
+	return oid->len == 7 && is_oid_pkcs9(oid) &&
+		oid->oid[6] == 20;
+}
+
+
+static int is_oid_pkcs9_local_key_id(struct asn1_oid *oid)
+{
+	return oid->len == 7 && is_oid_pkcs9(oid) &&
+		oid->oid[6] == 21;
+}
+
+
+static int is_oid_pkcs9_x509_cert(struct asn1_oid *oid)
+{
+	return oid->len == 8 && is_oid_pkcs9(oid) &&
+		oid->oid[6] == 22 /* certTypes */ &&
+		oid->oid[7] == 1 /* x509Certificate */;
+}
+
+
+static int pkcs12_keybag(struct tlsv1_credentials *cred,
+			 const u8 *buf, size_t len)
+{
+	/* TODO */
+	return 0;
+}
+
+
+static int pkcs12_pkcs8_keybag(struct tlsv1_credentials *cred,
+			       const u8 *buf, size_t len,
+			       const char *passwd)
+{
+	struct crypto_private_key *key;
+
+	/* PKCS8ShroudedKeyBag ::= EncryptedPrivateKeyInfo */
+	key = pkcs8_enc_key_import(buf, len, passwd);
+	if (!key)
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "PKCS #12: Successfully decrypted PKCS8ShroudedKeyBag");
+	crypto_private_key_free(cred->key);
+	cred->key = key;
+
+	return 0;
+}
+
+
+static int pkcs12_certbag(struct tlsv1_credentials *cred,
+			  const u8 *buf, size_t len)
+{
+	struct asn1_hdr hdr;
+	struct asn1_oid oid;
+	char obuf[80];
+	const u8 *pos, *end;
+
+	/*
+	 * CertBag ::= SEQUENCE {
+	 *     certId      BAG-TYPE.&id   ({CertTypes}),
+	 *     certValue   [0] EXPLICIT BAG-TYPE.&Type ({CertTypes}{@certId})
+	 * }
+	 */
+
+	if (asn1_get_next(buf, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr, "PKCS #12: Expected SEQUENCE (CertBag)");
+		return -1;
+	}
+
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	if (asn1_get_oid(pos, end - pos, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Failed to parse OID (certId)");
+		return -1;
+	}
+
+	asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+	wpa_printf(MSG_DEBUG, "PKCS #12: certId %s", obuf);
+
+	if (!is_oid_pkcs9_x509_cert(&oid)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Ignored unsupported certificate type (certId %s)",
+			   obuf);
+	}
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 || !hdr.constructed ||
+	    !asn1_is_cs_tag(&hdr, 0)) {
+		asn1_unexpected(&hdr,
+				"PKCS #12: Expected [0] EXPLICIT (certValue)");
+		return -1;
+	}
+
+	if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 ||
+	    !asn1_is_octetstring(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #12: Expected OCTET STRING (x509Certificate)");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "PKCS #12: x509Certificate",
+		    hdr.payload, hdr.length);
+	if (cred->cert) {
+		struct x509_certificate *cert;
+
+		wpa_printf(MSG_DEBUG, "PKCS #12: Ignore extra certificate");
+		cert = x509_certificate_parse(hdr.payload, hdr.length);
+		if (!cert) {
+			wpa_printf(MSG_DEBUG,
+				   "PKCS #12: Failed to parse x509Certificate");
+			return 0;
+		}
+		x509_certificate_chain_free(cert);
+
+		return 0;
+	}
+	return tlsv1_set_cert(cred, NULL, hdr.payload, hdr.length);
+}
+
+
+static int pkcs12_parse_attr_friendly_name(const u8 *pos, const u8 *end)
+{
+	struct asn1_hdr hdr;
+
+	/*
+	 * RFC 2985, 5.5.1:
+	 * friendlyName ATTRIBUTE ::= {
+	 *         WITH SYNTAX BMPString (SIZE(1..pkcs-9-ub-friendlyName))
+	 *         EQUALITY MATCHING RULE caseIgnoreMatch
+	 *         SINGLE VALUE TRUE
+	 *          ID pkcs-9-at-friendlyName
+	 * }
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_bmpstring(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #12: Expected BMPSTRING (friendlyName)");
+		return 0;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "PKCS #12: friendlyName",
+			  hdr.payload, hdr.length);
+	return 0;
+}
+
+
+static int pkcs12_parse_attr_local_key_id(const u8 *pos, const u8 *end)
+{
+	struct asn1_hdr hdr;
+
+	/*
+	 * RFC 2985, 5.5.2:
+	 * localKeyId ATTRIBUTE ::= {
+	 *         WITH SYNTAX OCTET STRING
+	 *         EQUALITY MATCHING RULE octetStringMatch
+	 *         SINGLE VALUE TRUE
+	 *         ID pkcs-9-at-localKeyId
+	 * }
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_octetstring(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #12: Expected OCTET STRING (localKeyID)");
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "PKCS #12: localKeyID",
+			hdr.payload, hdr.length);
+	return 0;
+}
+
+
+static int pkcs12_parse_attr(const u8 *pos, size_t len)
+{
+	const u8 *end = pos + len;
+	struct asn1_hdr hdr;
+	struct asn1_oid a_oid;
+	char obuf[80];
+
+	/*
+	 * PKCS12Attribute ::= SEQUENCE {
+	 * attrId      ATTRIBUTE.&id ({PKCS12AttrSet}),
+	 * attrValues  SET OF ATTRIBUTE.&Type ({PKCS12AttrSet}{@attrId})
+	 * }
+	 */
+
+	if (asn1_get_oid(pos, end - pos, &a_oid, &pos)) {
+		wpa_printf(MSG_DEBUG, "PKCS #12: Failed to parse OID (attrId)");
+		return -1;
+	}
+
+	asn1_oid_to_str(&a_oid, obuf, sizeof(obuf));
+	wpa_printf(MSG_DEBUG, "PKCS #12: attrId %s", obuf);
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 || !asn1_is_set(&hdr)) {
+		asn1_unexpected(&hdr, "PKCS #12: Expected SET (attrValues)");
+		return -1;
+	}
+	wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: attrValues",
+			hdr.payload, hdr.length);
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	if (is_oid_pkcs9_friendly_name(&a_oid))
+		return pkcs12_parse_attr_friendly_name(pos, end);
+	if (is_oid_pkcs9_local_key_id(&a_oid))
+		return pkcs12_parse_attr_local_key_id(pos, end);
+
+	wpa_printf(MSG_DEBUG, "PKCS #12: Ignore unknown attribute");
+	return 0;
+}
+
+
+static int pkcs12_safebag(struct tlsv1_credentials *cred,
+			  const u8 *buf, size_t len, const char *passwd)
+{
+	struct asn1_hdr hdr;
+	struct asn1_oid oid;
+	char obuf[80];
+	const u8 *pos = buf, *end = buf + len;
+	const u8 *value;
+	size_t value_len;
+
+	wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: SafeBag", buf, len);
+
+	/* BAG-TYPE ::= TYPE-IDENTIFIER */
+	if (asn1_get_oid(pos, end - pos, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Failed to parse OID (BAG-TYPE)");
+		return -1;
+	}
+
+	asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+	wpa_printf(MSG_DEBUG, "PKCS #12: BAG-TYPE %s", obuf);
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 || !hdr.constructed ||
+	    !asn1_is_cs_tag(&hdr, 0)) {
+		asn1_unexpected(&hdr,
+				"PKCS #12: Expected [0] EXPLICIT (bagValue)");
+		return 0;
+	}
+	value = hdr.payload;
+	value_len = hdr.length;
+	wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: bagValue", value, value_len);
+	pos = hdr.payload + hdr.length;
+
+	if (pos < end) {
+		/* bagAttributes  SET OF PKCS12Attribute OPTIONAL */
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    !asn1_is_set(&hdr)) {
+			asn1_unexpected(&hdr,
+					"PKCS #12: Expected SET (bagAttributes)");
+			return -1;
+		}
+		wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: bagAttributes",
+				hdr.payload, hdr.length);
+
+		pos = hdr.payload;
+		end = hdr.payload + hdr.length;
+		while (pos < end) {
+			/* PKCS12Attribute ::= SEQUENCE */
+			if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+			    !asn1_is_sequence(&hdr)) {
+				asn1_unexpected(&hdr,
+						"PKCS #12: Expected SEQUENCE (PKCS12Attribute)");
+				return -1;
+			}
+			if (pkcs12_parse_attr(hdr.payload, hdr.length) < 0)
+				return -1;
+			pos = hdr.payload + hdr.length;
+		}
+	}
+
+	if (pkcs12_is_bagtype_oid(&oid, 1))
+		return pkcs12_keybag(cred, value, value_len);
+	if (pkcs12_is_bagtype_oid(&oid, 2))
+		return pkcs12_pkcs8_keybag(cred, value, value_len, passwd);
+	if (pkcs12_is_bagtype_oid(&oid, 3))
+		return pkcs12_certbag(cred, value, value_len);
+
+	wpa_printf(MSG_DEBUG, "PKCS #12: Ignore unsupported BAG-TYPE");
+	return 0;
+}
+
+
+static int pkcs12_safecontents(struct tlsv1_credentials *cred,
+			       const u8 *buf, size_t len,
+			       const char *passwd)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+
+	/* SafeContents ::= SEQUENCE OF SafeBag */
+	if (asn1_get_next(buf, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #12: Expected SEQUENCE (SafeContents)");
+		return -1;
+	}
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	/*
+	 * SafeBag ::= SEQUENCE {
+	 *   bagId          BAG-TYPE.&id ({PKCS12BagSet})
+	 *   bagValue       [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}),
+	 *   bagAttributes  SET OF PKCS12Attribute OPTIONAL
+	 * }
+	 */
+
+	while (pos < end) {
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    !asn1_is_sequence(&hdr)) {
+			asn1_unexpected(&hdr,
+					"PKCS #12: Expected SEQUENCE (SafeBag)");
+			return -1;
+		}
+		if (pkcs12_safebag(cred, hdr.payload, hdr.length, passwd) < 0)
+			return -1;
+		pos = hdr.payload + hdr.length;
+	}
+
+	return 0;
+}
+
+
+static int pkcs12_parse_content_data(struct tlsv1_credentials *cred,
+				     const u8 *pos, const u8 *end,
+				     const char *passwd)
+{
+	struct asn1_hdr hdr;
+
+	/* Data ::= OCTET STRING */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_octetstring(&hdr)) {
+		asn1_unexpected(&hdr, "PKCS #12: Expected OCTET STRING (Data)");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "PKCS #12: Data", hdr.payload, hdr.length);
+
+	return pkcs12_safecontents(cred, hdr.payload, hdr.length, passwd);
+}
+
+
+static int pkcs12_parse_content_enc_data(struct tlsv1_credentials *cred,
+					 const u8 *pos, const u8 *end,
+					 const char *passwd)
+{
+	struct asn1_hdr hdr;
+	struct asn1_oid oid;
+	char buf[80];
+	const u8 *enc_alg;
+	u8 *data;
+	size_t enc_alg_len, data_len;
+	int res = -1;
+
+	/*
+	 * EncryptedData ::= SEQUENCE {
+	 *   version Version,
+	 *   encryptedContentInfo EncryptedContentInfo }
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #12: Expected SEQUENCE (EncryptedData)");
+		return 0;
+	}
+	pos = hdr.payload;
+
+	/* Version ::= INTEGER */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 || !asn1_is_integer(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #12: No INTEGER tag found for version");
+		return -1;
+	}
+	if (hdr.length != 1 || hdr.payload[0] != 0) {
+		wpa_printf(MSG_DEBUG, "PKCS #12: Unrecognized PKCS #7 version");
+		return -1;
+	}
+	pos = hdr.payload + hdr.length;
+
+	wpa_hexdump(MSG_MSGDUMP, "PKCS #12: EncryptedContentInfo",
+		    pos, end - pos);
+
+	/*
+	 * EncryptedContentInfo ::= SEQUENCE {
+	 *   contentType ContentType,
+	 *   contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
+	 *   encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL }
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #12: Expected SEQUENCE (EncryptedContentInfo)");
+		return -1;
+	}
+
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	/* ContentType ::= OBJECT IDENTIFIER */
+	if (asn1_get_oid(pos, end - pos, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Could not find OBJECT IDENTIFIER (contentType)");
+		return -1;
+	}
+	asn1_oid_to_str(&oid, buf, sizeof(buf));
+	wpa_printf(MSG_DEBUG, "PKCS #12: EncryptedContentInfo::contentType %s",
+		   buf);
+
+	if (!is_oid_pkcs7_data(&oid)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Unsupported EncryptedContentInfo::contentType %s",
+			   buf);
+		return 0;
+	}
+
+	/* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #12: Expected SEQUENCE (ContentEncryptionAlgorithmIdentifier)");
+		return -1;
+	}
+	enc_alg = hdr.payload;
+	enc_alg_len = hdr.length;
+	pos = hdr.payload + hdr.length;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.constructed ||
+	    !asn1_is_cs_tag(&hdr, 0)) {
+		asn1_unexpected(&hdr,
+				"PKCS #12: Expected [0] IMPLICIT (encryptedContent)");
+		return -1;
+	}
+
+	/* EncryptedContent ::= OCTET STRING */
+	data = pkcs5_decrypt(enc_alg, enc_alg_len, hdr.payload, hdr.length,
+			     passwd, &data_len);
+	if (data) {
+		wpa_hexdump_key(MSG_MSGDUMP,
+				"PKCS #12: Decrypted encryptedContent",
+				data, data_len);
+		res = pkcs12_safecontents(cred, data, data_len, passwd);
+		os_free(data);
+	}
+
+	return res;
+}
+
+
+static int pkcs12_parse_content(struct tlsv1_credentials *cred,
+				const u8 *buf, size_t len,
+				const char *passwd)
+{
+	const u8 *pos = buf;
+	const u8 *end = buf + len;
+	struct asn1_oid oid;
+	char txt[80];
+	struct asn1_hdr hdr;
+
+	wpa_hexdump(MSG_MSGDUMP, "PKCS #12: ContentInfo", buf, len);
+
+	if (asn1_get_oid(pos, end - pos, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Could not find OBJECT IDENTIFIER (contentType)");
+		return 0;
+	}
+
+	asn1_oid_to_str(&oid, txt, sizeof(txt));
+	wpa_printf(MSG_DEBUG, "PKCS #12: contentType %s", txt);
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 || !hdr.constructed ||
+	    !asn1_is_cs_tag(&hdr, 0)) {
+		asn1_unexpected(&hdr,
+				"PKCS #12: Expected [0] EXPLICIT (content)");
+		return 0;
+	}
+	pos = hdr.payload;
+
+	if (is_oid_pkcs7_data(&oid))
+		return pkcs12_parse_content_data(cred, pos, end, passwd);
+	if (is_oid_pkcs7_enc_data(&oid))
+		return pkcs12_parse_content_enc_data(cred, pos, end, passwd);
+
+	wpa_printf(MSG_DEBUG, "PKCS #12: Ignored unsupported contentType %s",
+		   txt);
+
+	return 0;
+}
+
+
+static int pkcs12_parse(struct tlsv1_credentials *cred,
+			const u8 *key, size_t len, const char *passwd)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+	struct asn1_oid oid;
+	char buf[80];
+
+	/*
+	 * PFX ::= SEQUENCE {
+	 *     version     INTEGER {v3(3)}(v3,...),
+	 *     authSafe    ContentInfo,
+	 *     macData     MacData OPTIONAL
+	 * }
+	 */
+
+	if (asn1_get_next(key, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #12: Expected SEQUENCE (PFX); assume PKCS #12 not used");
+		return -1;
+	}
+
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 || !asn1_is_integer(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #12: No INTEGER tag found for version");
+		return -1;
+	}
+	if (hdr.length != 1 || hdr.payload[0] != 3) {
+		wpa_printf(MSG_DEBUG, "PKCS #12: Unrecognized version");
+		return -1;
+	}
+	pos = hdr.payload + hdr.length;
+
+	/*
+	 * ContentInfo ::= SEQUENCE {
+	 *   contentType ContentType,
+	 *   content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }
+	 */
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #12: Expected SEQUENCE (authSafe); assume PKCS #12 not used");
+		return -1;
+	}
+
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	/* ContentType ::= OBJECT IDENTIFIER */
+	if (asn1_get_oid(pos, end - pos, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Could not find OBJECT IDENTIFIER (contentType); assume PKCS #12 not used");
+		return -1;
+	}
+	asn1_oid_to_str(&oid, buf, sizeof(buf));
+	wpa_printf(MSG_DEBUG, "PKCS #12: contentType %s", buf);
+	if (!is_oid_pkcs7_data(&oid)) {
+		wpa_printf(MSG_DEBUG, "PKCS #12: Unsupported contentType %s",
+			   buf);
+		return -1;
+	}
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 || !hdr.constructed ||
+	    !asn1_is_cs_tag(&hdr, 0)) {
+		asn1_unexpected(&hdr,
+				"PKCS #12: Expected [0] EXPLICIT (content); assume PKCS #12 not used");
+		return -1;
+	}
+
+	pos = hdr.payload;
+
+	/* Data ::= OCTET STRING */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_octetstring(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #12: Expected OCTET STRING (Data); assume PKCS #12 not used");
+		return -1;
+	}
+
+	/*
+	 * AuthenticatedSafe ::= SEQUENCE OF ContentInfo
+	 *     -- Data if unencrypted
+	 *     -- EncryptedData if password-encrypted
+	 *     -- EnvelopedData if public key-encrypted
+	 */
+	wpa_hexdump(MSG_MSGDUMP, "PKCS #12: Data content",
+		    hdr.payload, hdr.length);
+
+	if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 ||
+	    !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"PKCS #12: Expected SEQUENCE within Data content; assume PKCS #12 not used");
+		return -1;
+	}
+
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	while (end > pos) {
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    !asn1_is_sequence(&hdr)) {
+			asn1_unexpected(&hdr,
+					"PKCS #12: Expected SEQUENCE (ContentInfo); assume PKCS #12 not used");
+			return -1;
+		}
+		if (pkcs12_parse_content(cred, hdr.payload, hdr.length,
+					 passwd) < 0)
+			return -1;
+
+		pos = hdr.payload + hdr.length;
+	}
+
+	return 0;
+}
+
+#endif /* PKCS12_FUNCS */
+
+
+static int tlsv1_set_key(struct tlsv1_credentials *cred,
+			 const u8 *key, size_t len, const char *passwd)
+{
+	cred->key = crypto_private_key_import(key, len, passwd);
+	if (cred->key == NULL)
+		cred->key = tlsv1_set_key_pem(key, len);
+	if (cred->key == NULL)
+		cred->key = tlsv1_set_key_enc_pem(key, len, passwd);
+#ifdef PKCS12_FUNCS
+	if (!cred->key)
+		pkcs12_parse(cred, key, len, passwd);
+#endif /* PKCS12_FUNCS */
+	if (cred->key == NULL) {
+		wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key");
+		return -1;
+	}
+	return 0;
+}
+
+
+/**
+ * tlsv1_set_private_key - Set private key
+ * @cred: TLSv1 credentials from tlsv1_cred_alloc()
+ * @private_key: File or reference name for the key in PEM or DER format
+ * @private_key_passwd: Passphrase for decrypted private key, %NULL if no
+ * passphrase is used.
+ * @private_key_blob: private_key as inlined data or %NULL if not used
+ * @private_key_blob_len: private_key_blob length
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_set_private_key(struct tlsv1_credentials *cred,
+			  const char *private_key,
+			  const char *private_key_passwd,
+			  const u8 *private_key_blob,
+			  size_t private_key_blob_len)
+{
+	crypto_private_key_free(cred->key);
+	cred->key = NULL;
+
+	if (private_key_blob)
+		return tlsv1_set_key(cred, private_key_blob,
+				     private_key_blob_len,
+				     private_key_passwd);
+
+	if (private_key) {
+		u8 *buf;
+		size_t len;
+		int ret;
+
+		buf = (u8 *) os_readfile(private_key, &len);
+		if (buf == NULL) {
+			wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
+				   private_key);
+			return -1;
+		}
+
+		ret = tlsv1_set_key(cred, buf, len, private_key_passwd);
+		os_free(buf);
+		return ret;
+	}
+
+	return 0;
+}
+
+
+static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred,
+				  const u8 *dh, size_t len)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+
+	pos = dh;
+	end = dh + len;
+
+	/*
+	 * DHParameter ::= SEQUENCE {
+	 *   prime INTEGER, -- p
+	 *   base INTEGER, -- g
+	 *   privateValueLength INTEGER OPTIONAL }
+	 */
+
+	/* DHParamer ::= SEQUENCE */
+	if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"DH: DH parameters did not start with a valid SEQUENCE");
+		return -1;
+	}
+	pos = hdr.payload;
+
+	/* prime INTEGER */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_integer(&hdr)) {
+		asn1_unexpected(&hdr, "DH: No INTEGER tag found for p");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "DH: prime (p)", hdr.payload, hdr.length);
+	if (hdr.length == 0)
+		return -1;
+	os_free(cred->dh_p);
+	cred->dh_p = os_memdup(hdr.payload, hdr.length);
+	if (cred->dh_p == NULL)
+		return -1;
+	cred->dh_p_len = hdr.length;
+	pos = hdr.payload + hdr.length;
+
+	/* base INTEGER */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_integer(&hdr)) {
+		asn1_unexpected(&hdr, "DH: No INTEGER tag found for g");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "DH: base (g)", hdr.payload, hdr.length);
+	if (hdr.length == 0)
+		return -1;
+	os_free(cred->dh_g);
+	cred->dh_g = os_memdup(hdr.payload, hdr.length);
+	if (cred->dh_g == NULL)
+		return -1;
+	cred->dh_g_len = hdr.length;
+
+	return 0;
+}
+
+
+static const char *pem_dhparams_begin = "-----BEGIN DH PARAMETERS-----";
+static const char *pem_dhparams_end = "-----END DH PARAMETERS-----";
+
+
+static int tlsv1_set_dhparams_blob(struct tlsv1_credentials *cred,
+				   const u8 *buf, size_t len)
+{
+	const u8 *pos, *end;
+	unsigned char *der;
+	size_t der_len;
+
+	pos = search_tag(pem_dhparams_begin, buf, len);
+	if (!pos) {
+		wpa_printf(MSG_DEBUG, "TLSv1: No PEM dhparams tag found - "
+			   "assume DER format");
+		return tlsv1_set_dhparams_der(cred, buf, len);
+	}
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format dhparams into DER "
+		   "format");
+
+	pos += os_strlen(pem_dhparams_begin);
+	end = search_tag(pem_dhparams_end, pos, buf + len - pos);
+	if (end == NULL) {
+		wpa_printf(MSG_INFO, "TLSv1: Could not find PEM dhparams end "
+			   "tag (%s)", pem_dhparams_end);
+		return -1;
+	}
+
+	der = base64_decode((const char *) pos, end - pos, &der_len);
+	if (der == NULL) {
+		wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM dhparams");
+		return -1;
+	}
+
+	if (tlsv1_set_dhparams_der(cred, der, der_len) < 0) {
+		wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM dhparams "
+			   "DER conversion");
+		os_free(der);
+		return -1;
+	}
+
+	os_free(der);
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_set_dhparams - Set Diffie-Hellman parameters
+ * @cred: TLSv1 credentials from tlsv1_cred_alloc()
+ * @dh_file: File or reference name for the DH params in PEM or DER format
+ * @dh_blob: DH params as inlined data or %NULL if not used
+ * @dh_blob_len: dh_blob length
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file,
+		       const u8 *dh_blob, size_t dh_blob_len)
+{
+	if (dh_blob)
+		return tlsv1_set_dhparams_blob(cred, dh_blob, dh_blob_len);
+
+	if (dh_file) {
+		u8 *buf;
+		size_t len;
+		int ret;
+
+		buf = (u8 *) os_readfile(dh_file, &len);
+		if (buf == NULL) {
+			wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
+				   dh_file);
+			return -1;
+		}
+
+		ret = tlsv1_set_dhparams_blob(cred, buf, len);
+		os_free(buf);
+		return ret;
+	}
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_cred.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_cred.h
new file mode 100644
index 0000000..716e93c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_cred.h
@@ -0,0 +1,48 @@
+/*
+ * TLSv1 credentials
+ * 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.
+ */
+
+#ifndef TLSV1_CRED_H
+#define TLSV1_CRED_H
+
+struct tlsv1_credentials {
+	struct x509_certificate *trusted_certs;
+	struct x509_certificate *cert;
+	struct crypto_private_key *key;
+
+	unsigned int cert_probe:1;
+	unsigned int ca_cert_verify:1;
+	unsigned int server_cert_only:1;
+	u8 srv_cert_hash[32];
+
+	/* Diffie-Hellman parameters */
+	u8 *dh_p; /* prime */
+	size_t dh_p_len;
+	u8 *dh_g; /* generator */
+	size_t dh_g_len;
+
+	char *ocsp_stapling_response;
+	char *ocsp_stapling_response_multi;
+};
+
+
+struct tlsv1_credentials * tlsv1_cred_alloc(void);
+void tlsv1_cred_free(struct tlsv1_credentials *cred);
+int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert,
+		      const u8 *cert_blob, size_t cert_blob_len,
+		      const char *path);
+int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert,
+		   const u8 *cert_blob, size_t cert_blob_len);
+int tlsv1_set_private_key(struct tlsv1_credentials *cred,
+			  const char *private_key,
+			  const char *private_key_passwd,
+			  const u8 *private_key_blob,
+			  size_t private_key_blob_len);
+int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file,
+		       const u8 *dh_blob, size_t dh_blob_len);
+
+#endif /* TLSV1_CRED_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_record.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_record.c
new file mode 100644
index 0000000..0c6897a
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_record.c
@@ -0,0 +1,485 @@
+/*
+ * TLSv1 Record Protocol
+ * 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/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+
+
+/**
+ * tlsv1_record_set_cipher_suite - TLS record layer: Set cipher suite
+ * @rl: Pointer to TLS record layer data
+ * @cipher_suite: New cipher suite
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to prepare TLS record layer for cipher suite change.
+ * tlsv1_record_change_write_cipher() and
+ * tlsv1_record_change_read_cipher() functions can then be used to change the
+ * currently used ciphers.
+ */
+int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl,
+				  u16 cipher_suite)
+{
+	const struct tls_cipher_suite *suite;
+	const struct tls_cipher_data *data;
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Selected cipher suite: 0x%04x",
+		   cipher_suite);
+	rl->cipher_suite = cipher_suite;
+
+	suite = tls_get_cipher_suite(cipher_suite);
+	if (suite == NULL)
+		return -1;
+
+	if (suite->hash == TLS_HASH_MD5) {
+		rl->hash_alg = CRYPTO_HASH_ALG_HMAC_MD5;
+		rl->hash_size = MD5_MAC_LEN;
+	} else if (suite->hash == TLS_HASH_SHA) {
+		rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA1;
+		rl->hash_size = SHA1_MAC_LEN;
+	} else if (suite->hash == TLS_HASH_SHA256) {
+		rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA256;
+		rl->hash_size = SHA256_MAC_LEN;
+	}
+
+	data = tls_get_cipher_data(suite->cipher);
+	if (data == NULL)
+		return -1;
+
+	rl->key_material_len = data->key_material;
+	rl->iv_size = data->block_size;
+	rl->cipher_alg = data->alg;
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_record_change_write_cipher - TLS record layer: Change write cipher
+ * @rl: Pointer to TLS record layer data
+ * Returns: 0 on success (cipher changed), -1 on failure
+ *
+ * This function changes TLS record layer to use the new cipher suite
+ * configured with tlsv1_record_set_cipher_suite() for writing.
+ */
+int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl)
+{
+	wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New write cipher suite "
+		   "0x%04x", rl->cipher_suite);
+	rl->write_cipher_suite = rl->cipher_suite;
+	os_memset(rl->write_seq_num, 0, TLS_SEQ_NUM_LEN);
+
+	if (rl->write_cbc) {
+		crypto_cipher_deinit(rl->write_cbc);
+		rl->write_cbc = NULL;
+	}
+	if (rl->cipher_alg != CRYPTO_CIPHER_NULL) {
+		rl->write_cbc = crypto_cipher_init(rl->cipher_alg,
+						   rl->write_iv, rl->write_key,
+						   rl->key_material_len);
+		if (rl->write_cbc == NULL) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize "
+				   "cipher");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_record_change_read_cipher - TLS record layer: Change read cipher
+ * @rl: Pointer to TLS record layer data
+ * Returns: 0 on success (cipher changed), -1 on failure
+ *
+ * This function changes TLS record layer to use the new cipher suite
+ * configured with tlsv1_record_set_cipher_suite() for reading.
+ */
+int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl)
+{
+	wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New read cipher suite "
+		   "0x%04x", rl->cipher_suite);
+	rl->read_cipher_suite = rl->cipher_suite;
+	os_memset(rl->read_seq_num, 0, TLS_SEQ_NUM_LEN);
+
+	if (rl->read_cbc) {
+		crypto_cipher_deinit(rl->read_cbc);
+		rl->read_cbc = NULL;
+	}
+	if (rl->cipher_alg != CRYPTO_CIPHER_NULL) {
+		rl->read_cbc = crypto_cipher_init(rl->cipher_alg,
+						  rl->read_iv, rl->read_key,
+						  rl->key_material_len);
+		if (rl->read_cbc == NULL) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize "
+				   "cipher");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_record_send - TLS record layer: Send a message
+ * @rl: Pointer to TLS record layer data
+ * @content_type: Content type (TLS_CONTENT_TYPE_*)
+ * @buf: Buffer for the generated TLS message (needs to have extra space for
+ * header, IV (TLS v1.1), and HMAC)
+ * @buf_size: Maximum buf size
+ * @payload: Payload to be sent
+ * @payload_len: Length of the payload
+ * @out_len: Buffer for returning the used buf length
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function fills in the TLS record layer header, adds HMAC, and encrypts
+ * the data using the current write cipher.
+ */
+int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
+		      size_t buf_size, const u8 *payload, size_t payload_len,
+		      size_t *out_len)
+{
+	u8 *pos, *ct_start, *length, *cpayload;
+	struct crypto_hash *hmac;
+	size_t clen;
+	int explicit_iv;
+
+	pos = buf;
+	if (pos + TLS_RECORD_HEADER_LEN > buf + buf_size)
+		return -1;
+
+	/* ContentType type */
+	ct_start = pos;
+	*pos++ = content_type;
+	/* ProtocolVersion version */
+	WPA_PUT_BE16(pos, rl->tls_version);
+	pos += 2;
+	/* uint16 length */
+	length = pos;
+	WPA_PUT_BE16(length, payload_len);
+	pos += 2;
+
+	cpayload = pos;
+	explicit_iv = rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL &&
+		rl->iv_size && rl->tls_version >= TLS_VERSION_1_1;
+	if (explicit_iv) {
+		/* opaque IV[Cipherspec.block_length] */
+		if (pos + rl->iv_size > buf + buf_size)
+			return -1;
+
+		/*
+		 * Use random number R per the RFC 4346, 6.2.3.2 CBC Block
+		 * Cipher option 2a.
+		 */
+
+		if (os_get_random(pos, rl->iv_size))
+			return -1;
+		pos += rl->iv_size;
+	}
+
+	/*
+	 * opaque fragment[TLSPlaintext.length]
+	 * (opaque content[TLSCompressed.length] in GenericBlockCipher)
+	 */
+	if (pos + payload_len > buf + buf_size)
+		return -1;
+	os_memmove(pos, payload, payload_len);
+	pos += payload_len;
+
+	if (rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL) {
+		/*
+		 * MAC calculated over seq_num + TLSCompressed.type +
+		 * TLSCompressed.version + TLSCompressed.length +
+		 * TLSCompressed.fragment
+		 */
+		hmac = crypto_hash_init(rl->hash_alg, rl->write_mac_secret,
+					rl->hash_size);
+		if (hmac == NULL) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
+				   "to initialize HMAC");
+			return -1;
+		}
+		crypto_hash_update(hmac, rl->write_seq_num, TLS_SEQ_NUM_LEN);
+		/* type + version + length + fragment */
+		crypto_hash_update(hmac, ct_start, TLS_RECORD_HEADER_LEN);
+		crypto_hash_update(hmac, payload, payload_len);
+		clen = buf + buf_size - pos;
+		if (clen < rl->hash_size) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Not "
+				   "enough room for MAC");
+			crypto_hash_finish(hmac, NULL, NULL);
+			return -1;
+		}
+
+		if (crypto_hash_finish(hmac, pos, &clen) < 0) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
+				   "to calculate HMAC");
+			return -1;
+		}
+		wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Write HMAC",
+			    pos, clen);
+		pos += clen;
+		if (rl->iv_size) {
+			size_t len = pos - cpayload;
+			size_t pad;
+			pad = (len + 1) % rl->iv_size;
+			if (pad)
+				pad = rl->iv_size - pad;
+			if (pos + pad + 1 > buf + buf_size) {
+				wpa_printf(MSG_DEBUG, "TLSv1: No room for "
+					   "block cipher padding");
+				return -1;
+			}
+			os_memset(pos, pad, pad + 1);
+			pos += pad + 1;
+		}
+
+		if (crypto_cipher_encrypt(rl->write_cbc, cpayload,
+					  cpayload, pos - cpayload) < 0)
+			return -1;
+	}
+
+	WPA_PUT_BE16(length, pos - length - 2);
+	inc_byte_array(rl->write_seq_num, TLS_SEQ_NUM_LEN);
+
+	*out_len = pos - buf;
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_record_receive - TLS record layer: Process a received message
+ * @rl: Pointer to TLS record layer data
+ * @in_data: Received data
+ * @in_len: Length of the received data
+ * @out_data: Buffer for output data (must be at least as long as in_data)
+ * @out_len: Set to maximum out_data length by caller; used to return the
+ * length of the used data
+ * @alert: Buffer for returning an alert value on failure
+ * Returns: Number of bytes used from in_data on success, 0 if record was not
+ *	complete (more data needed), or -1 on failure
+ *
+ * This function decrypts the received message, verifies HMAC and TLS record
+ * layer header.
+ */
+int tlsv1_record_receive(struct tlsv1_record_layer *rl,
+			 const u8 *in_data, size_t in_len,
+			 u8 *out_data, size_t *out_len, u8 *alert)
+{
+	size_t i, rlen, hlen;
+	u8 padlen;
+	struct crypto_hash *hmac;
+	u8 len[2], hash[100];
+	int force_mac_error = 0;
+	u8 ct;
+
+	if (in_len < TLS_RECORD_HEADER_LEN) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu) - "
+			   "need more data",
+			   (unsigned long) in_len);
+		wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
+			    in_data, in_len);
+		return 0;
+	}
+
+	ct = in_data[0];
+	rlen = WPA_GET_BE16(in_data + 3);
+	wpa_printf(MSG_DEBUG, "TLSv1: Received content type %d version %d.%d "
+		   "length %d", ct, in_data[1], in_data[2], (int) rlen);
+
+	/*
+	 * TLS v1.0 and v1.1 RFCs were not exactly clear on the use of the
+	 * protocol version in record layer. As such, accept any {03,xx} value
+	 * to remain compatible with existing implementations.
+	 */
+	if (in_data[1] != 0x03) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version "
+			   "%u.%u", in_data[1], in_data[2]);
+		*alert = TLS_ALERT_PROTOCOL_VERSION;
+		return -1;
+	}
+
+	/* TLSCiphertext must not be more than 2^14+2048 bytes */
+	if (TLS_RECORD_HEADER_LEN + rlen > 18432) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)",
+			   (unsigned long) (TLS_RECORD_HEADER_LEN + rlen));
+		*alert = TLS_ALERT_RECORD_OVERFLOW;
+		return -1;
+	}
+
+	in_data += TLS_RECORD_HEADER_LEN;
+	in_len -= TLS_RECORD_HEADER_LEN;
+
+	if (rlen > in_len) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Not all record data included "
+			   "(rlen=%lu > in_len=%lu)",
+			   (unsigned long) rlen, (unsigned long) in_len);
+		return 0;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
+		    in_data, rlen);
+
+	if (ct != TLS_CONTENT_TYPE_HANDSHAKE &&
+	    ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC &&
+	    ct != TLS_CONTENT_TYPE_ALERT &&
+	    ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Ignore record with unknown "
+			   "content type 0x%x", ct);
+		*alert = TLS_ALERT_UNEXPECTED_MESSAGE;
+		return -1;
+	}
+
+	in_len = rlen;
+
+	if (*out_len < in_len) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Not enough output buffer for "
+			   "processing received record");
+		*alert = TLS_ALERT_INTERNAL_ERROR;
+		return -1;
+	}
+
+	if (rl->read_cipher_suite != TLS_NULL_WITH_NULL_NULL) {
+		size_t plen;
+		if (crypto_cipher_decrypt(rl->read_cbc, in_data,
+					  out_data, in_len) < 0) {
+			*alert = TLS_ALERT_DECRYPTION_FAILED;
+			return -1;
+		}
+		plen = in_len;
+		wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted "
+				"data", out_data, plen);
+
+		if (rl->iv_size) {
+			/*
+			 * TLS v1.0 defines different alert values for various
+			 * failures. That may information to aid in attacks, so
+			 * use the same bad_record_mac alert regardless of the
+			 * issues.
+			 *
+			 * In addition, instead of returning immediately on
+			 * error, run through the MAC check to make timing
+			 * attacks more difficult.
+			 */
+
+			if (rl->tls_version >= TLS_VERSION_1_1) {
+				/* Remove opaque IV[Cipherspec.block_length] */
+				if (plen < rl->iv_size) {
+					wpa_printf(MSG_DEBUG, "TLSv1.1: Not "
+						   "enough room for IV");
+					force_mac_error = 1;
+					goto check_mac;
+				}
+				os_memmove(out_data, out_data + rl->iv_size,
+					   plen - rl->iv_size);
+				plen -= rl->iv_size;
+			}
+
+			/* Verify and remove padding */
+			if (plen == 0) {
+				wpa_printf(MSG_DEBUG, "TLSv1: Too short record"
+					   " (no pad)");
+				force_mac_error = 1;
+				goto check_mac;
+			}
+			padlen = out_data[plen - 1];
+			if (padlen >= plen) {
+				wpa_printf(MSG_DEBUG, "TLSv1: Incorrect pad "
+					   "length (%u, plen=%lu) in "
+					   "received record",
+					   padlen, (unsigned long) plen);
+				force_mac_error = 1;
+				goto check_mac;
+			}
+			for (i = plen - padlen - 1; i < plen - 1; i++) {
+				if (out_data[i] != padlen) {
+					wpa_hexdump(MSG_DEBUG,
+						    "TLSv1: Invalid pad in "
+						    "received record",
+						    out_data + plen - padlen -
+						    1, padlen + 1);
+					force_mac_error = 1;
+					goto check_mac;
+				}
+			}
+
+			plen -= padlen + 1;
+
+			wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - "
+					"Decrypted data with IV and padding "
+					"removed", out_data, plen);
+		}
+
+	check_mac:
+		if (plen < rl->hash_size) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no "
+				   "hash value");
+			*alert = TLS_ALERT_BAD_RECORD_MAC;
+			return -1;
+		}
+
+		plen -= rl->hash_size;
+
+		hmac = crypto_hash_init(rl->hash_alg, rl->read_mac_secret,
+					rl->hash_size);
+		if (hmac == NULL) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
+				   "to initialize HMAC");
+			*alert = TLS_ALERT_INTERNAL_ERROR;
+			return -1;
+		}
+
+		crypto_hash_update(hmac, rl->read_seq_num, TLS_SEQ_NUM_LEN);
+		/* type + version + length + fragment */
+		crypto_hash_update(hmac, in_data - TLS_RECORD_HEADER_LEN, 3);
+		WPA_PUT_BE16(len, plen);
+		crypto_hash_update(hmac, len, 2);
+		crypto_hash_update(hmac, out_data, plen);
+		hlen = sizeof(hash);
+		if (crypto_hash_finish(hmac, hash, &hlen) < 0) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
+				   "to calculate HMAC");
+			*alert = TLS_ALERT_INTERNAL_ERROR;
+			return -1;
+		}
+		if (hlen != rl->hash_size ||
+		    os_memcmp_const(hash, out_data + plen, hlen) != 0 ||
+		    force_mac_error) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in "
+				   "received message (force_mac_error=%d)",
+				   force_mac_error);
+			*alert = TLS_ALERT_BAD_RECORD_MAC;
+			return -1;
+		}
+
+		*out_len = plen;
+	} else {
+		os_memcpy(out_data, in_data, in_len);
+		*out_len = in_len;
+	}
+
+	/* TLSCompressed must not be more than 2^14+1024 bytes */
+	if (TLS_RECORD_HEADER_LEN + *out_len > 17408) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)",
+			   (unsigned long) (TLS_RECORD_HEADER_LEN + *out_len));
+		*alert = TLS_ALERT_RECORD_OVERFLOW;
+		return -1;
+	}
+
+	inc_byte_array(rl->read_seq_num, TLS_SEQ_NUM_LEN);
+
+	return TLS_RECORD_HEADER_LEN + rlen;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_record.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_record.h
new file mode 100644
index 0000000..48abcb0
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_record.h
@@ -0,0 +1,71 @@
+/*
+ * TLSv1 Record Protocol
+ * 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.
+ */
+
+#ifndef TLSV1_RECORD_H
+#define TLSV1_RECORD_H
+
+#include "crypto/crypto.h"
+
+#define TLS_MAX_WRITE_MAC_SECRET_LEN 32
+#define TLS_MAX_WRITE_KEY_LEN 32
+#define TLS_MAX_IV_LEN 16
+#define TLS_MAX_KEY_BLOCK_LEN (2 * (TLS_MAX_WRITE_MAC_SECRET_LEN + \
+				    TLS_MAX_WRITE_KEY_LEN + TLS_MAX_IV_LEN))
+
+#define TLS_SEQ_NUM_LEN 8
+#define TLS_RECORD_HEADER_LEN 5
+
+/* ContentType */
+enum {
+	TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC = 20,
+	TLS_CONTENT_TYPE_ALERT = 21,
+	TLS_CONTENT_TYPE_HANDSHAKE = 22,
+	TLS_CONTENT_TYPE_APPLICATION_DATA = 23
+};
+
+struct tlsv1_record_layer {
+	u16 tls_version;
+
+	u8 write_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN];
+	u8 read_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN];
+	u8 write_key[TLS_MAX_WRITE_KEY_LEN];
+	u8 read_key[TLS_MAX_WRITE_KEY_LEN];
+	u8 write_iv[TLS_MAX_IV_LEN];
+	u8 read_iv[TLS_MAX_IV_LEN];
+
+	size_t hash_size;
+	size_t key_material_len;
+	size_t iv_size; /* also block_size */
+
+	enum crypto_hash_alg hash_alg;
+	enum crypto_cipher_alg cipher_alg;
+
+	u8 write_seq_num[TLS_SEQ_NUM_LEN];
+	u8 read_seq_num[TLS_SEQ_NUM_LEN];
+
+	u16 cipher_suite;
+	u16 write_cipher_suite;
+	u16 read_cipher_suite;
+
+	struct crypto_cipher *write_cbc;
+	struct crypto_cipher *read_cbc;
+};
+
+
+int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl,
+				  u16 cipher_suite);
+int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl);
+int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl);
+int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
+		      size_t buf_size, const u8 *payload, size_t payload_len,
+		      size_t *out_len);
+int tlsv1_record_receive(struct tlsv1_record_layer *rl,
+			 const u8 *in_data, size_t in_len,
+			 u8 *out_data, size_t *out_len, u8 *alert);
+
+#endif /* TLSV1_RECORD_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_server.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_server.c
new file mode 100644
index 0000000..12dcc85
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_server.c
@@ -0,0 +1,867 @@
+/*
+ * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246)
+ * Copyright (c) 2006-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 "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+#include "tlsv1_server.h"
+#include "tlsv1_server_i.h"
+
+/* TODO:
+ * Support for a message fragmented across several records (RFC 2246, 6.2.1)
+ */
+
+
+void tlsv1_server_log(struct tlsv1_server *conn, const char *fmt, ...)
+{
+	va_list ap;
+	char *buf;
+	int buflen;
+
+	va_start(ap, fmt);
+	buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+	va_end(ap);
+
+	buf = os_malloc(buflen);
+	if (buf == NULL)
+		return;
+	va_start(ap, fmt);
+	vsnprintf(buf, buflen, fmt, ap);
+	va_end(ap);
+
+	wpa_printf(MSG_DEBUG, "TLSv1: %s", buf);
+	if (conn->log_cb)
+		conn->log_cb(conn->log_cb_ctx, buf);
+
+	os_free(buf);
+}
+
+
+void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description)
+{
+	conn->alert_level = level;
+	conn->alert_description = description;
+}
+
+
+int tlsv1_server_derive_keys(struct tlsv1_server *conn,
+			     const u8 *pre_master_secret,
+			     size_t pre_master_secret_len)
+{
+	u8 seed[2 * TLS_RANDOM_LEN];
+	u8 key_block[TLS_MAX_KEY_BLOCK_LEN];
+	u8 *pos;
+	size_t key_block_len;
+
+	if (pre_master_secret) {
+		wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret",
+				pre_master_secret, pre_master_secret_len);
+		os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
+		os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
+			  TLS_RANDOM_LEN);
+		if (tls_prf(conn->rl.tls_version,
+			    pre_master_secret, pre_master_secret_len,
+			    "master secret", seed, 2 * TLS_RANDOM_LEN,
+			    conn->master_secret, TLS_MASTER_SECRET_LEN)) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive "
+				   "master_secret");
+			return -1;
+		}
+		wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret",
+				conn->master_secret, TLS_MASTER_SECRET_LEN);
+	}
+
+	os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
+	os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN);
+	key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len +
+			     conn->rl.iv_size);
+	if (tls_prf(conn->rl.tls_version,
+		    conn->master_secret, TLS_MASTER_SECRET_LEN,
+		    "key expansion", seed, 2 * TLS_RANDOM_LEN,
+		    key_block, key_block_len)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block");
+		return -1;
+	}
+	wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block",
+			key_block, key_block_len);
+
+	pos = key_block;
+
+	/* client_write_MAC_secret */
+	os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size);
+	pos += conn->rl.hash_size;
+	/* server_write_MAC_secret */
+	os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size);
+	pos += conn->rl.hash_size;
+
+	/* client_write_key */
+	os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len);
+	pos += conn->rl.key_material_len;
+	/* server_write_key */
+	os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len);
+	pos += conn->rl.key_material_len;
+
+	/* client_write_IV */
+	os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size);
+	pos += conn->rl.iv_size;
+	/* server_write_IV */
+	os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size);
+	pos += conn->rl.iv_size;
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_server_handshake - Process TLS handshake
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @in_data: Input data from TLS peer
+ * @in_len: Input data length
+ * @out_len: Length of the output buffer.
+ * Returns: Pointer to output data, %NULL on failure
+ */
+u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
+			    const u8 *in_data, size_t in_len,
+			    size_t *out_len)
+{
+	const u8 *pos, *end;
+	u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct;
+	size_t in_msg_len;
+	int used;
+
+	if (in_data == NULL || in_len == 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: No input data to server");
+		return NULL;
+	}
+
+	pos = in_data;
+	end = in_data + in_len;
+	in_msg = os_malloc(in_len);
+	if (in_msg == NULL)
+		return NULL;
+
+	/* Each received packet may include multiple records */
+	while (pos < end) {
+		in_msg_len = in_len;
+		used = tlsv1_record_receive(&conn->rl, pos, end - pos,
+					    in_msg, &in_msg_len, &alert);
+		if (used < 0) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Processing received "
+				   "record failed");
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+			goto failed;
+		}
+		if (used == 0) {
+			/* need more data */
+			wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not "
+				   "yet supported");
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_INTERNAL_ERROR);
+			goto failed;
+		}
+		ct = pos[0];
+
+		in_pos = in_msg;
+		in_end = in_msg + in_msg_len;
+
+		/* Each received record may include multiple messages of the
+		 * same ContentType. */
+		while (in_pos < in_end) {
+			in_msg_len = in_end - in_pos;
+			if (tlsv1_server_process_handshake(conn, ct, in_pos,
+							   &in_msg_len) < 0)
+				goto failed;
+			in_pos += in_msg_len;
+		}
+
+		pos += used;
+	}
+
+	os_free(in_msg);
+	in_msg = NULL;
+
+	msg = tlsv1_server_handshake_write(conn, out_len);
+
+failed:
+	os_free(in_msg);
+	if (conn->alert_level) {
+		if (conn->state == FAILED) {
+			/* Avoid alert loops */
+			wpa_printf(MSG_DEBUG, "TLSv1: Drop alert loop");
+			os_free(msg);
+			return NULL;
+		}
+		conn->state = FAILED;
+		os_free(msg);
+		msg = tlsv1_server_send_alert(conn, conn->alert_level,
+					      conn->alert_description,
+					      out_len);
+		conn->write_alerts++;
+	}
+
+	return msg;
+}
+
+
+/**
+ * tlsv1_server_encrypt - Encrypt data into TLS tunnel
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @in_data: Pointer to plaintext data to be encrypted
+ * @in_len: Input buffer length
+ * @out_data: Pointer to output buffer (encrypted TLS data)
+ * @out_len: Maximum out_data length
+ * Returns: Number of bytes written to out_data, -1 on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * send data in the encrypted tunnel.
+ */
+int tlsv1_server_encrypt(struct tlsv1_server *conn,
+			 const u8 *in_data, size_t in_len,
+			 u8 *out_data, size_t out_len)
+{
+	size_t rlen;
+
+	wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
+			in_data, in_len);
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
+			      out_data, out_len, in_data, in_len, &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	return rlen;
+}
+
+
+/**
+ * tlsv1_server_decrypt - Decrypt data from TLS tunnel
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @in_data: Pointer to input buffer (encrypted TLS data)
+ * @in_len: Input buffer length
+ * @out_data: Pointer to output buffer (decrypted data from TLS tunnel)
+ * @out_len: Maximum out_data length
+ * Returns: Number of bytes written to out_data, -1 on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * receive data from the encrypted tunnel.
+ */
+int tlsv1_server_decrypt(struct tlsv1_server *conn,
+			 const u8 *in_data, size_t in_len,
+			 u8 *out_data, size_t out_len)
+{
+	const u8 *in_end, *pos;
+	int used;
+	u8 alert, *out_end, *out_pos, ct;
+	size_t olen;
+
+	pos = in_data;
+	in_end = in_data + in_len;
+	out_pos = out_data;
+	out_end = out_data + out_len;
+
+	while (pos < in_end) {
+		ct = pos[0];
+		olen = out_end - out_pos;
+		used = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
+					    out_pos, &olen, &alert);
+		if (used < 0) {
+			tlsv1_server_log(conn, "Record layer processing failed");
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+			return -1;
+		}
+		if (used == 0) {
+			/* need more data */
+			wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not "
+				   "yet supported");
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+			return -1;
+		}
+
+		if (ct == TLS_CONTENT_TYPE_ALERT) {
+			if (olen < 2) {
+				tlsv1_server_log(conn, "Alert underflow");
+				tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+						   TLS_ALERT_DECODE_ERROR);
+				return -1;
+			}
+			tlsv1_server_log(conn, "Received alert %d:%d",
+					 out_pos[0], out_pos[1]);
+			conn->read_alerts++;
+			if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) {
+				/* Continue processing */
+				pos += used;
+				continue;
+			}
+
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   out_pos[1]);
+			return -1;
+		}
+
+		if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+			tlsv1_server_log(conn, "Unexpected content type 0x%x",
+					 pos[0]);
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_UNEXPECTED_MESSAGE);
+			return -1;
+		}
+
+#ifdef CONFIG_TESTING_OPTIONS
+		if ((conn->test_flags &
+		     (TLS_BREAK_VERIFY_DATA | TLS_BREAK_SRV_KEY_X_HASH |
+		      TLS_BREAK_SRV_KEY_X_SIGNATURE)) &&
+		    !conn->test_failure_reported) {
+			tlsv1_server_log(conn, "TEST-FAILURE: Client ApplData received after invalid handshake");
+			conn->test_failure_reported = 1;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+		out_pos += olen;
+		if (out_pos > out_end) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough "
+				   "for processing the received record");
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_INTERNAL_ERROR);
+			return -1;
+		}
+
+		pos += used;
+	}
+
+	return out_pos - out_data;
+}
+
+
+/**
+ * tlsv1_server_global_init - Initialize TLSv1 server
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function must be called before using any other TLSv1 server functions.
+ */
+int tlsv1_server_global_init(void)
+{
+	return crypto_global_init();
+}
+
+
+/**
+ * tlsv1_server_global_deinit - Deinitialize TLSv1 server
+ *
+ * This function can be used to deinitialize the TLSv1 server that was
+ * initialized by calling tlsv1_server_global_init(). No TLSv1 server functions
+ * can be called after this before calling tlsv1_server_global_init() again.
+ */
+void tlsv1_server_global_deinit(void)
+{
+	crypto_global_deinit();
+}
+
+
+/**
+ * tlsv1_server_init - Initialize TLSv1 server connection
+ * @cred: Pointer to server credentials from tlsv1_server_cred_alloc()
+ * Returns: Pointer to TLSv1 server connection data or %NULL on failure
+ */
+struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred)
+{
+	struct tlsv1_server *conn;
+	size_t count;
+	u16 *suites;
+
+	conn = os_zalloc(sizeof(*conn));
+	if (conn == NULL)
+		return NULL;
+
+	conn->cred = cred;
+
+	conn->state = CLIENT_HELLO;
+
+	if (tls_verify_hash_init(&conn->verify) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify "
+			   "hash");
+		os_free(conn);
+		return NULL;
+	}
+
+	count = 0;
+	suites = conn->cipher_suites;
+	suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA256;
+	suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256;
+	suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
+	suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+	suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA256;
+	suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
+	suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
+	suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+	suites[count++] = TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA;
+	suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
+	suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
+	suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
+	conn->num_cipher_suites = count;
+
+	return conn;
+}
+
+
+static void tlsv1_server_clear_data(struct tlsv1_server *conn)
+{
+	tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
+	tlsv1_record_change_write_cipher(&conn->rl);
+	tlsv1_record_change_read_cipher(&conn->rl);
+	tls_verify_hash_free(&conn->verify);
+
+	crypto_public_key_free(conn->client_rsa_key);
+	conn->client_rsa_key = NULL;
+
+	os_free(conn->session_ticket);
+	conn->session_ticket = NULL;
+	conn->session_ticket_len = 0;
+	conn->use_session_ticket = 0;
+
+	os_free(conn->dh_secret);
+	conn->dh_secret = NULL;
+	conn->dh_secret_len = 0;
+}
+
+
+/**
+ * tlsv1_server_deinit - Deinitialize TLSv1 server connection
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ */
+void tlsv1_server_deinit(struct tlsv1_server *conn)
+{
+	tlsv1_server_clear_data(conn);
+	os_free(conn);
+}
+
+
+/**
+ * tlsv1_server_established - Check whether connection has been established
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * Returns: 1 if connection is established, 0 if not
+ */
+int tlsv1_server_established(struct tlsv1_server *conn)
+{
+	return conn->state == ESTABLISHED;
+}
+
+
+/**
+ * tlsv1_server_prf - Use TLS-PRF to derive keying material
+ * @conn: TLSv1 server connection data from tlsv1_server_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
+ * @server_random_first: seed is 0 = client_random|server_random,
+ * 1 = server_random|client_random
+ * @out: Buffer for output data from TLS-PRF
+ * @out_len: Length of the output buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_server_prf(struct tlsv1_server *conn, const char *label,
+		     const u8 *context, size_t context_len,
+		     int server_random_first, u8 *out, size_t out_len)
+{
+	u8 *seed, *pos;
+	size_t seed_len = 2 * TLS_RANDOM_LEN;
+	int res;
+
+	if (conn->state != ESTABLISHED)
+		return -1;
+
+	if (context_len > 65535)
+		return -1;
+
+	if (context)
+		seed_len += 2 + context_len;
+
+	seed = os_malloc(seed_len);
+	if (!seed)
+		return -1;
+
+	if (server_random_first) {
+		os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
+		os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random,
+			  TLS_RANDOM_LEN);
+	} else {
+		os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
+		os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
+			  TLS_RANDOM_LEN);
+	}
+
+	if (context) {
+		pos = seed + 2 * TLS_RANDOM_LEN;
+		WPA_PUT_BE16(pos, context_len);
+		pos += 2;
+		os_memcpy(pos, context, context_len);
+	}
+
+	res = tls_prf(conn->rl.tls_version,
+		      conn->master_secret, TLS_MASTER_SECRET_LEN,
+		      label, seed, seed_len, out, out_len);
+	os_free(seed);
+	return res;
+}
+
+
+/**
+ * tlsv1_server_get_cipher - Get current cipher name
+ * @conn: TLSv1 server connection data from tlsv1_server_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 tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf,
+			    size_t buflen)
+{
+	char *cipher;
+
+	switch (conn->rl.cipher_suite) {
+	case TLS_RSA_WITH_RC4_128_MD5:
+		cipher = "RC4-MD5";
+		break;
+	case TLS_RSA_WITH_RC4_128_SHA:
+		cipher = "RC4-SHA";
+		break;
+	case TLS_RSA_WITH_DES_CBC_SHA:
+		cipher = "DES-CBC-SHA";
+		break;
+	case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+		cipher = "DES-CBC3-SHA";
+		break;
+	case TLS_DHE_RSA_WITH_DES_CBC_SHA:
+		cipher = "DHE-RSA-DES-CBC-SHA";
+		break;
+	case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+		cipher = "DHE-RSA-DES-CBC3-SHA";
+		break;
+	case TLS_DH_anon_WITH_RC4_128_MD5:
+		cipher = "ADH-RC4-MD5";
+		break;
+	case TLS_DH_anon_WITH_DES_CBC_SHA:
+		cipher = "ADH-DES-SHA";
+		break;
+	case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA:
+		cipher = "ADH-DES-CBC3-SHA";
+		break;
+	case TLS_RSA_WITH_AES_128_CBC_SHA:
+		cipher = "AES-128-SHA";
+		break;
+	case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+		cipher = "DHE-RSA-AES-128-SHA";
+		break;
+	case TLS_DH_anon_WITH_AES_128_CBC_SHA:
+		cipher = "ADH-AES-128-SHA";
+		break;
+	case TLS_RSA_WITH_AES_256_CBC_SHA:
+		cipher = "AES-256-SHA";
+		break;
+	case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+		cipher = "DHE-RSA-AES-256-SHA";
+		break;
+	case TLS_DH_anon_WITH_AES_256_CBC_SHA:
+		cipher = "ADH-AES-256-SHA";
+		break;
+	case TLS_RSA_WITH_AES_128_CBC_SHA256:
+		cipher = "AES-128-SHA256";
+		break;
+	case TLS_RSA_WITH_AES_256_CBC_SHA256:
+		cipher = "AES-256-SHA256";
+		break;
+	case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+		cipher = "DHE-RSA-AES-128-SHA256";
+		break;
+	case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+		cipher = "DHE-RSA-AES-256-SHA256";
+		break;
+	case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
+		cipher = "ADH-AES-128-SHA256";
+		break;
+	case TLS_DH_anon_WITH_AES_256_CBC_SHA256:
+		cipher = "ADH-AES-256-SHA256";
+		break;
+	default:
+		return -1;
+	}
+
+	if (os_strlcpy(buf, cipher, buflen) >= buflen)
+		return -1;
+	return 0;
+}
+
+
+/**
+ * tlsv1_server_shutdown - Shutdown TLS connection
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_server_shutdown(struct tlsv1_server *conn)
+{
+	conn->state = CLIENT_HELLO;
+
+	if (tls_verify_hash_init(&conn->verify) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify "
+			   "hash");
+		return -1;
+	}
+
+	tlsv1_server_clear_data(conn);
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_server_resumed - Was session resumption used
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * Returns: 1 if current session used session resumption, 0 if not
+ */
+int tlsv1_server_resumed(struct tlsv1_server *conn)
+{
+	return 0;
+}
+
+
+/**
+ * tlsv1_server_get_random - Get random data from TLS connection
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @keys: Structure of random data (filled on success)
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_server_get_random(struct tlsv1_server *conn, struct tls_random *keys)
+{
+	os_memset(keys, 0, sizeof(*keys));
+	if (conn->state == CLIENT_HELLO)
+		return -1;
+
+	keys->client_random = conn->client_random;
+	keys->client_random_len = TLS_RANDOM_LEN;
+
+	if (conn->state != SERVER_HELLO) {
+		keys->server_random = conn->server_random;
+		keys->server_random_len = TLS_RANDOM_LEN;
+	}
+
+	return 0;
+}
+
+
+/**
+ * tlsv1_server_get_keyblock_size - Get TLS key_block size
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * Returns: Size of the key_block for the negotiated cipher suite or -1 on
+ * failure
+ */
+int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn)
+{
+	if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO)
+		return -1;
+
+	return 2 * (conn->rl.hash_size + conn->rl.key_material_len +
+		    conn->rl.iv_size);
+}
+
+
+/**
+ * tlsv1_server_set_cipher_list - Configure acceptable cipher suites
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers
+ * (TLS_CIPHER_*).
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers)
+{
+	size_t count;
+	u16 *suites;
+
+	/* TODO: implement proper configuration of cipher suites */
+	if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) {
+		count = 0;
+		suites = conn->cipher_suites;
+		suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+		suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+		suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
+		suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
+		suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
+		suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA;
+		suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
+		suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
+		suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5;
+		suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA;
+		conn->num_cipher_suites = count;
+	}
+
+	return 0;
+}
+
+
+int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer)
+{
+	conn->verify_peer = verify_peer;
+	return 0;
+}
+
+
+void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn,
+					tlsv1_server_session_ticket_cb cb,
+					void *ctx)
+{
+	wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)",
+		   cb, ctx);
+	conn->session_ticket_cb = cb;
+	conn->session_ticket_cb_ctx = ctx;
+}
+
+
+void tlsv1_server_set_log_cb(struct tlsv1_server *conn,
+			     void (*cb)(void *ctx, const char *msg), void *ctx)
+{
+	conn->log_cb = cb;
+	conn->log_cb_ctx = ctx;
+}
+
+
+int tlsv1_server_get_failed(struct tlsv1_server *conn)
+{
+	return conn->state == FAILED;
+}
+
+
+int tlsv1_server_get_read_alerts(struct tlsv1_server *conn)
+{
+	return conn->read_alerts;
+}
+
+
+int tlsv1_server_get_write_alerts(struct tlsv1_server *conn)
+{
+	return conn->write_alerts;
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+void tlsv1_server_set_test_flags(struct tlsv1_server *conn, u32 flags)
+{
+	conn->test_flags = flags;
+}
+
+
+static const u8 test_tls_prime15[1] = {
+	15
+};
+
+static const u8 test_tls_prime511b[64] = {
+	0x50, 0xfb, 0xf1, 0xae, 0x01, 0xf1, 0xfe, 0xe6,
+	0xe1, 0xae, 0xdc, 0x1e, 0xbe, 0xfb, 0x9e, 0x58,
+	0x9a, 0xd7, 0x54, 0x9d, 0x6b, 0xb3, 0x78, 0xe2,
+	0x39, 0x7f, 0x30, 0x01, 0x25, 0xa1, 0xf9, 0x7c,
+	0x55, 0x0e, 0xa1, 0x15, 0xcc, 0x36, 0x34, 0xbb,
+	0x6c, 0x8b, 0x64, 0x45, 0x15, 0x7f, 0xd3, 0xe7,
+	0x31, 0xc8, 0x8e, 0x56, 0x8e, 0x95, 0xdc, 0xea,
+	0x9e, 0xdf, 0xf7, 0x56, 0xdd, 0xb0, 0x34, 0xdb
+};
+
+static const u8 test_tls_prime767b[96] = {
+	0x4c, 0xdc, 0xb8, 0x21, 0x20, 0x9d, 0xe8, 0xa3,
+	0x53, 0xd9, 0x1c, 0x18, 0xc1, 0x3a, 0x58, 0x67,
+	0xa7, 0x85, 0xf9, 0x28, 0x9b, 0xce, 0xc0, 0xd1,
+	0x05, 0x84, 0x61, 0x97, 0xb2, 0x86, 0x1c, 0xd0,
+	0xd1, 0x96, 0x23, 0x29, 0x8c, 0xc5, 0x30, 0x68,
+	0x3e, 0xf9, 0x05, 0xba, 0x60, 0xeb, 0xdb, 0xee,
+	0x2d, 0xdf, 0x84, 0x65, 0x49, 0x87, 0x90, 0x2a,
+	0xc9, 0x8e, 0x34, 0x63, 0x6d, 0x9a, 0x2d, 0x32,
+	0x1c, 0x46, 0xd5, 0x4e, 0x20, 0x20, 0x90, 0xac,
+	0xd5, 0x48, 0x79, 0x99, 0x0c, 0xe6, 0xed, 0xbf,
+	0x79, 0xc2, 0x47, 0x50, 0x95, 0x38, 0x38, 0xbc,
+	0xde, 0xb0, 0xd2, 0xe8, 0x97, 0xcb, 0x22, 0xbb
+};
+
+static const u8 test_tls_prime58[128] = {
+	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,
+	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,
+	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,
+	0x03, 0xc1, 0xba, 0xc8, 0x25, 0xbe, 0x2d, 0xf3
+};
+
+static const u8 test_tls_non_prime[] = {
+	/*
+	 * This is not a prime and the value has the following factors:
+	 * 13736783488716579923 * 16254860191773456563 * 18229434976173670763 *
+	 * 11112313018289079419 * 10260802278580253339 * 12394009491575311499 *
+	 * 12419059668711064739 * 14317973192687985827 * 10498605410533203179 *
+	 * 16338688760390249003 * 11128963991123878883 * 12990532258280301419 *
+	 * 3
+	 */
+	0x0C, 0x8C, 0x36, 0x9C, 0x6F, 0x71, 0x2E, 0xA7,
+	0xAB, 0x32, 0xD3, 0x0F, 0x68, 0x3D, 0xB2, 0x6D,
+	0x81, 0xDD, 0xC4, 0x84, 0x0D, 0x9C, 0x6E, 0x36,
+	0x29, 0x70, 0xF3, 0x1E, 0x9A, 0x42, 0x0B, 0x67,
+	0x82, 0x6B, 0xB1, 0xF2, 0xAF, 0x55, 0x28, 0xE7,
+	0xDB, 0x67, 0x6C, 0xF7, 0x6B, 0xAC, 0xAC, 0xE5,
+	0xF7, 0x9F, 0xD4, 0x63, 0x55, 0x70, 0x32, 0x7C,
+	0x70, 0xFB, 0xAF, 0xB8, 0xEB, 0x37, 0xCF, 0x3F,
+	0xFE, 0x94, 0x73, 0xF9, 0x7A, 0xC7, 0x12, 0x2E,
+	0x9B, 0xB4, 0x7D, 0x08, 0x60, 0x83, 0x43, 0x52,
+	0x83, 0x1E, 0xA5, 0xFC, 0xFA, 0x87, 0x12, 0xF4,
+	0x64, 0xE2, 0xCE, 0x71, 0x17, 0x72, 0xB6, 0xAB
+};
+
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+void tlsv1_server_get_dh_p(struct tlsv1_server *conn, const u8 **dh_p,
+			   size_t *dh_p_len)
+{
+	*dh_p = conn->cred->dh_p;
+	*dh_p_len = conn->cred->dh_p_len;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (conn->test_flags & TLS_DHE_PRIME_511B) {
+		tlsv1_server_log(conn, "TESTING: Use short 511-bit prime with DHE");
+		*dh_p = test_tls_prime511b;
+		*dh_p_len = sizeof(test_tls_prime511b);
+	} else if (conn->test_flags & TLS_DHE_PRIME_767B) {
+		tlsv1_server_log(conn, "TESTING: Use short 767-bit prime with DHE");
+		*dh_p = test_tls_prime767b;
+		*dh_p_len = sizeof(test_tls_prime767b);
+	} else if (conn->test_flags & TLS_DHE_PRIME_15) {
+		tlsv1_server_log(conn, "TESTING: Use bogus 15 \"prime\" with DHE");
+		*dh_p = test_tls_prime15;
+		*dh_p_len = sizeof(test_tls_prime15);
+	} else if (conn->test_flags & TLS_DHE_PRIME_58B) {
+		tlsv1_server_log(conn, "TESTING: Use short 58-bit prime in long container with DHE");
+		*dh_p = test_tls_prime58;
+		*dh_p_len = sizeof(test_tls_prime58);
+	} else if (conn->test_flags & TLS_DHE_NON_PRIME) {
+		tlsv1_server_log(conn, "TESTING: Use claim non-prime as the DHE prime");
+		*dh_p = test_tls_non_prime;
+		*dh_p_len = sizeof(test_tls_non_prime);
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_server.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_server.h
new file mode 100644
index 0000000..c9c0875
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_server.h
@@ -0,0 +1,58 @@
+/*
+ * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246)
+ * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TLSV1_SERVER_H
+#define TLSV1_SERVER_H
+
+#include "tlsv1_cred.h"
+
+struct tlsv1_server;
+
+int tlsv1_server_global_init(void);
+void tlsv1_server_global_deinit(void);
+struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred);
+void tlsv1_server_deinit(struct tlsv1_server *conn);
+int tlsv1_server_established(struct tlsv1_server *conn);
+int tlsv1_server_prf(struct tlsv1_server *conn, const char *label,
+		     const u8 *context, size_t context_len,
+		     int server_random_first, u8 *out, size_t out_len);
+u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
+			    const u8 *in_data, size_t in_len, size_t *out_len);
+int tlsv1_server_encrypt(struct tlsv1_server *conn,
+			 const u8 *in_data, size_t in_len,
+			 u8 *out_data, size_t out_len);
+int tlsv1_server_decrypt(struct tlsv1_server *conn,
+			 const u8 *in_data, size_t in_len,
+			 u8 *out_data, size_t out_len);
+int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf,
+			    size_t buflen);
+int tlsv1_server_shutdown(struct tlsv1_server *conn);
+int tlsv1_server_resumed(struct tlsv1_server *conn);
+int tlsv1_server_get_random(struct tlsv1_server *conn, struct tls_random *data);
+int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn);
+int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers);
+int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer);
+
+typedef int (*tlsv1_server_session_ticket_cb)
+(void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
+ const u8 *server_random, u8 *master_secret);
+
+void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn,
+					tlsv1_server_session_ticket_cb cb,
+					void *ctx);
+
+void tlsv1_server_set_log_cb(struct tlsv1_server *conn,
+			     void (*cb)(void *ctx, const char *msg), void *ctx);
+
+int tlsv1_server_get_failed(struct tlsv1_server *conn);
+int tlsv1_server_get_read_alerts(struct tlsv1_server *conn);
+int tlsv1_server_get_write_alerts(struct tlsv1_server *conn);
+
+void tlsv1_server_set_test_flags(struct tlsv1_server *conn, u32 flags);
+
+#endif /* TLSV1_SERVER_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_server_i.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_server_i.h
new file mode 100644
index 0000000..2622585
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_server_i.h
@@ -0,0 +1,89 @@
+/*
+ * TLSv1 server - internal structures
+ * 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.
+ */
+
+#ifndef TLSV1_SERVER_I_H
+#define TLSV1_SERVER_I_H
+
+struct tlsv1_server {
+	enum {
+		CLIENT_HELLO, SERVER_HELLO, SERVER_CERTIFICATE,
+		SERVER_KEY_EXCHANGE, SERVER_CERTIFICATE_REQUEST,
+		SERVER_HELLO_DONE, CLIENT_CERTIFICATE, CLIENT_KEY_EXCHANGE,
+		CERTIFICATE_VERIFY, CHANGE_CIPHER_SPEC, CLIENT_FINISHED,
+		SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED,
+		ESTABLISHED, FAILED
+	} state;
+
+	struct tlsv1_record_layer rl;
+
+	u8 session_id[TLS_SESSION_ID_MAX_LEN];
+	size_t session_id_len;
+	u8 client_random[TLS_RANDOM_LEN];
+	u8 server_random[TLS_RANDOM_LEN];
+	u8 master_secret[TLS_MASTER_SECRET_LEN];
+
+	u8 alert_level;
+	u8 alert_description;
+
+	int read_alerts, write_alerts;
+
+	struct crypto_public_key *client_rsa_key;
+
+	struct tls_verify_hash verify;
+
+#define MAX_CIPHER_COUNT 30
+	u16 cipher_suites[MAX_CIPHER_COUNT];
+	size_t num_cipher_suites;
+
+	u16 cipher_suite;
+
+	struct tlsv1_credentials *cred;
+
+	int verify_peer;
+	u16 client_version;
+
+	u8 *session_ticket;
+	size_t session_ticket_len;
+
+	tlsv1_server_session_ticket_cb session_ticket_cb;
+	void *session_ticket_cb_ctx;
+
+	void (*log_cb)(void *ctx, const char *msg);
+	void *log_cb_ctx;
+
+	int use_session_ticket;
+	unsigned int status_request:1;
+	unsigned int status_request_v2:1;
+	unsigned int status_request_multi:1;
+
+	u8 *dh_secret;
+	size_t dh_secret_len;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	u32 test_flags;
+	int test_failure_reported;
+#endif /* CONFIG_TESTING_OPTIONS */
+};
+
+
+void tlsv1_server_log(struct tlsv1_server *conn, const char *fmt, ...)
+PRINTF_FORMAT(2, 3);
+
+void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description);
+int tlsv1_server_derive_keys(struct tlsv1_server *conn,
+			     const u8 *pre_master_secret,
+			     size_t pre_master_secret_len);
+u8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len);
+u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level,
+			     u8 description, size_t *out_len);
+int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct,
+				   const u8 *buf, size_t *len);
+void tlsv1_server_get_dh_p(struct tlsv1_server *conn, const u8 **dh_p,
+			   size_t *dh_p_len);
+
+#endif /* TLSV1_SERVER_I_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_server_read.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_server_read.c
new file mode 100644
index 0000000..e957678
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_server_read.c
@@ -0,0 +1,1320 @@
+/*
+ * TLSv1 server - read handshake message
+ * Copyright (c) 2006-2014, 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/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/tls.h"
+#include "x509v3.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+#include "tlsv1_server.h"
+#include "tlsv1_server_i.h"
+
+
+static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct,
+					   const u8 *in_data, size_t *in_len);
+static int tls_process_change_cipher_spec(struct tlsv1_server *conn,
+					  u8 ct, const u8 *in_data,
+					  size_t *in_len);
+
+
+static int testing_cipher_suite_filter(struct tlsv1_server *conn, u16 suite)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+	if ((conn->test_flags &
+	     (TLS_BREAK_SRV_KEY_X_HASH | TLS_BREAK_SRV_KEY_X_SIGNATURE |
+	      TLS_DHE_PRIME_511B | TLS_DHE_PRIME_767B | TLS_DHE_PRIME_15 |
+	      TLS_DHE_PRIME_58B | TLS_DHE_NON_PRIME)) &&
+	    suite != TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 &&
+	    suite != TLS_DHE_RSA_WITH_AES_256_CBC_SHA &&
+	    suite != TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 &&
+	    suite != TLS_DHE_RSA_WITH_AES_128_CBC_SHA &&
+	    suite != TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA)
+		return 1;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	return 0;
+}
+
+
+static void tls_process_status_request_item(struct tlsv1_server *conn,
+					    const u8 *req, size_t req_len)
+{
+	const u8 *pos, *end;
+	u8 status_type;
+
+	pos = req;
+	end = req + req_len;
+
+	/*
+	 * RFC 6961, 2.2:
+	 * struct {
+	 *   CertificateStatusType status_type;
+	 *   uint16 request_length;
+	 *   select (status_type) {
+	 *     case ocsp: OCSPStatusRequest;
+	 *     case ocsp_multi: OCSPStatusRequest;
+	 *   } request;
+	 * } CertificateStatusRequestItemV2;
+	 *
+	 * enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType;
+	 */
+
+	if (end - pos < 1)
+		return; /* Truncated data */
+
+	status_type = *pos++;
+	wpa_printf(MSG_DEBUG, "TLSv1: CertificateStatusType %u", status_type);
+	if (status_type != 1 && status_type != 2)
+		return; /* Unsupported status type */
+	/*
+	 * For now, only OCSP stapling is supported, so ignore the specific
+	 * request, if any.
+	 */
+	wpa_hexdump(MSG_DEBUG, "TLSv1: OCSPStatusRequest", pos, end - pos);
+
+	if (status_type == 2)
+		conn->status_request_multi = 1;
+}
+
+
+static void tls_process_status_request_v2(struct tlsv1_server *conn,
+					  const u8 *ext, size_t ext_len)
+{
+	const u8 *pos, *end;
+
+	conn->status_request_v2 = 1;
+
+	pos = ext;
+	end = ext + ext_len;
+
+	/*
+	 * RFC 6961, 2.2:
+	 * struct {
+	 *   CertificateStatusRequestItemV2
+	 *                    certificate_status_req_list<1..2^16-1>;
+	 * } CertificateStatusRequestListV2;
+	 */
+
+	while (end - pos >= 2) {
+		u16 len;
+
+		len = WPA_GET_BE16(pos);
+		pos += 2;
+		if (len > end - pos)
+			break; /* Truncated data */
+		tls_process_status_request_item(conn, pos, len);
+		pos += len;
+	}
+}
+
+
+static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
+				    const u8 *in_data, size_t *in_len)
+{
+	const u8 *pos, *end, *c;
+	size_t left, len, i, j;
+	u16 cipher_suite;
+	u16 num_suites;
+	int compr_null_found;
+	u16 ext_type, ext_len;
+
+	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+		tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x",
+				 ct);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 4) {
+		tlsv1_server_log(conn,
+				 "Truncated handshake message (expected ClientHello)");
+		goto decode_error;
+	}
+
+	/* HandshakeType msg_type */
+	if (*pos != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) {
+		tlsv1_server_log(conn, "Received unexpected handshake message %d (expected ClientHello)",
+				 *pos);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+	tlsv1_server_log(conn, "Received ClientHello");
+	pos++;
+	/* uint24 length */
+	len = WPA_GET_BE24(pos);
+	pos += 3;
+	left -= 4;
+
+	if (len > left) {
+		tlsv1_server_log(conn,
+				 "Truncated ClientHello (len=%d left=%d)",
+				 (int) len, (int) left);
+		goto decode_error;
+	}
+
+	/* body - ClientHello */
+
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello", pos, len);
+	end = pos + len;
+
+	/* ProtocolVersion client_version */
+	if (end - pos < 2) {
+		tlsv1_server_log(conn, "Truncated ClientHello/client_version");
+		goto decode_error;
+	}
+	conn->client_version = WPA_GET_BE16(pos);
+	tlsv1_server_log(conn, "Client version %d.%d",
+			 conn->client_version >> 8,
+			 conn->client_version & 0xff);
+	if (conn->client_version < TLS_VERSION_1) {
+		tlsv1_server_log(conn, "Unexpected protocol version in ClientHello %u.%u",
+				 conn->client_version >> 8,
+				 conn->client_version & 0xff);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_PROTOCOL_VERSION);
+		return -1;
+	}
+	pos += 2;
+
+	if (TLS_VERSION == TLS_VERSION_1)
+		conn->rl.tls_version = TLS_VERSION_1;
+#ifdef CONFIG_TLSV12
+	else if (conn->client_version >= TLS_VERSION_1_2)
+		conn->rl.tls_version = TLS_VERSION_1_2;
+#endif /* CONFIG_TLSV12 */
+	else if (conn->client_version > TLS_VERSION_1_1)
+		conn->rl.tls_version = TLS_VERSION_1_1;
+	else
+		conn->rl.tls_version = conn->client_version;
+	tlsv1_server_log(conn, "Using TLS v%s",
+			 tls_version_str(conn->rl.tls_version));
+
+	/* Random random */
+	if (end - pos < TLS_RANDOM_LEN) {
+		tlsv1_server_log(conn, "Truncated ClientHello/client_random");
+		goto decode_error;
+	}
+
+	os_memcpy(conn->client_random, pos, TLS_RANDOM_LEN);
+	pos += TLS_RANDOM_LEN;
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random",
+		    conn->client_random, TLS_RANDOM_LEN);
+
+	/* SessionID session_id */
+	if (end - pos < 1) {
+		tlsv1_server_log(conn, "Truncated ClientHello/session_id len");
+		goto decode_error;
+	}
+	if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN) {
+		tlsv1_server_log(conn, "Truncated ClientHello/session_id");
+		goto decode_error;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: client session_id", pos + 1, *pos);
+	pos += 1 + *pos;
+	/* TODO: add support for session resumption */
+
+	/* CipherSuite cipher_suites<2..2^16-1> */
+	if (end - pos < 2) {
+		tlsv1_server_log(conn,
+				 "Truncated ClientHello/cipher_suites len");
+		goto decode_error;
+	}
+	num_suites = WPA_GET_BE16(pos);
+	pos += 2;
+	if (end - pos < num_suites) {
+		tlsv1_server_log(conn, "Truncated ClientHello/cipher_suites");
+		goto decode_error;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: client cipher suites",
+		    pos, num_suites);
+	if (num_suites & 1) {
+		tlsv1_server_log(conn, "Odd len ClientHello/cipher_suites");
+		goto decode_error;
+	}
+	num_suites /= 2;
+
+	cipher_suite = 0;
+	for (i = 0; !cipher_suite && i < conn->num_cipher_suites; i++) {
+		if (testing_cipher_suite_filter(conn, conn->cipher_suites[i]))
+			continue;
+		c = pos;
+		for (j = 0; j < num_suites; j++) {
+			u16 tmp = WPA_GET_BE16(c);
+			c += 2;
+			if (!cipher_suite && tmp == conn->cipher_suites[i]) {
+				cipher_suite = tmp;
+				break;
+			}
+		}
+	}
+	pos += num_suites * 2;
+	if (!cipher_suite) {
+		tlsv1_server_log(conn, "No supported cipher suite available");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_ILLEGAL_PARAMETER);
+		return -1;
+	}
+
+	if (tlsv1_record_set_cipher_suite(&conn->rl, cipher_suite) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to set CipherSuite for "
+			   "record layer");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	conn->cipher_suite = cipher_suite;
+
+	/* CompressionMethod compression_methods<1..2^8-1> */
+	if (end - pos < 1) {
+		tlsv1_server_log(conn,
+				 "Truncated ClientHello/compression_methods len");
+		goto decode_error;
+	}
+	num_suites = *pos++;
+	if (end - pos < num_suites) {
+		tlsv1_server_log(conn,
+				 "Truncated ClientHello/compression_methods");
+		goto decode_error;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: client compression_methods",
+		    pos, num_suites);
+	compr_null_found = 0;
+	for (i = 0; i < num_suites; i++) {
+		if (*pos++ == TLS_COMPRESSION_NULL)
+			compr_null_found = 1;
+	}
+	if (!compr_null_found) {
+		tlsv1_server_log(conn, "Client does not accept NULL compression");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_ILLEGAL_PARAMETER);
+		return -1;
+	}
+
+	if (end - pos == 1) {
+		tlsv1_server_log(conn, "Unexpected extra octet in the end of ClientHello: 0x%02x",
+				 *pos);
+		goto decode_error;
+	}
+
+	if (end - pos >= 2) {
+		/* Extension client_hello_extension_list<0..2^16-1> */
+		ext_len = WPA_GET_BE16(pos);
+		pos += 2;
+
+		tlsv1_server_log(conn, "%u bytes of ClientHello extensions",
+				 ext_len);
+		if (end - pos != ext_len) {
+			tlsv1_server_log(conn, "Invalid ClientHello extension list length %u (expected %u)",
+					 ext_len, (unsigned int) (end - pos));
+			goto decode_error;
+		}
+
+		/*
+		 * struct {
+		 *   ExtensionType extension_type (0..65535)
+		 *   opaque extension_data<0..2^16-1>
+		 * } Extension;
+		 */
+
+		while (pos < end) {
+			if (end - pos < 2) {
+				tlsv1_server_log(conn, "Invalid extension_type field");
+				goto decode_error;
+			}
+
+			ext_type = WPA_GET_BE16(pos);
+			pos += 2;
+
+			if (end - pos < 2) {
+				tlsv1_server_log(conn, "Invalid extension_data length field");
+				goto decode_error;
+			}
+
+			ext_len = WPA_GET_BE16(pos);
+			pos += 2;
+
+			if (end - pos < ext_len) {
+				tlsv1_server_log(conn, "Invalid extension_data field");
+				goto decode_error;
+			}
+
+			tlsv1_server_log(conn, "ClientHello Extension type %u",
+					 ext_type);
+			wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello "
+				    "Extension data", pos, ext_len);
+
+			if (ext_type == TLS_EXT_SESSION_TICKET) {
+				os_free(conn->session_ticket);
+				conn->session_ticket = os_malloc(ext_len);
+				if (conn->session_ticket) {
+					os_memcpy(conn->session_ticket, pos,
+						  ext_len);
+					conn->session_ticket_len = ext_len;
+				}
+			} else if (ext_type == TLS_EXT_STATUS_REQUEST) {
+				conn->status_request = 1;
+			} else if (ext_type == TLS_EXT_STATUS_REQUEST_V2) {
+				tls_process_status_request_v2(conn, pos,
+							      ext_len);
+			}
+
+			pos += ext_len;
+		}
+	}
+
+	*in_len = end - in_data;
+
+	tlsv1_server_log(conn, "ClientHello OK - proceed to ServerHello");
+	conn->state = SERVER_HELLO;
+
+	return 0;
+
+decode_error:
+	tlsv1_server_log(conn, "Failed to decode ClientHello");
+	tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			   TLS_ALERT_DECODE_ERROR);
+	return -1;
+}
+
+
+static int tls_process_certificate(struct tlsv1_server *conn, u8 ct,
+				   const u8 *in_data, size_t *in_len)
+{
+	const u8 *pos, *end;
+	size_t left, len, list_len, cert_len, idx;
+	u8 type;
+	struct x509_certificate *chain = NULL, *last = NULL, *cert;
+	int reason;
+
+	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+		tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x",
+				 ct);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 4) {
+		tlsv1_server_log(conn, "Too short Certificate message (len=%lu)",
+				 (unsigned long) left);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	type = *pos++;
+	len = WPA_GET_BE24(pos);
+	pos += 3;
+	left -= 4;
+
+	if (len > left) {
+		tlsv1_server_log(conn, "Unexpected Certificate message length (len=%lu != left=%lu)",
+				 (unsigned long) len, (unsigned long) left);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	if (type == TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) {
+		if (conn->verify_peer) {
+			tlsv1_server_log(conn, "Client did not include Certificate");
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_UNEXPECTED_MESSAGE);
+			return -1;
+		}
+
+		return tls_process_client_key_exchange(conn, ct, in_data,
+						       in_len);
+	}
+	if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) {
+		tlsv1_server_log(conn, "Received unexpected handshake message %d (expected Certificate/ClientKeyExchange)",
+				 type);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	tlsv1_server_log(conn, "Received Certificate (certificate_list len %lu)",
+			 (unsigned long) len);
+
+	/*
+	 * opaque ASN.1Cert<2^24-1>;
+	 *
+	 * struct {
+	 *     ASN.1Cert certificate_list<1..2^24-1>;
+	 * } Certificate;
+	 */
+
+	end = pos + len;
+
+	if (end - pos < 3) {
+		tlsv1_server_log(conn, "Too short Certificate (left=%lu)",
+				 (unsigned long) left);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	list_len = WPA_GET_BE24(pos);
+	pos += 3;
+
+	if ((size_t) (end - pos) != list_len) {
+		tlsv1_server_log(conn, "Unexpected certificate_list length (len=%lu left=%lu)",
+				 (unsigned long) list_len,
+				 (unsigned long) (end - pos));
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	idx = 0;
+	while (pos < end) {
+		if (end - pos < 3) {
+			tlsv1_server_log(conn, "Failed to parse certificate_list");
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_DECODE_ERROR);
+			x509_certificate_chain_free(chain);
+			return -1;
+		}
+
+		cert_len = WPA_GET_BE24(pos);
+		pos += 3;
+
+		if ((size_t) (end - pos) < cert_len) {
+			tlsv1_server_log(conn, "Unexpected certificate length (len=%lu left=%lu)",
+					 (unsigned long) cert_len,
+					 (unsigned long) (end - pos));
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_DECODE_ERROR);
+			x509_certificate_chain_free(chain);
+			return -1;
+		}
+
+		tlsv1_server_log(conn, "Certificate %lu (len %lu)",
+				 (unsigned long) idx, (unsigned long) cert_len);
+
+		if (idx == 0) {
+			crypto_public_key_free(conn->client_rsa_key);
+			if (tls_parse_cert(pos, cert_len,
+					   &conn->client_rsa_key)) {
+				tlsv1_server_log(conn, "Failed to parse the certificate");
+				tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+						   TLS_ALERT_BAD_CERTIFICATE);
+				x509_certificate_chain_free(chain);
+				return -1;
+			}
+		}
+
+		cert = x509_certificate_parse(pos, cert_len);
+		if (cert == NULL) {
+			tlsv1_server_log(conn, "Failed to parse the certificate");
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_BAD_CERTIFICATE);
+			x509_certificate_chain_free(chain);
+			return -1;
+		}
+
+		if (last == NULL)
+			chain = cert;
+		else
+			last->next = cert;
+		last = cert;
+
+		idx++;
+		pos += cert_len;
+	}
+
+	if (x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
+					    &reason, 0) < 0) {
+		int tls_reason;
+		tlsv1_server_log(conn, "Server certificate chain validation failed (reason=%d)",
+				 reason);
+		switch (reason) {
+		case X509_VALIDATE_BAD_CERTIFICATE:
+			tls_reason = TLS_ALERT_BAD_CERTIFICATE;
+			break;
+		case X509_VALIDATE_UNSUPPORTED_CERTIFICATE:
+			tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE;
+			break;
+		case X509_VALIDATE_CERTIFICATE_REVOKED:
+			tls_reason = TLS_ALERT_CERTIFICATE_REVOKED;
+			break;
+		case X509_VALIDATE_CERTIFICATE_EXPIRED:
+			tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED;
+			break;
+		case X509_VALIDATE_CERTIFICATE_UNKNOWN:
+			tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN;
+			break;
+		case X509_VALIDATE_UNKNOWN_CA:
+			tls_reason = TLS_ALERT_UNKNOWN_CA;
+			break;
+		default:
+			tls_reason = TLS_ALERT_BAD_CERTIFICATE;
+			break;
+		}
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, tls_reason);
+		x509_certificate_chain_free(chain);
+		return -1;
+	}
+
+	if (chain && (chain->extensions_present & X509_EXT_EXT_KEY_USAGE) &&
+	    !(chain->ext_key_usage &
+	      (X509_EXT_KEY_USAGE_ANY | X509_EXT_KEY_USAGE_CLIENT_AUTH))) {
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_BAD_CERTIFICATE);
+		x509_certificate_chain_free(chain);
+		return -1;
+	}
+
+	x509_certificate_chain_free(chain);
+
+	*in_len = end - in_data;
+
+	conn->state = CLIENT_KEY_EXCHANGE;
+
+	return 0;
+}
+
+
+static int tls_process_client_key_exchange_rsa(
+	struct tlsv1_server *conn, const u8 *pos, const u8 *end)
+{
+	u8 *out;
+	size_t outlen, outbuflen;
+	u16 encr_len;
+	int res;
+	int use_random = 0;
+
+	if (end - pos < 2) {
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	encr_len = WPA_GET_BE16(pos);
+	pos += 2;
+	if (pos + encr_len > end) {
+		tlsv1_server_log(conn, "Invalid ClientKeyExchange format: encr_len=%u left=%u",
+				 encr_len, (unsigned int) (end - pos));
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	outbuflen = outlen = end - pos;
+	out = os_malloc(outlen >= TLS_PRE_MASTER_SECRET_LEN ?
+			outlen : TLS_PRE_MASTER_SECRET_LEN);
+	if (out == NULL) {
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	/*
+	 * struct {
+	 *   ProtocolVersion client_version;
+	 *   opaque random[46];
+	 * } PreMasterSecret;
+	 *
+	 * struct {
+	 *   public-key-encrypted PreMasterSecret pre_master_secret;
+	 * } EncryptedPreMasterSecret;
+	 */
+
+	/*
+	 * Note: To avoid Bleichenbacher attack, we do not report decryption or
+	 * parsing errors from EncryptedPreMasterSecret processing to the
+	 * client. Instead, a random pre-master secret is used to force the
+	 * handshake to fail.
+	 */
+
+	if (crypto_private_key_decrypt_pkcs1_v15(conn->cred->key,
+						 pos, encr_len,
+						 out, &outlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt "
+			   "PreMasterSecret (encr_len=%u outlen=%lu)",
+			   encr_len, (unsigned long) outlen);
+		use_random = 1;
+	}
+
+	if (!use_random && outlen != TLS_PRE_MASTER_SECRET_LEN) {
+		tlsv1_server_log(conn, "Unexpected PreMasterSecret length %lu",
+				 (unsigned long) outlen);
+		use_random = 1;
+	}
+
+	if (!use_random && WPA_GET_BE16(out) != conn->client_version) {
+		tlsv1_server_log(conn, "Client version in ClientKeyExchange does not match with version in ClientHello");
+		use_random = 1;
+	}
+
+	if (use_random) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Using random premaster secret "
+			   "to avoid revealing information about private key");
+		outlen = TLS_PRE_MASTER_SECRET_LEN;
+		if (os_get_random(out, outlen)) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
+				   "data");
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_INTERNAL_ERROR);
+			os_free(out);
+			return -1;
+		}
+	}
+
+	res = tlsv1_server_derive_keys(conn, out, outlen);
+
+	/* Clear the pre-master secret since it is not needed anymore */
+	os_memset(out, 0, outbuflen);
+	os_free(out);
+
+	if (res) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int tls_process_client_key_exchange_dh(
+	struct tlsv1_server *conn, const u8 *pos, const u8 *end)
+{
+	const u8 *dh_yc;
+	u16 dh_yc_len;
+	u8 *shared;
+	size_t shared_len;
+	int res;
+	const u8 *dh_p;
+	size_t dh_p_len;
+
+	/*
+	 * struct {
+	 *   select (PublicValueEncoding) {
+	 *     case implicit: struct { };
+	 *     case explicit: opaque dh_Yc<1..2^16-1>;
+	 *   } dh_public;
+	 * } ClientDiffieHellmanPublic;
+	 */
+
+	tlsv1_server_log(conn, "ClientDiffieHellmanPublic received");
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientDiffieHellmanPublic",
+		    pos, end - pos);
+
+	if (end == pos) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Implicit public value encoding "
+			   "not supported");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	if (end - pos < 3) {
+		tlsv1_server_log(conn, "Invalid client public value length");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	dh_yc_len = WPA_GET_BE16(pos);
+	dh_yc = pos + 2;
+
+	if (dh_yc_len > end - dh_yc) {
+		tlsv1_server_log(conn, "Client public value overflow (length %d)",
+				 dh_yc_len);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)",
+		    dh_yc, dh_yc_len);
+
+	if (conn->cred == NULL || conn->cred->dh_p == NULL ||
+	    conn->dh_secret == NULL) {
+		wpa_printf(MSG_DEBUG, "TLSv1: No DH parameters available");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	tlsv1_server_get_dh_p(conn, &dh_p, &dh_p_len);
+
+	shared_len = dh_p_len;
+	shared = os_malloc(shared_len);
+	if (shared == NULL) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for "
+			   "DH");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	/* shared = Yc^secret mod p */
+	if (crypto_mod_exp(dh_yc, dh_yc_len, conn->dh_secret,
+			   conn->dh_secret_len, dh_p, dh_p_len,
+			   shared, &shared_len)) {
+		os_free(shared);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "TLSv1: Shared secret from DH key exchange",
+			shared, shared_len);
+
+	os_memset(conn->dh_secret, 0, conn->dh_secret_len);
+	os_free(conn->dh_secret);
+	conn->dh_secret = NULL;
+
+	res = tlsv1_server_derive_keys(conn, shared, shared_len);
+
+	/* Clear the pre-master secret since it is not needed anymore */
+	os_memset(shared, 0, shared_len);
+	os_free(shared);
+
+	if (res) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct,
+					   const u8 *in_data, size_t *in_len)
+{
+	const u8 *pos, *end;
+	size_t left, len;
+	u8 type;
+	tls_key_exchange keyx;
+	const struct tls_cipher_suite *suite;
+
+	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+		tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x",
+				 ct);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 4) {
+		tlsv1_server_log(conn, "Too short ClientKeyExchange (Left=%lu)",
+				 (unsigned long) left);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	type = *pos++;
+	len = WPA_GET_BE24(pos);
+	pos += 3;
+	left -= 4;
+
+	if (len > left) {
+		tlsv1_server_log(conn, "Mismatch in ClientKeyExchange length (len=%lu != left=%lu)",
+				 (unsigned long) len, (unsigned long) left);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	end = pos + len;
+
+	if (type != TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) {
+		tlsv1_server_log(conn, "Received unexpected handshake message %d (expected ClientKeyExchange)",
+				 type);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	tlsv1_server_log(conn, "Received ClientKeyExchange");
+
+	wpa_hexdump(MSG_DEBUG, "TLSv1: ClientKeyExchange", pos, len);
+
+	suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+	if (suite == NULL)
+		keyx = TLS_KEY_X_NULL;
+	else
+		keyx = suite->key_exchange;
+
+	if ((keyx == TLS_KEY_X_DH_anon || keyx == TLS_KEY_X_DHE_RSA) &&
+	    tls_process_client_key_exchange_dh(conn, pos, end) < 0)
+		return -1;
+
+	if (keyx != TLS_KEY_X_DH_anon && keyx != TLS_KEY_X_DHE_RSA &&
+	    tls_process_client_key_exchange_rsa(conn, pos, end) < 0)
+		return -1;
+
+	*in_len = end - in_data;
+
+	conn->state = CERTIFICATE_VERIFY;
+
+	return 0;
+}
+
+
+static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct,
+					  const u8 *in_data, size_t *in_len)
+{
+	const u8 *pos, *end;
+	size_t left, len;
+	u8 type;
+	size_t hlen;
+	u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos;
+	u8 alert;
+
+	if (ct == TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
+		if (conn->verify_peer) {
+			tlsv1_server_log(conn, "Client did not include CertificateVerify");
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_UNEXPECTED_MESSAGE);
+			return -1;
+		}
+
+		return tls_process_change_cipher_spec(conn, ct, in_data,
+						      in_len);
+	}
+
+	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+		tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x",
+				 ct);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 4) {
+		tlsv1_server_log(conn, "Too short CertificateVerify message (len=%lu)",
+				 (unsigned long) left);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	type = *pos++;
+	len = WPA_GET_BE24(pos);
+	pos += 3;
+	left -= 4;
+
+	if (len > left) {
+		tlsv1_server_log(conn, "Unexpected CertificateVerify message length (len=%lu != left=%lu)",
+				 (unsigned long) len, (unsigned long) left);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	end = pos + len;
+
+	if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY) {
+		tlsv1_server_log(conn, "Received unexpected handshake message %d (expected CertificateVerify)",
+				 type);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	tlsv1_server_log(conn, "Received CertificateVerify");
+
+	/*
+	 * struct {
+	 *   Signature signature;
+	 * } CertificateVerify;
+	 */
+
+	hpos = hash;
+
+#ifdef CONFIG_TLSV12
+	if (conn->rl.tls_version == TLS_VERSION_1_2) {
+		/*
+		 * RFC 5246, 4.7:
+		 * TLS v1.2 adds explicit indication of the used signature and
+		 * hash algorithms.
+		 *
+		 * struct {
+		 *   HashAlgorithm hash;
+		 *   SignatureAlgorithm signature;
+		 * } SignatureAndHashAlgorithm;
+		 */
+		if (end - pos < 2) {
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_DECODE_ERROR);
+			return -1;
+		}
+		if (pos[0] != TLS_HASH_ALG_SHA256 ||
+		    pos[1] != TLS_SIGN_ALG_RSA) {
+			wpa_printf(MSG_DEBUG, "TLSv1.2: Unsupported hash(%u)/"
+				   "signature(%u) algorithm",
+				   pos[0], pos[1]);
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_INTERNAL_ERROR);
+			return -1;
+		}
+		pos += 2;
+
+		hlen = SHA256_MAC_LEN;
+		if (conn->verify.sha256_cert == NULL ||
+		    crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) <
+		    0) {
+			conn->verify.sha256_cert = NULL;
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_INTERNAL_ERROR);
+			return -1;
+		}
+		conn->verify.sha256_cert = NULL;
+	} else {
+#endif /* CONFIG_TLSV12 */
+
+	hlen = MD5_MAC_LEN;
+	if (conn->verify.md5_cert == NULL ||
+	    crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) {
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		conn->verify.md5_cert = NULL;
+		crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL);
+		conn->verify.sha1_cert = NULL;
+		return -1;
+	}
+	hpos += MD5_MAC_LEN;
+
+	conn->verify.md5_cert = NULL;
+	hlen = SHA1_MAC_LEN;
+	if (conn->verify.sha1_cert == NULL ||
+	    crypto_hash_finish(conn->verify.sha1_cert, hpos, &hlen) < 0) {
+		conn->verify.sha1_cert = NULL;
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	conn->verify.sha1_cert = NULL;
+
+	hlen += MD5_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+	}
+#endif /* CONFIG_TLSV12 */
+
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen);
+
+	if (tls_verify_signature(conn->rl.tls_version, conn->client_rsa_key,
+				 hash, hlen, pos, end - pos, &alert) < 0) {
+		tlsv1_server_log(conn, "Invalid Signature in CertificateVerify");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+		return -1;
+	}
+
+	*in_len = end - in_data;
+
+	conn->state = CHANGE_CIPHER_SPEC;
+
+	return 0;
+}
+
+
+static int tls_process_change_cipher_spec(struct tlsv1_server *conn,
+					  u8 ct, const u8 *in_data,
+					  size_t *in_len)
+{
+	const u8 *pos;
+	size_t left;
+
+	if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
+		tlsv1_server_log(conn, "Expected ChangeCipherSpec; received content type 0x%x",
+				 ct);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 1) {
+		tlsv1_server_log(conn, "Too short ChangeCipherSpec");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	if (*pos != TLS_CHANGE_CIPHER_SPEC) {
+		tlsv1_server_log(conn, "Expected ChangeCipherSpec; received data 0x%x",
+				 *pos);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	tlsv1_server_log(conn, "Received ChangeCipherSpec");
+	if (tlsv1_record_change_read_cipher(&conn->rl) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher "
+			   "for record layer");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	*in_len = pos + 1 - in_data;
+
+	conn->state = CLIENT_FINISHED;
+
+	return 0;
+}
+
+
+static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct,
+				       const u8 *in_data, size_t *in_len)
+{
+	const u8 *pos, *end;
+	size_t left, len, hlen;
+	u8 verify_data[TLS_VERIFY_DATA_LEN];
+	u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if ((conn->test_flags &
+	     (TLS_BREAK_SRV_KEY_X_HASH | TLS_BREAK_SRV_KEY_X_SIGNATURE)) &&
+	    !conn->test_failure_reported) {
+		tlsv1_server_log(conn, "TEST-FAILURE: Client Finished received after invalid ServerKeyExchange");
+		conn->test_failure_reported = 1;
+	}
+
+	if ((conn->test_flags & TLS_DHE_PRIME_15) &&
+	    !conn->test_failure_reported) {
+		tlsv1_server_log(conn, "TEST-FAILURE: Client Finished received after bogus DHE \"prime\" 15");
+		conn->test_failure_reported = 1;
+	}
+
+	if ((conn->test_flags & TLS_DHE_PRIME_58B) &&
+	    !conn->test_failure_reported) {
+		tlsv1_server_log(conn, "TEST-FAILURE: Client Finished received after short 58-bit DHE prime in long container");
+		conn->test_failure_reported = 1;
+	}
+
+	if ((conn->test_flags & TLS_DHE_PRIME_511B) &&
+	    !conn->test_failure_reported) {
+		tlsv1_server_log(conn, "TEST-WARNING: Client Finished received after short 511-bit DHE prime (insecure)");
+		conn->test_failure_reported = 1;
+	}
+
+	if ((conn->test_flags & TLS_DHE_PRIME_767B) &&
+	    !conn->test_failure_reported) {
+		tlsv1_server_log(conn, "TEST-NOTE: Client Finished received after 767-bit DHE prime (relatively insecure)");
+		conn->test_failure_reported = 1;
+	}
+
+	if ((conn->test_flags & TLS_DHE_NON_PRIME) &&
+	    !conn->test_failure_reported) {
+		tlsv1_server_log(conn, "TEST-NOTE: Client Finished received after non-prime claimed as DHE prime");
+		conn->test_failure_reported = 1;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+		tlsv1_server_log(conn, "Expected Finished; received content type 0x%x",
+				 ct);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 4) {
+		tlsv1_server_log(conn, "Too short record (left=%lu) forFinished",
+				 (unsigned long) left);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	if (pos[0] != TLS_HANDSHAKE_TYPE_FINISHED) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; received "
+			   "type 0x%x", pos[0]);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	len = WPA_GET_BE24(pos + 1);
+
+	pos += 4;
+	left -= 4;
+
+	if (len > left) {
+		tlsv1_server_log(conn, "Too short buffer for Finished (len=%lu > left=%lu)",
+				 (unsigned long) len, (unsigned long) left);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+	end = pos + len;
+	if (len != TLS_VERIFY_DATA_LEN) {
+		tlsv1_server_log(conn, "Unexpected verify_data length in Finished: %lu (expected %d)",
+				 (unsigned long) len, TLS_VERIFY_DATA_LEN);
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished",
+		    pos, TLS_VERIFY_DATA_LEN);
+
+#ifdef CONFIG_TLSV12
+	if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+		hlen = SHA256_MAC_LEN;
+		if (conn->verify.sha256_client == NULL ||
+		    crypto_hash_finish(conn->verify.sha256_client, hash, &hlen)
+		    < 0) {
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_INTERNAL_ERROR);
+			conn->verify.sha256_client = NULL;
+			return -1;
+		}
+		conn->verify.sha256_client = NULL;
+	} else {
+#endif /* CONFIG_TLSV12 */
+
+	hlen = MD5_MAC_LEN;
+	if (conn->verify.md5_client == NULL ||
+	    crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) {
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		conn->verify.md5_client = NULL;
+		crypto_hash_finish(conn->verify.sha1_client, NULL, NULL);
+		conn->verify.sha1_client = NULL;
+		return -1;
+	}
+	conn->verify.md5_client = NULL;
+	hlen = SHA1_MAC_LEN;
+	if (conn->verify.sha1_client == NULL ||
+	    crypto_hash_finish(conn->verify.sha1_client, hash + MD5_MAC_LEN,
+			       &hlen) < 0) {
+		conn->verify.sha1_client = NULL;
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	conn->verify.sha1_client = NULL;
+	hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+	}
+#endif /* CONFIG_TLSV12 */
+
+	if (tls_prf(conn->rl.tls_version,
+		    conn->master_secret, TLS_MASTER_SECRET_LEN,
+		    "client finished", hash, hlen,
+		    verify_data, TLS_VERIFY_DATA_LEN)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_DECRYPT_ERROR);
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)",
+			verify_data, TLS_VERIFY_DATA_LEN);
+
+	if (os_memcmp_const(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
+		tlsv1_server_log(conn, "Mismatch in verify_data");
+		conn->state = FAILED;
+		return -1;
+	}
+
+	tlsv1_server_log(conn, "Received Finished");
+
+	*in_len = end - in_data;
+
+	if (conn->use_session_ticket) {
+		/* Abbreviated handshake using session ticket; RFC 4507 */
+		tlsv1_server_log(conn, "Abbreviated handshake completed successfully");
+		conn->state = ESTABLISHED;
+	} else {
+		/* Full handshake */
+		conn->state = SERVER_CHANGE_CIPHER_SPEC;
+	}
+
+	return 0;
+}
+
+
+int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct,
+				   const u8 *buf, size_t *len)
+{
+	if (ct == TLS_CONTENT_TYPE_ALERT) {
+		if (*len < 2) {
+			tlsv1_server_log(conn, "Alert underflow");
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_DECODE_ERROR);
+			return -1;
+		}
+		tlsv1_server_log(conn, "Received alert %d:%d", buf[0], buf[1]);
+		*len = 2;
+		conn->state = FAILED;
+		return -1;
+	}
+
+	switch (conn->state) {
+	case CLIENT_HELLO:
+		if (tls_process_client_hello(conn, ct, buf, len))
+			return -1;
+		break;
+	case CLIENT_CERTIFICATE:
+		if (tls_process_certificate(conn, ct, buf, len))
+			return -1;
+		break;
+	case CLIENT_KEY_EXCHANGE:
+		if (tls_process_client_key_exchange(conn, ct, buf, len))
+			return -1;
+		break;
+	case CERTIFICATE_VERIFY:
+		if (tls_process_certificate_verify(conn, ct, buf, len))
+			return -1;
+		break;
+	case CHANGE_CIPHER_SPEC:
+		if (tls_process_change_cipher_spec(conn, ct, buf, len))
+			return -1;
+		break;
+	case CLIENT_FINISHED:
+		if (tls_process_client_finished(conn, ct, buf, len))
+			return -1;
+		break;
+	default:
+		tlsv1_server_log(conn, "Unexpected state %d while processing received message",
+				 conn->state);
+		return -1;
+	}
+
+	if (ct == TLS_CONTENT_TYPE_HANDSHAKE)
+		tls_verify_hash_add(&conn->verify, buf, *len);
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_server_write.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_server_write.c
new file mode 100644
index 0000000..545abae
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/tlsv1_server_write.c
@@ -0,0 +1,1093 @@
+/*
+ * TLSv1 server - write handshake message
+ * Copyright (c) 2006-2014, 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/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/tls.h"
+#include "crypto/random.h"
+#include "x509v3.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+#include "tlsv1_server.h"
+#include "tlsv1_server_i.h"
+
+
+static size_t tls_server_cert_chain_der_len(struct tlsv1_server *conn)
+{
+	size_t len = 0;
+	struct x509_certificate *cert;
+
+	cert = conn->cred ? conn->cred->cert : NULL;
+	while (cert) {
+		len += 3 + cert->cert_len;
+		if (x509_certificate_self_signed(cert))
+			break;
+		cert = x509_certificate_get_subject(conn->cred->trusted_certs,
+						    &cert->issuer);
+	}
+
+	return len;
+}
+
+
+static int tls_write_server_hello(struct tlsv1_server *conn,
+				  u8 **msgpos, u8 *end)
+{
+	u8 *pos, *rhdr, *hs_start, *hs_length, *ext_start;
+	struct os_time now;
+	size_t rlen;
+
+	pos = *msgpos;
+
+	tlsv1_server_log(conn, "Send ServerHello");
+	rhdr = pos;
+	pos += TLS_RECORD_HEADER_LEN;
+
+	os_get_time(&now);
+#ifdef TEST_FUZZ
+	now.sec = 0xfffefdfc;
+#endif /* TEST_FUZZ */
+	WPA_PUT_BE32(conn->server_random, now.sec);
+	if (random_get_bytes(conn->server_random + 4, TLS_RANDOM_LEN - 4)) {
+		wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
+			   "server_random");
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: server_random",
+		    conn->server_random, TLS_RANDOM_LEN);
+
+	conn->session_id_len = TLS_SESSION_ID_MAX_LEN;
+	if (random_get_bytes(conn->session_id, conn->session_id_len)) {
+		wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
+			   "session_id");
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: session_id",
+		    conn->session_id, conn->session_id_len);
+
+	/* opaque fragment[TLSPlaintext.length] */
+
+	/* Handshake */
+	hs_start = pos;
+	/* HandshakeType msg_type */
+	*pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO;
+	/* uint24 length (to be filled) */
+	hs_length = pos;
+	pos += 3;
+	/* body - ServerHello */
+	/* ProtocolVersion server_version */
+	WPA_PUT_BE16(pos, conn->rl.tls_version);
+	pos += 2;
+	/* Random random: uint32 gmt_unix_time, opaque random_bytes */
+	os_memcpy(pos, conn->server_random, TLS_RANDOM_LEN);
+	pos += TLS_RANDOM_LEN;
+	/* SessionID session_id */
+	*pos++ = conn->session_id_len;
+	os_memcpy(pos, conn->session_id, conn->session_id_len);
+	pos += conn->session_id_len;
+	/* CipherSuite cipher_suite */
+	WPA_PUT_BE16(pos, conn->cipher_suite);
+	pos += 2;
+	/* CompressionMethod compression_method */
+	*pos++ = TLS_COMPRESSION_NULL;
+
+	/* Extension */
+	ext_start = pos;
+	pos += 2;
+
+	if (conn->status_request) {
+		/* Add a status_request extension with empty extension_data */
+		/* ExtensionsType extension_type = status_request(5) */
+		WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST);
+		pos += 2;
+		/* opaque extension_data<0..2^16-1> length */
+		WPA_PUT_BE16(pos, 0);
+		pos += 2;
+	}
+
+	if (conn->status_request_v2) {
+		/*
+		  Add a status_request_v2 extension with empty extension_data
+		*/
+		/* ExtensionsType extension_type = status_request_v2(17) */
+		WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST_V2);
+		pos += 2;
+		/* opaque extension_data<0..2^16-1> length */
+		WPA_PUT_BE16(pos, 0);
+		pos += 2;
+	}
+
+	if (conn->session_ticket && conn->session_ticket_cb) {
+		int res = conn->session_ticket_cb(
+			conn->session_ticket_cb_ctx,
+			conn->session_ticket, conn->session_ticket_len,
+			conn->client_random, conn->server_random,
+			conn->master_secret);
+		if (res < 0) {
+			tlsv1_server_log(conn, "SessionTicket callback indicated failure");
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_HANDSHAKE_FAILURE);
+			return -1;
+		}
+		conn->use_session_ticket = res;
+
+		if (conn->use_session_ticket) {
+			if (tlsv1_server_derive_keys(conn, NULL, 0) < 0) {
+				wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
+					   "derive keys");
+				tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+						   TLS_ALERT_INTERNAL_ERROR);
+				return -1;
+			}
+		}
+
+		/*
+		 * RFC 4507 specifies that server would include an empty
+		 * SessionTicket extension in ServerHello and a
+		 * NewSessionTicket message after the ServerHello. However,
+		 * EAP-FAST (RFC 4851), i.e., the only user of SessionTicket
+		 * extension at the moment, does not use such extensions.
+		 *
+		 * TODO: Add support for configuring RFC 4507 behavior and make
+		 * EAP-FAST disable it.
+		 */
+	}
+
+	if (pos == ext_start + 2)
+		pos -= 2; /* no extensions */
+	else
+		WPA_PUT_BE16(ext_start, pos - ext_start - 2);
+
+	WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+			      rhdr, end - rhdr, hs_start, pos - hs_start,
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	pos = rhdr + rlen;
+
+	*msgpos = pos;
+
+	return 0;
+}
+
+
+static int tls_write_server_certificate(struct tlsv1_server *conn,
+					u8 **msgpos, u8 *end)
+{
+	u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start;
+	size_t rlen;
+	struct x509_certificate *cert;
+	const struct tls_cipher_suite *suite;
+
+	suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+	if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Do not send Certificate when "
+			   "using anonymous DH");
+		return 0;
+	}
+
+	pos = *msgpos;
+	if (TLS_RECORD_HEADER_LEN + 1 + 3 + 3 > end - pos) {
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	tlsv1_server_log(conn, "Send Certificate");
+	rhdr = pos;
+	pos += TLS_RECORD_HEADER_LEN;
+
+	/* opaque fragment[TLSPlaintext.length] */
+
+	/* Handshake */
+	hs_start = pos;
+	/* HandshakeType msg_type */
+	*pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE;
+	/* uint24 length (to be filled) */
+	hs_length = pos;
+	pos += 3;
+	/* body - Certificate */
+	/* uint24 length (to be filled) */
+	cert_start = pos;
+	pos += 3;
+	cert = conn->cred->cert;
+	while (cert) {
+		if (3 + cert->cert_len > (size_t) (end - pos)) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space "
+				   "for Certificate (cert_len=%lu left=%lu)",
+				   (unsigned long) cert->cert_len,
+				   (unsigned long) (end - pos));
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_INTERNAL_ERROR);
+			return -1;
+		}
+		WPA_PUT_BE24(pos, cert->cert_len);
+		pos += 3;
+		os_memcpy(pos, cert->cert_start, cert->cert_len);
+		pos += cert->cert_len;
+
+		if (x509_certificate_self_signed(cert))
+			break;
+		cert = x509_certificate_get_subject(conn->cred->trusted_certs,
+						    &cert->issuer);
+	}
+	if (cert == conn->cred->cert || cert == NULL) {
+		/*
+		 * Server was not configured with all the needed certificates
+		 * to form a full certificate chain. The client may fail to
+		 * validate the chain unless it is configured with all the
+		 * missing CA certificates.
+		 */
+		wpa_printf(MSG_DEBUG, "TLSv1: Full server certificate chain "
+			   "not configured - validation may fail");
+	}
+	WPA_PUT_BE24(cert_start, pos - cert_start - 3);
+
+	WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+			      rhdr, end - rhdr, hs_start, pos - hs_start,
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	pos = rhdr + rlen;
+
+	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+	*msgpos = pos;
+
+	return 0;
+}
+
+
+static int tls_write_server_certificate_status(struct tlsv1_server *conn,
+					       u8 **msgpos, u8 *end,
+					       int ocsp_multi,
+					       char *ocsp_resp,
+					       size_t ocsp_resp_len)
+{
+	u8 *pos, *rhdr, *hs_start, *hs_length;
+	size_t rlen;
+
+	if (!ocsp_resp) {
+		 /*
+		  * Client did not request certificate status or there is no
+		  * matching response cached.
+		  */
+		return 0;
+	}
+
+	pos = *msgpos;
+	if (TLS_RECORD_HEADER_LEN + 1 + 3 + 1 + 3 + ocsp_resp_len >
+	    (unsigned int) (end - pos)) {
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	tlsv1_server_log(conn, "Send CertificateStatus (multi=%d)", ocsp_multi);
+	rhdr = pos;
+	pos += TLS_RECORD_HEADER_LEN;
+
+	/* opaque fragment[TLSPlaintext.length] */
+
+	/* Handshake */
+	hs_start = pos;
+	/* HandshakeType msg_type */
+	*pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS;
+	/* uint24 length (to be filled) */
+	hs_length = pos;
+	pos += 3;
+
+	/* body - CertificateStatus
+	 *
+	 * struct {
+	 *     CertificateStatusType status_type;
+	 *     select (status_type) {
+	 *         case ocsp: OCSPResponse;
+	 *         case ocsp_multi: OCSPResponseList;
+	 *     } response;
+	 * } CertificateStatus;
+	 *
+	 * opaque OCSPResponse<1..2^24-1>;
+	 *
+	 * struct {
+	 *   OCSPResponse ocsp_response_list<1..2^24-1>;
+	 * } OCSPResponseList;
+	 */
+
+	/* CertificateStatusType status_type */
+	if (ocsp_multi)
+		*pos++ = 2; /* ocsp_multi(2) */
+	else
+		*pos++ = 1; /* ocsp(1) */
+	/* uint24 length of OCSPResponse */
+	WPA_PUT_BE24(pos, ocsp_resp_len);
+	pos += 3;
+	os_memcpy(pos, ocsp_resp, ocsp_resp_len);
+	pos += ocsp_resp_len;
+
+	WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+			      rhdr, end - rhdr, hs_start, pos - hs_start,
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	pos = rhdr + rlen;
+
+	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+	*msgpos = pos;
+
+	return 0;
+}
+
+
+static int tls_write_server_key_exchange(struct tlsv1_server *conn,
+					 u8 **msgpos, u8 *end)
+{
+	tls_key_exchange keyx;
+	const struct tls_cipher_suite *suite;
+	u8 *pos, *rhdr, *hs_start, *hs_length, *server_params;
+	size_t rlen;
+	u8 *dh_ys;
+	size_t dh_ys_len;
+	const u8 *dh_p;
+	size_t dh_p_len;
+
+	suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+	if (suite == NULL)
+		keyx = TLS_KEY_X_NULL;
+	else
+		keyx = suite->key_exchange;
+
+	if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: No ServerKeyExchange needed");
+		return 0;
+	}
+
+	if (keyx != TLS_KEY_X_DH_anon && keyx != TLS_KEY_X_DHE_RSA) {
+		wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not yet "
+			   "supported with key exchange type %d", keyx);
+		return -1;
+	}
+
+	if (conn->cred == NULL || conn->cred->dh_p == NULL ||
+	    conn->cred->dh_g == NULL) {
+		wpa_printf(MSG_DEBUG, "TLSv1: No DH parameters available for "
+			   "ServerKeyExhcange");
+		return -1;
+	}
+
+	tlsv1_server_get_dh_p(conn, &dh_p, &dh_p_len);
+
+	os_free(conn->dh_secret);
+	conn->dh_secret_len = dh_p_len;
+	conn->dh_secret = os_malloc(conn->dh_secret_len);
+	if (conn->dh_secret == NULL) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+			   "memory for secret (Diffie-Hellman)");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	if (random_get_bytes(conn->dh_secret, conn->dh_secret_len)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
+			   "data for Diffie-Hellman");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		os_free(conn->dh_secret);
+		conn->dh_secret = NULL;
+		return -1;
+	}
+
+	if (os_memcmp(conn->dh_secret, dh_p, conn->dh_secret_len) > 0)
+		conn->dh_secret[0] = 0; /* make sure secret < p */
+
+	pos = conn->dh_secret;
+	while (pos + 1 < conn->dh_secret + conn->dh_secret_len && *pos == 0)
+		pos++;
+	if (pos != conn->dh_secret) {
+		os_memmove(conn->dh_secret, pos,
+			   conn->dh_secret_len - (pos - conn->dh_secret));
+		conn->dh_secret_len -= pos - conn->dh_secret;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "TLSv1: DH server's secret value",
+			conn->dh_secret, conn->dh_secret_len);
+
+	/* Ys = g^secret mod p */
+	dh_ys_len = dh_p_len;
+	dh_ys = os_malloc(dh_ys_len);
+	if (dh_ys == NULL) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate memory for "
+			   "Diffie-Hellman");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	if (crypto_mod_exp(conn->cred->dh_g, conn->cred->dh_g_len,
+			   conn->dh_secret, conn->dh_secret_len,
+			   dh_p, dh_p_len, dh_ys, &dh_ys_len)) {
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		os_free(dh_ys);
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)",
+		    dh_ys, dh_ys_len);
+
+	/*
+	 * struct {
+	 *    select (KeyExchangeAlgorithm) {
+	 *       case diffie_hellman:
+	 *          ServerDHParams params;
+	 *          Signature signed_params;
+	 *       case rsa:
+	 *          ServerRSAParams params;
+	 *          Signature signed_params;
+	 *    };
+	 * } ServerKeyExchange;
+	 *
+	 * struct {
+	 *    opaque dh_p<1..2^16-1>;
+	 *    opaque dh_g<1..2^16-1>;
+	 *    opaque dh_Ys<1..2^16-1>;
+	 * } ServerDHParams;
+	 */
+
+	pos = *msgpos;
+
+	tlsv1_server_log(conn, "Send ServerKeyExchange");
+	rhdr = pos;
+	pos += TLS_RECORD_HEADER_LEN;
+
+	/* opaque fragment[TLSPlaintext.length] */
+
+	/* Handshake */
+	hs_start = pos;
+	/* HandshakeType msg_type */
+	*pos++ = TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE;
+	/* uint24 length (to be filled) */
+	hs_length = pos;
+	pos += 3;
+
+	/* body - ServerDHParams */
+	server_params = pos;
+	/* dh_p */
+	if (2 + dh_p_len > (size_t) (end - pos)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
+			   "dh_p");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		os_free(dh_ys);
+		return -1;
+	}
+	WPA_PUT_BE16(pos, dh_p_len);
+	pos += 2;
+	os_memcpy(pos, dh_p, dh_p_len);
+	pos += dh_p_len;
+
+	/* dh_g */
+	if (2 + conn->cred->dh_g_len > (size_t) (end - pos)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
+			   "dh_g");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		os_free(dh_ys);
+		return -1;
+	}
+	WPA_PUT_BE16(pos, conn->cred->dh_g_len);
+	pos += 2;
+	os_memcpy(pos, conn->cred->dh_g, conn->cred->dh_g_len);
+	pos += conn->cred->dh_g_len;
+
+	/* dh_Ys */
+	if (2 + dh_ys_len > (size_t) (end - pos)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
+			   "dh_Ys");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		os_free(dh_ys);
+		return -1;
+	}
+	WPA_PUT_BE16(pos, dh_ys_len);
+	pos += 2;
+	os_memcpy(pos, dh_ys, dh_ys_len);
+	pos += dh_ys_len;
+	os_free(dh_ys);
+
+	/*
+	 * select (SignatureAlgorithm)
+	 * {   case anonymous: struct { };
+	 *     case rsa:
+	 *         digitally-signed struct {
+	 *             opaque md5_hash[16];
+	 *             opaque sha_hash[20];
+	 *         };
+	 *     case dsa:
+	 *         digitally-signed struct {
+	 *             opaque sha_hash[20];
+	 *         };
+	 * } Signature;
+	 *
+	 * md5_hash
+	 *     MD5(ClientHello.random + ServerHello.random + ServerParams);
+	 *
+	 * sha_hash
+	 *     SHA(ClientHello.random + ServerHello.random + ServerParams);
+	 */
+
+	if (keyx == TLS_KEY_X_DHE_RSA) {
+		u8 hash[100];
+		u8 *signed_start;
+		size_t clen;
+		int hlen;
+
+		if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+#ifdef CONFIG_TLSV12
+			hlen = tlsv12_key_x_server_params_hash(
+				conn->rl.tls_version, TLS_HASH_ALG_SHA256,
+				conn->client_random,
+				conn->server_random, server_params,
+				pos - server_params, hash + 19);
+
+			/*
+			 * RFC 5246, 4.7:
+			 * TLS v1.2 adds explicit indication of the used
+			 * signature and hash algorithms.
+			 *
+			 * struct {
+			 *   HashAlgorithm hash;
+			 *   SignatureAlgorithm signature;
+			 * } SignatureAndHashAlgorithm;
+			 */
+			if (hlen < 0 || end - pos < 2) {
+				tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+						   TLS_ALERT_INTERNAL_ERROR);
+				return -1;
+			}
+			*pos++ = TLS_HASH_ALG_SHA256;
+			*pos++ = TLS_SIGN_ALG_RSA;
+
+			/*
+			 * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5
+			 *
+			 * DigestInfo ::= SEQUENCE {
+			 *   digestAlgorithm DigestAlgorithm,
+			 *   digest OCTET STRING
+			 * }
+			 *
+			 * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11}
+			 *
+			 * DER encoded DigestInfo for SHA256 per RFC 3447:
+			 * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00
+			 * 04 20 || H
+			 */
+			hlen += 19;
+			os_memcpy(hash,
+				  "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65"
+				  "\x03\x04\x02\x01\x05\x00\x04\x20", 19);
+
+#else /* CONFIG_TLSV12 */
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_INTERNAL_ERROR);
+			return -1;
+#endif /* CONFIG_TLSV12 */
+		} else {
+			hlen = tls_key_x_server_params_hash(
+				conn->rl.tls_version, conn->client_random,
+				conn->server_random, server_params,
+				pos - server_params, hash, sizeof(hash));
+		}
+
+		if (hlen < 0) {
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_INTERNAL_ERROR);
+			return -1;
+		}
+
+		wpa_hexdump(MSG_MSGDUMP, "TLS: ServerKeyExchange signed_params hash",
+			    hash, hlen);
+#ifdef CONFIG_TESTING_OPTIONS
+		if (conn->test_flags & TLS_BREAK_SRV_KEY_X_HASH) {
+			tlsv1_server_log(conn, "TESTING: Break ServerKeyExchange signed params hash");
+			hash[hlen - 1] ^= 0x80;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+		/*
+		 * RFC 2246, 4.7:
+		 * In digital signing, one-way hash functions are used as input
+		 * for a signing algorithm. A digitally-signed element is
+		 * encoded as an opaque vector <0..2^16-1>, where the length is
+		 * specified by the signing algorithm and key.
+		 *
+		 * In RSA signing, a 36-byte structure of two hashes (one SHA
+		 * and one MD5) is signed (encrypted with the private key). It
+		 * is encoded with PKCS #1 block type 0 or type 1 as described
+		 * in [PKCS1].
+		 */
+		signed_start = pos; /* length to be filled */
+		pos += 2;
+		clen = end - pos;
+		if (conn->cred == NULL ||
+		    crypto_private_key_sign_pkcs1(conn->cred->key, hash, hlen,
+						  pos, &clen) < 0) {
+			wpa_printf(MSG_DEBUG, "TLSv1: Failed to sign hash (PKCS #1)");
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_INTERNAL_ERROR);
+			return -1;
+		}
+		WPA_PUT_BE16(signed_start, clen);
+#ifdef CONFIG_TESTING_OPTIONS
+		if (conn->test_flags & TLS_BREAK_SRV_KEY_X_SIGNATURE) {
+			tlsv1_server_log(conn, "TESTING: Break ServerKeyExchange signed params signature");
+			pos[clen - 1] ^= 0x80;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+		pos += clen;
+	}
+
+	WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+			      rhdr, end - rhdr, hs_start, pos - hs_start,
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	pos = rhdr + rlen;
+
+	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+	*msgpos = pos;
+
+	return 0;
+}
+
+
+static int tls_write_server_certificate_request(struct tlsv1_server *conn,
+						u8 **msgpos, u8 *end)
+{
+	u8 *pos, *rhdr, *hs_start, *hs_length;
+	size_t rlen;
+
+	if (!conn->verify_peer) {
+		wpa_printf(MSG_DEBUG, "TLSv1: No CertificateRequest needed");
+		return 0;
+	}
+
+	pos = *msgpos;
+
+	tlsv1_server_log(conn, "Send CertificateRequest");
+	rhdr = pos;
+	pos += TLS_RECORD_HEADER_LEN;
+
+	/* opaque fragment[TLSPlaintext.length] */
+
+	/* Handshake */
+	hs_start = pos;
+	/* HandshakeType msg_type */
+	*pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST;
+	/* uint24 length (to be filled) */
+	hs_length = pos;
+	pos += 3;
+	/* body - CertificateRequest */
+
+	/*
+	 * enum {
+	 *   rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), dss_fixed_dh(4),
+	 *   (255)
+	 * } ClientCertificateType;
+	 * ClientCertificateType certificate_types<1..2^8-1>
+	 */
+	*pos++ = 1;
+	*pos++ = 1; /* rsa_sign */
+
+	/*
+	 * opaque DistinguishedName<1..2^16-1>
+	 * DistinguishedName certificate_authorities<3..2^16-1>
+	 */
+	/* TODO: add support for listing DNs for trusted CAs */
+	WPA_PUT_BE16(pos, 0);
+	pos += 2;
+
+	WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+			      rhdr, end - rhdr, hs_start, pos - hs_start,
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	pos = rhdr + rlen;
+
+	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+	*msgpos = pos;
+
+	return 0;
+}
+
+
+static int tls_write_server_hello_done(struct tlsv1_server *conn,
+				       u8 **msgpos, u8 *end)
+{
+	u8 *pos;
+	size_t rlen;
+	u8 payload[4];
+
+	tlsv1_server_log(conn, "Send ServerHelloDone");
+
+	/* opaque fragment[TLSPlaintext.length] */
+
+	/* Handshake */
+	pos = payload;
+	/* HandshakeType msg_type */
+	*pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE;
+	/* uint24 length */
+	WPA_PUT_BE24(pos, 0);
+	pos += 3;
+	/* body - ServerHelloDone (empty) */
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+			      *msgpos, end - *msgpos, payload, pos - payload,
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	tls_verify_hash_add(&conn->verify, payload, pos - payload);
+
+	*msgpos += rlen;
+
+	return 0;
+}
+
+
+static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn,
+					       u8 **msgpos, u8 *end)
+{
+	size_t rlen;
+	u8 payload[1];
+
+	tlsv1_server_log(conn, "Send ChangeCipherSpec");
+
+	payload[0] = TLS_CHANGE_CIPHER_SPEC;
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC,
+			      *msgpos, end - *msgpos, payload, sizeof(payload),
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	if (tlsv1_record_change_write_cipher(&conn->rl) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for "
+			   "record layer");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	*msgpos += rlen;
+
+	return 0;
+}
+
+
+static int tls_write_server_finished(struct tlsv1_server *conn,
+				     u8 **msgpos, u8 *end)
+{
+	u8 *pos, *hs_start;
+	size_t rlen, hlen;
+	u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN];
+	u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+
+	pos = *msgpos;
+
+	tlsv1_server_log(conn, "Send Finished");
+
+	/* Encrypted Handshake Message: Finished */
+
+#ifdef CONFIG_TLSV12
+	if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+		hlen = SHA256_MAC_LEN;
+		if (conn->verify.sha256_server == NULL ||
+		    crypto_hash_finish(conn->verify.sha256_server, hash, &hlen)
+		    < 0) {
+			conn->verify.sha256_server = NULL;
+			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					   TLS_ALERT_INTERNAL_ERROR);
+			return -1;
+		}
+		conn->verify.sha256_server = NULL;
+	} else {
+#endif /* CONFIG_TLSV12 */
+
+	hlen = MD5_MAC_LEN;
+	if (conn->verify.md5_server == NULL ||
+	    crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) {
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		conn->verify.md5_server = NULL;
+		crypto_hash_finish(conn->verify.sha1_server, NULL, NULL);
+		conn->verify.sha1_server = NULL;
+		return -1;
+	}
+	conn->verify.md5_server = NULL;
+	hlen = SHA1_MAC_LEN;
+	if (conn->verify.sha1_server == NULL ||
+	    crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN,
+			       &hlen) < 0) {
+		conn->verify.sha1_server = NULL;
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	conn->verify.sha1_server = NULL;
+	hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+	}
+#endif /* CONFIG_TLSV12 */
+
+	if (tls_prf(conn->rl.tls_version,
+		    conn->master_secret, TLS_MASTER_SECRET_LEN,
+		    "server finished", hash, hlen,
+		    verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)",
+			verify_data + 1 + 3, TLS_VERIFY_DATA_LEN);
+#ifdef CONFIG_TESTING_OPTIONS
+	if (conn->test_flags & TLS_BREAK_VERIFY_DATA) {
+		tlsv1_server_log(conn, "TESTING: Break verify_data (server)");
+		verify_data[1 + 3 + 1] ^= 0x80;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* Handshake */
+	pos = hs_start = verify_data;
+	/* HandshakeType msg_type */
+	*pos++ = TLS_HANDSHAKE_TYPE_FINISHED;
+	/* uint24 length */
+	WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN);
+	pos += 3;
+	pos += TLS_VERIFY_DATA_LEN;
+	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+	if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+			      *msgpos, end - *msgpos, hs_start, pos - hs_start,
+			      &rlen) < 0) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	*msgpos += rlen;
+
+	return 0;
+}
+
+
+static u8 * tls_send_server_hello(struct tlsv1_server *conn, size_t *out_len)
+{
+	u8 *msg, *end, *pos;
+	size_t msglen;
+	int ocsp_multi = 0;
+	char *ocsp_resp = NULL;
+	size_t ocsp_resp_len = 0;
+
+	*out_len = 0;
+
+	if (conn->status_request_multi &&
+	    conn->cred->ocsp_stapling_response_multi) {
+		ocsp_resp = os_readfile(
+			conn->cred->ocsp_stapling_response_multi,
+			&ocsp_resp_len);
+		ocsp_multi = 1;
+	} else if ((conn->status_request || conn->status_request_v2) &&
+		   conn->cred->ocsp_stapling_response) {
+		ocsp_resp = os_readfile(conn->cred->ocsp_stapling_response,
+					&ocsp_resp_len);
+	}
+	if (!ocsp_resp)
+		ocsp_resp_len = 0;
+
+	msglen = 1000 + tls_server_cert_chain_der_len(conn) + ocsp_resp_len;
+
+	msg = os_malloc(msglen);
+	if (msg == NULL) {
+		os_free(ocsp_resp);
+		return NULL;
+	}
+
+	pos = msg;
+	end = msg + msglen;
+
+	if (tls_write_server_hello(conn, &pos, end) < 0) {
+		os_free(msg);
+		os_free(ocsp_resp);
+		return NULL;
+	}
+
+	if (conn->use_session_ticket) {
+		os_free(ocsp_resp);
+
+		/* Abbreviated handshake using session ticket; RFC 4507 */
+		if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 ||
+		    tls_write_server_finished(conn, &pos, end) < 0) {
+			os_free(msg);
+			return NULL;
+		}
+
+		*out_len = pos - msg;
+
+		conn->state = CHANGE_CIPHER_SPEC;
+
+		return msg;
+	}
+
+	/* Full handshake */
+	if (tls_write_server_certificate(conn, &pos, end) < 0 ||
+	    tls_write_server_certificate_status(conn, &pos, end, ocsp_multi,
+						ocsp_resp, ocsp_resp_len) < 0 ||
+	    tls_write_server_key_exchange(conn, &pos, end) < 0 ||
+	    tls_write_server_certificate_request(conn, &pos, end) < 0 ||
+	    tls_write_server_hello_done(conn, &pos, end) < 0) {
+		os_free(msg);
+		os_free(ocsp_resp);
+		return NULL;
+	}
+	os_free(ocsp_resp);
+
+	*out_len = pos - msg;
+
+	conn->state = CLIENT_CERTIFICATE;
+
+	return msg;
+}
+
+
+static u8 * tls_send_change_cipher_spec(struct tlsv1_server *conn,
+					size_t *out_len)
+{
+	u8 *msg, *end, *pos;
+
+	*out_len = 0;
+
+	msg = os_malloc(1000);
+	if (msg == NULL)
+		return NULL;
+
+	pos = msg;
+	end = msg + 1000;
+
+	if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 ||
+	    tls_write_server_finished(conn, &pos, end) < 0) {
+		os_free(msg);
+		return NULL;
+	}
+
+	*out_len = pos - msg;
+
+	tlsv1_server_log(conn, "Handshake completed successfully");
+	conn->state = ESTABLISHED;
+
+	return msg;
+}
+
+
+u8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len)
+{
+	switch (conn->state) {
+	case SERVER_HELLO:
+		return tls_send_server_hello(conn, out_len);
+	case SERVER_CHANGE_CIPHER_SPEC:
+		return tls_send_change_cipher_spec(conn, out_len);
+	default:
+		if (conn->state == ESTABLISHED && conn->use_session_ticket) {
+			/* Abbreviated handshake was already completed. */
+			return NULL;
+		}
+		tlsv1_server_log(conn, "Unexpected state %d while generating reply",
+				 conn->state);
+		return NULL;
+	}
+}
+
+
+u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level,
+			     u8 description, size_t *out_len)
+{
+	u8 *alert, *pos, *length;
+
+	tlsv1_server_log(conn, "Send Alert(%d:%d)", level, description);
+	*out_len = 0;
+
+	alert = os_malloc(10);
+	if (alert == NULL)
+		return NULL;
+
+	pos = alert;
+
+	/* TLSPlaintext */
+	/* ContentType type */
+	*pos++ = TLS_CONTENT_TYPE_ALERT;
+	/* ProtocolVersion version */
+	WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version :
+		     TLS_VERSION);
+	pos += 2;
+	/* uint16 length (to be filled) */
+	length = pos;
+	pos += 2;
+	/* opaque fragment[TLSPlaintext.length] */
+
+	/* Alert */
+	/* AlertLevel level */
+	*pos++ = level;
+	/* AlertDescription description */
+	*pos++ = description;
+
+	WPA_PUT_BE16(length, pos - length - 2);
+	*out_len = pos - alert;
+
+	return alert;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/x509v3.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/x509v3.c
new file mode 100644
index 0000000..b006e99
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/x509v3.c
@@ -0,0 +1,2291 @@
+/*
+ * X.509v3 certificate parsing and processing (RFC 3280 profile)
+ * Copyright (c) 2006-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 "common.h"
+#include "crypto/crypto.h"
+#include "asn1.h"
+#include "x509v3.h"
+
+
+void x509_free_name(struct x509_name *name)
+{
+	size_t i;
+
+	for (i = 0; i < name->num_attr; i++) {
+		os_free(name->attr[i].value);
+		name->attr[i].value = NULL;
+		name->attr[i].type = X509_NAME_ATTR_NOT_USED;
+	}
+	name->num_attr = 0;
+	os_free(name->email);
+	name->email = NULL;
+
+	os_free(name->alt_email);
+	os_free(name->dns);
+	os_free(name->uri);
+	os_free(name->ip);
+	name->alt_email = name->dns = name->uri = NULL;
+	name->ip = NULL;
+	name->ip_len = 0;
+	os_memset(&name->rid, 0, sizeof(name->rid));
+}
+
+
+/**
+ * x509_certificate_free - Free an X.509 certificate
+ * @cert: Certificate to be freed
+ */
+void x509_certificate_free(struct x509_certificate *cert)
+{
+	if (cert == NULL)
+		return;
+	if (cert->next) {
+		wpa_printf(MSG_DEBUG, "X509: x509_certificate_free: cer=%p "
+			   "was still on a list (next=%p)\n",
+			   cert, cert->next);
+	}
+	x509_free_name(&cert->issuer);
+	x509_free_name(&cert->subject);
+	os_free(cert->public_key);
+	os_free(cert->sign_value);
+	os_free(cert->subject_dn);
+	os_free(cert);
+}
+
+
+/**
+ * x509_certificate_free - Free an X.509 certificate chain
+ * @cert: Pointer to the first certificate in the chain
+ */
+void x509_certificate_chain_free(struct x509_certificate *cert)
+{
+	struct x509_certificate *next;
+
+	while (cert) {
+		next = cert->next;
+		cert->next = NULL;
+		x509_certificate_free(cert);
+		cert = next;
+	}
+}
+
+
+static int x509_whitespace(char c)
+{
+	return c == ' ' || c == '\t';
+}
+
+
+static void x509_str_strip_whitespace(char *a)
+{
+	char *ipos, *opos;
+	int remove_whitespace = 1;
+
+	ipos = opos = a;
+
+	while (*ipos) {
+		if (remove_whitespace && x509_whitespace(*ipos))
+			ipos++;
+		else {
+			remove_whitespace = x509_whitespace(*ipos);
+			*opos++ = *ipos++;
+		}
+	}
+
+	*opos-- = '\0';
+	if (opos > a && x509_whitespace(*opos))
+		*opos = '\0';
+}
+
+
+static int x509_str_compare(const char *a, const char *b)
+{
+	char *aa, *bb;
+	int ret;
+
+	if (!a && b)
+		return -1;
+	if (a && !b)
+		return 1;
+	if (!a && !b)
+		return 0;
+
+	aa = os_strdup(a);
+	bb = os_strdup(b);
+
+	if (aa == NULL || bb == NULL) {
+		os_free(aa);
+		os_free(bb);
+		return os_strcasecmp(a, b);
+	}
+
+	x509_str_strip_whitespace(aa);
+	x509_str_strip_whitespace(bb);
+
+	ret = os_strcasecmp(aa, bb);
+
+	os_free(aa);
+	os_free(bb);
+
+	return ret;
+}
+
+
+/**
+ * x509_name_compare - Compare X.509 certificate names
+ * @a: Certificate name
+ * @b: Certificate name
+ * Returns: <0, 0, or >0 based on whether a is less than, equal to, or
+ * greater than b
+ */
+int x509_name_compare(struct x509_name *a, struct x509_name *b)
+{
+	int res;
+	size_t i;
+
+	if (!a && b)
+		return -1;
+	if (a && !b)
+		return 1;
+	if (!a && !b)
+		return 0;
+	if (a->num_attr < b->num_attr)
+		return -1;
+	if (a->num_attr > b->num_attr)
+		return 1;
+
+	for (i = 0; i < a->num_attr; i++) {
+		if (a->attr[i].type < b->attr[i].type)
+			return -1;
+		if (a->attr[i].type > b->attr[i].type)
+			return -1;
+		res = x509_str_compare(a->attr[i].value, b->attr[i].value);
+		if (res)
+			return res;
+	}
+	res = x509_str_compare(a->email, b->email);
+	if (res)
+		return res;
+
+	return 0;
+}
+
+
+int x509_parse_algorithm_identifier(const u8 *buf, size_t len,
+				    struct x509_algorithm_identifier *id,
+				    const u8 **next)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+
+	/*
+	 * AlgorithmIdentifier ::= SEQUENCE {
+	 *     algorithm            OBJECT IDENTIFIER,
+	 *     parameters           ANY DEFINED BY algorithm OPTIONAL
+	 * }
+	 */
+
+	if (asn1_get_next(buf, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"X509: Expected SEQUENCE (AlgorithmIdentifier)");
+		return -1;
+	}
+	if (hdr.length > buf + len - hdr.payload)
+		return -1;
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	*next = end;
+
+	if (asn1_get_oid(pos, end - pos, &id->oid, &pos))
+		return -1;
+
+	/* TODO: optional parameters */
+
+	return 0;
+}
+
+
+static int x509_parse_public_key(const u8 *buf, size_t len,
+				 struct x509_certificate *cert,
+				 const u8 **next)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+
+	/*
+	 * SubjectPublicKeyInfo ::= SEQUENCE {
+	 *     algorithm            AlgorithmIdentifier,
+	 *     subjectPublicKey     BIT STRING
+	 * }
+	 */
+
+	pos = buf;
+	end = buf + len;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"X509: Expected SEQUENCE (SubjectPublicKeyInfo)");
+		return -1;
+	}
+	pos = hdr.payload;
+
+	if (hdr.length > end - pos)
+		return -1;
+	end = pos + hdr.length;
+	*next = end;
+
+	if (x509_parse_algorithm_identifier(pos, end - pos,
+					    &cert->public_key_alg, &pos))
+		return -1;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_bitstring(&hdr)) {
+		asn1_unexpected(&hdr,
+				"X509: Expected BITSTRING (subjectPublicKey)");
+		return -1;
+	}
+	if (hdr.length < 1)
+		return -1;
+	pos = hdr.payload;
+	if (*pos) {
+		wpa_printf(MSG_DEBUG,
+			   "X509: BITSTRING (subjectPublicKey) - %d unused bits",
+			   *pos);
+		/*
+		 * TODO: should this be rejected? X.509 certificates are
+		 * unlikely to use such a construction. Now we would end up
+		 * including the extra bits in the buffer which may also be
+		 * ok.
+		 */
+	}
+	os_free(cert->public_key);
+	cert->public_key = os_memdup(pos + 1, hdr.length - 1);
+	if (cert->public_key == NULL) {
+		wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for "
+			   "public key");
+		return -1;
+	}
+	cert->public_key_len = hdr.length - 1;
+	wpa_hexdump(MSG_MSGDUMP, "X509: subjectPublicKey",
+		    cert->public_key, cert->public_key_len);
+
+	return 0;
+}
+
+
+int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name,
+		    const u8 **next)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end, *set_pos, *set_end, *seq_pos, *seq_end;
+	struct asn1_oid oid;
+	char *val;
+
+	/*
+	 * Name ::= CHOICE { RDNSequence }
+	 * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+	 * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue
+	 * AttributeTypeAndValue ::= SEQUENCE {
+	 *     type     AttributeType,
+	 *     value    AttributeValue
+	 * }
+	 * AttributeType ::= OBJECT IDENTIFIER
+	 * AttributeValue ::= ANY DEFINED BY AttributeType
+	 */
+
+	if (asn1_get_next(buf, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"X509: Expected SEQUENCE (Name / RDNSequencer)");
+		return -1;
+	}
+	pos = hdr.payload;
+
+	if (hdr.length > buf + len - pos)
+		return -1;
+
+	end = *next = pos + hdr.length;
+
+	while (pos < end) {
+		enum x509_name_attr_type type;
+
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    !asn1_is_set(&hdr)) {
+			asn1_unexpected(&hdr,
+					"X509: Expected SET (RelativeDistinguishedName)");
+			x509_free_name(name);
+			return -1;
+		}
+
+		set_pos = hdr.payload;
+		pos = set_end = hdr.payload + hdr.length;
+
+		if (asn1_get_next(set_pos, set_end - set_pos, &hdr) < 0 ||
+		    !asn1_is_sequence(&hdr)) {
+			asn1_unexpected(&hdr,
+					"X509: Expected SEQUENCE (AttributeTypeAndValue)");
+			x509_free_name(name);
+			return -1;
+		}
+
+		seq_pos = hdr.payload;
+		seq_end = hdr.payload + hdr.length;
+
+		if (asn1_get_oid(seq_pos, seq_end - seq_pos, &oid, &seq_pos)) {
+			x509_free_name(name);
+			return -1;
+		}
+
+		if (asn1_get_next(seq_pos, seq_end - seq_pos, &hdr) < 0 ||
+		    hdr.class != ASN1_CLASS_UNIVERSAL) {
+			wpa_printf(MSG_DEBUG, "X509: Failed to parse "
+				   "AttributeValue");
+			x509_free_name(name);
+			return -1;
+		}
+
+		if (!asn1_is_string_type(&hdr)) {
+			wpa_printf(MSG_DEBUG,
+				   "X509: Ignore non-string type attribute (tag 0x%x)",
+				   hdr.tag);
+			continue;
+		}
+
+		/* RFC 3280:
+		 * MUST: country, organization, organizational-unit,
+		 * distinguished name qualifier, state or province name,
+		 * common name, serial number.
+		 * SHOULD: locality, title, surname, given name, initials,
+		 * pseudonym, generation qualifier.
+		 * MUST: domainComponent (RFC 2247).
+		 */
+		type = X509_NAME_ATTR_NOT_USED;
+		if (oid.len == 4 &&
+		    oid.oid[0] == 2 && oid.oid[1] == 5 && oid.oid[2] == 4) {
+			/* id-at ::= 2.5.4 */
+			switch (oid.oid[3]) {
+			case 3:
+				/* commonName */
+				type = X509_NAME_ATTR_CN;
+				break;
+			case 6:
+				/*  countryName */
+				type = X509_NAME_ATTR_C;
+				break;
+			case 7:
+				/* localityName */
+				type = X509_NAME_ATTR_L;
+				break;
+			case 8:
+				/* stateOrProvinceName */
+				type = X509_NAME_ATTR_ST;
+				break;
+			case 10:
+				/* organizationName */
+				type = X509_NAME_ATTR_O;
+				break;
+			case 11:
+				/* organizationalUnitName */
+				type = X509_NAME_ATTR_OU;
+				break;
+			}
+		} else if (oid.len == 7 &&
+			   oid.oid[0] == 1 && oid.oid[1] == 2 &&
+			   oid.oid[2] == 840 && oid.oid[3] == 113549 &&
+			   oid.oid[4] == 1 && oid.oid[5] == 9 &&
+			   oid.oid[6] == 1) {
+			/* 1.2.840.113549.1.9.1 - e-mailAddress */
+			os_free(name->email);
+			name->email = os_malloc(hdr.length + 1);
+			if (name->email == NULL) {
+				x509_free_name(name);
+				return -1;
+			}
+			os_memcpy(name->email, hdr.payload, hdr.length);
+			name->email[hdr.length] = '\0';
+			continue;
+		} else if (oid.len == 7 &&
+			   oid.oid[0] == 0 && oid.oid[1] == 9 &&
+			   oid.oid[2] == 2342 && oid.oid[3] == 19200300 &&
+			   oid.oid[4] == 100 && oid.oid[5] == 1 &&
+			   oid.oid[6] == 25) {
+			/* 0.9.2342.19200300.100.1.25 - domainComponent */
+			type = X509_NAME_ATTR_DC;
+		}
+
+		if (type == X509_NAME_ATTR_NOT_USED) {
+			wpa_hexdump(MSG_DEBUG, "X509: Unrecognized OID",
+				    (u8 *) oid.oid,
+				    oid.len * sizeof(oid.oid[0]));
+			wpa_hexdump_ascii(MSG_MSGDUMP, "X509: Attribute Data",
+					  hdr.payload, hdr.length);
+			continue;
+		}
+
+		if (name->num_attr == X509_MAX_NAME_ATTRIBUTES) {
+			wpa_printf(MSG_INFO, "X509: Too many Name attributes");
+			x509_free_name(name);
+			return -1;
+		}
+
+		val = dup_binstr(hdr.payload, hdr.length);
+		if (val == NULL) {
+			x509_free_name(name);
+			return -1;
+		}
+		if (os_strlen(val) != hdr.length) {
+			wpa_printf(MSG_INFO, "X509: Reject certificate with "
+				   "embedded NUL byte in a string (%s[NUL])",
+				   val);
+			os_free(val);
+			x509_free_name(name);
+			return -1;
+		}
+
+		name->attr[name->num_attr].type = type;
+		name->attr[name->num_attr].value = val;
+		name->num_attr++;
+	}
+
+	return 0;
+}
+
+
+static char * x509_name_attr_str(enum x509_name_attr_type type)
+{
+	switch (type) {
+	case X509_NAME_ATTR_NOT_USED:
+		return "[N/A]";
+	case X509_NAME_ATTR_DC:
+		return "DC";
+	case X509_NAME_ATTR_CN:
+		return "CN";
+	case X509_NAME_ATTR_C:
+		return "C";
+	case X509_NAME_ATTR_L:
+		return "L";
+	case X509_NAME_ATTR_ST:
+		return "ST";
+	case X509_NAME_ATTR_O:
+		return "O";
+	case X509_NAME_ATTR_OU:
+		return "OU";
+	}
+	return "?";
+}
+
+
+/**
+ * x509_name_string - Convert an X.509 certificate name into a string
+ * @name: Name to convert
+ * @buf: Buffer for the string
+ * @len: Maximum buffer length
+ */
+void x509_name_string(struct x509_name *name, char *buf, size_t len)
+{
+	char *pos, *end;
+	int ret;
+	size_t i;
+
+	if (len == 0)
+		return;
+
+	pos = buf;
+	end = buf + len;
+
+	for (i = 0; i < name->num_attr; i++) {
+		ret = os_snprintf(pos, end - pos, "%s=%s, ",
+				  x509_name_attr_str(name->attr[i].type),
+				  name->attr[i].value);
+		if (os_snprintf_error(end - pos, ret))
+			goto done;
+		pos += ret;
+	}
+
+	if (pos > buf + 1 && pos[-1] == ' ' && pos[-2] == ',') {
+		pos--;
+		*pos = '\0';
+		pos--;
+		*pos = '\0';
+	}
+
+	if (name->email) {
+		ret = os_snprintf(pos, end - pos, "/emailAddress=%s",
+				  name->email);
+		if (os_snprintf_error(end - pos, ret))
+			goto done;
+		pos += ret;
+	}
+
+done:
+	if (pos < end)
+		*pos = '\0';
+	end[-1] = '\0';
+}
+
+
+static int parse_uint2(const char *pos, size_t len)
+{
+	char buf[3];
+	int ret;
+
+	if (len < 2)
+		return -1;
+	buf[0] = pos[0];
+	buf[1] = pos[1];
+	buf[2] = 0x00;
+	if (sscanf(buf, "%2d", &ret) != 1)
+		return -1;
+	return ret;
+}
+
+
+static int parse_uint4(const char *pos, size_t len)
+{
+	char buf[5];
+	int ret;
+
+	if (len < 4)
+		return -1;
+	buf[0] = pos[0];
+	buf[1] = pos[1];
+	buf[2] = pos[2];
+	buf[3] = pos[3];
+	buf[4] = 0x00;
+	if (sscanf(buf, "%4d", &ret) != 1)
+		return -1;
+	return ret;
+}
+
+
+int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, os_time_t *val)
+{
+	const char *pos, *end;
+	int year, month, day, hour, min, sec;
+
+	/*
+	 * Time ::= CHOICE {
+	 *     utcTime        UTCTime,
+	 *     generalTime    GeneralizedTime
+	 * }
+	 *
+	 * UTCTime: YYMMDDHHMMSSZ
+	 * GeneralizedTime: YYYYMMDDHHMMSSZ
+	 */
+
+	pos = (const char *) buf;
+	end = pos + len;
+
+	switch (asn1_tag) {
+	case ASN1_TAG_UTCTIME:
+		if (len != 13 || buf[12] != 'Z') {
+			wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized "
+					  "UTCTime format", buf, len);
+			return -1;
+		}
+		year = parse_uint2(pos, end - pos);
+		if (year < 0) {
+			wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse "
+					  "UTCTime year", buf, len);
+			return -1;
+		}
+		if (year < 50)
+			year += 2000;
+		else
+			year += 1900;
+		pos += 2;
+		break;
+	case ASN1_TAG_GENERALIZEDTIME:
+		if (len != 15 || buf[14] != 'Z') {
+			wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized "
+					  "GeneralizedTime format", buf, len);
+			return -1;
+		}
+		year = parse_uint4(pos, end - pos);
+		if (year < 0) {
+			wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse "
+					  "GeneralizedTime year", buf, len);
+			return -1;
+		}
+		pos += 4;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "X509: Expected UTCTime or "
+			   "GeneralizedTime - found tag 0x%x", asn1_tag);
+		return -1;
+	}
+
+	month = parse_uint2(pos, end - pos);
+	if (month < 0) {
+		wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+				  "(month)", buf, len);
+		return -1;
+	}
+	pos += 2;
+
+	day = parse_uint2(pos, end - pos);
+	if (day < 0) {
+		wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+				  "(day)", buf, len);
+		return -1;
+	}
+	pos += 2;
+
+	hour = parse_uint2(pos, end - pos);
+	if (hour < 0) {
+		wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+				  "(hour)", buf, len);
+		return -1;
+	}
+	pos += 2;
+
+	min = parse_uint2(pos, end - pos);
+	if (min < 0) {
+		wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+				  "(min)", buf, len);
+		return -1;
+	}
+	pos += 2;
+
+	sec = parse_uint2(pos, end - pos);
+	if (sec < 0) {
+		wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+				  "(sec)", buf, len);
+		return -1;
+	}
+
+	if (os_mktime(year, month, day, hour, min, sec, val) < 0) {
+		wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to convert Time",
+				  buf, len);
+		if (year < 1970) {
+			/*
+			 * At least some test certificates have been configured
+			 * to use dates prior to 1970. Set the date to
+			 * beginning of 1970 to handle these case.
+			 */
+			wpa_printf(MSG_DEBUG, "X509: Year=%d before epoch - "
+				   "assume epoch as the time", year);
+			*val = 0;
+			return 0;
+		}
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int x509_parse_validity(const u8 *buf, size_t len,
+			       struct x509_certificate *cert, const u8 **next)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos;
+	size_t plen;
+
+	/*
+	 * Validity ::= SEQUENCE {
+	 *     notBefore      Time,
+	 *     notAfter       Time
+	 * }
+	 *
+	 * RFC 3280, 4.1.2.5:
+	 * CAs conforming to this profile MUST always encode certificate
+	 * validity dates through the year 2049 as UTCTime; certificate
+	 * validity dates in 2050 or later MUST be encoded as GeneralizedTime.
+	 */
+
+	if (asn1_get_next(buf, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr, "X509: Expected SEQUENCE (Validity)");
+		return -1;
+	}
+	pos = hdr.payload;
+	plen = hdr.length;
+
+	if (plen > (size_t) (buf + len - pos))
+		return -1;
+
+	*next = pos + plen;
+
+	if (asn1_get_next(pos, plen, &hdr) < 0 ||
+	    (!asn1_is_utctime(&hdr) && !asn1_is_generalizedtime(&hdr)) ||
+	    x509_parse_time(hdr.payload, hdr.length, hdr.tag,
+			    &cert->not_before) < 0) {
+		wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notBefore "
+				  "Time", hdr.payload, hdr.length);
+		return -1;
+	}
+
+	pos = hdr.payload + hdr.length;
+	plen = *next - pos;
+
+	if (asn1_get_next(pos, plen, &hdr) < 0 ||
+	    (!asn1_is_utctime(&hdr) && !asn1_is_generalizedtime(&hdr)) ||
+	    x509_parse_time(hdr.payload, hdr.length, hdr.tag,
+			    &cert->not_after) < 0) {
+		wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notAfter "
+				  "Time", hdr.payload, hdr.length);
+		return -1;
+	}
+
+	wpa_printf(MSG_MSGDUMP, "X509: Validity: notBefore: %lu notAfter: %lu",
+		   (unsigned long) cert->not_before,
+		   (unsigned long) cert->not_after);
+
+	return 0;
+}
+
+
+static int x509_id_ce_oid(struct asn1_oid *oid)
+{
+	/* id-ce arc from X.509 for standard X.509v3 extensions */
+	return oid->len >= 4 &&
+		oid->oid[0] == 2 /* joint-iso-ccitt */ &&
+		oid->oid[1] == 5 /* ds */ &&
+		oid->oid[2] == 29 /* id-ce */;
+}
+
+
+static int x509_any_ext_key_usage_oid(struct asn1_oid *oid)
+{
+	return oid->len == 6 &&
+		x509_id_ce_oid(oid) &&
+		oid->oid[3] == 37 /* extKeyUsage */ &&
+		oid->oid[4] == 0 /* anyExtendedKeyUsage */;
+}
+
+
+static int x509_parse_ext_key_usage(struct x509_certificate *cert,
+				    const u8 *pos, size_t len)
+{
+	struct asn1_hdr hdr;
+
+	/*
+	 * KeyUsage ::= BIT STRING {
+	 *     digitalSignature        (0),
+	 *     nonRepudiation          (1),
+	 *     keyEncipherment         (2),
+	 *     dataEncipherment        (3),
+	 *     keyAgreement            (4),
+	 *     keyCertSign             (5),
+	 *     cRLSign                 (6),
+	 *     encipherOnly            (7),
+	 *     decipherOnly            (8) }
+	 */
+
+	if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_bitstring(&hdr) ||
+	    hdr.length < 1) {
+		asn1_unexpected(&hdr, "X509: Expected BIT STRING in KeyUsage");
+		return -1;
+	}
+
+	cert->extensions_present |= X509_EXT_KEY_USAGE;
+	cert->key_usage = asn1_bit_string_to_long(hdr.payload, hdr.length);
+
+	wpa_printf(MSG_DEBUG, "X509: KeyUsage 0x%lx", cert->key_usage);
+
+	return 0;
+}
+
+
+static int x509_parse_ext_basic_constraints(struct x509_certificate *cert,
+					    const u8 *pos, size_t len)
+{
+	struct asn1_hdr hdr;
+	unsigned long value;
+	size_t left;
+	const u8 *end_seq;
+
+	/*
+	 * BasicConstraints ::= SEQUENCE {
+	 * cA                      BOOLEAN DEFAULT FALSE,
+	 * pathLenConstraint       INTEGER (0..MAX) OPTIONAL }
+	 */
+
+	if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"X509: Expected SEQUENCE in BasicConstraints");
+		return -1;
+	}
+
+	cert->extensions_present |= X509_EXT_BASIC_CONSTRAINTS;
+
+	if (hdr.length == 0)
+		return 0;
+
+	end_seq = hdr.payload + hdr.length;
+	if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0) {
+		wpa_printf(MSG_DEBUG, "X509: Failed to parse "
+			   "BasicConstraints");
+		return -1;
+	}
+
+	if (asn1_is_boolean(&hdr)) {
+		cert->ca = hdr.payload[0];
+
+		pos = hdr.payload + hdr.length;
+		if (pos >= end_seq) {
+			/* No optional pathLenConstraint */
+			wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d",
+				   cert->ca);
+			return 0;
+		}
+		if (asn1_get_next(pos, end_seq - pos, &hdr) < 0) {
+			wpa_printf(MSG_DEBUG, "X509: Failed to parse "
+				   "BasicConstraints");
+			return -1;
+		}
+	}
+
+	if (!asn1_is_integer(&hdr)) {
+		asn1_unexpected(&hdr,
+				"X509: Expected INTEGER in BasicConstraints");
+		return -1;
+	}
+
+	pos = hdr.payload;
+	left = hdr.length;
+	value = 0;
+	while (left) {
+		value <<= 8;
+		value |= *pos++;
+		left--;
+	}
+
+	cert->path_len_constraint = value;
+	cert->extensions_present |= X509_EXT_PATH_LEN_CONSTRAINT;
+
+	wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d "
+		   "pathLenConstraint=%lu",
+		   cert->ca, cert->path_len_constraint);
+
+	return 0;
+}
+
+
+static int x509_parse_alt_name_rfc8222(struct x509_name *name,
+				       const u8 *pos, size_t len)
+{
+	/* rfc822Name IA5String */
+	wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - rfc822Name", pos, len);
+	os_free(name->alt_email);
+	name->alt_email = os_zalloc(len + 1);
+	if (name->alt_email == NULL)
+		return -1;
+	os_memcpy(name->alt_email, pos, len);
+	if (os_strlen(name->alt_email) != len) {
+		wpa_printf(MSG_INFO, "X509: Reject certificate with "
+			   "embedded NUL byte in rfc822Name (%s[NUL])",
+			   name->alt_email);
+		os_free(name->alt_email);
+		name->alt_email = NULL;
+		return -1;
+	}
+	return 0;
+}
+
+
+static int x509_parse_alt_name_dns(struct x509_name *name,
+				   const u8 *pos, size_t len)
+{
+	/* dNSName IA5String */
+	wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - dNSName", pos, len);
+	os_free(name->dns);
+	name->dns = os_zalloc(len + 1);
+	if (name->dns == NULL)
+		return -1;
+	os_memcpy(name->dns, pos, len);
+	if (os_strlen(name->dns) != len) {
+		wpa_printf(MSG_INFO, "X509: Reject certificate with "
+			   "embedded NUL byte in dNSName (%s[NUL])",
+			   name->dns);
+		os_free(name->dns);
+		name->dns = NULL;
+		return -1;
+	}
+	return 0;
+}
+
+
+static int x509_parse_alt_name_uri(struct x509_name *name,
+				   const u8 *pos, size_t len)
+{
+	/* uniformResourceIdentifier IA5String */
+	wpa_hexdump_ascii(MSG_MSGDUMP,
+			  "X509: altName - uniformResourceIdentifier",
+			  pos, len);
+	os_free(name->uri);
+	name->uri = os_zalloc(len + 1);
+	if (name->uri == NULL)
+		return -1;
+	os_memcpy(name->uri, pos, len);
+	if (os_strlen(name->uri) != len) {
+		wpa_printf(MSG_INFO, "X509: Reject certificate with "
+			   "embedded NUL byte in uniformResourceIdentifier "
+			   "(%s[NUL])", name->uri);
+		os_free(name->uri);
+		name->uri = NULL;
+		return -1;
+	}
+	return 0;
+}
+
+
+static int x509_parse_alt_name_ip(struct x509_name *name,
+				       const u8 *pos, size_t len)
+{
+	/* iPAddress OCTET STRING */
+	wpa_hexdump(MSG_MSGDUMP, "X509: altName - iPAddress", pos, len);
+	os_free(name->ip);
+	name->ip = os_memdup(pos, len);
+	if (name->ip == NULL)
+		return -1;
+	name->ip_len = len;
+	return 0;
+}
+
+
+static int x509_parse_alt_name_rid(struct x509_name *name,
+				   const u8 *pos, size_t len)
+{
+	char buf[80];
+
+	/* registeredID OBJECT IDENTIFIER */
+	if (asn1_parse_oid(pos, len, &name->rid) < 0)
+		return -1;
+
+	asn1_oid_to_str(&name->rid, buf, sizeof(buf));
+	wpa_printf(MSG_MSGDUMP, "X509: altName - registeredID: %s", buf);
+
+	return 0;
+}
+
+
+static int x509_parse_ext_alt_name(struct x509_name *name,
+				   const u8 *pos, size_t len)
+{
+	struct asn1_hdr hdr;
+	const u8 *p, *end;
+
+	/*
+	 * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
+	 *
+	 * GeneralName ::= CHOICE {
+	 *     otherName                       [0]     OtherName,
+	 *     rfc822Name                      [1]     IA5String,
+	 *     dNSName                         [2]     IA5String,
+	 *     x400Address                     [3]     ORAddress,
+	 *     directoryName                   [4]     Name,
+	 *     ediPartyName                    [5]     EDIPartyName,
+	 *     uniformResourceIdentifier       [6]     IA5String,
+	 *     iPAddress                       [7]     OCTET STRING,
+	 *     registeredID                    [8]     OBJECT IDENTIFIER }
+	 *
+	 * OtherName ::= SEQUENCE {
+	 *     type-id    OBJECT IDENTIFIER,
+	 *     value      [0] EXPLICIT ANY DEFINED BY type-id }
+	 *
+	 * EDIPartyName ::= SEQUENCE {
+	 *     nameAssigner            [0]     DirectoryString OPTIONAL,
+	 *     partyName               [1]     DirectoryString }
+	 */
+
+	for (p = pos, end = pos + len; p < end; p = hdr.payload + hdr.length) {
+		int res;
+
+		if (asn1_get_next(p, end - p, &hdr) < 0) {
+			wpa_printf(MSG_DEBUG, "X509: Failed to parse "
+				   "SubjectAltName item");
+			return -1;
+		}
+
+		if (hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC)
+			continue;
+
+		switch (hdr.tag) {
+		case 1:
+			res = x509_parse_alt_name_rfc8222(name, hdr.payload,
+							  hdr.length);
+			break;
+		case 2:
+			res = x509_parse_alt_name_dns(name, hdr.payload,
+						      hdr.length);
+			break;
+		case 6:
+			res = x509_parse_alt_name_uri(name, hdr.payload,
+						      hdr.length);
+			break;
+		case 7:
+			res = x509_parse_alt_name_ip(name, hdr.payload,
+						     hdr.length);
+			break;
+		case 8:
+			res = x509_parse_alt_name_rid(name, hdr.payload,
+						      hdr.length);
+			break;
+		case 0: /* TODO: otherName */
+		case 3: /* TODO: x500Address */
+		case 4: /* TODO: directoryName */
+		case 5: /* TODO: ediPartyName */
+		default:
+			res = 0;
+			break;
+		}
+		if (res < 0)
+			return res;
+	}
+
+	return 0;
+}
+
+
+static int x509_parse_ext_subject_alt_name(struct x509_certificate *cert,
+					   const u8 *pos, size_t len)
+{
+	struct asn1_hdr hdr;
+
+	/* SubjectAltName ::= GeneralNames */
+
+	if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"X509: Expected SEQUENCE in SubjectAltName");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "X509: SubjectAltName");
+	cert->extensions_present |= X509_EXT_SUBJECT_ALT_NAME;
+
+	if (hdr.length == 0)
+		return 0;
+
+	return x509_parse_ext_alt_name(&cert->subject, hdr.payload,
+				       hdr.length);
+}
+
+
+static int x509_parse_ext_issuer_alt_name(struct x509_certificate *cert,
+					  const u8 *pos, size_t len)
+{
+	struct asn1_hdr hdr;
+
+	/* IssuerAltName ::= GeneralNames */
+
+	if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"X509: Expected SEQUENCE in IssuerAltName");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "X509: IssuerAltName");
+	cert->extensions_present |= X509_EXT_ISSUER_ALT_NAME;
+
+	if (hdr.length == 0)
+		return 0;
+
+	return x509_parse_ext_alt_name(&cert->issuer, hdr.payload,
+				       hdr.length);
+}
+
+
+static int x509_id_cert_policy_any_oid(struct asn1_oid *oid)
+{
+	return oid->len == 5 &&
+		oid->oid[0] == 2 /* iso/itu-t */ &&
+		oid->oid[1] == 5 /* X.500 Directory Services */ &&
+		oid->oid[2] == 29 /* id-ce */ &&
+		oid->oid[3] == 32 /* id-ce-certificate-policies */ &&
+		oid->oid[4] == 0 /* anyPolicy */;
+}
+
+
+static int x509_id_wfa_oid(struct asn1_oid *oid)
+{
+	return oid->len >= 7 &&
+		oid->oid[0] == 1 /* iso */ &&
+		oid->oid[1] == 3 /* identified-organization */ &&
+		oid->oid[2] == 6 /* dod */ &&
+		oid->oid[3] == 1 /* internet */ &&
+		oid->oid[4] == 4 /* private */ &&
+		oid->oid[5] == 1 /* enterprise */ &&
+		oid->oid[6] == 40808 /* WFA */;
+}
+
+
+static int x509_id_wfa_tod_oid(struct asn1_oid *oid)
+{
+	return oid->len >= 9 &&
+		x509_id_wfa_oid(oid) &&
+		oid->oid[7] == 1 &&
+		oid->oid[8] == 3;
+}
+
+
+static int x509_id_wfa_tod_strict_oid(struct asn1_oid *oid)
+{
+	return oid->len == 10 &&
+		x509_id_wfa_tod_oid(oid) &&
+		oid->oid[9] == 1;
+}
+
+
+static int x509_id_wfa_tod_tofu_oid(struct asn1_oid *oid)
+{
+	return oid->len == 10 &&
+		x509_id_wfa_tod_oid(oid) &&
+		oid->oid[9] == 2;
+}
+
+
+static int x509_parse_ext_certificate_policies(struct x509_certificate *cert,
+					       const u8 *pos, size_t len)
+{
+	struct asn1_hdr hdr;
+	const u8 *end;
+
+	/*
+	 * certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
+	 *
+	 * PolicyInformation ::= SEQUENCE {
+	 *      policyIdentifier   CertPolicyId,
+	 *      policyQualifiers   SEQUENCE SIZE (1..MAX) OF
+	 *                              PolicyQualifierInfo OPTIONAL }
+	 *
+	 * CertPolicyId ::= OBJECT IDENTIFIER
+	 */
+
+	if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"X509: Expected SEQUENCE (certificatePolicies)");
+		return -1;
+	}
+	if (hdr.length > pos + len - hdr.payload)
+		return -1;
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	wpa_hexdump(MSG_MSGDUMP, "X509: certificatePolicies", pos, end - pos);
+
+	while (pos < end) {
+		const u8 *pol_end;
+		struct asn1_oid oid;
+		char buf[80];
+
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    !asn1_is_sequence(&hdr)) {
+			asn1_unexpected(&hdr,
+					"X509: Expected SEQUENCE (PolicyInformation)");
+			return -1;
+		}
+		if (hdr.length > end - hdr.payload)
+			return -1;
+		pos = hdr.payload;
+		pol_end = pos + hdr.length;
+		wpa_hexdump(MSG_MSGDUMP, "X509: PolicyInformation",
+			    pos, pol_end - pos);
+
+		if (asn1_get_oid(pos, pol_end - pos, &oid, &pos))
+			return -1;
+		if (x509_id_cert_policy_any_oid(&oid)) {
+			os_strlcpy(buf, "anyPolicy-STRICT", sizeof(buf));
+			cert->certificate_policy |=
+				X509_EXT_CERT_POLICY_ANY;
+		} else if (x509_id_wfa_tod_strict_oid(&oid)) {
+			os_strlcpy(buf, "TOD-STRICT", sizeof(buf));
+			cert->certificate_policy |=
+				X509_EXT_CERT_POLICY_TOD_STRICT;
+		} else if (x509_id_wfa_tod_tofu_oid(&oid)) {
+			os_strlcpy(buf, "TOD-TOFU", sizeof(buf));
+			cert->certificate_policy |=
+				X509_EXT_CERT_POLICY_TOD_TOFU;
+		} else {
+			asn1_oid_to_str(&oid, buf, sizeof(buf));
+		}
+		wpa_printf(MSG_DEBUG, "policyIdentifier: %s", buf);
+
+		pos = pol_end;
+	}
+
+	cert->extensions_present |= X509_EXT_CERTIFICATE_POLICY;
+
+	return 0;
+}
+
+
+static int x509_id_pkix_oid(struct asn1_oid *oid)
+{
+	return oid->len >= 7 &&
+		oid->oid[0] == 1 /* iso */ &&
+		oid->oid[1] == 3 /* identified-organization */ &&
+		oid->oid[2] == 6 /* dod */ &&
+		oid->oid[3] == 1 /* internet */ &&
+		oid->oid[4] == 5 /* security */ &&
+		oid->oid[5] == 5 /* mechanisms */ &&
+		oid->oid[6] == 7 /* id-pkix */;
+}
+
+
+static int x509_id_kp_oid(struct asn1_oid *oid)
+{
+	/* id-kp */
+	return oid->len >= 8 &&
+		x509_id_pkix_oid(oid) &&
+		oid->oid[7] == 3 /* id-kp */;
+}
+
+
+static int x509_id_kp_server_auth_oid(struct asn1_oid *oid)
+{
+	/* id-kp */
+	return oid->len == 9 &&
+		x509_id_kp_oid(oid) &&
+		oid->oid[8] == 1 /* id-kp-serverAuth */;
+}
+
+
+static int x509_id_kp_client_auth_oid(struct asn1_oid *oid)
+{
+	/* id-kp */
+	return oid->len == 9 &&
+		x509_id_kp_oid(oid) &&
+		oid->oid[8] == 2 /* id-kp-clientAuth */;
+}
+
+
+static int x509_id_kp_ocsp_oid(struct asn1_oid *oid)
+{
+	/* id-kp */
+	return oid->len == 9 &&
+		x509_id_kp_oid(oid) &&
+		oid->oid[8] == 9 /* id-kp-OCSPSigning */;
+}
+
+
+static int x509_parse_ext_ext_key_usage(struct x509_certificate *cert,
+					const u8 *pos, size_t len)
+{
+	struct asn1_hdr hdr;
+	const u8 *end;
+	struct asn1_oid oid;
+
+	/*
+	 * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
+	 *
+	 * KeyPurposeId ::= OBJECT IDENTIFIER
+	 */
+
+	if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"X509: Expected SEQUENCE (ExtKeyUsageSyntax)");
+		return -1;
+	}
+	if (hdr.length > pos + len - hdr.payload)
+		return -1;
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	wpa_hexdump(MSG_MSGDUMP, "X509: ExtKeyUsageSyntax", pos, end - pos);
+
+	while (pos < end) {
+		char buf[80];
+
+		if (asn1_get_oid(pos, end - pos, &oid, &pos))
+			return -1;
+		if (x509_any_ext_key_usage_oid(&oid)) {
+			os_strlcpy(buf, "anyExtendedKeyUsage", sizeof(buf));
+			cert->ext_key_usage |= X509_EXT_KEY_USAGE_ANY;
+		} else if (x509_id_kp_server_auth_oid(&oid)) {
+			os_strlcpy(buf, "id-kp-serverAuth", sizeof(buf));
+			cert->ext_key_usage |= X509_EXT_KEY_USAGE_SERVER_AUTH;
+		} else if (x509_id_kp_client_auth_oid(&oid)) {
+			os_strlcpy(buf, "id-kp-clientAuth", sizeof(buf));
+			cert->ext_key_usage |= X509_EXT_KEY_USAGE_CLIENT_AUTH;
+		} else if (x509_id_kp_ocsp_oid(&oid)) {
+			os_strlcpy(buf, "id-kp-OCSPSigning", sizeof(buf));
+			cert->ext_key_usage |= X509_EXT_KEY_USAGE_OCSP;
+		} else {
+			asn1_oid_to_str(&oid, buf, sizeof(buf));
+		}
+		wpa_printf(MSG_DEBUG, "ExtKeyUsage KeyPurposeId: %s", buf);
+	}
+
+	cert->extensions_present |= X509_EXT_EXT_KEY_USAGE;
+
+	return 0;
+}
+
+
+static int x509_parse_extension_data(struct x509_certificate *cert,
+				     struct asn1_oid *oid,
+				     const u8 *pos, size_t len)
+{
+	if (!x509_id_ce_oid(oid))
+		return 1;
+
+	/* TODO: add other extensions required by RFC 3280, Ch 4.2:
+	 * name constraints (section 4.2.1.11)
+	 * policy constraints (section 4.2.1.12)
+	 * inhibit any-policy (section 4.2.1.15)
+	 */
+	switch (oid->oid[3]) {
+	case 15: /* id-ce-keyUsage */
+		return x509_parse_ext_key_usage(cert, pos, len);
+	case 17: /* id-ce-subjectAltName */
+		return x509_parse_ext_subject_alt_name(cert, pos, len);
+	case 18: /* id-ce-issuerAltName */
+		return x509_parse_ext_issuer_alt_name(cert, pos, len);
+	case 19: /* id-ce-basicConstraints */
+		return x509_parse_ext_basic_constraints(cert, pos, len);
+	case 32: /* id-ce-certificatePolicies */
+		return x509_parse_ext_certificate_policies(cert, pos, len);
+	case 37: /* id-ce-extKeyUsage */
+		return x509_parse_ext_ext_key_usage(cert, pos, len);
+	default:
+		return 1;
+	}
+}
+
+
+static int x509_parse_extension(struct x509_certificate *cert,
+				const u8 *pos, size_t len, const u8 **next)
+{
+	const u8 *end;
+	struct asn1_hdr hdr;
+	struct asn1_oid oid;
+	int critical_ext = 0, res;
+	char buf[80];
+
+	/*
+	 * Extension  ::=  SEQUENCE  {
+	 *     extnID      OBJECT IDENTIFIER,
+	 *     critical    BOOLEAN DEFAULT FALSE,
+	 *     extnValue   OCTET STRING
+	 * }
+	 */
+
+	if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr, "X509: Expected SEQUENCE in Extensions");
+		return -1;
+	}
+	pos = hdr.payload;
+	*next = end = pos + hdr.length;
+
+	if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0) {
+		wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data for "
+			   "Extension (expected OID)");
+		return -1;
+	}
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    (!asn1_is_boolean(&hdr) && !asn1_is_octetstring(&hdr))) {
+		asn1_unexpected(&hdr,
+				"X509: Expected BOOLEAN or OCTETSTRING in Extensions");
+		return -1;
+	}
+
+	if (hdr.tag == ASN1_TAG_BOOLEAN) {
+		critical_ext = hdr.payload[0];
+		pos = hdr.payload;
+		/*
+		 * Number of CA certificates seem to be using Private class in
+		 * one of the X.509v3 extensions, so let's accept that instead
+		 * of rejecting the certificate. asn1_is_octetstring() covers
+		 * the more common case.
+		 */
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    (!asn1_is_octetstring(&hdr) &&
+		     !(hdr.class == ASN1_CLASS_PRIVATE &&
+		       hdr.tag == ASN1_TAG_OCTETSTRING))) {
+			asn1_unexpected(&hdr,
+					"X509: Expected OCTETSTRING in Extensions");
+			return -1;
+		}
+	}
+
+	asn1_oid_to_str(&oid, buf, sizeof(buf));
+	wpa_printf(MSG_DEBUG, "X509: Extension: extnID=%s critical=%d",
+		   buf, critical_ext);
+	wpa_hexdump(MSG_MSGDUMP, "X509: extnValue", hdr.payload, hdr.length);
+
+	res = x509_parse_extension_data(cert, &oid, hdr.payload, hdr.length);
+	if (res < 0)
+		return res;
+	if (res == 1 && critical_ext) {
+		wpa_printf(MSG_INFO, "X509: Unknown critical extension %s",
+			   buf);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int x509_parse_extensions(struct x509_certificate *cert,
+				 const u8 *pos, size_t len)
+{
+	const u8 *end;
+	struct asn1_hdr hdr;
+
+	/* Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension */
+
+	if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr, "X509: Expected SEQUENCE for Extensions");
+		return -1;
+	}
+
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	while (pos < end) {
+		if (x509_parse_extension(cert, pos, end - pos, &pos)
+		    < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+static int x509_parse_tbs_certificate(const u8 *buf, size_t len,
+				      struct x509_certificate *cert,
+				      const u8 **next)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+	size_t left;
+	char sbuf[128];
+	unsigned long value;
+	const u8 *subject_dn;
+
+	/* tbsCertificate TBSCertificate ::= SEQUENCE */
+	if (asn1_get_next(buf, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"X509: tbsCertificate did not start with a valid SEQUENCE");
+		return -1;
+	}
+	pos = hdr.payload;
+	end = *next = pos + hdr.length;
+
+	/*
+	 * version [0]  EXPLICIT Version DEFAULT v1
+	 * Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0)
+		return -1;
+	pos = hdr.payload;
+
+	if (asn1_is_cs_tag(&hdr, 0) && hdr.constructed) {
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    !asn1_is_integer(&hdr)) {
+			asn1_unexpected(&hdr,
+					"X509: No INTEGER tag found for version field");
+			return -1;
+		}
+		if (hdr.length != 1) {
+			wpa_printf(MSG_DEBUG, "X509: Unexpected version field "
+				   "length %u (expected 1)", hdr.length);
+			return -1;
+		}
+		pos = hdr.payload;
+		left = hdr.length;
+		value = 0;
+		while (left) {
+			value <<= 8;
+			value |= *pos++;
+			left--;
+		}
+
+		cert->version = value;
+		if (cert->version != X509_CERT_V1 &&
+		    cert->version != X509_CERT_V2 &&
+		    cert->version != X509_CERT_V3) {
+			wpa_printf(MSG_DEBUG, "X509: Unsupported version %d",
+				   cert->version + 1);
+			return -1;
+		}
+
+		if (asn1_get_next(pos, end - pos, &hdr) < 0)
+			return -1;
+	} else
+		cert->version = X509_CERT_V1;
+	wpa_printf(MSG_MSGDUMP, "X509: Version X.509v%d", cert->version + 1);
+
+	/* serialNumber CertificateSerialNumber ::= INTEGER */
+	if (!asn1_is_integer(&hdr) ||
+	    hdr.length < 1 || hdr.length > X509_MAX_SERIAL_NUM_LEN) {
+		asn1_unexpected(&hdr,
+				"X509: No INTEGER tag found for serialNumber");
+		return -1;
+	}
+
+	pos = hdr.payload + hdr.length;
+	while (hdr.length > 0 && hdr.payload[0] == 0) {
+		hdr.payload++;
+		hdr.length--;
+	}
+	os_memcpy(cert->serial_number, hdr.payload, hdr.length);
+	cert->serial_number_len = hdr.length;
+	wpa_hexdump(MSG_MSGDUMP, "X509: serialNumber", cert->serial_number,
+		    cert->serial_number_len);
+
+	/* signature AlgorithmIdentifier */
+	if (x509_parse_algorithm_identifier(pos, end - pos, &cert->signature,
+					    &pos))
+		return -1;
+
+	/* issuer Name */
+	if (x509_parse_name(pos, end - pos, &cert->issuer, &pos))
+		return -1;
+	x509_name_string(&cert->issuer, sbuf, sizeof(sbuf));
+	wpa_printf(MSG_MSGDUMP, "X509: issuer %s", sbuf);
+
+	/* validity Validity */
+	if (x509_parse_validity(pos, end - pos, cert, &pos))
+		return -1;
+
+	/* subject Name */
+	subject_dn = pos;
+	if (x509_parse_name(pos, end - pos, &cert->subject, &pos))
+		return -1;
+	cert->subject_dn = os_malloc(pos - subject_dn);
+	if (!cert->subject_dn)
+		return -1;
+	cert->subject_dn_len = pos - subject_dn;
+	os_memcpy(cert->subject_dn, subject_dn, cert->subject_dn_len);
+	x509_name_string(&cert->subject, sbuf, sizeof(sbuf));
+	wpa_printf(MSG_MSGDUMP, "X509: subject %s", sbuf);
+
+	/* subjectPublicKeyInfo SubjectPublicKeyInfo */
+	if (x509_parse_public_key(pos, end - pos, cert, &pos))
+		return -1;
+
+	if (pos == end)
+		return 0;
+
+	if (cert->version == X509_CERT_V1)
+		return 0;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
+		asn1_unexpected(&hdr,
+				"X509: Expected Context-Specific tag to parse optional tbsCertificate field(s)");
+		return -1;
+	}
+
+	if (hdr.tag == 1) {
+		/* issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL */
+		wpa_printf(MSG_DEBUG, "X509: issuerUniqueID");
+		/* TODO: parse UniqueIdentifier ::= BIT STRING */
+
+		pos = hdr.payload + hdr.length;
+		if (pos == end)
+			return 0;
+
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
+			asn1_unexpected(&hdr,
+					"X509: Expected Context-Specific tag to parse optional tbsCertificate field(s)");
+			return -1;
+		}
+	}
+
+	if (hdr.tag == 2) {
+		/* subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL */
+		wpa_printf(MSG_DEBUG, "X509: subjectUniqueID");
+		/* TODO: parse UniqueIdentifier ::= BIT STRING */
+
+		pos = hdr.payload + hdr.length;
+		if (pos == end)
+			return 0;
+
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
+			asn1_unexpected(&hdr,
+					"X509: Expected Context-Specific tag to parse optional tbsCertificate field(s)");
+			return -1;
+		}
+	}
+
+	if (hdr.tag != 3) {
+		wpa_printf(MSG_DEBUG,
+			   "X509: Ignored unexpected Context-Specific constructed %d tag %d in optional tbsCertificate fields",
+			   hdr.constructed, hdr.tag);
+		return 0;
+	}
+
+	/* extensions      [3]  EXPLICIT Extensions OPTIONAL */
+
+	if (cert->version != X509_CERT_V3) {
+		wpa_printf(MSG_DEBUG, "X509: X.509%d certificate and "
+			   "Extensions data which are only allowed for "
+			   "version 3", cert->version + 1);
+		return -1;
+	}
+
+	if (x509_parse_extensions(cert, hdr.payload, hdr.length) < 0)
+		return -1;
+
+	pos = hdr.payload + hdr.length;
+	if (pos < end) {
+		wpa_hexdump(MSG_DEBUG,
+			    "X509: Ignored extra tbsCertificate data",
+			    pos, end - pos);
+	}
+
+	return 0;
+}
+
+
+static int x509_rsadsi_oid(struct asn1_oid *oid)
+{
+	return oid->len >= 4 &&
+		oid->oid[0] == 1 /* iso */ &&
+		oid->oid[1] == 2 /* member-body */ &&
+		oid->oid[2] == 840 /* us */ &&
+		oid->oid[3] == 113549 /* rsadsi */;
+}
+
+
+static int x509_pkcs_oid(struct asn1_oid *oid)
+{
+	return oid->len >= 5 &&
+		x509_rsadsi_oid(oid) &&
+		oid->oid[4] == 1 /* pkcs */;
+}
+
+
+static int x509_digest_oid(struct asn1_oid *oid)
+{
+	return oid->len >= 5 &&
+		x509_rsadsi_oid(oid) &&
+		oid->oid[4] == 2 /* digestAlgorithm */;
+}
+
+
+int x509_sha1_oid(struct asn1_oid *oid)
+{
+	return oid->len == 6 &&
+		oid->oid[0] == 1 /* iso */ &&
+		oid->oid[1] == 3 /* identified-organization */ &&
+		oid->oid[2] == 14 /* oiw */ &&
+		oid->oid[3] == 3 /* secsig */ &&
+		oid->oid[4] == 2 /* algorithms */ &&
+		oid->oid[5] == 26 /* id-sha1 */;
+}
+
+
+static int x509_sha2_oid(struct asn1_oid *oid)
+{
+	return oid->len == 9 &&
+		oid->oid[0] == 2 /* joint-iso-itu-t */ &&
+		oid->oid[1] == 16 /* country */ &&
+		oid->oid[2] == 840 /* us */ &&
+		oid->oid[3] == 1 /* organization */ &&
+		oid->oid[4] == 101 /* gov */ &&
+		oid->oid[5] == 3 /* csor */ &&
+		oid->oid[6] == 4 /* nistAlgorithm */ &&
+		oid->oid[7] == 2 /* hashAlgs */;
+}
+
+
+int x509_sha256_oid(struct asn1_oid *oid)
+{
+	return x509_sha2_oid(oid) &&
+		oid->oid[8] == 1 /* sha256 */;
+}
+
+
+int x509_sha384_oid(struct asn1_oid *oid)
+{
+	return x509_sha2_oid(oid) &&
+		oid->oid[8] == 2 /* sha384 */;
+}
+
+
+int x509_sha512_oid(struct asn1_oid *oid)
+{
+	return x509_sha2_oid(oid) &&
+		oid->oid[8] == 3 /* sha512 */;
+}
+
+
+/**
+ * x509_certificate_parse - Parse a X.509 certificate in DER format
+ * @buf: Pointer to the X.509 certificate in DER format
+ * @len: Buffer length
+ * Returns: Pointer to the parsed certificate or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned certificate by calling
+ * x509_certificate_free().
+ */
+struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end, *hash_start;
+	struct x509_certificate *cert;
+
+	cert = os_zalloc(sizeof(*cert) + len);
+	if (cert == NULL)
+		return NULL;
+	os_memcpy(cert + 1, buf, len);
+	cert->cert_start = (u8 *) (cert + 1);
+	cert->cert_len = len;
+
+	pos = buf;
+	end = buf + len;
+
+	/* RFC 3280 - X.509 v3 certificate / ASN.1 DER */
+
+	/* Certificate ::= SEQUENCE */
+	if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"X509: Certificate did not start with a valid SEQUENCE");
+		x509_certificate_free(cert);
+		return NULL;
+	}
+	pos = hdr.payload;
+
+	if (hdr.length > end - pos) {
+		x509_certificate_free(cert);
+		return NULL;
+	}
+
+	if (hdr.length < end - pos) {
+		wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER "
+			    "encoded certificate",
+			    pos + hdr.length, end - (pos + hdr.length));
+		end = pos + hdr.length;
+	}
+
+	hash_start = pos;
+	cert->tbs_cert_start = cert->cert_start + (hash_start - buf);
+	if (x509_parse_tbs_certificate(pos, end - pos, cert, &pos)) {
+		x509_certificate_free(cert);
+		return NULL;
+	}
+	cert->tbs_cert_len = pos - hash_start;
+
+	/* signatureAlgorithm AlgorithmIdentifier */
+	if (x509_parse_algorithm_identifier(pos, end - pos,
+					    &cert->signature_alg, &pos)) {
+		x509_certificate_free(cert);
+		return NULL;
+	}
+
+	/* signatureValue BIT STRING */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_bitstring(&hdr)) {
+		asn1_unexpected(&hdr,
+				"X509: Expected BITSTRING (signatureValue)");
+		x509_certificate_free(cert);
+		return NULL;
+	}
+	if (hdr.length < 1) {
+		x509_certificate_free(cert);
+		return NULL;
+	}
+	pos = hdr.payload;
+	if (*pos) {
+		wpa_printf(MSG_DEBUG,
+			   "X509: BITSTRING (signatureValue) - %d unused bits",
+			   *pos);
+		/* PKCS #1 v1.5 10.2.1:
+		 * It is an error if the length in bits of the signature S is
+		 * not a multiple of eight.
+		 */
+		x509_certificate_free(cert);
+		return NULL;
+	}
+	os_free(cert->sign_value);
+	cert->sign_value = os_memdup(pos + 1, hdr.length - 1);
+	if (cert->sign_value == NULL) {
+		wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for "
+			   "signatureValue");
+		x509_certificate_free(cert);
+		return NULL;
+	}
+	cert->sign_value_len = hdr.length - 1;
+	wpa_hexdump(MSG_MSGDUMP, "X509: signature",
+		    cert->sign_value, cert->sign_value_len);
+
+	return cert;
+}
+
+
+/**
+ * x509_certificate_check_signature - Verify certificate signature
+ * @issuer: Issuer certificate
+ * @cert: Certificate to be verified
+ * Returns: 0 if cert has a valid signature that was signed by the issuer,
+ * -1 if not
+ */
+int x509_certificate_check_signature(struct x509_certificate *issuer,
+				     struct x509_certificate *cert)
+{
+	return x509_check_signature(issuer, &cert->signature,
+				    cert->sign_value, cert->sign_value_len,
+				    cert->tbs_cert_start, cert->tbs_cert_len);
+}
+
+
+int x509_check_signature(struct x509_certificate *issuer,
+			 struct x509_algorithm_identifier *signature,
+			 const u8 *sign_value, size_t sign_value_len,
+			 const u8 *signed_data, size_t signed_data_len)
+{
+	struct crypto_public_key *pk;
+	u8 *data;
+	const u8 *pos, *end, *next, *da_end;
+	size_t data_len;
+	struct asn1_hdr hdr;
+	struct asn1_oid oid;
+	u8 hash[64];
+	size_t hash_len;
+	const u8 *addr[1] = { signed_data };
+	size_t len[1] = { signed_data_len };
+
+	if (!x509_pkcs_oid(&signature->oid) ||
+	    signature->oid.len != 7 ||
+	    signature->oid.oid[5] != 1 /* pkcs-1 */) {
+		wpa_printf(MSG_DEBUG, "X509: Unrecognized signature "
+			   "algorithm");
+		return -1;
+	}
+
+	pk = crypto_public_key_import(issuer->public_key,
+				      issuer->public_key_len);
+	if (pk == NULL)
+		return -1;
+
+	data_len = sign_value_len;
+	data = os_malloc(data_len);
+	if (data == NULL) {
+		crypto_public_key_free(pk);
+		return -1;
+	}
+
+	if (crypto_public_key_decrypt_pkcs1(pk, sign_value,
+					    sign_value_len, data,
+					    &data_len) < 0) {
+		wpa_printf(MSG_DEBUG, "X509: Failed to decrypt signature");
+		crypto_public_key_free(pk);
+		os_free(data);
+		return -1;
+	}
+	crypto_public_key_free(pk);
+
+	wpa_hexdump(MSG_MSGDUMP, "X509: Signature data D", data, data_len);
+
+	/*
+	 * PKCS #1 v1.5, 10.1.2:
+	 *
+	 * DigestInfo ::= SEQUENCE {
+	 *     digestAlgorithm DigestAlgorithmIdentifier,
+	 *     digest Digest
+	 * }
+	 *
+	 * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
+	 *
+	 * Digest ::= OCTET STRING
+	 *
+	 */
+	if (asn1_get_next(data, data_len, &hdr) < 0 ||
+	    !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr, "X509: Expected SEQUENCE (DigestInfo)");
+		os_free(data);
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "X509: DigestInfo", hdr.payload, hdr.length);
+
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	/*
+	 * X.509:
+	 * AlgorithmIdentifier ::= SEQUENCE {
+	 *     algorithm            OBJECT IDENTIFIER,
+	 *     parameters           ANY DEFINED BY algorithm OPTIONAL
+	 * }
+	 */
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_sequence(&hdr)) {
+		asn1_unexpected(&hdr,
+				"X509: Expected SEQUENCE (AlgorithmIdentifier)");
+		os_free(data);
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "X509: DigestAlgorithmIdentifier",
+		    hdr.payload, hdr.length);
+	da_end = hdr.payload + hdr.length;
+
+	if (asn1_get_oid(hdr.payload, hdr.length, &oid, &next)) {
+		wpa_printf(MSG_DEBUG, "X509: Failed to parse digestAlgorithm");
+		os_free(data);
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "X509: Digest algorithm parameters",
+		    next, da_end - next);
+
+	/*
+	 * RFC 5754: The correct encoding for the SHA2 algorithms would be to
+	 * omit the parameters, but there are implementation that encode these
+	 * as a NULL element. Allow these two cases and reject anything else.
+	 */
+	if (da_end > next &&
+	    (asn1_get_next(next, da_end - next, &hdr) < 0 ||
+	     !asn1_is_null(&hdr) ||
+	     hdr.payload + hdr.length != da_end)) {
+		wpa_printf(MSG_DEBUG,
+			   "X509: Unexpected digest algorithm parameters");
+		os_free(data);
+		return -1;
+	}
+
+	if (x509_sha1_oid(&oid)) {
+		if (signature->oid.oid[6] != 5 /* sha-1WithRSAEncryption */) {
+			wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA1 "
+				   "does not match with certificate "
+				   "signatureAlgorithm (%lu)",
+				   signature->oid.oid[6]);
+			os_free(data);
+			return -1;
+		}
+		goto skip_digest_oid;
+	}
+
+	if (x509_sha256_oid(&oid)) {
+		if (signature->oid.oid[6] !=
+		    11 /* sha2561WithRSAEncryption */) {
+			wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA256 "
+				   "does not match with certificate "
+				   "signatureAlgorithm (%lu)",
+				   signature->oid.oid[6]);
+			os_free(data);
+			return -1;
+		}
+		goto skip_digest_oid;
+	}
+
+	if (x509_sha384_oid(&oid)) {
+		if (signature->oid.oid[6] != 12 /* sha384WithRSAEncryption */) {
+			wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA384 "
+				   "does not match with certificate "
+				   "signatureAlgorithm (%lu)",
+				   signature->oid.oid[6]);
+			os_free(data);
+			return -1;
+		}
+		goto skip_digest_oid;
+	}
+
+	if (x509_sha512_oid(&oid)) {
+		if (signature->oid.oid[6] != 13 /* sha512WithRSAEncryption */) {
+			wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA512 "
+				   "does not match with certificate "
+				   "signatureAlgorithm (%lu)",
+				   signature->oid.oid[6]);
+			os_free(data);
+			return -1;
+		}
+		goto skip_digest_oid;
+	}
+
+	if (!x509_digest_oid(&oid)) {
+		wpa_printf(MSG_DEBUG, "X509: Unrecognized digestAlgorithm");
+		os_free(data);
+		return -1;
+	}
+	switch (oid.oid[5]) {
+	case 5: /* md5 */
+		if (signature->oid.oid[6] != 4 /* md5WithRSAEncryption */) {
+			wpa_printf(MSG_DEBUG, "X509: digestAlgorithm MD5 does "
+				   "not match with certificate "
+				   "signatureAlgorithm (%lu)",
+				   signature->oid.oid[6]);
+			os_free(data);
+			return -1;
+		}
+		break;
+	case 2: /* md2 */
+	case 4: /* md4 */
+	default:
+		wpa_printf(MSG_DEBUG, "X509: Unsupported digestAlgorithm "
+			   "(%lu)", oid.oid[5]);
+		os_free(data);
+		return -1;
+	}
+
+skip_digest_oid:
+	/* Digest ::= OCTET STRING */
+	pos = da_end;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    !asn1_is_octetstring(&hdr)) {
+		asn1_unexpected(&hdr, "X509: Expected OCTETSTRING (Digest)");
+		os_free(data);
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "X509: Decrypted Digest",
+		    hdr.payload, hdr.length);
+
+	switch (signature->oid.oid[6]) {
+	case 4: /* md5WithRSAEncryption */
+		md5_vector(1, addr, len, hash);
+		hash_len = 16;
+		wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (MD5)",
+			    hash, hash_len);
+		break;
+	case 5: /* sha-1WithRSAEncryption */
+		sha1_vector(1, addr, len, hash);
+		hash_len = 20;
+		wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA1)",
+			    hash, hash_len);
+		break;
+	case 11: /* sha256WithRSAEncryption */
+		sha256_vector(1, addr, len, hash);
+		hash_len = 32;
+		wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA256)",
+			    hash, hash_len);
+		break;
+	case 12: /* sha384WithRSAEncryption */
+		sha384_vector(1, addr, len, hash);
+		hash_len = 48;
+		wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA384)",
+			    hash, hash_len);
+		break;
+	case 13: /* sha512WithRSAEncryption */
+		sha512_vector(1, addr, len, hash);
+		hash_len = 64;
+		wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA512)",
+			    hash, hash_len);
+		break;
+	case 2: /* md2WithRSAEncryption */
+	default:
+		wpa_printf(MSG_INFO, "X509: Unsupported certificate signature "
+			   "algorithm (%lu)", signature->oid.oid[6]);
+		os_free(data);
+		return -1;
+	}
+
+	if (hdr.length != hash_len ||
+	    os_memcmp_const(hdr.payload, hash, hdr.length) != 0) {
+		wpa_printf(MSG_INFO, "X509: Certificate Digest does not match "
+			   "with calculated tbsCertificate hash");
+		os_free(data);
+		return -1;
+	}
+
+	if (hdr.payload + hdr.length < data + data_len) {
+		wpa_hexdump(MSG_INFO,
+			    "X509: Extra data after certificate signature hash",
+			    hdr.payload + hdr.length,
+			    data + data_len - hdr.payload - hdr.length);
+		os_free(data);
+		return -1;
+	}
+
+	os_free(data);
+
+	wpa_printf(MSG_DEBUG, "X509: Certificate Digest matches with "
+		   "calculated tbsCertificate hash");
+
+	return 0;
+}
+
+
+static int x509_valid_issuer(const struct x509_certificate *cert)
+{
+	if ((cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS) &&
+	    !cert->ca) {
+		wpa_printf(MSG_DEBUG, "X509: Non-CA certificate used as an "
+			   "issuer");
+		return -1;
+	}
+
+	if (cert->version == X509_CERT_V3 &&
+	    !(cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS)) {
+		wpa_printf(MSG_DEBUG, "X509: v3 CA certificate did not "
+			   "include BasicConstraints extension");
+		return -1;
+	}
+
+	if ((cert->extensions_present & X509_EXT_KEY_USAGE) &&
+	    !(cert->key_usage & X509_KEY_USAGE_KEY_CERT_SIGN)) {
+		wpa_printf(MSG_DEBUG, "X509: Issuer certificate did not have "
+			   "keyCertSign bit in Key Usage");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * x509_certificate_chain_validate - Validate X.509 certificate chain
+ * @trusted: List of trusted certificates
+ * @chain: Certificate chain to be validated (first chain must be issued by
+ * signed by the second certificate in the chain and so on)
+ * @reason: Buffer for returning failure reason (X509_VALIDATE_*)
+ * Returns: 0 if chain is valid, -1 if not
+ */
+int x509_certificate_chain_validate(struct x509_certificate *trusted,
+				    struct x509_certificate *chain,
+				    int *reason, int disable_time_checks)
+{
+	long unsigned idx;
+	int chain_trusted = 0;
+	struct x509_certificate *cert, *trust;
+	char buf[128];
+	struct os_time now;
+
+	*reason = X509_VALIDATE_OK;
+
+	wpa_printf(MSG_DEBUG, "X509: Validate certificate chain");
+	os_get_time(&now);
+
+	for (cert = chain, idx = 0; cert; cert = cert->next, idx++) {
+		cert->issuer_trusted = 0;
+		x509_name_string(&cert->subject, buf, sizeof(buf));
+		wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf);
+
+		if (chain_trusted)
+			continue;
+
+		if (!disable_time_checks &&
+		    ((unsigned long) now.sec <
+		     (unsigned long) cert->not_before ||
+		     (unsigned long) now.sec >
+		     (unsigned long) cert->not_after)) {
+			wpa_printf(MSG_INFO, "X509: Certificate not valid "
+				   "(now=%lu not_before=%lu not_after=%lu)",
+				   now.sec, cert->not_before, cert->not_after);
+			*reason = X509_VALIDATE_CERTIFICATE_EXPIRED;
+			return -1;
+		}
+
+		if (cert->next) {
+			if (x509_name_compare(&cert->issuer,
+					      &cert->next->subject) != 0) {
+				wpa_printf(MSG_DEBUG, "X509: Certificate "
+					   "chain issuer name mismatch");
+				x509_name_string(&cert->issuer, buf,
+						 sizeof(buf));
+				wpa_printf(MSG_DEBUG, "X509: cert issuer: %s",
+					   buf);
+				x509_name_string(&cert->next->subject, buf,
+						 sizeof(buf));
+				wpa_printf(MSG_DEBUG, "X509: next cert "
+					   "subject: %s", buf);
+				*reason = X509_VALIDATE_CERTIFICATE_UNKNOWN;
+				return -1;
+			}
+
+			if (x509_valid_issuer(cert->next) < 0) {
+				*reason = X509_VALIDATE_BAD_CERTIFICATE;
+				return -1;
+			}
+
+			if ((cert->next->extensions_present &
+			     X509_EXT_PATH_LEN_CONSTRAINT) &&
+			    idx > cert->next->path_len_constraint) {
+				wpa_printf(MSG_DEBUG, "X509: pathLenConstraint"
+					   " not met (idx=%lu issuer "
+					   "pathLenConstraint=%lu)", idx,
+					   cert->next->path_len_constraint);
+				*reason = X509_VALIDATE_BAD_CERTIFICATE;
+				return -1;
+			}
+
+			if (x509_certificate_check_signature(cert->next, cert)
+			    < 0) {
+				wpa_printf(MSG_DEBUG, "X509: Invalid "
+					   "certificate signature within "
+					   "chain");
+				*reason = X509_VALIDATE_BAD_CERTIFICATE;
+				return -1;
+			}
+		}
+
+		for (trust = trusted; trust; trust = trust->next) {
+			if (x509_name_compare(&cert->issuer, &trust->subject)
+			    == 0)
+				break;
+		}
+
+		if (trust) {
+			wpa_printf(MSG_DEBUG, "X509: Found issuer from the "
+				   "list of trusted certificates");
+			if (x509_valid_issuer(trust) < 0) {
+				*reason = X509_VALIDATE_BAD_CERTIFICATE;
+				return -1;
+			}
+
+			if (x509_certificate_check_signature(trust, cert) < 0)
+			{
+				wpa_printf(MSG_DEBUG, "X509: Invalid "
+					   "certificate signature");
+				*reason = X509_VALIDATE_BAD_CERTIFICATE;
+				return -1;
+			}
+
+			wpa_printf(MSG_DEBUG, "X509: Trusted certificate "
+				   "found to complete the chain");
+			cert->issuer_trusted = 1;
+			chain_trusted = 1;
+		}
+	}
+
+	if (!chain_trusted) {
+		wpa_printf(MSG_DEBUG, "X509: Did not find any of the issuers "
+			   "from the list of trusted certificates");
+		if (trusted) {
+			*reason = X509_VALIDATE_UNKNOWN_CA;
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG, "X509: Certificate chain validation "
+			   "disabled - ignore unknown CA issue");
+	}
+
+	wpa_printf(MSG_DEBUG, "X509: Certificate chain valid");
+
+	return 0;
+}
+
+
+/**
+ * x509_certificate_get_subject - Get a certificate based on Subject name
+ * @chain: Certificate chain to search through
+ * @name: Subject name to search for
+ * Returns: Pointer to the certificate with the given Subject name or
+ * %NULL on failure
+ */
+struct x509_certificate *
+x509_certificate_get_subject(struct x509_certificate *chain,
+			     struct x509_name *name)
+{
+	struct x509_certificate *cert;
+
+	for (cert = chain; cert; cert = cert->next) {
+		if (x509_name_compare(&cert->subject, name) == 0)
+			return cert;
+	}
+	return NULL;
+}
+
+
+/**
+ * x509_certificate_self_signed - Is the certificate self-signed?
+ * @cert: Certificate
+ * Returns: 1 if certificate is self-signed, 0 if not
+ */
+int x509_certificate_self_signed(struct x509_certificate *cert)
+{
+	return x509_name_compare(&cert->issuer, &cert->subject) == 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/x509v3.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/x509v3.h
new file mode 100644
index 0000000..e3b108f
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/tls/x509v3.h
@@ -0,0 +1,164 @@
+/*
+ * X.509v3 certificate parsing and processing
+ * 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.
+ */
+
+#ifndef X509V3_H
+#define X509V3_H
+
+#include "asn1.h"
+
+struct x509_algorithm_identifier {
+	struct asn1_oid oid;
+};
+
+struct x509_name_attr {
+	enum x509_name_attr_type {
+		X509_NAME_ATTR_NOT_USED,
+		X509_NAME_ATTR_DC,
+		X509_NAME_ATTR_CN,
+		X509_NAME_ATTR_C,
+		X509_NAME_ATTR_L,
+		X509_NAME_ATTR_ST,
+		X509_NAME_ATTR_O,
+		X509_NAME_ATTR_OU
+	} type;
+	char *value;
+};
+
+#define X509_MAX_NAME_ATTRIBUTES 20
+
+struct x509_name {
+	struct x509_name_attr attr[X509_MAX_NAME_ATTRIBUTES];
+	size_t num_attr;
+	char *email; /* emailAddress */
+
+	/* from alternative name extension */
+	char *alt_email; /* rfc822Name */
+	char *dns; /* dNSName */
+	char *uri; /* uniformResourceIdentifier */
+	u8 *ip; /* iPAddress */
+	size_t ip_len; /* IPv4: 4, IPv6: 16 */
+	struct asn1_oid rid; /* registeredID */
+};
+
+#define X509_MAX_SERIAL_NUM_LEN 20
+
+struct x509_certificate {
+	struct x509_certificate *next;
+	enum { X509_CERT_V1 = 0, X509_CERT_V2 = 1, X509_CERT_V3 = 2 } version;
+	u8 serial_number[X509_MAX_SERIAL_NUM_LEN];
+	size_t serial_number_len;
+	struct x509_algorithm_identifier signature;
+	struct x509_name issuer;
+	struct x509_name subject;
+	u8 *subject_dn;
+	size_t subject_dn_len;
+	os_time_t not_before;
+	os_time_t not_after;
+	struct x509_algorithm_identifier public_key_alg;
+	u8 *public_key;
+	size_t public_key_len;
+	struct x509_algorithm_identifier signature_alg;
+	u8 *sign_value;
+	size_t sign_value_len;
+
+	/* Extensions */
+	unsigned int extensions_present;
+#define X509_EXT_BASIC_CONSTRAINTS		(1 << 0)
+#define X509_EXT_PATH_LEN_CONSTRAINT		(1 << 1)
+#define X509_EXT_KEY_USAGE			(1 << 2)
+#define X509_EXT_SUBJECT_ALT_NAME		(1 << 3)
+#define X509_EXT_ISSUER_ALT_NAME		(1 << 4)
+#define X509_EXT_EXT_KEY_USAGE			(1 << 5)
+#define X509_EXT_CERTIFICATE_POLICY		(1 << 6)
+
+	/* BasicConstraints */
+	int ca; /* cA */
+	unsigned long path_len_constraint; /* pathLenConstraint */
+
+	/* KeyUsage */
+	unsigned long key_usage;
+#define X509_KEY_USAGE_DIGITAL_SIGNATURE	(1 << 0)
+#define X509_KEY_USAGE_NON_REPUDIATION		(1 << 1)
+#define X509_KEY_USAGE_KEY_ENCIPHERMENT		(1 << 2)
+#define X509_KEY_USAGE_DATA_ENCIPHERMENT	(1 << 3)
+#define X509_KEY_USAGE_KEY_AGREEMENT		(1 << 4)
+#define X509_KEY_USAGE_KEY_CERT_SIGN		(1 << 5)
+#define X509_KEY_USAGE_CRL_SIGN			(1 << 6)
+#define X509_KEY_USAGE_ENCIPHER_ONLY		(1 << 7)
+#define X509_KEY_USAGE_DECIPHER_ONLY		(1 << 8)
+
+	/* ExtKeyUsage */
+	unsigned long ext_key_usage;
+#define X509_EXT_KEY_USAGE_ANY			(1 << 0)
+#define X509_EXT_KEY_USAGE_SERVER_AUTH		(1 << 1)
+#define X509_EXT_KEY_USAGE_CLIENT_AUTH		(1 << 2)
+#define X509_EXT_KEY_USAGE_OCSP			(1 << 3)
+
+	/* CertificatePolicy */
+	unsigned long certificate_policy;
+#define X509_EXT_CERT_POLICY_ANY		(1 << 0)
+#define X509_EXT_CERT_POLICY_TOD_STRICT		(1 << 1)
+#define X509_EXT_CERT_POLICY_TOD_TOFU		(1 << 2)
+
+	/*
+	 * The DER format certificate follows struct x509_certificate. These
+	 * pointers point to that buffer.
+	 */
+	const u8 *cert_start;
+	size_t cert_len;
+	const u8 *tbs_cert_start;
+	size_t tbs_cert_len;
+
+	/* Meta data used for certificate validation */
+	unsigned int ocsp_good:1;
+	unsigned int ocsp_revoked:1;
+	unsigned int issuer_trusted:1;
+};
+
+enum {
+	X509_VALIDATE_OK,
+	X509_VALIDATE_BAD_CERTIFICATE,
+	X509_VALIDATE_UNSUPPORTED_CERTIFICATE,
+	X509_VALIDATE_CERTIFICATE_REVOKED,
+	X509_VALIDATE_CERTIFICATE_EXPIRED,
+	X509_VALIDATE_CERTIFICATE_UNKNOWN,
+	X509_VALIDATE_UNKNOWN_CA
+};
+
+void x509_certificate_free(struct x509_certificate *cert);
+int x509_parse_algorithm_identifier(const u8 *buf, size_t len,
+				    struct x509_algorithm_identifier *id,
+				    const u8 **next);
+int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name,
+		    const u8 **next);
+int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, os_time_t *val);
+struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len);
+void x509_free_name(struct x509_name *name);
+void x509_name_string(struct x509_name *name, char *buf, size_t len);
+int x509_name_compare(struct x509_name *a, struct x509_name *b);
+void x509_certificate_chain_free(struct x509_certificate *cert);
+int x509_check_signature(struct x509_certificate *issuer,
+			 struct x509_algorithm_identifier *signature,
+			 const u8 *sign_value, size_t sign_value_len,
+			 const u8 *signed_data, size_t signed_data_len);
+int x509_certificate_check_signature(struct x509_certificate *issuer,
+				     struct x509_certificate *cert);
+int x509_certificate_chain_validate(struct x509_certificate *trusted,
+				    struct x509_certificate *chain,
+				    int *reason, int disable_time_checks);
+struct x509_certificate *
+x509_certificate_get_subject(struct x509_certificate *chain,
+			     struct x509_name *name);
+int x509_certificate_self_signed(struct x509_certificate *cert);
+
+int x509_sha1_oid(struct asn1_oid *oid);
+int x509_sha256_oid(struct asn1_oid *oid);
+int x509_sha384_oid(struct asn1_oid *oid);
+int x509_sha512_oid(struct asn1_oid *oid);
+
+#endif /* X509V3_H */