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