1 /* 2 * Copyright (C) 2018 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.server.locksettings.recoverablekeystore; 18 19 import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_CERTIFICATE; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.os.RemoteException; 24 import android.os.ServiceSpecificException; 25 import android.security.keystore.recovery.TrustedRootCertificates; 26 import android.util.Log; 27 import android.util.Pair; 28 29 import com.android.internal.widget.LockPatternUtils; 30 31 import java.security.cert.X509Certificate; 32 import java.util.Date; 33 import java.util.HashMap; 34 import java.util.Map; 35 36 import javax.crypto.SecretKey; 37 38 /** 39 * The class provides helper methods to support end-to-end test with insecure certificate. 40 */ 41 public class TestOnlyInsecureCertificateHelper { 42 private static final String TAG = "TestCertHelper"; 43 44 /** 45 * Constructor for the helper class. 46 */ TestOnlyInsecureCertificateHelper()47 public TestOnlyInsecureCertificateHelper() { 48 } 49 50 /** 51 * Returns a root certificate installed in the system for given alias. 52 * Returns default secure certificate if alias is empty or null. 53 * Can return insecure certificate for its alias. 54 */ 55 public @NonNull X509Certificate getRootCertificate(String rootCertificateAlias)56 getRootCertificate(String rootCertificateAlias) throws RemoteException { 57 rootCertificateAlias = getDefaultCertificateAliasIfEmpty(rootCertificateAlias); 58 if (isTestOnlyCertificateAlias(rootCertificateAlias)) { 59 return TrustedRootCertificates.getTestOnlyInsecureCertificate(); 60 } 61 62 X509Certificate rootCertificate = 63 TrustedRootCertificates.getRootCertificate(rootCertificateAlias); 64 if (rootCertificate == null) { 65 throw new ServiceSpecificException( 66 ERROR_INVALID_CERTIFICATE, "The provided root certificate alias is invalid"); 67 } 68 return rootCertificate; 69 } 70 71 /** 72 * Returns hardcoded validation date for e2e tests. 73 */ getValidationDate(String rootCertificateAlias)74 public @Nullable Date getValidationDate(String rootCertificateAlias) { 75 if (isTestOnlyCertificateAlias(rootCertificateAlias)) { 76 // Certificate used for e2e test is expired. 77 return new Date(2019 - 1900, 1, 30); 78 } else { 79 return null; // Use current time 80 } 81 } 82 getDefaultCertificateAliasIfEmpty( @ullable String rootCertificateAlias)83 public @NonNull String getDefaultCertificateAliasIfEmpty( 84 @Nullable String rootCertificateAlias) { 85 if (rootCertificateAlias == null || rootCertificateAlias.isEmpty()) { 86 Log.e(TAG, "rootCertificateAlias is null or empty - use secure default value"); 87 // Use the default Google Key Vault Service CA certificate if the alias is not provided 88 rootCertificateAlias = TrustedRootCertificates.GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS; 89 } 90 return rootCertificateAlias; 91 } 92 isTestOnlyCertificateAlias(String rootCertificateAlias)93 public boolean isTestOnlyCertificateAlias(String rootCertificateAlias) { 94 return TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS 95 .equals(rootCertificateAlias); 96 } 97 isValidRootCertificateAlias(String rootCertificateAlias)98 public boolean isValidRootCertificateAlias(String rootCertificateAlias) { 99 return TrustedRootCertificates.getRootCertificates().containsKey(rootCertificateAlias) 100 || isTestOnlyCertificateAlias(rootCertificateAlias); 101 } 102 103 /** 104 * Checks whether a password is in "Insecure mode" 105 * @param credentialType the type of credential, e.g. pattern and password 106 * @param credential the pattern or password 107 * @return true, if the credential is in "Insecure mode" 108 */ doesCredentialSupportInsecureMode(int credentialType, byte[] credential)109 public boolean doesCredentialSupportInsecureMode(int credentialType, byte[] credential) { 110 if (credential == null) { 111 return false; 112 } 113 if (credentialType != LockPatternUtils.CREDENTIAL_TYPE_PASSWORD 114 && credentialType != LockPatternUtils.CREDENTIAL_TYPE_PIN) { 115 return false; 116 } 117 byte[] insecurePasswordPrefixBytes = 118 TrustedRootCertificates.INSECURE_PASSWORD_PREFIX.getBytes(); 119 if (credential.length < insecurePasswordPrefixBytes.length) { 120 return false; 121 } 122 for (int i = 0; i < insecurePasswordPrefixBytes.length; i++) { 123 if (credential[i] != insecurePasswordPrefixBytes[i]) { 124 return false; 125 } 126 } 127 return true; 128 } 129 keepOnlyWhitelistedInsecureKeys( Map<String, Pair<SecretKey, byte[]>> rawKeys)130 public Map<String, Pair<SecretKey, byte[]>> keepOnlyWhitelistedInsecureKeys( 131 Map<String, Pair<SecretKey, byte[]>> rawKeys) { 132 if (rawKeys == null) { 133 return null; 134 } 135 Map<String, Pair<SecretKey, byte[]>> filteredKeys = new HashMap<>(); 136 for (Map.Entry<String, Pair<SecretKey, byte[]>> entry : rawKeys.entrySet()) { 137 String alias = entry.getKey(); 138 if (alias != null 139 && alias.startsWith(TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX)) { 140 filteredKeys.put(entry.getKey(), 141 Pair.create(entry.getValue().first, entry.getValue().second)); 142 Log.d(TAG, "adding key with insecure alias " + alias + " to the recovery snapshot"); 143 } 144 } 145 return filteredKeys; 146 } 147 } 148