summaryrefslogtreecommitdiff
path: root/keystore/java/android/security/KeyStoreSecurityLevel.java
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2020-11-16 22:51:03 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2020-11-16 22:51:03 +0000
commitaeb15e8592b9a74937afbb3fa232d2ced5971f3c (patch)
tree1d60d433579554151324eb04664f6205ba426d53 /keystore/java/android/security/KeyStoreSecurityLevel.java
parentfebaec9f3e70de992cc6cc72789aa95c0b80a66f (diff)
parent4392c6977ce935a084ab30baeed511f170a606d5 (diff)
Merge changes I9731d978,I9e325782,I441a4d4d,I86a85e48,I9268fd66, ...
* changes: Keystore 2.0 SPI: Install legacy Keystore provider as AndroidKeyStoreLegacy Keystore 2.0 SPI: Zygote install Keystore2 provider conditionally Keystore 2.0 SPI: Evolve the generator SPI. Keystore 2.0 SPI: Evolve Factory SPI Keystore 2.0 SPI: AndroidKeyStoreProvider loads keys from Keystore 2.0 Keystore 2.0 SPI: Evolve the Crypto SPI. Keystore 2.0 SPI: KeyParameter utilities. Keystore 2.0 SPI: Update the chunked streamer. Keystore 2.0 SPI: KeyStoreCryptoOperationUtils Keystore 2.0 SPI: KeyStoreKeys adopt Keystore 2.0 Keystore 2.0: Shim around the basic functionality of Keystore 2.0 Keystore 2.0 SPI: Duplicate Keystore SPI to android.security.keystore2 package
Diffstat (limited to 'keystore/java/android/security/KeyStoreSecurityLevel.java')
-rw-r--r--keystore/java/android/security/KeyStoreSecurityLevel.java217
1 files changed, 217 insertions, 0 deletions
diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java
new file mode 100644
index 000000000000..9d3b62278ba0
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreSecurityLevel.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+import android.annotation.NonNull;
+import android.app.compat.CompatChanges;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.security.keystore.BackendBusyException;
+import android.security.keystore.KeyStoreConnectException;
+import android.system.keystore2.AuthenticatorSpec;
+import android.system.keystore2.CreateOperationResponse;
+import android.system.keystore2.IKeystoreSecurityLevel;
+import android.system.keystore2.KeyDescriptor;
+import android.system.keystore2.KeyMetadata;
+import android.system.keystore2.KeyParameter;
+import android.system.keystore2.ResponseCode;
+import android.util.Log;
+
+import java.util.Calendar;
+import java.util.Collection;
+
+/**
+ * This is a shim around the security level specific interface of Keystore 2.0. Services with
+ * this interface are instantiated per KeyMint backend, each having there own security level.
+ * Thus this object representation of a security level.
+ * @hide
+ */
+public class KeyStoreSecurityLevel {
+ private static final String TAG = "KeyStoreSecurityLevel";
+ private final IKeystoreSecurityLevel mSecurityLevel;
+
+ public KeyStoreSecurityLevel(IKeystoreSecurityLevel securityLevel) {
+ this.mSecurityLevel = securityLevel;
+ }
+
+ private <R> R handleExceptions(CheckedRemoteRequest<R> request) throws KeyStoreException {
+ try {
+ return request.execute();
+ } catch (ServiceSpecificException e) {
+ throw new KeyStoreException(e.errorCode, "");
+ } catch (RemoteException e) {
+ // Log exception and report invalid operation handle.
+ // This should prompt the caller drop the reference to this operation and retry.
+ Log.e(TAG, "Could not connect to Keystore.", e);
+ throw new KeyStoreException(ResponseCode.SYSTEM_ERROR, "");
+ }
+ }
+
+ /**
+ * Creates a new keystore operation.
+ * @see IKeystoreSecurityLevel#createOperation(KeyDescriptor, KeyParameter[], boolean) for more
+ * details.
+ * @param keyDescriptor
+ * @param args
+ * @return
+ * @throws KeyStoreException
+ * @hide
+ */
+ public KeyStoreOperation createOperation(@NonNull KeyDescriptor keyDescriptor,
+ Collection<KeyParameter> args) throws KeyStoreException {
+ while (true) {
+ try {
+ CreateOperationResponse createOperationResponse =
+ mSecurityLevel.createOperation(
+ keyDescriptor,
+ args.toArray(new KeyParameter[args.size()]),
+ false /* forced */
+ );
+ Long challenge = null;
+ if (createOperationResponse.operationChallenge != null) {
+ challenge = createOperationResponse.operationChallenge.challenge;
+ }
+ KeyParameter[] parameters = null;
+ if (createOperationResponse.parameters != null) {
+ parameters = createOperationResponse.parameters.keyParameter;
+ }
+ return new KeyStoreOperation(
+ createOperationResponse.iOperation,
+ challenge,
+ parameters);
+ } catch (ServiceSpecificException e) {
+ switch (e.errorCode) {
+ case ResponseCode.BACKEND_BUSY: {
+ if (CompatChanges.isChangeEnabled(
+ KeyStore2.KEYSTORE_OPERATION_CREATION_MAY_FAIL)) {
+ // Starting with Android S we inform the caller about the
+ // backend being busy.
+ throw new BackendBusyException();
+ } else {
+ // Before Android S operation creation must always succeed. So we
+ // just have to retry. We do so with a randomized back-off between
+ // 50 and 250ms.
+ // It is a little awkward that we cannot break out of this loop
+ // by interrupting this thread. But that is the expected behavior.
+ // There is some comfort in the fact that interrupting a thread
+ // also does not unblock a thread waiting for a binder transaction.
+ interruptedPreservingSleep((long) (Math.random() * 200 + 50));
+ }
+ break;
+ }
+ default:
+ throw new KeyStoreException(e.errorCode, "");
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Cannot connect to keystore", e);
+ throw new KeyStoreConnectException();
+ }
+ }
+ }
+
+ /**
+ * Generates a new key in Keystore.
+ * @see IKeystoreSecurityLevel#generateKey(KeyDescriptor, KeyDescriptor, KeyParameter[], int,
+ * byte[]) for more details.
+ * @param descriptor
+ * @param attestationKey
+ * @param args
+ * @param flags
+ * @param entropy
+ * @return
+ * @throws KeyStoreException
+ * @hide
+ */
+ public KeyMetadata generateKey(@NonNull KeyDescriptor descriptor, KeyDescriptor attestationKey,
+ Collection<KeyParameter> args, int flags, byte[] entropy)
+ throws KeyStoreException {
+ return handleExceptions(() -> mSecurityLevel.generateKey(
+ descriptor, attestationKey, args.toArray(new KeyParameter[args.size()]),
+ flags, entropy));
+ }
+
+ /**
+ * Imports a key into Keystore.
+ * @see IKeystoreSecurityLevel#importKey(KeyDescriptor, KeyDescriptor, KeyParameter[], int,
+ * byte[]) for more details.
+ * @param descriptor
+ * @param attestationKey
+ * @param args
+ * @param flags
+ * @param keyData
+ * @return
+ * @throws KeyStoreException
+ * @hide
+ */
+ public KeyMetadata importKey(KeyDescriptor descriptor, KeyDescriptor attestationKey,
+ Collection<KeyParameter> args, int flags, byte[] keyData)
+ throws KeyStoreException {
+ return handleExceptions(() -> mSecurityLevel.importKey(descriptor, attestationKey,
+ args.toArray(new KeyParameter[args.size()]), flags, keyData));
+ }
+
+ /**
+ * Imports a wrapped key into Keystore.
+ * @see IKeystoreSecurityLevel#importWrappedKey(KeyDescriptor, KeyDescriptor, byte[],
+ * KeyParameter[], AuthenticatorSpec[]) for more details.
+ * @param wrappedKeyDescriptor
+ * @param wrappingKeyDescriptor
+ * @param wrappedKey
+ * @param maskingKey
+ * @param args
+ * @param authenticatorSpecs
+ * @return
+ * @throws KeyStoreException
+ * @hide
+ */
+ public KeyMetadata importWrappedKey(@NonNull KeyDescriptor wrappedKeyDescriptor,
+ @NonNull KeyDescriptor wrappingKeyDescriptor,
+ @NonNull byte[] wrappedKey, byte[] maskingKey,
+ Collection<KeyParameter> args, @NonNull AuthenticatorSpec[] authenticatorSpecs)
+ throws KeyStoreException {
+ KeyDescriptor keyDescriptor = new KeyDescriptor();
+ keyDescriptor.alias = wrappedKeyDescriptor.alias;
+ keyDescriptor.nspace = wrappedKeyDescriptor.nspace;
+ keyDescriptor.blob = wrappedKey;
+ keyDescriptor.domain = wrappedKeyDescriptor.domain;
+
+ return handleExceptions(() -> mSecurityLevel.importWrappedKey(wrappedKeyDescriptor,
+ wrappingKeyDescriptor, maskingKey,
+ args.toArray(new KeyParameter[args.size()]), authenticatorSpecs));
+ }
+
+ protected static void interruptedPreservingSleep(long millis) {
+ boolean wasInterrupted = false;
+ Calendar calendar = Calendar.getInstance();
+ long target = calendar.getTimeInMillis() + millis;
+ while (true) {
+ try {
+ Thread.sleep(target - calendar.getTimeInMillis());
+ break;
+ } catch (InterruptedException e) {
+ wasInterrupted = true;
+ } catch (IllegalArgumentException e) {
+ // This means that the argument to sleep was negative.
+ // So we are done sleeping.
+ break;
+ }
+ }
+ if (wasInterrupted) {
+ Thread.currentThread().interrupt();
+ }
+ }
+}