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