• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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;
18 
19 import android.annotation.NonNull;
20 import android.app.compat.CompatChanges;
21 import android.hardware.security.keymint.KeyParameter;
22 import android.os.Binder;
23 import android.os.RemoteException;
24 import android.os.ServiceSpecificException;
25 import android.security.keystore.BackendBusyException;
26 import android.security.keystore.KeyStoreConnectException;
27 import android.system.keystore2.AuthenticatorSpec;
28 import android.system.keystore2.CreateOperationResponse;
29 import android.system.keystore2.IKeystoreSecurityLevel;
30 import android.system.keystore2.KeyDescriptor;
31 import android.system.keystore2.KeyMetadata;
32 import android.system.keystore2.ResponseCode;
33 import android.util.Log;
34 
35 import java.util.Calendar;
36 import java.util.Collection;
37 
38 /**
39  * This is a shim around the security level specific interface of Keystore 2.0. Services with
40  * this interface are instantiated per KeyMint backend, each having there own security level.
41  * Thus this object representation of a security level.
42  * @hide
43  */
44 public class KeyStoreSecurityLevel {
45     private static final String TAG = "KeyStoreSecurityLevel";
46     private final IKeystoreSecurityLevel mSecurityLevel;
47 
KeyStoreSecurityLevel(IKeystoreSecurityLevel securityLevel)48     public KeyStoreSecurityLevel(IKeystoreSecurityLevel securityLevel) {
49         Binder.allowBlocking(securityLevel.asBinder());
50         this.mSecurityLevel = securityLevel;
51     }
52 
handleExceptions(CheckedRemoteRequest<R> request)53     private <R> R handleExceptions(CheckedRemoteRequest<R> request) throws KeyStoreException {
54         try {
55             return request.execute();
56         } catch (ServiceSpecificException e) {
57             throw KeyStore2.getKeyStoreException(e.errorCode);
58         } catch (RemoteException e) {
59             // Log exception and report invalid operation handle.
60             // This should prompt the caller drop the reference to this operation and retry.
61             Log.e(TAG, "Could not connect to Keystore.", e);
62             throw new KeyStoreException(ResponseCode.SYSTEM_ERROR, "");
63         }
64     }
65 
66     /**
67      * Creates a new keystore operation.
68      * @see IKeystoreSecurityLevel#createOperation(KeyDescriptor, KeyParameter[], boolean) for more
69      * details.
70      * @param keyDescriptor
71      * @param args
72      * @return
73      * @throws KeyStoreException
74      * @hide
75      */
createOperation(@onNull KeyDescriptor keyDescriptor, Collection<KeyParameter> args)76     public KeyStoreOperation createOperation(@NonNull KeyDescriptor keyDescriptor,
77             Collection<KeyParameter> args) throws KeyStoreException {
78         while (true) {
79             try {
80                 CreateOperationResponse createOperationResponse =
81                         mSecurityLevel.createOperation(
82                                 keyDescriptor,
83                                 args.toArray(new KeyParameter[args.size()]),
84                                 false /* forced */
85                         );
86                 Long challenge = null;
87                 if (createOperationResponse.operationChallenge != null) {
88                     challenge = createOperationResponse.operationChallenge.challenge;
89                 }
90                 KeyParameter[] parameters = null;
91                 if (createOperationResponse.parameters != null) {
92                     parameters = createOperationResponse.parameters.keyParameter;
93                 }
94                 return new KeyStoreOperation(
95                         createOperationResponse.iOperation,
96                         challenge,
97                         parameters);
98             } catch (ServiceSpecificException e) {
99                 switch (e.errorCode) {
100                     case ResponseCode.BACKEND_BUSY: {
101                         long backOffHint = (long) (Math.random() * 80 + 20);
102                         if (CompatChanges.isChangeEnabled(
103                                 KeyStore2.KEYSTORE_OPERATION_CREATION_MAY_FAIL)) {
104                             // Starting with Android S we inform the caller about the
105                             // backend being busy.
106                             throw new BackendBusyException(backOffHint);
107                         } else {
108                             // Before Android S operation creation must always succeed. So we
109                             // just have to retry. We do so with a randomized back-off between
110                             // 20 and 100ms.
111                             // It is a little awkward that we cannot break out of this loop
112                             // by interrupting this thread. But that is the expected behavior.
113                             // There is some comfort in the fact that interrupting a thread
114                             // also does not unblock a thread waiting for a binder transaction.
115                             interruptedPreservingSleep(backOffHint);
116                         }
117                         break;
118                     }
119                     default:
120                         throw KeyStore2.getKeyStoreException(e.errorCode);
121                 }
122             } catch (RemoteException e) {
123                 Log.w(TAG, "Cannot connect to keystore", e);
124                 throw new KeyStoreConnectException();
125             }
126         }
127     }
128 
129     /**
130      * Generates a new key in Keystore.
131      * @see IKeystoreSecurityLevel#generateKey(KeyDescriptor, KeyDescriptor, KeyParameter[], int,
132      * byte[]) for more details.
133      * @param descriptor
134      * @param attestationKey
135      * @param args
136      * @param flags
137      * @param entropy
138      * @return
139      * @throws KeyStoreException
140      * @hide
141      */
generateKey(@onNull KeyDescriptor descriptor, KeyDescriptor attestationKey, Collection<KeyParameter> args, int flags, byte[] entropy)142     public KeyMetadata generateKey(@NonNull KeyDescriptor descriptor, KeyDescriptor attestationKey,
143             Collection<KeyParameter> args, int flags, byte[] entropy)
144             throws KeyStoreException {
145         return handleExceptions(() -> mSecurityLevel.generateKey(
146                 descriptor, attestationKey, args.toArray(new KeyParameter[args.size()]),
147                 flags, entropy));
148     }
149 
150     /**
151      * Imports a key into Keystore.
152      * @see IKeystoreSecurityLevel#importKey(KeyDescriptor, KeyDescriptor, KeyParameter[], int,
153      * byte[]) for more details.
154      * @param descriptor
155      * @param attestationKey
156      * @param args
157      * @param flags
158      * @param keyData
159      * @return
160      * @throws KeyStoreException
161      * @hide
162      */
importKey(KeyDescriptor descriptor, KeyDescriptor attestationKey, Collection<KeyParameter> args, int flags, byte[] keyData)163     public KeyMetadata importKey(KeyDescriptor descriptor, KeyDescriptor attestationKey,
164             Collection<KeyParameter> args, int flags, byte[] keyData)
165             throws KeyStoreException {
166         return handleExceptions(() -> mSecurityLevel.importKey(descriptor, attestationKey,
167                 args.toArray(new KeyParameter[args.size()]), flags, keyData));
168     }
169 
170     /**
171      * Imports a wrapped key into Keystore.
172      * @see IKeystoreSecurityLevel#importWrappedKey(KeyDescriptor, KeyDescriptor, byte[],
173      * KeyParameter[], AuthenticatorSpec[]) for more details.
174      * @param wrappedKeyDescriptor
175      * @param wrappingKeyDescriptor
176      * @param wrappedKey
177      * @param maskingKey
178      * @param args
179      * @param authenticatorSpecs
180      * @return
181      * @throws KeyStoreException
182      * @hide
183      */
importWrappedKey(@onNull KeyDescriptor wrappedKeyDescriptor, @NonNull KeyDescriptor wrappingKeyDescriptor, @NonNull byte[] wrappedKey, byte[] maskingKey, Collection<KeyParameter> args, @NonNull AuthenticatorSpec[] authenticatorSpecs)184     public KeyMetadata importWrappedKey(@NonNull KeyDescriptor wrappedKeyDescriptor,
185             @NonNull KeyDescriptor wrappingKeyDescriptor,
186             @NonNull byte[] wrappedKey, byte[] maskingKey,
187             Collection<KeyParameter> args, @NonNull AuthenticatorSpec[] authenticatorSpecs)
188             throws KeyStoreException {
189         KeyDescriptor keyDescriptor = new KeyDescriptor();
190         keyDescriptor.alias = wrappedKeyDescriptor.alias;
191         keyDescriptor.nspace = wrappedKeyDescriptor.nspace;
192         keyDescriptor.blob = wrappedKey;
193         keyDescriptor.domain = wrappedKeyDescriptor.domain;
194 
195         return handleExceptions(() -> mSecurityLevel.importWrappedKey(keyDescriptor,
196                 wrappingKeyDescriptor, maskingKey,
197                 args.toArray(new KeyParameter[args.size()]), authenticatorSpecs));
198     }
199 
interruptedPreservingSleep(long millis)200     protected static void interruptedPreservingSleep(long millis) {
201         boolean wasInterrupted = false;
202         Calendar calendar = Calendar.getInstance();
203         long target = calendar.getTimeInMillis() + millis;
204         while (true) {
205             try {
206                 Thread.sleep(target - calendar.getTimeInMillis());
207                 break;
208             } catch (InterruptedException e) {
209                 wasInterrupted = true;
210             } catch (IllegalArgumentException e) {
211                 // This means that the argument to sleep was negative.
212                 // So we are done sleeping.
213                 break;
214             }
215         }
216         if (wasInterrupted) {
217             Thread.currentThread().interrupt();
218         }
219     }
220 }
221