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 Original line Diff line number Diff line
@@ -14,7 +14,7 @@
 * limitations under the License.
 * limitations under the License.
 */
 */


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


/* @hide */
/* @hide */
parcelable KeyEntryRecoveryData;
parcelable KeyEntryRecoveryData;
+207 −82
Original line number Original line Diff line number Diff line
@@ -19,37 +19,40 @@ package android.security.recoverablekeystore;
import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.RemoteException;
import android.os.ServiceManager;
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 com.android.internal.widget.ILockSettings;


import java.util.List;
import java.util.List;


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


    private final ILockSettings mBinder;
    public static final String PERMISSION_RECOVER_KEYSTORE = "android.permission.RECOVER_KEYSTORE";

    // Exception codes, should be in sync with {@code KeyStoreException}.
    public static final int SYSTEM_ERROR = 4;


    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;
    public static final int UNINITIALIZED_RECOVERY_PUBLIC_KEY = 20;

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


    private final ILockSettings mBinder;

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


    /**
    /** @hide */
     * @hide
     */
    public static RecoverableKeyStoreLoader getInstance() {
    public static RecoverableKeyStoreLoader getInstance() {
        ILockSettings lockSettings =
        ILockSettings lockSettings =
                ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings"));
                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
         * @hide
         */
         */
    public static class RecoverableKeyStoreLoaderException extends Exception {
        static RecoverableKeyStoreLoaderException fromServiceSpecificException(
        private final int mErrorCode;
                ServiceSpecificException e) throws RecoverableKeyStoreLoaderException {
            throw RecoverableKeyStoreLoaderException.fromErrorCode(e.errorCode);
        }


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


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


    /**
    /**
     * Returns data necessary to store all recoverable keys for given account.
     * Returns data necessary to store all recoverable keys for given account. Key material is
     * Key material is encrypted with user secret and recovery public key.
     * 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)
    public KeyStoreRecoveryData getRecoveryData(@NonNull byte[] account)
            throws RecoverableKeyStoreLoaderException {
            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
     * Server parameters used to generate new recovery key blobs. This value will be included in
     * {@code KeyStoreRecoveryData.getEncryptedRecoveryKeyBlob()}.
     * {@code KeyStoreRecoveryData.getEncryptedRecoveryKeyBlob()}. The same value must be included
     * The same value must be included in vaultParams  {@link startRecoverySession}
     * in vaultParams {@link startRecoverySession}
     *
     *
     * @param serverParameters included in recovery key blob.
     * @see #getRecoveryData
     * @see #getRecoveryData
     * @throws RecoverableKeyStoreLoaderException If parameters rotation is rate limited.
     * @throws RecoverableKeyStoreLoaderException If parameters rotation is rate limited.
     * @hide
     */
     */
    public void updateServerParameters(long serverParameters)
    public void setServerParameters(long serverParameters)
            throws RecoverableKeyStoreLoaderException {
            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.
     * Updates recovery status for given keys. It is used to notify keystore that key was
     * It is used to notify keystore that key was successfully stored on the server or
     * successfully stored on the server or there were an error. Returned as a part of KeyInfo data
     * there were an error. Returned as a part of KeyInfo data structure.
     * structure.
     *
     *
     * @param packageName Application whose recoverable keys' statuses are to be updated.
     * @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
     * @param aliases List of application-specific key aliases. If the array is empty, updates the
     *     status for all existing recoverable keys.
     *     status for all existing recoverable keys.
     * @param status Status specific to recovery agent.
     * @param status Status specific to recovery agent.
     */
     */
    public void setRecoveryStatus(@NonNull String packageName, @Nullable String[] aliases,
    public void setRecoveryStatus(
            int status) throws NameNotFoundException, RecoverableKeyStoreLoaderException {
            @NonNull String packageName, @Nullable String[] aliases, int status)
        throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
            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.
     * Specifies a set of secret types used for end-to-end keystore encryption. Knowing all of them
     * Knowing all of them is necessary to recover data.
     * is necessary to recover data.
     *
     *
     * @param secretTypes {@link KeyStoreRecoveryMetadata#TYPE_LOCKSCREEN} or
     * @param secretTypes {@link KeyStoreRecoveryMetadata#TYPE_LOCKSCREEN} or {@link
     * {@link KeyStoreRecoveryMetadata#TYPE_CUSTOM_PASSWORD}
     *     KeyStoreRecoveryMetadata#TYPE_CUSTOM_PASSWORD}
     */
     */
    public void setRecoverySecretTypes(@NonNull @KeyStoreRecoveryMetadata.UserSecretType
    public void setRecoverySecretTypes(
            int[] secretTypes) throws RecoverableKeyStoreLoaderException {
            @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] secretTypes)
        throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
            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.
     * Defines a set of secret types used for end-to-end keystore encryption. Knowing all of them is
     * Knowing all of them is necessary to generate KeyStoreRecoveryData.
     * necessary to generate KeyStoreRecoveryData.
     *
     * @return list of recovery secret types
     * @see KeyStoreRecoveryData
     * @see KeyStoreRecoveryData
     */
     */
    public @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] getRecoverySecretTypes()
    public @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] getRecoverySecretTypes()
            throws RecoverableKeyStoreLoaderException {
            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.
     * Returns a list of recovery secret types, necessary to create a pending recovery snapshot.
     * When user enters a secret of a pending type
     * When user enters a secret of a pending type {@link #recoverySecretAvailable} should be
     * {@link #recoverySecretAvailable} should be called.
     * called.
     *
     * @return list of recovery secret types
     */
     */
    public @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] getPendingRecoverySecretTypes()
    public @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] getPendingRecoverySecretTypes()
            throws RecoverableKeyStoreLoaderException {
            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.
     * Method notifies KeyStore that a user-generated secret is available. This method generates a
     * This method generates a symmetric session key which a trusted remote device can use
     * symmetric session key which a trusted remote device can use to return a recovery key. Caller
     * to return a recovery key.
     * should use {@link KeyStoreRecoveryMetadata#clearSecret} to override the secret value in
     * Caller should use {@link KeyStoreRecoveryMetadata#clearSecret} to override the secret value
     * memory.
     * in memory.
     *
     *
     * @param recoverySecret user generated secret together with parameters necessary to regenerate
     * @param recoverySecret user generated secret together with parameters necessary to
     *     it on a new device.
     * regenerate it on a new device.
     */
     */
    public void recoverySecretAvailable(@NonNull KeyStoreRecoveryMetadata recoverySecret)
    public void recoverySecretAvailable(@NonNull KeyStoreRecoveryMetadata recoverySecret)
            throws RecoverableKeyStoreLoaderException {
            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.
     * 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
     * The method generates symmetric key for a session, which trusted remote device can use to
     * to return recovery key.
     * return recovery key.
     *
     *
     * @param sessionId ID for recovery session.
     * @param sessionId ID for recovery session.
     * @param verifierPublicKey Certificate with Public key used to create the recovery blob on
     * @param verifierPublicKey Certificate with Public key used to create the recovery blob on the
     * the source device. Keystore will verify the certificate using root of trust.
     *     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.
     * @param vaultParams Must match the parameters in the corresponding field in the recovery blob.
     *     Used to limit number of guesses.
     *     Used to limit number of guesses.
     * @param vaultChallenge Data passed from server for this recovery session and used to prevent
     * @param vaultChallenge Data passed from server for this recovery session and used to prevent
     *     replay attacks
     *     replay attacks
     * @param secrets Secrets provided by user, the method only uses type and secret fields.
     * @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
     * @return Binary blob with recovery claim. It is encrypted with verifierPublicKey and contains
     * contains a proof of user secrets, session symmetric key and parameters necessary to identify
     *     a proof of user secrets, session symmetric key and parameters necessary to identify the
     * the counter with the number of failed recovery attempts.
     *     counter with the number of failed recovery attempts.
     */
     */
    public @NonNull byte[] startRecoverySession(@NonNull String sessionId,
    public @NonNull byte[] startRecoverySession(
            @NonNull byte[] verifierPublicKey, @NonNull byte[] vaultParams,
            @NonNull String sessionId,
            @NonNull byte[] vaultChallenge, @NonNull List<KeyStoreRecoveryMetadata> secrets)
            @NonNull byte[] verifierPublicKey,
            @NonNull byte[] vaultParams,
            @NonNull byte[] vaultChallenge,
            @NonNull List<KeyStoreRecoveryMetadata> secrets)
            throws RecoverableKeyStoreLoaderException {
            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 sessionId Id for recovery session, same as in = {@link startRecoverySession}.
     * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
     * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
     * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob
     * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob
     * and session. KeyStore only uses package names from the application info in
     *     and session. KeyStore only uses package names from the application info in {@link
     * {@link KeyEntryRecoveryData}. Caller is responsibility to perform certificates check.
     *     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)
            @NonNull List<KeyEntryRecoveryData> applicationKeys)
            throws RecoverableKeyStoreLoaderException {
            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 Original line Diff line number Diff line
@@ -17,6 +17,10 @@
package com.android.internal.widget;
package com.android.internal.widget;


import android.app.trust.IStrongAuthTracker;
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.ICheckCredentialProgressCallback;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.internal.widget.VerifyCredentialResponse;


@@ -52,4 +56,22 @@ interface ILockSettings {
    boolean setLockCredentialWithToken(String credential, int type, long tokenHandle,
    boolean setLockCredentialWithToken(String credential, int type, long tokenHandle,
            in byte[] token, int requestedQuality, int userId);
            in byte[] token, int requestedQuality, int userId);
    void unlockUserWithToken(long tokenHandle, in byte[] token, 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.