[attestation] Add API to check AVF RKP Hal presence in VM Attestation
This cl adds a new API in VirtualMachineManager to check whether
remote attestation is supported on a device.
Since Remote Attestation is a strongly recommended feature for Android
V, the new API is needed to determine whether we should proceed with
the Remote Attestation CTS tests.
Bug: 329652894
Test: atest MicrodroidTests
Change-Id: I0941914e7a5f1a483705d3faf7091b47ada41b1f
diff --git a/java/framework/api/test-current.txt b/java/framework/api/test-current.txt
index a8b2088..25eab18 100644
--- a/java/framework/api/test-current.txt
+++ b/java/framework/api/test-current.txt
@@ -26,6 +26,7 @@
public class VirtualMachineManager {
method @FlaggedApi("com.android.system.virtualmachine.flags.avf_v_test_apis") @NonNull public java.util.List<java.lang.String> getSupportedOSList() throws android.system.virtualmachine.VirtualMachineException;
method @FlaggedApi("com.android.system.virtualmachine.flags.avf_v_test_apis") @RequiresPermission(android.system.virtualmachine.VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION) public boolean isFeatureEnabled(String) throws android.system.virtualmachine.VirtualMachineException;
+ method @FlaggedApi("com.android.system.virtualmachine.flags.avf_v_test_apis") @RequiresPermission(android.system.virtualmachine.VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION) public boolean isRemoteAttestationSupported() throws android.system.virtualmachine.VirtualMachineException;
field @FlaggedApi("com.android.system.virtualmachine.flags.avf_v_test_apis") public static final String FEATURE_DICE_CHANGES = "com.android.kvm.DICE_CHANGES";
field @FlaggedApi("com.android.system.virtualmachine.flags.avf_v_test_apis") public static final String FEATURE_LLPVM_CHANGES = "com.android.kvm.LLPVM_CHANGES";
field @FlaggedApi("com.android.system.virtualmachine.flags.avf_v_test_apis") public static final String FEATURE_MULTI_TENANT = "com.android.kvm.MULTI_TENANT";
diff --git a/java/framework/src/android/system/virtualmachine/VirtualMachineManager.java b/java/framework/src/android/system/virtualmachine/VirtualMachineManager.java
index 5020ff0..9c965ec 100644
--- a/java/framework/src/android/system/virtualmachine/VirtualMachineManager.java
+++ b/java/framework/src/android/system/virtualmachine/VirtualMachineManager.java
@@ -393,4 +393,24 @@
}
}
}
+
+ /**
+ * Returns {@code true} if the pVM remote attestation feature is supported. Remote attestation
+ * allows a protected VM to attest its authenticity to a remote server.
+ *
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(Flags.FLAG_AVF_V_TEST_APIS)
+ @RequiresPermission(VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION)
+ public boolean isRemoteAttestationSupported() throws VirtualMachineException {
+ synchronized (sCreateLock) {
+ VirtualizationService service = VirtualizationService.getInstance();
+ try {
+ return service.getBinder().isRemoteAttestationSupported();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+ }
}
diff --git a/service_vm/test_apk/Android.bp b/service_vm/test_apk/Android.bp
index cd992db..e69b348 100644
--- a/service_vm/test_apk/Android.bp
+++ b/service_vm/test_apk/Android.bp
@@ -45,7 +45,10 @@
rust_ffi {
name: "libvm_attestation_test_payload",
defaults: ["vm_attestation_test_payload_defaults"],
- visibility: [":__subpackages__"],
+ visibility: [
+ ":__subpackages__",
+ "//packages/modules/Virtualization/tests/testapk",
+ ],
}
android_test {
diff --git a/service_vm/test_apk/aidl/com/android/virt/vm_attestation/testservice/IAttestationService.aidl b/service_vm/test_apk/aidl/com/android/virt/vm_attestation/testservice/IAttestationService.aidl
index 34c8549..18df572 100644
--- a/service_vm/test_apk/aidl/com/android/virt/vm_attestation/testservice/IAttestationService.aidl
+++ b/service_vm/test_apk/aidl/com/android/virt/vm_attestation/testservice/IAttestationService.aidl
@@ -21,6 +21,27 @@
const int PORT = 5679;
/**
+ * The status of the attestation.
+ *
+ * The status here maps to the status defined in
+ * vm_payload/include/vm_payload.h
+ */
+ @Backing(type="int")
+ enum AttestationStatus {
+ /** The remote attestation completes successfully. */
+ ATTESTATION_OK = 0,
+
+ /** The challenge size is not between 0 and 64. */
+ ATTESTATION_ERROR_INVALID_CHALLENGE = 1,
+
+ /** Failed to attest the VM. Please retry at a later time. */
+ ATTESTATION_ERROR_ATTESTATION_FAILED = 2,
+
+ /** Remote attestation is not supported in the current environment. */
+ ATTESTATION_ERROR_UNSUPPORTED = 3,
+ }
+
+ /**
* The result of signing a message with the attested key.
*/
parcelable SigningResult {
@@ -29,6 +50,9 @@
/** The DER-encoded attestation X509 certificate chain. */
byte[] certificateChain;
+
+ /** The status of the attestation. */
+ AttestationStatus status;
}
/**
diff --git a/service_vm/test_apk/src/native/main.rs b/service_vm/test_apk/src/native/main.rs
index a04fb1f..ff21bd8 100644
--- a/service_vm/test_apk/src/native/main.rs
+++ b/service_vm/test_apk/src/native/main.rs
@@ -18,7 +18,8 @@
use avflog::LogResult;
use com_android_virt_vm_attestation_testservice::{
aidl::com::android::virt::vm_attestation::testservice::IAttestationService::{
- BnAttestationService, IAttestationService, SigningResult::SigningResult, PORT,
+ AttestationStatus::AttestationStatus, BnAttestationService, IAttestationService,
+ SigningResult::SigningResult, PORT,
},
binder::{self, unstable_api::AsNative, BinderFeatures, Interface, IntoBinderResult, Strong},
};
@@ -103,14 +104,18 @@
challenge: &[u8],
message: &[u8],
) -> binder::Result<SigningResult> {
- let res = AttestationResult::request_attestation(challenge)
- .map_err(|e| anyhow!("Unexpected status: {:?}", status_to_cstr(e)))
- .with_log()
- .or_service_specific_exception(-1)?;
+ let res = match AttestationResult::request_attestation(challenge) {
+ Ok(res) => res,
+ Err(status) => {
+ let status = to_attestation_status(status);
+ return Ok(SigningResult { certificateChain: vec![], signature: vec![], status });
+ }
+ };
let certificate_chain =
res.certificate_chain().with_log().or_service_specific_exception(-1)?;
+ let status = AttestationStatus::ATTESTATION_OK;
let signature = res.sign(message).with_log().or_service_specific_exception(-1)?;
- Ok(SigningResult { certificateChain: certificate_chain, signature })
+ Ok(SigningResult { certificateChain: certificate_chain, signature, status })
}
fn validateAttestationResult(&self) -> binder::Result<()> {
@@ -119,6 +124,21 @@
}
}
+fn to_attestation_status(status: AVmAttestationStatus) -> AttestationStatus {
+ match status {
+ AVmAttestationStatus::ATTESTATION_OK => AttestationStatus::ATTESTATION_OK,
+ AVmAttestationStatus::ATTESTATION_ERROR_INVALID_CHALLENGE => {
+ AttestationStatus::ATTESTATION_ERROR_INVALID_CHALLENGE
+ }
+ AVmAttestationStatus::ATTESTATION_ERROR_ATTESTATION_FAILED => {
+ AttestationStatus::ATTESTATION_ERROR_ATTESTATION_FAILED
+ }
+ AVmAttestationStatus::ATTESTATION_ERROR_UNSUPPORTED => {
+ AttestationStatus::ATTESTATION_ERROR_UNSUPPORTED
+ }
+ }
+}
+
#[derive(Debug)]
struct AttestationResult(NonNull<AVmAttestationResult>);
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index 732be94..1ed48d0 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -23,6 +23,7 @@
static_libs: [
"com.android.microdroid.testservice-java",
"com.android.microdroid.test.vmshare_service-java",
+ "com.android.virt.vm_attestation.testservice-java",
],
certificate: ":MicrodroidTestAppCert",
sdk_version: "test_current",
@@ -53,6 +54,7 @@
"MicrodroidExitNativeLib",
"MicrodroidPrivateLinkingNativeLib",
"MicrodroidCrashNativeLib",
+ "libvm_attestation_test_payload",
],
min_sdk_version: "33",
// Defined in ../vmshareapp/Android.bp
diff --git a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
index efacf8f..45beb14 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -28,6 +28,8 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.TruthJUnit.assume;
+import com.android.virt.vm_attestation.testservice.IAttestationService.AttestationStatus;
+import com.android.virt.vm_attestation.testservice.IAttestationService.SigningResult;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
@@ -115,6 +117,8 @@
@RunWith(Parameterized.class)
public class MicrodroidTests extends MicrodroidDeviceTestBase {
private static final String TAG = "MicrodroidTests";
+ private static final String VM_ATTESTATION_PAYLOAD_PATH = "libvm_attestation_test_payload.so";
+ private static final String VM_ATTESTATION_MESSAGE = "Hello RKP from AVF!";
@Rule public Timeout globalTimeout = Timeout.seconds(300);
@@ -210,6 +214,47 @@
@Test
@CddTest(requirements = {"9.17/C-1-1", "9.17/C-2-1"})
+ public void vmAttestationWhenRemoteAttestationIsSupported() throws Exception {
+ // pVM remote attestation is only supported on protected VMs.
+ assumeProtectedVM();
+ assumeFeatureEnabled(VirtualMachineManager.FEATURE_REMOTE_ATTESTATION);
+ assume().withMessage("Test needs Remote Attestation support")
+ .that(getVirtualMachineManager().isRemoteAttestationSupported())
+ .isTrue();
+ VirtualMachineConfig config =
+ newVmConfigBuilderWithPayloadBinary(VM_ATTESTATION_PAYLOAD_PATH)
+ .setProtectedVm(mProtectedVm)
+ .setDebugLevel(DEBUG_LEVEL_FULL)
+ .build();
+ VirtualMachine vm =
+ forceCreateNewVirtualMachine("cts_attestation_with_rkpd_supported", config);
+
+ // Check with an invalid challenge.
+ byte[] invalidChallenge = new byte[65];
+ Arrays.fill(invalidChallenge, (byte) 0xbb);
+ SigningResult signingResultInvalidChallenge =
+ runVmAttestationService(
+ TAG, vm, invalidChallenge, VM_ATTESTATION_MESSAGE.getBytes());
+ assertThat(signingResultInvalidChallenge.status)
+ .isEqualTo(AttestationStatus.ATTESTATION_ERROR_INVALID_CHALLENGE);
+
+ // Check with a valid challenge.
+ byte[] challenge = new byte[32];
+ Arrays.fill(challenge, (byte) 0xac);
+ SigningResult signingResult =
+ runVmAttestationService(TAG, vm, challenge, VM_ATTESTATION_MESSAGE.getBytes());
+ assertWithMessage(
+ "VM attestation should either succeed or fail when the network is unstable")
+ .that(signingResult.status)
+ .isAnyOf(
+ AttestationStatus.ATTESTATION_OK,
+ AttestationStatus.ATTESTATION_ERROR_ATTESTATION_FAILED);
+ // TODO(b/330662600): Check the certificate chain and the signature after refactoring the
+ // x509 util method in RkpdVmAttestationTest.
+ }
+
+ @Test
+ @CddTest(requirements = {"9.17/C-1-1", "9.17/C-2-1"})
public void createAndRunNoDebugVm() throws Exception {
assumeSupportedDevice();
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index ea05bdb..d0d3878 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -313,6 +313,11 @@
fn enableTestAttestation(&self) -> binder::Result<()> {
GLOBAL_SERVICE.enableTestAttestation()
}
+
+ fn isRemoteAttestationSupported(&self) -> binder::Result<bool> {
+ check_manage_access()?;
+ GLOBAL_SERVICE.isRemoteAttestationSupported()
+ }
}
impl VirtualizationService {
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl
index e11d8b8..e2063a9 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl
@@ -86,4 +86,9 @@
* associated to the fake key pair when the VM requests attestation in testing mode.
*/
void enableTestAttestation();
+
+ /**
+ * Returns {@code true} if the pVM remote attestation feature is supported
+ */
+ boolean isRemoteAttestationSupported();
}
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
index 8af881b..c6575c8 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
@@ -77,6 +77,11 @@
void enableTestAttestation();
/**
+ * Returns {@code true} if the pVM remote attestation feature is supported
+ */
+ boolean isRemoteAttestationSupported();
+
+ /**
* Get a list of assignable devices.
*/
AssignableDevice[] getAssignableDevices();
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 208bdce..5ddb8c3 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -355,6 +355,10 @@
Ok(certificate_chain)
}
+ fn isRemoteAttestationSupported(&self) -> binder::Result<bool> {
+ remotely_provisioned_component_service_exists()
+ }
+
fn getAssignableDevices(&self) -> binder::Result<Vec<AssignableDevice>> {
check_use_custom_virtual_machine()?;