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

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

Merge "Add PlatformEncryptionKey (again)"

parents 93592a9b 67b228c4
Loading
Loading
Loading
Loading
+62 −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;

/**
 * Private key stored in AndroidKeyStore. Used to wrap recoverable keys before writing them to disk.
 *
 * <p>Identified by a generation ID, which increments whenever a new platform key is generated. A
 * new key must be generated whenever the user disables their lock screen, as the decryption key is
 * tied to lock-screen authentication.
 *
 * <p>One current platform key exists per profile on the device. (As each must be tied to a
 * different user's lock screen.)
 *
 * @hide
 */
public class PlatformEncryptionKey {

    private final int mGenerationId;
    private final AndroidKeyStoreSecretKey mKey;

    /**
     * A new instance.
     *
     * @param generationId The generation ID of the key.
     * @param key The secret key handle. Can be used to encrypt WITHOUT requiring screen unlock.
     */
    public PlatformEncryptionKey(int generationId, AndroidKeyStoreSecretKey key) {
        mGenerationId = generationId;
        mKey = key;
    }

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

    /**
     * Returns the actual key, which can only be used to encrypt.
     */
    public AndroidKeyStoreSecretKey getKey() {
        return mKey;
    }
}
+3 −3
Original line number Diff line number Diff line
@@ -56,7 +56,7 @@ public class RecoverableKeyGenerator {
     * @hide
     */
    public static RecoverableKeyGenerator newInstance(
            AndroidKeyStoreSecretKey platformKey, RecoverableKeyStorage recoverableKeyStorage)
            PlatformEncryptionKey platformKey, RecoverableKeyStorage recoverableKeyStorage)
            throws NoSuchAlgorithmException {
        // NB: This cannot use AndroidKeyStore as the provider, as we need access to the raw key
        // material, so that it can be synced to disk in encrypted form.
@@ -66,11 +66,11 @@ public class RecoverableKeyGenerator {

    private final KeyGenerator mKeyGenerator;
    private final RecoverableKeyStorage mRecoverableKeyStorage;
    private final AndroidKeyStoreSecretKey mPlatformKey;
    private final PlatformEncryptionKey mPlatformKey;

    private RecoverableKeyGenerator(
            KeyGenerator keyGenerator,
            AndroidKeyStoreSecretKey platformKey,
            PlatformEncryptionKey platformKey,
            RecoverableKeyStorage recoverableKeyStorage) {
        mKeyGenerator = keyGenerator;
        mRecoverableKeyStorage = recoverableKeyStorage;
+12 −7
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ public class WrappedKey {
    private static final String APPLICATION_KEY_ALGORITHM = "AES";
    private static final int GCM_TAG_LENGTH_BITS = 128;

    private final int mPlatformKeyGenerationId;
    private final byte[] mNonce;
    private final byte[] mKeyMaterial;

@@ -55,8 +56,8 @@ public class WrappedKey {
     *     {@link android.security.keystore.AndroidKeyStoreKey} for an example of a key that does
     *     not expose its key material.
     */
    public static WrappedKey fromSecretKey(
            SecretKey wrappingKey, SecretKey key) throws InvalidKeyException, KeyStoreException {
    public static WrappedKey fromSecretKey(PlatformEncryptionKey wrappingKey, SecretKey key)
            throws InvalidKeyException, KeyStoreException {
        if (key.getEncoded() == null) {
            throw new InvalidKeyException(
                    "key does not expose encoded material. It cannot be wrapped.");
@@ -70,7 +71,7 @@ public class WrappedKey {
                    "Android does not support AES/GCM/NoPadding. This should never happen.");
        }

        cipher.init(Cipher.WRAP_MODE, wrappingKey);
        cipher.init(Cipher.WRAP_MODE, wrappingKey.getKey());
        byte[] encryptedKeyMaterial;
        try {
            encryptedKeyMaterial = cipher.wrap(key);
@@ -90,7 +91,10 @@ public class WrappedKey {
            }
        }

        return new WrappedKey(/*mNonce=*/ cipher.getIV(), /*mKeyMaterial=*/ encryptedKeyMaterial);
        return new WrappedKey(
                /*nonce=*/ cipher.getIV(),
                /*keyMaterial=*/ encryptedKeyMaterial,
                /*platformKeyGenerationId=*/ wrappingKey.getGenerationId());
    }

    /**
@@ -98,12 +102,14 @@ public class WrappedKey {
     *
     * @param nonce The nonce with which the key material was encrypted.
     * @param keyMaterial The encrypted bytes of the key material.
     * @param platformKeyGenerationId The generation ID of the key used to wrap this key.
     *
     * @hide
     */
    public WrappedKey(byte[] nonce, byte[] keyMaterial) {
    public WrappedKey(byte[] nonce, byte[] keyMaterial, int platformKeyGenerationId) {
        mNonce = nonce;
        mKeyMaterial = keyMaterial;
        mPlatformKeyGenerationId = platformKeyGenerationId;
    }

    /**
@@ -131,8 +137,7 @@ public class WrappedKey {
     * @hide
     */
    public int getPlatformKeyGenerationId() {
        // TODO(robertberry) Implement. See ag/3362855.
        return 1;
        return mPlatformKeyGenerationId;
    }

    /**
+7 −6
Original line number Diff line number Diff line
@@ -62,13 +62,12 @@ public class RecoverableKeyStoreDb {
     *
     * @param uid Uid of the application to whom the key belongs.
     * @param alias The alias of the key in the AndroidKeyStore.
     * @param wrappedKey The wrapped bytes of the key.
     * @param generationId The generation ID of the platform key that wrapped the key.
     * @param wrappedKey The wrapped key.
     * @return The primary key of the inserted row, or -1 if failed.
     *
     * @hide
     */
    public long insertKey(int uid, String alias, WrappedKey wrappedKey, int generationId) {
    public long insertKey(int uid, String alias, WrappedKey wrappedKey) {
        SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put(KeysEntry.COLUMN_NAME_UID, uid);
@@ -76,7 +75,7 @@ public class RecoverableKeyStoreDb {
        values.put(KeysEntry.COLUMN_NAME_NONCE, wrappedKey.getNonce());
        values.put(KeysEntry.COLUMN_NAME_WRAPPED_KEY, wrappedKey.getKeyMaterial());
        values.put(KeysEntry.COLUMN_NAME_LAST_SYNCED_AT, -1);
        values.put(KeysEntry.COLUMN_NAME_GENERATION_ID, generationId);
        values.put(KeysEntry.COLUMN_NAME_GENERATION_ID, wrappedKey.getPlatformKeyGenerationId());
        return db.replace(KeysEntry.TABLE_NAME, /*nullColumnHack=*/ null, values);
    }

@@ -123,7 +122,9 @@ public class RecoverableKeyStoreDb {
                    cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_NONCE));
            byte[] keyMaterial = cursor.getBlob(
                    cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_WRAPPED_KEY));
            return new WrappedKey(nonce, keyMaterial);
            int generationId = cursor.getInt(
                    cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_GENERATION_ID));
            return new WrappedKey(nonce, keyMaterial, generationId);
        }
    }

@@ -168,7 +169,7 @@ public class RecoverableKeyStoreDb {
                        cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_WRAPPED_KEY));
                String alias = cursor.getString(
                        cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_ALIAS));
                keys.put(alias, new WrappedKey(nonce, keyMaterial));
                keys.put(alias, new WrappedKey(nonce, keyMaterial, platformKeyGenerationId));
            }
            return keys;
        }
+3 −2
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ import javax.crypto.SecretKey;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class RecoverableKeyGeneratorTest {
    private static final int TEST_GENERATION_ID = 3;
    private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
    private static final String KEY_ALGORITHM = "AES";
    private static final String TEST_ALIAS = "karlin";
@@ -58,14 +59,14 @@ public class RecoverableKeyGeneratorTest {

    @Captor ArgumentCaptor<KeyProtection> mKeyProtectionArgumentCaptor;

    private AndroidKeyStoreSecretKey mPlatformKey;
    private PlatformEncryptionKey mPlatformKey;
    private SecretKey mKeyHandle;
    private RecoverableKeyGenerator mRecoverableKeyGenerator;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        mPlatformKey = generateAndroidKeyStoreKey();
        mPlatformKey = new PlatformEncryptionKey(TEST_GENERATION_ID, generateAndroidKeyStoreKey());
        mKeyHandle = generateKey();
        mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(
                mPlatformKey, mRecoverableKeyStorage);
Loading