FILS: Add FILS SK auth PFS support in AP mode

This adds an option to configure hostapd to enable use of perfect
forward secrecy option in FILS shared key authentication. A new build
option CONFIG_FILS_SK_PFS=y can be used to include this functionality. A
new runtime configuration parameter fils_dh_group is used to enable this
by specifying which DH group to use. For example, fils_dh_group=19 would
allow FILS SK PFS to be used with a 256-bit random ECP group.

Signed-off-by: Jouni Malinen <j@w1.fi>
This commit is contained in:
Jouni Malinen 2017-03-12 22:40:56 +02:00
parent cad291d671
commit 1764559eef
10 changed files with 149 additions and 14 deletions

View file

@ -272,6 +272,10 @@ L_CFLAGS += -DCONFIG_FILS
OBJS += src/ap/fils_hlp.c OBJS += src/ap/fils_hlp.c
NEED_SHA384=y NEED_SHA384=y
NEED_AES_SIV=y NEED_AES_SIV=y
ifdef CONFIG_FILS_SK_PFS
L_CFLAGS += -DCONFIG_FILS_SK_PFS
NEED_ECC=y
endif
endif endif
ifdef CONFIG_WNM ifdef CONFIG_WNM

View file

@ -316,6 +316,10 @@ CFLAGS += -DCONFIG_FILS
OBJS += ../src/ap/fils_hlp.o OBJS += ../src/ap/fils_hlp.o
NEED_SHA384=y NEED_SHA384=y
NEED_AES_SIV=y NEED_AES_SIV=y
ifdef CONFIG_FILS_SK_PFS
CFLAGS += -DCONFIG_FILS_SK_PFS
NEED_ECC=y
endif
endif endif
ifdef CONFIG_WNM ifdef CONFIG_WNM

View file

