Explorar el Código

wpa_supplicant: Adding SAE modules with testcase

This change ports SAE(Simultaneous Authentication of Equals)
feature from wpa_supplicant and makes it work with mbedtls
crypto APIs. Currently only group 19 is supported. A sample
SAE handshake is included in the testcase. Other minor
changes for DH groups are also included.
Sagar Bijwe hace 6 años
padre
commit
aceb141d2b

+ 2 - 2
components/esp_wifi/include/esp_wifi_crypto_types.h

@@ -116,7 +116,7 @@ typedef int (*esp_aes_unwrap_t)(const unsigned char *kek, int n, const unsigned
   * @param mac  Buffer for the hash (32 bytes).
   *
   */
-typedef void (*esp_hmac_sha256_vector_t)(const unsigned char *key, int key_len, int num_elem,
+typedef int (*esp_hmac_sha256_vector_t)(const unsigned char *key, int key_len, int num_elem,
 			                   const unsigned char *addr[], const int *len, unsigned char *mac);
 
 /**
@@ -131,7 +131,7 @@ typedef void (*esp_hmac_sha256_vector_t)(const unsigned char *key, int key_len,
   * @param buf_len  Number of bytes of key to generate.
   *
   */
-typedef void (*esp_sha256_prf_t)(const unsigned char *key, int key_len, const char *label,
+typedef int (*esp_sha256_prf_t)(const unsigned char *key, int key_len, const char *label,
 	                           const unsigned char *data, int data_len, unsigned char *buf, int buf_len);
 
 /**

+ 2 - 0
components/wpa_supplicant/CMakeLists.txt

@@ -3,6 +3,7 @@ set(srcs "port/os_xtensa.c"
     "src/ap/ieee802_1x.c"
     "src/ap/wpa_auth.c"
     "src/ap/wpa_auth_ie.c"
+    "src/common/sae.c"
     "src/common/wpa_common.c"
     "src/crypto/aes-cbc.c"
     "src/crypto/aes-ccm.c"
@@ -107,4 +108,5 @@ target_compile_definitions(${COMPONENT_LIB} PRIVATE
     ESP32_WORKAROUND
     CONFIG_ECC
     CONFIG_IEEE80211W
+    CONFIG_WPA3_SAE
     )

+ 18 - 0
components/wpa_supplicant/src/common/ieee802_11_defs.h

@@ -141,6 +141,24 @@
 #define WLAN_STATUS_INVALID_PMKID 53
 #define WLAN_STATUS_INVALID_MDIE 54
 #define WLAN_STATUS_INVALID_FTIE 55
+#define WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED 59
+#define WLAN_STATUS_NO_OUTSTANDING_GAS_REQ 60
+#define WLAN_STATUS_GAS_RESP_NOT_RECEIVED 61
+#define WLAN_STATUS_STA_TIMED_OUT_WAITING_FOR_GAS_RESP 62
+#define WLAN_STATUS_GAS_RESP_LARGER_THAN_LIMIT 63
+#define WLAN_STATUS_REQ_REFUSED_HOME 64
+#define WLAN_STATUS_ADV_SRV_UNREACHABLE 65
+#define WLAN_STATUS_REQ_REFUSED_SSPN 67
+#define WLAN_STATUS_REQ_REFUSED_UNAUTH_ACCESS 68
+#define WLAN_STATUS_INVALID_RSNIE 72
+#define WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ 76
+#define WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED 77
+#define WLAN_STATUS_TRANSMISSION_FAILURE 79
+#define WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION 82
+#define WLAN_STATUS_PENDING_ADMITTING_FST_SESSION 86
+#define WLAN_STATUS_QUERY_RESP_OUTSTANDING 95
+#define WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL 99
+#define WLAN_STATUS_ASSOC_DENIED_NO_VHT 104
 
 /* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */
 #define WLAN_REASON_UNSPECIFIED 1

+ 1280 - 0
components/wpa_supplicant/src/common/sae.c

@@ -0,0 +1,1280 @@
+/*
+ * Simultaneous authentication of equals
+ * Copyright (c) 2012-2016, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifdef CONFIG_WPA3_SAE
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "crypto/dh_groups.h"
+#include "ieee802_11_defs.h"
+#include "sae.h"
+#include "esp_wifi_crypto_types.h"
+
+/*TBD Move the this api to proper files once they are taken out of lib*/
+void wpabuf_clear_free(struct wpabuf *buf)
+{
+    if (buf) {
+        os_memset(wpabuf_mhead(buf), 0, wpabuf_len(buf));
+        wpabuf_free(buf);
+    }
+}
+
+void bin_clear_free(void *bin, size_t len)
+{
+    if (bin) {
+        os_memset(bin, 0, len);
+        os_free(bin);
+    }
+}
+
+int sae_set_group(struct sae_data *sae, u16 group)
+{
+    struct sae_temporary_data *tmp;
+
+    sae_clear_data(sae);
+    tmp = sae->tmp = os_zalloc(sizeof(*tmp));
+    if (tmp == NULL)
+        return -1;
+
+    /* First, check if this is an ECC group */
+	tmp->ec = crypto_ec_init(group);
+	if (tmp->ec) {
+		sae->group = group;
+		tmp->prime_len = crypto_ec_prime_len(tmp->ec);
+		tmp->prime = crypto_ec_get_prime(tmp->ec);
+		tmp->order = crypto_ec_get_order(tmp->ec);
+		return 0;
+	}
+
+	/* Not an ECC group, check FFC */
+	tmp->dh = dh_groups_get(group);
+	if (tmp->dh) {
+		sae->group = group;
+		tmp->prime_len = tmp->dh->prime_len;
+		if (tmp->prime_len > SAE_MAX_PRIME_LEN) {
+			sae_clear_data(sae);
+			return -1;
+		}
+
+		tmp->prime_buf = crypto_bignum_init_set(tmp->dh->prime,
+							tmp->prime_len);
+		if (tmp->prime_buf == NULL) {
+			sae_clear_data(sae);
+			return -1;
+		}
+		tmp->prime = tmp->prime_buf;
+
+		tmp->order_buf = crypto_bignum_init_set(tmp->dh->order,
+							tmp->dh->order_len);
+		if (tmp->order_buf == NULL) {
+			sae_clear_data(sae);
+			return -1;
+		}
+		tmp->order = tmp->order_buf;
+
+		return 0;
+	}
+
+	/* Unsupported group */
+	return -1;
+}
+
+void sae_clear_temp_data(struct sae_data *sae)
+{
+	struct sae_temporary_data *tmp;
+	if (sae == NULL || sae->tmp == NULL)
+		return;
+	tmp = sae->tmp;
+	crypto_ec_deinit(tmp->ec);
+	crypto_bignum_deinit(tmp->prime_buf, 0);
+	crypto_bignum_deinit(tmp->order_buf, 0);
+	crypto_bignum_deinit(tmp->sae_rand, 1);
+	crypto_bignum_deinit(tmp->pwe_ffc, 1);
+	crypto_bignum_deinit(tmp->own_commit_scalar, 0);
+	crypto_bignum_deinit(tmp->own_commit_element_ffc, 0);
+	crypto_bignum_deinit(tmp->peer_commit_element_ffc, 0);
+	crypto_ec_point_deinit(tmp->pwe_ecc, 1);
+	crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0);
+	crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0);
+	wpabuf_free(tmp->anti_clogging_token);
+	bin_clear_free(tmp, sizeof(*tmp));
+	sae->tmp = NULL;
+}
+
+void sae_clear_data(struct sae_data *sae)
+{
+	if (sae == NULL)
+		return;
+	sae_clear_temp_data(sae);
+	crypto_bignum_deinit(sae->peer_commit_scalar, 0);
+	os_memset(sae, 0, sizeof(*sae));
+}
+
+static void buf_shift_right(u8 *buf, size_t len, size_t bits)
+{
+	size_t i;
+	for (i = len - 1; i > 0; i--)
+		buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits);
+	buf[0] >>= bits;
+}
+
+static struct crypto_bignum * sae_get_rand(struct sae_data *sae)
+{
+	u8 val[SAE_MAX_PRIME_LEN];
+	int iter = 0;
+	struct crypto_bignum *bn = NULL;
+	int order_len_bits = crypto_bignum_bits(sae->tmp->order);
+	size_t order_len = (order_len_bits + 7) / 8;
+
+	if (order_len > sizeof(val))
+		return NULL;
+
+	for (;;) {
+		if (iter++ > 100 || random_get_bytes(val, order_len) < 0)
+			return NULL;
+		if (order_len_bits % 8)
+			buf_shift_right(val, order_len, 8 - order_len_bits % 8);
+		bn = crypto_bignum_init_set(val, order_len);
+		if (bn == NULL)
+			return NULL;
+		if (crypto_bignum_is_zero(bn) ||
+		    crypto_bignum_is_one(bn) ||
+		    crypto_bignum_cmp(bn, sae->tmp->order) >= 0) {
+			crypto_bignum_deinit(bn, 0);
+			continue;
+		}
+		break;
+	}
+
+	os_memset(val, 0, order_len);
+	return bn;
+}
+
+static struct crypto_bignum * sae_get_rand_and_mask(struct sae_data *sae)
+{
+	crypto_bignum_deinit(sae->tmp->sae_rand, 1);
+	sae->tmp->sae_rand = sae_get_rand(sae);
+	if (sae->tmp->sae_rand == NULL)
+		return NULL;
+	return sae_get_rand(sae);
+}
+
+static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key)
+{
+	wpa_printf(MSG_DEBUG, "SAE: PWE derivation - addr1=" MACSTR
+		   " addr2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2));
+	if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) {
+		os_memcpy(key, addr1, ETH_ALEN);
+		os_memcpy(key + ETH_ALEN, addr2, ETH_ALEN);
+	} else {
+		os_memcpy(key, addr2, ETH_ALEN);
+		os_memcpy(key + ETH_ALEN, addr1, ETH_ALEN);
+	}
+}
+
+static struct crypto_bignum *
+get_rand_1_to_p_1(const u8 *prime, size_t prime_len, size_t prime_bits,
+		  int *r_odd)
+{
+	for (;;) {
+		struct crypto_bignum *r;
+		u8 tmp[SAE_MAX_ECC_PRIME_LEN];
+
+		if (random_get_bytes(tmp, prime_len) < 0)
+			break;
+		if (prime_bits % 8)
+			buf_shift_right(tmp, prime_len, 8 - prime_bits % 8);
+		if (os_memcmp(tmp, prime, prime_len) >= 0)
+			continue;
+		r = crypto_bignum_init_set(tmp, prime_len);
+		if (!r)
+			break;
+		if (crypto_bignum_is_zero(r)) {
+			crypto_bignum_deinit(r, 0);
+			continue;
+		}
+
+		*r_odd = tmp[prime_len - 1] & 0x01;
+		return r;
+	}
+
+	return NULL;
+}
+
+static int is_quadratic_residue_blind(struct sae_data *sae,
+				      const u8 *prime, size_t bits,
+				      const struct crypto_bignum *qr,
+				      const struct crypto_bignum *qnr,
+				      const struct crypto_bignum *y_sqr)
+{
+	struct crypto_bignum *r, *num;
+	int r_odd, check, res = -1;
+
+	/*
+	 * Use the blinding technique to mask y_sqr while determining
+	 * whether it is a quadratic residue modulo p to avoid leaking
+	 * timing information while determining the Legendre symbol.
+	 *
+	 * v = y_sqr
+	 * r = a random number between 1 and p-1, inclusive
+	 * num = (v * r * r) modulo p
+	 */
+	r = get_rand_1_to_p_1(prime, sae->tmp->prime_len, bits, &r_odd);
+	if (!r)
+		return -1;
+
+	num = crypto_bignum_init();
+	if (!num ||
+	    crypto_bignum_mulmod(y_sqr, r, sae->tmp->prime, num) < 0 ||
+	    crypto_bignum_mulmod(num, r, sae->tmp->prime, num) < 0)
+		goto fail;
+
+	if (r_odd) {
+		/*
+		 * num = (num * qr) module p
+		 * LGR(num, p) = 1 ==> quadratic residue
+		 */
+		if (crypto_bignum_mulmod(num, qr, sae->tmp->prime, num) < 0)
+			goto fail;
+		check = 1;
+	} else {
+		/*
+		 * num = (num * qnr) module p
+		 * LGR(num, p) = -1 ==> quadratic residue
+		 */
+		if (crypto_bignum_mulmod(num, qnr, sae->tmp->prime, num) < 0)
+			goto fail;
+		check = -1;
+	}
+
+	res = crypto_bignum_legendre(num, sae->tmp->prime);
+	if (res == -2) {
+		res = -1;
+		goto fail;
+	}
+	res = res == check;
+fail:
+	crypto_bignum_deinit(num, 1);
+	crypto_bignum_deinit(r, 1);
+	return res;
+}
+
+static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
+				 const u8 *prime,
+				 const struct crypto_bignum *qr,
+				 const struct crypto_bignum *qnr,
+				 struct crypto_bignum **ret_x_cand)
+{
+	u8 pwd_value[SAE_MAX_ECC_PRIME_LEN];
+	struct crypto_bignum *y_sqr, *x_cand;
+	int res;
+	size_t bits;
+
+	*ret_x_cand = NULL;
+
+	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
+
+	/* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
+	bits = crypto_ec_prime_len_bits(sae->tmp->ec);
+	if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking",
+			    prime, sae->tmp->prime_len, pwd_value, bits) < 0)
+		return -1;
+	if (bits % 8)
+		buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8);
+	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
+			pwd_value, sae->tmp->prime_len);
+
+	if (os_memcmp(pwd_value, prime, sae->tmp->prime_len) >= 0)
+		return 0;
+
+	x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
+	if (!x_cand)
+		return -1;
+	y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand);
+	if (!y_sqr) {
+		crypto_bignum_deinit(x_cand, 1);
+		return -1;
+	}
+
+	res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr);
+	crypto_bignum_deinit(y_sqr, 1);
+	if (res <= 0) {
+		crypto_bignum_deinit(x_cand, 1);
+		return res;
+	}
+
+	*ret_x_cand = x_cand;
+	return 1;
+}
+
+static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
+				 struct crypto_bignum *pwe)
+{
+	u8 pwd_value[SAE_MAX_PRIME_LEN];
+	size_t bits = sae->tmp->prime_len * 8;
+	u8 exp[1];
+	struct crypto_bignum *a, *b;
+	int res;
+
+	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
+
+	/* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
+	if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking",
+			    sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value,
+			    bits) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value,
+			sae->tmp->prime_len);
+
+	if (os_memcmp(pwd_value, sae->tmp->dh->prime, sae->tmp->prime_len) >= 0)
+	{
+		wpa_printf(MSG_DEBUG, "SAE: pwd-value >= p");
+		return 0;
+	}
+
+	/* PWE = pwd-value^((p-1)/r) modulo p */
+
+	a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
+
+	if (sae->tmp->dh->safe_prime) {
+		/*
+		 * r = (p-1)/2 for the group used here, so this becomes:
+		 * PWE = pwd-value^2 modulo p
+		 */
+		exp[0] = 2;
+		b = crypto_bignum_init_set(exp, sizeof(exp));
+	} else {
+		/* Calculate exponent: (p-1)/r */
+		exp[0] = 1;
+		b = crypto_bignum_init_set(exp, sizeof(exp));
+		if (b == NULL ||
+		    crypto_bignum_sub(sae->tmp->prime, b, b) < 0 ||
+		    crypto_bignum_div(b, sae->tmp->order, b) < 0) {
+			crypto_bignum_deinit(b, 0);
+			b = NULL;
+		}
+	}
+
+	if (a == NULL || b == NULL)
+		res = -1;
+	else
+		res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
+
+	crypto_bignum_deinit(a, 0);
+	crypto_bignum_deinit(b, 0);
+
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE");
+		return -1;
+	}
+
+	/* if (PWE > 1) --> found */
+	if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) {
+		wpa_printf(MSG_DEBUG, "SAE: PWE <= 1");
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "SAE: PWE found");
+	return 1;
+}
+
+static int get_random_qr_qnr(const u8 *prime, size_t prime_len,
+			     const struct crypto_bignum *prime_bn,
+			     size_t prime_bits, struct crypto_bignum **qr,
+			     struct crypto_bignum **qnr)
+{
+	*qr = NULL;
+	*qnr = NULL;
+
+	while (!(*qr) || !(*qnr)) {
+		u8 tmp[SAE_MAX_ECC_PRIME_LEN];
+		struct crypto_bignum *q;
+		int res;
+
+		if (random_get_bytes(tmp, prime_len) < 0)
+			break;
+		if (prime_bits % 8)
+			buf_shift_right(tmp, prime_len, 8 - prime_bits % 8);
+		if (os_memcmp(tmp, prime, prime_len) >= 0)
+			continue;
+		q = crypto_bignum_init_set(tmp, prime_len);
+		if (!q)
+			break;
+		res = crypto_bignum_legendre(q, prime_bn);
+
+		if (res == 1 && !(*qr))
+			*qr = q;
+		else if (res == -1 && !(*qnr))
+			*qnr = q;
+		else
+			crypto_bignum_deinit(q, 0);
+	}
+
+	return (*qr && *qnr) ? 0 : -1;
+}
+
+static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
+			      const u8 *addr2, const u8 *password,
+			      size_t password_len)
+{
+	u8 counter, k = 40;
+	u8 addrs[2 * ETH_ALEN];
+	const u8 *addr[2];
+	size_t len[2];
+	u8 dummy_password[32];
+	size_t dummy_password_len;
+	int pwd_seed_odd = 0;
+	u8 prime[SAE_MAX_ECC_PRIME_LEN];
+	size_t prime_len;
+	struct crypto_bignum *x = NULL, *qr, *qnr;
+	size_t bits;
+	int res;
+
+	dummy_password_len = password_len;
+	if (dummy_password_len > sizeof(dummy_password))
+		dummy_password_len = sizeof(dummy_password);
+	if (random_get_bytes(dummy_password, dummy_password_len) < 0)
+		return -1;
+
+	prime_len = sae->tmp->prime_len;
+	if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
+				 prime_len) < 0)
+		return -1;
+	bits = crypto_ec_prime_len_bits(sae->tmp->ec);
+
+	/*
+	 * Create a random quadratic residue (qr) and quadratic non-residue
+	 * (qnr) modulo p for blinding purposes during the loop.
+	 */
+	if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits,
+			      &qr, &qnr) < 0)
+		return -1;
+
+	wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
+			      password, password_len);
+
+	/*
+	 * H(salt, ikm) = HMAC-SHA256(salt, ikm)
+	 * base = password
+	 * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC),
+	 *              base || counter)
+	 */
+	sae_pwd_seed_key(addr1, addr2, addrs);
+
+	addr[0] = password;
+	len[0] = password_len;
+	addr[1] = &counter;
+	len[1] = sizeof(counter);
+
+	/*
+	 * Continue for at least k iterations to protect against side-channel
+	 * attacks that attempt to determine the number of iterations required
+	 * in the loop.
+	 */
+	for (counter = 1; counter <= k || !x; counter++) {
+		u8 pwd_seed[SHA256_MAC_LEN];
+		struct crypto_bignum *x_cand;
+
+		if (counter > 200) {
+			/* This should not happen in practice */
+			wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE");
+			break;
+		}
+
+		wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
+		if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len,
+				       pwd_seed) < 0)
+			break;
+
+		res = sae_test_pwd_seed_ecc(sae, pwd_seed,
+					    prime, qr, qnr, &x_cand);
+		if (res < 0)
+			goto fail;
+		if (res > 0 && !x) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE: Selected pwd-seed with counter %u",
+				   counter);
+			x = x_cand;
+			pwd_seed_odd = pwd_seed[SHA256_MAC_LEN - 1] & 0x01;
+			os_memset(pwd_seed, 0, sizeof(pwd_seed));
+
+			/*
+			 * Use a dummy password for the following rounds, if
+			 * any.
+			 */
+			addr[0] = dummy_password;
+			len[0] = dummy_password_len;
+		} else if (res > 0) {
+			crypto_bignum_deinit(x_cand, 1);
+		}
+	}
+	
+	if (!x) {
+		wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
+		res = -1;
+		goto fail;
+	}
+
+	if (!sae->tmp->pwe_ecc)
+		sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec);
+	if (!sae->tmp->pwe_ecc)
+		res = -1;
+	else
+		res = crypto_ec_point_solve_y_coord(sae->tmp->ec,
+						    sae->tmp->pwe_ecc, x,
+						    pwd_seed_odd);
+	crypto_bignum_deinit(x, 1);
+	if (res < 0) {
+		/*
+		 * This should not happen since we already checked that there
+		 * is a result.
+		 */
+		wpa_printf(MSG_DEBUG, "SAE: Could not solve y");
+	}
+
+fail:
+	crypto_bignum_deinit(qr, 0);
+	crypto_bignum_deinit(qnr, 0);
+
+	return res;
+}
+
+static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
+			      const u8 *addr2, const u8 *password,
+			      size_t password_len)
+{
+	u8 counter;
+	u8 addrs[2 * ETH_ALEN];
+	const u8 *addr[2];
+	size_t len[2];
+	int found = 0;
+
+	if (sae->tmp->pwe_ffc == NULL) {
+		sae->tmp->pwe_ffc = crypto_bignum_init();
+		if (sae->tmp->pwe_ffc == NULL)
+			return -1;
+	}
+
+	wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
+			      password, password_len);
+
+	/*
+	 * H(salt, ikm) = HMAC-SHA256(salt, ikm)
+	 * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC),
+	 *              password || counter)
+	 */
+	sae_pwd_seed_key(addr1, addr2, addrs);
+
+	addr[0] = password;
+	len[0] = password_len;
+	addr[1] = &counter;
+	len[1] = sizeof(counter);
+
+	for (counter = 1; !found; counter++) {
+		u8 pwd_seed[SHA256_MAC_LEN];
+		int res;
+
+		if (counter > 200) {
+			/* This should not happen in practice */
+			wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE");
+			break;
+		}
+
+		wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
+		if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len,
+				       pwd_seed) < 0)
+			break;
+		res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc);
+		if (res < 0)
+			break;
+		if (res > 0) {
+			wpa_printf(MSG_DEBUG, "SAE: Use this PWE");
+			found = 1;
+		}
+	}
+
+	return found ? 0 : -1;
+}
+
+static int sae_derive_commit_element_ecc(struct sae_data *sae,
+					 struct crypto_bignum *mask)
+{
+	/* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */
+	if (!sae->tmp->own_commit_element_ecc) {
+		sae->tmp->own_commit_element_ecc =
+			crypto_ec_point_init(sae->tmp->ec);
+		if (!sae->tmp->own_commit_element_ecc)
+			return -1;
+	}
+
+	if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, mask,
+				sae->tmp->own_commit_element_ecc) < 0 ||
+	    crypto_ec_point_invert(sae->tmp->ec,
+				   sae->tmp->own_commit_element_ecc) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int sae_derive_commit_element_ffc(struct sae_data *sae,
+					 struct crypto_bignum *mask)
+{
+	/* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */
+	if (!sae->tmp->own_commit_element_ffc) {
+		sae->tmp->own_commit_element_ffc = crypto_bignum_init();
+		if (!sae->tmp->own_commit_element_ffc)
+			return -1;
+	}
+
+	if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, mask, sae->tmp->prime,
+				  sae->tmp->own_commit_element_ffc) < 0 ||
+	    crypto_bignum_inverse(sae->tmp->own_commit_element_ffc,
+				  sae->tmp->prime,
+				  sae->tmp->own_commit_element_ffc) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int sae_derive_commit(struct sae_data *sae)
+{
+	struct crypto_bignum *mask;
+	int ret = -1;
+	unsigned int counter = 0;
+
+	do {
+		counter++;
+		if (counter > 100) {
+			/*
+			 * This cannot really happen in practice if the random
+			 * number generator is working. Anyway, to avoid even a
+			 * theoretical infinite loop, break out after 100
+			 * attemps.
+			 */
+			return -1;
+		}
+
+		mask = sae_get_rand_and_mask(sae);
+		if (mask == NULL) {
+			wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask");
+			return -1;
+		}
+
+		/* commit-scalar = (rand + mask) modulo r */
+		if (!sae->tmp->own_commit_scalar) {
+			sae->tmp->own_commit_scalar = crypto_bignum_init();
+			if (!sae->tmp->own_commit_scalar)
+				goto fail;
+		}
+		crypto_bignum_add(sae->tmp->sae_rand, mask,
+				  sae->tmp->own_commit_scalar);
+		crypto_bignum_mod(sae->tmp->own_commit_scalar, sae->tmp->order,
+				  sae->tmp->own_commit_scalar);
+	} while (crypto_bignum_is_zero(sae->tmp->own_commit_scalar) ||
+		 crypto_bignum_is_one(sae->tmp->own_commit_scalar));
+
+	if ((sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0) ||
+	    (sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0))
+		goto fail;
+
+	ret = 0;
+fail:
+	crypto_bignum_deinit(mask, 1);
+	return ret;
+}
+
+int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
+		       const u8 *password, size_t password_len,
+		       struct sae_data *sae)
+{
+	if (sae->tmp == NULL ||
+	    (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password,
+						password_len) < 0) ||
+	    (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password,
+						password_len) < 0) ||
+	    sae_derive_commit(sae) < 0)
+		return -1;
+	return 0;
+}
+
+static int sae_derive_k_ecc(struct sae_data *sae, u8 *k)
+{
+	struct crypto_ec_point *K;
+	int ret = -1;
+
+	K = crypto_ec_point_init(sae->tmp->ec);
+	if (K == NULL)
+		goto fail;
+
+	/*
+	 * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE),
+	 *                                        PEER-COMMIT-ELEMENT)))
+	 * If K is identity element (point-at-infinity), reject
+	 * k = F(K) (= x coordinate)
+	 */
+
+	if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc,
+				sae->peer_commit_scalar, K) < 0 ||
+	    crypto_ec_point_add(sae->tmp->ec, K,
+				sae->tmp->peer_commit_element_ecc, K) < 0 ||
+	    crypto_ec_point_mul(sae->tmp->ec, K, sae->tmp->sae_rand, K) < 0 ||
+	    crypto_ec_point_is_at_infinity(sae->tmp->ec, K) ||
+	    crypto_ec_point_to_bin(sae->tmp->ec, K, k, NULL) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k");
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len);
+
+	ret = 0;
+fail:
+	crypto_ec_point_deinit(K, 1);
+	return ret;
+}
+
+static int sae_derive_k_ffc(struct sae_data *sae, u8 *k)
+{
+	struct crypto_bignum *K;
+	int ret = -1;
+
+	K = crypto_bignum_init();
+	if (K == NULL)
+		goto fail;
+
+	/*
+	 * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE),
+	 *                                        PEER-COMMIT-ELEMENT)))
+	 * If K is identity element (one), reject.
+	 * k = F(K) (= x coordinate)
+	 */
+
+	if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, sae->peer_commit_scalar,
+				  sae->tmp->prime, K) < 0 ||
+	    crypto_bignum_mulmod(K, sae->tmp->peer_commit_element_ffc,
+				 sae->tmp->prime, K) < 0 ||
+	    crypto_bignum_exptmod(K, sae->tmp->sae_rand, sae->tmp->prime, K) < 0
+	    ||
+	    crypto_bignum_is_one(K) ||
+	    crypto_bignum_to_bin(K, k, SAE_MAX_PRIME_LEN, sae->tmp->prime_len) <
+	    0) {
+		wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k");
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len);
+
+	ret = 0;
+fail:
+	crypto_bignum_deinit(K, 1);
+	return ret;
+}
+
+static int sae_derive_keys(struct sae_data *sae, const u8 *k)
+{
+	u8 null_key[SAE_KEYSEED_KEY_LEN], val[SAE_MAX_PRIME_LEN];
+	u8 keyseed[SHA256_MAC_LEN];
+	u8 keys[SAE_KCK_LEN + SAE_PMK_LEN];
+	struct crypto_bignum *tmp;
+	int ret = -1;
+
+	tmp = crypto_bignum_init();
+	if (tmp == NULL)
+		goto fail;
+
+	/* keyseed = H(<0>32, k)
+	 * KCK || PMK = KDF-512(keyseed, "SAE KCK and PMK",
+	 *                      (commit-scalar + peer-commit-scalar) modulo r)
+	 * PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128)
+	 */
+
+	os_memset(null_key, 0, sizeof(null_key));
+	hmac_sha256(null_key, sizeof(null_key), k, sae->tmp->prime_len,
+		    keyseed);
+	wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, sizeof(keyseed));
+
+	crypto_bignum_add(sae->tmp->own_commit_scalar, sae->peer_commit_scalar,
+			  tmp);
+	crypto_bignum_mod(tmp, sae->tmp->order, tmp);
+	crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->prime_len);
+	wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN);
+	if (sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK",
+		       val, sae->tmp->prime_len, keys, sizeof(keys)) < 0)
+		goto fail;
+	os_memset(keyseed, 0, sizeof(keyseed));
+	os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN);
+	os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN);
+	os_memcpy(sae->pmkid, val, SAE_PMKID_LEN);
+	os_memset(keys, 0, sizeof(keys));
+	wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN);
+
+	ret = 0;
+fail:
+	crypto_bignum_deinit(tmp, 0);
+	return ret;
+}
+
+int sae_process_commit(struct sae_data *sae)
+{
+	u8 k[SAE_MAX_PRIME_LEN];
+	if (sae->tmp == NULL ||
+	    (sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) ||
+	    (sae->tmp->dh && sae_derive_k_ffc(sae, k) < 0) ||
+	    sae_derive_keys(sae, k) < 0)
+		return -1;
+	return 0;
+}
+
+void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
+		      const struct wpabuf *token)
+{
+	u8 *pos;
+
+	if (sae->tmp == NULL)
+		return;
+
+	wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */
+	if (token) {
+		wpabuf_put_buf(buf, token);
+		wpa_hexdump(MSG_DEBUG, "SAE: Anti-clogging token",
+			    wpabuf_head(token), wpabuf_len(token));
+	}
+	pos = wpabuf_put(buf, sae->tmp->prime_len);
+	crypto_bignum_to_bin(sae->tmp->own_commit_scalar, pos,
+			     sae->tmp->prime_len, sae->tmp->prime_len);
+	wpa_hexdump(MSG_DEBUG, "SAE: own commit-scalar",
+		    pos, sae->tmp->prime_len);
+	if (sae->tmp->ec) {
+		pos = wpabuf_put(buf, 2 * sae->tmp->prime_len);
+		crypto_ec_point_to_bin(sae->tmp->ec,
+				       sae->tmp->own_commit_element_ecc,
+				       pos, pos + sae->tmp->prime_len);
+		wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(x)",
+			    pos, sae->tmp->prime_len);
+		wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(y)",
+			    pos + sae->tmp->prime_len, sae->tmp->prime_len);
+	} else {
+		pos = wpabuf_put(buf, sae->tmp->prime_len);
+		crypto_bignum_to_bin(sae->tmp->own_commit_element_ffc, pos,
+				     sae->tmp->prime_len, sae->tmp->prime_len);
+		wpa_hexdump(MSG_DEBUG, "SAE: own commit-element",
+			    pos, sae->tmp->prime_len);
+	}
+}
+
+u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group)
+{
+	if (allowed_groups) {
+		int i;
+		for (i = 0; allowed_groups[i] > 0; i++) {
+			if (allowed_groups[i] == group)
+				break;
+		}
+		if (allowed_groups[i] != group) {
+			wpa_printf(MSG_DEBUG, "SAE: Proposed group %u not "
+				   "enabled in the current configuration",
+				   group);
+			return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+		}
+	}
+
+	if (sae->state == SAE_COMMITTED && group != sae->group) {
+		wpa_printf(MSG_DEBUG, "SAE: Do not allow group to be changed");
+		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+	}
+
+	if (group != sae->group && sae_set_group(sae, group) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u",
+			   group);
+		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+	}
+
+	if (sae->tmp == NULL) {
+		wpa_printf(MSG_DEBUG, "SAE: Group information not yet initialized");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	if (sae->tmp->dh && !allowed_groups) {
+		wpa_printf(MSG_DEBUG, "SAE: Do not allow FFC group %u without "
+			   "explicit configuration enabling it", group);
+		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+	}
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos,
+				   const u8 *end, const u8 **token,
+				   size_t *token_len)
+{
+	if ((sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end - *pos) {
+		size_t tlen = end - (*pos + (sae->tmp->ec ? 3 : 2) *
+				     sae->tmp->prime_len);
+		wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen);
+		if (token)
+			*token = *pos;
+		if (token_len)
+			*token_len = tlen;
+		*pos += tlen;
+	} else {
+		if (token)
+			*token = NULL;
+		if (token_len)
+			*token_len = 0;
+	}
+}
+
+static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos,
+				   const u8 *end)
+{
+	struct crypto_bignum *peer_scalar;
+
+	if (sae->tmp->prime_len > end - *pos) {
+		wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	peer_scalar = crypto_bignum_init_set(*pos, sae->tmp->prime_len);
+	if (peer_scalar == NULL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	/*
+	 * IEEE Std 802.11-2012, 11.3.8.6.1: If there is a protocol instance for
+	 * the peer and it is in Authenticated state, the new Commit Message
+	 * shall be dropped if the peer-scalar is identical to the one used in
+	 * the existing protocol instance.
+	 */
+	if (sae->state == SAE_ACCEPTED && sae->peer_commit_scalar &&
+	    crypto_bignum_cmp(sae->peer_commit_scalar, peer_scalar) == 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Do not accept re-use of previous "
+			   "peer-commit-scalar");
+		crypto_bignum_deinit(peer_scalar, 0);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	/* 1 < scalar < r */
+	if (crypto_bignum_is_zero(peer_scalar) ||
+	    crypto_bignum_is_one(peer_scalar) ||
+	    crypto_bignum_cmp(peer_scalar, sae->tmp->order) >= 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar");
+		crypto_bignum_deinit(peer_scalar, 0);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+
+	crypto_bignum_deinit(sae->peer_commit_scalar, 0);
+	sae->peer_commit_scalar = peer_scalar;
+	wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-scalar",
+		    *pos, sae->tmp->prime_len);
+	*pos += sae->tmp->prime_len;
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos,
+					const u8 *end)
+{
+	u8 prime[SAE_MAX_ECC_PRIME_LEN];
+
+	if (2 * sae->tmp->prime_len > end - pos) {
+		wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
+			   "commit-element");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
+				 sae->tmp->prime_len) < 0)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	/* element x and y coordinates < p */
+	if (os_memcmp(pos, prime, sae->tmp->prime_len) >= 0 ||
+	    os_memcmp(pos + sae->tmp->prime_len, prime,
+		      sae->tmp->prime_len) >= 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer "
+			   "element");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)",
+		    pos, sae->tmp->prime_len);
+	wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)",
+		    pos + sae->tmp->prime_len, sae->tmp->prime_len);
+
+	crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0);
+	sae->tmp->peer_commit_element_ecc =
+		crypto_ec_point_from_bin(sae->tmp->ec, pos);
+	if (sae->tmp->peer_commit_element_ecc == NULL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	if (!crypto_ec_point_is_on_curve(sae->tmp->ec,
+					 sae->tmp->peer_commit_element_ecc)) {
+		wpa_printf(MSG_DEBUG, "SAE: Peer element is not on curve");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos,
+					const u8 *end)
+{
+	struct crypto_bignum *res, *one;
+	const u8 one_bin[1] = { 0x01 };
+
+	if (sae->tmp->prime_len > end - pos) {
+		wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
+			   "commit-element");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", pos,
+		    sae->tmp->prime_len);
+
+	crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0);
+	sae->tmp->peer_commit_element_ffc =
+		crypto_bignum_init_set(pos, sae->tmp->prime_len);
+	if (sae->tmp->peer_commit_element_ffc == NULL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	/* 1 < element < p - 1 */
+	res = crypto_bignum_init();
+	one = crypto_bignum_init_set(one_bin, sizeof(one_bin));
+	if (!res || !one ||
+	    crypto_bignum_sub(sae->tmp->prime, one, res) ||
+	    crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) ||
+	    crypto_bignum_is_one(sae->tmp->peer_commit_element_ffc) ||
+	    crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc, res) >= 0) {
+		crypto_bignum_deinit(res, 0);
+		crypto_bignum_deinit(one, 0);
+		wpa_printf(MSG_DEBUG, "SAE: Invalid peer element");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	crypto_bignum_deinit(one, 0);
+
+	/* scalar-op(r, ELEMENT) = 1 modulo p */
+	if (crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc,
+				  sae->tmp->order, sae->tmp->prime, res) < 0 ||
+	    !crypto_bignum_is_one(res)) {
+		wpa_printf(MSG_DEBUG, "SAE: Invalid peer element (scalar-op)");
+		crypto_bignum_deinit(res, 0);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	crypto_bignum_deinit(res, 0);
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+static u16 sae_parse_commit_element(struct sae_data *sae, const u8 *pos,
+				    const u8 *end)
+{
+	if (sae->tmp->dh)
+		return sae_parse_commit_element_ffc(sae, pos, end);
+	return sae_parse_commit_element_ecc(sae, pos, end);
+}
+
+u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
+		     const u8 **token, size_t *token_len, int *allowed_groups)
+{
+	const u8 *pos = data, *end = data + len;
+	u16 res;
+	
+	/* Check Finite Cyclic Group */
+	if (end - pos < 2)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos));
+	if (res != WLAN_STATUS_SUCCESS)
+		return res;
+	pos += 2;
+
+	/* Optional Anti-Clogging Token */
+	sae_parse_commit_token(sae, &pos, end, token, token_len);
+
+	/* commit-scalar */
+	res = sae_parse_commit_scalar(sae, &pos, end);
+	if (res != WLAN_STATUS_SUCCESS)
+		return res;
+
+	/* commit-element */
+	res = sae_parse_commit_element(sae, pos, end);
+	if (res != WLAN_STATUS_SUCCESS)
+		return res;
+
+	/*
+	 * Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as
+	 * the values we sent which would be evidence of a reflection attack.
+	 */
+	if (!sae->tmp->own_commit_scalar ||
+	    crypto_bignum_cmp(sae->tmp->own_commit_scalar,
+			      sae->peer_commit_scalar) != 0 ||
+	    (sae->tmp->dh &&
+	     (!sae->tmp->own_commit_element_ffc ||
+	      crypto_bignum_cmp(sae->tmp->own_commit_element_ffc,
+				sae->tmp->peer_commit_element_ffc) != 0)) ||
+	    (sae->tmp->ec &&
+	     (!sae->tmp->own_commit_element_ecc ||
+	      crypto_ec_point_cmp(sae->tmp->ec,
+				  sae->tmp->own_commit_element_ecc,
+				  sae->tmp->peer_commit_element_ecc) != 0)))
+		return WLAN_STATUS_SUCCESS; /* scalars/elements are different */
+
+	/*
+	 * This is a reflection attack - return special value to trigger caller
+	 * to silently discard the frame instead of replying with a specific
+	 * status code.
+	 */
+	return SAE_SILENTLY_DISCARD;
+}
+
+static void sae_cn_confirm(struct sae_data *sae, const u8 *sc,
+			   const struct crypto_bignum *scalar1,
+			   const u8 *element1, size_t element1_len,
+			   const struct crypto_bignum *scalar2,
+			   const u8 *element2, size_t element2_len,
+			   u8 *confirm)
+{
+	const u8 *addr[5];
+	size_t len[5];
+	u8 scalar_b1[SAE_MAX_PRIME_LEN], scalar_b2[SAE_MAX_PRIME_LEN];
+
+	/* Confirm
+	 * CN(key, X, Y, Z, ...) =
+	 *    HMAC-SHA256(key, D2OS(X) || D2OS(Y) || D2OS(Z) | ...)
+	 * confirm = CN(KCK, send-confirm, commit-scalar, COMMIT-ELEMENT,
+	 *              peer-commit-scalar, PEER-COMMIT-ELEMENT)
+	 * verifier = CN(KCK, peer-send-confirm, peer-commit-scalar,
+	 *               PEER-COMMIT-ELEMENT, commit-scalar, COMMIT-ELEMENT)
+	 */
+	addr[0] = sc;
+	len[0] = 2;
+	crypto_bignum_to_bin(scalar1, scalar_b1, sizeof(scalar_b1),
+			     sae->tmp->prime_len);
+	addr[1] = scalar_b1;
+	len[1] = sae->tmp->prime_len;
+	addr[2] = element1;
+	len[2] = element1_len;
+	crypto_bignum_to_bin(scalar2, scalar_b2, sizeof(scalar_b2),
+			     sae->tmp->prime_len);
+	addr[3] = scalar_b2;
+	len[3] = sae->tmp->prime_len;
+	addr[4] = element2;
+	len[4] = element2_len;
+	hmac_sha256_vector(sae->tmp->kck, sizeof(sae->tmp->kck), 5, addr, len,
+			   confirm);
+}
+
+static void sae_cn_confirm_ecc(struct sae_data *sae, const u8 *sc,
+			       const struct crypto_bignum *scalar1,
+			       const struct crypto_ec_point *element1,
+			       const struct crypto_bignum *scalar2,
+			       const struct crypto_ec_point *element2,
+			       u8 *confirm)
+{
+	u8 element_b1[2 * SAE_MAX_ECC_PRIME_LEN];
+	u8 element_b2[2 * SAE_MAX_ECC_PRIME_LEN];
+
+	crypto_ec_point_to_bin(sae->tmp->ec, element1, element_b1,
+			       element_b1 + sae->tmp->prime_len);
+	crypto_ec_point_to_bin(sae->tmp->ec, element2, element_b2,
+			       element_b2 + sae->tmp->prime_len);
+
+	sae_cn_confirm(sae, sc, scalar1, element_b1, 2 * sae->tmp->prime_len,
+		       scalar2, element_b2, 2 * sae->tmp->prime_len, confirm);
+}
+
+static void sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc,
+			       const struct crypto_bignum *scalar1,
+			       const struct crypto_bignum *element1,
+			       const struct crypto_bignum *scalar2,
+			       const struct crypto_bignum *element2,
+			       u8 *confirm)
+{
+	u8 element_b1[SAE_MAX_PRIME_LEN];
+	u8 element_b2[SAE_MAX_PRIME_LEN];
+
+	crypto_bignum_to_bin(element1, element_b1, sizeof(element_b1),
+			     sae->tmp->prime_len);
+	crypto_bignum_to_bin(element2, element_b2, sizeof(element_b2),
+			     sae->tmp->prime_len);
+
+	sae_cn_confirm(sae, sc, scalar1, element_b1, sae->tmp->prime_len,
+		       scalar2, element_b2, sae->tmp->prime_len, confirm);
+}
+
+void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf)
+{
+	const u8 *sc;
+
+	if (sae->tmp == NULL)
+		return;
+
+	/* Send-Confirm */
+	sc = wpabuf_put(buf, 0);
+	wpabuf_put_le16(buf, sae->send_confirm);
+	sae->send_confirm++;
+
+	if (sae->tmp->ec)
+		sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar,
+				   sae->tmp->own_commit_element_ecc,
+				   sae->peer_commit_scalar,
+				   sae->tmp->peer_commit_element_ecc,
+				   wpabuf_put(buf, SHA256_MAC_LEN));
+	else
+		sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar,
+				   sae->tmp->own_commit_element_ffc,
+				   sae->peer_commit_scalar,
+				   sae->tmp->peer_commit_element_ffc,
+				   wpabuf_put(buf, SHA256_MAC_LEN));
+}
+
+int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len)
+{
+	u8 verifier[SHA256_MAC_LEN];
+
+	if (len < 2 + SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG, "SAE: Too short confirm message");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data));
+
+	if (sae->tmp == NULL) {
+		wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available");
+		return -1;
+	}
+
+	if (sae->tmp->ec)
+		sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar,
+				   sae->tmp->peer_commit_element_ecc,
+				   sae->tmp->own_commit_scalar,
+				   sae->tmp->own_commit_element_ecc,
+				   verifier);
+	else
+		sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar,
+				   sae->tmp->peer_commit_element_ffc,
+				   sae->tmp->own_commit_scalar,
+				   sae->tmp->own_commit_element_ffc,
+				   verifier);
+
+	if (os_memcmp(verifier, data + 2, SHA256_MAC_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch");
+		wpa_hexdump(MSG_DEBUG, "SAE: Received confirm",
+			    data + 2, SHA256_MAC_LEN);
+		wpa_hexdump(MSG_DEBUG, "SAE: Calculated verifier",
+			    verifier, SHA256_MAC_LEN);
+		return -1;
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_WPA3_SAE */

+ 85 - 0
components/wpa_supplicant/src/common/sae.h

@@ -0,0 +1,85 @@
+/*
+ * Simultaneous authentication of equals
+ * Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifdef CONFIG_WPA3_SAE
+
+#ifndef SAE_H
+#define SAE_H
+
+#include "esp_err.h"
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "utils/wpa_debug.h"
+
+#define SAE_KCK_LEN 32
+#define SAE_PMK_LEN 32
+#define SAE_PMKID_LEN 16
+#define SAE_KEYSEED_KEY_LEN 32
+#define SAE_MAX_PRIME_LEN 512
+#define SAE_MAX_ECC_PRIME_LEN 66
+#define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN)
+#define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_PRIME_LEN)
+
+/* Special value returned by sae_parse_commit() */
+#define SAE_SILENTLY_DISCARD 65535
+
+struct sae_temporary_data {
+	u8 kck[SAE_KCK_LEN];
+	struct crypto_bignum *own_commit_scalar;
+	struct crypto_bignum *own_commit_element_ffc;
+	struct crypto_ec_point *own_commit_element_ecc;
+	struct crypto_bignum *peer_commit_element_ffc;
+	struct crypto_ec_point *peer_commit_element_ecc;
+	struct crypto_ec_point *pwe_ecc;
+	struct crypto_bignum *pwe_ffc;
+	struct crypto_bignum *sae_rand;
+	struct crypto_ec *ec;
+	int prime_len;
+	const struct dh_group *dh;
+	const struct crypto_bignum *prime;
+	const struct crypto_bignum *order;
+	struct crypto_bignum *prime_buf;
+	struct crypto_bignum *order_buf;
+	struct wpabuf *anti_clogging_token;
+};
+
+enum {
+	SAE_MSG_COMMIT = 1,
+	SAE_MSG_CONFIRM = 2,
+};
+
+struct sae_data {
+	enum { SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED } state;
+	u16 send_confirm;
+	u8 pmk[SAE_PMK_LEN];
+	u8 pmkid[SAE_PMKID_LEN];
+	struct crypto_bignum *peer_commit_scalar;
+	u16 group;
+	int sync;
+	struct sae_temporary_data *tmp;
+};
+
+int sae_set_group(struct sae_data *sae, u16 group);
+void sae_clear_temp_data(struct sae_data *sae);
+void sae_clear_data(struct sae_data *sae);
+
+int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
+		       const u8 *password, size_t password_len,
+		       struct sae_data *sae);
+int sae_process_commit(struct sae_data *sae);
+void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
+		      const struct wpabuf *token);
+u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
+		     const u8 **token, size_t *token_len, int *allowed_groups);
+void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf);
+int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len);
+u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group);
+
+#endif /* SAE_H */
+#endif /* CONFIG_WPA3_SAE */

