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

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

Merge "Update recovery snapshot version."

parents 14c2ae4a 77183eff
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -1284,6 +1284,7 @@ public class LockSettingsService extends ILockSettings.Stub {
            fixateNewestUserKeyAuth(userId);
            synchronizeUnifiedWorkChallengeForProfiles(userId, null);
            notifyActivePasswordMetricsAvailable(null, userId);
            mRecoverableKeyStoreManager.lockScreenSecretChanged(credentialType, credential, userId);
            return;
        }
        if (credential == null) {
@@ -1333,6 +1334,8 @@ public class LockSettingsService extends ILockSettings.Stub {
                    .verifyChallenge(userId, 0, willStore.hash, credential.getBytes());
            setUserKeyProtection(userId, credential, convertResponse(gkResponse));
            fixateNewestUserKeyAuth(userId);
            mRecoverableKeyStoreManager.lockScreenSecretChanged(credentialType, credential,
                userId);
            // Refresh the auth token
            doVerifyCredential(credential, credentialType, true, 0, userId, null /* progressCallback */);
            synchronizeUnifiedWorkChallengeForProfiles(userId, null);
+75 −23
Original line number Diff line number Diff line
@@ -69,6 +69,7 @@ public class KeySyncTask implements Runnable {
    private final int mUserId;
    private final int mCredentialType;
    private final String mCredential;
    private final boolean mCredentialUpdated;
    private final PlatformKeyManager.Factory mPlatformKeyManagerFactory;
    private final RecoverySnapshotStorage mRecoverySnapshotStorage;
    private final RecoverySnapshotListenersStorage mSnapshotListenersStorage;
@@ -80,7 +81,8 @@ public class KeySyncTask implements Runnable {
            RecoverySnapshotListenersStorage recoverySnapshotListenersStorage,
            int userId,
            int credentialType,
            String credential
            String credential,
            boolean credentialUpdated
    ) throws NoSuchAlgorithmException, KeyStoreException, InsecureUserException {
        return new KeySyncTask(
                recoverableKeyStoreDb,
@@ -89,6 +91,7 @@ public class KeySyncTask implements Runnable {
                userId,
                credentialType,
                credential,
                credentialUpdated,
                () -> PlatformKeyManager.getInstance(context, recoverableKeyStoreDb));
    }

@@ -99,6 +102,7 @@ public class KeySyncTask implements Runnable {
     * @param userId The uid of the user whose profile has been unlocked.
     * @param credentialType The type of credential - i.e., pattern or password.
     * @param credential The credential, encoded as a {@link String}.
     * @param credentialUpdated signals weather credentials were updated.
     * @param platformKeyManagerFactory Instantiates a {@link PlatformKeyManager} for the user.
     *     This is a factory to enable unit testing, as otherwise it would be impossible to test
     *     without a screen unlock occurring!
@@ -111,12 +115,14 @@ public class KeySyncTask implements Runnable {
            int userId,
            int credentialType,
            String credential,
            boolean credentialUpdated,
            PlatformKeyManager.Factory platformKeyManagerFactory) {
        mSnapshotListenersStorage = recoverySnapshotListenersStorage;
        mRecoverableKeyStoreDb = recoverableKeyStoreDb;
        mUserId = userId;
        mCredentialType = credentialType;
        mCredential = credential;
        mCredentialUpdated = credentialUpdated;
        mPlatformKeyManagerFactory = platformKeyManagerFactory;
        mRecoverySnapshotStorage = snapshotStorage;
    }
@@ -124,29 +130,44 @@ public class KeySyncTask implements Runnable {
    @Override
    public void run() {
        try {
            // Only one task is active If user unlocks phone many times in a short time interval.
            synchronized(KeySyncTask.class) {
                syncKeys();
            }
        } catch (Exception e) {
            Log.e(TAG, "Unexpected exception thrown during KeySyncTask", e);
        }
    }

    private void syncKeys() {
        if (!isSyncPending()) {
            Log.d(TAG, "Key sync not needed.");
        if (mCredentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
            // Application keys for the user will not be available for sync.
            Log.w(TAG, "Credentials are not set for user " + mUserId);
            return;
        }

        int recoveryAgentUid = mRecoverableKeyStoreDb.getRecoveryAgentUid(mUserId);
        if (recoveryAgentUid == -1) {
        List<Integer> recoveryAgents = mRecoverableKeyStoreDb.getRecoveryAgents(mUserId);
        for (int uid : recoveryAgents) {
            syncKeysForAgent(uid);
        }
        if (recoveryAgents.isEmpty()) {
            Log.w(TAG, "No recovery agent initialized for user " + mUserId);
        }
    }

    private void syncKeysForAgent(int recoveryAgentUid) {
        if (!shoudCreateSnapshot(recoveryAgentUid)) {
            Log.d(TAG, "Key sync not needed.");
            return;
        }

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

        PublicKey publicKey = getVaultPublicKey();
        PublicKey publicKey = mRecoverableKeyStoreDb.getRecoveryServicePublicKey(mUserId,
                recoveryAgentUid);
        if (publicKey == null) {
            Log.w(TAG, "Not initialized for KeySync: no public key set. Cancelling task.");
            return;
@@ -163,7 +184,7 @@ public class KeySyncTask implements Runnable {

        Map<String, SecretKey> rawKeys;
        try {
            rawKeys = getKeysToSync();
            rawKeys = getKeysToSync(recoveryAgentUid);
        } catch (GeneralSecurityException e) {
            Log.e(TAG, "Failed to load recoverable keys for sync", e);
            return;
@@ -196,10 +217,19 @@ public class KeySyncTask implements Runnable {
            return;
        }

        // TODO: where do we get counter_id from here?
        Long counterId;
        // counter id is generated exactly once for each credentials value.
        if (mCredentialUpdated) {
            counterId = generateAndStoreCounterId(recoveryAgentUid);
        } else {
            counterId = mRecoverableKeyStoreDb.getCounterId(mUserId, recoveryAgentUid);
            if (counterId == null) {
                counterId = generateAndStoreCounterId(recoveryAgentUid);
            }
        }
        byte[] vaultParams = KeySyncUtils.packVaultParams(
                publicKey,
                /*counterId=*/ 1,
                counterId,
                TRUSTED_HARDWARE_MAX_ATTEMPTS,
                deviceId);

@@ -217,7 +247,7 @@ public class KeySyncTask implements Runnable {
            Log.e(TAG,"Could not encrypt with recovery key", e);
            return;
        }

        // TODO: store raw data in RecoveryServiceMetadataEntry and generate Parcelables later
        KeyStoreRecoveryMetadata metadata = new KeyStoreRecoveryMetadata(
                /*userSecretType=*/ TYPE_LOCKSCREEN,
                /*lockScreenUiFormat=*/ mCredentialType,
@@ -226,40 +256,62 @@ public class KeySyncTask implements Runnable {
        ArrayList<KeyStoreRecoveryMetadata> metadataList = new ArrayList<>();
        metadataList.add(metadata);

        // TODO: implement snapshot version
        mRecoverySnapshotStorage.put(mUserId, new KeyStoreRecoveryData(
                /*snapshotVersion=*/ 1,
        int snapshotVersion = incrementSnapshotVersion(recoveryAgentUid);

        // If application keys are not updated, snapshot will not be created on next unlock.
        mRecoverableKeyStoreDb.setShouldCreateSnapshot(mUserId, recoveryAgentUid, false);

        mRecoverySnapshotStorage.put(recoveryAgentUid, new KeyStoreRecoveryData(
                snapshotVersion,
                /*recoveryMetadata=*/ metadataList,
                /*applicationKeyBlobs=*/ createApplicationKeyEntries(encryptedApplicationKeys),
                /*encryptedRecoveryKeyblob=*/ encryptedRecoveryKey));

        mSnapshotListenersStorage.recoverySnapshotAvailable(recoveryAgentUid);
    }

    private PublicKey getVaultPublicKey() {
        return mRecoverableKeyStoreDb.getRecoveryServicePublicKey(mUserId);
    @VisibleForTesting
    int incrementSnapshotVersion(int recoveryAgentUid) {
        Long snapshotVersion = mRecoverableKeyStoreDb.getSnapshotVersion(mUserId, recoveryAgentUid);
        snapshotVersion = snapshotVersion == null ? 1 : snapshotVersion + 1;
        mRecoverableKeyStoreDb.setSnapshotVersion(mUserId, recoveryAgentUid, snapshotVersion);

        return snapshotVersion.intValue();
    }

    private long generateAndStoreCounterId(int recoveryAgentUid) {
        long counter = new SecureRandom().nextLong();
        mRecoverableKeyStoreDb.setCounterId(mUserId, recoveryAgentUid, counter);
        return counter;
    }

    /**
     * Returns all of the recoverable keys for the user.
     */
    private Map<String, SecretKey> getKeysToSync()
    private Map<String, SecretKey> getKeysToSync(int recoveryAgentUid)
            throws InsecureUserException, KeyStoreException, UnrecoverableKeyException,
            NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException {
        PlatformKeyManager platformKeyManager = mPlatformKeyManagerFactory.newInstance();
        PlatformDecryptionKey decryptKey = platformKeyManager.getDecryptKey(mUserId);
        Map<String, WrappedKey> wrappedKeys = mRecoverableKeyStoreDb.getAllKeys(
                mUserId, decryptKey.getGenerationId());
                mUserId, recoveryAgentUid, decryptKey.getGenerationId());
        return WrappedKey.unwrapKeys(decryptKey, wrappedKeys);
    }

    /**
     * Returns {@code true} if a sync is pending.
     * @param recoveryAgentUid uid of the recovery agent.
     */
    private boolean isSyncPending() {
        // TODO: implement properly. For now just always syncing if the user has any recoverable
        // keys. We need to keep track of when the store's state actually changes.
        return !mRecoverableKeyStoreDb.getAllKeys(
                mUserId, mRecoverableKeyStoreDb.getPlatformKeyGenerationId(mUserId)).isEmpty();
    private boolean shoudCreateSnapshot(int recoveryAgentUid) {
        if (mCredentialUpdated) {
            // Sync credential if at least one snapshot was created.
            if (mRecoverableKeyStoreDb.getSnapshotVersion(mUserId, recoveryAgentUid) != null) {
                mRecoverableKeyStoreDb.setShouldCreateSnapshot(mUserId, recoveryAgentUid, true);
                return true;
            }
        }

        return mRecoverableKeyStoreDb.getShouldCreateSnapshot(mUserId, recoveryAgentUid);
    }

    /**
+1 −0
Original line number Diff line number Diff line
@@ -99,6 +99,7 @@ public class RecoverableKeyGenerator {
                            Locale.US, "Failed writing (%d, %s) to database.", uid, alias));
        }

        mDatabase.setShouldCreateSnapshot(userId, uid, true);
        return key.getEncoded();
    }
}
+37 −7
Original line number Diff line number Diff line
@@ -174,8 +174,8 @@ public class RecoverableKeyStoreManager {
    public @NonNull KeyStoreRecoveryData getRecoveryData(@NonNull byte[] account)
            throws RemoteException {
        checkRecoverKeyStorePermission();

        KeyStoreRecoveryData snapshot = mSnapshotStorage.get(UserHandle.getCallingUserId());
        int uid = Binder.getCallingUid();
        KeyStoreRecoveryData snapshot = mSnapshotStorage.get(uid);
        if (snapshot == null) {
            throw new ServiceSpecificException(RecoverableKeyStoreLoader.ERROR_NO_SNAPSHOT_PENDING);
        }
@@ -433,7 +433,13 @@ public class RecoverableKeyStoreManager {
    }

    public void removeKey(@NonNull String alias) throws RemoteException {
        mDatabase.removeKey(Binder.getCallingUid(), alias);
        int uid = Binder.getCallingUid();
        int userId = UserHandle.getCallingUserId();

        boolean wasRemoved = mDatabase.removeKey(uid, alias);
        if (wasRemoved) {
            mDatabase.setShouldCreateSnapshot(userId, uid, true);
        }
    }

    private byte[] decryptRecoveryKey(
@@ -505,7 +511,8 @@ public class RecoverableKeyStoreManager {
                    mListenersStorage,
                    userId,
                    storedHashType,
                    credential));
                    credential,
                    /*credentialUpdated=*/ false));
        } catch (NoSuchAlgorithmException e) {
            Log.wtf(TAG, "Should never happen - algorithm unavailable for KeySync", e);
        } catch (KeyStoreException e) {
@@ -515,12 +522,35 @@ public class RecoverableKeyStoreManager {
        }
    }

    /** This function can only be used inside LockSettingsService. */
    /**
     * This function can only be used inside LockSettingsService.
     * @param storedHashType from {@code CredentialHash}
     * @param credential - unencrypted String
     * @param userId for the user whose lock screen credentials were changed.
     * @hide
     */
    public void lockScreenSecretChanged(
            @KeyStoreRecoveryMetadata.LockScreenUiFormat int type,
            int storedHashType,
            @Nullable String credential,
            int userId) {
        throw new UnsupportedOperationException();
        // So as not to block the critical path unlocking the phone, defer to another thread.
        try {
            mExecutorService.execute(KeySyncTask.newInstance(
                    mContext,
                    mDatabase,
                    mSnapshotStorage,
                    mListenersStorage,
                    userId,
                    storedHashType,
                    credential,
                    /*credentialUpdated=*/ true));
        } catch (NoSuchAlgorithmException e) {
            Log.wtf(TAG, "Should never happen - algorithm unavailable for KeySync", e);
        } catch (KeyStoreException e) {
            Log.e(TAG, "Key store error encountered during recoverable key sync", e);
        } catch (InsecureUserException e) {
            Log.wtf(TAG, "Impossible - insecure user, but user just entered lock screen", e);
        }
    }

    private void checkRecoverKeyStorePermission() {
+174 −39

File changed.

Preview size limit exceeded, changes collapsed.

Loading