diff mbox

[PATCHv3,2/4] FT: new rrb message format

Message ID 1477048312-27164-3-git-send-email-michael-dev@fami-braun.de
State Changes Requested
Headers show

Commit Message

michael-dev Oct. 21, 2016, 11:11 a.m. UTC
Convert FT RRB into new TLV based format.
Use AES+HMAC-SHA256 in Encrypt-Then-Mac mode as AED cipher.

This breaks backward compatiblity.

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

--
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      |   2 +
 hostapd/config_file.c |  23 +-
 src/ap/wpa_auth.h     | 101 ++++----
 src/ap/wpa_auth_ft.c  | 695 +++++++++++++++++++++++++++++++++++++-------------
 src/ap/wpa_auth_i.h   |   2 +-
 5 files changed, 601 insertions(+), 222 deletions(-)
diff mbox

Patch

diff --git a/hostapd/Makefile b/hostapd/Makefile
index a667bfb..87ec1af 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -296,6 +296,8 @@  NEED_SHA256=y
 NEED_AES_OMAC1=y
 NEED_AES_UNWRAP=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 8e7bcc7..2644b1e 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
@@ -992,6 +993,24 @@  static int hostapd_config_tx_queue(struct hostapd_config *conf,
 
 
 #ifdef CONFIG_IEEE80211R
+static int rkh_dervice_key(struct ft_remote_key *rkey, const char *pos)
+{
+	u8 key[16];
+
+	if (hexstr2bin(pos, key, sizeof(key)))
+		return -1;
+
+	if (hmac_sha256_kdf(key, sizeof(key), "FT ENCRYPT", NULL, 0,
+			    rkey->encrypt, sizeof(rkey->encrypt)) < 0)
+		return -1;
+
+	if (hmac_sha256_kdf(key, sizeof(key), "FT MAC", NULL, 0,
+			    rkey->mac, sizeof(rkey->mac)) < 0)
+		wpa_printf(MSG_ERROR, "hmac_sha256_kdf failed");
+
+	return 0;
+}
+
 static int add_r0kh(struct hostapd_bss_config *bss, char *value)
 {
 	struct ft_remote_r0kh *r0kh;
@@ -1025,7 +1044,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(&r0kh->key, pos) < 0) {
 		wpa_printf(MSG_ERROR, "Invalid R0KH key: '%s'", pos);
 		os_free(r0kh);
 		return -1;
@@ -1070,7 +1089,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(&r1kh->key, pos) < 0) {
 		wpa_printf(MSG_ERROR, "Invalid R1KH key: '%s'", pos);
 		os_free(r1kh);
 		return -1;
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index b56a69b..2041813 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -42,54 +42,58 @@  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;
+/* new packet format */
+
+/* packet layout
+ *  | IEEE 802 extended OUI ethertype frame header |
+ *  | multiple of of struct ft_rrbv1_tlv |
+ *  | all-zero padding for encryption (blocksize) | 8 byte extra for keywrap |
+ *  | SHA256-HMAC of size SHA256_MAC_LEN |
+ * where
+ *  packet_type = FT_PACKET_R0KH_R1KH*
+ *  action_length = size of all struct ft_rrbv1_tlv (without final padding)
+ *                  (aka plaintext length)
+ *  encryption: covers all struct ft_rrbv1_tlv
+ */
 
-#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          1 /* size FT_RRB_NONCE_LEN */
+#define FT_RRB_TIMESTAMP      2 /* le32 unix seconds */
+
+#define FT_RRB_R0KH_ID        3 /* FT_R0KH_ID_MAX_LEN */
+#define FT_RRB_R1KH_ID        4 /* FT_R1KH_ID_LEN */
+#define FT_RRB_S1KH_ID        5 /* ETH_ALEN */
+
+#define FT_RRB_PMK_R0_NAME    6 /* WPA_PMK_NAME_LEN */
+#define FT_RRB_PMK_R0         7 /* PMK_LEN */
+#define FT_RRB_PMK_R1_NAME    8 /* WPA_PMK_NAME_LEN */
+#define FT_RRB_PMK_R1         9 /* PMK_LEN */
+
+#define FT_RRB_PAIRWISE      10 /* le16 */
 
-#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];
+struct ft_rrbv1_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:
+ *   required: NONCE, R0KH_ID, PMK_R0_NAME, R1KH_ID, S1KH_ID
+ *
+ * response frame TLVs:
+ *   required: NONCE, R1KH_ID, S1KH_ID, all session-TLVs
+ *
+ * push frame TLVs:
+ *   required: TIMESTAMP, R1KH_ID, S1KH_ID, PMK_R0_NAME,
+ *             session-TLVs
+ */
+
 #ifdef _MSC_VER
 #pragma pack(pop)
 #endif /* _MSC_VER */
@@ -103,12 +107,17 @@  struct rsn_pmksa_cache_entry;
 struct eapol_state_machine;
 
 
+struct ft_remote_key {
+	u8 encrypt[16]; /* encryption key */
+	u8 mac[16]; /* authentication key */
+};
+
 struct ft_remote_r0kh {
 	struct ft_remote_r0kh *next;
 	u8 addr[ETH_ALEN];
 	u8 id[FT_R0KH_ID_MAX_LEN];
 	size_t id_len;
-	u8 key[16];
+	struct ft_remote_key key;
 };
 
 
@@ -116,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];
+	struct ft_remote_key key;
 };
 
 
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index fc96675..a620136 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -15,6 +15,7 @@ 
 #include "common/ieee802_11_common.h"
 #include "crypto/aes_wrap.h"
 #include "crypto/random.h"
