VM: Introduce writePayloadRpData/readPayloadRpData

VM payload require an api to allow storing n bytes' data with
confidentialty & tamper evidence integrity guarantees.

Microdroid Manager implements this using the vm_secret module, which
uses the payload's DICE chain to store/get secret from Secretkeeper.

Additionally introduce a test that uses these api.
Test: #rollbackProtectedDataOfPayload
Bug: 378911776

Change-Id: Id39f5c6c626531029bf33ef5d28dc237881e40e6
diff --git a/libs/libvm_payload/Android.bp b/libs/libvm_payload/Android.bp
index bb91737..1ebbe39 100644
--- a/libs/libvm_payload/Android.bp
+++ b/libs/libvm_payload/Android.bp
@@ -34,6 +34,7 @@
     bindgen_flags: [
         "--default-enum-style rust",
         "--allowlist-type=AVmAttestationStatus",
+        "--allowlist-type=AVmAccessRollbackProtectedSecretStatus",
     ],
     visibility: [":__subpackages__"],
 }
diff --git a/libs/libvm_payload/include/vm_payload.h b/libs/libvm_payload/include/vm_payload.h
index 5e15607..43fba82 100644
--- a/libs/libvm_payload/include/vm_payload.h
+++ b/libs/libvm_payload/include/vm_payload.h
@@ -52,6 +52,22 @@
 } AVmAttestationStatus;
 
 /**
+ * Introduced in API 36.
+ * Status type used to indicate error while accessing RollbackProtectedSecret.
+ */
+typedef enum AVmAccessRollbackProtectedSecretStatus : int32_t {
+    /**
+     * Relevant Entry not found. This can happen either due to no value was ever written or because
+     * Android maliciously deleted the value (deletions may not be authenticated).
+     */
+    AVMACCESSROLLBACKPROTECTEDSECRETSTATUS_ENTRY_NOT_FOUND = -1,
+    /** Requested access size is not supported by the implementation */
+    AVMACCESSROLLBACKPROTECTEDSECRETSTATUS_BAD_SIZE = -2,
+    /** Access failed, this could be due to lacking support from Hardware */
+    AVMACCESSROLLBACKPROTECTEDSECRETSTATUS_ACCESS_FAILED = -3,
+} AVmAccessRollbackProtectedSecretStatus;
+
+/**
  * Notifies the host that the payload is ready.
  *
  * If the host app has set a `VirtualMachineCallback` for the VM, its
@@ -259,5 +275,33 @@
 size_t AVmAttestationResult_getCertificateAt(const AVmAttestationResult* _Nonnull result,
                                              size_t index, void* _Nullable data, size_t size)
         __INTRODUCED_IN(__ANDROID_API_V__);
+/**
+ * Writes up to n bytes from buffer starting at `buf`, on behalf of the payload, to rollback
+ * detectable storage. The number of bytes written may be less than n if, for example, the
+ * underlying storage has size constraints. This stored data is confidential to the pVM and
+ * protected via appropriate DICE policy on the payload's DICE chain.
+ *
+ * \param buf A pointer to data to be written. This should have the size of at least n bytes.
+ * \param n The maximum number of bytes to be filled in `buf`.
+ *
+ *  \return On success, the number of bytes written is returned. On error, appropriate
+ * AVmAccessRollbackProtectedSecretStatus (negative number) is returned.
+ */
+
+int32_t AVmPayload_writeRollbackProtectedSecret(const void* _Nonnull buf, size_t n)
+        __INTRODUCED_IN(36);
+/**
+ * Read up to n bytes of payload's data in rollback detectable storage into `buf`.
+ *
+ * \param buf A pointer to buffer where the requested data is written. This should have the size of
+ * at least n bytes.
+ * \param n The maximum number of bytes to be read.
+ *
+ *  \return On success, the number of bytes that would have been written to `buf` if n was
+ * sufficiently large. On error, appropriate AVmAccessRollbackProtectedSecretStatus(a negative
+ * number) is returned.
+ */
+int32_t AVmPayload_readRollbackProtectedSecret(void* _Nullable buf, size_t n) __INTRODUCED_IN(36);
+;
 
 __END_DECLS
