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

Commit 9f31bddd authored by Seth Moore's avatar Seth Moore Committed by Automerger Merge Worker
Browse files

Merge "Map RkpProxyException error codes into get key errors" am: 5cd5d98e...

Merge "Map RkpProxyException error codes into get key errors" am: 5cd5d98e am: edb504eb am: 071a5cc3

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/2415815



Change-Id: I2ecab136b8295a4c8cac8f3c72c7f4e5e2886ffd
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 36077a53 071a5cc3
Loading
Loading
Loading
Loading
+33 −2
Original line number Diff line number Diff line
@@ -25,6 +25,36 @@ import android.security.rkp.RemotelyProvisionedKey;
 * @hide
 */
oneway interface IGetKeyCallback {
    enum ErrorCode {
        /**
         * An unexpected error occurred and there's no standard way to describe it. See the
         * corresponding error string for more information.
         */
        ERROR_UNKNOWN = 1,

        /**
         * Device will not receive remotely provisioned keys because it's running vulnerable
         * code. The device needs to be updated to a fixed build to recover.
         */
        ERROR_REQUIRES_SECURITY_PATCH = 2,

        /**
         * Indicates that the attestation key pool has been exhausted, and the remote key
         * provisioning server cannot currently be reached. Clients should wait for the
         * device to have connectivity, then retry.
         */
        ERROR_PENDING_INTERNET_CONNECTIVITY = 3,

        /**
         * Indicates that this device will never be able to provision attestation keys using
         * the remote provsisioning server. This may be due to multiple causes, such as the
         * device is not registered with the remote provisioning backend or the device has
         * been permanently revoked. Clients who receive this error should not attempt to
         * retry key creation.
         */
        ERROR_PERMANENT = 5,
    }

    /**
     * Called in response to {@link IRegistration.getKey}, indicating
     * a remotely-provisioned key is available.
@@ -42,8 +72,9 @@ oneway interface IGetKeyCallback {
    /**
     * Called when an error has occurred while trying to get a remotely provisioned key.
     *
     * @param error A description of what failed, suitable for logging.
     * @param error allows code to handle certain errors, if desired
     * @param description human-readable explanation of what failed, suitable for logging.
     */
    void onError(String error);
    void onError(ErrorCode error, String description);
}
+26 −2
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.security.rkp.IRegistration;
import android.security.rkp.IStoreUpgradedKeyCallback;
import android.security.rkp.service.RegistrationProxy;
import android.security.rkp.service.RemotelyProvisionedKey;
import android.security.rkp.service.RkpProxyException;
import android.util.Log;

import java.util.Set;
@@ -68,13 +69,35 @@ final class RemoteProvisioningRegistration extends IRegistration.Stub {
            if (e instanceof OperationCanceledException) {
                Log.i(TAG, "Operation cancelled for client " + mCallback.hashCode());
                wrapCallback(mCallback::onCancel);
            } else if (e instanceof RkpProxyException) {
                Log.e(TAG, "RKP error fetching key for client " + mCallback.hashCode(), e);
                RkpProxyException rkpException = (RkpProxyException) e;
                wrapCallback(() -> mCallback.onError(toGetKeyError(rkpException),
                        e.getMessage()));
            } else {
                Log.e(TAG, "Error fetching key for client " + mCallback.hashCode(), e);
                wrapCallback(() -> mCallback.onError(e.getMessage()));
                wrapCallback(() -> mCallback.onError(IGetKeyCallback.ErrorCode.ERROR_UNKNOWN,
                        e.getMessage()));
            }
        }
    }

    private byte toGetKeyError(RkpProxyException exception) {
        switch (exception.getError()) {
            case RkpProxyException.ERROR_UNKNOWN:
                return IGetKeyCallback.ErrorCode.ERROR_UNKNOWN;
            case RkpProxyException.ERROR_REQUIRES_SECURITY_PATCH:
                return IGetKeyCallback.ErrorCode.ERROR_REQUIRES_SECURITY_PATCH;
            case RkpProxyException.ERROR_PENDING_INTERNET_CONNECTIVITY:
                return IGetKeyCallback.ErrorCode.ERROR_PENDING_INTERNET_CONNECTIVITY;
            case RkpProxyException.ERROR_PERMANENT:
                return IGetKeyCallback.ErrorCode.ERROR_PERMANENT;
            default:
                Log.e(TAG, "Unexpected error code in RkpProxyException", exception);
                return IGetKeyCallback.ErrorCode.ERROR_UNKNOWN;
        }
    }

    RemoteProvisioningRegistration(RegistrationProxy registration, Executor executor) {
        mRegistration = registration;
        mExecutor = executor;
@@ -97,7 +120,8 @@ final class RemoteProvisioningRegistration extends IRegistration.Stub {
        } catch (Exception e) {
            Log.e(TAG, "getKeyAsync threw an exception for client " + callback.hashCode(), e);
            mGetKeyOperations.remove(callback);
            wrapCallback(() -> callback.onError(e.getMessage()));
            wrapCallback(() -> callback.onError(IGetKeyCallback.ErrorCode.ERROR_UNKNOWN,
                    e.getMessage()));
        }
    }

