[PATCHv6,2/5] FT: new rrb message format

Submitted by michael-dev@fami-braun.de on April 2, 2017, 12:52 p.m.

Details

Message ID 1491137573-643-3-git-send-email-michael-dev@fami-braun.de
State Under Review
Headers show

Commit Message

michael-dev@fami-braun.de April 2, 2017, 12:52 p.m.
Convert FT RRB into new TLV based format.
Use AES+SIV as AED cipher.
This needs at least 32 byte long keys. These can be provided either
by a config filee change or letting a KDF derive the 32 byte key used
from the 16 byte key given.

This breaks backward compatibility.

Signed-off-by: Michael Braun <michael-dev@fami-braun.de>

--
2017-04-01:
 - correct debug output in pull response handler
v5:
 - configuration file fallback for short keys
 - add authenticated-only packet area
v4:
 - AES-SIV
v3:
 - use new extended OUI ethertype
 - use encrypt-then-mac with aes+sha256 as AED
 - use different keys for encryption and mac using hmac_sha256_kdf
---
 hostapd/Makefile              |    3 +
 hostapd/config_file.c         |   24 +-
 hostapd/hostapd.conf          |   12 +-
 src/ap/hostapd.h              |    2 +
 src/ap/wpa_auth.h             |   99 ++--
 src/ap/wpa_auth_ft.c          | 1051 ++++++++++++++++++++++++++++++-----------
 src/ap/wpa_auth_glue.c        |    2 +-
 src/ap/wpa_auth_i.h           |    2 +-
 tests/hwsim/test_ap_ft.py     |   40 +-
 tests/hwsim/test_hapd_ctrl.py |   12 +-
 10 files changed, 902 insertions(+), 345 deletions(-)

Patch hide | download patch | download mbox

diff --git a/hostapd/Makefile b/hostapd/Makefile
index 298019e..ab144e2 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -295,7 +295,10 @@  OBJS += ../src/ap/wpa_auth_ft.o
 NEED_SHA256=y
 NEED_AES_OMAC1=y
 NEED_AES_UNWRAP=y
+NEED_AES_SIV=y
 NEED_ETH_P_OUI=y
+NEED_SHA256=y
+NEED_HMAC_SHA256_KDF=y
 endif
 
 ifdef NEED_ETH_P_OUI
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 7b43806..2070a74 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -20,6 +20,7 @@ 
 #include "ap/wpa_auth.h"
 #include "ap/ap_config.h"
 #include "config_file.h"
+#include "crypto/sha256.h"
 
 
 #ifndef CONFIG_NO_RADIUS
