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/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
index 97a5e78..35ee7dd 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -1826,6 +1826,79 @@
assertThat(testResults.mFileContent).isEqualTo(EXAMPLE_STRING);
}
+ private boolean deviceCapableOfProtectedVm() {
+ int capabilities = getVirtualMachineManager().getCapabilities();
+ if ((capabilities & CAPABILITY_PROTECTED_VM) != 0) {
+ return true;
+ }
+ return false;
+ }
+
+ private void ensureUpdatableVmSupported() throws Exception {
+ if (getVendorApiLevel() >= 202504 && deviceCapableOfProtectedVm()) {
+ assertTrue(
+ "Missing Updatable VM support, have you declared Secretkeeper interface?",
+ isUpdatableVmSupported());
+ } else {
+ assumeTrue("Device does not support Updatable VM", isUpdatableVmSupported());
+ }
+ }
+
+ @Test
+ public void rollbackProtectedDataOfPayload() throws Exception {
+ assumeSupportedDevice();
+ // Rollback protected data is only possible if Updatable VMs is supported -
+ // which implies Secretkeeper support.
+ ensureUpdatableVmSupported();
+ byte[] value1 = new byte[32];
+ Arrays.fill(value1, (byte) 0xcc);
+ byte[] value2 = new byte[32];
+ Arrays.fill(value2, (byte) 0xdd);
+
+ VirtualMachineConfig config =
+ newVmConfigBuilderWithPayloadBinary("MicrodroidTestNativeLib.so")
+ .setMemoryBytes(minMemoryRequired())
+ .setEncryptedStorageBytes(ENCRYPTED_STORAGE_BYTES)
+ .setDebugLevel(DEBUG_LEVEL_FULL)
+ .build();
+ VirtualMachine vm = forceCreateNewVirtualMachine("test_vm", config);
+ TestResults testResults =
+ runVmTestService(
+ TAG,
+ vm,
+ (ts, tr) -> {
+ tr.mPayloadRpData = ts.insecurelyReadPayloadRpData();
+ });
+ // ainsecurelyReadPayloadRpData()` must've failed since no data was ever written!
+ assertWithMessage("The read (unexpectedly) succeeded!")
+ .that(testResults.mException)
+ .isNotNull();
+
+ // Re-run the same VM & write/read th RP data & verify it what we just wrote!
+ testResults =
+ runVmTestService(
+ TAG,
+ vm,
+ (ts, tr) -> {
+ ts.insecurelyWritePayloadRpData(value1);
+ tr.mPayloadRpData = ts.insecurelyReadPayloadRpData();
+ ts.insecurelyWritePayloadRpData(value2);
+ });
+ testResults.assertNoException();
+ assertThat(testResults.mPayloadRpData).isEqualTo(value1);
+
+ // Re-run the same VM again
+ testResults =
+ runVmTestService(
+ TAG,
+ vm,
+ (ts, tr) -> {
+ tr.mPayloadRpData = ts.insecurelyReadPayloadRpData();
+ });
+ testResults.assertNoException();
+ assertThat(testResults.mPayloadRpData).isEqualTo(value2);
+ }
+
@Test
@CddTest(requirements = {"9.17/C-1-1", "9.17/C-2-1"})
public void canReadFileFromAssets_debugFull() throws Exception {
diff --git a/tests/testapk/src/native/testbinary.cpp b/tests/testapk/src/native/testbinary.cpp
index 632f648..a1739f9 100644
--- a/tests/testapk/src/native/testbinary.cpp
+++ b/tests/testapk/src/native/testbinary.cpp
@@ -344,6 +344,23 @@
return ScopedAStatus::ok();
}
+ ScopedAStatus insecurelyReadPayloadRpData(std::array<uint8_t, 32>* out) override {
+ int32_t ret = AVmPayload_readRollbackProtectedSecret(out->data(), 32);
+ if (ret != 32) {
+ return ScopedAStatus::fromServiceSpecificError(ret);
+ }
+ return ScopedAStatus::ok();
+ }
+
+ ScopedAStatus insecurelyWritePayloadRpData(
+ const std::array<uint8_t, 32>& inputData) override {
+ int32_t ret = AVmPayload_writeRollbackProtectedSecret(inputData.data(), 32);
+ if (ret != 32) {
+ return ScopedAStatus::fromServiceSpecificError(ret);
+ }
+ return ScopedAStatus::ok();
+ }
+
ScopedAStatus quit() override { exit(0); }
};
auto testService = ndk::SharedRefBase::make<TestService>();
diff --git a/tests/testapk/src/native/testbinary.rs b/tests/testapk/src/native/testbinary.rs
index e479342..2b2fa28 100644
--- a/tests/testapk/src/native/testbinary.rs
+++ b/tests/testapk/src/native/testbinary.rs
@@ -132,6 +132,12 @@
fn readLineFromConsole(&self) -> BinderResult<String> {
unimplemented()
}
+ fn insecurelyReadPayloadRpData(&self) -> BinderResult<[u8; 32]> {
+ unimplemented()
+ }
+ fn insecurelyWritePayloadRpData(&self, _: &[u8; 32]) -> BinderResult<()> {
+ unimplemented()
+ }
}
fn unimplemented<T>() -> BinderResult<T> {