@ -3658,6 +3658,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
} else if (os_strcmp(buf, "fils_realm") == 0) { } else if (os_strcmp(buf, "fils_realm") == 0) {
if (parse_fils_realm(bss, pos) < 0) if (parse_fils_realm(bss, pos) < 0)
return 1; return 1;
} else if (os_strcmp(buf, "fils_dh_group") == 0) {
bss->fils_dh_group = atoi(pos);
} else if (os_strcmp(buf, "dhcp_server") == 0) { } else if (os_strcmp(buf, "dhcp_server") == 0) {
if (hostapd_parse_ip_addr(pos, &bss->dhcp_server)) { if (hostapd_parse_ip_addr(pos, &bss->dhcp_server)) {
wpa_printf(MSG_ERROR, wpa_printf(MSG_ERROR,

View file

@ -358,6 +358,8 @@ CONFIG_IPV6=y
# Note: This is an experimental and not yet complete implementation. This # Note: This is an experimental and not yet complete implementation. This
# should not be enabled for production use. # should not be enabled for production use.
#CONFIG_FILS=y #CONFIG_FILS=y
# FILS shared key authentication with PFS
#CONFIG_FILS_SK_PFS=y
# Include internal line edit mode in hostapd_cli. This can be used to provide # Include internal line edit mode in hostapd_cli. This can be used to provide
# limited command line editing and history support. # limited command line editing and history support.

View file

@ -1393,6 +1393,11 @@ own_ip_addr=127.0.0.1
#fils_realm=example.com #fils_realm=example.com
#fils_realm=example.org #fils_realm=example.org
# FILS DH Group for PFS
# 0 = PFS disabled with FILS shared key authentication (default)
# 1-65535 DH Group to use for FILS PFS
#fils_dh_group=0
# DHCP server for FILS HLP # DHCP server for FILS HLP
# If configured, hostapd will act as a DHCP relay for all FILS HLP requests # If configured, hostapd will act as a DHCP relay for all FILS HLP requests
# that include a DHCPDISCOVER message and send them to the specific DHCP # that include a DHCPDISCOVER message and send them to the specific DHCP

View file

@ -610,6 +610,7 @@ struct hostapd_bss_config {
u8 fils_cache_id[FILS_CACHE_ID_LEN]; u8 fils_cache_id[FILS_CACHE_ID_LEN];
int fils_cache_id_set; int fils_cache_id_set;
struct dl_list fils_realms; /* list of struct fils_realm */ struct dl_list fils_realms; /* list of struct fils_realm */
int fils_dh_group;
struct hostapd_ip_addr dhcp_server; struct hostapd_ip_addr dhcp_server;
int dhcp_rapid_commit_proxy; int dhcp_rapid_commit_proxy;
unsigned int fils_hlp_wait_time; unsigned int fils_hlp_wait_time;

View file

@ -1014,8 +1014,9 @@ static void handle_auth_fils_finish(struct hostapd_data *hapd,
const u8 *msk, size_t msk_len); const u8 *msk, size_t msk_len);
static void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, static void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
const struct ieee80211_mgmt *mgmt, size_t len, const struct ieee80211_mgmt *mgmt, size_t len,
u16 auth_transaction, u16 status_code) u16 auth_alg, u16 auth_transaction,
u16 status_code)
{ {
u16 resp = WLAN_STATUS_SUCCESS; u16 resp = WLAN_STATUS_SUCCESS;
const u8 *pos, *end; const u8 *pos, *end;
@ -1033,8 +1034,76 @@ static void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields", wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields",
pos, end - pos); pos, end - pos);
/* TODO: Finite Cyclic Group when using PK or PFS */ /* TODO: FILS PK */
/* TODO: Element when using PK or PFS */ #ifdef CONFIG_FILS_SK_PFS
if (auth_alg == WLAN_AUTH_FILS_SK_PFS) {
u16 group;
struct wpabuf *pub;
size_t elem_len;
/* Using FILS PFS */
/* Finite Cyclic Group */
if (end - pos < 2) {
wpa_printf(MSG_DEBUG,
"FILS: No room for Finite Cyclic Group");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
group = WPA_GET_LE16(pos);
pos += 2;
if (group != hapd->conf->fils_dh_group) {
wpa_printf(MSG_DEBUG,
"FILS: Unsupported Finite Cyclic Group: %u (expected %u)",
group, hapd->conf->fils_dh_group);
resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
goto fail;
}
crypto_ecdh_deinit(sta->fils_ecdh);
sta->fils_ecdh = crypto_ecdh_init(group);
if (!sta->fils_ecdh) {
wpa_printf(MSG_INFO,
"FILS: Could not initialize ECDH with group %d",
group);
resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
goto fail;
}
pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1);
if (!pub) {
wpa_printf(MSG_DEBUG,
"FILS: Failed to derive ECDH public key");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
elem_len = wpabuf_len(pub);
wpabuf_free(pub);
/* Element */
if ((size_t) (end - pos) < elem_len) {
wpa_printf(MSG_DEBUG, "FILS: No room for Element");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
wpabuf_clear_free(sta->fils_dh_ss);
sta->fils_dh_ss = crypto_ecdh_set_peerkey(sta->fils_ecdh, 1,
pos, elem_len);
if (!sta->fils_dh_ss) {
wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", sta->fils_dh_ss);
pos += elem_len;
} else {
crypto_ecdh_deinit(sta->fils_ecdh);
sta->fils_ecdh = NULL;
wpabuf_clear_free(sta->fils_dh_ss);
sta->fils_dh_ss = NULL;
}
#endif /* CONFIG_FILS_SK_PFS */
wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos); wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos);
if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) { if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) {
@ -1173,6 +1242,8 @@ static void handle_auth_fils_finish(struct hostapd_data *hapd,
const u8 *pmk = NULL; const u8 *pmk = NULL;
size_t pmk_len = 0; size_t pmk_len = 0;
u8 pmk_buf[PMK_LEN_MAX]; u8 pmk_buf[PMK_LEN_MAX];
struct wpabuf *pub = NULL;
u16 auth_alg;
if (resp != WLAN_STATUS_SUCCESS) if (resp != WLAN_STATUS_SUCCESS)
goto fail; goto fail;
@ -1204,14 +1275,32 @@ static void handle_auth_fils_finish(struct hostapd_data *hapd,
wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS Nonce", wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS Nonce",
fils_nonce, FILS_NONCE_LEN); fils_nonce, FILS_NONCE_LEN);
data = wpabuf_alloc(1000 + ielen); #ifdef CONFIG_FILS_SK_PFS
if (sta->fils_dh_ss && sta->fils_ecdh) {
pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1);
if (!pub) {
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
}
#endif /* CONFIG_FILS_SK_PFS */
data = wpabuf_alloc(1000 + ielen + (pub ? wpabuf_len(pub) : 0));
if (!data) { if (!data) {
resp = WLAN_STATUS_UNSPECIFIED_FAILURE; resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail; goto fail;
} }
/* TODO: Finite Cyclic Group when using PK or PFS */ /* TODO: FILS PK */
/* TODO: Element when using PK or PFS */ #ifdef CONFIG_FILS_SK_PFS
if (pub) {
/* Finite Cyclic Group */
wpabuf_put_le16(data, hapd->conf->fils_dh_group);
/* Element */
wpabuf_put_buf(data, pub);
}
#endif /* CONFIG_FILS_SK_PFS */
/* RSNE */ /* RSNE */
wpabuf_put_data(data, ie, ielen); wpabuf_put_data(data, ie, ielen);
@ -1243,7 +1332,11 @@ static void handle_auth_fils_finish(struct hostapd_data *hapd,
if (fils_rmsk_to_pmk(wpa_auth_sta_key_mgmt(sta->wpa_sm), if (fils_rmsk_to_pmk(wpa_auth_sta_key_mgmt(sta->wpa_sm),
msk, msk_len, sta->fils_snonce, fils_nonce, msk, msk_len, sta->fils_snonce, fils_nonce,
NULL, 0, pmk_buf, &pmk_len)) { sta->fils_dh_ss ?
wpabuf_head(sta->fils_dh_ss) : NULL,
sta->fils_dh_ss ?
wpabuf_len(sta->fils_dh_ss) : 0,
pmk_buf, &pmk_len)) {
wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK"); wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE; resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
wpabuf_free(data); wpabuf_free(data);
@ -1273,8 +1366,10 @@ static void handle_auth_fils_finish(struct hostapd_data *hapd,
} }
fail: fail:
send_auth_reply(hapd, sta->addr, hapd->own_addr, WLAN_AUTH_FILS_SK, 2, auth_alg = (pub ||
resp, resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) ?
WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
send_auth_reply(hapd, sta->addr, hapd->own_addr, auth_alg, 2, resp,
data ? wpabuf_head(data) : (u8 *) "", data ? wpabuf_head(data) : (u8 *) "",
data ? wpabuf_len(data) : 0); data ? wpabuf_len(data) : 0);
wpabuf_free(data); wpabuf_free(data);
@ -1285,11 +1380,18 @@ fail:
"authentication OK (FILS)"); "authentication OK (FILS)");
sta->flags |= WLAN_STA_AUTH; sta->flags |= WLAN_STA_AUTH;
wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
sta->auth_alg = WLAN_AUTH_FILS_SK; sta->auth_alg = pub ? WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
mlme_authenticate_indication(hapd, sta); mlme_authenticate_indication(hapd, sta);
} }
os_free(ie_buf); os_free(ie_buf);
wpabuf_free(pub);
wpabuf_clear_free(sta->fils_dh_ss);
sta->fils_dh_ss = NULL;
#ifdef CONFIG_FILS_SK_PFS
crypto_ecdh_deinit(sta->fils_ecdh);
sta->fils_ecdh = NULL;
#endif /* CONFIG_FILS_SK_PFS */
} }
@ -1472,6 +1574,9 @@ static void handle_auth(struct hostapd_data *hapd,
#ifdef CONFIG_FILS #ifdef CONFIG_FILS
(hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) && (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
auth_alg == WLAN_AUTH_FILS_SK) || auth_alg == WLAN_AUTH_FILS_SK) ||
(hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
hapd->conf->fils_dh_group &&
auth_alg == WLAN_AUTH_FILS_SK_PFS) ||
#endif /* CONFIG_FILS */ #endif /* CONFIG_FILS */
((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) && ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
auth_alg == WLAN_AUTH_SHARED_KEY))) { auth_alg == WLAN_AUTH_SHARED_KEY))) {
@ -1738,8 +1843,9 @@ static void handle_auth(struct hostapd_data *hapd,
#endif /* CONFIG_SAE */ #endif /* CONFIG_SAE */
#ifdef CONFIG_FILS #ifdef CONFIG_FILS
case WLAN_AUTH_FILS_SK: case WLAN_AUTH_FILS_SK:
handle_auth_fils(hapd, sta, mgmt, len, auth_transaction, case WLAN_AUTH_FILS_SK_PFS:
status_code); handle_auth_fils(hapd, sta, mgmt, len, auth_alg,
auth_transaction, status_code);
return; return;
#endif /* CONFIG_FILS */ #endif /* CONFIG_FILS */
} }

