diff options
author | Vamsi Krishna <quic_vamsin@quicinc.com> | 2019-12-13 11:02:27 +0530 |
---|---|---|
committer | Sunil Ravi <sunilravi@google.com> | 2020-02-20 15:04:34 -0800 |
commit | 853857e57a4595ab67345fc8a198bb7b06e2aa0b (patch) | |
tree | 1d59c3027896e72544118862253cd3d1f3a6136f /tests | |
parent | 40369495ec8f3fc06f655d3dd2ab07ae4844c130 (diff) |
STA: Add support to connect to FILS enabled APs
Fast Initial Link Setup (FILS) feature defined in 802.11ai specifications
provides means to make faster reconnections with APs within a realm using
EAP-Reauthentication Protocol (ERP). When a STA connecting initially to an
enterprise AP on which FILS is enabled, does full 802.1x EAP handshakes and
generates ERP keys. The STA uses the ERP keys (until they are expired) in
subsequent connections to the enterprise APs within same realm as that of
the AP with which the STA has generated ERP keys to EAP-ERP process to
make connection faster. The EAP-ERP request and EAP-ERP response frames
will be exchanged via 802.11 authentication frames between STA and AP.
More details of EAP-ERP can be found at https://tools.ietf.org/html/rfc6696
FILS specifications defines two AKM suites FILS-SHA256 and FILS-SHA384 for
normal connection and two more AKM suites FILS-FT-SHA256 and FILS-FT-SHA384
for FT connections.
The first connection without using ERP keys uses open auth algorithm and
connects using full eap handshake and the subsequent connections with ERP
keys uses FILS auth algorithm.
DHCP with rapid commit optimizes the DHCP packet exchange. When using rapid
commit feature, DHCP server responds with DHCP ACK packet when it receives
DHCP discover packet. More details of DHCP with rapid commit can be found
at https://tools.ietf.org/html/rfc4039.
The DHCP discover and DHCP ACK packets can be sent with Association
request frame and Association response frame using HLP feature defined in
FILS protocol in order to get the IP address quickly.
When connecting to a FILS AP, start the DHCP client withPreConnection
option set to true before sending connect request to wpa_supplicant. DHCP
client will form discover packet and sends it to Wi-Fi module, then the
DHCP client moves to listen state to be able to receive and process
DHCP ACK or DHCP Offer packets. After receiving DHCP discover packet
from DHCP client, configure DHCP discover packet as HLP packet to
wpa_supplicant and then issue connect request to wpa_supplicant with
appropriate FILS AKM suite. The ERP config option also needs to be enabled
so that wpa_supplicant generates ERP keys while connecting to FILS APs.
If the STA is already connected to a different AP when wpa_supplicant
receives the connect request to the FILS AP, wpa_supplicant disconnects
with that AP before connecting to FILS AP and sends a disconnection event
to frameworks. Do not stop IpClient when disconnection event is received
if FILS connection is in progress, as the IpClient is started for ongoing
FILS connection.
If STA does not have ERP keys required for FILS connection or if the AP is
rejecting the connection in FILS mode probably because of AP not having ERP
keys, then STA will connect to FILS AP with open auth algorithm using full
802.1x EAP handshake. In these cases wpa_supplicant doesn't include HLP IEs
in connect request. Wpa_supplicant notifies framework through connection
event whether it sent HLP IEs to AP or not in the connection request. When
HLP IEs are not sent in association request frame, abort pre-DHCP
connection. When pre-DHCP connection is aborted, DHCP client transitions to
dhcpInit state and sends DHCP discover packet to AP through data path.
If STA has ERP keys, it will send the DHCP discover packet using HLP IEs
in Association Request frame. AP may respond in three ways.
1. AP may not send any DHCP packet uisng HLP IEs in Association Response
frame if the DHCP server doesn't send response within a time less than
802.11 mgmt. timeout. In this case, AP sends the DHCP packet as a data
packet after sending out Association Response frame.
2. AP may send a DHCP ACK packet using HLP IEs in Association Response
frame if the DHCP server supports DHCP with Rapid Commit feature and
sends response within a time less than 802.11 mgmt. timeout.
3. AP may send a DHCP OFFER packet using HLP IEs in Association Response
frame if the DHCP server doesn't support DHCP with Rapid Commit feature and
sends response within a time less than 802.11 mgmt. timeout.
Bug: 143259898
Test: atest.
Test: Basic wifi sanity.
Change-Id: Ib42eb5295fa2403d5a98ceddb15f501c130b61e5
Diffstat (limited to 'tests')
7 files changed, 654 insertions, 11 deletions
diff --git a/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java b/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java index c11f82fc6..d45f68f50 100644 --- a/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java +++ b/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java @@ -60,6 +60,7 @@ import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback; import android.net.ConnectivityManager; import android.net.DhcpResultsParcelable; import android.net.InetAddresses; +import android.net.Layer2PacketParcelable; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.MacAddress; @@ -150,6 +151,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.concurrent.CountDownLatch; @@ -341,6 +343,7 @@ public class ClientModeImplTest extends WifiBaseTest { static final int sFreq = 2437; static final int sFreq1 = 5240; static final String WIFI_IFACE_NAME = "mockWlan"; + static final String sFilsSsid = "FILS-AP"; ClientModeImpl mCmi; HandlerThread mWifiCoreThread; @@ -2688,10 +2691,9 @@ public class ClientModeImplTest extends WifiBaseTest { when(mWifiNative.getMacAddress(WIFI_IFACE_NAME)) .thenReturn(TEST_LOCAL_MAC_ADDRESS.toString()); - WifiConfiguration config = mock(WifiConfiguration.class); + WifiConfiguration config = new WifiConfiguration(); config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE; - when(config.getNetworkSelectionStatus()) - .thenReturn(new WifiConfiguration.NetworkSelectionStatus()); + config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(0)).thenReturn(config); mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); @@ -2718,10 +2720,9 @@ public class ClientModeImplTest extends WifiBaseTest { assertEquals(ClientModeImpl.CONNECT_MODE, mCmi.getOperationalModeForTest()); assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); - WifiConfiguration config = mock(WifiConfiguration.class); + WifiConfiguration config = new WifiConfiguration(); + config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE; - when(config.getNetworkSelectionStatus()) - .thenReturn(new WifiConfiguration.NetworkSelectionStatus()); when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(0)).thenReturn(config); mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); @@ -2765,11 +2766,10 @@ public class ClientModeImplTest extends WifiBaseTest { assertEquals(ClientModeImpl.CONNECT_MODE, mCmi.getOperationalModeForTest()); assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); - WifiConfiguration config = mock(WifiConfiguration.class); + WifiConfiguration config = new WifiConfiguration(); + config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT; config.setRandomizedMacAddress(MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS)); - when(config.getNetworkSelectionStatus()) - .thenReturn(new WifiConfiguration.NetworkSelectionStatus()); when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(0)).thenReturn(config); mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); @@ -3203,7 +3203,7 @@ public class ClientModeImplTest extends WifiBaseTest { disconnect(); mLooper.dispatchAll(); - verify(mWifiScoreCard).resetConnectionState(); + verify(mWifiScoreCard, times(1)).resetConnectionState(); verify(mWifiScoreCard, never()).noteWifiDisabled(any()); verify(mWifiHealthMonitor, never()).setWifiEnabled(false); @@ -3211,7 +3211,7 @@ public class ClientModeImplTest extends WifiBaseTest { mCmi.setWifiStateForApiCalls(WifiManager.WIFI_STATE_DISABLED); mCmi.setOperationalMode(ClientModeImpl.DISABLED_MODE, null); mLooper.dispatchAll(); - verify(mWifiScoreCard).resetConnectionState(); + verify(mWifiScoreCard, times(2)).resetConnectionState(); verify(mWifiHealthMonitor).setWifiEnabled(false); } @@ -4509,4 +4509,202 @@ public class ClientModeImplTest extends WifiBaseTest { assertTrue(mCmi.isWifiBandSupported(WifiScanner.WIFI_BAND_6_GHZ)); verify(mWifiNative).getChannelsForBand(WifiScanner.WIFI_BAND_6_GHZ); } + + /** + * Helper function for setting up fils test. + * + * @param isDriverSupportFils true if driver support fils. + * @return wifi configuration. + */ + private WifiConfiguration setupFilsTest(boolean isDriverSupportFils) { + assertEquals(ClientModeImpl.CONNECT_MODE, mCmi.getOperationalModeForTest()); + assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); + + WifiConfiguration config = new WifiConfiguration(); + config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP); + config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); + config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X); + config.SSID = ScanResultUtil.createQuotedSSID(sFilsSsid); + config.networkId = 1; + config.setRandomizedMacAddress(TEST_LOCAL_MAC_ADDRESS); + config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT; + + when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(anyInt())).thenReturn(config); + if (isDriverSupportFils) { + when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( + WifiManager.WIFI_FEATURE_FILS_SHA256 | WifiManager.WIFI_FEATURE_FILS_SHA384); + } else { + when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn((long) 0); + } + + return config; + } + + /** + * Helper function for setting up a scan result with FILS supported AP. + * + */ + private void setupFilsEnabledApInScanResult() { + String caps = "[WPA2-EAP+EAP-SHA256+FILS-SHA256-CCMP]" + + "[RSN-EAP+EAP-SHA256+FILS-SHA256-CCMP][ESS]"; + ScanResult scanResult = new ScanResult(WifiSsid.createFromAsciiEncoded(sFilsSsid), + sFilsSsid, sBSSID, 1245, 0, caps, -78, 2412, 1025, 22, 33, 20, 0, 0, true); + ScanResult.InformationElement ie = createIE(ScanResult.InformationElement.EID_SSID, + sFilsSsid.getBytes(StandardCharsets.UTF_8)); + scanResult.informationElements = new ScanResult.InformationElement[]{ie}; + List<ScanResult> scanResults = new ArrayList<>(); + scanResults.add(scanResult); + + when(mScanRequestProxy.getScanResults()).thenReturn(scanResults); + } + + + /** + * Helper function to send CMD_START_FILS_CONNECTION along with HLP IEs. + * + */ + private void prepareFilsHlpPktAndSendStartConnect() { + Layer2PacketParcelable l2Packet = new Layer2PacketParcelable(); + l2Packet.dstMacAddress = TEST_GLOBAL_MAC_ADDRESS; + l2Packet.payload = new byte[] {0x00, 0x12, 0x13, 0x00, 0x12, 0x13, 0x00, 0x12, 0x13, + 0x12, 0x13, 0x00, 0x12, 0x13, 0x00, 0x12, 0x13, 0x00, 0x12, 0x13, 0x55, 0x66}; + mCmi.sendMessage(ClientModeImpl.CMD_START_FILS_CONNECTION, 0, 0, + Collections.singletonList(l2Packet)); + mLooper.dispatchAll(); + } + + /** + * Verifies that while connecting to AP, the logic looks into the scan result and + * looks for AP matching the network type and ssid and update the wificonfig with FILS + * AKM if supported. + * + * @throws Exception + */ + @Test + public void testFilsAKMUpdateBeforeConnect() throws Exception { + initializeAndAddNetworkAndVerifySuccess(); + WifiConfiguration config = setupFilsTest(true); + setupFilsEnabledApInScanResult(); + + mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); + mLooper.dispatchAll(); + + assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.FILS_SHA256)); + verify(mWifiNative, never()).connectToNetwork(eq(WIFI_IFACE_NAME), eq(config)); + } + + /** + * Verifies that while connecting to AP, framework updates the wifi config with + * FILS AKM only if underlying driver support FILS feature. + * + * @throws Exception + */ + @Test + public void testFilsAkmIsNotAddedinWifiConfigIfDriverDoesNotSupportFils() throws Exception { + initializeAndAddNetworkAndVerifySuccess(); + WifiConfiguration config = setupFilsTest(false); + setupFilsEnabledApInScanResult(); + + mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); + mLooper.dispatchAll(); + + assertFalse(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.FILS_SHA256)); + verify(mWifiNative).connectToNetwork(eq(WIFI_IFACE_NAME), eq(config)); + } + + + /** + * Verifies that the HLP (DHCP) packets are send to wpa_supplicant + * prior to Fils connection. + * + * @throws Exception + */ + @Test + public void testFilsHlpUpdateBeforeFilsConnection() throws Exception { + initializeAndAddNetworkAndVerifySuccess(); + WifiConfiguration config = setupFilsTest(true); + setupFilsEnabledApInScanResult(); + + mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); + mLooper.dispatchAll(); + + prepareFilsHlpPktAndSendStartConnect(); + + verify(mWifiNative).flushAllHlp(eq(WIFI_IFACE_NAME)); + verify(mWifiNative).addHlpReq(eq(WIFI_IFACE_NAME), any(), any()); + verify(mWifiNative).connectToNetwork(eq(WIFI_IFACE_NAME), eq(config)); + } + + /** + * Verifies that an association rejection in first FILS connect attempt doesn't block + * the second connection attempt. + * + * @throws Exception + */ + @Test + public void testFilsSecondConnectAttemptIsNotBLockedAfterAssocReject() throws Exception { + initializeAndAddNetworkAndVerifySuccess(); + WifiConfiguration config = setupFilsTest(true); + setupFilsEnabledApInScanResult(); + + mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); + mLooper.dispatchAll(); + + prepareFilsHlpPktAndSendStartConnect(); + + verify(mWifiNative, times(1)).connectToNetwork(eq(WIFI_IFACE_NAME), eq(config)); + + mCmi.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT, 0, 2, sBSSID); + mLooper.dispatchAll(); + + mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); + mLooper.dispatchAll(); + prepareFilsHlpPktAndSendStartConnect(); + + verify(mWifiNative, times(2)).connectToNetwork(eq(WIFI_IFACE_NAME), eq(config)); + } + + /** + * Verifies Fils connection. + * + * @throws Exception + */ + @Test + public void testFilsConnection() throws Exception { + initializeAndAddNetworkAndVerifySuccess(); + WifiConfiguration config = setupFilsTest(true); + setupFilsEnabledApInScanResult(); + + mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); + mLooper.dispatchAll(); + + prepareFilsHlpPktAndSendStartConnect(); + + mCmi.sendMessage(WifiMonitor.FILS_NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); + mLooper.dispatchAll(); + + mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, + new StateChangeResult(0, WifiSsid.createFromAsciiEncoded(sFilsSsid), + sBSSID, SupplicantState.COMPLETED)); + mLooper.dispatchAll(); + + assertEquals("ObtainingIpState", getCurrentState().getName()); + + DhcpResultsParcelable dhcpResults = new DhcpResultsParcelable(); + dhcpResults.baseConfiguration = new StaticIpConfiguration(); + dhcpResults.baseConfiguration.gateway = InetAddresses.parseNumericAddress("1.2.3.4"); + dhcpResults.baseConfiguration.ipAddress = + new LinkAddress(InetAddresses.parseNumericAddress("192.168.1.100"), 0); + dhcpResults.baseConfiguration.dnsServers.add(InetAddresses.parseNumericAddress("8.8.8.8")); + dhcpResults.leaseDuration = 3600; + + injectDhcpSuccess(dhcpResults); + mLooper.dispatchAll(); + + WifiInfo wifiInfo = mCmi.getWifiInfo(); + assertNotNull(wifiInfo); + assertEquals(sBSSID, wifiInfo.getBSSID()); + assertTrue(WifiSsid.createFromAsciiEncoded(sFilsSsid).equals(wifiInfo.getWifiSsid())); + assertEquals("ConnectedState", getCurrentState().getName()); + } } diff --git a/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java b/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java index 2278d0dfe..64c6e0794 100644 --- a/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java +++ b/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java @@ -16,6 +16,8 @@ package com.android.server.wifi; import static android.net.wifi.WifiManager.WIFI_FEATURE_DPP; +import static android.net.wifi.WifiManager.WIFI_FEATURE_FILS_SHA256; +import static android.net.wifi.WifiManager.WIFI_FEATURE_FILS_SHA384; import static android.net.wifi.WifiManager.WIFI_FEATURE_MBO; import static android.net.wifi.WifiManager.WIFI_FEATURE_OCE; import static android.net.wifi.WifiManager.WIFI_FEATURE_OWE; @@ -1673,6 +1675,44 @@ public class SupplicantStaIfaceHalTest extends WifiBaseTest { } /** + * Test FILS SHA256 key management support. + */ + @Test + public void testGetKeyMgmtCapabilitiesFilsSha256() throws Exception { + setupMocksForHalV1_3(); + + executeAndValidateInitializationSequenceV1_3(); + + doAnswer(new GetKeyMgmtCapabilities_1_3Answer(android.hardware.wifi.supplicant.V1_3 + .ISupplicantStaNetwork.KeyMgmtMask.FILS_SHA256)) + .when(mISupplicantStaIfaceMockV13).getKeyMgmtCapabilities_1_3(any( + android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface + .getKeyMgmtCapabilities_1_3Callback.class)); + + assertEquals(WIFI_FEATURE_FILS_SHA256, + mDut.getAdvancedKeyMgmtCapabilities(WLAN0_IFACE_NAME)); + } + + /** + * Test FILS SHA384 key management support. + */ + @Test + public void testGetKeyMgmtCapabilitiesFilsSha384() throws Exception { + setupMocksForHalV1_3(); + + executeAndValidateInitializationSequenceV1_3(); + + doAnswer(new GetKeyMgmtCapabilities_1_3Answer(android.hardware.wifi.supplicant.V1_3 + .ISupplicantStaNetwork.KeyMgmtMask.FILS_SHA384)) + .when(mISupplicantStaIfaceMockV13).getKeyMgmtCapabilities_1_3(any( + android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface + .getKeyMgmtCapabilities_1_3Callback.class)); + + assertEquals(WIFI_FEATURE_FILS_SHA384, + mDut.getAdvancedKeyMgmtCapabilities(WLAN0_IFACE_NAME)); + } + + /** * Test Easy Connect (DPP) calls return failure if hal version is less than 1_2 */ @Test @@ -2534,4 +2574,199 @@ public class SupplicantStaIfaceHalTest extends WifiBaseTest { eq(WLAN0_IFACE_NAME), btmFrameDataCaptor.capture()); } + /** + * Tests the configuring of FILS HLP packet in supplicant. + */ + @Test + public void testAddHlpReq() throws Exception { + byte[] dstAddr = {0x45, 0x23, 0x12, 0x12, 0x12, 0x45}; + byte[] hlpPacket = {0x00, 0x01, 0x02, 0x03, 0x04, 0x12, 0x15, 0x34, 0x55, 0x12, + 0x12, 0x45, 0x23, 0x52, 0x32, 0x16, 0x15, 0x53, 0x62, 0x32, 0x32, 0x10}; + + setupMocksForHalV1_3(); + when(mISupplicantStaIfaceMockV13.filsHlpAddRequest(any(byte[].class), + any(ArrayList.class))).thenReturn(mStatusSuccess); + + // Fail before initialization is performed. + assertFalse(mDut.addHlpReq(WLAN0_IFACE_NAME, dstAddr, hlpPacket)); + verify(mISupplicantStaIfaceMockV13, never()).filsHlpAddRequest(any(byte[].class), + any(ArrayList.class)); + + executeAndValidateInitializationSequenceV1_3(); + assertNotNull(mISupplicantStaIfaceCallbackV13); + + ArrayList<Byte> hlpPayload = NativeUtil.byteArrayToArrayList(hlpPacket); + assertTrue(mDut.addHlpReq(WLAN0_IFACE_NAME, dstAddr, hlpPacket)); + verify(mISupplicantStaIfaceMockV13).filsHlpAddRequest(eq(dstAddr), eq(hlpPayload)); + } + + /** + * Tests the flushing of FILS HLP packet from supplicant. + */ + @Test + public void testFlushAllHlp() throws Exception { + + setupMocksForHalV1_3(); + when(mISupplicantStaIfaceMockV13.filsHlpFlushRequest()).thenReturn(mStatusSuccess); + + // Fail before initialization is performed. + assertFalse(mDut.flushAllHlp(WLAN0_IFACE_NAME)); + verify(mISupplicantStaIfaceMockV13, never()).filsHlpFlushRequest(); + + executeAndValidateInitializationSequenceV1_3(); + assertNotNull(mISupplicantStaIfaceCallbackV13); + + assertTrue(mDut.flushAllHlp(WLAN0_IFACE_NAME)); + verify(mISupplicantStaIfaceMockV13).filsHlpFlushRequest(); + } + + /** + * Tests the handling of state change V13 notification without + * any configured network. + */ + @Test + public void testonStateChangedV13CallbackWithNoConfiguredNetwork() throws Exception { + setupMocksForHalV1_3(); + executeAndValidateInitializationSequenceV1_3(); + assertNotNull(mISupplicantStaIfaceCallbackV13); + + mISupplicantStaIfaceCallbackV13.onStateChanged_1_3( + ISupplicantStaIfaceCallback.State.INACTIVE, + NativeUtil.macAddressToByteArray(BSSID), SUPPLICANT_NETWORK_ID, + NativeUtil.decodeSsid(SUPPLICANT_SSID), false); + + // Can't compare WifiSsid instances because they lack an equals. + verify(mWifiMonitor).broadcastSupplicantStateChangeEvent( + eq(WLAN0_IFACE_NAME), eq(WifiConfiguration.INVALID_NETWORK_ID), + any(WifiSsid.class), eq(BSSID), eq(SupplicantState.INACTIVE)); + } + + /** + * Tests the handling of state change V13 notification to + * associated after configuring a network. + */ + @Test + public void testStateChangeV13ToAssociatedCallback() throws Exception { + setupMocksForHalV1_3(); + executeAndValidateInitializationSequenceV1_3(); + int frameworkNetworkId = 6; + executeAndValidateConnectSequence(frameworkNetworkId, false); + assertNotNull(mISupplicantStaIfaceCallbackV13); + + mISupplicantStaIfaceCallbackV13.onStateChanged_1_3( + ISupplicantStaIfaceCallback.State.ASSOCIATED, + NativeUtil.macAddressToByteArray(BSSID), SUPPLICANT_NETWORK_ID, + NativeUtil.decodeSsid(SUPPLICANT_SSID), false); + + verify(mWifiMonitor).broadcastSupplicantStateChangeEvent( + eq(WLAN0_IFACE_NAME), eq(frameworkNetworkId), + any(WifiSsid.class), eq(BSSID), eq(SupplicantState.ASSOCIATED)); + } + + /** + * Tests the handling of state change V13 notification to + * completed after configuring a network. + */ + @Test + public void testStateChangeV13ToCompletedCallback() throws Exception { + InOrder wifiMonitorInOrder = inOrder(mWifiMonitor); + setupMocksForHalV1_3(); + executeAndValidateInitializationSequenceV1_3(); + assertNotNull(mISupplicantStaIfaceCallbackV13); + int frameworkNetworkId = 6; + executeAndValidateConnectSequence(frameworkNetworkId, false); + + mISupplicantStaIfaceCallbackV13.onStateChanged_1_3( + ISupplicantStaIfaceCallback.State.COMPLETED, + NativeUtil.macAddressToByteArray(BSSID), SUPPLICANT_NETWORK_ID, + NativeUtil.decodeSsid(SUPPLICANT_SSID), false); + + wifiMonitorInOrder.verify(mWifiMonitor).broadcastNetworkConnectionEvent( + eq(WLAN0_IFACE_NAME), eq(frameworkNetworkId), eq(BSSID)); + wifiMonitorInOrder.verify(mWifiMonitor).broadcastSupplicantStateChangeEvent( + eq(WLAN0_IFACE_NAME), eq(frameworkNetworkId), + any(WifiSsid.class), eq(BSSID), eq(SupplicantState.COMPLETED)); + } + + /** + * Tests the handling of incorrect network passwords, edge case + * when onStateChanged_1_3() is used. + * + * If the network is removed during 4-way handshake, do not call it a password mismatch. + */ + @Test + public void testNetworkRemovedDuring4wayWhenonStateChangedV13IsUsed() throws Exception { + executeAndValidateInitializationSequence(); + assertNotNull(mISupplicantStaIfaceCallback); + setupMocksForHalV1_3(); + executeAndValidateInitializationSequenceV1_3(); + assertNotNull(mISupplicantStaIfaceCallbackV13); + + int reasonCode = 3; + + mISupplicantStaIfaceCallbackV13.onStateChanged_1_3( + ISupplicantStaIfaceCallback.State.FOURWAY_HANDSHAKE, + NativeUtil.macAddressToByteArray(BSSID), + SUPPLICANT_NETWORK_ID, + NativeUtil.decodeSsid(SUPPLICANT_SSID), false); + mISupplicantStaIfaceCallback.onNetworkRemoved(SUPPLICANT_NETWORK_ID); + mISupplicantStaIfaceCallback.onDisconnected( + NativeUtil.macAddressToByteArray(BSSID), true, reasonCode); + verify(mWifiMonitor, times(0)).broadcastAuthenticationFailureEvent(any(), anyInt(), + anyInt()); + } + + /** + * Tests the handling of incorrect network passwords when + * onStateChanged_1_3() is used, edge case. + * + * If the disconnect reason is "IE in 4way differs", do not call it a password mismatch. + */ + @Test + public void testIeDiffersWhenonStateChangedV13IsUsed() throws Exception { + executeAndValidateInitializationSequence(); + assertNotNull(mISupplicantStaIfaceCallback); + setupMocksForHalV1_3(); + executeAndValidateInitializationSequenceV1_3(); + assertNotNull(mISupplicantStaIfaceCallbackV13); + + int reasonCode = ISupplicantStaIfaceCallback.ReasonCode.IE_IN_4WAY_DIFFERS; + + mISupplicantStaIfaceCallbackV13.onStateChanged_1_3( + ISupplicantStaIfaceCallback.State.FOURWAY_HANDSHAKE, + NativeUtil.macAddressToByteArray(BSSID), + SUPPLICANT_NETWORK_ID, + NativeUtil.decodeSsid(SUPPLICANT_SSID), false); + mISupplicantStaIfaceCallback.onDisconnected( + NativeUtil.macAddressToByteArray(BSSID), true, reasonCode); + verify(mWifiMonitor, times(0)).broadcastAuthenticationFailureEvent(any(), anyInt(), + anyInt()); + } + + /** + * Tests the handling of state change V13 notification to + * completed (with FILS HLP IE sent) after configuring a + * network. + */ + @Test + public void testStateChangeV13WithFilsHlpIESentToCompletedCallback() throws Exception { + InOrder wifiMonitorInOrder = inOrder(mWifiMonitor); + setupMocksForHalV1_3(); + executeAndValidateInitializationSequenceV1_3(); + assertNotNull(mISupplicantStaIfaceCallbackV13); + int frameworkNetworkId = 6; + executeAndValidateConnectSequence(frameworkNetworkId, false); + + mISupplicantStaIfaceCallbackV13.onStateChanged_1_3( + ISupplicantStaIfaceCallback.State.COMPLETED, + NativeUtil.macAddressToByteArray(BSSID), SUPPLICANT_NETWORK_ID, + NativeUtil.decodeSsid(SUPPLICANT_SSID), true); + + wifiMonitorInOrder.verify(mWifiMonitor).broadcastFilsNetworkConnectionEvent( + eq(WLAN0_IFACE_NAME), eq(frameworkNetworkId), eq(BSSID)); + wifiMonitorInOrder.verify(mWifiMonitor).broadcastSupplicantStateChangeEvent( + eq(WLAN0_IFACE_NAME), eq(frameworkNetworkId), + any(WifiSsid.class), eq(BSSID), eq(SupplicantState.COMPLETED)); + } + } diff --git a/tests/wifitests/src/com/android/server/wifi/SupplicantStaNetworkHalTest.java b/tests/wifitests/src/com/android/server/wifi/SupplicantStaNetworkHalTest.java index 949b8659f..616472c10 100644 --- a/tests/wifitests/src/com/android/server/wifi/SupplicantStaNetworkHalTest.java +++ b/tests/wifitests/src/com/android/server/wifi/SupplicantStaNetworkHalTest.java @@ -387,6 +387,29 @@ public class SupplicantStaNetworkHalTest extends WifiBaseTest { } /** + * Tests the saving/loading of WifiConfiguration with FILS AKM + * to wpa_supplicant. + */ + @Test + public void testTLSWifiEnterpriseConfigWithFilsEapErp() throws Exception { + // Now expose the V1.3 ISupplicantStaNetwork + createSupplicantStaNetwork(SupplicantStaNetworkVersion.V1_3); + + WifiConfiguration config = WifiConfigurationTestUtil.createEapNetwork(); + config.enterpriseConfig = + WifiConfigurationTestUtil.createTLSWifiEnterpriseConfigWithNonePhase2(); + config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.FILS_SHA256); + config.enterpriseConfig.setFieldValue(WifiEnterpriseConfig.EAP_ERP, "1"); + testWifiConfigurationSaveLoad(config); + // Check the supplicant variables to ensure that we have added the FILS AKM. + assertTrue((mSupplicantVariables.keyMgmtMask & android.hardware.wifi.supplicant.V1_3 + .ISupplicantStaNetwork.KeyMgmtMask.FILS_SHA256) + == android.hardware.wifi.supplicant.V1_3 + .ISupplicantStaNetwork.KeyMgmtMask.FILS_SHA256); + verify(mISupplicantStaNetworkV13).setEapErp(eq(true)); + } + + /** * Tests the saving of WifiConfiguration to wpa_supplicant. */ @Test @@ -974,6 +997,14 @@ public class SupplicantStaNetworkHalTest extends WifiBaseTest { Integer.parseInt(oppKeyCaching) == 1 ? true : false, mSupplicantVariables.eapProactiveKeyCaching); } + // There is no getter for this one, so check the supplicant variable. + String eapErp = + config.enterpriseConfig.getFieldValue(WifiEnterpriseConfig.EAP_ERP); + if (!TextUtils.isEmpty(eapErp)) { + assertEquals( + Integer.parseInt(eapErp) == 1 ? true : false, + mSupplicantVariables.eapErp); + } } /** @@ -1695,6 +1726,14 @@ public class SupplicantStaNetworkHalTest extends WifiBaseTest { }).when(mISupplicantStaNetworkV13) .getWapiCertSuite(any(android.hardware.wifi.supplicant.V1_3 .ISupplicantStaNetwork.getWapiCertSuiteCallback.class)); + + /** EAP ERP */ + doAnswer(new AnswerWithArguments() { + public SupplicantStatus answer(boolean enable) throws RemoteException { + mSupplicantVariables.eapErp = enable; + return mStatusSuccess; + } + }).when(mISupplicantStaNetworkV13).setEapErp(any(boolean.class)); } private SupplicantStatus createSupplicantStatus(int code) { @@ -1761,5 +1800,6 @@ public class SupplicantStaNetworkHalTest extends WifiBaseTest { public int ocsp; public ArrayList<Byte> serializedPmkCache; public String wapiCertSuite; + public boolean eapErp; } } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiMonitorTest.java b/tests/wifitests/src/com/android/server/wifi/WifiMonitorTest.java index 40ca9b9a2..6652e9b0c 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiMonitorTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiMonitorTest.java @@ -602,4 +602,23 @@ public class WifiMonitorTest extends WifiBaseTest { assertEquals(WifiMonitor.MBO_OCE_BSS_TM_HANDLING_DONE, messageCaptor.getValue().what); } + /** + * Broadcast fils network connection test. + */ + @Test + public void testBroadcastFilsNetworkConnectionEvent() { + mWifiMonitor.registerHandler( + WLAN_IFACE_NAME, WifiMonitor.FILS_NETWORK_CONNECTION_EVENT, mHandlerSpy); + int networkId = NETWORK_ID; + String bssid = BSSID; + mWifiMonitor.broadcastFilsNetworkConnectionEvent(WLAN_IFACE_NAME, networkId, bssid); + mLooper.dispatchAll(); + + ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); + verify(mHandlerSpy).handleMessage(messageCaptor.capture()); + assertEquals(WifiMonitor.FILS_NETWORK_CONNECTION_EVENT, messageCaptor.getValue().what); + assertEquals(networkId, messageCaptor.getValue().arg1); + assertEquals(bssid, (String) messageCaptor.getValue().obj); + } + } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java index 79df8fe6f..f1a908549 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java @@ -1033,4 +1033,33 @@ public class WifiNativeTest extends WifiBaseTest { WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_UNKNOWN); verify(mWificondControl, never()).sendMgmtFrame(any(), any(), anyInt(), any(), any()); } + + /** + * Tests that WifiNative#addHlpReq() calls + * SupplicantStaIfaceHal#addHlpReq() + */ + @Test + public void testaddHlpReq() { + byte[] hlpPacket = { + 0x40, 0x00, 0x3c, 0x00, (byte) 0xa8, (byte) 0xbd, 0x27, 0x5b, + 0x33, 0x72, (byte) 0xf4, (byte) 0xf5, (byte) 0xe8, 0x51, (byte) 0x9e, 0x09, + (byte) 0xa8, (byte) 0xbd, 0x27, 0x5b, 0x33, 0x72, (byte) 0xb0, 0x66, + 0x00, 0x00 + }; + mWifiNative.addHlpReq(WIFI_IFACE_NAME, TEST_MAC_ADDRESS, hlpPacket); + + verify(mStaIfaceHal).addHlpReq(eq(WIFI_IFACE_NAME), + eq(TEST_MAC_ADDRESS.toByteArray()), eq(hlpPacket)); + } + + /** + * Tests that WifiNative#flushAllHlp() calls + * SupplicantStaIfaceHal#flushAllHlp() + */ + @Test + public void testflushAllHlp() { + mWifiNative.flushAllHlp(WIFI_IFACE_NAME); + + verify(mStaIfaceHal).flushAllHlp(eq(WIFI_IFACE_NAME)); + } } diff --git a/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java index 4b98a59d9..874da7043 100644 --- a/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java +++ b/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java @@ -627,6 +627,88 @@ public class InformationElementUtilTest extends WifiBaseTest { } /** + * Test Capabilities.generateCapabilitiesString() with RSN IE, + * CCMP and FILS SHA256. Expect the function to return a string + * with the proper security information. + */ + @Test + public void buildCapabilities_rsnFilsSha256Element() { + InformationElement ieRsn = new InformationElement(); + ieRsn.id = InformationElement.EID_RSN; + ieRsn.bytes = new byte[] { + // RSNE Version (0x0001) + (byte) 0x01, (byte) 0x00, + // Group cipher suite: CCMP + (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x04, + // Number of cipher suites (1) + (byte) 0x01, (byte) 0x00, + // Cipher suite: CCMP + (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x04, + // Number of AKMs (3) + (byte) 0x03, (byte) 0x00, + // WPA AKM + (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x01, + // WPA SHA256 AKM + (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x05, + // FILS SHA256 AKM + (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x0E, + // RSN capabilities + (byte) 0x00, (byte) 0x00 }; + + InformationElement[] ies = new InformationElement[] { ieRsn }; + int beaconCap = 0x1 << 4; + + InformationElementUtil.Capabilities capabilities = + new InformationElementUtil.Capabilities(); + capabilities.from(ies, beaconCap, true); + String result = capabilities.generateCapabilitiesString(); + + assertEquals("[WPA2-EAP+EAP-SHA256+FILS-SHA256-CCMP][RSN-EAP+EAP-SHA256+FILS-SHA256-CCMP]", + result); + } + + /** + * Test Capabilities.generateCapabilitiesString() with RSN IE, + * CCMP and FILS SHA384. Expect the function to return a string + * with the proper security information. + */ + @Test + public void buildCapabilities_rsnFilsSha384Element() { + InformationElement ieRsn = new InformationElement(); + ieRsn.id = InformationElement.EID_RSN; + ieRsn.bytes = new byte[] { + // RSNE Version (0x0001) + (byte) 0x01, (byte) 0x00, + // Group cipher suite: CCMP + (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x04, + // Number of cipher suites (1) + (byte) 0x01, (byte) 0x00, + // Cipher suite: CCMP + (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x04, + // Number of AKMs (3) + (byte) 0x03, (byte) 0x00, + // WPA AKM + (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x01, + // WPA SHA256 AKM + (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x05, + // FILS SHA384 AKM + (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x0F, + // RSN capabilities + (byte) 0x00, (byte) 0x00 }; + + InformationElement[] ies = new InformationElement[] { ieRsn }; + int beaconCap = 0x1 << 4; + + InformationElementUtil.Capabilities capabilities = + new InformationElementUtil.Capabilities(); + capabilities.from(ies, beaconCap, true); + String result = capabilities.generateCapabilitiesString(); + + assertEquals("[WPA2-EAP+EAP-SHA256+FILS-SHA384-CCMP][RSN-EAP+EAP-SHA256+FILS-SHA384-CCMP]", + result); + } + + /** * Test Capabilities.generateCapabilitiesString() with both RSN and WPA1 IE which are malformed. * Expect the function to return a string with empty key management & pairswise cipher security * information. diff --git a/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java index bc5f880b4..0f69316e7 100644 --- a/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java +++ b/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java @@ -203,6 +203,46 @@ public class ScanResultUtilTest extends WifiBaseTest { assertFalse(ScanResultUtil.isScanResultForPskSaeTransitionNetwork(input)); } + /** + * Test that provided network supports FILS SHA256 AKM. + */ + @Test + public void testFilsSha256AkmSupportedNetwork() { + final String ssid = "FILS-AP"; + String caps = "[WPA2-EAP+EAP-SHA256+FILS-SHA256-CCMP]" + + "[RSN-EAP+EAP-SHA256+FILS-SHA256-CCMP][ESS]"; + + ScanResult input = new ScanResult(WifiSsid.createFromAsciiEncoded(ssid), ssid, + "ab:cd:01:ef:45:89", 1245, 0, caps, -78, 2450, 1025, 22, 33, 20, 0, + 0, true); + + input.informationElements = new InformationElement[] { + createIE(InformationElement.EID_SSID, ssid.getBytes(StandardCharsets.UTF_8)) + }; + + assertTrue(ScanResultUtil.isScanResultForFilsSha256Network(input)); + } + + /** + * Test that provided network supports FILS SHA384 AKM. + */ + @Test + public void testFilsSha384AkmSupportedNetwork() { + final String ssid = "FILS-AP"; + String caps = "[WPA2-EAP+EAP-SHA384+FILS-SHA384-CCMP]" + + "[RSN-EAP+EAP-SHA384+FILS-SHA384-CCMP][ESS]"; + + ScanResult input = new ScanResult(WifiSsid.createFromAsciiEncoded(ssid), ssid, + "ab:cd:01:ef:45:89", 1245, 0, caps, -78, 2450, 1025, 22, 33, 20, 0, + 0, true); + + input.informationElements = new InformationElement[] { + createIE(InformationElement.EID_SSID, ssid.getBytes(StandardCharsets.UTF_8)) + }; + + assertTrue(ScanResultUtil.isScanResultForFilsSha384Network(input)); + } + private static InformationElement createIE(int id, byte[] bytes) { InformationElement ie = new InformationElement(); ie.id = id; |