Browse Source

DPP: Authentication exchange

Add wpa_supplicant control interface commands for managing DPP
Authentication exchange.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
Jouni Malinen 7 years ago
parent
commit
30d27b048e

+ 1894 - 0
src/common/dpp.c

@@ -12,7 +12,14 @@
 #include "utils/common.h"
 #include "utils/base64.h"
 #include "common/ieee802_11_common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
 #include "crypto/crypto.h"
+#include "crypto/random.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "crypto/sha384.h"
+#include "crypto/sha512.h"
 #include "dpp.h"
 
 
@@ -29,6 +36,224 @@ static const struct dpp_curve_params dpp_curves[] = {
 };
 
 
+static struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix)
+{
+	int len, res;
+	EC_KEY *eckey;
+	struct wpabuf *buf;
+	unsigned char *pos;
+
+	eckey = EVP_PKEY_get1_EC_KEY(pkey);
+	if (!eckey)
+		return NULL;
+	EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED);
+	len = i2o_ECPublicKey(eckey, NULL);
+	if (len <= 0) {
+		wpa_printf(MSG_ERROR,
+			   "DDP: Failed to determine public key encoding length");
+		EC_KEY_free(eckey);
+		return NULL;
+	}
+
+	buf = wpabuf_alloc(len);
+	if (!buf) {
+		EC_KEY_free(eckey);
+		return NULL;
+	}
+
+	pos = wpabuf_put(buf, len);
+	res = i2o_ECPublicKey(eckey, &pos);
+	EC_KEY_free(eckey);
+	if (res != len) {
+		wpa_printf(MSG_ERROR,
+			   "DDP: Failed to encode public key (res=%d/%d)",
+			   res, len);
+		wpabuf_free(buf);
+		return NULL;
+	}
+
+	if (!prefix) {
+		/* Remove 0x04 prefix to match DPP definition */
+		pos = wpabuf_mhead(buf);
+		os_memmove(pos, pos + 1, len - 1);
+		buf->used--;
+	}
+
+	return buf;
+}
+
+
+static EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group,
+					     const u8 *buf_x, const u8 *buf_y,
+					     size_t len)
+{
+	EC_KEY *eckey = NULL;
+	BN_CTX *ctx;
+	EC_POINT *point = NULL;
+	BIGNUM *x = NULL, *y = NULL;
+	EVP_PKEY *pkey = NULL;
+
+	ctx = BN_CTX_new();
+	if (!ctx) {
+		wpa_printf(MSG_ERROR, "DPP: Out of memory");
+		return NULL;
+	}
+
+	point = EC_POINT_new(group);
+	x = BN_bin2bn(buf_x, len, NULL);
+	y = BN_bin2bn(buf_y, len, NULL);
+	if (!point || !x || !y) {
+		wpa_printf(MSG_ERROR, "DPP: Out of memory");
+		goto fail;
+	}
+
+	if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx)) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	if (!EC_POINT_is_on_curve(group, point, ctx) ||
+	    EC_POINT_is_at_infinity(group, point)) {
+		wpa_printf(MSG_ERROR, "DPP: Invalid point");
+		goto fail;
+	}
+
+	eckey = EC_KEY_new();
+	if (!eckey ||
+	    EC_KEY_set_group(eckey, group) != 1 ||
+	    EC_KEY_set_public_key(eckey, point) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to set EC_KEY: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
+
+	pkey = EVP_PKEY_new();
+	if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) {
+		wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY");
+		goto fail;
+	}
+
+out:
+	BN_free(x);
+	BN_free(y);
+	EC_KEY_free(eckey);
+	EC_POINT_free(point);
+	BN_CTX_free(ctx);
+	return pkey;
+fail:
+	EVP_PKEY_free(pkey);
+	pkey = NULL;
+	goto out;
+}
+
+
+static EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key,
+				       const u8 *buf, size_t len)
+{
+	EC_KEY *eckey;
+	const EC_GROUP *group;
+	EVP_PKEY *pkey = NULL;
+
+	if (len & 1)
+		return NULL;
+
+	eckey = EVP_PKEY_get1_EC_KEY(group_key);
+	if (!eckey) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Could not get EC_KEY from group_key");
+		return NULL;
+	}
+
+	group = EC_KEY_get0_group(eckey);
+	if (group)
+		pkey = dpp_set_pubkey_point_group(group, buf, buf + len / 2,
+						  len / 2);
+	else
+		wpa_printf(MSG_ERROR, "DPP: Could not get EC group");
+
+	EC_KEY_free(eckey);
+	return pkey;
+}
+
+
+struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type,
+			      size_t len)
+{
+	struct wpabuf *msg;
+
+	msg = wpabuf_alloc(7 + len);
+	if (!msg)
+		return NULL;
+	wpabuf_put_u8(msg, WLAN_ACTION_PUBLIC);
+	wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC);
+	wpabuf_put_be24(msg, OUI_WFA);
+	wpabuf_put_u8(msg, DPP_OUI_TYPE);
+	wpabuf_put_u8(msg, type);
+	return msg;
+}
+
+
+const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len)
+{
+	u16 id, alen;
+	const u8 *pos = buf, *end = buf + len;
+
+	while (end - pos >= 4) {
+		id = WPA_GET_LE16(pos);
+		pos += 2;
+		alen = WPA_GET_LE16(pos);
+		pos += 2;
+		if (alen > end - pos)
+			return NULL;
+		if (id == req_id) {
+			*ret_len = alen;
+			return pos;
+		}
+		pos += alen;
+	}
+
+	return NULL;
+}
+
+
+int dpp_check_attrs(const u8 *buf, size_t len)
+{
+	const u8 *pos, *end;
+
+	pos = buf;
+	end = buf + len;
+	while (end - pos >= 4) {
+		u16 id, alen;
+
+		id = WPA_GET_LE16(pos);
+		pos += 2;
+		alen = WPA_GET_LE16(pos);
+		pos += 2;
+		wpa_printf(MSG_MSGDUMP, "DPP: Attribute ID %04x len %u",
+			   id, alen);
+		if (alen > end - pos) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Truncated message - not enough room for the attribute - dropped");
+			return -1;
+		}
+		pos += alen;
+	}
+
+	if (end != pos) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected octets (%d) after the last attribute",
+			   (int) (end - pos));
+		return -1;
+	}
+
+	return 0;
+}
+
+
 void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info)
 {
 	if (!info)
@@ -219,6 +444,8 @@ static int dpp_parse_uri_pk(struct dpp_bootstrap_info *bi, const char *info)
 		wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
 		return -1;
 	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Public key hash",
+		    bi->pubkey_hash, SHA256_MAC_LEN);
 
 	/* DER encoded ASN.1 SubjectPublicKeyInfo
 	 *
@@ -630,3 +857,1670 @@ fail:
 	OPENSSL_free(der);
 	return NULL;
 }
+
+
+static int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1,
+			 unsigned int hash_len)
+{
+	u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+	const char *info = "first intermediate key";
+	int res = -1;
+
+	/* k1 = HKDF(<>, "first intermediate key", M.x) */
+
+	/* HKDF-Extract(<>, M.x) */
+	os_memset(salt, 0, hash_len);
+	if (hash_len == 32) {
+		if (hmac_sha256(salt, SHA256_MAC_LEN, Mx, Mx_len, prk) < 0)
+			return -1;
+	} else if (hash_len == 48) {
+		if (hmac_sha384(salt, SHA384_MAC_LEN, Mx, Mx_len, prk) < 0)
+			return -1;
+	} else if (hash_len == 64) {
+		if (hmac_sha512(salt, SHA512_MAC_LEN, Mx, Mx_len, prk) < 0)
+			return -1;
+	} else {
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=M.x)",
+			prk, hash_len);
+
+	/* HKDF-Expand(PRK, info, L) */
+	if (hash_len == 32)
+		res = hmac_sha256_kdf(prk, SHA256_MAC_LEN, NULL,
+				      (const u8 *) info, os_strlen(info),
+				      k1, SHA256_MAC_LEN);
+	else if (hash_len == 48)
+		res = hmac_sha384_kdf(prk, SHA384_MAC_LEN, NULL,
+				      (const u8 *) info, os_strlen(info),
+				      k1, SHA384_MAC_LEN);
+	else if (hash_len == 64)
+		res = hmac_sha512_kdf(prk, SHA512_MAC_LEN, NULL,
+				      (const u8 *) info, os_strlen(info),
+				      k1, SHA512_MAC_LEN);
+	os_memset(prk, 0, hash_len);
+	if (res < 0)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: k1 = HKDF-Expand(PRK, info, L)",
+			k1, hash_len);
+	return 0;
+}
+
+
+static int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2,
+			 unsigned int hash_len)
+{
+	u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+	const char *info = "second intermediate key";
+	int res;
+
+	/* k2 = HKDF(<>, "second intermediate key", N.x) */
+
+	/* HKDF-Extract(<>, N.x) */
+	os_memset(salt, 0, hash_len);
+	if (hash_len == 32)
+		res = hmac_sha256(salt, SHA256_MAC_LEN, Nx, Nx_len, prk);
+	else if (hash_len == 48)
+		res = hmac_sha384(salt, SHA384_MAC_LEN, Nx, Nx_len, prk);
+	else if (hash_len == 64)
+		res = hmac_sha512(salt, SHA512_MAC_LEN, Nx, Nx_len, prk);
+	else
+		res = -1;
+	if (res < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
+			prk, hash_len);
+
+	/* HKDF-Expand(PRK, info, L) */
+	if (hash_len == 32)
+		res = hmac_sha256_kdf(prk, SHA256_MAC_LEN, NULL,
+				      (const u8 *) info, os_strlen(info),
+				      k2, SHA256_MAC_LEN);
+	else if (hash_len == 48)
+		res = hmac_sha384_kdf(prk, SHA384_MAC_LEN, NULL,
+				      (const u8 *) info, os_strlen(info),
+				      k2, SHA384_MAC_LEN);
+	else if (hash_len == 64)
+		res = hmac_sha512_kdf(prk, SHA512_MAC_LEN, NULL,
+				      (const u8 *) info, os_strlen(info),
+				      k2, SHA512_MAC_LEN);
+	os_memset(prk, 0, hash_len);
+	if (res < 0)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: k2 = HKDF-Expand(PRK, info, L)",
+			k2, hash_len);
+	return 0;
+}
+
+
+static int dpp_derive_ke(struct dpp_authentication *auth, u8 *ke,
+			 unsigned int hash_len)
+{
+	size_t nonce_len;
+	u8 nonces[2 * DPP_MAX_NONCE_LEN];
+	const char *info_ke = "DPP Key";
+	u8 prk[DPP_MAX_HASH_LEN];
+	int res;
+	const u8 *addr[3];
+	size_t len[3];
+	size_t num_elem = 0;
+
+	/* ke = HKDF(I-nonce | R-nonce, "DPP Key", M.x | N.x [| L.x]) */
+
+	/* HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x]) */
+	nonce_len = auth->curve->nonce_len;
+	os_memcpy(nonces, auth->i_nonce, nonce_len);
+	os_memcpy(&nonces[nonce_len], auth->r_nonce, nonce_len);
+	addr[num_elem] = auth->Mx;
+	len[num_elem] = auth->secret_len;
+	num_elem++;
+	addr[num_elem] = auth->Nx;
+	len[num_elem] = auth->secret_len;
+	num_elem++;
+	if (auth->peer_bi && auth->own_bi) {
+		addr[num_elem] = auth->Lx;
+		len[num_elem] = auth->secret_len;
+		num_elem++;
+	}
+	if (hash_len == 32)
+		res = hmac_sha256_vector(nonces, 2 * nonce_len,
+					 num_elem, addr, len, prk);
+	else if (hash_len == 48)
+		res = hmac_sha384_vector(nonces, 2 * nonce_len,
+					 num_elem, addr, len, prk);
+	else if (hash_len == 64)
+		res = hmac_sha512_vector(nonces, 2 * nonce_len,
+					 num_elem, addr, len, prk);
+	else
+		res = -1;
+	if (res < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
+			prk, hash_len);
+
+	/* HKDF-Expand(PRK, info, L) */
+	if (hash_len == 32)
+		res = hmac_sha256_kdf(prk, SHA256_MAC_LEN, NULL,
+				      (const u8 *) info_ke, os_strlen(info_ke),
+				      ke, SHA256_MAC_LEN);
+	else if (hash_len == 48)
+		res = hmac_sha384_kdf(prk, SHA384_MAC_LEN, NULL,
+				      (const u8 *) info_ke, os_strlen(info_ke),
+				      ke, SHA384_MAC_LEN);
+	else if (hash_len == 64)
+		res = hmac_sha512_kdf(prk, SHA512_MAC_LEN, NULL,
+				      (const u8 *) info_ke, os_strlen(info_ke),
+				      ke, SHA512_MAC_LEN);
+	os_memset(prk, 0, hash_len);
+	if (res < 0)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ke = HKDF-Expand(PRK, info, L)",
+			ke, hash_len);
+	return 0;
+}
+
+
+struct dpp_authentication * dpp_auth_init(void *msg_ctx,
+					  struct dpp_bootstrap_info *peer_bi,
+					  struct dpp_bootstrap_info *own_bi,
+					  int configurator)
+{
+	struct dpp_authentication *auth;
+	size_t nonce_len;
+	EVP_PKEY_CTX *ctx = NULL;
+	size_t secret_len;
+	struct wpabuf *msg, *pi = NULL;
+	u8 clear[4 + DPP_MAX_NONCE_LEN + 4 + 1];
+	u8 wrapped_data[4 + DPP_MAX_NONCE_LEN + 4 + 1 + AES_BLOCK_SIZE];
+	u8 *pos;
+	const u8 *addr[1];
+	size_t len[1], siv_len;
+
+	auth = os_zalloc(sizeof(*auth));
+	if (!auth)
+		return NULL;
+	auth->msg_ctx = msg_ctx;
+	auth->initiator = 1;
+	auth->configurator = configurator;
+	auth->peer_bi = peer_bi;
+	auth->own_bi = own_bi;
+	auth->curve = peer_bi->curve;
+
+	nonce_len = auth->curve->nonce_len;
+	if (random_get_bytes(auth->i_nonce, nonce_len)) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", auth->i_nonce, nonce_len);
+
+	auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+	if (!auth->own_protocol_key)
+		goto fail;
+
+	pi = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+	if (!pi)
+		goto fail;
+
+	/* ECDH: M = pI * BR */
+	ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, auth->peer_bi->pubkey) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
+	    secret_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	auth->secret_len = secret_len;
+	EVP_PKEY_CTX_free(ctx);
+	ctx = NULL;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
+			auth->Mx, auth->secret_len);
+
+	if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
+			  auth->curve->hash_len) < 0)
+		goto fail;
+
+	/* Build DPP Authentication Request frame attributes */
+	msg = wpabuf_alloc(2 * (4 + SHA256_MAC_LEN) + 4 + wpabuf_len(pi) +
+			   4 + sizeof(wrapped_data));
+	if (!msg)
+		goto fail;
+	auth->req_attr = msg;
+
+	/* Responder Bootstrapping Key Hash */
+	wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
+	wpabuf_put_le16(msg, SHA256_MAC_LEN);
+	wpabuf_put_data(msg, auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+
+	/* Initiator Bootstrapping Key Hash */
+	wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
+	wpabuf_put_le16(msg, SHA256_MAC_LEN);
+	if (auth->own_bi)
+		wpabuf_put_data(msg, auth->own_bi->pubkey_hash, SHA256_MAC_LEN);
+	else
+		os_memset(wpabuf_put(msg, SHA256_MAC_LEN), 0, SHA256_MAC_LEN);
+
+	/* Initiator Protocol Key */
+	wpabuf_put_le16(msg, DPP_ATTR_I_PROTOCOL_KEY);
+	wpabuf_put_le16(msg, wpabuf_len(pi));
+	wpabuf_put_buf(msg, pi);
+	wpabuf_free(pi);
+	pi = NULL;
+
+	/* Wrapped data ({I-nonce, I-capabilities}k1) */
+	pos = clear;
+	/* I-nonce */
+	WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+	pos += 2;
+	WPA_PUT_LE16(pos, nonce_len);
+	pos += 2;
+	os_memcpy(pos, auth->i_nonce, nonce_len);
+	pos += nonce_len;
+	/* I-capabilities */
+	WPA_PUT_LE16(pos, DPP_ATTR_I_CAPABILITIES);
+	pos += 2;
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	auth->i_capab = configurator ? DPP_CAPAB_CONFIGURATOR :
+		DPP_CAPAB_ENROLLEE;
+	*pos++ = auth->i_capab;
+
+	addr[0] = wpabuf_head(msg);
+	len[0] = wpabuf_len(msg);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]);
+	siv_len = pos - clear;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
+	if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len,
+			    1, addr, len, wrapped_data) < 0)
+		goto fail;
+	siv_len += AES_BLOCK_SIZE;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, siv_len);
+
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, siv_len);
+	wpabuf_put_data(msg, wrapped_data, siv_len);
+
+	wpa_hexdump_buf(MSG_DEBUG,
+			"DPP: Authentication Request frame attributes", msg);
+
+	return auth;
+fail:
+	wpabuf_free(pi);
+	EVP_PKEY_CTX_free(ctx);
+	dpp_auth_deinit(auth);
+	return NULL;
+}
+
+
+static void dpp_auth_success(struct dpp_authentication *auth)
+{
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Authentication success - clear temporary keys");
+	os_memset(auth->Mx, 0, sizeof(auth->Mx));
+	os_memset(auth->Nx, 0, sizeof(auth->Nx));
+	os_memset(auth->Lx, 0, sizeof(auth->Lx));
+	os_memset(auth->k1, 0, sizeof(auth->k1));
+	os_memset(auth->k2, 0, sizeof(auth->k2));
+
+	auth->auth_success = 1;
+}
+
+
+static int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth)
+{
+	struct wpabuf *pix, *prx, *bix, *brx;
+	const u8 *addr[7];
+	size_t len[7];
+	size_t i, num_elem = 0;
+	size_t nonce_len;
+	u8 zero = 0;
+	int res = -1;
+
+	/* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+	nonce_len = auth->curve->nonce_len;
+
+	if (auth->initiator) {
+		pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+		prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+		if (auth->own_bi)
+			bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+		else
+			bix = NULL;
+		brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+	} else {
+		pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+		prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+		if (auth->peer_bi)
+			bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+		else
+			bix = NULL;
+		brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+	}
+	if (!pix || !prx || !brx)
+		goto fail;
+
+	addr[num_elem] = auth->i_nonce;
+	len[num_elem] = nonce_len;
+	num_elem++;
+
+	addr[num_elem] = auth->r_nonce;
+	len[num_elem] = nonce_len;
+	num_elem++;
+
+	addr[num_elem] = wpabuf_head(pix);
+	len[num_elem] = wpabuf_len(pix) / 2;
+	num_elem++;
+
+	addr[num_elem] = wpabuf_head(prx);
+	len[num_elem] = wpabuf_len(prx) / 2;
+	num_elem++;
+
+	if (bix) {
+		addr[num_elem] = wpabuf_head(bix);
+		len[num_elem] = wpabuf_len(bix) / 2;
+		num_elem++;
+	}
+
+	addr[num_elem] = wpabuf_head(brx);
+	len[num_elem] = wpabuf_len(brx) / 2;
+	num_elem++;
+
+	addr[num_elem] = &zero;
+	len[num_elem] = 1;
+	num_elem++;
+
+	wpa_printf(MSG_DEBUG, "DPP: R-auth hash components");
+	for (i = 0; i < num_elem; i++)
+		wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
+	if (auth->curve->hash_len == 32)
+		res = sha256_vector(num_elem, addr, len, r_auth);
+	else if (auth->curve->hash_len == 48)
+		res = sha384_vector(num_elem, addr, len, r_auth);
+	else if (auth->curve->hash_len == 64)
+		res = sha512_vector(num_elem, addr, len, r_auth);
+	else
+		res = -1;
+	if (res == 0)
+		wpa_hexdump(MSG_DEBUG, "DPP: R-auth", r_auth,
+			    auth->curve->hash_len);
+fail:
+	wpabuf_free(pix);
+	wpabuf_free(prx);
+	wpabuf_free(bix);
+	wpabuf_free(brx);
+	return res;
+}
+
+
+static int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth)
+{
+	struct wpabuf *pix = NULL, *prx = NULL, *bix = NULL, *brx = NULL;
+	const u8 *addr[7];
+	size_t len[7];
+	size_t i, num_elem = 0;
+	size_t nonce_len;
+	u8 one = 1;
+	int res = -1;
+
+	/* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
+	nonce_len = auth->curve->nonce_len;
+
+	if (auth->initiator) {
+		pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+		prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+		if (auth->own_bi)
+			bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+		else
+			bix = NULL;
+		if (!auth->peer_bi)
+			goto fail;
+		brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+	} else {
+		pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+		prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+		if (auth->peer_bi)
+			bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+		else
+			bix = NULL;
+		if (!auth->own_bi)
+			goto fail;
+		brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+	}
+	if (!pix || !prx || !brx)
+		goto fail;
+
+	addr[num_elem] = auth->r_nonce;
+	len[num_elem] = nonce_len;
+	num_elem++;
+
+	addr[num_elem] = auth->i_nonce;
+	len[num_elem] = nonce_len;
+	num_elem++;
+
+	addr[num_elem] = wpabuf_head(prx);
+	len[num_elem] = wpabuf_len(prx) / 2;
+	num_elem++;
+
+	addr[num_elem] = wpabuf_head(pix);
+	len[num_elem] = wpabuf_len(pix) / 2;
+	num_elem++;
+
+	addr[num_elem] = wpabuf_head(brx);
+	len[num_elem] = wpabuf_len(brx) / 2;
+	num_elem++;
+
+	if (bix) {
+		addr[num_elem] = wpabuf_head(bix);
+		len[num_elem] = wpabuf_len(bix) / 2;
+		num_elem++;
+	}
+
+	addr[num_elem] = &one;
+	len[num_elem] = 1;
+	num_elem++;
+
+	wpa_printf(MSG_DEBUG, "DPP: I-auth hash components");
+	for (i = 0; i < num_elem; i++)
+		wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
+	if (auth->curve->hash_len == 32)
+		res = sha256_vector(num_elem, addr, len, i_auth);
+	else if (auth->curve->hash_len == 48)
+		res = sha384_vector(num_elem, addr, len, i_auth);
+	else if (auth->curve->hash_len == 64)
+		res = sha512_vector(num_elem, addr, len, i_auth);
+	else
+		res = -1;
+	if (res == 0)
+		wpa_hexdump(MSG_DEBUG, "DPP: I-auth", i_auth,
+			    auth->curve->hash_len);
+fail:
+	wpabuf_free(pix);
+	wpabuf_free(prx);
+	wpabuf_free(bix);
+	wpabuf_free(brx);
+	return res;
+}
+
+
+static int dpp_auth_derive_l_responder(struct dpp_authentication *auth)
+{
+	const EC_GROUP *group;
+	EC_POINT *l = NULL;
+	EC_KEY *BI = NULL, *bR = NULL, *pR = NULL;
+	const EC_POINT *BI_point;
+	BN_CTX *bnctx;
+	BIGNUM *lx, *sum, *q;
+	const BIGNUM *bR_bn, *pR_bn;
+	int ret = -1;
+	int num_bytes, offset;
+
+	/* L = ((bR + pR) modulo q) * BI */
+
+	bnctx = BN_CTX_new();
+	sum = BN_new();
+	q = BN_new();
+	lx = BN_new();
+	if (!bnctx || !sum || !q || !lx)
+		goto fail;
+	BI = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey);
+	if (!BI)
+		goto fail;
+	BI_point = EC_KEY_get0_public_key(BI);
+	group = EC_KEY_get0_group(BI);
+	if (!group)
+		goto fail;
+
+	bR = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey);
+	pR = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key);
+	if (!bR || !pR)
+		goto fail;
+	bR_bn = EC_KEY_get0_private_key(bR);
+	pR_bn = EC_KEY_get0_private_key(pR);
+	if (!bR_bn || !pR_bn)
+		goto fail;
+	if (EC_GROUP_get_order(group, q, bnctx) != 1 ||
+	    BN_mod_add(sum, bR_bn, pR_bn, q, bnctx) != 1)
+		goto fail;
+	l = EC_POINT_new(group);
+	if (!l ||
+	    EC_POINT_mul(group, l, NULL, BI_point, sum, bnctx) != 1 ||
+	    EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
+						bnctx) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	num_bytes = BN_num_bytes(lx);
+	if ((size_t) num_bytes > auth->secret_len)
+		goto fail;
+	if (auth->secret_len > (size_t) num_bytes)
+		offset = auth->secret_len - num_bytes;
+	else
+		offset = 0;
+
+	os_memset(auth->Lx, 0, offset);
+	BN_bn2bin(lx, auth->Lx + offset);
+	wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
+	ret = 0;
+fail:
+	EC_POINT_clear_free(l);
+	EC_KEY_free(BI);
+	EC_KEY_free(bR);
+	EC_KEY_free(pR);
+	BN_clear_free(lx);
+	BN_clear_free(sum);
+	BN_free(q);
+	BN_CTX_free(bnctx);
+	return ret;
+}
+
+
+static int dpp_auth_derive_l_initiator(struct dpp_authentication *auth)
+{
+	const EC_GROUP *group;
+	EC_POINT *l = NULL, *sum = NULL;
+	EC_KEY *bI = NULL, *BR = NULL, *PR = NULL;
+	const EC_POINT *BR_point, *PR_point;
+	BN_CTX *bnctx;
+	BIGNUM *lx;
+	const BIGNUM *bI_bn;
+	int ret = -1;
+	int num_bytes, offset;
+
+	/* L = bI * (BR + PR) */
+
+	bnctx = BN_CTX_new();
+	lx = BN_new();
+	if (!bnctx || !lx)
+		goto fail;
+	BR = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey);
+	PR = EVP_PKEY_get1_EC_KEY(auth->peer_protocol_key);
+	if (!BR || !PR)
+		goto fail;
+	BR_point = EC_KEY_get0_public_key(BR);
+	PR_point = EC_KEY_get0_public_key(PR);
+
+	bI = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey);
+	if (!bI)
+		goto fail;
+	group = EC_KEY_get0_group(bI);
+	bI_bn = EC_KEY_get0_private_key(bI);
+	if (!group || !bI_bn)
+		goto fail;
+	sum = EC_POINT_new(group);
+	l = EC_POINT_new(group);
+	if (!sum || !l ||
+	    EC_POINT_add(group, sum, BR_point, PR_point, bnctx) != 1 ||
+	    EC_POINT_mul(group, l, NULL, sum, bI_bn, bnctx) != 1 ||
+	    EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
+						bnctx) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	num_bytes = BN_num_bytes(lx);
+	if ((size_t) num_bytes > auth->secret_len)
+		goto fail;
+	if (auth->secret_len > (size_t) num_bytes)
+		offset = auth->secret_len - num_bytes;
+	else
+		offset = 0;
+
+	os_memset(auth->Lx, 0, offset);
+	BN_bn2bin(lx, auth->Lx + offset);
+	wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
+	ret = 0;
+fail:
+	EC_POINT_clear_free(l);
+	EC_KEY_free(bI);
+	EC_KEY_free(BR);
+	EC_KEY_free(PR);
+	BN_clear_free(lx);
+	BN_CTX_free(bnctx);
+	return ret;
+}
+
+
+static int dpp_auth_build_resp(struct dpp_authentication *auth)
+{
+	size_t nonce_len;
+	EVP_PKEY_CTX *ctx = NULL;
+	size_t secret_len;
+	struct wpabuf *msg, *pr = NULL;
+	u8 r_auth[4 + DPP_MAX_HASH_LEN];
+	u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE];
+#define DPP_AUTH_RESP_CLEAR_LEN 2 * (4 + DPP_MAX_NONCE_LEN) + 4 + 1 + \
+		4 + sizeof(wrapped_r_auth)
+	size_t wrapped_r_auth_len;
+	u8 clear[DPP_AUTH_RESP_CLEAR_LEN];
+	u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN + AES_BLOCK_SIZE];
+	u8 *pos;
+	const u8 *addr[1];
+	size_t len[1], siv_len;
+
+	wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
+
+	nonce_len = auth->curve->nonce_len;
+	if (random_get_bytes(auth->r_nonce, nonce_len)) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", auth->r_nonce, nonce_len);
+
+	auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+	if (!auth->own_protocol_key)
+		goto fail;
+
+	pr = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+	if (!pr)
+		goto fail;
+
+	/* ECDH: N = pR * PI */
+	ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, auth->peer_protocol_key) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
+	    secret_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	EVP_PKEY_CTX_free(ctx);
+	ctx = NULL;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
+			auth->Nx, auth->secret_len);
+
+	if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
+			  auth->curve->hash_len) < 0)
+		goto fail;
+
+	if (auth->own_bi && auth->peer_bi) {
+		/* Mutual authentication */
+		if (dpp_auth_derive_l_responder(auth) < 0)
+			goto fail;
+	}
+
+	if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0)
+		goto fail;
+
+	/* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+	WPA_PUT_LE16(r_auth, DPP_ATTR_R_AUTH_TAG);
+	WPA_PUT_LE16(&r_auth[2], auth->curve->hash_len);
+	if (dpp_gen_r_auth(auth, r_auth + 4) < 0 ||
+	    aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+			    r_auth, 4 + auth->curve->hash_len,
+			    0, NULL, NULL, wrapped_r_auth) < 0)
+		goto fail;
+	wrapped_r_auth_len = 4 + auth->curve->hash_len + AES_BLOCK_SIZE;
+	wpa_hexdump(MSG_DEBUG, "DPP: {R-auth}ke",
+		    wrapped_r_auth, wrapped_r_auth_len);
+
+	/* Build DPP Authentication Response frame attributes */
+	msg = wpabuf_alloc(4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
+			   4 + wpabuf_len(pr) + 4 + sizeof(wrapped_data));
+	if (!msg)
+		goto fail;
+	wpabuf_free(auth->resp_attr);
+	auth->resp_attr = msg;
+
+	/* DPP Status */
+	wpabuf_put_le16(msg, DPP_ATTR_STATUS);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, DPP_STATUS_OK);
+
+	/* Responder Bootstrapping Key Hash */
+	wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
+	wpabuf_put_le16(msg, SHA256_MAC_LEN);
+	wpabuf_put_data(msg, auth->own_bi->pubkey_hash, SHA256_MAC_LEN);
+
+	if (auth->peer_bi) {
+		/* Mutual authentication */
+		/* Initiator Bootstrapping Key Hash */
+		wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
+		wpabuf_put_le16(msg, SHA256_MAC_LEN);
+		wpabuf_put_data(msg, auth->peer_bi->pubkey_hash,
+				SHA256_MAC_LEN);
+	}
+
+	/* Responder Protocol Key */
+	wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
+	wpabuf_put_le16(msg, wpabuf_len(pr));
+	wpabuf_put_buf(msg, pr);
+	wpabuf_free(pr);
+	pr = NULL;
+
+	/* Wrapped data ({R-nonce, I-nonce, R-capabilities, {R-auth}ke}k2) */
+	pos = clear;
+	/* R-nonce */
+	WPA_PUT_LE16(pos, DPP_ATTR_R_NONCE);
+	pos += 2;
+	WPA_PUT_LE16(pos, nonce_len);
+	pos += 2;
+	os_memcpy(pos, auth->r_nonce, nonce_len);
+	pos += nonce_len;
+	/* I-nonce */
+	WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+	pos += 2;
+	WPA_PUT_LE16(pos, nonce_len);
+	pos += 2;
+	os_memcpy(pos, auth->i_nonce, nonce_len);
+	pos += nonce_len;
+	/* R-capabilities */
+	WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES);
+	pos += 2;
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR :
+		DPP_CAPAB_ENROLLEE;
+	*pos++ = auth->r_capab;
+	/* {R-auth}ke */
+	WPA_PUT_LE16(pos, DPP_ATTR_WRAPPED_DATA);
+	pos += 2;
+	WPA_PUT_LE16(pos, wrapped_r_auth_len);
+	pos += 2;
+	os_memcpy(pos, wrapped_r_auth, wrapped_r_auth_len);
+	pos += wrapped_r_auth_len;
+
+	addr[0] = wpabuf_head(msg);
+	len[0] = wpabuf_len(msg);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]);
+	siv_len = pos - clear;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
+	if (aes_siv_encrypt(auth->k2, auth->curve->hash_len, clear, siv_len,
+			    1, addr, len, wrapped_data) < 0)
+		goto fail;
+	siv_len += AES_BLOCK_SIZE;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, siv_len);
+
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, siv_len);
+	wpabuf_put_data(msg, wrapped_data, siv_len);
+
+	wpa_hexdump_buf(MSG_DEBUG,
+			"DPP: Authentication Response frame attributes", msg);
+
+	return 0;
+
+fail:
+	wpabuf_free(pr);
+	return -1;
+}
+
+
+static int dpp_auth_build_resp_status(struct dpp_authentication *auth,
+				      enum dpp_status_error status)
+{
+	size_t nonce_len;
+	struct wpabuf *msg;
+#define DPP_AUTH_RESP_CLEAR_LEN2 4 + DPP_MAX_NONCE_LEN + 4 + 1
+	u8 clear[DPP_AUTH_RESP_CLEAR_LEN2];
+	u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN2 + AES_BLOCK_SIZE];
+	u8 *pos;
+	const u8 *addr[1];
+	size_t len[1], siv_len;
+
+	wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
+
+	/* Build DPP Authentication Response frame attributes */
+	msg = wpabuf_alloc(4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
+			   4 + sizeof(wrapped_data));
+	if (!msg)
+		goto fail;
+	wpabuf_free(auth->resp_attr);
+	auth->resp_attr = msg;
+
+	/* DPP Status */
+	wpabuf_put_le16(msg, DPP_ATTR_STATUS);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, status);
+
+	/* Responder Bootstrapping Key Hash */
+	wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
+	wpabuf_put_le16(msg, SHA256_MAC_LEN);
+	wpabuf_put_data(msg, auth->own_bi->pubkey_hash, SHA256_MAC_LEN);
+
+	if (auth->peer_bi) {
+		/* Mutual authentication */
+		/* Initiator Bootstrapping Key Hash */
+		wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
+		wpabuf_put_le16(msg, SHA256_MAC_LEN);
+		wpabuf_put_data(msg, auth->peer_bi->pubkey_hash,
+				SHA256_MAC_LEN);
+	}
+
+	/* Wrapped data ({I-nonce, R-capabilities}k1) */
+	pos = clear;
+	/* I-nonce */
+	nonce_len = auth->curve->nonce_len;
+	WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+	pos += 2;
+	WPA_PUT_LE16(pos, nonce_len);
+	pos += 2;
+	os_memcpy(pos, auth->i_nonce, nonce_len);
+	pos += nonce_len;
+	/* R-capabilities */
+	WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES);
+	pos += 2;
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR :
+		DPP_CAPAB_ENROLLEE;
+	*pos++ = auth->r_capab;
+
+	addr[0] = wpabuf_head(msg);
+	len[0] = wpabuf_len(msg);
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]);
+	siv_len = pos - clear;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
+	if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len,
+			    1, addr, len, wrapped_data) < 0)
+		goto fail;
+	siv_len += AES_BLOCK_SIZE;
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, siv_len);
+
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, siv_len);
+	wpabuf_put_data(msg, wrapped_data, siv_len);
+
+	wpa_hexdump_buf(MSG_DEBUG,
+			"DPP: Authentication Response frame attributes", msg);
+
+	return 0;
+
+fail:
+	return -1;
+}
+
+
+struct dpp_authentication *
+dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
+		struct dpp_bootstrap_info *peer_bi,
+		struct dpp_bootstrap_info *own_bi,
+		unsigned int freq, const u8 *attr_start,
+		const u8 *wrapped_data,	u16 wrapped_data_len)
+{
+	EVP_PKEY *pi = NULL;
+	EVP_PKEY_CTX *ctx = NULL;
+	size_t secret_len;
+	const u8 *addr[1];
+	size_t len[1];
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	const u8 *i_proto, *i_nonce, *i_capab, *i_bootstrap;
+	u16 i_proto_len, i_nonce_len, i_capab_len, i_bootstrap_len;
+	struct dpp_authentication *auth = NULL;
+	size_t attr_len;
+
+	if (wrapped_data_len < AES_BLOCK_SIZE)
+		return NULL;
+
+	attr_len = wrapped_data - 4 - attr_start;
+
+	auth = os_zalloc(sizeof(*auth));
+	if (!auth)
+		goto fail;
+	auth->msg_ctx = msg_ctx;
+	auth->peer_bi = peer_bi;
+	auth->own_bi = own_bi;
+	auth->curve = own_bi->curve;
+	auth->curr_freq = freq;
+
+	i_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_PROTOCOL_KEY,
+			       &i_proto_len);
+	if (!i_proto) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing required Initiator Protocol Key attribute");
+		goto fail;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key",
+		    i_proto, i_proto_len);
+
+	/* M = bR * PI */
+	pi = dpp_set_pubkey_point(own_bi->pubkey, i_proto, i_proto_len);
+	if (!pi) {
+		wpa_printf(MSG_DEBUG, "DPP: Invalid Initiator Protocol Key");
+		goto fail;
+	}
+	dpp_debug_print_key("Peer (Initiator) Protocol Key", pi);
+
+	ctx = EVP_PKEY_CTX_new(own_bi->pubkey, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, pi) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
+	    secret_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	auth->secret_len = secret_len;
+	EVP_PKEY_CTX_free(ctx);
+	ctx = NULL;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
+			auth->Mx, auth->secret_len);
+
+	if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
+			  auth->curve->hash_len) < 0)
+		goto fail;
+
+	addr[0] = attr_start;
+	len[0] = attr_len;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		goto fail;
+	if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    1, addr, len, unwrapped) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+		    unwrapped, unwrapped_len);
+
+	if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+			       &i_nonce_len);
+	if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing or invalid I-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+	os_memcpy(auth->i_nonce, i_nonce, i_nonce_len);
+
+	i_capab = dpp_get_attr(unwrapped, unwrapped_len,
+			       DPP_ATTR_I_CAPABILITIES,
+			       &i_capab_len);
+	if (!i_capab || i_capab_len < 1) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing or invalid I-capabilities");
+		goto fail;
+	}
+	auth->i_capab = i_capab[0];
+	wpa_printf(MSG_DEBUG, "DPP: I-capabilities: 0x%02x", auth->i_capab);
+
+	bin_clear_free(unwrapped, unwrapped_len);
+	unwrapped = NULL;
+
+	switch (auth->i_capab & DPP_CAPAB_ROLE_MASK) {
+	case DPP_CAPAB_ENROLLEE:
+		if (!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Local policy does not allow Configurator role");
+			goto not_compatible;
+		}
+		wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
+		auth->configurator = 1;
+		break;
+	case DPP_CAPAB_CONFIGURATOR:
+		if (!(dpp_allowed_roles & DPP_CAPAB_ENROLLEE)) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Local policy does not allow Enrollee role");
+			goto not_compatible;
+		}
+		wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
+		auth->configurator = 0;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "DPP: Unexpected role in I-capabilities");
+		goto not_compatible;
+	}
+
+	auth->peer_protocol_key = pi;
+	pi = NULL;
+	if (qr_mutual && !peer_bi && own_bi->type == DPP_BOOTSTRAP_QR_CODE) {
+		char hex[SHA256_MAC_LEN * 2 + 1];
+
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Mutual authentication required with QR Codes, but peer info is not yet available - request more time");
+		if (dpp_auth_build_resp_status(auth,
+					       DPP_STATUS_RESPONSE_PENDING) < 0)
+			goto fail;
+		i_bootstrap = dpp_get_attr(attr_start, attr_len,
+					   DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+					   &i_bootstrap_len);
+		if (i_bootstrap && i_bootstrap_len == SHA256_MAC_LEN) {
+			auth->response_pending = 1;
+			os_memcpy(auth->waiting_pubkey_hash,
+				  i_bootstrap, i_bootstrap_len);
+			wpa_snprintf_hex(hex, sizeof(hex), i_bootstrap,
+					 i_bootstrap_len);
+		} else {
+			hex[0] = '\0';
+		}
+
+		wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_SCAN_PEER_QR_CODE
+			"%s", hex);
+		return auth;
+	}
+	if (dpp_auth_build_resp(auth) < 0)
+		goto fail;
+
+	return auth;
+
+not_compatible:
+	wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
+		"i-capab=0x%02x", auth->i_capab);
+	if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)
+		auth->configurator = 1;
+	else
+		auth->configurator = 0;
+	auth->peer_protocol_key = pi;
+	pi = NULL;
+	if (dpp_auth_build_resp_status(auth, DPP_STATUS_NOT_COMPATIBLE) < 0)
+		goto fail;
+
+	auth->remove_on_tx_status = 1;
+	return auth;
+fail:
+	bin_clear_free(unwrapped, unwrapped_len);
+	EVP_PKEY_free(pi);
+	EVP_PKEY_CTX_free(ctx);
+	dpp_auth_deinit(auth);
+	return NULL;
+}
+
+
+int dpp_notify_new_qr_code(struct dpp_authentication *auth,
+			   struct dpp_bootstrap_info *peer_bi)
+{
+	if (!auth || !auth->response_pending ||
+	    os_memcmp(auth->waiting_pubkey_hash, peer_bi->pubkey_hash,
+		      SHA256_MAC_LEN) != 0)
+		return 0;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: New scanned QR Code has matching public key that was needed to continue DPP Authentication exchange with "
+		   MACSTR, MAC2STR(auth->peer_mac_addr));
+	auth->peer_bi = peer_bi;
+
+	if (dpp_auth_build_resp(auth) < 0)
+		return -1;
+
+	return 1;
+}
+
+
+static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth)
+{
+	struct wpabuf *msg;
+	u8 i_auth[4 + DPP_MAX_HASH_LEN];
+	size_t i_auth_len;
+	const u8 *addr[1];
+	size_t len[1];
+	u8 *wrapped_i_auth;
+
+	wpa_printf(MSG_DEBUG, "DPP: Build Authentication Confirmation");
+
+	i_auth_len = 4 + auth->curve->hash_len;
+	/* Build DPP Authentication Confirmation frame attributes */
+	msg = wpabuf_alloc(4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
+			   4 + i_auth_len + AES_BLOCK_SIZE);
+	if (!msg)
+		goto fail;
+
+	/* DPP Status */
+	wpabuf_put_le16(msg, DPP_ATTR_STATUS);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, DPP_STATUS_OK);
+
+	/* Responder Bootstrapping Key Hash */
+	wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
+	wpabuf_put_le16(msg, SHA256_MAC_LEN);
+	wpabuf_put_data(msg, auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+
+	if (auth->own_bi) {
+		/* Mutual authentication */
+		/* Initiator Bootstrapping Key Hash */
+		wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
+		wpabuf_put_le16(msg, SHA256_MAC_LEN);
+		wpabuf_put_data(msg, auth->own_bi->pubkey_hash, SHA256_MAC_LEN);
+	}
+
+	addr[0] = wpabuf_head(msg);
+	len[0] = wpabuf_len(msg);
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, i_auth_len + AES_BLOCK_SIZE);
+	wrapped_i_auth = wpabuf_put(msg, i_auth_len + AES_BLOCK_SIZE);
+	/* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
+	WPA_PUT_LE16(i_auth, DPP_ATTR_I_AUTH_TAG);
+	WPA_PUT_LE16(&i_auth[2], auth->curve->hash_len);
+	if (dpp_gen_i_auth(auth, i_auth + 4) < 0 ||
+	    aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+			    i_auth, i_auth_len,
+			    1, addr, len, wrapped_i_auth) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: {I-auth}ke",
+		    wrapped_i_auth, i_auth_len + AES_BLOCK_SIZE);
+
+	wpa_hexdump_buf(MSG_DEBUG,
+			"DPP: Authentication Confirmation frame attributes",
+			msg);
+	dpp_auth_success(auth);
+
+	return msg;
+
+fail:
+	return NULL;
+}
+
+
+static void
+dpp_auth_resp_rx_status(struct dpp_authentication *auth,
+			const u8 *attr_start, size_t attr_len,
+			const u8 *wrapped_data, u16 wrapped_data_len,
+			enum dpp_status_error status)
+{
+	const u8 *addr[1];
+	size_t len[1];
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	const u8 *i_nonce, *r_capab;
+	u16 i_nonce_len, r_capab_len;
+
+	if (status == DPP_STATUS_NOT_COMPATIBLE) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Responder reported incompatible roles");
+	} else if (status == DPP_STATUS_RESPONSE_PENDING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Responder reported more time needed");
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Responder reported failure (status %d)",
+			   status);
+		return;
+	}
+
+	addr[0] = attr_start;
+	len[0] = attr_len;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		goto fail;
+	if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    1, addr, len, unwrapped) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+		    unwrapped, unwrapped_len);
+
+	if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+			       &i_nonce_len);
+	if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing or invalid I-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+	if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: I-nonce mismatch");
+		goto fail;
+	}
+
+	r_capab = dpp_get_attr(unwrapped, unwrapped_len,
+			       DPP_ATTR_R_CAPABILITIES,
+			       &r_capab_len);
+	if (!r_capab || r_capab_len < 1) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing or invalid R-capabilities");
+		goto fail;
+	}
+	auth->r_capab = r_capab[0];
+	wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
+	if (status == DPP_STATUS_NOT_COMPATIBLE) {
+		wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
+			"r-capab=0x%02x", auth->r_capab);
+	} else if (status == DPP_STATUS_RESPONSE_PENDING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Continue waiting for full DPP Authentication Response");
+		wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_RESPONSE_PENDING);
+	}
+fail:
+	bin_clear_free(unwrapped, unwrapped_len);
+}
+
+
+struct wpabuf *
+dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *attr_start,
+		 size_t attr_len)
+{
+	EVP_PKEY *pr;
+	EVP_PKEY_CTX *ctx = NULL;
+	size_t secret_len;
+	const u8 *addr[1];
+	size_t len[1];
+	u8 *unwrapped = NULL, *unwrapped2 = NULL;
+	size_t unwrapped_len = 0, unwrapped2_len = 0;
+	const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *r_proto,
+		*r_nonce, *i_nonce, *r_capab, *wrapped2, *r_auth;
+	u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
+		r_proto_len, r_nonce_len, i_nonce_len, r_capab_len,
+		wrapped2_len, r_auth_len;
+	u8 r_auth2[DPP_MAX_HASH_LEN];
+
+	wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing required Wrapped data attribute");
+		return NULL;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
+		    wrapped_data, wrapped_data_len);
+
+	if (wrapped_data_len < AES_BLOCK_SIZE)
+		return NULL;
+
+	attr_len = wrapped_data - 4 - attr_start;
+
+	r_bootstrap = dpp_get_attr(attr_start, attr_len,
+				   DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+				   &r_bootstrap_len);
+	if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute");
+		return NULL;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
+		    r_bootstrap, r_bootstrap_len);
+	if (os_memcmp(r_bootstrap, auth->peer_bi->pubkey_hash,
+		      SHA256_MAC_LEN) != 0) {
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Expected Responder Bootstrapping Key Hash",
+			    auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+		return NULL;
+	}
+
+	i_bootstrap = dpp_get_attr(attr_start, attr_len,
+				   DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+				   &i_bootstrap_len);
+	if (i_bootstrap) {
+		if (i_bootstrap_len != SHA256_MAC_LEN) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Invalid Initiator Bootstrapping Key Hash attribute");
+			return NULL;
+		}
+		wpa_hexdump(MSG_MSGDUMP,
+			    "DPP: Initiator Bootstrapping Key Hash",
+			    i_bootstrap, i_bootstrap_len);
+		if (!auth->own_bi ||
+		    os_memcmp(i_bootstrap, auth->own_bi->pubkey_hash,
+			      SHA256_MAC_LEN) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Initiator Bootstrapping Key Hash attribute did not match");
+			return NULL;
+		}
+	}
+
+	status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
+			      &status_len);
+	if (!status || status_len < 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required DPP Status attribute");
+		return NULL;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+	auth->auth_resp_status = status[0];
+	if (status[0] != DPP_STATUS_OK) {
+		dpp_auth_resp_rx_status(auth, attr_start,
+					attr_len, wrapped_data,
+					wrapped_data_len, status[0]);
+		return NULL;
+	}
+
+	r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY,
+			       &r_proto_len);
+	if (!r_proto) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing required Responder Protocol Key attribute");
+		return NULL;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key",
+		    r_proto, r_proto_len);
+
+	/* N = pI * PR */
+	pr = dpp_set_pubkey_point(auth->own_protocol_key, r_proto, r_proto_len);
+	if (!pr) {
+		wpa_printf(MSG_DEBUG, "DPP: Invalid Responder Protocol Key");
+		return NULL;
+	}
+	dpp_debug_print_key("Peer (Responder) Protocol Key", pr);
+
+	ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, pr) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
+	    secret_len > DPP_MAX_SHARED_SECRET_LEN ||
+	    EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to derive ECDH shared secret: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	EVP_PKEY_CTX_free(ctx);
+	ctx = NULL;
+	auth->peer_protocol_key = pr;
+	pr = NULL;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
+			auth->Nx, auth->secret_len);
+
+	if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
+			  auth->curve->hash_len) < 0)
+		goto fail;
+
+	addr[0] = attr_start;
+	len[0] = attr_len;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		goto fail;
+	if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    1, addr, len, unwrapped) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+		    unwrapped, unwrapped_len);
+
+	if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
+			       &r_nonce_len);
+	if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing or invalid R-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len);
+	os_memcpy(auth->r_nonce, r_nonce, r_nonce_len);
+
+	i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+			       &i_nonce_len);
+	if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing or invalid I-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+	if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: I-nonce mismatch");
+		goto fail;
+	}
+
+	if (auth->own_bi && auth->peer_bi) {
+		/* Mutual authentication */
+		if (dpp_auth_derive_l_initiator(auth) < 0)
+			goto fail;
+	}
+
+	if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0)
+		goto fail;
+
+	r_capab = dpp_get_attr(unwrapped, unwrapped_len,
+			       DPP_ATTR_R_CAPABILITIES,
+			       &r_capab_len);
+	if (!r_capab || r_capab_len < 1) {
+		wpa_printf(MSG_DEBUG, "DPP: Missing or invalid R-capabilities");
+		goto fail;
+	}
+	auth->r_capab = r_capab[0];
+	wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
+	if ((auth->configurator && (auth->r_capab & DPP_CAPAB_CONFIGURATOR)) ||
+	    (!auth->configurator && (auth->r_capab & DPP_CAPAB_ENROLLEE))) {
+		wpa_printf(MSG_DEBUG, "DPP: Incompatible role selection");
+		goto fail;
+	}
+
+	wrapped2 = dpp_get_attr(unwrapped, unwrapped_len,
+				DPP_ATTR_WRAPPED_DATA, &wrapped2_len);
+	if (!wrapped2 || wrapped2_len < AES_BLOCK_SIZE) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid Secondary Wrapped Data");
+		goto fail;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped2, wrapped2_len);
+	unwrapped2_len = wrapped2_len - AES_BLOCK_SIZE;
+	unwrapped2 = os_malloc(unwrapped2_len);
+	if (!unwrapped2)
+		goto fail;
+	if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+			    wrapped2, wrapped2_len,
+			    0, NULL, NULL, unwrapped2) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+		    unwrapped2, unwrapped2_len);
+
+	if (dpp_check_attrs(unwrapped2, unwrapped2_len) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid attribute in secondary unwrapped data");
+		goto fail;
+	}
+
+	r_auth = dpp_get_attr(unwrapped2, unwrapped2_len, DPP_ATTR_R_AUTH_TAG,
+			       &r_auth_len);
+	if (!r_auth || r_auth_len != auth->curve->hash_len) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid Responder Authenticating Tag");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Received Responder Authenticating Tag",
+		    r_auth, r_auth_len);
+	/* R-auth' = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+	if (dpp_gen_r_auth(auth, r_auth2) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: Calculated Responder Authenticating Tag",
+		    r_auth2, r_auth_len);
+	if (os_memcmp(r_auth, r_auth2, r_auth_len) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Mismatching Responder Authenticating Tag");
+		goto fail;
+	}
+
+	bin_clear_free(unwrapped, unwrapped_len);
+	bin_clear_free(unwrapped2, unwrapped2_len);
+
+	return dpp_auth_build_conf(auth);
+
+fail:
+	bin_clear_free(unwrapped, unwrapped_len);
+	bin_clear_free(unwrapped2, unwrapped2_len);
+	EVP_PKEY_free(pr);
+	EVP_PKEY_CTX_free(ctx);
+	return NULL;
+}
+
+
+int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *attr_start,
+		     size_t attr_len)
+{
+	const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *i_auth;
+	u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
+		i_auth_len;
+	const u8 *addr[1];
+	size_t len[1];
+	u8 *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	u8 i_auth2[DPP_MAX_HASH_LEN];
+
+	wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing required Wrapped data attribute");
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
+		    wrapped_data, wrapped_data_len);
+
+	if (wrapped_data_len < AES_BLOCK_SIZE)
+		return -1;
+
+	attr_len = wrapped_data - 4 - attr_start;
+
+	r_bootstrap = dpp_get_attr(attr_start, attr_len,
+				   DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+				   &r_bootstrap_len);
+	if (!r_bootstrap || r_bootstrap > wrapped_data ||
+	    r_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute");
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
+		    r_bootstrap, r_bootstrap_len);
+	if (os_memcmp(r_bootstrap, auth->own_bi->pubkey_hash,
+		      SHA256_MAC_LEN) != 0) {
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Expected Responder Bootstrapping Key Hash",
+			    auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+		return -1;
+	}
+
+	i_bootstrap = dpp_get_attr(attr_start, attr_len,
+				   DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+				   &i_bootstrap_len);
+	if (i_bootstrap) {
+		if (i_bootstrap > wrapped_data ||
+		    i_bootstrap_len != SHA256_MAC_LEN) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Invalid Initiator Bootstrapping Key Hash attribute");
+			return -1;
+		}
+		wpa_hexdump(MSG_MSGDUMP,
+			    "DPP: Initiator Bootstrapping Key Hash",
+			    i_bootstrap, i_bootstrap_len);
+		if (!auth->peer_bi ||
+		    os_memcmp(i_bootstrap, auth->peer_bi->pubkey_hash,
+			      SHA256_MAC_LEN) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Initiator Bootstrapping Key Hash attribute did not match");
+			return -1;
+		}
+	}
+
+	status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
+			      &status_len);
+	if (!status || status_len < 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required DPP Status attribute");
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+	if (status[0] != DPP_STATUS_OK) {
+		wpa_printf(MSG_DEBUG, "DPP: Authentication failed");
+		return -1;
+	}
+
+	addr[0] = attr_start;
+	len[0] = attr_len;
+	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]);
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+		    wrapped_data, wrapped_data_len);
+	unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+	unwrapped = os_malloc(unwrapped_len);
+	if (!unwrapped)
+		return -1;
+	if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+			    wrapped_data, wrapped_data_len,
+			    1, addr, len, unwrapped) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+		    unwrapped, unwrapped_len);
+
+	if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Invalid attribute in unwrapped data");
+		goto fail;
+	}
+
+	i_auth = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
+			      &i_auth_len);
+	if (!i_auth || i_auth_len != auth->curve->hash_len) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid Initiator Authenticating Tag");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: Received Initiator Authenticating Tag",
+		    i_auth, i_auth_len);
+	/* I-auth' = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
+	if (dpp_gen_i_auth(auth, i_auth2) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "DPP: Calculated Initiator Authenticating Tag",
+		    i_auth2, i_auth_len);
+	if (os_memcmp(i_auth, i_auth2, i_auth_len) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Mismatching Initiator Authenticating Tag");
+		goto fail;
+	}
+
+	bin_clear_free(unwrapped, unwrapped_len);
+	dpp_auth_success(auth);
+	return 0;
+fail:
+	bin_clear_free(unwrapped, unwrapped_len);
+	return -1;
+}
+
+
+void dpp_auth_deinit(struct dpp_authentication *auth)
+{
+	if (!auth)
+		return;
+	EVP_PKEY_free(auth->own_protocol_key);
+	EVP_PKEY_free(auth->peer_protocol_key);
+	wpabuf_free(auth->req_attr);
+	wpabuf_free(auth->resp_attr);
+	bin_clear_free(auth, sizeof(*auth));
+}

+ 110 - 0
src/common/dpp.h

@@ -14,7 +14,64 @@
 #include "utils/list.h"
 #include "crypto/sha256.h"
 
+/* DPP Public Action frame identifiers - OUI_WFA */
+#define DPP_OUI_TYPE 0x1A
+
+enum dpp_public_action_frame_type {
+	DPP_PA_AUTHENTICATION_REQ = 0,
+	DPP_PA_AUTHENTICATION_RESP = 1,
+	DPP_PA_AUTHENTICATION_CONF = 2,
+	DPP_PA_PEER_DISCOVERY_REQ = 5,
+	DPP_PA_PEER_DISCOVERY_RESP = 6,
+	DPP_PA_PKEX_EXCHANGE_REQ = 7,
+	DPP_PA_PKEX_EXCHANGE_RESP = 8,
+	DPP_PA_PKEX_COMMIT_REVEAL_REQ = 9,
+	DPP_PA_PKEX_COMMIT_REVEAL_RESP = 10,
+};
+
+enum dpp_attribute_id {
+	DPP_ATTR_STATUS = 0x1000,
+	DPP_ATTR_I_BOOTSTRAP_KEY_HASH = 0x1001,
+	DPP_ATTR_R_BOOTSTRAP_KEY_HASH = 0x1002,
+	DPP_ATTR_I_PROTOCOL_KEY = 0x1003,
+	DPP_ATTR_WRAPPED_DATA = 0x1004,
+	DPP_ATTR_I_NONCE = 0x1005,
+	DPP_ATTR_I_CAPABILITIES = 0x1006,
+	DPP_ATTR_R_NONCE = 0x1007,
+	DPP_ATTR_R_CAPABILITIES = 0x1008,
+	DPP_ATTR_R_PROTOCOL_KEY = 0x1009,
+	DPP_ATTR_I_AUTH_TAG = 0x100A,
+	DPP_ATTR_R_AUTH_TAG = 0x100B,
+	DPP_ATTR_CONFIG_OBJ = 0x100C,
+	DPP_ATTR_CONNECTOR = 0x100D,
+	DPP_ATTR_CONFIG_ATTR_OBJ = 0x100E,
+	DPP_ATTR_BOOTSTRAP_KEY = 0x100F,
+	DPP_ATTR_PEER_NET_PK_HASH = 0x1010,
+	DPP_ATTR_OWN_NET_NK_HASH = 0x1011,
+	DPP_ATTR_FINITE_CYCLIC_GROUP = 0x1012,
+	DPP_ATTR_ENCRYPTED_KEY = 0x1013,
+	DPP_ATTR_ENROLLEE_NONCE = 0x1014,
+	DPP_ATTR_CODE_IDENTIFIER = 0x1015,
+};
+
+enum dpp_status_error {
+	DPP_STATUS_OK = 0,
+	DPP_STATUS_NOT_COMPATIBLE = 1,
+	DPP_STATUS_AUTH_FAILURE = 2,
+	DPP_STATUS_UNWRAP_FAILURE = 3,
+	DPP_STATUS_BAD_GROUP = 4,
+	DPP_STATUS_CONFIGURE_FAILURE = 5,
+	DPP_STATUS_RESPONSE_PENDING = 6,
+};
+
+#define DPP_CAPAB_ENROLLEE BIT(0)
+#define DPP_CAPAB_CONFIGURATOR BIT(1)
+#define DPP_CAPAB_ROLE_MASK (BIT(0) | BIT(1))
+
 #define DPP_BOOTSTRAP_MAX_FREQ 30
