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

Commit 9e8f1d8b authored by Dmitry Dementyev's avatar Dmitry Dementyev Committed by Android (Google) Code Review
Browse files

Merge "Reuse the same salt to compute recoverablekeystore snapshot." into main

parents 92403a1e 202cb58e
Loading
Loading
Loading
Loading
+22 −1
Original line number Diff line number Diff line
@@ -273,7 +273,7 @@ public class KeySyncTask implements Runnable {
        }

        boolean useScryptToHashCredential = shouldUseScryptToHashCredential();
        byte[] salt = generateSalt();
        byte[] salt = generateNewSaltIfNecessary();
        byte[] localLskfHash;
        if (useScryptToHashCredential) {
            localLskfHash = hashCredentialsByScrypt(salt, mCredential.getCredential());
@@ -480,6 +480,27 @@ public class KeySyncTask implements Runnable {
        }
    }

    /**
     * Reads salt from the database if LSKF is the same as in the previous snapshot.
     *
     * @return The salt.
     */
    private byte[] generateNewSaltIfNecessary() {
        if (mCredentialUpdated) {
            byte[] newSalt = generateSalt();
            mRecoverableKeyStoreDb.setLskfSalt(mUserId, newSalt);
            return newSalt;
        }
        byte[] storedSalt = mRecoverableKeyStoreDb.getLskfSalt(mUserId);
        if (storedSalt != null && storedSalt.length == SALT_LENGTH_BYTES) {
            return storedSalt;
        } else {
            byte[] newSalt = generateSalt();
            mRecoverableKeyStoreDb.setLskfSalt(mUserId, newSalt);
            return newSalt;
        }
    }

    /**
     * Generates a salt to include with the lock screen hash.
     *
+48 −0
Original line number Diff line number Diff line
@@ -440,6 +440,54 @@ public class RecoverableKeyStoreDb {
        }
    }

    /**
     * Sets the {@code lskfSalt} for the user {@code userId}.
     *
     * @return The number of updated rows.
     */
    public long setLskfSalt(int userId, byte[] lskfSalt) {
        SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put(UserMetadataEntry.COLUMN_NAME_USER_ID, userId);
        values.put(UserMetadataEntry.COLUMN_NAME_LSKF_SALT, lskfSalt);
        String selection = UserMetadataEntry.COLUMN_NAME_USER_ID + " = ?";
        String[] selectionArguments = new String[] {String.valueOf(userId)};

        ensureUserMetadataEntryExists(userId);
        return db.update(UserMetadataEntry.TABLE_NAME, values, selection, selectionArguments);
    }

    /**
     * Returns salt used to create previous snapshot for user {@code userId}.
     */
    public @Nullable byte[] getLskfSalt(int userId) {
        SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase();
        String[] projection = {
                UserMetadataEntry.COLUMN_NAME_LSKF_SALT};
        String selection =
                UserMetadataEntry.COLUMN_NAME_USER_ID + " = ?";
        String[] selectionArguments = {
                Integer.toString(userId)};

        try (Cursor cursor = db.query(
                UserMetadataEntry.TABLE_NAME,
                projection,
                selection,
                selectionArguments,
                /*groupBy=*/ null,
                /*having=*/ null,
                /*orderBy=*/ null)
        ) {
            if (cursor.getCount() == 0) {
                return null; // previous value was not stored.
            }
            cursor.moveToFirst();
            return cursor.getBlob(
                    cursor.getColumnIndexOrThrow(
                            UserMetadataEntry.COLUMN_NAME_LSKF_SALT));
        }
    }

    /**
     * Updates status of old keys to {@code RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE}.
     */
