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

Commit 0817c332 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add RecoverableKeyStoreLoader implementation in LockSettingsService."

parents 81bfe137 1aa96132
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -14,7 +14,7 @@
 * limitations under the License.
 */

package android.security.keystore.recoverablekeystore;
package android.security.recoverablekeystore;

/* @hide */
parcelable KeyEntryRecoveryData;
+207 −82
Original line number Diff line number Diff line
@@ -19,37 +19,40 @@ package android.security.recoverablekeystore;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.os.UserHandle;
import android.security.KeyStore;
import android.util.AndroidException;

import com.android.internal.widget.ILockSettings;

import java.util.List;

/**
 * A wrapper around KeyStore which lets key be exported to
 * trusted hardware on server side and recovered later.
 * A wrapper around KeyStore which lets key be exported to trusted hardware on server side and
 * recovered later.
 *
 * @hide
 */
public class RecoverableKeyStoreLoader {

    private final ILockSettings mBinder;

    // Exception codes, should be in sync with {@code KeyStoreException}.
    public static final int SYSTEM_ERROR = 4;
    public static final String PERMISSION_RECOVER_KEYSTORE = "android.permission.RECOVER_KEYSTORE";

    public static final int NO_ERROR = KeyStore.NO_ERROR;
    public static final int SYSTEM_ERROR = KeyStore.SYSTEM_ERROR;
    public static final int UNINITIALIZED_RECOVERY_PUBLIC_KEY = 20;

    // Too many updates to recovery public key or server parameters.
    public static final int RATE_LIMIT_EXCEEDED = 21;

    private final ILockSettings mBinder;

    private RecoverableKeyStoreLoader(ILockSettings binder) {
        mBinder = binder;
    }

