blob: 61a3a7e81105aaa59ea1d8741921cd0046507a48 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fuchsia/hardware/wlanif/c/banjo.h>
#include <fuchsia/wlan/ieee80211/cpp/fidl.h>
#include <zircon/errors.h>
#include "src/connectivity/wlan/drivers/testing/lib/sim-fake-ap/sim-fake-ap.h"
#include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/brcmu_wifi.h"
#include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/cfg80211.h"
#include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/fwil.h"
#include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/sim/sim.h"
#include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/sim/test/sim_test.h"
#include "src/connectivity/wlan/lib/common/cpp/include/wlan/common/macaddr.h"
namespace wlan::brcmfmac {
namespace wlan_ieee80211 = ::fuchsia::wlan::ieee80211;
constexpr wlan_channel_t kDefaultChannel = {
.primary = 9, .cbw = WLAN_CHANNEL_BANDWIDTH__20, .secondary80 = 0};
const common::MacAddr kDefaultBssid({0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc});
const common::MacAddr kWrongBssid({0x11, 0x22, 0x33, 0x44, 0x55, 0x66});
constexpr wlan_ssid_t kDefaultSsid = {.len = 15, .ssid = "Fuchsia Fake AP"};
constexpr uint8_t kIes[] = {
// SSID
0x00, 0x0f, 'F', 'u', 'c', 'h', 's', 'i', 'a', ' ', 'F', 'a', 'k', 'e', ' ', 'A', 'P',
// Supported rates
0x01, 0x08, 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c,
// DS parameter set - channel 157
0x03, 0x01, 0x9d,
// DTIM
0x05, 0x04, 0x00, 0x01, 0x00, 0x00,
// Power constraint
0x20, 0x01, 0x03,
// HT capabilities
0x2d, 0x1a, 0xef, 0x09, 0x1b, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// HT operation
0x3d, 0x16, 0x9d, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Overlapping BSS scan parameters
0x4a, 0x0e, 0x14, 0x00, 0x0a, 0x00, 0x2c, 0x01, 0xc8, 0x00, 0x14, 0x00, 0x05, 0x00, 0x19, 0x00,
// Extended capabilities
0x7f, 0x08, 0x01, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x40,
// VHT capabilities
0xbf, 0x0c, 0xb2, 0x01, 0x80, 0x33, 0xea, 0xff, 0x00, 0x00, 0xea, 0xff, 0x00, 0x00,
// VHT operation
0xc0, 0x05, 0x01, 0x9b, 0x00, 0xfc, 0xff,
// VHT Tx power envelope
0xc3, 0x04, 0x02, 0xc4, 0xc4, 0xc4,
// Vendor IE - WMM parameters
0xdd, 0x18, 0x00, 0x50, 0xf2, 0x02, 0x01, 0x01, 0x80, 0x00, 0x03, 0xa4, 0x00, 0x00, 0x27, 0xa4,
0x00, 0x00, 0x42, 0x43, 0x5e, 0x00, 0x62, 0x32, 0x2f, 0x00,
// Vendor IE - Atheros advanced capability
0xdd, 0x09, 0x00, 0x03, 0x7f, 0x01, 0x01, 0x00, 0x00, 0xff, 0x7f,
// RSN
0x30, 0x14, 0x01, 0x00, 0x00, 0x0f, 0xac, 0x04, 0x01, 0x00, 0x00, 0x0f, 0xac, 0x04, 0x01, 0x00,
0x00, 0x0f, 0xac, 0x02, 0x00, 0x00,
// Vendor IE - WPS
0xdd, 0x1d, 0x00, 0x50, 0xf2, 0x04, 0x10, 0x4a, 0x00, 0x01, 0x10, 0x10, 0x44, 0x00, 0x01, 0x02,
0x10, 0x3c, 0x00, 0x01, 0x03, 0x10, 0x49, 0x00, 0x06, 0x00, 0x37, 0x2a, 0x00, 0x01, 0x20};
const uint8_t test_key13[WLAN_MAX_KEY_LEN] = "testingwepkey";
const uint8_t test_key5[WLAN_MAX_KEY_LEN] = "wep40";
const uint32_t kDefaultKeyIndex = 0;
const size_t kWEP40KeyLen = 5;
const size_t kWEP104KeyLen = 13;
const size_t kCommitSaeFieldsLen = 6;
// Random bits represents fake sae_fields in SAE commit authentication frame.
const uint8_t kCommitSaeFields[] = {0xAA, 0xBC, 0xFD, 0x30, 0x68, 0x21};
const size_t kConfirmSaeFieldsLen = 7;
// Random bits represents fake sae_fields in SAE confirm authentication frame.
const uint8_t kConfirmSaeFields[] = {0xBC, 0xAA, 0x33, 0x65, 0x00, 0x88, 0x94};
constexpr zx::duration kTestDuration = zx::sec(50);
class AuthTest : public SimTest {
public:
AuthTest() : ap_(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel){};
// This enum is to trigger different workflow
enum SecurityType {
SEC_TYPE_WEP_SHARED40,
SEC_TYPE_WEP_SHARED104,
SEC_TYPE_WEP_OPEN,
SEC_TYPE_WPA1,
SEC_TYPE_WPA2,
SEC_TYPE_WPA3
};
enum SaeAuthState { COMMIT, CONFIRM, DONE };
struct AuthFrameContent {
AuthFrameContent(uint16_t seq_num, simulation::SimAuthType type,
wlan_ieee80211::StatusCode status)
: seq_num_(seq_num), type_(type), status_(status) {}
uint16_t seq_num_;
simulation::SimAuthType type_;
wlan_ieee80211::StatusCode status_;
};
void Init();
// Start the process of authentication
void StartAuth();
void VerifyAuthFrames();
void SecErrorInject();
// This is the interface we will use for our single client interface
SimInterface client_ifc_;
SimFirmware* sim_fw_;
uint32_t wsec_;
uint16_t auth_;
uint32_t wpa_auth_;
struct brcmf_wsec_key_le wsec_key_;
SecurityType sec_type_;
SaeAuthState sae_auth_state_ = COMMIT;
wlanif_sae_frame_t* sae_commit_frame = nullptr;
bool sae_ignore_confirm = false;
size_t auth_frame_count_ = 0;
simulation::FakeAp ap_;
wlan_join_result_t join_status_ = WLAN_JOIN_RESULT_SUCCESS;
wlan_auth_result_t auth_status_ = WLAN_AUTH_RESULT_SUCCESS;
wlan_assoc_result_t assoc_status_ = WLAN_ASSOC_RESULT_SUCCESS;
std::list<AuthFrameContent> rx_auth_frames_;
std::list<AuthFrameContent> expect_auth_frames_;
private:
// Stationifc overrides
void Rx(std::shared_ptr<const simulation::SimFrame> frame,
std::shared_ptr<const simulation::WlanRxInfo> info) override;
// SME callbacks
static wlanif_impl_ifc_protocol_ops_t sme_ops_;
wlanif_impl_ifc_protocol sme_protocol_ = {.ops = &sme_ops_, .ctx = this};
wlanif_set_keys_req CreateKeyReq(const uint8_t key[WLAN_MAX_KEY_LEN], const size_t key_count,
const uint32_t cipher_suite);
// Event handlers
void OnScanResult(const wlanif_scan_result_t* result);
void OnJoinConf(const wlanif_join_confirm_t* resp);
void OnAuthConf(const wlanif_auth_confirm_t* resp);
void OnAssocConf(const wlanif_assoc_confirm_t* resp);
void OnSaeHandshakeInd(const wlanif_sae_handshake_ind_t* ind);
void OnSaeFrameRx(const wlanif_sae_frame_t* frame);
};
void AuthTest::Rx(std::shared_ptr<const simulation::SimFrame> frame,
std::shared_ptr<const simulation::WlanRxInfo> info) {
ASSERT_EQ(frame->FrameType(), simulation::SimFrame::FRAME_TYPE_MGMT);
auto mgmt_frame = std::static_pointer_cast<const simulation::SimManagementFrame>(frame);
if (mgmt_frame->MgmtFrameType() != simulation::SimManagementFrame::FRAME_TYPE_AUTH) {
return;
}
auto auth_frame = std::static_pointer_cast<const simulation::SimAuthFrame>(mgmt_frame);
auth_frame_count_++;
rx_auth_frames_.emplace_back(auth_frame->seq_num_, auth_frame->auth_type_, auth_frame->status_);
}
wlanif_impl_ifc_protocol_ops_t AuthTest::sme_ops_ = {
.on_scan_result =
[](void* cookie, const wlanif_scan_result_t* result) {
static_cast<AuthTest*>(cookie)->OnScanResult(result);
},
.on_scan_end =
[](void* cookie, const wlanif_scan_end_t* end) {
// Ignore
},
.join_conf =
[](void* cookie, const wlanif_join_confirm_t* resp) {
static_cast<AuthTest*>(cookie)->OnJoinConf(resp);
},
.auth_conf =
[](void* cookie, const wlanif_auth_confirm_t* resp) {
static_cast<AuthTest*>(cookie)->OnAuthConf(resp);
},
.deauth_ind =
[](void* cookie, const wlanif_deauth_indication_t* ind) {
// Ignore
},
.assoc_conf =
[](void* cookie, const wlanif_assoc_confirm_t* resp) {
static_cast<AuthTest*>(cookie)->OnAssocConf(resp);
},
.signal_report = [](void* cookie, const wlanif_signal_report_indication* ind) {},
.sae_handshake_ind =
[](void* cookie, const wlanif_sae_handshake_ind_t* ind) {
static_cast<AuthTest*>(cookie)->OnSaeHandshakeInd(ind);
},
.sae_frame_rx =
[](void* cookie, const wlanif_sae_frame_t* frame) {
static_cast<AuthTest*>(cookie)->OnSaeFrameRx(frame);
},
};
void AuthTest::Init() {
ASSERT_EQ(SimTest::Init(), ZX_OK);
ASSERT_EQ(StartInterface(WLAN_INFO_MAC_ROLE_CLIENT, &client_ifc_, &sme_protocol_), ZX_OK);
sim_fw_ = device_->GetSim()->sim_fw.get();
ap_.EnableBeacon(zx::msec(100));
}
void AuthTest::VerifyAuthFrames() {
EXPECT_EQ(rx_auth_frames_.size(), expect_auth_frames_.size());
while (!expect_auth_frames_.empty()) {
ASSERT_EQ(rx_auth_frames_.empty(), false);
AuthFrameContent rx_content = rx_auth_frames_.front();
AuthFrameContent expect_content = expect_auth_frames_.front();
EXPECT_EQ(rx_content.seq_num_, expect_content.seq_num_);
EXPECT_EQ(rx_content.type_, expect_content.type_);
EXPECT_EQ(rx_content.status_, expect_content.status_);
rx_auth_frames_.pop_front();
expect_auth_frames_.pop_front();
}
}
void AuthTest::SecErrorInject() {
brcmf_simdev* sim = device_->GetSim();
sim->sim_fw->err_inj_.AddErrInjIovar("wsec", ZX_ERR_IO, BCME_OK, client_ifc_.iface_id_);
}
wlanif_set_keys_req AuthTest::CreateKeyReq(const uint8_t key[WLAN_MAX_KEY_LEN],
const size_t key_count, const uint32_t cipher_suite) {
set_key_descriptor key_des = {.key_list = key};
key_des.key_count = key_count;
key_des.key_id = kDefaultKeyIndex;
memcpy(key_des.address, kDefaultBssid.byte, ETH_ALEN);
key_des.cipher_suite_type = cipher_suite;
wlanif_set_keys_req set_keys_req = {};
set_keys_req.num_keys = 1;
set_keys_req.keylist[0] = key_des;
return set_keys_req;
}
void AuthTest::StartAuth() {
wlanif_join_req join_req = {};
memcpy(join_req.selected_bss.bssid, kDefaultBssid.byte, ETH_ALEN);
join_req.selected_bss.ies_bytes_list = kIes;
join_req.selected_bss.ies_bytes_count = sizeof(kIes);
join_req.selected_bss.chan = kDefaultChannel;
client_ifc_.if_impl_ops_->join_req(client_ifc_.if_impl_ctx_, &join_req);
}
void AuthTest::OnScanResult(const wlanif_scan_result_t* result) {
EXPECT_EQ(result->bss.cap, (uint16_t)32);
}
void AuthTest::OnJoinConf(const wlanif_join_confirm_t* resp) {
join_status_ = resp->result_code;
if (join_status_ != WLAN_JOIN_RESULT_SUCCESS) {
return;
}
brcmf_simdev* sim = device_->GetSim();
struct brcmf_if* ifp = brcmf_get_ifp(sim->drvr, client_ifc_.iface_id_);
zx_status_t status;
status = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec_);
EXPECT_EQ(status, ZX_OK);
EXPECT_EQ(wsec_, (uint32_t)0);
status = brcmf_fil_bsscfg_int_get(ifp, "wpa_auth", &wpa_auth_);
EXPECT_EQ(status, ZX_OK);
EXPECT_EQ(wpa_auth_, (uint32_t)0);
// Prepare auth request
wlanif_auth_req_t auth_req;
std::memcpy(auth_req.peer_sta_address, kDefaultBssid.byte, ETH_ALEN);
auth_req.auth_failure_timeout = 1000;
switch (sec_type_) {
case SEC_TYPE_WEP_SHARED104: {
wlanif_set_keys_req set_keys_req =
CreateKeyReq(&test_key13[0], kWEP104KeyLen, WPA_CIPHER_WEP_104);
client_ifc_.if_impl_ops_->set_keys_req(client_ifc_.if_impl_ctx_, &set_keys_req);
auth_req.auth_type = WLAN_AUTH_TYPE_SHARED_KEY;
break;
}
case SEC_TYPE_WEP_SHARED40: {
wlanif_set_keys_req set_keys_req =
CreateKeyReq(&test_key5[0], kWEP40KeyLen, WPA_CIPHER_WEP_40);
client_ifc_.if_impl_ops_->set_keys_req(client_ifc_.if_impl_ctx_, &set_keys_req);
auth_req.auth_type = WLAN_AUTH_TYPE_SHARED_KEY;
break;
}
case SEC_TYPE_WEP_OPEN: {
wlanif_set_keys_req set_keys_req =
CreateKeyReq(&test_key5[0], kWEP40KeyLen, WPA_CIPHER_WEP_40);
client_ifc_.if_impl_ops_->set_keys_req(client_ifc_.if_impl_ctx_, &set_keys_req);
auth_req.auth_type = WLAN_AUTH_TYPE_OPEN_SYSTEM;
break;
}
case SEC_TYPE_WPA1:
case SEC_TYPE_WPA2: {
auth_req.auth_type = WLAN_AUTH_TYPE_OPEN_SYSTEM;
break;
}
case SEC_TYPE_WPA3: {
auth_req.auth_type = WLAN_AUTH_TYPE_SAE;
break;
}
default:
return;
}
client_ifc_.if_impl_ops_->auth_req(client_ifc_.if_impl_ctx_, &auth_req);
}
void AuthTest::OnAuthConf(const wlanif_auth_confirm_t* resp) {
auth_status_ = resp->result_code;
if (auth_status_ != WLAN_AUTH_RESULT_SUCCESS) {
return;
}
brcmf_simdev* sim = device_->GetSim();
struct brcmf_if* ifp = brcmf_get_ifp(sim->drvr, client_ifc_.iface_id_);
zx_status_t status;
{
// Since auth_ is uint16_t, we need a uint32_t to get the iovar
uint32_t auth32;
status = brcmf_fil_bsscfg_int_get(ifp, "auth", &auth32);
auth_ = static_cast<uint16_t>(auth32);
EXPECT_EQ(status, ZX_OK);
}
status = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec_);
EXPECT_EQ(status, ZX_OK);
// The wsec_key iovar is only meaningful for WEP security
if (sec_type_ == SEC_TYPE_WEP_SHARED104 || sec_type_ == SEC_TYPE_WEP_SHARED40 ||
sec_type_ == SEC_TYPE_WEP_OPEN) {
status = brcmf_fil_bsscfg_data_get(ifp, "wsec_key", &wsec_key_, sizeof(wsec_key_));
EXPECT_EQ(status, ZX_OK);
EXPECT_EQ(wsec_key_.flags, (uint32_t)BRCMF_PRIMARY_KEY);
EXPECT_EQ(wsec_key_.index, kDefaultKeyIndex);
}
switch (sec_type_) {
case SEC_TYPE_WEP_SHARED104:
EXPECT_EQ(wsec_, (uint32_t)WEP_ENABLED);
EXPECT_EQ(auth_, (uint16_t)BRCMF_AUTH_MODE_AUTO);
EXPECT_EQ(wsec_key_.algo, (uint32_t)CRYPTO_ALGO_WEP128);
EXPECT_EQ(wsec_key_.len, kWEP104KeyLen);
EXPECT_EQ(memcmp(test_key13, wsec_key_.data, kWEP104KeyLen), 0);
break;
case SEC_TYPE_WEP_SHARED40:
EXPECT_EQ(wsec_, (uint32_t)WEP_ENABLED);
EXPECT_EQ(auth_, (uint16_t)BRCMF_AUTH_MODE_AUTO);
EXPECT_EQ(wsec_key_.algo, (uint32_t)CRYPTO_ALGO_WEP1);
EXPECT_EQ(wsec_key_.len, kWEP40KeyLen);
EXPECT_EQ(memcmp(test_key5, wsec_key_.data, kWEP40KeyLen), 0);
break;
case SEC_TYPE_WEP_OPEN:
EXPECT_EQ(wsec_, (uint32_t)WEP_ENABLED);
EXPECT_EQ(auth_, (uint16_t)BRCMF_AUTH_MODE_OPEN);
EXPECT_EQ(wsec_key_.algo, (uint32_t)CRYPTO_ALGO_WEP1);
EXPECT_EQ(wsec_key_.len, kWEP40KeyLen);
EXPECT_EQ(memcmp(test_key5, wsec_key_.data, kWEP40KeyLen), 0);
break;
case SEC_TYPE_WPA1:
case SEC_TYPE_WPA2:
// wsec iovar is not set before assoc_req sending to driver, now it should be default value.
EXPECT_EQ(wsec_, (uint32_t)WSEC_NONE);
EXPECT_EQ(auth_, (uint16_t)BRCMF_AUTH_MODE_OPEN);
break;
case SEC_TYPE_WPA3:
// wsec iovar is not set before assoc_req sending to driver, now it should be default value.
EXPECT_EQ(wsec_, (uint32_t)WSEC_NONE);
EXPECT_EQ(auth_, (uint16_t)BRCMF_AUTH_MODE_SAE);
break;
default:;
}
wlanif_assoc_req_t assoc_req = {.rsne_len = 0};
if (sec_type_ == SEC_TYPE_WEP_SHARED104 || sec_type_ == SEC_TYPE_WEP_SHARED40 ||
sec_type_ == SEC_TYPE_WEP_OPEN) {
assoc_req.vendor_ie_len = 0;
} else if (sec_type_ == SEC_TYPE_WPA1) {
// construct a fake vendor ie in wlanif_assoc_req.
uint16_t offset = 0;
uint8_t* ie = (uint8_t*)assoc_req.vendor_ie;
ie[offset++] = WLAN_IE_TYPE_VENDOR_SPECIFIC;
ie[offset++] = 22; // The length of following content.
memcpy(&ie[offset], MSFT_OUI, TLV_OUI_LEN);
offset += TLV_OUI_LEN;
ie[offset++] = WPA_OUI_TYPE;
// These two bytes are 16-bit version number.
ie[offset++] = 1; // Lower byte
ie[offset++] = 0; // Higher byte
memcpy(&ie[offset], MSFT_OUI, TLV_OUI_LEN);
offset += TLV_OUI_LEN;
ie[offset++] = WPA_CIPHER_TKIP; // Set multicast cipher suite.
// These two bytes indicate the length of unicast cipher list, in this case is 1.
ie[offset++] = 1; // Lower byte
ie[offset++] = 0; // Higher byte
memcpy(&ie[offset], MSFT_OUI, TLV_OUI_LEN);
offset += TLV_OUI_LEN; // The second WPA OUI.
ie[offset++] = WPA_CIPHER_CCMP_128; // Set unicast cipher suite.
// These two bytes indicate the length of auth management suite list, in this case is 1.
ie[offset++] = 1; // Lower byte
ie[offset++] = 0; // Higher byte
memcpy(&ie[offset], MSFT_OUI, TLV_OUI_LEN); // WPA OUI for auth management suite.
offset += TLV_OUI_LEN;
ie[offset++] = RSN_AKM_PSK; // Set auth management suite.
assoc_req.vendor_ie_len = offset;
ASSERT_EQ(assoc_req.vendor_ie_len, (const uint32_t)(ie[TLV_LEN_OFF] + TLV_HDR_LEN));
} else if (sec_type_ == SEC_TYPE_WPA2) {
// construct a fake rsne ie in wlanif_assoc_req.
uint16_t offset = 0;
uint8_t* ie = (uint8_t*)assoc_req.rsne;
ie[offset++] = WLAN_IE_TYPE_RSNE;
ie[offset++] = 20; // The length of following content.
// These two bytes are 16-bit version number.
ie[offset++] = 1; // Lower byte
ie[offset++] = 0; // Higher byte
memcpy(&ie[offset], RSN_OUI, TLV_OUI_LEN); // RSN OUI for multicast cipher suite.
offset += TLV_OUI_LEN;
ie[offset++] = WPA_CIPHER_TKIP; // Set multicast cipher suite.
// These two bytes indicate the length of unicast cipher list, in this case is 1.
ie[offset++] = 1; // Lower byte
ie[offset++] = 0; // Higher byte
memcpy(&ie[offset], RSN_OUI, TLV_OUI_LEN); // RSN OUI for unicast cipher suite.
offset += TLV_OUI_LEN;
ie[offset++] = WPA_CIPHER_CCMP_128; // Set unicast cipher suite.
// These two bytes indicate the length of auth management suite list, in this case is 1.
ie[offset++] = 1; // Lower byte
ie[offset++] = 0; // Higher byte
memcpy(&ie[offset], RSN_OUI, TLV_OUI_LEN); // RSN OUI for auth management suite.
offset += TLV_OUI_LEN;
ie[offset++] = RSN_AKM_PSK; // Set auth management suite.
// These two bytes indicate RSN capabilities, in this case is \x0c\x00.
ie[offset++] = 12; // Lower byte
ie[offset++] = 0; // Higher byte
assoc_req.rsne_len = offset;
ASSERT_EQ(assoc_req.rsne_len, (const uint32_t)(ie[TLV_LEN_OFF] + TLV_HDR_LEN));
} else if (sec_type_ == SEC_TYPE_WPA3) {
uint16_t offset = 0;
uint8_t* ie = (uint8_t*)assoc_req.rsne;
ie[offset++] = WLAN_IE_TYPE_RSNE;
ie[offset++] = 20; // The length of following content.
// These two bytes are 16-bit version number.
ie[offset++] = 1; // Lower byte
ie[offset++] = 0; // Higher byte
memcpy(&ie[offset], RSN_OUI, TLV_OUI_LEN); // RSN OUI for multicast cipher suite.
offset += TLV_OUI_LEN;
ie[offset++] = WPA_CIPHER_CCMP_128; // Set multicast cipher suite.
// These two bytes indicate the length of unicast cipher list, in this case is 1.
ie[offset++] = 1; // Lower byte
ie[offset++] = 0; // Higher byte
memcpy(&ie[offset], RSN_OUI, TLV_OUI_LEN); // RSN OUI for unicast cipher suite.
offset += TLV_OUI_LEN;
ie[offset++] = WPA_CIPHER_CCMP_128; // Set unicast cipher suite.
// These two bytes indicate the length of auth management suite list, in this case is 1.
ie[offset++] = 1; // Lower byte
ie[offset++] = 0; // Higher byte
memcpy(&ie[offset], RSN_OUI, TLV_OUI_LEN); // RSN OUI for auth management suite.
offset += TLV_OUI_LEN;
ie[offset++] = RSN_AKM_SAE_PSK; // Set auth management suite.
// These two bytes indicate RSN capabilities, in this case is \x0c\x00.
ie[offset++] = 12; // Lower byte
ie[offset++] = 0; // Higher byte
assoc_req.rsne_len = offset;
ASSERT_EQ(assoc_req.rsne_len, (const uint32_t)(ie[TLV_LEN_OFF] + TLV_HDR_LEN));
}
memcpy(assoc_req.peer_sta_address, kDefaultBssid.byte, ETH_ALEN);
client_ifc_.if_impl_ops_->assoc_req(client_ifc_.if_impl_ctx_, &assoc_req);
}
void AuthTest::OnAssocConf(const wlanif_assoc_confirm_t* resp) {
assoc_status_ = resp->result_code;
if (assoc_status_ != WLAN_ASSOC_RESULT_SUCCESS) {
return;
}
brcmf_simdev* sim = device_->GetSim();
struct brcmf_if* ifp = brcmf_get_ifp(sim->drvr, client_ifc_.iface_id_);
zx_status_t status;
status = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec_);
EXPECT_EQ(status, ZX_OK);
status = brcmf_fil_bsscfg_int_get(ifp, "wpa_auth", &wpa_auth_);
EXPECT_EQ(status, ZX_OK);
if (sec_type_ == SEC_TYPE_WPA1) {
// The wsec iovar is set after sending assoc_req to driver.
EXPECT_EQ(wsec_, (uint32_t)(TKIP_ENABLED | AES_ENABLED));
EXPECT_EQ(wpa_auth_, (uint32_t)WPA_AUTH_PSK);
}
if (sec_type_ == SEC_TYPE_WPA2) {
EXPECT_EQ(wsec_, (uint32_t)(TKIP_ENABLED | AES_ENABLED));
EXPECT_EQ(wpa_auth_, (uint32_t)WPA2_AUTH_PSK);
}
if (sec_type_ == SEC_TYPE_WPA3) {
EXPECT_EQ(wsec_, (uint32_t)(AES_ENABLED));
EXPECT_EQ(wpa_auth_, (uint32_t)WPA3_AUTH_SAE_PSK);
}
}
void AuthTest::OnSaeHandshakeInd(const wlanif_sae_handshake_ind_t* ind) {
common::MacAddr peer_sta_addr(ind->peer_sta_address);
ASSERT_EQ(peer_sta_addr, kDefaultBssid);
// Send the error injected commit frame instead if it exists.
if (sae_commit_frame != nullptr) {
client_ifc_.if_impl_ops_->sae_frame_tx(client_ifc_.if_impl_ctx_, sae_commit_frame);
return;
}
wlanif_sae_frame_t frame = {
.status_code = static_cast<uint16_t>(wlan_ieee80211::StatusCode::SUCCESS),
.seq_num = 1,
.sae_fields_list = kCommitSaeFields,
.sae_fields_count = kCommitSaeFieldsLen};
kDefaultBssid.CopyTo(frame.peer_sta_address);
client_ifc_.if_impl_ops_->sae_frame_tx(client_ifc_.if_impl_ctx_, &frame);
}
void AuthTest::OnSaeFrameRx(const wlanif_sae_frame_t* frame) {
common::MacAddr peer_sta_addr(frame->peer_sta_address);
ASSERT_EQ(peer_sta_addr, kDefaultBssid);
if (sae_auth_state_ == COMMIT) {
ASSERT_EQ(frame->seq_num, 1);
EXPECT_EQ(frame->status_code, static_cast<uint16_t>(wlan_ieee80211::StatusCode::SUCCESS));
EXPECT_EQ(frame->sae_fields_count, kCommitSaeFieldsLen);
EXPECT_EQ(memcmp(frame->sae_fields_list, kCommitSaeFields, kCommitSaeFieldsLen), 0);
wlanif_sae_frame_t next_frame = {
.status_code = static_cast<uint16_t>(wlan_ieee80211::StatusCode::SUCCESS),
.seq_num = 2,
.sae_fields_list = kConfirmSaeFields,
.sae_fields_count = kConfirmSaeFieldsLen};
kDefaultBssid.CopyTo(next_frame.peer_sta_address);
client_ifc_.if_impl_ops_->sae_frame_tx(client_ifc_.if_impl_ctx_, &next_frame);
sae_auth_state_ = CONFIRM;
} else if (sae_auth_state_ == CONFIRM) {
ASSERT_EQ(frame->seq_num, 2);
EXPECT_EQ(frame->status_code, static_cast<uint16_t>(wlan_ieee80211::StatusCode::SUCCESS));
EXPECT_EQ(frame->sae_fields_count, kConfirmSaeFieldsLen);
EXPECT_EQ(memcmp(frame->sae_fields_list, kConfirmSaeFields, kConfirmSaeFieldsLen), 0);
if (sae_ignore_confirm)
return;
wlanif_sae_handshake_resp_t resp = {
.status_code = static_cast<uint16_t>(wlan_ieee80211::StatusCode::SUCCESS)};
kDefaultBssid.CopyTo(resp.peer_sta_address);
client_ifc_.if_impl_ops_->sae_handshake_resp(client_ifc_.if_impl_ctx_, &resp);
sae_auth_state_ = DONE;
}
}
/*In the test part, we are actually testing two stages independently in each test case, for example
* in WEP104. The first stage is to test whether the iovars are correctly set into sim-fw, and
* whether the 13-byte key is consistent. The second stage is testing whether sim-fw is doing the
* right thing when we use SHARED KEY mode to do authencation with an AP in OPEN SYSTEM mode. If we
* change the first stage to WEP40, the second stage should have the same behaviour.
*/
TEST_F(AuthTest, WEP104) {
Init();
sec_type_ = SEC_TYPE_WEP_SHARED104;
ap_.SetSecurity({.auth_handling_mode = simulation::AUTH_TYPE_OPEN,
.sec_type = simulation::SEC_PROTO_TYPE_WEP});
env_->ScheduleNotification(std::bind(&AuthTest::StartAuth, this), zx::msec(10));
env_->Run(kTestDuration);
// The first SHARED KEY authentication request will fail, and switch to OPEN SYSTEM automatically.
// OPEN SYSTEM authentication will succeed.
expect_auth_frames_.emplace_back(1, simulation::AUTH_TYPE_SHARED_KEY,
wlan_ieee80211::StatusCode::SUCCESS);
expect_auth_frames_.emplace_back(2, simulation::AUTH_TYPE_SHARED_KEY,
wlan_ieee80211::StatusCode::REFUSED_REASON_UNSPECIFIED);
expect_auth_frames_.emplace_back(1, simulation::AUTH_TYPE_OPEN,
wlan_ieee80211::StatusCode::SUCCESS);
expect_auth_frames_.emplace_back(2, simulation::AUTH_TYPE_OPEN,
wlan_ieee80211::StatusCode::SUCCESS);
VerifyAuthFrames();
}
TEST_F(AuthTest, WEP40) {
Init();
sec_type_ = SEC_TYPE_WEP_SHARED40;
ap_.SetSecurity({.auth_handling_mode = simulation::AUTH_TYPE_SHARED_KEY,
.sec_type = simulation::SEC_PROTO_TYPE_WEP});
env_->ScheduleNotification(std::bind(&AuthTest::StartAuth, this), zx::msec(10));
env_->Run(kTestDuration);
// It should be a successful shared_key authentication
expect_auth_frames_.emplace_back(1, simulation::AUTH_TYPE_SHARED_KEY,
wlan_ieee80211::StatusCode::SUCCESS);
expect_auth_frames_.emplace_back(2, simulation::AUTH_TYPE_SHARED_KEY,
wlan_ieee80211::StatusCode::SUCCESS);
expect_auth_frames_.emplace_back(3, simulation::AUTH_TYPE_SHARED_KEY,
wlan_ieee80211::StatusCode::SUCCESS);
expect_auth_frames_.emplace_back(4, simulation::AUTH_TYPE_SHARED_KEY,
wlan_ieee80211::StatusCode::SUCCESS);
VerifyAuthFrames();
}
TEST_F(AuthTest, WEP40ChallengeFailure) {
Init();
sec_type_ = SEC_TYPE_WEP_SHARED40;
ap_.SetSecurity({.auth_handling_mode = simulation::AUTH_TYPE_SHARED_KEY,
.sec_type = simulation::SEC_PROTO_TYPE_WEP,
.expect_challenge_failure = true});
env_->ScheduleNotification(std::bind(&AuthTest::StartAuth, this), zx::msec(10));
env_->Run(kTestDuration);
// It should be a failed shared_key authentication
expect_auth_frames_.emplace_back(1, simulation::AUTH_TYPE_SHARED_KEY,
wlan_ieee80211::StatusCode::SUCCESS);
expect_auth_frames_.emplace_back(2, simulation::AUTH_TYPE_SHARED_KEY,
wlan_ieee80211::StatusCode::SUCCESS);
expect_auth_frames_.emplace_back(3, simulation::AUTH_TYPE_SHARED_KEY,
wlan_ieee80211::StatusCode::SUCCESS);
expect_auth_frames_.emplace_back(4, simulation::AUTH_TYPE_SHARED_KEY,
wlan_ieee80211::StatusCode::CHALLENGE_FAILURE);
VerifyAuthFrames();
// Assoc should have failed
EXPECT_NE(assoc_status_, WLAN_ASSOC_RESULT_SUCCESS);
}
TEST_F(AuthTest, WEPOPEN) {
Init();
sec_type_ = SEC_TYPE_WEP_OPEN;
ap_.SetSecurity({.auth_handling_mode = simulation::AUTH_TYPE_OPEN,
.sec_type = simulation::SEC_PROTO_TYPE_WEP});
env_->ScheduleNotification(std::bind(&AuthTest::StartAuth, this), zx::msec(10));
env_->Run(kTestDuration);
expect_auth_frames_.emplace_back(1, simulation::AUTH_TYPE_OPEN,
wlan_ieee80211::StatusCode::SUCCESS);
expect_auth_frames_.emplace_back(2, simulation::AUTH_TYPE_OPEN,
wlan_ieee80211::StatusCode::SUCCESS);
VerifyAuthFrames();
}
TEST_F(AuthTest, AuthFailTest) {
Init();
sec_type_ = SEC_TYPE_WEP_OPEN;
ap_.SetSecurity({.auth_handling_mode = simulation::AUTH_TYPE_OPEN,
.sec_type = simulation::SEC_PROTO_TYPE_OPEN});
brcmf_simdev* sim = device_->GetSim();
sim->sim_fw->err_inj_.AddErrInjIovar("auth", ZX_ERR_IO, BCME_OK, client_ifc_.iface_id_);
env_->ScheduleNotification(std::bind(&AuthTest::StartAuth, this), zx::msec(10));
env_->Run(kTestDuration);
EXPECT_NE(auth_status_, WLAN_AUTH_RESULT_SUCCESS);
}
TEST_F(AuthTest, WEPIgnoreTest) {
Init();
sec_type_ = SEC_TYPE_WEP_OPEN;
ap_.SetSecurity({.auth_handling_mode = simulation::AUTH_TYPE_OPEN,
.sec_type = simulation::SEC_PROTO_TYPE_WEP});
ap_.SetAssocHandling(simulation::FakeAp::ASSOC_IGNORED);
env_->ScheduleNotification(std::bind(&AuthTest::StartAuth, this), zx::msec(10));
env_->Run(kTestDuration);
// The auth request frames should be ignored and the auth timeout in sim-fw will be triggered,
// AuthHandleFailure() will retry for "max_retries" times and will send an BRCMF_E_SET_SSID event
// with status BRCMF_E_STATUS_FAIL finally.
uint32_t max_retries = 0;
brcmf_simdev* sim = device_->GetSim();
struct brcmf_if* ifp = brcmf_get_ifp(sim->drvr, client_ifc_.iface_id_);
zx_status_t status = brcmf_fil_iovar_int_get(ifp, "assoc_retry_max", &max_retries, nullptr);
EXPECT_EQ(status, ZX_OK);
for (uint32_t i = 0; i < max_retries + 1; i++) {
expect_auth_frames_.emplace_back(1, simulation::AUTH_TYPE_OPEN,
wlan_ieee80211::StatusCode::SUCCESS);
}
VerifyAuthFrames();
EXPECT_EQ(assoc_status_, WLAN_ASSOC_RESULT_REFUSED_REASON_UNSPECIFIED);
}
TEST_F(AuthTest, WPA1Test) {
Init();
sec_type_ = SEC_TYPE_WPA1;
ap_.SetSecurity({.auth_handling_mode = simulation::AUTH_TYPE_OPEN,
.sec_type = simulation::SEC_PROTO_TYPE_WPA1});
env_->ScheduleNotification(std::bind(&AuthTest::StartAuth, this), zx::msec(10));
env_->Run(kTestDuration);
expect_auth_frames_.emplace_back(1, simulation::AUTH_TYPE_OPEN,
wlan_ieee80211::StatusCode::SUCCESS);
expect_auth_frames_.emplace_back(2, simulation::AUTH_TYPE_OPEN,
wlan_ieee80211::StatusCode::SUCCESS);
VerifyAuthFrames();
// Make sure that OnAssocConf is called, so the check inside is called.
EXPECT_EQ(assoc_status_, WLAN_ASSOC_RESULT_SUCCESS);
}
TEST_F(AuthTest, WPA1FailTest) {
Init();
sec_type_ = SEC_TYPE_WPA1;
ap_.SetSecurity({.auth_handling_mode = simulation::AUTH_TYPE_OPEN,
.sec_type = simulation::SEC_PROTO_TYPE_WPA1});
brcmf_simdev* sim = device_->GetSim();
sim->sim_fw->err_inj_.AddErrInjIovar("wpaie", ZX_ERR_IO, BCME_OK, client_ifc_.iface_id_);
env_->ScheduleNotification(std::bind(&AuthTest::StartAuth, this), zx::msec(10));
env_->Run(kTestDuration);
// Assoc should have failed
EXPECT_NE(assoc_status_, WLAN_ASSOC_RESULT_SUCCESS);
}
TEST_F(AuthTest, WPA2Test) {
Init();
sec_type_ = SEC_TYPE_WPA2;
ap_.SetSecurity({.auth_handling_mode = simulation::AUTH_TYPE_OPEN,
.sec_type = simulation::SEC_PROTO_TYPE_WPA2});
env_->ScheduleNotification(std::bind(&AuthTest::StartAuth, this), zx::msec(10));
env_->Run(kTestDuration);
expect_auth_frames_.emplace_back(1, simulation::AUTH_TYPE_OPEN,
wlan_ieee80211::StatusCode::SUCCESS);
expect_auth_frames_.emplace_back(2, simulation::AUTH_TYPE_OPEN,
wlan_ieee80211::StatusCode::SUCCESS);
VerifyAuthFrames();
// Make sure that OnAssocConf is called, so the check inside is called.
EXPECT_EQ(assoc_status_, WLAN_ASSOC_RESULT_SUCCESS);
}
TEST_F(AuthTest, WPA2FailTest) {
Init();
sec_type_ = SEC_TYPE_WPA2;
ap_.SetSecurity({.auth_handling_mode = simulation::AUTH_TYPE_OPEN,
.sec_type = simulation::SEC_PROTO_TYPE_WPA2});
SecErrorInject();
env_->ScheduleNotification(std::bind(&AuthTest::StartAuth, this), zx::msec(10));
env_->Run(kTestDuration);
// Make sure that OnAssocConf is called, so the check inside is called.
EXPECT_NE(join_status_, WLAN_JOIN_RESULT_SUCCESS);
}
// This test case verifies that auth req will be refused when security types of client and AP are
// not matched.
TEST_F(AuthTest, WrongSecTypeAuthFail) {
Init();
// Client sec type is WPA1 while AP's is WEP.
sec_type_ = SEC_TYPE_WPA1;
ap_.SetSecurity({.auth_handling_mode = simulation::AUTH_TYPE_OPEN,
.sec_type = simulation::SEC_PROTO_TYPE_WEP});
env_->ScheduleNotification(std::bind(&AuthTest::StartAuth, this), zx::msec(10));
env_->Run(kTestDuration);
uint32_t max_retries = 0;
brcmf_simdev* sim = device_->GetSim();
struct brcmf_if* ifp = brcmf_get_ifp(sim->drvr, client_ifc_.iface_id_);
zx_status_t status = brcmf_fil_iovar_int_get(ifp, "assoc_retry_max", &max_retries, nullptr);
EXPECT_EQ(status, ZX_OK);
for (uint32_t i = 0; i < max_retries + 1; i++) {
expect_auth_frames_.emplace_back(1, simulation::AUTH_TYPE_OPEN,
wlan_ieee80211::StatusCode::SUCCESS);
expect_auth_frames_.emplace_back(2, simulation::AUTH_TYPE_OPEN,
wlan_ieee80211::StatusCode::REFUSED_REASON_UNSPECIFIED);
}
VerifyAuthFrames();
EXPECT_EQ(assoc_status_, WLAN_ASSOC_RESULT_REFUSED_REASON_UNSPECIFIED);
}
// Verify a normal SAE authentication work flow in driver.
TEST_F(AuthTest, WPA3Test) {
Init();
sec_type_ = SEC_TYPE_WPA3;
ap_.SetSecurity({.auth_handling_mode = simulation::AUTH_TYPE_SAE,
.sec_type = simulation::SEC_PROTO_TYPE_WPA3});
env_->ScheduleNotification(std::bind(&AuthTest::StartAuth, this), zx::msec(10));
env_->Run(kTestDuration);
expect_auth_frames_.emplace_back(1, simulation::AUTH_TYPE_SAE,
wlan_ieee80211::StatusCode::SUCCESS);
expect_auth_frames_.emplace_back(1, simulation::AUTH_TYPE_SAE,
wlan_ieee80211::StatusCode::SUCCESS);
expect_auth_frames_.emplace_back(2, simulation::AUTH_TYPE_SAE,
wlan_ieee80211::StatusCode::SUCCESS);
expect_auth_frames_.emplace_back(2, simulation::AUTH_TYPE_SAE,
wlan_ieee80211::StatusCode::SUCCESS);
VerifyAuthFrames();
// Make sure that OnAssocConf is called, so the check inside is called.
EXPECT_EQ(assoc_status_, WLAN_ASSOC_RESULT_SUCCESS);
EXPECT_EQ(sae_auth_state_, DONE);
}
// Verify that firmware will timeout if AP ignores the SAE auth frame.
TEST_F(AuthTest, WPA3ApIgnoreTest) {
Init();
sec_type_ = SEC_TYPE_WPA3;
ap_.SetSecurity({.auth_handling_mode = simulation::AUTH_TYPE_SAE,
.sec_type = simulation::SEC_PROTO_TYPE_WPA3});
ap_.SetAssocHandling(simulation::FakeAp::ASSOC_IGNORED);
env_->ScheduleNotification(std::bind(&AuthTest::StartAuth, this), zx::msec(10));
env_->Run(kTestDuration);
// Make sure firmware will not retry for external supplicant authentication.
expect_auth_frames_.emplace_back(1, simulation::AUTH_TYPE_SAE,
wlan_ieee80211::StatusCode::SUCCESS);
VerifyAuthFrames();
EXPECT_EQ(assoc_status_, WLAN_ASSOC_RESULT_REFUSED_REASON_UNSPECIFIED);
EXPECT_EQ(sae_auth_state_, COMMIT);
}
// Verify that firmware will timeout if external supplicant ignore the final SAE auth frame and fail
// to send out the handshake response.
TEST_F(AuthTest, WPA3SupplicantIgnoreTest) {
Init();
sec_type_ = SEC_TYPE_WPA3;
ap_.SetSecurity({.auth_handling_mode = simulation::AUTH_TYPE_SAE,
.sec_type = simulation::SEC_PROTO_TYPE_WPA3});
sae_ignore_confirm = true;
env_->ScheduleNotification(std::bind(&AuthTest::StartAuth, this), zx::msec(10));
env_->Run(kTestDuration);
// Make sure firmware will not retry for external supplicant authentication.
expect_auth_frames_.emplace_back(1, simulation::AUTH_TYPE_SAE,
wlan_ieee80211::StatusCode::SUCCESS);
expect_auth_frames_.emplace_back(1, simulation::AUTH_TYPE_SAE,
wlan_ieee80211::StatusCode::SUCCESS);
expect_auth_frames_.emplace_back(2, simulation::AUTH_TYPE_SAE,
wlan_ieee80211::StatusCode::SUCCESS);
expect_auth_frames_.emplace_back(2, simulation::AUTH_TYPE_SAE,
wlan_ieee80211::StatusCode::SUCCESS);
VerifyAuthFrames();
EXPECT_EQ(assoc_status_, WLAN_ASSOC_RESULT_REFUSED_REASON_UNSPECIFIED);
EXPECT_EQ(sae_auth_state_, CONFIRM);
}
// Verify that the AP will not reply to the sae frame with non-success status code, firmware will
// timeout.
TEST_F(AuthTest, WPA3FailStatusCode) {
Init();
sec_type_ = SEC_TYPE_WPA3;
ap_.SetSecurity({.auth_handling_mode = simulation::AUTH_TYPE_SAE,
.sec_type = simulation::SEC_PROTO_TYPE_WPA3});
ap_.SetAssocHandling(simulation::FakeAp::ASSOC_IGNORED);
wlanif_sae_frame_t frame = {
.status_code = static_cast<uint16_t>(wlan_ieee80211::StatusCode::REFUSED_REASON_UNSPECIFIED),
.seq_num = 1,
.sae_fields_list = kCommitSaeFields,
.sae_fields_count = kCommitSaeFieldsLen};
kDefaultBssid.CopyTo(frame.peer_sta_address);
sae_commit_frame = &frame;
env_->ScheduleNotification(std::bind(&AuthTest::StartAuth, this), zx::msec(10));
env_->Run(kTestDuration);
// Make sure firmware will not retry for external supplicant authentication.
expect_auth_frames_.emplace_back(1, simulation::AUTH_TYPE_SAE,
wlan_ieee80211::StatusCode::REFUSED_REASON_UNSPECIFIED);
VerifyAuthFrames();
EXPECT_EQ(assoc_status_, WLAN_ASSOC_RESULT_REFUSED_REASON_UNSPECIFIED);
EXPECT_EQ(sae_auth_state_, COMMIT);
}
// Verify that the firmware will timeout if the bssid in SAE auth frame is wrong, because SAE status
// will not be updated.
TEST_F(AuthTest, WPA3WrongBssid) {
Init();
sec_type_ = SEC_TYPE_WPA3;
ap_.SetSecurity({.auth_handling_mode = simulation::AUTH_TYPE_SAE,
.sec_type = simulation::SEC_PROTO_TYPE_WPA3});
ap_.SetAssocHandling(simulation::FakeAp::ASSOC_IGNORED);
wlanif_sae_frame_t frame = {
.status_code = static_cast<uint16_t>(wlan_ieee80211::StatusCode::SUCCESS),
.seq_num = 1,
.sae_fields_list = kCommitSaeFields,
.sae_fields_count = kCommitSaeFieldsLen};
// Use wrong bssid.
kWrongBssid.CopyTo(frame.peer_sta_address);
sae_commit_frame = &frame;
env_->ScheduleNotification(std::bind(&AuthTest::StartAuth, this), zx::msec(10));
env_->Run(kTestDuration);
// No auth frame will be sent out.
VerifyAuthFrames();
EXPECT_EQ(assoc_status_, WLAN_ASSOC_RESULT_REFUSED_REASON_UNSPECIFIED);
EXPECT_EQ(sae_auth_state_, COMMIT);
}
} // namespace wlan::brcmfmac