diff mbox series

[v2,09/20] PASN: Add common authentication frame build/validation functions

Message ID 20201216110033.8314-10-ilan.peer@intel.com
State Accepted
Headers show
Series Support base Pre association Security Negotiation (PASN) | expand

Commit Message

Peer, Ilan Dec. 16, 2020, 11 a.m. UTC
Add helper functions to construct a PASN authentication frame and
validate its content, which are common to both wpa_supplicant and
hostapd.

Signed-off-by: Ilan Peer <ilan.peer@intel.com>
---
 src/common/wpa_common.c | 404 ++++++++++++++++++++++++++++++++++++++++
 src/common/wpa_common.h |  29 +++
 2 files changed, 433 insertions(+)

Comments

Jouni Malinen Jan. 19, 2021, 2:38 p.m. UTC | #1
On Wed, Dec 16, 2020 at 01:00:22PM +0200, Ilan Peer wrote:
> Add helper functions to construct a PASN authentication frame and
> validate its content, which are common to both wpa_supplicant and
> hostapd.

> diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
> +void wpa_pasn_add_parameter_ie(struct wpabuf *buf, u16 pasn_group,
> +			       u8 wrapped_data_format,
> +			       struct wpabuf *pubkey,
> +			       struct wpabuf *comeback, u16 after)

