Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 788f03de authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge changes I9731d978,I9e325782,I441a4d4d,I86a85e48,I9268fd66, ... am: aeb15e85

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1471565

Change-Id: I3b5af42cdab9357fa4f4a0924af206f1df5488e4
parents b58226ef aeb15e85
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -42742,6 +42742,12 @@ package android.security.identity {
package android.security.keystore {
  public class BackendBusyException extends java.security.ProviderException {
    ctor public BackendBusyException();
    ctor public BackendBusyException(@NonNull String);
    ctor public BackendBusyException(@NonNull String, @NonNull Throwable);
  }
  public class KeyExpiredException extends java.security.InvalidKeyException {
    ctor public KeyExpiredException();
    ctor public KeyExpiredException(String);
+6 −0
Original line number Diff line number Diff line
@@ -40910,6 +40910,12 @@ package android.security.identity {
package android.security.keystore {
  public class BackendBusyException extends java.security.ProviderException {
    ctor public BackendBusyException();
    ctor public BackendBusyException(@NonNull String);
    ctor public BackendBusyException(@NonNull String, @NonNull Throwable);
  }
  public class KeyExpiredException extends java.security.InvalidKeyException {
    ctor public KeyExpiredException();
    ctor public KeyExpiredException(String);
+12 −1
Original line number Diff line number Diff line
@@ -73,6 +73,7 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.Provider;
import java.security.Security;
import java.util.Optional;

/**
 * Startup class for the zygote process.
@@ -225,7 +226,17 @@ public class ZygoteInit {
        // AndroidKeyStoreProvider.install() manipulates the list of JCA providers to insert
        // preferred providers. Note this is not done via security.properties as the JCA providers
        // are not on the classpath in the case of, for example, raw dalvikvm runtimes.
        // TODO b/171305684 This code is used to conditionally enable the installation of the
        //      Keystore 2.0 provider to enable teams adjusting to Keystore 2.0 at their own
        //      pace. This code will be removed when all calling code was adjusted to
        //      Keystore 2.0.
        Optional<Boolean> keystore2_enabled =
                android.sysprop.Keystore2Properties.keystore2_enabled();
        if (keystore2_enabled.isPresent() && keystore2_enabled.get()) {
            android.security.keystore2.AndroidKeyStoreProvider.install();
        } else {
            AndroidKeyStoreProvider.install();
        }
        Log.i(TAG, "Installed AndroidKeyStoreProvider in "
                + (SystemClock.uptimeMillis() - startTime) + "ms.");
        Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
+33 −0
Original line number Diff line number Diff line
/*
 * 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.os.RemoteException;

/**
 * This is a Producer of {@code R} that is expected to throw a {@link RemoteException}.
 *
 * It is used by Keystore2 service wrappers to handle and convert {@link RemoteException}
 * and {@link android.os.ServiceSpecificException} into {@link KeyStoreException}.
 *
 * @hide
 * @param <R>
 */
@FunctionalInterface
interface CheckedRemoteRequest<R> {
    R execute() throws RemoteException;
}
+277 −0
Original line number Diff line number Diff line
/*
 * 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.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.os.Build;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.system.keystore2.IKeystoreService;
import android.system.keystore2.KeyDescriptor;
import android.system.keystore2.KeyEntryResponse;
import android.system.keystore2.ResponseCode;
import android.util.Log;

import java.util.Calendar;

/**
 * @hide This should not be made public in its present form because it
 * assumes that private and secret key bytes are available and would
 * preclude the use of hardware crypto.
 */
public class KeyStore2 {
    private static final String TAG = "KeyStore";

    private static final int RECOVERY_GRACE_PERIOD_MS = 50;

    /**
     * Keystore operation creation may fail
     *
     * Keystore used to work under the assumption that the creation of cryptographic operations
     * always succeeds. However, the KeyMint backend has only a limited number of operation slots.
     * In order to keep up the appearance of "infinite" operation slots, the Keystore daemon
     * would prune least recently used operations if there is no available operation slot.
     * As a result, good operations could be terminated prematurely.
     *
     * This opens AndroidKeystore up to denial-of-service and unintended livelock situations.
     * E.g.: if multiple apps wake up at the same time, e.g., due to power management optimizations,
     * and attempt to perform crypto operations, they start terminating each others operations
     * without making any progress.
     *
     * To break out of livelocks and to discourage DoS attempts we have changed the pruning
     * strategy such that it prefers clients that use few operation slots and only briefly.
     * As a result we can, almost, guarantee that single operations that don't linger inactive
     * for more than 5 seconds will conclude unhampered by the pruning strategy. "Almost",
     * because there are operations related to file system encryption that can prune even
     * these operations, but those are extremely rare.
     *
     * As a side effect of this new pruning strategy operation creation can now fail if the
     * client has a lower pruning power than all of the existing operations.
     *
     * Pruning strategy
     *
     * To find a suitable candidate we compute the malus for the caller and each existing
     * operation. The malus is the inverse of the pruning power (caller) or pruning
     * resistance (existing operation). For the caller to be able to prune an operation it must
     * find an operation with a malus higher than its own.
     *
     * For more detail on the pruning strategy consult the implementation at
     * https://android.googlesource.com/platform/system/security/+/refs/heads/master/keystore2/src/operation.rs
     *
     * For older SDK version, KeyStore2 will poll the Keystore daemon for a free operation
     * slot. So to applications, targeting earlier SDK versions, it will still look like cipher and
     * signature object initialization always succeeds, however, it may take longer to get an
     * operation.
     *
     * All SDK version benefit from fairer operation slot scheduling and a better chance to
     * successfully conclude an operation.
     */
    @ChangeId
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
    static final long KEYSTORE_OPERATION_CREATION_MAY_FAIL = 169897160L;

    // Never use mBinder directly, use KeyStore2.getService() instead or better yet
    // handleRemoteExceptionWithRetry which retries connecting to Keystore once in case
    // of a remote exception.
    private IKeystoreService mBinder;


    @FunctionalInterface
    interface CheckedRemoteRequest<R> {
        R execute(IKeystoreService service) throws RemoteException;
    }

    private <R> R handleRemoteExceptionWithRetry(@NonNull CheckedRemoteRequest<R> request)
            throws KeyStoreException {
        IKeystoreService service = getService(false /* retryLookup */);
        boolean firstTry = true;
        while (true) {
            try {
                return request.execute(service);
            } catch (ServiceSpecificException e) {
                Log.e(TAG, "KeyStore exception", e);
                throw new KeyStoreException(e.errorCode, "");
            } catch (RemoteException e) {
                if (firstTry) {
                    Log.w(TAG, "Looks like we may have lost connection to the Keystore "
                            + "daemon.");
                    Log.w(TAG, "Retrying after giving Keystore "
                            + RECOVERY_GRACE_PERIOD_MS + "ms to recover.");
                    interruptedPreservingSleep(RECOVERY_GRACE_PERIOD_MS);
                    service = getService(true /* retry Lookup */);
                    firstTry = false;
                } else {
                    Log.e(TAG, "Cannot connect to Keystore daemon.", e);
                    throw new KeyStoreException(ResponseCode.SYSTEM_ERROR, "");
                }
            }
        }
    }


    private KeyStore2() {
        mBinder = null;
    }

    public static KeyStore2 getInstance() {
        return new KeyStore2();
    }

    private synchronized IKeystoreService getService(boolean retryLookup) {
        if (mBinder == null || retryLookup) {
            mBinder = IKeystoreService.Stub.asInterface(ServiceManager
                    .getService("android.system.keystore2"));
        }
        return mBinder;
    }

    void delete(KeyDescriptor descriptor) throws KeyStoreException {
        handleRemoteExceptionWithRetry((service) -> {
            service.deleteKey(descriptor);
            return 0;
        });
    }

    /**
     * List all entries in the keystore for in the given namespace.
     */
    public KeyDescriptor[] list(int domain, long namespace) throws KeyStoreException {
        return handleRemoteExceptionWithRetry((service) -> service.listEntries(domain, namespace));
    }

    /**
     * Create a grant that allows the grantee identified by {@code granteeUid} to use
     * the key specified by {@code descriptor} withint the restrictions given by
     * {@code accessVectore}.
     * @see IKeystoreService#grant(KeyDescriptor, int, int) for more details.
     * @param descriptor
     * @param granteeUid
     * @param accessVector
     * @return
     * @throws KeyStoreException
     * @hide
     */
    public KeyDescriptor grant(KeyDescriptor descriptor, int granteeUid, int accessVector)
            throws  KeyStoreException {
        return handleRemoteExceptionWithRetry(
                (service) -> service.grant(descriptor, granteeUid, accessVector)
        );
    }

    /**
     * Destroys a grant.
     * @see IKeystoreService#ungrant(KeyDescriptor, int) for more details.
     * @param descriptor
     * @param granteeUid
     * @throws KeyStoreException
     * @hide
     */
    public void ungrant(KeyDescriptor descriptor, int granteeUid)
            throws KeyStoreException {
        handleRemoteExceptionWithRetry((service) -> {
            service.ungrant(descriptor, granteeUid);
            return 0;
        });
    }

    /**
     * Retrieves a key entry from the keystore backend.
     * @see IKeystoreService#getKeyEntry(KeyDescriptor) for more details.
     * @param descriptor
     * @return
     * @throws KeyStoreException
     * @hide
     */
    public KeyEntryResponse getKeyEntry(@NonNull KeyDescriptor descriptor)
            throws KeyStoreException {
        return handleRemoteExceptionWithRetry((service) -> service.getKeyEntry(descriptor));
    }

    /**
     * Get the security level specific keystore interface from the keystore daemon.
     * @see IKeystoreService#getSecurityLevel(int) for more details.
     * @param securityLevel
     * @return
     * @throws KeyStoreException
     * @hide
     */
    public KeyStoreSecurityLevel getSecurityLevel(int securityLevel)
            throws KeyStoreException {
        return handleRemoteExceptionWithRetry((service) ->
            new KeyStoreSecurityLevel(
                    service.getSecurityLevel(securityLevel)
            )
        );
    }

    /**
     * Update the subcomponents of a key entry designated by the key descriptor.
     * @see IKeystoreService#updateSubcomponent(KeyDescriptor, byte[], byte[]) for more details.
     * @param key
     * @param publicCert
     * @param publicCertChain
     * @throws KeyStoreException
     * @hide
     */
    public void updateSubcomponents(@NonNull KeyDescriptor key, byte[] publicCert,
            byte[] publicCertChain) throws KeyStoreException {
        handleRemoteExceptionWithRetry((service) -> {
            service.updateSubcomponent(key, publicCert, publicCertChain);
            return 0;
        });
    }

    /**
     * Delete the key designed by the key descriptor.
     * @see IKeystoreService#deleteKey(KeyDescriptor) for more details.
     * @param descriptor
     * @throws KeyStoreException
     * @hide
     */
    public void deleteKey(@NonNull KeyDescriptor descriptor)
            throws KeyStoreException {
        handleRemoteExceptionWithRetry((service) -> {
            service.deleteKey(descriptor);
            return 0;
        });
    }

    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();
        }
    }

}
Loading