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;
}