@@ -1000,6 +1001,25 @@  static int hostapd_config_tx_queue(struct hostapd_config *conf,
 
 
 #ifdef CONFIG_IEEE80211R_AP
+static int rkh_dervice_key(const char *pos, u8 *key, size_t key_len)
+{
+	u8 oldkey[16];
+
+	if (!hexstr2bin(pos, key, key_len))
+		return 0;
+
+	/* try to use old short key for backward compatibility */
+	if (hexstr2bin(pos, oldkey, sizeof(oldkey)))
+		return -1;
+
+	if (hmac_sha256_kdf(oldkey, sizeof(oldkey), "FT OLDKEY", NULL, 0,
+			    key, key_len) < 0)
+		return -1;
+
+	return 0;
+}
+
+
 static int add_r0kh(struct hostapd_bss_config *bss, char *value)
 {
 	struct ft_remote_r0kh *r0kh;
@@ -1033,7 +1053,7 @@  static int add_r0kh(struct hostapd_bss_config *bss, char *value)
 	os_memcpy(r0kh->id, pos, r0kh->id_len);
 
 	pos = next;
-	if (hexstr2bin(pos, r0kh->key, sizeof(r0kh->key))) {
+	if (rkh_dervice_key(pos, r0kh->key, sizeof(r0kh->key)) < 0) {
 		wpa_printf(MSG_ERROR, "Invalid R0KH key: '%s'", pos);
 		os_free(r0kh);
 		return -1;
@@ -1078,7 +1098,7 @@  static int add_r1kh(struct hostapd_bss_config *bss, char *value)
 	}
 
 	pos = next;
-	if (hexstr2bin(pos, r1kh->key, sizeof(r1kh->key))) {
+	if (rkh_dervice_key(pos, r1kh->key, sizeof(r1kh->key)) < 0) {
 		wpa_printf(MSG_ERROR, "Invalid R1KH key: '%s'", pos);
 		os_free(r1kh);
 		return -1;
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 18c330b..7e61d1c 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -1452,21 +1452,21 @@  own_ip_addr=127.0.0.1
 #reassociation_deadline=1000
 
 # List of R0KHs in the same Mobility Domain
-# format: <MAC address> <NAS Identifier> <128-bit key as hex string>
+# format: <MAC address> <NAS Identifier> <256-bit key as hex string>
 # This list is used to map R0KH-ID (NAS Identifier) to a destination MAC
 # address when requesting PMK-R1 key from the R0KH that the STA used during the
 # Initial Mobility Domain Association.
-#r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f
-#r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff
+#r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
+#r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff
 # And so on.. One line per R0KH.
 
 # List of R1KHs in the same Mobility Domain
-# format: <MAC address> <R1KH-ID> <128-bit key as hex string>
+# format: <MAC address> <R1KH-ID> <256-bit key as hex string>
 # This list is used to map R1KH-ID to a destination MAC address when sending
 # PMK-R1 key from the R0KH. This is also the list of authorized R1KHs in the MD
 # that can request PMK-R1 keys.
-#r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f
-#r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff
+#r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
+#r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff
 # And so on.. One line per R1KH.
 
 # Whether PMK-R1 push is enabled at R0KH
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 452ca1e..e7c65f7 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -195,6 +195,8 @@  struct hostapd_data {
 	struct eth_p_oui_ctx *oui_pull;
 	struct eth_p_oui_ctx *oui_resp;
 	struct eth_p_oui_ctx *oui_push;
+	struct eth_p_oui_ctx *oui_sreq;
+	struct eth_p_oui_ctx *oui_sresp;
 #endif /* CONFIG_IEEE80211R_AP */
 
 	struct wps_context *wps;
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 22c2093..9bf5884 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -44,54 +44,61 @@  struct ft_rrb_frame {
 #define FT_PACKET_R0KH_R1KH_RESP 0x02
 #define FT_PACKET_R0KH_R1KH_PUSH 0x03
 
-#define FT_R0KH_R1KH_PULL_NONCE_LEN 16
-#define FT_R0KH_R1KH_PULL_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \
-				    WPA_PMK_NAME_LEN + FT_R1KH_ID_LEN + \
-				    ETH_ALEN)
-#define FT_R0KH_R1KH_PULL_PAD_LEN ((8 - FT_R0KH_R1KH_PULL_DATA_LEN % 8) % 8)
-
-struct ft_r0kh_r1kh_pull_frame {
-	u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN];
-	u8 pmk_r0_name[WPA_PMK_NAME_LEN];
-	u8 r1kh_id[FT_R1KH_ID_LEN];
-	u8 s1kh_id[ETH_ALEN];
-	u8 pad[FT_R0KH_R1KH_PULL_PAD_LEN]; /* 8-octet boundary for AES block */
-	u8 key_wrap_extra[8];
-} STRUCT_PACKED;
+/* packet layout
+ *  IEEE 802 extended OUI ethertype frame header
+ *  u16 authlen
+ *  multiple of of struct ft_rrb_tlv (authenticated only, length = authlen)
+ *  multiple of of struct ft_rrb_tlv (aes-siv encrypted, all-zero padded to
+ *                                      blocksize, aes-siv needs an extra
+ *                                      blocksize length)
+ */
 
-#define FT_R0KH_R1KH_RESP_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \
-				    FT_R1KH_ID_LEN + ETH_ALEN + PMK_LEN + \
-				    WPA_PMK_NAME_LEN + 2)
-#define FT_R0KH_R1KH_RESP_PAD_LEN ((8 - FT_R0KH_R1KH_RESP_DATA_LEN % 8) % 8)
-struct ft_r0kh_r1kh_resp_frame {
-	u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; /* copied from pull */
-	u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */
-	u8 s1kh_id[ETH_ALEN]; /* copied from pull */
-	u8 pmk_r1[PMK_LEN];
-	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
-	le16 pairwise;
-	u8 pad[FT_R0KH_R1KH_RESP_PAD_LEN]; /* 8-octet boundary for AES block */
-	u8 key_wrap_extra[8];
-} STRUCT_PACKED;
+#define FT_RRB_NONCE_LEN 16
+
+#define FT_RRB_LAST_EMPTY     0 /* placeholder or padding */
+
+#define FT_RRB_NONCE          2 /* size FT_RRB_NONCE_LEN */
+#define FT_RRB_TIMESTAMP      3 /* le32 unix seconds */
+
+#define FT_RRB_R0KH_ID        4 /* FT_R0KH_ID_MAX_LEN */
+#define FT_RRB_R1KH_ID        5 /* FT_R1KH_ID_LEN */
+#define FT_RRB_S1KH_ID        6 /* ETH_ALEN */
+
+#define FT_RRB_PMK_R0_NAME    7 /* WPA_PMK_NAME_LEN */
+#define FT_RRB_PMK_R0         8 /* PMK_LEN */
+#define FT_RRB_PMK_R1_NAME    9 /* WPA_PMK_NAME_LEN */
+#define FT_RRB_PMK_R1        10 /* PMK_LEN */
 
-#define FT_R0KH_R1KH_PUSH_DATA_LEN (4 + FT_R1KH_ID_LEN + ETH_ALEN + \
-				    WPA_PMK_NAME_LEN + PMK_LEN + \
-				    WPA_PMK_NAME_LEN + 2)
-#define FT_R0KH_R1KH_PUSH_PAD_LEN ((8 - FT_R0KH_R1KH_PUSH_DATA_LEN % 8) % 8)
-struct ft_r0kh_r1kh_push_frame {
-	/* Encrypted with AES key-wrap */
-	u8 timestamp[4]; /* current time in seconds since unix epoch, little
-			  * endian */
-	u8 r1kh_id[FT_R1KH_ID_LEN];
-	u8 s1kh_id[ETH_ALEN];
-	u8 pmk_r0_name[WPA_PMK_NAME_LEN];
-	u8 pmk_r1[PMK_LEN];
-	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
-	le16 pairwise;
-	u8 pad[FT_R0KH_R1KH_PUSH_PAD_LEN]; /* 8-octet boundary for AES block */
-	u8 key_wrap_extra[8];
+#define FT_RRB_PAIRWISE      11 /* le16 */
+
+struct ft_rrb_tlv {
+	le16 type;
+	le16 len;
+	/* followed by data of length len */
 } STRUCT_PACKED;
 
+/* session TLVs:
+ *   required: PMK_R1, PMK_R1_NAME, PAIRWISE
+ *
+ * pull frame TLVs:
+ *   auth:
+ *     required: NONCE, R0KH_ID, R1KH_ID
+ *   encrypted:
+ *     required: PMK_R0_NAME, S1KH_ID
+ *
+ * response frame TLVs:
+ *   auth:
+ *     required: NONCE, R0KH_ID, R1KH_ID
+ *   encrypted:
+ *     required: S1KH_ID, session TLVs
+ *
+ * push frame TLVs:
+ *   auth:
+ *     required: TIMESTAMP, R0KH_ID, R1KH_ID
+ *   encrypted:
+ *     required: S1KH_ID, PMK_R0_NAME, session TLVs
+ */
+
 #ifdef _MSC_VER
 #pragma pack(pop)
 #endif /* _MSC_VER */
@@ -110,7 +117,7 @@  struct ft_remote_r0kh {
 	u8 addr[ETH_ALEN];
 	u8 id[FT_R0KH_ID_MAX_LEN];
 	size_t id_len;
-	u8 key[16];
+	u8 key[32];
 };
 
 
@@ -118,7 +125,7 @@  struct ft_remote_r1kh {
 	struct ft_remote_r1kh *next;
 	u8 addr[ETH_ALEN];
 	u8 id[FT_R1KH_ID_LEN];
-	u8 key[16];
+	u8 key[32];
 };
 
 
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 1d4c519..f5a8d83 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -13,6 +13,8 @@ 
 #include "utils/list.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
 #include "crypto/aes_wrap.h"
 #include "crypto/random.h"
 #include "ap_config.h"
@@ -24,11 +26,379 @@ 
 
 #ifdef CONFIG_IEEE80211R_AP
 
+static const char *ft_rrb_ad = "hostapd FT RRB";
+
 static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
 				     const u8 *current_ap, const u8 *sta_addr,
 				     u16 status, const u8 *resp_ies,
 				     size_t resp_ies_len);
 
+struct tlv_list {
+	int type;
+	int len;
+	const u8 *data;
+};
+
+
+/**
+ * decrypt message
+ * @data  full packet
+ * @key   encryption and mac key
+ * @plain pointer to store pointer for plaintext
+ *        (only action_length many bytes)
+ *        needs to be freed by caller if not null
+ *        will only be returned on success
+ * @return 0 on success, -1 on error
+ */
+static int wpa_ft_rrb_decrypt(const u8 *key, const size_t key_len,
+			      const u8 *enc, const size_t enc_len,
+			      const u8 *auth, const size_t auth_len,
+			      const u8 *src_addr, const u8 type,
+			      u8 **plain, size_t *plain_size)
+{
+	size_t len;
+
+	wpa_hexdump_key(MSG_DEBUG, "FT: decrypt using key", key, key_len);
+
+	if (!key) { /* skip decryption */
+		*plain = os_memdup(enc, enc_len);
+		if (enc_len > 0 && !*plain)
+			goto err;
+
+		*plain_size = enc_len;
+
+		return 0;
+	}
+
+	*plain = NULL;
+
+	/* siv overhead */
+	if (enc_len < AES_BLOCK_SIZE)
+		goto err;
+
+	/* len of plaintext + AES-SIV overhead
+	 * remember: tlv type 0x0000 is reserved or padding and termination */
+	len = enc_len - AES_BLOCK_SIZE;
+
+	/* length a multiple of blocksize? */
+	if (len % AES_BLOCK_SIZE != 0)
+		goto err;
+
+	*plain = os_zalloc(enc_len);
+	if (!*plain)
+		goto err;
+
+	const u8 *ad[4] = { src_addr, auth, &type, (u8 *) ft_rrb_ad };
+	size_t ad_len[4] = { ETH_ALEN, auth_len, sizeof(type),
+			     os_strlen(ft_rrb_ad) };
+	if (aes_siv_decrypt(key, key_len, enc, enc_len, 4, ad, ad_len,
+			    *plain) < 0)
+		goto err;
+
+	*plain_size = len;
+	return 0;
+err:
+	os_free(*plain);
+	*plain = NULL;
+	*plain_size = 0;
+
+	wpa_printf(MSG_ERROR, "FT: Failed to decrypt");
+
+	return -1;
+}
+
+
+/* get first tlv record in packet matching type
+ * @data (decrypted) packet
+ * @return 0 on success else -1
+ */
+static int wpa_ft_rrb_get_tlv(const u8 *plain, const size_t plain_len,
+			      int type, size_t *tlv_len, const u8 **tlv_data)
+{
+	struct ft_rrb_tlv *f;
+	size_t left;
+	le16 type16;
+	size_t len;
+
+	left = plain_len;
+	type16 = host_to_le16(type);
+
+	while (left >= sizeof(*f)) {
+		f = (struct ft_rrb_tlv *) plain;
+
+		left -= sizeof(*f);
+		plain += sizeof(*f);
+		len = le_to_host16(f->len);
+
+		if (left < len) {
+			wpa_printf(MSG_DEBUG, "FT: RRB message truncated");
+			break;
+		}
+
+		if (f->type == type16) {
+			*tlv_len = len;
+			*tlv_data = plain;
+			return 0;
+		}
+
+		left -= len;
+		plain += len;
+	}
+
+	return -1;
+}
+
+
+static void wpa_ft_rrb_dump(const u8 *plain, const size_t plain_len)
+{
+	struct ft_rrb_tlv *f;
+	size_t left;
+	size_t len;
+
+	left = plain_len;
+
+	wpa_printf(MSG_DEBUG, "FT: RRB dump message");
+	while (left >= sizeof(*f)) {
+		f = (struct ft_rrb_tlv *) plain;
+
+		left -= sizeof(*f);
+		plain += sizeof(*f);
+		len = le_to_host16(f->len);
+
+		wpa_printf(MSG_DEBUG, "FT: RRB TLV type = %d, len = %zu",
+			   le_to_host16(f->type), len);
+
+		if (left < len) {
+			wpa_printf(MSG_DEBUG, "FT: RRB message truncated: left"
+				   " %zu bytes, need %zu", left, len);
+			break;
+		}
+
+		wpa_hexdump(MSG_DEBUG, "FT: RRB TLV data", plain, len);
+
+		left -= len;
+		plain += len;
+	}
+
+	if (left > 0)
+		wpa_hexdump(MSG_DEBUG, "FT: RRB TLV padding", plain, left);
+
+	wpa_printf(MSG_DEBUG, "FT: RRB dump message end");
+}
+
+
+static inline size_t wpa_ft_tlv_len(const struct tlv_list *tlvs)
+{
+	size_t tlv_len = 0;
+	int i;
+
+	if (!tlvs)
+		return 0;
+
+	for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) {
+		tlv_len += sizeof(struct ft_rrb_tlv);
+		tlv_len += tlvs[i].len;
+	}
+
+	return tlv_len;
+}
+
+
+static inline size_t wpa_ft_tlv_lin(const struct tlv_list *tlvs, u8 *start,
+				    u8 *endpos)
+{
+	int i;
+	size_t tlv_len;
+	struct ft_rrb_tlv *hdr;
+	u8 *pos = start;
+
+	if (!tlvs)
+		return 0;
+
+	tlv_len = 0;
+	pos = start + tlv_len;
+	for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) {
+		tlv_len += sizeof(*hdr);
+		if (start + tlv_len > endpos)
+			return tlv_len;
+		hdr = (struct ft_rrb_tlv *) pos;
+		hdr->type = host_to_le16(tlvs[i].type);
+		hdr->len = host_to_le16(tlvs[i].len);
+		pos = start + tlv_len;
+
+		tlv_len += tlvs[i].len;
+		if (start + tlv_len > endpos)
+			return tlv_len;
+		os_memcpy(pos, tlvs[i].data, tlvs[i].len);
+		pos = start + tlv_len;
+	}
+
+	return tlv_len;
+}
+
+
+static int wpa_ft_rrb_lin(const struct tlv_list *tlvs1,
+			  const struct tlv_list *tlvs2,
+			  int pad, u8 **plain, size_t *plain_len)
+{
+	u8 *pos, *endpos;
+	size_t tlv_len, pad_len;
+
+	tlv_len = 0;
+	tlv_len += wpa_ft_tlv_len(tlvs1);
+	tlv_len += wpa_ft_tlv_len(tlvs2);
+
+	pad_len = 0;
+	if (pad && (tlv_len % AES_BLOCK_SIZE != 0))
+		pad_len = AES_BLOCK_SIZE - (tlv_len % AES_BLOCK_SIZE);
+
+	*plain_len = tlv_len + pad_len;
+	*plain  = os_zalloc(*plain_len);
+	if (*plain == NULL) {
+		wpa_printf(MSG_ERROR, "FT: Failed to allocate plaintext");
+		goto err;
+	}
+
+	pos = *plain;
+	endpos = *plain + tlv_len;
+	pos += wpa_ft_tlv_lin(tlvs1, pos, endpos);
+	pos += wpa_ft_tlv_lin(tlvs2, pos, endpos);
+
+	/* sanity check */
+	if (pos != endpos) {
+		wpa_printf(MSG_ERROR, "FT: length error building RRB");
+		goto err;
+	}
+
+	return 0;
+
+err:
+	os_free(*plain);
+	*plain = NULL;
+	*plain_len = 0;
+	return -1;
+}
+
+
+static int wpa_ft_rrb_encrypt(const u8 *key, const size_t key_len,
+			      const u8 *plain, const size_t plain_len,
+			      const u8 *auth, const size_t auth_len,
+			      const u8 *src_addr, const u8 type, u8 *enc)
+{
+	const u8 *ad[4] = { src_addr, auth, &type, (u8 *) ft_rrb_ad };
+	size_t ad_len[4] = { ETH_ALEN, auth_len, sizeof(type),
+			     os_strlen(ft_rrb_ad) };
+
+	wpa_hexdump_key(MSG_DEBUG, "FT: encrypt using key", key, key_len);
+
+	if (!key)
+		/* encryption not needed, return plaintext as packet */
+		os_memcpy(enc, plain, plain_len);
+	else if (aes_siv_encrypt(key, key_len, plain, plain_len, 4, ad,
+				   ad_len, enc) < 0) {
+		wpa_printf(MSG_ERROR, "FT: Failed to encrypt RRB-OUI message");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * encrypt message
+ * @frame  ft_rrb_frame
+ * @key    encryption and mac key
+ * @packet pointer to store pointer for ciphertext
+ *         (only action_length many bytes)
+ *         needs to be freed by caller if not null
+ *         will only be returned on success
+ * @return 0 on success, -1 on error
+ */
+static int wpa_ft_rrb_build(const u8 *key, const size_t key_len,
+			    const struct tlv_list *tlvs_enc0,
+			    const struct tlv_list *tlvs_enc1,
+			    const struct tlv_list *tlvs_auth,
+			    const u8 *src_addr, const u8 type,
+			    u8 **packet, size_t *packet_len)
+{
+	u8 *plain = NULL, *auth = NULL, *enc;
+	size_t plain_len, auth_len;
+	u16 *head;
+	int ret = -1;
+
+	if (wpa_ft_rrb_lin(tlvs_enc0, tlvs_enc1, key ? 1 : 0,
+			   &plain, &plain_len) < 0)
+		goto out;
+
+	if (wpa_ft_rrb_lin(tlvs_auth, NULL, 0, &auth, &auth_len) < 0)
+		goto out;
+
+	*packet_len = sizeof(*head) + auth_len + plain_len;
+	if (key)
+		*packet_len += AES_BLOCK_SIZE;
+	*packet = os_zalloc(*packet_len);
+
+	if (*packet == NULL)
+		goto out;
+
+	enc = *packet + sizeof(*head) + auth_len;
+	if (wpa_ft_rrb_encrypt(key, key_len, plain, plain_len, auth,
+				      auth_len, src_addr, type, enc) < 0)
+		goto out;
+
+	os_memcpy(*packet + sizeof(*head), auth, auth_len);
+
+	head = (u16 *) *packet;
+	*head = host_to_le16(auth_len);
+
+	ret = 0;
+
+out:
+	os_free(plain);
+	os_free(auth);
+
+	if (ret) {
+		wpa_printf(MSG_ERROR, "FT: Failed to build RRB-OUI message");
+		os_free(*packet);
+		*packet = NULL;
+		*packet_len = 0;
+	}
+
+	return ret;
+}
+
+
+#define RRB_GET_SRC(srcfield, type, field, txt, checklength) do { \
+	if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \
+				&f_##field##_len, &f_##field) < 0 || \
+	    (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \
+		wpa_printf(MSG_DEBUG, "FT: Missing required " #field " in " \
+			   "%s from " MACSTR, txt, MAC2STR(src_addr)); \
+		wpa_ft_rrb_dump(srcfield, srcfield##_len); \
+		goto out; \
+	} \
+} while (0)
+
+#define RRB_GET(type, field, txt, checklength) \
+	RRB_GET_SRC(plain, type, field, txt, checklength)
+#define RRB_GET_AUTH(type, field, txt, checklength) \
+	RRB_GET_SRC(auth, type, field, txt, checklength)
+
+#define RRB_GET_OPTIONAL_SRC(srcfield, type, field, txt, checklength) do { \
+	if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \
+				&f_##field##_len, &f_##field) < 0 || \
+	    (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \
+		wpa_printf(MSG_DEBUG, "FT: Missing optional " #field " in " \
+			   "%s from " MACSTR, txt, MAC2STR(src_addr)); \
+		f_##field##_len = 0; \
+		f_##field = NULL; \
+	} \
+} while (0)
+
+#define RRB_GET_OPTIONAL(type, field, txt, checklength) \
+	RRB_GET_OPTIONAL_SRC(plain, type, field, txt, checklength)
+#define RRB_GET_OPTIONAL_AUTH(type, field, txt, checklength) \
+	RRB_GET_OPTIONAL_SRC(auth, type, field, txt, checklength)
 
 static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst,
 			   const u8 *data, size_t data_len)
@@ -252,7 +622,7 @@  static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
 
 static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth,
 			       const u8 *spa, const u8 *pmk_r0_name,
-			       u8 *pmk_r0, int *pairwise)
+			       const struct wpa_ft_pmk_r0_sa **r0_out)
 {
 	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
 	struct wpa_ft_pmk_r0_sa *r0;
@@ -262,15 +632,14 @@  static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth,
 		if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 &&
 		    os_memcmp_const(r0->pmk_r0_name, pmk_r0_name,
 				    WPA_PMK_NAME_LEN) == 0) {
-			os_memcpy(pmk_r0, r0->pmk_r0, PMK_LEN);
-			if (pairwise)
-				*pairwise = r0->pairwise;
+			*r0_out = r0;
 			return 0;
 		}
 
 		r0 = r0->next;
 	}
 
