[test] Add e2e instrumentation test for VM attestation

This cl adds an e2e test to check the VM attestation with the
following steps:

- Provisioning a pair of mock keys in virtualizationservice.
- Requesting VM attestation from a running VM and tracing this
step.
- Validating the attestation result.

To facilitate the testing process, two new APIs have been added.
The first API allows for the provisioning of mock keys, while the
second API facilitates the request for attestation in testing mode.

The new test has been added to busytown config at cl/605577401.

Bug: 318333789
Test: atest VmAttestationTestApp
Change-Id: Icf9d39c704af316f7625cf9d057d255243eca445
diff --git a/vm_payload/include-restricted/vm_payload_restricted.h b/vm_payload/include-restricted/vm_payload_restricted.h
index 15c37ed..d7324a8 100644
--- a/vm_payload/include-restricted/vm_payload_restricted.h
+++ b/vm_payload/include-restricted/vm_payload_restricted.h
@@ -55,4 +55,25 @@
  */
 size_t AVmPayload_getDiceAttestationCdi(void* _Nullable data, size_t size);
 
+/**
+ * Requests attestation for the VM for testing only.
+ *
+ * This function is only for testing and will not return a real RKP server backed
+ * certificate chain.
+ *
+ * Prior to calling this function, the caller must provision a key pair to be used in
+ * this function with `VirtualMachineManager#enableTestAttestation`.
+ *
+ * \param challenge A pointer to the challenge buffer.
+ * \param challenge_size size of the challenge. The maximum supported challenge size is
+ *          64 bytes. The status ATTESTATION_ERROR_INVALID_CHALLENGE will be returned if
+ *          an invalid challenge is passed.
+ * \param result The remote attestation result will be filled here if the attestation
+ *               succeeds. The result remains valid until it is freed with
+ *              `AVmPayload_freeAttestationResult`.
+ */
+attestation_status_t AVmPayload_requestAttestationForTesting(
+        const void* _Nonnull challenge, size_t challenge_size,
+        struct AVmAttestationResult* _Nullable* _Nonnull result) __INTRODUCED_IN(__ANDROID_API_V__);
+
 __END_DECLS
diff --git a/vm_payload/include/vm_payload.h b/vm_payload/include/vm_payload.h
index 3483e1d..af755c9 100644
--- a/vm_payload/include/vm_payload.h
+++ b/vm_payload/include/vm_payload.h
@@ -211,7 +211,7 @@
  * If `size` is smaller than the total size of the signature, the signature will be
  * truncated to this `size`.
  *
- * \return The total size of the signature.
+ * \return The size of the signature, or the size needed if the supplied buffer is too small.
  *
  * [RFC 6979]: https://datatracker.ietf.org/doc/html/rfc6979
  */
diff --git a/vm_payload/libvm_payload.map.txt b/vm_payload/libvm_payload.map.txt
index 975a5a3..caf8f84 100644
--- a/vm_payload/libvm_payload.map.txt
+++ b/vm_payload/libvm_payload.map.txt
@@ -8,6 +8,7 @@
     AVmPayload_getApkContentsPath;       # systemapi introduced=UpsideDownCake
     AVmPayload_getEncryptedStoragePath;  # systemapi introduced=UpsideDownCake
     AVmPayload_requestAttestation;       # systemapi introduced=VanillaIceCream
+    AVmPayload_requestAttestationForTesting; # systemapi introduced=VanillaIceCream
     AVmAttestationResult_getPrivateKey;  # systemapi introduced=VanillaIceCream
     AVmAttestationResult_sign;           # systemapi introduced=VanillaIceCream
     AVmAttestationResult_free;           # systemapi introduced=VanillaIceCream
diff --git a/vm_payload/src/lib.rs b/vm_payload/src/lib.rs
index 7978059..6188b21 100644
--- a/vm_payload/src/lib.rs
+++ b/vm_payload/src/lib.rs
@@ -39,6 +39,9 @@
 };
 use vm_payload_status_bindgen::attestation_status_t;
 
