[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()?;