+	*r0_out = NULL;
 	return -1;
 }
 
@@ -325,48 +694,125 @@  static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth,
 }
 
 
+static void wpa_ft_rrb_lookup_r0kh(struct wpa_authenticator *wpa_auth,
+				   const u8 *src_addr, const u8 *f_r0kh_id,
+				   size_t f_r0kh_id_len,
+				   struct ft_remote_r0kh **r0kh_out)
+{
+	struct ft_remote_r0kh *r0kh;
+
+	r0kh = wpa_auth->conf.r0kh_list;
+	for (; r0kh; r0kh = r0kh->next) {
+		if (src_addr &&  os_memcmp(r0kh->addr, src_addr, ETH_ALEN))
+			continue;
+		if (f_r0kh_id && (r0kh->id_len != f_r0kh_id_len ||
+		    os_memcmp_const(f_r0kh_id, r0kh->id, f_r0kh_id_len)))
+			continue;
+		break;
+	}
+
+	if (!r0kh)
+		wpa_printf(MSG_DEBUG, "FT: No matching R0KH found");
+
+	*r0kh_out = r0kh;
+}
+
+
+static void wpa_ft_rrb_lookup_r1kh(struct wpa_authenticator *wpa_auth,
+				   const u8 *src_addr, const u8 *f_r1kh_id,
+				   struct ft_remote_r1kh **r1kh_out)
+{
+	struct ft_remote_r1kh *r1kh;
+
+	r1kh = wpa_auth->conf.r1kh_list;
+	for (; r1kh; r1kh = r1kh->next) {
+		if (src_addr && os_memcmp(r1kh->addr, src_addr, ETH_ALEN))
+			continue;
+		if (f_r1kh_id &&
+		    os_memcmp_const(r1kh->id, f_r1kh_id, FT_R1KH_ID_LEN))
+			continue;
+		break;
+	}
+
+	if (!r1kh)
+		wpa_printf(MSG_DEBUG, "FT: No matching R1KH found");
+
+	*r1kh_out = r1kh;
+}
+
+
+static int wpa_ft_rrb_check_r0kh(struct wpa_authenticator *wpa_auth,
+				 const u8 *f_r0kh_id, size_t f_r0kh_id_len)
+{
+	if (f_r0kh_id_len != wpa_auth->conf.r0_key_holder_len ||
+	    os_memcmp_const(f_r0kh_id, wpa_auth->conf.r0_key_holder,
+			    f_r0kh_id_len) != 0)
+		return -1;
+
+	return 0;
+}
+
+
+static int wpa_ft_rrb_check_r1kh(struct wpa_authenticator *wpa_auth,
+				 const u8 *f_r1kh_id)
+{
+	if (os_memcmp_const(f_r1kh_id, wpa_auth->conf.r1_key_holder,
+			    FT_R1KH_ID_LEN) != 0)
+		return -1;
+
+	return 0;
+}
+
+
 static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
 			      const u8 *ies, size_t ies_len,
 			      const u8 *pmk_r0_name)
 {
 	struct ft_remote_r0kh *r0kh;
-	struct ft_r0kh_r1kh_pull_frame frame, f;
+	u8 *packet = NULL;
+	const u8 *key;
+	size_t packet_len, key_len;
 
-	r0kh = sm->wpa_auth->conf.r0kh_list;
-	while (r0kh) {
-		if (r0kh->id_len == sm->r0kh_id_len &&
-		    os_memcmp_const(r0kh->id, sm->r0kh_id, sm->r0kh_id_len) ==
-		    0)
-			break;
-		r0kh = r0kh->next;
-	}
+	wpa_ft_rrb_lookup_r0kh(sm->wpa_auth, NULL, sm->r0kh_id, sm->r0kh_id_len,
+			       &r0kh);
 	if (r0kh == NULL) {
 		wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
 			    sm->r0kh_id, sm->r0kh_id_len);
 		return -1;
 	}
