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

Commit aa3f4cad authored by Robert Berry's avatar Robert Berry
Browse files

Hook up using initialized public key in KeySyncTask

Not sure if this is correct, PTAL. We won't have a specific uid when
the phone is unlocked, only the userId. Should the public key be
uid-specific or just userId-specific?

Test: adb shell am instrument -w -e package com.android.server.locksettings.recoverablekeystore com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
Change-Id: Ic2ec442c8a283e747542fafa9d7b0462aa185532
parent 7b270369
Loading
Loading
Loading
Loading
+11 −19
Original line number Diff line number Diff line
@@ -69,7 +69,6 @@ public class KeySyncTask implements Runnable {
    private final int mCredentialType;
    private final String mCredential;
    private final PlatformKeyManager.Factory mPlatformKeyManagerFactory;
    private final VaultKeySupplier mVaultKeySupplier;
    private final RecoverySnapshotStorage mRecoverySnapshotStorage;
    private final RecoverySnapshotListenersStorage mSnapshotListenersStorage;

@@ -89,10 +88,7 @@ public class KeySyncTask implements Runnable {
                userId,
                credentialType,
                credential,
                () -> PlatformKeyManager.getInstance(context, recoverableKeyStoreDb, userId),
                () -> {
                    throw new UnsupportedOperationException("Not implemented vault key.");
                });
                () -> PlatformKeyManager.getInstance(context, recoverableKeyStoreDb, userId));
    }

    /**
@@ -114,15 +110,13 @@ public class KeySyncTask implements Runnable {
            int userId,
            int credentialType,
            String credential,
            PlatformKeyManager.Factory platformKeyManagerFactory,
            VaultKeySupplier vaultKeySupplier) {
            PlatformKeyManager.Factory platformKeyManagerFactory) {
        mSnapshotListenersStorage = recoverySnapshotListenersStorage;
        mRecoverableKeyStoreDb = recoverableKeyStoreDb;
        mUserId = userId;
        mCredentialType = credentialType;
        mCredential = credential;
        mPlatformKeyManagerFactory = platformKeyManagerFactory;
        mVaultKeySupplier = vaultKeySupplier;
        mRecoverySnapshotStorage = snapshotStorage;
    }

@@ -142,7 +136,6 @@ public class KeySyncTask implements Runnable {
        }

        int recoveryAgentUid = mRecoverableKeyStoreDb.getRecoveryAgentUid(mUserId);

        if (recoveryAgentUid == -1) {
            Log.w(TAG, "No recovery agent initialized for user " + mUserId);
            return;
@@ -153,6 +146,13 @@ public class KeySyncTask implements Runnable {
            return;
        }

        PublicKey publicKey = getVaultPublicKey();

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

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

@@ -197,7 +197,7 @@ public class KeySyncTask implements Runnable {
        byte[] encryptedRecoveryKey;
        try {
            encryptedRecoveryKey = KeySyncUtils.thmEncryptRecoveryKey(
                    mVaultKeySupplier.get(),
                    publicKey,
                    localLskfHash,
                    vaultParams,
                    recoveryKey);
@@ -227,8 +227,7 @@ public class KeySyncTask implements Runnable {
    }

    private PublicKey getVaultPublicKey() {
        // TODO: fill this in
        throw new UnsupportedOperationException("TODO: get vault public key.");
        return mRecoverableKeyStoreDb.getRecoveryServicePublicKey(mUserId);
    }

    /**
@@ -339,11 +338,4 @@ public class KeySyncTask implements Runnable {
        }
        return keyEntries;
    }

    /**
     * TODO: until this is in the database, so we can test.
     */
    public interface VaultKeySupplier {
        PublicKey get();
    }
}
+55 −10
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@ import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.security.recoverablekeystore.RecoverableKeyStoreLoader;
import android.text.TextUtils;
import android.util.Log;

