FT: New RRB message format

Convert FT RRB into a new TLV based format. Use AES-SIV as AEAD cipher
to protect the messages.

This needs at least 32 byte long keys. These can be provided either
by a config file change or letting a KDF derive the 32 byte key used
from the 16 byte key given.

This breaks backward compatibility, i.e., hostapd needs to be updated on
all APs at the same time to allow FT to remain functional.

Signed-off-by: Michael Braun <michael-dev@fami-braun.de>
This commit is contained in:
Michael Braun 2017-04-02 14:52:50 +02:00 committed by Jouni Malinen
parent 50bd8e0a90
commit 245fc96e5f
8 changed files with 877 additions and 313 deletions

View file

@ -252,7 +252,10 @@ OBJS += src/ap/wpa_auth_ft.c
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

View file

@ -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

View file

@ -14,6 +14,7 @@
#include "utils/common.h"
#include "utils/uuid.h"
#include "common/ieee802_11_defs.h"
#include "crypto/sha256.h"
#include "drivers/driver.h"
#include "eap_server/eap.h"
#include "radius/radius_client.h"
@ -1000,6 +1001,26 @@ static int hostapd_config_tx_queue(struct hostapd_config *conf,
#ifdef CONFIG_IEEE80211R_AP
static int rkh_derive_key(const char *pos, u8 *key, size_t key_len)
{
u8 oldkey[16];
int ret;
if (!hexstr2bin(pos, key, key_len))
return 0;
/* Try to use old short key for backwards compatibility */
if (hexstr2bin(pos, oldkey, sizeof(oldkey)))
return -1;
ret = hmac_sha256_kdf(oldkey, sizeof(oldkey), "FT OLDKEY", NULL, 0,
key, key_len);
os_memset(oldkey, 0, sizeof(oldkey));
return ret;
}
static int add_r0kh(struct hostapd_bss_config *bss, char *value)
{
struct ft_remote_r0kh *r0kh;
@ -1033,7 +1054,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_derive_key(pos, r0kh->key, sizeof(r0kh->key)) < 0) {
wpa_printf(MSG_ERROR, "Invalid R0KH key: '%s'", pos);
os_free(r0kh);
return -1;
@ -1078,7 +1099,7 @@ static int add_r1kh(struct hostapd_bss_config *bss, char *value)
}
pos = next;
if (hexstr2bin(pos, r1kh->key, sizeof(r1kh->key))) {
if (rkh_derive_key(pos, r1kh->key, sizeof(r1kh->key)) < 0) {
wpa_printf(MSG_ERROR, "Invalid R1KH key: '%s'", pos);
os_free(r1kh);
return -1;

View file

@ -1452,23 +1452,29 @@ 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.
# Note: The R0KH/R1KH keys used to be 128-bit in length before the message
# format was changed. That shorter key length is still supported for backwards
# compatibility of the configuration files. If such a shorter key is used, a
# 256-bit key is derived from it. For new deployments, configuring the 256-bit
# key is recommended.
# Whether PMK-R1 push is enabled at R0KH
# 0 = do not push PMK-R1 to all configured R1KHs (default)
# 1 = push PMK-R1 to all configured R1KHs whenever a new PMK-R0 is derived

View file

@ -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;

View file

@ -44,53 +44,64 @@ 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)
/* packet layout
* IEEE 802 extended OUI ethertype frame header
* u16 authlen (little endian)
* multiple of struct ft_rrb_tlv (authenticated only, length = authlen)
* multiple of struct ft_rrb_tlv (AES-SIV encrypted, AES-SIV needs an extra
* blocksize length)
*
* AES-SIV AAD;
* source MAC address (6)
* authenticated-only TLVs (authlen)
* subtype (1; FT_PACKET_*)
*/
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];
#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_RRB_PAIRWISE 11 /* le16 */
struct ft_rrb_tlv {
le16 type;
le16 len;
/* followed by data of length len */
} STRUCT_PACKED;
#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_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_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)
@ -110,7 +121,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 +129,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];
};

File diff suppressed because it is too large Load diff

View file

@ -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 */