> +	if (comeback) {
> +		wpa_printf(MSG_DEBUG, "PASN: Adding comeback data");
> +
> +		/*
> +		 * 2 octets for the 'after' field + 1 octet for the length +
> +		 * actual cookie data
> +		 */
> +		params->len += 3 + wpabuf_len(comeback);
> +		params->control |= WPA_PASN_CTRL_COMEBACK_INFO_PRESENT;
> +
> +		wpabuf_put_le16(buf, after);

The Comeback After subfield is not included in frames sent by the non-AP
STA, so I'll extend this to accept after == -1 as a special case to
cover that.

> +	if (pubkey) {
> +		wpa_printf(MSG_DEBUG, "PASN: Adding public key");
> +
> +		/*
> +		 * 2 octets for the finite cyclic group + 2 octets public key
> +		 * length + the actual key
> +		 */
> +		params->len += 2 + 2 + wpabuf_len(pubkey);
> +		params->control |= WPA_PASN_CTRL_GROUP_AND_KEY_PRESENT;
> +
> +		wpabuf_put_le16(buf, pasn_group);
> +		wpabuf_put_le16(buf, wpabuf_len(pubkey));
> +		wpabuf_put_buf(buf, pubkey);

The Ephemeral Public Key Length field has size of one octet in
P802.11az/D2.6, so I'll update this to match.

> +int wpa_pasn_parse_parameter_ie(const u8 *data, u8 len,
> +				struct wpa_pasn_params_data *pasn_params)

> +	if (params->control & WPA_PASN_CTRL_COMEBACK_INFO_PRESENT) {
> +		if (len < 3 || len < *(pos + 2) + 3) {
> +			wpa_printf(MSG_DEBUG,
> +				   "PASN: Invalid parameters IE: comeback len");
> +			return -1;
> +		}
> +
> +		pasn_params->after = WPA_GET_LE16(pos);

Similar change here in parsing..

> +	if (params->control & WPA_PASN_CTRL_GROUP_AND_KEY_PRESENT) {
> +		if (len < 4 || len < WPA_GET_LE16(pos + 2) + 4) {

> +		pasn_params->group = WPA_GET_LE16(pos);
> +		pasn_params->pubkey_len = WPA_GET_LE16(pos + 2);

And here..
Peer, Ilan Jan. 20, 2021, 7:45 a.m. UTC | #2
Hi,

> 
> > diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
> > +void wpa_pasn_add_parameter_ie(struct wpabuf *buf, u16 pasn_group,
> > +			       u8 wrapped_data_format,
> > +			       struct wpabuf *pubkey,
> > +			       struct wpabuf *comeback, u16 after)
> 
> > +	if (comeback) {
> > +		wpa_printf(MSG_DEBUG, "PASN: Adding comeback data");
> > +
> > +		/*
> > +		 * 2 octets for the 'after' field + 1 octet for the length +
> > +		 * actual cookie data
> > +		 */
> > +		params->len += 3 + wpabuf_len(comeback);
> > +		params->control |=
> WPA_PASN_CTRL_COMEBACK_INFO_PRESENT;
> > +
> > +		wpabuf_put_le16(buf, after);
> 
> The Comeback After subfield is not included in frames sent by the non-AP
> STA, so I'll extend this to accept after == -1 as a special case to cover that.
> 

Maybe we can clarify this in the specification? 

> > +	if (pubkey) {
> > +		wpa_printf(MSG_DEBUG, "PASN: Adding public key");
> > +
> > +		/*
> > +		 * 2 octets for the finite cyclic group + 2 octets public key
> > +		 * length + the actual key
> > +		 */
> > +		params->len += 2 + 2 + wpabuf_len(pubkey);
> > +		params->control |=
> WPA_PASN_CTRL_GROUP_AND_KEY_PRESENT;
> > +
> > +		wpabuf_put_le16(buf, pasn_group);
> > +		wpabuf_put_le16(buf, wpabuf_len(pubkey));
> > +		wpabuf_put_buf(buf, pubkey);
> 
> The Ephemeral Public Key Length field has size of one octet in
> P802.11az/D2.6, so I'll update this to match.
> 

Sure.

> > +int wpa_pasn_parse_parameter_ie(const u8 *data, u8 len,
> > +				struct wpa_pasn_params_data
> *pasn_params)
> 
> > +	if (params->control &
> WPA_PASN_CTRL_COMEBACK_INFO_PRESENT) {
> > +		if (len < 3 || len < *(pos + 2) + 3) {
> > +			wpa_printf(MSG_DEBUG,
> > +				   "PASN: Invalid parameters IE: comeback
> len");
> > +			return -1;
> > +		}
> > +
> > +		pasn_params->after = WPA_GET_LE16(pos);
> 
> Similar change here in parsing..
> 
> > +	if (params->control &
> WPA_PASN_CTRL_GROUP_AND_KEY_PRESENT) {
> > +		if (len < 4 || len < WPA_GET_LE16(pos + 2) + 4) {
> 
> > +		pasn_params->group = WPA_GET_LE16(pos);
> > +		pasn_params->pubkey_len = WPA_GET_LE16(pos + 2);
> 
> And here..
> 

Thanks for pointing these out,

Regards,

Ilan.
Jouni Malinen Jan. 20, 2021, 11:13 a.m. UTC | #3
On Wed, Jan 20, 2021 at 07:45:29AM +0000, Peer, Ilan wrote:
> > The Comeback After subfield is not included in frames sent by the non-AP
> > STA, so I'll extend this to accept after == -1 as a special case to cover that.
> > 
> 
> Maybe we can clarify this in the specification? 

Would you have a more specific suggestion on how the definition should
be clarified? P802.11az/D2.6 defines the subfield to have "0 or 2" as
the length and uses following language to describe it: "Comeback After
subfield shall not be present (i.e. zero octets) in PASN authentication
frames from a non-AP STA." 

That "shall" word is not appropriate in Clause 9, so this should be
cleaned up anyway. It might also be clearer to talk about the first and
third PASN Authentication frame instead of "from a non-AP STA"
especially in this Clause 9 that is supposed to describe the frame
format and not behavior.  If you have some ideas on how this would be
easier to notice and understand, I can request this to be modified once
D3.0 goes out to a ballot.
Peer, Ilan Jan. 20, 2021, 2:14 p.m. UTC | #4
Hi,

> -----Original Message-----
> From: Jouni Malinen <j@w1.fi>
> Sent: Wednesday, January 20, 2021 13:13
> To: Peer, Ilan <ilan.peer@intel.com>
> Cc: hostap@lists.infradead.org
> Subject: Re: [PATCH v2 09/20] PASN: Add common authentication frame
> build/validation functions
> 
> On Wed, Jan 20, 2021 at 07:45:29AM +0000, Peer, Ilan wrote:
> > > The Comeback After subfield is not included in frames sent by the
> > > non-AP STA, so I'll extend this to accept after == -1 as a special case to
> cover that.
> > >
> >
> > Maybe we can clarify this in the specification?
> 
> Would you have a more specific suggestion on how the definition should be
> clarified? P802.11az/D2.6 defines the subfield to have "0 or 2" as the length
> and uses following language to describe it: "Comeback After subfield shall
> not be present (i.e. zero octets) in PASN authentication frames from a non-
> AP STA."
>

Reading this again, I think that at least this part is clear enough, so I do not think
a change is needed.

> That "shall" word is not appropriate in Clause 9, so this should be cleaned up
> anyway. It might also be clearer to talk about the first and third PASN
> Authentication frame instead of "from a non-AP STA"
> especially in this Clause 9 that is supposed to describe the frame format and

Agree.

> not behavior.  If you have some ideas on how this would be easier to notice
> and understand, I can request this to be modified once
> D3.0 goes out to a ballot.

Regards,

Ilan.
diff mbox series

Patch

diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index f8b38eab90..7031208189 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -1505,6 +1505,10 @@  static int rsn_key_mgmt_to_bitfield(const u8 *s)
 #endif /* CONFIG_DPP */
 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OSEN)
 		return WPA_KEY_MGMT_OSEN;
+#ifdef CONFIG_PASN
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PASN)
+		return WPA_KEY_MGMT_PASN;
+#endif /* CONFIG_PASN */
 	return 0;
 }
 
@@ -3262,3 +3266,403 @@  int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie)
 
 	return ret;
 }
+
+
+#ifdef CONFIG_PASN
+/*
+ * wpa_pasn_build_auth_header - Add the MAC header and initialize authentication
+ * frame for PASN
+ *
+ * @buf: pointer to a buffer in which the data would be set
+ * @bssid: the BSSID of the AP
+ * @src: source address
+ * @dst: destination address
+ * @trans_seq: authentication transaction sequence number
+ * @status: authentication status
+ */
+void wpa_pasn_build_auth_header(struct wpabuf *buf, const u8 *bssid,
+				const u8 *src, const u8 *dst,
+				u8 trans_seq, u16 status)
+{
+	struct ieee80211_mgmt *auth;
+
+	wpa_printf(MSG_DEBUG, "PASN: Add authentication header. tran_seq=%u",
+		   trans_seq);
+
+	auth = wpabuf_put(buf, offsetof(struct ieee80211_mgmt,
+					u.auth.variable));
+
+	auth->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
+					   (WLAN_FC_STYPE_AUTH << 4));
+
+	os_memcpy(auth->da, dst, ETH_ALEN);
+	os_memcpy(auth->sa, src, ETH_ALEN);
+	os_memcpy(auth->bssid, bssid, ETH_ALEN);
+
+	auth->u.auth.auth_alg = host_to_le16(WLAN_AUTH_PASN);
+	auth->seq_ctrl = 0;
+	auth->u.auth.auth_transaction = host_to_le16(trans_seq);
+	auth->u.auth.status_code = host_to_le16(status);
+}
+
+/*
+ * wpa_pasn_add_rsne - Add an RNSE IE for PASN authentication
+ *
+ * @buf: pointer to a buffer in which the data would be set
+ * @pmkid: optional PMKID. Can be NULL.
+ * @akmp: authentication and key management protocol
+ * @cipher: the cipher suite
+ */
+void wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid,
+		       int akmp, int cipher)
+{
+	struct rsn_ie_hdr *hdr;
+	u32 suite;
+	u16 capab;
+	u8 *pos;
+	u8 rsn_ie_len;
+
+	wpa_printf(MSG_DEBUG, "PASN: Add RSN IE");
+
+	rsn_ie_len = sizeof(*hdr) + RSN_SELECTOR_LEN +
+		2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 +
+		RSN_SELECTOR_LEN + 2 + (pmkid ? PMKID_LEN : 0);
+
+	hdr = wpabuf_put(buf, rsn_ie_len);
+	hdr->elem_id = WLAN_EID_RSN;
+	hdr->len = rsn_ie_len - 2;
+	WPA_PUT_LE16(hdr->version, RSN_VERSION);
+	pos = (u8 *)(hdr + 1);
+
+	/* Group addressed data is not allowed */
+	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+	pos += RSN_SELECTOR_LEN;
+
+	/* Add the pairwise cipher */
+	*pos++ = 1;
+	*pos++ = 0;
+	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, cipher);
+	RSN_SELECTOR_PUT(pos, suite);
+	pos += RSN_SELECTOR_LEN;
+
+	/* Add the AKM suite */
+	*pos++ = 1;
+	*pos++ = 0;
+
+	switch (akmp) {
+	case WPA_KEY_MGMT_PASN:
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PASN);
+		break;
+#ifdef CONFIG_SAE
+	case WPA_KEY_MGMT_SAE:
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE);
+		break;
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+	case WPA_KEY_MGMT_FILS_SHA256:
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256);
+		break;
+	case WPA_KEY_MGMT_FILS_SHA384:
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384);
+		break;
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R
+	case WPA_KEY_MGMT_FT_PSK:
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
+		break;
+	case WPA_KEY_MGMT_FT_IEEE8021X:
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
+		break;
+	case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384);
+		break;
+#endif /* CONFIG_IEEE80211R */
+	default:
+		wpa_printf(MSG_ERROR, "PASN: invalid AKMP=0x%x",
+			   akmp);
+	}
+	pos += RSN_SELECTOR_LEN;
+
+	/* RSN Capabilities: PASN mandates both MFP capable and required */
+	capab = WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR;
+	WPA_PUT_LE16(pos, capab);
+	pos += 2;
+
+	if (pmkid) {
+		wpa_printf(MSG_DEBUG, "PASN: Adding PMKID");
+
+		*pos++ = 1;
+		*pos++ = 0;
+		os_memcpy(pos, pmkid, PMKID_LEN);
+		pos += PMKID_LEN;
+	} else {
+		*pos++ = 0;
+		*pos++ = 0;
+	}
+
+	/* Group addressed management is not allowed */
+	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+	pos += RSN_SELECTOR_LEN;
+}
+
+
+/*
+ * wpa_pasn_add_parameter_ie - Add PASN parameters IE for PASN authentication
+ *
+ * @buf: pointer to a buffer in which the data would be set
+ * @wrapped_data_format: format of the data in the wrapped data IE
+ * @pubkey: a buffer holding the local public key. Can be NULL
+ * @comeback: a buffer holding the comeback token. Can be NULL
+ * @after: if comeback is set, defined the comeback time in seconds
+ */
+void wpa_pasn_add_parameter_ie(struct wpabuf *buf, u16 pasn_group,
+			       u8 wrapped_data_format,
+			       struct wpabuf *pubkey,
+			       struct wpabuf *comeback, u16 after)
+{
+	struct pasn_parameter_ie *params;
+
+	wpa_printf(MSG_DEBUG, "PASN: Add PASN parameters IE");
+
+	params = wpabuf_put(buf, sizeof(*params));
+
+	params->id = WLAN_EID_EXTENSION;
+	params->len = sizeof(*params) - 2;
+	params->id_ext = WLAN_EID_EXT_PASN_PARAMS;
+	params->control = 0;
+	params->wrapped_data_format = wrapped_data_format;
+
+	if (comeback) {
+		wpa_printf(MSG_DEBUG, "PASN: Adding comeback data");
+
+		/*
+		 * 2 octets for the 'after' field + 1 octet for the length +
+		 * actual cookie data
+		 */
+		params->len += 3 + wpabuf_len(comeback);
+		params->control |= WPA_PASN_CTRL_COMEBACK_INFO_PRESENT;
+
+		wpabuf_put_le16(buf, after);
+		wpabuf_put_u8(buf, wpabuf_len(comeback));
+		wpabuf_put_buf(buf, comeback);
+	}
+
+	if (pubkey) {
+		wpa_printf(MSG_DEBUG, "PASN: Adding public key");
+
+		/*
+		 * 2 octets for the finite cyclic group + 2 octets public key
+		 * length + the actual key
+		 */
+		params->len += 2 + 2 + wpabuf_len(pubkey);
+		params->control |= WPA_PASN_CTRL_GROUP_AND_KEY_PRESENT;
+
+		wpabuf_put_le16(buf, pasn_group);
+		wpabuf_put_le16(buf, wpabuf_len(pubkey));
+		wpabuf_put_buf(buf, pubkey);
+	}
+}
+
+/*
+ * wpa_pasn_add_wrapped_data - Adds a wrapped data IE to PASN authentication
+ * frame. If needed, the wrapped data IE would be fragmented.
+ *
+ * @buf: pointer to a buffer in which the data would be set
+ * @wrapped_data_buf: buffer holding the wrapped data
+ */
+void wpa_pasn_add_wrapped_data(struct wpabuf *buf,
+			       struct wpabuf *wrapped_data_buf)
+{
+	const u8 *data;
+	size_t data_len;
+	u8 len;
+
+	if (!wrapped_data_buf)
+		return;
+
+	wpa_printf(MSG_DEBUG, "PASN: Add wrapped data");
+
+	data = wpabuf_head_u8(wrapped_data_buf);
+	data_len = wpabuf_len(wrapped_data_buf);
+
+	/* nothing to add */
+	if (!data_len)
+		return;
+
+	if (data_len <= 254)
+		len = 1 + data_len;
+	else
+		len = 255;
+
+	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+	wpabuf_put_u8(buf, len);
+	wpabuf_put_u8(buf, WLAN_EID_EXT_WRAPPED_DATA);
+	wpabuf_put_data(buf, data, len - 1);
+
+	data += len - 1;
+	data_len -= len - 1;
+
+	while (data_len) {
+		wpabuf_put_u8(buf, WLAN_EID_FRAGMENT);
+		len = data_len > 255 ? 255 : data_len;
+		wpabuf_put_u8(buf, len);
+		wpabuf_put_data(buf, data, len);
+		data += len;
+		data_len -= len;
+	}
+}
+
+/*
+ * wpa_pasn_validate_rsne - Validate PSAN specific data of RSNE
+ *
+ * @data: parsed representation of an RSNE
+ * Returns -1 for invalid data; otherwise 0
+ */
+int wpa_pasn_validate_rsne(const struct wpa_ie_data *data)
+{
+	u16 capab = WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR;
+
+	if (data->proto != WPA_PROTO_RSN)
+		return -1;
+
+	if ((data->capabilities & capab) != capab) {
+		wpa_printf(MSG_DEBUG, "PASN: Invalid RSNE capabilities");
+		return -1;
+	}
+
+	if (!data->has_group || data->group_cipher != WPA_CIPHER_GTK_NOT_USED) {
+		wpa_printf(MSG_DEBUG, "PASN: Invalid group data cipher");
+		return -1;
+	}
+
+	if (!data->has_pairwise || !data->pairwise_cipher ||
+	    (data->pairwise_cipher & (data->pairwise_cipher - 1))) {
+		wpa_printf(MSG_DEBUG, "PASN: No valid pairwise suite");
+		return -1;
+	}
+
+	switch (data->key_mgmt) {
+#ifdef CONFIG_SAE
+	case WPA_KEY_MGMT_SAE:
+	/* fall through */
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+	case WPA_KEY_MGMT_FILS_SHA256:
+	case WPA_KEY_MGMT_FILS_SHA384:
+	/* fall through */
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R
+	case WPA_KEY_MGMT_FT_PSK:
+	case WPA_KEY_MGMT_FT_IEEE8021X:
+	case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+	/* fall through */
+#endif /* CONFIG_IEEE80211R */
+	case WPA_KEY_MGMT_PASN:
+		break;
+	default:
+		wpa_printf(MSG_ERROR, "PASN: invalid key_mgmt: %u %u",
+			   data->key_mgmt, WPA_KEY_MGMT_SAE);
+		return -1;
+	}
+
+	if (data->mgmt_group_cipher != WPA_CIPHER_GTK_NOT_USED) {
+		wpa_printf(MSG_DEBUG, "PASN: Invalid group mgmt cipher");
+		return -1;
+	}
+
+	if (data->num_pmkid > 1) {
+		wpa_printf(MSG_DEBUG, "PASN: Invalid number of PMKIDs");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/*
+ * wpa_pasn_parse_parameter_ie - Validates PASN parameters IE
+ *
+ * @data: pointer to the PASN parameters IE (starting with the EID).
+ * @len: length of the data in the PASN parameters IE
+ * @pasn_params: On successful return would hold the parsed PASN parameters.
+ *
+ * Note: on successful return, the pointers in &pasn_params point to the data in
+ * the IE and are not locally allocated (so they should not be freed etc.)
+ *
+ * Return -1 for invalid data; otherwise 0
+ */
+int wpa_pasn_parse_parameter_ie(const u8 *data, u8 len,
+				struct wpa_pasn_params_data *pasn_params)
+{
+	struct pasn_parameter_ie *params = (struct pasn_parameter_ie *)data;
+	const u8 *pos = (const u8 *)(params + 1);
+
+	if (!pasn_params) {
+		wpa_printf(MSG_DEBUG, "PASN: Invalid params");
+		return -1;
+	}
+
+	if (!params || ((size_t)(params->len + 2) < sizeof(*params)) ||
+	    params->len + 2 != len) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Invalid parameters IE. len=(%u, %u)",
+			   params ? params->len : 0, len);
+		return -1;
+	}
+
+	os_memset(pasn_params, 0, sizeof(*pasn_params));
+
+	switch (params->wrapped_data_format) {
+	case WPA_PASN_NO_WRAPPED_DATA:
+	case WPA_PASN_WRAPPED_DATA_SAE:
+	case WPA_PASN_WRAPPED_DATA_FILS_SK:
+	case WPA_PASN_WRAPPED_DATA_FT:
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "PASN: Invalid wrapped data format");
+		return -1;
+	}
+
+	pasn_params->wrapped_data_format = params->wrapped_data_format;
+
+	len -= sizeof(*params);
+
+	if (params->control & WPA_PASN_CTRL_COMEBACK_INFO_PRESENT) {
+		if (len < 3 || len < *(pos + 2) + 3) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Invalid parameters IE: comeback len");
+			return -1;
+		}
+
+		pasn_params->after = WPA_GET_LE16(pos);
+		pasn_params->comeback_len = *(pos + 2);
+		pasn_params->comeback = pos + 3;
+
+		len -= 3 + pasn_params->comeback_len;
+		pos += 3 + pasn_params->comeback_len;
+	}
+
+	if (params->control & WPA_PASN_CTRL_GROUP_AND_KEY_PRESENT) {
+		if (len < 4 || len < WPA_GET_LE16(pos + 2) + 4) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Invalid parameters IE: group and key");
+			return -1;
+		}
+
+		pasn_params->group = WPA_GET_LE16(pos);
+		pasn_params->pubkey_len = WPA_GET_LE16(pos + 2);
+		pasn_params->pubkey = pos + 4;
+
+		len -= 4 + pasn_params->pubkey_len;
+		pos += 4 + pasn_params->pubkey_len;
+	}
+
+	if (len) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Invalid parameters IE. Bytes left=%u", len);
+		return -1;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_PASN */
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 0117ae719e..a094686d4e 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -535,6 +535,16 @@  struct pasn_parameter_ie {
 	u8 wrapped_data_format;
 } STRUCT_PACKED;
 
