[attestation] Rename test_apk to demo_apk for Vm attestation

A separate test_apk will be added in a subsequent cl for e2e test.

Specifically, the demo_apk calls the real request attestation API
and interacts with RKPD to retrieve the real remotely provisioned
keys; while the test_apk will call the request attestation API
for testing only and will not trigger RKPD, a mock key will be used
in this case.

Test: Run VmAttestationDemoApp manually
Change-Id: I5e02dc071d167156e98088829227e83300899461
diff --git a/service_vm/demo_apk/Android.bp b/service_vm/demo_apk/Android.bp
new file mode 100644
index 0000000..5644819
--- /dev/null
+++ b/service_vm/demo_apk/Android.bp
@@ -0,0 +1,34 @@
+package {
+    default_team: "trendy_team_virtualization",
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_app {
+    name: "VmAttestationDemoApp",
+    installable: true,
+    jni_libs: ["libvm_attestation_payload"],
+    jni_uses_platform_apis: true,
+    use_embedded_native_libs: true,
+    sdk_version: "system_current",
+    compile_multilib: "first",
+    apex_available: ["com.android.virt"],
+}
+
+rust_defaults {
+    name: "vm_attestation_payload_defaults",
+    crate_name: "vm_attestation_payload",
+    defaults: ["avf_build_flags_rust"],
+    srcs: ["src/main.rs"],
+    prefer_rlib: true,
+    rustlibs: [
+        "libandroid_logger",
+        "libanyhow",
+        "liblog_rust",
+        "libvm_payload_bindgen",
+    ],
+}
+
+rust_ffi {
+    name: "libvm_attestation_payload",
+    defaults: ["vm_attestation_payload_defaults"],
+}
diff --git a/service_vm/demo_apk/AndroidManifest.xml b/service_vm/demo_apk/AndroidManifest.xml
new file mode 100644
index 0000000..228195d
--- /dev/null
+++ b/service_vm/demo_apk/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="com.android.virt.vm_attestation.demo">
+     <uses-permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE" />
+     <uses-permission android:name="android.permission.USE_CUSTOM_VIRTUAL_MACHINE" />
+
+     <application android:hasCode="false"/>
+</manifest>
diff --git a/service_vm/demo_apk/README.md b/service_vm/demo_apk/README.md
new file mode 100644
index 0000000..551d47b
--- /dev/null
+++ b/service_vm/demo_apk/README.md
@@ -0,0 +1,53 @@
+# VmAttestationDemoApp
+
+## Overview
+
+The *VmAttestationDemoApp* is an Android application that provides a practical
+demonstration of how to interact with the VM Attestation APIs. This app focuses
+on the payload of the Android app and the payload performs two main tasks:
+requesting attestation and validating the attestation result.
+
+## Building
+
+To build the VmAttestationDemoApp, use the following command:
+
+```
+m VmAttestationDemoApp
+```
+
+## Installing
+
+To install the app on your device, execute the following command:
+
+```
+adb install $ANDROID_PRODUCT_OUT/system/app/VmAttestationDemoApp/VmAttestationDemoApp.apk
+```
+
+## Running
+
+Before running the app, make sure that the device has an internet connection and
+that the remote provisioning host is not empty. You can use the following
+command to check the remote provisioning host:
+
+```
+$ adb shell getprop remote_provisioning.hostname
+remoteprovisioning.googleapis.com
+```
+
+Once you have confirmed the remote provisioning host, you can run the app using
+the following command:
+
+```
+TEST_ROOT=/data/local/tmp/virt && adb shell /apex/com.android.virt/bin/vm run-app \
+  --config-path assets/config.json --debug full \
+  $(adb shell pm path com.android.virt.vm_attestation.demo | cut -c 9-) \
+  $TEST_ROOT/VmAttestationDemoApp.apk.idsig \
+  $TEST_ROOT/instance.vm_attestation.debug.img --protected
+```
+
+Please note that remote attestation is only available for protected VMs.
+Therefore, ensure that the VM is launched in protected mode using the
+`--protected` flag.
+
+If everything is set up correctly, you should be able to see the attestation
+result printed out in the VM logs.
diff --git a/service_vm/demo_apk/assets/config.json b/service_vm/demo_apk/assets/config.json
new file mode 100644
index 0000000..1684696
--- /dev/null
+++ b/service_vm/demo_apk/assets/config.json
@@ -0,0 +1,10 @@
+{
+    "os": {
+      "name": "microdroid"
+    },
+    "task": {
+      "type": "microdroid_launcher",
+      "command": "libvm_attestation_payload.so"
+    },
+    "export_tombstones": true
+  }
\ No newline at end of file
diff --git a/service_vm/demo_apk/src/main.rs b/service_vm/demo_apk/src/main.rs
new file mode 100644
index 0000000..0d1efb0
--- /dev/null
+++ b/service_vm/demo_apk/src/main.rs
@@ -0,0 +1,229 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Main executable of Service VM client for manual testing.
+
+use anyhow::{anyhow, ensure, Result};
+use log::{error, info};
+use std::{
+    ffi::{c_void, CStr},
+    panic,
+    ptr::{self, NonNull},
+    result,
+};
+use vm_payload_bindgen::{
+    attestation_status_t, AVmAttestationResult, AVmAttestationResult_free,
+    AVmAttestationResult_getCertificateAt, AVmAttestationResult_getCertificateCount,
+    AVmAttestationResult_getPrivateKey, AVmAttestationResult_resultToString,
+    AVmAttestationResult_sign, AVmPayload_requestAttestation,
+};
+
+/// Entry point of the Service VM client.
+#[allow(non_snake_case)]
+#[no_mangle]
+pub extern "C" fn AVmPayload_main() {
+    android_logger::init_once(
+        android_logger::Config::default()
+            .with_tag("service_vm_client")
+            .with_max_level(log::LevelFilter::Debug),
+    );
+    // Redirect panic messages to logcat.
+    panic::set_hook(Box::new(|panic_info| {
+        error!("{}", panic_info);
+    }));
+    if let Err(e) = try_main() {
+        error!("failed with {:?}", e);
+        std::process::exit(1);
+    }
+}
+
+fn try_main() -> Result<()> {
+    info!("Welcome to Service VM Client!");
+
+    let too_big_challenge = &[0u8; 66];
+    let res = AttestationResult::request_attestation(too_big_challenge);
+    ensure!(res.is_err());
+    let status = res.unwrap_err();
+    ensure!(
+        status == attestation_status_t::ATTESTATION_ERROR_INVALID_CHALLENGE,
+        "Unexpected status: {:?}",
+        status
+    );
+    info!("Status: {:?}", status_to_cstr(status));
+
+    // The data below is only a placeholder generated randomly with urandom
+    let challenge = &[
+        0x6c, 0xad, 0x52, 0x50, 0x15, 0xe7, 0xf4, 0x1d, 0xa5, 0x60, 0x7e, 0xd2, 0x7d, 0xf1, 0x51,
+        0x67, 0xc3, 0x3e, 0x73, 0x9b, 0x30, 0xbd, 0x04, 0x20, 0x2e, 0xde, 0x3b, 0x1d, 0xc8, 0x07,
+        0x11, 0x7b,
+    ];
+    let res = AttestationResult::request_attestation(challenge)
+        .map_err(|e| anyhow!("Unexpected status: {:?}", status_to_cstr(e)))?;
+
+    let cert_chain = res.certificate_chain()?;
+    info!("Attestation result certificateChain = {:?}", cert_chain);
+
+    let private_key = res.private_key()?;
+    info!("Attestation result privateKey = {:?}", private_key);
+
+    let message = b"Hello from Service VM client";
+    info!("Signing message: {:?}", message);
+    let signature = res.sign(message)?;
+    info!("Signature: {:?}", signature);
+
+    Ok(())
+}
+
+#[derive(Debug)]
+struct AttestationResult(NonNull<AVmAttestationResult>);
+
+impl AttestationResult {
+    fn request_attestation(challenge: &[u8]) -> result::Result<Self, attestation_status_t> {
+        let mut res: *mut AVmAttestationResult = ptr::null_mut();
+        // SAFETY: It is safe as we only read the challenge within its bounds and the
+        // function does not retain any reference to it.
+        let status = unsafe {
+            AVmPayload_requestAttestation(
+                challenge.as_ptr() as *const c_void,
+                challenge.len(),
+                &mut res,
+            )
+        };
+        if status == attestation_status_t::ATTESTATION_OK {
+            info!("Attestation succeeds. Status: {:?}", status_to_cstr(status));
+            let res = NonNull::new(res).expect("The attestation result is null");
+            Ok(Self(res))
+        } else {
+            Err(status)
+        }
+    }
+
+    fn certificate_chain(&self) -> Result<Vec<Box<[u8]>>> {
+        let num_certs = get_certificate_count(self.as_ref());
+        let mut certs = Vec::with_capacity(num_certs);
+        for i in 0..num_certs {
+            certs.push(get_certificate_at(self.as_ref(), i)?);
+        }
+        Ok(certs)
+    }
+
+    fn private_key(&self) -> Result<Box<[u8]>> {
+        get_private_key(self.as_ref())
+    }
+
+    fn sign(&self, message: &[u8]) -> Result<Box<[u8]>> {
+        sign_with_attested_key(self.as_ref(), message)
+    }
+}
+
+impl AsRef<AVmAttestationResult> for AttestationResult {
+    fn as_ref(&self) -> &AVmAttestationResult {
+        // SAFETY: This field is private, and only populated with a successful call to
+        // `AVmPayload_requestAttestation`.
+        unsafe { self.0.as_ref() }
+    }
+}
+
+impl Drop for AttestationResult {
+    fn drop(&mut self) {
+        // SAFETY: This field is private, and only populated with a successful call to
+        // `AVmPayload_requestAttestation`, and not freed elsewhere.
+        unsafe { AVmAttestationResult_free(self.0.as_ptr()) };
+    }
+}
+
+fn get_certificate_count(res: &AVmAttestationResult) -> usize {
+    // SAFETY: The result is returned by `AVmPayload_requestAttestation` and should be valid
+    // before getting freed.
+    unsafe { AVmAttestationResult_getCertificateCount(res) }
+}
+
+fn get_certificate_at(res: &AVmAttestationResult, index: usize) -> Result<Box<[u8]>> {
+    let size =
+        // SAFETY: The result is returned by `AVmPayload_requestAttestation` and should be valid
+        // before getting freed.
+        unsafe { AVmAttestationResult_getCertificateAt(res, index, ptr::null_mut(), 0) };
+    let mut cert = vec![0u8; size];
+    // SAFETY: The result is returned by `AVmPayload_requestAttestation` and should be valid
+    // before getting freed. This function only writes within the bounds of `cert`.
+    // And `cert` cannot overlap `res` because we just allocated it.
+    let size = unsafe {
+        AVmAttestationResult_getCertificateAt(
+            res,
+            index,
+            cert.as_mut_ptr() as *mut c_void,
+            cert.len(),
+        )
+    };
+    ensure!(size == cert.len());
+    Ok(cert.into_boxed_slice())
+}
+
+fn get_private_key(res: &AVmAttestationResult) -> Result<Box<[u8]>> {
+    let size =
+        // SAFETY: The result is returned by `AVmPayload_requestAttestation` and should be valid
+        // before getting freed.
+        unsafe { AVmAttestationResult_getPrivateKey(res, ptr::null_mut(), 0) };
+    let mut private_key = vec![0u8; size];
+    // SAFETY: The result is returned by `AVmPayload_requestAttestation` and should be valid
+    // before getting freed. This function only writes within the bounds of `private_key`.
+    // And `private_key` cannot overlap `res` because we just allocated it.
+    let size = unsafe {
+        AVmAttestationResult_getPrivateKey(
+            res,
+            private_key.as_mut_ptr() as *mut c_void,
+            private_key.len(),
+        )
+    };
+    ensure!(size == private_key.len());
+    Ok(private_key.into_boxed_slice())
+}
+
+fn sign_with_attested_key(res: &AVmAttestationResult, message: &[u8]) -> Result<Box<[u8]>> {
+    // SAFETY: The result is returned by `AVmPayload_requestAttestation` and should be valid
+    // before getting freed.
+    let size = unsafe {
+        AVmAttestationResult_sign(
+            res,
+            message.as_ptr() as *const c_void,
+            message.len(),
+            ptr::null_mut(),
+            0,
+        )
+    };
+    let mut signature = vec![0u8; size];
+    // SAFETY: The result is returned by `AVmPayload_requestAttestation` and should be valid
+    // before getting freed. This function only writes within the bounds of `signature`.
+    // And `signature` cannot overlap `res` because we just allocated it.
+    let size = unsafe {
+        AVmAttestationResult_sign(
+            res,
+            message.as_ptr() as *const c_void,
+            message.len(),
+            signature.as_mut_ptr() as *mut c_void,
+            signature.len(),
+        )
+    };
+    ensure!(size == signature.len());
+    Ok(signature.into_boxed_slice())
+}
+
+fn status_to_cstr(status: attestation_status_t) -> &'static CStr {
+    // SAFETY: The function only reads the given enum status and returns a pointer to a
+    // static string.
+    let message = unsafe { AVmAttestationResult_resultToString(status) };
+    // SAFETY: The pointer returned by `AVmAttestationResult_resultToString` is guaranteed to
+    // point to a valid C String that lives forever.
+    unsafe { CStr::from_ptr(message) }
+}