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