Fix Suite B 192-bit AKM to use proper PMK length

In addition to the PTK length increasing, the length of the PMK was
increased (from 256 to 384 bits) for the 00-0f-ac:12 AKM. This part was
missing from the initial implementation and a fixed length (256-bit) PMK
was used for all AKMs.

Fix this by adding more complete support for variable length PMK and use
384 bits from MSK instead of 256 bits when using this AKM. This is not
backwards compatible with the earlier implementations.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
Jouni Malinen 2015-10-14 01:18:11 +03:00 committed by Jouni Malinen
parent ae7d9fbd3d
commit 207976f053
11 changed files with 73 additions and 27 deletions

View file

@ -2575,7 +2575,7 @@ static void ieee802_1x_finished(struct hostapd_data *hapd,
session_timeout = dot11RSNAConfigPMKLifetime; session_timeout = dot11RSNAConfigPMKLifetime;
if (success && key && len >= PMK_LEN && !sta->remediation && if (success && key && len >= PMK_LEN && !sta->remediation &&
!sta->hs20_deauth_requested && !sta->hs20_deauth_requested &&
wpa_auth_pmksa_add(sta->wpa_sm, key, session_timeout, wpa_auth_pmksa_add(sta->wpa_sm, key, len, session_timeout,
sta->eapol_sm) == 0) { sta->eapol_sm) == 0) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
HOSTAPD_LEVEL_DEBUG, HOSTAPD_LEVEL_DEBUG,

View file

@ -258,7 +258,7 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
struct rsn_pmksa_cache_entry *entry, *pos; struct rsn_pmksa_cache_entry *entry, *pos;
struct os_reltime now; struct os_reltime now;
if (pmk_len > PMK_LEN) if (pmk_len > PMK_LEN_MAX)
return NULL; return NULL;
if (wpa_key_mgmt_suite_b(akmp) && !kck) if (wpa_key_mgmt_suite_b(akmp) && !kck)

View file

@ -17,7 +17,7 @@
struct rsn_pmksa_cache_entry { struct rsn_pmksa_cache_entry {
struct rsn_pmksa_cache_entry *next, *hnext; struct rsn_pmksa_cache_entry *next, *hnext;
u8 pmkid[PMKID_LEN]; u8 pmkid[PMKID_LEN];
u8 pmk[PMK_LEN]; u8 pmk[PMK_LEN_MAX];
size_t pmk_len; size_t pmk_len;
os_time_t expiration; os_time_t expiration;
int akmp; /* WPA_KEY_MGMT_* */ int akmp; /* WPA_KEY_MGMT_* */

View file

@ -44,7 +44,8 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
struct wpa_group *group); struct wpa_group *group);
static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
const u8 *pmk, struct wpa_ptk *ptk); const u8 *pmk, unsigned int pmk_len,
struct wpa_ptk *ptk);
static void wpa_group_free(struct wpa_authenticator *wpa_auth, static void wpa_group_free(struct wpa_authenticator *wpa_auth,
struct wpa_group *group); struct wpa_group *group);
static void wpa_group_get(struct wpa_authenticator *wpa_auth, static void wpa_group_get(struct wpa_authenticator *wpa_auth,
@ -827,6 +828,7 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
struct wpa_ptk PTK; struct wpa_ptk PTK;
int ok = 0; int ok = 0;
const u8 *pmk = NULL; const u8 *pmk = NULL;
unsigned int pmk_len;
for (;;) { for (;;) {
if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
@ -834,10 +836,13 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
sm->p2p_dev_addr, pmk); sm->p2p_dev_addr, pmk);
if (pmk == NULL) if (pmk == NULL)
break; break;
} else pmk_len = PMK_LEN;
} else {
pmk = sm->PMK; pmk = sm->PMK;
pmk_len = sm->pmk_len;
}
wpa_derive_ptk(sm, sm->alt_SNonce, pmk, &PTK); wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK);
if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len) if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len)
== 0) { == 0) {
@ -1904,11 +1909,27 @@ SM_STATE(WPA_PTK, INITPMK)
#endif /* CONFIG_IEEE80211R */ #endif /* CONFIG_IEEE80211R */
if (sm->pmksa) { if (sm->pmksa) {
wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache"); wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache");
os_memcpy(sm->PMK, sm->pmksa->pmk, PMK_LEN); os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
sm->pmk_len = sm->pmksa->pmk_len;
} else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) { } else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) {
unsigned int pmk_len;
if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
pmk_len = PMK_LEN_SUITE_B_192;
else
pmk_len = PMK_LEN;
wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine " wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine "
"(len=%lu)", (unsigned long) len); "(MSK len=%lu PMK len=%u)", (unsigned long) len,
os_memcpy(sm->PMK, msk, PMK_LEN); pmk_len);
if (len < pmk_len) {
wpa_printf(MSG_DEBUG,
"WPA: MSK not long enough (%u) to create PMK (%u)",
(unsigned int) len, (unsigned int) pmk_len);
sm->Disconnect = TRUE;
return;
}
os_memcpy(sm->PMK, msk, pmk_len);
sm->pmk_len = pmk_len;
#ifdef CONFIG_IEEE80211R #ifdef CONFIG_IEEE80211R
if (len >= 2 * PMK_LEN) { if (len >= 2 * PMK_LEN) {
os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN); os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN);
@ -1943,6 +1964,7 @@ SM_STATE(WPA_PTK, INITPSK)
psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL); psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL);
if (psk) { if (psk) {
os_memcpy(sm->PMK, psk, PMK_LEN); os_memcpy(sm->PMK, psk, PMK_LEN);
sm->pmk_len = PMK_LEN;
#ifdef CONFIG_IEEE80211R #ifdef CONFIG_IEEE80211R
os_memcpy(sm->xxkey, psk, PMK_LEN); os_memcpy(sm->xxkey, psk, PMK_LEN);
sm->xxkey_len = PMK_LEN; sm->xxkey_len = PMK_LEN;
@ -1994,7 +2016,7 @@ SM_STATE(WPA_PTK, PTKSTART)
* Calculate PMKID since no PMKSA cache entry was * Calculate PMKID since no PMKSA cache entry was
* available with pre-calculated PMKID. * available with pre-calculated PMKID.
*/ */
rsn_pmkid(sm->PMK, PMK_LEN, sm->wpa_auth->addr, rsn_pmkid(sm->PMK, sm->pmk_len, sm->wpa_auth->addr,
sm->addr, &pmkid[2 + RSN_SELECTOR_LEN], sm->addr, &pmkid[2 + RSN_SELECTOR_LEN],
wpa_key_mgmt_sha256(sm->wpa_key_mgmt)); wpa_key_mgmt_sha256(sm->wpa_key_mgmt));
} }
@ -2006,14 +2028,15 @@ SM_STATE(WPA_PTK, PTKSTART)
static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
const u8 *pmk, struct wpa_ptk *ptk) const u8 *pmk, unsigned int pmk_len,
struct wpa_ptk *ptk)
{ {
#ifdef CONFIG_IEEE80211R #ifdef CONFIG_IEEE80211R
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
return wpa_auth_derive_ptk_ft(sm, pmk, ptk); return wpa_auth_derive_ptk_ft(sm, pmk, ptk);
#endif /* CONFIG_IEEE80211R */ #endif /* CONFIG_IEEE80211R */
return wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion", return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion",
sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce, sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce,
ptk, sm->wpa_key_mgmt, sm->pairwise); ptk, sm->wpa_key_mgmt, sm->pairwise);
} }
@ -2024,6 +2047,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
struct wpa_ptk PTK; struct wpa_ptk PTK;
int ok = 0, psk_found = 0; int ok = 0, psk_found = 0;
const u8 *pmk = NULL; const u8 *pmk = NULL;
unsigned int pmk_len;
SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
sm->EAPOLKeyReceived = FALSE; sm->EAPOLKeyReceived = FALSE;
@ -2039,10 +2063,13 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
if (pmk == NULL) if (pmk == NULL)
break; break;
psk_found = 1; psk_found = 1;
} else pmk_len = PMK_LEN;
} else {
pmk = sm->PMK; pmk = sm->PMK;
pmk_len = sm->pmk_len;
}
wpa_derive_ptk(sm, sm->SNonce, pmk, &PTK); wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK);
if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK,
sm->last_rx_eapol_key, sm->last_rx_eapol_key,
@ -2092,6 +2119,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
* state machine data based on whatever PSK was selected here. * state machine data based on whatever PSK was selected here.
*/ */
os_memcpy(sm->PMK, pmk, PMK_LEN); os_memcpy(sm->PMK, pmk, PMK_LEN);
sm->pmk_len = PMK_LEN;
} }
sm->MICVerified = TRUE; sm->MICVerified = TRUE;
@ -3243,13 +3271,21 @@ const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len)
int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
unsigned int pmk_len,
int session_timeout, struct eapol_state_machine *eapol) int session_timeout, struct eapol_state_machine *eapol)
{ {
if (sm == NULL || sm->wpa != WPA_VERSION_WPA2 || if (sm == NULL || sm->wpa != WPA_VERSION_WPA2 ||
sm->wpa_auth->conf.disable_pmksa_caching) sm->wpa_auth->conf.disable_pmksa_caching)
return -1; return -1;
if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN, if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
if (pmk_len > PMK_LEN_SUITE_B_192)
pmk_len = PMK_LEN_SUITE_B_192;
} else if (pmk_len > PMK_LEN) {
pmk_len = PMK_LEN;
}
if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, pmk_len,
sm->PTK.kck, sm->PTK.kck_len, sm->PTK.kck, sm->PTK.kck_len,
sm->wpa_auth->addr, sm->addr, session_timeout, sm->wpa_auth->addr, sm->addr, session_timeout,
eapol, sm->wpa_key_mgmt)) eapol, sm->wpa_key_mgmt))