+ 2 - 4
components/wpa_supplicant/src/crypto/crypto_mbedtls.c

@@ -25,10 +25,6 @@
 #include "mbedtls/entropy.h"
 #include "mbedtls/ctr_drbg.h"
 
-
-
-#define IANA_SECP256R1 19
-
 #ifdef ESP_PLATFORM
 int crypto_get_random(void *buf, size_t len)
 {
@@ -496,6 +492,8 @@ int crypto_ec_point_solve_y_coord(struct crypto_ec *e,
         if (y_bit) {
             MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(y, &e->group.P, y));
         }
+        MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&((mbedtls_ecp_point* )p)->X, (const mbedtls_mpi*) x));
+        MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&((mbedtls_ecp_point *)p)->Z, 1));
     } else {
         ret = 1;
     }

+ 668 - 14
components/wpa_supplicant/src/crypto/dh_groups.c

@@ -44,6 +44,21 @@ static const u8 dh_group1_prime[96] = {
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 };
 
+static const u8 dh_group1_order[96] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1D, 0x1B, 0x10,
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
 /* RFC 4306, B.2. Group 2 - 1024 Bit MODP
  * Generator: 2
  * Prime: 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }
@@ -68,6 +83,25 @@ static const u8 dh_group2_prime[128] = {
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 };
 
+static const u8 dh_group2_order[128] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x73, 0x29, 0xC0,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
 #endif /* ALL_DH_GROUPS */
 
 /* RFC 3526, 2. Group 5 - 1536 Bit MODP
@@ -102,6 +136,33 @@ static const u8 dh_group5_prime[192] = {
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 };
 
+static const u8 dh_group5_order[192] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x11, 0xB9, 0x93,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
 #ifdef ALL_DH_GROUPS
 
 /* RFC 3526, 3. Group 14 - 2048 Bit MODP
@@ -144,6 +205,41 @@ static const u8 dh_group14_prime[256] = {
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 };
 
+static const u8 dh_group14_order[256] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
+	0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
+	0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
+	0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
+	0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
+	0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
+	0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
+	0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
+	0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x56, 0x55, 0x34,
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
 /* RFC 3526, 4. Group 15 - 3072 Bit MODP
  * Generator: 2
  * Prime: 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 }
@@ -200,6 +296,57 @@ static const u8 dh_group15_prime[384] = {
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 };
 
+static const u8 dh_group15_order[384] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
+	0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
+	0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
+	0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
+	0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
+	0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
+	0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
+	0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
+	0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16,
+	0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19,
+	0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32,
+	0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85,
+	0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E,
+	0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63,
+	0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB,
+	0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE,
+	0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35,
+	0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32,
+	0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32,
+	0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06,
+	0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6,
+	0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71,
+	0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98,
+	0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47,
+	0x25, 0xC1, 0x68, 0x90, 0x54, 0x9D, 0x69, 0x65,
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
 /* RFC 3526, 5. Group 16 - 4096 Bit MODP
  * Generator: 2
  * Prime: 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 }
@@ -272,6 +419,73 @@ static const u8 dh_group16_prime[512] = {
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 };
 
+static const u8 dh_group16_order[512] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
+	0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
+	0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
+	0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
+	0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
+	0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
+	0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
+	0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
+	0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16,
+	0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19,
+	0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32,
+	0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85,
+	0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E,
+	0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63,
+	0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB,
+	0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE,
+	0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35,
+	0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32,
+	0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32,
+	0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06,
+	0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6,
+	0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71,
+	0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98,
+	0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47,
+	0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00,
+	0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B,
+	0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93,
+	0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E,
+	0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED,
+	0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74,
+	0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C,
+	0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53,
+	0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E,
+	0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1,
+	0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6,
+	0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7,
+	0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E,
+	0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54,
+	0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0,
+	0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47,
+	0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x03, 0x18, 0xCC,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
 /* RFC 3526, 6. Group 17 - 6144 Bit MODP
  * Generator: 2
  * Prime: 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 }
@@ -376,6 +590,105 @@ static const u8 dh_group17_prime[768] = {
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 };
 
+static const u8 dh_group17_order[768] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
+	0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
+	0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
+	0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
+	0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
+	0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
+	0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
+	0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
+	0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16,
+	0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19,
+	0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32,
+	0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85,
+	0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E,
+	0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63,
+	0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB,
+	0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE,
+	0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35,
+	0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32,
+	0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32,
+	0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06,
+	0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6,
+	0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71,
+	0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98,
+	0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47,
+	0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00,
+	0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B,
+	0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93,
+	0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E,
+	0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED,
+	0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74,
+	0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C,
+	0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53,
+	0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E,
+	0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1,
+	0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6,
+	0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7,
+	0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E,
+	0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54,
+	0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0,
+	0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47,
+	0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x01, 0x42, 0x49,
+	0x1B, 0x61, 0xFD, 0x5A, 0x69, 0x3E, 0x38, 0x13,
+	0x60, 0xEA, 0x6E, 0x59, 0x30, 0x13, 0x23, 0x6F,
+	0x64, 0xBA, 0x8F, 0x3B, 0x1E, 0xDD, 0x1B, 0xDE,
+	0xFC, 0x7F, 0xCA, 0x03, 0x56, 0xCF, 0x29, 0x87,
+	0x72, 0xED, 0x9C, 0x17, 0xA0, 0x98, 0x00, 0xD7,
+	0x58, 0x35, 0x29, 0xF6, 0xC8, 0x13, 0xEC, 0x18,
+	0x8B, 0xCB, 0x93, 0xD8, 0x43, 0x2D, 0x44, 0x8C,
+	0x6D, 0x1F, 0x6D, 0xF5, 0xE7, 0xCD, 0x8A, 0x76,
+	0xA2, 0x67, 0x36, 0x5D, 0x67, 0x6A, 0x5D, 0x8D,
+	0xED, 0xBF, 0x8A, 0x23, 0xF3, 0x66, 0x12, 0xA5,
+	0x99, 0x90, 0x28, 0xA8, 0x95, 0xEB, 0xD7, 0xA1,
+	0x37, 0xDC, 0x7A, 0x00, 0x9B, 0xC6, 0x69, 0x5F,
+	0xAC, 0xC1, 0xE5, 0x00, 0xE3, 0x25, 0xC9, 0x76,
+	0x78, 0x19, 0x75, 0x0A, 0xE8, 0xB9, 0x0E, 0x81,
+	0xFA, 0x41, 0x6B, 0xE7, 0x37, 0x3A, 0x7F, 0x7B,
+	0x6A, 0xAF, 0x38, 0x17, 0xA3, 0x4C, 0x06, 0x41,
+	0x5A, 0xD4, 0x20, 0x18, 0xC8, 0x05, 0x8E, 0x4F,
+	0x2C, 0xF3, 0xE4, 0xBF, 0xDF, 0x63, 0xF4, 0x79,
+	0x91, 0xD4, 0xBD, 0x3F, 0x1B, 0x66, 0x44, 0x5F,
+	0x07, 0x8E, 0xA2, 0xDB, 0xFF, 0xAC, 0x2D, 0x62,
+	0xA5, 0xEA, 0x03, 0xD9, 0x15, 0xA0, 0xAA, 0x55,
+	0x66, 0x47, 0xB6, 0xBF, 0x5F, 0xA4, 0x70, 0xEC,
+	0x0A, 0x66, 0x2F, 0x69, 0x07, 0xC0, 0x1B, 0xF0,
+	0x53, 0xCB, 0x8A, 0xF7, 0x79, 0x4D, 0xF1, 0x94,
+	0x03, 0x50, 0xEA, 0xC5, 0xDB, 0xE2, 0xED, 0x3B,
+	0x7A, 0xA8, 0x55, 0x1E, 0xC5, 0x0F, 0xDF, 0xF8,
+	0x75, 0x8C, 0xE6, 0x58, 0xD1, 0x89, 0xEA, 0xAE,
+	0x6D, 0x2B, 0x64, 0xF6, 0x17, 0x79, 0x4B, 0x19,
+	0x1C, 0x3F, 0xF4, 0x6B, 0xB7, 0x1E, 0x02, 0x34,
+	0x02, 0x1F, 0x47, 0xB3, 0x1F, 0xA4, 0x30, 0x77,
+	0x09, 0x5F, 0x96, 0xAD, 0x85, 0xBA, 0x3A, 0x6B,
+	0x73, 0x4A, 0x7C, 0x8F, 0x36, 0xE6, 0x20, 0x12,
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
 /* RFC 3526, 7. Group 18 - 8192 Bit MODP
  * Generator: 2
  * Prime: 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 }
@@ -512,24 +825,362 @@ static const u8 dh_group18_prime[1024] = {
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 };
 
-#endif /* ALL_DH_GROUPS */
+static const u8 dh_group18_order[1024] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
+	0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
+	0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
+	0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
+	0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
+	0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
+	0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
+	0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
+	0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16,
+	0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19,
+	0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32,
+	0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85,
+	0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E,
+	0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63,
+	0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB,
+	0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE,
+	0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35,
+	0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32,
+	0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32,
+	0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06,
+	0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6,
+	0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71,
+	0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98,
+	0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47,
+	0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00,
+	0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B,
+	0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93,
+	0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E,
+	0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED,
+	0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74,
+	0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C,
+	0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53,
+	0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E,
+	0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1,
+	0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6,
+	0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7,
+	0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E,
+	0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54,
+	0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0,
+	0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47,
+	0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x01, 0x42, 0x49,
+	0x1B, 0x61, 0xFD, 0x5A, 0x69, 0x3E, 0x38, 0x13,
+	0x60, 0xEA, 0x6E, 0x59, 0x30, 0x13, 0x23, 0x6F,
+	0x64, 0xBA, 0x8F, 0x3B, 0x1E, 0xDD, 0x1B, 0xDE,
+	0xFC, 0x7F, 0xCA, 0x03, 0x56, 0xCF, 0x29, 0x87,
+	0x72, 0xED, 0x9C, 0x17, 0xA0, 0x98, 0x00, 0xD7,
+	0x58, 0x35, 0x29, 0xF6, 0xC8, 0x13, 0xEC, 0x18,
+	0x8B, 0xCB, 0x93, 0xD8, 0x43, 0x2D, 0x44, 0x8C,
+	0x6D, 0x1F, 0x6D, 0xF5, 0xE7, 0xCD, 0x8A, 0x76,
+	0xA2, 0x67, 0x36, 0x5D, 0x67, 0x6A, 0x5D, 0x8D,
+	0xED, 0xBF, 0x8A, 0x23, 0xF3, 0x66, 0x12, 0xA5,
+	0x99, 0x90, 0x28, 0xA8, 0x95, 0xEB, 0xD7, 0xA1,
+	0x37, 0xDC, 0x7A, 0x00, 0x9B, 0xC6, 0x69, 0x5F,
+	0xAC, 0xC1, 0xE5, 0x00, 0xE3, 0x25, 0xC9, 0x76,
+	0x78, 0x19, 0x75, 0x0A, 0xE8, 0xB9, 0x0E, 0x81,
+	0xFA, 0x41, 0x6B, 0xE7, 0x37, 0x3A, 0x7F, 0x7B,
+	0x6A, 0xAF, 0x38, 0x17, 0xA3, 0x4C, 0x06, 0x41,
+	0x5A, 0xD4, 0x20, 0x18, 0xC8, 0x05, 0x8E, 0x4F,
+	0x2C, 0xF3, 0xE4, 0xBF, 0xDF, 0x63, 0xF4, 0x79,
+	0x91, 0xD4, 0xBD, 0x3F, 0x1B, 0x66, 0x44, 0x5F,
+	0x07, 0x8E, 0xA2, 0xDB, 0xFF, 0xAC, 0x2D, 0x62,
+	0xA5, 0xEA, 0x03, 0xD9, 0x15, 0xA0, 0xAA, 0x55,
+	0x66, 0x47, 0xB6, 0xBF, 0x5F, 0xA4, 0x70, 0xEC,
+	0x0A, 0x66, 0x2F, 0x69, 0x07, 0xC0, 0x1B, 0xF0,
+	0x53, 0xCB, 0x8A, 0xF7, 0x79, 0x4D, 0xF1, 0x94,
+	0x03, 0x50, 0xEA, 0xC5, 0xDB, 0xE2, 0xED, 0x3B,
+	0x7A, 0xA8, 0x55, 0x1E, 0xC5, 0x0F, 0xDF, 0xF8,
+	0x75, 0x8C, 0xE6, 0x58, 0xD1, 0x89, 0xEA, 0xAE,
+	0x6D, 0x2B, 0x64, 0xF6, 0x17, 0x79, 0x4B, 0x19,
+	0x1C, 0x3F, 0xF4, 0x6B, 0xB7, 0x1E, 0x02, 0x34,
+	0x02, 0x1F, 0x47, 0xB3, 0x1F, 0xA4, 0x30, 0x77,
+	0x09, 0x5F, 0x96, 0xAD, 0x85, 0xBA, 0x3A, 0x6B,
+	0x73, 0x4A, 0x7C, 0x8F, 0x36, 0xDF, 0x08, 0xAC,
+	0xBA, 0x51, 0xC9, 0x37, 0x89, 0x7F, 0x72, 0xF2,
+	0x1C, 0x3B, 0xBE, 0x5B, 0x54, 0x99, 0x6F, 0xC6,
+	0x6C, 0x5F, 0x62, 0x68, 0x39, 0xDC, 0x98, 0xDD,
+	0x1D, 0xE4, 0x19, 0x5B, 0x46, 0xCE, 0xE9, 0x80,
+	0x3A, 0x0F, 0xD3, 0xDF, 0xC5, 0x7E, 0x23, 0xF6,
+	0x92, 0xBB, 0x7B, 0x49, 0xB5, 0xD2, 0x12, 0x33,
+	0x1D, 0x55, 0xB1, 0xCE, 0x2D, 0x72, 0x7A, 0xB4,
+	0x1A, 0x11, 0xDA, 0x3A, 0x15, 0xF8, 0xE4, 0xBC,
+	0x11, 0xC7, 0x8B, 0x65, 0xF1, 0xCE, 0xB2, 0x96,
+	0xF1, 0xFE, 0xDC, 0x5F, 0x7E, 0x42, 0x45, 0x6C,
+	0x91, 0x11, 0x17, 0x02, 0x52, 0x01, 0xBE, 0x03,
+	0x89, 0xF5, 0xAB, 0xD4, 0x0D, 0x11, 0xF8, 0x63,
+	0x9A, 0x39, 0xFE, 0x32, 0x36, 0x75, 0x18, 0x35,
+	0xA5, 0xE5, 0xE4, 0x43, 0x17, 0xC1, 0xC2, 0xEE,
+	0xFD, 0x4E, 0xA5, 0xBF, 0xD1, 0x60, 0x43, 0xF4,
+	0x3C, 0xB4, 0x19, 0x81, 0xF6, 0xAD, 0xEE, 0x9D,
+	0x03, 0x15, 0x9E, 0x7A, 0xD9, 0xD1, 0x3C, 0x53,
+	0x36, 0x95, 0x09, 0xFC, 0x1F, 0xA2, 0x7C, 0x16,
+	0xEF, 0x98, 0x87, 0x70, 0x3A, 0x55, 0xB5, 0x1B,
+	0x22, 0xCB, 0xF4, 0x4C, 0xD0, 0x12, 0xAE, 0xE0,
+	0xB2, 0x79, 0x8E, 0x62, 0x84, 0x23, 0x42, 0x8E,
+	0xFC, 0xD5, 0xA4, 0x0C, 0xAE, 0xF6, 0xBF, 0x50,
+	0xD8, 0xEA, 0x88, 0x5E, 0xBF, 0x73, 0xA6, 0xB9,
+	0xFD, 0x79, 0xB5, 0xE1, 0x8F, 0x67, 0xD1, 0x34,
+	0x1A, 0xC8, 0x23, 0x7A, 0x75, 0xC3, 0xCF, 0xC9,
+	0x20, 0x04, 0xA1, 0xC5, 0xA4, 0x0E, 0x36, 0x6B,
+	0xC4, 0x4D, 0x00, 0x17, 0x6A, 0xF7, 0x1C, 0x15,
+	0xE4, 0x8C, 0x86, 0xD3, 0x7E, 0x01, 0x37, 0x23,
+	0xCA, 0xAC, 0x72, 0x23, 0xAB, 0x3B, 0xF4, 0xD5,
+	0x4F, 0x18, 0x28, 0x71, 0x3B, 0x2B, 0x4A, 0x6F,
+	0xE4, 0x0F, 0xAB, 0x74, 0x40, 0x5C, 0xB7, 0x38,
+	0xB0, 0x64, 0xC0, 0x6E, 0xCC, 0x76, 0xE9, 0xEF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
 
