Pass a VM secret to KeyMint from microdroid_manager
This secret will be used to protect the keyblobs so that only a VM that
gets the same secret will be able to use those blobs. It is held in a
system property so that it won't be lost should KeyMint happen to
restart and has SELinux rules to ensure only microdroid_manager can set
the value and only KeyMint can read the value.
Bug: 190578423
Test: atest MicrodroidHostTestCases
Change-Id: I675cc9d6e9942090a761b83a6b9456b5c9909747
diff --git a/microdroid/keymint/service.cpp b/microdroid/keymint/service.cpp
index 325e852..8e20f44 100644
--- a/microdroid/keymint/service.cpp
+++ b/microdroid/keymint/service.cpp
@@ -18,16 +18,84 @@
#include <AndroidKeyMintDevice.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/result.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
+#include <keymaster/android_keymaster_utils.h>
+#include <keymaster/mem.h>
#include <keymaster/soft_keymaster_logger.h>
+#include <openssl/digest.h>
+#include <openssl/hkdf.h>
+#include <openssl/is_boringssl.h>
+#include <openssl/sha.h>
#include "MicrodroidKeyMintDevice.h"
using aidl::android::hardware::security::keymint::MicrodroidKeyMintDevice;
using aidl::android::hardware::security::keymint::SecurityLevel;
+using android::base::Error;
+using android::base::GetProperty;
+using android::base::Result;
+
+using keymaster::KeymasterBlob;
+using keymaster::KeymasterKeyBlob;
+using keymaster::memset_s;
+
+namespace {
+
+template <typename T, class... Args>
+std::shared_ptr<T> addService(Args&&... args) {
+ std::shared_ptr<T> ser = ndk::SharedRefBase::make<T>(std::forward<Args>(args)...);
+ auto instanceName = std::string(T::descriptor) + "/default";
+ LOG(INFO) << "adding keymint service instance: " << instanceName;
+ binder_status_t status =
+ AServiceManager_addService(ser->asBinder().get(), instanceName.c_str());
+ CHECK(status == STATUS_OK);
+ return ser;
+}
+
+Result<KeymasterKeyBlob> getRootKey() {
+ const std::string prop = "ro.vmsecret.keymint";
+ const std::chrono::seconds timeout(15);
+ while (!android::base::WaitForPropertyCreation(prop, timeout)) {
+ LOG(WARNING) << "waited " << timeout.count() << "seconds for " << prop
+ << ", still waiting...";
+ }
+
+ // In a small effort to avoid spreading the secret around too widely in
+ // memory, move the secert into a buffer that will wipe itself and clear
+ // the original string.
+ std::string secretProp = GetProperty(prop, "");
+ KeymasterBlob secret(reinterpret_cast<const uint8_t*>(secretProp.data()), secretProp.size());
+ memset_s(secretProp.data(), 0, secretProp.size());
+ if (secret.size() < 64u) return Error() << "secret is too small";
+
+ // Derive the root key from the secret to avoid getting locked into using
+ // the secret directly.
+ KeymasterKeyBlob rootKey(SHA512_DIGEST_LENGTH);
+ const uint8_t kRootKeyIkm[] = "keymint_root_key";
+ const uint8_t* kNoSalt = nullptr;
+ const size_t kNoSaltLen = 0;
+ if (!HKDF(rootKey.writable_data(), rootKey.size(), EVP_sha512(), (uint8_t*)secret.begin(),
+ secret.size(), kNoSalt, kNoSaltLen, kRootKeyIkm, sizeof(kRootKeyIkm))) {
+ return Error() << "Failed to derive a key";
+ }
+ if (rootKey.size() < 64u) return Error() << "root key is too small";
+
+ LOG(INFO) << "root key obtained";
+ return rootKey;
+}
+
+} // namespace
+
int main() {
+ auto rootKey = getRootKey();
+ if (!rootKey.ok()) {
+ LOG(FATAL) << "Failed to get root key: " << rootKey.error();
+ }
+
// Zero threads seems like a useless pool, but below we'll join this thread
// to it, increasing the pool size to 1.
ABinderProcess_setThreadPoolMaxThreadCount(0);