View file

@ -279,6 +279,7 @@ void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm);
const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth,
size_t *len); size_t *len);
int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
unsigned int pmk_len,
int session_timeout, struct eapol_state_machine *eapol); int session_timeout, struct eapol_state_machine *eapol);
int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
const u8 *pmk, size_t len, const u8 *sta_addr, const u8 *pmk, size_t len, const u8 *sta_addr,

View file

@ -60,7 +60,8 @@ struct wpa_state_machine {
u8 SNonce[WPA_NONCE_LEN]; u8 SNonce[WPA_NONCE_LEN];
u8 alt_SNonce[WPA_NONCE_LEN]; u8 alt_SNonce[WPA_NONCE_LEN];
u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN]; u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN];
u8 PMK[PMK_LEN]; u8 PMK[PMK_LEN_MAX];
unsigned int pmk_len;
struct wpa_ptk PTK; struct wpa_ptk PTK;
Boolean PTK_valid; Boolean PTK_valid;
Boolean pairwise_set; Boolean pairwise_set;

View file

@ -12,6 +12,8 @@
/* IEEE 802.11i */ /* IEEE 802.11i */
#define PMKID_LEN 16 #define PMKID_LEN 16
#define PMK_LEN 32 #define PMK_LEN 32
#define PMK_LEN_SUITE_B_192 48
#define PMK_LEN_MAX 48
#define WPA_REPLAY_COUNTER_LEN 8 #define WPA_REPLAY_COUNTER_LEN 8
#define WPA_NONCE_LEN 32 #define WPA_NONCE_LEN 32
#define WPA_KEY_RSC_LEN 8 #define WPA_KEY_RSC_LEN 8