+#define DPP_MAX_NONCE_LEN 32
+#define DPP_MAX_HASH_LEN 64
+#define DPP_MAX_SHARED_SECRET_LEN 66
 
 struct dpp_curve_params {
 	const char *name;
@@ -44,6 +101,37 @@ struct dpp_bootstrap_info {
 	const struct dpp_curve_params *curve;
 };
 
+struct dpp_authentication {
+	void *msg_ctx;
+	const struct dpp_curve_params *curve;
+	struct dpp_bootstrap_info *peer_bi;
+	struct dpp_bootstrap_info *own_bi;
+	u8 waiting_pubkey_hash[SHA256_MAC_LEN];
+	int response_pending;
+	enum dpp_status_error auth_resp_status;
+	u8 peer_mac_addr[ETH_ALEN];
+	u8 i_nonce[DPP_MAX_NONCE_LEN];
+	u8 r_nonce[DPP_MAX_NONCE_LEN];
+	u8 i_capab;
+	u8 r_capab;
+	EVP_PKEY *own_protocol_key;
+	EVP_PKEY *peer_protocol_key;
+	struct wpabuf *req_attr;
+	struct wpabuf *resp_attr;
+	unsigned int curr_freq;
+	size_t secret_len;
+	u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
+	u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
+	u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
+	u8 k1[DPP_MAX_HASH_LEN];
+	u8 k2[DPP_MAX_HASH_LEN];
+	u8 ke[DPP_MAX_HASH_LEN];
+	int initiator;
+	int configurator;
+	int remove_on_tx_status;
+	int auth_success;
+};
+
 void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info);
 int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi,
 			    const char *chan_list);