@@ -36,10 +35,8 @@ import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.StringJoiner;
@@ -368,6 +365,7 @@ public class RecoverableKeyStoreDb {
     *
     * @hide
     */
    @Nullable
    public PublicKey getRecoveryServicePublicKey(int userId, int uid) {
        SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase();

@@ -409,12 +407,8 @@ public class RecoverableKeyStoreDb {
                return null;
            }
            byte[] keyBytes = cursor.getBlob(idx);
            X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(keyBytes);
            try {
                return KeyFactory.getInstance("EC").generatePublic(pkSpec);
            } catch (NoSuchAlgorithmException e) {
                // Should never happen
                throw new RuntimeException(e);
                return decodeX509Key(keyBytes);
            } catch (InvalidKeySpecException e) {
                Log.wtf(TAG,
                        String.format(Locale.US,
@@ -519,6 +513,48 @@ public class RecoverableKeyStoreDb {
        }
    }

    /**
     * Returns the first (and only?) public key for {@code userId}.
     *
     * @param userId The uid of the profile whose keys are to be synced.
     * @return The public key, or null if none exists.
     */
    @Nullable
    public PublicKey getRecoveryServicePublicKey(int userId) {
        SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase();

        String[] projection = { RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY };
        String selection =
                RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + " = ?";
        String[] selectionArguments = { Integer.toString(userId) };

        try (
            Cursor cursor = db.query(
                    RecoveryServiceMetadataEntry.TABLE_NAME,
                    projection,
                    selection,
                    selectionArguments,
                    /*groupBy=*/ null,
                    /*having=*/ null,
                    /*orderBy=*/ null)
        ) {
            if (cursor.getCount() < 1) {
                return null;
            }

            cursor.moveToFirst();
            byte[] keyBytes = cursor.getBlob(cursor.getColumnIndexOrThrow(
                    RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY));

            try {
                return decodeX509Key(keyBytes);
            } catch (InvalidKeySpecException e) {
                Log.wtf(TAG, "Could not decode public key for " + userId);
                return null;
            }
        }
    }

    /**
     * Updates the server parameters given by the application initializing the local recovery
     * components.
@@ -619,5 +655,14 @@ public class RecoverableKeyStoreDb {
        mKeyStoreDbHelper.close();
    }

    // TODO: Add method for updating the 'last synced' time.
    @Nullable
    private static PublicKey decodeX509Key(byte[] keyBytes) throws InvalidKeySpecException {
        X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(keyBytes);
        try {
            return KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
        } catch (NoSuchAlgorithmException e) {
            // Should never happen
            throw new RuntimeException(e);
        }
    }
}
+1 −2
Original line number Diff line number Diff line
@@ -109,8 +109,7 @@ public class KeySyncTaskTest {
                TEST_USER_ID,
                TEST_CREDENTIAL_TYPE,
                TEST_CREDENTIAL,
                () -> mPlatformKeyManager,
                () -> mKeyPair.getPublic());
                () -> mPlatformKeyManager);

        mWrappingKey = generateAndroidKeyStoreKey();
        mEncryptKey = new PlatformEncryptionKey(TEST_GENERATION_ID, mWrappingKey);
+11 −1
Original line number Diff line number Diff line
@@ -328,7 +328,6 @@ public class RecoverableKeyStoreDbTest {
    }

    @Test

    public void getRecoveryAgentUid_returnsUidIfSet() throws Exception {
        int userId = 12;
        int uid = 190992;
@@ -436,6 +435,17 @@ public class RecoverableKeyStoreDbTest {
                pubkey2);
    }

    @Test
    public void getRecoveryServicePublicKey_returnsFirstKey() throws Exception {
        int userId = 68;
        int uid = 12904;
        PublicKey publicKey = genRandomPublicKey();

        mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId, uid, publicKey);

        assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId)).isEqualTo(publicKey);
    }

    @Test
    public void setServerParameters_replaceOldValue() throws Exception {
        int userId = 12;