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