@@ -52,5 +140,27 @@ int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info);
 struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri);
 char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
 		  const u8 *privkey, size_t privkey_len);
+struct dpp_authentication * dpp_auth_init(void *msg_ctx,
+					  struct dpp_bootstrap_info *peer_bi,
+					  struct dpp_bootstrap_info *own_bi,
+					  int configurator);
+struct dpp_authentication *
+dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
+		struct dpp_bootstrap_info *peer_bi,
+		struct dpp_bootstrap_info *own_bi,
+		unsigned int freq, const u8 *attr_start,
+		const u8 *wrapped_data, u16 wrapped_data_len);
+struct wpabuf *
+dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *attr_start,
+		 size_t attr_len);
+int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *attr_start,
+		     size_t attr_len);
+int dpp_notify_new_qr_code(struct dpp_authentication *auth,
+			   struct dpp_bootstrap_info *peer_bi);
+void dpp_auth_deinit(struct dpp_authentication *auth);
+struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type,
+			      size_t len);
+const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len);
+int dpp_check_attrs(const u8 *buf, size_t len);
 
 #endif /* DPP_H */

+ 6 - 0
src/common/wpa_ctrl.h

@@ -143,6 +143,12 @@ extern "C" {
 #define WPS_EVENT_ER_AP_SETTINGS "WPS-ER-AP-SETTINGS "
 #define WPS_EVENT_ER_SET_SEL_REG "WPS-ER-AP-SET-SEL-REG "
 
+/* DPP events */
+#define DPP_EVENT_AUTH_SUCCESS "DPP-AUTH-SUCCESS "
+#define DPP_EVENT_NOT_COMPATIBLE "DPP-NOT-COMPATIBLE "
+#define DPP_EVENT_RESPONSE_PENDING "DPP-RESPONSE-PENDING "
+#define DPP_EVENT_SCAN_PEER_QR_CODE "DPP-SCAN-PEER-QR-CODE "
+
 /* MESH events */
 #define MESH_GROUP_STARTED "MESH-GROUP-STARTED "
 #define MESH_GROUP_REMOVED "MESH-GROUP-REMOVED "

+ 7 - 0
wpa_supplicant/Android.mk

@@ -247,6 +247,13 @@ ifdef CONFIG_DPP
 L_CFLAGS += -DCONFIG_DPP
 OBJS += src/common/dpp.c
 OBJS += dpp_supplicant.c
+NEED_AES_SIV=y
+NEED_HMAC_SHA256_KDF=y
+NEED_HMAC_SHA384_KDF=y
+NEED_HMAC_SHA512_KDF=y
+NEED_SHA256=y
+NEED_SHA384=y
+NEED_SHA512=y
 endif
 
 ifdef CONFIG_OWE

+ 7 - 0
wpa_supplicant/Makefile

@@ -280,6 +280,13 @@ ifdef CONFIG_DPP
 CFLAGS += -DCONFIG_DPP
 OBJS += ../src/common/dpp.o
 OBJS += dpp_supplicant.o
+NEED_AES_SIV=y
+NEED_HMAC_SHA256_KDF=y
+NEED_HMAC_SHA384_KDF=y
+NEED_HMAC_SHA512_KDF=y
+NEED_SHA256=y
+NEED_SHA384=y
+NEED_SHA512=y
 endif
 
 ifdef CONFIG_OWE

+ 12 - 0
wpa_supplicant/ctrl_iface.c

@@ -7558,6 +7558,10 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
 	wpa_s->after_wps = 0;
 	wpa_s->known_wps_freq = 0;
 
+#ifdef CONFIG_DPP
+	wpas_dpp_deinit(wpa_s);
+#endif /* CONFIG_DPP */
+
 #ifdef CONFIG_TDLS
 #ifdef CONFIG_TDLS_TESTING
 	tdls_testing = 0;
@@ -10187,6 +10191,14 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
 			if (os_snprintf_error(reply_size, reply_len))
 				reply_len = -1;
 		}