    /**
     * @hide
     */
    /** @hide */
    public static RecoverableKeyStoreLoader getInstance() {
        ILockSettings lockSettings =
                ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings"));
@@ -57,29 +60,69 @@ public class RecoverableKeyStoreLoader {
    }

    /**
     * Exceptions returned by {@link RecoverableKeyStoreLoader}.
     *
     * @hide
     */
    public static class RecoverableKeyStoreLoaderException extends AndroidException {
        private int mErrorCode;

        /**
         * Creates new {@link #RecoverableKeyStoreLoaderException} instance from the error code.
         *
         * @param errorCode
         * @hide
         */
        public static RecoverableKeyStoreLoaderException fromErrorCode(int errorCode) {
            return new RecoverableKeyStoreLoaderException(
                    errorCode, getMessageFromErrorCode(errorCode));
        }

        /**
         * Creates new {@link #RecoverableKeyStoreLoaderException} from {@link
         * ServiceSpecificException}.
         *
         * @param e exception thrown on service side.
         * @hide
         */
    public static class RecoverableKeyStoreLoaderException extends Exception {
        private final int mErrorCode;
        static RecoverableKeyStoreLoaderException fromServiceSpecificException(
                ServiceSpecificException e) throws RecoverableKeyStoreLoaderException {
            throw RecoverableKeyStoreLoaderException.fromErrorCode(e.errorCode);
        }

        public RecoverableKeyStoreLoaderException(int errorCode, String message) {
        private RecoverableKeyStoreLoaderException(int errorCode, String message) {
            super(message);
            mErrorCode = errorCode;
        }

        /** Returns errorCode. */
        public int getErrorCode() {
            return mErrorCode;
        }

        /** @hide */
        private static String getMessageFromErrorCode(int errorCode) {
            switch (errorCode) {
                case NO_ERROR:
                    return "OK";
                case SYSTEM_ERROR:
                    return "System error";
                case UNINITIALIZED_RECOVERY_PUBLIC_KEY:
                    return "Recovery service is not initialized";
                case RATE_LIMIT_EXCEEDED:
                    return "Rate limit exceeded";
                default:
                    return String.valueOf("Unknown error code " + errorCode);
            }
        }
    }

    /**
     * Initializes key recovery service for the calling application. RecoverableKeyStoreLoader
     * randomly chooses one of the keys from the list
     * and keeps it to use for future key export operations. Collection of all keys
     * in the list must be signed by the provided {@code rootCertificateAlias}, which must also be
     * present in the list of root certificates preinstalled on the device. The random selection
     * allows RecoverableKeyStoreLoader to select which of a set of remote recovery service
     * devices will be used.
     * randomly chooses one of the keys from the list and keeps it to use for future key export
     * operations. Collection of all keys in the list must be signed by the provided {@code
     * rootCertificateAlias}, which must also be present in the list of root certificates
     * preinstalled on the device. The random selection allows RecoverableKeyStoreLoader to select
     * which of a set of remote recovery service devices will be used.
     *
     * <p>In addition, RecoverableKeyStoreLoader enforces a delay of three months between
     * consecutive initialization attempts, to limit the ability of an attacker to often switch
@@ -91,124 +134,197 @@ public class RecoverableKeyStoreLoader {
     *     limited.
     * @hide
     */
    public void initRecoveryService(@NonNull String rootCertificateAlias,
            @NonNull byte[] signedPublicKeyList)
    public void initRecoveryService(
            @NonNull String rootCertificateAlias, @NonNull byte[] signedPublicKeyList)
            throws RecoverableKeyStoreLoaderException {
        throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
        // TODO: extend widget/ILockSettings.aidl
        /* try {
            mBinder.initRecoveryService(rootCertificate, publicKeyList);
        try {
            mBinder.initRecoveryService(
                    rootCertificateAlias, signedPublicKeyList, UserHandle.getCallingUserId());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } */
        } catch (ServiceSpecificException e) {
            throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
        }
    }

    /**
     * Returns data necessary to store all recoverable keys for given account.
     * Key material is encrypted with user secret and recovery public key.
     * Returns data necessary to store all recoverable keys for given account. Key material is
     * encrypted with user secret and recovery public key.
     *
     * @param account specific to Recovery agent.
     * @return Data necessary to recover keystore.
     * @hide
     */
    public KeyStoreRecoveryData getRecoveryData(@NonNull byte[] account)
            throws RecoverableKeyStoreLoaderException {
        throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
        try {
            KeyStoreRecoveryData recoveryData =
                    mBinder.getRecoveryData(account, UserHandle.getCallingUserId());
            return recoveryData;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } catch (ServiceSpecificException e) {
            throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
        }
    }

    /**
     * Server parameters used to generate new recovery key blobs. This value will be included in
     * {@code KeyStoreRecoveryData.getEncryptedRecoveryKeyBlob()}.
     * The same value must be included in vaultParams  {@link startRecoverySession}
     * {@code KeyStoreRecoveryData.getEncryptedRecoveryKeyBlob()}. The same value must be included
     * in vaultParams {@link startRecoverySession}
     *
     * @param serverParameters included in recovery key blob.
     * @see #getRecoveryData
     * @throws RecoverableKeyStoreLoaderException If parameters rotation is rate limited.
     * @hide
     */
    public void updateServerParameters(long serverParameters)
    public void setServerParameters(long serverParameters)
            throws RecoverableKeyStoreLoaderException {
        throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
        try {
            mBinder.setServerParameters(serverParameters, UserHandle.getCallingUserId());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } catch (ServiceSpecificException e) {
            throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
        }
    }

    /**
     * Updates recovery status for given keys.
     * It is used to notify keystore that key was successfully stored on the server or
     * there were an error. Returned as a part of KeyInfo data structure.
     * Updates recovery status for given keys. It is used to notify keystore that key was
     * successfully stored on the server or there were an error. Returned as a part of KeyInfo data
     * structure.
     *
     * @param packageName Application whose recoverable keys' statuses are to be updated.
     * @param aliases List of application-specific key aliases. If the array is empty, updates the
     *     status for all existing recoverable keys.
     * @param status Status specific to recovery agent.
     */
    public void setRecoveryStatus(@NonNull String packageName, @Nullable String[] aliases,
            int status) throws NameNotFoundException, RecoverableKeyStoreLoaderException {
        throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
    public void setRecoveryStatus(
            @NonNull String packageName, @Nullable String[] aliases, int status)
            throws NameNotFoundException, RecoverableKeyStoreLoaderException {
        try {
            mBinder.setRecoveryStatus(packageName, aliases, status, UserHandle.getCallingUserId());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } catch (ServiceSpecificException e) {
            throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
        }
    }

    /**
     * Specifies a set of secret types used for end-to-end keystore encryption.
     * Knowing all of them is necessary to recover data.
     * Specifies a set of secret types used for end-to-end keystore encryption. Knowing all of them
     * is necessary to recover data.
     *
     * @param secretTypes {@link KeyStoreRecoveryMetadata#TYPE_LOCKSCREEN} or
     * {@link KeyStoreRecoveryMetadata#TYPE_CUSTOM_PASSWORD}
     * @param secretTypes {@link KeyStoreRecoveryMetadata#TYPE_LOCKSCREEN} or {@link
     *     KeyStoreRecoveryMetadata#TYPE_CUSTOM_PASSWORD}
     */
    public void setRecoverySecretTypes(@NonNull @KeyStoreRecoveryMetadata.UserSecretType
            int[] secretTypes) throws RecoverableKeyStoreLoaderException {
        throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
    public void setRecoverySecretTypes(
            @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] secretTypes)
            throws RecoverableKeyStoreLoaderException {
        try {
            mBinder.setRecoverySecretTypes(secretTypes, UserHandle.getCallingUserId());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } catch (ServiceSpecificException e) {
            throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
        }
    }

    /**
     * Defines a set of secret types used for end-to-end keystore encryption.
     * Knowing all of them is necessary to generate KeyStoreRecoveryData.
     * Defines a set of secret types used for end-to-end keystore encryption. Knowing all of them is
     * necessary to generate KeyStoreRecoveryData.
     *
     * @return list of recovery secret types
     * @see KeyStoreRecoveryData
     */
    public @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] getRecoverySecretTypes()
            throws RecoverableKeyStoreLoaderException {
        throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
        try {
            return mBinder.getRecoverySecretTypes(UserHandle.getCallingUserId());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } catch (ServiceSpecificException e) {
            throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
        }
    }

    /**
     * Returns a list of recovery secret types, necessary to create a pending recovery snapshot.
     * When user enters a secret of a pending type
     * {@link #recoverySecretAvailable} should be called.
     * When user enters a secret of a pending type {@link #recoverySecretAvailable} should be
     * called.
     *
     * @return list of recovery secret types
     */
    public @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] getPendingRecoverySecretTypes()
            throws RecoverableKeyStoreLoaderException {
        throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
        try {
            return mBinder.getPendingRecoverySecretTypes(UserHandle.getCallingUserId());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } catch (ServiceSpecificException e) {
            throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
        }
    }

    /**
     * Method notifies KeyStore that a user-generated secret is available.
     * This method generates a symmetric session key which a trusted remote device can use
     * to return a recovery key.
     * Caller should use {@link KeyStoreRecoveryMetadata#clearSecret} to override the secret value
     * in memory.
     *
     * @param recoverySecret user generated secret together with parameters necessary to
     * regenerate it on a new device.
     * Method notifies KeyStore that a user-generated secret is available. This method generates a
     * symmetric session key which a trusted remote device can use to return a recovery key. Caller
     * should use {@link KeyStoreRecoveryMetadata#clearSecret} to override the secret value in
     * memory.
     *
     * @param recoverySecret user generated secret together with parameters necessary to regenerate
     *     it on a new device.
     */
    public void recoverySecretAvailable(@NonNull KeyStoreRecoveryMetadata recoverySecret)
            throws RecoverableKeyStoreLoaderException {
        throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
        try {
            mBinder.recoverySecretAvailable(recoverySecret, UserHandle.getCallingUserId());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } catch (ServiceSpecificException e) {
            throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
        }
    }

    /**
     * Initializes recovery session and returns a blob with proof of recovery secrets possession.
     * The method generates symmetric key for a session, which trusted remote device can use
     * to return recovery key.
     * The method generates symmetric key for a session, which trusted remote device can use to
     * return recovery key.
     *
     * @param sessionId ID for recovery session.
     * @param verifierPublicKey Certificate with Public key used to create the recovery blob on
     * the source device. Keystore will verify the certificate using root of trust.
     * @param verifierPublicKey Certificate with Public key used to create the recovery blob on the
     *     source device. Keystore will verify the certificate using root of trust.
     * @param vaultParams Must match the parameters in the corresponding field in the recovery blob.
     *     Used to limit number of guesses.
     * @param vaultChallenge Data passed from server for this recovery session and used to prevent
     *     replay attacks
     * @param secrets Secrets provided by user, the method only uses type and secret fields.
     * @return Binary blob with recovery claim. It is encrypted with verifierPublicKey and
     * contains a proof of user secrets, session symmetric key and parameters necessary to identify
     * the counter with the number of failed recovery attempts.
     * @return Binary blob with recovery claim. It is encrypted with verifierPublicKey and contains
     *     a proof of user secrets, session symmetric key and parameters necessary to identify the
     *     counter with the number of failed recovery attempts.
     */
    public @NonNull byte[] startRecoverySession(@NonNull String sessionId,
            @NonNull byte[] verifierPublicKey, @NonNull byte[] vaultParams,
            @NonNull byte[] vaultChallenge, @NonNull List<KeyStoreRecoveryMetadata> secrets)
    public @NonNull byte[] startRecoverySession(
            @NonNull String sessionId,
            @NonNull byte[] verifierPublicKey,
            @NonNull byte[] vaultParams,
            @NonNull byte[] vaultChallenge,
            @NonNull List<KeyStoreRecoveryMetadata> secrets)
            throws RecoverableKeyStoreLoaderException {
        throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
        try {
            byte[] recoveryClaim =
                    mBinder.startRecoverySession(
                            sessionId,
                            verifierPublicKey,
                            vaultParams,
                            vaultChallenge,
                            secrets,
                            UserHandle.getCallingUserId());
            return recoveryClaim;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } catch (ServiceSpecificException e) {
            throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
        }
    }

    /**
@@ -217,12 +333,21 @@ public class RecoverableKeyStoreLoader {
     * @param sessionId Id for recovery session, same as in = {@link startRecoverySession}.
     * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
     * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob
     * and session. KeyStore only uses package names from the application info in
     * {@link KeyEntryRecoveryData}. Caller is responsibility to perform certificates check.
     *     and session. KeyStore only uses package names from the application info in {@link
     *     KeyEntryRecoveryData}. Caller is responsibility to perform certificates check.
     */
    public void recoverKeys(@NonNull String sessionId, @NonNull byte[] recoveryKeyBlob,
    public void recoverKeys(
            @NonNull String sessionId,
            @NonNull byte[] recoveryKeyBlob,
            @NonNull List<KeyEntryRecoveryData> applicationKeys)
            throws RecoverableKeyStoreLoaderException {
        throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
        try {
            mBinder.recoverKeys(
                    sessionId, recoveryKeyBlob, applicationKeys, UserHandle.getCallingUserId());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } catch (ServiceSpecificException e) {
            throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
        }
    }
}
+22 −0
Original line number Diff line number Diff line
@@ -17,6 +17,10 @@
package com.android.internal.widget;

import android.app.trust.IStrongAuthTracker;
import android.os.Bundle;
import android.security.recoverablekeystore.KeyEntryRecoveryData;
import android.security.recoverablekeystore.KeyStoreRecoveryData;
import android.security.recoverablekeystore.KeyStoreRecoveryMetadata;
import com.android.internal.widget.ICheckCredentialProgressCallback;
import com.android.internal.widget.VerifyCredentialResponse;

@@ -52,4 +56,22 @@ interface ILockSettings {
    boolean setLockCredentialWithToken(String credential, int type, long tokenHandle,
            in byte[] token, int requestedQuality, int userId);
    void unlockUserWithToken(long tokenHandle, in byte[] token, int userId);

    // RecoverableKeyStoreLoader methods.
    // {@code ServiceSpecificException} may be thrown to signal an error, which caller can
    // convert to  {@code RecoverableKeyStoreLoader}.
    void initRecoveryService(in String rootCertificateAlias, in byte[] signedPublicKeyList,
            int userId);
    KeyStoreRecoveryData getRecoveryData(in byte[] account, int userId);
    void setServerParameters(long serverParameters, int userId);
    void setRecoveryStatus(in String packageName, in String[] aliases, int status, int userId);
    void setRecoverySecretTypes(in int[] secretTypes, int userId);
    int[] getRecoverySecretTypes(int userId);
    int[] getPendingRecoverySecretTypes(int userId);
    void recoverySecretAvailable(in KeyStoreRecoveryMetadata recoverySecret, int userId);
    byte[] startRecoverySession(in String sessionId,
            in byte[] verifierPublicKey, in byte[] vaultParams, in byte[] vaultChallenge,
            in List<KeyStoreRecoveryMetadata> secrets, int userId);
    void recoverKeys(in String sessionId, in byte[] recoveryKeyBlob,
            in List<KeyEntryRecoveryData> applicationKeys, int userId);
}
+82 −1

File changed.

Preview size limit exceeded, changes collapsed.

+211 −0

File added.

Preview size limit exceeded, changes collapsed.