+	key = r0kh->key;
+	key_len = sizeof(r0kh->key);
 
-	wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH "
-		   "address " MACSTR, MAC2STR(r0kh->addr));
+	wpa_printf(MSG_DEBUG, "FT: Send pull request to remote R0KH address "
+		   MACSTR, MAC2STR(r0kh->addr));
 
-	os_memset(&frame, 0, sizeof(frame));
-	/* aes_wrap() does not support inplace encryption, so use a temporary
-	 * buffer for the data. */
-	if (random_get_bytes(f.nonce, FT_R0KH_R1KH_PULL_NONCE_LEN)) {
+	if (random_get_bytes(sm->ft_pending_pull_nonce, FT_RRB_NONCE_LEN) < 0) {
 		wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
 			   "nonce");
 		return -1;
 	}
-	os_memcpy(sm->ft_pending_pull_nonce, f.nonce,
-		  FT_R0KH_R1KH_PULL_NONCE_LEN);
-	os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
-	os_memcpy(f.r1kh_id, sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
-	os_memcpy(f.s1kh_id, sm->addr, ETH_ALEN);
-	os_memset(f.pad, 0, sizeof(f.pad));
 
-	if (aes_wrap(r0kh->key, sizeof(r0kh->key),
-		     (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
-		     f.nonce, frame.nonce) < 0)
+	struct tlv_list req_enc[] = {
+		{ .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN,
+		  .data = pmk_r0_name },
+		{ .type = FT_RRB_S1KH_ID, .len = ETH_ALEN,
+		  .data = sm->addr },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
+
+	struct tlv_list req_auth[] = {
+		{ .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN,
+		  .data = sm->ft_pending_pull_nonce },
+		{ .type = FT_RRB_R0KH_ID, .len = sm->r0kh_id_len,
+		  .data = sm->r0kh_id },
+		{ .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
+		  .data = sm->wpa_auth->conf.r1_key_holder },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
+
+	if (wpa_ft_rrb_build(key, key_len, req_enc, NULL, req_auth,
+			     sm->wpa_auth->addr, FT_PACKET_R0KH_R1KH_PULL,
+			     &packet, &packet_len) < 0)
 		return -1;
 
 	wpabuf_free(sm->ft_pending_req_ies);
@@ -375,7 +821,10 @@  static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
 		return -1;
 
 	wpa_ft_rrb_oui_send(sm->wpa_auth, r0kh->addr, FT_PACKET_R0KH_R1KH_PULL,
-			    (u8 *) &frame, sizeof(frame));
+			    packet, packet_len);
+
+	os_free(packet);
+	packet = NULL;
 
 	return 0;
 }
@@ -1422,99 +1871,215 @@  static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
 }
 
 
+static int wpa_ft_rrb_build_r0(const u8 *key, const size_t key_len,
+			       const struct tlv_list *tlvs,
+			       const struct wpa_ft_pmk_r0_sa *pmk_r0,
+			       const u8 *r1kh_id, const u8 *s1kh_id,
+			       const struct tlv_list *tlv_auth,
+			       const u8 *src_addr, const u8 type,
+			       u8 **packet, size_t *packet_len)
+{
+	u8 pmk_r1[PMK_LEN];
+	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+	u8 f_pairwise[sizeof(le16)];
+	int ret;
+
+	if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh_id,
+			      s1kh_id, pmk_r1, pmk_r1_name) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
+	WPA_PUT_LE16(f_pairwise, pmk_r0->pairwise);
+
+	struct tlv_list sess_tlv[] = {
+		{ .type = FT_RRB_PMK_R1, .len = sizeof(pmk_r1),
+		  .data = pmk_r1 },
+		{ .type = FT_RRB_PMK_R1_NAME, .len = sizeof(pmk_r1_name),
+		  .data = pmk_r1_name },
+		{ .type = FT_RRB_PAIRWISE, .len = sizeof(f_pairwise),
+		  .data = f_pairwise },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
+
+	ret = wpa_ft_rrb_build(key, key_len, tlvs, sess_tlv, tlv_auth,
+			       src_addr, type, packet, packet_len);
+
+	os_memset(pmk_r1, 0, sizeof(pmk_r1));
+
+	return ret;
+
+}
+
+
 static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
 			      const u8 *src_addr,
-			      const u8 *data, size_t data_len)
+			      const u8 *enc, size_t enc_len,
+			      const u8 *auth, size_t auth_len)
 {
-	struct ft_r0kh_r1kh_pull_frame f;
-	const u8 *crypt;
-	u8 *plain;
+	const char *msgtype = "pull request";
+	const int type = FT_PACKET_R0KH_R1KH_PULL;
+	u8 *plain = NULL, *packet = NULL;
+	size_t plain_len = 0, packet_len = 0;
 	struct ft_remote_r1kh *r1kh;
-	struct ft_r0kh_r1kh_resp_frame resp, r;
-	u8 pmk_r0[PMK_LEN];
+	const u8 *key = NULL;
+	size_t key_len = 0;
+	const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id, *f_s1kh_id, *f_pmk_r0_name;
+	size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len, f_s1kh_id_len;
+	size_t f_pmk_r0_name_len;
+	const struct wpa_ft_pmk_r0_sa *r0;
+	int ret = -1;
+
+	wpa_printf(MSG_DEBUG, "FT: Received %s from " MACSTR,
+		   msgtype, MAC2STR(src_addr));
+
+	RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1);
+	wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len);
+
+	if (wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len)) {
+		wpa_printf(MSG_DEBUG, "FT: R0KH-ID mismatch");
+		goto out;
+	}
+
+	RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN);
+	wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id));
+
+	wpa_ft_rrb_lookup_r1kh(wpa_auth, src_addr, f_r1kh_id, &r1kh);
+	if (r1kh == NULL)
+		goto out;
+	key = r1kh->key;
+	key_len = sizeof(r1kh->key);
+
+	RRB_GET_AUTH(FT_RRB_NONCE, nonce, "pull request", FT_RRB_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len);
+
+	if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len,
+			       src_addr, type, &plain, &plain_len) < 0)
+		goto out;
+
+	RRB_GET(FT_RRB_PMK_R0_NAME, pmk_r0_name, msgtype, WPA_PMK_NAME_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", f_pmk_r0_name,
+		    f_pmk_r0_name_len);
+
+	RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id));
+
+	struct tlv_list resp[] = {
+		{ .type = FT_RRB_S1KH_ID, .len = f_s1kh_id_len,
+		  .data = f_s1kh_id },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
+	struct tlv_list resp_auth[] = {
+		{ .type = FT_RRB_NONCE, .len = f_nonce_len,
+		  .data = f_nonce },
+		{ .type = FT_RRB_R0KH_ID, .len = f_r0kh_id_len,
+		  .data = f_r0kh_id },
+		{ .type = FT_RRB_R1KH_ID, .len = f_r1kh_id_len,
+		  .data = f_r1kh_id },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
+
+	if (wpa_ft_fetch_pmk_r0(wpa_auth, f_s1kh_id, f_pmk_r0_name, &r0) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: No matching PMK-R0-Name found");
+		goto out;
+	}
+
+	ret = wpa_ft_rrb_build_r0(key, key_len, resp, r0, f_r1kh_id, f_s1kh_id,
+				  resp_auth, wpa_auth->addr,
+				  FT_PACKET_R0KH_R1KH_RESP,
+				  &packet, &packet_len);
+
+	if (!ret)
+		wpa_ft_rrb_oui_send(wpa_auth, src_addr,
+				    FT_PACKET_R0KH_R1KH_RESP, packet,
+				    packet_len);
+
+out:
+	os_free(plain); plain = NULL;
+	os_free(packet); packet = NULL;
+
+	return 0;
+}
+
+
+/* @returns  0 on success
+ *          -1 on error
+ */
+static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth,
+			    const u8 *src_addr, const u8 type,
+			    const u8 *enc, size_t enc_len,
+			    const u8 *auth, size_t auth_len,
+			    const char *msgtype, u8 *s1kh_id_out)
+{
+	u8 *plain = NULL;
+	size_t plain_len = 0;
+	struct ft_remote_r0kh *r0kh;
+	const u8 *key;
+	size_t key_len;
+	const u8 *f_r1kh_id, *f_s1kh_id, *f_r0kh_id;
+	const u8 *f_pmk_r1_name, *f_pairwise, *f_pmk_r1;
+	size_t f_r1kh_id_len, f_s1kh_id_len, f_r0kh_id_len;
+	size_t f_pmk_r1_name_len, f_pairwise_len, f_pmk_r1_len;
 	int pairwise;
+	int ret = -1;
 
-	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull");
+	RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1);
+	wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len);
 
