Loading core/java/android/security/rkp/IGetKeyCallback.aidl +33 −2 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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); } services/core/java/com/android/server/security/rkp/RemoteProvisioningRegistration.java +26 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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())); } } Loading services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningRegistrationTest.java +35 −4 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; /** Loading Loading @@ -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) -> Loading @@ -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); Loading Loading @@ -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); } Loading Loading
core/java/android/security/rkp/IGetKeyCallback.aidl +33 −2 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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); }
services/core/java/com/android/server/security/rkp/RemoteProvisioningRegistration.java +26 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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())); } } Loading
services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningRegistrationTest.java +35 −4 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; /** Loading Loading @@ -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) -> Loading @@ -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); Loading Loading @@ -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); } Loading