+	} else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) {
+		if (wpas_dpp_auth_init(wpa_s, buf + 13) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DPP_LISTEN ", 11) == 0) {
+		if (wpas_dpp_listen(wpa_s, buf + 11) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "DPP_STOP_LISTEN") == 0) {
+		wpas_dpp_listen_stop(wpa_s);
 #endif /* CONFIG_DPP */
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);

+ 588 - 0
wpa_supplicant/dpp_supplicant.c

@@ -9,11 +9,25 @@
 #include "utils/includes.h"
 
 #include "utils/common.h"
+#include "utils/eloop.h"
 #include "common/dpp.h"
 #include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "offchannel.h"
 #include "dpp_supplicant.h"
 
 
+static int wpas_dpp_listen_start(struct wpa_supplicant *wpa_s,
+				 unsigned int freq);
+static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s,
+			       unsigned int freq, const u8 *dst,
+			       const u8 *src, const u8 *bssid,
+			       const u8 *data, size_t data_len,
+			       enum offchannel_send_action_result result);
+
+static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+
 static unsigned int wpas_dpp_next_id(struct wpa_supplicant *wpa_s)
 {
 	struct dpp_bootstrap_info *bi;
@@ -37,6 +51,7 @@ static unsigned int wpas_dpp_next_id(struct wpa_supplicant *wpa_s)
 int wpas_dpp_qr_code(struct wpa_supplicant *wpa_s, const char *cmd)
 {
 	struct dpp_bootstrap_info *bi;
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
 
 	bi = dpp_parse_qr_code(cmd);
 	if (!bi)
@@ -45,6 +60,27 @@ int wpas_dpp_qr_code(struct wpa_supplicant *wpa_s, const char *cmd)
 	bi->id = wpas_dpp_next_id(wpa_s);
 	dl_list_add(&wpa_s->dpp_bootstrap, &bi->list);
 
+	if (auth && auth->response_pending &&
+	    dpp_notify_new_qr_code(auth, bi) == 1) {
+		struct wpabuf *msg;
+
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Sending out pending authentication response");
+		msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP,
+				    wpabuf_len(auth->resp_attr));
+		if (!msg)
+			goto out;
+		wpabuf_put_buf(msg, wpa_s->dpp_auth->resp_attr);
+
+		offchannel_send_action(wpa_s, auth->curr_freq,
+				       auth->peer_mac_addr, wpa_s->own_addr,
+				       broadcast,
+				       wpabuf_head(msg), wpabuf_len(msg),
+				       500, wpas_dpp_tx_status, 0);
+		wpabuf_free(msg);
+	}
+
+out:
 	return bi->id;
 }
 