-	if (data_len < sizeof(f))
-		return -1;
+	RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN);
+	wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id));
 
-	r1kh = wpa_auth->conf.r1kh_list;
-	while (r1kh) {
-		if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0)
-			break;
-		r1kh = r1kh->next;
-	}
-	if (r1kh == NULL) {
-		wpa_printf(MSG_DEBUG, "FT: No matching R1KH address found for "
-			   "PMK-R1 pull source address " MACSTR,
-			   MAC2STR(src_addr));
-		return -1;
+	if (wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id)) {
+		wpa_printf(MSG_DEBUG, "FT: R1KH-ID mismatch");
+		goto out;
 	}
 
-	crypt = data + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce);
-	os_memset(&f, 0, sizeof(f));
-	plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce);
-	/* aes_unwrap() does not support inplace decryption, so use a temporary
-	 * buffer for the data. */
-	if (aes_unwrap(r1kh->key, sizeof(r1kh->key),
-		       (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
-		       crypt, plain) < 0) {
-		wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
-			   "request from " MACSTR, MAC2STR(src_addr));
-		return -1;
-	}
+	wpa_ft_rrb_lookup_r0kh(wpa_auth, src_addr, f_r0kh_id, f_r0kh_id_len,
+			       &r0kh);
+	if (r0kh == NULL)
+		goto out;
+	key = r0kh->key;
+	key_len = sizeof(r0kh->key);
 
-	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce",
-		    f.nonce, sizeof(f.nonce));
-	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name",
-		    f.pmk_r0_name, WPA_PMK_NAME_LEN);
-	wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID="
-		   MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id));
-
-	os_memset(&resp, 0, sizeof(resp));
-	/* aes_wrap() does not support inplace encryption, so use a temporary
-	 * buffer for the data. */
-	os_memcpy(r.nonce, f.nonce, sizeof(f.nonce));
-	os_memcpy(r.r1kh_id, f.r1kh_id, FT_R1KH_ID_LEN);
-	os_memcpy(r.s1kh_id, f.s1kh_id, ETH_ALEN);
-	if (wpa_ft_fetch_pmk_r0(wpa_auth, f.s1kh_id, f.pmk_r0_name, pmk_r0,
-				&pairwise) < 0) {
-		wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name found for "
-			   "PMK-R1 pull");
-		return -1;
-	}
+	if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len,
+			       src_addr, type, &plain, &plain_len) < 0)
+		goto out;
 
-	if (wpa_derive_pmk_r1(pmk_r0, f.pmk_r0_name, f.r1kh_id, f.s1kh_id,
-			      r.pmk_r1, r.pmk_r1_name) < 0) {
-		os_memset(pmk_r0, 0, PMK_LEN);
-		return -1;
-	}
-	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", r.pmk_r1, PMK_LEN);
-	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name,
-		    WPA_PMK_NAME_LEN);
-	r.pairwise = host_to_le16(pairwise);
-	os_memset(r.pad, 0, sizeof(r.pad));
+	RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id));
 
-	if (aes_wrap(r1kh->key, sizeof(r1kh->key),
-		     (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
-		     r.nonce, resp.nonce) < 0) {
-		os_memset(pmk_r0, 0, PMK_LEN);
-		return -1;
-	}
+	if (s1kh_id_out)
+		os_memcpy(s1kh_id_out, f_s1kh_id, ETH_ALEN);
 
-	os_memset(pmk_r0, 0, PMK_LEN);
+	RRB_GET(FT_RRB_PAIRWISE, pairwise, msgtype, sizeof(le16));
+	wpa_hexdump(MSG_DEBUG, "FT: pairwise", f_pairwise, f_pairwise_len);
 
-	wpa_ft_rrb_oui_send(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_RESP,
-			    (u8 *) &resp, sizeof(resp));
+	RRB_GET(FT_RRB_PMK_R1_NAME, pmk_r1_name, msgtype, WPA_PMK_NAME_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name",
+		    f_pmk_r1_name, WPA_PMK_NAME_LEN);
+
+	RRB_GET(FT_RRB_PMK_R1, pmk_r1, msgtype, PMK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f_pmk_r1, PMK_LEN);
+
+	pairwise = WPA_GET_LE16(f_pairwise);
+
+	if (wpa_ft_store_pmk_r1(wpa_auth, f_s1kh_id, f_pmk_r1, f_pmk_r1_name,
+				pairwise) < 0)
+		goto out;
+
+	ret = 0;
+out:
+	if (plain) {
+		os_memset(plain, 0, plain_len);
+		os_free(plain); plain = NULL;
+	}
+
+	return ret;
 
-	return 0;
 }
 
 
-static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx)
+static void ft_finish_pull(struct wpa_state_machine *sm)
 {
-	struct wpa_state_machine *sm = eloop_ctx;
 	int res;
 	u8 *resp_ies;
 	size_t resp_ies_len;
@@ -1538,169 +2103,105 @@  static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx)
 }
 
 
