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