diff --git a/libs/libvm_payload/libvm_payload.map.txt b/libs/libvm_payload/libvm_payload.map.txt
index 3daad00..0c6b56d 100644
--- a/libs/libvm_payload/libvm_payload.map.txt
+++ b/libs/libvm_payload/libvm_payload.map.txt
@@ -15,6 +15,8 @@
     AVmAttestationStatus_toString;       # systemapi introduced=VanillaIceCream
     AVmAttestationResult_getCertificateCount; # systemapi introduced=VanillaIceCream
     AVmAttestationResult_getCertificateAt; # systemapi introduced=VanillaIceCream
+    AVmPayload_writeRollbackProtectedSecret; # systemapi introduced=36
+    AVmPayload_readRollbackProtectedSecret; # systemapi introduced=36
   local:
     *;
 };
diff --git a/libs/libvm_payload/src/lib.rs b/libs/libvm_payload/src/lib.rs
index eb81752..29b0cbd 100644
--- a/libs/libvm_payload/src/lib.rs
+++ b/libs/libvm_payload/src/lib.rs
@@ -16,14 +16,14 @@
 
 use android_system_virtualization_payload::aidl::android::system::virtualization::payload:: IVmPayloadService::{
     IVmPayloadService, ENCRYPTEDSTORE_MOUNTPOINT, VM_APK_CONTENTS_PATH,
-    VM_PAYLOAD_SERVICE_SOCKET_NAME, AttestationResult::AttestationResult,
+    VM_PAYLOAD_SERVICE_SOCKET_NAME, AttestationResult::AttestationResult
 };
 use anyhow::{bail, ensure, Context, Result};
 use binder::{
     unstable_api::{new_spibinder, AIBinder},
     Strong, ExceptionCode,
 };
-use log::{error, info, LevelFilter};
+use log::{error, info, LevelFilter, debug};
 use rpcbinder::{RpcServer, RpcSession};
 use openssl::{ec::EcKey, sha::sha256, ecdsa::EcdsaSig};
 use std::convert::Infallible;
@@ -38,9 +38,12 @@
     Mutex,
 };
 use vm_payload_status_bindgen::AVmAttestationStatus;
+use vm_payload_status_bindgen::AVmAccessRollbackProtectedSecretStatus::{AVMACCESSROLLBACKPROTECTEDSECRETSTATUS_ENTRY_NOT_FOUND, AVMACCESSROLLBACKPROTECTEDSECRETSTATUS_ACCESS_FAILED, AVMACCESSROLLBACKPROTECTEDSECRETSTATUS_BAD_SIZE};
+use std::cmp::min;
 
 /// Maximum size of an ECDSA signature for EC P-256 key is 72 bytes.
 const MAX_ECDSA_P256_SIGNATURE_SIZE: usize = 72;
+const RP_DATA_SIZE: usize = 32;
 
 static VM_APK_CONTENTS_PATH_C: LazyLock<CString> =
     LazyLock::new(|| CString::new(VM_APK_CONTENTS_PATH).expect("CString::new failed"));
@@ -566,3 +569,86 @@
         ptr::null()
     }
 }
