Merge "Map RkpProxyException error codes into get key errors"
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user