+#include "crypto/sha256.h"
 #include "ap_config.h"
 #include "ieee802_11.h"
 #include "wmm.h"
@@ -30,6 +31,279 @@  static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
 				     size_t resp_ies_len);
 
 
+/**
+ * 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 struct ft_remote_key *key,
+			      const u8 *data, const size_t data_len,
+			      u8 **plain, size_t *plain_size)
+{
+	int len, aes_len;
+	u8 mac[SHA256_MAC_LEN];
+
+	*plain = NULL;
+
+	/* extra aes wrap bytes + sha256 length */
+	if (data_len < 8 + SHA256_MAC_LEN)
+		goto err;
+
+	aes_len = data_len - SHA256_MAC_LEN;
+
+	/* len of plaintext + all-zero padding (up to 8 bytes)
+	 * rember: tlv type 0x0000 is reserved or padding and termination */
+	len = aes_len - 8;
+
+	/* length a multiple of blocksize? */
+	if (len % 8 != 0)
+		goto err;
+
+	if (hmac_sha256(key->mac, sizeof(key->mac), data, aes_len, mac))
+		goto err;
+
+	if (os_memcmp(mac, data + aes_len, SHA256_MAC_LEN) != 0)
+		goto err;
+
+	*plain = os_zalloc(len);
+	if (!*plain)
+		goto err;
+
+	if (aes_unwrap(key->encrypt, sizeof(key->encrypt), len / 8, data,
+		       *plain))
+		goto err;
+
+	*plain_size = len;
+	return 0;
+err:
+	os_free(*plain);
+	*plain = NULL;
+	*plain_size = 0;
+
+	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_rrbv1_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_rrbv1_tlv *) plain;
+
+		left -= sizeof(*f);
+		plain += sizeof(*f);
+		len = le_to_host16(f->len);
+
+		if (left < len)
+			break;
+
+		if (f->type == type16) {
+			*tlv_len = len;
+			*tlv_data = plain;
+			return 0;
+		}
+
+		left -= len;
+		plain += len;
+	}
+
+	return -1;
+}
+
+
+struct tlv_list {
+	int type;
+	int len;
+	const u8 *data;
+};
+
+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_rrbv1_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_rrbv1_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_rrbv1_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,
+			  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 = (8 - (tlv_len % 8)) % 8;
+
+	*plain_len = tlv_len + pad_len;
+	*plain  = os_zalloc(*plain_len);
+	if (*plain == NULL)
+		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 struct ft_remote_key *key,
+			      const u8 *plain, size_t plain_len,
+			      u8 **packet, size_t *packet_len)
+{
+	int aes_len;
+
+	aes_len = plain_len + 8;
+	*packet_len = aes_len + SHA256_MAC_LEN;
+	*packet = os_zalloc(*packet_len);
+
+	if (*packet == NULL)
+		goto err;
+
+	if (aes_wrap(key->encrypt, sizeof(key->encrypt), plain_len / 8, plain,
+		     *packet))
+		goto err;
+
+	if (hmac_sha256(key->mac, sizeof(key->mac), *packet, aes_len,
+			*packet + aes_len) < 0)
+		goto err;
+
+	return 0;
+
+err:
+	os_free(*packet);
+	*packet = NULL;
+	*packet_len = 0;
+
+	return -1;
+}
+
+/**
+ * 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 struct ft_remote_key *key,
+			    const struct tlv_list *tlvs1,
+			    const struct tlv_list *tlvs2,
+			    u8 **packet, size_t *packet_len)
+{
+	u8 *plain;
+	size_t plain_len;
+	int ret = -1;
+
+	if (wpa_ft_rrb_lin(tlvs1, tlvs2, &plain, &plain_len) < 0)
+		goto out;
+
+	if (wpa_ft_rrb_encrypt(key, plain, plain_len, packet,
+			       packet_len) < 0)
+		goto out;
+
+	ret = 0;
+
+out:
+	os_free(plain);
+
+	if (ret) {
+		os_free(*packet);
+		*packet = NULL;
+		*packet_len = 0;
+	}
+
+	return ret;
+}
+
+
+#define RRB_GET(type, field, txt, checklength) do { \
+	if (wpa_ft_rrb_get_tlv(plain, plain_len, type, \
+				&f_##field##_len, &f_##field) < 0 || \
+	    (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \
+		wpa_printf(MSG_DEBUG, "FT: Missing " #field " in PMK-R1 " \
+			   "%s from " MACSTR, txt, MAC2STR(src_addr)); \
+		goto out; \
+	} \
+} while (0)
+
+
 static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst,
 			   const u8 *data, size_t data_len)
 {
@@ -252,7 +526,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 +536,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;
 }
 
@@ -330,7 +603,8 @@  static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
 			      const u8 *pmk_r0_name)
 {
 	struct ft_remote_r0kh *r0kh;
-	struct ft_r0kh_r1kh_pull_frame frame, f;
+	u8 *packet = NULL;
+	size_t packet_len;
 
 	r0kh = sm->wpa_auth->conf.r0kh_list;
 	while (r0kh) {
@@ -349,25 +623,30 @@  static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
 	wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 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_tlv[] = {
+		{ .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_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN,
+		  .data = pmk_r0_name },
+		{ .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
+		  .data = sm->wpa_auth->conf.r1_key_holder },
+		{ .type = FT_RRB_S1KH_ID, .len = ETH_ALEN,
+		  .data = sm->addr },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
+
+	if (wpa_ft_rrb_build(&r0kh->key, req_tlv, NULL, &packet,
+			     &packet_len) < 0) {
 		return -1;
+	}
 
 	wpabuf_free(sm->ft_pending_req_ies);
 	sm->ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len);
@@ -375,7 +654,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;
 }
@@ -1419,23 +1701,57 @@  static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
 }
 
 
+static int wpa_ft_rrb_build_r0(const struct ft_remote_key *key,
+			       const struct tlv_list *tlvs,
+			       const struct wpa_ft_pmk_r0_sa *pmk_r0,
+			       const u8 *r1kh_id, const u8 *s1kh_id,
+			       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;
+
+	wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh_id,
+			  s1kh_id, pmk_r1, pmk_r1_name);
+	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, tlvs, sess_tlv, 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)
 {
-	struct ft_r0kh_r1kh_pull_frame f;
-	const u8 *crypt;
-	u8 *plain;
+	u8 *plain, *packet = NULL;
+	size_t plain_len, packet_len;
 	struct ft_remote_r1kh *r1kh;
-	struct ft_r0kh_r1kh_resp_frame resp, r;
-	u8 pmk_r0[PMK_LEN];
-	int pairwise;
+	int ret;
+	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;
+	struct tlv_list *sess_tlv = NULL;
+	const struct wpa_ft_pmk_r0_sa *r0;
 
 	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull");
 
-	if (data_len < sizeof(f))
-		return -1;
-
 	r1kh = wpa_auth->conf.r1kh_list;
 	while (r1kh) {
 		if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0)
@@ -1449,58 +1765,61 @@  static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
 		return -1;
 	}
 
-	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) {
+	if (wpa_ft_rrb_decrypt(&r1kh->key, data, data_len, &plain,
+			       &plain_len) < 0) {
 		wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
 			   "request from " MACSTR, MAC2STR(src_addr));
 		return -1;
 	}
 
+	RRB_GET(FT_RRB_R0KH_ID, r0kh_id, "pull request", -1);
+	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)
+		goto out;
+
+	RRB_GET(FT_RRB_NONCE, nonce, "pull request", FT_RRB_NONCE_LEN);
 	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce",
-		    f.nonce, sizeof(f.nonce));
+		    f_nonce, f_nonce_len);
+
+	RRB_GET(FT_RRB_PMK_R0_NAME, pmk_r0_name, "pull request",
+		WPA_PMK_NAME_LEN);
 	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name",
-		    f.pmk_r0_name, WPA_PMK_NAME_LEN);
+		    f_pmk_r0_name, f_pmk_r0_name_len);
+
+	RRB_GET(FT_RRB_R1KH_ID, r1kh_id, "pull request", FT_R1KH_ID_LEN);
+	RRB_GET(FT_RRB_S1KH_ID, s1kh_id, "pull request", ETH_ALEN);
 	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;
-	}
+		   MACSTR, MAC2STR(f_r1kh_id), MAC2STR(f_s1kh_id));
 
-	wpa_derive_pmk_r1(pmk_r0, f.pmk_r0_name, f.r1kh_id, f.s1kh_id,
-			  r.pmk_r1, r.pmk_r1_name);
-	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));
+	struct tlv_list resp_tlv[] = {
+		{ .type = FT_RRB_NONCE, .len = f_nonce_len,
+		  .data = f_nonce },
+		{ .type = FT_RRB_R1KH_ID, .len = f_r1kh_id_len,
+		  .data = f_r1kh_id },
+		{ .type = FT_RRB_S1KH_ID, .len = f_s1kh_id_len,
+		  .data = f_s1kh_id },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
 
-	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 (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 for "
+			   "PMK-R1 pull request");
+		goto out;
 	}
 
-	os_memset(pmk_r0, 0, PMK_LEN);
+	ret = wpa_ft_rrb_build_r0(&r1kh->key, resp_tlv, r0, f_r1kh_id,
+				  f_s1kh_id, &packet, &packet_len);
+
+	if (!ret)
+		wpa_ft_rrb_oui_send(wpa_auth, src_addr,
+				    FT_PACKET_R0KH_R1KH_RESP, packet,
+				    packet_len);
 
-	wpa_ft_rrb_oui_send(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_RESP,
-			    (u8 *) &resp, sizeof(resp));
+out:
+	os_free(plain); plain = NULL;
+	os_free(packet); packet = NULL;
+	os_free(sess_tlv); sess_tlv = NULL;
 
 	return 0;
 }
@@ -1532,14 +1851,19 @@  static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx)
 }
 
 
+struct ft_pull_resp_cb_ctx {
+	const u8 *s1kh_id;
+	const u8 *nonce;
+};
+
 static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx)
 {
-	struct ft_r0kh_r1kh_resp_frame *frame = ctx;
+	struct ft_pull_resp_cb_ctx *info = ctx;
 
-	if (os_memcmp(frame->s1kh_id, sm->addr, ETH_ALEN) != 0)
+	if (os_memcmp(info->s1kh_id, sm->addr, ETH_ALEN) != 0)
 		return 0;
-	if (os_memcmp(frame->nonce, sm->ft_pending_pull_nonce,
-		      FT_R0KH_R1KH_PULL_NONCE_LEN) != 0)
+	if (os_memcmp(info->nonce, sm->ft_pending_pull_nonce,
+		      FT_RRB_NONCE_LEN) != 0)
 		return 0;
 	if (sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL)
 		return 0;
@@ -1551,21 +1875,71 @@  static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx)
 }
 
 
+/* @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 *plain, size_t plain_len,
+			    const char *msgtype)
+{
+	const u8 *f_r1kh_id, *f_s1kh_id;
+	const u8 *f_pmk_r1_name, *f_pairwise, *f_pmk_r1;
+	size_t f_r1kh_id_len, f_s1kh_id_len;
+	size_t f_pmk_r1_name_len, f_pairwise_len, f_pmk_r1_len;
+	int pairwise;
+	int ret = -1;
+
+	RRB_GET(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN);
+	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 %s did not use a matching "
+			   "R1KH-ID", msgtype);
+		goto out;
+	}
+
+
+	RRB_GET(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN);
+	RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "FT: PMK-R1 %s", msgtype);
+	wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR " S1KH-ID=" MACSTR,
+		   MAC2STR(f_r1kh_id), MAC2STR(f_s1kh_id));
+
+	RRB_GET(FT_RRB_PAIRWISE, pairwise, msgtype, sizeof(le16));
+	RRB_GET(FT_RRB_PMK_R1_NAME, pmk_r1_name, msgtype, 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);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name",
+		    f_pmk_r1_name, WPA_PMK_NAME_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)
+		return -1;
+
+	ret = 0;
+
+out:
+	return ret;
+
+}
+
 static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
 			      const u8 *src_addr,
 			      const u8 *data, size_t data_len)
 {
-	struct ft_r0kh_r1kh_resp_frame f;
-	const u8 *crypt;
 	u8 *plain;
+	size_t plain_len;
 	struct ft_remote_r0kh *r0kh;
-	int pairwise, res;
+	int ret;
+	struct ft_pull_resp_cb_ctx ctx;
+	const u8 *f_nonce, *f_s1kh_id;
+	size_t f_nonce_len, f_s1kh_id_len;
 
 	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)
@@ -1579,44 +1953,41 @@  static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
 		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) {
+	if (wpa_ft_rrb_decrypt(&r0kh->key, data, data_len, &plain,
+			       &plain_len) < 0) {
 		wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
 			   "response from " MACSTR, MAC2STR(src_addr));
 		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;
-	}
+	RRB_GET(FT_RRB_NONCE, nonce, "pull response", FT_RRB_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", f_nonce, f_nonce_len);
+
+	RRB_GET(FT_RRB_S1KH_ID, s1kh_id, "pull response", ETH_ALEN);
+
+	ret = wpa_ft_rrb_rx_r1(wpa_auth, src_addr, plain, plain_len,
+			       "pull response");
+
+	if (ret == -1)
+		goto out;
 
-	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;
+	os_memset(&ctx, 0, sizeof(ctx));
+
+	ctx.s1kh_id = f_s1kh_id;
+	ctx.nonce = f_nonce;
+
+	wpa_auth_for_each_sta(wpa_auth, ft_pull_resp_cb, &ctx);
+
+out:
+	if (plain) {
+		os_memset(plain, 0, plain_len);
+		os_free(plain);
+		plain = NULL;
+	}
+
+	return ret;
 }
 
 
@@ -1624,19 +1995,17 @@  static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
 			      const u8 *src_addr,
 			      const u8 *data, size_t data_len)
 {
-	struct ft_r0kh_r1kh_push_frame f;
-	const u8 *crypt;
+	int ret = -1;
 	u8 *plain;
+	size_t plain_len;
 	struct ft_remote_r0kh *r0kh;
 	struct os_time now;
 	os_time_t tsend;
-	int pairwise;
+	const u8 *f_timestamp;
+	size_t f_timestamp_len;
 
 	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)
@@ -1650,53 +2019,36 @@  static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
 		return -1;
 	}
 
-	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) {
+	if (wpa_ft_rrb_decrypt(&r0kh->key, data, data_len, &plain,
+			       &plain_len) < 0) {
 		wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from "
 			   MACSTR, MAC2STR(src_addr));
 		return -1;
 	}
 
+	RRB_GET(FT_RRB_TIMESTAMP, timestamp, "push", sizeof(le32));
 	os_get_time(&now);
-	tsend = WPA_GET_LE32(f.timestamp);
+	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);
-		return -1;
+		goto out;
 	}
 
-	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));
-		return -1;
-	}
+	if (wpa_ft_rrb_rx_r1(wpa_auth, src_addr, plain, plain_len,
+			     "push") < 0)
+		goto out;
 
-	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);
+	ret = 0;
+out:
 
-	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);
+	os_memset(plain, 0, plain_len);
+	os_free(plain);
+	plain = NULL;
 
-	return 0;
+	return ret;
 }
 
 
@@ -1850,40 +2202,37 @@  void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
 static void 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);
-	wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh->id,
-			  s1kh_id, f.pmk_r1, f.pmk_r1_name);
-	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_tlv[] = {
+		{ .type = FT_RRB_TIMESTAMP, .len = sizeof(f_timestamp),
+		  .data = f_timestamp },
+		{ .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
+		  .data = r1kh->id },
+		{ .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 },
+	};
+
+	if (wpa_ft_rrb_build_r0(&r1kh->key, push_tlv, pmk_r0, r1kh->id,
+				s1kh_id, &packet, &packet_len) < 0)
 		return;
 
 	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;
 }
 
 
@@ -1911,7 +2260,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_i.h b/src/ap/wpa_auth_i.h
index 72b7eb3..90d7645 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -128,7 +128,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 */