mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-11-26 02:19:26 +01:00
d38bd513c9
802.11ai mandates that the RSN element is included during authentication for FILS. This previously was happening by chance since supplicant_ie was being included with CMD_AUTHENTICATE. This included more than just the RSNE so that was removed in an earlier commit. Now FILS builds the RSNE itself and includes this with CMD_AUTHENTICATE.
611 lines
15 KiB
C
611 lines
15 KiB
C
|
|
/*
|
|
*
|
|
* Wireless daemon for Linux
|
|
*
|
|
* Copyright (C) 2019 Intel Corporation. All rights reserved.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <ell/ell.h>
|
|
|
|
#include "src/ie.h"
|
|
#include "src/fils.h"
|
|
#include "src/handshake.h"
|
|
#include "src/mpdu.h"
|
|
#include "src/crypto.h"
|
|
#include "src/util.h"
|
|
#include "src/missing.h"
|
|
#include "src/erp.h"
|
|
#include "src/auth-proto.h"
|
|
|
|
#define FILS_NONCE_LEN 16
|
|
#define FILS_SESSION_LEN 8
|
|
|
|
struct fils_sm {
|
|
struct auth_proto ap;
|
|
struct erp_state *erp;
|
|
struct handshake_state *hs;
|
|
void *user_data;
|
|
|
|
fils_tx_authenticate_func_t auth;
|
|
fils_tx_associate_func_t assoc;
|
|
|
|
uint8_t nonce[FILS_NONCE_LEN];
|
|
uint8_t anonce[FILS_NONCE_LEN];
|
|
uint8_t session[FILS_SESSION_LEN];
|
|
|
|
uint8_t ick[48];
|
|
size_t ick_len;
|
|
uint8_t kek_and_tk[64 + 16];
|
|
size_t kek_len;
|
|
uint8_t pmk[48];
|
|
size_t pmk_len;
|
|
uint8_t pmkid[16];
|
|
|
|
uint8_t fils_ft[48];
|
|
size_t fils_ft_len;
|
|
};
|
|
|
|
static void fils_derive_pmkid(struct fils_sm *fils, const uint8_t *erp_data,
|
|
size_t len)
|
|
{
|
|
struct l_checksum *sha;
|
|
enum l_checksum_type type;
|
|
|
|
if (fils->hs->akm_suite & (IE_RSN_AKM_SUITE_FILS_SHA256 |
|
|
IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256))
|
|
type = L_CHECKSUM_SHA256;
|
|
else
|
|
type = L_CHECKSUM_SHA384;
|
|
|
|
sha = l_checksum_new(type);
|
|
l_checksum_update(sha, erp_data, len);
|
|
l_checksum_get_digest(sha, fils->pmkid, sizeof(fils->pmkid));
|
|
l_checksum_free(sha);
|
|
}
|
|
|
|
static void fils_erp_tx_func(const uint8_t *eap_data, size_t len,
|
|
void *user_data)
|
|
{
|
|
struct fils_sm *fils = user_data;
|
|
struct ie_tlv_builder builder;
|
|
uint8_t data[256];
|
|
uint8_t *ptr = data;
|
|
unsigned int tlv_len;
|
|
struct ie_rsn_info rsn_info;
|
|
uint8_t *rsne;
|
|
|
|
l_getrandom(fils->nonce, 16);
|
|
l_getrandom(fils->session, 8);
|
|
|
|
fils_derive_pmkid(fils, eap_data, len);
|
|
|
|
/* transaction */
|
|
l_put_le16(1, ptr);
|
|
ptr += 2;
|
|
/* status success */
|
|
l_put_le16(0, ptr);
|
|
ptr += 2;
|
|
|
|
ie_tlv_builder_init(&builder, ptr, sizeof(data) - 4);
|
|
|
|
ie_parse_rsne_from_data(fils->hs->supplicant_ie,
|
|
fils->hs->supplicant_ie[1] + 2,
|
|
&rsn_info);
|
|
rsne = alloca(256);
|
|
ie_build_rsne(&rsn_info, rsne);
|
|
|
|
ie_tlv_builder_next(&builder, IE_TYPE_RSN);
|
|
ie_tlv_builder_set_data(&builder, rsne + 2, rsne[1]);
|
|
|
|
ie_tlv_builder_next(&builder, IE_TYPE_FILS_NONCE);
|
|
ie_tlv_builder_set_data(&builder, fils->nonce, sizeof(fils->nonce));
|
|
|
|
ie_tlv_builder_next(&builder, IE_TYPE_FILS_SESSION);
|
|
ie_tlv_builder_set_data(&builder, fils->session, sizeof(fils->session));
|
|
|
|
ie_tlv_builder_next(&builder, IE_TYPE_FILS_WRAPPED_DATA);
|
|
ie_tlv_builder_set_data(&builder, eap_data, len);
|
|
|
|
if (fils->hs->mde) {
|
|
ie_tlv_builder_next(&builder, IE_TYPE_MOBILITY_DOMAIN);
|
|
ie_tlv_builder_set_data(&builder, fils->hs->mde + 2,
|
|
fils->hs->mde[1]);
|
|
}
|
|
|
|
ie_tlv_builder_finalize(&builder, &tlv_len);
|
|
|
|
fils->auth(data, ptr - data + tlv_len, fils->user_data);
|
|
}
|
|
|
|
static int fils_derive_key_data(struct fils_sm *fils)
|
|
{
|
|
const void *rmsk;
|
|
size_t rmsk_len;
|
|
struct ie_tlv_builder builder;
|
|
uint8_t key[FILS_NONCE_LEN * 2];
|
|
uint8_t key_data[64 + 48 + 16 + 48]; /* largest ICK, KEK, TK, FILS-FT */
|
|
uint8_t key_auth[48];
|
|
uint8_t data[44];
|
|
uint8_t *ptr = data;
|
|
size_t hash_len;
|
|
struct iovec iov[4];
|
|
size_t iov_elems = 0;
|
|
size_t fils_ft_len = 0;
|
|
bool sha384;
|
|
unsigned int ie_len;
|
|
uint8_t *rsne = NULL;
|
|
|
|
rmsk = erp_get_rmsk(fils->erp, &rmsk_len);
|
|
|
|
if (fils->hs->akm_suite == IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256)
|
|
fils_ft_len = 32;
|
|
else if (fils->hs->akm_suite == IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA384)
|
|
fils_ft_len = 48;
|
|
/*
|
|
* IEEE 802.11ai - Section 12.12.2.5.3
|
|
*/
|
|
if (fils->hs->akm_suite & (IE_RSN_AKM_SUITE_FILS_SHA256 |
|
|
IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256)) {
|
|
sha384 = false;
|
|
hash_len = 32;
|
|
} else {
|
|
sha384 = true;
|
|
hash_len = 48;
|
|
}
|
|
|
|
fils->kek_len = handshake_state_get_kek_len(fils->hs);
|
|
|
|
/* key is SNonce || ANonce */
|
|
memcpy(key, fils->nonce, sizeof(fils->nonce));
|
|
memcpy(key + FILS_NONCE_LEN, fils->anonce, sizeof(fils->anonce));
|
|
|
|
if (sha384)
|
|
hmac_sha384(key, sizeof(key), rmsk, rmsk_len,
|
|
fils->pmk, hash_len);
|
|
else
|
|
hmac_sha256(key, sizeof(key), rmsk, rmsk_len,
|
|
fils->pmk, hash_len);
|
|
|
|
fils->pmk_len = hash_len;
|
|
|
|
/*
|
|
* IEEE 802.11ai - 12.12.2.5.3 PTKSA key derivation with FILS
|
|
* authentication
|
|
*
|
|
* FILS-Key-Data = PRF-X(PMK, “FILS PTK Derivation”, SPA || AA ||
|
|
* SNonce || ANonce)
|
|
*/
|
|
memcpy(ptr, fils->hs->spa, 6);
|
|
ptr += 6;
|
|
memcpy(ptr, fils->hs->aa, 6);
|
|
ptr += 6;
|
|
memcpy(ptr, fils->nonce, sizeof(fils->nonce));
|
|
ptr += sizeof(fils->nonce);
|
|
memcpy(ptr, fils->anonce, sizeof(fils->anonce));
|
|
ptr += sizeof(fils->anonce);
|
|
|
|
if (sha384)
|
|
kdf_sha384(fils->pmk, hash_len, "FILS PTK Derivation",
|
|
strlen("FILS PTK Derivation"), data,
|
|
sizeof(data), key_data,
|
|
hash_len + fils->kek_len + 16 + fils_ft_len);
|
|
else
|
|
kdf_sha256(fils->pmk, hash_len, "FILS PTK Derivation",
|
|
strlen("FILS PTK Derivation"), data,
|
|
sizeof(data), key_data,
|
|
hash_len + fils->kek_len + 16 + fils_ft_len);
|
|
|
|
ptr = data;
|
|
|
|
/*
|
|
* IEEE 802.11ai - 12.12.2.6.2 (Re)Association Request for FILS key
|
|
* confirmation
|
|
*
|
|
* Key-Auth = HMAC-Hash(ICK, SNonce || ANonce || STA-MAC || AP-BSSID)
|
|
*/
|
|
memcpy(ptr, fils->nonce, sizeof(fils->nonce));
|
|
ptr += sizeof(fils->nonce);
|
|
memcpy(ptr, fils->anonce, sizeof(fils->anonce));
|
|
ptr += sizeof(fils->anonce);
|
|
memcpy(ptr, fils->hs->spa, 6);
|
|
ptr += 6;
|
|
memcpy(ptr, fils->hs->aa, 6);
|
|
ptr += 6;
|
|
|
|
memcpy(fils->ick, key_data, hash_len);
|
|
fils->ick_len = hash_len;
|
|
|
|
if (fils_ft_len) {
|
|
memcpy(fils->fils_ft, key_data + hash_len + fils->kek_len + 16,
|
|
fils_ft_len);
|
|
fils->fils_ft_len = fils_ft_len;
|
|
}
|
|
|
|
handshake_state_set_fils_ft(fils->hs, fils->fils_ft, fils->fils_ft_len);
|
|
|
|
if (sha384)
|
|
hmac_sha384(fils->ick, hash_len, data, ptr - data,
|
|
key_auth, hash_len);
|
|
else
|
|
hmac_sha256(fils->ick, hash_len, data, ptr - data,
|
|
key_auth, hash_len);
|
|
|
|
ie_tlv_builder_init(&builder, NULL, 0);
|
|
|
|
ie_tlv_builder_next(&builder, IE_TYPE_FILS_KEY_CONFIRMATION);
|
|
ie_tlv_builder_set_data(&builder, key_auth, hash_len);
|
|
|
|
ie_tlv_builder_next(&builder, IE_TYPE_FILS_SESSION);
|
|
ie_tlv_builder_set_data(&builder, fils->session, sizeof(fils->session));
|
|
|
|
iov[iov_elems].iov_base = ie_tlv_builder_finalize(&builder, &ie_len);
|
|
iov[iov_elems].iov_len = ie_len;
|
|
iov_elems++;
|
|
iov[iov_elems].iov_base = fils->hs->supplicant_ie;
|
|
iov[iov_elems].iov_len = fils->hs->supplicant_ie[1] + 2;
|
|
iov_elems++;
|
|
|
|
if (fils->hs->mde) {
|
|
struct ie_rsn_info rsn_info;
|
|
|
|
/*
|
|
* IEEE 8021.11ai Section 13.2.4:
|
|
*
|
|
* If a key hierarchy already exists for this STA belonging to
|
|
* the same mobility domain (i.e., having the same MDID), the
|
|
* R0KH shall delete the existing PMK-R0 security association
|
|
* and PMK-R1 security associations.
|
|
*
|
|
* All this means is we need to re-derive the new FT keys. This
|
|
* will rederive the PTK too, but it will be overwritten with
|
|
* the FILS PTK after associate
|
|
*/
|
|
handshake_state_derive_ptk(fils->hs);
|
|
|
|
iov[iov_elems].iov_base = fils->hs->mde;
|
|
iov[iov_elems].iov_len = fils->hs->mde[1] + 2;
|
|
iov_elems++;
|
|
|
|
if (ie_parse_rsne_from_data(fils->hs->supplicant_ie,
|
|
fils->hs->supplicant_ie[1] + 2,
|
|
&rsn_info) < 0)
|
|
return -EBADMSG;
|
|
|
|
rsn_info.num_pmkids = 1;
|
|
rsn_info.pmkids = fils->hs->pmk_r1_name;
|
|
|
|
rsne = alloca(256);
|
|
ie_build_rsne(&rsn_info, rsne);
|
|
|
|
iov[iov_elems].iov_base = rsne;
|
|
iov[iov_elems].iov_len = rsne[1] + 2;
|
|
iov_elems += 1;
|
|
}
|
|
|
|
memcpy(data, fils->nonce, sizeof(fils->nonce));
|
|
memcpy(data + sizeof(fils->nonce), fils->anonce, sizeof(fils->anonce));
|
|
|
|
memcpy(fils->kek_and_tk, key_data + hash_len, fils->kek_len + 16);
|
|
|
|
fils->assoc(iov, iov_elems, fils->kek_and_tk, fils->kek_len, data,
|
|
FILS_NONCE_LEN * 2, fils->user_data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool fils_start(struct auth_proto *driver)
|
|
{
|
|
struct fils_sm *fils = l_container_of(driver, struct fils_sm, ap);
|
|
|
|
return erp_start(fils->erp);
|
|
}
|
|
|
|
static void fils_free(struct auth_proto *driver)
|
|
{
|
|
struct fils_sm *fils = l_container_of(driver, struct fils_sm, ap);
|
|
|
|
erp_free(fils->erp);
|
|
|
|
explicit_bzero(fils->ick, sizeof(fils->ick));
|
|
explicit_bzero(fils->kek_and_tk, sizeof(fils->kek_and_tk));
|
|
explicit_bzero(fils->pmk, fils->pmk_len);
|
|
explicit_bzero(fils->pmkid, sizeof(fils->pmkid));
|
|
|
|
l_free(fils);
|
|
}
|
|
|
|
static int fils_rx_authenticate(struct auth_proto *driver, const uint8_t *frame,
|
|
size_t len)
|
|
{
|
|
struct fils_sm *fils = l_container_of(driver, struct fils_sm, ap);
|
|
const struct mmpdu_header *hdr = mpdu_validate(frame, len);
|
|
const struct mmpdu_authentication *auth;
|
|
uint16_t alg;
|
|
struct ie_tlv_iter iter;
|
|
const uint8_t *anonce = NULL;
|
|
const uint8_t *session = NULL;
|
|
const uint8_t *wrapped = NULL;
|
|
size_t wrapped_len = 0;
|
|
const uint8_t *rsne = NULL;
|
|
const uint8_t *mde = NULL;
|
|
const uint8_t *fte = NULL;
|
|
|
|
if (!hdr) {
|
|
l_debug("Auth frame header did not validate");
|
|
return -EBADMSG;
|
|
}
|
|
|
|
auth = mmpdu_body(hdr);
|
|
|
|
if (!auth) {
|
|
l_debug("Auth frame body did not validate");
|
|
return -EBADMSG;
|
|
}
|
|
|
|
if (auth->status != 0) {
|
|
l_debug("invalid status %u", auth->status);
|
|
return L_LE16_TO_CPU(auth->status);
|
|
}
|
|
|
|
alg = L_LE16_TO_CPU(auth->algorithm);
|
|
if (alg != MMPDU_AUTH_ALGO_FILS_SK &&
|
|
alg != MMPDU_AUTH_ALGO_FILS_SK_PFS) {
|
|
l_debug("invalid auth algorithm %u", auth->algorithm);
|
|
return MMPDU_STATUS_CODE_UNSUP_AUTH_ALG;
|
|
}
|
|
|
|
ie_tlv_iter_init(&iter, auth->ies, (const uint8_t *) hdr + len -
|
|
auth->ies);
|
|
while (ie_tlv_iter_next(&iter)) {
|
|
switch (iter.tag) {
|
|
case IE_TYPE_FILS_NONCE:
|
|
if (iter.len != FILS_NONCE_LEN)
|
|
goto invalid_ies;
|
|
|
|
anonce = iter.data;
|
|
break;
|
|
case IE_TYPE_FILS_SESSION:
|
|
if (iter.len != FILS_SESSION_LEN)
|
|
goto invalid_ies;
|
|
|
|
session = iter.data;
|
|
break;
|
|
case IE_TYPE_FILS_WRAPPED_DATA:
|
|
wrapped = iter.data;
|
|
wrapped_len = iter.len;
|
|
break;
|
|
case IE_TYPE_RSN:
|
|
if (rsne)
|
|
goto invalid_ies;
|
|
|
|
rsne = ie_tlv_iter_get_data(&iter) - 2;
|
|
break;
|
|
|
|
case IE_TYPE_MOBILITY_DOMAIN:
|
|
if (mde)
|
|
goto invalid_ies;
|
|
|
|
mde = ie_tlv_iter_get_data(&iter) - 2;
|
|
break;
|
|
|
|
case IE_TYPE_FAST_BSS_TRANSITION:
|
|
if (fte)
|
|
goto invalid_ies;
|
|
|
|
fte = ie_tlv_iter_get_data(&iter) - 2;
|
|
break;
|
|
|
|
default:
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (!anonce || !session || !wrapped) {
|
|
l_debug("Auth did not include required IEs");
|
|
goto invalid_ies;
|
|
}
|
|
|
|
if (mde)
|
|
handshake_state_set_mde(fils->hs, mde);
|
|
|
|
if (fte) {
|
|
struct handshake_state *hs = fils->hs;
|
|
uint32_t kck_len = handshake_state_get_kck_len(hs);
|
|
struct ie_ft_info ft_info;
|
|
|
|
if (ie_parse_fast_bss_transition_from_data(fte, fte[1] + 2,
|
|
kck_len, &ft_info) < 0)
|
|
goto invalid_ies;
|
|
|
|
handshake_state_set_fte(fils->hs, fte);
|
|
handshake_state_set_kh_ids(fils->hs, ft_info.r0khid,
|
|
ft_info.r0khid_len,
|
|
ft_info.r1khid);
|
|
}
|
|
|
|
memcpy(fils->anonce, anonce, FILS_NONCE_LEN);
|
|
|
|
if (erp_rx_packet(fils->erp, wrapped, wrapped_len) < 0)
|
|
goto invalid_ies;
|
|
|
|
return fils_derive_key_data(fils);
|
|
|
|
invalid_ies:
|
|
return MMPDU_STATUS_CODE_INVALID_ELEMENT;
|
|
}
|
|
|
|
static int fils_rx_associate(struct auth_proto *driver, const uint8_t *frame,
|
|
size_t len)
|
|
{
|
|
struct fils_sm *fils = l_container_of(driver, struct fils_sm, ap);
|
|
const struct mmpdu_header *hdr = mpdu_validate(frame, len);
|
|
const struct mmpdu_association_response *assoc;
|
|
struct ie_tlv_iter iter;
|
|
uint8_t key_rsc[8];
|
|
const uint8_t *gtk = NULL;
|
|
size_t gtk_len;
|
|
uint8_t gtk_key_index;
|
|
const uint8_t *igtk = NULL;
|
|
size_t igtk_len;
|
|
uint8_t igtk_key_index;
|
|
const uint8_t *ap_key_auth = NULL;
|
|
uint8_t expected_key_auth[48];
|
|
bool sha384 = (fils->hs->akm_suite & (IE_RSN_AKM_SUITE_FILS_SHA384 |
|
|
IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA384));
|
|
uint8_t data[44];
|
|
uint8_t *ptr = data;
|
|
|
|
if (!hdr) {
|
|
l_debug("Assoc frame header did not validate");
|
|
return -EBADMSG;
|
|
}
|
|
|
|
assoc = mmpdu_body(hdr);
|
|
|
|
if (!assoc) {
|
|
l_debug("Assoc frame body did not validate");
|
|
return -EBADMSG;
|
|
}
|
|
|
|
if (assoc->status_code != 0)
|
|
return L_CPU_TO_LE16(assoc->status_code);
|
|
|
|
ie_tlv_iter_init(&iter, assoc->ies, (const uint8_t *) hdr + len -
|
|
assoc->ies);
|
|
|
|
while (ie_tlv_iter_next(&iter)) {
|
|
switch (iter.tag) {
|
|
case IE_TYPE_KEY_DELIVERY:
|
|
if (iter.len < 8)
|
|
goto invalid_ies;
|
|
|
|
memcpy(key_rsc, iter.data, 8);
|
|
|
|
gtk = handshake_util_find_gtk_kde(iter.data + 8,
|
|
iter.len - 8,
|
|
>k_len);
|
|
if (!gtk)
|
|
goto invalid_ies;
|
|
|
|
gtk_key_index = util_bit_field(gtk[0], 0, 2);
|
|
gtk += 2;
|
|
gtk_len -= 2;
|
|
|
|
if (!fils->hs->mfp)
|
|
break;
|
|
|
|
igtk = handshake_util_find_igtk_kde(iter.data + 8,
|
|
iter.len - 8,
|
|
&igtk_len);
|
|
if (!igtk)
|
|
goto invalid_ies;
|
|
|
|
igtk_key_index = l_get_le16(igtk);
|
|
igtk += 2;
|
|
igtk_len -= 2;
|
|
|
|
break;
|
|
case IE_TYPE_FILS_KEY_CONFIRMATION:
|
|
if (sha384 && iter.len != 48)
|
|
goto invalid_ies;
|
|
|
|
if (!sha384 && iter.len != 32)
|
|
goto invalid_ies;
|
|
|
|
ap_key_auth = iter.data;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!ap_key_auth) {
|
|
l_debug("Associate did not include KeyAuth IE");
|
|
goto invalid_ies;
|
|
}
|
|
|
|
ptr = data;
|
|
|
|
memcpy(ptr, fils->anonce, sizeof(fils->anonce));
|
|
ptr += sizeof(fils->anonce);
|
|
memcpy(ptr, fils->nonce, sizeof(fils->nonce));
|
|
ptr += sizeof(fils->nonce);
|
|
memcpy(ptr, fils->hs->aa, 6);
|
|
ptr += 6;
|
|
memcpy(ptr, fils->hs->spa, 6);
|
|
ptr += 6;
|
|
|
|
if (sha384)
|
|
hmac_sha384(fils->ick, fils->ick_len, data, ptr - data,
|
|
expected_key_auth, fils->ick_len);
|
|
else
|
|
hmac_sha256(fils->ick, fils->ick_len, data, ptr - data,
|
|
expected_key_auth, fils->ick_len);
|
|
|
|
if (memcmp(ap_key_auth, expected_key_auth, fils->ick_len)) {
|
|
l_error("AP KeyAuth did not verify");
|
|
return -EBADMSG;
|
|
}
|
|
|
|
handshake_state_set_pmk(fils->hs, fils->pmk, fils->pmk_len);
|
|
handshake_state_set_pmkid(fils->hs, fils->pmkid);
|
|
|
|
if (gtk)
|
|
handshake_state_install_gtk(fils->hs, gtk_key_index, gtk,
|
|
gtk_len, key_rsc, 6);
|
|
|
|
if (igtk)
|
|
handshake_state_install_igtk(fils->hs, igtk_key_index,
|
|
igtk + 6, igtk_len - 6, igtk);
|
|
|
|
handshake_state_set_ptk(fils->hs, fils->kek_and_tk, fils->kek_len + 16);
|
|
handshake_state_install_ptk(fils->hs);
|
|
|
|
return 0;
|
|
|
|
invalid_ies:
|
|
return MMPDU_STATUS_CODE_INVALID_ELEMENT;
|
|
}
|
|
|
|
struct auth_proto *fils_sm_new(struct handshake_state *hs,
|
|
fils_tx_authenticate_func_t auth,
|
|
fils_tx_associate_func_t assoc,
|
|
void *user_data)
|
|
{
|
|
struct fils_sm *fils;
|
|
|
|
fils = l_new(struct fils_sm, 1);
|
|
|
|
fils->auth = auth;
|
|
fils->assoc = assoc;
|
|
fils->user_data = user_data;
|
|
fils->hs = hs;
|
|
|
|
fils->ap.start = fils_start;
|
|
fils->ap.free = fils_free;
|
|
fils->ap.rx_authenticate = fils_rx_authenticate;
|
|
fils->ap.rx_associate = fils_rx_associate;
|
|
|
|
fils->erp = erp_new(hs->erp_cache, fils_erp_tx_func, fils);
|
|
|
|
return &fils->ap;
|
|
}
|