Merge "Separate updateAad from update & other cleanups"
diff --git a/keystore-engine/Android.bp b/keystore-engine/Android.bp
index b8127d3..9980765 100644
--- a/keystore-engine/Android.bp
+++ b/keystore-engine/Android.bp
@@ -27,6 +27,7 @@
srcs: [
"android_engine.cpp",
"keystore_backend_binder.cpp",
+ "keystore2_engine.cpp",
],
cflags: [
@@ -36,7 +37,9 @@
],
shared_libs: [
+ "android.system.keystore2-V1-ndk_platform",
"libbinder",
+ "libbinder_ndk",
"libcrypto",
"libcutils",
"libhidlbase",
@@ -58,6 +61,7 @@
srcs: [
"android_engine.cpp",
"keystore_backend_hidl.cpp",
+ "keystore2_engine.cpp",
],
cflags: [
@@ -68,7 +72,10 @@
],
shared_libs: [
+ "android.system.keystore2-V1-ndk_platform",
"android.system.wifi.keystore@1.0",
+ "libbase",
+ "libbinder_ndk",
"libcrypto",
"liblog",
"libhidlbase",
diff --git a/keystore-engine/android_engine.cpp b/keystore-engine/android_engine.cpp
index e3525b2..5881523 100644
--- a/keystore-engine/android_engine.cpp
+++ b/keystore-engine/android_engine.cpp
@@ -23,10 +23,7 @@
#define LOG_TAG "keystore-engine"
#include <pthread.h>
-#include <sys/socket.h>
-#include <stdarg.h>
#include <string.h>
-#include <unistd.h>
#include <log/log.h>
@@ -41,6 +38,8 @@
#include <memory>
+#include "keystore2_engine.h"
+
#ifndef BACKEND_WIFI_HIDL
#include "keystore_backend_binder.h"
#else
@@ -335,6 +334,10 @@
EVP_PKEY* EVP_PKEY_from_keystore(const char* key_id) {
ALOGV("EVP_PKEY_from_keystore(\"%s\")", key_id);
+ if (auto ks2_key = EVP_PKEY_from_keystore2(key_id)) {
+ return ks2_key;
+ }
+
ensure_keystore_engine();
uint8_t *pubkey = nullptr;
diff --git a/keystore-engine/keystore2_engine.cpp b/keystore-engine/keystore2_engine.cpp
new file mode 100644
index 0000000..8d25f48
--- /dev/null
+++ b/keystore-engine/keystore2_engine.cpp
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "keystore2_engine.h"
+
+#include <aidl/android/system/keystore2/IKeystoreService.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android/binder_manager.h>
+
+#include <private/android_filesystem_config.h>
+
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+#include <openssl/ec_key.h>
+#include <openssl/ecdsa.h>
+#include <openssl/engine.h>
+#include <openssl/rsa.h>
+#include <openssl/x509.h>
+
+#define AT __func__ << ":" << __LINE__ << " "
+
+constexpr const char keystore2_service_name[] = "android.system.keystore2";
+const std::string keystore2_grant_id_prefix("ks2_keystore-engine_grant_id:");
+
+/**
+ * Keystore 2.0 namespace identifiers.
+ * Keep in sync with system/sepolicy/private/keystore2_key_contexts.
+ */
+constexpr const int64_t KS2_NAMESPACE_WIFI = 102;
+
+namespace ks2 = ::aidl::android::system::keystore2;
+namespace KMV1 = ::aidl::android::hardware::security::keymint;
+
+namespace {
+
+int64_t getNamespaceforCurrentUid() {
+ auto uid = getuid();
+ switch (uid) {
+ case AID_WIFI:
+ return KS2_NAMESPACE_WIFI;
+ // 0 is the super user namespace, and nothing has access to this namespace on user builds.
+ // So this will always fail.
+ default:
+ return 0;
+ }
+}
+
+struct Keystore2KeyBackend {
+ ks2::KeyDescriptor descriptor_;
+ std::shared_ptr<ks2::IKeystoreSecurityLevel> i_keystore_security_level_;
+};
+
+/* key_backend_dup is called when one of the RSA or EC_KEY objects is duplicated. */
+extern "C" int key_backend_dup(CRYPTO_EX_DATA* /* to */, const CRYPTO_EX_DATA* /* from */,
+ void** from_d, int /* index */, long /* argl */, void* /* argp */) {
+ auto key_backend = reinterpret_cast<std::shared_ptr<Keystore2KeyBackend>*>(*from_d);
+ if (key_backend != nullptr) {
+ *from_d = new std::shared_ptr<Keystore2KeyBackend>(*key_backend);
+ }
+ return 1;
+}
+
+/* key_backend_free is called when one of the RSA, DSA or EC_KEY object is freed. */
+extern "C" void key_backend_free(void* /* parent */, void* ptr, CRYPTO_EX_DATA* /* ad */,
+ int /* index */, long /* argl */, void* /* argp */) {
+ delete reinterpret_cast<std::shared_ptr<Keystore2KeyBackend>*>(ptr);
+}
+
+extern "C" int rsa_private_transform(RSA* rsa, uint8_t* out, const uint8_t* in, size_t len);
+extern "C" int ecdsa_sign(const uint8_t* digest, size_t digest_len, uint8_t* sig,
+ unsigned int* sig_len, EC_KEY* ec_key);
+/* KeystoreEngine is a BoringSSL ENGINE that implements RSA and ECDSA by
+ * forwarding the requested operations to Keystore. */
+class Keystore2Engine {
+ public:
+ Keystore2Engine()
+ : rsa_index_(RSA_get_ex_new_index(0 /* argl */, nullptr /* argp */, nullptr /* new_func */,
+ key_backend_dup, key_backend_free)),
+ ec_key_index_(EC_KEY_get_ex_new_index(0 /* argl */, nullptr /* argp */,
+ nullptr /* new_func */, key_backend_dup,
+ key_backend_free)),
+ engine_(ENGINE_new()) {
+ memset(&rsa_method_, 0, sizeof(rsa_method_));
+ rsa_method_.common.is_static = 1;
+ rsa_method_.private_transform = rsa_private_transform;
+ rsa_method_.flags = RSA_FLAG_OPAQUE;
+ ENGINE_set_RSA_method(engine_, &rsa_method_, sizeof(rsa_method_));
+
+ memset(&ecdsa_method_, 0, sizeof(ecdsa_method_));
+ ecdsa_method_.common.is_static = 1;
+ ecdsa_method_.sign = ecdsa_sign;
+ ecdsa_method_.flags = ECDSA_FLAG_OPAQUE;
+ ENGINE_set_ECDSA_method(engine_, &ecdsa_method_, sizeof(ecdsa_method_));
+ }
+
+ int rsa_ex_index() const { return rsa_index_; }
+ int ec_key_ex_index() const { return ec_key_index_; }
+
+ const ENGINE* engine() const { return engine_; }
+
+ static const Keystore2Engine& get() {
+ static Keystore2Engine engine;
+ return engine;
+ }
+
+ private:
+ const int rsa_index_;
+ const int ec_key_index_;
+ RSA_METHOD rsa_method_;
+ ECDSA_METHOD ecdsa_method_;
+ ENGINE* const engine_;
+};
+
+#define OWNERSHIP_TRANSFERRED(x) x.release()
+
+/* wrap_rsa returns an |EVP_PKEY| that contains an RSA key where the public
+ * part is taken from |public_rsa| and the private operations are forwarded to
+ * KeyStore and operate on the key named |key_id|. */
+bssl::UniquePtr<EVP_PKEY> wrap_rsa(std::shared_ptr<Keystore2KeyBackend> key_backend,
+ const RSA* public_rsa) {
+ bssl::UniquePtr<RSA> rsa(RSA_new_method(Keystore2Engine::get().engine()));
+ if (rsa.get() == nullptr) {
+ return nullptr;
+ }
+
+ auto key_backend_copy = new decltype(key_backend)(key_backend);
+
+ if (!RSA_set_ex_data(rsa.get(), Keystore2Engine::get().rsa_ex_index(), key_backend_copy)) {
+ delete key_backend_copy;
+ return nullptr;
+ }
+
+ rsa->n = BN_dup(public_rsa->n);
+ rsa->e = BN_dup(public_rsa->e);
+ if (rsa->n == nullptr || rsa->e == nullptr) {
+ return nullptr;
+ }
+
+ bssl::UniquePtr<EVP_PKEY> result(EVP_PKEY_new());
+ if (result.get() == nullptr || !EVP_PKEY_assign_RSA(result.get(), rsa.get())) {
+ return nullptr;
+ }
+ OWNERSHIP_TRANSFERRED(rsa);
+
+ return result;
+}
+
+/* wrap_ecdsa returns an |EVP_PKEY| that contains an ECDSA key where the public
+ * part is taken from |public_rsa| and the private operations are forwarded to
+ * KeyStore and operate on the key named |key_id|. */
+bssl::UniquePtr<EVP_PKEY> wrap_ecdsa(std::shared_ptr<Keystore2KeyBackend> key_backend,
+ const EC_KEY* public_ecdsa) {
+ bssl::UniquePtr<EC_KEY> ec(EC_KEY_new_method(Keystore2Engine::get().engine()));
+ if (ec.get() == nullptr) {
+ return nullptr;
+ }
+
+ if (!EC_KEY_set_group(ec.get(), EC_KEY_get0_group(public_ecdsa)) ||
+ !EC_KEY_set_public_key(ec.get(), EC_KEY_get0_public_key(public_ecdsa))) {
+ return nullptr;
+ }
+
+ auto key_backend_copy = new decltype(key_backend)(key_backend);
+
+ if (!EC_KEY_set_ex_data(ec.get(), Keystore2Engine::get().ec_key_ex_index(), key_backend_copy)) {
+ delete key_backend_copy;
+ return nullptr;
+ }
+
+ bssl::UniquePtr<EVP_PKEY> result(EVP_PKEY_new());
+ if (result.get() == nullptr || !EVP_PKEY_assign_EC_KEY(result.get(), ec.get())) {
+ return nullptr;
+ }
+ OWNERSHIP_TRANSFERRED(ec);
+
+ return result;
+}
+
+std::optional<std::vector<uint8_t>> keystore2_sign(const Keystore2KeyBackend& key_backend,
+ std::vector<uint8_t> input,
+ KMV1::Algorithm algorithm) {
+ auto sec_level = key_backend.i_keystore_security_level_;
+ ks2::CreateOperationResponse response;
+
+ std::vector<KMV1::KeyParameter> op_params(4);
+ op_params[0] = KMV1::KeyParameter{
+ .tag = KMV1::Tag::PURPOSE,
+ .value = KMV1::KeyParameterValue::make<KMV1::KeyParameterValue::keyPurpose>(
+ KMV1::KeyPurpose::SIGN)};
+ op_params[1] = KMV1::KeyParameter{
+ .tag = KMV1::Tag::ALGORITHM,
+ .value = KMV1::KeyParameterValue::make<KMV1::KeyParameterValue::algorithm>(algorithm)};
+ op_params[2] = KMV1::KeyParameter{
+ .tag = KMV1::Tag::PADDING,
+ .value = KMV1::KeyParameterValue::make<KMV1::KeyParameterValue::paddingMode>(
+ KMV1::PaddingMode::NONE)};
+ op_params[3] =
+ KMV1::KeyParameter{.tag = KMV1::Tag::DIGEST,
+ .value = KMV1::KeyParameterValue::make<KMV1::KeyParameterValue::digest>(
+ KMV1::Digest::NONE)};
+
+ auto rc = sec_level->createOperation(key_backend.descriptor_, op_params, false /* forced */,
+ &response);
+ if (!rc.isOk()) {
+ auto exception_code = rc.getExceptionCode();
+ if (exception_code == EX_SERVICE_SPECIFIC) {
+ LOG(ERROR) << AT << "Keystore createOperation returned service specific error: "
+ << rc.getServiceSpecificError();
+ } else {
+ LOG(ERROR) << AT << "Communication with Keystore createOperation failed error: "
+ << exception_code;
+ }
+ return std::nullopt;
+ }
+
+ auto op = response.iOperation;
+
+ std::optional<std::vector<uint8_t>> output = std::nullopt;
+ rc = op->finish(std::move(input), {}, &output);
+ if (!rc.isOk()) {
+ auto exception_code = rc.getExceptionCode();
+ if (exception_code == EX_SERVICE_SPECIFIC) {
+ LOG(ERROR) << AT << "Keystore finish returned service specific error: "
+ << rc.getServiceSpecificError();
+ } else {
+ LOG(ERROR) << AT
+ << "Communication with Keystore finish failed error: " << exception_code;
+ }
+ return std::nullopt;
+ }
+
+ if (!output) {
+ LOG(ERROR) << AT << "We did not get a signature from Keystore.";
+ }
+
+ return output;
+}
+
+/* rsa_private_transform takes a big-endian integer from |in|, calculates the
+ * d'th power of it, modulo the RSA modulus, and writes the result as a
+ * big-endian integer to |out|. Both |in| and |out| are |len| bytes long. It
+ * returns one on success and zero otherwise. */
+extern "C" int rsa_private_transform(RSA* rsa, uint8_t* out, const uint8_t* in, size_t len) {
+ auto key_backend = reinterpret_cast<std::shared_ptr<Keystore2KeyBackend>*>(
+ RSA_get_ex_data(rsa, Keystore2Engine::get().rsa_ex_index()));
+
+ if (key_backend == nullptr) {
+ LOG(ERROR) << AT << "Invalid key.";
+ return 0;
+ }
+
+ auto output =
+ keystore2_sign(**key_backend, std::vector<uint8_t>(in, in + len), KMV1::Algorithm::RSA);
+ if (!output) {
+ return 0;
+ }
+
+ if (output->size() > len) {
+ /* The result of the RSA operation can never be larger than the size of
+ * the modulus so we assume that the result has extra zeros on the
+ * left. This provides attackers with an oracle, but there's nothing
+ * that we can do about it here. */
+ LOG(WARNING) << "Reply len " << output->size() << " greater than expected " << len;
+ memcpy(out, &output->data()[output->size() - len], len);
+ } else if (output->size() < len) {
+ /* If the Keystore implementation returns a short value we assume that
+ * it's because it removed leading zeros from the left side. This is
+ * bad because it provides attackers with an oracle but we cannot do
+ * anything about a broken Keystore implementation here. */
+ LOG(WARNING) << "Reply len " << output->size() << " less than expected " << len;
+ memset(out, 0, len);
+ memcpy(out + len - output->size(), output->data(), output->size());
+ } else {
+ memcpy(out, output->data(), len);
+ }
+
+ return 1;
+}
+
+/* ecdsa_sign signs |digest_len| bytes from |digest| with |ec_key| and writes
+ * the resulting signature (an ASN.1 encoded blob) to |sig|. It returns one on
+ * success and zero otherwise. */
+extern "C" int ecdsa_sign(const uint8_t* digest, size_t digest_len, uint8_t* sig,
+ unsigned int* sig_len, EC_KEY* ec_key) {
+ auto key_backend = reinterpret_cast<std::shared_ptr<Keystore2KeyBackend>*>(
+ EC_KEY_get_ex_data(ec_key, Keystore2Engine::get().ec_key_ex_index()));
+
+ if (key_backend == nullptr) {
+ LOG(ERROR) << AT << "Invalid key.";
+ return 0;
+ }
+
+ size_t ecdsa_size = ECDSA_size(ec_key);
+
+ auto output = keystore2_sign(**key_backend, std::vector<uint8_t>(digest, digest + digest_len),
+ KMV1::Algorithm::EC);
+ if (!output) {
+ LOG(ERROR) << "There was an error during ecdsa_sign.";
+ return 0;
+ }
+
+ if (output->size() == 0) {
+ LOG(ERROR) << "No valid signature returned";
+ return 0;
+ } else if (output->size() > ecdsa_size) {
+ LOG(ERROR) << "Signature is too large";
+ return 0;
+ }
+
+ memcpy(sig, output->data(), output->size());
+ *sig_len = output->size();
+
+ return 1;
+}
+
+} // namespace
+
+/* EVP_PKEY_from_keystore returns an |EVP_PKEY| that contains either an RSA or
+ * ECDSA key where the public part of the key reflects the value of the key
+ * named |key_id| in Keystore and the private operations are forwarded onto
+ * KeyStore. */
+extern "C" EVP_PKEY* EVP_PKEY_from_keystore2(const char* key_id) {
+ ::ndk::SpAIBinder keystoreBinder(AServiceManager_checkService(keystore2_service_name));
+ auto keystore2 = ks2::IKeystoreService::fromBinder(keystoreBinder);
+
+ if (!keystore2) {
+ LOG(ERROR) << AT << "Unable to connect to Keystore 2.0.";
+ return nullptr;
+ }
+
+ std::string alias = key_id;
+ if (android::base::StartsWith(alias, "USRPKEY_")) {
+ LOG(WARNING) << AT << "Keystore backend used with legacy alias prefix - ignoring.";
+ alias = alias.substr(8);
+ }
+
+ ks2::KeyDescriptor descriptor = {
+ .domain = ks2::Domain::SELINUX,
+ .nspace = getNamespaceforCurrentUid(),
+ .alias = alias,
+ .blob = std::nullopt,
+ };
+
+ // If the key_id starts with the grant id prefix, we parse the following string as numeric
+ // grant id. We can then use the grant domain without alias to load the designated key.
+ if (alias.find(keystore2_grant_id_prefix) == 0) {
+ std::stringstream s(alias.substr(keystore2_grant_id_prefix.size()));
+ s >> std::hex >> reinterpret_cast<uint64_t&>(descriptor.nspace);
+ descriptor.domain = ks2::Domain::GRANT;
+ descriptor.alias = std::nullopt;
+ }
+
+ ks2::KeyEntryResponse response;
+ auto rc = keystore2->getKeyEntry(descriptor, &response);
+ if (!rc.isOk()) {
+ auto exception_code = rc.getExceptionCode();
+ if (exception_code == EX_SERVICE_SPECIFIC) {
+ LOG(ERROR) << AT << "Keystore getKeyEntry returned service specific error: "
+ << rc.getServiceSpecificError();
+ } else {
+ LOG(ERROR) << AT << "Communication with Keystore getKeyEntry failed error: "
+ << exception_code;
+ }
+ return nullptr;
+ }
+
+ if (!response.metadata.certificate) {
+ LOG(ERROR) << AT << "No public key found.";
+ return nullptr;
+ }
+
+ const uint8_t* p = response.metadata.certificate->data();
+ bssl::UniquePtr<X509> x509(d2i_X509(nullptr, &p, response.metadata.certificate->size()));
+ if (!x509) {
+ LOG(ERROR) << AT << "Failed to parse x509 certificate.";
+ return nullptr;
+ }
+ bssl::UniquePtr<EVP_PKEY> pkey(X509_get_pubkey(x509.get()));
+ if (!pkey) {
+ LOG(ERROR) << AT << "Failed to extract public key.";
+ return nullptr;
+ }
+
+ auto key_backend = std::make_shared<Keystore2KeyBackend>(
+ Keystore2KeyBackend{response.metadata.key, response.iSecurityLevel});
+
+ bssl::UniquePtr<EVP_PKEY> result;
+ switch (EVP_PKEY_type(pkey->type)) {
+ case EVP_PKEY_RSA: {
+ bssl::UniquePtr<RSA> public_rsa(EVP_PKEY_get1_RSA(pkey.get()));
+ result = wrap_rsa(key_backend, public_rsa.get());
+ break;
+ }
+ case EVP_PKEY_EC: {
+ bssl::UniquePtr<EC_KEY> public_ecdsa(EVP_PKEY_get1_EC_KEY(pkey.get()));
+ result = wrap_ecdsa(key_backend, public_ecdsa.get());
+ break;
+ }
+ default:
+ LOG(ERROR) << AT << "Unsupported key type " << EVP_PKEY_type(pkey->type);
+ return nullptr;
+ }
+
+ return result.release();
+}
diff --git a/keystore-engine/keystore2_engine.h b/keystore-engine/keystore2_engine.h
new file mode 100644
index 0000000..a8381d9
--- /dev/null
+++ b/keystore-engine/keystore2_engine.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <openssl/evp.h>
+
+extern "C" EVP_PKEY* EVP_PKEY_from_keystore2(const char* key_id);
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index 812d5e6..aaa5659 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -42,6 +42,7 @@
"libkeystore2_crypto_rust",
"libkeystore2_km_compat",
"libkeystore2_selinux",
+ "libkeystore2_vintf_rust",
"liblazy_static",
"liblibc",
"liblibsqlite3_sys",
@@ -87,6 +88,7 @@
"libkeystore2_km_compat",
"libkeystore2_selinux",
"libkeystore2_test_utils",
+ "libkeystore2_vintf_rust",
"liblazy_static",
"liblibc",
"liblibsqlite3_sys",
diff --git a/keystore2/aidl/Android.bp b/keystore2/aidl/Android.bp
index d529fa9..c92417b 100644
--- a/keystore2/aidl/Android.bp
+++ b/keystore2/aidl/Android.bp
@@ -118,6 +118,9 @@
aidl_interface {
name: "android.security.usermanager",
srcs: [ "android/security/usermanager/*.aidl" ],
+ imports: [
+ "android.system.keystore2",
+ ],
unstable: true,
backend: {
java: {
diff --git a/keystore2/aidl/android/security/usermanager/IKeystoreUserManager.aidl b/keystore2/aidl/android/security/usermanager/IKeystoreUserManager.aidl
index 3690b1c..83edb1a 100644
--- a/keystore2/aidl/android/security/usermanager/IKeystoreUserManager.aidl
+++ b/keystore2/aidl/android/security/usermanager/IKeystoreUserManager.aidl
@@ -14,11 +14,14 @@
package android.security.usermanager;
+import android.system.keystore2.Domain;
+
// TODO: mark the interface with @SensitiveData when the annotation is ready (b/176110256).
/**
* IKeystoreUserManager interface exposes the methods for adding/removing users and changing the
* user's password.
+ * @hide
*/
interface IKeystoreUserManager {
@@ -31,6 +34,7 @@
* user id.
*
* @param userId - Android user id
+ * @hide
*/
void onUserAdded(in int userId);
@@ -42,6 +46,7 @@
* `ResponseCode::SYSTEM_ERROR` - if failed to delete the keys of the user being deleted.
*
* @param userId - Android user id
+ * @hide
*/
void onUserRemoved(in int userId);
@@ -56,6 +61,18 @@
*
* @param userId - Android user id
* @param password - a secret derived from the synthetic password of the user
+ * @hide
*/
void onUserPasswordChanged(in int userId, in @nullable byte[] password);
+
+ /**
+ * This function deletes all keys within a namespace. It mainly gets called when an app gets
+ * removed and all resources of this app need to be cleaned up.
+ *
+ * @param domain - One of Domain.APP or Domain.SELINUX.
+ * @param nspace - The UID of the app that is to be cleared if domain is Domain.APP or
+ * the SEPolicy namespace if domain is Domain.SELINUX.
+ * @hide
+ */
+ void clearNamespace(Domain domain, long nspace);
}
diff --git a/keystore2/keystore2.rc b/keystore2/keystore2.rc
index c5fc72a..2d1f05a 100644
--- a/keystore2/keystore2.rc
+++ b/keystore2/keystore2.rc
@@ -8,13 +8,12 @@
# Start Keystore 2 conditionally
# TODO b/171563717 Remove when Keystore 2 migration is complete.
-on nonencrypted && property:persist.android.security.keystore2.enable=true
+on property:persist.android.security.keystore2.enable=true
enable keystore2
service keystore2 /system/bin/keystore2 /data/misc/keystore
- class main
+ class early_hal
user keystore
group keystore readproc log
writepid /dev/cpuset/foreground/tasks
- # TODO b/171563717 Remove when Keystore 2 migration is complete.
disabled
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index db06bff..40860be 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -2522,6 +2522,51 @@
.context("In get_key_km_uuid.")
}
+ /// Delete all artifacts belonging to the namespace given by the domain-namespace tuple.
+ /// This leaves all of the blob entries orphaned for subsequent garbage collection.
+ pub fn unbind_keys_for_namespace(&mut self, domain: Domain, namespace: i64) -> Result<()> {
+ if !(domain == Domain::APP || domain == Domain::SELINUX) {
+ return Err(KsError::Rc(ResponseCode::INVALID_ARGUMENT))
+ .context("In unbind_keys_for_namespace.");
+ }
+ self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ tx.execute(
+ "DELETE FROM persistent.keymetadata
+ WHERE keyentryid IN (
+ SELECT id FROM persistent.keyentry
+ WHERE domain = ? AND namespace = ?
+ );",
+ params![domain.0, namespace],
+ )
+ .context("Trying to delete keymetadata.")?;
+ tx.execute(
+ "DELETE FROM persistent.keyparameter
+ WHERE keyentryid IN (
+ SELECT id FROM persistent.keyentry
+ WHERE domain = ? AND namespace = ?
+ );",
+ params![domain.0, namespace],
+ )
+ .context("Trying to delete keyparameters.")?;
+ tx.execute(
+ "DELETE FROM persistent.grant
+ WHERE keyentryid IN (
+ SELECT id FROM persistent.keyentry
+ WHERE domain = ? AND namespace = ?
+ );",
+ params![domain.0, namespace],
+ )
+ .context("Trying to delete grants.")?;
+ tx.execute(
+ "DELETE FROM persistent.keyentry WHERE domain = ? AND namespace = ?;",
+ params![domain.0, namespace],
+ )
+ .context("Trying to delete keyentry.")?;
+ Ok(()).need_gc()
+ })
+ .context("In unbind_keys_for_namespace")
+ }
+
/// Delete the keys created on behalf of the user, denoted by the user id.
/// Delete all the keys unless 'keep_non_super_encrypted_keys' set to true.
/// Returned boolean is to hint the garbage collector to delete the unbound keys.
diff --git a/keystore2/src/globals.rs b/keystore2/src/globals.rs
index 9668ee3..e1b41b5 100644
--- a/keystore2/src/globals.rs
+++ b/keystore2/src/globals.rs
@@ -35,6 +35,7 @@
use android_hardware_security_keymint::binder::{StatusCode, Strong};
use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
use anyhow::{Context, Result};
+use keystore2_vintf::get_aidl_instances;
use lazy_static::lazy_static;
use std::sync::{Arc, Mutex};
use std::{cell::RefCell, sync::Once};
@@ -175,38 +176,49 @@
/// If no native KeyMint device can be found this function also brings
/// up the compatibility service and attempts to connect to the legacy wrapper.
fn connect_keymint(security_level: &SecurityLevel) -> Result<(Asp, KeyMintHardwareInfo)> {
+ let keymint_instances =
+ get_aidl_instances("android.hardware.security.keymint", 1, "IKeyMintDevice");
+
let service_name = match *security_level {
- SecurityLevel::TRUSTED_ENVIRONMENT => format!("{}/default", KEYMINT_SERVICE_NAME),
- SecurityLevel::STRONGBOX => format!("{}/strongbox", KEYMINT_SERVICE_NAME),
+ SecurityLevel::TRUSTED_ENVIRONMENT => {
+ if keymint_instances.as_vec()?.iter().any(|instance| *instance == "default") {
+ Some(format!("{}/default", KEYMINT_SERVICE_NAME))
+ } else {
+ None
+ }
+ }
+ SecurityLevel::STRONGBOX => {
+ if keymint_instances.as_vec()?.iter().any(|instance| *instance == "strongbox") {
+ Some(format!("{}/strongbox", KEYMINT_SERVICE_NAME))
+ } else {
+ None
+ }
+ }
_ => {
return Err(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
.context("In connect_keymint.")
}
};
- let keymint = map_binder_status_code(binder::get_interface(&service_name))
- .context("In connect_keymint: Trying to connect to genuine KeyMint service.")
- .or_else(|e| {
- match e.root_cause().downcast_ref::<Error>() {
- Some(Error::BinderTransaction(StatusCode::NAME_NOT_FOUND)) => {
- // This is a no-op if it was called before.
- keystore2_km_compat::add_keymint_device_service();
+ let keymint = if let Some(service_name) = service_name {
+ map_binder_status_code(binder::get_interface(&service_name))
+ .context("In connect_keymint: Trying to connect to genuine KeyMint service.")
+ } else {
+ // This is a no-op if it was called before.
+ keystore2_km_compat::add_keymint_device_service();
- let keystore_compat_service: Strong<dyn IKeystoreCompatService> =
- map_binder_status_code(binder::get_interface("android.security.compat"))
- .context("In connect_keymint: Trying to connect to compat service.")?;
- map_binder_status(keystore_compat_service.getKeyMintDevice(*security_level))
- .map_err(|e| match e {
- Error::BinderTransaction(StatusCode::NAME_NOT_FOUND) => {
- Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)
- }
- e => e,
- })
- .context("In connect_keymint: Trying to get Legacy wrapper.")
+ let keystore_compat_service: Strong<dyn IKeystoreCompatService> =
+ map_binder_status_code(binder::get_interface("android.security.compat"))
+ .context("In connect_keymint: Trying to connect to compat service.")?;
+ map_binder_status(keystore_compat_service.getKeyMintDevice(*security_level))
+ .map_err(|e| match e {
+ Error::BinderTransaction(StatusCode::NAME_NOT_FOUND) => {
+ Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)
}
- _ => Err(e),
- }
- })?;
+ e => e,
+ })
+ .context("In connect_keymint: Trying to get Legacy wrapper.")
+ }?;
let hw_info = map_km_error(keymint.getHardwareInfo())
.context("In connect_keymint: Failed to get hardware info.")?;
@@ -250,33 +262,33 @@
/// If no native SecureClock device can be found brings up the compatibility service and attempts
/// to connect to the legacy wrapper.
fn connect_secureclock() -> Result<Asp> {
- let secureclock = map_binder_status_code(binder::get_interface(TIME_STAMP_SERVICE_NAME))
- .context("In connect_secureclock: Trying to connect to genuine secure clock service.")
- .or_else(|e| {
- match e.root_cause().downcast_ref::<Error>() {
- Some(Error::BinderTransaction(StatusCode::NAME_NOT_FOUND)) => {
- // This is a no-op if it was called before.
- keystore2_km_compat::add_keymint_device_service();
+ let secureclock_instances =
+ get_aidl_instances("android.hardware.security.secureclock", 1, "ISecureClock");
- let keystore_compat_service: Strong<dyn IKeystoreCompatService> =
- map_binder_status_code(binder::get_interface("android.security.compat"))
- .context(
- "In connect_secureclock: Trying to connect to compat service.",
- )?;
+ let secure_clock_available =
+ secureclock_instances.as_vec()?.iter().any(|instance| *instance == "default");
- // Legacy secure clock services were only implemented by TEE.
- map_binder_status(keystore_compat_service.getSecureClock())
- .map_err(|e| match e {
- Error::BinderTransaction(StatusCode::NAME_NOT_FOUND) => {
- Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)
- }
- e => e,
- })
- .context("In connect_secureclock: Trying to get Legacy wrapper.")
+ let secureclock = if secure_clock_available {
+ map_binder_status_code(binder::get_interface(TIME_STAMP_SERVICE_NAME))
+ .context("In connect_secureclock: Trying to connect to genuine secure clock service.")
+ } else {
+ // This is a no-op if it was called before.
+ keystore2_km_compat::add_keymint_device_service();
+
+ let keystore_compat_service: Strong<dyn IKeystoreCompatService> =
+ map_binder_status_code(binder::get_interface("android.security.compat"))
+ .context("In connect_secureclock: Trying to connect to compat service.")?;
+
+ // Legacy secure clock services were only implemented by TEE.
+ map_binder_status(keystore_compat_service.getSecureClock())
+ .map_err(|e| match e {
+ Error::BinderTransaction(StatusCode::NAME_NOT_FOUND) => {
+ Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)
}
- _ => Err(e),
- }
- })?;
+ e => e,
+ })
+ .context("In connect_secureclock: Trying to get Legacy wrapper.")
+ }?;
Ok(Asp::new(secureclock.as_binder()))
}
@@ -298,18 +310,28 @@
"android.hardware.security.keymint.IRemotelyProvisionedComponent";
fn connect_remotely_provisioned_component(security_level: &SecurityLevel) -> Result<Asp> {
+ let remotely_prov_instances =
+ get_aidl_instances("android.hardware.security.keymint", 1, "IRemotelyProvisionedComponent");
+
let service_name = match *security_level {
SecurityLevel::TRUSTED_ENVIRONMENT => {
- format!("{}/default", REMOTE_PROVISIONING_HAL_SERVICE_NAME)
+ if remotely_prov_instances.as_vec()?.iter().any(|instance| *instance == "default") {
+ Some(format!("{}/default", REMOTE_PROVISIONING_HAL_SERVICE_NAME))
+ } else {
+ None
+ }
}
- SecurityLevel::STRONGBOX => format!("{}/strongbox", REMOTE_PROVISIONING_HAL_SERVICE_NAME),
- _ => {
- // Given the integration of IRemotelyProvisionedComponent with KeyMint, it is reasonable
- // to return HARDWARE_TYPE_UNAVAILABLE as a Km error if it cannot be found.
- return Err(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
- .context("In connect_remotely_provisioned_component.");
+ SecurityLevel::STRONGBOX => {
+ if remotely_prov_instances.as_vec()?.iter().any(|instance| *instance == "strongbox") {
+ Some(format!("{}/strongbox", REMOTE_PROVISIONING_HAL_SERVICE_NAME))
+ } else {
+ None
+ }
}
- };
+ _ => None,
+ }
+ .ok_or(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
+ .context("In connect_remotely_provisioned_component.")?;
let rem_prov_hal: Strong<dyn IRemotelyProvisionedComponent> =
map_binder_status_code(binder::get_interface(&service_name))
diff --git a/keystore2/src/legacy_migrator.rs b/keystore2/src/legacy_migrator.rs
index 9ffe86c..1ae8719 100644
--- a/keystore2/src/legacy_migrator.rs
+++ b/keystore2/src/legacy_migrator.rs
@@ -362,11 +362,18 @@
}
}
- /// Deletes all keys belonging to the given uid, migrating them into the database
+ /// Deletes all keys belonging to the given namespace, migrating them into the database
/// for subsequent garbage collection if necessary.
- pub fn bulk_delete_uid(&self, uid: u32, keep_non_super_encrypted_keys: bool) -> Result<()> {
+ pub fn bulk_delete_uid(&self, domain: Domain, nspace: i64) -> Result<()> {
+ let uid = match (domain, nspace) {
+ (Domain::APP, nspace) => nspace as u32,
+ (Domain::SELINUX, Self::WIFI_NAMESPACE) => Self::AID_WIFI,
+ // Nothing to do.
+ _ => return Ok(()),
+ };
+
let result = self.do_serialized(move |migrator_state| {
- migrator_state.bulk_delete(BulkDeleteRequest::Uid(uid), keep_non_super_encrypted_keys)
+ migrator_state.bulk_delete(BulkDeleteRequest::Uid(uid), false)
});
result.unwrap_or(Ok(()))
diff --git a/keystore2/src/user_manager.rs b/keystore2/src/user_manager.rs
index 8e09144..3c393c5 100644
--- a/keystore2/src/user_manager.rs
+++ b/keystore2/src/user_manager.rs
@@ -24,6 +24,7 @@
BnKeystoreUserManager, IKeystoreUserManager,
};
use android_security_usermanager::binder::{Interface, Result as BinderResult};
+use android_system_keystore2::aidl::android::system::keystore2::Domain::Domain;
use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
use anyhow::{Context, Result};
use binder::{IBinder, Strong};
@@ -85,6 +86,17 @@
})
.context("In add_or_remove_user: Trying to delete keys from db.")
}
+
+ fn clear_namespace(domain: Domain, nspace: i64) -> Result<()> {
+ // Permission check. Must return on error. Do not touch the '?'.
+ check_keystore_permission(KeystorePerm::clear_uid()).context("In clear_namespace.")?;
+
+ LEGACY_MIGRATOR
+ .bulk_delete_uid(domain, nspace)
+ .context("In clear_namespace: Trying to delete legacy keys.")?;
+ DB.with(|db| db.borrow_mut().unbind_keys_for_namespace(domain, nspace))
+ .context("In clear_namespace: Trying to delete keys from db.")
+ }
}
impl Interface for UserManager {}
@@ -101,4 +113,8 @@
fn onUserRemoved(&self, user_id: i32) -> BinderResult<()> {
map_or_log_err(Self::add_or_remove_user(user_id), Ok)
}
+
+ fn clearNamespace(&self, domain: Domain, nspace: i64) -> BinderResult<()> {
+ map_or_log_err(Self::clear_namespace(domain, nspace), Ok)
+ }
}
diff --git a/keystore2/src/vintf/Android.bp b/keystore2/src/vintf/Android.bp
new file mode 100644
index 0000000..da7935a
--- /dev/null
+++ b/keystore2/src/vintf/Android.bp
@@ -0,0 +1,70 @@
+// Copyright 2021, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+rust_library {
+ name: "libkeystore2_vintf_rust",
+ crate_name: "keystore2_vintf",
+ srcs: ["lib.rs"],
+ rustlibs: [
+ "libkeystore2_vintf_bindgen",
+ ],
+ shared_libs: [
+ "libkeystore2_vintf_cpp",
+ "libvintf",
+ ],
+}
+
+cc_library {
+ name: "libkeystore2_vintf_cpp",
+ srcs: [
+ "vintf.cpp",
+ ],
+ shared_libs: [
+ "libvintf",
+ ],
+}
+
+rust_bindgen {
+ name: "libkeystore2_vintf_bindgen",
+ wrapper_src: "vintf.hpp",
+ crate_name: "keystore2_vintf_bindgen",
+ source_stem: "bindings",
+ host_supported: true,
+ shared_libs: ["libvintf"],
+ bindgen_flags: [
+ "--size_t-is-usize",
+ "--whitelist-function", "getHalNames",
+ "--whitelist-function", "getHalNamesAndVersions",
+ "--whitelist-function", "getAidlInstances",
+ "--whitelist-function", "freeNames",
+ ],
+}
+
+rust_test {
+ name: "keystore2_vintf_test",
+ crate_name: "keystore2_vintf_test",
+ srcs: ["lib.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ rustlibs: [
+ "libkeystore2_vintf_bindgen",
+ ],
+ static_libs: [
+ "libkeystore2_vintf_cpp",
+ ],
+ shared_libs: [
+ "libc++",
+ "libvintf",
+ ],
+}
diff --git a/keystore2/src/vintf/lib.rs b/keystore2/src/vintf/lib.rs
new file mode 100644
index 0000000..c3d6d8a
--- /dev/null
+++ b/keystore2/src/vintf/lib.rs
@@ -0,0 +1,99 @@
+// Copyright 2021, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Bindings for getting the list of HALs.
+
+use keystore2_vintf_bindgen::{freeNames, getAidlInstances, getHalNames, getHalNamesAndVersions};
+use std::ffi::{CStr, CString};
+use std::os::raw::c_char;
+use std::str::Utf8Error;
+
+/// A struct that contains a list of HALs (optionally with version numbers).
+/// To use it, call as_vec to get a Vec view of the data it contains.
+pub struct HalNames {
+ data: *mut *mut c_char,
+ len: usize,
+}
+
+impl Drop for HalNames {
+ fn drop(&mut self) {
+ // Safety: The memory is allocated by our C shim so it must free it as well.
+ unsafe { freeNames(self.data, self.len) }
+ }
+}
+
+impl<'a> HalNames {
+ /// Get a Vec view of the list of HALs.
+ pub fn as_vec(&'a self) -> Result<Vec<&'a str>, Utf8Error> {
+ // Safety: self.data contains self.len C strings.
+ // The lifetimes ensure that the HalNames (and hence the strings) live
+ // at least as long as the returned vector.
+ unsafe { (0..self.len).map(|i| CStr::from_ptr(*self.data.add(i)).to_str()) }.collect()
+ }
+}
+
+/// Gets all HAL names.
+/// Note that this is not a zero-cost shim: it will make copies of the strings.
+pub fn get_hal_names() -> HalNames {
+ let mut len: usize = 0;
+ // Safety: We'll wrap this in HalNames to free the memory it allocates.
+ // It stores the size of the array it returns in len.
+ let raw_strs = unsafe { getHalNames(&mut len) };
+ HalNames { data: raw_strs, len }
+}
+
+/// Gets all HAL names and versions.
+/// Note that this is not a zero-cost shim: it will make copies of the strings.
+pub fn get_hal_names_and_versions() -> HalNames {
+ let mut len: usize = 0;
+ // Safety: We'll wrap this in HalNames to free the memory it allocates.
+ // It stores the size of the array it returns in len.
+ let raw_strs = unsafe { getHalNamesAndVersions(&mut len) };
+ HalNames { data: raw_strs, len }
+}
+
+/// Gets the instances of the given package, version, and interface tuple.
+/// Note that this is not a zero-cost shim: it will make copies of the strings.
+pub fn get_aidl_instances(package: &str, version: usize, interface_name: &str) -> HalNames {
+ let mut len: usize = 0;
+ let packages = CString::new(package).expect("Failed to make CString from package.");
+ let interface_name =
+ CString::new(interface_name).expect("Failed to make CString from interface_name.");
+ // Safety: We'll wrap this in HalNames to free the memory it allocates.
+ // It stores the size of the array it returns in len.
+ let raw_strs =
+ unsafe { getAidlInstances(&mut len, packages.as_ptr(), version, interface_name.as_ptr()) };
+ HalNames { data: raw_strs, len }
+}
+
+#[cfg(test)]
+mod tests {
+
+ use super::*;
+
+ #[test]
+ fn test() -> Result<(), Utf8Error> {
+ let result = get_hal_names();
+ let names = result.as_vec()?;
+ assert_ne!(names.len(), 0);
+
+ let result = get_hal_names_and_versions();
+ let names_and_versions = result.as_vec()?;
+ assert_ne!(names_and_versions.len(), 0);
+
+ assert!(names_and_versions.len() >= names.len());
+
+ Ok(())
+ }
+}
diff --git a/keystore2/src/vintf/vintf.cpp b/keystore2/src/vintf/vintf.cpp
new file mode 100644
index 0000000..dbdc046
--- /dev/null
+++ b/keystore2/src/vintf/vintf.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vintf.hpp"
+
+#include <vintf/HalManifest.h>
+#include <vintf/VintfObject.h>
+
+// Converts a set<string> into a C-style array of C strings.
+static char** convert(const std::set<std::string>& names) {
+ char** ret = new char*[names.size()];
+ char** ptr = ret;
+ for (const auto& name : names) {
+ *(ptr++) = strdup(name.c_str());
+ }
+ return ret;
+}
+
+char** getHalNames(size_t* len) {
+ auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
+ const auto names = manifest->getHalNames();
+ *len = names.size();
+ return convert(names);
+}
+
+char** getHalNamesAndVersions(size_t* len) {
+ auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
+ const auto names = manifest->getHalNamesAndVersions();
+ *len = names.size();
+ return convert(names);
+}
+
+char** getAidlInstances(size_t* len, const char* package, size_t version,
+ const char* interfaceName) {
+ auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
+ const auto names = manifest->getAidlInstances(package, version, interfaceName);
+ *len = names.size();
+ return convert(names);
+}
+
+void freeNames(char** names, size_t len) {
+ for (int i = 0; i < len; i++) {
+ free(names[i]);
+ }
+ delete[] names;
+}
diff --git a/keystore2/src/vintf/vintf.hpp b/keystore2/src/vintf/vintf.hpp
new file mode 100644
index 0000000..75e80f6
--- /dev/null
+++ b/keystore2/src/vintf/vintf.hpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VINTF_H__
+#define __VINTF_H__
+
+#include <stddef.h>
+
+extern "C" {
+
+char** getHalNames(size_t* len);
+char** getHalNamesAndVersions(size_t* len);
+char** getAidlInstances(size_t* len, const char* package, size_t version,
+ const char* interfaceName);
+void freeNames(char** names, size_t len);
+}
+
+#endif // __VINTF_H__
diff --git a/ondevice-signing/Keymaster.cpp b/ondevice-signing/Keymaster.cpp
index 267dec8..6cfb565 100644
--- a/ondevice-signing/Keymaster.cpp
+++ b/ondevice-signing/Keymaster.cpp
@@ -66,6 +66,9 @@
}
}
+ if (devToUse == nullptr) {
+ LOG(WARNING) << "Didn't find a keymaster to use.";
+ }
mDevice = devToUse;
return mDevice != nullptr;
diff --git a/ondevice-signing/KeymasterSigningKey.cpp b/ondevice-signing/KeymasterSigningKey.cpp
index f35f92b..2b748e4 100644
--- a/ondevice-signing/KeymasterSigningKey.cpp
+++ b/ondevice-signing/KeymasterSigningKey.cpp
@@ -61,7 +61,11 @@
Result<void> KeymasterSigningKey::createSigningKey() {
KeymasterSigningKey signingKey;
- mKeymaster = Keymaster::getInstance();
+ auto keymaster = Keymaster::getInstance();
+ if (!keymaster.has_value()) {
+ return Error() << "Failed to initialize keymaster.";
+ }
+ mKeymaster = keymaster;
auto keyBlob = mKeymaster->createKey();
@@ -112,8 +116,12 @@
}
Result<void> KeymasterSigningKey::initializeFromKeyblob(const std::string& path) {
- mKeymaster = Keymaster::getInstance();
std::string keyBlobData;
+ auto keymaster = Keymaster::getInstance();
+ if (!keymaster.has_value()) {
+ return Error() << "Failed to initialize keymaster.";
+ }
+ mKeymaster = keymaster;
bool result = ReadFileToString(path, &keyBlobData);
if (!result) {