summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorRubin Xu <rubinxu@google.com>2015-11-29 15:32:47 +0000
committerRubin Xu <rubinxu@google.com>2016-01-26 11:00:07 +0000
commit78463be32c01259841f4c0b670b742c3c2040cf3 (patch)
tree8a669ec1ca9e9e696f6778becfc1745e77488bcf /tests
parent5b6b6d00d817acd25b55badf226c42b8d9811399 (diff)
Support multiple CA certificates for EAP network
Copy all configured CA certificates to key store and set up their aliases when adding networks. Remove all certificates when the network is removed. Bug: 22547958 Change-Id: I91836911bfc84ec2922c5db4e9019a2872f361f0
Diffstat (limited to 'tests')
-rw-r--r--tests/wifitests/src/android/net/wifi/FakeKeys.java67
-rw-r--r--tests/wifitests/src/android/net/wifi/WifiEnterpriseConfigTest.java113
-rw-r--r--tests/wifitests/src/com/android/server/wifi/MockKeyStore.java165
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java208
4 files changed, 552 insertions, 1 deletions
diff --git a/tests/wifitests/src/android/net/wifi/FakeKeys.java b/tests/wifitests/src/android/net/wifi/FakeKeys.java
new file mode 100644
index 000000000..8a1e7b47f
--- /dev/null
+++ b/tests/wifitests/src/android/net/wifi/FakeKeys.java
@@ -0,0 +1,67 @@
+package android.net.wifi;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+/**
+ * A class containing test certificates.
+ */
+public class FakeKeys {
+ private static final String CA_CERT0_STRING = "-----BEGIN CERTIFICATE-----\n" +
+ "MIIDKDCCAhCgAwIBAgIJAILlFdwzLVurMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV\n" +
+ "BAMTB0VBUCBDQTEwHhcNMTYwMTEyMTE1MDE1WhcNMjYwMTA5MTE1MDE1WjASMRAw\n" +
+ "DgYDVQQDEwdFQVAgQ0ExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA\n" +
+ "znAPUz26Msae4ws43czR41/J2QtrSIZUKmVUsVumDbYHrPNvTXKSMXAcewORDQYX\n" +
+ "RqvHvpn8CscB1+oGXZvHwxj4zV0WKoK2zeXkau3vcyl3HIKupJfq2TEACefVjj0t\n" +
+ "JW+X35PGWp9/H5zIUNVNVjS7Ums84IvKhRB8512PB9UyHagXYVX5GWpAcVpyfrlR\n" +
+ "FI9Qdhh+Pbk0uyktdbf/CdfgHOoebrTtwRljM0oDtX+2Cv6j0wBK7hD8pPvf1+uy\n" +
+ "GzczigAU/4Kw7eZqydf9B+5RupR+IZipX41xEiIrKRwqi517WWzXcjaG2cNbf451\n" +
+ "xpH5PnV3i1tq04jMGQUzFwIDAQABo4GAMH4wHQYDVR0OBBYEFIwX4vs8BiBcScod\n" +
+ "5noZHRM8E4+iMEIGA1UdIwQ7MDmAFIwX4vs8BiBcScod5noZHRM8E4+ioRakFDAS\n" +
+ "MRAwDgYDVQQDEwdFQVAgQ0ExggkAguUV3DMtW6swDAYDVR0TBAUwAwEB/zALBgNV\n" +
+ "HQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAFfQqOTA7Rv7K+luQ7pnas4BYwHE\n" +
+ "9GEP/uohv6KOy0TGQFbrRTjFoLVNB9BZ1ymMDZ0/TIwIUc7wi7a8t5mEqYH153wW\n" +
+ "aWooiSjyLLhuI4sNrNCOtisdBq2r2MFXt6h0mAQYOPv8R8K7/fgSxGFqzhyNmmVL\n" +
+ "1qBJldx34SpwsTALQVPb4hGwJzZfr1PcpEQx6xMnTl8xEWZE3Ms99uaUxbQqIwRu\n" +
+ "LgAOkNCmY2m89VhzaHJ1uV85AdM/tD+Ysmlnnjt9LRCejbBipjIGjOXrg1JP+lxV\n" +
+ "muM4vH+P/mlmxsPPz0d65b+EGmJZpoLkO/tdNNvCYzjJpTEWpEsO6NMhKYo=\n" +
+ "-----END CERTIFICATE-----\n";
+ public static final X509Certificate CA_CERT0 = loadCertificate(CA_CERT0_STRING);
+
+ private static final String CA_CERT1_STRING = "-----BEGIN CERTIFICATE-----\n" +
+ "MIIDKDCCAhCgAwIBAgIJAOM5SzKO2pzCMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV\n" +
+ "BAMTB0VBUCBDQTAwHhcNMTYwMTEyMDAxMDQ3WhcNMjYwMTA5MDAxMDQ3WjASMRAw\n" +
+ "DgYDVQQDEwdFQVAgQ0EwMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA\n" +
+ "89ug+IEKVQXnJGKg5g4uVHg6J/8iRUxR5k2eH5o03hrJNMfN2D+cBe/wCiZcnWbI\n" +
+ "GbGZACWm2nQth2wy9Zgm2LOd3b4ocrHYls3XLq6Qb5Dd7a0JKU7pdGufiNVEkrmF\n" +
+ "EB+N64wgwH4COTvCiN4erp5kyJwkfqAl2xLkZo0C464c9XoyQOXbmYD9A8v10wZu\n" +
+ "jyNsEo7Nr2USyw+qhjWSbFbEirP77Tvx+7pJQJwdtk1V9Tn73T2dGF2WHYejei9S\n" +
+ "mcWpdIUqsu9etYH+zDmtu7I1xlkwiaVsNr2+D+qaCJyOYqrDTKVNK5nmbBPXDWZc\n" +
+ "NoDbTOoqquX7xONpq9M6jQIDAQABo4GAMH4wHQYDVR0OBBYEFAZ3A2S4qJZZwuNY\n" +
+ "wkJ6mAdc0gVdMEIGA1UdIwQ7MDmAFAZ3A2S4qJZZwuNYwkJ6mAdc0gVdoRakFDAS\n" +
+ "MRAwDgYDVQQDEwdFQVAgQ0EwggkA4zlLMo7anMIwDAYDVR0TBAUwAwEB/zALBgNV\n" +
+ "HQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAHmdMwEhtys4d0E+t7owBmoVR+lU\n" +
+ "hMCcRtWs8YKX5WIM2kTweT0h/O1xwE1mWmRv/IbDAEb8od4BjAQLhIcolStr2JaO\n" +
+ "9ZzyxjOnNzqeErh/1DHDbb/moPpqfeJ8YiEz7nH/YU56Q8iCPO7TsgS0sNNE7PfN\n" +
+ "IUsBW0yHRgpQ4OxWmiZG2YZWiECRzAC0ecPzo59N5iH4vLQIMTMYquiDeMPQnn1e\n" +
+ "NDGxG8gCtDKIaS6tMg3a28MvWB094pr2ETou8O1C8Ji0Y4hE8QJmSdT7I4+GZjgW\n" +
+ "g94DZ5RiL7sdp3vC48CXOmeT61YBIvhGUsE1rPhXqkpqQ3Z3C4TFF0jXZZc=\n" +
+ "-----END CERTIFICATE-----\n";
+ public static final X509Certificate CA_CERT1 = loadCertificate(CA_CERT1_STRING);
+
+
+ private static X509Certificate loadCertificate(String blob) {
+ try {
+ final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ InputStream stream = new ByteArrayInputStream(blob.getBytes(StandardCharsets.UTF_8));
+
+ return (X509Certificate) certFactory.generateCertificate(stream);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+}
diff --git a/tests/wifitests/src/android/net/wifi/WifiEnterpriseConfigTest.java b/tests/wifitests/src/android/net/wifi/WifiEnterpriseConfigTest.java
new file mode 100644
index 000000000..ce5a7d230
--- /dev/null
+++ b/tests/wifitests/src/android/net/wifi/WifiEnterpriseConfigTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.net.wifi;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.security.Credentials;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.security.cert.X509Certificate;
+
+
+/**
+ * Unit tests for {@link android.net.wifi.WifiEnterpriseConfig}.
+ */
+@SmallTest
+public class WifiEnterpriseConfigTest {
+ // Maintain a ground truth of the keystore uri prefix which is expected by wpa_supplicant.
+ public static final String KEYSTORE_URI = "keystore://";
+ public static final String CA_CERT_PREFIX = KEYSTORE_URI + Credentials.CA_CERTIFICATE;
+ public static final String KEYSTORES_URI = "keystores://";
+
+ private WifiEnterpriseConfig mEnterpriseConfig;
+
+ @Before
+ public void setUp() throws Exception {
+ mEnterpriseConfig = new WifiEnterpriseConfig();
+ }
+
+ @Test
+ public void testSetGetSingleCaCertificate() {
+ X509Certificate cert0 = FakeKeys.CA_CERT0;
+ mEnterpriseConfig.setCaCertificate(cert0);
+ assertEquals(mEnterpriseConfig.getCaCertificate(), cert0);
+ }
+
+ @Test
+ public void testSetGetMultipleCaCertificates() {
+ X509Certificate cert0 = FakeKeys.CA_CERT0;
+ X509Certificate cert1 = FakeKeys.CA_CERT1;
+ mEnterpriseConfig.setCaCertificates(new X509Certificate[] {cert0, cert1});
+ X509Certificate[] result = mEnterpriseConfig.getCaCertificates();
+ assertEquals(result.length, 2);
+ assertTrue(result[0] == cert0 && result[1] == cert1);
+ }
+
+ @Test
+ public void testSaveSingleCaCertificateAlias() {
+ final String alias = "single_alias 0";
+ mEnterpriseConfig.setCaCertificateAliases(new String[] {alias});
+ assertEquals(getCaCertField(), CA_CERT_PREFIX + alias);
+ }
+
+ @Test
+ public void testLoadSingleCaCertificateAlias() {
+ final String alias = "single_alias 1";
+ setCaCertField(CA_CERT_PREFIX + alias);
+ String[] aliases = mEnterpriseConfig.getCaCertificateAliases();
+ assertEquals(aliases.length, 1);
+ assertEquals(aliases[0], alias);
+ }
+
+ @Test
+ public void testSaveMultipleCaCertificates() {
+ final String alias0 = "single_alias 0";
+ final String alias1 = "single_alias 1";
+ mEnterpriseConfig.setCaCertificateAliases(new String[] {alias0, alias1});
+ assertEquals(getCaCertField(), String.format("%s%s %s",
+ KEYSTORES_URI,
+ WifiEnterpriseConfig.encodeCaCertificateAlias(Credentials.CA_CERTIFICATE + alias0),
+ WifiEnterpriseConfig.encodeCaCertificateAlias(Credentials.CA_CERTIFICATE + alias1)));
+ }
+
+ @Test
+ public void testLoadMultipleCaCertificates() {
+ final String alias0 = "single_alias 0";
+ final String alias1 = "single_alias 1";
+ setCaCertField(String.format("%s%s %s",
+ KEYSTORES_URI,
+ WifiEnterpriseConfig.encodeCaCertificateAlias(Credentials.CA_CERTIFICATE + alias0),
+ WifiEnterpriseConfig.encodeCaCertificateAlias(Credentials.CA_CERTIFICATE + alias1)));
+ String[] aliases = mEnterpriseConfig.getCaCertificateAliases();
+ assertEquals(aliases.length, 2);
+ assertEquals(aliases[0], alias0);
+ assertEquals(aliases[1], alias1);
+ }
+
+ private String getCaCertField() {
+ return mEnterpriseConfig.getFieldValue(WifiEnterpriseConfig.CA_CERT_KEY, "");
+ }
+
+ private void setCaCertField(String value) {
+ mEnterpriseConfig.setFieldValue(WifiEnterpriseConfig.CA_CERT_KEY, value);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/MockKeyStore.java b/tests/wifitests/src/com/android/server/wifi/MockKeyStore.java
new file mode 100644
index 000000000..4f6c16dc7
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/MockKeyStore.java
@@ -0,0 +1,165 @@
+package com.android.server.wifi;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.security.KeyStore;
+import android.util.SparseArray;
+
+import org.mockito.Matchers;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.Arrays;
+import java.util.HashMap;
+
+class MockKeyStore {
+
+ public static class KeyBlob {
+ public byte[] blob;
+ public int flag;
+
+ public void update(byte[] blob, int flag) {
+ this.blob = Arrays.copyOf(blob, blob.length);
+ this.flag = flag;
+ }
+ }
+ private SparseArray<HashMap<String, KeyBlob>> mStore;
+
+ public MockKeyStore() {
+ mStore = new SparseArray<HashMap<String, KeyBlob>>();
+ }
+
+ public KeyStore createMock() {
+ KeyStore mock = mock(KeyStore.class);
+ when(mock.state()).thenReturn(KeyStore.State.UNLOCKED);
+
+ when(mock.put(anyString(), Matchers.any(byte[].class), anyInt(), anyInt()))
+ .thenAnswer(new Answer<Boolean>() {
+
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ Object[] args = invocation.getArguments();
+ return put((String) args[0], (byte[]) args[1], (Integer) args[2],
+ (Integer) args[3]);
+ }
+ });
+
+ when(mock.importKey(anyString(), Matchers.any(byte[].class), anyInt(), anyInt()))
+ .thenAnswer(new Answer<Boolean>() {
+
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ Object[] args = invocation.getArguments();
+ return importKey((String) args[0], (byte[]) args[1], (Integer) args[2],
+ (Integer) args[3]);
+ }
+ });
+
+ when(mock.delete(anyString(), anyInt())).thenAnswer(new Answer<Boolean>() {
+
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ Object[] args = invocation.getArguments();
+ return delete((String) args[0], (Integer) args[1]);
+ }
+ });
+
+ when(mock.contains(anyString(), anyInt())).thenAnswer(new Answer<Boolean>() {
+
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ Object[] args = invocation.getArguments();
+ return contains((String) args[0], (Integer) args[1]);
+ }
+ });
+
+ when(mock.duplicate(anyString(), anyInt(), anyString(), anyInt()))
+ .thenAnswer(new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ Object[] args = invocation.getArguments();
+ return duplicate((String) args[0], (Integer) args[1], (String) args[2],
+ (Integer) args[3]);
+ }
+ });
+ return mock;
+ }
+
+ private KeyBlob access(int uid, String key, boolean createIfNotExist) {
+ if (mStore.get(uid) == null) {
+ mStore.put(uid, new HashMap<String, KeyBlob>());
+ }
+ HashMap<String, KeyBlob> map = mStore.get(uid);
+ if (map.containsKey(key)) {
+ return map.get(key);
+ } else {
+ if (createIfNotExist) {
+ KeyBlob blob = new KeyBlob();
+ map.put(key, blob);
+ return blob;
+ } else {
+ return null;
+ }
+ }
+ }
+
+ public KeyBlob getKeyBlob(int uid, String key) {
+ return access(uid, key, false);
+ }
+
+ private boolean put(String key, byte[] value, int uid, int flags) {
+ access(uid, key, true).update(value, flags);
+ return true;
+ }
+
+ private boolean importKey(String keyName, byte[] key, int uid, int flags) {
+ return put(keyName, key, uid, flags);
+ }
+
+ private boolean delete(String key, int uid) {
+ if (mStore.get(uid) != null) {
+ mStore.get(uid).remove(key);
+ }
+ return true;
+ }
+
+ private boolean contains(String key, int uid) {
+ return access(uid, key, false) != null;
+ }
+
+ private boolean duplicate(String srcKey, int srcUid, String destKey, int destUid) {
+ for (int i = 0; i < mStore.size(); i++) {
+ int key = mStore.keyAt(i);
+ // Cannot copy to itself
+ if (srcKey.equals(destKey) && key == destUid) {
+ continue;
+ }
+ if (srcUid == -1 || srcUid == key) {
+ HashMap<String, KeyBlob> map = mStore.get(key);
+ if (map.containsKey(srcKey)) {
+ KeyBlob blob = map.get(srcKey);
+ access(destUid, destKey, true).update(blob.blob, blob.flag);
+ break;
+ }
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("KeyStore {");
+ for (int i = 0; i < mStore.size(); i++) {
+ int uid = mStore.keyAt(i);
+ for (String keyName : mStore.get(uid).keySet()) {
+ sb.append(String.format("%d:%s, ", uid, keyName));
+ }
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+} \ No newline at end of file
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java
index 638a726e6..1d0160370 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java
@@ -21,18 +21,27 @@ import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyObject;
import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.intThat;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.net.wifi.FakeKeys;
import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.net.wifi.WifiEnterpriseConfig;
+import android.net.wifi.WifiEnterpriseConfig.Eap;
+import android.net.wifi.WifiEnterpriseConfig.Phase2;
+import android.os.Process;
import android.os.UserHandle;
+import android.security.Credentials;
import android.test.AndroidTestCase;
+import android.text.TextUtils;
import com.android.server.net.DelayedDiskWrite;
import com.android.server.wifi.MockAnswerUtil.AnswerWithArguments;
@@ -42,6 +51,8 @@ import com.android.server.wifi.hotspot2.pps.HomeSP;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -50,10 +61,14 @@ import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileOutputStream;
+import java.io.IOException;
import java.lang.reflect.Field;
import java.math.BigInteger;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.BitSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
@@ -96,6 +111,7 @@ public class WifiConfigStoreTest extends AndroidTestCase {
private WifiConfigStore mConfigStore;
private ConfigurationMap mConfiguredNetworks;
public byte[] mNetworkHistory;
+ private MockKeyStore mMockKeyStore;
@Override
public void setUp() throws Exception {
@@ -133,6 +149,11 @@ public class WifiConfigStoreTest extends AndroidTestCase {
final Field moManagerField = WifiConfigStore.class.getDeclaredField("mMOManager");
moManagerField.setAccessible(true);
moManagerField.set(mConfigStore, mMOManager);
+
+ mMockKeyStore = new MockKeyStore();
+ final Field mKeyStoreField = WifiConfigStore.class.getDeclaredField("mKeyStore");
+ mKeyStoreField.setAccessible(true);
+ mKeyStoreField.set(mConfigStore, mMockKeyStore.createMock());
}
private void switchUser(int newUserId) {
@@ -629,4 +650,189 @@ public class WifiConfigStoreTest extends AndroidTestCase {
public void testHandleUserSwitchWithoutEphemeral() throws Exception {
verifyHandleUserSwitch(USER_IDS[0], USER_IDS[2], false);
}
+
+ public void testSaveLoadEapNetworks() {
+ testSaveLoadSingleEapNetwork("eap network", new EnterpriseConfig(Eap.TTLS)
+ .setPhase2(Phase2.MSCHAPV2)
+ .setIdentity("username", "password")
+ .setCaCerts(new X509Certificate[] {FakeKeys.CA_CERT0}));
+ testSaveLoadSingleEapNetwork("eap network", new EnterpriseConfig(Eap.TTLS)
+ .setPhase2(Phase2.MSCHAPV2)
+ .setIdentity("username", "password")
+ .setCaCerts(new X509Certificate[] {FakeKeys.CA_CERT1, FakeKeys.CA_CERT0}));
+
+ }
+
+ private void testSaveLoadSingleEapNetwork(String ssid, EnterpriseConfig eapConfig) {
+ final HashMap<String, String> networkVariables = new HashMap<String, String>();
+ reset(mWifiNative);
+ when(mWifiNative.addNetwork()).thenReturn(0);
+ when(mWifiNative.setNetworkVariable(anyInt(), anyString(), anyString())).thenAnswer(
+ new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ Object[] args = invocation.getArguments();
+ // Verify that no wpa_supplicant variables were written for any other
+ // network configurations.
+ assertEquals((Integer) args[0], (Integer) 0);
+ String name = (String) args[1];
+ String value = (String) args[2];
+ networkVariables.put(name, value);
+ return true;
+ }
+ });
+ when(mWifiNative.getNetworkVariable(anyInt(), anyString())).then(
+ new Answer<String>() {
+ @Override
+ public String answer(InvocationOnMock invocationOnMock) throws Throwable {
+ Object args[] = invocationOnMock.getArguments();
+ Integer netId = (Integer) args[0];
+ String name = (String) args[1];
+ // Verify that no wpa_supplicant variables were read for any other
+ // network configurations.
+ assertEquals(netId, (Integer) 0);
+ return networkVariables.get(name);
+ }
+ });
+ when(mWifiNative.setNetworkExtra(eq(0), anyString(), (Map<String, String>) anyObject()))
+ .thenReturn(true);
+
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = ssid;
+ config.creatorUid = Process.WIFI_UID;
+ config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
+ config.enterpriseConfig = eapConfig.enterpriseConfig;
+
+ // Store a network configuration.
+ mConfigStore.saveNetwork(config, Process.WIFI_UID);
+
+ // Verify that wpa_supplicant variables were written correctly for the network
+ // configuration.
+ verify(mWifiNative).addNetwork();
+ assertEquals(eapConfig.eap,
+ unquote(networkVariables.get(WifiEnterpriseConfig.EAP_KEY)));
+ assertEquals(eapConfig.phase2,
+ unquote(networkVariables.get(WifiEnterpriseConfig.PHASE2_KEY)));
+ assertEquals(eapConfig.identity,
+ unquote(networkVariables.get(WifiEnterpriseConfig.IDENTITY_KEY)));
+ assertEquals(eapConfig.password,
+ unquote(networkVariables.get(WifiEnterpriseConfig.PASSWORD_KEY)));
+ assertSavedCaCerts(eapConfig,
+ unquote(networkVariables.get(WifiEnterpriseConfig.CA_CERT_KEY)));
+
+ // Prepare the scan result.
+ final String header = "network id / ssid / bssid / flags";
+ String networks = header + "\n" + Integer.toString(0) + "\t" + ssid + "\tany";
+ when(mWifiNative.listNetworks(anyInt())).thenReturn(header);
+ when(mWifiNative.listNetworks(-1)).thenReturn(networks);
+
+ // Load back the configuration.
+ mConfigStore.loadConfiguredNetworks();
+ List<WifiConfiguration> configs = mConfigStore.getConfiguredNetworks();
+ assertEquals(1, configs.size());
+ WifiConfiguration loadedConfig = configs.get(0);
+ assertEquals(ssid, unquote(loadedConfig.SSID));
+ BitSet keyMgmt = new BitSet();
+ keyMgmt.set(KeyMgmt.WPA_EAP);
+ assertEquals(keyMgmt, loadedConfig.allowedKeyManagement);
+ assertEquals(eapConfig.enterpriseConfig.getEapMethod(),
+ loadedConfig.enterpriseConfig.getEapMethod());
+ assertEquals(eapConfig.enterpriseConfig.getPhase2Method(),
+ loadedConfig.enterpriseConfig.getPhase2Method());
+ assertEquals(eapConfig.enterpriseConfig.getIdentity(),
+ loadedConfig.enterpriseConfig.getIdentity());
+ assertEquals(eapConfig.enterpriseConfig.getPassword(),
+ loadedConfig.enterpriseConfig.getPassword());
+ asserCaCertsAliasesMatch(eapConfig.caCerts,
+ loadedConfig.enterpriseConfig.getCaCertificateAliases());
+ }
+
+ private String unquote(String value) {
+ if (value == null) {
+ return null;
+ }
+ int length = value.length();
+ if ((length > 1) && (value.charAt(0) == '"')
+ && (value.charAt(length - 1) == '"')) {
+ return value.substring(1, length - 1);
+ } else {
+ return value;
+ }
+ }
+
+ private void asserCaCertsAliasesMatch(X509Certificate[] certs, String[] aliases) {
+ assertEquals(certs.length, aliases.length);
+ List<String> aliasList = new ArrayList<String>(Arrays.asList(aliases));
+ try {
+ for (int i = 0; i < certs.length; i++) {
+ byte[] certPem = Credentials.convertToPem(certs[i]);
+ boolean found = false;
+ for (int j = 0; j < aliasList.size(); j++) {
+ byte[] keystoreCert = mMockKeyStore.getKeyBlob(Process.WIFI_UID,
+ Credentials.CA_CERTIFICATE + aliasList.get(j)).blob;
+ if (Arrays.equals(keystoreCert, certPem)) {
+ found = true;
+ aliasList.remove(j);
+ break;
+ }
+ }
+ assertTrue(found);
+ }
+ } catch (CertificateEncodingException | IOException e) {
+ fail("Cannot convert CA certificate to encoded form.");
+ }
+ }
+
+ private void assertSavedCaCerts(EnterpriseConfig eapConfig, String caCertVariable) {
+ ArrayList<String> aliases = new ArrayList<String>();
+ if (TextUtils.isEmpty(caCertVariable)) {
+ // Do nothing.
+ } else if (caCertVariable.startsWith(WifiEnterpriseConfig.CA_CERT_PREFIX)) {
+ aliases.add(caCertVariable.substring(WifiEnterpriseConfig.CA_CERT_PREFIX.length()));
+ } else if (caCertVariable.startsWith(WifiEnterpriseConfig.KEYSTORES_URI)) {
+ String[] encodedAliases = TextUtils.split(
+ caCertVariable.substring(WifiEnterpriseConfig.KEYSTORES_URI.length()),
+ WifiEnterpriseConfig.CA_CERT_ALIAS_DELIMITER);
+ for (String encodedAlias : encodedAliases) {
+ String alias = WifiEnterpriseConfig.decodeCaCertificateAlias(encodedAlias);
+ assertTrue(alias.startsWith(Credentials.CA_CERTIFICATE));
+ aliases.add(alias.substring(Credentials.CA_CERTIFICATE.length()));
+ }
+ } else {
+ fail("Unrecognized ca_cert variable: " + caCertVariable);
+ }
+ asserCaCertsAliasesMatch(eapConfig.caCerts, aliases.toArray(new String[aliases.size()]));
+ }
+
+ private static class EnterpriseConfig {
+ public String eap;
+ public String phase2;
+ public String identity;
+ public String password;
+ public X509Certificate[] caCerts;
+ public WifiEnterpriseConfig enterpriseConfig;
+
+ public EnterpriseConfig(int eapMethod) {
+ enterpriseConfig = new WifiEnterpriseConfig();
+ enterpriseConfig.setEapMethod(eapMethod);
+ eap = Eap.strings[eapMethod];
+ }
+ public EnterpriseConfig setPhase2(int phase2Method) {
+ enterpriseConfig.setPhase2Method(phase2Method);
+ phase2 = "auth=" + Phase2.strings[phase2Method];
+ return this;
+ }
+ public EnterpriseConfig setIdentity(String identity, String password) {
+ enterpriseConfig.setIdentity(identity);
+ enterpriseConfig.setPassword(password);
+ this.identity = identity;
+ this.password = password;
+ return this;
+ }
+ public EnterpriseConfig setCaCerts(X509Certificate[] certs) {
+ enterpriseConfig.setCaCertificates(certs);
+ caCerts = certs;
+ return this;
+ }
+ }
}