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