+/// Maximum size of an ECDSA signature for EC P-256 key is 72 bytes.
+const MAX_ECDSA_P256_SIGNATURE_SIZE: usize = 72;
+
 lazy_static! {
     static ref VM_APK_CONTENTS_PATH_C: CString =
         CString::new(VM_APK_CONTENTS_PATH).expect("CString::new failed");
@@ -273,9 +276,6 @@
 /// Behavior is undefined if any of the following conditions are violated:
 ///
 /// * `challenge` must be [valid] for reads of `challenge_size` bytes.
-/// * `res` must be [valid] to write the attestation result.
-/// * The region of memory beginning at `challenge` with `challenge_size` bytes must not
-///  overlap with the region of memory `res` points to.
 ///
 /// [valid]: ptr#safety
 #[no_mangle]
@@ -284,6 +284,60 @@
     challenge_size: usize,
     res: &mut *mut AttestationResult,
 ) -> attestation_status_t {
+    // SAFETY: The caller guarantees that `challenge` is valid for reads and `res` is valid
+    // for writes.
+    unsafe {
+        request_attestation(
+            challenge,
+            challenge_size,
+            false, // test_mode
+            res,
+        )
+    }
+}
+
+/// Requests the remote attestation of the client VM for testing.
+///
+/// # Safety
+///
+/// Behavior is undefined if any of the following conditions are violated:
+///
+/// * `challenge` must be [valid] for reads of `challenge_size` bytes.
+///
+/// [valid]: ptr#safety
+#[no_mangle]
+pub unsafe extern "C" fn AVmPayload_requestAttestationForTesting(
+    challenge: *const u8,
+    challenge_size: usize,
+    res: &mut *mut AttestationResult,
+) -> attestation_status_t {
+    // SAFETY: The caller guarantees that `challenge` is valid for reads and `res` is valid
+    // for writes.
+    unsafe {
+        request_attestation(
+            challenge,
+            challenge_size,
+            true, // test_mode
+            res,
+        )
+    }
+}
+
+/// Requests the remote attestation of the client VM.
+///
+/// # Safety
+///
+/// Behavior is undefined if any of the following conditions are violated:
+///
+/// * `challenge` must be [valid] for reads of `challenge_size` bytes.
+///
+/// [valid]: ptr#safety
+unsafe fn request_attestation(
+    challenge: *const u8,
+    challenge_size: usize,
+    test_mode: bool,
+    res: &mut *mut AttestationResult,
+) -> attestation_status_t {
     initialize_logging();
     const MAX_CHALLENGE_SIZE: usize = 64;
     if challenge_size > MAX_CHALLENGE_SIZE {
@@ -297,7 +351,7 @@
         unsafe { std::slice::from_raw_parts(challenge, challenge_size) }
     };
     let service = unwrap_or_abort(get_vm_payload_service());
-    match service.requestAttestation(challenge) {
+    match service.requestAttestation(challenge, test_mode) {
         Ok(attestation_res) => {
             *res = Box::into_raw(Box::new(attestation_res));
             attestation_status_t::ATTESTATION_OK
@@ -400,27 +454,36 @@
     data: *mut u8,
     size: usize,
 ) -> usize {
+    // A DER-encoded ECDSA signature can have varying sizes even with the same EC Key and message,
+    // due to the encoding of the random values r and s that are part of the signature.
+    if size == 0 {
+        return MAX_ECDSA_P256_SIGNATURE_SIZE;
+    }
     if message_size == 0 {
         panic!("Message to be signed must not be empty.")
     }
     // SAFETY: See the requirements on `message` above.
     let message = unsafe { std::slice::from_raw_parts(message, message_size) };
     let signature = unwrap_or_abort(try_ecdsa_sign(message, &res.privateKey));
-    if size != 0 {
-        let data = NonNull::new(data).expect("data must not be null when size > 0");
-        // SAFETY: See the requirements on `data` above. The number of bytes copied doesn't exceed
-        // the length of either buffer, and the caller ensures that `signature` cannot overlap
-        // `data`. We allow data to be null, which is never valid, but only if size == 0
-        // which is checked above.
-        unsafe {
-            ptr::copy_nonoverlapping(
-                signature.as_ptr(),
-                data.as_ptr(),
-                std::cmp::min(signature.len(), size),
-            )
-        };
+    let data = NonNull::new(data).expect("data must not be null when size > 0");
+    // SAFETY: See the requirements on `data` above. The number of bytes copied doesn't exceed
+    // the length of either buffer, and the caller ensures that `signature` cannot overlap
+    // `data`. We allow data to be null, which is never valid, but only if size == 0
+    // which is checked above.
+    unsafe {
+        ptr::copy_nonoverlapping(
+            signature.as_ptr(),
+            data.as_ptr(),
+            usize::min(signature.len(), size),
+        )
+    };
+    if size < signature.len() {
+        // If the buffer is too small, return the maximum size of the signature to allow the caller
+        // to allocate a buffer large enough to call this function again.
+        MAX_ECDSA_P256_SIGNATURE_SIZE
+    } else {
+        signature.len()
     }
-    signature.len()
 }
 
 fn try_ecdsa_sign(message: &[u8], der_encoded_ec_private_key: &[u8]) -> Result<Vec<u8>> {