@@ -215,6 +251,553 @@ const char * wpas_dpp_bootstrap_get_uri(struct wpa_supplicant *wpa_s,
 }
 
 
+static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s,
+			       unsigned int freq, const u8 *dst,
+			       const u8 *src, const u8 *bssid,
+			       const u8 *data, size_t data_len,
+			       enum offchannel_send_action_result result)
+{
+	wpa_printf(MSG_DEBUG, "DPP: TX status: freq=%u dst=" MACSTR
+		   " result=%s",
+		   freq, MAC2STR(dst),
+		   result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" :
+		   (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" :
+		    "FAILED"));
+
+	if (!wpa_s->dpp_auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore TX status since there is no ongoing authentication exchange");
+		return;
+	}
+
+	if (wpa_s->dpp_auth->remove_on_tx_status) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Terminate authentication exchange due to an earlier error");
+		dpp_auth_deinit(wpa_s->dpp_auth);
+		wpa_s->dpp_auth = NULL;
+		return;
+	}
+
+	if (!is_broadcast_ether_addr(dst) &&
+	    result != OFFCHANNEL_SEND_ACTION_SUCCESS) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unicast DPP Action frame was not ACKed");
+		/* TODO: In case of DPP Authentication Request frame, move to
+		 * the next channel immediately */
+	}
+}
+
+
+static void wpas_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+	if (!wpa_s->dpp_auth)
+		return;
+	wpa_printf(MSG_DEBUG, "DPP: Continue reply wait on channel %u MHz",
+		   wpa_s->dpp_auth->curr_freq);
+	wpas_dpp_listen_start(wpa_s, wpa_s->dpp_auth->curr_freq);
+}
+
+
+int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	const char *pos;
+	struct dpp_bootstrap_info *peer_bi, *own_bi = NULL;
+	struct wpabuf *msg;
+	const u8 *dst;
+	int res;
+	int configurator = 1;
+	unsigned int wait_time;
+
+	pos = os_strstr(cmd, " peer=");
+	if (!pos)
+		return -1;
+	pos += 6;
+	peer_bi = dpp_bootstrap_get_id(wpa_s, atoi(pos));
+	if (!peer_bi) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Could not find bootstrapping info for the identified peer");
+		return -1;
+	}
+
+	pos = os_strstr(cmd, " own=");
+	if (pos) {
+		pos += 5;
+		own_bi = dpp_bootstrap_get_id(wpa_s, atoi(pos));
+		if (!own_bi) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Could not find bootstrapping info for the identified local entry");
+			return -1;
+		}
+
+		if (peer_bi->curve != own_bi->curve) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Mismatching curves in bootstrapping info (peer=%s own=%s)",
+				   peer_bi->curve->name, own_bi->curve->name);
+			return -1;
+		}
+	}
+
+	pos = os_strstr(cmd, " role=");
+	if (pos) {
+		pos += 6;
+		if (os_strncmp(pos, "configurator", 12) == 0)
+			configurator = 1;
+		else if (os_strncmp(pos, "enrollee", 8) == 0)
+			configurator = 0;
+		else
+			return -1;
+	}
+
+	if (wpa_s->dpp_auth) {
+		eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
+		offchannel_send_action_done(wpa_s);
+		dpp_auth_deinit(wpa_s->dpp_auth);
+	}
+	wpa_s->dpp_auth = dpp_auth_init(wpa_s, peer_bi, own_bi, configurator);
+	if (!wpa_s->dpp_auth)
+		return -1;
+
+	/* TODO: Support iteration over all frequencies and filtering of
+	 * frequencies based on locally enabled channels that allow initiation
+	 * of transmission. */
+	if (peer_bi->num_freq > 0)
+		wpa_s->dpp_auth->curr_freq = peer_bi->freq[0];
+	else
+		wpa_s->dpp_auth->curr_freq = 2412;
+
+	msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_REQ,
+			    wpabuf_len(wpa_s->dpp_auth->req_attr));
+	if (!msg)
+		return -1;
+	wpabuf_put_buf(msg, wpa_s->dpp_auth->req_attr);
+
+	if (is_zero_ether_addr(peer_bi->mac_addr)) {
+		dst = broadcast;
+	} else {
+		dst = peer_bi->mac_addr;
+		os_memcpy(wpa_s->dpp_auth->peer_mac_addr, peer_bi->mac_addr,
+			  ETH_ALEN);
+	}
+	eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
+	wait_time = wpa_s->max_remain_on_chan;
+	if (wait_time > 2000)
+		wait_time = 2000;
+	eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
+			       wpas_dpp_reply_wait_timeout,
+			       wpa_s, NULL);
+	res = offchannel_send_action(wpa_s, wpa_s->dpp_auth->curr_freq,
+				     dst, wpa_s->own_addr, broadcast,
+				     wpabuf_head(msg), wpabuf_len(msg),
+				     wait_time, wpas_dpp_tx_status, 0);
+	wpabuf_free(msg);
+
+	return res;
+}
+
+
+struct wpas_dpp_listen_work {
+	unsigned int freq;
+	unsigned int duration;
+	struct wpabuf *probe_resp_ie;
+};
+
+
+static void wpas_dpp_listen_work_free(struct wpas_dpp_listen_work *lwork)
+{
+	if (!lwork)
+		return;
+	os_free(lwork);
+}
+
+
+static void wpas_dpp_listen_work_done(struct wpa_supplicant *wpa_s)
+{
+	struct wpas_dpp_listen_work *lwork;
+
+	if (!wpa_s->dpp_listen_work)
+		return;
+
+	lwork = wpa_s->dpp_listen_work->ctx;
+	wpas_dpp_listen_work_free(lwork);
+	radio_work_done(wpa_s->dpp_listen_work);
+	wpa_s->dpp_listen_work = NULL;
+}
+
+
+static void dpp_start_listen_cb(struct wpa_radio_work *work, int deinit)
+{
+	struct wpa_supplicant *wpa_s = work->wpa_s;
+	struct wpas_dpp_listen_work *lwork = work->ctx;
+
+	if (deinit) {
+		if (work->started) {
+			wpa_s->dpp_listen_work = NULL;
+			wpas_dpp_listen_stop(wpa_s);
+		}
+		wpas_dpp_listen_work_free(lwork);
+		return;
+	}
+
+	wpa_s->dpp_listen_work = work;
+
+	wpa_s->dpp_pending_listen_freq = lwork->freq;
+
+	if (wpa_drv_remain_on_channel(wpa_s, lwork->freq,
+				      wpa_s->max_remain_on_chan) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to request the driver to remain on channel (%u MHz) for listen",
+			   lwork->freq);
+		wpas_dpp_listen_work_done(wpa_s);
+		wpa_s->dpp_pending_listen_freq = 0;
+		return;
+	}
+	wpa_s->off_channel_freq = 0;
+	wpa_s->roc_waiting_drv_freq = lwork->freq;
+}
+
+
+static int wpas_dpp_listen_start(struct wpa_supplicant *wpa_s,
+				 unsigned int freq)
+{
+	struct wpas_dpp_listen_work *lwork;
+
+	if (wpa_s->dpp_listen_work) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Reject start_listen since dpp_listen_work already exists");
+		return -1;
+	}
+
+	if (wpa_s->dpp_listen_freq)
+		wpas_dpp_listen_stop(wpa_s);
+	wpa_s->dpp_listen_freq = freq;
+
+	lwork = os_zalloc(sizeof(*lwork));
+	if (!lwork)
+		return -1;
+	lwork->freq = freq;
+
+	if (radio_add_work(wpa_s, freq, "dpp-listen", 0, dpp_start_listen_cb,
+			   lwork) < 0) {
+		wpas_dpp_listen_work_free(lwork);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int wpas_dpp_listen(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	int freq;
+
+	freq = atoi(cmd);
+	if (freq <= 0)
+		return -1;
+
+	if (os_strstr(cmd, " role=configurator"))
+		wpa_s->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR;
+	else if (os_strstr(cmd, " role=enrollee"))
+		wpa_s->dpp_allowed_roles = DPP_CAPAB_ENROLLEE;
+	else
+		wpa_s->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR |
+			DPP_CAPAB_ENROLLEE;
+	wpa_s->dpp_qr_mutual = os_strstr(cmd, " qr=mutual") != NULL;
+	if (wpa_s->dpp_listen_freq == (unsigned int) freq) {
+		wpa_printf(MSG_DEBUG, "DPP: Already listening on %u MHz",
+			   freq);
+		return 0;
+	}
+
+	return wpas_dpp_listen_start(wpa_s, freq);
+}
+
+
+void wpas_dpp_listen_stop(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->dpp_listen_freq)
+		return;
+
+	wpa_printf(MSG_DEBUG, "DPP: Stop listen on %u MHz",
+		   wpa_s->dpp_listen_freq);
+	wpa_drv_cancel_remain_on_channel(wpa_s);
+	wpa_s->dpp_listen_freq = 0;
+	wpas_dpp_listen_work_done(wpa_s);
+}
+
+
+void wpas_dpp_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+				   unsigned int freq)
+{
+	if (!wpa_s->dpp_listen_freq && !wpa_s->dpp_pending_listen_freq)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: remain-on-channel callback (off_channel_freq=%u dpp_pending_listen_freq=%d roc_waiting_drv_freq=%d freq=%u)",
+		   wpa_s->off_channel_freq, wpa_s->dpp_pending_listen_freq,
+		   wpa_s->roc_waiting_drv_freq, freq);
+	if (wpa_s->off_channel_freq &&
+	    wpa_s->off_channel_freq == wpa_s->dpp_pending_listen_freq) {
+		wpa_printf(MSG_DEBUG, "DPP: Listen on %u MHz started", freq);
+		wpa_s->dpp_pending_listen_freq = 0;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore remain-on-channel callback (off_channel_freq=%u dpp_pending_listen_freq=%d freq=%u)",
+			   wpa_s->off_channel_freq,
+			   wpa_s->dpp_pending_listen_freq, freq);
+	}
+}
+
+
+void wpas_dpp_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+					  unsigned int freq)
+{
+	wpas_dpp_listen_work_done(wpa_s);
+
+	if (wpa_s->dpp_auth) {
+		/* Continue listen with a new remain-on-channel */
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Continue wait on %u MHz for the ongoing DPP provisioning session",
+			   wpa_s->dpp_auth->curr_freq);
+		wpas_dpp_listen_start(wpa_s, wpa_s->dpp_auth->curr_freq);
+		return;
+	}
+
+	if (wpa_s->dpp_listen_freq) {
+		/* Continue listen with a new remain-on-channel */
+		wpas_dpp_listen_start(wpa_s, wpa_s->dpp_listen_freq);
+	}
+}
+
+
+static void wpas_dpp_rx_auth_req(struct wpa_supplicant *wpa_s, const u8 *src,
+				 const u8 *buf, size_t len, unsigned int freq)
+{
+	const u8 *r_bootstrap, *i_bootstrap, *wrapped_data;
+	u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len;
+	struct dpp_bootstrap_info *bi, *own_bi = NULL, *peer_bi = NULL;
+	struct wpabuf *msg;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR,
+		   MAC2STR(src));
+
+	wrapped_data = dpp_get_attr(buf, len, DPP_ATTR_WRAPPED_DATA,
+				    &wrapped_data_len);
+	if (!wrapped_data) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing required Wrapped data attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped data",
+		    wrapped_data, wrapped_data_len);
+
+	r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+				   &r_bootstrap_len);
+	if (!r_bootstrap || r_bootstrap > wrapped_data ||
+	    r_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+		    r_bootstrap, r_bootstrap_len);
+
+	i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+				   &i_bootstrap_len);
+	if (!i_bootstrap || i_bootstrap > wrapped_data ||
+	    i_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Initiator Bootstrapping Key Hash attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash",
+		    i_bootstrap, i_bootstrap_len);
+
+	/* Try to find own and peer bootstrapping key matches based on the
+	 * received hash values */
+	dl_list_for_each(bi, &wpa_s->dpp_bootstrap, struct dpp_bootstrap_info,
+			 list) {
+		if (!own_bi && bi->own &&
+		    os_memcmp(bi->pubkey_hash, r_bootstrap,
+			      SHA256_MAC_LEN) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Found matching own bootstrapping information");
+			own_bi = bi;
+		}
+
+		if (!peer_bi && !bi->own &&
+		    os_memcmp(bi->pubkey_hash, i_bootstrap,
+			      SHA256_MAC_LEN) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Found matching peer bootstrapping information");
+			peer_bi = bi;
+		}
+
+		if (own_bi && peer_bi)
+			break;
+	}
+
+	if (!own_bi) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No matching own bootstrapping key found - ignore message");
+		return;
+	}
+
+	if (wpa_s->dpp_auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Already in DPP authentication exchange - ignore new one");
+		return;
+	}
+
+	wpa_s->dpp_auth = dpp_auth_req_rx(wpa_s, wpa_s->dpp_allowed_roles,
+					  wpa_s->dpp_qr_mutual,
+					  peer_bi, own_bi, freq, buf,
+					  wrapped_data, wrapped_data_len);
+	if (!wpa_s->dpp_auth) {
+		wpa_printf(MSG_DEBUG, "DPP: No response generated");
+		return;
+	}
+	os_memcpy(wpa_s->dpp_auth->peer_mac_addr, src, ETH_ALEN);
+
+	msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP,
+			    wpabuf_len(wpa_s->dpp_auth->resp_attr));
+	if (!msg)
+		return;
+	wpabuf_put_buf(msg, wpa_s->dpp_auth->resp_attr);
+
+	offchannel_send_action(wpa_s, wpa_s->dpp_auth->curr_freq,
+			       src, wpa_s->own_addr, broadcast,
+			       wpabuf_head(msg), wpabuf_len(msg),
+			       500, wpas_dpp_tx_status, 0);
+	wpabuf_free(msg);
+}
+
+
+static void wpas_dpp_rx_auth_resp(struct wpa_supplicant *wpa_s, const u8 *src,
+				  const u8 *buf, size_t len)
+{
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+	struct wpabuf *msg, *attr;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication Response from " MACSTR,
+		   MAC2STR(src));
+
+	if (!auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Authentication in progress - drop");
+		return;
+	}
+
+	if (!is_zero_ether_addr(auth->peer_mac_addr) &&
+	    os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+			   MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+		return;
+	}
+
+	eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
+
+	attr = dpp_auth_resp_rx(auth, buf, len);
+	if (!attr) {
+		if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Start wait for full response");
+			offchannel_send_action_done(wpa_s);
+			wpas_dpp_listen_start(wpa_s, auth->curr_freq);
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "DPP: No confirm generated");
+		return;
+	}
+	os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
+
+	msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_CONF, wpabuf_len(attr));
+	if (!msg) {
+		wpabuf_free(attr);
+		return;
+	}
+	wpabuf_put_buf(msg, attr);
+	wpabuf_free(attr);
+
+	offchannel_send_action(wpa_s, auth->curr_freq,
+			       src, wpa_s->own_addr, broadcast,
+			       wpabuf_head(msg), wpabuf_len(msg),
+			       500, wpas_dpp_tx_status, 0);
+	wpabuf_free(msg);
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=1");
+}
+
+
+static void wpas_dpp_rx_auth_conf(struct wpa_supplicant *wpa_s, const u8 *src,
+				  const u8 *buf, size_t len)
+{
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation from " MACSTR,
+		   MAC2STR(src));
+
+	if (!auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Authentication in progress - drop");
+		return;
+	}
+
+	if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+			   MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+		return;
+	}
+
+	if (dpp_auth_conf_rx(auth, buf, len) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Authentication failed");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=0");
+}
+
+
+void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src,
+			const u8 *buf, size_t len, unsigned int freq)
+{
+	enum dpp_public_action_frame_type type;
+
+	if (len < 1)
+		return;
+	type = buf[0];
+	buf++;
+	len--;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Received DPP Public Action frame type %d from "
+		   MACSTR " freq=%u",
+		   type, MAC2STR(src), freq);
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes", buf, len);
+	if (dpp_check_attrs(buf, len) < 0)
+		return;
+
+	switch (type) {
+	case DPP_PA_AUTHENTICATION_REQ:
+		wpas_dpp_rx_auth_req(wpa_s, src, buf, len, freq);
+		break;
+	case DPP_PA_AUTHENTICATION_RESP:
+		wpas_dpp_rx_auth_resp(wpa_s, src, buf, len);
+		break;
+	case DPP_PA_AUTHENTICATION_CONF:
+		wpas_dpp_rx_auth_conf(wpa_s, src, buf, len);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignored unsupported frame subtype %d", type);
+		break;
+	}
+}
+
+
 int wpas_dpp_init(struct wpa_supplicant *wpa_s)
 {
 	dl_list_init(&wpa_s->dpp_bootstrap);
@@ -227,5 +810,10 @@ void wpas_dpp_deinit(struct wpa_supplicant *wpa_s)
 {
 	if (!wpa_s->dpp_init_done)
 		return;
+	eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
+	offchannel_send_action_done(wpa_s);
+	wpas_dpp_listen_stop(wpa_s);
 	dpp_bootstrap_del(wpa_s, 0);
+	dpp_auth_deinit(wpa_s->dpp_auth);
+	wpa_s->dpp_auth = NULL;
 }

+ 9 - 0
wpa_supplicant/dpp_supplicant.h

@@ -14,6 +14,15 @@ int wpas_dpp_bootstrap_gen(struct wpa_supplicant *wpa_s, const char *cmd);
 int wpas_dpp_bootstrap_remove(struct wpa_supplicant *wpa_s, const char *id);
 const char * wpas_dpp_bootstrap_get_uri(struct wpa_supplicant *wpa_s,
 					unsigned int id);
+int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd);
+int wpas_dpp_listen(struct wpa_supplicant *wpa_s, const char *cmd);
+void wpas_dpp_listen_stop(struct wpa_supplicant *wpa_s);
+void wpas_dpp_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+				   unsigned int freq);
+void wpas_dpp_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+					  unsigned int freq);
+void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src,
+			const u8 *buf, size_t len, unsigned int freq);
 int wpas_dpp_init(struct wpa_supplicant *wpa_s);
 void wpas_dpp_deinit(struct wpa_supplicant *wpa_s);
 