-static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx)
+struct ft_get_sta_ctx {
+	const u8 *nonce;
+	const u8 *s1kh_id;
+	struct wpa_state_machine *sm;
+};
+
+
+static int ft_get_sta_cb(struct wpa_state_machine *sm, void *ctx)
 {
-	struct ft_r0kh_r1kh_resp_frame *frame = ctx;
+	struct ft_get_sta_ctx *info = ctx;
 
-	if (os_memcmp(frame->s1kh_id, sm->addr, ETH_ALEN) != 0 ||
-	    os_memcmp(frame->nonce, sm->ft_pending_pull_nonce,
-		      FT_R0KH_R1KH_PULL_NONCE_LEN) != 0 ||
+	if ((info->s1kh_id && os_memcmp(info->s1kh_id, sm->addr, ETH_ALEN)) ||
+	    os_memcmp(info->nonce, sm->ft_pending_pull_nonce,
+		      FT_RRB_NONCE_LEN) != 0 ||
 	    sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL)
 		return 0;
 
-	wpa_printf(MSG_DEBUG, "FT: Response to a pending pull request for "
-		   MACSTR " - process from timeout", MAC2STR(sm->addr));
-	eloop_register_timeout(0, 0, ft_pull_resp_cb_finish, sm, NULL);
+	info->sm = sm;
+
 	return 1;
 }
 
 
 static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
 			      const u8 *src_addr,
-			      const u8 *data, size_t data_len)
+			      const u8 *enc, size_t enc_len,
+			      const u8 *auth, size_t auth_len)
 {
-	struct ft_r0kh_r1kh_resp_frame f;
-	const u8 *crypt;
-	u8 *plain;
-	struct ft_remote_r0kh *r0kh;
-	int pairwise, res;
-
-	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response");
-
-	if (data_len < sizeof(f))
-		return -1;
-
-	r0kh = wpa_auth->conf.r0kh_list;
-	while (r0kh) {
-		if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0)
-			break;
-		r0kh = r0kh->next;
-	}
-	if (r0kh == NULL) {
-		wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for "
-			   "PMK-R0 pull response source address " MACSTR,
-			   MAC2STR(src_addr));
+	const char *msgtype = "pull response";
+	const int type = FT_PACKET_R0KH_R1KH_RESP;
+	int ret = -1;
+	struct ft_get_sta_ctx ctx;
+	u8 s1kh_id[ETH_ALEN];
+	const u8 *f_nonce;
+	size_t f_nonce_len;
+
+	wpa_printf(MSG_DEBUG, "FT: Received %s", msgtype);
+
+	RRB_GET_AUTH(FT_RRB_NONCE, nonce, msgtype, FT_RRB_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len);
+
+	os_memset(&ctx, 0, sizeof(ctx));
+	ctx.nonce = f_nonce;
+	if (!wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) {
+		/* nonce not found */
+		wpa_printf(MSG_DEBUG, "FT: Invalid nonce");
 		return -1;
 	}
 
-	crypt = data + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce);
-	os_memset(&f, 0, sizeof(f));
-	plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce);
-	/* aes_unwrap() does not support inplace decryption, so use a temporary
-	 * buffer for the data. */
-	if (aes_unwrap(r0kh->key, sizeof(r0kh->key),
-		       (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
-		       crypt, plain) < 0) {
-		wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
-			   "response from " MACSTR, MAC2STR(src_addr));
+	ret = wpa_ft_rrb_rx_r1(wpa_auth, src_addr, type, enc, enc_len, auth,
+			       auth_len, msgtype, s1kh_id);
+	if (ret < 0)
 		return -1;
-	}
 
-	if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder,
-			    FT_R1KH_ID_LEN) != 0) {
-		wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull response did not use a "
-			   "matching R1KH-ID");
-		return -1;
+	ctx.s1kh_id = s1kh_id;
+	if (wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) {
+		wpa_printf(MSG_DEBUG, "FT: Response to a pending pull request "
+			   "for " MACSTR, MAC2STR(ctx.sm->addr));
+		ft_finish_pull(ctx.sm);
 	}
 
-	pairwise = le_to_host16(f.pairwise);
-	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce",
-		    f.nonce, sizeof(f.nonce));
-	wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID="
-		   MACSTR " pairwise=0x%x",
-		   MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise);
-	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1",
-			f.pmk_r1, PMK_LEN);
-	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name",
-			f.pmk_r1_name, WPA_PMK_NAME_LEN);
-
-	res = wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
-				  pairwise);
-	wpa_printf(MSG_DEBUG, "FT: Look for pending pull request");
-	wpa_auth_for_each_sta(wpa_auth, ft_pull_resp_cb, &f);
-	os_memset(f.pmk_r1, 0, PMK_LEN);
-
-	return res ? 0 : -1;
+out:
+	return ret;
 }
 
 
 static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
 			      const u8 *src_addr,
-			      const u8 *data, size_t data_len)
+			      const u8 *enc, size_t enc_len,
+			      const u8 *auth, size_t auth_len)
 {
-	struct ft_r0kh_r1kh_push_frame f;
-	const u8 *crypt;
-	u8 *plain;
-	struct ft_remote_r0kh *r0kh;
+	const char *msgtype = "push";
+	const int type = FT_PACKET_R0KH_R1KH_PUSH;
 	struct os_time now;
-	os_time_t tsend;
-	int pairwise;
-
-	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push");
-
-	if (data_len < sizeof(f))
-		return -1;
-
-	r0kh = wpa_auth->conf.r0kh_list;
-	while (r0kh) {
-		if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0)
-			break;
-		r0kh = r0kh->next;
-	}
-	if (r0kh == NULL) {
-		wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for "
-			   "PMK-R0 push source address " MACSTR,
-			   MAC2STR(src_addr));
-		return -1;
-	}
+	struct os_time tsend;
+	const u8 *f_timestamp;
+	size_t f_timestamp_len;
 
-	crypt = data + offsetof(struct ft_r0kh_r1kh_push_frame, timestamp);
-	os_memset(&f, 0, sizeof(f));
-	plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame,
-				       timestamp);
-	/* aes_unwrap() does not support inplace decryption, so use a temporary
-	 * buffer for the data. */
-	if (aes_unwrap(r0kh->key, sizeof(r0kh->key),
-		       (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
-		       crypt, plain) < 0) {
-		wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from "
-			   MACSTR, MAC2STR(src_addr));
-		return -1;
-	}
+	wpa_printf(MSG_DEBUG, "FT: Received %s", msgtype);
 
+	RRB_GET_AUTH(FT_RRB_TIMESTAMP, timestamp, msgtype, sizeof(le32));
+	tsend.sec = WPA_GET_LE32(f_timestamp);
+	wpa_printf(MSG_DEBUG, "FT: timestamp=%ld", tsend.sec);
 	os_get_time(&now);
-	tsend = WPA_GET_LE32(f.timestamp);
-	if ((now.sec > tsend && now.sec - tsend > 60) ||
-	    (now.sec < tsend && tsend - now.sec > 60)) {
-		wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not have a valid "
-			   "timestamp: sender time %d own time %d\n",
-			   (int) tsend, (int) now.sec);
+	if ((now.sec > tsend.sec && now.sec - tsend.sec > 60) ||
+	    (now.sec < tsend.sec && tsend.sec - now.sec > 60)) {
+		wpa_printf(MSG_DEBUG, "FT: did not have a valid "
+			   "timestamp: sender time %ld own time %ld\n",
+			   tsend.sec, now.sec);
 		return -1;
 	}
 
-	if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder,
-			    FT_R1KH_ID_LEN) != 0) {
-		wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not use a matching "
-			   "R1KH-ID (received " MACSTR " own " MACSTR ")",
-			   MAC2STR(f.r1kh_id),
-			   MAC2STR(wpa_auth->conf.r1_key_holder));
+	if (wpa_ft_rrb_rx_r1(wpa_auth, src_addr, type, enc, enc_len, auth,
+			     auth_len, msgtype, NULL) < 0)
 		return -1;
-	}
-
-	pairwise = le_to_host16(f.pairwise);
-	wpa_printf(MSG_DEBUG, "FT: PMK-R1 push - R1KH-ID=" MACSTR " S1KH-ID="
-		   MACSTR " pairwise=0x%x",
-		   MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise);
-	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 push - PMK-R1",
-			f.pmk_r1, PMK_LEN);
-	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 push - PMKR1Name",
-			f.pmk_r1_name, WPA_PMK_NAME_LEN);
-
-	wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
-			    pairwise);
-	os_memset(f.pmk_r1, 0, PMK_LEN);
 
 	return 0;
+out:
+	return -1;
 }
 
 
@@ -1826,6 +2327,10 @@  void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
 		       const u8 *dst_addr, u8 oui_suffix, const u8 *data,
 		       size_t data_len)
 {
+	const u16 *head;
+	const u8 *auth, *enc;
+	size_t alen, elen;
+
 	wpa_printf(MSG_DEBUG, "FT: RRB-OUI received frame from remote AP "
 		   MACSTR, MAC2STR(src_addr));
 	wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame - oui_suffix=%d", oui_suffix);
@@ -1845,15 +2350,31 @@  void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
 		return;
 	}
 
