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

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

Merge "Throw on attempt to unwrap a WrappedKey with old PlatformKey"

parents dcddaa57 57019387
Loading
Loading
Loading
Loading
+35 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.locksettings.recoverablekeystore;

/**
 * Error thrown when the {@link PlatformDecryptionKey} instance has a different generation ID from
 * the {@link WrappedKey} instance.
 *
 * @hide
 */
public class BadPlatformKeyException extends Exception {

    /**
     * A new instance with {@code message}.
     *
     * @hide
     */
    public BadPlatformKeyException(String message) {
        super(message);
    }
}
+65 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.locksettings.recoverablekeystore;

import android.security.keystore.AndroidKeyStoreSecretKey;

/**
 * Used to unwrap recoverable keys before syncing them with remote storage.
 *
 * <p>This is a private key stored in AndroidKeyStore. Has an associated generation ID, which is
 * stored with wrapped keys, allowing us to ensure the wrapped key has the same version as the
 * platform key.
 *
 * @hide
 */
public class PlatformDecryptionKey {

    private final int mGenerationId;
    private final AndroidKeyStoreSecretKey mKey;

    /**
     * A new instance.
     *
     * @param generationId The generation ID of the platform key.
     * @param key The key handle in AndroidKeyStore.
     *
     * @hide
     */
    public PlatformDecryptionKey(int generationId, AndroidKeyStoreSecretKey key) {
        mGenerationId = generationId;
        mKey = key;
    }

    /**
     * Returns the generation ID.
     *
     * @hide
     */
    public int getGenerationId() {
        return mGenerationId;
    }

    /**
     * Returns the actual key, which can be used to decrypt.
     *
     * @hide
     */
    public AndroidKeyStoreSecretKey getKey() {
        return mKey;
    }
}
+27 −3
Original line number Diff line number Diff line
@@ -124,27 +124,51 @@ public class WrappedKey {
        return mKeyMaterial;
    }


    /**
     * Returns the generation ID of the platform key, with which this key was wrapped.
     *
     * @hide
     */
    public int getPlatformKeyGenerationId() {
        // TODO(robertberry) Implement. See ag/3362855.
        return 1;
    }

    /**
     * Unwraps the {@code wrappedKeys} with the {@code platformKey}.
     *
     * @return The unwrapped keys, indexed by alias.
     * @throws NoSuchAlgorithmException if AES/GCM/NoPadding Cipher or AES key type is unavailable.
     * @throws BadPlatformKeyException if the {@code platformKey} has a different generation ID to
     *     any of the {@code wrappedKeys}.
     *
     * @hide
     */
    public static Map<String, SecretKey> unwrapKeys(
            SecretKey platformKey,
            PlatformDecryptionKey platformKey,
            Map<String, WrappedKey> wrappedKeys)
            throws NoSuchAlgorithmException, NoSuchPaddingException {
            throws NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException {
        HashMap<String, SecretKey> unwrappedKeys = new HashMap<>();
        Cipher cipher = Cipher.getInstance(KEY_WRAP_CIPHER_ALGORITHM);
        int platformKeyGenerationId = platformKey.getGenerationId();

        for (String alias : wrappedKeys.keySet()) {
            WrappedKey wrappedKey = wrappedKeys.get(alias);
            if (wrappedKey.getPlatformKeyGenerationId() != platformKeyGenerationId) {
                throw new BadPlatformKeyException(String.format(
                        Locale.US,
                        "WrappedKey with alias '%s' was wrapped with platform key %d, not "
                                + "platform key %d",
                        alias,
                        wrappedKey.getPlatformKeyGenerationId(),
                        platformKey.getGenerationId()));
            }

            try {
                cipher.init(
                        Cipher.UNWRAP_MODE,
                        platformKey,
                        platformKey.getKey(),
                        new GCMParameterSpec(GCM_TAG_LENGTH_BITS, wrappedKey.getNonce()));
            } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
                Log.e(TAG,
+31 −3
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.locksettings.recoverablekeystore;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import android.security.keystore.AndroidKeyStoreSecretKey;
import android.security.keystore.KeyGenParameterSpec;
@@ -46,6 +47,7 @@ public class WrappedKeyTest {
    private static final String KEY_ALGORITHM = "AES";
    private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";
    private static final String WRAPPING_KEY_ALIAS = "WrappedKeyTestWrappingKeyAlias";
    private static final int GENERATION_ID = 1;
    private static final int GCM_TAG_LENGTH_BYTES = 16;
    private static final int BITS_PER_BYTE = 8;
    private static final int GCM_TAG_LENGTH_BITS = GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE;
@@ -77,9 +79,9 @@ public class WrappedKeyTest {
    @Test
    public void decryptWrappedKeys_decryptsWrappedKeys() throws Exception {
        String alias = "karlin";
        SecretKey platformKey = generateAndroidKeyStoreKey();
        PlatformDecryptionKey platformKey = generatePlatformDecryptionKey();
        SecretKey appKey = generateKey();
        WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, appKey);
        WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey.getKey(), appKey);
        HashMap<String, WrappedKey> keysByAlias = new HashMap<>();
        keysByAlias.put(alias, wrappedKey);

@@ -99,11 +101,29 @@ public class WrappedKeyTest {
        keysByAlias.put(alias, wrappedKey);

        Map<String, SecretKey> unwrappedKeys = WrappedKey.unwrapKeys(
                generateAndroidKeyStoreKey(), keysByAlias);
                generatePlatformDecryptionKey(), keysByAlias);

        assertEquals(0, unwrappedKeys.size());
    }

    @Test
    public void decryptWrappedKeys_throwsIfPlatformKeyGenerationIdDoesNotMatch() throws Exception {
        WrappedKey wrappedKey = WrappedKey.fromSecretKey(generateKey(), generateKey());
        HashMap<String, WrappedKey> keysByAlias = new HashMap<>();
        keysByAlias.put("benji", wrappedKey);

        try {
            WrappedKey.unwrapKeys(
                    generatePlatformDecryptionKey(/*generationId=*/ 2), keysByAlias);
            fail("Should have thrown.");
        } catch (BadPlatformKeyException e) {
            assertEquals(
                    "WrappedKey with alias 'benji' was wrapped with platform key 1,"
                            + " not platform key 2",
                    e.getMessage());
        }
    }

    private SecretKey generateKey() throws Exception {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
        keyGenerator.init(/*keySize=*/ 256);
@@ -122,4 +142,12 @@ public class WrappedKeyTest {
                .build());
        return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
    }

    private PlatformDecryptionKey generatePlatformDecryptionKey() throws Exception {
        return generatePlatformDecryptionKey(GENERATION_ID);
    }

    private PlatformDecryptionKey generatePlatformDecryptionKey(int generationId) throws Exception {
        return new PlatformDecryptionKey(generationId, generateAndroidKeyStoreKey());
    }
}