View file

@ -130,7 +130,7 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
struct rsn_pmksa_cache_entry *entry, *pos, *prev; struct rsn_pmksa_cache_entry *entry, *pos, *prev;
struct os_reltime now; struct os_reltime now;
if (pmk_len > PMK_LEN) if (pmk_len > PMK_LEN_MAX)
return NULL; return NULL;
if (wpa_key_mgmt_suite_b(akmp) && !kck) if (wpa_key_mgmt_suite_b(akmp) && !kck)

View file

@ -15,7 +15,7 @@
struct rsn_pmksa_cache_entry { struct rsn_pmksa_cache_entry {
struct rsn_pmksa_cache_entry *next; struct rsn_pmksa_cache_entry *next;
u8 pmkid[PMKID_LEN]; u8 pmkid[PMKID_LEN];
u8 pmk[PMK_LEN]; u8 pmk[PMK_LEN_MAX];
size_t pmk_len; size_t pmk_len;
os_time_t expiration; os_time_t expiration;
int akmp; /* WPA_KEY_MGMT_* */ int akmp; /* WPA_KEY_MGMT_* */

View file

@ -206,15 +206,21 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
#endif /* CONFIG_IEEE80211R */ #endif /* CONFIG_IEEE80211R */
} else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) { } else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) {
int res, pmk_len; int res, pmk_len;
pmk_len = PMK_LEN;
res = eapol_sm_get_key(sm->eapol, sm->pmk, PMK_LEN); if (sm->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
pmk_len = PMK_LEN_SUITE_B_192;
else
pmk_len = PMK_LEN;
res = eapol_sm_get_key(sm->eapol, sm->pmk, pmk_len);
if (res) { if (res) {
/* if (pmk_len == PMK_LEN) {
* EAP-LEAP is an exception from other EAP methods: it /*
* uses only 16-byte PMK. * EAP-LEAP is an exception from other EAP
*/ * methods: it uses only 16-byte PMK.
res = eapol_sm_get_key(sm->eapol, sm->pmk, 16); */
pmk_len = 16; res = eapol_sm_get_key(sm->eapol, sm->pmk, 16);
pmk_len = 16;
}
} else { } else {
#ifdef CONFIG_IEEE80211R #ifdef CONFIG_IEEE80211R
u8 buf[2 * PMK_LEN]; u8 buf[2 * PMK_LEN];

View file

@ -19,7 +19,7 @@ struct wpa_eapol_key;
* struct wpa_sm - Internal WPA state machine data * struct wpa_sm - Internal WPA state machine data
*/ */
struct wpa_sm { struct wpa_sm {
u8 pmk[PMK_LEN]; u8 pmk[PMK_LEN_MAX];
size_t pmk_len; size_t pmk_len;
struct wpa_ptk ptk, tptk; struct wpa_ptk ptk, tptk;
int ptk_set, tptk_set; int ptk_set, tptk_set;