+	head = (u16 *) data;
+	if (data_len < sizeof(*head)) {
+		wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short");
+		return;
+	}
+
+	alen = le_to_host16(*head);
+	if (data_len < sizeof(*head) + alen) {
+		wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short");
+		return;
+	}
+
+	auth = data + sizeof(*head);
+	enc = data + sizeof(*head) + alen;
+	elen = data_len - sizeof(*head) - alen;
+
 	switch (oui_suffix) {
 	case FT_PACKET_R0KH_R1KH_PULL:
-		wpa_ft_rrb_rx_pull(wpa_auth, src_addr, data, data_len);
+		wpa_ft_rrb_rx_pull(wpa_auth, src_addr, enc, elen, auth, alen);
 		break;
 	case FT_PACKET_R0KH_R1KH_RESP:
-		wpa_ft_rrb_rx_resp(wpa_auth, src_addr, data, data_len);
+		wpa_ft_rrb_rx_resp(wpa_auth, src_addr, enc, elen, auth, alen);
 		break;
 	case FT_PACKET_R0KH_R1KH_PUSH:
-		wpa_ft_rrb_rx_push(wpa_auth, src_addr, data, data_len);
+		wpa_ft_rrb_rx_push(wpa_auth, src_addr, enc, elen, auth, alen);
 		break;
 	}
 }
@@ -1862,41 +2383,45 @@  void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
 static int wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
 				  struct wpa_ft_pmk_r0_sa *pmk_r0,
 				  struct ft_remote_r1kh *r1kh,
-				  const u8 *s1kh_id, int pairwise)
+				  const u8 *s1kh_id)
 {
-	struct ft_r0kh_r1kh_push_frame frame, f;
 	struct os_time now;
-	const u8 *plain;
-	u8 *crypt;
-
-	os_memset(&frame, 0, sizeof(frame));
-	/* aes_wrap() does not support inplace encryption, so use a temporary
-	 * buffer for the data. */
-	os_memcpy(f.r1kh_id, r1kh->id, FT_R1KH_ID_LEN);
-	os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN);
-	os_memcpy(f.pmk_r0_name, pmk_r0->pmk_r0_name, WPA_PMK_NAME_LEN);
-	if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh->id,
-			      s1kh_id, f.pmk_r1, f.pmk_r1_name) < 0)
-		return -1;
-	wpa_printf(MSG_DEBUG, "FT: R1KH-ID " MACSTR, MAC2STR(r1kh->id));
-	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f.pmk_r1, PMK_LEN);
-	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", f.pmk_r1_name,
-		    WPA_PMK_NAME_LEN);
+	u8 *packet;
+	size_t packet_len;
+	u8 f_timestamp[sizeof(le32)];
+
 	os_get_time(&now);
