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.keystore2; 18 19 import android.app.ActivityThread; 20 import android.hardware.biometrics.BiometricManager; 21 import android.hardware.security.keymint.ErrorCode; 22 import android.security.GateKeeper; 23 import android.security.KeyStore; 24 import android.security.KeyStoreException; 25 import android.security.KeyStoreOperation; 26 import android.security.keymaster.KeymasterDefs; 27 import android.security.keystore.KeyExpiredException; 28 import android.security.keystore.KeyNotYetValidException; 29 import android.security.keystore.KeyPermanentlyInvalidatedException; 30 import android.security.keystore.UserNotAuthenticatedException; 31 import android.system.keystore2.Authorization; 32 import android.system.keystore2.ResponseCode; 33 import android.util.Log; 34 35 import libcore.util.EmptyArray; 36 37 import java.security.GeneralSecurityException; 38 import java.security.InvalidAlgorithmParameterException; 39 import java.security.InvalidKeyException; 40 import java.security.SecureRandom; 41 import java.util.ArrayList; 42 import java.util.List; 43 44 /** 45 * Assorted utility methods for implementing crypto operations on top of KeyStore. 46 * 47 * @hide 48 */ 49 abstract class KeyStoreCryptoOperationUtils { 50 51 private static volatile SecureRandom sRng; 52 KeyStoreCryptoOperationUtils()53 private KeyStoreCryptoOperationUtils() {} 54 55 canUserAuthorizationSucceed(AndroidKeyStoreKey key)56 public static boolean canUserAuthorizationSucceed(AndroidKeyStoreKey key) { 57 List<Long> keySids = new ArrayList<Long>(); 58 for (Authorization p : key.getAuthorizations()) { 59 switch(p.keyParameter.tag) { 60 case KeymasterDefs.KM_TAG_USER_SECURE_ID: 61 keySids.add(p.keyParameter.value.getLongInteger()); 62 break; 63 default: 64 break; 65 } 66 } 67 if (keySids.isEmpty()) { 68 // Key is not bound to any SIDs -- no amount of authentication will help here. 69 return false; 70 } 71 long rootSid = GateKeeper.getSecureUserId(); 72 if ((rootSid != 0) && (keySids.contains(rootSid))) { 73 // One of the key's SIDs is the current root SID -- user can be authenticated 74 // against that SID. 75 return true; 76 } 77 78 long[] biometricSids = ActivityThread 79 .currentApplication() 80 .getSystemService(BiometricManager.class) 81 .getAuthenticatorIds(); 82 83 // The key must contain every biometric SID. This is because the current API surface 84 // treats all biometrics (capable of keystore integration) equally. e.g. if the 85 // device has multiple keystore-capable sensors, and one of the sensor's SIDs 86 // changed, 1) there is no way for a developer to specify authentication with a 87 // specific sensor (the one that hasn't changed), and 2) currently the only 88 // signal to developers is the UserNotAuthenticatedException, which doesn't 89 // indicate a specific sensor. 90 boolean canUnlockViaBiometrics = biometricSids.length > 0; 91 for (long sid : biometricSids) { 92 if (!keySids.contains(sid)) { 93 canUnlockViaBiometrics = false; 94 break; 95 } 96 } 97 98 if (canUnlockViaBiometrics) { 99 // All of the biometric SIDs are contained in the key's SIDs. 100 return true; 101 } 102 103 // None of the key's SIDs can ever be authenticated 104 return false; 105 } 106 107 /** 108 * Returns an {@link InvalidKeyException} corresponding to the provided 109 * {@link KeyStoreException}. 110 */ getInvalidKeyException( AndroidKeyStoreKey key, KeyStoreException e)111 public static InvalidKeyException getInvalidKeyException( 112 AndroidKeyStoreKey key, KeyStoreException e) { 113 switch (e.getErrorCode()) { 114 case KeymasterDefs.KM_ERROR_KEY_EXPIRED: 115 return new KeyExpiredException(); 116 case KeymasterDefs.KM_ERROR_KEY_NOT_YET_VALID: 117 return new KeyNotYetValidException(); 118 case ResponseCode.KEY_NOT_FOUND: 119 // TODO is this the right exception in this case? 120 case ResponseCode.KEY_PERMANENTLY_INVALIDATED: 121 return new KeyPermanentlyInvalidatedException(); 122 case ResponseCode.LOCKED: 123 case ResponseCode.UNINITIALIZED: 124 case KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED: 125 // TODO b/173111727 remove response codes LOCKED and UNINITIALIZED 126 return new UserNotAuthenticatedException(); 127 default: 128 return new InvalidKeyException("Keystore operation failed", e); 129 } 130 } 131 132 /** 133 * Returns the exception to be thrown by the {@code Cipher.init} method of the crypto operation 134 * in response to {@code KeyStore.begin} operation or {@code null} if the {@code init} method 135 * should succeed. 136 */ getExceptionForCipherInit( AndroidKeyStoreKey key, KeyStoreException e)137 public static GeneralSecurityException getExceptionForCipherInit( 138 AndroidKeyStoreKey key, KeyStoreException e) { 139 if (e.getErrorCode() == KeyStore.NO_ERROR) { 140 return null; 141 } 142 143 // Cipher-specific cases 144 switch (e.getErrorCode()) { 145 case KeymasterDefs.KM_ERROR_INVALID_NONCE: 146 return new InvalidAlgorithmParameterException("Invalid IV"); 147 case KeymasterDefs.KM_ERROR_CALLER_NONCE_PROHIBITED: 148 return new InvalidAlgorithmParameterException("Caller-provided IV not permitted"); 149 } 150 151 // General cases 152 return getInvalidKeyException(key, e); 153 } 154 155 /** 156 * Returns the requested number of random bytes to mix into keystore/keymaster RNG. 157 * 158 * @param rng RNG from which to obtain the random bytes or {@code null} for the platform-default 159 * RNG. 160 */ getRandomBytesToMixIntoKeystoreRng(SecureRandom rng, int sizeBytes)161 static byte[] getRandomBytesToMixIntoKeystoreRng(SecureRandom rng, int sizeBytes) { 162 if (sizeBytes <= 0) { 163 return EmptyArray.BYTE; 164 } 165 if (rng == null) { 166 rng = getRng(); 167 } 168 byte[] result = new byte[sizeBytes]; 169 rng.nextBytes(result); 170 return result; 171 } 172 getRng()173 private static SecureRandom getRng() { 174 // IMPLEMENTATION NOTE: It's OK to share a SecureRandom instance because SecureRandom is 175 // required to be thread-safe. 176 if (sRng == null) { 177 sRng = new SecureRandom(); 178 } 179 return sRng; 180 } 181 abortOperation(KeyStoreOperation operation)182 static void abortOperation(KeyStoreOperation operation) { 183 if (operation != null) { 184 try { 185 operation.abort(); 186 } catch (KeyStoreException e) { 187 // Invalid operation handle is very common at this point. It occurs every time 188 // an already finalized operation gets aborted. 189 if (e.getErrorCode() != ErrorCode.INVALID_OPERATION_HANDLE) { 190 // This error gets logged but ignored. Dropping the reference 191 // to the KeyStoreOperation is enough to clean up all related resources even 192 // in the Keystore daemon. It gets logged anyway, because it may indicate some 193 // underlying problem that is worth debugging. 194 Log.w( 195 "KeyStoreCryptoOperationUtils", 196 "Encountered error trying to abort a keystore operation.", 197 e 198 ); 199 } 200 } 201 } 202 } 203 getOrMakeOperationChallenge(KeyStoreOperation operation, AndroidKeyStoreKey key)204 static long getOrMakeOperationChallenge(KeyStoreOperation operation, AndroidKeyStoreKey key) 205 throws KeyPermanentlyInvalidatedException { 206 if (operation.getChallenge() != null) { 207 if (!KeyStoreCryptoOperationUtils.canUserAuthorizationSucceed(key)) { 208 throw new KeyPermanentlyInvalidatedException(); 209 } 210 return operation.getChallenge(); 211 } else { 212 // Keystore won't give us an operation challenge if the operation doesn't 213 // need user authorization. So we make our own. 214 return getRng().nextLong(); 215 } 216 } 217 } 218