Keystore signing and verification test

After initially connecting to Keystore, continue to create a key to sign
and verify a message.

Bug: 190578423
Test: atest MicrodroidHostTestCases
Change-Id: I4ddd91839689c07ffa6b5f5396f025b1f0471653
diff --git a/microdroid/sepolicy/system/private/keystore2_key_contexts b/microdroid/sepolicy/system/private/keystore2_key_contexts
index 3833971..4e7c260 100644
--- a/microdroid/sepolicy/system/private/keystore2_key_contexts
+++ b/microdroid/sepolicy/system/private/keystore2_key_contexts
@@ -26,3 +26,7 @@
 # resume_on_reboot_key is a keystore2_key namespace intended for resume on reboot.
 120            u:object_r:resume_on_reboot_key:s0
 
+# vm_payload_key is a keystore2_key namespace intended for microdroid VM payloads.
+# TODO(b/191843770): sort out a longer term policy
+140            u:object_r:vm_payload_key:s0
+
diff --git a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
index f9794f7..54541c0 100644
--- a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
+++ b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
@@ -105,7 +105,7 @@
                 is("Hello Microdroid " + testLib + " arg1 arg2"));
 
         // Check that keystore was found by the payload
-        assertThat(runOnMicrodroid("getprop", "debug.microdroid.test_keystore"), is("PASS"));
+        assertThat(runOnMicrodroid("getprop", "debug.microdroid.test.keystore"), is("PASS"));
 
         // Shutdown microdroid
         runOnAndroid(VIRT_APEX + "bin/vm", "stop", cid);
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index 1122b25..f72d616 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -16,6 +16,7 @@
     srcs: ["src/native/*.cpp"],
     shared_libs: [
         "android.system.keystore2-V1-ndk_platform",
+        "libbase",
         "libbinder_ndk",
     ],
 }
diff --git a/tests/testapk/src/native/testbinary.cpp b/tests/testapk/src/native/testbinary.cpp
index 682ab2a..5510ae1 100644
--- a/tests/testapk/src/native/testbinary.cpp
+++ b/tests/testapk/src/native/testbinary.cpp
@@ -14,31 +14,171 @@
  * limitations under the License.
  */
 #include <aidl/android/system/keystore2/IKeystoreService.h>
+#include <android-base/result.h>
 #include <android/binder_auto_utils.h>
 #include <android/binder_manager.h>
 #include <stdio.h>
 #include <sys/system_properties.h>
 
+using aidl::android::hardware::security::keymint::Algorithm;
+using aidl::android::hardware::security::keymint::Digest;
+using aidl::android::hardware::security::keymint::KeyParameter;
+using aidl::android::hardware::security::keymint::KeyParameterValue;
+using aidl::android::hardware::security::keymint::KeyPurpose;
 using aidl::android::hardware::security::keymint::SecurityLevel;
+using aidl::android::hardware::security::keymint::Tag;
 
+using aidl::android::system::keystore2::CreateOperationResponse;
+using aidl::android::system::keystore2::Domain;
 using aidl::android::system::keystore2::IKeystoreSecurityLevel;
 using aidl::android::system::keystore2::IKeystoreService;