View file

@ -630,7 +630,10 @@ u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid)
fils_info |= BIT(8); /* HESSID Included */ fils_info |= BIT(8); /* HESSID Included */
/* FILS Shared Key Authentication without PFS Supported */ /* FILS Shared Key Authentication without PFS Supported */
fils_info |= BIT(9); fils_info |= BIT(9);
/* TODO: B10: FILS Shared Key Authentication with PFS Supported */ if (hapd->conf->fils_dh_group) {
/* FILS Shared Key Authentication with PFS Supported */
fils_info |= BIT(10);
}
/* TODO: B11: FILS Public Key Authentication Supported */ /* TODO: B11: FILS Public Key Authentication Supported */
/* B12..B15: Reserved */ /* B12..B15: Reserved */
WPA_PUT_LE16(pos, fils_info); WPA_PUT_LE16(pos, fils_info);

View file

@ -345,6 +345,10 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
wpabuf_free(sta->fils_hlp_resp); wpabuf_free(sta->fils_hlp_resp);
wpabuf_free(sta->hlp_dhcp_discover); wpabuf_free(sta->hlp_dhcp_discover);
eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
#ifdef CONFIG_FILS_SK_PFS
crypto_ecdh_deinit(sta->fils_ecdh);
wpabuf_clear_free(sta->fils_dh_ss);
#endif /* CONFIG_FILS_SK_PFS */
#endif /* CONFIG_FILS */ #endif /* CONFIG_FILS */
#ifdef CONFIG_OWE #ifdef CONFIG_OWE

View file

@ -231,6 +231,10 @@ struct sta_info {
unsigned int fils_dhcp_rapid_commit_proxy:1; unsigned int fils_dhcp_rapid_commit_proxy:1;
struct wpabuf *fils_hlp_resp; struct wpabuf *fils_hlp_resp;
struct wpabuf *hlp_dhcp_discover; struct wpabuf *hlp_dhcp_discover;
#ifdef CONFIG_FILS_SK_PFS
struct crypto_ecdh *fils_ecdh;
#endif /* CONFIG_FILS_SK_PFS */
struct wpabuf *fils_dh_ss;
#endif /* CONFIG_FILS */ #endif /* CONFIG_FILS */
#ifdef CONFIG_OWE #ifdef CONFIG_OWE