-	WPA_PUT_LE32(f.timestamp, now.sec);
-	f.pairwise = host_to_le16(pairwise);
-	os_memset(f.pad, 0, sizeof(f.pad));
-	plain = ((const u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame,
-					     timestamp);
-	crypt = ((u8 *) &frame) + offsetof(struct ft_r0kh_r1kh_push_frame,
-					   timestamp);
-	if (aes_wrap(r1kh->key, sizeof(r1kh->key),
-		     (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
-		     plain, crypt) < 0)
+	WPA_PUT_LE32(f_timestamp, now.sec);
+
+	struct tlv_list push[] = {
+		{ .type = FT_RRB_S1KH_ID, .len = ETH_ALEN,
+		  .data = s1kh_id },
+		{ .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN,
+		  .data = pmk_r0->pmk_r0_name },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
+	struct tlv_list push_auth[] = {
+		{ .type = FT_RRB_TIMESTAMP, .len = sizeof(f_timestamp),
+		  .data = f_timestamp },
+		{ .type = FT_RRB_R0KH_ID,
+		  .len = wpa_auth->conf.r0_key_holder_len,
+		  .data = wpa_auth->conf.r0_key_holder },
+		{ .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
+		  .data = r1kh->id },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
+
+	if (wpa_ft_rrb_build_r0(r1kh->key, sizeof(r1kh->key), push, pmk_r0,
+				r1kh->id, s1kh_id, push_auth, wpa_auth->addr,
+				FT_PACKET_R0KH_R1KH_PUSH,
+				&packet, &packet_len) < 0)
 		return -1;
 
 	wpa_ft_rrb_oui_send(wpa_auth, r1kh->addr, FT_PACKET_R0KH_R1KH_PUSH,
-			    (u8 *) &frame, sizeof(frame));
+			    packet, packet_len);
+
+	os_free(packet);
+	packet = NULL;
 	return 0;
 }
 
@@ -1925,7 +2450,7 @@  void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr)
 
 	r1kh = wpa_auth->conf.r1kh_list;
 	while (r1kh) {
-		wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr, r0->pairwise);
+		wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr);
 		r1kh = r1kh->next;
 	}
 }
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 948cfd3..cce7b1f 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -789,7 +789,7 @@  static void hostapd_rrb_oui_receive(void *ctx, const u8 *src_addr,
 {
 	struct hostapd_data *hapd = ctx;
 
-	wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> "
+	wpa_printf(MSG_DEBUG, "FT: RRB-OUI received packet " MACSTR " -> "
 		   MACSTR, MAC2STR(src_addr), MAC2STR(dst_addr));
 	if (!is_multicast_ether_addr(dst_addr) &&
 	    os_memcmp(hapd->own_addr, dst_addr, ETH_ALEN) != 0)
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 90318d8..3279ad4 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -121,7 +121,7 @@  struct wpa_state_machine {
 			      const u8 *ies, size_t ies_len);
 	void *ft_pending_cb_ctx;
 	struct wpabuf *ft_pending_req_ies;
-	u8 ft_pending_pull_nonce[FT_R0KH_R1KH_PULL_NONCE_LEN];
+	u8 ft_pending_pull_nonce[FT_RRB_NONCE_LEN];
 	u8 ft_pending_auth_transaction;
 	u8 ft_pending_current_ap[ETH_ALEN];
 #endif /* CONFIG_IEEE80211R_AP */
diff --git a/tests/hwsim/test_ap_ft.py b/tests/hwsim/test_ap_ft.py
index bcd06f0..7764495 100644
--- a/tests/hwsim/test_ap_ft.py
+++ b/tests/hwsim/test_ap_ft.py
@@ -55,9 +55,9 @@  def ft_params1a(rsn=True, ssid=None, passphrase=None):
 
 def ft_params1(rsn=True, ssid=None, passphrase=None):
     params = ft_params1a(rsn, ssid, passphrase)
-    params['r0kh'] = [ "02:00:00:00:03:00 nas1.w1.fi 100102030405060708090a0b0c0d0e0f",
-                       "02:00:00:00:04:00 nas2.w1.fi 300102030405060708090a0b0c0d0e0f" ]
-    params['r1kh'] = "02:00:00:00:04:00 00:01:02:03:04:06 200102030405060708090a0b0c0d0e0f"
+    params['r0kh'] = [ "02:00:00:00:03:00 nas1.w1.fi 100102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f",
+                       "02:00:00:00:04:00 nas2.w1.fi 300102030405060708090a0b0c0d0e0f300102030405060708090a0b0c0d0e0f" ]
+    params['r1kh'] = "02:00:00:00:04:00 00:01:02:03:04:06 200102030405060708090a0b0c0d0e0f200102030405060708090a0b0c0d0e0f"
     return params
 
 def ft_params2a(rsn=True, ssid=None, passphrase=None):
@@ -68,36 +68,36 @@  def ft_params2a(rsn=True, ssid=None, passphrase=None):
 
 def ft_params2(rsn=True, ssid=None, passphrase=None):
     params = ft_params2a(rsn, ssid, passphrase)
-    params['r0kh'] = [ "02:00:00:00:03:00 nas1.w1.fi 200102030405060708090a0b0c0d0e0f",
-                       "02:00:00:00:04:00 nas2.w1.fi 000102030405060708090a0b0c0d0e0f" ]
-    params['r1kh'] = "02:00:00:00:03:00 00:01:02:03:04:05 300102030405060708090a0b0c0d0e0f"
+    params['r0kh'] = [ "02:00:00:00:03:00 nas1.w1.fi 200102030405060708090a0b0c0d0e0f200102030405060708090a0b0c0d0e0f",
+                       "02:00:00:00:04:00 nas2.w1.fi 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f" ]
+    params['r1kh'] = "02:00:00:00:03:00 00:01:02:03:04:05 300102030405060708090a0b0c0d0e0f300102030405060708090a0b0c0d0e0f"
     return params
 
 def ft_params1_r0kh_mismatch(rsn=True, ssid=None, passphrase=None):
     params = ft_params(rsn, ssid, passphrase)
     params['nas_identifier'] = "nas1.w1.fi"
     params['r1_key_holder'] = "000102030405"
-    params['r0kh'] = [ "02:00:00:00:03:00 nas1.w1.fi 100102030405060708090a0b0c0d0e0f",
-                       "12:00:00:00:04:00 nas2.w1.fi 300102030405060708090a0b0c0d0e0f" ]
-    params['r1kh'] = "12:00:00:00:04:00 10:01:02:03:04:06 200102030405060708090a0b0c0d0e0f"
+    params['r0kh'] = [ "02:00:00:00:03:00 nas1.w1.fi 100102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f",
+                       "12:00:00:00:04:00 nas2.w1.fi 300102030405060708090a0b0c0d0e0f300102030405060708090a0b0c0d0e0f" ]
+    params['r1kh'] = "12:00:00:00:04:00 10:01:02:03:04:06 200102030405060708090a0b0c0d0e0f200102030405060708090a0b0c0d0e0f"
     return params
 
 def ft_params2_incorrect_rrb_key(rsn=True, ssid=None, passphrase=None):
     params = ft_params(rsn, ssid, passphrase)
     params['nas_identifier'] = "nas2.w1.fi"
     params['r1_key_holder'] = "000102030406"
-    params['r0kh'] = [ "02:00:00:00:03:00 nas1.w1.fi 200102030405060708090a0b0c0d0ef1",
-                       "02:00:00:00:04:00 nas2.w1.fi 000102030405060708090a0b0c0d0ef2" ]
-    params['r1kh'] = "02:00:00:00:03:00 00:01:02:03:04:05 300102030405060708090a0b0c0d0ef3"
+    params['r0kh'] = [ "02:00:00:00:03:00 nas1.w1.fi 200102030405060708090a0b0c0d0ef1200102030405060708090a0b0c0d0ef1",
+                       "02:00:00:00:04:00 nas2.w1.fi 000102030405060708090a0b0c0d0ef2000102030405060708090a0b0c0d0ef2" ]
+    params['r1kh'] = "02:00:00:00:03:00 00:01:02:03:04:05 300102030405060708090a0b0c0d0ef3300102030405060708090a0b0c0d0ef3"
     return params
 
 def ft_params2_r0kh_mismatch(rsn=True, ssid=None, passphrase=None):
     params = ft_params(rsn, ssid, passphrase)
     params['nas_identifier'] = "nas2.w1.fi"
     params['r1_key_holder'] = "000102030406"
-    params['r0kh'] = [ "12:00:00:00:03:00 nas1.w1.fi 200102030405060708090a0b0c0d0e0f",
-                       "02:00:00:00:04:00 nas2.w1.fi 000102030405060708090a0b0c0d0e0f" ]
-    params['r1kh'] = "12:00:00:00:03:00 10:01:02:03:04:05 300102030405060708090a0b0c0d0e0f"
+    params['r0kh'] = [ "12:00:00:00:03:00 nas1.w1.fi 200102030405060708090a0b0c0d0e0f200102030405060708090a0b0c0d0e0f",
+                       "02:00:00:00:04:00 nas2.w1.fi 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f" ]
+    params['r1kh'] = "12:00:00:00:03:00 10:01:02:03:04:05 300102030405060708090a0b0c0d0e0f300102030405060708090a0b0c0d0e0f"
     return params
 
 def run_roams(dev, apdev, hapd0, hapd1, ssid, passphrase, over_ds=False,
@@ -1061,7 +1061,7 @@  def test_ap_ft_ap_oom3(dev, apdev):
         # This will fail due to not being able to send out PMK-R1 pull request
         dev[0].roam(bssid1)
 
-    with fail_test(hapd1, 1, "aes_wrap;wpa_ft_pull_pmk_r1"):
+    with fail_test(hapd1, 1, "aes_siv_encrypt;wpa_ft_pull_pmk_r1"):
         # This will fail due to not being able to send out PMK-R1 pull request
         dev[0].roam(bssid1)
 
@@ -1270,7 +1270,7 @@  def test_ap_ft_ap_oom10(dev, apdev):
     bssid1 = hapd1.own_addr()
     dev[0].scan_for_bss(bssid1, freq="2412")
 
-    with fail_test(hapd0, 1, "aes_unwrap;wpa_ft_rrb_rx_pull"):
+    with fail_test(hapd0, 1, "aes_siv_decrypt;wpa_ft_rrb_rx_pull"):
         # This will fail to roam
         if "OK" not in dev[0].request("FT_DS " + bssid1):
             raise Exception("FT_DS failed")
@@ -1282,13 +1282,13 @@  def test_ap_ft_ap_oom10(dev, apdev):
             raise Exception("FT_DS failed")
         wait_fail_trigger(hapd0, "GET_FAIL")
 
-    with fail_test(hapd0, 1, "aes_wrap;wpa_ft_rrb_rx_pull"):
+    with fail_test(hapd0, 1, "aes_siv_encrypt;wpa_ft_rrb_rx_pull"):
         # This will fail to roam
         if "OK" not in dev[0].request("FT_DS " + bssid1):
             raise Exception("FT_DS failed")
         wait_fail_trigger(hapd0, "GET_FAIL")
 
-    with fail_test(hapd1, 1, "aes_unwrap;wpa_ft_rrb_rx_resp"):
+    with fail_test(hapd1, 1, "aes_siv_decrypt;wpa_ft_rrb_rx_resp"):
         # This will fail to roam
         if "OK" not in dev[0].request("FT_DS " + bssid1):
             raise Exception("FT_DS failed")
@@ -1310,7 +1310,7 @@  def test_ap_ft_ap_oom11(dev, apdev):
         wait_fail_trigger(hapd0, "GET_FAIL")
 
     dev[1].scan_for_bss(bssid0, freq="2412")
-    with fail_test(hapd0, 1, "aes_wrap;wpa_ft_generate_pmk_r1"):
+    with fail_test(hapd0, 1, "aes_siv_encrypt;wpa_ft_generate_pmk_r1"):
         dev[1].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
                        scan_freq="2412")
         wait_fail_trigger(hapd0, "GET_FAIL")
diff --git a/tests/hwsim/test_hapd_ctrl.py b/tests/hwsim/test_hapd_ctrl.py
index d18a6b4..0a32fda 100644
--- a/tests/hwsim/test_hapd_ctrl.py
+++ b/tests/hwsim/test_hapd_ctrl.py
@@ -295,12 +295,12 @@  def test_hapd_ctrl_set_error_cases(dev, apdev):
                "wep_key_len_broadcast 20",
                "wep_rekey_period -1",
                "wep_default_key 4",
-               "r0kh 02:00:00:00:03:0q nas1.w1.fi 100102030405060708090a0b0c0d0e0f",
-               "r0kh 02:00:00:00:03:00 12345678901234567890123456789012345678901234567890.nas1.w1.fi 100102030405060708090a0b0c0d0e0f",
-               "r0kh 02:00:00:00:03:00 nas1.w1.fi 100q02030405060708090a0b0c0d0e0f",
-               "r1kh 02:00:00:00:04:q0 00:01:02:03:04:06 200102030405060708090a0b0c0d0e0f",
-               "r1kh 02:00:00:00:04:00 00:01:02:03:04:q6 200102030405060708090a0b0c0d0e0f",
-               "r1kh 02:00:00:00:04:00 00:01:02:03:04:06 2q0102030405060708090a0b0c0d0e0f",
+               "r0kh 02:00:00:00:03:0q nas1.w1.fi 100102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f",
+               "r0kh 02:00:00:00:03:00 12345678901234567890123456789012345678901234567890.nas1.w1.fi 100102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f",
+               "r0kh 02:00:00:00:03:00 nas1.w1.fi 100q02030405060708090a0b0c0d0e0f100q02030405060708090a0b0c0d0e0f",
+               "r1kh 02:00:00:00:04:q0 00:01:02:03:04:06 200102030405060708090a0b0c0d0e0f200102030405060708090a0b0c0d0e0f",
+               "r1kh 02:00:00:00:04:00 00:01:02:03:04:q6 200102030405060708090a0b0c0d0e0f200102030405060708090a0b0c0d0e0f",
+               "r1kh 02:00:00:00:04:00 00:01:02:03:04:06 2q0102030405060708090a0b0c0d0e0f2q0102030405060708090a0b0c0d0e0f",
                "roaming_consortium 1",
                "roaming_consortium 12",
                "roaming_consortium 112233445566778899aabbccddeeff00",