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

Commit 4857cb50 authored by Bo Zhu's avatar Bo Zhu
Browse files

Continue to decrypt other application keys if one fails to decrypt

An exception will still be thrown if all the application keys fail to
decrypt.

Test: adb shell am instrument -w -e package \
com.android.server.locksettings.recoverablekeystore \
com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
Bug: 72932279
Change-Id: I95befcd46672b77380226e61369759de5617ecea
parent ac5438c5
Loading
Loading
Loading
Loading
+13 −10
Original line number Diff line number Diff line
@@ -519,11 +519,11 @@ public class RecoverableKeyStoreManager {
        byte[] locallyEncryptedKey;
        try {
            // TODO: Remove the extraneous logging here
            Log.e(TAG, constructLoggingMessage("sessionEntry.getKeyClaimant()",
            Log.d(TAG, constructLoggingMessage("sessionEntry.getKeyClaimant()",
                    sessionEntry.getKeyClaimant()));
            Log.e(TAG, constructLoggingMessage("sessionEntry.getVaultParams()",
            Log.d(TAG, constructLoggingMessage("sessionEntry.getVaultParams()",
                    sessionEntry.getVaultParams()));
            Log.e(TAG, constructLoggingMessage("encryptedClaimResponse", encryptedClaimResponse));
            Log.d(TAG, constructLoggingMessage("encryptedClaimResponse", encryptedClaimResponse));
            locallyEncryptedKey = KeySyncUtils.decryptRecoveryClaimResponse(
                    sessionEntry.getKeyClaimant(),
                    sessionEntry.getVaultParams(),
@@ -543,9 +543,9 @@ public class RecoverableKeyStoreManager {

        try {
            // TODO: Remove the extraneous logging here
            Log.e(TAG, constructLoggingMessage("sessionEntry.getLskfHash()",
            Log.d(TAG, constructLoggingMessage("sessionEntry.getLskfHash()",
                    sessionEntry.getLskfHash()));
            Log.e(TAG, constructLoggingMessage("locallyEncryptedKey", locallyEncryptedKey));
            Log.d(TAG, constructLoggingMessage("locallyEncryptedKey", locallyEncryptedKey));
            return KeySyncUtils.decryptRecoveryKey(sessionEntry.getLskfHash(), locallyEncryptedKey);
        } catch (InvalidKeyException e) {
            Log.e(TAG, "Got InvalidKeyException during decrypting recovery key", e);
@@ -585,8 +585,8 @@ public class RecoverableKeyStoreManager {

            try {
                // TODO: Remove the extraneous logging here
                Log.e(TAG, constructLoggingMessage("recoveryKey", recoveryKey));
                Log.e(TAG, constructLoggingMessage("encryptedKeyMaterial", encryptedKeyMaterial));
                Log.d(TAG, constructLoggingMessage("recoveryKey", recoveryKey));
                Log.d(TAG, constructLoggingMessage("encryptedKeyMaterial", encryptedKeyMaterial));
                byte[] keyMaterial =
                        KeySyncUtils.decryptApplicationKey(recoveryKey, encryptedKeyMaterial);
                keyMaterialByAlias.put(alias, keyMaterial);
@@ -600,13 +600,16 @@ public class RecoverableKeyStoreManager {
                throw new ServiceSpecificException(ERROR_DECRYPTION_FAILED,
                        "Failed to recover key with alias '" + alias + "': " + e.getMessage());
            } catch (AEADBadTagException e) {
                // TODO: Remove the extraneous logging here
                Log.e(TAG, "Got AEADBadTagException during decrypting application key with alias: "
                        + alias, e);
                throw new ServiceSpecificException(ERROR_DECRYPTION_FAILED,
                        "Failed to recover key with alias '" + alias + "': " + e.getMessage());
                // Ignore the exception to continue to recover the other application keys.
            }
        }
        if (keyMaterialByAlias.isEmpty()) {
            Log.e(TAG, "Failed to recover any of the application keys.");
            throw new ServiceSpecificException(ERROR_DECRYPTION_FAILED,
                    "Failed to recover any of the application keys.");
        }
        return keyMaterialByAlias;
    }

+42 −3
Original line number Diff line number Diff line
@@ -125,6 +125,7 @@ public class RecoverableKeyStoreManagerTest {
    private static final byte[] RECOVERY_RESPONSE_HEADER =
            "V1 reencrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
    private static final String TEST_ALIAS = "nick";
    private static final String TEST_ALIAS2 = "bob";
    private static final int RECOVERABLE_KEY_SIZE_BYTES = 32;
    private static final int GENERATION_ID = 1;
    private static final byte[] NONCE = getUtf8Bytes("nonce");
@@ -424,7 +425,7 @@ public class RecoverableKeyStoreManagerTest {
    }

    @Test
    public void recoverKeys_throwsIfFailedToDecryptAnApplicationKey() throws Exception {
    public void recoverKeys_throwsIfFailedToDecryptAllApplicationKeys() throws Exception {
        mRecoverableKeyStoreManager.startRecoverySession(
                TEST_SESSION_ID,
                TEST_PUBLIC_KEY,
@@ -442,7 +443,7 @@ public class RecoverableKeyStoreManagerTest {
                keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey);
        WrappedApplicationKey badApplicationKey = new WrappedApplicationKey(
                TEST_ALIAS,
                randomBytes(32));
                encryptedApplicationKey(randomRecoveryKey(), randomBytes(32)));

        try {
            mRecoverableKeyStoreManager.recoverKeys(
@@ -451,7 +452,7 @@ public class RecoverableKeyStoreManagerTest {
                    /*applicationKeys=*/ ImmutableList.of(badApplicationKey));
            fail("should have thrown");
        } catch (ServiceSpecificException e) {
            assertThat(e.getMessage()).startsWith("Failed to recover key with alias 'nick'");
            assertThat(e.getMessage()).startsWith("Failed to recover any of the application keys");
        }
    }

@@ -486,6 +487,44 @@ public class RecoverableKeyStoreManagerTest {
        assertThat(recoveredKeys.get(TEST_ALIAS)).isEqualTo(applicationKeyBytes);
    }

    @Test
    public void recoverKeys_worksOnOtherApplicationKeysIfOneDecryptionFails() throws Exception {
        mRecoverableKeyStoreManager.startRecoverySession(
                TEST_SESSION_ID,
                TEST_PUBLIC_KEY,
                TEST_VAULT_PARAMS,
                TEST_VAULT_CHALLENGE,
                ImmutableList.of(new KeyChainProtectionParams(
                        TYPE_LOCKSCREEN,
                        UI_FORMAT_PASSWORD,
                        KeyDerivationParams.createSha256Params(TEST_SALT),
                        TEST_SECRET)));
        byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID)
                .getKeyClaimant();
        SecretKey recoveryKey = randomRecoveryKey();
        byte[] encryptedClaimResponse = encryptClaimResponse(
                keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey);

        byte[] applicationKeyBytes1 = randomBytes(32);
        byte[] applicationKeyBytes2 = randomBytes(32);

        WrappedApplicationKey applicationKey1 = new WrappedApplicationKey(
                TEST_ALIAS,
                // Use a different recovery key here, so the decryption will fail
                encryptedApplicationKey(randomRecoveryKey(), applicationKeyBytes1));
        WrappedApplicationKey applicationKey2 = new WrappedApplicationKey(
                TEST_ALIAS2,
                encryptedApplicationKey(recoveryKey, applicationKeyBytes2));

        Map<String, byte[]> recoveredKeys = mRecoverableKeyStoreManager.recoverKeys(
                TEST_SESSION_ID,
                encryptedClaimResponse,
                ImmutableList.of(applicationKey1, applicationKey2));

        assertThat(recoveredKeys).hasSize(1);
        assertThat(recoveredKeys.get(TEST_ALIAS2)).isEqualTo(applicationKeyBytes2);
    }

    @Test
    public void setSnapshotCreatedPendingIntent() throws Exception {
        int uid = Binder.getCallingUid();