Merge "Map RkpProxyException error codes into get key errors" am: 5cd5d98eb7
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/2415815 Change-Id: Ibab28bf7b7cd8da7d32e6c748f2a4ab2cc83e6bb Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -25,6 +25,36 @@ import android.security.rkp.RemotelyProvisionedKey;
|
|||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
oneway interface IGetKeyCallback {
|
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
|
* Called in response to {@link IRegistration.getKey}, indicating
|
||||||
* a remotely-provisioned key is available.
|
* 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.
|
* 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.IStoreUpgradedKeyCallback;
|
||||||
import android.security.rkp.service.RegistrationProxy;
|
import android.security.rkp.service.RegistrationProxy;
|
||||||
import android.security.rkp.service.RemotelyProvisionedKey;
|
import android.security.rkp.service.RemotelyProvisionedKey;
|
||||||
|
import android.security.rkp.service.RkpProxyException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -68,13 +69,35 @@ final class RemoteProvisioningRegistration extends IRegistration.Stub {
|
|||||||
if (e instanceof OperationCanceledException) {
|
if (e instanceof OperationCanceledException) {
|
||||||
Log.i(TAG, "Operation cancelled for client " + mCallback.hashCode());
|
Log.i(TAG, "Operation cancelled for client " + mCallback.hashCode());
|
||||||
wrapCallback(mCallback::onCancel);
|
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 {
|
} else {
|
||||||
Log.e(TAG, "Error fetching key for client " + mCallback.hashCode(), e);
|
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) {
|
RemoteProvisioningRegistration(RegistrationProxy registration, Executor executor) {
|
||||||
mRegistration = registration;
|
mRegistration = registration;
|
||||||
mExecutor = executor;
|
mExecutor = executor;
|
||||||
@@ -97,7 +120,8 @@ final class RemoteProvisioningRegistration extends IRegistration.Stub {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "getKeyAsync threw an exception for client " + callback.hashCode(), e);
|
Log.e(TAG, "getKeyAsync threw an exception for client " + callback.hashCode(), e);
|
||||||
mGetKeyOperations.remove(callback);
|
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.any;
|
||||||
import static org.mockito.Mockito.anyInt;
|
import static org.mockito.Mockito.anyInt;
|
||||||
import static org.mockito.Mockito.argThat;
|
import static org.mockito.Mockito.argThat;
|
||||||
|
import static org.mockito.Mockito.contains;
|
||||||
import static org.mockito.Mockito.doAnswer;
|
import static org.mockito.Mockito.doAnswer;
|
||||||
import static org.mockito.Mockito.doThrow;
|
import static org.mockito.Mockito.doThrow;
|
||||||
import static org.mockito.Mockito.eq;
|
import static org.mockito.Mockito.eq;
|
||||||
@@ -39,6 +40,7 @@ import android.security.rkp.IGetKeyCallback;
|
|||||||
import android.security.rkp.IStoreUpgradedKeyCallback;
|
import android.security.rkp.IStoreUpgradedKeyCallback;
|
||||||
import android.security.rkp.service.RegistrationProxy;
|
import android.security.rkp.service.RegistrationProxy;
|
||||||
import android.security.rkp.service.RemotelyProvisionedKey;
|
import android.security.rkp.service.RemotelyProvisionedKey;
|
||||||
|
import android.security.rkp.service.RkpProxyException;
|
||||||
|
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
@@ -48,8 +50,9 @@ import org.junit.runner.RunWith;
|
|||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
import org.mockito.stubbing.VoidAnswer4;
|
import org.mockito.stubbing.VoidAnswer4;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.lang.reflect.Field;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -104,7 +107,7 @@ public class RemoteProvisioningRegistrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getKeyHandlesError() throws Exception {
|
public void getKeyHandlesArbitraryException() throws Exception {
|
||||||
Exception expectedException = new Exception("oops!");
|
Exception expectedException = new Exception("oops!");
|
||||||
doAnswer(
|
doAnswer(
|
||||||
answerGetKeyAsync((keyId, cancelSignal, executor, receiver) ->
|
answerGetKeyAsync((keyId, cancelSignal, executor, receiver) ->
|
||||||
@@ -112,10 +115,37 @@ public class RemoteProvisioningRegistrationTest {
|
|||||||
.when(mRegistrationProxy).getKeyAsync(eq(0), any(), any(), any());
|
.when(mRegistrationProxy).getKeyAsync(eq(0), any(), any(), any());
|
||||||
IGetKeyCallback callback = mock(IGetKeyCallback.class);
|
IGetKeyCallback callback = mock(IGetKeyCallback.class);
|
||||||
mRegistration.getKey(0, callback);
|
mRegistration.getKey(0, callback);
|
||||||
verify(callback).onError(eq(expectedException.getMessage()));
|
verify(callback).onError(eq(IGetKeyCallback.ErrorCode.ERROR_UNKNOWN), eq("oops!"));
|
||||||
verifyNoMoreInteractions(callback);
|
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
|
@Test
|
||||||
public void getKeyCancelDuringProxyOperation() throws Exception {
|
public void getKeyCancelDuringProxyOperation() throws Exception {
|
||||||
IGetKeyCallback callback = mock(IGetKeyCallback.class);
|
IGetKeyCallback callback = mock(IGetKeyCallback.class);
|
||||||
@@ -179,7 +209,8 @@ public class RemoteProvisioningRegistrationTest {
|
|||||||
|
|
||||||
IGetKeyCallback callback = mock(IGetKeyCallback.class);
|
IGetKeyCallback callback = mock(IGetKeyCallback.class);
|
||||||
mRegistration.getKey(0, callback);
|
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));
|
assertThrows(IllegalArgumentException.class, () -> mRegistration.cancelGetKey(callback));
|
||||||
verifyNoMoreInteractions(callback);
|
verifyNoMoreInteractions(callback);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user