+/*
+ * RFC 5114, 2.1.
+ * Group 22 - 1024-bit MODP Group with 160-bit Prime Order Subgroup
+ */
+static const u8 dh_group22_generator[] = {
+	0xA4, 0xD1, 0xCB, 0xD5, 0xC3, 0xFD, 0x34, 0x12,
+	0x67, 0x65, 0xA4, 0x42, 0xEF, 0xB9, 0x99, 0x05,
+	0xF8, 0x10, 0x4D, 0xD2, 0x58, 0xAC, 0x50, 0x7F,
+	0xD6, 0x40, 0x6C, 0xFF, 0x14, 0x26, 0x6D, 0x31,
+	0x26, 0x6F, 0xEA, 0x1E, 0x5C, 0x41, 0x56, 0x4B,
+	0x77, 0x7E, 0x69, 0x0F, 0x55, 0x04, 0xF2, 0x13,
+	0x16, 0x02, 0x17, 0xB4, 0xB0, 0x1B, 0x88, 0x6A,
+	0x5E, 0x91, 0x54, 0x7F, 0x9E, 0x27, 0x49, 0xF4,
+	0xD7, 0xFB, 0xD7, 0xD3, 0xB9, 0xA9, 0x2E, 0xE1,
+	0x90, 0x9D, 0x0D, 0x22, 0x63, 0xF8, 0x0A, 0x76,
+	0xA6, 0xA2, 0x4C, 0x08, 0x7A, 0x09, 0x1F, 0x53,
+	0x1D, 0xBF, 0x0A, 0x01, 0x69, 0xB6, 0xA2, 0x8A,
+	0xD6, 0x62, 0xA4, 0xD1, 0x8E, 0x73, 0xAF, 0xA3,
+	0x2D, 0x77, 0x9D, 0x59, 0x18, 0xD0, 0x8B, 0xC8,
+	0x85, 0x8F, 0x4D, 0xCE, 0xF9, 0x7C, 0x2A, 0x24,
+	0x85, 0x5E, 0x6E, 0xEB, 0x22, 0xB3, 0xB2, 0xE5
+};
+static const u8 dh_group22_prime[] = {
+	0xB1, 0x0B, 0x8F, 0x96, 0xA0, 0x80, 0xE0, 0x1D,
+	0xDE, 0x92, 0xDE, 0x5E, 0xAE, 0x5D, 0x54, 0xEC,
+	0x52, 0xC9, 0x9F, 0xBC, 0xFB, 0x06, 0xA3, 0xC6,
+	0x9A, 0x6A, 0x9D, 0xCA, 0x52, 0xD2, 0x3B, 0x61,
+	0x60, 0x73, 0xE2, 0x86, 0x75, 0xA2, 0x3D, 0x18,
+	0x98, 0x38, 0xEF, 0x1E, 0x2E, 0xE6, 0x52, 0xC0,
+	0x13, 0xEC, 0xB4, 0xAE, 0xA9, 0x06, 0x11, 0x23,
+	0x24, 0x97, 0x5C, 0x3C, 0xD4, 0x9B, 0x83, 0xBF,
+	0xAC, 0xCB, 0xDD, 0x7D, 0x90, 0xC4, 0xBD, 0x70,
+	0x98, 0x48, 0x8E, 0x9C, 0x21, 0x9A, 0x73, 0x72,
+	0x4E, 0xFF, 0xD6, 0xFA, 0xE5, 0x64, 0x47, 0x38,
+	0xFA, 0xA3, 0x1A, 0x4F, 0xF5, 0x5B, 0xCC, 0xC0,
+	0xA1, 0x51, 0xAF, 0x5F, 0x0D, 0xC8, 0xB4, 0xBD,
+	0x45, 0xBF, 0x37, 0xDF, 0x36, 0x5C, 0x1A, 0x65,
+	0xE6, 0x8C, 0xFD, 0xA7, 0x6D, 0x4D, 0xA7, 0x08,
+	0xDF, 0x1F, 0xB2, 0xBC, 0x2E, 0x4A, 0x43, 0x71
+};
+static const u8 dh_group22_order[] = {
+	0xF5, 0x18, 0xAA, 0x87, 0x81, 0xA8, 0xDF, 0x27,
+	0x8A, 0xBA, 0x4E, 0x7D, 0x64, 0xB7, 0xCB, 0x9D,
+	0x49, 0x46, 0x23, 0x53
+};
 
