1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.keychain; 18 19 import static org.mockito.Mockito.mock; 20 import static org.mockito.Mockito.when; 21 22 import android.util.Base64; 23 24 import com.android.keychain.internal.KeyInfoProvider; 25 26 import com.google.common.collect.ImmutableList; 27 28 import org.junit.Assert; 29 import org.junit.Before; 30 import org.junit.Test; 31 import org.junit.runner.RunWith; 32 import org.mockito.Mockito; 33 import org.robolectric.RobolectricTestRunner; 34 import org.robolectric.RuntimeEnvironment; 35 import org.robolectric.shadows.ShadowApplication; 36 37 import java.io.ByteArrayInputStream; 38 import java.security.KeyStore; 39 import java.security.KeyStoreException; 40 import java.security.KeyStoreSpi; 41 import java.security.cert.Certificate; 42 import java.security.cert.CertificateException; 43 import java.security.cert.CertificateFactory; 44 import java.util.ArrayList; 45 import java.util.Collections; 46 import java.util.concurrent.CancellationException; 47 import java.util.concurrent.ExecutionException; 48 import java.util.concurrent.TimeUnit; 49 import java.util.concurrent.TimeoutException; 50 51 import javax.security.auth.x500.X500Principal; 52 53 @RunWith(RobolectricTestRunner.class) 54 public final class AliasLoaderTest { 55 // Generated using: 56 // openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 3650 -out certificate.pem 57 private static final String SELF_SIGNED_RSA_CERT_1_B64 = 58 "MIIDlDCCAnygAwIBAgIJAJsWcaXZlx7GMA0GCSqGSIb3DQEBCwUAMF8xCzAJBgNV\n" 59 + "BAYTAlVLMQ8wDQYDVQQIDAZMb25kb24xGzAZBgNVBAoMEkFPU1AgVGVzdCBkYXRh\n" 60 + "IG9uZTEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UEAwwHYW5kcm9pZDAeFw0xODA4\n" 61 + "MjMxNjAwNTFaFw0yODA4MjAxNjAwNTFaMF8xCzAJBgNVBAYTAlVLMQ8wDQYDVQQI\n" 62 + "DAZMb25kb24xGzAZBgNVBAoMEkFPU1AgVGVzdCBkYXRhIG9uZTEQMA4GA1UECwwH\n" 63 + "QW5kcm9pZDEQMA4GA1UEAwwHYW5kcm9pZDCCASIwDQYJKoZIhvcNAQEBBQADggEP\n" 64 + "ADCCAQoCggEBAMgyezTnRdmITmxXQNgG4UmCdvAaOQ7H+iB6wHfgT9iajoiGF9I9\n" 65 + "Efdx6QnnM6S3N4BD5MGb9IPvF79aXJlWgd9Q+l1vOG0bcpB9KVDrui1IjNW/R+X3\n" 66 + "0VKg2xa5+6kYTXnlI5GZF2pG8GCuoubsFkbfMTpmonAOdKDsfPLVSKbWoaNsFtli\n" 67 + "zXIpJDpK+QHY9yMvJ7lBme3f8OVOBC2OzetCUScTWl1Q9JqFHNgluk2in8mfwban\n" 68 + "DE7fdXGrnUNuJ31h5SBjLMAoaLTjxL9Vn0W3wiB/G8lVAgOJs+wJ5G7PVa/A7ZGB\n" 69 + "RGNySfpWs1yrpSUIxx4p9Gh3SVJ+WAceaH0CAwEAAaNTMFEwHQYDVR0OBBYEFJdE\n" 70 + "O9ssB/FgSEzvgrkLbthzJ4QpMB8GA1UdIwQYMBaAFJdEO9ssB/FgSEzvgrkLbthz\n" 71 + "J4QpMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAB//kA875v/7\n" 72 + "pOYFtacYYd6pU4JFhCnIpKTuc8ee3C1pJd9hScI1P5tiM8dnRscmTT5puE62OKE/\n" 73 + "XdlnCkBm9oAD2xXwK+W2fTFVidLHrjSdihTmyUbjfNOm5SS3Z6S9OmPPb4Ei4WXh\n" 74 + "qqCrk3OQ00A6agfTy0qzW1wQT9DE1uyLAZ1jdTMD2wNwzQP7IzoTx+ay985eRC1V\n" 75 + "pquK9kOHhnhGn/kSrZgpQB1rUmpm+IrdpwkIUIdnMyiuIrQa40D+bKRmOWpNKUH9\n" 76 + "4MCeitS4W9LfQyDj3hktD5hf4hxRIb185gN7v/Uf2Ft87rnFdtR1xum4JDagosOv\n" 77 + "vvF/HE7ofuw=\n"; 78 79 private static final String SELF_SIGNED_RSA_CERT_2_B64 = 80 "MIIDlDCCAnygAwIBAgIJAL4ZhppdcG5IMA0GCSqGSIb3DQEBCwUAMF8xCzAJBgNV\n" 81 + "BAYTAlVLMQ8wDQYDVQQIDAZMb25kb24xGzAZBgNVBAoMEkFPU1AgdGVzdCBkYXRh\n" 82 + "IHR3bzEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UEAwwHYW5kcm9pZDAeFw0xODA4\n" 83 + "MjMxNjA2NDhaFw0yODA4MjAxNjA2NDhaMF8xCzAJBgNVBAYTAlVLMQ8wDQYDVQQI\n" 84 + "DAZMb25kb24xGzAZBgNVBAoMEkFPU1AgdGVzdCBkYXRhIHR3bzEQMA4GA1UECwwH\n" 85 + "QW5kcm9pZDEQMA4GA1UEAwwHYW5kcm9pZDCCASIwDQYJKoZIhvcNAQEBBQADggEP\n" 86 + "ADCCAQoCggEBAMn7UvVsQquyotNNt9B/JCa84jcPfIV3RBDSYvcTrr1KyVIIJSmo\n" 87 + "JnUQYt6yRN9HjOOckuOSRAtzumqYuW1tpMDgDlORImwvX2pwQfJLT8dErgAaNGYu\n" 88 + "xjIUJ1Dwusuw891F/nFvACHOPWfgpcz4WJo1SQCUIObeuENebUurDnyYCMOD0+t3\n" 89 + "e4POaE6pF4VqjoDHX8slexonzkxZ3e7V2zrgpRBx+TUHq69GexpVj5frUSxjEllf\n" 90 + "58hNwwbPArpW73102wkqb7bCqeJ9f7fSY01hmhgIRn1uqy6jPfq9HqaXF+QhGVkT\n" 91 + "Oeoauu1o3/oTUmtbkifQvVGhsW/VX5v9hl0CAwEAAaNTMFEwHQYDVR0OBBYEFAO6\n" 92 + "1Tw6JddbJZI+iV0hIyRLOCGBMB8GA1UdIwQYMBaAFAO61Tw6JddbJZI+iV0hIyRL\n" 93 + "OCGBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAIQQEOvTqAn\n" 94 + "EpdZmrwQIBRvWT8pYyf4168CXEFDTXj5ODQY3IZ2hCOpIhJHPGc8RTVPNL0/LwIH\n" 95 + "fncgU+uLYL+CUH1DjqZK+99QvcxJkIi6+lQQynlCT4OXpo4AyLl4U4vpjKO9QUdi\n" 96 + "TiFXDMgM0iYyZU88ABgGYDsNUZuL+zj3rABszzeoxyQDjVrfUZQ1SnPYnN9wLlPj\n" 97 + "fFdyRBpJyZPOABXsJlB6AO3a5Erk6DBB3kj1EmN1b/oYqWgNBg2pBBW7fhxx8MVj\n" 98 + "q1bnYwvwp2Iq/oHoGbr80ZNl97i8YQiXEHG0N9Y+PzaTJ12bREA+0Q6ZBfe9V9ya\n" 99 + "IgPhDc8Rb0g=\n"; 100 101 // Generated with: 102 // openssl req -newkey ec:<(openssl ecparam -name secp256k1) -nodes -keyout key.pem -x509 \ 103 // -days 3650 -out certificate.pem 104 private static final String SELF_SIGNED_EC_CERT_1_B64 = 105 "MIICBDCCAaugAwIBAgIJAIOxj+lhuGDBMAoGCCqGSM49BAMCMF8xCzAJBgNVBAYT\n" 106 + "AlVLMQ8wDQYDVQQIDAZMb25kb24xGzAZBgNVBAoMEkFPU1AgdGVzdCBkYXRhIG9u\n" 107 + "ZTEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UEAwwHYW5kcm9pZDAeFw0xODA4MjMx\n" 108 + "NjEzMzJaFw0yODA4MjAxNjEzMzJaMF8xCzAJBgNVBAYTAlVLMQ8wDQYDVQQIDAZM\n" 109 + "b25kb24xGzAZBgNVBAoMEkFPU1AgdGVzdCBkYXRhIG9uZTEQMA4GA1UECwwHQW5k\n" 110 + "cm9pZDEQMA4GA1UEAwwHYW5kcm9pZDBWMBAGByqGSM49AgEGBSuBBAAKA0IABJ/K\n" 111 + "6Z1d4T+LdDKdl+QkiLs/oJ0fBQmVezo4H0tY7EOugsydZGaem0CyEtZX/0Nki4To\n" 112 + "XvgUB2jFGRERYPDkM/WjUzBRMB0GA1UdDgQWBBTsJksMH345+fGhJYmyiR9xJEXt\n" 113 + "MDAfBgNVHSMEGDAWgBTsJksMH345+fGhJYmyiR9xJEXtMDAPBgNVHRMBAf8EBTAD\n" 114 + "AQH/MAoGCCqGSM49BAMCA0cAMEQCIFDrt1eB11O/lD4CHAaaZQ82WvoXgJC89rol\n" 115 + "EuDcG/j3AiBJ80KVSTmim6k6RWEMIHP78mCLnpwKnwVAk9On5xkJ0Q=="; 116 117 private static final String SELF_SIGNED_EC_CERT_2_B64 = 118 "MIICBDCCAaugAwIBAgIJAMDRz9Ey2tIPMAoGCCqGSM49BAMCMF8xCzAJBgNVBAYT\n" 119 + "AlVLMQ8wDQYDVQQIDAZMb25kb24xGzAZBgNVBAoMEkFPU1AgdGVzdCBkYXRhIHR3\n" 120 + "bzEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UEAwwHYW5kcm9pZDAeFw0xODA4MjMx\n" 121 + "NjE0MDdaFw0yODA4MjAxNjE0MDdaMF8xCzAJBgNVBAYTAlVLMQ8wDQYDVQQIDAZM\n" 122 + "b25kb24xGzAZBgNVBAoMEkFPU1AgdGVzdCBkYXRhIHR3bzEQMA4GA1UECwwHQW5k\n" 123 + "cm9pZDEQMA4GA1UEAwwHYW5kcm9pZDBWMBAGByqGSM49AgEGBSuBBAAKA0IABA7p\n" 124 + "osQCNiI+RBy29ydpFGBPypbIy8U3Ylgujo8k4B20evWj5CKYP9Cw0gCypwBB9uYM\n" 125 + "706diiK6rFbKXFhhcUGjUzBRMB0GA1UdDgQWBBSYWWkKZqiiNXC75cZHoIpRwMMd\n" 126 + "7zAfBgNVHSMEGDAWgBSYWWkKZqiiNXC75cZHoIpRwMMd7zAPBgNVHRMBAf8EBTAD\n" 127 + "AQH/MAoGCCqGSM49BAMCA0cAMEQCIFh7LgrBMMMSqAF8PdWy+bV8jUuQqwOQ34Mo\n" 128 + "MtghI6eYAiAOuAXmRZiwVjnB9rH3f2Vy3rbMgfD3/AYzREqVnuZD0Q=="; 129 130 private static final X500Principal ISSUER_ONE = 131 new X500Principal("CN=android, OU=Android, O=AOSP test data one, ST=London, C=UK"); 132 133 private KeyInfoProvider mDummyInfoProvider; 134 private KeyChainActivity.CertificateParametersFilter mDummyChecker; 135 private byte[] mRSACertOne; 136 private byte[] mRSACertTwo; 137 private byte[] mECCertOne; 138 private byte[] mECCertTwo; 139 private ArrayList<byte[]> mIssuers; 140 private KeyStoreSpi mKeyStoreSpi; 141 private KeyStore mKeyStore; 142 private KeyInfoProvider mInfoProvider; 143 toCertificate(byte[] bytes)144 private Certificate toCertificate(byte[] bytes) throws CertificateException { 145 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 146 return cf.generateCertificate(new ByteArrayInputStream(bytes)); 147 } 148 toCertificateChain(byte[] bytes)149 private Certificate[] toCertificateChain(byte[] bytes) throws CertificateException { 150 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 151 return new Certificate[]{cf.generateCertificate(new ByteArrayInputStream(bytes))}; 152 } 153 154 @Before setUp()155 public void setUp() throws Exception { 156 mRSACertOne = Base64.decode(SELF_SIGNED_RSA_CERT_1_B64, Base64.DEFAULT); 157 mRSACertTwo = Base64.decode(SELF_SIGNED_RSA_CERT_2_B64, Base64.DEFAULT); 158 mECCertOne = Base64.decode(SELF_SIGNED_EC_CERT_1_B64, Base64.DEFAULT); 159 mECCertTwo = Base64.decode(SELF_SIGNED_EC_CERT_2_B64, Base64.DEFAULT); 160 mIssuers = new ArrayList<byte[]>(); 161 mIssuers.add(ISSUER_ONE.getEncoded()); 162 mDummyInfoProvider = 163 new KeyInfoProvider() { 164 public boolean isUserSelectable(String alias) { 165 return true; 166 } 167 }; 168 169 mDummyChecker = mock(KeyChainActivity.CertificateParametersFilter.class); 170 when(mDummyChecker.shouldPresentCertificate(Mockito.anyString())).thenReturn(true); 171 172 mKeyStoreSpi = mock(KeyStoreSpi.class); 173 mKeyStore = new KeyStore(mKeyStoreSpi, null, "test") {}; 174 mKeyStore.load(null); 175 mInfoProvider = mock(KeyInfoProvider.class); 176 } 177 178 @Test testAliasLoader_loadsAllAliases()179 public void testAliasLoader_loadsAllAliases() 180 throws InterruptedException, ExecutionException, CancellationException, 181 TimeoutException { 182 prepareKeyStoreWithAliases(ImmutableList.of("b", "c", "a")); 183 184 KeyChainActivity.AliasLoader loader = 185 new KeyChainActivity.AliasLoader( 186 mKeyStore, 187 RuntimeEnvironment.application, 188 mDummyInfoProvider, 189 mDummyChecker); 190 loader.execute(); 191 192 ShadowApplication.runBackgroundTasks(); 193 KeyChainActivity.CertificateAdapter result = loader.get(5, TimeUnit.SECONDS); 194 Assert.assertNotNull(result); 195 Assert.assertEquals(3, result.getCount()); 196 Assert.assertEquals("a", result.getItem(0)); 197 Assert.assertEquals("b", result.getItem(1)); 198 Assert.assertEquals("c", result.getItem(2)); 199 } 200 201 @Test testAliasLoader_copesWithNoAliases()202 public void testAliasLoader_copesWithNoAliases() 203 throws InterruptedException, ExecutionException, CancellationException, 204 TimeoutException { 205 when(mKeyStoreSpi.engineAliases()).thenReturn(Collections.enumeration(ImmutableList.of())); 206 207 KeyChainActivity.AliasLoader loader = 208 new KeyChainActivity.AliasLoader( 209 mKeyStore, 210 RuntimeEnvironment.application, 211 mDummyInfoProvider, 212 mDummyChecker); 213 loader.execute(); 214 215 ShadowApplication.runBackgroundTasks(); 216 KeyChainActivity.CertificateAdapter result = loader.get(5, TimeUnit.SECONDS); 217 Assert.assertNotNull(result); 218 Assert.assertEquals(0, result.getCount()); 219 } 220 221 @Test testAliasLoader_filtersNonUserSelectableAliases()222 public void testAliasLoader_filtersNonUserSelectableAliases() 223 throws InterruptedException, ExecutionException, CancellationException, 224 TimeoutException { 225 prepareKeyStoreWithAliases(ImmutableList.of("b", "c", "a")); 226 when(mInfoProvider.isUserSelectable("a")).thenReturn(false); 227 when(mInfoProvider.isUserSelectable("b")).thenReturn(true); 228 when(mInfoProvider.isUserSelectable("c")).thenReturn(false); 229 230 KeyChainActivity.AliasLoader loader = 231 new KeyChainActivity.AliasLoader( 232 mKeyStore, RuntimeEnvironment.application, mInfoProvider, mDummyChecker); 233 loader.execute(); 234 235 ShadowApplication.runBackgroundTasks(); 236 KeyChainActivity.CertificateAdapter result = loader.get(5, TimeUnit.SECONDS); 237 Assert.assertNotNull(result); 238 Assert.assertEquals(1, result.getCount()); 239 Assert.assertEquals("b", result.getItem(0)); 240 } 241 242 @Test testAliasLoader_filtersAliasesWithNonConformingParameters()243 public void testAliasLoader_filtersAliasesWithNonConformingParameters() 244 throws InterruptedException, ExecutionException, CancellationException, 245 TimeoutException, KeyStoreException { 246 prepareKeyStoreWithAliases(ImmutableList.of("a", "b", "c", "d")); 247 when(mInfoProvider.isUserSelectable("a")).thenReturn(true); 248 when(mInfoProvider.isUserSelectable("b")).thenReturn(true); 249 when(mInfoProvider.isUserSelectable("c")).thenReturn(false); 250 when(mInfoProvider.isUserSelectable("d")).thenReturn(false); 251 252 KeyChainActivity.CertificateParametersFilter checker = 253 mock(KeyChainActivity.CertificateParametersFilter.class); 254 // The first alias is user-selectable and should be presented. 255 when(checker.shouldPresentCertificate("a")).thenReturn(true); 256 // The second alias is user-selectable but should not be presented. 257 when(checker.shouldPresentCertificate("b")).thenReturn(false); 258 // The third alias is not user-selectable but should be presented. 259 when(checker.shouldPresentCertificate("c")).thenReturn(true); 260 // The fourth alias is not user-selectable and should not be presented. 261 when(checker.shouldPresentCertificate("d")).thenReturn(false); 262 263 KeyChainActivity.AliasLoader loader = 264 new KeyChainActivity.AliasLoader( 265 mKeyStore, RuntimeEnvironment.application, mInfoProvider, checker); 266 loader.execute(); 267 268 ShadowApplication.runBackgroundTasks(); 269 KeyChainActivity.CertificateAdapter result = loader.get(5, TimeUnit.SECONDS); 270 Assert.assertNotNull(result); 271 Assert.assertEquals(1, result.getCount()); 272 Assert.assertEquals("a", result.getItem(0)); 273 } 274 prepareKeyStoreWithAliases(ImmutableList<String> aliases)275 private void prepareKeyStoreWithAliases(ImmutableList<String> aliases) { 276 when(mKeyStoreSpi.engineAliases()).thenReturn(Collections.enumeration(aliases)); 277 for (int i = 0; i < aliases.size(); i++) { 278 when(mKeyStoreSpi.engineIsKeyEntry(aliases.get(i))).thenReturn(true); 279 } 280 } 281 prepareKeyStoreWithCertificates()282 private void prepareKeyStoreWithCertificates() throws CertificateException { 283 when(mKeyStoreSpi.engineGetCertificate("rsa1")).thenReturn(toCertificate(mRSACertOne)); 284 when(mKeyStoreSpi.engineGetCertificate("ec1")).thenReturn(toCertificate(mECCertOne)); 285 when(mKeyStoreSpi.engineGetCertificate("rsa2")).thenReturn(toCertificate(mRSACertTwo)); 286 when(mKeyStoreSpi.engineGetCertificate("ec2")).thenReturn(toCertificate(mECCertTwo)); 287 288 when(mKeyStoreSpi.engineGetCertificateChain("rsa1")) 289 .thenReturn(toCertificateChain(mRSACertOne)); 290 when(mKeyStoreSpi.engineGetCertificateChain("ec1")) 291 .thenReturn(toCertificateChain(mECCertOne)); 292 when(mKeyStoreSpi.engineGetCertificateChain("rsa2")) 293 .thenReturn(toCertificateChain(mRSACertTwo)); 294 when(mKeyStoreSpi.engineGetCertificateChain("ec2")) 295 .thenReturn(toCertificateChain(mECCertTwo)); 296 } 297 298 @Test testCertificateParametersFilter_filtersByKey()299 public void testCertificateParametersFilter_filtersByKey() 300 throws CancellationException, CertificateException { 301 prepareKeyStoreWithCertificates(); 302 303 KeyChainActivity.CertificateParametersFilter ec_checker = 304 new KeyChainActivity.CertificateParametersFilter( 305 mKeyStore, new String[] {"EC"}, new ArrayList<byte[]>()); 306 Assert.assertFalse(ec_checker.shouldPresentCertificate("rsa1")); 307 Assert.assertTrue(ec_checker.shouldPresentCertificate("ec1")); 308 309 KeyChainActivity.CertificateParametersFilter rsa_and_ec_checker = 310 new KeyChainActivity.CertificateParametersFilter( 311 mKeyStore, new String[] {"EC", "RSA"}, new ArrayList<byte[]>()); 312 Assert.assertTrue(rsa_and_ec_checker.shouldPresentCertificate("rsa1")); 313 Assert.assertTrue(rsa_and_ec_checker.shouldPresentCertificate("ec1")); 314 } 315 316 @Test testCertificateParametersFilter_filtersByIssuer()317 public void testCertificateParametersFilter_filtersByIssuer() 318 throws CancellationException, CertificateException { 319 prepareKeyStoreWithCertificates(); 320 321 KeyChainActivity.CertificateParametersFilter issuer_checker = 322 new KeyChainActivity.CertificateParametersFilter( 323 mKeyStore, new String[] {}, mIssuers); 324 Assert.assertTrue(issuer_checker.shouldPresentCertificate("rsa1")); 325 Assert.assertTrue(issuer_checker.shouldPresentCertificate("ec1")); 326 Assert.assertFalse(issuer_checker.shouldPresentCertificate("rsa2")); 327 Assert.assertFalse(issuer_checker.shouldPresentCertificate("ec2")); 328 } 329 330 @Test testCertificateParametersFilter_filtersByIssuerAndKey()331 public void testCertificateParametersFilter_filtersByIssuerAndKey() 332 throws CancellationException, CertificateException { 333 prepareKeyStoreWithCertificates(); 334 335 KeyChainActivity.CertificateParametersFilter issuer_checker = 336 new KeyChainActivity.CertificateParametersFilter( 337 mKeyStore, new String[] {"EC"}, mIssuers); 338 Assert.assertFalse(issuer_checker.shouldPresentCertificate("rsa1")); 339 Assert.assertTrue(issuer_checker.shouldPresentCertificate("ec1")); 340 Assert.assertFalse(issuer_checker.shouldPresentCertificate("rsa2")); 341 Assert.assertFalse(issuer_checker.shouldPresentCertificate("ec2")); 342 } 343 } 344