+using aidl::android::system::keystore2::KeyDescriptor;
+using aidl::android::system::keystore2::KeyMetadata;
+
+using android::base::Error;
+using android::base::Result;
 
 namespace {
 
-bool test_keystore() {
+Result<void> test_keystore() {
+    // Connect to Keystore.
     ndk::SpAIBinder binder(
             AServiceManager_getService("android.system.keystore2.IKeystoreService/default"));
     auto service = IKeystoreService::fromBinder(binder);
     if (service == nullptr) {
-        return false;
+        return Error() << "Failed to find Keystore";
     }
     std::shared_ptr<IKeystoreSecurityLevel> securityLevel;
     auto status = service->getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT, &securityLevel);
     if (!status.isOk()) {
-        return false;
+        return Error() << "Failed to get security level";
     }
-    return true;
+
+    // Create a signing key.
+    std::vector<KeyParameter> params;
+
+    KeyParameter algo;
+    algo.tag = Tag::ALGORITHM;
+    algo.value = KeyParameterValue::make<KeyParameterValue::algorithm>(Algorithm::HMAC);
+    params.push_back(algo);
+
+    KeyParameter key_size;
+    key_size.tag = Tag::KEY_SIZE;
+    key_size.value = KeyParameterValue::make<KeyParameterValue::integer>(256);
+    params.push_back(key_size);
+
+    KeyParameter min_mac_length;
+    min_mac_length.tag = Tag::MIN_MAC_LENGTH;
+    min_mac_length.value = KeyParameterValue::make<KeyParameterValue::integer>(256);
+    params.push_back(min_mac_length);
+
+    KeyParameter digest;
+    digest.tag = Tag::DIGEST;
+    digest.value = KeyParameterValue::make<KeyParameterValue::digest>(Digest::SHA_2_256);
+    params.push_back(digest);
+
+    KeyParameter purposeSign;
+    purposeSign.tag = Tag::PURPOSE;
+    purposeSign.value = KeyParameterValue::make<KeyParameterValue::keyPurpose>(KeyPurpose::SIGN);
+    params.push_back(purposeSign);
+
+    KeyParameter purposeVerify;
+    purposeVerify.tag = Tag::PURPOSE;
+    purposeVerify.value =
+            KeyParameterValue::make<KeyParameterValue::keyPurpose>(KeyPurpose::VERIFY);
+    params.push_back(purposeVerify);
+
+    KeyParameter auth;
+    auth.tag = Tag::NO_AUTH_REQUIRED;
+    auth.value = KeyParameterValue::make<KeyParameterValue::boolValue>(true);
+    params.push_back(auth);
+
+    KeyDescriptor descriptor;
+    descriptor.domain = Domain::SELINUX;
+    descriptor.alias = "payload-test-key";
+    descriptor.nspace = 140; // vm_payload_key
+
+    KeyMetadata metadata;
+    status = securityLevel->generateKey(descriptor, {}, params, 0, {}, &metadata);
+    if (!status.isOk()) {
+        return Error() << "Failed to create new HMAC key";
+    }
+
+    // Sign something.
+    params.clear();
+    params.push_back(algo);
+    params.push_back(digest);
+    params.push_back(purposeSign);
+
+    KeyParameter mac_length;
+    mac_length.tag = Tag::MAC_LENGTH;
+    mac_length.value = KeyParameterValue::make<KeyParameterValue::integer>(256);
+    params.push_back(mac_length);
+
+    CreateOperationResponse opResponse;
+    status = securityLevel->createOperation(descriptor, params, false, &opResponse);
+    if (!status.isOk()) {
+        return Error() << "Failed to create keystore signing operation: "
+                       << status.getServiceSpecificError();
+    }
+    auto operation = opResponse.iOperation;
+
+    std::string message = "This is the message to sign";
+    std::optional<std::vector<uint8_t>> out;
+    status = operation->update({message.begin(), message.end()}, &out);
+    if (!status.isOk()) {
+        return Error() << "Failed to call keystore update operation.";
+    }
+
+    std::optional<std::vector<uint8_t>> signature;
+    status = operation->finish({}, {}, &signature);
+    if (!status.isOk()) {
+        return Error() << "Failed to call keystore finish operation.";
+    }
+
+    if (!signature.has_value()) {
+        return Error() << "Didn't receive a signature from keystore finish operation.";
+    }
+
+    // Verify the signature.
+    params.clear();
+    params.push_back(algo);
+    params.push_back(digest);
+    params.push_back(purposeVerify);
+
+    status = securityLevel->createOperation(descriptor, params, false, &opResponse);
+    if (!status.isOk()) {
+        return Error() << "Failed to create keystore verification operation: "
+                       << status.getServiceSpecificError();
+    }
+    operation = opResponse.iOperation;
+
+    status = operation->update({message.begin(), message.end()}, &out);
+    if (!status.isOk()) {
+        return Error() << "Failed to call keystore update operation.";
+    }
+
+    std::optional<std::vector<uint8_t>> out_signature;
+    status = operation->finish({}, signature.value(), &out_signature);
+    if (!status.isOk()) {
+        return Error() << "Failed to call keystore finish operation.";
+    }
+
+    return {};
+}
+
+template <typename T>
+void report_test(std::string name, Result<T> result) {
+    auto property = "debug.microdroid.test." + name;
+    std::stringstream outcome;
+    if (result.ok()) {
+        outcome << "PASS";
+    } else {
+        outcome << "FAIL: " << result.error();
+        // Pollute stdout with the error in case the property is truncated.
+        std::cout << "[" << name << "] test failed: " << result.error() << "\n";
+    }
+    __system_property_set(property.c_str(), outcome.str().c_str());
 }
 
 } // Anonymous namespace
@@ -55,6 +195,6 @@
     printf("\n");
 
     __system_property_set("debug.microdroid.app.run", "true");
-    __system_property_set("debug.microdroid.test_keystore", test_keystore() ? "PASS" : "FAIL");
+    report_test("keystore", test_keystore());
     return 0;
 }