+
+/// Writes up to n bytes from buffer starting at `buf`, on behalf of the payload, to rollback
+/// detectable storage and return the number of bytes written or appropriate (negative) status.
+/// For this implementation, the backing storage is Secretkeeper HAL, which allows storing & reading
+/// of 32 bytes secret!
+///
+/// # Safety
+///
+/// Behavior is undefined if any of the following conditions are violated:
+///
+/// * `buf` must be [valid] for reads of n bytes.
+///
+/// [valid]: ptr#safety
+#[no_mangle]
+pub unsafe extern "C" fn AVmPayload_writeRollbackProtectedSecret(buf: *const u8, n: usize) -> i32 {
+    initialize_logging();
+    if n < RP_DATA_SIZE {
+        error!(
+            "Requested writing {} bytes, while Secretkeeper supports only {} bytes",
+            n, RP_DATA_SIZE
+        );
+        return AVMACCESSROLLBACKPROTECTEDSECRETSTATUS_BAD_SIZE as i32;
+    }
+    // Safety: See the requirements on `buf` above and we just checked that n >= RP_DATA_SIZE.
+    let buf = unsafe { std::slice::from_raw_parts(buf, RP_DATA_SIZE) };
+    match try_writing_payload_rollback_protected_data(buf.try_into().unwrap()) {
+        Ok(()) => RP_DATA_SIZE as i32,
+        Err(e) => {
+            error!("Failed to write rollback protected data: {e:?}");
+            AVMACCESSROLLBACKPROTECTEDSECRETSTATUS_ACCESS_FAILED as i32
+        }
+    }
+}
+
+/// Read up to n bytes of payload's data in rollback detectable storage into `buf`.
+/// For this implementation, the backing storage is Secretkeeper HAL, which allows storing & reading
+/// of 32 bytes secret!
+///
+/// # Safety
+///
+/// Behavior is undefined if any of the following conditions are violated:
+///
+/// * `buf` must be [valid] for writes of n bytes.
+///
+/// [valid]: ptr#safety
+#[no_mangle]
+pub unsafe extern "C" fn AVmPayload_readRollbackProtectedSecret(buf: *mut u8, n: usize) -> i32 {
+    initialize_logging();
+    match try_read_rollback_protected_data() {
+        Err(e) => {
+            error!("Failed to read rollback protected data: {e:?}");
+            AVMACCESSROLLBACKPROTECTEDSECRETSTATUS_ACCESS_FAILED as i32
+        }
+        Ok(stored_data) => {
+            if let Some(stored_data) = stored_data {
+                // SAFETY: See the requirements on `buf` above; `stored_data` is known to have
+                // length `RP_DATA_SIZE`, and cannot overlap `data` because we just allocated
+                // it.
+                unsafe {
+                    ptr::copy_nonoverlapping(stored_data.as_ptr(), buf, min(n, RP_DATA_SIZE));
+                }
+                RP_DATA_SIZE as i32
+            } else {
+                debug!("No relevant entry found in Secretkeeper");
+                AVMACCESSROLLBACKPROTECTEDSECRETSTATUS_ENTRY_NOT_FOUND as i32
+            }
+        }
+    }
+}
+
+fn try_writing_payload_rollback_protected_data(data: &[u8; RP_DATA_SIZE]) -> Result<()> {
+    get_vm_payload_service()?
+        .writePayloadRpData(data)
+        .context("Failed to write payload rollback protected data")?;
+    Ok(())
+}
+
+fn try_read_rollback_protected_data() -> Result<Option<[u8; RP_DATA_SIZE]>> {
+    let rp = get_vm_payload_service()?
+        .readPayloadRpData()
+        .context("Failed to read rollback protected data")?;
+    Ok(rp)
+}
diff --git a/libs/libvm_payload/wrapper/lib.rs b/libs/libvm_payload/wrapper/lib.rs
index b9ce6c8..133b14e 100644
--- a/libs/libvm_payload/wrapper/lib.rs
+++ b/libs/libvm_payload/wrapper/lib.rs
@@ -31,7 +31,9 @@
 use std::ptr;
 use vm_payload_bindgen::{
     AIBinder, AVmPayload_getApkContentsPath, AVmPayload_getEncryptedStoragePath,
-    AVmPayload_getVmInstanceSecret, AVmPayload_notifyPayloadReady, AVmPayload_runVsockRpcServer,
+    AVmPayload_getVmInstanceSecret, AVmPayload_notifyPayloadReady,
+    AVmPayload_readRollbackProtectedSecret, AVmPayload_runVsockRpcServer,
+    AVmPayload_writeRollbackProtectedSecret,
 };
 
 /// The functions declared here are restricted to VMs created with a config file;
@@ -194,3 +196,15 @@
         )
     }
 }
+
+/// Read payload's `data` written on behalf of the payload in Secretkeeper.
+pub fn read_rollback_protected_secret(data: &mut [u8]) -> i32 {
+    // SAFETY: The function only reads from`[data]` within its bounds.
+    unsafe { AVmPayload_readRollbackProtectedSecret(data.as_ptr() as *mut c_void, data.len()) }
+}
+
+/// Write `data`, on behalf of the payload, to Secretkeeper.
+pub fn write_rollback_protected_secret(data: &[u8]) -> i32 {
+    // SAFETY: The function only writes to `[data]` within its bounds.
+    unsafe { AVmPayload_writeRollbackProtectedSecret(data.as_ptr() as *const c_void, data.len()) }
+}