From 1bdb7ab3af9b78414592808e8467bcb3e3d82e04 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Wed, 24 Nov 2010 00:52:46 +0200 Subject: [PATCH] Re-initialize GMK and Key Counter on first station connection This adds more time for the system entropy pool to be filled before requesting random data for generating the WPA/WPA2 encryption keys. This can be helpful especially on embedded devices that do not have hardware random number generator and may lack good sources of randomness especially early in the bootup sequence when hostapd is likely to be started. GMK and Key Counter are still initialized once in the beginning to match the RSN Authenticator state machine behavior and to make sure that the driver does not transmit broadcast frames unencrypted. However, both GMK (and GTK derived from it) and Key Counter will be re-initialized when the first station connects and is about to enter 4-way handshake. --- src/ap/wpa_auth.c | 100 ++++++++++++++++++++++++++++++++++---------- src/ap/wpa_auth_i.h | 1 + 2 files changed, 80 insertions(+), 21 deletions(-) diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index dc9fef1da..f3f4b5f41 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -44,6 +44,8 @@ static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, static void wpa_request_new_ptk(struct wpa_state_machine *sm); static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, struct wpa_group *group); +static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); static const u32 dot11RSNAConfigGroupUpdateCount = 4; static const u32 dot11RSNAConfigPairwiseUpdateCount = 4; @@ -298,12 +300,40 @@ static void wpa_group_set_key_len(struct wpa_group *group, int cipher) } +static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + u8 buf[ETH_ALEN + 8 + sizeof(group)]; + u8 rkey[32]; + + if (os_get_random(group->GMK, WPA_GMK_LEN) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "GMK", group->GMK, WPA_GMK_LEN); + + /* + * Counter = PRF-256(Random number, "Init Counter", + * Local MAC Address || Time) + */ + os_memcpy(buf, wpa_auth->addr, ETH_ALEN); + wpa_get_ntp_timestamp(buf + ETH_ALEN); + os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group)); + if (os_get_random(rkey, sizeof(rkey)) < 0) + return -1; + + if (sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf), + group->Counter, WPA_NONCE_LEN) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "Key Counter", + group->Counter, WPA_NONCE_LEN); + + return 0; +} + + static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, int vlan_id) { struct wpa_group *group; - u8 buf[ETH_ALEN + 8 + sizeof(group)]; - u8 rkey[32]; group = os_zalloc(sizeof(struct wpa_group)); if (group == NULL) @@ -314,25 +344,18 @@ static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, wpa_group_set_key_len(group, wpa_auth->conf.wpa_group); - /* Counter = PRF-256(Random number, "Init Counter", - * Local MAC Address || Time) + /* + * Set initial GMK/Counter value here. The actual values that will be + * used in negotiations will be set once the first station tries to + * connect. This allows more time for collecting additional randomness + * on embedded devices. */ - os_memcpy(buf, wpa_auth->addr, ETH_ALEN); - wpa_get_ntp_timestamp(buf + ETH_ALEN); - os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group)); - if (os_get_random(rkey, sizeof(rkey)) || - os_get_random(group->GMK, WPA_GMK_LEN)) { + if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0) { wpa_printf(MSG_ERROR, "Failed to get random data for WPA " "initialization."); os_free(group); return NULL; } - wpa_hexdump_key(MSG_DEBUG, "GMK", group->GMK, WPA_GMK_LEN); - - sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf), - group->Counter, WPA_NONCE_LEN); - wpa_hexdump_key(MSG_DEBUG, "Key Counter", - group->Counter, WPA_NONCE_LEN); group->GInit = TRUE; wpa_group_sm_step(wpa_auth, group); @@ -1428,9 +1451,33 @@ SM_STATE(WPA_PTK, AUTHENTICATION) } +static void wpa_group_first_station(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + /* + * System has run bit further than at the time hostapd was started + * potentially very early during boot up. This provides better chances + * of collecting more randomness on embedded systems. Re-initialize the + * GMK and Counter here to improve their strength if there was not + * enough entropy available immediately after system startup. + */ + wpa_printf(MSG_DEBUG, "WPA: Re-initialize GMK/Counter on first " + "station"); + wpa_group_init_gmk_and_counter(wpa_auth, group); + wpa_gtk_update(wpa_auth, group); + wpa_group_config_group_keys(wpa_auth, group); +} + + SM_STATE(WPA_PTK, AUTHENTICATION2) { SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk); + + if (!sm->group->first_sta_seen) { + wpa_group_first_station(sm->wpa_auth, sm->group); + sm->group->first_sta_seen = TRUE; + } + os_memcpy(sm->ANonce, sm->group->Counter, WPA_NONCE_LEN); inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); sm->ReAuthenticationRequest = FALSE; @@ -2212,15 +2259,11 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, } -static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth, - struct wpa_group *group) +static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) { int ret = 0; - wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " - "SETKEYSDONE (VLAN-ID %d)", group->vlan_id); - group->changed = TRUE; - group->wpa_group_state = WPA_GROUP_SETKEYSDONE; if (wpa_auth_set_key(wpa_auth, group->vlan_id, wpa_alg_enum(wpa_auth->conf.wpa_group), NULL, group->GN, group->GTK[group->GN - 1], @@ -2240,6 +2283,21 @@ static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth, } +static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " + "SETKEYSDONE (VLAN-ID %d)", group->vlan_id); + group->changed = TRUE; + group->wpa_group_state = WPA_GROUP_SETKEYSDONE; + + if (wpa_group_config_group_keys(wpa_auth, group) < 0) + return -1; + + return 0; +} + + static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index b69129ff4..bc6962fd3 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -145,6 +145,7 @@ struct wpa_group { u8 GTK[2][WPA_GTK_MAX_LEN]; u8 GNonce[WPA_NONCE_LEN]; Boolean changed; + Boolean first_sta_seen; #ifdef CONFIG_IEEE80211W u8 IGTK[2][WPA_IGTK_LEN]; int GN_igtk, GM_igtk;