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

Commit b8b030bd authored by Dmitry Dementyev's avatar Dmitry Dementyev
Browse files

Add more methods to RecoverableKeyStoreLoader.

1) Methods to get key status.
2) Register pending intent to get notification about new recovery
snapshots.

Test: none
Bug: 66499222
Change-Id: I4d5f8c1a6581b5e08f4589e19961d93c499689e1
parent e51ffaa6
Loading
Loading
Loading
Loading
+97 −4
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.security.recoverablekeystore;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -29,6 +30,7 @@ import android.util.AndroidException;
import com.android.internal.widget.ILockSettings;

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

/**
 * A wrapper around KeyStore which lets key be exported to trusted hardware on server side and
@@ -43,9 +45,23 @@ public class RecoverableKeyStoreLoader {
    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.
    /**
     * Rate limit is enforced to prevent using too many trusted remote devices, since each device
     * can have its own number of user secret guesses allowed.
     *
     * @hide
     */
    public static final int RATE_LIMIT_EXCEEDED = 21;

    /** Key has been successfully synced. */
    public static final int RECOVERY_STATUS_SYNCED = 0;
    /** Waiting for recovery agent to sync the key. */
    public static final int RECOVERY_STATUS_SYNC_IN_PROGRESS = 1;
    /** Recovery account is not available. */
    public static final int RECOVERY_STATUS_MISSING_ACCOUNT = 2;
    /** Key cannot be synced. */
    public static final int RECOVERY_STATUS_PERMANENT_FAILURE = 3;

    private final ILockSettings mBinder;

    private RecoverableKeyStoreLoader(ILockSettings binder) {
@@ -155,7 +171,7 @@ public class RecoverableKeyStoreLoader {
     * @return Data necessary to recover keystore.
     * @hide
     */
    public KeyStoreRecoveryData getRecoveryData(@NonNull byte[] account)
    public @NonNull KeyStoreRecoveryData getRecoveryData(@NonNull byte[] account)
            throws RecoverableKeyStoreLoaderException {
        try {
            KeyStoreRecoveryData recoveryData =
@@ -168,6 +184,50 @@ public class RecoverableKeyStoreLoader {
        }
    }

    /**
     * Sets a listener which notifies recovery agent that new recovery snapshot is available. {@link
     * #getRecoveryData} can be used to get the snapshot. Note that every recovery agent can have at
     * most one registered listener at any time.
     *
     * @param intent triggered when new snapshot is available. Unregisters listener if the value is
     *     {@code null}.
     * @hide
     */
    public void setSnapshotCreatedPendingIntent(@Nullable PendingIntent intent)
            throws RecoverableKeyStoreLoaderException {
        try {
            mBinder.setSnapshotCreatedPendingIntent(intent, UserHandle.getCallingUserId());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } catch (ServiceSpecificException e) {
            throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
        }
    }

    /**
     * Returns a map from recovery agent accounts to corresponding KeyStore recovery snapshot
     * version. Version zero is used, if no snapshots were created for the account.
     *
     * @return Map from recovery agent accounts to snapshot versions.
     * @see KeyStoreRecoveryData.getSnapshotVersion
     * @hide
     */
    public @NonNull Map<byte[], Integer> getRecoverySnapshotVersions()
            throws RecoverableKeyStoreLoaderException {
        try {
            // IPC doesn't support generic Maps.
            @SuppressWarnings("unchecked")
            Map<byte[], Integer> result =
                    (Map<byte[], Integer>)
                            mBinder.getRecoverySnapshotVersions(UserHandle.getCallingUserId());
            return result;
        } 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
@@ -191,8 +251,8 @@ public class RecoverableKeyStoreLoader {

    /**
     * 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.
     * successfully stored on the server or there were an error. Application can check this value
     * using {@code getRecoveyStatus}.
     *
     * @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
@@ -211,6 +271,39 @@ public class RecoverableKeyStoreLoader {
        }
    }

    /**
     * Returns a {@code Map} from Application's KeyStore key aliases to their recovery status.
     * Negative status values are reserved for recovery agent specific codes. List of common codes:
     *
     * <ul>
     *   <li>{@link #RECOVERY_STATUS_SYNCED}
     *   <li>{@link #RECOVERY_STATUS_SYNC_IN_PROGRESS}
     *   <li>{@link #RECOVERY_STATUS_MISSING_ACCOUNT}
     *   <li>{@link #RECOVERY_STATUS_PERMANENT_FAILURE}
     * </ul>
     *
     * @param packageName Application whose recoverable keys' statuses are to be retrieved. if
     *     {@code null} caller's package will be used.
     * @return {@code Map} from KeyStore alias to recovery status.
     * @see #setRecoveryStatus
     * @hide
     */
    public Map<String, Integer> getRecoveryStatus(@Nullable String packageName)
            throws RecoverableKeyStoreLoaderException {
        try {
            // IPC doesn't support generic Maps.
            @SuppressWarnings("unchecked")
            Map<String, Integer> result =
                    (Map<String, Integer>)
                            mBinder.getRecoveryStatus(packageName, UserHandle.getCallingUserId());
            return result;
        } 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.
+6 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.internal.widget;

import android.app.PendingIntent;
import android.app.trust.IStrongAuthTracker;
import android.os.Bundle;
import android.security.recoverablekeystore.KeyEntryRecoveryData;
@@ -24,6 +25,8 @@ import android.security.recoverablekeystore.KeyStoreRecoveryMetadata;
import com.android.internal.widget.ICheckCredentialProgressCallback;
import com.android.internal.widget.VerifyCredentialResponse;

import java.util.Map;

/** {@hide} */
interface ILockSettings {
    void setBoolean(in String key, in boolean value, in int userId);
@@ -63,8 +66,11 @@ interface ILockSettings {
    void initRecoveryService(in String rootCertificateAlias, in byte[] signedPublicKeyList,
            int userId);
    KeyStoreRecoveryData getRecoveryData(in byte[] account, int userId);
    void setSnapshotCreatedPendingIntent(in PendingIntent intent, int userId);
    Map getRecoverySnapshotVersions(int userId);
    void setServerParameters(long serverParameters, int userId);
    void setRecoveryStatus(in String packageName, in String[] aliases, int status, int userId);
    Map getRecoveryStatus(in String packageName, int userId);
    void setRecoverySecretTypes(in int[] secretTypes, int userId);
    int[] getRecoverySecretTypes(int userId);
    int[] getPendingRecoverySecretTypes(int userId);
+13 −0
Original line number Diff line number Diff line
@@ -1943,6 +1943,15 @@ public class LockSettingsService extends ILockSettings.Stub {
        return mRecoverableKeyStoreManager.getRecoveryData(account, userId);
    }

    public void setSnapshotCreatedPendingIntent(@Nullable PendingIntent intent, int userId)
            throws RemoteException {
        mRecoverableKeyStoreManager.setSnapshotCreatedPendingIntent(intent, userId);
    }

    public Map getRecoverySnapshotVersions(int userId) throws RemoteException {
        return mRecoverableKeyStoreManager.getRecoverySnapshotVersions(userId);
    }

    @Override
    public void setServerParameters(long serverParameters, int userId) throws RemoteException {
        mRecoverableKeyStoreManager.setServerParameters(serverParameters, userId);
@@ -1954,6 +1963,10 @@ public class LockSettingsService extends ILockSettings.Stub {
        mRecoverableKeyStoreManager.setRecoveryStatus(packageName, aliases, status, userId);
    }

    public Map getRecoveryStatus(@Nullable String packageName, int userId) throws RemoteException {
        return mRecoverableKeyStoreManager.getRecoveryStatus(packageName, userId);
    }

    @Override
    public void setRecoverySecretTypes(@NonNull @KeyStoreRecoveryMetadata.UserSecretType
            int[] secretTypes, int userId) throws RemoteException {
+39 −4
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.locksettings.recoverablekeystore;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Context;
import android.os.Binder;
import android.os.RemoteException;
@@ -33,6 +34,7 @@ import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Class with {@link RecoverableKeyStoreLoader} API implementation and internal methods to interact
@@ -77,7 +79,7 @@ public class RecoverableKeyStoreManager {
     * @return recovery data
     * @hide
     */
    public KeyStoreRecoveryData getRecoveryData(@NonNull byte[] account, int userId)
    public @NonNull KeyStoreRecoveryData getRecoveryData(@NonNull byte[] account, int userId)
            throws RemoteException {
        checkRecoverKeyStorePermission();
        final int callingUid = Binder.getCallingUid(); // Recovery agent uid.
@@ -100,6 +102,24 @@ public class RecoverableKeyStoreManager {
                RecoverableKeyStoreLoader.UNINITIALIZED_RECOVERY_PUBLIC_KEY);
    }

    public void setSnapshotCreatedPendingIntent(@Nullable PendingIntent intent, int userId)
            throws RemoteException {
        checkRecoverKeyStorePermission();
        throw new UnsupportedOperationException();
    }

    /**
     * Gets recovery snapshot versions for all accounts. Note that snapshot may have 0 application
     * keys, but it still needs to be synced, if previous versions were not empty.
     *
     * @return Map from Recovery agent account to snapshot version.
     */
    public @NonNull Map<byte[], Integer> getRecoverySnapshotVersions(int userId)
            throws RemoteException {
        checkRecoverKeyStorePermission();
        throw new UnsupportedOperationException();
    }

    public void setServerParameters(long serverParameters, int userId) throws RemoteException {
        checkRecoverKeyStorePermission();
        throw new UnsupportedOperationException();
@@ -112,6 +132,21 @@ public class RecoverableKeyStoreManager {
        throw new UnsupportedOperationException();
    }

    /**
     * Gets recovery status for keys {@code packageName}.
     *
     * @param packageName which recoverable keys statuses will be returned
     * @return Map from KeyStore alias to recovery status
     */
    public @NonNull Map<String, Integer> getRecoveryStatus(@Nullable String packageName, int userId)
            throws RemoteException {
        // Any application should be able to check status for its own keys.
        // If caller is a recovery agent it can check statuses for other packages, but
        // only for recoverable keys it manages.
        checkRecoverKeyStorePermission();
        throw new UnsupportedOperationException();
    }

    /**
     * Sets recovery secrets list used by all recovery agents for given {@code userId}
     *
@@ -130,7 +165,7 @@ public class RecoverableKeyStoreManager {
     * @return secret types
     * @hide
     */
    public int[] getRecoverySecretTypes(int userId) throws RemoteException {
    public @NonNull int[] getRecoverySecretTypes(int userId) throws RemoteException {
        checkRecoverKeyStorePermission();
        throw new UnsupportedOperationException();
    }
@@ -141,7 +176,7 @@ public class RecoverableKeyStoreManager {
     * @return secret types
     * @hide
     */
    public int[] getPendingRecoverySecretTypes(int userId) throws RemoteException {
    public @NonNull int[] getPendingRecoverySecretTypes(int userId) throws RemoteException {
        checkRecoverKeyStorePermission();
        throw new UnsupportedOperationException();
    }
@@ -164,7 +199,7 @@ public class RecoverableKeyStoreManager {
     * @return recovery claim
     * @hide
     */
    public byte[] startRecoverySession(
    public @NonNull byte[] startRecoverySession(
            @NonNull String sessionId,
            @NonNull byte[] verifierPublicKey,
            @NonNull byte[] vaultParams,