-#define DH_GROUP(id) \
+/*
+ * RFC 5114, 2.2.
+ * Group 23 - 2048-bit MODP Group with 224-bit Prime Order Subgroup
+ */
+static const u8 dh_group23_generator[] = {
+	0xAC, 0x40, 0x32, 0xEF, 0x4F, 0x2D, 0x9A, 0xE3,
+	0x9D, 0xF3, 0x0B, 0x5C, 0x8F, 0xFD, 0xAC, 0x50,
+	0x6C, 0xDE, 0xBE, 0x7B, 0x89, 0x99, 0x8C, 0xAF,
+	0x74, 0x86, 0x6A, 0x08, 0xCF, 0xE4, 0xFF, 0xE3,
+	0xA6, 0x82, 0x4A, 0x4E, 0x10, 0xB9, 0xA6, 0xF0,
+	0xDD, 0x92, 0x1F, 0x01, 0xA7, 0x0C, 0x4A, 0xFA,
+	0xAB, 0x73, 0x9D, 0x77, 0x00, 0xC2, 0x9F, 0x52,
+	0xC5, 0x7D, 0xB1, 0x7C, 0x62, 0x0A, 0x86, 0x52,
+	0xBE, 0x5E, 0x90, 0x01, 0xA8, 0xD6, 0x6A, 0xD7,
+	0xC1, 0x76, 0x69, 0x10, 0x19, 0x99, 0x02, 0x4A,
+	0xF4, 0xD0, 0x27, 0x27, 0x5A, 0xC1, 0x34, 0x8B,
+	0xB8, 0xA7, 0x62, 0xD0, 0x52, 0x1B, 0xC9, 0x8A,
+	0xE2, 0x47, 0x15, 0x04, 0x22, 0xEA, 0x1E, 0xD4,
+	0x09, 0x93, 0x9D, 0x54, 0xDA, 0x74, 0x60, 0xCD,
+	0xB5, 0xF6, 0xC6, 0xB2, 0x50, 0x71, 0x7C, 0xBE,
+	0xF1, 0x80, 0xEB, 0x34, 0x11, 0x8E, 0x98, 0xD1,
+	0x19, 0x52, 0x9A, 0x45, 0xD6, 0xF8, 0x34, 0x56,
+	0x6E, 0x30, 0x25, 0xE3, 0x16, 0xA3, 0x30, 0xEF,
+	0xBB, 0x77, 0xA8, 0x6F, 0x0C, 0x1A, 0xB1, 0x5B,
+	0x05, 0x1A, 0xE3, 0xD4, 0x28, 0xC8, 0xF8, 0xAC,
+	0xB7, 0x0A, 0x81, 0x37, 0x15, 0x0B, 0x8E, 0xEB,
+	0x10, 0xE1, 0x83, 0xED, 0xD1, 0x99, 0x63, 0xDD,
+	0xD9, 0xE2, 0x63, 0xE4, 0x77, 0x05, 0x89, 0xEF,
+	0x6A, 0xA2, 0x1E, 0x7F, 0x5F, 0x2F, 0xF3, 0x81,
+	0xB5, 0x39, 0xCC, 0xE3, 0x40, 0x9D, 0x13, 0xCD,
+	0x56, 0x6A, 0xFB, 0xB4, 0x8D, 0x6C, 0x01, 0x91,
+	0x81, 0xE1, 0xBC, 0xFE, 0x94, 0xB3, 0x02, 0x69,
+	0xED, 0xFE, 0x72, 0xFE, 0x9B, 0x6A, 0xA4, 0xBD,
+	0x7B, 0x5A, 0x0F, 0x1C, 0x71, 0xCF, 0xFF, 0x4C,
+	0x19, 0xC4, 0x18, 0xE1, 0xF6, 0xEC, 0x01, 0x79,
+	0x81, 0xBC, 0x08, 0x7F, 0x2A, 0x70, 0x65, 0xB3,
+	0x84, 0xB8, 0x90, 0xD3, 0x19, 0x1F, 0x2B, 0xFA
+};
+static const u8 dh_group23_prime[] = {
+	0xAD, 0x10, 0x7E, 0x1E, 0x91, 0x23, 0xA9, 0xD0,
+	0xD6, 0x60, 0xFA, 0xA7, 0x95, 0x59, 0xC5, 0x1F,
+	0xA2, 0x0D, 0x64, 0xE5, 0x68, 0x3B, 0x9F, 0xD1,
+	0xB5, 0x4B, 0x15, 0x97, 0xB6, 0x1D, 0x0A, 0x75,
+	0xE6, 0xFA, 0x14, 0x1D, 0xF9, 0x5A, 0x56, 0xDB,
+	0xAF, 0x9A, 0x3C, 0x40, 0x7B, 0xA1, 0xDF, 0x15,
+	0xEB, 0x3D, 0x68, 0x8A, 0x30, 0x9C, 0x18, 0x0E,
+	0x1D, 0xE6, 0xB8, 0x5A, 0x12, 0x74, 0xA0, 0xA6,
+	0x6D, 0x3F, 0x81, 0x52, 0xAD, 0x6A, 0xC2, 0x12,
+	0x90, 0x37, 0xC9, 0xED, 0xEF, 0xDA, 0x4D, 0xF8,
+	0xD9, 0x1E, 0x8F, 0xEF, 0x55, 0xB7, 0x39, 0x4B,
+	0x7A, 0xD5, 0xB7, 0xD0, 0xB6, 0xC1, 0x22, 0x07,
+	0xC9, 0xF9, 0x8D, 0x11, 0xED, 0x34, 0xDB, 0xF6,
+	0xC6, 0xBA, 0x0B, 0x2C, 0x8B, 0xBC, 0x27, 0xBE,
+	0x6A, 0x00, 0xE0, 0xA0, 0xB9, 0xC4, 0x97, 0x08,
+	0xB3, 0xBF, 0x8A, 0x31, 0x70, 0x91, 0x88, 0x36,
+	0x81, 0x28, 0x61, 0x30, 0xBC, 0x89, 0x85, 0xDB,
+	0x16, 0x02, 0xE7, 0x14, 0x41, 0x5D, 0x93, 0x30,
+	0x27, 0x82, 0x73, 0xC7, 0xDE, 0x31, 0xEF, 0xDC,
+	0x73, 0x10, 0xF7, 0x12, 0x1F, 0xD5, 0xA0, 0x74,
+	0x15, 0x98, 0x7D, 0x9A, 0xDC, 0x0A, 0x48, 0x6D,
+	0xCD, 0xF9, 0x3A, 0xCC, 0x44, 0x32, 0x83, 0x87,
+	0x31, 0x5D, 0x75, 0xE1, 0x98, 0xC6, 0x41, 0xA4,
+	0x80, 0xCD, 0x86, 0xA1, 0xB9, 0xE5, 0x87, 0xE8,
+	0xBE, 0x60, 0xE6, 0x9C, 0xC9, 0x28, 0xB2, 0xB9,
+	0xC5, 0x21, 0x72, 0xE4, 0x13, 0x04, 0x2E, 0x9B,
+	0x23, 0xF1, 0x0B, 0x0E, 0x16, 0xE7, 0x97, 0x63,
+	0xC9, 0xB5, 0x3D, 0xCF, 0x4B, 0xA8, 0x0A, 0x29,
+	0xE3, 0xFB, 0x73, 0xC1, 0x6B, 0x8E, 0x75, 0xB9,
+	0x7E, 0xF3, 0x63, 0xE2, 0xFF, 0xA3, 0x1F, 0x71,
+	0xCF, 0x9D, 0xE5, 0x38, 0x4E, 0x71, 0xB8, 0x1C,
+	0x0A, 0xC4, 0xDF, 0xFE, 0x0C, 0x10, 0xE6, 0x4F
+};
+static const u8 dh_group23_order[] = {
+	0x80, 0x1C, 0x0D, 0x34, 0xC5, 0x8D, 0x93, 0xFE,
+	0x99, 0x71, 0x77, 0x10, 0x1F, 0x80, 0x53, 0x5A,
+	0x47, 0x38, 0xCE, 0xBC, 0xBF, 0x38, 0x9A, 0x99,
+	0xB3, 0x63, 0x71, 0xEB
+};
+
+/*
+ * RFC 5114, 2.3.
+ * Group 24 - 2048-bit MODP Group with 256-bit Prime Order Subgroup
+ */
+static const u8 dh_group24_generator[] = {
+	0x3F, 0xB3, 0x2C, 0x9B, 0x73, 0x13, 0x4D, 0x0B,
+	0x2E, 0x77, 0x50, 0x66, 0x60, 0xED, 0xBD, 0x48,
+	0x4C, 0xA7, 0xB1, 0x8F, 0x21, 0xEF, 0x20, 0x54,
+	0x07, 0xF4, 0x79, 0x3A, 0x1A, 0x0B, 0xA1, 0x25,
+	0x10, 0xDB, 0xC1, 0x50, 0x77, 0xBE, 0x46, 0x3F,
+	0xFF, 0x4F, 0xED, 0x4A, 0xAC, 0x0B, 0xB5, 0x55,
+	0xBE, 0x3A, 0x6C, 0x1B, 0x0C, 0x6B, 0x47, 0xB1,
+	0xBC, 0x37, 0x73, 0xBF, 0x7E, 0x8C, 0x6F, 0x62,
+	0x90, 0x12, 0x28, 0xF8, 0xC2, 0x8C, 0xBB, 0x18,
+	0xA5, 0x5A, 0xE3, 0x13, 0x41, 0x00, 0x0A, 0x65,
+	0x01, 0x96, 0xF9, 0x31, 0xC7, 0x7A, 0x57, 0xF2,
+	0xDD, 0xF4, 0x63, 0xE5, 0xE9, 0xEC, 0x14, 0x4B,
+	0x77, 0x7D, 0xE6, 0x2A, 0xAA, 0xB8, 0xA8, 0x62,
+	0x8A, 0xC3, 0x76, 0xD2, 0x82, 0xD6, 0xED, 0x38,
+	0x64, 0xE6, 0x79, 0x82, 0x42, 0x8E, 0xBC, 0x83,
+	0x1D, 0x14, 0x34, 0x8F, 0x6F, 0x2F, 0x91, 0x93,
+	0xB5, 0x04, 0x5A, 0xF2, 0x76, 0x71, 0x64, 0xE1,
+	0xDF, 0xC9, 0x67, 0xC1, 0xFB, 0x3F, 0x2E, 0x55,
+	0xA4, 0xBD, 0x1B, 0xFF, 0xE8, 0x3B, 0x9C, 0x80,
+	0xD0, 0x52, 0xB9, 0x85, 0xD1, 0x82, 0xEA, 0x0A,
+	0xDB, 0x2A, 0x3B, 0x73, 0x13, 0xD3, 0xFE, 0x14,
+	0xC8, 0x48, 0x4B, 0x1E, 0x05, 0x25, 0x88, 0xB9,
+	0xB7, 0xD2, 0xBB, 0xD2, 0xDF, 0x01, 0x61, 0x99,
+	0xEC, 0xD0, 0x6E, 0x15, 0x57, 0xCD, 0x09, 0x15,
+	0xB3, 0x35, 0x3B, 0xBB, 0x64, 0xE0, 0xEC, 0x37,
+	0x7F, 0xD0, 0x28, 0x37, 0x0D, 0xF9, 0x2B, 0x52,
+	0xC7, 0x89, 0x14, 0x28, 0xCD, 0xC6, 0x7E, 0xB6,
+	0x18, 0x4B, 0x52, 0x3D, 0x1D, 0xB2, 0x46, 0xC3,
+	0x2F, 0x63, 0x07, 0x84, 0x90, 0xF0, 0x0E, 0xF8,
+	0xD6, 0x47, 0xD1, 0x48, 0xD4, 0x79, 0x54, 0x51,
+	0x5E, 0x23, 0x27, 0xCF, 0xEF, 0x98, 0xC5, 0x82,
+	0x66, 0x4B, 0x4C, 0x0F, 0x6C, 0xC4, 0x16, 0x59
+};
+static const u8 dh_group24_prime[] = {
+	0x87, 0xA8, 0xE6, 0x1D, 0xB4, 0xB6, 0x66, 0x3C,
+	0xFF, 0xBB, 0xD1, 0x9C, 0x65, 0x19, 0x59, 0x99,
+	0x8C, 0xEE, 0xF6, 0x08, 0x66, 0x0D, 0xD0, 0xF2,
+	0x5D, 0x2C, 0xEE, 0xD4, 0x43, 0x5E, 0x3B, 0x00,
+	0xE0, 0x0D, 0xF8, 0xF1, 0xD6, 0x19, 0x57, 0xD4,
+	0xFA, 0xF7, 0xDF, 0x45, 0x61, 0xB2, 0xAA, 0x30,
+	0x16, 0xC3, 0xD9, 0x11, 0x34, 0x09, 0x6F, 0xAA,
+	0x3B, 0xF4, 0x29, 0x6D, 0x83, 0x0E, 0x9A, 0x7C,
+	0x20, 0x9E, 0x0C, 0x64, 0x97, 0x51, 0x7A, 0xBD,
+	0x5A, 0x8A, 0x9D, 0x30, 0x6B, 0xCF, 0x67, 0xED,
+	0x91, 0xF9, 0xE6, 0x72, 0x5B, 0x47, 0x58, 0xC0,
+	0x22, 0xE0, 0xB1, 0xEF, 0x42, 0x75, 0xBF, 0x7B,
+	0x6C, 0x5B, 0xFC, 0x11, 0xD4, 0x5F, 0x90, 0x88,
+	0xB9, 0x41, 0xF5, 0x4E, 0xB1, 0xE5, 0x9B, 0xB8,
+	0xBC, 0x39, 0xA0, 0xBF, 0x12, 0x30, 0x7F, 0x5C,
+	0x4F, 0xDB, 0x70, 0xC5, 0x81, 0xB2, 0x3F, 0x76,
+	0xB6, 0x3A, 0xCA, 0xE1, 0xCA, 0xA6, 0xB7, 0x90,
+	0x2D, 0x52, 0x52, 0x67, 0x35, 0x48, 0x8A, 0x0E,
+	0xF1, 0x3C, 0x6D, 0x9A, 0x51, 0xBF, 0xA4, 0xAB,
+	0x3A, 0xD8, 0x34, 0x77, 0x96, 0x52, 0x4D, 0x8E,
+	0xF6, 0xA1, 0x67, 0xB5, 0xA4, 0x18, 0x25, 0xD9,
+	0x67, 0xE1, 0x44, 0xE5, 0x14, 0x05, 0x64, 0x25,
+	0x1C, 0xCA, 0xCB, 0x83, 0xE6, 0xB4, 0x86, 0xF6,
+	0xB3, 0xCA, 0x3F, 0x79, 0x71, 0x50, 0x60, 0x26,
+	0xC0, 0xB8, 0x57, 0xF6, 0x89, 0x96, 0x28, 0x56,
+	0xDE, 0xD4, 0x01, 0x0A, 0xBD, 0x0B, 0xE6, 0x21,
+	0xC3, 0xA3, 0x96, 0x0A, 0x54, 0xE7, 0x10, 0xC3,
+	0x75, 0xF2, 0x63, 0x75, 0xD7, 0x01, 0x41, 0x03,
+	0xA4, 0xB5, 0x43, 0x30, 0xC1, 0x98, 0xAF, 0x12,
+	0x61, 0x16, 0xD2, 0x27, 0x6E, 0x11, 0x71, 0x5F,
+	0x69, 0x38, 0x77, 0xFA, 0xD7, 0xEF, 0x09, 0xCA,
+	0xDB, 0x09, 0x4A, 0xE9, 0x1E, 0x1A, 0x15, 0x97
+};
+
+static const u8 dh_group24_order[] = {
+	0x8C, 0xF8, 0x36, 0x42, 0xA7, 0x09, 0xA0, 0x97,
+	0xB4, 0x47, 0x99, 0x76, 0x40, 0x12, 0x9D, 0xA2,
+	0x99, 0xB1, 0xA4, 0x7D, 0x1E, 0xB3, 0x75, 0x0B,
+	0xA3, 0x08, 0xB0, 0xFE, 0x64, 0xF5, 0xFB, 0xD3
+};
+
+#endif /* ALL_DH_GROUPS */
+
+#define DH_GROUP(id,safe) \
 { id, dh_group ## id ## _generator, sizeof(dh_group ## id ## _generator), \
-dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime) }
-		
+dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime), \
+dh_group ## id ## _order, sizeof(dh_group ## id ## _order), safe }
 
