diff mbox series

[1/9] dpp: prep for moving AAD within dpp_append_wrapped_data

Message ID 20240327151957.1446149-1-prestwoj@gmail.com (mailing list archive)
State New
Headers show
Series [1/9] dpp: prep for moving AAD within dpp_append_wrapped_data | expand

Checks

Context Check Description
tedd_an/pre-ci_am success Success
prestwoj/iwd-alpine-ci-fetch success Fetch PR
prestwoj/iwd-ci-fetch success Fetch PR
prestwoj/iwd-ci-gitlint success GitLint
prestwoj/iwd-alpine-ci-makedistcheck success Make Distcheck
prestwoj/iwd-alpine-ci-build success Build - Configure
prestwoj/iwd-ci-build success Build - Configure
prestwoj/iwd-ci-clang fail Clang IWD - make FAIL: src/dpp.c:1700:47: error: unused variable 'qi' [-Werror,-Wunused-variable] _auto_(l_ecc_point_free) struct l_ecc_point *qi = NULL; ^ 1 error generated. make[1]: *** [Makefile:2545: src/dpp.o] Error 1 make[1]: *** Waiting for unfinished jobs.... make: *** [Makefile:1716: all] Error 2
prestwoj/iwd-ci-makecheckvalgrind success Make Check w/Valgrind
prestwoj/iwd-ci-makecheck success Make Check
prestwoj/iwd-alpine-ci-makecheckvalgrind success Make Check w/Valgrind
prestwoj/iwd-alpine-ci-makecheck success Make Check
prestwoj/iwd-alpine-ci-incremental_build success Incremental Build with patches
prestwoj/iwd-ci-incremental_build success Incremental Build with patches
prestwoj/iwd-ci-testrunner success test-runner PASS
prestwoj/iwd-ci-makedistcheck success Make Distcheck

Commit Message

James Prestwood March 27, 2024, 3:19 p.m. UTC
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(-)

Comments

Denis Kenzior April 2, 2024, 3:10 p.m. UTC | #1
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
James Prestwood April 15, 2024, 2:02 p.m. UTC | #2
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
Denis Kenzior April 15, 2024, 7 p.m. UTC | #3
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
diff mbox series

Patch

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,