+struct wpa_pasn_params_data {
+	u8 wrapped_data_format;
+	u16 after;
+	u8 comeback_len;
+	const u8 *comeback;
+	u16 group;
+	u16 pubkey_len;
+	const u8 *pubkey;
+};
+
 int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse,
 		     int use_sha384);
 
@@ -635,4 +645,23 @@  int pasn_mic(const u8 *kck, int akmp, int cipher,
 int pasn_auth_frame_hash(int akmp, int cipher, const u8 *data, size_t len,
 			 u8 *hash);
 
+void wpa_pasn_build_auth_header(struct wpabuf *buf, const u8 *bssid,
+				const u8 *src, const u8 *dst,
+				u8 trans_seq, u16 status);
+
+void wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid,
+		       int akmp, int cipher);
+
+void wpa_pasn_add_parameter_ie(struct wpabuf *buf, u16 pasn_group,
+			       u8 wrapped_data_format,
+			       struct wpabuf *pubkey,
+			       struct wpabuf *comeback, u16 after);
+
+void wpa_pasn_add_wrapped_data(struct wpabuf *buf,
+			       struct wpabuf *wrapped_data_buf);
+
+int wpa_pasn_validate_rsne(const struct wpa_ie_data *data);
+int wpa_pasn_parse_parameter_ie(const u8 *data, u8 len,
+				struct wpa_pasn_params_data *pasn_params);
+
 #endif /* WPA_COMMON_H */