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

Commit 889e78cb authored by Robert Berry's avatar Robert Berry Committed by Android (Google) Code Review
Browse files

Merge "Add RecoverySession importKeyChainSnapshot method" into pi-dev

parents 82235880 4a5c87de
Loading
Loading
Loading
Loading
+13 −12
Original line number Diff line number Diff line
@@ -547,10 +547,7 @@ public class RecoveryController {
            if (grantAlias == null) {
                throw new InternalRecoveryServiceException("null grant alias");
            }
            return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
                    mKeyStore,
                    grantAlias,
                    KeyStore.UID_SELF);
            return getKeyFromGrant(grantAlias);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } catch (UnrecoverableKeyException e) {
@@ -581,10 +578,7 @@ public class RecoveryController {
            if (grantAlias == null) {
                throw new InternalRecoveryServiceException("Null grant alias");
            }
            return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
                    mKeyStore,
                    grantAlias,
                    KeyStore.UID_SELF);
            return getKeyFromGrant(alias);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } catch (UnrecoverableKeyException e) {
@@ -614,10 +608,7 @@ public class RecoveryController {
            if (grantAlias == null) {
                return null;
            }
            return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
                    mKeyStore,
                    grantAlias,
                    KeyStore.UID_SELF);
            return getKeyFromGrant(grantAlias);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } catch (ServiceSpecificException e) {
@@ -625,6 +616,16 @@ public class RecoveryController {
        }
    }

    /**
     * Returns the key with the given {@code grantAlias}.
     */
    Key getKeyFromGrant(String grantAlias) throws UnrecoverableKeyException {
        return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
                mKeyStore,
                grantAlias,
                KeyStore.UID_SELF);
    }

    /**
     * Removes a key called {@code alias} from the recoverable key store.
     *
+62 −0
Original line number Diff line number Diff line
@@ -16,17 +16,22 @@

package android.security.keystore.recovery;

import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.ArrayMap;
import android.util.Log;

import java.security.Key;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertPath;
import java.security.cert.CertificateException;
import java.util.List;
import java.util.Locale;
import java.util.Map;

/**
@@ -243,6 +248,63 @@ public class RecoverySession implements AutoCloseable {
        }
    }

    /**
     * Imports key chain snapshot recovered from a remote vault.
     *
     * @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.
     * @throws SessionExpiredException if {@code session} has since been closed.
     * @throws DecryptionFailedException if unable to decrypt the snapshot.
     * @throws InternalRecoveryServiceException if an error occurs internal to the recovery service.
     *
     * @hide
     */
    @RequiresPermission(Manifest.permission.RECOVER_KEYSTORE)
    public Map<String, Key> recoverKeyChainSnapshot(
            @NonNull byte[] recoveryKeyBlob,
            @NonNull List<WrappedApplicationKey> applicationKeys
    ) throws SessionExpiredException, DecryptionFailedException, InternalRecoveryServiceException {
        try {
            Map<String, String> grantAliases = mRecoveryController
                    .getBinder()
                    .recoverKeyChainSnapshot(mSessionId, recoveryKeyBlob, applicationKeys);
            return getKeysFromGrants(grantAliases);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } catch (ServiceSpecificException e) {
            if (e.errorCode == RecoveryController.ERROR_DECRYPTION_FAILED) {
                throw new DecryptionFailedException(e.getMessage());
            }
            if (e.errorCode == RecoveryController.ERROR_SESSION_EXPIRED) {
                throw new SessionExpiredException(e.getMessage());
            }
            throw mRecoveryController.wrapUnexpectedServiceSpecificException(e);
        }
    }

    /** Given a map from alias to grant alias, returns a map from alias to a {@link Key} handle. */
    private Map<String, Key> getKeysFromGrants(Map<String, String> grantAliases)
            throws InternalRecoveryServiceException {
        ArrayMap<String, Key> keysByAlias = new ArrayMap<>(grantAliases.size());
        for (String alias : grantAliases.keySet()) {
            String grantAlias = grantAliases.get(alias);
            Key key;
            try {
                key = mRecoveryController.getKeyFromGrant(grantAlias);
            } catch (UnrecoverableKeyException e) {
                throw new InternalRecoveryServiceException(
                        String.format(
                                Locale.US,
                                "Failed to get key '%s' from grant '%s'",
                                alias,
                                grantAlias), e);
            }
            keysByAlias.put(alias, key);
        }
        return keysByAlias;
    }

    /**
     * An internal session ID, used by the framework to match recovery claims to snapshot responses.
     *
+4 −0
Original line number Diff line number Diff line
@@ -83,5 +83,9 @@ interface ILockSettings {
            in List<KeyChainProtectionParams> secrets);
    Map/*<String, byte[]>*/ recoverKeys(in String sessionId, in byte[] recoveryKeyBlob,
            in List<WrappedApplicationKey> applicationKeys);
    Map/*<String, String>*/ recoverKeyChainSnapshot(
            in String sessionId,
            in byte[] recoveryKeyBlob,
            in List<WrappedApplicationKey> applicationKeys);
    void closeSession(in String sessionId);
}
+10 −2
Original line number Diff line number Diff line
@@ -2064,12 +2064,20 @@ public class LockSettingsService extends ILockSettings.Stub {
        mRecoverableKeyStoreManager.closeSession(sessionId);
    }

    @Override
    public Map<String, String> recoverKeyChainSnapshot(
            @NonNull String sessionId,
            @NonNull byte[] recoveryKeyBlob,
            @NonNull List<WrappedApplicationKey> applicationKeys) throws RemoteException {
        return mRecoverableKeyStoreManager.recoverKeyChainSnapshot(
                sessionId, recoveryKeyBlob, applicationKeys);
    }

    @Override
    public Map<String, byte[]> recoverKeys(@NonNull String sessionId,
            @NonNull byte[] recoveryKeyBlob, @NonNull List<WrappedApplicationKey> applicationKeys)
            throws RemoteException {
        return mRecoverableKeyStoreManager.recoverKeys(
                sessionId, recoveryKeyBlob, applicationKeys);
        return mRecoverableKeyStoreManager.recoverKeys(sessionId, recoveryKeyBlob, applicationKeys);
    }

    @Override