-static struct dh_group dh_groups[] = {
-	DH_GROUP(5),
+static const struct dh_group dh_groups[] = {
+	DH_GROUP(5, 1),
 #ifdef ALL_DH_GROUPS
-	DH_GROUP(1),
-	DH_GROUP(2),
-	DH_GROUP(14),
-	DH_GROUP(15),
-	DH_GROUP(16),
-	DH_GROUP(17),
-	DH_GROUP(18)
+	DH_GROUP(1, 1),
+	DH_GROUP(2, 1),
+	DH_GROUP(14, 1),
+	DH_GROUP(15, 1),
+	DH_GROUP(16, 1),
+	DH_GROUP(17, 1),
+	DH_GROUP(18, 1),
+	DH_GROUP(22, 0),
+	DH_GROUP(23, 0),
+	DH_GROUP(24, 0)
 #endif /* ALL_DH_GROUPS */
 };
 
@@ -583,8 +1234,11 @@ dh_init(const struct dh_group *dh, struct wpabuf **priv)
 
 	pv_len = dh->prime_len;
 	pv = wpabuf_alloc(pv_len);
-	if (pv == NULL)
+	if (pv == NULL) {
+		wpabuf_free(*priv);
+		*priv = NULL;
 		return NULL;
+	}
 
 	if (crypto_mod_exp(dh->generator, dh->generator_len,
 						wpabuf_head(*priv), wpabuf_len(*priv),

+ 3 - 0
components/wpa_supplicant/src/crypto/dh_groups.h

@@ -21,6 +21,9 @@ struct dh_group {
 	size_t generator_len;
 	const u8 *prime;
 	size_t prime_len;
+	const u8 *order;
+	size_t order_len;
+	unsigned int safe_prime:1;
 };
 
 const struct dh_group * dh_groups_get(int id);

+ 60 - 18
components/wpa_supplicant/src/crypto/sha256.c

@@ -42,10 +42,10 @@
  * @addr: Pointers to the data areas
  * @len: Lengths of the data blocks
  * @mac: Buffer for the hash (32 bytes)
+ * Returns: 0 on success, -1 on failure
  */
-void 
-hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
-			const u8 *addr[], const size_t *len, u8 *mac)
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
 {
 	unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */
 	unsigned char tk[32];
@@ -57,12 +57,13 @@ hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
 		 * Fixed limit on the number of fragments to avoid having to
 		 * allocate memory (which could fail).
 		 */
-		return;
+		return -1;
 	}
 
         /* if key is longer than 64 bytes reset it to key = SHA256(key) */
         if (key_len > 64) {
-		sha256_vector(1, &key, &key_len, tk);
+		if (sha256_vector(1, &key, &key_len, tk) < 0)
+			return -1;
 		key = tk;
 		key_len = 32;
         }
@@ -90,7 +91,8 @@ hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
 		_addr[i + 1] = addr[i];
 		_len[i + 1] = len[i];
 	}
-	sha256_vector(1 + num_elem, _addr, _len, mac);
+	if (sha256_vector(1 + num_elem, _addr, _len, mac) < 0)
+		return -1;
 
 	os_memset(k_pad, 0, sizeof(k_pad));
 	os_memcpy(k_pad, key, key_len);
@@ -103,10 +105,9 @@ hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
 	_len[0] = 64;
 	_addr[1] = mac;
 	_len[1] = SHA256_MAC_LEN;
-	sha256_vector(2, _addr, _len, mac);
+	return sha256_vector(2, _addr, _len, mac);
 }
 
