1 /* 2 * Copyright (C) 2015 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 android.security.keystore; 18 19 import android.security.Credentials; 20 import android.security.GateKeeper; 21 import android.security.KeyStore; 22 import android.security.keymaster.KeyCharacteristics; 23 import android.security.keymaster.KeymasterDefs; 24 25 import java.math.BigInteger; 26 import java.security.InvalidKeyException; 27 import java.security.ProviderException; 28 import java.security.spec.InvalidKeySpecException; 29 import java.security.spec.KeySpec; 30 import java.util.ArrayList; 31 import java.util.Date; 32 import java.util.List; 33 34 import javax.crypto.SecretKey; 35 import javax.crypto.SecretKeyFactorySpi; 36 import javax.crypto.spec.SecretKeySpec; 37 38 /** 39 * {@link SecretKeyFactorySpi} backed by Android Keystore. 40 * 41 * @hide 42 */ 43 public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { 44 45 private final KeyStore mKeyStore = KeyStore.getInstance(); 46 47 @Override engineGetKeySpec(SecretKey key, @SuppressWarnings("rawtypes") Class keySpecClass)48 protected KeySpec engineGetKeySpec(SecretKey key, 49 @SuppressWarnings("rawtypes") Class keySpecClass) throws InvalidKeySpecException { 50 if (keySpecClass == null) { 51 throw new InvalidKeySpecException("keySpecClass == null"); 52 } 53 if (!(key instanceof AndroidKeyStoreSecretKey)) { 54 throw new InvalidKeySpecException("Only Android KeyStore secret keys supported: " + 55 ((key != null) ? key.getClass().getName() : "null")); 56 } 57 if (SecretKeySpec.class.isAssignableFrom(keySpecClass)) { 58 throw new InvalidKeySpecException( 59 "Key material export of Android KeyStore keys is not supported"); 60 } 61 if (!KeyInfo.class.equals(keySpecClass)) { 62 throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName()); 63 } 64 AndroidKeyStoreKey keystoreKey = (AndroidKeyStoreKey) key; 65 String keyAliasInKeystore = keystoreKey.getAlias(); 66 String entryAlias; 67 if (keyAliasInKeystore.startsWith(Credentials.USER_PRIVATE_KEY)) { 68 entryAlias = keyAliasInKeystore.substring(Credentials.USER_PRIVATE_KEY.length()); 69 } else if (keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)){ 70 // key has legacy prefix 71 entryAlias = keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length()); 72 } else { 73 throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore); 74 } 75 76 return getKeyInfo(mKeyStore, entryAlias, keyAliasInKeystore, keystoreKey.getUid()); 77 } 78 getKeyInfo(KeyStore keyStore, String entryAlias, String keyAliasInKeystore, int keyUid)79 static KeyInfo getKeyInfo(KeyStore keyStore, String entryAlias, String keyAliasInKeystore, 80 int keyUid) { 81 KeyCharacteristics keyCharacteristics = new KeyCharacteristics(); 82 int errorCode = keyStore.getKeyCharacteristics( 83 keyAliasInKeystore, null, null, keyUid, keyCharacteristics); 84 if (errorCode != KeyStore.NO_ERROR) { 85 throw new ProviderException("Failed to obtain information about key." 86 + " Keystore error: " + errorCode); 87 } 88 89 boolean insideSecureHardware; 90 @KeyProperties.OriginEnum int origin; 91 int keySize; 92 @KeyProperties.PurposeEnum int purposes; 93 String[] encryptionPaddings; 94 String[] signaturePaddings; 95 @KeyProperties.DigestEnum String[] digests; 96 @KeyProperties.BlockModeEnum String[] blockModes; 97 int keymasterSwEnforcedUserAuthenticators; 98 int keymasterHwEnforcedUserAuthenticators; 99 List<BigInteger> keymasterSecureUserIds; 100 try { 101 if (keyCharacteristics.hwEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) { 102 insideSecureHardware = true; 103 origin = KeyProperties.Origin.fromKeymaster( 104 keyCharacteristics.hwEnforced.getEnum(KeymasterDefs.KM_TAG_ORIGIN, -1)); 105 } else if (keyCharacteristics.swEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) { 106 insideSecureHardware = false; 107 origin = KeyProperties.Origin.fromKeymaster( 108 keyCharacteristics.swEnforced.getEnum(KeymasterDefs.KM_TAG_ORIGIN, -1)); 109 } else { 110 throw new ProviderException("Key origin not available"); 111 } 112 long keySizeUnsigned = 113 keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1); 114 if (keySizeUnsigned == -1) { 115 throw new ProviderException("Key size not available"); 116 } else if (keySizeUnsigned > Integer.MAX_VALUE) { 117 throw new ProviderException("Key too large: " + keySizeUnsigned + " bits"); 118 } 119 keySize = (int) keySizeUnsigned; 120 purposes = KeyProperties.Purpose.allFromKeymaster( 121 keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_PURPOSE)); 122 123 List<String> encryptionPaddingsList = new ArrayList<String>(); 124 List<String> signaturePaddingsList = new ArrayList<String>(); 125 // Keymaster stores both types of paddings in the same array -- we split it into two. 126 for (int keymasterPadding : keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_PADDING)) { 127 try { 128 @KeyProperties.EncryptionPaddingEnum String jcaPadding = 129 KeyProperties.EncryptionPadding.fromKeymaster(keymasterPadding); 130 encryptionPaddingsList.add(jcaPadding); 131 } catch (IllegalArgumentException e) { 132 try { 133 @KeyProperties.SignaturePaddingEnum String padding = 134 KeyProperties.SignaturePadding.fromKeymaster(keymasterPadding); 135 signaturePaddingsList.add(padding); 136 } catch (IllegalArgumentException e2) { 137 throw new ProviderException( 138 "Unsupported encryption padding: " + keymasterPadding); 139 } 140 } 141 142 } 143 encryptionPaddings = 144 encryptionPaddingsList.toArray(new String[encryptionPaddingsList.size()]); 145 signaturePaddings = 146 signaturePaddingsList.toArray(new String[signaturePaddingsList.size()]); 147 148 digests = KeyProperties.Digest.allFromKeymaster( 149 keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_DIGEST)); 150 blockModes = KeyProperties.BlockMode.allFromKeymaster( 151 keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_BLOCK_MODE)); 152 keymasterSwEnforcedUserAuthenticators = 153 keyCharacteristics.swEnforced.getEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0); 154 keymasterHwEnforcedUserAuthenticators = 155 keyCharacteristics.hwEnforced.getEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0); 156 keymasterSecureUserIds = 157 keyCharacteristics.getUnsignedLongs(KeymasterDefs.KM_TAG_USER_SECURE_ID); 158 } catch (IllegalArgumentException e) { 159 throw new ProviderException("Unsupported key characteristic", e); 160 } 161 162 Date keyValidityStart = keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME); 163 Date keyValidityForOriginationEnd = 164 keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME); 165 Date keyValidityForConsumptionEnd = 166 keyCharacteristics.getDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME); 167 boolean userAuthenticationRequired = 168 !keyCharacteristics.getBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); 169 long userAuthenticationValidityDurationSeconds = 170 keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, -1); 171 if (userAuthenticationValidityDurationSeconds > Integer.MAX_VALUE) { 172 throw new ProviderException("User authentication timeout validity too long: " 173 + userAuthenticationValidityDurationSeconds + " seconds"); 174 } 175 boolean userAuthenticationRequirementEnforcedBySecureHardware = (userAuthenticationRequired) 176 && (keymasterHwEnforcedUserAuthenticators != 0) 177 && (keymasterSwEnforcedUserAuthenticators == 0); 178 boolean userAuthenticationValidWhileOnBody = 179 keyCharacteristics.hwEnforced.getBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY); 180 boolean trustedUserPresenceRequred = 181 keyCharacteristics.hwEnforced.getBoolean( 182 KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED); 183 184 boolean invalidatedByBiometricEnrollment = false; 185 if (keymasterSwEnforcedUserAuthenticators == KeymasterDefs.HW_AUTH_BIOMETRIC 186 || keymasterHwEnforcedUserAuthenticators == KeymasterDefs.HW_AUTH_BIOMETRIC) { 187 // Fingerprint-only key; will be invalidated if the root SID isn't in the SID list. 188 invalidatedByBiometricEnrollment = keymasterSecureUserIds != null 189 && !keymasterSecureUserIds.isEmpty() 190 && !keymasterSecureUserIds.contains(getGateKeeperSecureUserId()); 191 } 192 193 boolean userConfirmationRequired = keyCharacteristics.hwEnforced.getBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED); 194 195 return new KeyInfo(entryAlias, 196 insideSecureHardware, 197 origin, 198 keySize, 199 keyValidityStart, 200 keyValidityForOriginationEnd, 201 keyValidityForConsumptionEnd, 202 purposes, 203 encryptionPaddings, 204 signaturePaddings, 205 digests, 206 blockModes, 207 userAuthenticationRequired, 208 (int) userAuthenticationValidityDurationSeconds, 209 userAuthenticationRequirementEnforcedBySecureHardware, 210 userAuthenticationValidWhileOnBody, 211 trustedUserPresenceRequred, 212 invalidatedByBiometricEnrollment, 213 userConfirmationRequired); 214 } 215 getGateKeeperSecureUserId()216 private static BigInteger getGateKeeperSecureUserId() throws ProviderException { 217 try { 218 return BigInteger.valueOf(GateKeeper.getSecureUserId()); 219 } catch (IllegalStateException e) { 220 throw new ProviderException("Failed to get GateKeeper secure user ID", e); 221 } 222 } 223 224 @Override engineGenerateSecret(KeySpec keySpec)225 protected SecretKey engineGenerateSecret(KeySpec keySpec) throws InvalidKeySpecException { 226 throw new InvalidKeySpecException( 227 "To generate secret key in Android Keystore, use KeyGenerator initialized with " 228 + KeyGenParameterSpec.class.getName()); 229 } 230 231 @Override engineTranslateKey(SecretKey key)232 protected SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException { 233 if (key == null) { 234 throw new InvalidKeyException("key == null"); 235 } else if (!(key instanceof AndroidKeyStoreSecretKey)) { 236 throw new InvalidKeyException( 237 "To import a secret key into Android Keystore, use KeyStore.setEntry"); 238 } 239 240 return key; 241 } 242 } 243