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

Commit 00c3f598 authored by Tianjie's avatar Tianjie
Browse files

Add a specific error code for provider mismatches

If the device used HAL based RoR to arm the escrow key, the recovery
will fail if we switch to server based RoR after reboot. Set a
specific error for metrics purpose.

Bug: 183140900
Test: atest FrameworksServicesTests:RebootEscrowManagerTests
Change-Id: I1f33a8c6cf111d868a2b96a155f5a0926a7c788a
parent aec4f406
Loading
Loading
Loading
Loading
+18 −1
Original line number Diff line number Diff line
@@ -70,6 +70,7 @@ class RebootEscrowManager {
    public static final String REBOOT_ESCROW_ARMED_KEY = "reboot_escrow_armed_count";

    static final String REBOOT_ESCROW_KEY_ARMED_TIMESTAMP = "reboot_escrow_key_stored_timestamp";
    static final String REBOOT_ESCROW_KEY_PROVIDER = "reboot_escrow_key_provider";

    /**
     * The verified boot 2.0 vbmeta digest of the current slot, the property value is always
@@ -113,6 +114,7 @@ class RebootEscrowManager {
            ERROR_LOAD_ESCROW_KEY,
            ERROR_RETRY_COUNT_EXHAUSTED,
            ERROR_UNLOCK_ALL_USERS,
            ERROR_PROVIDER_MISMATCH,
    })
    @Retention(RetentionPolicy.SOURCE)
    @interface RebootEscrowErrorCode {
@@ -124,6 +126,7 @@ class RebootEscrowManager {
    static final int ERROR_LOAD_ESCROW_KEY = 3;
    static final int ERROR_RETRY_COUNT_EXHAUSTED = 4;
    static final int ERROR_UNLOCK_ALL_USERS = 5;
    static final int ERROR_PROVIDER_MISMATCH = 6;

    private @RebootEscrowErrorCode int mLoadEscrowDataErrorCode = ERROR_NONE;

@@ -360,8 +363,16 @@ class RebootEscrowManager {

        if (escrowKey == null) {
            if (mLoadEscrowDataErrorCode == ERROR_NONE) {
                // Specifically check if the RoR provider has changed after reboot.
                int providerType = mInjector.serverBasedResumeOnReboot()
                        ? RebootEscrowProviderInterface.TYPE_SERVER_BASED
                        : RebootEscrowProviderInterface.TYPE_HAL;
                if (providerType != mStorage.getInt(REBOOT_ESCROW_KEY_PROVIDER, -1, USER_SYSTEM)) {
                    mLoadEscrowDataErrorCode = ERROR_PROVIDER_MISMATCH;
                } else {
                    mLoadEscrowDataErrorCode = ERROR_LOAD_ESCROW_KEY;
                }
            }
            onGetRebootEscrowKeyFailed(users, attemptNumber + 1);
            return;
        }
@@ -387,6 +398,7 @@ class RebootEscrowManager {
        mStorage.removeKey(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, USER_SYSTEM);
        mStorage.removeKey(REBOOT_ESCROW_KEY_VBMETA_DIGEST, USER_SYSTEM);
        mStorage.removeKey(REBOOT_ESCROW_KEY_OTHER_VBMETA_DIGEST, USER_SYSTEM);
        mStorage.removeKey(REBOOT_ESCROW_KEY_PROVIDER, USER_SYSTEM);
    }

    private int getVbmetaDigestStatusOnRestoreComplete() {
@@ -435,6 +447,7 @@ class RebootEscrowManager {
        if (!success && mLoadEscrowDataErrorCode == ERROR_NONE) {
            mLoadEscrowDataErrorCode = ERROR_UNKNOWN;
        }

        // TODO(179105110) report the duration since boot complete.
        mInjector.reportMetric(success, mLoadEscrowDataErrorCode, serviceType, attemptCount,
                escrowDurationInSeconds, vbmetaDigestStatus, -1);
@@ -586,6 +599,9 @@ class RebootEscrowManager {
            return false;
        }

        int actualProviderType = rebootEscrowProvider.getType();
        // TODO(b/183140900) Fail the reboot if provider type mismatches.

        RebootEscrowKey escrowKey;
        synchronized (mKeyGenerationLock) {
            escrowKey = mPendingRebootEscrowKey;
@@ -612,6 +628,7 @@ class RebootEscrowManager {
                    USER_SYSTEM);
            mStorage.setString(REBOOT_ESCROW_KEY_OTHER_VBMETA_DIGEST,
                    mInjector.getVbmetaDigest(true), USER_SYSTEM);
            mStorage.setInt(REBOOT_ESCROW_KEY_PROVIDER, actualProviderType, USER_SYSTEM);
            mEventLog.addEntry(RebootEscrowEvent.SET_ARMED_STATUS);
        }

+5 −0
Original line number Diff line number Diff line
@@ -59,6 +59,11 @@ class RebootEscrowProviderHalImpl implements RebootEscrowProviderInterface {
        mInjector = injector;
    }

    @Override
    public int getType() {
        return TYPE_HAL;
    }

    @Override
    public boolean hasRebootEscrowSupport() {
        return mInjector.getRebootEscrow() != null;
+19 −0
Original line number Diff line number Diff line
@@ -16,7 +16,11 @@

package com.android.server.locksettings;

import android.annotation.IntDef;

import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import javax.crypto.SecretKey;

@@ -28,6 +32,21 @@ import javax.crypto.SecretKey;
 * @hide
 */
public interface RebootEscrowProviderInterface {
    @IntDef(prefix = {"TYPE_"}, value = {
            TYPE_HAL,
            TYPE_SERVER_BASED,
    })
    @Retention(RetentionPolicy.SOURCE)
    @interface RebootEscrowProviderType {
    }
    int TYPE_HAL = 0;
    int TYPE_SERVER_BASED = 1;

    /**
     * Returns the reboot escrow provider type.
     */
    @RebootEscrowProviderType int getType();

    /**
     * Returns true if the secure store/discard of reboot escrow key is supported.
     */
+5 −0
Original line number Diff line number Diff line
@@ -94,6 +94,11 @@ class RebootEscrowProviderServerBasedImpl implements RebootEscrowProviderInterfa
        mInjector = injector;
    }

    @Override
    public int getType() {
        return TYPE_SERVER_BASED;
    }

    @Override
    public boolean hasRebootEscrowSupport() {
        return mInjector.getServiceConnection() != null;
+92 −3
Original line number Diff line number Diff line
@@ -113,6 +113,8 @@ public class RebootEscrowManagerTests {

        long getCurrentTimeMillis();

        boolean forceServerBased();

        void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount,
                int escrowDurationInSeconds, int vbmetaDigestStatus, int durationSinceBootComplete);
    }
@@ -177,6 +179,9 @@ public class RebootEscrowManagerTests {

        @Override
        public boolean serverBasedResumeOnReboot() {
            if (mInjected.forceServerBased()) {
                return true;
            }
            return mServerBased;
        }

@@ -221,6 +226,7 @@ public class RebootEscrowManagerTests {
        public void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount,
                int escrowDurationInSeconds, int vbmetaDigestStatus,
                int durationSinceBootComplete) {

            mInjected.reportMetric(success, errorCode, serviceType, attemptCount,
                    escrowDurationInSeconds, vbmetaDigestStatus, durationSinceBootComplete);
        }
@@ -496,6 +502,84 @@ public class RebootEscrowManagerTests {
        verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
    }

    @Test
    public void loadRebootEscrowDataIfAvailable_ServerBasedRemoteException_Failure()
            throws Exception {
        setServerBasedRebootEscrowProvider();

        when(mInjected.getBootCount()).thenReturn(0);
        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
        mService.setRebootEscrowListener(mockListener);
        mService.prepareRebootEscrow();

        clearInvocations(mServiceConnection);
        mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
        verify(mockListener).onPreparedForReboot(eq(true));
        verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());

        // Use x -> x for both wrap & unwrap functions.
        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
                .thenAnswer(invocation -> invocation.getArgument(0));
        assertTrue(mService.armRebootEscrowIfNeeded());
        verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
        assertTrue(mStorage.hasRebootEscrowServerBlob());

        // pretend reboot happens here
        when(mInjected.getBootCount()).thenReturn(1);
        ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
        ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(),
                metricsErrorCodeCaptor.capture(), eq(2) /* Server based */,
                eq(1) /* attempt count */, anyInt(), eq(0) /* vbmeta status */, anyInt());

        when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(RemoteException.class);
        mService.loadRebootEscrowDataIfAvailable(null);
        verify(mServiceConnection).unwrap(any(), anyLong());
        assertFalse(metricsSuccessCaptor.getValue());
        assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY),
                metricsErrorCodeCaptor.getValue());
    }

    @Test
    public void loadRebootEscrowDataIfAvailable_ServerBasedIoError_RetryFailure() throws Exception {
        setServerBasedRebootEscrowProvider();

        when(mInjected.getBootCount()).thenReturn(0);
        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
        mService.setRebootEscrowListener(mockListener);
        mService.prepareRebootEscrow();

        clearInvocations(mServiceConnection);
        mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
        verify(mockListener).onPreparedForReboot(eq(true));
        verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());

        // Use x -> x for both wrap & unwrap functions.
        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
                .thenAnswer(invocation -> invocation.getArgument(0));
        assertTrue(mService.armRebootEscrowIfNeeded());
        verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
        assertTrue(mStorage.hasRebootEscrowServerBlob());

        // pretend reboot happens here
        when(mInjected.getBootCount()).thenReturn(1);
        ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
        ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(),
                metricsErrorCodeCaptor.capture(), eq(2) /* Server based */,
                eq(2) /* attempt count */, anyInt(), eq(0) /* vbmeta status */, anyInt());
        when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(IOException.class);

        HandlerThread thread = new HandlerThread("RebootEscrowManagerTest");
        thread.start();
        mService.loadRebootEscrowDataIfAvailable(new Handler(thread.getLooper()));
        // Sleep 5s for the retry to complete
        Thread.sleep(5 * 1000);
        assertFalse(metricsSuccessCaptor.getValue());
        assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_RETRY_COUNT_EXHAUSTED),
                metricsErrorCodeCaptor.getValue());
    }

    @Test
    public void loadRebootEscrowDataIfAvailable_ServerBased_RetrySuccess() throws Exception {
        setServerBasedRebootEscrowProvider();
@@ -659,12 +743,17 @@ public class RebootEscrowManagerTests {

        when(mInjected.getBootCount()).thenReturn(1);
        ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
        ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
        // Return a null escrow key
        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(),
                anyInt() /* error code */, eq(1) /* HAL based */, eq(1) /* attempt count */,
                anyInt(), anyInt(), anyInt());
        when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> new byte[32]);
                metricsErrorCodeCaptor.capture(), eq(1) /* HAL based */,
                eq(1) /* attempt count */, anyInt(), anyInt(), anyInt());

        when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> null);
        mService.loadRebootEscrowDataIfAvailable(null);
        verify(mRebootEscrow).retrieveKey();
        assertFalse(metricsSuccessCaptor.getValue());
        assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY),
                metricsErrorCodeCaptor.getValue());
    }
}