+5 −0
Original line number Diff line number Diff line
@@ -103,6 +103,11 @@ class RecoverableKeyStoreDbContract {
         * Number of invalid lockscreen credentials guess from a remote device.
         */
        static final String COLUMN_NAME_BAD_REMOTE_GUESS_COUNTER = "bad_remote_guess_counter";

        /**
         * Salt used to create previous keychain snapshot.
         */
        static final String COLUMN_NAME_LSKF_SALT = "lskf_salt";
    }

    /**
+26 −3
Original line number Diff line number Diff line
@@ -35,7 +35,9 @@ class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper {

    // v6 - added user id serial number.
    // v7 - added bad guess counter for remote LSKF check;
    // v8 - added field to store LSKF salt.
    static final int DATABASE_VERSION_7 = 7;
    static final int DATABASE_VERSION_8 = 8;
    private static final String DATABASE_NAME = "recoverablekeystore.db";

    private static final String SQL_CREATE_KEYS_ENTRY =
@@ -70,6 +72,16 @@ class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper {
                    + UserMetadataEntry.COLUMN_NAME_BAD_REMOTE_GUESS_COUNTER
                    + " INTEGER DEFAULT 0)";

    private static final String SQL_CREATE_USER_METADATA_ENTRY_FOR_V8 =
            "CREATE TABLE " + UserMetadataEntry.TABLE_NAME + "( "
                    + UserMetadataEntry._ID + " INTEGER PRIMARY KEY,"
                    + UserMetadataEntry.COLUMN_NAME_USER_ID + " INTEGER UNIQUE,"
                    + UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID + " INTEGER,"
                    + UserMetadataEntry.COLUMN_NAME_USER_SERIAL_NUMBER + " INTEGER DEFAULT -1,"
                    + UserMetadataEntry.COLUMN_NAME_BAD_REMOTE_GUESS_COUNTER
                    + " INTEGER DEFAULT 0,"
                    + UserMetadataEntry.COLUMN_NAME_LSKF_SALT + " BLOB)";

    private static final String SQL_CREATE_RECOVERY_SERVICE_METADATA_ENTRY =
            "CREATE TABLE " + RecoveryServiceMetadataEntry.TABLE_NAME + " ("
                    + RecoveryServiceMetadataEntry._ID + " INTEGER PRIMARY KEY,"
@@ -118,13 +130,13 @@ class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper {
    }

    private static int getDbVersion(Context context) {
        return DATABASE_VERSION_7;
        return DATABASE_VERSION_8;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(SQL_CREATE_KEYS_ENTRY);
        db.execSQL(SQL_CREATE_USER_METADATA_ENTRY_FOR_V7);
        db.execSQL(SQL_CREATE_USER_METADATA_ENTRY_FOR_V8);
        db.execSQL(SQL_CREATE_RECOVERY_SERVICE_METADATA_ENTRY);
        db.execSQL(SQL_CREATE_ROOT_OF_TRUST_ENTRY);
    }
@@ -173,8 +185,13 @@ class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper {
                }
                oldVersion = 7;
            }
            if (oldVersion < 8 && newVersion >= 8) {
                upgradeDbForVersion8(db);
                oldVersion = 8;
            }
        } catch (SQLiteException e) {
            Log.e(TAG, "Recreating recoverablekeystore after unexpected upgrade error.", e);
            Log.e(TAG, "Recreating recoverablekeystore database after unexpected upgrade error.",
                    e);
            dropAllKnownTables(db); // Wipe database.
            onCreate(db);
            return;
@@ -234,6 +251,12 @@ class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper {
                 /*defaultStr=*/ null);
    }

    private void upgradeDbForVersion8(SQLiteDatabase db) {
        Log.d(TAG, "Updating recoverable keystore database to version 8");
        addColumnToTable(db, UserMetadataEntry.TABLE_NAME,
                UserMetadataEntry.COLUMN_NAME_LSKF_SALT, "BLOB", /*defaultStr=*/ null);
    }

    private static void addColumnToTable(
            SQLiteDatabase db, String tableName, String column, String columnType,
            String defaultStr) {
+36 −0
Original line number Diff line number Diff line
@@ -95,6 +95,8 @@ public class KeySyncTaskTest {
    private static final int TEST_RECOVERY_AGENT_UID2 = 10010;
    private static final byte[] TEST_VAULT_HANDLE =
            new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
    private static final byte[] LSKF_SALT =
            new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, -16};
    private static final String TEST_APP_KEY_ALIAS = "rcleaver";
    private static final byte[] TEST_APP_KEY_METADATA_NULL = null;
    private static final byte[] TEST_APP_KEY_METADATA_NON_NULL =
@@ -862,6 +864,40 @@ public class KeySyncTaskTest {
        assertThat(mRecoverableKeyStoreDb.getBadRemoteGuessCounter(TEST_USER_ID_2)).isEqualTo(4);
    }

    @Test
    public void run_storesLskfSalt() throws Exception {
        LockscreenCredential shortPassword = LockscreenCredential.createPassword("e2e4");
        mKeySyncTask = new KeySyncTask(
                mRecoverableKeyStoreDb,
                mRecoverySnapshotStorage,
                mSnapshotListenersStorage,
                TEST_USER_ID,
                shortPassword,
                /*credentialUpdated=*/ false,
                mPlatformKeyManager,
                mTestOnlyInsecureCertificateHelper,
                mMockScrypt);
        mRecoverableKeyStoreDb.setServerParams(
                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
        mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
        addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
        mRecoverableKeyStoreDb.setLskfSalt(TEST_USER_ID, LSKF_SALT);
        setExpectedScryptArgument(shortPassword);

        mKeySyncTask.run();

        KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
        assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat())
                .isEqualTo(UI_FORMAT_PASSWORD);
        KeyDerivationParams keyDerivationParams =
                keyChainSnapshot.getKeyChainProtectionParams().get(0).getKeyDerivationParams();
        byte[] storedSalt = mRecoverableKeyStoreDb.getLskfSalt(TEST_USER_ID);
        assertThat(storedSalt).isEqualTo(keyDerivationParams.getSalt());
        assertThat(storedSalt).isEqualTo(LSKF_SALT);
    }

    private SecretKey addApplicationKey(int userId, int recoveryAgentUid, String alias)
            throws Exception{
        return addApplicationKey(userId, recoveryAgentUid, alias, TEST_APP_KEY_METADATA_NULL);
Loading