+35 −4
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static org.mockito.AdditionalAnswers.answerVoid;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.contains;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.eq;
@@ -39,6 +40,7 @@ import android.security.rkp.IGetKeyCallback;
import android.security.rkp.IStoreUpgradedKeyCallback;
import android.security.rkp.service.RegistrationProxy;
import android.security.rkp.service.RemotelyProvisionedKey;
import android.security.rkp.service.RkpProxyException;

import androidx.test.runner.AndroidJUnit4;

@@ -48,8 +50,9 @@ import org.junit.runner.RunWith;
import org.mockito.stubbing.Answer;
import org.mockito.stubbing.VoidAnswer4;

import java.time.Duration;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.Executor;

/**
@@ -104,7 +107,7 @@ public class RemoteProvisioningRegistrationTest {
    }

    @Test
    public void getKeyHandlesError() throws Exception {
    public void getKeyHandlesArbitraryException() throws Exception {
        Exception expectedException = new Exception("oops!");
        doAnswer(
                answerGetKeyAsync((keyId, cancelSignal, executor, receiver) ->
@@ -112,10 +115,37 @@ public class RemoteProvisioningRegistrationTest {
                .when(mRegistrationProxy).getKeyAsync(eq(0), any(), any(), any());
        IGetKeyCallback callback = mock(IGetKeyCallback.class);
        mRegistration.getKey(0, callback);
        verify(callback).onError(eq(expectedException.getMessage()));
        verify(callback).onError(eq(IGetKeyCallback.ErrorCode.ERROR_UNKNOWN), eq("oops!"));
        verifyNoMoreInteractions(callback);
    }

    @Test
    public void getKeyMapsRkpErrorsCorrectly() throws Exception {
        Map<Byte, Integer> expectedConversions = Map.of(
                IGetKeyCallback.ErrorCode.ERROR_UNKNOWN,
                RkpProxyException.ERROR_UNKNOWN,
                IGetKeyCallback.ErrorCode.ERROR_REQUIRES_SECURITY_PATCH,
                RkpProxyException.ERROR_REQUIRES_SECURITY_PATCH,
                IGetKeyCallback.ErrorCode.ERROR_PENDING_INTERNET_CONNECTIVITY,
                RkpProxyException.ERROR_PENDING_INTERNET_CONNECTIVITY,
                IGetKeyCallback.ErrorCode.ERROR_PERMANENT,
                RkpProxyException.ERROR_PERMANENT);

        for (Field errorField: IGetKeyCallback.ErrorCode.class.getFields()) {
            byte error = (Byte) errorField.get(null);
            Exception expectedException = new RkpProxyException(expectedConversions.get(error),
                    errorField.getName());
            doAnswer(
                    answerGetKeyAsync((keyId, cancelSignal, executor, receiver) ->
                            executor.execute(() -> receiver.onError(expectedException))))
                    .when(mRegistrationProxy).getKeyAsync(eq(0), any(), any(), any());
            IGetKeyCallback callback = mock(IGetKeyCallback.class);
            mRegistration.getKey(0, callback);
            verify(callback).onError(eq(error), contains(errorField.getName()));
            verifyNoMoreInteractions(callback);
        }
    }

    @Test
    public void getKeyCancelDuringProxyOperation() throws Exception {
        IGetKeyCallback callback = mock(IGetKeyCallback.class);
@@ -179,7 +209,8 @@ public class RemoteProvisioningRegistrationTest {

        IGetKeyCallback callback = mock(IGetKeyCallback.class);
        mRegistration.getKey(0, callback);
        verify(callback).onError(eq(expectedException.getMessage()));
        verify(callback).onError(eq(IGetKeyCallback.ErrorCode.ERROR_UNKNOWN),
                eq(expectedException.getMessage()));
        assertThrows(IllegalArgumentException.class, () -> mRegistration.cancelGetKey(callback));
        verifyNoMoreInteractions(callback);
    }