+ 18 - 0
wpa_supplicant/events.c

@@ -28,6 +28,7 @@
 #include "notify.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
+#include "common/dpp.h"
 #include "crypto/random.h"
 #include "blacklist.h"
 #include "wpas_glue.h"
@@ -46,6 +47,7 @@
 #include "mesh.h"
 #include "mesh_mpm.h"
 #include "wmm_ac.h"
+#include "dpp_supplicant.h"
 
 
 #ifndef CONFIG_NO_SCAN_PROCESSING
@@ -3582,6 +3584,18 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s,
 	}
 #endif /* CONFIG_FST */
 
+#ifdef CONFIG_DPP
+	if (category == WLAN_ACTION_PUBLIC && plen >= 5 &&
+	    payload[0] == WLAN_PA_VENDOR_SPECIFIC &&
+	    WPA_GET_BE24(&payload[1]) == OUI_WFA &&
+	    payload[4] == DPP_OUI_TYPE) {
+		payload += 5;
+		plen -= 5;
+		wpas_dpp_rx_action(wpa_s, mgmt->sa, payload, plen, freq);
+		return;
+	}
+#endif /* CONFIG_DPP */
+
 	wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
 			   category, payload, plen, freq);
 	if (wpa_s->ifmsh)
@@ -4171,6 +4185,10 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 #endif /* CONFIG_OFFCHANNEL */
 		wpas_p2p_cancel_remain_on_channel_cb(
 			wpa_s, data->remain_on_channel.freq);
+#ifdef CONFIG_DPP
+		wpas_dpp_cancel_remain_on_channel_cb(
+			wpa_s, data->remain_on_channel.freq);
+#endif /* CONFIG_DPP */
 		break;
 	case EVENT_EAPOL_RX:
 		wpa_supplicant_rx_eapol(wpa_s, data->eapol_rx.src,

+ 6 - 0
wpa_supplicant/wpa_supplicant_i.h

@@ -1160,6 +1160,12 @@ struct wpa_supplicant {
 #ifdef CONFIG_DPP
 	struct dl_list dpp_bootstrap; /* struct dpp_bootstrap_info */
 	int dpp_init_done;
+	struct dpp_authentication *dpp_auth;
+	struct wpa_radio_work *dpp_listen_work;
+	unsigned int dpp_pending_listen_freq;
+	unsigned int dpp_listen_freq;
+	u8 dpp_allowed_roles;
+	int dpp_qr_mutual;
 #endif /* CONFIG_DPP */
 };