-
 /**
  * hmac_sha256 - HMAC-SHA256 over data buffer (RFC 2104)
  * @key: Key for HMAC operations
@@ -115,14 +116,13 @@ hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
  * @data_len: Length of the data area
  * @mac: Buffer for the hash (20 bytes)
  */
-void 
+void
 hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
-		 size_t data_len, u8 *mac)
+                 size_t data_len, u8 *mac)
 {
-	hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+        hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
 }
 
-
 /**
  * sha256_prf - SHA256-based Pseudo-Random Function (IEEE 802.11r, 8.5.1.5.2)
  * @key: Key for PRF
@@ -132,13 +132,37 @@ hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
  * @data_len: Length of the data
  * @buf: Buffer for the generated pseudo-random key
  * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure
  *
  * This function is used to derive new, cryptographically separate keys from a
  * given key.
  */
-void 
-sha256_prf(const u8 *key, size_t key_len, const char *label,
+int sha256_prf(const u8 *key, size_t key_len, const char *label,
 		const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+	return sha256_prf_bits(key, key_len, label, data, data_len, buf,
+			       buf_len * 8);
+}
+
+/**
+ * sha256_prf_bits - IEEE Std 802.11-2012, 11.6.1.7.2 Key derivation function
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bits of key to generate
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key. If the requested buf_len is not divisible by eight, the least
+ * significant 1-7 bits of the last octet in the output are not part of the
+ * requested output.
+ */
+int sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
+		    const u8 *data, size_t data_len, u8 *buf,
+		    size_t buf_len_bits)
 {
 	u16 counter = 1;
 	size_t pos, plen;
@@ -146,6 +170,7 @@ sha256_prf(const u8 *key, size_t key_len, const char *label,
 	const u8 *addr[4];
 	size_t len[4];
 	u8 counter_le[2], length_le[2];
+	size_t buf_len = (buf_len_bits + 7) / 8;
 
 	addr[0] = counter_le;
 	len[0] = 2;
@@ -156,20 +181,37 @@ sha256_prf(const u8 *key, size_t key_len, const char *label,
 	addr[3] = length_le;
 	len[3] = sizeof(length_le);
 
-	WPA_PUT_LE16(length_le, buf_len * 8);
+	WPA_PUT_LE16(length_le, buf_len_bits);
 	pos = 0;
 	while (pos < buf_len) {
 		plen = buf_len - pos;
 		WPA_PUT_LE16(counter_le, counter);
 		if (plen >= SHA256_MAC_LEN) {
-			hmac_sha256_vector(key, key_len, 4, addr, len,
-					   &buf[pos]);
+			if (hmac_sha256_vector(key, key_len, 4, addr, len,
+					       &buf[pos]) < 0)
+				return -1;
 			pos += SHA256_MAC_LEN;
 		} else {
-			hmac_sha256_vector(key, key_len, 4, addr, len, hash);
+			if (hmac_sha256_vector(key, key_len, 4, addr, len,
+					       hash) < 0)
+				return -1;
 			os_memcpy(&buf[pos], hash, plen);
+			pos += plen;
 			break;
 		}
 		counter++;
 	}
+
+	/*
+	 * Mask out unused bits in the last octet if it does not use all the
+	 * bits.
+	 */
+	if (buf_len_bits % 8) {
+		u8 mask = 0xff << (8 - buf_len_bits % 8);
+		buf[pos - 1] &= mask;
+	}
+
+	os_memset(hash, 0, sizeof(hash));
+
+	return 0;
 }

+ 5 - 2
components/wpa_supplicant/src/crypto/sha256.h

@@ -17,11 +17,14 @@
 
 #define SHA256_MAC_LEN 32
 
-void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
 		      const u8 *addr[], const size_t *len, u8 *mac);
 void hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
 		 size_t data_len, u8 *mac);
