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

Commit 148e9970 authored by Janis Danisevskis's avatar Janis Danisevskis
Browse files

resolve merge conflicts of 07bfeb36 to master

Test: I solemnly swear I tested this conflict resolution.
Bug: None
Change-Id: Iab84b0fa8c708845aecd13dd45c242147829fd8b
parents 92c2f231 07bfeb36
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -41197,6 +41197,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();
        }
    }

}
+141 −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.os.RemoteException;
import android.os.ServiceSpecificException;
import android.security.keymaster.KeymasterDefs;
import android.system.keystore2.IKeystoreOperation;
import android.system.keystore2.KeyParameter;
import android.system.keystore2.ResponseCode;
import android.util.Log;

/**
 * @hide
 */
public class KeyStoreOperation {
    static final String TAG = "KeyStoreOperation";
    private final IKeystoreOperation mOperation;
    private final Long mChallenge;
    private final KeyParameter[] mParameters;

    public KeyStoreOperation(
            @NonNull IKeystoreOperation operation,
            Long challenge,
            KeyParameter[] parameters
    ) {
        this.mOperation = operation;
        this.mChallenge = challenge;
        this.mParameters = parameters;
    }

    /**
     * Gets the challenge associated with this operation.
     * @return null if the operation does not required authorization. A 64bit operation
     *         challenge otherwise.
     */
    public Long getChallenge() {
        return mChallenge;
    }

    /**
     * Gets the parameters associated with this operation.
     * @return
     */
    public KeyParameter[] getParameters() {
        return mParameters;
    }

    private <R> R handleExceptions(@NonNull CheckedRemoteRequest<R> request)
            throws KeyStoreException {
        try {
            return request.execute();
        } catch (ServiceSpecificException e) {
            switch(e.errorCode) {
                case ResponseCode.OPERATION_BUSY: {
                    throw new IllegalThreadStateException(
                            "Cannot update the same operation concurrently."
                    );
                }
                default:
                    // TODO Human readable string. Use something like KeyStore.getKeyStoreException
                    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,
                    "Remote exception while advancing a KeyStoreOperation.",
                    e
            );
            throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_OPERATION_HANDLE, "");
        }
    }

    /**
     * Updates the Keystore operation represented by this object with more associated data.
     * @see IKeystoreOperation#updateAad(byte[]) for more details.
     * @param input
     * @throws KeyStoreException
     */
    public void updateAad(@NonNull byte[] input) throws KeyStoreException {
        handleExceptions(() -> {
            mOperation.updateAad(input);
            return 0;
        });
    }

    /**
     * Updates the Keystore operation represented by this object.
     * @see IKeystoreOperation#update(byte[]) for more details.
     * @param input
     * @return
     * @throws KeyStoreException
     * @hide
     */
    public byte[] update(@NonNull byte[] input) throws KeyStoreException {
        return handleExceptions(() -> mOperation.update(input));
    }

    /**
     * Finalizes the Keystore operation represented by this object.
     * @see IKeystoreOperation#finish(byte[], byte[]) for more details.
     * @param input
     * @param signature
     * @return
     * @throws KeyStoreException
     * @hide
     */
    public byte[] finish(byte[] input, byte[] signature) throws KeyStoreException {
        return handleExceptions(() -> mOperation.finish(input, signature));
    }

    /**
     * Aborts the Keystore operation represented by this object.
     * @see IKeystoreOperation#abort() for more details.
     * @throws KeyStoreException
     * @hide
     */
    public void abort() throws KeyStoreException {
        handleExceptions(() -> {
            mOperation.abort();
            return 0;
        });
    }
}
Loading