1189251Ssam/* 2189251Ssam * WPA Supplicant - PeerKey for Direct Link Setup (DLS) 3189251Ssam * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi> 4189251Ssam * 5189251Ssam * This program is free software; you can redistribute it and/or modify 6189251Ssam * it under the terms of the GNU General Public License version 2 as 7189251Ssam * published by the Free Software Foundation. 8189251Ssam * 9189251Ssam * Alternatively, this software may be distributed under the terms of BSD 10189251Ssam * license. 11189251Ssam * 12189251Ssam * See README and COPYING for more details. 13189251Ssam */ 14189251Ssam 15189251Ssam#include "includes.h" 16189251Ssam 17189251Ssam#ifdef CONFIG_PEERKEY 18189251Ssam 19189251Ssam#include "common.h" 20189251Ssam#include "eloop.h" 21214734Srpaulo#include "crypto/sha1.h" 22214734Srpaulo#include "crypto/sha256.h" 23214734Srpaulo#include "common/ieee802_11_defs.h" 24189251Ssam#include "wpa.h" 25189251Ssam#include "wpa_i.h" 26189251Ssam#include "wpa_ie.h" 27189251Ssam#include "peerkey.h" 28189251Ssam 29189251Ssam 30189251Ssamstatic u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len) 31189251Ssam{ 32189251Ssam os_memcpy(pos, ie, ie_len); 33189251Ssam return pos + ie_len; 34189251Ssam} 35189251Ssam 36189251Ssam 37189251Ssamstatic u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len) 38189251Ssam{ 39189251Ssam *pos++ = WLAN_EID_VENDOR_SPECIFIC; 40189251Ssam *pos++ = RSN_SELECTOR_LEN + data_len; 41189251Ssam RSN_SELECTOR_PUT(pos, kde); 42189251Ssam pos += RSN_SELECTOR_LEN; 43189251Ssam os_memcpy(pos, data, data_len); 44189251Ssam pos += data_len; 45189251Ssam return pos; 46189251Ssam} 47189251Ssam 48189251Ssam 49189251Ssamstatic void wpa_supplicant_smk_timeout(void *eloop_ctx, void *timeout_ctx) 50189251Ssam{ 51189251Ssam#if 0 52189251Ssam struct wpa_sm *sm = eloop_ctx; 53189251Ssam struct wpa_peerkey *peerkey = timeout_ctx; 54189251Ssam#endif 55189251Ssam /* TODO: time out SMK and any STK that was generated using this SMK */ 56189251Ssam} 57189251Ssam 58189251Ssam 59189251Ssamstatic void wpa_supplicant_peerkey_free(struct wpa_sm *sm, 60189251Ssam struct wpa_peerkey *peerkey) 61189251Ssam{ 62189251Ssam eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey); 63189251Ssam os_free(peerkey); 64189251Ssam} 65189251Ssam 66189251Ssam 67189251Ssamstatic int wpa_supplicant_send_smk_error(struct wpa_sm *sm, const u8 *dst, 68189251Ssam const u8 *peer, 69189251Ssam u16 mui, u16 error_type, int ver) 70189251Ssam{ 71189251Ssam size_t rlen; 72189251Ssam struct wpa_eapol_key *err; 73189251Ssam struct rsn_error_kde error; 74189251Ssam u8 *rbuf, *pos; 75189251Ssam size_t kde_len; 76189251Ssam u16 key_info; 77189251Ssam 78189251Ssam kde_len = 2 + RSN_SELECTOR_LEN + sizeof(error); 79189251Ssam if (peer) 80189251Ssam kde_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN; 81189251Ssam 82189251Ssam rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, 83189251Ssam NULL, sizeof(*err) + kde_len, &rlen, 84189251Ssam (void *) &err); 85189251Ssam if (rbuf == NULL) 86189251Ssam return -1; 87189251Ssam 88189251Ssam err->type = EAPOL_KEY_TYPE_RSN; 89189251Ssam key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC | 90189251Ssam WPA_KEY_INFO_SECURE | WPA_KEY_INFO_ERROR | 91189251Ssam WPA_KEY_INFO_REQUEST; 92189251Ssam WPA_PUT_BE16(err->key_info, key_info); 93189251Ssam WPA_PUT_BE16(err->key_length, 0); 94189251Ssam os_memcpy(err->replay_counter, sm->request_counter, 95189251Ssam WPA_REPLAY_COUNTER_LEN); 96189251Ssam inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); 97189251Ssam 98189251Ssam WPA_PUT_BE16(err->key_data_length, (u16) kde_len); 99189251Ssam pos = (u8 *) (err + 1); 100189251Ssam 101189251Ssam if (peer) { 102189251Ssam /* Peer MAC Address KDE */ 103189251Ssam pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN); 104189251Ssam } 105189251Ssam 106189251Ssam /* Error KDE */ 107189251Ssam error.mui = host_to_be16(mui); 108189251Ssam error.error_type = host_to_be16(error_type); 109189251Ssam wpa_add_kde(pos, RSN_KEY_DATA_ERROR, (u8 *) &error, sizeof(error)); 110189251Ssam 111189251Ssam if (peer) { 112189251Ssam wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error (peer " 113189251Ssam MACSTR " mui %d error_type %d)", 114189251Ssam MAC2STR(peer), mui, error_type); 115189251Ssam } else { 116189251Ssam wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error " 117189251Ssam "(mui %d error_type %d)", mui, error_type); 118189251Ssam } 119189251Ssam 120189251Ssam wpa_eapol_key_send(sm, sm->ptk.kck, ver, dst, ETH_P_EAPOL, 121189251Ssam rbuf, rlen, err->key_mic); 122189251Ssam 123189251Ssam return 0; 124189251Ssam} 125189251Ssam 126189251Ssam 127189251Ssamstatic int wpa_supplicant_send_smk_m3(struct wpa_sm *sm, 128189251Ssam const unsigned char *src_addr, 129189251Ssam const struct wpa_eapol_key *key, 130189251Ssam int ver, struct wpa_peerkey *peerkey) 131189251Ssam{ 132189251Ssam size_t rlen; 133189251Ssam struct wpa_eapol_key *reply; 134189251Ssam u8 *rbuf, *pos; 135189251Ssam size_t kde_len; 136189251Ssam u16 key_info; 137189251Ssam 138189251Ssam /* KDEs: Peer RSN IE, Initiator MAC Address, Initiator Nonce */ 139189251Ssam kde_len = peerkey->rsnie_p_len + 140189251Ssam 2 + RSN_SELECTOR_LEN + ETH_ALEN + 141189251Ssam 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN; 142189251Ssam 143189251Ssam rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, 144189251Ssam NULL, sizeof(*reply) + kde_len, &rlen, 145189251Ssam (void *) &reply); 146189251Ssam if (rbuf == NULL) 147189251Ssam return -1; 148189251Ssam 149189251Ssam reply->type = EAPOL_KEY_TYPE_RSN; 150189251Ssam key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC | 151189251Ssam WPA_KEY_INFO_SECURE; 152189251Ssam WPA_PUT_BE16(reply->key_info, key_info); 153189251Ssam WPA_PUT_BE16(reply->key_length, 0); 154189251Ssam os_memcpy(reply->replay_counter, key->replay_counter, 155189251Ssam WPA_REPLAY_COUNTER_LEN); 156189251Ssam 157189251Ssam os_memcpy(reply->key_nonce, peerkey->pnonce, WPA_NONCE_LEN); 158189251Ssam 159189251Ssam WPA_PUT_BE16(reply->key_data_length, (u16) kde_len); 160189251Ssam pos = (u8 *) (reply + 1); 161189251Ssam 162189251Ssam /* Peer RSN IE */ 163189251Ssam pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len); 164189251Ssam 165189251Ssam /* Initiator MAC Address KDE */ 166189251Ssam pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peerkey->addr, ETH_ALEN); 167189251Ssam 168189251Ssam /* Initiator Nonce */ 169189251Ssam wpa_add_kde(pos, RSN_KEY_DATA_NONCE, peerkey->inonce, WPA_NONCE_LEN); 170189251Ssam 171189251Ssam wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK M3"); 172189251Ssam wpa_eapol_key_send(sm, sm->ptk.kck, ver, src_addr, ETH_P_EAPOL, 173189251Ssam rbuf, rlen, reply->key_mic); 174189251Ssam 175189251Ssam return 0; 176189251Ssam} 177189251Ssam 178189251Ssam 179189251Ssamstatic int wpa_supplicant_process_smk_m2( 180189251Ssam struct wpa_sm *sm, const unsigned char *src_addr, 181189251Ssam const struct wpa_eapol_key *key, size_t extra_len, int ver) 182189251Ssam{ 183189251Ssam struct wpa_peerkey *peerkey; 184189251Ssam struct wpa_eapol_ie_parse kde; 185189251Ssam struct wpa_ie_data ie; 186189251Ssam int cipher; 187189251Ssam struct rsn_ie_hdr *hdr; 188189251Ssam u8 *pos; 189189251Ssam 190189251Ssam wpa_printf(MSG_DEBUG, "RSN: Received SMK M2"); 191189251Ssam 192189251Ssam if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) { 193189251Ssam wpa_printf(MSG_INFO, "RSN: SMK handshake not allowed for " 194189251Ssam "the current network"); 195189251Ssam return -1; 196189251Ssam } 197189251Ssam 198189251Ssam if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) < 199189251Ssam 0) { 200189251Ssam wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M2"); 201189251Ssam return -1; 202189251Ssam } 203189251Ssam 204189251Ssam if (kde.rsn_ie == NULL || kde.mac_addr == NULL || 205189251Ssam kde.mac_addr_len < ETH_ALEN) { 206189251Ssam wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in " 207189251Ssam "SMK M2"); 208189251Ssam return -1; 209189251Ssam } 210189251Ssam 211189251Ssam wpa_printf(MSG_DEBUG, "RSN: SMK M2 - SMK initiator " MACSTR, 212189251Ssam MAC2STR(kde.mac_addr)); 213189251Ssam 214189251Ssam if (kde.rsn_ie_len > PEERKEY_MAX_IE_LEN) { 215189251Ssam wpa_printf(MSG_INFO, "RSN: Too long Initiator RSN IE in SMK " 216189251Ssam "M2"); 217189251Ssam return -1; 218189251Ssam } 219189251Ssam 220189251Ssam if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) { 221189251Ssam wpa_printf(MSG_INFO, "RSN: Failed to parse RSN IE in SMK M2"); 222189251Ssam return -1; 223189251Ssam } 224189251Ssam 225189251Ssam cipher = ie.pairwise_cipher & sm->allowed_pairwise_cipher; 226189251Ssam if (cipher & WPA_CIPHER_CCMP) { 227189251Ssam wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey"); 228189251Ssam cipher = WPA_CIPHER_CCMP; 229189251Ssam } else if (cipher & WPA_CIPHER_TKIP) { 230189251Ssam wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey"); 231189251Ssam cipher = WPA_CIPHER_TKIP; 232189251Ssam } else { 233189251Ssam wpa_printf(MSG_INFO, "RSN: No acceptable cipher in SMK M2"); 234189251Ssam wpa_supplicant_send_smk_error(sm, src_addr, kde.mac_addr, 235189251Ssam STK_MUI_SMK, STK_ERR_CPHR_NS, 236189251Ssam ver); 237189251Ssam return -1; 238189251Ssam } 239189251Ssam 240189251Ssam /* TODO: find existing entry and if found, use that instead of adding 241189251Ssam * a new one; how to handle the case where both ends initiate at the 242189251Ssam * same time? */ 243189251Ssam peerkey = os_zalloc(sizeof(*peerkey)); 244189251Ssam if (peerkey == NULL) 245189251Ssam return -1; 246189251Ssam os_memcpy(peerkey->addr, kde.mac_addr, ETH_ALEN); 247189251Ssam os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN); 248189251Ssam os_memcpy(peerkey->rsnie_i, kde.rsn_ie, kde.rsn_ie_len); 249189251Ssam peerkey->rsnie_i_len = kde.rsn_ie_len; 250189251Ssam peerkey->cipher = cipher; 251189251Ssam#ifdef CONFIG_IEEE80211W 252189251Ssam if (ie.key_mgmt & (WPA_KEY_MGMT_IEEE8021X_SHA256 | 253189251Ssam WPA_KEY_MGMT_PSK_SHA256)) 254189251Ssam peerkey->use_sha256 = 1; 255189251Ssam#endif /* CONFIG_IEEE80211W */ 256189251Ssam 257189251Ssam if (os_get_random(peerkey->pnonce, WPA_NONCE_LEN)) { 258214734Srpaulo wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, 259189251Ssam "WPA: Failed to get random data for PNonce"); 260189251Ssam wpa_supplicant_peerkey_free(sm, peerkey); 261189251Ssam return -1; 262189251Ssam } 263189251Ssam 264189251Ssam hdr = (struct rsn_ie_hdr *) peerkey->rsnie_p; 265189251Ssam hdr->elem_id = WLAN_EID_RSN; 266189251Ssam WPA_PUT_LE16(hdr->version, RSN_VERSION); 267189251Ssam pos = (u8 *) (hdr + 1); 268189251Ssam /* Group Suite can be anything for SMK RSN IE; receiver will just 269189251Ssam * ignore it. */ 270189251Ssam RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); 271189251Ssam pos += RSN_SELECTOR_LEN; 272189251Ssam /* Include only the selected cipher in pairwise cipher suite */ 273189251Ssam WPA_PUT_LE16(pos, 1); 274189251Ssam pos += 2; 275189251Ssam if (cipher == WPA_CIPHER_CCMP) 276189251Ssam RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); 277189251Ssam else if (cipher == WPA_CIPHER_TKIP) 278189251Ssam RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); 279189251Ssam pos += RSN_SELECTOR_LEN; 280189251Ssam 281189251Ssam hdr->len = (pos - peerkey->rsnie_p) - 2; 282189251Ssam peerkey->rsnie_p_len = pos - peerkey->rsnie_p; 283189251Ssam wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake", 284189251Ssam peerkey->rsnie_p, peerkey->rsnie_p_len); 285189251Ssam 286189251Ssam wpa_supplicant_send_smk_m3(sm, src_addr, key, ver, peerkey); 287189251Ssam 288189251Ssam peerkey->next = sm->peerkey; 289189251Ssam sm->peerkey = peerkey; 290189251Ssam 291189251Ssam return 0; 292189251Ssam} 293189251Ssam 294189251Ssam 295189251Ssam/** 296189251Ssam * rsn_smkid - Derive SMK identifier 297189251Ssam * @smk: Station master key (32 bytes) 298189251Ssam * @pnonce: Peer Nonce 299189251Ssam * @mac_p: Peer MAC address 300189251Ssam * @inonce: Initiator Nonce 301189251Ssam * @mac_i: Initiator MAC address 302189251Ssam * @use_sha256: Whether to use SHA256-based KDF 303189251Ssam * 304189251Ssam * 8.5.1.4 Station to station (STK) key hierarchy 305189251Ssam * SMKID = HMAC-SHA1-128(SMK, "SMK Name" || PNonce || MAC_P || INonce || MAC_I) 306189251Ssam */ 307189251Ssamstatic void rsn_smkid(const u8 *smk, const u8 *pnonce, const u8 *mac_p, 308189251Ssam const u8 *inonce, const u8 *mac_i, u8 *smkid, 309189251Ssam int use_sha256) 310189251Ssam{ 311189251Ssam char *title = "SMK Name"; 312189251Ssam const u8 *addr[5]; 313189251Ssam const size_t len[5] = { 8, WPA_NONCE_LEN, ETH_ALEN, WPA_NONCE_LEN, 314189251Ssam ETH_ALEN }; 315189251Ssam unsigned char hash[SHA256_MAC_LEN]; 316189251Ssam 317189251Ssam addr[0] = (u8 *) title; 318189251Ssam addr[1] = pnonce; 319189251Ssam addr[2] = mac_p; 320189251Ssam addr[3] = inonce; 321189251Ssam addr[4] = mac_i; 322189251Ssam 323189251Ssam#ifdef CONFIG_IEEE80211W 324189251Ssam if (use_sha256) 325189251Ssam hmac_sha256_vector(smk, PMK_LEN, 5, addr, len, hash); 326189251Ssam else 327189251Ssam#endif /* CONFIG_IEEE80211W */ 328189251Ssam hmac_sha1_vector(smk, PMK_LEN, 5, addr, len, hash); 329189251Ssam os_memcpy(smkid, hash, PMKID_LEN); 330189251Ssam} 331189251Ssam 332189251Ssam 333189251Ssamstatic void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm, 334189251Ssam struct wpa_peerkey *peerkey) 335189251Ssam{ 336189251Ssam size_t mlen; 337189251Ssam struct wpa_eapol_key *msg; 338189251Ssam u8 *mbuf; 339189251Ssam size_t kde_len; 340189251Ssam u16 key_info, ver; 341189251Ssam 342189251Ssam kde_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; 343189251Ssam 344189251Ssam mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, 345189251Ssam sizeof(*msg) + kde_len, &mlen, 346189251Ssam (void *) &msg); 347189251Ssam if (mbuf == NULL) 348189251Ssam return; 349189251Ssam 350189251Ssam msg->type = EAPOL_KEY_TYPE_RSN; 351189251Ssam 352189251Ssam if (peerkey->cipher == WPA_CIPHER_CCMP) 353189251Ssam ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; 354189251Ssam else 355189251Ssam ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; 356189251Ssam 357189251Ssam key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK; 358189251Ssam WPA_PUT_BE16(msg->key_info, key_info); 359189251Ssam 360189251Ssam if (peerkey->cipher == WPA_CIPHER_CCMP) 361189251Ssam WPA_PUT_BE16(msg->key_length, 16); 362189251Ssam else 363189251Ssam WPA_PUT_BE16(msg->key_length, 32); 364189251Ssam 365189251Ssam os_memcpy(msg->replay_counter, peerkey->replay_counter, 366189251Ssam WPA_REPLAY_COUNTER_LEN); 367189251Ssam inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN); 368189251Ssam 369189251Ssam WPA_PUT_BE16(msg->key_data_length, kde_len); 370189251Ssam wpa_add_kde((u8 *) (msg + 1), RSN_KEY_DATA_PMKID, 371189251Ssam peerkey->smkid, PMKID_LEN); 372189251Ssam 373189251Ssam if (os_get_random(peerkey->inonce, WPA_NONCE_LEN)) { 374214734Srpaulo wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, 375189251Ssam "RSN: Failed to get random data for INonce (STK)"); 376189251Ssam os_free(mbuf); 377189251Ssam return; 378189251Ssam } 379189251Ssam wpa_hexdump(MSG_DEBUG, "RSN: INonce for STK 4-Way Handshake", 380189251Ssam peerkey->inonce, WPA_NONCE_LEN); 381189251Ssam os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN); 382189251Ssam 383189251Ssam wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 1/4 to " MACSTR, 384189251Ssam MAC2STR(peerkey->addr)); 385189251Ssam wpa_eapol_key_send(sm, NULL, ver, peerkey->addr, ETH_P_EAPOL, 386189251Ssam mbuf, mlen, NULL); 387189251Ssam} 388189251Ssam 389189251Ssam 390189251Ssamstatic void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm, 391189251Ssam struct wpa_peerkey *peerkey) 392189251Ssam{ 393189251Ssam size_t mlen; 394189251Ssam struct wpa_eapol_key *msg; 395189251Ssam u8 *mbuf, *pos; 396189251Ssam size_t kde_len; 397189251Ssam u16 key_info, ver; 398189251Ssam be32 lifetime; 399189251Ssam 400189251Ssam kde_len = peerkey->rsnie_i_len + 401189251Ssam 2 + RSN_SELECTOR_LEN + sizeof(lifetime); 402189251Ssam 403189251Ssam mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, 404189251Ssam sizeof(*msg) + kde_len, &mlen, 405189251Ssam (void *) &msg); 406189251Ssam if (mbuf == NULL) 407189251Ssam return; 408189251Ssam 409189251Ssam msg->type = EAPOL_KEY_TYPE_RSN; 410189251Ssam 411189251Ssam if (peerkey->cipher == WPA_CIPHER_CCMP) 412189251Ssam ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; 413189251Ssam else 414189251Ssam ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; 415189251Ssam 416189251Ssam key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK | 417189251Ssam WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE; 418189251Ssam WPA_PUT_BE16(msg->key_info, key_info); 419189251Ssam 420189251Ssam if (peerkey->cipher == WPA_CIPHER_CCMP) 421189251Ssam WPA_PUT_BE16(msg->key_length, 16); 422189251Ssam else 423189251Ssam WPA_PUT_BE16(msg->key_length, 32); 424189251Ssam 425189251Ssam os_memcpy(msg->replay_counter, peerkey->replay_counter, 426189251Ssam WPA_REPLAY_COUNTER_LEN); 427189251Ssam inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN); 428189251Ssam 429189251Ssam WPA_PUT_BE16(msg->key_data_length, kde_len); 430189251Ssam pos = (u8 *) (msg + 1); 431189251Ssam pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len); 432189251Ssam lifetime = host_to_be32(peerkey->lifetime); 433189251Ssam wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, 434189251Ssam (u8 *) &lifetime, sizeof(lifetime)); 435189251Ssam 436189251Ssam os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN); 437189251Ssam 438189251Ssam wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 3/4 to " MACSTR, 439189251Ssam MAC2STR(peerkey->addr)); 440189251Ssam wpa_eapol_key_send(sm, peerkey->stk.kck, ver, peerkey->addr, 441189251Ssam ETH_P_EAPOL, mbuf, mlen, msg->key_mic); 442189251Ssam} 443189251Ssam 444189251Ssam 445189251Ssamstatic int wpa_supplicant_process_smk_m4(struct wpa_peerkey *peerkey, 446189251Ssam struct wpa_eapol_ie_parse *kde) 447189251Ssam{ 448189251Ssam wpa_printf(MSG_DEBUG, "RSN: Received SMK M4 (Initiator " MACSTR ")", 449189251Ssam MAC2STR(kde->mac_addr)); 450189251Ssam 451189251Ssam if (os_memcmp(kde->smk + PMK_LEN, peerkey->pnonce, WPA_NONCE_LEN) != 0) 452189251Ssam { 453189251Ssam wpa_printf(MSG_INFO, "RSN: PNonce in SMK KDE does not " 454189251Ssam "match with the one used in SMK M3"); 455189251Ssam return -1; 456189251Ssam } 457189251Ssam 458189251Ssam if (os_memcmp(kde->nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) { 459189251Ssam wpa_printf(MSG_INFO, "RSN: INonce in SMK M4 did not " 460189251Ssam "match with the one received in SMK M2"); 461189251Ssam return -1; 462189251Ssam } 463189251Ssam 464189251Ssam return 0; 465189251Ssam} 466189251Ssam 467189251Ssam 468189251Ssamstatic int wpa_supplicant_process_smk_m5(struct wpa_sm *sm, 469189251Ssam const unsigned char *src_addr, 470189251Ssam const struct wpa_eapol_key *key, 471189251Ssam int ver, 472189251Ssam struct wpa_peerkey *peerkey, 473189251Ssam struct wpa_eapol_ie_parse *kde) 474189251Ssam{ 475189251Ssam int cipher; 476189251Ssam struct wpa_ie_data ie; 477189251Ssam 478189251Ssam wpa_printf(MSG_DEBUG, "RSN: Received SMK M5 (Peer " MACSTR ")", 479189251Ssam MAC2STR(kde->mac_addr)); 480189251Ssam if (kde->rsn_ie == NULL || kde->rsn_ie_len > PEERKEY_MAX_IE_LEN || 481189251Ssam wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0) { 482189251Ssam wpa_printf(MSG_INFO, "RSN: No RSN IE in SMK M5"); 483189251Ssam /* TODO: abort negotiation */ 484189251Ssam return -1; 485189251Ssam } 486189251Ssam 487189251Ssam if (os_memcmp(key->key_nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) { 488189251Ssam wpa_printf(MSG_INFO, "RSN: Key Nonce in SMK M5 does " 489189251Ssam "not match with INonce used in SMK M1"); 490189251Ssam return -1; 491189251Ssam } 492189251Ssam 493189251Ssam if (os_memcmp(kde->smk + PMK_LEN, peerkey->inonce, WPA_NONCE_LEN) != 0) 494189251Ssam { 495189251Ssam wpa_printf(MSG_INFO, "RSN: INonce in SMK KDE does not " 496189251Ssam "match with the one used in SMK M1"); 497189251Ssam return -1; 498189251Ssam } 499189251Ssam 500189251Ssam os_memcpy(peerkey->rsnie_p, kde->rsn_ie, kde->rsn_ie_len); 501189251Ssam peerkey->rsnie_p_len = kde->rsn_ie_len; 502189251Ssam os_memcpy(peerkey->pnonce, kde->nonce, WPA_NONCE_LEN); 503189251Ssam 504189251Ssam cipher = ie.pairwise_cipher & sm->allowed_pairwise_cipher; 505189251Ssam if (cipher & WPA_CIPHER_CCMP) { 506189251Ssam wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey"); 507189251Ssam peerkey->cipher = WPA_CIPHER_CCMP; 508189251Ssam } else if (cipher & WPA_CIPHER_TKIP) { 509189251Ssam wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey"); 510189251Ssam peerkey->cipher = WPA_CIPHER_TKIP; 511189251Ssam } else { 512189251Ssam wpa_printf(MSG_INFO, "RSN: SMK Peer STA " MACSTR " selected " 513189251Ssam "unacceptable cipher", MAC2STR(kde->mac_addr)); 514189251Ssam wpa_supplicant_send_smk_error(sm, src_addr, kde->mac_addr, 515189251Ssam STK_MUI_SMK, STK_ERR_CPHR_NS, 516189251Ssam ver); 517189251Ssam /* TODO: abort negotiation */ 518189251Ssam return -1; 519189251Ssam } 520189251Ssam 521189251Ssam return 0; 522189251Ssam} 523189251Ssam 524189251Ssam 525189251Ssamstatic int wpa_supplicant_process_smk_m45( 526189251Ssam struct wpa_sm *sm, const unsigned char *src_addr, 527189251Ssam const struct wpa_eapol_key *key, size_t extra_len, int ver) 528189251Ssam{ 529189251Ssam struct wpa_peerkey *peerkey; 530189251Ssam struct wpa_eapol_ie_parse kde; 531189251Ssam u32 lifetime; 532189251Ssam struct os_time now; 533189251Ssam 534189251Ssam if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) { 535189251Ssam wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for " 536189251Ssam "the current network"); 537189251Ssam return -1; 538189251Ssam } 539189251Ssam 540189251Ssam if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) < 541189251Ssam 0) { 542189251Ssam wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M4/M5"); 543189251Ssam return -1; 544189251Ssam } 545189251Ssam 546189251Ssam if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || 547189251Ssam kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN || 548189251Ssam kde.smk == NULL || kde.smk_len < PMK_LEN + WPA_NONCE_LEN || 549189251Ssam kde.lifetime == NULL || kde.lifetime_len < 4) { 550189251Ssam wpa_printf(MSG_INFO, "RSN: No MAC Address, Nonce, SMK, or " 551189251Ssam "Lifetime KDE in SMK M4/M5"); 552189251Ssam return -1; 553189251Ssam } 554189251Ssam 555189251Ssam for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { 556189251Ssam if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) == 0 && 557189251Ssam os_memcmp(peerkey->initiator ? peerkey->inonce : 558189251Ssam peerkey->pnonce, 559189251Ssam key->key_nonce, WPA_NONCE_LEN) == 0) 560189251Ssam break; 561189251Ssam } 562189251Ssam if (peerkey == NULL) { 563189251Ssam wpa_printf(MSG_INFO, "RSN: No matching SMK handshake found " 564189251Ssam "for SMK M4/M5: peer " MACSTR, 565189251Ssam MAC2STR(kde.mac_addr)); 566189251Ssam return -1; 567189251Ssam } 568189251Ssam 569189251Ssam if (peerkey->initiator) { 570189251Ssam if (wpa_supplicant_process_smk_m5(sm, src_addr, key, ver, 571189251Ssam peerkey, &kde) < 0) 572189251Ssam return -1; 573189251Ssam } else { 574189251Ssam if (wpa_supplicant_process_smk_m4(peerkey, &kde) < 0) 575189251Ssam return -1; 576189251Ssam } 577189251Ssam 578189251Ssam os_memcpy(peerkey->smk, kde.smk, PMK_LEN); 579189251Ssam peerkey->smk_complete = 1; 580189251Ssam wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", peerkey->smk, PMK_LEN); 581189251Ssam lifetime = WPA_GET_BE32(kde.lifetime); 582189251Ssam wpa_printf(MSG_DEBUG, "RSN: SMK lifetime %u seconds", lifetime); 583189251Ssam if (lifetime > 1000000000) 584189251Ssam lifetime = 1000000000; /* avoid overflowing expiration time */ 585189251Ssam peerkey->lifetime = lifetime; 586189251Ssam os_get_time(&now); 587189251Ssam peerkey->expiration = now.sec + lifetime; 588189251Ssam eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout, 589189251Ssam sm, peerkey); 590189251Ssam 591189251Ssam if (peerkey->initiator) { 592189251Ssam rsn_smkid(peerkey->smk, peerkey->pnonce, peerkey->addr, 593189251Ssam peerkey->inonce, sm->own_addr, peerkey->smkid, 594189251Ssam peerkey->use_sha256); 595189251Ssam wpa_supplicant_send_stk_1_of_4(sm, peerkey); 596189251Ssam } else { 597189251Ssam rsn_smkid(peerkey->smk, peerkey->pnonce, sm->own_addr, 598189251Ssam peerkey->inonce, peerkey->addr, peerkey->smkid, 599189251Ssam peerkey->use_sha256); 600189251Ssam } 601189251Ssam wpa_hexdump(MSG_DEBUG, "RSN: SMKID", peerkey->smkid, PMKID_LEN); 602189251Ssam 603189251Ssam return 0; 604189251Ssam} 605189251Ssam 606189251Ssam 607189251Ssamstatic int wpa_supplicant_process_smk_error( 608189251Ssam struct wpa_sm *sm, const unsigned char *src_addr, 609189251Ssam const struct wpa_eapol_key *key, size_t extra_len) 610189251Ssam{ 611189251Ssam struct wpa_eapol_ie_parse kde; 612189251Ssam struct rsn_error_kde error; 613189251Ssam u8 peer[ETH_ALEN]; 614189251Ssam u16 error_type; 615189251Ssam 616189251Ssam wpa_printf(MSG_DEBUG, "RSN: Received SMK Error"); 617189251Ssam 618189251Ssam if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) { 619189251Ssam wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for " 620189251Ssam "the current network"); 621189251Ssam return -1; 622189251Ssam } 623189251Ssam 624189251Ssam if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) < 625189251Ssam 0) { 626189251Ssam wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error"); 627189251Ssam return -1; 628189251Ssam } 629189251Ssam 630189251Ssam if (kde.error == NULL || kde.error_len < sizeof(error)) { 631189251Ssam wpa_printf(MSG_INFO, "RSN: No Error KDE in SMK Error"); 632189251Ssam return -1; 633189251Ssam } 634189251Ssam 635189251Ssam if (kde.mac_addr && kde.mac_addr_len >= ETH_ALEN) 636189251Ssam os_memcpy(peer, kde.mac_addr, ETH_ALEN); 637214734Srpaulo else 638214734Srpaulo os_memset(peer, 0, ETH_ALEN); 639189251Ssam os_memcpy(&error, kde.error, sizeof(error)); 640189251Ssam error_type = be_to_host16(error.error_type); 641214734Srpaulo wpa_msg(sm->ctx->msg_ctx, MSG_INFO, 642189251Ssam "RSN: SMK Error KDE received: MUI %d error_type %d peer " 643189251Ssam MACSTR, 644189251Ssam be_to_host16(error.mui), error_type, 645189251Ssam MAC2STR(peer)); 646189251Ssam 647189251Ssam if (kde.mac_addr && 648189251Ssam (error_type == STK_ERR_STA_NR || error_type == STK_ERR_STA_NRSN || 649189251Ssam error_type == STK_ERR_CPHR_NS)) { 650189251Ssam struct wpa_peerkey *peerkey; 651189251Ssam 652189251Ssam for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { 653189251Ssam if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) == 654189251Ssam 0) 655189251Ssam break; 656189251Ssam } 657189251Ssam if (peerkey == NULL) { 658189251Ssam wpa_printf(MSG_DEBUG, "RSN: No matching SMK handshake " 659189251Ssam "found for SMK Error"); 660189251Ssam return -1; 661189251Ssam } 662189251Ssam /* TODO: abort SMK/STK handshake and remove all related keys */ 663189251Ssam } 664189251Ssam 665189251Ssam return 0; 666189251Ssam} 667189251Ssam 668189251Ssam 669189251Ssamstatic void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm, 670189251Ssam struct wpa_peerkey *peerkey, 671189251Ssam const struct wpa_eapol_key *key, 672189251Ssam u16 ver) 673189251Ssam{ 674189251Ssam struct wpa_eapol_ie_parse ie; 675189251Ssam const u8 *kde; 676189251Ssam size_t len, kde_buf_len; 677189251Ssam struct wpa_ptk *stk; 678189251Ssam u8 buf[8], *kde_buf, *pos; 679189251Ssam be32 lifetime; 680189251Ssam 681189251Ssam wpa_printf(MSG_DEBUG, "RSN: RX message 1 of STK 4-Way Handshake from " 682189251Ssam MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); 683189251Ssam 684189251Ssam os_memset(&ie, 0, sizeof(ie)); 685189251Ssam 686189251Ssam /* RSN: msg 1/4 should contain SMKID for the selected SMK */ 687189251Ssam kde = (const u8 *) (key + 1); 688189251Ssam len = WPA_GET_BE16(key->key_data_length); 689189251Ssam wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", kde, len); 690189251Ssam if (wpa_supplicant_parse_ies(kde, len, &ie) < 0 || ie.pmkid == NULL) { 691189251Ssam wpa_printf(MSG_DEBUG, "RSN: No SMKID in STK 1/4"); 692189251Ssam return; 693189251Ssam } 694189251Ssam if (os_memcmp(ie.pmkid, peerkey->smkid, PMKID_LEN) != 0) { 695189251Ssam wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 1/4", 696189251Ssam ie.pmkid, PMKID_LEN); 697189251Ssam return; 698189251Ssam } 699189251Ssam 700189251Ssam if (os_get_random(peerkey->pnonce, WPA_NONCE_LEN)) { 701214734Srpaulo wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, 702189251Ssam "RSN: Failed to get random data for PNonce"); 703189251Ssam return; 704189251Ssam } 705189251Ssam wpa_hexdump(MSG_DEBUG, "WPA: Renewed PNonce", 706189251Ssam peerkey->pnonce, WPA_NONCE_LEN); 707189251Ssam 708189251Ssam /* Calculate STK which will be stored as a temporary STK until it has 709189251Ssam * been verified when processing message 3/4. */ 710189251Ssam stk = &peerkey->tstk; 711189251Ssam wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion", 712189251Ssam sm->own_addr, peerkey->addr, 713189251Ssam peerkey->pnonce, key->key_nonce, 714189251Ssam (u8 *) stk, sizeof(*stk), 715189251Ssam peerkey->use_sha256); 716189251Ssam /* Supplicant: swap tx/rx Mic keys */ 717189251Ssam os_memcpy(buf, stk->u.auth.tx_mic_key, 8); 718189251Ssam os_memcpy(stk->u.auth.tx_mic_key, stk->u.auth.rx_mic_key, 8); 719189251Ssam os_memcpy(stk->u.auth.rx_mic_key, buf, 8); 720189251Ssam peerkey->tstk_set = 1; 721189251Ssam 722189251Ssam kde_buf_len = peerkey->rsnie_p_len + 723189251Ssam 2 + RSN_SELECTOR_LEN + sizeof(lifetime) + 724189251Ssam 2 + RSN_SELECTOR_LEN + PMKID_LEN; 725189251Ssam kde_buf = os_malloc(kde_buf_len); 726189251Ssam if (kde_buf == NULL) 727189251Ssam return; 728189251Ssam pos = kde_buf; 729189251Ssam pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len); 730189251Ssam lifetime = host_to_be32(peerkey->lifetime); 731189251Ssam pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, 732189251Ssam (u8 *) &lifetime, sizeof(lifetime)); 733189251Ssam wpa_add_kde(pos, RSN_KEY_DATA_PMKID, peerkey->smkid, PMKID_LEN); 734189251Ssam 735189251Ssam if (wpa_supplicant_send_2_of_4(sm, peerkey->addr, key, ver, 736189251Ssam peerkey->pnonce, kde_buf, kde_buf_len, 737189251Ssam stk)) { 738189251Ssam os_free(kde_buf); 739189251Ssam return; 740189251Ssam } 741189251Ssam os_free(kde_buf); 742189251Ssam 743189251Ssam os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN); 744189251Ssam} 745189251Ssam 746189251Ssam 747189251Ssamstatic void wpa_supplicant_update_smk_lifetime(struct wpa_sm *sm, 748189251Ssam struct wpa_peerkey *peerkey, 749189251Ssam struct wpa_eapol_ie_parse *kde) 750189251Ssam{ 751189251Ssam u32 lifetime; 752189251Ssam struct os_time now; 753189251Ssam 754189251Ssam if (kde->lifetime == NULL || kde->lifetime_len < sizeof(lifetime)) 755189251Ssam return; 756189251Ssam 757189251Ssam lifetime = WPA_GET_BE32(kde->lifetime); 758189251Ssam 759189251Ssam if (lifetime >= peerkey->lifetime) { 760189251Ssam wpa_printf(MSG_DEBUG, "RSN: Peer used SMK lifetime %u seconds " 761189251Ssam "which is larger than or equal to own value %u " 762189251Ssam "seconds - ignored", lifetime, peerkey->lifetime); 763189251Ssam return; 764189251Ssam } 765189251Ssam 766189251Ssam wpa_printf(MSG_DEBUG, "RSN: Peer used shorter SMK lifetime %u seconds " 767189251Ssam "(own was %u seconds) - updated", 768189251Ssam lifetime, peerkey->lifetime); 769189251Ssam peerkey->lifetime = lifetime; 770189251Ssam 771189251Ssam os_get_time(&now); 772189251Ssam peerkey->expiration = now.sec + lifetime; 773189251Ssam eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey); 774189251Ssam eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout, 775189251Ssam sm, peerkey); 776189251Ssam} 777189251Ssam 778189251Ssam 779189251Ssamstatic void wpa_supplicant_process_stk_2_of_4(struct wpa_sm *sm, 780189251Ssam struct wpa_peerkey *peerkey, 781189251Ssam const struct wpa_eapol_key *key, 782189251Ssam u16 ver) 783189251Ssam{ 784189251Ssam struct wpa_eapol_ie_parse kde; 785189251Ssam const u8 *keydata; 786189251Ssam size_t len; 787189251Ssam 788189251Ssam wpa_printf(MSG_DEBUG, "RSN: RX message 2 of STK 4-Way Handshake from " 789189251Ssam MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); 790189251Ssam 791189251Ssam os_memset(&kde, 0, sizeof(kde)); 792189251Ssam 793189251Ssam /* RSN: msg 2/4 should contain SMKID for the selected SMK and RSN IE 794189251Ssam * from the peer. It may also include Lifetime KDE. */ 795189251Ssam keydata = (const u8 *) (key + 1); 796189251Ssam len = WPA_GET_BE16(key->key_data_length); 797189251Ssam wpa_hexdump(MSG_DEBUG, "RSN: msg 2/4 key data", keydata, len); 798189251Ssam if (wpa_supplicant_parse_ies(keydata, len, &kde) < 0 || 799189251Ssam kde.pmkid == NULL || kde.rsn_ie == NULL) { 800189251Ssam wpa_printf(MSG_DEBUG, "RSN: No SMKID or RSN IE in STK 2/4"); 801189251Ssam return; 802189251Ssam } 803189251Ssam 804189251Ssam if (os_memcmp(kde.pmkid, peerkey->smkid, PMKID_LEN) != 0) { 805189251Ssam wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 2/4", 806189251Ssam kde.pmkid, PMKID_LEN); 807189251Ssam return; 808189251Ssam } 809189251Ssam 810189251Ssam if (kde.rsn_ie_len != peerkey->rsnie_p_len || 811189251Ssam os_memcmp(kde.rsn_ie, peerkey->rsnie_p, kde.rsn_ie_len) != 0) { 812189251Ssam wpa_printf(MSG_INFO, "RSN: Peer RSN IE in SMK and STK " 813189251Ssam "handshakes did not match"); 814189251Ssam wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in SMK handshake", 815189251Ssam peerkey->rsnie_p, peerkey->rsnie_p_len); 816189251Ssam wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in STK handshake", 817189251Ssam kde.rsn_ie, kde.rsn_ie_len); 818189251Ssam return; 819189251Ssam } 820189251Ssam 821189251Ssam wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde); 822189251Ssam 823189251Ssam wpa_supplicant_send_stk_3_of_4(sm, peerkey); 824189251Ssam os_memcpy(peerkey->pnonce, key->key_nonce, WPA_NONCE_LEN); 825189251Ssam} 826189251Ssam 827189251Ssam 828189251Ssamstatic void wpa_supplicant_process_stk_3_of_4(struct wpa_sm *sm, 829189251Ssam struct wpa_peerkey *peerkey, 830189251Ssam const struct wpa_eapol_key *key, 831189251Ssam u16 ver) 832189251Ssam{ 833189251Ssam struct wpa_eapol_ie_parse kde; 834189251Ssam const u8 *keydata; 835189251Ssam size_t len, key_len; 836189251Ssam const u8 *_key; 837189251Ssam u8 key_buf[32], rsc[6]; 838189251Ssam 839189251Ssam wpa_printf(MSG_DEBUG, "RSN: RX message 3 of STK 4-Way Handshake from " 840189251Ssam MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); 841189251Ssam 842189251Ssam os_memset(&kde, 0, sizeof(kde)); 843189251Ssam 844189251Ssam /* RSN: msg 3/4 should contain Initiator RSN IE. It may also include 845189251Ssam * Lifetime KDE. */ 846189251Ssam keydata = (const u8 *) (key + 1); 847189251Ssam len = WPA_GET_BE16(key->key_data_length); 848189251Ssam wpa_hexdump(MSG_DEBUG, "RSN: msg 3/4 key data", keydata, len); 849189251Ssam if (wpa_supplicant_parse_ies(keydata, len, &kde) < 0) { 850189251Ssam wpa_printf(MSG_DEBUG, "RSN: Failed to parse key data in " 851189251Ssam "STK 3/4"); 852189251Ssam return; 853189251Ssam } 854189251Ssam 855189251Ssam if (kde.rsn_ie_len != peerkey->rsnie_i_len || 856189251Ssam os_memcmp(kde.rsn_ie, peerkey->rsnie_i, kde.rsn_ie_len) != 0) { 857189251Ssam wpa_printf(MSG_INFO, "RSN: Initiator RSN IE in SMK and STK " 858189251Ssam "handshakes did not match"); 859189251Ssam wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in SMK " 860189251Ssam "handshake", 861189251Ssam peerkey->rsnie_i, peerkey->rsnie_i_len); 862189251Ssam wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in STK " 863189251Ssam "handshake", 864189251Ssam kde.rsn_ie, kde.rsn_ie_len); 865189251Ssam return; 866189251Ssam } 867189251Ssam 868189251Ssam if (os_memcmp(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN) != 0) { 869189251Ssam wpa_printf(MSG_WARNING, "RSN: INonce from message 1 of STK " 870189251Ssam "4-Way Handshake differs from 3 of STK 4-Way " 871189251Ssam "Handshake - drop packet (src=" MACSTR ")", 872189251Ssam MAC2STR(peerkey->addr)); 873189251Ssam return; 874189251Ssam } 875189251Ssam 876189251Ssam wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde); 877189251Ssam 878189251Ssam if (wpa_supplicant_send_4_of_4(sm, peerkey->addr, key, ver, 879189251Ssam WPA_GET_BE16(key->key_info), 880189251Ssam NULL, 0, &peerkey->stk)) 881189251Ssam return; 882189251Ssam 883189251Ssam _key = (u8 *) peerkey->stk.tk1; 884189251Ssam if (peerkey->cipher == WPA_CIPHER_TKIP) { 885189251Ssam /* Swap Tx/Rx keys for Michael MIC */ 886189251Ssam os_memcpy(key_buf, _key, 16); 887189251Ssam os_memcpy(key_buf + 16, peerkey->stk.u.auth.rx_mic_key, 8); 888189251Ssam os_memcpy(key_buf + 24, peerkey->stk.u.auth.tx_mic_key, 8); 889189251Ssam _key = key_buf; 890189251Ssam key_len = 32; 891189251Ssam } else 892189251Ssam key_len = 16; 893189251Ssam 894189251Ssam os_memset(rsc, 0, 6); 895189251Ssam if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1, 896189251Ssam rsc, sizeof(rsc), _key, key_len) < 0) { 897189251Ssam wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the " 898189251Ssam "driver."); 899189251Ssam return; 900189251Ssam } 901189251Ssam} 902189251Ssam 903189251Ssam 904189251Ssamstatic void wpa_supplicant_process_stk_4_of_4(struct wpa_sm *sm, 905189251Ssam struct wpa_peerkey *peerkey, 906189251Ssam const struct wpa_eapol_key *key, 907189251Ssam u16 ver) 908189251Ssam{ 909189251Ssam u8 rsc[6]; 910189251Ssam 911189251Ssam wpa_printf(MSG_DEBUG, "RSN: RX message 4 of STK 4-Way Handshake from " 912189251Ssam MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); 913189251Ssam 914189251Ssam os_memset(rsc, 0, 6); 915189251Ssam if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1, 916189251Ssam rsc, sizeof(rsc), (u8 *) peerkey->stk.tk1, 917189251Ssam peerkey->cipher == WPA_CIPHER_TKIP ? 32 : 16) < 0) { 918189251Ssam wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the " 919189251Ssam "driver."); 920189251Ssam return; 921189251Ssam } 922189251Ssam} 923189251Ssam 924189251Ssam 925189251Ssam/** 926189251Ssam * peerkey_verify_eapol_key_mic - Verify PeerKey MIC 927189251Ssam * @sm: Pointer to WPA state machine data from wpa_sm_init() 928189251Ssam * @peerkey: Pointer to the PeerKey data for the peer 929189251Ssam * @key: Pointer to the EAPOL-Key frame header 930189251Ssam * @ver: Version bits from EAPOL-Key Key Info 931189251Ssam * @buf: Pointer to the beginning of EAPOL-Key frame 932189251Ssam * @len: Length of the EAPOL-Key frame 933189251Ssam * Returns: 0 on success, -1 on failure 934189251Ssam */ 935189251Ssamint peerkey_verify_eapol_key_mic(struct wpa_sm *sm, 936189251Ssam struct wpa_peerkey *peerkey, 937189251Ssam struct wpa_eapol_key *key, u16 ver, 938189251Ssam const u8 *buf, size_t len) 939189251Ssam{ 940189251Ssam u8 mic[16]; 941189251Ssam int ok = 0; 942189251Ssam 943189251Ssam if (peerkey->initiator && !peerkey->stk_set) { 944189251Ssam wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion", 945189251Ssam sm->own_addr, peerkey->addr, 946189251Ssam peerkey->inonce, key->key_nonce, 947189251Ssam (u8 *) &peerkey->stk, sizeof(peerkey->stk), 948189251Ssam peerkey->use_sha256); 949189251Ssam peerkey->stk_set = 1; 950189251Ssam } 951189251Ssam 952189251Ssam os_memcpy(mic, key->key_mic, 16); 953189251Ssam if (peerkey->tstk_set) { 954189251Ssam os_memset(key->key_mic, 0, 16); 955189251Ssam wpa_eapol_key_mic(peerkey->tstk.kck, ver, buf, len, 956189251Ssam key->key_mic); 957189251Ssam if (os_memcmp(mic, key->key_mic, 16) != 0) { 958189251Ssam wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC " 959189251Ssam "when using TSTK - ignoring TSTK"); 960189251Ssam } else { 961189251Ssam ok = 1; 962189251Ssam peerkey->tstk_set = 0; 963189251Ssam peerkey->stk_set = 1; 964189251Ssam os_memcpy(&peerkey->stk, &peerkey->tstk, 965189251Ssam sizeof(peerkey->stk)); 966189251Ssam } 967189251Ssam } 968189251Ssam 969189251Ssam if (!ok && peerkey->stk_set) { 970189251Ssam os_memset(key->key_mic, 0, 16); 971189251Ssam wpa_eapol_key_mic(peerkey->stk.kck, ver, buf, len, 972189251Ssam key->key_mic); 973189251Ssam if (os_memcmp(mic, key->key_mic, 16) != 0) { 974189251Ssam wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC " 975189251Ssam "- dropping packet"); 976189251Ssam return -1; 977189251Ssam } 978189251Ssam ok = 1; 979189251Ssam } 980189251Ssam 981189251Ssam if (!ok) { 982189251Ssam wpa_printf(MSG_WARNING, "RSN: Could not verify EAPOL-Key MIC " 983189251Ssam "- dropping packet"); 984189251Ssam return -1; 985189251Ssam } 986189251Ssam 987189251Ssam os_memcpy(peerkey->replay_counter, key->replay_counter, 988189251Ssam WPA_REPLAY_COUNTER_LEN); 989189251Ssam peerkey->replay_counter_set = 1; 990189251Ssam return 0; 991189251Ssam} 992189251Ssam 993189251Ssam 994189251Ssam/** 995189251Ssam * wpa_sm_stkstart - Send EAPOL-Key Request for STK handshake (STK M1) 996189251Ssam * @sm: Pointer to WPA state machine data from wpa_sm_init() 997189251Ssam * @peer: MAC address of the peer STA 998189251Ssam * Returns: 0 on success, or -1 on failure 999189251Ssam * 1000189251Ssam * Send an EAPOL-Key Request to the current authenticator to start STK 1001189251Ssam * handshake with the peer. 1002189251Ssam */ 1003189251Ssamint wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) 1004189251Ssam{ 1005189251Ssam size_t rlen, kde_len; 1006189251Ssam struct wpa_eapol_key *req; 1007189251Ssam int key_info, ver; 1008189251Ssam u8 bssid[ETH_ALEN], *rbuf, *pos, *count_pos; 1009189251Ssam u16 count; 1010189251Ssam struct rsn_ie_hdr *hdr; 1011189251Ssam struct wpa_peerkey *peerkey; 1012189251Ssam struct wpa_ie_data ie; 1013189251Ssam 1014189251Ssam if (sm->proto != WPA_PROTO_RSN || !sm->ptk_set || !sm->peerkey_enabled) 1015189251Ssam return -1; 1016189251Ssam 1017189251Ssam if (sm->ap_rsn_ie && 1018189251Ssam wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &ie) == 0 && 1019189251Ssam !(ie.capabilities & WPA_CAPABILITY_PEERKEY_ENABLED)) { 1020189251Ssam wpa_printf(MSG_DEBUG, "RSN: Current AP does not support STK"); 1021189251Ssam return -1; 1022189251Ssam } 1023189251Ssam 1024189251Ssam if (sm->pairwise_cipher == WPA_CIPHER_CCMP) 1025189251Ssam ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; 1026189251Ssam else 1027189251Ssam ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; 1028189251Ssam 1029189251Ssam if (wpa_sm_get_bssid(sm, bssid) < 0) { 1030189251Ssam wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key " 1031189251Ssam "SMK M1"); 1032189251Ssam return -1; 1033189251Ssam } 1034189251Ssam 1035189251Ssam /* TODO: find existing entry and if found, use that instead of adding 1036189251Ssam * a new one */ 1037189251Ssam peerkey = os_zalloc(sizeof(*peerkey)); 1038189251Ssam if (peerkey == NULL) 1039189251Ssam return -1; 1040189251Ssam peerkey->initiator = 1; 1041189251Ssam os_memcpy(peerkey->addr, peer, ETH_ALEN); 1042189251Ssam#ifdef CONFIG_IEEE80211W 1043189251Ssam if (wpa_key_mgmt_sha256(sm->key_mgmt)) 1044189251Ssam peerkey->use_sha256 = 1; 1045189251Ssam#endif /* CONFIG_IEEE80211W */ 1046189251Ssam 1047189251Ssam /* SMK M1: 1048189251Ssam * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, 1049189251Ssam * MIC=MIC, DataKDs=(RSNIE_I, MAC_P KDE)) 1050189251Ssam */ 1051189251Ssam 1052189251Ssam hdr = (struct rsn_ie_hdr *) peerkey->rsnie_i; 1053189251Ssam hdr->elem_id = WLAN_EID_RSN; 1054189251Ssam WPA_PUT_LE16(hdr->version, RSN_VERSION); 1055189251Ssam pos = (u8 *) (hdr + 1); 1056189251Ssam /* Group Suite can be anything for SMK RSN IE; receiver will just 1057189251Ssam * ignore it. */ 1058189251Ssam RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); 1059189251Ssam pos += RSN_SELECTOR_LEN; 1060189251Ssam count_pos = pos; 1061189251Ssam pos += 2; 1062189251Ssam 1063189251Ssam count = 0; 1064189251Ssam if (sm->allowed_pairwise_cipher & WPA_CIPHER_CCMP) { 1065189251Ssam RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); 1066189251Ssam pos += RSN_SELECTOR_LEN; 1067189251Ssam count++; 1068189251Ssam } 1069189251Ssam if (sm->allowed_pairwise_cipher & WPA_CIPHER_TKIP) { 1070189251Ssam RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); 1071189251Ssam pos += RSN_SELECTOR_LEN; 1072189251Ssam count++; 1073189251Ssam } 1074189251Ssam WPA_PUT_LE16(count_pos, count); 1075189251Ssam 1076189251Ssam hdr->len = (pos - peerkey->rsnie_i) - 2; 1077189251Ssam peerkey->rsnie_i_len = pos - peerkey->rsnie_i; 1078189251Ssam wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake", 1079189251Ssam peerkey->rsnie_i, peerkey->rsnie_i_len); 1080189251Ssam 1081189251Ssam kde_len = peerkey->rsnie_i_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN; 1082189251Ssam 1083189251Ssam rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, 1084189251Ssam sizeof(*req) + kde_len, &rlen, 1085189251Ssam (void *) &req); 1086189251Ssam if (rbuf == NULL) { 1087189251Ssam wpa_supplicant_peerkey_free(sm, peerkey); 1088189251Ssam return -1; 1089189251Ssam } 1090189251Ssam 1091189251Ssam req->type = EAPOL_KEY_TYPE_RSN; 1092189251Ssam key_info = WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC | 1093189251Ssam WPA_KEY_INFO_SECURE | WPA_KEY_INFO_REQUEST | ver; 1094189251Ssam WPA_PUT_BE16(req->key_info, key_info); 1095189251Ssam WPA_PUT_BE16(req->key_length, 0); 1096189251Ssam os_memcpy(req->replay_counter, sm->request_counter, 1097189251Ssam WPA_REPLAY_COUNTER_LEN); 1098189251Ssam inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); 1099189251Ssam 1100189251Ssam if (os_get_random(peerkey->inonce, WPA_NONCE_LEN)) { 1101214734Srpaulo wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, 1102189251Ssam "WPA: Failed to get random data for INonce"); 1103189251Ssam os_free(rbuf); 1104189251Ssam wpa_supplicant_peerkey_free(sm, peerkey); 1105189251Ssam return -1; 1106189251Ssam } 1107189251Ssam os_memcpy(req->key_nonce, peerkey->inonce, WPA_NONCE_LEN); 1108189251Ssam wpa_hexdump(MSG_DEBUG, "WPA: INonce for SMK handshake", 1109189251Ssam req->key_nonce, WPA_NONCE_LEN); 1110189251Ssam 1111189251Ssam WPA_PUT_BE16(req->key_data_length, (u16) kde_len); 1112189251Ssam pos = (u8 *) (req + 1); 1113189251Ssam 1114189251Ssam /* Initiator RSN IE */ 1115189251Ssam pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len); 1116189251Ssam /* Peer MAC address KDE */ 1117189251Ssam wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN); 1118189251Ssam 1119189251Ssam wpa_printf(MSG_INFO, "RSN: Sending EAPOL-Key SMK M1 Request (peer " 1120189251Ssam MACSTR ")", MAC2STR(peer)); 1121189251Ssam wpa_eapol_key_send(sm, sm->ptk.kck, ver, bssid, ETH_P_EAPOL, 1122189251Ssam rbuf, rlen, req->key_mic); 1123189251Ssam 1124189251Ssam peerkey->next = sm->peerkey; 1125189251Ssam sm->peerkey = peerkey; 1126189251Ssam 1127189251Ssam return 0; 1128189251Ssam} 1129189251Ssam 1130189251Ssam 1131189251Ssam/** 1132189251Ssam * peerkey_deinit - Free PeerKey values 1133189251Ssam * @sm: Pointer to WPA state machine data from wpa_sm_init() 1134189251Ssam */ 1135189251Ssamvoid peerkey_deinit(struct wpa_sm *sm) 1136189251Ssam{ 1137189251Ssam struct wpa_peerkey *prev, *peerkey = sm->peerkey; 1138189251Ssam while (peerkey) { 1139189251Ssam prev = peerkey; 1140189251Ssam peerkey = peerkey->next; 1141189251Ssam os_free(prev); 1142189251Ssam } 1143189251Ssam} 1144189251Ssam 1145189251Ssam 1146189251Ssamvoid peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey, 1147189251Ssam struct wpa_eapol_key *key, u16 key_info, u16 ver) 1148189251Ssam{ 1149189251Ssam if ((key_info & (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) == 1150189251Ssam (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) { 1151189251Ssam /* 3/4 STK 4-Way Handshake */ 1152189251Ssam wpa_supplicant_process_stk_3_of_4(sm, peerkey, key, ver); 1153189251Ssam } else if (key_info & WPA_KEY_INFO_ACK) { 1154189251Ssam /* 1/4 STK 4-Way Handshake */ 1155189251Ssam wpa_supplicant_process_stk_1_of_4(sm, peerkey, key, ver); 1156189251Ssam } else if (key_info & WPA_KEY_INFO_SECURE) { 1157189251Ssam /* 4/4 STK 4-Way Handshake */ 1158189251Ssam wpa_supplicant_process_stk_4_of_4(sm, peerkey, key, ver); 1159189251Ssam } else { 1160189251Ssam /* 2/4 STK 4-Way Handshake */ 1161189251Ssam wpa_supplicant_process_stk_2_of_4(sm, peerkey, key, ver); 1162189251Ssam } 1163189251Ssam} 1164189251Ssam 1165189251Ssam 1166189251Ssamvoid peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr, 1167189251Ssam struct wpa_eapol_key *key, size_t extra_len, 1168189251Ssam u16 key_info, u16 ver) 1169189251Ssam{ 1170189251Ssam if (key_info & WPA_KEY_INFO_ERROR) { 1171189251Ssam /* SMK Error */ 1172189251Ssam wpa_supplicant_process_smk_error(sm, src_addr, key, extra_len); 1173189251Ssam } else if (key_info & WPA_KEY_INFO_ACK) { 1174189251Ssam /* SMK M2 */ 1175189251Ssam wpa_supplicant_process_smk_m2(sm, src_addr, key, extra_len, 1176189251Ssam ver); 1177189251Ssam } else { 1178189251Ssam /* SMK M4 or M5 */ 1179189251Ssam wpa_supplicant_process_smk_m45(sm, src_addr, key, extra_len, 1180189251Ssam ver); 1181189251Ssam } 1182189251Ssam} 1183189251Ssam 1184189251Ssam#endif /* CONFIG_PEERKEY */ 1185