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

Commit a9fdc8b2 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Encode vault params in key sync task"

parents f6e903af 94ea4e4c
Loading
Loading
Loading
Loading
+13 −4
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@ public class KeySyncTask implements Runnable {
    private static final int SALT_LENGTH_BYTES = 16;
    private static final int LENGTH_PREFIX_BYTES = Integer.BYTES;
    private static final String LOCK_SCREEN_HASH_ALGORITHM = "SHA-256";
    private static final int TRUSTED_HARDWARE_MAX_ATTEMPTS = 10;

    private final RecoverableKeyStoreDb mRecoverableKeyStoreDb;
    private final int mUserId;
@@ -140,19 +141,23 @@ public class KeySyncTask implements Runnable {
            Log.w(TAG, "No recovery agent initialized for user " + mUserId);
            return;
        }

        if (!mSnapshotListenersStorage.hasListener(recoveryAgentUid)) {
            Log.w(TAG, "No pending intent registered for recovery agent " + recoveryAgentUid);
            return;
        }

        PublicKey publicKey = getVaultPublicKey();

        if (publicKey == null) {
            Log.w(TAG, "Not initialized for KeySync: no public key set. Cancelling task.");
            return;
        }

        Long deviceId = mRecoverableKeyStoreDb.getServerParameters(mUserId, recoveryAgentUid);
        if (deviceId == null) {
            Log.w(TAG, "No device ID set for user " + mUserId);
            return;
        }

        byte[] salt = generateSalt();
        byte[] localLskfHash = hashCredentials(salt, mCredential);

@@ -191,8 +196,12 @@ public class KeySyncTask implements Runnable {
            return;
        }

        // TODO: construct vault params and vault metadata
        byte[] vaultParams = {};
        // TODO: where do we get counter_id from here?
        byte[] vaultParams = KeySyncUtils.packVaultParams(
                publicKey,
                /*counterId=*/ 1,
                TRUSTED_HARDWARE_MAX_ATTEMPTS,
                deviceId);

        byte[] encryptedRecoveryKey;
        try {
+23 −0
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.server.locksettings.recoverablekeystore;

import com.android.internal.annotations.VisibleForTesting;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
@@ -60,6 +62,7 @@ public class KeySyncUtils {
    private static final byte[] THM_KF_HASH_PREFIX = "THM_KF_hash".getBytes(StandardCharsets.UTF_8);

    private static final int KEY_CLAIMANT_LENGTH_BYTES = 16;
    private static final int VAULT_PARAMS_LENGTH_BYTES = 85;

    /**
     * Encrypts the recovery key using both the lock screen hash and the remote storage's public
@@ -280,6 +283,26 @@ public class KeySyncUtils {
        return keyFactory.generatePublic(publicKeySpec);
    }

    /**
     * Packs vault params into a binary format.
     *
     * @param thmPublicKey Public key of the trusted hardware module.
     * @param counterId ID referring to the specific counter in the hardware module.
     * @param maxAttempts Maximum allowed guesses before trusted hardware wipes key.
     * @param deviceId ID of the device.
     * @return The binary vault params, ready for sync.
     */
    public static byte[] packVaultParams(
            PublicKey thmPublicKey, long counterId, int maxAttempts, long deviceId) {
        return ByteBuffer.allocate(VAULT_PARAMS_LENGTH_BYTES)
                .order(ByteOrder.LITTLE_ENDIAN)
                .put(SecureBox.encodePublicKey(thmPublicKey))
                .putLong(counterId)
                .putInt(maxAttempts)
                .putLong(deviceId)
                .array();
    }

    /**
     * Returns the concatenation of all the given {@code arrays}.
     */
+7 −1
Original line number Diff line number Diff line
@@ -372,7 +372,13 @@ public class SecureBox {
        }
    }

    @VisibleForTesting
    /**
     * Encodes public key in format expected by the secure hardware module. This is used as part
     * of the vault params.
     *
     * @param publicKey The public key.
     * @return The key packed into a 65-byte array.
     */
    static byte[] encodePublicKey(PublicKey publicKey) {
        ECPoint point = ((ECPublicKey) publicKey).getW();
        byte[] x = point.getAffineX().toByteArray();
+29 −2
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ public class KeySyncTaskTest {
    private static final int TEST_USER_ID = 1000;
    private static final int TEST_APP_UID = 10009;
    private static final int TEST_RECOVERY_AGENT_UID = 90873;
    private static final long TEST_DEVICE_ID = 13295035643L;
    private static final String TEST_APP_KEY_ALIAS = "rcleaver";
    private static final int TEST_GENERATION_ID = 2;
    private static final int TEST_CREDENTIAL_TYPE = CREDENTIAL_TYPE_PASSWORD;
@@ -223,6 +224,8 @@ public class KeySyncTaskTest {
    @Test
    public void run_doesNotSendAnythingIfNoRecoveryAgentPendingIntentRegistered() throws Exception {
        SecretKey applicationKey = generateKey();
        mRecoverableKeyStoreDb.setServerParameters(
                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_DEVICE_ID);
        mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
        mRecoverableKeyStoreDb.insertKey(
                TEST_USER_ID,
@@ -237,9 +240,29 @@ public class KeySyncTaskTest {
        assertNull(mRecoverySnapshotStorage.get(TEST_USER_ID));
    }

    @Test
    public void run_doesNotSendAnythingIfNoDeviceIdIsSet() throws Exception {
        SecretKey applicationKey = generateKey();
        mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
        mRecoverableKeyStoreDb.insertKey(
                TEST_USER_ID,
                TEST_APP_UID,
                TEST_APP_KEY_ALIAS,
                WrappedKey.fromSecretKey(mEncryptKey, applicationKey));
        mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
        when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);

        mKeySyncTask.run();

        assertNull(mRecoverySnapshotStorage.get(TEST_USER_ID));
    }

    @Test
    public void run_sendsEncryptedKeysIfAvailableToSync() throws Exception {
        SecretKey applicationKey = generateKey();
        mRecoverableKeyStoreDb.setServerParameters(
                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_DEVICE_ID);
        mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
        mRecoverableKeyStoreDb.insertKey(
                TEST_USER_ID,
@@ -261,11 +284,15 @@ public class KeySyncTaskTest {
        byte[] lockScreenHash = KeySyncTask.hashCredentials(
                keyDerivationParameters.getSalt(),
                TEST_CREDENTIAL);
        // TODO: what should vault params be here?
        // TODO: what should counter_id be here?
        byte[] recoveryKey = decryptThmEncryptedKey(
                lockScreenHash,
                recoveryData.getEncryptedRecoveryKeyBlob(),
                /*vaultParams=*/ new byte[0]);
                /*vaultParams=*/ KeySyncUtils.packVaultParams(
                        mKeyPair.getPublic(),
                        /*counterId=*/ 1,
                        /*maxAttempts=*/ 10,
                        TEST_DEVICE_ID));
        List<KeyEntryRecoveryData> applicationKeys = recoveryData.getApplicationKeyBlobs();
        assertEquals(1, applicationKeys.size());
        KeyEntryRecoveryData keyData = applicationKeys.get(0);
+81 −0
Original line number Diff line number Diff line
@@ -30,9 +30,12 @@ import com.google.common.collect.ImmutableMap;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.MessageDigest;
import java.security.PublicKey;
import java.util.Arrays;
import java.util.Map;
import java.util.Random;
@@ -57,6 +60,8 @@ public class KeySyncUtilsTest {
            "V1 KF_claim".getBytes(StandardCharsets.UTF_8);
    private static final byte[] RECOVERY_RESPONSE_HEADER =
            "V1 reencrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
    private static final int PUBLIC_KEY_LENGTH_BYTES = 65;
    private static final int VAULT_PARAMS_LENGTH_BYTES = 85;

    @Test
    public void calculateThmKfHash_isShaOfLockScreenHashWithPrefix() throws Exception {
@@ -336,6 +341,82 @@ public class KeySyncUtilsTest {
        }
    }

    @Test
    public void packVaultParams_returns85Bytes() throws Exception {
        PublicKey thmPublicKey = SecureBox.genKeyPair().getPublic();

        byte[] packedForm = KeySyncUtils.packVaultParams(
                thmPublicKey,
                /*counterId=*/ 1001L,
                /*maxAttempts=*/ 10,
                /*deviceId=*/ 1L);

        assertEquals(VAULT_PARAMS_LENGTH_BYTES, packedForm.length);
    }

    @Test
    public void packVaultParams_encodesPublicKeyInFirst65Bytes() throws Exception {
        PublicKey thmPublicKey = SecureBox.genKeyPair().getPublic();

        byte[] packedForm = KeySyncUtils.packVaultParams(
                thmPublicKey,
                /*counterId=*/ 1001L,
                /*maxAttempts=*/ 10,
                /*deviceId=*/ 1L);

        assertArrayEquals(
                SecureBox.encodePublicKey(thmPublicKey),
                Arrays.copyOf(packedForm, PUBLIC_KEY_LENGTH_BYTES));
    }

    @Test
    public void packVaultParams_encodesCounterIdAsSecondParam() throws Exception {
        long counterId = 103502L;

        byte[] packedForm = KeySyncUtils.packVaultParams(
                SecureBox.genKeyPair().getPublic(),
                counterId,
                /*maxAttempts=*/ 10,
                /*deviceId=*/ 1L);

        ByteBuffer byteBuffer = ByteBuffer.wrap(packedForm)
                .order(ByteOrder.LITTLE_ENDIAN);
        byteBuffer.position(PUBLIC_KEY_LENGTH_BYTES);
        assertEquals(counterId, byteBuffer.getLong());
    }

    @Test
    public void packVaultParams_encodesMaxAttemptsAsThirdParam() throws Exception {
        int maxAttempts = 10;

        byte[] packedForm = KeySyncUtils.packVaultParams(
                SecureBox.genKeyPair().getPublic(),
                /*counterId=*/ 1001L,
                maxAttempts,
                /*deviceId=*/ 1L);

        ByteBuffer byteBuffer = ByteBuffer.wrap(packedForm)
                .order(ByteOrder.LITTLE_ENDIAN);
        byteBuffer.position(PUBLIC_KEY_LENGTH_BYTES + Long.BYTES);
        assertEquals(maxAttempts, byteBuffer.getInt());
    }

    @Test
    public void packVaultParams_encodesDeviceIdAsLastParam() throws Exception {
        long deviceId = 102942158152L;

        byte[] packedForm = KeySyncUtils.packVaultParams(
                SecureBox.genKeyPair().getPublic(),
                /*counterId=*/ 10021L,
                /*maxAttempts=*/ 10,
                deviceId);

        ByteBuffer byteBuffer = ByteBuffer.wrap(packedForm)
                .order(ByteOrder.LITTLE_ENDIAN);
        byteBuffer.position(PUBLIC_KEY_LENGTH_BYTES + Long.BYTES + Integer.BYTES);
        assertEquals(deviceId, byteBuffer.getLong());
    }

    private static byte[] randomBytes(int n) {
        byte[] bytes = new byte[n];
        new Random().nextBytes(bytes);