+77 −4
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Context;
import android.os.Binder;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.UserHandle;
@@ -41,6 +42,7 @@ import android.security.keystore.recovery.RecoveryController;
import android.security.keystore.recovery.TrustedRootCertificates;
import android.security.keystore.recovery.WrappedApplicationKey;
import android.security.KeyStore;
import android.util.ArrayMap;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
@@ -555,6 +557,78 @@ public class RecoverableKeyStoreManager {
        }
    }

    /**
     * Invoked by a recovery agent after a successful recovery claim is sent to the remote vault
     * service.
     *
     * @param sessionId The session ID used to generate the claim. See
     *     {@link #startRecoverySession(String, byte[], byte[], byte[], List)}.
     * @param encryptedRecoveryKey The encrypted recovery key blob returned by the remote vault
     *     service.
     * @param applicationKeys The encrypted key blobs returned by the remote vault service. These
     *     were wrapped with the recovery key.
     * @throws RemoteException if an error occurred recovering the keys.
     */
    public Map<String, String> recoverKeyChainSnapshot(
            @NonNull String sessionId,
            @NonNull byte[] encryptedRecoveryKey,
            @NonNull List<WrappedApplicationKey> applicationKeys) throws RemoteException {
        checkRecoverKeyStorePermission();
        int userId = UserHandle.getCallingUserId();
        int uid = Binder.getCallingUid();
        RecoverySessionStorage.Entry sessionEntry = mRecoverySessionStorage.get(uid, sessionId);
        if (sessionEntry == null) {
            throw new ServiceSpecificException(ERROR_SESSION_EXPIRED,
                    String.format(Locale.US,
                            "Application uid=%d does not have pending session '%s'",
                            uid,
                            sessionId));
        }

        try {
            byte[] recoveryKey = decryptRecoveryKey(sessionEntry, encryptedRecoveryKey);
            Map<String, byte[]> keysByAlias = recoverApplicationKeys(recoveryKey, applicationKeys);
            return importKeyMaterials(userId, uid, keysByAlias);
        } catch (KeyStoreException e) {
            throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
        } finally {
            sessionEntry.destroy();
            mRecoverySessionStorage.remove(uid);
        }
    }

    /**
     * Imports the key materials, returning a map from alias to grant alias for the calling user.
     *
     * @param userId The calling user ID.
     * @param uid The calling uid.
     * @param keysByAlias The key materials, keyed by alias.
     * @throws KeyStoreException if an error occurs importing the key or getting the grant.
     */
    private Map<String, String> importKeyMaterials(
            int userId, int uid, Map<String, byte[]> keysByAlias) throws KeyStoreException {
        ArrayMap<String, String> grantAliasesByAlias = new ArrayMap<>(keysByAlias.size());
        for (String alias : keysByAlias.keySet()) {
            mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, keysByAlias.get(alias));
            String grantAlias = getAlias(userId, uid, alias);
            Log.i(TAG, String.format(Locale.US, "Import %s -> %s", alias, grantAlias));
            grantAliasesByAlias.put(alias, grantAlias);
        }
        return grantAliasesByAlias;
    }

    /**
     * Returns an alias for the key.
     *
     * @param userId The user ID of the calling process.
     * @param uid The uid of the calling process.
     * @param alias The alias of the key.
     * @return The alias in the calling process's keystore.
     */
    private String getAlias(int userId, int uid, String alias) {
        return mApplicationKeyStorage.getGrantAlias(userId, uid, alias);
    }

    /**
     * Deprecated
     * Generates a key named {@code alias} in the recoverable store for the calling uid. Then
@@ -632,7 +706,7 @@ public class RecoverableKeyStoreManager {
            byte[] secretKey =
                    mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, uid, alias);
            mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, secretKey);
            return mApplicationKeyStorage.getGrantAlias(userId, uid, alias);
            return getAlias(userId, uid, alias);
        } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) {
            throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
        }
@@ -683,7 +757,7 @@ public class RecoverableKeyStoreManager {

            // Import the key to Android KeyStore and get grant
            mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, keyBytes);
            return mApplicationKeyStorage.getGrantAlias(userId, uid, alias);
            return getAlias(userId, uid, alias);
        } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) {
            throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
        }
@@ -697,8 +771,7 @@ public class RecoverableKeyStoreManager {
    public String getKey(@NonNull String alias) throws RemoteException {
        int uid = Binder.getCallingUid();
        int userId = UserHandle.getCallingUserId();
        String grantAlias = mApplicationKeyStorage.getGrantAlias(userId, uid, alias);
        return grantAlias;
        return getAlias(userId, uid, alias);
    }

    private byte[] decryptRecoveryKey(
Loading