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

Commit 282b4069 authored by Al Sutton's avatar Al Sutton
Browse files

Import RestoreKeyFetcher

Bug: 111386661
Test: atest BackupEncryptionRoboTests
Change-Id: I4163ebda82f588c624255f2a090e4ddab55e998f
parent 70f1945f
Loading
Loading
Loading
Loading
+71 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.backup.encryption.keys;

import android.security.keystore.recovery.InternalRecoveryServiceException;

import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager.RecoverableKeyStoreSecondaryKeyManagerProvider;
import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;

import java.security.InvalidAlgorithmParameterException;
import java.security.KeyException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.util.Optional;

import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;

/** Fetches the secondary key and uses it to unwrap the tertiary key during restore. */
public class RestoreKeyFetcher {

    /**
     * Retrieves the secondary key with the given alias and uses it to unwrap the given wrapped
     * tertiary key.
     *
     * @param secondaryKeyManagerProvider Provider which creates {@link
     *     RecoverableKeyStoreSecondaryKeyManager}
     * @param secondaryKeyAlias Alias of the secondary key used to wrap the tertiary key
     * @param wrappedTertiaryKey Tertiary key wrapped with the secondary key above
     * @return The unwrapped tertiary key
     */
    public static SecretKey unwrapTertiaryKey(
            RecoverableKeyStoreSecondaryKeyManagerProvider secondaryKeyManagerProvider,
            String secondaryKeyAlias,
            WrappedKeyProto.WrappedKey wrappedTertiaryKey)
            throws KeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException,
                    NoSuchPaddingException {
        Optional<RecoverableKeyStoreSecondaryKey> secondaryKey =
                getSecondaryKey(secondaryKeyManagerProvider, secondaryKeyAlias);
        if (!secondaryKey.isPresent()) {
            throw new KeyException("No key:" + secondaryKeyAlias);
        }

        return KeyWrapUtils.unwrap(secondaryKey.get().getSecretKey(), wrappedTertiaryKey);
    }

    private static Optional<RecoverableKeyStoreSecondaryKey> getSecondaryKey(
            RecoverableKeyStoreSecondaryKeyManagerProvider secondaryKeyManagerProvider,
            String secondaryKeyAlias)
            throws KeyException {
        try {
            return secondaryKeyManagerProvider.get().get(secondaryKeyAlias);
        } catch (InternalRecoveryServiceException | UnrecoverableKeyException e) {
            throw new KeyException("Could not retrieve key:" + secondaryKeyAlias, e);
        }
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ android_robolectric_test {
        "backup-encryption-protos",
        "platform-test-annotations",
        "testng",
        "truth-prebuilt",
    ],
    instrumentation_for: "BackupEncryption",
}
+127 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.backup.encryption.keys;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;

import android.platform.test.annotations.Presubmit;

import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;

import java.security.InvalidKeyException;
import java.security.KeyException;
import java.security.SecureRandom;
import java.util.Optional;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/** Test the restore key fetcher */
@RunWith(RobolectricTestRunner.class)
@Presubmit
public class RestoreKeyFetcherTest {

    private static final String KEY_GENERATOR_ALGORITHM = "AES";

    private static final String TEST_SECONDARY_KEY_ALIAS = "test_2ndary_key";
    private static final byte[] TEST_SECONDARY_KEY_BYTES = new byte[256 / Byte.SIZE];

    @Mock private RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager;

    /** Initialise the mocks **/
    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    /** Ensure the unwrap method works as expected */
    @Test
    public void unwrapTertiaryKey_returnsUnwrappedKey() throws Exception {
        RecoverableKeyStoreSecondaryKey secondaryKey = createSecondaryKey();
        SecretKey tertiaryKey = createTertiaryKey();
        WrappedKeyProto.WrappedKey wrappedTertiaryKey =
                KeyWrapUtils.wrap(secondaryKey.getSecretKey(), tertiaryKey);
        when(mSecondaryKeyManager.get(TEST_SECONDARY_KEY_ALIAS))
                .thenReturn(Optional.of(secondaryKey));

        SecretKey actualTertiaryKey =
                RestoreKeyFetcher.unwrapTertiaryKey(
                        () -> mSecondaryKeyManager,
                        TEST_SECONDARY_KEY_ALIAS,
                        wrappedTertiaryKey);

        assertThat(actualTertiaryKey).isEqualTo(tertiaryKey);
    }

    /** Ensure that missing secondary keys are detected and an appropriate exception is thrown */
    @Test
    public void unwrapTertiaryKey_missingSecondaryKey_throwsSpecificException() throws Exception {
        WrappedKeyProto.WrappedKey wrappedTertiaryKey =
                KeyWrapUtils.wrap(createSecondaryKey().getSecretKey(), createTertiaryKey());
        when(mSecondaryKeyManager.get(TEST_SECONDARY_KEY_ALIAS)).thenReturn(Optional.empty());

        assertThrows(
                KeyException.class,
                () ->
                        RestoreKeyFetcher.unwrapTertiaryKey(
                                () -> mSecondaryKeyManager,
                                TEST_SECONDARY_KEY_ALIAS,
                                wrappedTertiaryKey));
    }

    /** Ensure that invalid secondary keys are detected and an appropriate exception is thrown */
    @Test
    public void unwrapTertiaryKey_badSecondaryKey_throws() throws Exception {
        RecoverableKeyStoreSecondaryKey badSecondaryKey =
                new RecoverableKeyStoreSecondaryKey(
                        TEST_SECONDARY_KEY_ALIAS,
                        new SecretKeySpec(new byte[] {0, 1}, KEY_GENERATOR_ALGORITHM));

        WrappedKeyProto.WrappedKey wrappedTertiaryKey =
                KeyWrapUtils.wrap(createSecondaryKey().getSecretKey(), createTertiaryKey());
        when(mSecondaryKeyManager.get(TEST_SECONDARY_KEY_ALIAS))
                .thenReturn(Optional.of(badSecondaryKey));

        assertThrows(
                InvalidKeyException.class,
                () ->
                        RestoreKeyFetcher.unwrapTertiaryKey(
                                () -> mSecondaryKeyManager,
                                TEST_SECONDARY_KEY_ALIAS,
                                wrappedTertiaryKey));
    }

    private static RecoverableKeyStoreSecondaryKey createSecondaryKey() {
        return new RecoverableKeyStoreSecondaryKey(
                TEST_SECONDARY_KEY_ALIAS,
                new SecretKeySpec(TEST_SECONDARY_KEY_BYTES, KEY_GENERATOR_ALGORITHM));
    }

    private static SecretKey createTertiaryKey() {
        return new TertiaryKeyGenerator(new SecureRandom(new byte[] {0})).generate();
    }
}