iwd.lists.linux.dev archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/9] dpp: prep for moving AAD within dpp_append_wrapped_data
@ 2024-03-27 15:19 James Prestwood
  2024-03-27 15:19 ` [PATCH 2/9] dpp-util: move AAD logic within dpp_append_wrapped_attributes James Prestwood
                   ` (8 more replies)
  0 siblings, 9 replies; 15+ messages in thread
From: James Prestwood @ 2024-03-27 15:19 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

The AAD pointers for DPP are specific to the frame type. This is
currently sorted out by the caller within the respective frame
building functions but its quite unreadable. There are some comments
but lots of magic numbers. This should be moved within the
dpp_append_wrapped_data utility but the first step is to make the
frame buffer continuous. This will allow the entire frame to be
passed and dpp_append_wrapped_data can calculate the AAD offsets
itself.
---
 src/dpp.c | 239 +++++++++++++++++++++++++-----------------------------
 1 file changed, 112 insertions(+), 127 deletions(-)

diff --git a/src/dpp.c b/src/dpp.c
index 567fe8d2..5aac22a7 100644
--- a/src/dpp.c
+++ b/src/dpp.c
@@ -648,7 +648,7 @@ static void dpp_frame_retry(struct dpp_sm *dpp)
 
 static size_t dpp_build_header(const uint8_t *src, const uint8_t *dest,
 				enum dpp_frame_type type,
-				uint8_t buf[static 32])
+				uint8_t *buf)
 {
 	uint8_t *ptr = buf + 24;
 
@@ -672,7 +672,7 @@ static size_t dpp_build_header(const uint8_t *src, const uint8_t *dest,
 
 static size_t dpp_build_config_header(const uint8_t *src, const uint8_t *dest,
 					uint8_t diag_token,
-					uint8_t buf[static 37])
+					uint8_t *buf)
 {
 	uint8_t *ptr = buf + 24;
 
@@ -753,22 +753,21 @@ static void dpp_configuration_start(struct dpp_sm *dpp, const uint8_t *addr)
 {
 	const char *json = "{\"name\":\"IWD\",\"wi-fi_tech\":\"infra\","
 				"\"netRole\":\"sta\"}";
-	struct iovec iov[3];
-	uint8_t hdr[37];
-	uint8_t attrs[512];
+	struct iovec iov;
+	uint8_t frame[512];
 	size_t json_len = strlen(json);
-	uint8_t *ptr = attrs;
+	uint8_t *ptr = frame;
+	uint8_t *lptr;
 
 	l_getrandom(&dpp->diag_token, 1);
 
-	iov[0].iov_len = dpp_build_config_header(
-					netdev_get_address(dpp->netdev),
-					addr, dpp->diag_token, hdr);
-	iov[0].iov_base = hdr;
+	ptr += dpp_build_config_header(netdev_get_address(dpp->netdev),
+					addr, dpp->diag_token, ptr);
 
 	l_getrandom(dpp->e_nonce, dpp->nonce_len);
 
 	/* length */
+	lptr = ptr;
 	ptr += 2;
 
 	/*
@@ -780,42 +779,39 @@ static void dpp_configuration_start(struct dpp_sm *dpp, const uint8_t *addr)
 	 * In this case there is no query request/response fields, nor any
 	 * attributes besides wrapped data meaning zero AD components.
 	 */
-	ptr += dpp_append_wrapped_data(NULL, 0, NULL, 0, ptr, sizeof(attrs),
+	ptr += dpp_append_wrapped_data(NULL, 0, NULL, 0, ptr, sizeof(frame),
 			dpp->ke, dpp->key_len, 2,
 			DPP_ATTR_ENROLLEE_NONCE, dpp->nonce_len, dpp->e_nonce,
 			DPP_ATTR_CONFIGURATION_REQUEST, json_len, json);
 
-	l_put_le16(ptr - attrs - 2, attrs);
+	l_put_le16(ptr - lptr - 2, lptr);
 
-	iov[1].iov_base = attrs;
-	iov[1].iov_len = ptr - attrs;
+	iov.iov_base = frame;
+	iov.iov_len = ptr - frame;
 
 	dpp->state = DPP_STATE_CONFIGURING;
 
-	dpp_send_frame(dpp, iov, 2, dpp->current_freq);
+	dpp_send_frame(dpp, &iov, 1, dpp->current_freq);
 }
 
 static void send_config_result(struct dpp_sm *dpp, const uint8_t *to)
 {
-	uint8_t hdr[32];
-	struct iovec iov[2];
-	uint8_t attrs[256];
-	uint8_t *ptr = attrs;
+	struct iovec iov;
+	uint8_t frame[256];
+	uint8_t *ptr = frame;
 	uint8_t zero = 0;
 
-	iov[0].iov_len = dpp_build_header(netdev_get_address(dpp->netdev), to,
-					DPP_FRAME_CONFIGURATION_RESULT, hdr);
-	iov[0].iov_base = hdr;
-
-	ptr += dpp_append_wrapped_data(hdr + 26, 6, attrs, 0, ptr,
-			sizeof(attrs), dpp->ke, dpp->key_len, 2,
+	ptr += dpp_build_header(netdev_get_address(dpp->netdev), to,
+					DPP_FRAME_CONFIGURATION_RESULT, ptr);
+	ptr += dpp_append_wrapped_data(frame + 26, 6, ptr, 0, ptr,
+			sizeof(frame), dpp->ke, dpp->key_len, 2,
 			DPP_ATTR_STATUS, (size_t) 1, &zero,
 			DPP_ATTR_ENROLLEE_NONCE, dpp->nonce_len, dpp->e_nonce);
 
-	iov[1].iov_base = attrs;
-	iov[1].iov_len = ptr - attrs;
+	iov.iov_base = frame;
+	iov.iov_len = ptr - frame;
 
-	dpp_send_frame(dpp, iov, 2, dpp->current_freq);
+	dpp_send_frame(dpp, &iov, 1, dpp->current_freq);
 }
 
 static void dpp_write_config(struct dpp_configuration *config,
@@ -1162,18 +1158,20 @@ static void dpp_handle_config_response_frame(const struct mmpdu_header *frame,
 static void dpp_send_config_response(struct dpp_sm *dpp, uint8_t status)
 {
 	_auto_(l_free) char *json = NULL;
-	struct iovec iov[3];
-	uint8_t hdr[41];
-	uint8_t attrs[512];
+	struct iovec iov;
+	uint8_t frame[512];
 	size_t json_len;
-	uint8_t *ptr = hdr + 24;
+	uint8_t *ptr = frame;
+	uint8_t *lptr;
+
+	memset(frame, 0, sizeof(frame));
 
-	memset(hdr, 0, sizeof(hdr));
+	l_put_le16(0x00d0, ptr);
+	memcpy(ptr + 4, dpp->peer_addr, 6);
+	memcpy(ptr + 10, netdev_get_address(dpp->netdev), 6);
+	memcpy(ptr + 16, broadcast, 6);
 
-	l_put_le16(0x00d0, hdr);
-	memcpy(hdr + 4, dpp->peer_addr, 6);
-	memcpy(hdr + 10, netdev_get_address(dpp->netdev), 6);
-	memcpy(hdr + 16, broadcast, 6);
+	ptr += 24;
 
 	*ptr++ = 0x04;
 	*ptr++ = 0x0b;
@@ -1192,11 +1190,7 @@ static void dpp_send_config_response(struct dpp_sm *dpp, uint8_t status)
 	*ptr++ = 0x1a;
 	*ptr++ = 1;
 
-	iov[0].iov_base = hdr;
-	iov[0].iov_len = ptr - hdr;
-
-	ptr = attrs;
-
+	lptr = ptr;
 	ptr += 2; /* length */
 
 	ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &status, 1);
@@ -1211,26 +1205,26 @@ static void dpp_send_config_response(struct dpp_sm *dpp, uint8_t status)
 		json = dpp_configuration_to_json(dpp->config);
 		json_len = strlen(json);
 
-		ptr += dpp_append_wrapped_data(attrs + 2, ptr - attrs - 2,
-						NULL, 0, ptr, sizeof(attrs),
+		ptr += dpp_append_wrapped_data(lptr + 2, ptr - lptr - 2,
+						NULL, 0, ptr, sizeof(frame),
 						dpp->ke, dpp->key_len, 2,
 						DPP_ATTR_ENROLLEE_NONCE,
 						dpp->nonce_len, dpp->e_nonce,
 						DPP_ATTR_CONFIGURATION_OBJECT,
 						json_len, json);
 	} else
-		ptr += dpp_append_wrapped_data(attrs + 2, ptr - attrs - 2,
-						NULL, 0, ptr, sizeof(attrs),
+		ptr += dpp_append_wrapped_data(lptr + 2, ptr - lptr - 2,
+						NULL, 0, ptr, sizeof(frame),
 						dpp->ke, dpp->key_len, 2,
 						DPP_ATTR_ENROLLEE_NONCE,
 						dpp->nonce_len, dpp->e_nonce);
 
-	l_put_le16(ptr - attrs - 2, attrs);
+	l_put_le16(ptr - lptr - 2, lptr);
 
-	iov[1].iov_base = attrs;
-	iov[1].iov_len = ptr - attrs;
+	iov.iov_base = frame;
+	iov.iov_len = ptr - frame;
 
-	dpp_send_frame(dpp, iov, 2, dpp->current_freq);
+	dpp_send_frame(dpp, &iov, 1, dpp->current_freq);
 }
 
 static bool dpp_check_config_header(const uint8_t *ptr)
@@ -1498,13 +1492,13 @@ static void dpp_handle_config_result_frame(struct dpp_sm *dpp,
  */
 static void send_authenticate_response(struct dpp_sm *dpp)
 {
-	uint8_t hdr[32];
-	uint8_t attrs[512];
-	uint8_t *ptr = attrs;
+	uint8_t frame[512];
+	uint8_t *ptr = frame;
+	uint8_t *attrs;
 	uint8_t status = DPP_STATUS_OK;
 	uint64_t r_proto_key[L_ECC_MAX_DIGITS * 2];
 	uint8_t version = 2;
-	struct iovec iov[3];
+	struct iovec iov;
 	uint8_t wrapped2_plaintext[dpp->key_len + 4];
 	uint8_t wrapped2[dpp->key_len + 16 + 8];
 	size_t wrapped2_len;
@@ -1512,11 +1506,10 @@ static void send_authenticate_response(struct dpp_sm *dpp)
 	l_ecc_point_get_data(dpp->own_proto_public, r_proto_key,
 				sizeof(r_proto_key));
 
-	iov[0].iov_len = dpp_build_header(netdev_get_address(dpp->netdev),
+	ptr += dpp_build_header(netdev_get_address(dpp->netdev),
 				dpp->peer_addr,
-				DPP_FRAME_AUTHENTICATION_RESPONSE, hdr);
-	iov[0].iov_base = hdr;
-
+				DPP_FRAME_AUTHENTICATION_RESPONSE, ptr);
+	attrs = ptr;
 	ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &status, 1);
 	ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH,
 				dpp->own_boot_hash, 32);
@@ -1545,17 +1538,17 @@ static void send_authenticate_response(struct dpp_sm *dpp)
 
 	wrapped2_len += 16;
 
-	ptr += dpp_append_wrapped_data(hdr + 26, 6, attrs, ptr - attrs,
-			ptr, sizeof(attrs), dpp->k2, dpp->key_len, 4,
+	ptr += dpp_append_wrapped_data(frame + 26, 6, attrs, ptr - attrs,
+			ptr, sizeof(frame), dpp->k2, dpp->key_len, 4,
 			DPP_ATTR_RESPONDER_NONCE, dpp->nonce_len, dpp->r_nonce,
 			DPP_ATTR_INITIATOR_NONCE, dpp->nonce_len, dpp->i_nonce,
 			DPP_ATTR_RESPONDER_CAPABILITIES, (size_t) 1, &dpp->role,
 			DPP_ATTR_WRAPPED_DATA, wrapped2_len, wrapped2);
 
-	iov[1].iov_base = attrs;
-	iov[1].iov_len = ptr - attrs;
+	iov.iov_base = frame;
+	iov.iov_len = ptr - frame;
 
-	dpp_send_frame(dpp, iov, 2, dpp->current_freq);
+	dpp_send_frame(dpp, &iov, 1, dpp->current_freq);
 }
 
 static void authenticate_confirm(struct dpp_sm *dpp, const uint8_t *from,
@@ -1699,34 +1692,33 @@ static void dpp_auth_request_failed(struct dpp_sm *dpp,
 					enum dpp_status status,
 					void *k1)
 {
-	uint8_t hdr[32];
-	uint8_t attrs[128];
-	uint8_t *ptr = attrs;
+	uint8_t frame[128];
+	uint8_t *ptr = frame;
+	uint8_t *attrs;
 	uint8_t version = 2;
 	uint8_t s = status;
-	struct iovec iov[2];
+	struct iovec iov;
 
-	iov[0].iov_len = dpp_build_header(netdev_get_address(dpp->netdev),
+	ptr += dpp_build_header(netdev_get_address(dpp->netdev),
 				dpp->peer_addr,
-				DPP_FRAME_AUTHENTICATION_RESPONSE, hdr);
-	iov[0].iov_base = hdr;
-
+				DPP_FRAME_AUTHENTICATION_RESPONSE, ptr);
+	attrs = ptr;
 	ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &s, 1);
 	ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH,
 				dpp->own_boot_hash, 32);
 
 	ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION, &version, 1);
 
-	ptr += dpp_append_wrapped_data(hdr + 26, 6, attrs, ptr - attrs,
-			ptr, sizeof(attrs) - (ptr - attrs), k1, dpp->key_len, 2,
+	ptr += dpp_append_wrapped_data(frame + 26, 6, attrs, ptr - attrs,
+			ptr, sizeof(frame) - (ptr - attrs), k1, dpp->key_len, 2,
 			DPP_ATTR_INITIATOR_NONCE, dpp->nonce_len, dpp->i_nonce,
 			DPP_ATTR_RESPONDER_CAPABILITIES,
 			(size_t) 1, &dpp->role);
 
-	iov[1].iov_base = attrs;
-	iov[1].iov_len = ptr - attrs;
+	iov.iov_base = frame;
+	iov.iov_len = ptr - frame;
 
-	dpp_send_frame(dpp, iov, 2, dpp->current_freq);
+	dpp_send_frame(dpp, &iov, 1, dpp->current_freq);
 }
 
 static bool dpp_check_roles(struct dpp_sm *dpp, uint8_t peer_capa)
@@ -1771,12 +1763,12 @@ static void dpp_presence_announce(struct dpp_sm *dpp)
 
 static bool dpp_send_authenticate_request(struct dpp_sm *dpp)
 {
-	uint8_t hdr[32];
-	uint8_t attrs[256];
-	uint8_t *ptr = attrs;
+	uint8_t frame[256];
+	uint8_t *ptr = frame;
+	uint8_t *attrs;
 	uint64_t i_proto_key[L_ECC_MAX_DIGITS * 2];
 	uint8_t version = 2;
-	struct iovec iov[2];
+	struct iovec iov;
 	struct station *station = station_find(netdev_get_ifindex(dpp->netdev));
 	struct scan_bss *bss = station_get_connected_bss(station);
 
@@ -1789,10 +1781,10 @@ static bool dpp_send_authenticate_request(struct dpp_sm *dpp)
 	l_ecc_point_get_data(dpp->own_proto_public, i_proto_key,
 				sizeof(i_proto_key));
 
-	iov[0].iov_len = dpp_build_header(netdev_get_address(dpp->netdev),
+	ptr += dpp_build_header(netdev_get_address(dpp->netdev),
 				dpp->peer_addr,
-				DPP_FRAME_AUTHENTICATION_REQUEST, hdr);
-	iov[0].iov_base = hdr;
+				DPP_FRAME_AUTHENTICATION_REQUEST, ptr);
+	attrs = ptr;
 
 	ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH,
 				dpp->peer_boot_hash, 32);
@@ -1810,16 +1802,16 @@ static bool dpp_send_authenticate_request(struct dpp_sm *dpp)
 		ptr += dpp_append_attr(ptr, DPP_ATTR_CHANNEL, pair, 2);
 	}
 
-	ptr += dpp_append_wrapped_data(hdr + 26, 6, attrs, ptr - attrs,
-			ptr, sizeof(attrs), dpp->k1, dpp->key_len, 2,
+	ptr += dpp_append_wrapped_data(frame + 26, 6, attrs, ptr - attrs,
+			ptr, sizeof(frame), dpp->k1, dpp->key_len, 2,
 			DPP_ATTR_INITIATOR_NONCE, dpp->nonce_len, dpp->i_nonce,
 			DPP_ATTR_INITIATOR_CAPABILITIES,
 			(size_t) 1, &dpp->role);
 
-	iov[1].iov_base = attrs;
-	iov[1].iov_len = ptr - attrs;
+	iov.iov_base = frame;
+	iov.iov_len = ptr - frame;
 
-	dpp_send_frame(dpp, iov, 2, dpp->current_freq);
+	dpp_send_frame(dpp, &iov, 1, dpp->current_freq);
 
 	return true;
 }
@@ -1862,31 +1854,28 @@ static void dpp_send_pkex_exchange_request(struct dpp_sm *dpp)
 
 static void dpp_send_commit_reveal_request(struct dpp_sm *dpp)
 {
-	struct iovec iov[2];
-	uint8_t hdr[41];
-	uint8_t attrs[512];
-	uint8_t *ptr = attrs;
+	struct iovec iov;
+	uint8_t frame[512];
+	uint8_t *ptr = frame;
 	uint8_t zero = 0;
 	uint8_t a_pub[L_ECC_POINT_MAX_BYTES];
 	ssize_t a_len;
 
 	a_len = l_ecc_point_get_data(dpp->boot_public, a_pub, sizeof(a_pub));
 
-	iov[0].iov_len = dpp_build_header(netdev_get_address(dpp->netdev),
+	ptr += dpp_build_header(netdev_get_address(dpp->netdev),
 					dpp->peer_addr,
 					DPP_FRAME_PKEX_COMMIT_REVEAL_REQUEST,
-					hdr);
-	iov[0].iov_base = hdr;
-
-	ptr += dpp_append_wrapped_data(hdr + 26, 6, &zero, 1, ptr,
-			sizeof(attrs), dpp->z, dpp->z_len, 2,
+					ptr);
+	ptr += dpp_append_wrapped_data(frame + 26, 6, &zero, 1, ptr,
+			sizeof(frame), dpp->z, dpp->z_len, 2,
 			DPP_ATTR_BOOTSTRAPPING_KEY, a_len, a_pub,
 			DPP_ATTR_INITIATOR_AUTH_TAG, dpp->u_len, dpp->u);
 
-	iov[1].iov_base = attrs;
-	iov[1].iov_len = ptr - attrs;
+	iov.iov_base = frame;
+	iov.iov_len = ptr - frame;
 
-	dpp_send_frame(dpp, iov, 2, dpp->current_freq);
+	dpp_send_frame(dpp, &iov, 1, dpp->current_freq);
 }
 
 static void dpp_roc_started(void *user_data)
@@ -2272,17 +2261,16 @@ auth_request_failed:
 
 static void dpp_send_authenticate_confirm(struct dpp_sm *dpp)
 {
-	uint8_t hdr[32];
-	struct iovec iov[2];
-	uint8_t attrs[256];
-	uint8_t *ptr = attrs;
+	struct iovec iov;
+	uint8_t frame[256];
+	uint8_t *ptr = frame;
+	uint8_t *attrs;
 	uint8_t zero = 0;
 
-	iov[0].iov_len = dpp_build_header(netdev_get_address(dpp->netdev),
+	ptr += dpp_build_header(netdev_get_address(dpp->netdev),
 					dpp->peer_addr,
-					DPP_FRAME_AUTHENTICATION_CONFIRM, hdr);
-	iov[0].iov_base = hdr;
-
+					DPP_FRAME_AUTHENTICATION_CONFIRM, ptr);
+	attrs = ptr;
 	ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &zero, 1);
 	ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH,
 					dpp->peer_boot_hash, 32);
@@ -2290,15 +2278,15 @@ static void dpp_send_authenticate_confirm(struct dpp_sm *dpp)
 		ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_BOOT_KEY_HASH,
 					dpp->own_boot_hash, 32);
 
-	ptr += dpp_append_wrapped_data(hdr + 26, 6, attrs, ptr - attrs, ptr,
-			sizeof(attrs), dpp->ke, dpp->key_len, 1,
+	ptr += dpp_append_wrapped_data(frame + 26, 6, attrs, ptr - attrs, ptr,
+			sizeof(frame), dpp->ke, dpp->key_len, 1,
 			DPP_ATTR_INITIATOR_AUTH_TAG, dpp->key_len,
 			dpp->auth_tag);
 
-	iov[1].iov_base = attrs;
-	iov[1].iov_len = ptr - attrs;
+	iov.iov_base = frame;
+	iov.iov_len = ptr - frame;
 
-	dpp_send_frame(dpp, iov, 2, dpp->current_freq);
+	dpp_send_frame(dpp, &iov, 1, dpp->current_freq);
 }
 
 static void authenticate_response(struct dpp_sm *dpp, const uint8_t *from,
@@ -3311,11 +3299,10 @@ bad_group:
 static void dpp_send_commit_reveal_response(struct dpp_sm *dpp,
 						const uint8_t *v, size_t v_len)
 {
-	uint8_t hdr[32];
-	uint8_t attrs[256];
-	uint8_t *ptr = attrs;
+	uint8_t frame[256];
+	uint8_t *ptr = frame;
 	uint8_t one = 1;
-	struct iovec iov[2];
+	struct iovec iov;
 	const uint8_t *own_mac = netdev_get_address(dpp->netdev);
 	uint8_t b_pub[L_ECC_POINT_MAX_BYTES];
 	size_t b_len;
@@ -3323,19 +3310,17 @@ static void dpp_send_commit_reveal_response(struct dpp_sm *dpp,
 	b_len = l_ecc_point_get_data(dpp->boot_public, b_pub, sizeof(b_pub));
 
 
-	iov[0].iov_len = dpp_build_header(own_mac, dpp->peer_addr,
-				DPP_FRAME_PKEX_COMMIT_REVEAL_RESPONSE, hdr);
-	iov[0].iov_base = hdr;
-
-	ptr += dpp_append_wrapped_data(hdr + 26, 6, &one, 1, ptr,
-			sizeof(attrs), dpp->z, dpp->z_len, 2,
+	ptr += dpp_build_header(own_mac, dpp->peer_addr,
+				DPP_FRAME_PKEX_COMMIT_REVEAL_RESPONSE, ptr);
+	ptr += dpp_append_wrapped_data(frame + 26, 6, &one, 1, ptr,
+			sizeof(frame), dpp->z, dpp->z_len, 2,
 			DPP_ATTR_BOOTSTRAPPING_KEY, b_len, b_pub,
 			DPP_ATTR_RESPONDER_AUTH_TAG, v_len, v);
 
-	iov[1].iov_base = attrs;
-	iov[1].iov_len = ptr - attrs;
+	iov.iov_base = frame;
+	iov.iov_len = ptr - frame;
 
-	dpp_send_frame(dpp, iov, 2, dpp->current_freq);
+	dpp_send_frame(dpp, &iov, 1, dpp->current_freq);
 }
 
 static void dpp_handle_pkex_commit_reveal_request(struct dpp_sm *dpp,
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 2/9] dpp-util: move AAD logic within dpp_append_wrapped_attributes
  2024-03-27 15:19 [PATCH 1/9] dpp: prep for moving AAD within dpp_append_wrapped_data James Prestwood
@ 2024-03-27 15:19 ` James Prestwood
  2024-04-02 15:19   ` Denis Kenzior
  2024-03-27 15:19 ` [PATCH 3/9] dpp-util: add dpp_append_point James Prestwood
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 15+ messages in thread
From: James Prestwood @ 2024-03-27 15:19 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

Leaving it up to the caller to calcluate the AAD resulted in lots of
magic values, and any comments associated are spread out within
dpp.c. The AAD values can be calculated entirely by the frame
contents so move that within dpp_append_wrapped_data.

The caller now only needs to pass the frame (after the mpdu header),
the length, and the offset to where the wrapped data should start.
The new AAD calculation includes all relavent comments so magic
offsets are documented.

The reason the entire mmpdu_body is not passed to
dpp_append_wrapped_attributes (and one byte further) is to future
proof for DPP encapsulation using TCP. For this, the category byte
is omitted and only the action byte and further is encapsulated.
Having dpp_append_wrapped_attributes start at the action byte
allows it to work regardless of 8021x or TCP encapsulation.
---
 src/dpp-util.c | 167 +++++++++++++++++++++++++++++++++++++++++++++----
 src/dpp-util.h |   5 +-
 src/dpp.c      |  99 +++++++++++++++++------------
 3 files changed, 218 insertions(+), 53 deletions(-)

diff --git a/src/dpp-util.c b/src/dpp-util.c
index cfdedbdd..ada7ed96 100644
--- a/src/dpp-util.c
+++ b/src/dpp-util.c
@@ -39,6 +39,11 @@
 #include "ell/asn1-private.h"
 #include "src/ie.h"
 
+#define DPP_ACTION_VENDOR_SPECIFIC	0x09
+#define DPP_ACTION_GAS_REQUEST		0x0a
+#define DPP_ACTION_GAS_RESPONSE		0x0b
+#define DPP_HDR_LEN			6
+
 /* WFA Easy Connect v3.0 C.1 Role-specific Elements for NIST p256 */
 static const uint8_t dpp_pkex_initiator_p256[64] = {
 	/* X */
@@ -463,22 +468,91 @@ uint8_t *dpp_unwrap_attr(const void *ad0, size_t ad0_len, const void *ad1,
 	return unwrapped;
 }
 
+static bool dpp_aad(const uint8_t *frame, size_t frame_len, uint8_t *to,
+			const uint8_t **ad0, size_t *ad0_len,
+			const uint8_t **ad1, size_t *ad1_len)
+{
+	/* For PKEX frames */
+	static uint8_t zero = 0;
+	static uint8_t one = 1;
+	enum dpp_frame_type type;
+	/* OUI field (inclusive) */
+	const uint8_t *start = frame + 1;
+
+	if (frame_len < 6)
+		return false;
+
+	type = l_get_u8(frame + 6);
+
+	switch (type) {
+
+	case DPP_FRAME_AUTHENTICATION_REQUEST:
+	case DPP_FRAME_AUTHENTICATION_RESPONSE:
+	case DPP_FRAME_AUTHENTICATION_CONFIRM:
+	case DPP_FRAME_CONFIGURATION_RESULT:
+		/*
+		 * Section 6.3.1.4 Protocol Conventions
+		 * All other invocations of AES-SIV in the DPP Authentication
+		 * protocol shall pass a vector of AAD having two components of
+		 * AAD in the following order:
+		 *     (1) the DPP header, as defined in Table 34, from the OUI
+		 *         field (inclusive) to the DPP Frame Type field
+		 *         (inclusive); and
+		 *     (2) all octets in a DPP Public Action frame after the DPP
+		 *         Frame Type field up to and including the last octet
+		 *         of the last attribute before the Wrapped Data
+		 *         attribute
+		 *
+		 * Note: The configuration result frame uses identical wordage
+		 *       but is in Section 6.4.1
+		 */
+		*ad0 = start;
+		*ad0_len = DPP_HDR_LEN;
+		*ad1 = start + DPP_HDR_LEN;
+		*ad1_len = to - start - DPP_HDR_LEN;
+		return true;
+	case DPP_FRAME_PKEX_COMMIT_REVEAL_REQUEST:
+		/*
+		 * The AAD for this operation shall consist of two components:
+		 *     (1) the DPP header, as defined in Table 34, from the OUI
+		 *         field (inclusive) to the DPP Frame Type field
+		 *         (inclusive); and
+		 *     (2) a single octet of the value zero
+		 */
+		*ad0 = start;
+		*ad0_len = DPP_HDR_LEN;
+		*ad1 = &zero;
+		*ad1_len = 1;
+		return true;
+	case DPP_FRAME_PKEX_COMMIT_REVEAL_RESPONSE:
+		/*
+		 * The AAD for this operation shall consist of two components:
+		 *     (1) the DPP header, as defined in Table 34, from the OUI
+		 *         field (inclusive) to the DPP Frame Type field
+		 *         (inclusive); and
+		 *     (2) a single octet of the value one
+		 */
+		*ad0 = start;
+		*ad0_len = DPP_HDR_LEN;
+		*ad1 = &one;
+		*ad1_len = 1;
+		return true;
+	default:
+		return false;
+	}
+}
+
 /*
- * Encrypt DPP attributes encapsulated in DPP wrapped data.
- *
- * ad0/ad0_len - frame specific AD0 component
- * ad1/ad0_len - frame specific AD1 component
- * to - buffer to encrypt data.
- * to_len - size of 'to'
+ * frame - start of action frame (excluding mpdu header and category)
+ * frame_len - total frame buffer size
+ * to - current position of DPP attributes (where wrapped data will start)
  * key - key used to encrypt
  * key_len - size of 'key'
  * num_attrs - number of attributes listed (type, length, data triplets)
  * ... - List of attributes, Type, Length, and data
  */
-size_t dpp_append_wrapped_data(const void *ad0, size_t ad0_len,
-				const void *ad1, size_t ad1_len,
-				uint8_t *to, size_t to_len,
-				const void *key, size_t key_len,
+size_t dpp_append_wrapped_data(const uint8_t *frame, size_t frame_len,
+				uint8_t *to, const void *key, size_t key_len,
 				size_t num_attrs, ...)
 {
 	size_t i;
@@ -488,6 +562,77 @@ size_t dpp_append_wrapped_data(const void *ad0, size_t ad0_len,
 	struct iovec ad[2];
 	size_t ad_size = 0;
 	va_list va;
+	uint8_t action;
+	const uint8_t *ad0 = NULL;
+	const uint8_t *ad1 = NULL;
+	size_t ad0_len, ad1_len;
+
+	/*
+	 * First determine the frame type. This could be passed in but due to
+	 * The config protocol using GAS request/response frames not all frames
+	 * map to a dpp_frame_type enum. Due to this, minimal parsing is done
+	 * on the frame to determine the type, and in turn the AAD
+	 * offsets/lengths.
+	 */
+	if (frame_len < 1)
+		return 0;
+
+	action = *frame;
+
+	switch (action) {
+	case DPP_ACTION_VENDOR_SPECIFIC:
+		if (!dpp_aad(frame, frame_len, to, &ad0, &ad0_len,
+				&ad1, &ad1_len))
+			return 0;
+
+		break;
+	/*
+	 * Section 6.4.1 Overview
+	 *
+	 * "AAD for use with AES-SIV for protected messages in the DPP
+	 * Configuration protocol shall consist of all octets in the
+	 * Query Request and Query Response fields up to the first octet
+	 * of the Wrapped Data attribute, which is the last attribute in a DPP
+	 * Configuration frame. When the number of octets of AAD is zero, the
+	 * number of components of AAD passed to AES-SIV is zero
+	 */
+	case DPP_ACTION_GAS_REQUEST:
+		/*
+		 * 8.3.2 DPP Configuration Request frame
+		 * The attributes begin 14 bytes after the action (inclusive)
+		 */
+		if (frame_len < 14)
+			return 0;
+
+		/* Start of query request */
+		ad0 = frame + 14;
+		/* "up to the first octet of the Wrapped Data attribute" */
+		ad0_len = to - frame - 14;
+
+		if (!ad0_len)
+			ad0 = NULL;
+
+		break;
+	case DPP_ACTION_GAS_RESPONSE:
+		/*
+		 * 8.3.3 DPP Configuration Response frame
+		 * The attributes begin 18 bytes after the action (inclusive)
+		 */
+		if (frame_len < 18)
+			return 0;
+
+		/* Start of query response */
+		ad0 = frame + 18;
+		/* "up to the first octet of the Wrapped Data attribute" */
+		ad0_len = to - frame - 18;
+
+		if (!ad0_len)
+			ad0 = NULL;
+
+		break;
+	default:
+		return 0;
+	}
 
 	va_start(va, num_attrs);
 
@@ -500,7 +645,7 @@ size_t dpp_append_wrapped_data(const void *ad0, size_t ad0_len,
 
 	va_end(va);
 
-	if (to_len < attrs_len + 4 + 16)
+	if (frame_len - (to - frame) < attrs_len + 4 + 16)
 		return false;
 
 	plaintext = l_malloc(attrs_len);
diff --git a/src/dpp-util.h b/src/dpp-util.h
index dc8a894b..387750aa 100644
--- a/src/dpp-util.h
+++ b/src/dpp-util.h
@@ -148,9 +148,8 @@ uint8_t *dpp_unwrap_attr(const void *ad0, size_t ad0_len, const void *ad1,
 				size_t *unwrapped_len);
 size_t dpp_append_attr(uint8_t *to, enum dpp_attribute_type type,
 				void *attr, size_t attr_len);
-size_t dpp_append_wrapped_data(const void *ad0, size_t ad0_len, const void *ad1,
-				size_t ad1_len, uint8_t *to, size_t to_len,
-				const void *key, size_t key_len,
+size_t dpp_append_wrapped_data(const uint8_t *frame, size_t frame_len,
+				uint8_t *to, const void *key, size_t key_len,
 				size_t num_attrs, ...);
 
 char *dpp_generate_uri(const uint8_t *asn1, size_t asn1_len, uint8_t version,
diff --git a/src/dpp.c b/src/dpp.c
index 5aac22a7..d710aa98 100644
--- a/src/dpp.c
+++ b/src/dpp.c
@@ -758,6 +758,9 @@ static void dpp_configuration_start(struct dpp_sm *dpp, const uint8_t *addr)
 	size_t json_len = strlen(json);
 	uint8_t *ptr = frame;
 	uint8_t *lptr;
+	struct mmpdu_header *hdr = (struct mmpdu_header *)frame;
+
+	memset(frame, 0, sizeof(frame));
 
 	l_getrandom(&dpp->diag_token, 1);
 
@@ -779,7 +782,8 @@ static void dpp_configuration_start(struct dpp_sm *dpp, const uint8_t *addr)
 	 * In this case there is no query request/response fields, nor any
 	 * attributes besides wrapped data meaning zero AD components.
 	 */
-	ptr += dpp_append_wrapped_data(NULL, 0, NULL, 0, ptr, sizeof(frame),
+	ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1,
+			sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr,
 			dpp->ke, dpp->key_len, 2,
 			DPP_ATTR_ENROLLEE_NONCE, dpp->nonce_len, dpp->e_nonce,
 			DPP_ATTR_CONFIGURATION_REQUEST, json_len, json);
@@ -800,11 +804,15 @@ static void send_config_result(struct dpp_sm *dpp, const uint8_t *to)
 	uint8_t frame[256];
 	uint8_t *ptr = frame;
 	uint8_t zero = 0;
+	struct mmpdu_header *hdr = (struct mmpdu_header *)frame;
+
+	memset(frame, 0, sizeof(frame));
 
 	ptr += dpp_build_header(netdev_get_address(dpp->netdev), to,
 					DPP_FRAME_CONFIGURATION_RESULT, ptr);
-	ptr += dpp_append_wrapped_data(frame + 26, 6, ptr, 0, ptr,
-			sizeof(frame), dpp->ke, dpp->key_len, 2,
+	ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1,
+			sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr,
+			dpp->ke, dpp->key_len, 2,
 			DPP_ATTR_STATUS, (size_t) 1, &zero,
 			DPP_ATTR_ENROLLEE_NONCE, dpp->nonce_len, dpp->e_nonce);
 
@@ -1163,6 +1171,7 @@ static void dpp_send_config_response(struct dpp_sm *dpp, uint8_t status)
 	size_t json_len;
 	uint8_t *ptr = frame;
 	uint8_t *lptr;
+	struct mmpdu_header *hdr = (struct mmpdu_header *)frame;
 
 	memset(frame, 0, sizeof(frame));
 
@@ -1205,19 +1214,19 @@ static void dpp_send_config_response(struct dpp_sm *dpp, uint8_t status)
 		json = dpp_configuration_to_json(dpp->config);
 		json_len = strlen(json);
 
-		ptr += dpp_append_wrapped_data(lptr + 2, ptr - lptr - 2,
-						NULL, 0, ptr, sizeof(frame),
-						dpp->ke, dpp->key_len, 2,
-						DPP_ATTR_ENROLLEE_NONCE,
-						dpp->nonce_len, dpp->e_nonce,
-						DPP_ATTR_CONFIGURATION_OBJECT,
-						json_len, json);
+		ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1,
+				sizeof(frame) - mmpdu_header_len(hdr) - 1,
+				ptr, dpp->ke, dpp->key_len, 2,
+				DPP_ATTR_ENROLLEE_NONCE,
+				dpp->nonce_len, dpp->e_nonce,
+				DPP_ATTR_CONFIGURATION_OBJECT,
+				json_len, json);
 	} else
-		ptr += dpp_append_wrapped_data(lptr + 2, ptr - lptr - 2,
-						NULL, 0, ptr, sizeof(frame),
-						dpp->ke, dpp->key_len, 2,
-						DPP_ATTR_ENROLLEE_NONCE,
-						dpp->nonce_len, dpp->e_nonce);
+		ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1,
+				sizeof(frame) - mmpdu_header_len(hdr) - 1,
+				ptr, dpp->ke, dpp->key_len, 2,
+				DPP_ATTR_ENROLLEE_NONCE,
+				dpp->nonce_len, dpp->e_nonce);
 
 	l_put_le16(ptr - lptr - 2, lptr);
 
@@ -1494,7 +1503,6 @@ static void send_authenticate_response(struct dpp_sm *dpp)
 {
 	uint8_t frame[512];
 	uint8_t *ptr = frame;
-	uint8_t *attrs;
 	uint8_t status = DPP_STATUS_OK;
 	uint64_t r_proto_key[L_ECC_MAX_DIGITS * 2];
 	uint8_t version = 2;
@@ -1502,6 +1510,9 @@ static void send_authenticate_response(struct dpp_sm *dpp)
 	uint8_t wrapped2_plaintext[dpp->key_len + 4];
 	uint8_t wrapped2[dpp->key_len + 16 + 8];
 	size_t wrapped2_len;
+	struct mmpdu_header *hdr = (struct mmpdu_header *)frame;
+
+	memset(frame, 0, sizeof(frame));
 
 	l_ecc_point_get_data(dpp->own_proto_public, r_proto_key,
 				sizeof(r_proto_key));
@@ -1509,7 +1520,6 @@ static void send_authenticate_response(struct dpp_sm *dpp)
 	ptr += dpp_build_header(netdev_get_address(dpp->netdev),
 				dpp->peer_addr,
 				DPP_FRAME_AUTHENTICATION_RESPONSE, ptr);
-	attrs = ptr;
 	ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &status, 1);
 	ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH,
 				dpp->own_boot_hash, 32);
@@ -1538,8 +1548,9 @@ static void send_authenticate_response(struct dpp_sm *dpp)
 
 	wrapped2_len += 16;
 
-	ptr += dpp_append_wrapped_data(frame + 26, 6, attrs, ptr - attrs,
-			ptr, sizeof(frame), dpp->k2, dpp->key_len, 4,
+	ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1,
+			sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr,
+			dpp->k2, dpp->key_len, 4,
 			DPP_ATTR_RESPONDER_NONCE, dpp->nonce_len, dpp->r_nonce,
 			DPP_ATTR_INITIATOR_NONCE, dpp->nonce_len, dpp->i_nonce,
 			DPP_ATTR_RESPONDER_CAPABILITIES, (size_t) 1, &dpp->role,
@@ -1694,23 +1705,25 @@ static void dpp_auth_request_failed(struct dpp_sm *dpp,
 {
 	uint8_t frame[128];
 	uint8_t *ptr = frame;
-	uint8_t *attrs;
 	uint8_t version = 2;
 	uint8_t s = status;
 	struct iovec iov;
+	struct mmpdu_header *hdr = (struct mmpdu_header *)frame;
+
+	memset(frame, 0, sizeof(frame));
 
 	ptr += dpp_build_header(netdev_get_address(dpp->netdev),
 				dpp->peer_addr,
 				DPP_FRAME_AUTHENTICATION_RESPONSE, ptr);
-	attrs = ptr;
 	ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &s, 1);
 	ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH,
 				dpp->own_boot_hash, 32);
 
 	ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION, &version, 1);
 
-	ptr += dpp_append_wrapped_data(frame + 26, 6, attrs, ptr - attrs,
-			ptr, sizeof(frame) - (ptr - attrs), k1, dpp->key_len, 2,
+	ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1,
+			sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr,
+			k1, dpp->key_len, 2,
 			DPP_ATTR_INITIATOR_NONCE, dpp->nonce_len, dpp->i_nonce,
 			DPP_ATTR_RESPONDER_CAPABILITIES,
 			(size_t) 1, &dpp->role);
@@ -1765,12 +1778,14 @@ static bool dpp_send_authenticate_request(struct dpp_sm *dpp)
 {
 	uint8_t frame[256];
 	uint8_t *ptr = frame;
-	uint8_t *attrs;
 	uint64_t i_proto_key[L_ECC_MAX_DIGITS * 2];
 	uint8_t version = 2;
 	struct iovec iov;
 	struct station *station = station_find(netdev_get_ifindex(dpp->netdev));
 	struct scan_bss *bss = station_get_connected_bss(station);
+	struct mmpdu_header *hdr = (struct mmpdu_header *)frame;
+
+	memset(frame, 0, sizeof(frame));
 
 	/* Got disconnected by the time the peer was discovered */
 	if (dpp->role == DPP_CAPABILITY_CONFIGURATOR && !bss) {
@@ -1784,8 +1799,6 @@ static bool dpp_send_authenticate_request(struct dpp_sm *dpp)
 	ptr += dpp_build_header(netdev_get_address(dpp->netdev),
 				dpp->peer_addr,
 				DPP_FRAME_AUTHENTICATION_REQUEST, ptr);
-	attrs = ptr;
-
 	ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH,
 				dpp->peer_boot_hash, 32);
 	ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_BOOT_KEY_HASH,
@@ -1802,8 +1815,9 @@ static bool dpp_send_authenticate_request(struct dpp_sm *dpp)
 		ptr += dpp_append_attr(ptr, DPP_ATTR_CHANNEL, pair, 2);
 	}
 
-	ptr += dpp_append_wrapped_data(frame + 26, 6, attrs, ptr - attrs,
-			ptr, sizeof(frame), dpp->k1, dpp->key_len, 2,
+	ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1,
+			sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr,
+			dpp->k1, dpp->key_len, 2,
 			DPP_ATTR_INITIATOR_NONCE, dpp->nonce_len, dpp->i_nonce,
 			DPP_ATTR_INITIATOR_CAPABILITIES,
 			(size_t) 1, &dpp->role);
@@ -1857,9 +1871,11 @@ static void dpp_send_commit_reveal_request(struct dpp_sm *dpp)
 	struct iovec iov;
 	uint8_t frame[512];
 	uint8_t *ptr = frame;
-	uint8_t zero = 0;
 	uint8_t a_pub[L_ECC_POINT_MAX_BYTES];
 	ssize_t a_len;
+	struct mmpdu_header *hdr = (struct mmpdu_header *)frame;
+
+	memset(frame, 0, sizeof(frame));
 
 	a_len = l_ecc_point_get_data(dpp->boot_public, a_pub, sizeof(a_pub));
 
@@ -1867,8 +1883,9 @@ static void dpp_send_commit_reveal_request(struct dpp_sm *dpp)
 					dpp->peer_addr,
 					DPP_FRAME_PKEX_COMMIT_REVEAL_REQUEST,
 					ptr);
-	ptr += dpp_append_wrapped_data(frame + 26, 6, &zero, 1, ptr,
-			sizeof(frame), dpp->z, dpp->z_len, 2,
+	ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1,
+			sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr,
+			dpp->z, dpp->z_len, 2,
 			DPP_ATTR_BOOTSTRAPPING_KEY, a_len, a_pub,
 			DPP_ATTR_INITIATOR_AUTH_TAG, dpp->u_len, dpp->u);
 
@@ -2264,13 +2281,14 @@ static void dpp_send_authenticate_confirm(struct dpp_sm *dpp)
 	struct iovec iov;
 	uint8_t frame[256];
 	uint8_t *ptr = frame;
-	uint8_t *attrs;
 	uint8_t zero = 0;
+	struct mmpdu_header *hdr = (struct mmpdu_header *)frame;
+
+	memset(frame, 0, sizeof(frame));
 
 	ptr += dpp_build_header(netdev_get_address(dpp->netdev),
 					dpp->peer_addr,
 					DPP_FRAME_AUTHENTICATION_CONFIRM, ptr);
-	attrs = ptr;
 	ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &zero, 1);
 	ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH,
 					dpp->peer_boot_hash, 32);
@@ -2278,8 +2296,9 @@ static void dpp_send_authenticate_confirm(struct dpp_sm *dpp)
 		ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_BOOT_KEY_HASH,
 					dpp->own_boot_hash, 32);
 
-	ptr += dpp_append_wrapped_data(frame + 26, 6, attrs, ptr - attrs, ptr,
-			sizeof(frame), dpp->ke, dpp->key_len, 1,
+	ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1,
+			sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr,
+			dpp->ke, dpp->key_len, 1,
 			DPP_ATTR_INITIATOR_AUTH_TAG, dpp->key_len,
 			dpp->auth_tag);
 
@@ -3301,19 +3320,21 @@ static void dpp_send_commit_reveal_response(struct dpp_sm *dpp,
 {
 	uint8_t frame[256];
 	uint8_t *ptr = frame;
-	uint8_t one = 1;
 	struct iovec iov;
 	const uint8_t *own_mac = netdev_get_address(dpp->netdev);
 	uint8_t b_pub[L_ECC_POINT_MAX_BYTES];
 	size_t b_len;
+	struct mmpdu_header *hdr = (struct mmpdu_header *)frame;
 
-	b_len = l_ecc_point_get_data(dpp->boot_public, b_pub, sizeof(b_pub));
+	memset(frame, 0, sizeof(frame));
 
+	b_len = l_ecc_point_get_data(dpp->boot_public, b_pub, sizeof(b_pub));
 
 	ptr += dpp_build_header(own_mac, dpp->peer_addr,
 				DPP_FRAME_PKEX_COMMIT_REVEAL_RESPONSE, ptr);
-	ptr += dpp_append_wrapped_data(frame + 26, 6, &one, 1, ptr,
-			sizeof(frame), dpp->z, dpp->z_len, 2,
+	ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1,
+			sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr,
+			dpp->z, dpp->z_len, 2,
 			DPP_ATTR_BOOTSTRAPPING_KEY, b_len, b_pub,
 			DPP_ATTR_RESPONDER_AUTH_TAG, v_len, v);
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 3/9] dpp-util: add dpp_append_point
  2024-03-27 15:19 [PATCH 1/9] dpp: prep for moving AAD within dpp_append_wrapped_data James Prestwood
  2024-03-27 15:19 ` [PATCH 2/9] dpp-util: move AAD logic within dpp_append_wrapped_attributes James Prestwood
@ 2024-03-27 15:19 ` James Prestwood
  2024-03-27 15:19 ` [PATCH 4/9] dpp: use dpp_append_point James Prestwood
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 15+ messages in thread
From: James Prestwood @ 2024-03-27 15:19 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

Any time DPP needs to append a point it has to use a temporary
buffer to copy into, then copy that into the message buffer.
Instead of this add a special purpose API to add the type/length
then copy the data directly into the message buffer.
---
 src/dpp-util.c | 13 +++++++++++++
 src/dpp-util.h |  2 ++
 2 files changed, 15 insertions(+)

diff --git a/src/dpp-util.c b/src/dpp-util.c
index ada7ed96..81f34047 100644
--- a/src/dpp-util.c
+++ b/src/dpp-util.c
@@ -697,6 +697,19 @@ size_t dpp_append_wrapped_data(const uint8_t *frame, size_t frame_len,
 	return attrs_len + 4 + 16;
 }
 
+size_t dpp_append_point(uint8_t *to, enum dpp_attribute_type type,
+				struct l_ecc_point *point)
+{
+	const struct l_ecc_curve *c = l_ecc_point_get_curve(point);
+	size_t len = l_ecc_curve_get_scalar_bytes(c) * 2;
+
+	l_put_le16(type, to);
+	l_put_le16(len, to + 2);
+	l_ecc_point_get_data(point, to + 4, len);
+
+	return len + 4;
+}
+
 /*
  * EasyConnect 2.0 Table 3. Key and Nonce Length Dependency on Prime Length
  */
diff --git a/src/dpp-util.h b/src/dpp-util.h
index 387750aa..1ff9004d 100644
--- a/src/dpp-util.h
+++ b/src/dpp-util.h
@@ -151,6 +151,8 @@ size_t dpp_append_attr(uint8_t *to, enum dpp_attribute_type type,
 size_t dpp_append_wrapped_data(const uint8_t *frame, size_t frame_len,
 				uint8_t *to, const void *key, size_t key_len,
 				size_t num_attrs, ...);
+size_t dpp_append_point(uint8_t *to, enum dpp_attribute_type type,
+				struct l_ecc_point *point);
 
 char *dpp_generate_uri(const uint8_t *asn1, size_t asn1_len, uint8_t version,
 			const uint8_t *mac, const uint32_t *freqs,
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 4/9] dpp: use dpp_append_point
  2024-03-27 15:19 [PATCH 1/9] dpp: prep for moving AAD within dpp_append_wrapped_data James Prestwood
  2024-03-27 15:19 ` [PATCH 2/9] dpp-util: move AAD logic within dpp_append_wrapped_attributes James Prestwood
  2024-03-27 15:19 ` [PATCH 3/9] dpp-util: add dpp_append_point James Prestwood
@ 2024-03-27 15:19 ` James Prestwood
  2024-03-27 15:19 ` [PATCH 5/9] dpp-common: Skeleton for common DPP module James Prestwood
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 15+ messages in thread
From: James Prestwood @ 2024-03-27 15:19 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

Use dpp_append_point where possible to avoid temporarily copying data
---
 src/dpp.c | 28 ++++++----------------------
 1 file changed, 6 insertions(+), 22 deletions(-)

diff --git a/src/dpp.c b/src/dpp.c
index d710aa98..47ebd495 100644
--- a/src/dpp.c
+++ b/src/dpp.c
@@ -1504,7 +1504,6 @@ static void send_authenticate_response(struct dpp_sm *dpp)
 	uint8_t frame[512];
 	uint8_t *ptr = frame;
 	uint8_t status = DPP_STATUS_OK;
-	uint64_t r_proto_key[L_ECC_MAX_DIGITS * 2];
 	uint8_t version = 2;
 	struct iovec iov;
 	uint8_t wrapped2_plaintext[dpp->key_len + 4];
@@ -1514,9 +1513,6 @@ static void send_authenticate_response(struct dpp_sm *dpp)
 
 	memset(frame, 0, sizeof(frame));
 
-	l_ecc_point_get_data(dpp->own_proto_public, r_proto_key,
-				sizeof(r_proto_key));
-
 	ptr += dpp_build_header(netdev_get_address(dpp->netdev),
 				dpp->peer_addr,
 				DPP_FRAME_AUTHENTICATION_RESPONSE, ptr);
@@ -1526,8 +1522,8 @@ static void send_authenticate_response(struct dpp_sm *dpp)
 	if (dpp->mutual_auth)
 		ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_BOOT_KEY_HASH,
 				dpp->peer_boot_hash, 32);
-	ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_PROTOCOL_KEY,
-				r_proto_key, dpp->key_len * 2);
+	ptr += dpp_append_point(ptr, DPP_ATTR_RESPONDER_PROTOCOL_KEY,
+				dpp->own_proto_public);
 	ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION, &version, 1);
 
 	/* Wrap up secondary data (R-Auth) */
@@ -1778,7 +1774,6 @@ static bool dpp_send_authenticate_request(struct dpp_sm *dpp)
 {
 	uint8_t frame[256];
 	uint8_t *ptr = frame;
-	uint64_t i_proto_key[L_ECC_MAX_DIGITS * 2];
 	uint8_t version = 2;
 	struct iovec iov;
 	struct station *station = station_find(netdev_get_ifindex(dpp->netdev));
@@ -1793,9 +1788,6 @@ static bool dpp_send_authenticate_request(struct dpp_sm *dpp)
 		return false;
 	}
 
-	l_ecc_point_get_data(dpp->own_proto_public, i_proto_key,
-				sizeof(i_proto_key));
-
 	ptr += dpp_build_header(netdev_get_address(dpp->netdev),
 				dpp->peer_addr,
 				DPP_FRAME_AUTHENTICATION_REQUEST, ptr);
@@ -1803,8 +1795,8 @@ static bool dpp_send_authenticate_request(struct dpp_sm *dpp)
 				dpp->peer_boot_hash, 32);
 	ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_BOOT_KEY_HASH,
 				dpp->own_boot_hash, 32);
-	ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_PROTOCOL_KEY,
-				i_proto_key, dpp->key_len * 2);
+	ptr += dpp_append_point(ptr, DPP_ATTR_INITIATOR_PROTOCOL_KEY,
+				dpp->own_proto_public);
 	ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION, &version, 1);
 
 	if (dpp->role == DPP_CAPABILITY_CONFIGURATOR &&
@@ -1835,7 +1827,6 @@ static void dpp_send_pkex_exchange_request(struct dpp_sm *dpp)
 	uint8_t hdr[32];
 	uint8_t attrs[256];
 	uint8_t *ptr = attrs;
-	uint64_t m_data[L_ECC_MAX_DIGITS * 2];
 	uint16_t group;
 	struct iovec iov[2];
 	const uint8_t *own_mac = netdev_get_address(dpp->netdev);
@@ -1855,10 +1846,7 @@ static void dpp_send_pkex_exchange_request(struct dpp_sm *dpp)
 		ptr += dpp_append_attr(ptr, DPP_ATTR_CODE_IDENTIFIER,
 					dpp->pkex_id, strlen(dpp->pkex_id));
 
-	l_ecc_point_get_data(dpp->pkex_m, m_data, sizeof(m_data));
-
-	ptr += dpp_append_attr(ptr, DPP_ATTR_ENCRYPTED_KEY,
-				m_data, dpp->key_len * 2);
+	ptr += dpp_append_point(ptr, DPP_ATTR_ENCRYPTED_KEY, dpp->pkex_m);
 
 	iov[1].iov_base = attrs;
 	iov[1].iov_len = ptr - attrs;
@@ -3018,7 +3006,6 @@ static void dpp_send_pkex_exchange_response(struct dpp_sm *dpp,
 	uint8_t hdr[32];
 	uint8_t attrs[256];
 	uint8_t *ptr = attrs;
-	uint64_t n_data[L_ECC_MAX_DIGITS * 2];
 	uint16_t group;
 	uint8_t status = DPP_STATUS_OK;
 	struct iovec iov[2];
@@ -3036,10 +3023,7 @@ static void dpp_send_pkex_exchange_response(struct dpp_sm *dpp,
 		ptr += dpp_append_attr(ptr, DPP_ATTR_CODE_IDENTIFIER,
 					dpp->pkex_id, strlen(dpp->pkex_id));
 
-	l_ecc_point_get_data(n, n_data, sizeof(n_data));
-
-	ptr += dpp_append_attr(ptr, DPP_ATTR_ENCRYPTED_KEY,
-				n_data, dpp->key_len * 2);
+	ptr += dpp_append_point(ptr, DPP_ATTR_ENCRYPTED_KEY, n);
 
 	iov[1].iov_base = attrs;
 	iov[1].iov_len = ptr - attrs;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 5/9] dpp-common: Skeleton for common DPP module
  2024-03-27 15:19 [PATCH 1/9] dpp: prep for moving AAD within dpp_append_wrapped_data James Prestwood
                   ` (2 preceding siblings ...)
  2024-03-27 15:19 ` [PATCH 4/9] dpp: use dpp_append_point James Prestwood
@ 2024-03-27 15:19 ` James Prestwood
  2024-03-27 15:19 ` [PATCH 6/9] dpp-common: add TX/RX handlers to dpp_sm James Prestwood
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 15+ messages in thread
From: James Prestwood @ 2024-03-27 15:19 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

In order to expand the features for DPP e.g. TCP or a DPP controller
the state machine needs to be both transport agnostic and buildable
for other apps outside of IWD-core.

To do this the core DPP state machine is being moved into a common
module. The consumers of the common SM will create a dpp_sm object
for each protocol run, and destroy it afterwards. The SM will take
a write callback, event callback, the boostrapping keys, and expose
a read handler:

 - The write callback will be called by the SM whenever there is a
   frame to be sent out. In order to support 802.11 encapsulation
   dpp_sm_set_write_handler can be used to notify the SM that writes
   are allowed or disallowed (by passing NULL). This is done to
   allow the 802.11 encapsulating module to prepare any offchannel
   requests before sending out frames.
 - The event callback is called by the SM whenever there are events
   requiring some handling by the encapsulating protocol, or when
   DPP has succeeded or failed.
 - The read handler can be called by the encapsulating protocol
   in order to RX frames to the SM.

Making the SM common will allow both pure-802.11 DPP and
DPP-over-TCP to utilize the same state machine. In addition a
DPP-controller can now be implemented using the common SM but be
entirely standalone from IWD.

Since a lot of the code was moved from dpp.c the original copyright
was retained.
---
 src/dpp-common.c | 518 +++++++++++++++++++++++++++++++++++++++++++++++
 src/dpp-common.h | 110 ++++++++++
 2 files changed, 628 insertions(+)
 create mode 100644 src/dpp-common.c
 create mode 100644 src/dpp-common.h

diff --git a/src/dpp-common.c b/src/dpp-common.c
new file mode 100644
index 00000000..45099d12
--- /dev/null
+++ b/src/dpp-common.c
@@ -0,0 +1,518 @@
+/*
+ *
+ *  Wireless daemon for Linux
+ *
+ *  Copyright (C) 2021  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2024  Locus Robotics
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <ell/ell.h>
+
+#include "linux/nl80211.h"
+
+#include "src/missing.h"
+#include "src/dbus.h"
+#include "src/netdev.h"
+#include "src/module.h"
+#include "src/dpp-util.h"
+#include "src/band.h"
+#include "src/frame-xchg.h"
+#include "src/offchannel.h"
+#include "src/wiphy.h"
+#include "src/ie.h"
+#include "src/iwd.h"
+#include "src/util.h"
+#include "src/crypto.h"
+#include "src/mpdu.h"
+#include "ell/useful.h"
+#include "src/common.h"
+#include "src/json.h"
+#include "src/storage.h"
+#include "src/station.h"
+#include "src/scan.h"
+#include "src/network.h"
+#include "src/handshake.h"
+#include "src/nl80211util.h"
+#include "src/knownnetworks.h"
+#include "src/dpp-common.h"
+
+#define DPP_HDR_LEN			6
+#define DPP_ACTION_VENDOR_SPECIFIC	0x09
+#define DPP_ACTION_GAS_REQUEST		0x0a
+#define DPP_ACTION_GAS_RESPONSE		0x0b
+#define DPP_AUTH_PROTO_TIMEOUT		10
+#define DPP_PKEX_PROTO_TIMEOUT		120
+
+struct dpp_sm {
+	char *uri;
+	uint8_t role;
+
+	uint8_t *own_asn1;
+	size_t own_asn1_len;
+	uint8_t *peer_asn1;
+	size_t peer_asn1_len;
+	uint8_t own_boot_hash[32];
+	uint8_t peer_boot_hash[32];
+	uint8_t own_chirp_hash[32];
+	const struct l_ecc_curve *curve;
+	size_t key_len;
+	size_t nonce_len;
+	struct l_ecc_scalar *boot_private;
+	struct l_ecc_point *boot_public;
+	struct l_ecc_point *peer_boot_public;
+
+	enum dpp_state state;
+
+	uint8_t r_nonce[32];
+	uint8_t i_nonce[32];
+	uint8_t e_nonce[32];
+
+	struct l_ecc_scalar *m;
+	uint64_t ke[L_ECC_MAX_DIGITS];
+	uint64_t k1[L_ECC_MAX_DIGITS];
+	uint64_t k2[L_ECC_MAX_DIGITS];
+	uint64_t auth_tag[L_ECC_MAX_DIGITS];
+
+	struct l_ecc_scalar *proto_private;
+	struct l_ecc_point *own_proto_public;
+
+	struct l_ecc_point *peer_proto_public;
+
+	uint8_t diag_token;
+
+	/* Timeout of auth/config/PKEX protocols */
+	uint64_t proto_timeout;
+	struct l_timeout *timeout;
+
+	struct dpp_configuration *config;
+
+	/* PKEX-specific values */
+	char *pkex_id;
+	char *pkex_key;
+	uint8_t pkex_version;
+	struct l_ecc_point *peer_encr_key;
+	struct l_ecc_point *pkex_m;
+	/* Ephemeral key Y' or X' for enrollee or configurator */
+	struct l_ecc_point *y_or_x;
+	/* Ephemeral key pair y/Y or x/X */
+	struct l_ecc_point *pkex_public;
+	struct l_ecc_scalar *pkex_private;
+	uint8_t z[L_ECC_SCALAR_MAX_BYTES];
+	size_t z_len;
+	uint8_t u[L_ECC_SCALAR_MAX_BYTES];
+	size_t u_len;
+	uint8_t pkex_own_mac[6];
+	uint8_t pkex_peer_mac[6];
+	/* Set to either own/peer mac depending on configuration */
+	const uint8_t *mac_initiator;
+	const uint8_t *mac_responder;
+
+	/*
+	 * Since the authenticate frame may request a channel switch we do need
+	 * to expose this detail within the common code.
+	 */
+	uint8_t channel[2];
+
+	uint8_t *frame_pending;
+	size_t frame_len;
+	dpp_write_cb_t write;
+	dpp_event_cb_t event_cb;
+	void *user_data;
+
+	bool skip_presence : 1;
+	bool mutual_auth : 1;
+	bool initiator : 1;
+};
+
+static void dpp_failed(struct dpp_sm *dpp)
+{
+	if (dpp->event_cb)
+		dpp->event_cb(DPP_EVENT_FAILED, NULL, dpp->user_data);
+}
+
+static void dpp_protocol_timeout(struct l_timeout *timeout, void *user_data)
+{
+	struct dpp_sm *dpp = user_data;
+
+	l_debug("DPP timed out");
+
+	dpp_failed(dpp);
+}
+
+static void dpp_reset_protocol_timer(struct dpp_sm *dpp, uint64_t time)
+{
+	if (dpp->timeout)
+		l_timeout_modify(dpp->timeout, time);
+	else
+		dpp->timeout = l_timeout_create(time, dpp_protocol_timeout,
+						dpp, NULL);
+}
+
+struct dpp_sm *dpp_sm_new(dpp_event_cb_t event,
+				const struct l_ecc_point *boot_public,
+				const struct l_ecc_scalar *boot_private,
+				void *user_data)
+{
+	struct dpp_sm *dpp = l_new(struct dpp_sm, 1);
+
+	dpp->state = DPP_STATE_NOTHING;
+	dpp->curve = l_ecc_point_get_curve(boot_public);
+	dpp->key_len = l_ecc_curve_get_scalar_bytes(dpp->curve);
+	dpp->nonce_len = dpp_nonce_len_from_key_len(dpp->key_len);
+	dpp->boot_public = l_ecc_point_clone(boot_public);
+	dpp->boot_private = l_ecc_scalar_clone(boot_private);
+	dpp->own_asn1 = dpp_point_to_asn1(dpp->boot_public, &dpp->own_asn1_len);
+	dpp->event_cb = event;
+	dpp->user_data = user_data;
+
+	dpp_hash(L_CHECKSUM_SHA256, dpp->own_boot_hash, 1, dpp->own_asn1,
+			dpp->own_asn1_len);
+	dpp_hash(L_CHECKSUM_SHA256, dpp->own_chirp_hash, 2, "chirp",
+			strlen("chirp"), dpp->own_asn1, dpp->own_asn1_len);
+
+	return dpp;
+}
+
+void dpp_sm_set_write_handler(struct dpp_sm *dpp, dpp_write_cb_t write)
+{
+	dpp->write = write;
+
+	if (!dpp->write)
+		return;
+
+	/* Handle writing frame */
+}
+
+static void dpp_free_auth_data(struct dpp_sm *dpp)
+{
+	if (dpp->own_proto_public) {
+		l_ecc_point_free(dpp->own_proto_public);
+		dpp->own_proto_public = NULL;
+	}
+
+	if (dpp->proto_private) {
+		l_ecc_scalar_free(dpp->proto_private);
+		dpp->proto_private = NULL;
+	}
+
+	if (dpp->peer_proto_public) {
+		l_ecc_point_free(dpp->peer_proto_public);
+		dpp->peer_proto_public = NULL;
+	}
+
+	if (dpp->peer_boot_public) {
+		l_ecc_point_free(dpp->peer_boot_public);
+		dpp->peer_boot_public = NULL;
+	}
+
+	if (dpp->m) {
+		l_ecc_scalar_free(dpp->m);
+		dpp->m = NULL;
+	}
+
+	if (dpp->pkex_m) {
+		l_ecc_point_free(dpp->pkex_m);
+		dpp->pkex_m = NULL;
+	}
+
+	if (dpp->y_or_x) {
+		l_ecc_point_free(dpp->y_or_x);
+		dpp->y_or_x = NULL;
+	}
+
+	if (dpp->pkex_public) {
+		l_ecc_point_free(dpp->pkex_public);
+		dpp->pkex_public = NULL;
+	}
+
+	if (dpp->pkex_private) {
+		l_ecc_scalar_free(dpp->pkex_private);
+		dpp->pkex_private = NULL;
+	}
+}
+
+static void dpp_free_pending_pkex_data(struct dpp_sm *dpp)
+{
+	if (dpp->pkex_id) {
+		l_free(dpp->pkex_id);
+		dpp->pkex_id = NULL;
+	}
+
+	if (dpp->pkex_key) {
+		l_free(dpp->pkex_key);
+		dpp->pkex_key = NULL;
+	}
+
+	if (dpp->peer_encr_key) {
+		l_ecc_point_free(dpp->peer_encr_key);
+		dpp->peer_encr_key = NULL;
+	}
+}
+
+void dpp_sm_free(struct dpp_sm *dpp)
+{
+	if (dpp->boot_public) {
+		l_ecc_point_free(dpp->boot_public);
+		dpp->boot_public = NULL;
+	}
+
+	if (dpp->boot_private) {
+		l_ecc_scalar_free(dpp->boot_private);
+		dpp->boot_private = NULL;
+	}
+
+	if (dpp->timeout) {
+		l_timeout_remove(dpp->timeout);
+		dpp->timeout = NULL;
+	}
+
+	if (dpp->config) {
+		dpp_configuration_free(dpp->config);
+		dpp->config = NULL;
+	}
+
+	if (dpp->peer_asn1) {
+		l_free(dpp->peer_asn1);
+		dpp->peer_asn1 = NULL;
+	}
+
+	if (dpp->own_asn1) {
+		l_free(dpp->own_asn1);
+		dpp->own_asn1 = NULL;
+	}
+
+	if (dpp->frame_pending) {
+		l_free(dpp->frame_pending);
+		dpp->frame_pending = NULL;
+	}
+
+	dpp->state = DPP_STATE_NOTHING;
+	dpp->pkex_version = 0;
+
+	explicit_bzero(dpp->r_nonce, dpp->nonce_len);
+	explicit_bzero(dpp->i_nonce, dpp->nonce_len);
+	explicit_bzero(dpp->e_nonce, dpp->nonce_len);
+	explicit_bzero(dpp->ke, dpp->key_len);
+	explicit_bzero(dpp->k1, dpp->key_len);
+	explicit_bzero(dpp->k2, dpp->key_len);
+	explicit_bzero(dpp->auth_tag, dpp->key_len);
+	explicit_bzero(dpp->z, dpp->key_len);
+	explicit_bzero(dpp->u, dpp->u_len);
+
+	dpp_free_pending_pkex_data(dpp);
+
+	dpp_free_auth_data(dpp);
+
+	l_free(dpp);
+}
+
+static bool dpp_start_authentication(struct dpp_sm *dpp)
+{
+	l_ecdh_generate_key_pair(dpp->curve, &dpp->proto_private,
+					&dpp->own_proto_public);
+
+	l_getrandom(dpp->i_nonce, dpp->nonce_len);
+
+	dpp->m = dpp_derive_k1(dpp->peer_boot_public,
+					dpp->proto_private, dpp->k1);
+
+	dpp->state = DPP_STATE_AUTHENTICATING;
+
+	return true;
+}
+
+void dpp_sm_set_peer_bootstrap(struct dpp_sm *dpp,
+					struct l_ecc_point *public)
+{
+	dpp->peer_boot_public = l_ecc_point_clone(public);
+	dpp->peer_asn1 = dpp_point_to_asn1(public, &dpp->peer_asn1_len);
+
+	dpp_hash(L_CHECKSUM_SHA256, dpp->peer_boot_hash, 1, dpp->peer_asn1,
+			dpp->peer_asn1_len);
+}
+
+const uint8_t *dpp_sm_get_own_asn1(struct dpp_sm *dpp, size_t *len)
+{
+	*len = dpp->own_asn1_len;
+
+	return dpp->own_asn1;
+}
+
+void dpp_sm_set_configuration(struct dpp_sm *dpp,
+					struct dpp_configuration *config)
+{
+	dpp->config = config;
+}
+
+const struct dpp_configuration *dpp_sm_get_configuration(struct dpp_sm *dpp)
+{
+	return dpp->config;
+}
+
+void dpp_sm_set_role(struct dpp_sm *dpp, enum dpp_capability role)
+{
+	dpp->role = role;
+}
+
+void dpp_sm_set_skip_presence(struct dpp_sm *dpp, bool skip)
+{
+	dpp->skip_presence = skip;
+}
+
+void dpp_sm_set_channel(struct dpp_sm *dpp, uint8_t oper_class, uint8_t channel)
+{
+	dpp->channel[0] = oper_class;
+	dpp->channel[1] = channel;
+}
+
+enum dpp_state dpp_sm_get_state(struct dpp_sm *dpp)
+{
+	return dpp->state;
+}
+
+bool dpp_sm_start_initiator(struct dpp_sm *dpp)
+{
+	if (L_WARN_ON(!dpp->peer_boot_public))
+		return false;
+
+	dpp->initiator = true;
+
+	if (dpp->skip_presence) {
+		dpp_start_authentication(dpp);
+		dpp_reset_protocol_timer(dpp, DPP_AUTH_PROTO_TIMEOUT);
+	} else
+		dpp->state = DPP_STATE_PRESENCE;
+
+	return true;
+}
+
+bool dpp_sm_start_responder(struct dpp_sm *dpp)
+{
+	l_ecdh_generate_key_pair(dpp->curve, &dpp->proto_private,
+					&dpp->own_proto_public);
+	dpp->initiator = false;
+
+	dpp->state = DPP_STATE_PRESENCE;
+
+	return true;
+}
+
+void dpp_sm_set_pkex_identifier(struct dpp_sm *dpp, const char *identifier)
+{
+	dpp->pkex_id = l_strdup(identifier);
+}
+
+void dpp_sm_set_pkex_key(struct dpp_sm *dpp, const char *key)
+{
+	dpp->pkex_key = l_strdup(key);
+}
+
+void dpp_sm_set_pkex_own_mac(struct dpp_sm *dpp, const uint8_t *mac)
+{
+	memcpy(dpp->pkex_own_mac, mac, 6);
+}
+
+void dpp_sm_set_pkex_peer_mac(struct dpp_sm *dpp, const uint8_t *mac)
+{
+	memcpy(dpp->pkex_peer_mac, mac, 6);
+
+	if (dpp->initiator) {
+		dpp->mac_responder = dpp->pkex_peer_mac;
+		dpp->mac_initiator = dpp->pkex_own_mac;
+	} else {
+		dpp->mac_initiator = dpp->pkex_peer_mac;
+		dpp->mac_responder = dpp->pkex_own_mac;
+	}
+}
+
+bool dpp_sm_pkex_start_initiator(struct dpp_sm *dpp)
+{
+	_auto_(l_ecc_point_free) struct l_ecc_point *qi = NULL;
+
+	if (!dpp->pkex_key)
+		return false;
+
+	/*
+	 * "DPP R2 devices are expected to use PKEXv1 by default"
+	 *
+	 * TODO: Support setting version (v2 required for TCP encapsulation)
+	 */
+	dpp->pkex_version = 1;
+
+	if (!l_ecdh_generate_key_pair(dpp->curve, &dpp->pkex_private,
+					&dpp->pkex_public))
+		return false;
+
+	/*
+	 * "If Qi is the point-at-infinity, the code shall be deleted and the
+	 * user should be notified to provision a new code"
+	 */
+	qi = dpp_derive_qi(dpp->curve, dpp->pkex_key, dpp->pkex_id,
+				dpp->pkex_own_mac);
+	if (!qi || l_ecc_point_is_infinity(qi)) {
+		l_debug("Cannot derive Qi, provision a new code");
+		goto failed;
+	}
+
+	dpp->pkex_m = l_ecc_point_new(dpp->curve);
+
+	if (!l_ecc_point_add(dpp->pkex_m, dpp->pkex_public, qi))
+		goto failed;
+
+	dpp->initiator = true;
+
+	dpp->mac_initiator = dpp->pkex_own_mac;
+	/* Won't know until we receive a response */
+	dpp->mac_responder = NULL;
+
+	dpp_reset_protocol_timer(dpp, DPP_AUTH_PROTO_TIMEOUT);
+
+	/* Send exchange request */
+
+	return true;
+
+failed:
+	return false;
+}
+
+bool dpp_sm_pkex_start_responder(struct dpp_sm *dpp)
+{
+	dpp->initiator = false;
+
+	dpp->mac_responder = dpp->pkex_own_mac;
+	/* Won't know until we receive the first frame */
+	dpp->mac_initiator = NULL;
+
+	dpp->state = DPP_STATE_PKEX_EXCHANGE;
+	dpp->pkex_version = 1;
+
+	dpp_reset_protocol_timer(dpp, DPP_PKEX_PROTO_TIMEOUT);
+
+	return true;
+}
+
+void dpp_handle_rx(struct dpp_sm *dpp, const uint8_t *data, size_t len)
+{
+	/* Handle frame */
+}
diff --git a/src/dpp-common.h b/src/dpp-common.h
new file mode 100644
index 00000000..52075cd6
--- /dev/null
+++ b/src/dpp-common.h
@@ -0,0 +1,110 @@
+/*
+ *
+ *  Wireless daemon for Linux
+ *
+ *  Copyright (C) 2024  Locus Robotics
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct dpp_sm;
+struct l_ecc_point;
+struct l_ecc_scalar;
+struct dpp_configuration;
+
+enum dpp_state {
+	DPP_STATE_NOTHING,
+	DPP_STATE_PRESENCE,
+	DPP_STATE_PKEX_EXCHANGE,
+	DPP_STATE_PKEX_COMMIT_REVEAL,
+	DPP_STATE_AUTHENTICATING,
+	DPP_STATE_CONFIGURING,
+};
+
+enum dpp_capability {
+	DPP_CAPABILITY_ENROLLEE = 0x01,
+	DPP_CAPABILITY_CONFIGURATOR = 0x02,
+};
+
+enum dpp_event {
+	/*
+	 * PEER_ACCEPTED indicates an initial DPP frame has been received and
+	 * accepted. This could be either a PKEX or an auth frame when DPP is
+	 * running as either a configurator or enrollee. This should be
+	 * used by the encapsulating protocol to note the peer address (MAC
+	 * or IP) and from then on only accept frames from this peer until DPP
+	 * has completed.
+	 */
+	DPP_EVENT_PEER_ACCEPTED,
+	/*
+	 * The authenticate request frame included a channel attribute. The
+	 * encapsulating protocol must switch to this channel to continue the
+	 * protocol (only applicable to 802.11 encapsulation). Event data is
+	 * two bytes: [oper_class, channel]
+	 */
+	DPP_EVENT_CHANNEL_SWITCH,
+	/*
+	 * A key corresponding to an identifier (set in event_data) is now
+	 * required. The encapsulating protocol must retrieve the key and
+	 * notify using dpp_sm_set_pkex_key().
+	 */
+	DPP_EVENT_PKEX_KEY_REQUESTED,
+	DPP_EVENT_SUCCESS,
+	DPP_EVENT_FAILED,
+};
+
+typedef void (*dpp_event_cb_t)(enum dpp_event event, const void *event_data,
+				void *user_data);
+
+typedef void (*dpp_write_cb_t)(const uint8_t *data, size_t len,
+				void *user_data);
+
+struct dpp_sm *dpp_sm_new(dpp_event_cb_t event,
+				const struct l_ecc_point *boot_public,
+				const struct l_ecc_scalar *boot_private,
+				void *user_data);
+
+void dpp_sm_free(struct dpp_sm *dpp);
+
+void dpp_handle_rx(struct dpp_sm *dpp, const uint8_t *data, size_t len);
+void dpp_sm_set_write_handler(struct dpp_sm *dpp, dpp_write_cb_t write);
+
+void dpp_sm_set_peer_bootstrap(struct dpp_sm *dpp,
+					struct l_ecc_point *public);
+void dpp_sm_set_own_bootstrap(struct dpp_sm *dpp, struct l_ecc_point *public,
+					struct l_ecc_scalar *private);
+const uint8_t *dpp_sm_get_own_asn1(struct dpp_sm *dpp, size_t *len);
+
+void dpp_sm_set_configuration(struct dpp_sm *dpp,
+					struct dpp_configuration *config);
+const struct dpp_configuration *dpp_sm_get_configuration(struct dpp_sm *dpp);
+void dpp_sm_set_role(struct dpp_sm *dpp, enum dpp_capability role);
+void dpp_sm_set_skip_presence(struct dpp_sm *dpp, bool skip);
+
+void dpp_sm_set_channel(struct dpp_sm *dpp, uint8_t oper_class,
+			uint8_t channel);
+enum dpp_state dpp_sm_get_state(struct dpp_sm *dpp);
+
+bool dpp_sm_start_initiator(struct dpp_sm *dpp);
+bool dpp_sm_start_responder(struct dpp_sm *dpp);
+
+void dpp_sm_set_pkex_identifier(struct dpp_sm *dpp, const char *identifier);
+void dpp_sm_set_pkex_key(struct dpp_sm *dpp, const char *key);
+void dpp_sm_set_pkex_own_mac(struct dpp_sm *dpp, const uint8_t *mac);
+void dpp_sm_set_pkex_peer_mac(struct dpp_sm *dpp, const uint8_t *mac);
+
+bool dpp_sm_pkex_start_initiator(struct dpp_sm *dpp);
+bool dpp_sm_pkex_start_responder(struct dpp_sm *dpp);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 6/9] dpp-common: add TX/RX handlers to dpp_sm
  2024-03-27 15:19 [PATCH 1/9] dpp: prep for moving AAD within dpp_append_wrapped_data James Prestwood
                   ` (3 preceding siblings ...)
  2024-03-27 15:19 ` [PATCH 5/9] dpp-common: Skeleton for common DPP module James Prestwood
@ 2024-03-27 15:19 ` James Prestwood
  2024-04-02 15:27   ` Denis Kenzior
  2024-03-27 15:19 ` [PATCH 7/9] build: add dpp-common.{c,h} James Prestwood
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 15+ messages in thread
From: James Prestwood @ 2024-03-27 15:19 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

This copies the bulk of the protocol handling from dpp.c into
dpp-common.c. There are no logical changes but some adaptations
needed to be made in order to isolate the encapsulating protocol
from the common protocol.

 - The DPP header is now reduced to not include the mmpdu header
   or catagory byte.
 - A channel switch must now notify the encapsulating protocol
   since DPP common has no concept of frequency/channels
 - When a peer is accepted its up to the encapsulating protocol
   to filter frames only from this peer for the remainder of the
   protocol. With PKEX the peer/own MACs actually do need to be
   communicated to dpp-common due to some of the key generation.
 - When a PKEX key is not set into the SM its up to the
   encapsulating protocol to provide the key after a
   PKEX_KEY_REQUESTED event. Once the key is obtained the SM
   can resume the PKEX protocol.

Other changes were made for readability. Sending/handler function
names were made consistent. The DPP config header is now built
and parsed with common functions (as opposed to manually). Extra
validation here and there was added as it was noticed (checks for
required attributes, return value checks, etc).
---
 src/dpp-common.c | 2246 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 2242 insertions(+), 4 deletions(-)

diff --git a/src/dpp-common.c b/src/dpp-common.c
index 45099d12..2d4bbb31 100644
--- a/src/dpp-common.c
+++ b/src/dpp-common.c
@@ -145,6 +145,70 @@ struct dpp_sm {
 	bool initiator : 1;
 };
 
+/*
+ * Builds only the core DPP header. This is shared between DPP over 80211 and
+ * over TCP. Those protocols are required to encapsulate the core message as
+ * required by the spec.
+ */
+static size_t dpp_build_header(enum dpp_frame_type type, uint8_t *buf)
+{
+	uint8_t *ptr = buf;
+
+	*ptr++ = DPP_ACTION_VENDOR_SPECIFIC;
+	memcpy(ptr, wifi_alliance_oui, 3);
+	ptr += 3;
+	*ptr++ = 0x1a;			/* WiFi Alliance DPP OI type */
+	*ptr++ = 1;			/* Cryptosuite */
+	*ptr++ = type;
+
+	return ptr - buf;
+}
+
+/*
+ * For some reason the DPP spec decided to use GAS frame formats only for the
+ * configuration request and response. There is no explicit DPP frame type for
+ * these messages, hence the boolean 'request' parameter. The header will be
+ * formatted either for a configuration request or configuration response.
+ *
+ * See Section 8.3 DPP Generic Advertisement Service (GAS) frames
+ *
+ * As with the more generic header above, the specific protocol handlers will
+ * be required to add extra header information (80211 or TCP).
+ */
+static size_t dpp_build_config_header(bool request, uint8_t diag_token,
+					uint8_t *buf)
+{
+	uint8_t *ptr = buf;
+
+	*ptr++ = request ? DPP_ACTION_GAS_REQUEST: DPP_ACTION_GAS_RESPONSE;
+	*ptr++ = diag_token;
+
+	if (!request) {
+		/* Status */
+		l_put_le16(0, ptr);
+		ptr += 2;
+		/* Not fragmented */
+		l_put_le16(0, ptr);
+		ptr += 2;
+	}
+
+	*ptr++ = IE_TYPE_ADVERTISEMENT_PROTOCOL;
+	*ptr++ = 8; /* len */
+	if (request)
+		*ptr++ = 0x7f;
+	else
+		*ptr++ = 0x00;
+
+	*ptr++ = IE_TYPE_VENDOR_SPECIFIC;
+	*ptr++ = 5;
+	memcpy(ptr, wifi_alliance_oui, 3);
+	ptr += 3;
+	*ptr++ = 0x1a;
+	*ptr++ = 1;
+
+	return ptr - buf;
+}
+
 static void dpp_failed(struct dpp_sm *dpp)
 {
 	if (dpp->event_cb)
@@ -169,6 +233,503 @@ static void dpp_reset_protocol_timer(struct dpp_sm *dpp, uint64_t time)
 						dpp, NULL);
 }
 
+static bool dpp_check_roles(struct dpp_sm *dpp, uint8_t peer_capa)
+{
+	if (dpp->role == DPP_CAPABILITY_ENROLLEE &&
+			!(peer_capa & DPP_CAPABILITY_CONFIGURATOR))
+		return false;
+	else if (dpp->role == DPP_CAPABILITY_CONFIGURATOR &&
+			!(peer_capa & DPP_CAPABILITY_ENROLLEE))
+		return false;
+
+	return true;
+}
+
+static void dpp_try_write(struct dpp_sm *dpp, const uint8_t *frame,
+				size_t frame_len)
+{
+	if (!dpp->write) {
+		if (dpp->frame_pending) {
+			l_free(dpp->frame_pending);
+			dpp->frame_pending = NULL;
+		}
+
+		l_debug("No write handler, waiting to send");
+		dpp->frame_pending = l_memdup(frame, frame_len);
+		dpp->frame_len = frame_len;
+		return;
+	}
+
+	dpp->write(frame, frame_len, dpp->user_data);
+}
+
+static void dpp_send_presence(struct dpp_sm *dpp)
+{
+	uint8_t frame[DPP_HDR_LEN + 64];
+	uint8_t *ptr = frame;
+
+	ptr += dpp_build_header(DPP_FRAME_PRESENCE_ANNOUNCEMENT, ptr);
+	ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH,
+				dpp->own_chirp_hash,
+				sizeof(dpp->own_chirp_hash));
+
+	dpp->state = DPP_STATE_PRESENCE;
+
+	l_debug("sending presence announcement");
+
+	dpp_try_write(dpp, frame, ptr - frame);
+}
+
+static void dpp_send_auth_request(struct dpp_sm *dpp)
+{
+	uint8_t frame[DPP_HDR_LEN + 256];
+	uint8_t *ptr = frame;
+	uint8_t version = 2;
+
+	ptr += dpp_build_header(DPP_FRAME_AUTHENTICATION_REQUEST, ptr);
+	ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH,
+				dpp->peer_boot_hash, 32);
+	ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_BOOT_KEY_HASH,
+				dpp->own_boot_hash, 32);
+	ptr += dpp_append_point(ptr, DPP_ATTR_INITIATOR_PROTOCOL_KEY,
+				dpp->own_proto_public);
+	ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION, &version, 1);
+
+	if (dpp->role == DPP_CAPABILITY_CONFIGURATOR && dpp->channel[0])
+		ptr += dpp_append_attr(ptr, DPP_ATTR_CHANNEL, dpp->channel, 2);
+
+	ptr += dpp_append_wrapped_data(frame, sizeof(frame), ptr,
+			dpp->k1, dpp->key_len, 2,
+			DPP_ATTR_INITIATOR_NONCE, dpp->nonce_len, dpp->i_nonce,
+			DPP_ATTR_INITIATOR_CAPABILITIES,
+			(size_t) 1, &dpp->role);
+
+	dpp->state = DPP_STATE_AUTHENTICATING;
+
+	l_debug("sending auth request");
+
+	dpp_try_write(dpp, frame, ptr - frame);
+}
+
+static void dpp_send_auth_response(struct dpp_sm *dpp)
+{
+	uint8_t frame[DPP_HDR_LEN + 512];
+	uint8_t *ptr = frame;
+	uint8_t status = DPP_STATUS_OK;
+	uint8_t version = 2;
+	uint8_t wrapped2_plaintext[dpp->key_len + 4];
+	uint8_t wrapped2[dpp->key_len + 16 + 8];
+	size_t wrapped2_len;
+
+	ptr += dpp_build_header(DPP_FRAME_AUTHENTICATION_RESPONSE, ptr);
+	ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &status, 1);
+	ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH,
+				dpp->own_boot_hash, 32);
+	if (dpp->mutual_auth)
+		ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_BOOT_KEY_HASH,
+				dpp->peer_boot_hash, 32);
+	ptr += dpp_append_point(ptr, DPP_ATTR_RESPONDER_PROTOCOL_KEY,
+				dpp->own_proto_public);
+	ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION, &version, 1);
+
+	/* Wrap up secondary data (R-Auth) */
+	wrapped2_len = dpp_append_attr(wrapped2_plaintext,
+					DPP_ATTR_RESPONDER_AUTH_TAG,
+					dpp->auth_tag, dpp->key_len);
+	/*
+	 * "Invocations of AES-SIV in the DPP Authentication protocol that
+	 * produce ciphertext that is part of an additional AES-SIV invocation
+	 * do not use AAD; in other words, the number of AAD components is set
+	 * to zero."
+	 */
+	if (!aes_siv_encrypt(dpp->ke, dpp->key_len, wrapped2_plaintext,
+					dpp->key_len + 4, NULL, 0, wrapped2)) {
+		l_error("Failed to encrypt wrapped data");
+		return;
+	}
+
+	wrapped2_len += 16;
+
+	ptr += dpp_append_wrapped_data(frame, sizeof(frame), ptr,
+			dpp->k2, dpp->key_len, 4,
+			DPP_ATTR_RESPONDER_NONCE, dpp->nonce_len, dpp->r_nonce,
+			DPP_ATTR_INITIATOR_NONCE, dpp->nonce_len, dpp->i_nonce,
+			DPP_ATTR_RESPONDER_CAPABILITIES, (size_t) 1, &dpp->role,
+			DPP_ATTR_WRAPPED_DATA, wrapped2_len, wrapped2);
+
+	l_debug("send auth response");
+
+	dpp_try_write(dpp, frame, ptr - frame);
+}
+
+static void dpp_send_auth_request_failed(struct dpp_sm *dpp,
+					enum dpp_status status,
+					void *k1)
+{
+	uint8_t frame[DPP_HDR_LEN + 128];
+	uint8_t *ptr = frame;
+	uint8_t version = 2;
+	uint8_t s = status;
+
+	ptr += dpp_build_header(DPP_FRAME_AUTHENTICATION_RESPONSE, ptr);
+	ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &s, 1);
+	ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH,
+				dpp->own_boot_hash, 32);
+	ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION, &version, 1);
+	ptr += dpp_append_wrapped_data(frame, sizeof(frame), ptr,
+			k1, dpp->key_len, 2,
+			DPP_ATTR_INITIATOR_NONCE, dpp->nonce_len, dpp->i_nonce,
+			DPP_ATTR_RESPONDER_CAPABILITIES,
+			(size_t) 1, &dpp->role);
+
+	l_debug("sending auth request failed");
+
+	dpp_try_write(dpp, frame, ptr - frame);
+}
+
+static void dpp_send_auth_confirm(struct dpp_sm *dpp)
+{
+	uint8_t frame[DPP_HDR_LEN + 256];
+	uint8_t *ptr = frame;
+	uint8_t zero = 0;
+
+	ptr += dpp_build_header(DPP_FRAME_AUTHENTICATION_CONFIRM, ptr);
+	ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &zero, 1);
+	ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH,
+					dpp->peer_boot_hash, 32);
+	if (dpp->mutual_auth)
+		ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_BOOT_KEY_HASH,
+					dpp->own_boot_hash, 32);
+
+	ptr += dpp_append_wrapped_data(frame, sizeof(frame), ptr,
+			dpp->ke, dpp->key_len, 1,
+			DPP_ATTR_INITIATOR_AUTH_TAG, dpp->key_len,
+			dpp->auth_tag);
+
+	l_debug("sending auth confirm");
+
+	dpp_try_write(dpp, frame, ptr - frame);
+}
+
+static void dpp_send_config_request(struct dpp_sm *dpp)
+{
+	const char *json = "{\"name\":\"IWD\",\"wi-fi_tech\":\"infra\","
+				"\"netRole\":\"sta\"}";
+	size_t json_len = strlen(json);
+	uint8_t frame[DPP_HDR_LEN + 512];
+	uint8_t *ptr = frame;
+	uint8_t *lptr;
+
+	l_getrandom(&dpp->diag_token, 1);
+	l_getrandom(dpp->e_nonce, dpp->nonce_len);
+
+	ptr += dpp_build_config_header(true, dpp->diag_token, ptr);
+
+	/* Save length location */
+	lptr = ptr;
+	ptr += 2;
+
+	ptr += dpp_append_wrapped_data(frame, sizeof(frame), ptr,
+			dpp->ke, dpp->key_len, 2,
+			DPP_ATTR_ENROLLEE_NONCE, dpp->nonce_len, dpp->e_nonce,
+			DPP_ATTR_CONFIGURATION_REQUEST, json_len, json);
+
+	l_put_le16(ptr - lptr - 2, lptr);
+
+	dpp->state = DPP_STATE_CONFIGURING;
+
+	l_debug("sending config request");
+
+	dpp_try_write(dpp, frame, ptr - frame);
+}
+
+static void dpp_send_config_response(struct dpp_sm *dpp, uint8_t status)
+{
+	_auto_(l_free) char *json = NULL;
+	uint8_t frame[512];
+	size_t json_len;
+	uint8_t *ptr = frame;
+	uint8_t *lptr;
+
+	ptr += dpp_build_config_header(false, dpp->diag_token, ptr);
+
+	/* Save length location */
+	lptr = ptr;
+	ptr += 2;
+
+	ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &status, 1);
+
+	/*
+	 * There are several failure status codes that can be used (defined in
+	 * 6.4.3.1), each with their own set of attributes that should be
+	 * included. For now IWD's basic DPP implementation will assume
+	 * STATUS_CONFIGURE_FAILURE which only includes the E-Nonce.
+	 */
+	if (status == DPP_STATUS_OK) {
+		json = dpp_configuration_to_json(dpp->config);
+		json_len = strlen(json);
+
+		ptr += dpp_append_wrapped_data(frame, sizeof(frame), ptr,
+						dpp->ke, dpp->key_len, 2,
+						DPP_ATTR_ENROLLEE_NONCE,
+						dpp->nonce_len, dpp->e_nonce,
+						DPP_ATTR_CONFIGURATION_OBJECT,
+						json_len, json);
+	} else
+		ptr += dpp_append_wrapped_data(frame, sizeof(frame), ptr,
+						dpp->ke, dpp->key_len, 2,
+						DPP_ATTR_ENROLLEE_NONCE,
+						dpp->nonce_len, dpp->e_nonce);
+
+	l_put_le16(ptr - lptr - 2, lptr);
+
+	l_debug("sending config response");
+
+	dpp_try_write(dpp, frame, ptr - frame);
+}
+
+static void dpp_send_config_result(struct dpp_sm *dpp)
+{
+	uint8_t frame[DPP_HDR_LEN + 256];
+	uint8_t *ptr = frame;
+	uint8_t zero = 0;
+
+	memset(frame, 0, sizeof(frame));
+
+	ptr += dpp_build_header(DPP_FRAME_CONFIGURATION_RESULT, ptr);
+	ptr += dpp_append_wrapped_data(frame, sizeof(frame), ptr,
+			dpp->ke, dpp->key_len, 2,
+			DPP_ATTR_STATUS, (size_t) 1, &zero,
+			DPP_ATTR_ENROLLEE_NONCE, dpp->nonce_len, dpp->e_nonce);
+
+	l_debug("sending config result");
+
+	dpp_try_write(dpp, frame, ptr - frame);
+}
+
+static void dpp_send_pkex_exchange_request(struct dpp_sm *dpp)
+{
+	uint8_t frame[DPP_HDR_LEN + 256];
+	uint8_t *ptr = frame;
+	uint16_t group;
+
+	memset(frame, 0, sizeof(frame));
+
+	l_put_le16(l_ecc_curve_get_ike_group(dpp->curve), &group);
+
+	ptr += dpp_build_header(DPP_FRAME_PKEX_VERSION1_XCHG_REQUEST, ptr);
+
+	ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION,
+				&dpp->pkex_version, 1);
+	ptr += dpp_append_attr(ptr, DPP_ATTR_FINITE_CYCLIC_GROUP,
+				&group, 2);
+
+	if (dpp->pkex_id)
+		ptr += dpp_append_attr(ptr, DPP_ATTR_CODE_IDENTIFIER,
+					dpp->pkex_id, strlen(dpp->pkex_id));
+
+	ptr += dpp_append_point(ptr, DPP_ATTR_ENCRYPTED_KEY, dpp->pkex_m);
+
+	dpp->state = DPP_STATE_PKEX_EXCHANGE;
+
+	l_debug("sending PKEX v1 exchange request");
+
+	dpp_try_write(dpp, frame, ptr - frame);
+}
+
+static void dpp_send_pkex_exchange_response(struct dpp_sm *dpp,
+						struct l_ecc_point *n)
+{
+	uint8_t frame[DPP_HDR_LEN + 256];
+	uint8_t *ptr = frame;
+	uint16_t group;
+	uint8_t status = DPP_STATUS_OK;
+
+	memset(frame, 0, sizeof(frame));
+
+	l_put_le16(l_ecc_curve_get_ike_group(dpp->curve), &group);
+
+	ptr += dpp_build_header(DPP_FRAME_PKEX_XCHG_RESPONSE, ptr);
+	ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &status, 1);
+
+	if (dpp->pkex_id)
+		ptr += dpp_append_attr(ptr, DPP_ATTR_CODE_IDENTIFIER,
+					dpp->pkex_id, strlen(dpp->pkex_id));
+
+	ptr += dpp_append_point(ptr, DPP_ATTR_ENCRYPTED_KEY, n);
+
+	dpp->state = DPP_STATE_PKEX_COMMIT_REVEAL;
+
+	l_debug("sending PKEX exchange response");
+
+	dpp_try_write(dpp, frame, ptr - frame);
+}
+
+static void dpp_send_pkex_bad_group(struct dpp_sm *dpp)
+{
+	uint8_t frame[DPP_HDR_LEN + 256];
+	uint8_t *ptr = frame;
+	uint16_t group;
+	uint8_t status = DPP_STATUS_BAD_GROUP;
+
+	memset(frame, 0, sizeof(frame));
+
+	l_put_le16(l_ecc_curve_get_ike_group(dpp->curve), &group);
+
+	ptr += dpp_build_header(DPP_FRAME_PKEX_XCHG_RESPONSE, ptr);
+	ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &status, 1);
+	ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION,
+				&dpp->pkex_version, 1);
+	ptr += dpp_append_attr(ptr, DPP_ATTR_FINITE_CYCLIC_GROUP, &group, 2);
+
+	l_debug("sending PKEX bad group");
+
+	dpp_try_write(dpp, frame, ptr - frame);
+}
+
+static void dpp_send_pkex_bad_code(struct dpp_sm *dpp)
+{
+	uint8_t frame[DPP_HDR_LEN + 256];
+	uint8_t *ptr = frame;
+	uint8_t status = DPP_STATUS_BAD_CODE;
+
+	memset(frame, 0, sizeof(frame));
+
+	ptr += dpp_build_header(DPP_FRAME_PKEX_XCHG_RESPONSE, ptr);
+	ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &status, 1);
+	ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION,
+				&dpp->pkex_version, 1);
+	if (dpp->pkex_id)
+		ptr += dpp_append_attr(ptr, DPP_ATTR_CODE_IDENTIFIER,
+					dpp->pkex_id, strlen(dpp->pkex_id));
+
+	l_debug("sending PKEX bad code");
+
+	dpp_try_write(dpp, frame, ptr - frame);
+}
+
+static void dpp_send_pkex_commit_reveal_request(struct dpp_sm *dpp)
+{
+	uint8_t frame[DPP_HDR_LEN + 512];
+	uint8_t *ptr = frame;
+	uint8_t a_pub[L_ECC_POINT_MAX_BYTES];
+	ssize_t a_len;
+
+	memset(frame, 0, sizeof(frame));
+
+	a_len = l_ecc_point_get_data(dpp->boot_public, a_pub, sizeof(a_pub));
+
+	ptr += dpp_build_header(DPP_FRAME_PKEX_COMMIT_REVEAL_REQUEST, ptr);
+
+	ptr += dpp_append_wrapped_data(frame, sizeof(frame), ptr,
+			dpp->z, dpp->z_len, 2,
+			DPP_ATTR_BOOTSTRAPPING_KEY, a_len, a_pub,
+			DPP_ATTR_INITIATOR_AUTH_TAG, dpp->u_len, dpp->u);
+
+	dpp->state = DPP_STATE_PKEX_COMMIT_REVEAL;
+
+	l_debug("sending PKEX commit reveal request");
+
+	dpp_try_write(dpp, frame, ptr - frame);
+}
+
+static void dpp_send_pkex_commit_reveal_response(struct dpp_sm *dpp,
+						const uint8_t *v, size_t v_len)
+{
+	uint8_t frame[DPP_HDR_LEN + 512];
+	uint8_t *ptr = frame;
+	uint8_t b_pub[L_ECC_POINT_MAX_BYTES];
+	size_t b_len;
+
+	memset(frame, 0, sizeof(frame));
+
+	b_len = l_ecc_point_get_data(dpp->boot_public, b_pub, sizeof(b_pub));
+
+	ptr += dpp_build_header(DPP_FRAME_PKEX_COMMIT_REVEAL_RESPONSE, ptr);
+	ptr += dpp_append_wrapped_data(frame, sizeof(frame), ptr,
+			dpp->z, dpp->z_len, 2,
+			DPP_ATTR_BOOTSTRAPPING_KEY, b_len, b_pub,
+			DPP_ATTR_RESPONDER_AUTH_TAG, v_len, v);
+
+	l_debug("sending PKEX commit reveal response");
+
+	dpp_try_write(dpp, frame, ptr - frame);
+}
+
+static void dpp_process_pkex_exchange_request(struct dpp_sm *dpp,
+						struct l_ecc_point *m)
+{
+	_auto_(l_ecc_point_free) struct l_ecc_point *n = NULL;
+	_auto_(l_ecc_point_free) struct l_ecc_point *qr = NULL;
+	_auto_(l_ecc_point_free) struct l_ecc_point *qi = NULL;
+	_auto_(l_ecc_point_free) struct l_ecc_point *k = NULL;
+
+	l_debug("");
+
+	/* Qi = H(MAC-Initiator | [identifier | ] code) * Pi */
+	qi = dpp_derive_qi(dpp->curve, dpp->pkex_key, dpp->pkex_id,
+				dpp->mac_initiator);
+	if (!qi) {
+		l_debug("could not derive Qi");
+		return;
+	}
+
+	/* X' = M - Qi */
+	dpp->y_or_x = l_ecc_point_new(dpp->curve);
+
+	l_ecc_point_inverse(qi);
+	l_ecc_point_add(dpp->y_or_x, m, qi);
+
+	/*
+	 * "The resulting ephemeral key, denoted X’, is checked whether it is
+	 * the point-at-infinity. If it is not valid, the protocol silently
+	 * fails"
+	 */
+	if (l_ecc_point_is_infinity(dpp->y_or_x)) {
+		l_debug("X' is at infinity, ignore message");
+		dpp_failed(dpp);
+		return;
+	}
+
+	qr = dpp_derive_qr(dpp->curve, dpp->pkex_key, dpp->pkex_id,
+				dpp->mac_responder);
+	if (!qr || l_ecc_point_is_infinity(qr)) {
+		l_debug("Qr did not derive");
+		l_ecc_point_free(dpp->y_or_x);
+		dpp->y_or_x = NULL;
+		goto bad_code;
+	}
+
+	/*
+	 * "The Responder then generates a random ephemeral keypair, y/Y,
+	 * encrypts Y with Qr to obtain the result, denoted N."
+	 */
+	l_ecdh_generate_key_pair(dpp->curve, &dpp->pkex_private,
+					&dpp->pkex_public);
+
+	/* N = Y + Qr */
+	n = l_ecc_point_new(dpp->curve);
+
+	l_ecc_point_add(n, dpp->pkex_public, qr);
+
+	/* K = y * X' */
+
+	k = l_ecc_point_new(dpp->curve);
+
+	l_ecc_point_multiply(k, dpp->pkex_private, dpp->y_or_x);
+
+	/* z = HKDF(<>, info | M.x | N.x | code, K.x) */
+	dpp_derive_z(dpp->mac_initiator, dpp->mac_responder, n, m, k,
+			dpp->pkex_key, dpp->pkex_id, dpp->z, &dpp->z_len);
+
+	dpp_send_pkex_exchange_response(dpp, n);
+
+	return;
+
+bad_code:
+	dpp_send_pkex_bad_code(dpp);
+}
+
 struct dpp_sm *dpp_sm_new(dpp_event_cb_t event,
 				const struct l_ecc_point *boot_public,
 				const struct l_ecc_scalar *boot_private,
@@ -201,7 +762,25 @@ void dpp_sm_set_write_handler(struct dpp_sm *dpp, dpp_write_cb_t write)
 	if (!dpp->write)
 		return;
 
-	/* Handle writing frame */
+	/*
+	 * Presence/exchange are special cases because 802.11 encapsulation
+	 * requires going off channel for specific amounts of time. We cannot
+	 * easily start a timer within the common code and try and remained
+	 * synced with the offchannel callbacks.
+	 * Instead let the offchannel callbacks dictate when these frames
+	 * are sent out. All other write operations are driven by the reception
+	 * of other frames.
+	 */
+	if (dpp->state == DPP_STATE_PRESENCE && !dpp->initiator)
+		dpp_send_presence(dpp);
+	else if ( dpp->state == DPP_STATE_PKEX_EXCHANGE && dpp->initiator)
+		dpp_send_pkex_exchange_request(dpp);
+	else if (dpp->frame_pending) {
+		dpp->write(dpp->frame_pending, dpp->frame_len, dpp->user_data);
+
+		l_free(dpp->frame_pending);
+		dpp->frame_pending = NULL;
+	}
 }
 
 static void dpp_free_auth_data(struct dpp_sm *dpp)
@@ -339,6 +918,9 @@ static bool dpp_start_authentication(struct dpp_sm *dpp)
 
 	dpp->state = DPP_STATE_AUTHENTICATING;
 
+	if (dpp->initiator)
+		dpp_send_auth_request(dpp);
+
 	return true;
 }
 
@@ -413,6 +995,16 @@ bool dpp_sm_start_responder(struct dpp_sm *dpp)
 					&dpp->own_proto_public);
 	dpp->initiator = false;
 
+	/*
+	 * An Enrollee acting in a Responder role uses DPP Presence
+	 * Announcement to signal a potential Configurator that it is
+	 * ready to engage in a DPP exchange
+	 */
+	if (dpp->role == DPP_CAPABILITY_ENROLLEE) {
+		if (!dpp->skip_presence)
+			dpp_send_presence(dpp);
+	}
+
 	dpp->state = DPP_STATE_PRESENCE;
 
 	return true;
@@ -426,6 +1018,10 @@ void dpp_sm_set_pkex_identifier(struct dpp_sm *dpp, const char *identifier)
 void dpp_sm_set_pkex_key(struct dpp_sm *dpp, const char *key)
 {
 	dpp->pkex_key = l_strdup(key);
+
+	/* This was set after the key was requested, send the response now */
+	if (dpp->state == DPP_STATE_PKEX_EXCHANGE)
+		dpp_process_pkex_exchange_request(dpp, dpp->peer_encr_key);
 }
 
 void dpp_sm_set_pkex_own_mac(struct dpp_sm *dpp, const uint8_t *mac)
@@ -488,7 +1084,7 @@ bool dpp_sm_pkex_start_initiator(struct dpp_sm *dpp)
 
 	dpp_reset_protocol_timer(dpp, DPP_AUTH_PROTO_TIMEOUT);
 
-	/* Send exchange request */
+	dpp_send_pkex_exchange_request(dpp);
 
 	return true;
 
@@ -512,7 +1108,1649 @@ bool dpp_sm_pkex_start_responder(struct dpp_sm *dpp)
 	return true;
 }
 
-void dpp_handle_rx(struct dpp_sm *dpp, const uint8_t *data, size_t len)
+static void dpp_handle_presence_announcement(struct dpp_sm *dpp,
+						const uint8_t *frame,
+						size_t frame_len)
+{
+	struct dpp_attr_iter iter;
+	enum dpp_attribute_type type;
+	size_t len;
+	const uint8_t *data;
+	const void *r_boot = NULL;
+	size_t r_boot_len = 0;
+	uint8_t hash[32];
+
+	l_debug("Presence announcement");
+
+	/* Must be a configurator, in an initiator role, in PRESENCE state */
+	if (dpp->state != DPP_STATE_PRESENCE)
+		return;
+
+	if (dpp->role != DPP_CAPABILITY_CONFIGURATOR)
+		return;
+
+	dpp_attr_iter_init(&iter, frame + 6, frame_len - 6);
+
+	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
+		switch (type) {
+		case DPP_ATTR_RESPONDER_BOOT_KEY_HASH:
+			r_boot = data;
+			r_boot_len = len;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (!r_boot || r_boot_len != 32) {
+		l_debug("No responder boot hash");
+		return;
+	}
+
+	/* Hash what we have for the peer and check its our enrollee */
+	dpp_hash(L_CHECKSUM_SHA256, hash, 2, "chirp", strlen("chirp"),
+			dpp->peer_asn1, dpp->peer_asn1_len);
+
+	if (memcmp(hash, r_boot, sizeof(hash))) {
+		l_debug("Peers boot hash did not match");
+		return;
+	}
+
+	if (dpp->event_cb)
+		dpp->event_cb(DPP_EVENT_PEER_ACCEPTED, NULL, dpp->user_data);
+
+	dpp->state = DPP_STATE_AUTHENTICATING;
+
+	dpp_reset_protocol_timer(dpp, DPP_AUTH_PROTO_TIMEOUT);
+
+	dpp_start_authentication(dpp);
+}
+
+static void dpp_handle_auth_request(struct dpp_sm *dpp,
+					const uint8_t *frame, size_t frame_len)
 {
-	/* Handle frame */
+	struct dpp_attr_iter iter;
+	enum dpp_attribute_type type;
+	size_t len;
+	const uint8_t *data;
+	const uint8_t *r_boot = NULL;
+	const uint8_t *i_boot = NULL;
+	const uint8_t *i_proto = NULL;
+	const void *wrapped = NULL;
+	const uint8_t *i_nonce = NULL;
+	uint8_t i_capa = 0;
+	size_t r_boot_len = 0, i_proto_len = 0, wrapped_len = 0;
+	size_t i_nonce_len = 0;
+	_auto_(l_free) uint8_t *unwrapped = NULL;
+	_auto_(l_ecc_scalar_free) struct l_ecc_scalar *m = NULL;
+	_auto_(l_ecc_scalar_free) struct l_ecc_scalar *n = NULL;
+	_auto_(l_ecc_point_free) struct l_ecc_point *l = NULL;
+	struct l_ecc_point *bi = NULL;
+	uint64_t k1[L_ECC_MAX_DIGITS];
+	const void *ad0 = frame;
+	const void *ad1 = frame + 6;
+
+	if (dpp->state != DPP_STATE_PRESENCE &&
+				dpp->state != DPP_STATE_AUTHENTICATING)
+		return;
+
+	l_debug("authenticate request");
+
+	dpp_attr_iter_init(&iter, frame + 6, frame_len - 6);
+
+	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
+		switch (type) {
+		case DPP_ATTR_INITIATOR_BOOT_KEY_HASH:
+			i_boot = data;
+			/*
+			 * This attribute is required by the spec, but only
+			 * used for mutual authentication.
+			 */
+			break;
+		case DPP_ATTR_RESPONDER_BOOT_KEY_HASH:
+			r_boot = data;
+			r_boot_len = len;
+			break;
+		case DPP_ATTR_INITIATOR_PROTOCOL_KEY:
+			i_proto = data;
+			i_proto_len = len;
+			break;
+		case DPP_ATTR_WRAPPED_DATA:
+			/* I-Nonce/I-Capabilities part of wrapped data */
+			wrapped = data;
+			wrapped_len = len;
+			break;
+
+		/* Optional attributes */
+		case DPP_ATTR_PROTOCOL_VERSION:
+			if (l_get_u8(data) != 2) {
+				l_debug("Protocol version did not match");
+				return;
+			}
+
+			break;
+
+		case DPP_ATTR_CHANNEL:
+			if (len != 2)
+				return;
+
+			/*
+			 * Not part of the spec, but IWD puts a requirement on
+			 * enrollees that they must come to the configurators
+			 * channel to preserve performance
+			 */
+			if (dpp->role == DPP_CAPABILITY_CONFIGURATOR)
+				break;
+
+			if (dpp->event_cb)
+				dpp->event_cb(DPP_EVENT_CHANNEL_SWITCH, data,
+						dpp->user_data);
+
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (!r_boot || !i_boot || !i_proto || !wrapped) {
+		l_debug("missing attributes");
+		goto auth_request_failed;
+	}
+
+	if (r_boot_len != 32 || memcmp(dpp->own_boot_hash,
+					r_boot, r_boot_len)) {
+		l_debug("Responder boot key hash failed to verify");
+		goto auth_request_failed;
+	}
+
+	dpp->peer_proto_public = l_ecc_point_from_data(dpp->curve,
+						L_ECC_POINT_TYPE_FULL,
+						i_proto, i_proto_len);
+	if (!dpp->peer_proto_public) {
+		l_debug("Initiators protocol key invalid");
+		goto auth_request_failed;
+	}
+
+	m = dpp_derive_k1(dpp->peer_proto_public, dpp->boot_private, k1);
+	if (!m) {
+		l_debug("deriving k1/m failed");
+		goto auth_request_failed;
+	}
+
+	unwrapped = dpp_unwrap_attr(ad0, 6, ad1, wrapped - 4 - ad1,
+			k1, dpp->key_len, wrapped, wrapped_len, &wrapped_len);
+	if (!unwrapped) {
+		l_debug("failed to unwrap data");
+		goto auth_request_failed;
+	}
+
+	dpp_attr_iter_init(&iter, unwrapped, wrapped_len);
+
+	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
+		switch (type) {
+		case DPP_ATTR_INITIATOR_NONCE:
+			i_nonce = data;
+			i_nonce_len = len;
+			break;
+		case DPP_ATTR_INITIATOR_CAPABILITIES:
+			/*
+			 * "If the Responder is not capable of supporting the
+			 * role indicated by the Initiator, it shall respond
+			 * with a DPP Authentication Response frame indicating
+			 * failure by adding the DPP Status field set to
+			 * STATUS_NOT_COMPATIBLE"
+			 */
+			i_capa = l_get_u8(data);
+
+			if (!dpp_check_roles(dpp, i_capa)) {
+				l_debug("Peer does not support required role");
+				dpp_send_auth_request_failed(dpp,
+						DPP_STATUS_NOT_COMPATIBLE, k1);
+				goto auth_request_failed;
+			}
+
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (i_nonce_len != dpp->nonce_len) {
+		l_debug("I-Nonce has unexpected length %zu", i_nonce_len);
+		goto auth_request_failed;
+	}
+
+	memcpy(dpp->i_nonce, i_nonce, i_nonce_len);
+
+	if (dpp->mutual_auth) {
+		l = dpp_derive_lr(dpp->boot_private, dpp->proto_private,
+					dpp->peer_boot_public);
+		bi = dpp->peer_boot_public;
+	}
+
+	/* Derive keys k2, ke, and R-Auth for authentication response */
+
+	n = dpp_derive_k2(dpp->peer_proto_public, dpp->proto_private, dpp->k2);
+	if (!n) {
+		l_debug("deriving k2/n failed");
+		goto auth_request_failed;
+	}
+
+	l_getrandom(dpp->r_nonce, dpp->nonce_len);
+
+	if (!dpp_derive_ke(dpp->i_nonce, dpp->r_nonce, m, n, l, dpp->ke)) {
+		l_debug("deriving ke failed");
+		goto auth_request_failed;
+	}
+
+	if (!dpp_derive_r_auth(dpp->i_nonce, dpp->r_nonce, dpp->nonce_len,
+				dpp->peer_proto_public, dpp->own_proto_public,
+				bi, dpp->boot_public, dpp->auth_tag)) {
+		l_debug("deriving R_auth failed");
+		goto auth_request_failed;
+	}
+
+	dpp->state = DPP_STATE_AUTHENTICATING;
+
+	dpp_reset_protocol_timer(dpp, DPP_AUTH_PROTO_TIMEOUT);
+
+	dpp_send_auth_response(dpp);
+
+	return;
+
+auth_request_failed:
+	l_debug("handle auth request failed");
+	dpp->state = DPP_STATE_PRESENCE;
+	dpp_free_auth_data(dpp);
+}
+
+static void dpp_handle_auth_response(struct dpp_sm *dpp,
+					const uint8_t *frame, size_t frame_len)
+{
+	struct dpp_attr_iter iter;
+	enum dpp_attribute_type type;
+	size_t len;
+	const uint8_t *data;
+	int status = -1;
+	const void *r_boot_hash = NULL;
+	const void *r_proto = NULL;
+	size_t r_proto_len = 0;
+	const void *wrapped = NULL;
+	size_t wrapped_len;
+	_auto_(l_free) uint8_t *unwrapped1 = NULL;
+	_auto_(l_free) uint8_t *unwrapped2 = NULL;
+	const void *r_nonce = NULL;
+	const void *i_nonce = NULL;
+	const void *r_auth = NULL;
+	_auto_(l_ecc_point_free) struct l_ecc_point *r_proto_key = NULL;
+	_auto_(l_ecc_scalar_free) struct l_ecc_scalar *n = NULL;
+	_auto_(l_ecc_point_free) struct l_ecc_point *l = NULL;
+	struct l_ecc_point *bi = NULL;
+	const void *ad0 = frame;
+	const void *ad1 = frame + 6;
+	uint64_t r_auth_derived[L_ECC_MAX_DIGITS];
+
+	l_debug("Authenticate response");
+
+	if (dpp->state != DPP_STATE_AUTHENTICATING)
+		return;
+
+	dpp_attr_iter_init(&iter, frame + 6, frame_len - 6);
+
+	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
+		switch (type) {
+		case DPP_ATTR_STATUS:
+			if (len != 1)
+				return;
+
+			status = l_get_u8(data);
+			break;
+		case DPP_ATTR_RESPONDER_BOOT_KEY_HASH:
+			r_boot_hash = data;
+			break;
+		case DPP_ATTR_RESPONDER_PROTOCOL_KEY:
+			r_proto = data;
+			r_proto_len = len;
+			break;
+		case DPP_ATTR_PROTOCOL_VERSION:
+			if (len != 1 || l_get_u8(data) != 2)
+				return;
+			break;
+		case DPP_ATTR_WRAPPED_DATA:
+			wrapped = data;
+			wrapped_len = len;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (status != DPP_STATUS_OK || !r_boot_hash || !r_proto || !wrapped) {
+		l_debug("Auth response bad status or missing attributes");
+		return;
+	}
+
+	r_proto_key = l_ecc_point_from_data(dpp->curve, L_ECC_POINT_TYPE_FULL,
+						r_proto, r_proto_len);
+	if (!r_proto_key) {
+		l_debug("Peers protocol key was invalid");
+		return;
+	}
+
+	n = dpp_derive_k2(r_proto_key, dpp->proto_private, dpp->k2);
+
+	unwrapped1 = dpp_unwrap_attr(ad0, 6, ad1, wrapped - 4 - ad1, dpp->k2,
+					dpp->key_len, wrapped, wrapped_len,
+					&wrapped_len);
+	if (!unwrapped1) {
+		l_debug("Failed to unwrap primary data");
+		return;
+	}
+
+	wrapped = NULL;
+
+	dpp_attr_iter_init(&iter, unwrapped1, wrapped_len);
+
+	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
+		switch (type) {
+		case DPP_ATTR_RESPONDER_NONCE:
+			if (len != dpp->nonce_len)
+				return;
+
+			r_nonce = data;
+			break;
+		case DPP_ATTR_INITIATOR_NONCE:
+			if (len != dpp->nonce_len)
+				return;
+
+			i_nonce = data;
+			break;
+		case DPP_ATTR_RESPONDER_CAPABILITIES:
+			break;
+		case DPP_ATTR_WRAPPED_DATA:
+			wrapped = data;
+			wrapped_len = len;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (!r_nonce || !i_nonce || !wrapped) {
+		l_debug("Wrapped data missing attributes");
+		return;
+	}
+
+	if (dpp->mutual_auth) {
+		l = dpp_derive_li(dpp->peer_boot_public, r_proto_key,
+					dpp->boot_private);
+		bi = dpp->boot_public;
+	}
+
+	if (!dpp_derive_ke(i_nonce, r_nonce, dpp->m, n, l, dpp->ke)) {
+		l_debug("Failed to derive ke");
+		return;
+	}
+
+	unwrapped2 = dpp_unwrap_attr(NULL, 0, NULL, 0, dpp->ke, dpp->key_len,
+					wrapped, wrapped_len, &wrapped_len);
+	if (!unwrapped2) {
+		l_debug("Failed to unwrap secondary data");
+		return;
+	}
+
+	dpp_attr_iter_init(&iter, unwrapped2, wrapped_len);
+
+	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
+		switch (type) {
+		case DPP_ATTR_RESPONDER_AUTH_TAG:
+			if (len != dpp->key_len)
+				return;
+
+			r_auth = data;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (!r_auth) {
+		l_debug("R-Auth was not in secondary wrapped data");
+		return;
+	}
+
+	if (!dpp_derive_r_auth(i_nonce, r_nonce, dpp->nonce_len,
+				dpp->own_proto_public, r_proto_key, bi,
+				dpp->peer_boot_public, r_auth_derived)) {
+		l_debug("Failed to derive r_auth");
+		return;
+	}
+
+	if (memcmp(r_auth, r_auth_derived, dpp->key_len)) {
+		l_debug("R-Auth did not verify");
+		return;
+	}
+
+	if (!dpp_derive_i_auth(r_nonce, i_nonce, dpp->nonce_len,
+				r_proto_key, dpp->own_proto_public,
+				dpp->peer_boot_public, bi, dpp->auth_tag)) {
+		l_debug("Could not derive I-Auth");
+		return;
+	}
+
+	dpp_send_auth_confirm(dpp);
+
+	if (dpp->role == DPP_CAPABILITY_ENROLLEE)
+		dpp_send_config_request(dpp);
+}
+
+static void dpp_handle_auth_confirm(struct dpp_sm *dpp,
+					const uint8_t *frame, size_t frame_len)
+{
+	struct dpp_attr_iter iter;
+	enum dpp_attribute_type type;
+	size_t len;
+	const uint8_t *data;
+	int status = -1;
+	const uint8_t *r_boot_hash = NULL;
+	const void *wrapped = NULL;
+	const uint8_t *i_auth = NULL;
+	size_t i_auth_len;
+	_auto_(l_free) uint8_t *unwrapped = NULL;
+	size_t wrapped_len = 0;
+	uint64_t i_auth_check[L_ECC_MAX_DIGITS];
+	const void *unwrap_key;
+	const void *ad0 = frame;
+	const void *ad1 = frame + 6;
+	struct l_ecc_point *bi = NULL;
+
+	if (dpp->state != DPP_STATE_AUTHENTICATING)
+		return;
+
+	l_debug("authenticate confirm");
+
+	dpp_attr_iter_init(&iter, frame + 6, frame_len - 6);
+
+	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
+		switch (type) {
+		case DPP_ATTR_STATUS:
+			status = l_get_u8(data);
+			break;
+		case DPP_ATTR_RESPONDER_BOOT_KEY_HASH:
+			r_boot_hash = data;
+			/*
+			 * Spec requires this, but does not mention if anything
+			 * is to be done with it.
+			 */
+			break;
+		case DPP_ATTR_INITIATOR_BOOT_KEY_HASH:
+			/* No mutual authentication */
+			break;
+		case DPP_ATTR_WRAPPED_DATA:
+			wrapped = data;
+			wrapped_len = len;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (!r_boot_hash || !wrapped) {
+		l_debug("Attributes missing from authenticate confirm");
+		return;
+	}
+
+	/*
+	 * "The Responder obtains the DPP Authentication Confirm frame and
+	 * checks the value of the DPP Status field. If the value of the DPP
+	 * Status field is STATUS_NOT_COMPATIBLE or STATUS_AUTH_FAILURE, the
+	 * Responder unwraps the wrapped data portion of the frame using k2"
+	 */
+	if (status == DPP_STATUS_OK)
+		unwrap_key = dpp->ke;
+	else if (status == DPP_STATUS_NOT_COMPATIBLE ||
+				status == DPP_STATUS_AUTH_FAILURE)
+		unwrap_key = dpp->k2;
+	else
+		goto auth_confirm_failed;
+
+	unwrapped = dpp_unwrap_attr(ad0, 6, ad1, wrapped - 4 - ad1,
+			unwrap_key, dpp->key_len, wrapped, wrapped_len,
+			&wrapped_len);
+	if (!unwrapped)
+		goto auth_confirm_failed;
+
+	if (status != DPP_STATUS_OK) {
+		/*
+		 * "If unwrapping is successful, the Responder should generate
+		 * an alert indicating the reason for the protocol failure."
+		 */
+		l_debug("Authentication failed due to status %s",
+				status == DPP_STATUS_NOT_COMPATIBLE ?
+				"NOT_COMPATIBLE" : "AUTH_FAILURE");
+		goto auth_confirm_failed;
+	}
+
+	dpp_attr_iter_init(&iter, unwrapped, wrapped_len);
+
+	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
+		switch (type) {
+		case DPP_ATTR_INITIATOR_AUTH_TAG:
+			i_auth = data;
+			i_auth_len = len;
+			break;
+		case DPP_ATTR_RESPONDER_NONCE:
+			/* Only if error */
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (!i_auth || i_auth_len != dpp->key_len) {
+		l_debug("I-Auth missing from wrapped data");
+		goto auth_confirm_failed;
+	}
+
+	if (dpp->mutual_auth)
+		bi = dpp->peer_boot_public;
+
+	dpp_derive_i_auth(dpp->r_nonce, dpp->i_nonce, dpp->nonce_len,
+				dpp->own_proto_public, dpp->peer_proto_public,
+				dpp->boot_public, bi, i_auth_check);
+
+	if (memcmp(i_auth, i_auth_check, i_auth_len)) {
+		l_error("I-Auth did not verify");
+		goto auth_confirm_failed;
+	}
+
+	l_debug("Authentication successful");
+
+	if (dpp->role == DPP_CAPABILITY_ENROLLEE)
+		dpp_send_config_request(dpp);
+
+	return;
+
+auth_confirm_failed:
+	dpp->state = DPP_STATE_PRESENCE;
+	dpp_free_auth_data(dpp);
+}
+
+/* Parses the config header (GAS) and returns the start of the payload */
+static const uint8_t *dpp_parse_config_header(uint8_t action,
+						const uint8_t *data, size_t len,
+						uint8_t *diag_token_out,
+						uint16_t *status_out,
+						uint16_t *comeback_out,
+						size_t *attr_len_out)
+{
+	uint8_t adv_protocol_id[] = { 0xDD, 0x05, 0x50, 0x6F,
+					0x9A, 0x1A, 0x01 };
+	uint8_t diag_token;
+	uint16_t status = 0;
+	uint16_t comeback = 0;
+	uint16_t attr_len;
+	const uint8_t *ptr = data;
+
+	if (len < 1)
+		return NULL;
+
+	diag_token = *ptr++;
+
+	switch (action) {
+	case DPP_ACTION_GAS_REQUEST:
+		if (len < 12)
+			return NULL;
+		break;
+	case DPP_ACTION_GAS_RESPONSE:
+		if (len < 14)
+			return NULL;
+
+		status = l_get_le16(ptr);
+		ptr += 2;
+		comeback = l_get_le16(ptr);
+		ptr += 2;
+
+		break;
+	default:
+		return NULL;
+	}
+
+	if (*ptr++ != IE_TYPE_ADVERTISEMENT_PROTOCOL)
+		return NULL;
+
+	/* Length of advertisement protocol fields */
+	if (*ptr++ != 0x08)
+		return NULL;
+	/*
+	 * Unfortunately wpa_supplicant hard codes 0x7f as the Query Response
+	 * Info so we need to handle both cases.
+	 */
+	if (*ptr != 0x7f && *ptr != 0x00)
+		return NULL;
+	ptr++;
+
+	if (memcmp(ptr, adv_protocol_id, sizeof(adv_protocol_id)))
+		return NULL;
+
+	ptr += sizeof(adv_protocol_id);
+
+	attr_len = l_get_le16(ptr);
+	ptr += 2;
+
+	/* Check the attribute length matches the total length */
+	if (attr_len > len - (ptr - data))
+		return false;
+
+	if (diag_token_out)
+		*diag_token_out = diag_token;
+
+	if (action == DPP_ACTION_GAS_RESPONSE) {
+		if (status_out)
+			*status_out = status;
+		if (comeback_out)
+			*comeback_out = comeback;
+	}
+
+	if (attr_len_out)
+		*attr_len_out = attr_len;
+
+	return ptr;
+}
+
+static void dpp_handle_config_request(struct dpp_sm *dpp, const uint8_t *frame,
+				size_t frame_len)
+{
+	const uint8_t *attrs;
+	size_t attrs_len;
+	struct dpp_attr_iter iter;
+	enum dpp_attribute_type type;
+	const uint8_t *data;
+	size_t len;
+	const char *json = NULL;
+	size_t json_len = 0;
+	struct json_contents *c;
+	const uint8_t *wrapped = NULL;
+	const uint8_t *e_nonce = NULL;
+	size_t wrapped_len = 0;
+	_auto_(l_free) uint8_t *unwrapped = NULL;
+	struct json_iter jsiter;
+	_auto_(l_free) char *tech = NULL;
+	_auto_(l_free) char *role = NULL;
+
+	attrs = dpp_parse_config_header(DPP_ACTION_GAS_REQUEST, frame,
+					frame_len, &dpp->diag_token,
+					NULL, NULL, &attrs_len);
+	if (!attrs)
+		return;
+
+	if (dpp->state != DPP_STATE_AUTHENTICATING) {
+		l_debug("Configuration request in wrong state");
+		return;
+	}
+
+	if (dpp->role != DPP_CAPABILITY_CONFIGURATOR)
+		return;
+
+	dpp_attr_iter_init(&iter, attrs, attrs_len);
+
+	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
+		switch (type) {
+		case DPP_ATTR_WRAPPED_DATA:
+			wrapped = data;
+			wrapped_len = len;
+			break;
+		default:
+			/* Wrapped data should be only attribute */
+			return;
+		}
+	}
+
+	if (!wrapped) {
+		l_debug("Wrapped data missing");
+		return;
+	}
+
+	unwrapped = dpp_unwrap_attr(NULL, 0, NULL, 0, dpp->ke,
+					dpp->key_len, wrapped, wrapped_len,
+					&wrapped_len);
+	if (!unwrapped) {
+		l_debug("Failed to unwrap");
+		return;
+	}
+
+	dpp_attr_iter_init(&iter, unwrapped, wrapped_len);
+
+	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
+		switch (type) {
+		case DPP_ATTR_ENROLLEE_NONCE:
+			if (len != dpp->nonce_len)
+				break;
+
+			e_nonce = data;
+			break;
+		case DPP_ATTR_CONFIGURATION_REQUEST:
+			json = (const char *)data;
+			json_len = len;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (!json || !e_nonce) {
+		l_debug("No configuration object in response");
+		return;
+	}
+
+	c = json_contents_new(json, json_len);
+	if (!c) {
+		json_contents_free(c);
+		return;
+	}
+
+	json_iter_init(&jsiter, c);
+
+	/*
+	 * Check mandatory values (Table 7). There isn't much that can be done
+	 * with these, but the spec requires they be included.
+	 */
+	if (!json_iter_parse(&jsiter,
+			JSON_MANDATORY("name", JSON_STRING, NULL),
+			JSON_MANDATORY("wi-fi_tech", JSON_STRING, &tech),
+			JSON_MANDATORY("netRole", JSON_STRING, &role),
+			JSON_UNDEFINED))
+		goto configure_failure;
+
+	if (strcmp(tech, "infra"))
+		goto configure_failure;
+
+	if (strcmp(role, "sta"))
+		goto configure_failure;
+
+	json_contents_free(c);
+
+	memcpy(dpp->e_nonce, e_nonce, dpp->nonce_len);
+
+	dpp->state = DPP_STATE_CONFIGURING;
+
+	dpp_send_config_response(dpp, DPP_STATUS_OK);
+
+	return;
+
+configure_failure:
+	dpp_send_config_response(dpp, DPP_STATUS_CONFIGURE_FAILURE);
+	/*
+	 * The other peer is still authenticated, and can potentially send
+	 * additional requests so keep this session alive.
+	 */
+}
+
+static void dpp_handle_config_response(struct dpp_sm *dpp, const uint8_t *frame,
+					size_t frame_len)
+{
+	const uint8_t *attrs;
+	size_t attrs_len;
+	uint16_t status;
+	uint8_t diag_token;
+	uint16_t comeback;
+	struct dpp_attr_iter iter;
+	enum dpp_attribute_type type;
+	size_t len;
+	const uint8_t *data;
+	const char *json = NULL;
+	size_t json_len = 0;
+	int dstatus = -1;
+	const uint8_t *wrapped = NULL;
+	const uint8_t *e_nonce = NULL;
+	size_t wrapped_len = 0;
+	_auto_(l_free) uint8_t *unwrapped = NULL;
+
+	l_debug("config response");
+
+	attrs = dpp_parse_config_header(DPP_ACTION_GAS_RESPONSE, frame,
+					frame_len, &diag_token, &status,
+					&comeback, &attrs_len);
+	if (!attrs || diag_token != dpp->diag_token || status != 0) {
+		l_debug("failed to parse header");
+		return;
+	}
+
+	/* TODO: comeback delay */
+	if (comeback != 0) {
+		l_debug("comeback not zero");
+		return;
+	}
+
+	if (dpp->state != DPP_STATE_CONFIGURING) {
+		l_debug("state not configuring");
+		return;
+	}
+
+
+	dpp_attr_iter_init(&iter, attrs, attrs_len);
+
+	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
+		switch (type) {
+		case DPP_ATTR_STATUS:
+			dstatus = l_get_u8(data);
+			break;
+		case DPP_ATTR_WRAPPED_DATA:
+			wrapped = data;
+			wrapped_len = len;
+			break;
+		default:
+			/*
+			 * TODO: CSR Attribute
+			 */
+			break;
+		}
+	}
+
+	if (dstatus != DPP_STATUS_OK || !wrapped) {
+		l_debug("Bad status or missing attributes");
+		return;
+	}
+
+	unwrapped = dpp_unwrap_attr(attrs, wrapped - attrs - 4, NULL, 0, dpp->ke,
+					dpp->key_len, wrapped, wrapped_len,
+					&wrapped_len);
+	if (!unwrapped) {
+		l_debug("Failed to unwrap");
+		return;
+	}
+
+	dpp_attr_iter_init(&iter, unwrapped, wrapped_len);
+
+	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
+		switch (type) {
+		case DPP_ATTR_ENROLLEE_NONCE:
+			if (len != dpp->nonce_len)
+				break;
+
+			if (memcmp(data, dpp->e_nonce, dpp->nonce_len))
+				break;
+
+			e_nonce = data;
+			break;
+		case DPP_ATTR_CONFIGURATION_OBJECT:
+			json = (const char *)data;
+			json_len = len;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (!json || !e_nonce) {
+		l_debug("No configuration object in response");
+		return;
+	}
+
+	dpp->config = dpp_parse_configuration_object(json, json_len);
+	if (!dpp->config) {
+		l_error("Configuration object did not parse");
+		return;
+	}
+
+	dpp_send_config_result(dpp);
+
+	l_timeout_remove(dpp->timeout);
+	dpp->timeout = NULL;
+
+	if (dpp->event_cb)
+		dpp->event_cb(DPP_EVENT_SUCCESS, NULL, dpp->user_data);
+}
+
+static void dpp_handle_config_result_frame(struct dpp_sm *dpp,
+						const uint8_t *frame,
+						size_t frame_len)
+{
+	struct dpp_attr_iter iter;
+	enum dpp_attribute_type type;
+	size_t len;
+	const uint8_t *data;
+	int status = -1;
+	const void *e_nonce = NULL;
+	const void *wrapped = NULL;
+	size_t wrapped_len;
+	_auto_(l_free) void *unwrapped = NULL;
+	const void *ad0 = frame;
+	const void *ad1 = frame + 6;
+
+	if (dpp->state != DPP_STATE_CONFIGURING)
+		return;
+
+	dpp_attr_iter_init(&iter, frame + 6, frame_len - 6);
+
+	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
+		switch (type) {
+		case DPP_ATTR_WRAPPED_DATA:
+			wrapped = data;
+			wrapped_len = len;
+			break;
+		default:
+			/* Wrapped data should be only attribute */
+			return;
+		}
+	}
+
+	if (!wrapped) {
+		l_debug("No wrapped data in config result");
+		return;
+	}
+
+	unwrapped = dpp_unwrap_attr(ad0, 6, ad1, wrapped - 4 - ad1,
+					dpp->ke, dpp->key_len, wrapped,
+					wrapped_len, &wrapped_len);
+	if (!unwrapped) {
+		l_debug("Failed to unwrap DPP configuration result");
+		return;
+	}
+
+	dpp_attr_iter_init(&iter, unwrapped, wrapped_len);
+
+	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
+		switch (type) {
+		case DPP_ATTR_STATUS:
+			status = l_get_u8(data);
+			break;
+		case DPP_ATTR_ENROLLEE_NONCE:
+			e_nonce = data;
+			break;
+		default:
+			break;
+		}
+	}
+
+	l_timeout_remove(dpp->timeout);
+	dpp->timeout = NULL;
+
+	if (!dpp->event_cb)
+		return;
+
+	if (status != DPP_STATUS_OK || !e_nonce)
+		dpp_failed(dpp);
+	else
+		dpp->event_cb(DPP_EVENT_SUCCESS, NULL, dpp->user_data);
+}
+
+static void dpp_pkex_bad_group(struct dpp_sm *dpp, uint16_t group)
+{
+	uint16_t own_group = l_ecc_curve_get_ike_group(dpp->curve);
+
+	/*
+	 * TODO: The spec allows group negotiation, but it is not yet
+	 *       implemented.
+	 */
+	if (!group)
+		return;
+	/*
+	 * Section 5.6.2
+	 * "If the Responder's offered group offers less security
+	 * than the Initiator's offered group, then the Initiator should
+	 * ignore this message"
+	 */
+	if (group < own_group) {
+		l_debug("Offered group %u is less secure, ignoring",
+				group);
+		return;
+	}
+	/*
+	 * Section 5.6.2
+	 * "If the Responder's offered group offers equivalent or better
+	 * security than the Initiator's offered group, then the
+	 * Initiator may choose to abort its original request and try
+	 * another exchange using the group offered by the Responder"
+	 */
+	if (group >= own_group) {
+		l_debug("Offered group %u is the same or more secure, "
+			" but group negotiation is not supported", group);
+		return;
+	}
+}
+
+static void dpp_pkex_bad_code(struct dpp_sm *dpp)
+{
+	_auto_(l_ecc_point_free) struct l_ecc_point *qr = NULL;
+
+	qr = dpp_derive_qr(dpp->curve, dpp->pkex_key, dpp->pkex_id,
+				dpp->mac_responder);
+	if (!qr || l_ecc_point_is_infinity(qr)) {
+		l_debug("Qr computed to zero, new code should be provisioned");
+		return;
+	}
+
+	l_debug("Qr computed successfully but responder indicated otherwise");
+}
+
+static void dpp_handle_pkex_exchange_response(struct dpp_sm *dpp,
+						const uint8_t *frame,
+						size_t frame_len)
+{
+	struct dpp_attr_iter iter;
+	enum dpp_attribute_type type;
+	size_t len;
+	const uint8_t *data;
+	const uint8_t *status = NULL;
+	uint8_t version = 0;
+	const void *identifier = NULL;
+	size_t identifier_len = 0;
+	const void *key = NULL;
+	size_t key_len = 0;
+	uint16_t group = 0;
+	_auto_(l_ecc_point_free) struct l_ecc_point *n = NULL;
+	_auto_(l_ecc_point_free) struct l_ecc_point *j = NULL;
+	_auto_(l_ecc_point_free) struct l_ecc_point *qr = NULL;
+	_auto_(l_ecc_point_free) struct l_ecc_point *k = NULL;
+
+	l_debug("PKEX exchange response");
+
+	if (dpp->state != DPP_STATE_PKEX_EXCHANGE)
+		return;
+
+	if (dpp->role != DPP_CAPABILITY_ENROLLEE)
+		return;
+
+	dpp_attr_iter_init(&iter, frame + 6, frame_len - 6);
+
+	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
+		switch (type) {
+		case DPP_ATTR_STATUS:
+			if (len != 1)
+				return;
+
+			status = data;
+			break;
+		case DPP_ATTR_PROTOCOL_VERSION:
+			if (len != 1)
+				return;
+
+			version = l_get_u8(data);
+			break;
+		case DPP_ATTR_CODE_IDENTIFIER:
+			identifier = data;
+			identifier_len = len;
+			break;
+		case DPP_ATTR_ENCRYPTED_KEY:
+			if (len != dpp->key_len * 2)
+				return;
+
+			key = data;
+			key_len = len;
+			break;
+		case DPP_ATTR_FINITE_CYCLIC_GROUP:
+			if (len != 2)
+				return;
+
+			group = l_get_le16(data);
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (!status) {
+		l_debug("No status attribute, ignoring");
+		return;
+	}
+
+	if (!key) {
+		l_debug("No encrypted key, ignoring");
+		return;
+	}
+
+	if (*status != DPP_STATUS_OK)
+		goto handle_status;
+
+	if (dpp->pkex_id) {
+		if (!identifier || identifier_len != strlen(dpp->pkex_id) ||
+					memcmp(dpp->pkex_id, identifier,
+						identifier_len)) {
+			l_debug("mismatch identifier, ignoring");
+			return;
+		}
+	}
+
+	if (version && version != dpp->pkex_version) {
+		l_debug("PKEX version does not match, igoring");
+		return;
+	}
+
+	n = l_ecc_point_from_data(dpp->curve, L_ECC_POINT_TYPE_FULL,
+					key, key_len);
+	if (!n) {
+		l_debug("failed to parse peers encrypted key");
+		goto failed;
+	}
+
+	/*
+	 * TODO: PKEX protocol version 2 does not require the initiator or
+	 *       responder MAC addresses. If using protocol version 2 there will
+	 *       be no requirement for the encapsulating protocol to set this.
+	 *
+	 * Until then (and always for protocol 1) the encapsulating protocol
+	 * must set the peers address in the PEER_ACCEPTED callback for PKEX.
+	 */
+	if (dpp->event_cb)
+		dpp->event_cb(DPP_EVENT_PEER_ACCEPTED, NULL, dpp->user_data);
+
+	if (L_WARN_ON(!dpp->mac_responder))
+		goto failed;
+
+	qr = dpp_derive_qr(dpp->curve, dpp->pkex_key, dpp->pkex_id,
+				dpp->mac_responder);
+	if (!qr)
+		goto failed;
+
+	dpp->y_or_x = l_ecc_point_new(dpp->curve);
+
+	/* Y' = N - Qr */
+	l_ecc_point_inverse(qr);
+	l_ecc_point_add(dpp->y_or_x, n, qr);
+
+	/*
+	 * "The resulting ephemeral key, denoted Y’, is then checked whether it
+	 * is the point-at-infinity. If it is not valid, the protocol ends
+	 * unsuccessfully"
+	 */
+	if (l_ecc_point_is_infinity(dpp->y_or_x)) {
+		l_debug("Y' computed to infinity, failing");
+		goto failed;
+	}
+
+	k = l_ecc_point_new(dpp->curve);
+
+	/* K = Y' * x */
+	l_ecc_point_multiply(k, dpp->pkex_private, dpp->y_or_x);
+
+	dpp_derive_z(dpp->mac_initiator, dpp->mac_responder, n, dpp->pkex_m, k,
+				dpp->pkex_key, dpp->pkex_id,
+				dpp->z, &dpp->z_len);
+
+	/* J = a * Y' */
+	j = l_ecc_point_new(dpp->curve);
+
+	l_ecc_point_multiply(j, dpp->boot_private, dpp->y_or_x);
+
+	if (!dpp_derive_u(j, dpp->mac_initiator, dpp->boot_public, dpp->y_or_x,
+				dpp->pkex_public, dpp->u, &dpp->u_len)) {
+		l_debug("failed to compute u");
+		goto failed;
+	}
+
+	dpp_send_pkex_commit_reveal_request(dpp);
+
+	return;
+
+handle_status:
+	switch (*status) {
+	case DPP_STATUS_BAD_GROUP:
+		dpp_pkex_bad_group(dpp, group);
+		break;
+	case DPP_STATUS_BAD_CODE:
+		dpp_pkex_bad_code(dpp);
+		break;
+	default:
+		l_debug("Unhandled status %u", *status);
+		break;
+	}
+
+failed:
+	dpp_failed(dpp);
+}
+
+static void dpp_handle_pkex_v1_exchange_request(struct dpp_sm *dpp,
+					const uint8_t *frame, size_t frame_len)
+{
+	struct dpp_attr_iter iter;
+	enum dpp_attribute_type type;
+	size_t len;
+	const uint8_t *data;
+	uint8_t version = 0;
+	uint16_t group = 0;
+	const void *id = NULL;
+	size_t id_len = 0;
+	const void *key = NULL;
+	size_t key_len = 0;
+	_auto_(l_ecc_point_free) struct l_ecc_point *m = NULL;
+
+	l_debug("PKEX exchange request");
+
+	if (dpp->state != DPP_STATE_PKEX_EXCHANGE)
+		return;
+
+	if (dpp->role != DPP_CAPABILITY_CONFIGURATOR)
+		return;
+
+	dpp_attr_iter_init(&iter, frame + 6, frame_len - 6);
+
+	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
+		switch (type) {
+		case DPP_ATTR_PROTOCOL_VERSION:
+			if (len != 1)
+				return;
+
+			version = l_get_u8(data);
+			break;
+		case DPP_ATTR_FINITE_CYCLIC_GROUP:
+			if (len != 2)
+				return;
+
+			group = l_get_le16(data);
+			break;
+		case DPP_ATTR_CODE_IDENTIFIER:
+			id = data;
+			id_len = len;
+			break;
+		case DPP_ATTR_ENCRYPTED_KEY:
+			key = data;
+			key_len = len;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (!key || !group) {
+		l_debug("initiator did not provide group or key, ignoring");
+		return;
+	}
+
+	if (group != l_ecc_curve_get_ike_group(dpp->curve)) {
+		l_debug("initiator is not using the same group");
+		goto bad_group;
+	}
+
+	/*
+	 * If the group isn't the same the key length won't match, so check
+	 * this here after we've determined the groups are equal
+	 */
+	if (key_len != dpp->key_len * 2) {
+		l_debug("Unexpected encrypted key length");
+		return;
+	}
+
+	if (version && version != dpp->pkex_version) {
+		l_debug("initiator is not using the same version, ignoring");
+		return;
+	}
+
+	if (dpp->pkex_id) {
+		if (!id || id_len != strlen(dpp->pkex_id) ||
+				memcmp(dpp->pkex_id, id, id_len)) {
+			l_debug("mismatch identifier, ignoring");
+			return;
+		}
+	}
+
+	m = l_ecc_point_from_data(dpp->curve, L_ECC_POINT_TYPE_FULL,
+					key, key_len);
+	if (!m) {
+		l_debug("could not parse key from initiator, ignoring");
+		return;
+	}
+
+	if (dpp->event_cb)
+		dpp->event_cb(DPP_EVENT_PEER_ACCEPTED, NULL, dpp->user_data);
+
+	if (!dpp->pkex_key) {
+		/*
+		 * "If an optional code identifier is used, it shall be a UTF-8
+		 *  string not greater than eighty (80) octets"
+		 */
+		if (!id || id_len > 80 || !l_utf8_validate(id, id_len, NULL)) {
+			l_debug("Configurator started with agent but enrollee "
+				"sent invalid or no identifier, ignoring");
+			return;
+		}
+
+		dpp->pkex_id = l_strndup(id, id_len);
+
+		if (dpp->event_cb)
+			dpp->event_cb(DPP_EVENT_PKEX_KEY_REQUESTED,
+					dpp->pkex_id, dpp->user_data);
+
+		/* Save the encrypted key/identifier for the agent callback */
+		dpp->peer_encr_key = l_steal_ptr(m);
+
+		return;
+	}
+
+	dpp_process_pkex_exchange_request(dpp, m);
+
+	return;
+
+bad_group:
+	dpp_send_pkex_bad_group(dpp);
+}
+
+static void dpp_handle_pkex_commit_reveal_request(struct dpp_sm *dpp,
+					const uint8_t *frame, size_t frame_len)
+{
+	struct dpp_attr_iter iter;
+	enum dpp_attribute_type type;
+	size_t len;
+	const uint8_t *data;
+	const void *wrapped = NULL;
+	size_t wrapped_len = 0;
+	_auto_(l_free) uint8_t *unwrapped = NULL;
+	size_t unwrapped_len;
+	uint8_t zero = 0;
+	const void *key = 0;
+	size_t key_len = 0;
+	const void *i_auth = NULL;
+	size_t i_auth_len = 0;
+	_auto_(l_ecc_point_free) struct l_ecc_point *j = NULL;
+	_auto_(l_ecc_point_free) struct l_ecc_point *l = NULL;
+	_auto_(l_ecc_point_free) struct l_ecc_point *peer_public = NULL;
+	uint8_t u[L_ECC_SCALAR_MAX_BYTES];
+	size_t u_len = 0;
+	uint8_t v[L_ECC_SCALAR_MAX_BYTES];
+	size_t v_len = 0;
+
+	l_debug("PKEX commit-reveal request");
+
+	if (dpp->state != DPP_STATE_PKEX_COMMIT_REVEAL)
+		return;
+
+	if (dpp->role != DPP_CAPABILITY_CONFIGURATOR)
+		return;
+
+	dpp_attr_iter_init(&iter, frame + 6, frame_len - 6);
+
+	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
+		switch (type) {
+		case DPP_ATTR_WRAPPED_DATA:
+			wrapped = data;
+			wrapped_len = len;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (!wrapped) {
+		l_debug("No wrapped data");
+		return;
+	}
+
+	unwrapped = dpp_unwrap_attr(frame, 6, &zero, 1, dpp->z, dpp->z_len,
+					wrapped, wrapped_len, &unwrapped_len);
+	if (!unwrapped) {
+		l_debug("Failed to unwrap attributes");
+		return;
+	}
+
+	dpp_attr_iter_init(&iter, unwrapped, unwrapped_len);
+
+	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
+		switch (type) {
+		case DPP_ATTR_BOOTSTRAPPING_KEY:
+			if (len != dpp->key_len * 2)
+				return;
+
+			key = data;
+			key_len = len;
+			break;
+		case DPP_ATTR_INITIATOR_AUTH_TAG:
+			if (len != 32)
+				return;
+
+			i_auth = data;
+			i_auth_len = len;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (!key || !i_auth) {
+		l_debug("missing attributes");
+		return;
+	}
+
+	peer_public = l_ecc_point_from_data(dpp->curve,
+					L_ECC_POINT_TYPE_FULL, key, key_len);
+	if (!peer_public) {
+		l_debug("peers boostrapping key did not validate");
+		goto failed;
+	}
+
+	/* J' = y * A' */
+	j = l_ecc_point_new(dpp->curve);
+
+	l_ecc_point_multiply(j, dpp->pkex_private, peer_public);
+
+	if (!dpp_derive_u(j, dpp->mac_initiator, peer_public,
+			dpp->pkex_public, dpp->y_or_x, u, &u_len)) {
+		l_debug("Failed to derive u");
+		goto failed;
+	}
+
+	if (memcmp(u, i_auth, i_auth_len)) {
+		l_debug("Initiator auth tag did not verify");
+		goto failed;
+	}
+
+	/* L' = x * B' */
+	l = l_ecc_point_new(dpp->curve);
+
+	l_ecc_point_multiply(l, dpp->boot_private, dpp->y_or_x);
+
+	if (!dpp_derive_v(l, dpp->mac_responder, dpp->boot_public, dpp->y_or_x,
+				dpp->pkex_public, v, &v_len)) {
+		l_debug("Failed to derive v");
+		goto failed;
+	}
+
+	dpp_sm_set_peer_bootstrap(dpp, peer_public);
+
+	dpp_send_pkex_commit_reveal_response(dpp, v, v_len);
+
+	dpp->mutual_auth = true;
+	dpp_start_authentication(dpp);
+
+	return;
+
+failed:
+	dpp_failed(dpp);
+}
+
+static void dpp_handle_pkex_commit_reveal_response(struct dpp_sm *dpp,
+					const uint8_t *frame, size_t frame_len)
+{
+	struct dpp_attr_iter iter;
+	enum dpp_attribute_type type;
+	size_t len;
+	const uint8_t *data;
+	const uint8_t *wrapped = NULL;
+	size_t wrapped_len = 0;
+	uint8_t one = 1;
+	_auto_(l_free) uint8_t *unwrapped = NULL;
+	size_t unwrapped_len = 0;
+	const uint8_t *boot_key = NULL;
+	size_t boot_key_len = 0;
+	const uint8_t *r_auth = NULL;
+	uint8_t v[L_ECC_SCALAR_MAX_BYTES];
+	size_t v_len;
+	_auto_(l_ecc_point_free) struct l_ecc_point *l = NULL;
+	_auto_(l_ecc_point_free) struct l_ecc_point *peer_boot = NULL;
+
+	l_debug("PKEX commit reveal response");
+
+	if (dpp->state != DPP_STATE_PKEX_COMMIT_REVEAL)
+		return;
+
+	if (dpp->role != DPP_CAPABILITY_ENROLLEE)
+		return;
+
+	dpp_attr_iter_init(&iter, frame + 6, frame_len - 6);
+
+	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
+		switch (type) {
+		case DPP_ATTR_WRAPPED_DATA:
+			wrapped = data;
+			wrapped_len = len;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (!wrapped) {
+		l_debug("No wrapped data");
+		return;
+	}
+
+	unwrapped = dpp_unwrap_attr(frame, 6, &one, 1, dpp->z, dpp->z_len,
+					wrapped, wrapped_len, &unwrapped_len);
+	if (!unwrapped) {
+		l_debug("Failed to unwrap Reveal-Commit message");
+		return;
+	}
+
+	dpp_attr_iter_init(&iter, unwrapped, unwrapped_len);
+
+	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
+		switch (type) {
+		case DPP_ATTR_BOOTSTRAPPING_KEY:
+			if (len != dpp->key_len * 2)
+				return;
+
+			boot_key = data;
+			boot_key_len = len;
+			break;
+		case DPP_ATTR_RESPONDER_AUTH_TAG:
+			if (len != 32)
+				return;
+
+			r_auth = data;
+			break;
+		default:
+			break;
+		}
+	}
+
+	peer_boot = l_ecc_point_from_data(dpp->curve, L_ECC_POINT_TYPE_FULL,
+							boot_key, boot_key_len);
+	if (!peer_boot) {
+		l_debug("Peer public bootstrapping key was invalid");
+		goto failed;
+	}
+
+	/* L = b * X' */
+	l = l_ecc_point_new(dpp->curve);
+
+	l_ecc_point_multiply(l, dpp->pkex_private, peer_boot);
+
+	if (!dpp_derive_v(l, dpp->mac_responder, peer_boot,
+				dpp->pkex_public, dpp->y_or_x, v, &v_len)) {
+		l_debug("Failed to derive v");
+		goto failed;
+	}
+
+	if (memcmp(v, r_auth, v_len)) {
+		l_debug("Bootstrapping data did not verify");
+		goto failed;
+	}
+
+	dpp_sm_set_peer_bootstrap(dpp, peer_boot);
+
+	dpp->mutual_auth = true;
+	dpp_start_authentication(dpp);
+
+	return;
+
+failed:
+	dpp_failed(dpp);
+}
+
+static bool dpp_validate_header(const uint8_t *data, size_t len,
+					enum dpp_frame_type *type)
+{
+	/* Ensure header + message type */
+	if (len < 6)
+		return false;
+
+	if (memcmp(data, wifi_alliance_oui, 3))
+		return false;
+
+	/* WiFi Allicance DPP OI type */
+	if (data[3] != 0x1a)
+		return false;
+
+	/* Cryptosuite */
+	if (data[4] != 1)
+		return false;
+
+	if (type)
+		*type = data[5];
+
+	return true;
+}
+
+static void dpp_handle_frame(struct dpp_sm *dpp,
+				const uint8_t *data, size_t len)
+{
+	enum dpp_frame_type type;
+
+	if (!dpp_validate_header(data, len, &type)) {
+		l_debug("header did not validate");
+		return;
+	}
+
+	switch (type) {
+	case DPP_FRAME_AUTHENTICATION_REQUEST:
+		dpp_handle_auth_request(dpp, data, len);
+		break;
+	case DPP_FRAME_AUTHENTICATION_RESPONSE:
+		dpp_handle_auth_response(dpp, data, len);
+		break;
+	case DPP_FRAME_AUTHENTICATION_CONFIRM:
+		dpp_handle_auth_confirm(dpp, data, len);
+		break;
+	case DPP_FRAME_CONFIGURATION_RESULT:
+		dpp_handle_config_result_frame(dpp, data, len);
+		break;
+	case DPP_FRAME_PRESENCE_ANNOUNCEMENT:
+		dpp_handle_presence_announcement(dpp, data, len);
+		break;
+	case DPP_FRAME_PKEX_XCHG_RESPONSE:
+		dpp_handle_pkex_exchange_response(dpp, data, len);
+		break;
+	case DPP_FRAME_PKEX_COMMIT_REVEAL_RESPONSE:
+		dpp_handle_pkex_commit_reveal_response(dpp, data, len);
+		break;
+	case DPP_FRAME_PKEX_VERSION1_XCHG_REQUEST:
+		dpp_handle_pkex_v1_exchange_request(dpp, data, len);
+		break;
+	case DPP_FRAME_PKEX_COMMIT_REVEAL_REQUEST:
+		dpp_handle_pkex_commit_reveal_request(dpp, data, len);
+		break;
+	default:
+		l_debug("Unhandled DPP frame %u", type);
+		break;
+	}
+}
+
+void dpp_handle_rx(struct dpp_sm *dpp, const uint8_t *data, size_t len)
+{
+	if (len < 1)
+		return;
+
+	switch (*data) {
+	case DPP_ACTION_VENDOR_SPECIFIC:
+		dpp_handle_frame(dpp, data + 1, len - 1);
+		break;
+	/* For the GAS frames save the action byte to validate the frame */
+	case DPP_ACTION_GAS_REQUEST:
+		dpp_handle_config_request(dpp, data + 1, len - 1);
+		break;
+	case DPP_ACTION_GAS_RESPONSE:
+		dpp_handle_config_response(dpp, data + 1, len - 1);
+		break;
+	default:
+		break;
+	}
 }
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 7/9] build: add dpp-common.{c,h}
  2024-03-27 15:19 [PATCH 1/9] dpp: prep for moving AAD within dpp_append_wrapped_data James Prestwood
                   ` (4 preceding siblings ...)
  2024-03-27 15:19 ` [PATCH 6/9] dpp-common: add TX/RX handlers to dpp_sm James Prestwood
@ 2024-03-27 15:19 ` James Prestwood
  2024-03-27 15:19 ` [PATCH 8/9] dpp: remove most crypto/frame processing James Prestwood
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 15+ messages in thread
From: James Prestwood @ 2024-03-27 15:19 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

---
 Makefile.am | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Makefile.am b/Makefile.am
index be287572..e9f690f2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -265,6 +265,7 @@ src_iwd_SOURCES = src/main.c linux/nl80211.h src/iwd.h src/missing.h \
 					src/dpp-util.h src/dpp-util.c \
 					src/json.h src/json.c \
 					src/dpp.c \
+					src/dpp-common.h src/dpp-common.c \
 					$(eap_sources) \
 					$(builtin_sources)
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 8/9] dpp: remove most crypto/frame processing
  2024-03-27 15:19 [PATCH 1/9] dpp: prep for moving AAD within dpp_append_wrapped_data James Prestwood
                   ` (5 preceding siblings ...)
  2024-03-27 15:19 ` [PATCH 7/9] build: add dpp-common.{c,h} James Prestwood
@ 2024-03-27 15:19 ` James Prestwood
  2024-03-27 15:19 ` [PATCH 9/9] dpp: use common state machine James Prestwood
  2024-04-02 15:10 ` [PATCH 1/9] dpp: prep for moving AAD within dpp_append_wrapped_data Denis Kenzior
  8 siblings, 0 replies; 15+ messages in thread
From: James Prestwood @ 2024-03-27 15:19 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

To prep for using dpp-common all (or most) of the crypto and frame
processing has been removed from dpp.c. This commit only serves as
a temporary transition to using dpp-common in order to make the
patches more readable. DPP is entirely broken with only this patch.
---
 src/dpp.c | 3232 ++++-------------------------------------------------
 1 file changed, 235 insertions(+), 2997 deletions(-)

diff --git a/src/dpp.c b/src/dpp.c
index 47ebd495..31d0ef28 100644
--- a/src/dpp.c
+++ b/src/dpp.c
@@ -57,8 +57,6 @@
 
 #define DPP_FRAME_MAX_RETRIES 5
 #define DPP_FRAME_RETRY_TIMEOUT 1
-#define DPP_AUTH_PROTO_TIMEOUT 10
-#define DPP_PKEX_PROTO_TIMEOUT 120
 
 static uint32_t netdev_watch;
 static struct l_genl_family *nl80211;
@@ -96,7 +94,7 @@ struct pkex_agent {
 	uint32_t pending_id;
 };
 
-struct dpp_sm {
+struct dpp {
 	struct netdev *netdev;
 	char *uri;
 	uint8_t role;
@@ -106,18 +104,8 @@ struct dpp_sm {
 
 	uint64_t wdev_id;
 
-	uint8_t *own_asn1;
-	size_t own_asn1_len;
-	uint8_t *peer_asn1;
-	size_t peer_asn1_len;
-	uint8_t own_boot_hash[32];
-	uint8_t peer_boot_hash[32];
-	const struct l_ecc_curve *curve;
-	size_t key_len;
-	size_t nonce_len;
 	struct l_ecc_scalar *boot_private;
 	struct l_ecc_point *boot_public;
-	struct l_ecc_point *peer_boot_public;
 
 	enum dpp_state state;
 	enum dpp_interface interface;
@@ -140,22 +128,6 @@ struct dpp_sm {
 	uint32_t offchannel_id;
 
 	uint8_t peer_addr[6];
-	uint8_t r_nonce[32];
-	uint8_t i_nonce[32];
-	uint8_t e_nonce[32];
-
-	struct l_ecc_scalar *m;
-	uint64_t ke[L_ECC_MAX_DIGITS];
-	uint64_t k1[L_ECC_MAX_DIGITS];
-	uint64_t k2[L_ECC_MAX_DIGITS];
-	uint64_t auth_tag[L_ECC_MAX_DIGITS];
-
-	struct l_ecc_scalar *proto_private;
-	struct l_ecc_point *own_proto_public;
-
-	struct l_ecc_point *peer_proto_public;
-
-	uint8_t diag_token;
 
 	/* Timeout of either auth/config protocol */
 	struct l_timeout *timeout;
@@ -175,24 +147,12 @@ struct dpp_sm {
 	/* PKEX-specific values */
 	char *pkex_id;
 	char *pkex_key;
-	uint8_t pkex_version;
-	struct l_ecc_point *peer_encr_key;
-	struct l_ecc_point *pkex_m;
-	/* Ephemeral key Y' or X' for enrollee or configurator */
-	struct l_ecc_point *y_or_x;
-	/* Ephemeral key pair y/Y or x/X */
-	struct l_ecc_point *pkex_public;
-	struct l_ecc_scalar *pkex_private;
-	uint8_t z[L_ECC_SCALAR_MAX_BYTES];
-	size_t z_len;
-	uint8_t u[L_ECC_SCALAR_MAX_BYTES];
-	size_t u_len;
+
 	uint32_t pkex_scan_id;
 
 	bool mcast_support : 1;
 	bool roc_started : 1;
 	bool channel_switch : 1;
-	bool mutual_auth : 1;
 };
 
 static const char *dpp_role_to_string(enum dpp_capability role)
@@ -212,7 +172,7 @@ static bool dpp_pkex_get_started(struct l_dbus *dbus,
 				struct l_dbus_message_builder *builder,
 				void *user_data)
 {
-	struct dpp_sm *dpp = user_data;
+	struct dpp *dpp = user_data;
 	bool started = (dpp->state != DPP_STATE_NOTHING &&
 			dpp->interface == DPP_INTERFACE_PKEX);
 
@@ -226,7 +186,7 @@ static bool dpp_pkex_get_role(struct l_dbus *dbus,
 				struct l_dbus_message_builder *builder,
 				void *user_data)
 {
-	struct dpp_sm *dpp = user_data;
+	struct dpp *dpp = user_data;
 	const char *role;
 
 	if (dpp->state == DPP_STATE_NOTHING ||
@@ -246,7 +206,7 @@ static bool dpp_get_started(struct l_dbus *dbus,
 				struct l_dbus_message_builder *builder,
 				void *user_data)
 {
-	struct dpp_sm *dpp = user_data;
+	struct dpp *dpp = user_data;
 	bool started = (dpp->state != DPP_STATE_NOTHING &&
 			dpp->interface == DPP_INTERFACE_DPP);
 
@@ -260,7 +220,7 @@ static bool dpp_get_role(struct l_dbus *dbus,
 				struct l_dbus_message_builder *builder,
 				void *user_data)
 {
-	struct dpp_sm *dpp = user_data;
+	struct dpp *dpp = user_data;
 	const char *role;
 
 	if (dpp->state == DPP_STATE_NOTHING ||
@@ -280,7 +240,7 @@ static bool dpp_get_uri(struct l_dbus *dbus,
 				struct l_dbus_message_builder *builder,
 				void *user_data)
 {
-	struct dpp_sm *dpp = user_data;
+	struct dpp *dpp = user_data;
 
 	if (dpp->state == DPP_STATE_NOTHING ||
 				dpp->interface != DPP_INTERFACE_DPP)
@@ -290,7 +250,7 @@ static bool dpp_get_uri(struct l_dbus *dbus,
 	return true;
 }
 
-static void dpp_property_changed_notify(struct dpp_sm *dpp)
+static void dpp_property_changed_notify(struct dpp *dpp)
 {
 	const char *path = netdev_get_path(dpp->netdev);
 
@@ -340,26 +300,6 @@ static void *dpp_serialize_iovec(struct iovec *iov, size_t iov_len,
 	return ret;
 }
 
-static void dpp_free_pending_pkex_data(struct dpp_sm *dpp)
-{
-	if (dpp->pkex_id) {
-		l_free(dpp->pkex_id);
-		dpp->pkex_id = NULL;
-	}
-
-	if (dpp->pkex_key) {
-		l_free(dpp->pkex_key);
-		dpp->pkex_key = NULL;
-	}
-
-	memset(dpp->peer_addr, 0, sizeof(dpp->peer_addr));
-
-	if (dpp->peer_encr_key) {
-		l_ecc_point_free(dpp->peer_encr_key);
-		dpp->peer_encr_key = NULL;
-	}
-}
-
 static void pkex_agent_free(void *data)
 {
 	struct pkex_agent *agent = data;
@@ -370,7 +310,7 @@ static void pkex_agent_free(void *data)
 	l_free(agent);
 }
 
-static void dpp_agent_cancel(struct dpp_sm *dpp)
+static void dpp_agent_cancel(struct dpp *dpp)
 {
 	struct l_dbus_message *msg;
 
@@ -386,7 +326,7 @@ static void dpp_agent_cancel(struct dpp_sm *dpp)
 	l_dbus_send(dbus_get_bus(), msg);
 }
 
-static void dpp_agent_release(struct dpp_sm *dpp)
+static void dpp_agent_release(struct dpp *dpp)
 {
 	struct l_dbus_message *msg;
 
@@ -400,7 +340,7 @@ static void dpp_agent_release(struct dpp_sm *dpp)
 	l_dbus_send(dbus_get_bus(), msg);
 }
 
-static void dpp_destroy_agent(struct dpp_sm *dpp)
+static void dpp_destroy_agent(struct dpp *dpp)
 {
 	if (!dpp->agent)
 		return;
@@ -418,56 +358,7 @@ static void dpp_destroy_agent(struct dpp_sm *dpp)
 	dpp->agent = NULL;
 }
 
-static void dpp_free_auth_data(struct dpp_sm *dpp)
-{
-	if (dpp->own_proto_public) {
-		l_ecc_point_free(dpp->own_proto_public);
-		dpp->own_proto_public = NULL;
-	}
-
-	if (dpp->proto_private) {
-		l_ecc_scalar_free(dpp->proto_private);
-		dpp->proto_private = NULL;
-	}
-
-	if (dpp->peer_proto_public) {
-		l_ecc_point_free(dpp->peer_proto_public);
-		dpp->peer_proto_public = NULL;
-	}
-
-	if (dpp->peer_boot_public) {
-		l_ecc_point_free(dpp->peer_boot_public);
-		dpp->peer_boot_public = NULL;
-	}
-
-	if (dpp->m) {
-		l_ecc_scalar_free(dpp->m);
-		dpp->m = NULL;
-	}
-
-	if (dpp->pkex_m) {
-		l_ecc_point_free(dpp->pkex_m);
-		dpp->pkex_m = NULL;
-	}
-
-	if (dpp->y_or_x) {
-		l_ecc_point_free(dpp->y_or_x);
-		dpp->y_or_x = NULL;
-	}
-
-	if (dpp->pkex_public) {
-		l_ecc_point_free(dpp->pkex_public);
-		dpp->pkex_public = NULL;
-	}
-
-	if (dpp->pkex_private) {
-		l_ecc_scalar_free(dpp->pkex_private);
-		dpp->pkex_private = NULL;
-	}
-
-}
-
-static void dpp_reset(struct dpp_sm *dpp)
+static void dpp_reset(struct dpp *dpp)
 {
 	if (dpp->uri) {
 		l_free(dpp->uri);
@@ -499,11 +390,6 @@ static void dpp_reset(struct dpp_sm *dpp)
 		dpp->connect_scan_id = 0;
 	}
 
-	if (dpp->peer_asn1) {
-		l_free(dpp->peer_asn1);
-		dpp->peer_asn1 = NULL;
-	}
-
 	if (dpp->frame_pending) {
 		l_free(dpp->frame_pending);
 		dpp->frame_pending = NULL;
@@ -524,44 +410,23 @@ static void dpp_reset(struct dpp_sm *dpp)
 		dpp->connect_idle = NULL;
 	}
 
-	dpp->state = DPP_STATE_NOTHING;
 	dpp->new_freq = 0;
 	dpp->frame_retry = 0;
 	dpp->frame_cookie = 0;
-	dpp->pkex_version = 0;
-
-	explicit_bzero(dpp->r_nonce, dpp->nonce_len);
-	explicit_bzero(dpp->i_nonce, dpp->nonce_len);
-	explicit_bzero(dpp->e_nonce, dpp->nonce_len);
-	explicit_bzero(dpp->ke, dpp->key_len);
-	explicit_bzero(dpp->k1, dpp->key_len);
-	explicit_bzero(dpp->k2, dpp->key_len);
-	explicit_bzero(dpp->auth_tag, dpp->key_len);
-	explicit_bzero(dpp->z, dpp->key_len);
-	explicit_bzero(dpp->u, dpp->u_len);
 
 	dpp_destroy_agent(dpp);
 
-	dpp_free_pending_pkex_data(dpp);
-
-	dpp_free_auth_data(dpp);
-
 	dpp_property_changed_notify(dpp);
 
 	dpp->interface = DPP_INTERFACE_UNBOUND;
 }
 
-static void dpp_free(struct dpp_sm *dpp)
+static void dpp_free(struct dpp *dpp)
 {
 	struct station *station = station_find(netdev_get_ifindex(dpp->netdev));
 
 	dpp_reset(dpp);
 
-	if (dpp->own_asn1) {
-		l_free(dpp->own_asn1);
-		dpp->own_asn1 = NULL;
-	}
-
 	if (dpp->boot_public) {
 		l_ecc_point_free(dpp->boot_public);
 		dpp->boot_public = NULL;
@@ -586,7 +451,7 @@ static void dpp_free(struct dpp_sm *dpp)
 
 static void dpp_send_frame_cb(struct l_genl_msg *msg, void *user_data)
 {
-	struct dpp_sm *dpp = user_data;
+	struct dpp *dpp = user_data;
 	int err = l_genl_msg_get_error(msg);
 
 	if (err < 0) {
@@ -599,7 +464,7 @@ static void dpp_send_frame_cb(struct l_genl_msg *msg, void *user_data)
 		l_error("Error parsing frame cookie");
 }
 
-static void dpp_send_frame(struct dpp_sm *dpp,
+static void dpp_send_frame(struct dpp *dpp,
 				struct iovec *iov, size_t iov_len,
 				uint32_t freq)
 {
@@ -632,7 +497,7 @@ static void dpp_send_frame(struct dpp_sm *dpp,
 	}
 }
 
-static void dpp_frame_retry(struct dpp_sm *dpp)
+static void dpp_frame_retry(struct dpp *dpp)
 {
 	struct iovec iov;
 
@@ -645,183 +510,6 @@ static void dpp_frame_retry(struct dpp_sm *dpp)
 	dpp->frame_pending = NULL;
 }
 
-
-static size_t dpp_build_header(const uint8_t *src, const uint8_t *dest,
-				enum dpp_frame_type type,
-				uint8_t *buf)
-{
-	uint8_t *ptr = buf + 24;
-
-	memset(buf, 0, 32);
-
-	l_put_le16(0x00d0, buf);
-	memcpy(buf + 4, dest, 6);
-	memcpy(buf + 10, src, 6);
-	memcpy(buf + 16, broadcast, 6);
-
-	*ptr++ = 0x04;			/* Category: Public */
-	*ptr++ = 0x09;			/* Action: Vendor specific usage */
-	memcpy(ptr, wifi_alliance_oui, 3);
-	ptr += 3;
-	*ptr++ = 0x1a;			/* WiFi Alliance DPP OI type */
-	*ptr++ = 1;			/* Cryptosuite */
-	*ptr++ = type;
-
-	return ptr - buf;
-}
-
-static size_t dpp_build_config_header(const uint8_t *src, const uint8_t *dest,
-					uint8_t diag_token,
-					uint8_t *buf)
-{
-	uint8_t *ptr = buf + 24;
-
-	memset(buf, 0, 37);
-
-	l_put_le16(0x00d0, buf);
-	memcpy(buf + 4, dest, 6);
-	memcpy(buf + 10, src, 6);
-	memcpy(buf + 16, broadcast, 6);
-
-	*ptr++ = 0x04; /* Public */
-	*ptr++ = 0x0a; /* Action */
-	*ptr++ = diag_token;
-
-	*ptr++ = IE_TYPE_ADVERTISEMENT_PROTOCOL;
-	*ptr++ = 8; /* len */
-	*ptr++ = 0x00;
-	*ptr++ = IE_TYPE_VENDOR_SPECIFIC;
-	*ptr++ = 5;
-	memcpy(ptr, wifi_alliance_oui, 3);
-	ptr += 3;
-	*ptr++ = 0x1a;
-	*ptr++ = 1;
-
-	return ptr - buf;
-}
-
-static void dpp_protocol_timeout(struct l_timeout *timeout, void *user_data)
-{
-	struct dpp_sm *dpp = user_data;
-
-	l_debug("DPP timed out");
-
-	dpp_reset(dpp);
-}
-
-static void dpp_reset_protocol_timer(struct dpp_sm *dpp, uint32_t time)
-{
-	if (dpp->timeout)
-		l_timeout_modify(dpp->timeout, time);
-	else
-		dpp->timeout = l_timeout_create(time, dpp_protocol_timeout,
-						dpp, NULL);
-}
-
-/*
- * The configuration protocols use of AD components is somewhat confusing
- * since the request/response frames are of a different format than the rest.
- * In addition there are situations where the components length is zero yet it
- * is still passed as such to AES-SIV.
- *
- * For the configuration request/response frames:
- *
- * "AAD for use with AES-SIV for protected messages in the DPP Configuration
- * protocol shall consist of all octets in the Query Request and Query Response
- * fields up to the first octet of the Wrapped Data attribute, which is the last
- * attribute in a DPP Configuration frame. When the number of octets of AAD is
- * zero, the number of components of AAD passed to AES-SIV is zero."
- *
- *  - For configuration requests the optional query request field is not
- *    included, therefore no AAD data is passed. (dpp_configuration_start)
- *
- *  - The configuration response does contain a query response field which is
- *    5 bytes. (dpp_handle_config_response_frame)
- *
- * For the configuration result/status, the same rules are used as the
- * authentication protocol. This is reiterated in section 6.4.1.
- *
- *  - For the configuration result there is some confusion as to exactly how the
- *    second AAD component should be passed (since the spec specifically
- *    mentions using two components). There are no attributes prior to the
- *    wrapped data component meaning the length would be zero.
- *    Hostapd/wpa_supplicant pass a zero length AAD component to AES-SIV which
- *    does effect the resulting encryption/decryption so this is also what IWD
- *    will do to remain compliant with it.
- */
-static void dpp_configuration_start(struct dpp_sm *dpp, const uint8_t *addr)
-{
-	const char *json = "{\"name\":\"IWD\",\"wi-fi_tech\":\"infra\","
-				"\"netRole\":\"sta\"}";
-	struct iovec iov;
-	uint8_t frame[512];
-	size_t json_len = strlen(json);
-	uint8_t *ptr = frame;
-	uint8_t *lptr;
-	struct mmpdu_header *hdr = (struct mmpdu_header *)frame;
-
-	memset(frame, 0, sizeof(frame));
-
-	l_getrandom(&dpp->diag_token, 1);
-
-	ptr += dpp_build_config_header(netdev_get_address(dpp->netdev),
-					addr, dpp->diag_token, ptr);
-
-	l_getrandom(dpp->e_nonce, dpp->nonce_len);
-
-	/* length */
-	lptr = ptr;
-	ptr += 2;
-
-	/*
-	 * "AAD for use with AES-SIV for protected messages in the DPP
-	 * Configuration protocol shall consist of all octets in the Query
-	 * Request and Query Response fields up to the first octet of the
-	 * Wrapped Data attribute"
-	 *
-	 * In this case there is no query request/response fields, nor any
-	 * attributes besides wrapped data meaning zero AD components.
-	 */
-	ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1,
-			sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr,
-			dpp->ke, dpp->key_len, 2,
-			DPP_ATTR_ENROLLEE_NONCE, dpp->nonce_len, dpp->e_nonce,
-			DPP_ATTR_CONFIGURATION_REQUEST, json_len, json);
-
-	l_put_le16(ptr - lptr - 2, lptr);
-
-	iov.iov_base = frame;
-	iov.iov_len = ptr - frame;
-
-	dpp->state = DPP_STATE_CONFIGURING;
-
-	dpp_send_frame(dpp, &iov, 1, dpp->current_freq);
-}
-
-static void send_config_result(struct dpp_sm *dpp, const uint8_t *to)
-{
-	struct iovec iov;
-	uint8_t frame[256];
-	uint8_t *ptr = frame;
-	uint8_t zero = 0;
-	struct mmpdu_header *hdr = (struct mmpdu_header *)frame;
-
-	memset(frame, 0, sizeof(frame));
-
-	ptr += dpp_build_header(netdev_get_address(dpp->netdev), to,
-					DPP_FRAME_CONFIGURATION_RESULT, ptr);
-	ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1,
-			sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr,
-			dpp->ke, dpp->key_len, 2,
-			DPP_ATTR_STATUS, (size_t) 1, &zero,
-			DPP_ATTR_ENROLLEE_NONCE, dpp->nonce_len, dpp->e_nonce);
-
-	iov.iov_base = frame;
-	iov.iov_len = ptr - frame;
-
-	dpp_send_frame(dpp, &iov, 1, dpp->current_freq);
-}
-
 static void dpp_write_config(struct dpp_configuration *config,
 				struct network *network)
 {
@@ -862,2600 +550,250 @@ static void dpp_scan_triggered(int err, void *user_data)
 
 static void dpp_start_connect(struct l_idle *idle, void *user_data)
 {
-	struct dpp_sm *dpp = user_data;
-	struct station *station = station_find(netdev_get_ifindex(dpp->netdev));
-	struct scan_bss *bss;
-	struct network *network;
-	int ret;
-
-	network = station_network_find(station, dpp->config->ssid,
-					SECURITY_PSK);
-
-	dpp_reset(dpp);
-
-	if (!network) {
-		l_debug("Network was not found!");
-		return;
-	}
-
-	l_debug("connecting to %s from DPP", network_get_ssid(network));
-
-	bss = network_bss_select(network, true);
-	ret = network_autoconnect(network, bss);
-	if (ret < 0)
-		l_warn("failed to connect after DPP (%d) %s", ret,
-			strerror(-ret));
-}
-
-static bool dpp_scan_results(int err, struct l_queue *bss_list,
-				const struct scan_freq_set *freqs,
-				void *userdata)
-{
-	struct dpp_sm *dpp = userdata;
-	struct station *station = station_find(netdev_get_ifindex(dpp->netdev));
-
-	if (err < 0)
-		goto reset;
-
-	if (!bss_list || l_queue_length(bss_list) == 0)
-		goto reset;
-
-	/*
-	 * The station watch _should_ detect this and reset, which cancels the
-	 * scan. But just in case...
-	 */
-	if (L_WARN_ON(station_get_connected_network(station)))
-		goto reset;
-
-	station_set_scan_results(station, bss_list, freqs, false);
-
-	dpp_start_connect(NULL, dpp);
-
-	return true;
-
-reset:
-	return false;
-}
-
-static void dpp_scan_destroy(void *userdata)
-{
-	struct dpp_sm *dpp = userdata;
-
-	dpp->connect_scan_id = 0;
-	dpp_reset(dpp);
-}
-
-static void dpp_known_network_watch(enum known_networks_event event,
-					const struct network_info *info,
-					void *user_data)
-{
-	struct dpp_sm *dpp = user_data;
-
-	/*
-	 * Check the following
-	 *  - DPP is enrolling
-	 *  - DPP finished (dpp->config is set)
-	 *  - This is for the network DPP just configured
-	 *  - DPP isn't already trying to connect (e.g. if the profile was
-	 *    immediately modified after DPP synced it).
-	 *  - DPP didn't start a scan for the network.
-	 */
-	if (dpp->role != DPP_CAPABILITY_ENROLLEE)
-		return;
-	if (!dpp->config)
-		return;
-	if (strcmp(info->ssid, dpp->config->ssid))
-		return;
-	if (dpp->connect_idle)
-		return;
-	if (dpp->connect_scan_id)
-		return;
-
-	switch (event) {
-	case KNOWN_NETWORKS_EVENT_ADDED:
-	case KNOWN_NETWORKS_EVENT_UPDATED:
-		/*
-		 * network.c takes care of updating the settings for the
-		 * network. This callback just tells us to begin the connection.
-		 * We do have use an idle here because there is no strict
-		 * guarantee of ordering between known network events, e.g. DPP
-		 * could have been called into prior to network and the network
-		 * object isn't updated yet.
-		 */
-		dpp->connect_idle = l_idle_create(dpp_start_connect, dpp, NULL);
-		break;
-	case KNOWN_NETWORKS_EVENT_REMOVED:
-		l_warn("profile was removed before DPP could connect");
-		break;
-	}
-}
-
-static void dpp_handle_config_response_frame(const struct mmpdu_header *frame,
-				const void *body, size_t body_len,
-				int rssi, void *user_data)
-{
-	struct dpp_sm *dpp = user_data;
-	const uint8_t *ptr = body;
-	uint16_t status;
-	uint16_t fragmented; /* Fragmented/Comeback delay field */
-	uint8_t adv_protocol_element[] = { 0x6C, 0x08, 0x7F };
-	uint8_t adv_protocol_id[] = { 0xDD, 0x05, 0x50, 0x6F,
-					0x9A, 0x1A, 0x01 };
-	uint16_t query_len;
-	struct dpp_attr_iter iter;
-	enum dpp_attribute_type type;
-	size_t len;
-	const uint8_t *data;
-	const char *json = NULL;
-	size_t json_len = 0;
-	int dstatus = -1;
-	const uint8_t *wrapped = NULL;
-	const uint8_t *e_nonce = NULL;
-	size_t wrapped_len = 0;
-	_auto_(l_free) uint8_t *unwrapped = NULL;
-	struct dpp_configuration *config;
+	struct dpp *dpp = user_data;
 	struct station *station = station_find(netdev_get_ifindex(dpp->netdev));
-	struct network *network = NULL;
-	struct scan_bss *bss = NULL;
-
-	if (dpp->state != DPP_STATE_CONFIGURING)
-		return;
-
-	/*
-	 * Can a configuration request come from someone other than who you
-	 * authenticated to?
-	 */
-	if (memcmp(dpp->peer_addr, frame->address_2, 6))
-		return;
-
-	if (body_len < 19)
-		return;
-
-	ptr += 2;
-
-	if (*ptr++ != dpp->diag_token)
-		return;
-
-	status = l_get_le16(ptr);
-	ptr += 2;
-
-	if (status != 0) {
-		l_debug("Bad configuration status %u", status);
-		return;
-	}
-
-	fragmented = l_get_le16(ptr);
-	ptr += 2;
-
-	/*
-	 * TODO: handle 0x0001 (fragmented), as well as comeback delay.
-	 */
-	if (fragmented != 0) {
-		l_debug("Fragmented messages not currently supported");
-		return;
-	}
-
-	if (memcmp(ptr, adv_protocol_element, sizeof(adv_protocol_element))) {
-		l_debug("Invalid Advertisement protocol element");
-		return;
-	}
-
-	ptr += sizeof(adv_protocol_element);
-
-	if (memcmp(ptr, adv_protocol_id, sizeof(adv_protocol_id))) {
-		l_debug("Invalid Advertisement protocol ID");
-		return;
-	}
-
-	ptr += sizeof(adv_protocol_id);
-
-	query_len = l_get_le16(ptr);
-	ptr += 2;
-
-	if (query_len > body_len - 19)
-		return;
-
-	dpp_attr_iter_init(&iter, ptr, query_len);
-
-	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
-		switch (type) {
-		case DPP_ATTR_STATUS:
-			dstatus = l_get_u8(data);
-			break;
-		case DPP_ATTR_WRAPPED_DATA:
-			wrapped = data;
-			wrapped_len = len;
-			break;
-		default:
-			/*
-			 * TODO: CSR Attribute
-			 */
-			break;
-		}
-	}
-
-	if (dstatus != DPP_STATUS_OK || !wrapped) {
-		l_debug("Bad status or missing attributes");
-		return;
-	}
-
-	unwrapped = dpp_unwrap_attr(ptr, wrapped - ptr - 4, NULL, 0, dpp->ke,
-					dpp->key_len, wrapped, wrapped_len,
-					&wrapped_len);
-	if (!unwrapped) {
-		l_debug("Failed to unwrap");
-		return;
-	}
-
-	dpp_attr_iter_init(&iter, unwrapped, wrapped_len);
-
-	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
-		switch (type) {
-		case DPP_ATTR_ENROLLEE_NONCE:
-			if (len != dpp->nonce_len)
-				break;
-
-			if (memcmp(data, dpp->e_nonce, dpp->nonce_len))
-				break;
-
-			e_nonce = data;
-			break;
-		case DPP_ATTR_CONFIGURATION_OBJECT:
-			json = (const char *)data;
-			json_len = len;
-			break;
-		default:
-			break;
-		}
-	}
-
-	if (!json || !e_nonce) {
-		l_debug("No configuration object in response");
-		return;
-	}
-
-	config = dpp_parse_configuration_object(json, json_len);
-	if (!config) {
-		l_error("Configuration object did not parse");
-		return;
-	}
-
-	/*
-	 * We should have a station device, but if not DPP can write the
-	 * credentials out and be done
-	 */
-	if (station) {
-		network = station_network_find(station, config->ssid,
-						SECURITY_PSK);
-		if (network)
-			bss = network_bss_select(network, true);
-	}
-
-	dpp_write_config(config, network);
-
-	send_config_result(dpp, dpp->peer_addr);
-
-	offchannel_cancel(dpp->wdev_id, dpp->offchannel_id);
-
-	if (network && bss) {
-		l_debug("delaying connect until settings are synced");
-		dpp->config = config;
-		return;
-	} else if (station) {
-		struct scan_parameters params = {0};
-
-		params.ssid = (void *) config->ssid;
-		params.ssid_len = config->ssid_len;
-
-		l_debug("Scanning for %s", config->ssid);
-
-		dpp->connect_scan_id = scan_active_full(dpp->wdev_id, &params,
-						dpp_scan_triggered,
-						dpp_scan_results, dpp,
-						dpp_scan_destroy);
-		if (dpp->connect_scan_id) {
-			dpp->config = config;
-			return;
-		}
-	}
-
-	dpp_configuration_free(config);
-	dpp_reset(dpp);
-}
-
-static void dpp_send_config_response(struct dpp_sm *dpp, uint8_t status)
-{
-	_auto_(l_free) char *json = NULL;
-	struct iovec iov;
-	uint8_t frame[512];
-	size_t json_len;
-	uint8_t *ptr = frame;
-	uint8_t *lptr;
-	struct mmpdu_header *hdr = (struct mmpdu_header *)frame;
-
-	memset(frame, 0, sizeof(frame));
-
-	l_put_le16(0x00d0, ptr);
-	memcpy(ptr + 4, dpp->peer_addr, 6);
-	memcpy(ptr + 10, netdev_get_address(dpp->netdev), 6);
-	memcpy(ptr + 16, broadcast, 6);
-
-	ptr += 24;
-
-	*ptr++ = 0x04;
-	*ptr++ = 0x0b;
-	*ptr++ = dpp->diag_token;
-	l_put_le16(0, ptr); /* status */
-	ptr += 2;
-	l_put_le16(0, ptr); /* fragmented (no) */
-	ptr += 2;
-	*ptr++ = IE_TYPE_ADVERTISEMENT_PROTOCOL;
-	*ptr++ = 0x08;
-	*ptr++ = 0x7f;
-	*ptr++ = IE_TYPE_VENDOR_SPECIFIC;
-	*ptr++ = 5;
-	memcpy(ptr, wifi_alliance_oui, sizeof(wifi_alliance_oui));
-	ptr += sizeof(wifi_alliance_oui);
-	*ptr++ = 0x1a;
-	*ptr++ = 1;
-
-	lptr = ptr;
-	ptr += 2; /* length */
-
-	ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &status, 1);
-
-	/*
-	 * There are several failure status codes that can be used (defined in
-	 * 6.4.3.1), each with their own set of attributes that should be
-	 * included. For now IWD's basic DPP implementation will assume
-	 * STATUS_CONFIGURE_FAILURE which only includes the E-Nonce.
-	 */
-	if (status == DPP_STATUS_OK) {
-		json = dpp_configuration_to_json(dpp->config);
-		json_len = strlen(json);
-
-		ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1,
-				sizeof(frame) - mmpdu_header_len(hdr) - 1,
-				ptr, dpp->ke, dpp->key_len, 2,
-				DPP_ATTR_ENROLLEE_NONCE,
-				dpp->nonce_len, dpp->e_nonce,
-				DPP_ATTR_CONFIGURATION_OBJECT,
-				json_len, json);
-	} else
-		ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1,
-				sizeof(frame) - mmpdu_header_len(hdr) - 1,
-				ptr, dpp->ke, dpp->key_len, 2,
-				DPP_ATTR_ENROLLEE_NONCE,
-				dpp->nonce_len, dpp->e_nonce);
-
-	l_put_le16(ptr - lptr - 2, lptr);
-
-	iov.iov_base = frame;
-	iov.iov_len = ptr - frame;
-
-	dpp_send_frame(dpp, &iov, 1, dpp->current_freq);
-}
-
-static bool dpp_check_config_header(const uint8_t *ptr)
-{
-	/*
-	 * Table 58. General Format of DPP Configuration Request frame
-	 *
-	 * Unfortunately wpa_supplicant hard codes 0x7f as the Query Response
-	 * Info so we need to handle both cases.
-	 */
-	return ptr[0] == IE_TYPE_ADVERTISEMENT_PROTOCOL &&
-			ptr[1] == 0x08 &&
-			(ptr[2] == 0x7f || ptr[2] == 0x00) &&
-			ptr[3] == IE_TYPE_VENDOR_SPECIFIC &&
-			ptr[4] == 5;
-}
-
-static void dpp_handle_config_request_frame(const struct mmpdu_header *frame,
-				const void *body, size_t body_len,
-				int rssi, void *user_data)
-{
-	struct dpp_sm *dpp = user_data;
-	const uint8_t *ptr = body;
-	struct dpp_attr_iter iter;
-	enum dpp_attribute_type type;
-	size_t len;
-	const uint8_t *data;
-	const char *json = NULL;
-	size_t json_len = 0;
-	struct json_contents *c;
-	const uint8_t *wrapped = NULL;
-	const uint8_t *e_nonce = NULL;
-	size_t wrapped_len = 0;
-	_auto_(l_free) uint8_t *unwrapped = NULL;
-	struct json_iter jsiter;
-	_auto_(l_free) char *tech = NULL;
-	_auto_(l_free) char *role = NULL;
-
-	if (dpp->state != DPP_STATE_AUTHENTICATING) {
-		l_debug("Configuration request in wrong state");
-		return;
-	}
-
-	if (dpp->role != DPP_CAPABILITY_CONFIGURATOR)
-		return;
-
-	if (memcmp(dpp->peer_addr, frame->address_2, 6)) {
-		l_debug("Configuration request not from authenticated peer");
-		return;
-	}
-
-	if (body_len < 15) {
-		l_debug("Configuration request data not long enough");
-		return;
-	}
-
-	ptr += 2;
-
-	dpp->diag_token = *ptr++;
-
-	if (!dpp_check_config_header(ptr))
-		return;
-
-	ptr += 5;
-
-	if (memcmp(ptr, wifi_alliance_oui, sizeof(wifi_alliance_oui)))
-		return;
-
-	ptr += sizeof(wifi_alliance_oui);
-
-	if (*ptr != 0x1a && *(ptr + 1) != 1)
-		return;
-
-	ptr += 2;
-
-	len = l_get_le16(ptr);
-	ptr += 2;
-
-	if (len > body_len - 15)
-		return;
-
-	dpp_attr_iter_init(&iter, ptr, len);
-
-	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
-		switch (type) {
-		case DPP_ATTR_WRAPPED_DATA:
-			wrapped = data;
-			wrapped_len = len;
-			break;
-		default:
-			/* Wrapped data should be only attribute */
-			return;
-		}
-	}
-
-	if (!wrapped) {
-		l_debug("Wrapped data missing");
-		return;
-	}
-
-	unwrapped = dpp_unwrap_attr(NULL, 0, NULL, 0, dpp->ke,
-					dpp->key_len, wrapped, wrapped_len,
-					&wrapped_len);
-	if (!unwrapped) {
-		l_debug("Failed to unwrap");
-		return;
-	}
-
-	dpp_attr_iter_init(&iter, unwrapped, wrapped_len);
-
-	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
-		switch (type) {
-		case DPP_ATTR_ENROLLEE_NONCE:
-			if (len != dpp->nonce_len)
-				break;
-
-			e_nonce = data;
-			break;
-		case DPP_ATTR_CONFIGURATION_REQUEST:
-			json = (const char *)data;
-			json_len = len;
-			break;
-		default:
-			break;
-		}
-	}
-
-	if (!json || !e_nonce) {
-		l_debug("No configuration object in response");
-		return;
-	}
-
-	c = json_contents_new(json, json_len);
-	if (!c) {
-		json_contents_free(c);
-		return;
-	}
-
-	json_iter_init(&jsiter, c);
-
-	/*
-	 * Check mandatory values (Table 7). There isn't much that can be done
-	 * with these, but the spec requires they be included.
-	 */
-	if (!json_iter_parse(&jsiter,
-			JSON_MANDATORY("name", JSON_STRING, NULL),
-			JSON_MANDATORY("wi-fi_tech", JSON_STRING, &tech),
-			JSON_MANDATORY("netRole", JSON_STRING, &role),
-			JSON_UNDEFINED))
-		goto configure_failure;
-
-	if (strcmp(tech, "infra"))
-		goto configure_failure;
-
-	if (strcmp(role, "sta"))
-		goto configure_failure;
-
-	json_contents_free(c);
-
-	memcpy(dpp->e_nonce, e_nonce, dpp->nonce_len);
-
-	dpp->state = DPP_STATE_CONFIGURING;
-
-	dpp_send_config_response(dpp, DPP_STATUS_OK);
-
-	return;
-
-configure_failure:
-	dpp_send_config_response(dpp, DPP_STATUS_CONFIGURE_FAILURE);
-	/*
-	 * The other peer is still authenticated, and can potentially send
-	 * additional requests so keep this session alive.
-	 */
-}
-
-static void dpp_handle_config_result_frame(struct dpp_sm *dpp,
-					const uint8_t *from, const void *body,
-					size_t body_len)
-{
-	struct dpp_attr_iter iter;
-	enum dpp_attribute_type type;
-	size_t len;
-	const uint8_t *data;
-	int status = -1;
-	const void *e_nonce = NULL;
-	const void *wrapped = NULL;
-	size_t wrapped_len;
-	_auto_(l_free) void *unwrapped = NULL;
-
-	if (dpp->state != DPP_STATE_CONFIGURING)
-		return;
-
-	dpp_attr_iter_init(&iter, body + 8, body_len - 8);
-
-	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
-		switch (type) {
-		case DPP_ATTR_WRAPPED_DATA:
-			wrapped = data;
-			wrapped_len = len;
-			break;
-		default:
-			/* Wrapped data should be only attribute */
-			return;
-		}
-	}
-
-	if (!wrapped)
-		return;
-
-	unwrapped = dpp_unwrap_attr(body + 2, wrapped - body - 6, wrapped, 0,
-					dpp->ke, dpp->key_len, wrapped,
-					wrapped_len, &wrapped_len);
-	if (!unwrapped) {
-		l_debug("Failed to unwrap DPP configuration result");
-		return;
-	}
-
-	dpp_attr_iter_init(&iter, unwrapped, wrapped_len);
-
-	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
-		switch (type) {
-		case DPP_ATTR_STATUS:
-			status = l_get_u8(data);
-			break;
-		case DPP_ATTR_ENROLLEE_NONCE:
-			e_nonce = data;
-			break;
-		default:
-			break;
-		}
-	}
-
-	if (status != DPP_STATUS_OK || !e_nonce)
-		l_debug("Enrollee signaled a failed configuration");
-	else
-		l_debug("Configuration success");
-
-	dpp_reset(dpp);
-}
-
-/*
- * The Authentication protocol has a consistent use of AD components, and this
- * use is defined in 6.3.1.4:
- *
- * "Invocations of AES-SIV in the DPP Authentication protocol that produce
- * ciphertext that is part of an additional AES-SIV invocation do not use AAD;
- * in other words, the number of AAD components is set to zero. All other
- * invocations of AES-SIV in the DPP Authentication protocol shall pass a vector
- * of AAD having two components of AAD in the following order: (1) the DPP
- * header, as defined in Table 30, from the OUI field (inclusive) to the DPP
- * Frame Type field (inclusive); and (2) all octets in a DPP Public Action frame
- * after the DPP Frame Type field up to and including the last octet of the last
- * attribute before the Wrapped Data attribute"
- *
- * In practice you see this as AD0 being some offset in the frame (offset to the
- * OUI). For outgoing packets this is 26 bytes offset since the header is built
- * manually. For incoming packets the offset is 2 bytes. The length is always
- * 6 bytes for AD0.
- *
- * The AD1 data is always the start of the attributes, and length is the number
- * of bytes from these attributes to wrapped data. e.g.
- *
- * ad1 = attrs
- * ad1_len = ptr - attrs
- */
-static void send_authenticate_response(struct dpp_sm *dpp)
-{
-	uint8_t frame[512];
-	uint8_t *ptr = frame;
-	uint8_t status = DPP_STATUS_OK;
-	uint8_t version = 2;
-	struct iovec iov;
-	uint8_t wrapped2_plaintext[dpp->key_len + 4];
-	uint8_t wrapped2[dpp->key_len + 16 + 8];
-	size_t wrapped2_len;
-	struct mmpdu_header *hdr = (struct mmpdu_header *)frame;
-
-	memset(frame, 0, sizeof(frame));
-
-	ptr += dpp_build_header(netdev_get_address(dpp->netdev),
-				dpp->peer_addr,
-				DPP_FRAME_AUTHENTICATION_RESPONSE, ptr);
-	ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &status, 1);
-	ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH,
-				dpp->own_boot_hash, 32);
-	if (dpp->mutual_auth)
-		ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_BOOT_KEY_HASH,
-				dpp->peer_boot_hash, 32);
-	ptr += dpp_append_point(ptr, DPP_ATTR_RESPONDER_PROTOCOL_KEY,
-				dpp->own_proto_public);
-	ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION, &version, 1);
-
-	/* Wrap up secondary data (R-Auth) */
-	wrapped2_len = dpp_append_attr(wrapped2_plaintext,
-					DPP_ATTR_RESPONDER_AUTH_TAG,
-					dpp->auth_tag, dpp->key_len);
-	/*
-	 * "Invocations of AES-SIV in the DPP Authentication protocol that
-	 * produce ciphertext that is part of an additional AES-SIV invocation
-	 * do not use AAD; in other words, the number of AAD components is set
-	 * to zero.""
-	 */
-	if (!aes_siv_encrypt(dpp->ke, dpp->key_len, wrapped2_plaintext,
-					dpp->key_len + 4, NULL, 0, wrapped2)) {
-		l_error("Failed to encrypt wrapped data");
-		return;
-	}
-
-	wrapped2_len += 16;
-
-	ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1,
-			sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr,
-			dpp->k2, dpp->key_len, 4,
-			DPP_ATTR_RESPONDER_NONCE, dpp->nonce_len, dpp->r_nonce,
-			DPP_ATTR_INITIATOR_NONCE, dpp->nonce_len, dpp->i_nonce,
-			DPP_ATTR_RESPONDER_CAPABILITIES, (size_t) 1, &dpp->role,
-			DPP_ATTR_WRAPPED_DATA, wrapped2_len, wrapped2);
-
-	iov.iov_base = frame;
-	iov.iov_len = ptr - frame;
-
-	dpp_send_frame(dpp, &iov, 1, dpp->current_freq);
-}
-
-static void authenticate_confirm(struct dpp_sm *dpp, const uint8_t *from,
-					const uint8_t *body, size_t body_len)
-{
-	struct dpp_attr_iter iter;
-	enum dpp_attribute_type type;
-	size_t len;
-	const uint8_t *data;
-	int status = -1;
-	const uint8_t *r_boot_hash = NULL;
-	const void *wrapped = NULL;
-	const uint8_t *i_auth = NULL;
-	size_t i_auth_len;
-	_auto_(l_free) uint8_t *unwrapped = NULL;
-	size_t wrapped_len = 0;
-	uint64_t i_auth_check[L_ECC_MAX_DIGITS];
-	const void *unwrap_key;
-	const void *ad0 = body + 2;
-	const void *ad1 = body + 8;
-	struct l_ecc_point *bi = NULL;
-
-	if (dpp->state != DPP_STATE_AUTHENTICATING)
-		return;
-
-	if (memcmp(from, dpp->peer_addr, 6))
-		return;
-
-	l_debug("authenticate confirm");
-
-	dpp_attr_iter_init(&iter, body + 8, body_len - 8);
-
-	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
-		switch (type) {
-		case DPP_ATTR_STATUS:
-			status = l_get_u8(data);
-			break;
-		case DPP_ATTR_RESPONDER_BOOT_KEY_HASH:
-			r_boot_hash = data;
-			/*
-			 * Spec requires this, but does not mention if anything
-			 * is to be done with it.
-			 */
-			break;
-		case DPP_ATTR_INITIATOR_BOOT_KEY_HASH:
-			/* No mutual authentication */
-			break;
-		case DPP_ATTR_WRAPPED_DATA:
-			wrapped = data;
-			wrapped_len = len;
-			break;
-		default:
-			break;
-		}
-	}
-
-	if (!r_boot_hash || !wrapped) {
-		l_debug("Attributes missing from authenticate confirm");
-		return;
-	}
-
-	/*
-	 * "The Responder obtains the DPP Authentication Confirm frame and
-	 * checks the value of the DPP Status field. If the value of the DPP
-	 * Status field is STATUS_NOT_COMPATIBLE or STATUS_AUTH_FAILURE, the
-	 * Responder unwraps the wrapped data portion of the frame using k2"
-	 */
-	if (status == DPP_STATUS_OK)
-		unwrap_key = dpp->ke;
-	else if (status == DPP_STATUS_NOT_COMPATIBLE ||
-				status == DPP_STATUS_AUTH_FAILURE)
-		unwrap_key = dpp->k2;
-	else
-		goto auth_confirm_failed;
-
-	unwrapped = dpp_unwrap_attr(ad0, 6, ad1, wrapped - 4 - ad1,
-			unwrap_key, dpp->key_len, wrapped, wrapped_len,
-			&wrapped_len);
-	if (!unwrapped)
-		goto auth_confirm_failed;
-
-	if (status != DPP_STATUS_OK) {
-		/*
-		 * "If unwrapping is successful, the Responder should generate
-		 * an alert indicating the reason for the protocol failure."
-		 */
-		l_debug("Authentication failed due to status %s",
-				status == DPP_STATUS_NOT_COMPATIBLE ?
-				"NOT_COMPATIBLE" : "AUTH_FAILURE");
-		goto auth_confirm_failed;
-	}
-
-	dpp_attr_iter_init(&iter, unwrapped, wrapped_len);
-
-	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
-		switch (type) {
-		case DPP_ATTR_INITIATOR_AUTH_TAG:
-			i_auth = data;
-			i_auth_len = len;
-			break;
-		case DPP_ATTR_RESPONDER_NONCE:
-			/* Only if error */
-			break;
-		default:
-			break;
-		}
-	}
-
-	if (!i_auth || i_auth_len != dpp->key_len) {
-		l_debug("I-Auth missing from wrapped data");
-		goto auth_confirm_failed;
-	}
-
-	if (dpp->mutual_auth)
-		bi = dpp->peer_boot_public;
-
-	dpp_derive_i_auth(dpp->r_nonce, dpp->i_nonce, dpp->nonce_len,
-				dpp->own_proto_public, dpp->peer_proto_public,
-				dpp->boot_public, bi, i_auth_check);
-
-	if (memcmp(i_auth, i_auth_check, i_auth_len)) {
-		l_error("I-Auth did not verify");
-		goto auth_confirm_failed;
-	}
-
-	l_debug("Authentication successful");
-
-	dpp_reset_protocol_timer(dpp, DPP_AUTH_PROTO_TIMEOUT);
-
-	if (dpp->role == DPP_CAPABILITY_ENROLLEE)
-		dpp_configuration_start(dpp, from);
-
-	return;
-
-auth_confirm_failed:
-	dpp->state = DPP_STATE_PRESENCE;
-	dpp_free_auth_data(dpp);
-}
-
-static void dpp_auth_request_failed(struct dpp_sm *dpp,
-					enum dpp_status status,
-					void *k1)
-{
-	uint8_t frame[128];
-	uint8_t *ptr = frame;
-	uint8_t version = 2;
-	uint8_t s = status;
-	struct iovec iov;
-	struct mmpdu_header *hdr = (struct mmpdu_header *)frame;
-
-	memset(frame, 0, sizeof(frame));
-
-	ptr += dpp_build_header(netdev_get_address(dpp->netdev),
-				dpp->peer_addr,
-				DPP_FRAME_AUTHENTICATION_RESPONSE, ptr);
-	ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &s, 1);
-	ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH,
-				dpp->own_boot_hash, 32);
-
-	ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION, &version, 1);
-
-	ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1,
-			sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr,
-			k1, dpp->key_len, 2,
-			DPP_ATTR_INITIATOR_NONCE, dpp->nonce_len, dpp->i_nonce,
-			DPP_ATTR_RESPONDER_CAPABILITIES,
-			(size_t) 1, &dpp->role);
-
-	iov.iov_base = frame;
-	iov.iov_len = ptr - frame;
-
-	dpp_send_frame(dpp, &iov, 1, dpp->current_freq);
-}
-
-static bool dpp_check_roles(struct dpp_sm *dpp, uint8_t peer_capa)
-{
-	if (dpp->role == DPP_CAPABILITY_ENROLLEE &&
-			!(peer_capa & DPP_CAPABILITY_CONFIGURATOR))
-		return false;
-	else if (dpp->role == DPP_CAPABILITY_CONFIGURATOR &&
-			!(peer_capa & DPP_CAPABILITY_ENROLLEE))
-		return false;
-
-	return true;
-}
-
-static void dpp_presence_announce(struct dpp_sm *dpp)
-{
-	struct netdev *netdev = dpp->netdev;
-	uint8_t hdr[32];
-	uint8_t attrs[32 + 4];
-	uint8_t hash[32];
-	uint8_t *ptr = attrs;
-	const uint8_t *addr = netdev_get_address(netdev);
-	struct iovec iov[2];
-
-	iov[0].iov_len = dpp_build_header(addr, broadcast,
-					DPP_FRAME_PRESENCE_ANNOUNCEMENT, hdr);
-	iov[0].iov_base = hdr;
-
-	dpp_hash(L_CHECKSUM_SHA256, hash, 2, "chirp", strlen("chirp"),
-			dpp->own_asn1, dpp->own_asn1_len);
-
-	ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH, hash, 32);
-
-	iov[1].iov_base = attrs;
-	iov[1].iov_len = ptr - attrs;
-
-	l_debug("Sending presence announcement on frequency %u and waiting %u",
-		dpp->current_freq, dpp->dwell);
-
-	dpp_send_frame(dpp, iov, 2, dpp->current_freq);
-}
-
-static bool dpp_send_authenticate_request(struct dpp_sm *dpp)
-{
-	uint8_t frame[256];
-	uint8_t *ptr = frame;
-	uint8_t version = 2;
-	struct iovec iov;
-	struct station *station = station_find(netdev_get_ifindex(dpp->netdev));
-	struct scan_bss *bss = station_get_connected_bss(station);
-	struct mmpdu_header *hdr = (struct mmpdu_header *)frame;
-
-	memset(frame, 0, sizeof(frame));
-
-	/* Got disconnected by the time the peer was discovered */
-	if (dpp->role == DPP_CAPABILITY_CONFIGURATOR && !bss) {
-		dpp_reset(dpp);
-		return false;
-	}
-
-	ptr += dpp_build_header(netdev_get_address(dpp->netdev),
-				dpp->peer_addr,
-				DPP_FRAME_AUTHENTICATION_REQUEST, ptr);
-	ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH,
-				dpp->peer_boot_hash, 32);
-	ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_BOOT_KEY_HASH,
-				dpp->own_boot_hash, 32);
-	ptr += dpp_append_point(ptr, DPP_ATTR_INITIATOR_PROTOCOL_KEY,
-				dpp->own_proto_public);
-	ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION, &version, 1);
-
-	if (dpp->role == DPP_CAPABILITY_CONFIGURATOR &&
-					dpp->current_freq != bss->frequency) {
-		uint8_t pair[2] = { 81,
-				band_freq_to_channel(bss->frequency, NULL) };
-
-		ptr += dpp_append_attr(ptr, DPP_ATTR_CHANNEL, pair, 2);
-	}
-
-	ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1,
-			sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr,
-			dpp->k1, dpp->key_len, 2,
-			DPP_ATTR_INITIATOR_NONCE, dpp->nonce_len, dpp->i_nonce,
-			DPP_ATTR_INITIATOR_CAPABILITIES,
-			(size_t) 1, &dpp->role);
-
-	iov.iov_base = frame;
-	iov.iov_len = ptr - frame;
-
-	dpp_send_frame(dpp, &iov, 1, dpp->current_freq);
-
-	return true;
-}
-
-static void dpp_send_pkex_exchange_request(struct dpp_sm *dpp)
-{
-	uint8_t hdr[32];
-	uint8_t attrs[256];
-	uint8_t *ptr = attrs;
-	uint16_t group;
-	struct iovec iov[2];
-	const uint8_t *own_mac = netdev_get_address(dpp->netdev);
-
-	l_put_le16(l_ecc_curve_get_ike_group(dpp->curve), &group);
-
-	iov[0].iov_len = dpp_build_header(own_mac, broadcast,
-				DPP_FRAME_PKEX_VERSION1_XCHG_REQUEST, hdr);
-	iov[0].iov_base = hdr;
-
-	ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION,
-				&dpp->pkex_version, 1);
-	ptr += dpp_append_attr(ptr, DPP_ATTR_FINITE_CYCLIC_GROUP,
-				&group, 2);
-
-	if (dpp->pkex_id)
-		ptr += dpp_append_attr(ptr, DPP_ATTR_CODE_IDENTIFIER,
-					dpp->pkex_id, strlen(dpp->pkex_id));
-
-	ptr += dpp_append_point(ptr, DPP_ATTR_ENCRYPTED_KEY, dpp->pkex_m);
-
-	iov[1].iov_base = attrs;
-	iov[1].iov_len = ptr - attrs;
-
-	dpp_send_frame(dpp, iov, 2, dpp->current_freq);
-}
-
-static void dpp_send_commit_reveal_request(struct dpp_sm *dpp)
-{
-	struct iovec iov;
-	uint8_t frame[512];
-	uint8_t *ptr = frame;
-	uint8_t a_pub[L_ECC_POINT_MAX_BYTES];
-	ssize_t a_len;
-	struct mmpdu_header *hdr = (struct mmpdu_header *)frame;
-
-	memset(frame, 0, sizeof(frame));
-
-	a_len = l_ecc_point_get_data(dpp->boot_public, a_pub, sizeof(a_pub));
-
-	ptr += dpp_build_header(netdev_get_address(dpp->netdev),
-					dpp->peer_addr,
-					DPP_FRAME_PKEX_COMMIT_REVEAL_REQUEST,
-					ptr);
-	ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1,
-			sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr,
-			dpp->z, dpp->z_len, 2,
-			DPP_ATTR_BOOTSTRAPPING_KEY, a_len, a_pub,
-			DPP_ATTR_INITIATOR_AUTH_TAG, dpp->u_len, dpp->u);
-
-	iov.iov_base = frame;
-	iov.iov_len = ptr - frame;
-
-	dpp_send_frame(dpp, &iov, 1, dpp->current_freq);
-}
-
-static void dpp_roc_started(void *user_data)
-{
-	struct dpp_sm *dpp = user_data;
-
-	/*
-	 * - If a configurator, nothing to do but wait for a request
-	 *   (unless multicast frame registration is unsupported in which case
-	 *   send an authenticate request now)
-	 * - If in the presence state continue sending announcements.
-	 * - If authenticating, and this is a result of a channel switch send
-	 *   the authenticate response now.
-	 */
-
-	dpp->roc_started = true;
-
-	/*
-	 * The retry timer indicates a frame was not acked in which case we
-	 * should not change any state or send any frames until that expires.
-	 */
-	if (dpp->retry_timeout)
-		return;
-
-	if (dpp->frame_pending) {
-		dpp_frame_retry(dpp);
-		return;
-	}
-
-	switch (dpp->state) {
-	case DPP_STATE_PRESENCE:
-		if (dpp->role == DPP_CAPABILITY_CONFIGURATOR)
-			return;
-
-		if (dpp->pending) {
-			struct l_dbus_message *reply =
-				l_dbus_message_new_method_return(dpp->pending);
-
-			l_dbus_message_set_arguments(reply, "s", dpp->uri);
-
-			dbus_pending_reply(&dpp->pending, reply);
-		}
-
-		dpp_presence_announce(dpp);
-		break;
-	case DPP_STATE_AUTHENTICATING:
-		/*
-		 * No multicast frame registration support, jump right into
-		 * sending auth frames. This diverges from the 2.0 spec, but in
-		 * reality the the main path nearly all drivers will hit.
-		 */
-		if (dpp->role == DPP_CAPABILITY_CONFIGURATOR) {
-			if (dpp->mcast_support)
-				return;
-
-			dpp_send_authenticate_request(dpp);
-			return;
-		}
-
-		if (dpp->new_freq) {
-			dpp->current_freq = dpp->new_freq;
-			dpp->new_freq = 0;
-			send_authenticate_response(dpp);
-		}
-
-		break;
-	case DPP_STATE_PKEX_EXCHANGE:
-		if (dpp->role == DPP_CAPABILITY_ENROLLEE)
-			dpp_send_pkex_exchange_request(dpp);
-
-		break;
-	case DPP_STATE_PKEX_COMMIT_REVEAL:
-		if (dpp->role == DPP_CAPABILITY_ENROLLEE)
-			dpp_send_commit_reveal_request(dpp);
-
-		break;
-	default:
-		break;
-	}
-}
-
-static void dpp_start_offchannel(struct dpp_sm *dpp, uint32_t freq);
-
-static void dpp_offchannel_timeout(int error, void *user_data)
-{
-	struct dpp_sm *dpp = user_data;
-
-	dpp->offchannel_id = 0;
-	dpp->roc_started = false;
-
-	/*
-	 * If cancelled this is likely due to netdev going down or from Stop().
-	 * Otherwise there was some other problem which is probably not
-	 * recoverable.
-	 */
-	if (error == -ECANCELED)
-		return;
-	else if (error == -EIO)
-		goto next_roc;
-	else if (error < 0)
-		goto protocol_failed;
-
-	switch (dpp->state) {
-	case DPP_STATE_PKEX_EXCHANGE:
-		if (dpp->role != DPP_CAPABILITY_CONFIGURATOR || !dpp->agent)
-			break;
-
-		/*
-		 * We have a pending agent request but it did not arrive in
-		 * time, we cant assume the enrollee will be waiting around
-		 * for our response so cancel the request and continue waiting
-		 * for another request
-		 */
-		if (dpp->agent->pending_id) {
-			dpp_free_pending_pkex_data(dpp);
-			dpp_agent_cancel(dpp);
-		}
-		/* Fall through */
-	case DPP_STATE_PRESENCE:
-		break;
-	case DPP_STATE_NOTHING:
-		/* Protocol already terminated */
-		return;
-	case DPP_STATE_AUTHENTICATING:
-	case DPP_STATE_CONFIGURING:
-	case DPP_STATE_PKEX_COMMIT_REVEAL:
-		goto next_roc;
-	}
-
-	dpp->freqs_idx++;
-
-	if (dpp->freqs_idx >= dpp->freqs_len) {
-		l_debug("Max retries offchannel");
-		dpp->freqs_idx = 0;
-	}
-
-	dpp->current_freq = dpp->freqs[dpp->freqs_idx];
-
-	l_debug("Offchannel timeout, moving to next frequency %u, duration %u",
-			dpp->current_freq, dpp->dwell);
-
-next_roc:
-	dpp_start_offchannel(dpp, dpp->current_freq);
-
-	return;
-
-protocol_failed:
-	dpp_reset(dpp);
-}
-
-static void dpp_start_offchannel(struct dpp_sm *dpp, uint32_t freq)
-{
-	/*
-	 * This needs to be handled carefully for a few reasons:
-	 *
-	 * First, the next offchannel operation needs to be started prior to
-	 * canceling an existing one. This is so the offchannel work can
-	 * continue uninterrupted without any other work items starting in
-	 * between canceling and starting the next (e.g. if a scan request is
-	 * sitting in the queue).
-	 *
-	 * Second, dpp_offchannel_timeout resets dpp->offchannel_id to zero
-	 * which is why the new ID is saved and only set to dpp->offchannel_id
-	 * once the previous offchannel work is cancelled (i.e. destroy() has
-	 * been called).
-	 */
-	uint32_t id = offchannel_start(netdev_get_wdev_id(dpp->netdev),
-				WIPHY_WORK_PRIORITY_OFFCHANNEL,
-				freq, dpp->dwell, dpp_roc_started,
-				dpp, dpp_offchannel_timeout);
-
-	if (dpp->offchannel_id)
-		offchannel_cancel(dpp->wdev_id, dpp->offchannel_id);
-
-	dpp->offchannel_id = id;
-}
-
-static void authenticate_request(struct dpp_sm *dpp, const uint8_t *from,
-					const uint8_t *body, size_t body_len)
-{
-	struct dpp_attr_iter iter;
-	enum dpp_attribute_type type;
-	size_t len;
-	const uint8_t *data;
-	const uint8_t *r_boot = NULL;
-	const uint8_t *i_boot = NULL;
-	const uint8_t *i_proto = NULL;
-	const void *wrapped = NULL;
-	const uint8_t *i_nonce = NULL;
-	uint8_t i_capa = 0;
-	size_t r_boot_len = 0, i_proto_len = 0, wrapped_len = 0;
-	size_t i_nonce_len = 0;
-	_auto_(l_free) uint8_t *unwrapped = NULL;
-	_auto_(l_ecc_scalar_free) struct l_ecc_scalar *m = NULL;
-	_auto_(l_ecc_scalar_free) struct l_ecc_scalar *n = NULL;
-	_auto_(l_ecc_point_free) struct l_ecc_point *l = NULL;
-	struct l_ecc_point *bi = NULL;
-	uint64_t k1[L_ECC_MAX_DIGITS];
-	const void *ad0 = body + 2;
-	const void *ad1 = body + 8;
-	uint32_t freq;
-
-	if (util_is_broadcast_address(from))
-		return;
-
-	if (dpp->state != DPP_STATE_PRESENCE &&
-				dpp->state != DPP_STATE_AUTHENTICATING)
-		return;
-
-	l_debug("authenticate request");
-
-	dpp_attr_iter_init(&iter, body + 8, body_len - 8);
-
-	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
-		switch (type) {
-		case DPP_ATTR_INITIATOR_BOOT_KEY_HASH:
-			i_boot = data;
-			/*
-			 * This attribute is required by the spec, but only
-			 * used for mutual authentication.
-			 */
-			break;
-		case DPP_ATTR_RESPONDER_BOOT_KEY_HASH:
-			r_boot = data;
-			r_boot_len = len;
-			break;
-		case DPP_ATTR_INITIATOR_PROTOCOL_KEY:
-			i_proto = data;
-			i_proto_len = len;
-			break;
-		case DPP_ATTR_WRAPPED_DATA:
-			/* I-Nonce/I-Capabilities part of wrapped data */
-			wrapped = data;
-			wrapped_len = len;
-			break;
-
-		/* Optional attributes */
-		case DPP_ATTR_PROTOCOL_VERSION:
-			if (l_get_u8(data) != 2) {
-				l_debug("Protocol version did not match");
-				return;
-			}
-
-			break;
-
-		case DPP_ATTR_CHANNEL:
-			if (len != 2)
-				return;
-
-			freq = oci_to_frequency(l_get_u8(data),
-						l_get_u8(data + 1));
-
-			if (freq == dpp->current_freq)
-				break;
-
-			/*
-			 * Configurators are already connected to a network, so
-			 * to preserve wireless performance the enrollee will
-			 * be required to be on this channel, not a channel it
-			 * requests.
-			 */
-			if (dpp->role == DPP_CAPABILITY_CONFIGURATOR)
-				return;
-
-			/*
-			 * Otherwise, as an enrollee, we can jump to whatever
-			 * channel the configurator requests
-			 */
-			dpp->new_freq = freq;
-
-			l_debug("Configurator requested a new frequency %u",
-					dpp->new_freq);
-
-			dpp_start_offchannel(dpp, dpp->new_freq);
-
-			break;
-		default:
-			break;
-		}
-	}
-
-	if (!r_boot || !i_boot || !i_proto || !wrapped)
-		goto auth_request_failed;
-
-	if (r_boot_len != 32 || memcmp(dpp->own_boot_hash,
-					r_boot, r_boot_len)) {
-		l_debug("Responder boot key hash failed to verify");
-		goto auth_request_failed;
-	}
-
-	dpp->peer_proto_public = l_ecc_point_from_data(dpp->curve,
-						L_ECC_POINT_TYPE_FULL,
-						i_proto, i_proto_len);
-	if (!dpp->peer_proto_public) {
-		l_debug("Initiators protocol key invalid");
-		goto auth_request_failed;
-	}
-
-	m = dpp_derive_k1(dpp->peer_proto_public, dpp->boot_private, k1);
-	if (!m)
-		goto auth_request_failed;
-
-	unwrapped = dpp_unwrap_attr(ad0, 6, ad1, wrapped - 4 - ad1,
-			k1, dpp->key_len, wrapped, wrapped_len, &wrapped_len);
-	if (!unwrapped)
-		goto auth_request_failed;
-
-	dpp_attr_iter_init(&iter, unwrapped, wrapped_len);
-
-	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
-		switch (type) {
-		case DPP_ATTR_INITIATOR_NONCE:
-			i_nonce = data;
-			i_nonce_len = len;
-			break;
-		case DPP_ATTR_INITIATOR_CAPABILITIES:
-			/*
-			 * "If the Responder is not capable of supporting the
-			 * role indicated by the Initiator, it shall respond
-			 * with a DPP Authentication Response frame indicating
-			 * failure by adding the DPP Status field set to
-			 * STATUS_NOT_COMPATIBLE"
-			 */
-			i_capa = l_get_u8(data);
-
-			if (!dpp_check_roles(dpp, i_capa)) {
-				l_debug("Peer does not support required role");
-				dpp_auth_request_failed(dpp,
-						DPP_STATUS_NOT_COMPATIBLE, k1);
-				goto auth_request_failed;
-			}
-
-			break;
-		default:
-			break;
-		}
-	}
-
-	if (i_nonce_len != dpp->nonce_len) {
-		l_debug("I-Nonce has unexpected length %zu", i_nonce_len);
-		goto auth_request_failed;
-	}
-
-	memcpy(dpp->i_nonce, i_nonce, i_nonce_len);
-
-	if (dpp->mutual_auth) {
-		l = dpp_derive_lr(dpp->boot_private, dpp->proto_private,
-					dpp->peer_boot_public);
-		bi = dpp->peer_boot_public;
-	}
-
-	/* Derive keys k2, ke, and R-Auth for authentication response */
-
-	n = dpp_derive_k2(dpp->peer_proto_public, dpp->proto_private, dpp->k2);
-	if (!n)
-		goto auth_request_failed;
-
-	l_getrandom(dpp->r_nonce, dpp->nonce_len);
-
-	if (!dpp_derive_ke(dpp->i_nonce, dpp->r_nonce, m, n, l, dpp->ke))
-		goto auth_request_failed;
-
-	if (!dpp_derive_r_auth(dpp->i_nonce, dpp->r_nonce, dpp->nonce_len,
-				dpp->peer_proto_public, dpp->own_proto_public,
-				bi, dpp->boot_public, dpp->auth_tag))
-		goto auth_request_failed;
-
-	memcpy(dpp->peer_addr, from, 6);
-
-	dpp->state = DPP_STATE_AUTHENTICATING;
-	dpp_reset_protocol_timer(dpp, DPP_AUTH_PROTO_TIMEOUT);
-
-	/* Don't send if the frequency is changing */
-	if (!dpp->new_freq)
-		send_authenticate_response(dpp);
-
-	return;
-
-auth_request_failed:
-	dpp->state = DPP_STATE_PRESENCE;
-	dpp_free_auth_data(dpp);
-}
-
-static void dpp_send_authenticate_confirm(struct dpp_sm *dpp)
-{
-	struct iovec iov;
-	uint8_t frame[256];
-	uint8_t *ptr = frame;
-	uint8_t zero = 0;
-	struct mmpdu_header *hdr = (struct mmpdu_header *)frame;
-
-	memset(frame, 0, sizeof(frame));
-
-	ptr += dpp_build_header(netdev_get_address(dpp->netdev),
-					dpp->peer_addr,
-					DPP_FRAME_AUTHENTICATION_CONFIRM, ptr);
-	ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &zero, 1);
-	ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH,
-					dpp->peer_boot_hash, 32);
-	if (dpp->mutual_auth)
-		ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_BOOT_KEY_HASH,
-					dpp->own_boot_hash, 32);
-
-	ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1,
-			sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr,
-			dpp->ke, dpp->key_len, 1,
-			DPP_ATTR_INITIATOR_AUTH_TAG, dpp->key_len,
-			dpp->auth_tag);
-
-	iov.iov_base = frame;
-	iov.iov_len = ptr - frame;
-
-	dpp_send_frame(dpp, &iov, 1, dpp->current_freq);
-}
-
-static void authenticate_response(struct dpp_sm *dpp, const uint8_t *from,
-					const uint8_t *body, size_t body_len)
-{
-	struct dpp_attr_iter iter;
-	enum dpp_attribute_type type;
-	size_t len;
-	const uint8_t *data;
-	int status = -1;
-	const void *r_boot_hash = NULL;
-	const void *r_proto = NULL;
-	size_t r_proto_len = 0;
-	const void *wrapped = NULL;
-	size_t wrapped_len;
-	_auto_(l_free) uint8_t *unwrapped1 = NULL;
-	_auto_(l_free) uint8_t *unwrapped2 = NULL;
-	const void *r_nonce = NULL;
-	const void *i_nonce = NULL;
-	const void *r_auth = NULL;
-	_auto_(l_ecc_point_free) struct l_ecc_point *r_proto_key = NULL;
-	_auto_(l_ecc_scalar_free) struct l_ecc_scalar *n = NULL;
-	_auto_(l_ecc_point_free) struct l_ecc_point *l = NULL;
-	struct l_ecc_point *bi = NULL;
-	const void *ad0 = body + 2;
-	const void *ad1 = body + 8;
-	uint64_t r_auth_derived[L_ECC_MAX_DIGITS];
-
-	l_debug("Authenticate response");
-
-	if (dpp->state != DPP_STATE_AUTHENTICATING)
-		return;
-
-	if (!dpp->freqs)
-		return;
-
-	if (memcmp(from, dpp->peer_addr, 6))
-		return;
-
-	dpp_attr_iter_init(&iter, body + 8, body_len - 8);
-
-	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
-		switch (type) {
-		case DPP_ATTR_STATUS:
-			if (len != 1)
-				return;
-
-			status = l_get_u8(data);
-			break;
-		case DPP_ATTR_RESPONDER_BOOT_KEY_HASH:
-			r_boot_hash = data;
-			break;
-		case DPP_ATTR_RESPONDER_PROTOCOL_KEY:
-			r_proto = data;
-			r_proto_len = len;
-			break;
-		case DPP_ATTR_PROTOCOL_VERSION:
-			if (len != 1 || l_get_u8(data) != 2)
-				return;
-			break;
-		case DPP_ATTR_WRAPPED_DATA:
-			wrapped = data;
-			wrapped_len = len;
-			break;
-		default:
-			break;
-		}
-	}
-
-	if (status != DPP_STATUS_OK || !r_boot_hash || !r_proto || !wrapped) {
-		l_debug("Auth response bad status or missing attributes");
-		return;
-	}
-
-	r_proto_key = l_ecc_point_from_data(dpp->curve, L_ECC_POINT_TYPE_FULL,
-						r_proto, r_proto_len);
-	if (!r_proto_key) {
-		l_debug("Peers protocol key was invalid");
-		return;
-	}
-
-	n = dpp_derive_k2(r_proto_key, dpp->proto_private, dpp->k2);
-
-	unwrapped1 = dpp_unwrap_attr(ad0, 6, ad1, wrapped - 4 - ad1, dpp->k2,
-					dpp->key_len, wrapped, wrapped_len,
-					&wrapped_len);
-	if (!unwrapped1) {
-		l_debug("Failed to unwrap primary data");
-		return;
-	}
-
-	wrapped = NULL;
-
-	dpp_attr_iter_init(&iter, unwrapped1, wrapped_len);
-
-	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
-		switch (type) {
-		case DPP_ATTR_RESPONDER_NONCE:
-			if (len != dpp->nonce_len)
-				return;
-
-			r_nonce = data;
-			break;
-		case DPP_ATTR_INITIATOR_NONCE:
-			if (len != dpp->nonce_len)
-				return;
-
-			i_nonce = data;
-			break;
-		case DPP_ATTR_RESPONDER_CAPABILITIES:
-			break;
-		case DPP_ATTR_WRAPPED_DATA:
-			wrapped = data;
-			wrapped_len = len;
-			break;
-		default:
-			break;
-		}
-	}
-
-	if (!r_nonce || !i_nonce || !wrapped) {
-		l_debug("Wrapped data missing attributes");
-		return;
-	}
-
-	if (dpp->mutual_auth) {
-		l = dpp_derive_li(dpp->peer_boot_public, r_proto_key,
-					dpp->boot_private);
-		bi = dpp->boot_public;
-	}
-
-	if (!dpp_derive_ke(i_nonce, r_nonce, dpp->m, n, l, dpp->ke)) {
-		l_debug("Failed to derive ke");
-		return;
-	}
-
-	unwrapped2 = dpp_unwrap_attr(NULL, 0, NULL, 0, dpp->ke, dpp->key_len,
-					wrapped, wrapped_len, &wrapped_len);
-	if (!unwrapped2) {
-		l_debug("Failed to unwrap secondary data");
-		return;
-	}
-
-	dpp_attr_iter_init(&iter, unwrapped2, wrapped_len);
-
-	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
-		switch (type) {
-		case DPP_ATTR_RESPONDER_AUTH_TAG:
-			if (len != dpp->key_len)
-				return;
-
-			r_auth = data;
-			break;
-		default:
-			break;
-		}
-	}
-
-	if (!r_auth) {
-		l_debug("R-Auth was not in secondary wrapped data");
-		return;
-	}
-
-	if (!dpp_derive_r_auth(i_nonce, r_nonce, dpp->nonce_len,
-				dpp->own_proto_public, r_proto_key, bi,
-				dpp->peer_boot_public, r_auth_derived)) {
-		l_debug("Failed to derive r_auth");
-		return;
-	}
-
-	if (memcmp(r_auth, r_auth_derived, dpp->key_len)) {
-		l_debug("R-Auth did not verify");
-		return;
-	}
-
-	if (!dpp_derive_i_auth(r_nonce, i_nonce, dpp->nonce_len,
-				r_proto_key, dpp->own_proto_public,
-				dpp->peer_boot_public, bi, dpp->auth_tag)) {
-		l_debug("Could not derive I-Auth");
-		return;
-	}
-
-	dpp->channel_switch = false;
-	dpp->current_freq = dpp->new_freq;
-
-	dpp_send_authenticate_confirm(dpp);
-
-	if (dpp->role == DPP_CAPABILITY_ENROLLEE)
-		dpp_configuration_start(dpp, from);
-
-}
-
-static void dpp_handle_presence_announcement(struct dpp_sm *dpp,
-						const uint8_t *from,
-						const uint8_t *body,
-						size_t body_len)
-{
-	struct dpp_attr_iter iter;
-	enum dpp_attribute_type type;
-	size_t len;
-	const uint8_t *data;
-	const void *r_boot = NULL;
-	size_t r_boot_len = 0;
-	uint8_t hash[32];
-
-	l_debug("Presence announcement "MAC, MAC_STR(from));
-
-	/* Must be a configurator, in an initiator role, in PRESENCE state */
-	if (dpp->state != DPP_STATE_PRESENCE)
-		return;
-
-	if (dpp->role != DPP_CAPABILITY_CONFIGURATOR)
-		return;
-
-	if (!dpp->freqs)
-		return;
-
-	/*
-	 * The URI may not have contained a MAC address, if this announcement
-	 * verifies set peer_addr then.
-	 */
-	if (!l_memeqzero(dpp->peer_addr, 6) &&
-				memcmp(from, dpp->peer_addr, 6)) {
-		l_debug("Unexpected source "MAC" expected "MAC, MAC_STR(from),
-						MAC_STR(dpp->peer_addr));
-		return;
-	}
-
-	dpp_attr_iter_init(&iter, body + 8, body_len - 8);
-
-	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
-		switch (type) {
-		case DPP_ATTR_RESPONDER_BOOT_KEY_HASH:
-			r_boot = data;
-			r_boot_len = len;
-			break;
-		default:
-			break;
-		}
-	}
-
-	if (!r_boot || r_boot_len != 32) {
-		l_debug("No responder boot hash");
-		return;
-	}
-
-	/* Hash what we have for the peer and check its our enrollee */
-	dpp_hash(L_CHECKSUM_SHA256, hash, 2, "chirp", strlen("chirp"),
-			dpp->peer_asn1, dpp->peer_asn1_len);
-
-	if (memcmp(hash, r_boot, sizeof(hash))) {
-		l_debug("Peers boot hash did not match");
-		return;
-	}
-
-	/*
-	 * This is the peer we expected, save away the address and derive the
-	 * initial keys.
-	 */
-	memcpy(dpp->peer_addr, from, 6);
-
-	dpp->state = DPP_STATE_AUTHENTICATING;
-
-	if (!dpp_send_authenticate_request(dpp))
-		return;
-
-	/*
-	 * Requested the peer to move to another channel for the remainder of
-	 * the protocol. IWD's current logic prohibits a configurator from
-	 * running while not connected, so we can assume here that the new
-	 * frequency is the same of the connected BSS. Wait until an ACK is
-	 * received for the auth request then cancel the offchannel request.
-	 */
-	if (dpp->current_freq != dpp->new_freq)
-		dpp->channel_switch = true;
-}
-
-static void dpp_pkex_bad_group(struct dpp_sm *dpp, uint16_t group)
-{
-	uint16_t own_group = l_ecc_curve_get_ike_group(dpp->curve);
-
-	/*
-	 * TODO: The spec allows group negotiation, but it is not yet
-	 *       implemented.
-	 */
-	if (!group)
-		return;
-	/*
-	 * Section 5.6.2
-	 * "If the Responder's offered group offers less security
-	 * than the Initiator's offered group, then the Initiator should
-	 * ignore this message"
-	 */
-	if (group < own_group) {
-		l_debug("Offered group %u is less secure, ignoring",
-				group);
-		return;
-	}
-	/*
-	 * Section 5.6.2
-	 * "If the Responder's offered group offers equivalent or better
-	 * security than the Initiator's offered group, then the
-	 * Initiator may choose to abort its original request and try
-	 * another exchange using the group offered by the Responder"
-	 */
-	if (group >= own_group) {
-		l_debug("Offered group %u is the same or more secure, "
-			" but group negotiation is not supported", group);
-		return;
-	}
-}
-
-static void dpp_pkex_bad_code(struct dpp_sm *dpp)
-{
-	_auto_(l_ecc_point_free) struct l_ecc_point *qr = NULL;
-
-	qr = dpp_derive_qr(dpp->curve, dpp->pkex_key, dpp->pkex_id,
-				netdev_get_address(dpp->netdev));
-	if (!qr || l_ecc_point_is_infinity(qr)) {
-		l_debug("Qr computed to zero, new code should be provisioned");
-		return;
-	}
-
-	l_debug("Qr computed successfully but responder indicated otherwise");
-}
-
-static void dpp_handle_pkex_exchange_response(struct dpp_sm *dpp,
-					const uint8_t *from,
-					const uint8_t *body, size_t body_len)
-{
-	struct dpp_attr_iter iter;
-	enum dpp_attribute_type type;
-	size_t len;
-	const uint8_t *data;
-	const uint8_t *status = NULL;
-	uint8_t version = 0;
-	const void *identifier = NULL;
-	size_t identifier_len = 0;
-	const void *key = NULL;
-	size_t key_len = 0;
-	uint16_t group = 0;
-	_auto_(l_ecc_point_free) struct l_ecc_point *n = NULL;
-	_auto_(l_ecc_point_free) struct l_ecc_point *j = NULL;
-	_auto_(l_ecc_point_free) struct l_ecc_point *qr = NULL;
-	_auto_(l_ecc_point_free) struct l_ecc_point *k = NULL;
-	const uint8_t *own_addr = netdev_get_address(dpp->netdev);
-
-	l_debug("PKEX response "MAC, MAC_STR(from));
-
-	if (dpp->state != DPP_STATE_PKEX_EXCHANGE)
-		return;
-
-	if (dpp->role != DPP_CAPABILITY_ENROLLEE)
-		return;
-
-	memcpy(dpp->peer_addr, from, 6);
-
-	dpp_attr_iter_init(&iter, body + 8, body_len - 8);
-
-	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
-		switch (type) {
-		case DPP_ATTR_STATUS:
-			if (len != 1)
-				return;
-
-			status = data;
-			break;
-		case DPP_ATTR_PROTOCOL_VERSION:
-			if (len != 1)
-				return;
-
-			version = l_get_u8(data);
-			break;
-		case DPP_ATTR_CODE_IDENTIFIER:
-			identifier = data;
-			identifier_len = len;
-			break;
-		case DPP_ATTR_ENCRYPTED_KEY:
-			if (len != dpp->key_len * 2)
-				return;
-
-			key = data;
-			key_len = len;
-			break;
-		case DPP_ATTR_FINITE_CYCLIC_GROUP:
-			if (len != 2)
-				return;
-
-			group = l_get_le16(data);
-			break;
-		default:
-			break;
-		}
-	}
-
-	if (!status) {
-		l_debug("No status attribute, ignoring");
-		return;
-	}
-
-	if (!key) {
-		l_debug("No encrypted key, ignoring");
-		return;
-	}
-
-	if (*status != DPP_STATUS_OK)
-		goto handle_status;
-
-	if (dpp->pkex_id) {
-		if (!identifier || identifier_len != strlen(dpp->pkex_id) ||
-					memcmp(dpp->pkex_id, identifier,
-						identifier_len)) {
-			l_debug("mismatch identifier, ignoring");
-			return;
-		}
-	}
-
-	if (version && version != dpp->pkex_version) {
-		l_debug("PKEX version does not match, igoring");
-		return;
-	}
-
-	n = l_ecc_point_from_data(dpp->curve, L_ECC_POINT_TYPE_FULL,
-					key, key_len);
-	if (!n) {
-		l_debug("failed to parse peers encrypted key");
-		goto failed;
-	}
-
-	qr = dpp_derive_qr(dpp->curve, dpp->pkex_key, dpp->pkex_id,
-				dpp->peer_addr);
-	if (!qr)
-		goto failed;
-
-	dpp->y_or_x = l_ecc_point_new(dpp->curve);
-
-	/* Y' = N - Qr */
-	l_ecc_point_inverse(qr);
-	l_ecc_point_add(dpp->y_or_x, n, qr);
-
-	/*
-	 * "The resulting ephemeral key, denoted Y’, is then checked whether it
-	 * is the point-at-infinity. If it is not valid, the protocol ends
-	 * unsuccessfully"
-	 */
-	if (l_ecc_point_is_infinity(dpp->y_or_x)) {
-		l_debug("Y' computed to infinity, failing");
-		goto failed;
-	}
-
-	k = l_ecc_point_new(dpp->curve);
-
-	/* K = Y' * x */
-	l_ecc_point_multiply(k, dpp->pkex_private, dpp->y_or_x);
-
-	dpp_derive_z(own_addr, dpp->peer_addr, n, dpp->pkex_m, k,
-				dpp->pkex_key, dpp->pkex_id,
-				dpp->z, &dpp->z_len);
-
-	/* J = a * Y' */
-	j = l_ecc_point_new(dpp->curve);
-
-	l_ecc_point_multiply(j, dpp->boot_private, dpp->y_or_x);
-
-	if (!dpp_derive_u(j, own_addr, dpp->boot_public, dpp->y_or_x,
-				dpp->pkex_public, dpp->u, &dpp->u_len)) {
-		l_debug("failed to compute u");
-		goto failed;
-	}
-
-	/*
-	 * Now that a response was successfully received, start another
-	 * offchannel with more time for the remainder of the protocol. After
-	 * PKEX, authentication will begin which handles the protocol timeout.
-	 * If the remainder of PKEX (commit-reveal exchange) cannot complete in
-	 * this time it will fail.
-	 */
-	dpp->dwell = (dpp->max_roc < 2000) ? dpp->max_roc : 2000;
-	dpp->state = DPP_STATE_PKEX_COMMIT_REVEAL;
-
-	dpp_property_changed_notify(dpp);
-
-	dpp_start_offchannel(dpp, dpp->current_freq);
-
-	return;
-
-handle_status:
-	switch (*status) {
-	case DPP_STATUS_BAD_GROUP:
-		dpp_pkex_bad_group(dpp, group);
-		break;
-	case DPP_STATUS_BAD_CODE:
-		dpp_pkex_bad_code(dpp);
-		break;
-	default:
-		l_debug("Unhandled status %u", *status);
-		break;
-	}
-
-failed:
-	dpp_reset(dpp);
-}
-
-static bool dpp_pkex_start_authentication(struct dpp_sm *dpp)
-{
-	dpp->uri = dpp_generate_uri(dpp->own_asn1, dpp->own_asn1_len, 2,
-					netdev_get_address(dpp->netdev),
-					&dpp->current_freq, 1, NULL, NULL);
-
-	l_ecdh_generate_key_pair(dpp->curve, &dpp->proto_private,
-					&dpp->own_proto_public);
-
-	l_getrandom(dpp->i_nonce, dpp->nonce_len);
-
-	dpp->peer_asn1 = dpp_point_to_asn1(dpp->peer_boot_public,
-						&dpp->peer_asn1_len);
-
-	dpp->m = dpp_derive_k1(dpp->peer_boot_public, dpp->proto_private,
-				dpp->k1);
-
-	dpp_hash(L_CHECKSUM_SHA256, dpp->peer_boot_hash, 1, dpp->peer_asn1,
-			dpp->peer_asn1_len);
-
-	dpp->state = DPP_STATE_AUTHENTICATING;
-	dpp->mutual_auth = true;
-
-	dpp_property_changed_notify(dpp);
-
-	if (dpp->role == DPP_CAPABILITY_ENROLLEE) {
-		dpp->new_freq = dpp->current_freq;
-
-		return dpp_send_authenticate_request(dpp);
-	}
-
-	return true;
-}
-
-static void dpp_handle_pkex_commit_reveal_response(struct dpp_sm *dpp,
-					const uint8_t *from,
-					const uint8_t *body, size_t body_len)
-{
-	struct dpp_attr_iter iter;
-	enum dpp_attribute_type type;
-	size_t len;
-	const uint8_t *data;
-	const uint8_t *wrapped = NULL;
-	size_t wrapped_len = 0;
-	uint8_t one = 1;
-	_auto_(l_free) uint8_t *unwrapped = NULL;
-	size_t unwrapped_len = 0;
-	const uint8_t *boot_key = NULL;
-	size_t boot_key_len = 0;
-	const uint8_t *r_auth = NULL;
-	uint8_t v[L_ECC_SCALAR_MAX_BYTES];
-	size_t v_len;
-	_auto_(l_ecc_point_free) struct l_ecc_point *l = NULL;
-
-	l_debug("PKEX commit reveal "MAC, MAC_STR(from));
-
-	if (dpp->state != DPP_STATE_PKEX_COMMIT_REVEAL)
-		return;
-
-	if (dpp->role != DPP_CAPABILITY_ENROLLEE)
-		return;
-
-	/*
-	 * The URI may not have contained a MAC address, if this announcement
-	 * verifies set peer_addr then.
-	 */
-	if (memcmp(from, dpp->peer_addr, 6)) {
-		l_debug("Unexpected source "MAC" expected "MAC, MAC_STR(from),
-						MAC_STR(dpp->peer_addr));
-		return;
-	}
-
-	dpp_attr_iter_init(&iter, body + 8, body_len - 8);
-
-	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
-		switch (type) {
-		case DPP_ATTR_WRAPPED_DATA:
-			wrapped = data;
-			wrapped_len = len;
-			break;
-		default:
-			break;
-		}
-	}
-
-	if (!wrapped) {
-		l_debug("No wrapped data");
-		return;
-	}
-
-	unwrapped = dpp_unwrap_attr(body + 2, 6, &one, 1, dpp->z, dpp->z_len,
-					wrapped, wrapped_len, &unwrapped_len);
-	if (!unwrapped) {
-		l_debug("Failed to unwrap Reveal-Commit message");
-		return;
-	}
-
-	dpp_attr_iter_init(&iter, unwrapped, unwrapped_len);
-
-	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
-		switch (type) {
-		case DPP_ATTR_BOOTSTRAPPING_KEY:
-			if (len != dpp->key_len * 2)
-				return;
-
-			boot_key = data;
-			boot_key_len = len;
-			break;
-		case DPP_ATTR_RESPONDER_AUTH_TAG:
-			if (len != 32)
-				return;
-
-			r_auth = data;
-			break;
-		default:
-			break;
-		}
-	}
-
-	dpp->peer_boot_public = l_ecc_point_from_data(dpp->curve,
-							L_ECC_POINT_TYPE_FULL,
-							boot_key, boot_key_len);
-	if (!dpp->peer_boot_public) {
-		l_debug("Peer public bootstrapping key was invalid");
-		goto failed;
-	}
-
-	/* L = b * X' */
-	l = l_ecc_point_new(dpp->curve);
-
-	l_ecc_point_multiply(l, dpp->pkex_private, dpp->peer_boot_public);
-
-	if (!dpp_derive_v(l, dpp->peer_addr, dpp->peer_boot_public,
-				dpp->pkex_public, dpp->y_or_x, v, &v_len)) {
-		l_debug("Failed to derive v");
-		goto failed;
-	}
-
-	if (memcmp(v, r_auth, v_len)) {
-		l_debug("Bootstrapping data did not verify");
-		goto failed;
-	}
-
-	if (dpp_pkex_start_authentication(dpp))
-		return;
-
-failed:
-	dpp_reset(dpp);
-}
-
-static void dpp_send_bad_group(struct dpp_sm *dpp, const uint8_t *addr)
-{
-	uint8_t hdr[32];
-	uint8_t attrs[256];
-	uint8_t *ptr = attrs;
-	uint16_t group;
-	uint8_t status = DPP_STATUS_BAD_GROUP;
-	struct iovec iov[2];
-	const uint8_t *own_mac = netdev_get_address(dpp->netdev);
-
-	l_put_le16(l_ecc_curve_get_ike_group(dpp->curve), &group);
-
-	iov[0].iov_len = dpp_build_header(own_mac, addr,
-				DPP_FRAME_PKEX_XCHG_RESPONSE, hdr);
-	iov[0].iov_base = hdr;
-
-	ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &status, 1);
-	ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION,
-				&dpp->pkex_version, 1);
-	ptr += dpp_append_attr(ptr, DPP_ATTR_FINITE_CYCLIC_GROUP, &group, 2);
-
-	iov[1].iov_base = attrs;
-	iov[1].iov_len = ptr - attrs;
-
-	dpp_send_frame(dpp, iov, 2, dpp->current_freq);
-}
-
-static void dpp_send_bad_code(struct dpp_sm *dpp, const uint8_t *addr)
-{
-	uint8_t hdr[32];
-	uint8_t attrs[256];
-	uint8_t *ptr = attrs;
-	uint8_t status = DPP_STATUS_BAD_CODE;
-	struct iovec iov[2];
-	const uint8_t *own_mac = netdev_get_address(dpp->netdev);
-
-	iov[0].iov_len = dpp_build_header(own_mac, addr,
-				DPP_FRAME_PKEX_XCHG_RESPONSE, hdr);
-	iov[0].iov_base = hdr;
-
-	ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &status, 1);
-	ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION,
-				&dpp->pkex_version, 1);
-	if (dpp->pkex_id)
-		ptr += dpp_append_attr(ptr, DPP_ATTR_CODE_IDENTIFIER,
-					dpp->pkex_id, strlen(dpp->pkex_id));
-
-	iov[1].iov_base = attrs;
-	iov[1].iov_len = ptr - attrs;
-
-	dpp_send_frame(dpp, iov, 2, dpp->current_freq);
-}
-
-static void dpp_send_pkex_exchange_response(struct dpp_sm *dpp,
-						struct l_ecc_point *n)
-{
-	uint8_t hdr[32];
-	uint8_t attrs[256];
-	uint8_t *ptr = attrs;
-	uint16_t group;
-	uint8_t status = DPP_STATUS_OK;
-	struct iovec iov[2];
-	const uint8_t *own_mac = netdev_get_address(dpp->netdev);
-
-	l_put_le16(l_ecc_curve_get_ike_group(dpp->curve), &group);
-
-	iov[0].iov_len = dpp_build_header(own_mac, dpp->peer_addr,
-				DPP_FRAME_PKEX_XCHG_RESPONSE, hdr);
-	iov[0].iov_base = hdr;
-
-	ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &status, 1);
-
-	if (dpp->pkex_id)
-		ptr += dpp_append_attr(ptr, DPP_ATTR_CODE_IDENTIFIER,
-					dpp->pkex_id, strlen(dpp->pkex_id));
-
-	ptr += dpp_append_point(ptr, DPP_ATTR_ENCRYPTED_KEY, n);
-
-	iov[1].iov_base = attrs;
-	iov[1].iov_len = ptr - attrs;
-
-	dpp->state = DPP_STATE_PKEX_COMMIT_REVEAL;
-
-	dpp_property_changed_notify(dpp);
-
-	dpp_send_frame(dpp, iov, 2, dpp->current_freq);
-}
-
-static void dpp_process_pkex_exchange_request(struct dpp_sm *dpp,
-						struct l_ecc_point *m)
-{
-	_auto_(l_ecc_point_free) struct l_ecc_point *n = NULL;
-	_auto_(l_ecc_point_free) struct l_ecc_point *qr = NULL;
-	_auto_(l_ecc_point_free) struct l_ecc_point *qi = NULL;
-	_auto_(l_ecc_point_free) struct l_ecc_point *k = NULL;
-	const uint8_t *own_addr = netdev_get_address(dpp->netdev);
-
-	/* Qi = H(MAC-Initiator | [identifier | ] code) * Pi */
-	qi = dpp_derive_qi(dpp->curve, dpp->pkex_key, dpp->pkex_id,
-				dpp->peer_addr);
-	if (!qi) {
-		l_debug("could not derive Qi");
-		return;
-	}
-
-	/* X' = M - Qi */
-	dpp->y_or_x = l_ecc_point_new(dpp->curve);
-
-	l_ecc_point_inverse(qi);
-	l_ecc_point_add(dpp->y_or_x, m, qi);
-
-	/*
-	 * "The resulting ephemeral key, denoted X’, is checked whether it is
-	 * the point-at-infinity. If it is not valid, the protocol silently
-	 * fails"
-	 */
-	if (l_ecc_point_is_infinity(dpp->y_or_x)) {
-		l_debug("X' is at infinity, ignore message");
-		dpp_reset(dpp);
-		return;
-	}
-
-	qr = dpp_derive_qr(dpp->curve, dpp->pkex_key, dpp->pkex_id, own_addr);
-	if (!qr || l_ecc_point_is_infinity(qr)) {
-		l_debug("Qr did not derive");
-		l_ecc_point_free(dpp->y_or_x);
-		dpp->y_or_x = NULL;
-		goto bad_code;
-	}
-
-	/*
-	 * "The Responder then generates a random ephemeral keypair, y/Y,
-	 * encrypts Y with Qr to obtain the result, denoted N."
-	 */
-	l_ecdh_generate_key_pair(dpp->curve, &dpp->pkex_private,
-					&dpp->pkex_public);
-
-	/* N = Y + Qr */
-	n = l_ecc_point_new(dpp->curve);
-
-	l_ecc_point_add(n, dpp->pkex_public, qr);
-
-	/* K = y * X' */
-
-	k = l_ecc_point_new(dpp->curve);
-
-	l_ecc_point_multiply(k, dpp->pkex_private, dpp->y_or_x);
-
-	/* z = HKDF(<>, info | M.x | N.x | code, K.x) */
-	dpp_derive_z(dpp->peer_addr, own_addr, n, m, k, dpp->pkex_key,
-				dpp->pkex_id, dpp->z, &dpp->z_len);
-
-	dpp_send_pkex_exchange_response(dpp, n);
-
-	return;
-
-bad_code:
-	dpp_send_bad_code(dpp, dpp->peer_addr);
-	return;
-}
-
-static void dpp_pkex_agent_reply(struct l_dbus_message *message,
-					void *user_data)
-{
-	struct dpp_sm *dpp = user_data;
-	const char *error, *text;
-	const char *code;
-
-	dpp->agent->pending_id = 0;
-
-	l_debug("SharedCodeAgent %s path %s replied", dpp->agent->owner,
-			dpp->agent->path);
-
-	if (l_dbus_message_get_error(message, &error, &text)) {
-		l_error("RequestSharedCode(%s) returned %s(\"%s\")",
-				dpp->pkex_id, error, text);
-		goto reset;
-	}
-
-	if (!l_dbus_message_get_arguments(message, "s", &code)) {
-		l_debug("Invalid arguments, check SharedCodeAgent!");
-		goto reset;
-	}
-
-	dpp->pkex_key = l_strdup(code);
-	dpp_process_pkex_exchange_request(dpp, dpp->peer_encr_key);
-
-	return;
-
-reset:
-	dpp_free_pending_pkex_data(dpp);
-}
-
-static bool dpp_pkex_agent_request(struct dpp_sm *dpp)
-{
-	struct l_dbus_message *msg;
-
-	if (!dpp->agent)
-		return false;
-
-	if (L_WARN_ON(dpp->agent->pending_id))
-		return false;
-
-	msg = l_dbus_message_new_method_call(dbus_get_bus(),
-						dpp->agent->owner,
-						dpp->agent->path,
-						IWD_SHARED_CODE_AGENT_INTERFACE,
-						"RequestSharedCode");
-	l_dbus_message_set_arguments(msg, "s", dpp->pkex_id);
-
-
-	dpp->agent->pending_id = l_dbus_send_with_reply(dbus_get_bus(),
-							msg,
-							dpp_pkex_agent_reply,
-							dpp, NULL);
-	return dpp->agent->pending_id != 0;
-}
-
-static void dpp_handle_pkex_exchange_request(struct dpp_sm *dpp,
-					const uint8_t *from,
-					const uint8_t *body, size_t body_len)
-{
-	struct dpp_attr_iter iter;
-	enum dpp_attribute_type type;
-	size_t len;
-	const uint8_t *data;
-	uint8_t version = 0;
-	uint16_t group = 0;
-	const void *id = NULL;
-	size_t id_len = 0;
-	const void *key = NULL;
-	size_t key_len = 0;
-	_auto_(l_ecc_point_free) struct l_ecc_point *m = NULL;
-
-	l_debug("PKEX exchange request "MAC, MAC_STR(from));
-
-	if (dpp->state != DPP_STATE_PKEX_EXCHANGE)
-		return;
-
-	if (dpp->role != DPP_CAPABILITY_CONFIGURATOR)
-		return;
-
-	if (!l_memeqzero(dpp->peer_addr, 6)) {
-		l_debug("Already configuring enrollee, ignoring");
-		return;
-	}
-
-	dpp_attr_iter_init(&iter, body + 8, body_len - 8);
-
-	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
-		switch (type) {
-		case DPP_ATTR_PROTOCOL_VERSION:
-			if (len != 1)
-				return;
-
-			version = l_get_u8(data);
-			break;
-		case DPP_ATTR_FINITE_CYCLIC_GROUP:
-			if (len != 2)
-				return;
-
-			group = l_get_le16(data);
-			break;
-		case DPP_ATTR_CODE_IDENTIFIER:
-			id = data;
-			id_len = len;
-			break;
-		case DPP_ATTR_ENCRYPTED_KEY:
-			key = data;
-			key_len = len;
-			break;
-		default:
-			break;
-		}
-	}
-
-	if (!key || !group) {
-		l_debug("initiator did not provide group or key, ignoring");
-		return;
-	}
+	struct scan_bss *bss;
+	struct network *network;
+	int ret;
 
-	if (group != l_ecc_curve_get_ike_group(dpp->curve)) {
-		l_debug("initiator is not using the same group");
-		goto bad_group;
-	}
+	network = station_network_find(station, dpp->config->ssid,
+					SECURITY_PSK);
 
-	/*
-	 * If the group isn't the same the key length won't match, so check
-	 * this here after we've determined the groups are equal
-	 */
-	if (key_len != dpp->key_len * 2) {
-		l_debug("Unexpected encrypted key length");
-		return;
-	}
+	dpp_reset(dpp);
 
-	if (version && version != dpp->pkex_version) {
-		l_debug("initiator is not using the same version, ignoring");
+	if (!network) {
+		l_debug("Network was not found!");
 		return;
 	}
 
-	if (dpp->pkex_id) {
-		if (!id || id_len != strlen(dpp->pkex_id) ||
-				memcmp(dpp->pkex_id, id, id_len)) {
-			l_debug("mismatch identifier, ignoring");
-			return;
-		}
-	}
+	l_debug("connecting to %s from DPP", network_get_ssid(network));
 
-	m = l_ecc_point_from_data(dpp->curve, L_ECC_POINT_TYPE_FULL,
-					key, key_len);
-	if (!m) {
-		l_debug("could not parse key from initiator, ignoring");
-		return;
-	}
+	bss = network_bss_select(network, true);
+	ret = network_autoconnect(network, bss);
+	if (ret < 0)
+		l_warn("failed to connect after DPP (%d) %s", ret,
+			strerror(-ret));
+}
 
-	memcpy(dpp->peer_addr, from, 6);
+static bool dpp_scan_results(int err, struct l_queue *bss_list,
+				const struct scan_freq_set *freqs,
+				void *userdata)
+{
+	struct dpp *dpp = userdata;
+	struct station *station = station_find(netdev_get_ifindex(dpp->netdev));
 
-	if (!dpp->pkex_key) {
-		/*
-		 * "If an optional code identifier is used, it shall be a UTF-8
-		 *  string not greater than eighty (80) octets"
-		 */
-		if (!id || id_len > 80 || !l_utf8_validate(id, id_len, NULL)) {
-			l_debug("Configurator started with agent but enrollee "
-				"sent invalid or no identifier, ignoring");
-			return;
-		}
+	if (err < 0)
+		goto reset;
 
-		dpp->pkex_id = l_strndup(id, id_len);
+	if (!bss_list || l_queue_length(bss_list) == 0)
+		goto reset;
 
-		/* Need to obtain code from agent */
-		if (!dpp_pkex_agent_request(dpp)) {
-			l_debug("Failed to request code from agent!");
-			dpp_free_pending_pkex_data(dpp);
-			return;
-		}
+	/*
+	 * The station watch _should_ detect this and reset, which cancels the
+	 * scan. But just in case...
+	 */
+	if (L_WARN_ON(station_get_connected_network(station)))
+		goto reset;
 
-		/* Save the encrypted key/identifier for the agent callback */
+	station_set_scan_results(station, bss_list, freqs, false);
 
-		dpp->peer_encr_key = l_steal_ptr(m);
+	dpp_start_connect(NULL, dpp);
 
-		return;
-	}
+	return true;
 
-	dpp_process_pkex_exchange_request(dpp, m);
+reset:
+	return false;
+}
 
-	return;
+static void dpp_scan_destroy(void *userdata)
+{
+	struct dpp *dpp = userdata;
 
-bad_group:
-	dpp_send_bad_group(dpp, from);
+	dpp->connect_scan_id = 0;
+	dpp_reset(dpp);
 }
 
-static void dpp_send_commit_reveal_response(struct dpp_sm *dpp,
-						const uint8_t *v, size_t v_len)
+static void dpp_known_network_watch(enum known_networks_event event,
+					const struct network_info *info,
+					void *user_data)
 {
-	uint8_t frame[256];
-	uint8_t *ptr = frame;
-	struct iovec iov;
-	const uint8_t *own_mac = netdev_get_address(dpp->netdev);
-	uint8_t b_pub[L_ECC_POINT_MAX_BYTES];
-	size_t b_len;
-	struct mmpdu_header *hdr = (struct mmpdu_header *)frame;
+	struct dpp *dpp = user_data;
 
-	memset(frame, 0, sizeof(frame));
+	/*
+	 * Check the following
+	 *  - DPP is enrolling
+	 *  - DPP finished (dpp->config is set)
+	 *  - This is for the network DPP just configured
+	 *  - DPP isn't already trying to connect (e.g. if the profile was
+	 *    immediately modified after DPP synced it).
+	 *  - DPP didn't start a scan for the network.
+	 */
+	if (dpp->role != DPP_CAPABILITY_ENROLLEE)
+		return;
+	if (!dpp->config)
+		return;
+	if (strcmp(info->ssid, dpp->config->ssid))
+		return;
+	if (dpp->connect_idle)
+		return;
+	if (dpp->connect_scan_id)
+		return;
 
-	b_len = l_ecc_point_get_data(dpp->boot_public, b_pub, sizeof(b_pub));
+	switch (event) {
+	case KNOWN_NETWORKS_EVENT_ADDED:
+	case KNOWN_NETWORKS_EVENT_UPDATED:
+		/*
+		 * network.c takes care of updating the settings for the
+		 * network. This callback just tells us to begin the connection.
+		 * We do have use an idle here because there is no strict
+		 * guarantee of ordering between known network events, e.g. DPP
+		 * could have been called into prior to network and the network
+		 * object isn't updated yet.
+		 */
+		dpp->connect_idle = l_idle_create(dpp_start_connect, dpp, NULL);
+		break;
+	case KNOWN_NETWORKS_EVENT_REMOVED:
+		l_warn("profile was removed before DPP could connect");
+		break;
+	}
+}
 
-	ptr += dpp_build_header(own_mac, dpp->peer_addr,
-				DPP_FRAME_PKEX_COMMIT_REVEAL_RESPONSE, ptr);
-	ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1,
-			sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr,
-			dpp->z, dpp->z_len, 2,
-			DPP_ATTR_BOOTSTRAPPING_KEY, b_len, b_pub,
-			DPP_ATTR_RESPONDER_AUTH_TAG, v_len, v);
+static void dpp_handle_config_frame(const struct mmpdu_header *frame,
+					const void *body, size_t body_len,
+					int rssi, void *user_data)
+{
+	struct dpp *dpp = user_data;
+	struct dpp_configuration *config = NULL;
+	struct station *station = station_find(netdev_get_ifindex(dpp->netdev));
+	struct network *network = NULL;
+	struct scan_bss *bss = NULL;
 
-	iov.iov_base = frame;
-	iov.iov_len = ptr - frame;
+	dpp_write_config(config, network);
 
-	dpp_send_frame(dpp, &iov, 1, dpp->current_freq);
-}
+	offchannel_cancel(dpp->wdev_id, dpp->offchannel_id);
 
-static void dpp_handle_pkex_commit_reveal_request(struct dpp_sm *dpp,
-					const uint8_t *from,
-					const uint8_t *body, size_t body_len)
-{
-	struct dpp_attr_iter iter;
-	enum dpp_attribute_type type;
-	size_t len;
-	const uint8_t *data;
-	const void *wrapped = NULL;
-	size_t wrapped_len = 0;
-	_auto_(l_free) uint8_t *unwrapped = NULL;
-	size_t unwrapped_len;
-	uint8_t zero = 0;
-	const void *key = 0;
-	size_t key_len = 0;
-	const void *i_auth = NULL;
-	size_t i_auth_len = 0;
-	_auto_(l_ecc_point_free) struct l_ecc_point *j = NULL;
-	_auto_(l_ecc_point_free) struct l_ecc_point *l = NULL;
-	uint8_t u[L_ECC_SCALAR_MAX_BYTES];
-	size_t u_len = 0;
-	uint8_t v[L_ECC_SCALAR_MAX_BYTES];
-	size_t v_len = 0;
-	const uint8_t *own_addr = netdev_get_address(dpp->netdev);
-
-	l_debug("PKEX commit-reveal request "MAC, MAC_STR(from));
-
-	if (dpp->state != DPP_STATE_PKEX_COMMIT_REVEAL)
+	if (network && bss) {
+		l_debug("delaying connect until settings are synced");
+		dpp->config = config;
 		return;
+	} else if (station) {
+		struct scan_parameters params = {0};
 
-	if (dpp->role != DPP_CAPABILITY_CONFIGURATOR)
-		return;
+		params.ssid = (void *) config->ssid;
+		params.ssid_len = config->ssid_len;
 
-	dpp_attr_iter_init(&iter, body + 8, body_len - 8);
+		l_debug("Scanning for %s", config->ssid);
 
-	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
-		switch (type) {
-		case DPP_ATTR_WRAPPED_DATA:
-			wrapped = data;
-			wrapped_len = len;
-			break;
-		default:
-			break;
+		dpp->connect_scan_id = scan_active_full(dpp->wdev_id, &params,
+						dpp_scan_triggered,
+						dpp_scan_results, dpp,
+						dpp_scan_destroy);
+		if (dpp->connect_scan_id) {
+			dpp->config = config;
+			return;
 		}
 	}
 
-	if (!wrapped) {
-		l_debug("No wrapped data");
-		return;
-	}
-
-	unwrapped = dpp_unwrap_attr(body + 2, 6, &zero, 1, dpp->z, dpp->z_len,
-					wrapped, wrapped_len, &unwrapped_len);
-	if (!unwrapped) {
-		l_debug("Failed to unwrap attributes");
-		return;
-	}
-
-	dpp_attr_iter_init(&iter, unwrapped, unwrapped_len);
+}
 
-	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
-		switch (type) {
-		case DPP_ATTR_BOOTSTRAPPING_KEY:
-			if (len != dpp->key_len * 2)
-				return;
+static void dpp_roc_started(void *user_data)
+{
+	struct dpp *dpp = user_data;
 
-			key = data;
-			key_len = len;
-			break;
-		case DPP_ATTR_INITIATOR_AUTH_TAG:
-			if (len != 32)
-				return;
+	/*
+	 * - If a configurator, nothing to do but wait for a request
+	 *   (unless multicast frame registration is unsupported in which case
+	 *   send an authenticate request now)
+	 * - If in the presence state continue sending announcements.
+	 * - If authenticating, and this is a result of a channel switch send
+	 *   the authenticate response now.
+	 */
 
-			i_auth = data;
-			i_auth_len = len;
-			break;
-		default:
-			break;
-		}
-	}
+	dpp->roc_started = true;
 
-	if (!key || !i_auth) {
-		l_debug("missing attributes");
+	/*
+	 * The retry timer indicates a frame was not acked in which case we
+	 * should not change any state or send any frames until that expires.
+	 */
+	if (dpp->retry_timeout)
 		return;
-	}
 
-	dpp->peer_boot_public = l_ecc_point_from_data(dpp->curve,
-					L_ECC_POINT_TYPE_FULL, key, key_len);
-	if (!dpp->peer_boot_public) {
-		l_debug("peers boostrapping key did not validate");
-		goto failed;
+	if (dpp->frame_pending) {
+		dpp_frame_retry(dpp);
+		return;
 	}
+}
 
-	/* J' = y * A' */
-	j = l_ecc_point_new(dpp->curve);
-
-	l_ecc_point_multiply(j, dpp->pkex_private, dpp->peer_boot_public);
+static void dpp_start_offchannel(struct dpp *dpp, uint32_t freq);
 
-	dpp_derive_u(j, dpp->peer_addr, dpp->peer_boot_public, dpp->pkex_public,
-			dpp->y_or_x, u, &u_len);
+static void dpp_offchannel_timeout(int error, void *user_data)
+{
+	struct dpp *dpp = user_data;
 
-	if (memcmp(u, i_auth, i_auth_len)) {
-		l_debug("Initiator auth tag did not verify");
-		goto failed;
-	}
+	dpp->offchannel_id = 0;
+	dpp->roc_started = false;
 
-	/* L' = x * B' */
-	l = l_ecc_point_new(dpp->curve);
+	/*
+	 * If cancelled this is likely due to netdev going down or from Stop().
+	 * Otherwise there was some other problem which is probably not
+	 * recoverable.
+	 */
+	if (error == -ECANCELED)
+		return;
+	else if (error == -EIO)
+		goto next_roc;
+	else if (error < 0)
+		goto protocol_failed;
 
-	l_ecc_point_multiply(l, dpp->boot_private, dpp->y_or_x);
+	dpp->freqs_idx++;
 
-	if (!dpp_derive_v(l, own_addr, dpp->boot_public, dpp->y_or_x,
-				dpp->pkex_public, v, &v_len)) {
-		l_debug("Failed to derive v");
-		goto failed;
+	if (dpp->freqs_idx >= dpp->freqs_len) {
+		l_debug("Max retries offchannel");
+		dpp->freqs_idx = 0;
 	}
 
-	dpp_send_commit_reveal_response(dpp, v, v_len);
+	dpp->current_freq = dpp->freqs[dpp->freqs_idx];
+
+	l_debug("Offchannel timeout, moving to next frequency %u, duration %u",
+			dpp->current_freq, dpp->dwell);
 
-	dpp_pkex_start_authentication(dpp);
+next_roc:
+	dpp_start_offchannel(dpp, dpp->current_freq);
 
 	return;
 
-failed:
+protocol_failed:
 	dpp_reset(dpp);
 }
 
-static void dpp_handle_frame(struct dpp_sm *dpp,
+static void dpp_start_offchannel(struct dpp *dpp, uint32_t freq)
+{
+	/*
+	 * This needs to be handled carefully for a few reasons:
+	 *
+	 * First, the next offchannel operation needs to be started prior to
+	 * canceling an existing one. This is so the offchannel work can
+	 * continue uninterrupted without any other work items starting in
+	 * between canceling and starting the next (e.g. if a scan request is
+	 * sitting in the queue).
+	 *
+	 * Second, dpp_offchannel_timeout resets dpp->offchannel_id to zero
+	 * which is why the new ID is saved and only set to dpp->offchannel_id
+	 * once the previous offchannel work is cancelled (i.e. destroy() has
+	 * been called).
+	 */
+	uint32_t id = offchannel_start(netdev_get_wdev_id(dpp->netdev),
+				WIPHY_WORK_PRIORITY_OFFCHANNEL,
+				freq, dpp->dwell, dpp_roc_started,
+				dpp, dpp_offchannel_timeout);
+
+	if (dpp->offchannel_id)
+		offchannel_cancel(dpp->wdev_id, dpp->offchannel_id);
+
+	dpp->offchannel_id = id;
+}
+
+static void dpp_handle_frame(struct dpp *dpp,
 				const struct mmpdu_header *frame,
 				const void *body, size_t body_len)
 {
@@ -3472,39 +810,6 @@ static void dpp_handle_frame(struct dpp_sm *dpp,
 	ptr = body + 7;
 
 	switch (*ptr) {
-	case DPP_FRAME_AUTHENTICATION_REQUEST:
-		authenticate_request(dpp, frame->address_2, body, body_len);
-		break;
-	case DPP_FRAME_AUTHENTICATION_RESPONSE:
-		authenticate_response(dpp, frame->address_2, body, body_len);
-		break;
-	case DPP_FRAME_AUTHENTICATION_CONFIRM:
-		authenticate_confirm(dpp, frame->address_2, body, body_len);
-		break;
-	case DPP_FRAME_CONFIGURATION_RESULT:
-		dpp_handle_config_result_frame(dpp, frame->address_2,
-						body, body_len);
-		break;
-	case DPP_FRAME_PRESENCE_ANNOUNCEMENT:
-		dpp_handle_presence_announcement(dpp, frame->address_2,
-							body, body_len);
-		break;
-	case DPP_FRAME_PKEX_XCHG_RESPONSE:
-		dpp_handle_pkex_exchange_response(dpp, frame->address_2, body,
-							body_len);
-		break;
-	case DPP_FRAME_PKEX_COMMIT_REVEAL_RESPONSE:
-		dpp_handle_pkex_commit_reveal_response(dpp, frame->address_2,
-							body, body_len);
-		break;
-	case DPP_FRAME_PKEX_VERSION1_XCHG_REQUEST:
-		dpp_handle_pkex_exchange_request(dpp, frame->address_2, body,
-							body_len);
-		break;
-	case DPP_FRAME_PKEX_COMMIT_REVEAL_REQUEST:
-		dpp_handle_pkex_commit_reveal_request(dpp, frame->address_2,
-							body, body_len);
-		break;
 	default:
 		l_debug("Unhandled DPP frame %u", *ptr);
 		break;
@@ -3513,7 +818,7 @@ static void dpp_handle_frame(struct dpp_sm *dpp,
 
 static bool match_wdev(const void *a, const void *b)
 {
-	const struct dpp_sm *dpp = a;
+	const struct dpp *dpp = a;
 	const uint64_t *wdev_id = b;
 
 	return *wdev_id == dpp->wdev_id;
@@ -3521,7 +826,7 @@ static bool match_wdev(const void *a, const void *b)
 
 static void dpp_frame_timeout(struct l_timeout *timeout, void *user_data)
 {
-	struct dpp_sm *dpp = user_data;
+	struct dpp *dpp = user_data;
 
 	l_timeout_remove(timeout);
 	dpp->retry_timeout = NULL;
@@ -3540,7 +845,7 @@ static void dpp_frame_timeout(struct l_timeout *timeout, void *user_data)
 
 static void dpp_mlme_notify(struct l_genl_msg *msg, void *user_data)
 {
-	struct dpp_sm *dpp;
+	struct dpp *dpp;
 	uint64_t wdev_id = 0;
 	uint64_t cookie = 0;
 	bool ack = false;
@@ -3620,7 +925,7 @@ retransmit:
 
 static void dpp_unicast_notify(struct l_genl_msg *msg, void *user_data)
 {
-	struct dpp_sm *dpp;
+	struct dpp *dpp;
 	const uint64_t *wdev_id = NULL;
 	struct l_genl_attr attr;
 	uint16_t type, len, frame_len;
@@ -3692,7 +997,7 @@ static void dpp_frame_watch_cb(struct l_genl_msg *msg, void *user_data)
  * require multicast support. This is only supported by ath9k, so adding
  * general support to frame-xchg isn't desireable.
  */
-static void dpp_frame_watch(struct dpp_sm *dpp, uint16_t frame_type,
+static void dpp_frame_watch(struct dpp *dpp, uint16_t frame_type,
 				const uint8_t *prefix, size_t prefix_len)
 {
 	struct l_genl_msg *msg;
@@ -3728,7 +1033,7 @@ static void dpp_frame_watch(struct dpp_sm *dpp, uint16_t frame_type,
  */
 static void dpp_station_state_watch(enum station_state state, void *user_data)
 {
-	struct dpp_sm *dpp = user_data;
+	struct dpp *dpp = user_data;
 
 	if (dpp->state == DPP_STATE_NOTHING)
 		return;
@@ -3779,7 +1084,7 @@ static void dpp_station_state_watch(enum station_state state, void *user_data)
 static void dpp_create(struct netdev *netdev)
 {
 	struct l_dbus *dbus = dbus_get_bus();
-	struct dpp_sm *dpp = l_new(struct dpp_sm, 1);
+	struct dpp *dpp = l_new(struct dpp, 1);
 	uint8_t dpp_conf_response_prefix[] = { 0x04, 0x0b };
 	uint8_t dpp_conf_request_prefix[] = { 0x04, 0x0a };
 	uint64_t wdev_id = netdev_get_wdev_id(netdev);
@@ -3789,32 +1094,22 @@ static void dpp_create(struct netdev *netdev)
 	dpp->state = DPP_STATE_NOTHING;
 	dpp->interface = DPP_INTERFACE_UNBOUND;
 	dpp->wdev_id = wdev_id;
-	dpp->curve = l_ecc_curve_from_ike_group(19);
-	dpp->key_len = l_ecc_curve_get_scalar_bytes(dpp->curve);
-	dpp->nonce_len = dpp_nonce_len_from_key_len(dpp->key_len);
+
 	dpp->max_roc = wiphy_get_max_roc_duration(wiphy_find_by_wdev(wdev_id));
 	dpp->mcast_support = wiphy_has_ext_feature(
 				wiphy_find_by_wdev(dpp->wdev_id),
 				NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS);
 
-	l_ecdh_generate_key_pair(dpp->curve, &dpp->boot_private,
-					&dpp->boot_public);
-
-	dpp->own_asn1 = dpp_point_to_asn1(dpp->boot_public, &dpp->own_asn1_len);
-
-	dpp_hash(L_CHECKSUM_SHA256, dpp->own_boot_hash, 1,
-			dpp->own_asn1, dpp->own_asn1_len);
-
 	l_dbus_object_add_interface(dbus, netdev_get_path(netdev),
 					IWD_DPP_INTERFACE, dpp);
 	l_dbus_object_add_interface(dbus, netdev_get_path(netdev),
 					IWD_DPP_PKEX_INTERFACE, dpp);
 	/*
-	 * Since both interfaces share the dpp_sm set this to 2. Currently both
+	 * Since both interfaces share the dpp set this to 2. Currently both
 	 * interfaces are added/removed in unison so we _could_ simply omit the
 	 * destroy callback on one of them. But for consistency and future
 	 * proofing use a reference count and the final interface being removed
-	 * will destroy the dpp_sm.
+	 * will destroy the dpp.
 	 */
 	dpp->refcount = 2;
 
@@ -3823,11 +1118,11 @@ static void dpp_create(struct netdev *netdev)
 	frame_watch_add(netdev_get_wdev_id(netdev), 0, 0x00d0,
 				dpp_conf_response_prefix,
 				sizeof(dpp_conf_response_prefix),
-				dpp_handle_config_response_frame, dpp, NULL);
+				dpp_handle_config_frame, dpp, NULL);
 	frame_watch_add(netdev_get_wdev_id(netdev), 0, 0x00d0,
 				dpp_conf_request_prefix,
 				sizeof(dpp_conf_request_prefix),
-				dpp_handle_config_request_frame, dpp, NULL);
+				dpp_handle_config_frame, dpp, NULL);
 
 	dpp->station_watch = station_add_state_watch(station,
 					dpp_station_state_watch, dpp, NULL);
@@ -3864,7 +1159,7 @@ static void dpp_netdev_watch(struct netdev *netdev,
 /*
  * EasyConnect 2.0 - 6.2.2
  */
-static uint32_t *dpp_add_default_channels(struct dpp_sm *dpp, size_t *len_out)
+static uint32_t *dpp_add_default_channels(struct dpp *dpp, size_t *len_out)
 {
 	struct wiphy *wiphy = wiphy_find_by_wdev(
 					netdev_get_wdev_id(dpp->netdev));
@@ -3901,7 +1196,7 @@ static uint32_t *dpp_add_default_channels(struct dpp_sm *dpp, size_t *len_out)
  * procedure is not being fully implemented. In reality doing this would result
  * in quite terrible DPP performance anyways.
  */
-static void dpp_start_presence(struct dpp_sm *dpp, uint32_t *limit_freqs,
+static void dpp_start_presence(struct dpp *dpp, uint32_t *limit_freqs,
 					size_t limit_len)
 {
 	if (limit_freqs) {
@@ -3921,7 +1216,7 @@ static struct l_dbus_message *dpp_dbus_start_enrollee(struct l_dbus *dbus,
 						struct l_dbus_message *message,
 						void *user_data)
 {
-	struct dpp_sm *dpp = user_data;
+	struct dpp *dpp = user_data;
 	uint32_t freq = band_channel_to_freq(6, BAND_FREQ_2_4_GHZ);
 	struct station *station = station_find(netdev_get_ifindex(dpp->netdev));
 
@@ -3939,17 +1234,10 @@ static struct l_dbus_message *dpp_dbus_start_enrollee(struct l_dbus *dbus,
 	} else if (!station)
 		l_debug("No station device, continuing anyways...");
 
-	dpp->uri = dpp_generate_uri(dpp->own_asn1, dpp->own_asn1_len, 2,
-					netdev_get_address(dpp->netdev), &freq,
-					1, NULL, NULL);
-
 	dpp->state = DPP_STATE_PRESENCE;
 	dpp->role = DPP_CAPABILITY_ENROLLEE;
 	dpp->interface = DPP_INTERFACE_DPP;
 
-	l_ecdh_generate_key_pair(dpp->curve, &dpp->proto_private,
-					&dpp->own_proto_public);
-
 	l_debug("DPP Start Enrollee: %s", dpp->uri);
 
 	dpp->pending = l_dbus_message_ref(message);
@@ -3971,7 +1259,7 @@ static struct l_dbus_message *dpp_dbus_start_enrollee(struct l_dbus *dbus,
  * will go offchannel to frequencies advertised by the enrollees URI or,
  * if no channels are provided, use a default channel list.
  */
-static bool dpp_configurator_start_presence(struct dpp_sm *dpp, const char *uri)
+static bool dpp_configurator_start_presence(struct dpp *dpp, const char *uri)
 {
 	_auto_(l_free) uint32_t *freqs = NULL;
 	size_t freqs_len = 0;
@@ -4002,20 +1290,8 @@ static bool dpp_configurator_start_presence(struct dpp_sm *dpp, const char *uri)
 	if (info->freqs)
 		freqs = scan_freq_set_to_fixed_array(info->freqs, &freqs_len);
 
-	dpp->peer_boot_public = l_ecc_point_clone(info->boot_public);
-	dpp->peer_asn1 = dpp_point_to_asn1(info->boot_public,
-						&dpp->peer_asn1_len);
-
 	dpp_free_uri_info(info);
 
-	if (!dpp->peer_asn1) {
-		l_debug("Peer boot key did not convert to asn1");
-		return false;
-	}
-
-	dpp_hash(L_CHECKSUM_SHA256, dpp->peer_boot_hash, 1, dpp->peer_asn1,
-			dpp->peer_asn1_len);
-
 	dpp_start_presence(dpp, freqs, freqs_len);
 
 	return true;
@@ -4027,7 +1303,7 @@ static struct l_dbus_message *dpp_start_configurator_common(
 						void *user_data,
 						bool responder)
 {
-	struct dpp_sm *dpp = user_data;
+	struct dpp *dpp = user_data;
 	struct l_dbus_message *reply;
 	struct station *station = station_find(netdev_get_ifindex(dpp->netdev));
 	struct scan_bss *bss;
@@ -4059,9 +1335,6 @@ static struct l_dbus_message *dpp_start_configurator_common(
 				dpp->interface != DPP_INTERFACE_UNBOUND)
 		return dbus_error_busy(message);
 
-	l_ecdh_generate_key_pair(dpp->curve, &dpp->proto_private,
-					&dpp->own_proto_public);
-
 	dpp->state = DPP_STATE_PRESENCE;
 
 	if (!responder) {
@@ -4071,12 +1344,6 @@ static struct l_dbus_message *dpp_start_configurator_common(
 		if (!dpp_configurator_start_presence(dpp, uri))
 			return dbus_error_invalid_args(message);
 
-		/* Since we have the peer's URI generate the keys now */
-		l_getrandom(dpp->i_nonce, dpp->nonce_len);
-
-		dpp->m = dpp_derive_k1(dpp->peer_boot_public,
-					dpp->proto_private, dpp->k1);
-
 		if (!dpp->mcast_support)
 			dpp->state = DPP_STATE_AUTHENTICATING;
 
@@ -4084,9 +1351,6 @@ static struct l_dbus_message *dpp_start_configurator_common(
 	} else
 		dpp->current_freq = bss->frequency;
 
-	dpp->uri = dpp_generate_uri(dpp->own_asn1, dpp->own_asn1_len, 2,
-					netdev_get_address(dpp->netdev),
-					&bss->frequency, 1, NULL, NULL);
 	dpp->role = DPP_CAPABILITY_CONFIGURATOR;
 	dpp->interface = DPP_INTERFACE_DPP;
 	dpp->config = dpp_configuration_new(settings,
@@ -4122,7 +1386,7 @@ static struct l_dbus_message *dpp_dbus_stop(struct l_dbus *dbus,
 						struct l_dbus_message *message,
 						void *user_data)
 {
-	struct dpp_sm *dpp = user_data;
+	struct dpp *dpp = user_data;
 
 	l_debug("");
 
@@ -4136,7 +1400,7 @@ static struct l_dbus_message *dpp_dbus_stop(struct l_dbus *dbus,
 
 static void dpp_pkex_scan_trigger(int err, void *user_data)
 {
-	struct dpp_sm *dpp = user_data;
+	struct dpp *dpp = user_data;
 
 	if (err < 0)
 		dpp_reset(dpp);
@@ -4152,7 +1416,7 @@ static void dpp_pkex_scan_trigger(int err, void *user_data)
  *           operation only in the 5.150 - 5.250 GHz band and Channel
  *           149 (5.745 GHz) otherwise
  */
-static uint32_t *dpp_default_freqs(struct dpp_sm *dpp, size_t *out_len)
+static uint32_t *dpp_default_freqs(struct dpp *dpp, size_t *out_len)
 {
 	struct wiphy *wiphy = wiphy_find_by_wdev(dpp->wdev_id);
 	uint32_t default_channels[3] = { 2437, 5220, 5745 };
@@ -4184,7 +1448,7 @@ static bool dpp_pkex_scan_notify(int err, struct l_queue *bss_list,
 					const struct scan_freq_set *freqs,
 					void *user_data)
 {
-	struct dpp_sm *dpp = user_data;
+	struct dpp *dpp = user_data;
 	const struct l_queue_entry *e;
 	_auto_(scan_freq_set_free) struct scan_freq_set *freq_set = NULL;
 
@@ -4216,8 +1480,6 @@ static bool dpp_pkex_scan_notify(int err, struct l_queue *bss_list,
 start:
 	dpp->current_freq = dpp->freqs[0];
 
-	dpp_reset_protocol_timer(dpp, DPP_PKEX_PROTO_TIMEOUT);
-
 	l_debug("PKEX start enrollee (id=%s)", dpp->pkex_id ?: "unset");
 
 	dpp_start_offchannel(dpp, dpp->current_freq);
@@ -4231,12 +1493,12 @@ failed:
 
 static void dpp_pkex_scan_destroy(void *user_data)
 {
-	struct dpp_sm *dpp = user_data;
+	struct dpp *dpp = user_data;
 
 	dpp->pkex_scan_id = 0;
 }
 
-static bool dpp_start_pkex_enrollee(struct dpp_sm *dpp, const char *key,
+static bool dpp_start_pkex_enrollee(struct dpp *dpp, const char *key,
 				const char *identifier)
 {
 	_auto_(l_ecc_point_free) struct l_ecc_point *qi = NULL;
@@ -4256,28 +1518,6 @@ static bool dpp_start_pkex_enrollee(struct dpp_sm *dpp, const char *key,
 	 * but for now use max_roc or 200ms, whichever is less.
 	 */
 	dpp->dwell = (dpp->max_roc < 200) ? dpp->max_roc : 200;
-	/* "DPP R2 devices are expected to use PKEXv1 by default" */
-	dpp->pkex_version = 1;
-
-	if (!l_ecdh_generate_key_pair(dpp->curve, &dpp->pkex_private,
-					&dpp->pkex_public))
-		goto failed;
-
-	/*
-	 * "If Qi is the point-at-infinity, the code shall be deleted and the
-	 * user should be notified to provision a new code"
-	 */
-	qi = dpp_derive_qi(dpp->curve, dpp->pkex_key, dpp->pkex_id,
-				netdev_get_address(dpp->netdev));
-	if (!qi || l_ecc_point_is_infinity(qi)) {
-		l_debug("Cannot derive Qi, provision a new code");
-		goto failed;
-	}
-
-	dpp->pkex_m = l_ecc_point_new(dpp->curve);
-
-	if (!l_ecc_point_add(dpp->pkex_m, dpp->pkex_public, qi))
-		goto failed;
 
 	dpp_property_changed_notify(dpp);
 
@@ -4349,7 +1589,7 @@ static struct l_dbus_message *dpp_dbus_pkex_start_enrollee(struct l_dbus *dbus,
 						struct l_dbus_message *message,
 						void *user_data)
 {
-	struct dpp_sm *dpp = user_data;
+	struct dpp *dpp = user_data;
 	const char *key;
 	const char *id;
 	struct station *station = station_find(netdev_get_ifindex(dpp->netdev));
@@ -4377,14 +1617,14 @@ invalid_args:
 
 static void pkex_agent_disconnect(struct l_dbus *dbus, void *user_data)
 {
-	struct dpp_sm *dpp = user_data;
+	struct dpp *dpp = user_data;
 
 	l_debug("SharedCodeAgent %s disconnected", dpp->agent->path);
 
 	dpp_reset(dpp);
 }
 
-static void dpp_create_agent(struct dpp_sm *dpp, const char *path,
+static void dpp_create_agent(struct dpp *dpp, const char *path,
 					struct l_dbus_message *message)
 {
 	const char *sender = l_dbus_message_get_sender(message);
@@ -4401,7 +1641,7 @@ static void dpp_create_agent(struct dpp_sm *dpp, const char *path,
 	l_debug("Registered a SharedCodeAgent on path %s", path);
 }
 
-static struct l_dbus_message *dpp_start_pkex_configurator(struct dpp_sm *dpp,
+static struct l_dbus_message *dpp_start_pkex_configurator(struct dpp *dpp,
 					const char *key, const char *identifier,
 					const char *agent_path,
 					struct l_dbus_message *message)
@@ -4444,12 +1684,10 @@ static struct l_dbus_message *dpp_start_pkex_configurator(struct dpp_sm *dpp,
 	dpp->state = DPP_STATE_PKEX_EXCHANGE;
 	dpp->interface = DPP_INTERFACE_PKEX;
 	dpp->current_freq = bss->frequency;
-	dpp->pkex_version = 1;
 	dpp->config = dpp_configuration_new(network_get_settings(network),
 						network_get_ssid(network),
 						hs->akm_suite);
 
-	dpp_reset_protocol_timer(dpp, DPP_PKEX_PROTO_TIMEOUT);
 	dpp_property_changed_notify(dpp);
 
 	if (dpp->pkex_key)
@@ -4465,7 +1703,7 @@ static struct l_dbus_message *dpp_dbus_pkex_configure_enrollee(
 						struct l_dbus_message *message,
 						void *user_data)
 {
-	struct dpp_sm *dpp = user_data;
+	struct dpp *dpp = user_data;
 	const char *key;
 	const char *id;
 
@@ -4482,7 +1720,7 @@ static struct l_dbus_message *dpp_dbus_pkex_start_configurator(
 						struct l_dbus_message *message,
 						void *user_data)
 {
-	struct dpp_sm *dpp = user_data;
+	struct dpp *dpp = user_data;
 	const char *path;
 
 	if (!l_dbus_message_get_arguments(message, "o", &path))
@@ -4512,7 +1750,7 @@ static void dpp_setup_interface(struct l_dbus_interface *interface)
 static struct l_dbus_message *dpp_dbus_pkex_stop(struct l_dbus *dbus,
 				struct l_dbus_message *message, void *user_data)
 {
-	struct dpp_sm *dpp = user_data;
+	struct dpp *dpp = user_data;
 
 	l_debug("");
 
@@ -4543,7 +1781,7 @@ static void dpp_setup_pkex_interface(struct l_dbus_interface *interface)
 
 static void dpp_destroy_interface(void *user_data)
 {
-	struct dpp_sm *dpp = user_data;
+	struct dpp *dpp = user_data;
 
 	if (--dpp->refcount)
 		return;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 9/9] dpp: use common state machine
  2024-03-27 15:19 [PATCH 1/9] dpp: prep for moving AAD within dpp_append_wrapped_data James Prestwood
                   ` (6 preceding siblings ...)
  2024-03-27 15:19 ` [PATCH 8/9] dpp: remove most crypto/frame processing James Prestwood
@ 2024-03-27 15:19 ` James Prestwood
  2024-04-02 15:10 ` [PATCH 1/9] dpp: prep for moving AAD within dpp_append_wrapped_data Denis Kenzior
  8 siblings, 0 replies; 15+ messages in thread
From: James Prestwood @ 2024-03-27 15:19 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

Begins using the common state machine within dpp-common.c. All
the message flow/handling is now taken care of by the SM. The
dpp.c module now only handles a specific set of functions
required by 802.11 encapsulation (and DBus):

 - Encapsulating and writing out frames for the SM.
 - Decapsulating and reading frames into the SM
 - Offchannel management; setting/unsetting the write handler as
   the offchannel work is ready or timed out.
 - DBus API.

The dpp.c module can now create its minimal dpp object on init and
the dpp_sm object lifetime follows the protocol. When DPP is
started dpp.c will set any options required for the configuration,
role, inititor vs responder, etc. DPP can then be started within
the SM, and any offchannel work should be queued. The basic flow
after that is quite simple:

 - If using offchannel the write handler must be set or unset as
   needed when frames can/can't be sent out. Offchannel frequencies
   are tracked by dpp.c, including presence/PKEX exchange frequency
   iteration.
 - Receved frames should be validated minimally: Prefix should be
   some type of DPP frame, and the source should be either a
   broadcast or from the expected peer (if known).
 - Received frames should be stripped of the mpdu header and
   category byte.
 - If the SM calls into the write handler the frame should be
   encapsulated with a mpdu header and category byte, then sent.
 - Upon receiving DPP events:
     DPP_EVENT_FAILED:
         The SM should be destroyed

     DPP_EVENT_PEER_ACCEPTED
         The MAC for the last message received should be tracked
         and only messages from the MAC should be passed to the SM
         going forward.

     DPP_EVENT_CHANNEL_SWITCH
         The channel should be switched to a new one, as communicated
         by the authentication request

     DPP_EVENT_PKEX_KEY_REQUESTED
         The PKEX key should be obtained and set into the SM using
         dpp_sm_set_pkex_key().

     DPP_EVENT_SUCCESS
         DPP completed. As an enrollee the configuration can now be
         obtained. The SM should be destroyed.
---
 src/dpp.c | 496 ++++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 353 insertions(+), 143 deletions(-)

diff --git a/src/dpp.c b/src/dpp.c
index 31d0ef28..cd7facbf 100644
--- a/src/dpp.c
+++ b/src/dpp.c
@@ -54,6 +54,7 @@
 #include "src/handshake.h"
 #include "src/nl80211util.h"
 #include "src/knownnetworks.h"
+#include "src/dpp-common.h"
 
 #define DPP_FRAME_MAX_RETRIES 5
 #define DPP_FRAME_RETRY_TIMEOUT 1
@@ -67,20 +68,6 @@ static uint32_t unicast_watch;
 
 static uint8_t dpp_prefix[] = { 0x04, 0x09, 0x50, 0x6f, 0x9a, 0x1a, 0x01 };
 
-enum dpp_state {
-	DPP_STATE_NOTHING,
-	DPP_STATE_PRESENCE,
-	DPP_STATE_PKEX_EXCHANGE,
-	DPP_STATE_PKEX_COMMIT_REVEAL,
-	DPP_STATE_AUTHENTICATING,
-	DPP_STATE_CONFIGURING,
-};
-
-enum dpp_capability {
-	DPP_CAPABILITY_ENROLLEE = 0x01,
-	DPP_CAPABILITY_CONFIGURATOR = 0x02,
-};
-
 enum dpp_interface {
 	DPP_INTERFACE_UNBOUND,
 	DPP_INTERFACE_DPP,
@@ -95,6 +82,7 @@ struct pkex_agent {
 };
 
 struct dpp {
+	struct dpp_sm *sm;
 	struct netdev *netdev;
 	char *uri;
 	uint8_t role;
@@ -104,10 +92,10 @@ struct dpp {
 
 	uint64_t wdev_id;
 
+	/* Could probably do away with storing these here */
 	struct l_ecc_scalar *boot_private;
 	struct l_ecc_point *boot_public;
 
-	enum dpp_state state;
 	enum dpp_interface interface;
 
 	struct pkex_agent *agent;
@@ -121,18 +109,17 @@ struct dpp {
 	size_t freqs_idx;
 	uint32_t dwell;
 	uint32_t current_freq;
-	uint32_t new_freq;
 	struct scan_freq_set *presence_list;
 	uint32_t max_roc;
 
 	uint32_t offchannel_id;
 
 	uint8_t peer_addr[6];
+	bool peer_accepted;
 
 	/* Timeout of either auth/config protocol */
 	struct l_timeout *timeout;
 
-	struct dpp_configuration *config;
 	uint32_t connect_scan_id;
 	uint64_t frame_cookie;
 	uint8_t frame_retry;
@@ -140,19 +127,12 @@ struct dpp {
 	size_t frame_size;
 	struct l_timeout *retry_timeout;
 
-	struct l_dbus_message *pending;
-
 	struct l_idle *connect_idle;
 
-	/* PKEX-specific values */
-	char *pkex_id;
-	char *pkex_key;
-
 	uint32_t pkex_scan_id;
 
 	bool mcast_support : 1;
 	bool roc_started : 1;
-	bool channel_switch : 1;
 };
 
 static const char *dpp_role_to_string(enum dpp_capability role)
@@ -173,8 +153,7 @@ static bool dpp_pkex_get_started(struct l_dbus *dbus,
 				void *user_data)
 {
 	struct dpp *dpp = user_data;
-	bool started = (dpp->state != DPP_STATE_NOTHING &&
-			dpp->interface == DPP_INTERFACE_PKEX);
+	bool started = (dpp->sm && dpp->interface == DPP_INTERFACE_PKEX);
 
 	l_dbus_message_builder_append_basic(builder, 'b', &started);
 
@@ -189,8 +168,7 @@ static bool dpp_pkex_get_role(struct l_dbus *dbus,
 	struct dpp *dpp = user_data;
 	const char *role;
 
-	if (dpp->state == DPP_STATE_NOTHING ||
-				dpp->interface != DPP_INTERFACE_PKEX)
+	if (!dpp->sm || dpp->interface != DPP_INTERFACE_PKEX)
 		return false;
 
 	role = dpp_role_to_string(dpp->role);
@@ -207,8 +185,7 @@ static bool dpp_get_started(struct l_dbus *dbus,
 				void *user_data)
 {
 	struct dpp *dpp = user_data;
-	bool started = (dpp->state != DPP_STATE_NOTHING &&
-			dpp->interface == DPP_INTERFACE_DPP);
+	bool started = (dpp->sm && dpp->interface == DPP_INTERFACE_DPP);
 
 	l_dbus_message_builder_append_basic(builder, 'b', &started);
 
@@ -223,8 +200,7 @@ static bool dpp_get_role(struct l_dbus *dbus,
 	struct dpp *dpp = user_data;
 	const char *role;
 
-	if (dpp->state == DPP_STATE_NOTHING ||
-				dpp->interface != DPP_INTERFACE_DPP)
+	if (!dpp->sm || dpp->interface != DPP_INTERFACE_DPP)
 		return false;
 
 	role = dpp_role_to_string(dpp->role);
@@ -242,8 +218,7 @@ static bool dpp_get_uri(struct l_dbus *dbus,
 {
 	struct dpp *dpp = user_data;
 
-	if (dpp->state == DPP_STATE_NOTHING ||
-				dpp->interface != DPP_INTERFACE_DPP)
+	if (!dpp->sm || dpp->interface != DPP_INTERFACE_DPP)
 		return false;
 
 	l_dbus_message_builder_append_basic(builder, 's', dpp->uri);
@@ -380,11 +355,6 @@ static void dpp_reset(struct dpp *dpp)
 		dpp->timeout = NULL;
 	}
 
-	if (dpp->config) {
-		dpp_configuration_free(dpp->config);
-		dpp->config = NULL;
-	}
-
 	if (dpp->connect_scan_id) {
 		scan_cancel(dpp->wdev_id, dpp->connect_scan_id);
 		dpp->connect_scan_id = 0;
@@ -410,7 +380,6 @@ static void dpp_reset(struct dpp *dpp)
 		dpp->connect_idle = NULL;
 	}
 
-	dpp->new_freq = 0;
 	dpp->frame_retry = 0;
 	dpp->frame_cookie = 0;
 
@@ -419,6 +388,11 @@ static void dpp_reset(struct dpp *dpp)
 	dpp_property_changed_notify(dpp);
 
 	dpp->interface = DPP_INTERFACE_UNBOUND;
+
+	if (dpp->sm) {
+		dpp_sm_free(dpp->sm);
+		dpp->sm = NULL;
+	}
 }
 
 static void dpp_free(struct dpp *dpp)
@@ -510,7 +484,27 @@ static void dpp_frame_retry(struct dpp *dpp)
 	dpp->frame_pending = NULL;
 }
 
-static void dpp_write_config(struct dpp_configuration *config,
+static size_t dpp_build_mpdu_header(const uint8_t *src,
+					const uint8_t *dest, uint8_t *buf)
+{
+	struct mmpdu_header *hdr = (struct mmpdu_header *)buf;
+	uint8_t *body;
+
+	hdr->fc.protocol_version = 0;
+	hdr->fc.type = MPDU_TYPE_MANAGEMENT;
+	hdr->fc.subtype = MPDU_MANAGEMENT_SUBTYPE_ACTION;
+	memcpy(hdr->address_1, dest, 6);
+	memcpy(hdr->address_2, src, 6);
+	memcpy(hdr->address_3, broadcast, 6);
+
+	body = buf + mmpdu_header_len(hdr);
+	/* Category: Public */
+	*body++ = 0x04;
+
+	return body - buf;
+}
+
+static void dpp_write_config(const struct dpp_configuration *config,
 				struct network *network)
 {
 	_auto_(l_settings_free) struct l_settings *settings = l_settings_new();
@@ -555,9 +549,13 @@ static void dpp_start_connect(struct l_idle *idle, void *user_data)
 	struct scan_bss *bss;
 	struct network *network;
 	int ret;
+	const struct dpp_configuration *config;
 
-	network = station_network_find(station, dpp->config->ssid,
-					SECURITY_PSK);
+	config = dpp_sm_get_configuration(dpp->sm);
+	if (L_WARN_ON(!config))
+		return;
+
+	network = station_network_find(station, config->ssid, SECURITY_PSK);
 
 	dpp_reset(dpp);
 
@@ -618,7 +616,8 @@ static void dpp_known_network_watch(enum known_networks_event event,
 					void *user_data)
 {
 	struct dpp *dpp = user_data;
-
+	struct station *station = station_find(netdev_get_ifindex(dpp->netdev));
+	const struct dpp_configuration *config;
 	/*
 	 * Check the following
 	 *  - DPP is enrolling
@@ -628,16 +627,23 @@ static void dpp_known_network_watch(enum known_networks_event event,
 	 *    immediately modified after DPP synced it).
 	 *  - DPP didn't start a scan for the network.
 	 */
+	if (!dpp->sm)
+		return;
+
+	config = dpp_sm_get_configuration(dpp->sm);
+
 	if (dpp->role != DPP_CAPABILITY_ENROLLEE)
 		return;
-	if (!dpp->config)
+	if (!config)
 		return;
-	if (strcmp(info->ssid, dpp->config->ssid))
+	if (strcmp(info->ssid, config->ssid))
 		return;
 	if (dpp->connect_idle)
 		return;
 	if (dpp->connect_scan_id)
 		return;
+	if (!station || station_get_connected_network(station))
+		return;
 
 	switch (event) {
 	case KNOWN_NETWORKS_EVENT_ADDED:
@@ -663,54 +669,40 @@ static void dpp_handle_config_frame(const struct mmpdu_header *frame,
 					int rssi, void *user_data)
 {
 	struct dpp *dpp = user_data;
-	struct dpp_configuration *config = NULL;
-	struct station *station = station_find(netdev_get_ifindex(dpp->netdev));
-	struct network *network = NULL;
-	struct scan_bss *bss = NULL;
 
-	dpp_write_config(config, network);
+	if (!dpp->sm)
+		return;
 
-	offchannel_cancel(dpp->wdev_id, dpp->offchannel_id);
+	dpp_handle_rx(dpp->sm, body + 1, body_len - 1);
+}
 
-	if (network && bss) {
-		l_debug("delaying connect until settings are synced");
-		dpp->config = config;
-		return;
-	} else if (station) {
-		struct scan_parameters params = {0};
+static void dpp_tx_frame(const uint8_t *data, size_t len,
+				void *user_data)
+{
+	struct dpp *dpp = user_data;
+	struct iovec iov[2];
+	uint8_t hdr[36];
 
-		params.ssid = (void *) config->ssid;
-		params.ssid_len = config->ssid_len;
+	memset(hdr, 0, sizeof(hdr));
 
-		l_debug("Scanning for %s", config->ssid);
+	iov[0].iov_len = dpp_build_mpdu_header(netdev_get_address(dpp->netdev),
+						dpp->peer_addr, hdr);
+	iov[0].iov_base = hdr;
 
-		dpp->connect_scan_id = scan_active_full(dpp->wdev_id, &params,
-						dpp_scan_triggered,
-						dpp_scan_results, dpp,
-						dpp_scan_destroy);
-		if (dpp->connect_scan_id) {
-			dpp->config = config;
-			return;
-		}
-	}
+	iov[1].iov_base = (void *)data;
+	iov[1].iov_len = len;
 
+	dpp_send_frame(dpp, iov, 2, dpp->current_freq);
 }
 
 static void dpp_roc_started(void *user_data)
 {
 	struct dpp *dpp = user_data;
 
-	/*
-	 * - If a configurator, nothing to do but wait for a request
-	 *   (unless multicast frame registration is unsupported in which case
-	 *   send an authenticate request now)
-	 * - If in the presence state continue sending announcements.
-	 * - If authenticating, and this is a result of a channel switch send
-	 *   the authenticate response now.
-	 */
-
 	dpp->roc_started = true;
 
+	dpp_sm_set_write_handler(dpp->sm, dpp_tx_frame);
+
 	/*
 	 * The retry timer indicates a frame was not acked in which case we
 	 * should not change any state or send any frames until that expires.
@@ -733,6 +725,8 @@ static void dpp_offchannel_timeout(int error, void *user_data)
 	dpp->offchannel_id = 0;
 	dpp->roc_started = false;
 
+	dpp_sm_set_write_handler(dpp->sm, NULL);
+
 	/*
 	 * If cancelled this is likely due to netdev going down or from Stop().
 	 * Otherwise there was some other problem which is probably not
@@ -790,6 +784,8 @@ static void dpp_start_offchannel(struct dpp *dpp, uint32_t freq)
 	if (dpp->offchannel_id)
 		offchannel_cancel(dpp->wdev_id, dpp->offchannel_id);
 
+	l_debug("starting offchannel on frequency %u, dwell=%u", freq, dpp->dwell);
+
 	dpp->offchannel_id = id;
 }
 
@@ -797,23 +793,16 @@ static void dpp_handle_frame(struct dpp *dpp,
 				const struct mmpdu_header *frame,
 				const void *body, size_t body_len)
 {
-	const uint8_t *ptr;
+	if (!dpp->sm)
+		return;
 
-	/*
-	 * Both handlers offset by 8 bytes to reach the beginning of the DPP
-	 * attributes. Easier checking this in one place, which also covers the
-	 * frame type byte.
-	 */
-	if (body_len < 8)
+	/* Frame from a different device after DPP has already started */
+	if (dpp->peer_accepted && memcmp(dpp->peer_addr, frame->address_2, 6))
 		return;
 
-	ptr = body + 7;
+	memcpy(dpp->peer_addr, frame->address_2, 6);
 
-	switch (*ptr) {
-	default:
-		l_debug("Unhandled DPP frame %u", *ptr);
-		break;
-	}
+	dpp_handle_rx(dpp->sm, body + 1, body_len - 1);
 }
 
 static bool match_wdev(const void *a, const void *b)
@@ -851,6 +840,7 @@ static void dpp_mlme_notify(struct l_genl_msg *msg, void *user_data)
 	bool ack = false;
 	struct iovec iov;
 	uint8_t cmd = l_genl_msg_get_command(msg);
+	enum dpp_state state;
 
 	if (cmd != NL80211_CMD_FRAME_TX_STATUS)
 		return;
@@ -863,16 +853,17 @@ static void dpp_mlme_notify(struct l_genl_msg *msg, void *user_data)
 		return;
 
 	dpp = l_queue_find(dpp_list, match_wdev, &wdev_id);
-	if (!dpp)
+	if (!dpp || !dpp->sm)
 		return;
 
+	state = dpp_sm_get_state(dpp->sm);
+
 	/*
 	 * Don't retransmit for presence or PKEX exchange if an enrollee, both
 	 * are broadcast frames which don't expect an ack.
 	 */
-	if (dpp->state == DPP_STATE_NOTHING ||
-			dpp->state == DPP_STATE_PRESENCE ||
-			(dpp->state == DPP_STATE_PKEX_EXCHANGE &&
+	if (state == DPP_STATE_PRESENCE ||
+			(state == DPP_STATE_PKEX_EXCHANGE &&
 			dpp->role == DPP_CAPABILITY_ENROLLEE))
 		return;
 
@@ -886,20 +877,6 @@ static void dpp_mlme_notify(struct l_genl_msg *msg, void *user_data)
 	if (!ack)
 		goto retransmit;
 
-	/*
-	 * Special handling for a channel transition when acting as a
-	 * configurator. The auth request was sent offchannel so we need to
-	 * wait for the ACK before going back to the connected channel.
-	 */
-	if (dpp->channel_switch) {
-		if (dpp->offchannel_id) {
-			offchannel_cancel(dpp->wdev_id, dpp->offchannel_id);
-			dpp->offchannel_id = 0;
-		}
-
-		dpp->channel_switch = false;
-	}
-
 	return;
 
 retransmit:
@@ -1035,7 +1012,7 @@ static void dpp_station_state_watch(enum station_state state, void *user_data)
 {
 	struct dpp *dpp = user_data;
 
-	if (dpp->state == DPP_STATE_NOTHING)
+	if (!dpp->sm)
 		return;
 
 	switch (state) {
@@ -1089,17 +1066,20 @@ static void dpp_create(struct netdev *netdev)
 	uint8_t dpp_conf_request_prefix[] = { 0x04, 0x0a };
 	uint64_t wdev_id = netdev_get_wdev_id(netdev);
 	struct station *station = station_find(netdev_get_ifindex(netdev));
+	const struct l_ecc_curve *curve = l_ecc_curve_from_ike_group(19);
 
 	dpp->netdev = netdev;
-	dpp->state = DPP_STATE_NOTHING;
 	dpp->interface = DPP_INTERFACE_UNBOUND;
 	dpp->wdev_id = wdev_id;
-
 	dpp->max_roc = wiphy_get_max_roc_duration(wiphy_find_by_wdev(wdev_id));
 	dpp->mcast_support = wiphy_has_ext_feature(
 				wiphy_find_by_wdev(dpp->wdev_id),
 				NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS);
 
+	/* TODO: Support a user-provided key pair */
+	l_ecdh_generate_key_pair(curve, &dpp->boot_private,
+					&dpp->boot_public);
+
 	l_dbus_object_add_interface(dbus, netdev_get_path(netdev),
 					IWD_DPP_INTERFACE, dpp);
 	l_dbus_object_add_interface(dbus, netdev_get_path(netdev),
@@ -1212,6 +1192,175 @@ static void dpp_start_presence(struct dpp *dpp, uint32_t *limit_freqs,
 	dpp_start_offchannel(dpp, dpp->current_freq);
 }
 
+static void dpp_event_success(struct dpp *dpp)
+{
+	struct station *station = station_find(netdev_get_ifindex(dpp->netdev));
+	struct network *network = NULL;
+	struct scan_bss *bss = NULL;
+	const struct dpp_configuration *config;
+
+	config = dpp_sm_get_configuration(dpp->sm);
+	if (L_WARN_ON(!config))
+		goto reset;
+
+	/*
+	 * We should have a station device, but if not DPP can write the
+	 * credentials out and be done
+	 */
+	if (station) {
+		network = station_network_find(station, config->ssid,
+						SECURITY_PSK);
+		if (network)
+			bss = network_bss_select(network, true);
+	}
+
+	dpp_write_config(config, network);
+
+	offchannel_cancel(dpp->wdev_id, dpp->offchannel_id);
+	dpp->offchannel_id = 0;
+
+	if (network && bss) {
+		l_debug("delaying connect until settings are synced");
+		return;
+	} else if (station) {
+		struct scan_parameters params = {0};
+
+		params.ssid = (void *) config->ssid;
+		params.ssid_len = config->ssid_len;
+
+		l_debug("Scanning for %s", config->ssid);
+
+		dpp->connect_scan_id = scan_active_full(dpp->wdev_id, &params,
+						dpp_scan_triggered,
+						dpp_scan_results, dpp,
+						dpp_scan_destroy);
+		if (dpp->connect_scan_id)
+			return;
+	}
+
+reset:
+	dpp_reset(dpp);
+}
+
+static void dpp_event_channel_switch(struct dpp *dpp,
+					const uint8_t *attr)
+{
+	uint32_t freq;
+
+	freq = oci_to_frequency(attr[0], attr[1]);
+
+	if (freq == dpp->current_freq)
+		return;
+
+	dpp->current_freq = freq;
+
+	dpp_start_offchannel(dpp, dpp->current_freq);
+
+	/*
+	 * The common code will attempt to write after this, so ensure that
+	 * gets delayed until we are on the right channel
+	 */
+	dpp_sm_set_write_handler(dpp->sm, NULL);
+}
+
+static void dpp_pkex_agent_reply(struct l_dbus_message *message,
+					void *user_data)
+{
+	struct dpp *dpp = user_data;
+	const char *error, *text;
+	const char *code;
+
+	dpp->agent->pending_id = 0;
+
+	l_debug("SharedCodeAgent %s path %s replied", dpp->agent->owner,
+			dpp->agent->path);
+
+	if (l_dbus_message_get_error(message, &error, &text)) {
+		l_error("RequestSharedCode() returned %s(\"%s\")",
+				error, text);
+		return;
+	}
+
+	if (!l_dbus_message_get_arguments(message, "s", &code)) {
+		l_debug("Invalid arguments, check SharedCodeAgent!");
+		return;
+	}
+
+	dpp_sm_set_pkex_key(dpp->sm, code);
+}
+
+static bool dpp_event_pkex_key_requested(struct dpp *dpp,
+					const char *identifier)
+{
+	struct l_dbus_message *msg;
+
+	if (!dpp->agent)
+		return false;
+
+	if (L_WARN_ON(dpp->agent->pending_id))
+		return false;
+
+	msg = l_dbus_message_new_method_call(dbus_get_bus(),
+						dpp->agent->owner,
+						dpp->agent->path,
+						IWD_SHARED_CODE_AGENT_INTERFACE,
+						"RequestSharedCode");
+	l_dbus_message_set_arguments(msg, "s", identifier);
+
+	dpp->agent->pending_id = l_dbus_send_with_reply(dbus_get_bus(),
+							msg,
+							dpp_pkex_agent_reply,
+							dpp, NULL);
+	return dpp->agent->pending_id != 0;
+}
+
+static void dpp_event_peer_accepted(struct dpp *dpp)
+{
+	dpp->peer_accepted = true;
+
+	if (dpp_sm_get_state(dpp->sm) != DPP_STATE_PKEX_EXCHANGE)
+		return;
+
+	dpp_sm_set_pkex_peer_mac(dpp->sm, dpp->peer_addr);
+
+	/*
+	 * PKEX dictates a 200ms timeout waiting for the exchange
+	 * response. After this there is no time requirement for the
+	 * remainder of the protocol. Increase the dwell time so we
+	 * have the best chance of receiving frames since we will now
+	 * remain on a single frequency.
+	 */
+	if (dpp->role == DPP_CAPABILITY_ENROLLEE)
+		dpp->dwell = (dpp->max_roc < 2000) ? dpp->max_roc : 2000;
+}
+
+static void dpp_event(enum dpp_event event, const void *event_data,
+			void *user_data)
+{
+	struct dpp *dpp = user_data;
+
+	switch (event) {
+	case DPP_EVENT_PEER_ACCEPTED:
+		dpp_event_peer_accepted(dpp);
+		break;
+	case DPP_EVENT_CHANNEL_SWITCH:
+		dpp_event_channel_switch(dpp, event_data);
+		break;
+	case DPP_EVENT_PKEX_KEY_REQUESTED:
+		dpp_event_pkex_key_requested(dpp, event_data);
+		break;
+	case DPP_EVENT_FAILED:
+		dpp_reset(dpp);
+		break;
+	case DPP_EVENT_SUCCESS:
+		if (dpp->role == DPP_CAPABILITY_ENROLLEE)
+			dpp_event_success(dpp);
+		else
+			dpp_reset(dpp);
+		break;
+	}
+}
+
 static struct l_dbus_message *dpp_dbus_start_enrollee(struct l_dbus *dbus,
 						struct l_dbus_message *message,
 						void *user_data)
@@ -1219,9 +1368,11 @@ static struct l_dbus_message *dpp_dbus_start_enrollee(struct l_dbus *dbus,
 	struct dpp *dpp = user_data;
 	uint32_t freq = band_channel_to_freq(6, BAND_FREQ_2_4_GHZ);
 	struct station *station = station_find(netdev_get_ifindex(dpp->netdev));
+	struct l_dbus_message *reply;
+	const uint8_t *own_asn1;
+	size_t own_asn1_len;
 
-	if (dpp->state != DPP_STATE_NOTHING ||
-				dpp->interface != DPP_INTERFACE_UNBOUND)
+	if (dpp->sm || dpp->interface != DPP_INTERFACE_UNBOUND)
 		return dbus_error_busy(message);
 
 	/*
@@ -1234,14 +1385,23 @@ static struct l_dbus_message *dpp_dbus_start_enrollee(struct l_dbus *dbus,
 	} else if (!station)
 		l_debug("No station device, continuing anyways...");
 
-	dpp->state = DPP_STATE_PRESENCE;
+	dpp->sm = dpp_sm_new(dpp_event, dpp->boot_public,
+				dpp->boot_private, dpp);
+
+	dpp_sm_set_role(dpp->sm, DPP_CAPABILITY_ENROLLEE);
+	dpp_sm_start_responder(dpp->sm);
+
+	own_asn1 = dpp_sm_get_own_asn1(dpp->sm, &own_asn1_len);
+
+	dpp->uri = dpp_generate_uri(own_asn1, own_asn1_len, 2,
+					netdev_get_address(dpp->netdev), &freq,
+					1, NULL, NULL);
+
 	dpp->role = DPP_CAPABILITY_ENROLLEE;
 	dpp->interface = DPP_INTERFACE_DPP;
 
 	l_debug("DPP Start Enrollee: %s", dpp->uri);
 
-	dpp->pending = l_dbus_message_ref(message);
-
 	/*
 	 * Going off spec here. Select a single channel to send presence
 	 * announcements on. This will be advertised in the URI. The full
@@ -1251,7 +1411,13 @@ static struct l_dbus_message *dpp_dbus_start_enrollee(struct l_dbus *dbus,
 
 	dpp_property_changed_notify(dpp);
 
-	return NULL;
+	memcpy(dpp->peer_addr, broadcast, 6);
+
+	reply = l_dbus_message_new_method_return(message);
+
+	l_dbus_message_set_arguments(reply, "s", dpp->uri);
+
+	return reply;
 }
 
 /*
@@ -1284,12 +1450,16 @@ static bool dpp_configurator_start_presence(struct dpp *dpp, const char *uri)
 		return false;
 	}
 
-	if (!l_memeqzero(info->mac, 6))
+	if (!l_memeqzero(info->mac, 6)) {
 		memcpy(dpp->peer_addr, info->mac, 6);
+		/* Set now so we restrict to only frames from this MAC */
+		dpp->peer_accepted = true;
+	}
 
 	if (info->freqs)
 		freqs = scan_freq_set_to_fixed_array(info->freqs, &freqs_len);
 
+	dpp_sm_set_peer_bootstrap(dpp->sm, info->boot_public);
 	dpp_free_uri_info(info);
 
 	dpp_start_presence(dpp, freqs, freqs_len);
@@ -1311,6 +1481,9 @@ static struct l_dbus_message *dpp_start_configurator_common(
 	struct l_settings *settings;
 	struct handshake_state *hs = netdev_get_handshake(dpp->netdev);
 	const char *uri;
+	struct dpp_configuration *config;
+	const uint8_t *own_asn1;
+	size_t own_asn1_len;
 
 	/*
 	 * For now limit the configurator to only configuring enrollees to the
@@ -1331,11 +1504,11 @@ static struct l_dbus_message *dpp_start_configurator_common(
 	if (network_get_security(network) != SECURITY_PSK)
 		return dbus_error_not_supported(message);
 
-	if (dpp->state != DPP_STATE_NOTHING ||
-				dpp->interface != DPP_INTERFACE_UNBOUND)
+	if (dpp->sm || dpp->interface != DPP_INTERFACE_UNBOUND)
 		return dbus_error_busy(message);
 
-	dpp->state = DPP_STATE_PRESENCE;
+	dpp->sm = dpp_sm_new(dpp_event, dpp->boot_public,
+				dpp->boot_private, dpp);
 
 	if (!responder) {
 		if (!l_dbus_message_get_arguments(message, "s", &uri))
@@ -1345,26 +1518,47 @@ static struct l_dbus_message *dpp_start_configurator_common(
 			return dbus_error_invalid_args(message);
 
 		if (!dpp->mcast_support)
-			dpp->state = DPP_STATE_AUTHENTICATING;
-
-		dpp->new_freq = bss->frequency;
+			dpp_sm_set_skip_presence(dpp->sm, true);
 	} else
 		dpp->current_freq = bss->frequency;
 
-	dpp->role = DPP_CAPABILITY_CONFIGURATOR;
-	dpp->interface = DPP_INTERFACE_DPP;
-	dpp->config = dpp_configuration_new(settings,
-						network_get_ssid(network),
+	config = dpp_configuration_new(settings, network_get_ssid(network),
 						hs->akm_suite);
 
+	dpp_sm_set_configuration(dpp->sm, config);
+	dpp_sm_set_role(dpp->sm, DPP_CAPABILITY_CONFIGURATOR);
+
+	own_asn1 = dpp_sm_get_own_asn1(dpp->sm, &own_asn1_len);
+
+	dpp->uri = dpp_generate_uri(own_asn1, own_asn1_len, 2,
+					netdev_get_address(dpp->netdev),
+					&bss->frequency, 1, NULL, NULL);
+	dpp->interface = DPP_INTERFACE_DPP;
+
+	dpp->dwell = (dpp->max_roc < 2000) ? dpp->max_roc : 2000;
+
 	dpp_property_changed_notify(dpp);
 
+	dpp->role = DPP_CAPABILITY_CONFIGURATOR;
+
+	memcpy(dpp->peer_addr, broadcast, 6);
+
 	l_debug("DPP Start Configurator: %s", dpp->uri);
 
 	reply = l_dbus_message_new_method_return(message);
 
 	l_dbus_message_set_arguments(reply, "s", dpp->uri);
 
+	/*
+	 * Configurators acting as responders are always on channel so
+	 * the writes can be done at any point.
+	 */
+	if (responder) {
+		dpp_sm_set_write_handler(dpp->sm, dpp_tx_frame);
+		dpp_sm_start_responder(dpp->sm);
+	} else
+		dpp_sm_start_initiator(dpp->sm);
+
 	return reply;
 }
 
@@ -1480,10 +1674,12 @@ static bool dpp_pkex_scan_notify(int err, struct l_queue *bss_list,
 start:
 	dpp->current_freq = dpp->freqs[0];
 
-	l_debug("PKEX start enrollee (id=%s)", dpp->pkex_id ?: "unset");
+	l_debug("PKEX start enrollee");
 
 	dpp_start_offchannel(dpp, dpp->current_freq);
 
+	dpp_sm_pkex_start_initiator(dpp->sm);
+
 	return false;
 
 failed:
@@ -1503,14 +1699,20 @@ static bool dpp_start_pkex_enrollee(struct dpp *dpp, const char *key,
 {
 	_auto_(l_ecc_point_free) struct l_ecc_point *qi = NULL;
 
+	dpp->sm = dpp_sm_new(dpp_event, dpp->boot_public,
+				dpp->boot_private, dpp);
+
 	if (identifier)
-		dpp->pkex_id = l_strdup(identifier);
+		dpp_sm_set_pkex_identifier(dpp->sm, identifier);
+
+	dpp_sm_set_pkex_key(dpp->sm, key);
+	dpp_sm_set_pkex_own_mac(dpp->sm, netdev_get_address(dpp->netdev));
+	dpp_sm_set_role(dpp->sm, DPP_CAPABILITY_ENROLLEE);
 
-	dpp->pkex_key = l_strdup(key);
 	memcpy(dpp->peer_addr, broadcast, 6);
 	dpp->role = DPP_CAPABILITY_ENROLLEE;
-	dpp->state = DPP_STATE_PKEX_EXCHANGE;
 	dpp->interface = DPP_INTERFACE_PKEX;
+
 	/*
 	 * In theory a driver could support a lesser duration than 200ms. This
 	 * complicates things since we would need to tack on additional
@@ -1596,8 +1798,7 @@ static struct l_dbus_message *dpp_dbus_pkex_start_enrollee(struct l_dbus *dbus,
 
 	l_debug("");
 
-	if (dpp->state != DPP_STATE_NOTHING ||
-				dpp->interface != DPP_INTERFACE_UNBOUND)
+	if (dpp->sm || dpp->interface != DPP_INTERFACE_UNBOUND)
 		return dbus_error_busy(message);
 
 	if (station && station_get_connected_network(station))
@@ -1651,9 +1852,9 @@ static struct l_dbus_message *dpp_start_pkex_configurator(struct dpp *dpp,
 	struct network *network = station_get_connected_network(station);
 	struct scan_bss *bss = station_get_connected_bss(station);
 	const struct l_settings *settings;
+	struct dpp_configuration *config;
 
-	if (dpp->state != DPP_STATE_NOTHING ||
-				dpp->interface != DPP_INTERFACE_UNBOUND)
+	if (dpp->sm || dpp->interface != DPP_INTERFACE_UNBOUND)
 		return dbus_error_busy(message);
 
 	if (!dpp->mcast_support) {
@@ -1671,30 +1872,39 @@ static struct l_dbus_message *dpp_start_pkex_configurator(struct dpp *dpp,
 		return dbus_error_not_configured(message);
 	}
 
+	dpp->sm = dpp_sm_new(dpp_event, dpp->boot_public,
+				dpp->boot_private, dpp);
+
 	if (identifier)
-		dpp->pkex_id = l_strdup(identifier);
+		dpp_sm_set_pkex_identifier(dpp->sm, identifier);
 
 	if (key)
-		dpp->pkex_key = l_strdup(key);
+		dpp_sm_set_pkex_key(dpp->sm, key);
 
 	if (agent_path)
 		dpp_create_agent(dpp, agent_path, message);
 
 	dpp->role = DPP_CAPABILITY_CONFIGURATOR;
-	dpp->state = DPP_STATE_PKEX_EXCHANGE;
 	dpp->interface = DPP_INTERFACE_PKEX;
 	dpp->current_freq = bss->frequency;
-	dpp->config = dpp_configuration_new(network_get_settings(network),
+	config = dpp_configuration_new(network_get_settings(network),
 						network_get_ssid(network),
 						hs->akm_suite);
 
+	dpp_sm_set_role(dpp->sm, DPP_CAPABILITY_CONFIGURATOR);
+	dpp_sm_set_configuration(dpp->sm, config);
+	dpp_sm_set_pkex_own_mac(dpp->sm, netdev_get_address(dpp->netdev));
+	dpp_sm_set_write_handler(dpp->sm, dpp_tx_frame);
+
 	dpp_property_changed_notify(dpp);
 
-	if (dpp->pkex_key)
+	if (key)
 		l_debug("Starting PKEX configurator for single enrollee");
 	else
 		l_debug("Starting PKEX configurator with agent");
 
+	dpp_sm_pkex_start_responder(dpp->sm);
+
 	return l_dbus_message_new_method_return(message);
 }
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/9] dpp: prep for moving AAD within dpp_append_wrapped_data
  2024-03-27 15:19 [PATCH 1/9] dpp: prep for moving AAD within dpp_append_wrapped_data James Prestwood
                   ` (7 preceding siblings ...)
  2024-03-27 15:19 ` [PATCH 9/9] dpp: use common state machine James Prestwood
@ 2024-04-02 15:10 ` Denis Kenzior
  2024-04-15 14:02   ` James Prestwood
  8 siblings, 1 reply; 15+ messages in thread
From: Denis Kenzior @ 2024-04-02 15:10 UTC (permalink / raw)
  To: James Prestwood, iwd

Hi James,

On 3/27/24 10:19, James Prestwood wrote:
> The AAD pointers for DPP are specific to the frame type. This is
> currently sorted out by the caller within the respective frame
> building functions but its quite unreadable. There are some comments
> but lots of magic numbers. This should be moved within the
> dpp_append_wrapped_data utility but the first step is to make the
> frame buffer continuous. This will allow the entire frame to be

continuous -> contiguous?

> passed and dpp_append_wrapped_data can calculate the AAD offsets
> itself.

I'm a bit confused as to why?  Whether we have an iov or a contiguous buffer, 
the end result is the same, no?  If you want to omit parts of the header from 
the wrapped data calculation, wouldn't you just break up the header into 
multiple iovs instead?

> ---
>   src/dpp.c | 239 +++++++++++++++++++++++++-----------------------------
>   1 file changed, 112 insertions(+), 127 deletions(-)
> 

<snip>

> diff --git a/src/dpp.c b/src/dpp.c
> index 567fe8d2..5aac22a7 100644
> --- a/src/dpp.c
> +++ b/src/dpp.c
> @@ -648,7 +648,7 @@ static void dpp_frame_retry(struct dpp_sm *dpp)
>   
>   static size_t dpp_build_header(const uint8_t *src, const uint8_t *dest,
>   				enum dpp_frame_type type,
> -				uint8_t buf[static 32])
> +				uint8_t *buf)

Is this really needed?

>   {
>   	uint8_t *ptr = buf + 24;
>   
> @@ -672,7 +672,7 @@ static size_t dpp_build_header(const uint8_t *src, const uint8_t *dest,
>   
>   static size_t dpp_build_config_header(const uint8_t *src, const uint8_t *dest,
>   					uint8_t diag_token,
> -					uint8_t buf[static 37])
> +					uint8_t *buf)

Or this?

>   {
>   	uint8_t *ptr = buf + 24;
>   

<snip>

> @@ -780,42 +779,39 @@ static void dpp_configuration_start(struct dpp_sm *dpp, const uint8_t *addr)
>   	 * In this case there is no query request/response fields, nor any
>   	 * attributes besides wrapped data meaning zero AD components.
>   	 */
> -	ptr += dpp_append_wrapped_data(NULL, 0, NULL, 0, ptr, sizeof(attrs),
> +	ptr += dpp_append_wrapped_data(NULL, 0, NULL, 0, ptr, sizeof(frame),

The sizeof(frame) is now likely wrong in this setup.  No error checking also 
worries me a bit.

<snip>

>   static void send_config_result(struct dpp_sm *dpp, const uint8_t *to)
>   {
> -	uint8_t hdr[32];
> -	struct iovec iov[2];
> -	uint8_t attrs[256];
> -	uint8_t *ptr = attrs;
> +	struct iovec iov;
> +	uint8_t frame[256];
> +	uint8_t *ptr = frame;
>   	uint8_t zero = 0;
>   
> -	iov[0].iov_len = dpp_build_header(netdev_get_address(dpp->netdev), to,
> -					DPP_FRAME_CONFIGURATION_RESULT, hdr);
> -	iov[0].iov_base = hdr;
> -
> -	ptr += dpp_append_wrapped_data(hdr + 26, 6, attrs, 0, ptr,
> -			sizeof(attrs), dpp->ke, dpp->key_len, 2,
> +	ptr += dpp_build_header(netdev_get_address(dpp->netdev), to,
> +					DPP_FRAME_CONFIGURATION_RESULT, ptr);
> +	ptr += dpp_append_wrapped_data(frame + 26, 6, ptr, 0, ptr,
> +			sizeof(frame), dpp->ke, dpp->key_len, 2,

Same comment here

>   			DPP_ATTR_STATUS, (size_t) 1, &zero,
>   			DPP_ATTR_ENROLLEE_NONCE, dpp->nonce_len, dpp->e_nonce);
>   
> -	iov[1].iov_base = attrs;
> -	iov[1].iov_len = ptr - attrs;
> +	iov.iov_base = frame;
> +	iov.iov_len = ptr - frame;
>   
> -	dpp_send_frame(dpp, iov, 2, dpp->current_freq);
> +	dpp_send_frame(dpp, &iov, 1, dpp->current_freq);
>   }
>   
>   static void dpp_write_config(struct dpp_configuration *config,

<snip>

> @@ -1211,26 +1205,26 @@ static void dpp_send_config_response(struct dpp_sm *dpp, uint8_t status)
>   		json = dpp_configuration_to_json(dpp->config);
>   		json_len = strlen(json);
>   
> -		ptr += dpp_append_wrapped_data(attrs + 2, ptr - attrs - 2,
> -						NULL, 0, ptr, sizeof(attrs),
> +		ptr += dpp_append_wrapped_data(lptr + 2, ptr - lptr - 2,
> +						NULL, 0, ptr, sizeof(frame),

Is sizeof(frame) correct here?

>   						dpp->ke, dpp->key_len, 2,
>   						DPP_ATTR_ENROLLEE_NONCE,
>   						dpp->nonce_len, dpp->e_nonce,
>   						DPP_ATTR_CONFIGURATION_OBJECT,
>   						json_len, json);
>   	} else
> -		ptr += dpp_append_wrapped_data(attrs + 2, ptr - attrs - 2,
> -						NULL, 0, ptr, sizeof(attrs),
> +		ptr += dpp_append_wrapped_data(lptr + 2, ptr - lptr - 2,
> +						NULL, 0, ptr, sizeof(frame),

and here?

>   						dpp->ke, dpp->key_len, 2,
>   						DPP_ATTR_ENROLLEE_NONCE,
>   						dpp->nonce_len, dpp->e_nonce);
>   
> -	l_put_le16(ptr - attrs - 2, attrs);
> +	l_put_le16(ptr - lptr - 2, lptr);
>   
> -	iov[1].iov_base = attrs;
> -	iov[1].iov_len = ptr - attrs;
> +	iov.iov_base = frame;
> +	iov.iov_len = ptr - frame;
>   
> -	dpp_send_frame(dpp, iov, 2, dpp->current_freq);
> +	dpp_send_frame(dpp, &iov, 1, dpp->current_freq);
>   }
>   
>   static bool dpp_check_config_header(const uint8_t *ptr)

<snip>

Regards,
-Denis

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 2/9] dpp-util: move AAD logic within dpp_append_wrapped_attributes
  2024-03-27 15:19 ` [PATCH 2/9] dpp-util: move AAD logic within dpp_append_wrapped_attributes James Prestwood
@ 2024-04-02 15:19   ` Denis Kenzior
  0 siblings, 0 replies; 15+ messages in thread
From: Denis Kenzior @ 2024-04-02 15:19 UTC (permalink / raw)
  To: James Prestwood, iwd

Hi James,

On 3/27/24 10:19, James Prestwood wrote:
> Leaving it up to the caller to calcluate the AAD resulted in lots of

typo, 'calculate'

> magic values, and any comments associated are spread out within
> dpp.c. The AAD values can be calculated entirely by the frame
> contents so move that within dpp_append_wrapped_data.
> 
> The caller now only needs to pass the frame (after the mpdu header),
> the length, and the offset to where the wrapped data should start.
> The new AAD calculation includes all relavent comments so magic

typo, 'relevant'

> offsets are documented.
> 
> The reason the entire mmpdu_body is not passed to
> dpp_append_wrapped_attributes (and one byte further) is to future
> proof for DPP encapsulation using TCP. For this, the category byte
> is omitted and only the action byte and further is encapsulated.
> Having dpp_append_wrapped_attributes start at the action byte
> allows it to work regardless of 8021x or TCP encapsulation.

Can you make the outer header its own iov?

> ---
>   src/dpp-util.c | 167 +++++++++++++++++++++++++++++++++++++++++++++----
>   src/dpp-util.h |   5 +-
>   src/dpp.c      |  99 +++++++++++++++++------------
>   3 files changed, 218 insertions(+), 53 deletions(-)
> 

<snip>

> +static bool dpp_aad(const uint8_t *frame, size_t frame_len, uint8_t *to,
> +			const uint8_t **ad0, size_t *ad0_len,
> +			const uint8_t **ad1, size_t *ad1_len)
> +{
> +	/* For PKEX frames */
> +	static uint8_t zero = 0;
> +	static uint8_t one = 1;

const?

> +	enum dpp_frame_type type;
> +	/* OUI field (inclusive) */
> +	const uint8_t *start = frame + 1;
> +
> +	if (frame_len < 6)
> +		return false;
> +
> +	type = l_get_u8(frame + 6);
> +
> +	switch (type) {
> +
> +	case DPP_FRAME_AUTHENTICATION_REQUEST:
> +	case DPP_FRAME_AUTHENTICATION_RESPONSE:
> +	case DPP_FRAME_AUTHENTICATION_CONFIRM:
> +	case DPP_FRAME_CONFIGURATION_RESULT:
> +		/*
> +		 * Section 6.3.1.4 Protocol Conventions
> +		 * All other invocations of AES-SIV in the DPP Authentication
> +		 * protocol shall pass a vector of AAD having two components of
> +		 * AAD in the following order:
> +		 *     (1) the DPP header, as defined in Table 34, from the OUI
> +		 *         field (inclusive) to the DPP Frame Type field
> +		 *         (inclusive); and
> +		 *     (2) all octets in a DPP Public Action frame after the DPP
> +		 *         Frame Type field up to and including the last octet
> +		 *         of the last attribute before the Wrapped Data
> +		 *         attribute
> +		 *
> +		 * Note: The configuration result frame uses identical wordage
> +		 *       but is in Section 6.4.1
> +		 */
> +		*ad0 = start;
> +		*ad0_len = DPP_HDR_LEN;
> +		*ad1 = start + DPP_HDR_LEN;
> +		*ad1_len = to - start - DPP_HDR_LEN;
> +		return true;
> +	case DPP_FRAME_PKEX_COMMIT_REVEAL_REQUEST:
> +		/*
> +		 * The AAD for this operation shall consist of two components:
> +		 *     (1) the DPP header, as defined in Table 34, from the OUI
> +		 *         field (inclusive) to the DPP Frame Type field
> +		 *         (inclusive); and
> +		 *     (2) a single octet of the value zero
> +		 */
> +		*ad0 = start;
> +		*ad0_len = DPP_HDR_LEN;
> +		*ad1 = &zero;
> +		*ad1_len = 1;
> +		return true;
> +	case DPP_FRAME_PKEX_COMMIT_REVEAL_RESPONSE:
> +		/*
> +		 * The AAD for this operation shall consist of two components:
> +		 *     (1) the DPP header, as defined in Table 34, from the OUI
> +		 *         field (inclusive) to the DPP Frame Type field
> +		 *         (inclusive); and
> +		 *     (2) a single octet of the value one
> +		 */
> +		*ad0 = start;
> +		*ad0_len = DPP_HDR_LEN;
> +		*ad1 = &one;
> +		*ad1_len = 1;
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
>   /*
> - * Encrypt DPP attributes encapsulated in DPP wrapped data.
> - *
> - * ad0/ad0_len - frame specific AD0 component
> - * ad1/ad0_len - frame specific AD1 component
> - * to - buffer to encrypt data.
> - * to_len - size of 'to'
> + * frame - start of action frame (excluding mpdu header and category)
> + * frame_len - total frame buffer size

What does 'total' mean here?

> + * to - current position of DPP attributes (where wrapped data will start)
>    * key - key used to encrypt
>    * key_len - size of 'key'
>    * num_attrs - number of attributes listed (type, length, data triplets)
>    * ... - List of attributes, Type, Length, and data
>    */
> -size_t dpp_append_wrapped_data(const void *ad0, size_t ad0_len,
> -				const void *ad1, size_t ad1_len,
> -				uint8_t *to, size_t to_len,
> -				const void *key, size_t key_len,
> +size_t dpp_append_wrapped_data(const uint8_t *frame, size_t frame_len,
> +				uint8_t *to, const void *key, size_t key_len,

Why do you drop the to_len parameter?

>   				size_t num_attrs, ...)
>   {
>   	size_t i;
> @@ -488,6 +562,77 @@ size_t dpp_append_wrapped_data(const void *ad0, size_t ad0_len,
>   	struct iovec ad[2];
>   	size_t ad_size = 0;
>   	va_list va;
> +	uint8_t action;
> +	const uint8_t *ad0 = NULL;
> +	const uint8_t *ad1 = NULL;
> +	size_t ad0_len, ad1_len;
> +
> +	/*
> +	 * First determine the frame type. This could be passed in but due to
> +	 * The config protocol using GAS request/response frames not all frames
> +	 * map to a dpp_frame_type enum. Due to this, minimal parsing is done
> +	 * on the frame to determine the type, and in turn the AAD
> +	 * offsets/lengths.
> +	 */
> +	if (frame_len < 1)
> +		return 0;

This seems suspicious.  Should the error return type be ssize_t and should this 
be returning a -errno?

> +
> +	action = *frame;
> +
> +	switch (action) {
> +	case DPP_ACTION_VENDOR_SPECIFIC:
> +		if (!dpp_aad(frame, frame_len, to, &ad0, &ad0_len,
> +				&ad1, &ad1_len))
> +			return 0;
> +
> +		break;
> +	/*
> +	 * Section 6.4.1 Overview
> +	 *
> +	 * "AAD for use with AES-SIV for protected messages in the DPP
> +	 * Configuration protocol shall consist of all octets in the
> +	 * Query Request and Query Response fields up to the first octet
> +	 * of the Wrapped Data attribute, which is the last attribute in a DPP
> +	 * Configuration frame. When the number of octets of AAD is zero, the
> +	 * number of components of AAD passed to AES-SIV is zero
> +	 */
> +	case DPP_ACTION_GAS_REQUEST:
> +		/*
> +		 * 8.3.2 DPP Configuration Request frame
> +		 * The attributes begin 14 bytes after the action (inclusive)
> +		 */
> +		if (frame_len < 14)
> +			return 0;
> +
> +		/* Start of query request */
> +		ad0 = frame + 14;
> +		/* "up to the first octet of the Wrapped Data attribute" */
> +		ad0_len = to - frame - 14;
> +
> +		if (!ad0_len)
> +			ad0 = NULL;
> +
> +		break;
> +	case DPP_ACTION_GAS_RESPONSE:
> +		/*
> +		 * 8.3.3 DPP Configuration Response frame
> +		 * The attributes begin 18 bytes after the action (inclusive)
> +		 */
> +		if (frame_len < 18)
> +			return 0;
> +
> +		/* Start of query response */
> +		ad0 = frame + 18;
> +		/* "up to the first octet of the Wrapped Data attribute" */
> +		ad0_len = to - frame - 18;
> +
> +		if (!ad0_len)
> +			ad0 = NULL;
> +
> +		break;
> +	default:
> +		return 0;
> +	}
>   
>   	va_start(va, num_attrs);
>   

<snip>

> @@ -758,6 +758,9 @@ static void dpp_configuration_start(struct dpp_sm *dpp, const uint8_t *addr)
>   	size_t json_len = strlen(json);
>   	uint8_t *ptr = frame;
>   	uint8_t *lptr;
> +	struct mmpdu_header *hdr = (struct mmpdu_header *)frame;
> +
> +	memset(frame, 0, sizeof(frame));

No explanation as to why this is needed?

>   
>   	l_getrandom(&dpp->diag_token, 1);
>   

Regards,
-Denis

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 6/9] dpp-common: add TX/RX handlers to dpp_sm
  2024-03-27 15:19 ` [PATCH 6/9] dpp-common: add TX/RX handlers to dpp_sm James Prestwood
@ 2024-04-02 15:27   ` Denis Kenzior
  2024-04-15 14:05     ` James Prestwood
  0 siblings, 1 reply; 15+ messages in thread
From: Denis Kenzior @ 2024-04-02 15:27 UTC (permalink / raw)
  To: James Prestwood, iwd

Hi James,

On 3/27/24 10:19, James Prestwood wrote:
> This copies the bulk of the protocol handling from dpp.c into
> dpp-common.c. There are no logical changes but some adaptations
> needed to be made in order to isolate the encapsulating protocol
> from the common protocol.
> 
>   - The DPP header is now reduced to not include the mmpdu header
>     or catagory byte.
>   - A channel switch must now notify the encapsulating protocol
>     since DPP common has no concept of frequency/channels
>   - When a peer is accepted its up to the encapsulating protocol
>     to filter frames only from this peer for the remainder of the
>     protocol. With PKEX the peer/own MACs actually do need to be
>     communicated to dpp-common due to some of the key generation.
>   - When a PKEX key is not set into the SM its up to the
>     encapsulating protocol to provide the key after a
>     PKEX_KEY_REQUESTED event. Once the key is obtained the SM
>     can resume the PKEX protocol.
>

There's a lot going on here, I think you're going to need to come up with a 
different strategy and break this patch up into digestible chunks.

> Other changes were made for readability. Sending/handler function
> names were made consistent. The DPP config header is now built
> and parsed with common functions (as opposed to manually). Extra
> validation here and there was added as it was noticed (checks for
> required attributes, return value checks, etc).

Much of this can likely be done in separate steps.

> ---
>   src/dpp-common.c | 2246 +++++++++++++++++++++++++++++++++++++++++++++-

I'm not taking or reviewing a 2kloc patch :)

>   1 file changed, 2242 insertions(+), 4 deletions(-)
> 

Regards,
-Denis

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/9] dpp: prep for moving AAD within dpp_append_wrapped_data
  2024-04-02 15:10 ` [PATCH 1/9] dpp: prep for moving AAD within dpp_append_wrapped_data Denis Kenzior
@ 2024-04-15 14:02   ` James Prestwood
  2024-04-15 19:00     ` Denis Kenzior
  0 siblings, 1 reply; 15+ messages in thread
From: James Prestwood @ 2024-04-15 14:02 UTC (permalink / raw)
  To: Denis Kenzior, iwd

Hi Denis,

On 4/2/24 8:10 AM, Denis Kenzior wrote:
> Hi James,
>
> On 3/27/24 10:19, James Prestwood wrote:
>> The AAD pointers for DPP are specific to the frame type. This is
>> currently sorted out by the caller within the respective frame
>> building functions but its quite unreadable. There are some comments
>> but lots of magic numbers. This should be moved within the
>> dpp_append_wrapped_data utility but the first step is to make the
>> frame buffer continuous. This will allow the entire frame to be
>
> continuous -> contiguous?
>
>> passed and dpp_append_wrapped_data can calculate the AAD offsets
>> itself.
>
> I'm a bit confused as to why?  Whether we have an iov or a contiguous 
> buffer, the end result is the same, no?  If you want to omit parts of 
> the header from the wrapped data calculation, wouldn't you just break 
> up the header into multiple iovs instead?
>
I did this to prep for patch 2, which uses buffer offsets and the frame 
type to calculate the AAD data. Its true the AAD data is always broken 
up into header and payload but I didn't want to use iov's because we 
would then have to assume iov[0] is the header and iov[1] is the 
payload. A single buffer makes it a lot clearer: we can minimally parse 
the frame, find the type, and calculate AAD offsets. With iov's we would 
have to check iov[0] for the type, then use some offset in iov[1] for 
the second AAD chunk. This then requires the iov's be exactly what we 
expect, which isn't a great API.

Yes, passing the entire frame also requires the buffer is exactly what 
we expect but documenting "the frame must start at the action byte" is a 
lot better than "iov[0] must be the header starting at the action byte" 
and "iov[1] must be the DPP attributes".

Thanks,

James


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 6/9] dpp-common: add TX/RX handlers to dpp_sm
  2024-04-02 15:27   ` Denis Kenzior
@ 2024-04-15 14:05     ` James Prestwood
  0 siblings, 0 replies; 15+ messages in thread
From: James Prestwood @ 2024-04-15 14:05 UTC (permalink / raw)
  To: Denis Kenzior, iwd


On 4/2/24 8:27 AM, Denis Kenzior wrote:
> Hi James,
>
> On 3/27/24 10:19, James Prestwood wrote:
>> This copies the bulk of the protocol handling from dpp.c into
>> dpp-common.c. There are no logical changes but some adaptations
>> needed to be made in order to isolate the encapsulating protocol
>> from the common protocol.
>>
>>   - The DPP header is now reduced to not include the mmpdu header
>>     or catagory byte.
>>   - A channel switch must now notify the encapsulating protocol
>>     since DPP common has no concept of frequency/channels
>>   - When a peer is accepted its up to the encapsulating protocol
>>     to filter frames only from this peer for the remainder of the
>>     protocol. With PKEX the peer/own MACs actually do need to be
>>     communicated to dpp-common due to some of the key generation.
>>   - When a PKEX key is not set into the SM its up to the
>>     encapsulating protocol to provide the key after a
>>     PKEX_KEY_REQUESTED event. Once the key is obtained the SM
>>     can resume the PKEX protocol.
>>
>
> There's a lot going on here, I think you're going to need to come up 
> with a different strategy and break this patch up into digestible chunks.
>
Sure, I tried, but its just a nasty build breaking change :)

I can try and make some of the improvements ahead of time, add frame 
handlers separately (auth/pkex/config/etc), then add the different events.

Thanks,

James



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/9] dpp: prep for moving AAD within dpp_append_wrapped_data
  2024-04-15 14:02   ` James Prestwood
@ 2024-04-15 19:00     ` Denis Kenzior
  0 siblings, 0 replies; 15+ messages in thread
From: Denis Kenzior @ 2024-04-15 19:00 UTC (permalink / raw)
  To: James Prestwood, iwd

Hi James,

On 4/15/24 09:02, James Prestwood wrote:
> Hi Denis,
> 
> On 4/2/24 8:10 AM, Denis Kenzior wrote:
>> Hi James,
>>
>> On 3/27/24 10:19, James Prestwood wrote:
>>> The AAD pointers for DPP are specific to the frame type. This is
>>> currently sorted out by the caller within the respective frame
>>> building functions but its quite unreadable. There are some comments
>>> but lots of magic numbers. This should be moved within the
>>> dpp_append_wrapped_data utility but the first step is to make the
>>> frame buffer continuous. This will allow the entire frame to be
>>
>> continuous -> contiguous?
>>
>>> passed and dpp_append_wrapped_data can calculate the AAD offsets
>>> itself.
>>
>> I'm a bit confused as to why?  Whether we have an iov or a contiguous buffer, 
>> the end result is the same, no?  If you want to omit parts of the header from 
>> the wrapped data calculation, wouldn't you just break up the header into 
>> multiple iovs instead?
>>
> I did this to prep for patch 2, which uses buffer offsets and the frame type to 
> calculate the AAD data. Its true the AAD data is always broken up into header 
> and payload but I didn't want to use iov's because we would then have to assume 
> iov[0] is the header and iov[1] is the payload. A single buffer makes it a lot 

TLDR: Using iovs this way was actually the intent.

Long time ago Andrew even had a set of patches that implemented / formalized 
using iovs for the various layers some time ago as well.  I don't remember the 
exact details now, but the idea was to be able to pass iovs around and share 
buffers without needing to copy data around.  I think the implementation had 
some problems, so it didn't go in.

> clearer: we can minimally parse the frame, find the type, and calculate AAD 
> offsets. With iov's we would have to check iov[0] for the type, then use some 
> offset in iov[1] for the second AAD chunk. This then requires the iov's be 
> exactly what we expect, which isn't a great API.

Agreed, iovs are not ideal.  Long term I would actually like us to implement 
some sort of sk_buff equivalent in ell.  It would abstract some of this and make 
it easier to manage head rooms / tail rooms for the various transports we use.

> 
> Yes, passing the entire frame also requires the buffer is exactly what we expect 
> but documenting "the frame must start at the action byte" is a lot better than 
> "iov[0] must be the header starting at the action byte" and "iov[1] must be the 
> DPP attributes".

I'm not sure that having offset calculation is really any better, particularly 
given that you have to make some rather invasive changes first.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2024-04-15 19:01 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-03-27 15:19 [PATCH 1/9] dpp: prep for moving AAD within dpp_append_wrapped_data James Prestwood
2024-03-27 15:19 ` [PATCH 2/9] dpp-util: move AAD logic within dpp_append_wrapped_attributes James Prestwood
2024-04-02 15:19   ` Denis Kenzior
2024-03-27 15:19 ` [PATCH 3/9] dpp-util: add dpp_append_point James Prestwood
2024-03-27 15:19 ` [PATCH 4/9] dpp: use dpp_append_point James Prestwood
2024-03-27 15:19 ` [PATCH 5/9] dpp-common: Skeleton for common DPP module James Prestwood
2024-03-27 15:19 ` [PATCH 6/9] dpp-common: add TX/RX handlers to dpp_sm James Prestwood
2024-04-02 15:27   ` Denis Kenzior
2024-04-15 14:05     ` James Prestwood
2024-03-27 15:19 ` [PATCH 7/9] build: add dpp-common.{c,h} James Prestwood
2024-03-27 15:19 ` [PATCH 8/9] dpp: remove most crypto/frame processing James Prestwood
2024-03-27 15:19 ` [PATCH 9/9] dpp: use common state machine James Prestwood
2024-04-02 15:10 ` [PATCH 1/9] dpp: prep for moving AAD within dpp_append_wrapped_data Denis Kenzior
2024-04-15 14:02   ` James Prestwood
2024-04-15 19:00     ` Denis Kenzior

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).