-void sha256_prf(const u8 *key, size_t key_len, const char *label,
+int sha256_prf(const u8 *key, size_t key_len, const char *label,
 	      const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+int sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
+		    const u8 *data, size_t data_len, u8 *buf,
+		    size_t buf_len_bits);
 
 #endif /* SHA256_H */

+ 2 - 0
components/wpa_supplicant/src/utils/common.h

@@ -454,4 +454,6 @@ void * __hide_aliasing_typecast(void *foo);
 #define WPA_MEM_DEFINED(ptr, len) do { } while (0)
 #endif /* CONFIG_VALGRIND */
 
+#define IANA_SECP256R1 19
+
 #endif /* COMMON_H */

+ 1 - 0
components/wpa_supplicant/test/CMakeLists.txt

@@ -10,3 +10,4 @@ file(MD5 ${esp_supplicant_dir}/src/esp_supplicant/esp_wifi_driver.h WIFI_SUPPLIC
 string(SUBSTRING "${WIFI_SUPPLICANT_MD5}" 0 7 WIFI_SUPPLICANT_MD5)
 
 add_definitions(-DWIFI_SUPPLICANT_MD5=\"${WIFI_SUPPLICANT_MD5}\")
+add_definitions(-DCONFIG_WPA3_SAE)

+ 1 - 1
components/wpa_supplicant/test/component.mk

@@ -8,4 +8,4 @@ COMPONENT_SRCDIRS := .
 COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
 
 WIFI_SUPPLICANT_MD5_VAL=\"$(shell md5sum $(IDF_PATH)/components/wpa_supplicant/src/esp_supplicant/esp_wifi_driver.h | cut -c 1-7)\"
-CFLAGS+=-DWIFI_SUPPLICANT_MD5=$(WIFI_SUPPLICANT_MD5_VAL)
+CFLAGS+=-DWIFI_SUPPLICANT_MD5=$(WIFI_SUPPLICANT_MD5_VAL) -DCONFIG_WPA3_SAE

+ 270 - 0
components/wpa_supplicant/test/test_sae.c

@@ -0,0 +1,270 @@
+// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifdef CONFIG_WPA3_SAE
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <time.h>
+#include "unity.h"
+#include <string.h>
+#include "crypto/crypto.h"
+#include "../src/common/sae.h"
+#include "utils/wpabuf.h"
+
+typedef struct crypto_bignum crypto_bignum;
+
+static struct wpabuf *wpabuf_alloc2(size_t len)
+{
+    struct wpabuf *buf = (struct wpabuf *)os_zalloc(sizeof(struct wpabuf) + len);
+    if (buf == NULL)
+        return NULL;
+    buf->size = len;
+    return buf;
+}
+
+/**
+ *  * wpabuf_free - Free a wpabuf
+ *   * @buf: wpabuf buffer
+ *    */
+void wpabuf_free2(struct wpabuf *buf)
+{
+    if (buf == NULL)
+        return;
+    os_free(buf->ext_data);
+    os_free(buf);
+}
+
+
+
+
+TEST_CASE("Test SAE functionality with ECC group", "[wpa3_sae]")
+{
+    ESP_LOGI("SAE Test", "### Beginning SAE init and deinit ###");
+    {
+        /* Test init and deinit*/
+        struct sae_data sae;
+        memset(&sae, 0, sizeof(sae));
+        TEST_ASSERT(sae_set_group(&sae, IANA_SECP256R1) == 0);
+        sae_clear_temp_data(&sae);
+        sae_clear_data(&sae);
+
+    }
+    ESP_LOGI("SAE Test", "=========== Complete ============");
+
+    ESP_LOGI("SAE Test", "### Beginning SAE commit msg formation and parsing ###");
+    {
+        /* Test SAE commit msg formation and parsing*/
+        struct sae_data sae;
+        u8 addr1[ETH_ALEN] = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0x11};
+        u8 addr2[ETH_ALEN] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+        u8 pwd[] = "ESP32-WPA3";
+        struct wpabuf *buf;
+        int default_groups[] = { IANA_SECP256R1, 0 };
+
+        memset(&sae, 0, sizeof(sae));
+
+        TEST_ASSERT(sae_set_group(&sae, IANA_SECP256R1) == 0);
+
+        TEST_ASSERT(sae_prepare_commit(addr1, addr2, pwd, strlen((const char *)pwd), NULL, &sae) == 0);
+
+        buf = wpabuf_alloc2(SAE_COMMIT_MAX_LEN);
+
+        TEST_ASSERT( buf != NULL);
+
+        sae_write_commit(&sae, buf, NULL);// No anti-clogging token
+
+        /* Parsing commit created by self will be detected as reflection attack*/
+        TEST_ASSERT(sae_parse_commit(&sae,
+                    wpabuf_mhead(buf), buf->used, NULL, 0, default_groups) == SAE_SILENTLY_DISCARD);
+
+        wpabuf_free2(buf);
+        sae_clear_temp_data(&sae);
+        sae_clear_data(&sae);
+
+    }
+    ESP_LOGI("SAE Test", "=========== Complete ============");
+
+    ESP_LOGI("SAE Test", "### Beginning SAE handshake ###");
+    {
+        /* SAE handshake*/
+        struct sae_data sae1; // STA1 data
+        struct sae_data sae2; // STA2 data
+        u8 addr1[ETH_ALEN] = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0x11};
+        u8 addr2[ETH_ALEN] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+        u8 pwd[] = "ESP32-WPA3";
+
+        memset(&sae1, 0, sizeof(sae1));
+        memset(&sae2, 0, sizeof(sae2));
+
+        struct wpabuf *buf1, *buf2, *buf3, *buf4;
+        int default_groups[] = { IANA_SECP256R1, 0 };
+
+        TEST_ASSERT(sae_set_group(&sae1, IANA_SECP256R1) == 0);
+        TEST_ASSERT(sae_set_group(&sae2, IANA_SECP256R1) == 0);
+
+        /* STA1 prepares for commit*/
+        TEST_ASSERT(sae_prepare_commit(addr1, addr2, pwd, strlen((const char *)pwd), NULL, &sae1) == 0);
+
+        /* STA2 prepares for commit*/
+        TEST_ASSERT(sae_prepare_commit(addr2, addr1, pwd, strlen((const char *)pwd), NULL, &sae2) == 0);
+
+        /* STA1 creates commit msg buffer*/
+        buf1 = wpabuf_alloc2(SAE_COMMIT_MAX_LEN);
+        TEST_ASSERT( buf1 != NULL);
+        sae_write_commit(&sae1, buf1, NULL);// No anti-clogging token
+	ESP_LOG_BUFFER_HEXDUMP("SAE: Commit1", wpabuf_mhead_u8(buf1), wpabuf_len(buf1), ESP_LOG_INFO);
+
+
+        /* STA2 creates commit msg buffer*/
+        buf2 = wpabuf_alloc2(SAE_COMMIT_MAX_LEN);
+        TEST_ASSERT( buf2 != NULL);
+        sae_write_commit(&sae2, buf2, NULL);// No anti-clogging token
+        ESP_LOG_BUFFER_HEXDUMP("SAE: Commit2", wpabuf_mhead_u8(buf2), wpabuf_len(buf2), ESP_LOG_INFO);
+
+        sae1.state = SAE_COMMITTED;
+        sae2.state = SAE_COMMITTED;
+
+        /* STA1 parses STA2 commit*/
+        TEST_ASSERT(sae_parse_commit(&sae1,
+                    wpabuf_mhead(buf2), buf2->used, NULL, 0, default_groups) == 0);
+
+        /* STA2 parses STA1 commit*/
+        TEST_ASSERT(sae_parse_commit(&sae2,
+                    wpabuf_mhead(buf1), buf1->used, NULL, 0, default_groups) == 0);
+
+        /* STA1 processes commit*/
+        TEST_ASSERT(sae_process_commit(&sae1) == 0);
+
+        /* STA2 processes commit*/
+        TEST_ASSERT(sae_process_commit(&sae2) == 0);
+
+        /* STA1 creates confirm msg buffer*/
+        buf3 = wpabuf_alloc2(SAE_COMMIT_MAX_LEN);
+        TEST_ASSERT( buf3 != NULL);
+        sae_write_confirm(&sae1, buf3);
+        ESP_LOG_BUFFER_HEXDUMP("SAE: Confirm1", wpabuf_mhead_u8(buf3), wpabuf_len(buf3), ESP_LOG_INFO);
+
+        /* STA2 creates confirm msg buffer*/
+        buf4 = wpabuf_alloc2(SAE_COMMIT_MAX_LEN);
+        TEST_ASSERT( buf3 != NULL);
+        sae_write_confirm(&sae2, buf4);
+        ESP_LOG_BUFFER_HEXDUMP("SAE: Confirm2", wpabuf_mhead_u8(buf4), wpabuf_len(buf4), ESP_LOG_INFO);
+
+        /* STA1 checks confirm from STA2*/
+        TEST_ASSERT(sae_check_confirm(&sae1, wpabuf_mhead(buf4), buf4->used) == 0);
+
+        /* STA2 checks confirm from STA1*/
+        TEST_ASSERT(sae_check_confirm(&sae2, wpabuf_mhead(buf3), buf3->used) == 0);
+
+        ESP_LOG_BUFFER_HEXDUMP("SAE: PMK1", sae1.pmk, SAE_PMK_LEN, ESP_LOG_INFO);
+        ESP_LOG_BUFFER_HEXDUMP("SAE: PMK2", sae2.pmk, SAE_PMK_LEN, ESP_LOG_INFO);
+
+        wpabuf_free2(buf1);
+        wpabuf_free2(buf2);
+        wpabuf_free2(buf3);
+        wpabuf_free2(buf4);
+        sae_clear_temp_data(&sae1);
+        sae_clear_temp_data(&sae2);
+        sae_clear_data(&sae1);
+        sae_clear_data(&sae2);
+
+    }
+    ESP_LOGI("SAE Test", "=========== Complete ============");
+
+    ESP_LOGI("SAE Test", "### SAE handshake negative testcase. ###");
+    {
+        /* SAE handshake failure when different passwords are used.*/
+        struct sae_data sae1; // STA1 data
+        struct sae_data sae2; // STA2 data
+        u8 addr1[ETH_ALEN] = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0x11};
+        u8 addr2[ETH_ALEN] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+        u8 pwd1[] = "abcd1234";
+        u8 pwd2[] = "wxyz5678";
+
+        memset(&sae1, 0, sizeof(sae1));
+        memset(&sae2, 0, sizeof(sae2));
+
+        struct wpabuf *buf1, *buf2, *buf3, *buf4;
+        int default_groups[] = { IANA_SECP256R1, 0 };
+
+        TEST_ASSERT(sae_set_group(&sae1, IANA_SECP256R1) == 0);
+        TEST_ASSERT(sae_set_group(&sae2, IANA_SECP256R1) == 0);
+
+        /* STA1 prepares for commit*/
+        TEST_ASSERT(sae_prepare_commit(addr1, addr2, pwd1, strlen((const char *)pwd), NULL, &sae1) == 0);
+
+        /* STA2 prepares for commit*/
+        TEST_ASSERT(sae_prepare_commit(addr2, addr1, pwd2, strlen((const char *)pwd), NULL, &sae2) == 0);
+
+        /* STA1 creates commit msg buffer*/
+        buf1 = wpabuf_alloc2(SAE_COMMIT_MAX_LEN);
+        TEST_ASSERT( buf1 != NULL);
+        sae_write_commit(&sae1, buf1, NULL);// No anti-clogging token
+
+        /* STA2 creates commit msg buffer*/
+        buf2 = wpabuf_alloc2(SAE_COMMIT_MAX_LEN);
+        TEST_ASSERT( buf2 != NULL);
+        sae_write_commit(&sae2, buf2, NULL);// No anti-clogging token
+
+        sae1.state = SAE_COMMITTED;
+        sae2.state = SAE_COMMITTED;
+
+        /* STA1 parses STA2 commit*/
+        TEST_ASSERT(sae_parse_commit(&sae1,
+                    wpabuf_mhead(buf2), buf2->used, NULL, 0, default_groups) == 0);
+
+        /* STA2 parses STA1 commit*/
+        TEST_ASSERT(sae_parse_commit(&sae2,
+                    wpabuf_mhead(buf1), buf1->used, NULL, 0, default_groups) == 0);
+
+        /* STA1 processes commit*/
+        TEST_ASSERT(sae_process_commit(&sae1) == 0);
+
+        /* STA2 processes commit*/
+        TEST_ASSERT(sae_process_commit(&sae2) == 0);
+
+        /* STA1 creates confirm msg buffer*/
+        buf3 = wpabuf_alloc2(SAE_COMMIT_MAX_LEN);
+        TEST_ASSERT( buf3 != NULL);
+        sae_write_confirm(&sae1, buf3);
+
+        /* STA2 creates confirm msg buffer*/
+        buf4 = wpabuf_alloc2(SAE_COMMIT_MAX_LEN);
+        TEST_ASSERT( buf3 != NULL);
+        sae_write_confirm(&sae2, buf4);
+
+        /* STA1 checks confirm from STA2 and the check fails*/
+        TEST_ASSERT(sae_check_confirm(&sae1, wpabuf_mhead(buf4), buf4->used) != 0);
+
+        /* STA2 checks confirm from STA1 and the check fails*/
+        TEST_ASSERT(sae_check_confirm(&sae2, wpabuf_mhead(buf3), buf3->used) != 0);
+
+        wpabuf_free2(buf1);
+        wpabuf_free2(buf2);
+        wpabuf_free2(buf3);
+        wpabuf_free2(buf4);
+        sae_clear_temp_data(&sae1);
+        sae_clear_temp_data(&sae2);
+        sae_clear_data(&sae1);
+        sae_clear_data(&sae2);
+
+    }
+    ESP_LOGI("SAE Test", "=========== Complete ============");
+
+}
+
+#endif /* CONFIG_WPA3_SAE */