Project import
diff --git a/security/MODULE_LICENSE_APACHE2 b/security/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/security/MODULE_LICENSE_APACHE2
diff --git a/security/NOTICE b/security/NOTICE
new file mode 100644
index 0000000..89ae7c4
--- /dev/null
+++ b/security/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2008-2015, 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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/security/keystore-engine/Android.mk b/security/keystore-engine/Android.mk
new file mode 100644
index 0000000..1f5d903
--- /dev/null
+++ b/security/keystore-engine/Android.mk
@@ -0,0 +1,60 @@
+# Copyright (C) 2012 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+
+ifneq (,$(wildcard $(TOP)/external/boringssl/flavor.mk))
+ include $(TOP)/external/boringssl/flavor.mk
+else
+ include $(TOP)/external/openssl/flavor.mk
+endif
+ifeq ($(OPENSSL_FLAVOR),BoringSSL)
+ LOCAL_MODULE := libkeystore-engine
+
+ LOCAL_SRC_FILES := \
+ android_engine.cpp
+else
+ LOCAL_MODULE := libkeystore
+
+ LOCAL_MODULE_RELATIVE_PATH := ssl/engines
+
+ LOCAL_SRC_FILES := \
+ eng_keystore.cpp \
+ keyhandle.cpp \
+ ecdsa_meth.cpp \
+ dsa_meth.cpp \
+ rsa_meth.cpp
+
+ LOCAL_C_INCLUDES += \
+ external/openssl/include \
+ external/openssl
+endif
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -fvisibility=hidden -Wall -Werror
+
+LOCAL_SHARED_LIBRARIES += \
+ libcrypto \
+ liblog \
+ libcutils \
+ libutils \
+ libbinder \
+ libkeystore_binder
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/security/keystore-engine/android_engine.cpp b/security/keystore-engine/android_engine.cpp
new file mode 100644
index 0000000..de67df2
--- /dev/null
+++ b/security/keystore-engine/android_engine.cpp
@@ -0,0 +1,454 @@
+/* Copyright 2014 The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include <UniquePtr.h>
+
+#include <sys/socket.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+#include <openssl/ec_key.h>
+#include <openssl/ecdsa.h>
+#include <openssl/engine.h>
+#include <openssl/evp.h>
+#include <openssl/rsa.h>
+#include <openssl/x509.h>
+
+#include <binder/IServiceManager.h>
+#include <keystore/keystore.h>
+#include <keystore/IKeystoreService.h>
+
+using namespace android;
+
+namespace {
+
+extern const RSA_METHOD keystore_rsa_method;
+extern const ECDSA_METHOD keystore_ecdsa_method;
+
+/* key_id_dup is called when one of the RSA or EC_KEY objects is duplicated. */
+int key_id_dup(CRYPTO_EX_DATA* /* to */,
+ const CRYPTO_EX_DATA* /* from */,
+ void** from_d,
+ int /* index */,
+ long /* argl */,
+ void* /* argp */) {
+ char *key_id = reinterpret_cast<char *>(*from_d);
+ if (key_id != NULL) {
+ *from_d = strdup(key_id);
+ }
+ return 1;
+}
+
+/* key_id_free is called when one of the RSA, DSA or EC_KEY object is freed. */
+void key_id_free(void* /* parent */,
+ void* ptr,
+ CRYPTO_EX_DATA* /* ad */,
+ int /* index */,
+ long /* argl */,
+ void* /* argp */) {
+ char *key_id = reinterpret_cast<char *>(ptr);
+ free(key_id);
+}
+
+/* KeystoreEngine is a BoringSSL ENGINE that implements RSA and ECDSA by
+ * forwarding the requested operations to Keystore. */
+class KeystoreEngine {
+ public:
+ KeystoreEngine()
+ : rsa_index_(RSA_get_ex_new_index(0 /* argl */,
+ NULL /* argp */,
+ NULL /* new_func */,
+ key_id_dup,
+ key_id_free)),
+ ec_key_index_(EC_KEY_get_ex_new_index(0 /* argl */,
+ NULL /* argp */,
+ NULL /* new_func */,
+ key_id_dup,
+ key_id_free)),
+ engine_(ENGINE_new()) {
+ ENGINE_set_RSA_method(
+ engine_, &keystore_rsa_method, sizeof(keystore_rsa_method));
+ ENGINE_set_ECDSA_method(
+ engine_, &keystore_ecdsa_method, sizeof(keystore_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_; }
+
+ private:
+ const int rsa_index_;
+ const int ec_key_index_;
+ ENGINE* const engine_;
+};
+
+pthread_once_t g_keystore_engine_once = PTHREAD_ONCE_INIT;
+KeystoreEngine *g_keystore_engine;
+
+/* init_keystore_engine is called to initialize |g_keystore_engine|. This
+ * should only be called by |pthread_once|. */
+void init_keystore_engine() {
+ g_keystore_engine = new KeystoreEngine;
+}
+
+/* ensure_keystore_engine ensures that |g_keystore_engine| is pointing to a
+ * valid |KeystoreEngine| object and creates one if not. */
+void ensure_keystore_engine() {
+ pthread_once(&g_keystore_engine_once, init_keystore_engine);
+}
+
+/* Many OpenSSL APIs take ownership of an argument on success but don't free
+ * the argument on failure. This means we need to tell our scoped pointers when
+ * we've transferred ownership, without triggering a warning by not using the
+ * result of release(). */
+#define OWNERSHIP_TRANSFERRED(obj) \
+ typeof ((obj).release()) _dummy __attribute__((unused)) = (obj).release()
+
+const char* rsa_get_key_id(const RSA* rsa) {
+ return reinterpret_cast<char*>(
+ RSA_get_ex_data(rsa, g_keystore_engine->rsa_ex_index()));
+}
+
+/* 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. */
+int rsa_private_transform(RSA *rsa, uint8_t *out, const uint8_t *in, size_t len) {
+ ALOGV("rsa_private_transform(%p, %p, %p, %u)", rsa, out, in, (unsigned) len);
+
+ const char *key_id = rsa_get_key_id(rsa);
+ if (key_id == NULL) {
+ ALOGE("key had no key_id!");
+ return 0;
+ }
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
+ sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
+
+ if (service == NULL) {
+ ALOGE("could not contact keystore");
+ return 0;
+ }
+
+ uint8_t* reply = NULL;
+ size_t reply_len;
+ int32_t ret = service->sign(String16(key_id), in, len, &reply, &reply_len);
+ if (ret < 0) {
+ ALOGW("There was an error during rsa_decrypt: could not connect");
+ return 0;
+ } else if (ret != 0) {
+ ALOGW("Error during sign from keystore: %d", ret);
+ return 0;
+ } else if (reply_len == 0) {
+ ALOGW("No valid signature returned");
+ free(reply);
+ return 0;
+ }
+
+ if (reply_len > 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. */
+ memcpy(out, reply + reply_len - len, len);
+ } else if (reply_len < 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. */
+ memset(out, 0, len);
+ memcpy(out + len - reply_len, reply, reply_len);
+ } else {
+ memcpy(out, reply, len);
+ }
+
+ free(reply);
+
+ ALOGV("rsa=%p keystore_rsa_priv_dec successful", rsa);
+ return 1;
+}
+
+const struct rsa_meth_st keystore_rsa_method = {
+ {
+ 0 /* references */,
+ 1 /* is_static */,
+ },
+ NULL /* app_data */,
+
+ NULL /* init */,
+ NULL /* finish */,
+
+ NULL /* size */,
+
+ NULL /* sign */,
+ NULL /* verify */,
+
+ NULL /* encrypt */,
+ NULL /* sign_raw */,
+ NULL /* decrypt */,
+ NULL /* verify_raw */,
+
+ rsa_private_transform,
+
+ NULL /* mod_exp */,
+ NULL /* bn_mod_exp */,
+
+ RSA_FLAG_CACHE_PUBLIC | RSA_FLAG_OPAQUE,
+
+ NULL /* keygen */,
+ NULL /* multi_prime_keygen */,
+ NULL /* supports_digest */,
+};
+
+const char* ecdsa_get_key_id(const EC_KEY* ec_key) {
+ return reinterpret_cast<char*>(
+ EC_KEY_get_ex_data(ec_key, g_keystore_engine->ec_key_ex_index()));
+}
+
+/* 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. */
+static int ecdsa_sign(const uint8_t* digest, size_t digest_len, uint8_t* sig,
+ unsigned int* sig_len, EC_KEY* ec_key) {
+ ALOGV("ecdsa_sign(%p, %u, %p)", digest, (unsigned) digest_len, ec_key);
+
+ const char *key_id = ecdsa_get_key_id(ec_key);
+ if (key_id == NULL) {
+ ALOGE("key had no key_id!");
+ return 0;
+ }
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
+ sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
+
+ if (service == NULL) {
+ ALOGE("could not contact keystore");
+ return 0;
+ }
+
+ size_t ecdsa_size = ECDSA_size(ec_key);
+
+ uint8_t* reply = NULL;
+ size_t reply_len;
+ int32_t ret = service->sign(String16(reinterpret_cast<const char*>(key_id)),
+ digest, digest_len, &reply, &reply_len);
+ if (ret < 0) {
+ ALOGW("There was an error during ecdsa_sign: could not connect");
+ return 0;
+ } else if (ret != 0) {
+ ALOGW("Error during sign from keystore: %d", ret);
+ return 0;
+ } else if (reply_len == 0) {
+ ALOGW("No valid signature returned");
+ free(reply);
+ return 0;
+ } else if (reply_len > ecdsa_size) {
+ ALOGW("Signature is too large");
+ free(reply);
+ return 0;
+ }
+
+ memcpy(sig, reply, reply_len);
+ *sig_len = reply_len;
+
+ ALOGV("ecdsa_sign(%p, %u, %p) => success", digest, (unsigned)digest_len,
+ ec_key);
+ return 1;
+}
+
+const ECDSA_METHOD keystore_ecdsa_method = {
+ {
+ 0 /* references */,
+ 1 /* is_static */
+ } /* common */,
+ NULL /* app_data */,
+
+ NULL /* init */,
+ NULL /* finish */,
+ NULL /* group_order_size */,
+ ecdsa_sign,
+ NULL /* verify */,
+ ECDSA_FLAG_OPAQUE,
+};
+
+struct EVP_PKEY_Delete {
+ void operator()(EVP_PKEY* p) const {
+ EVP_PKEY_free(p);
+ }
+};
+typedef UniquePtr<EVP_PKEY, EVP_PKEY_Delete> Unique_EVP_PKEY;
+
+struct RSA_Delete {
+ void operator()(RSA* p) const {
+ RSA_free(p);
+ }
+};
+typedef UniquePtr<RSA, RSA_Delete> Unique_RSA;
+
+struct EC_KEY_Delete {
+ void operator()(EC_KEY* ec) const {
+ EC_KEY_free(ec);
+ }
+};
+typedef UniquePtr<EC_KEY, EC_KEY_Delete> Unique_EC_KEY;
+
+/* 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|. */
+static EVP_PKEY *wrap_rsa(const char *key_id, const RSA *public_rsa) {
+ Unique_RSA rsa(RSA_new_method(g_keystore_engine->engine()));
+ if (rsa.get() == NULL) {
+ return NULL;
+ }
+
+ char *key_id_copy = strdup(key_id);
+ if (key_id_copy == NULL) {
+ return NULL;
+ }
+
+ if (!RSA_set_ex_data(rsa.get(), g_keystore_engine->rsa_ex_index(),
+ key_id_copy)) {
+ free(key_id_copy);
+ return NULL;
+ }
+
+ rsa->n = BN_dup(public_rsa->n);
+ rsa->e = BN_dup(public_rsa->e);
+ if (rsa->n == NULL || rsa->e == NULL) {
+ return NULL;
+ }
+
+ Unique_EVP_PKEY result(EVP_PKEY_new());
+ if (result.get() == NULL ||
+ !EVP_PKEY_assign_RSA(result.get(), rsa.get())) {
+ return NULL;
+ }
+ OWNERSHIP_TRANSFERRED(rsa);
+
+ return result.release();
+}
+
+/* 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|. */
+static EVP_PKEY *wrap_ecdsa(const char *key_id, const EC_KEY *public_ecdsa) {
+ Unique_EC_KEY ec(EC_KEY_new_method(g_keystore_engine->engine()));
+ if (ec.get() == NULL) {
+ return NULL;
+ }
+
+ 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 NULL;
+ }
+
+ char *key_id_copy = strdup(key_id);
+ if (key_id_copy == NULL) {
+ return NULL;
+ }
+
+ if (!EC_KEY_set_ex_data(ec.get(), g_keystore_engine->ec_key_ex_index(),
+ key_id_copy)) {
+ free(key_id_copy);
+ return NULL;
+ }
+
+ Unique_EVP_PKEY result(EVP_PKEY_new());
+ if (result.get() == NULL ||
+ !EVP_PKEY_assign_EC_KEY(result.get(), ec.get())) {
+ return NULL;
+ }
+ OWNERSHIP_TRANSFERRED(ec);
+
+ return result.release();
+}
+
+} /* anonymous namespace */
+
+extern "C" {
+
+EVP_PKEY* EVP_PKEY_from_keystore(const char* key_id) __attribute__((visibility("default")));
+
+/* 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. */
+EVP_PKEY* EVP_PKEY_from_keystore(const char* key_id) {
+ ALOGV("EVP_PKEY_from_keystore(\"%s\")", key_id);
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
+ sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
+
+ if (service == NULL) {
+ ALOGE("could not contact keystore");
+ return 0;
+ }
+
+ uint8_t *pubkey = NULL;
+ size_t pubkey_len;
+ int32_t ret = service->get_pubkey(String16(key_id), &pubkey, &pubkey_len);
+ if (ret < 0) {
+ ALOGW("could not contact keystore");
+ return NULL;
+ } else if (ret != 0) {
+ ALOGW("keystore reports error: %d", ret);
+ return NULL;
+ }
+
+ const uint8_t *inp = pubkey;
+ Unique_EVP_PKEY pkey(d2i_PUBKEY(NULL, &inp, pubkey_len));
+ free(pubkey);
+ if (pkey.get() == NULL) {
+ ALOGW("Cannot convert pubkey");
+ return NULL;
+ }
+
+ ensure_keystore_engine();
+
+ EVP_PKEY *result;
+ switch (EVP_PKEY_type(pkey->type)) {
+ case EVP_PKEY_RSA: {
+ Unique_RSA public_rsa(EVP_PKEY_get1_RSA(pkey.get()));
+ result = wrap_rsa(key_id, public_rsa.get());
+ break;
+ }
+ case EVP_PKEY_EC: {
+ Unique_EC_KEY public_ecdsa(EVP_PKEY_get1_EC_KEY(pkey.get()));
+ result = wrap_ecdsa(key_id, public_ecdsa.get());
+ break;
+ }
+ default:
+ ALOGE("Unsupported key type %d", EVP_PKEY_type(pkey->type));
+ result = NULL;
+ }
+
+ return result;
+}
+
+} // extern "C"
diff --git a/security/keystore-engine/dsa_meth.cpp b/security/keystore-engine/dsa_meth.cpp
new file mode 100644
index 0000000..3788364
--- /dev/null
+++ b/security/keystore-engine/dsa_meth.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <UniquePtr.h>
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "OpenSSL-keystore-dsa"
+#include <cutils/log.h>
+
+#include <binder/IServiceManager.h>
+#include <keystore/IKeystoreService.h>
+
+#include <openssl/dsa.h>
+#include <openssl/engine.h>
+
+#include "methods.h"
+
+
+using namespace android;
+
+struct DSA_SIG_Delete {
+ void operator()(DSA_SIG* p) const {
+ DSA_SIG_free(p);
+ }
+};
+typedef UniquePtr<DSA_SIG, struct DSA_SIG_Delete> Unique_DSA_SIG;
+
+static DSA_SIG* keystore_dsa_do_sign(const unsigned char *dgst, int dlen, DSA *dsa) {
+ ALOGV("keystore_dsa_do_sign(%p, %d, %p)", dgst, dlen, dsa);
+
+ uint8_t* key_id = reinterpret_cast<uint8_t*>(DSA_get_ex_data(dsa, dsa_key_handle));
+ if (key_id == NULL) {
+ ALOGE("key had no key_id!");
+ return 0;
+ }
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
+ sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
+
+ if (service == NULL) {
+ ALOGE("could not contact keystore");
+ return 0;
+ }
+
+ int num = DSA_size(dsa);
+
+ uint8_t* reply = NULL;
+ size_t replyLen;
+ int32_t ret = service->sign(String16(reinterpret_cast<const char*>(key_id)), dgst,
+ dlen, &reply, &replyLen);
+ if (ret < 0) {
+ ALOGW("There was an error during dsa_do_sign: could not connect");
+ return 0;
+ } else if (ret != 0) {
+ ALOGW("Error during sign from keystore: %d", ret);
+ return 0;
+ } else if (replyLen <= 0) {
+ ALOGW("No valid signature returned");
+ return 0;
+ } else if (replyLen > (size_t) num) {
+ ALOGW("Signature is too large");
+ return 0;
+ }
+
+ Unique_DSA_SIG dsa_sig(d2i_DSA_SIG(NULL,
+ const_cast<const unsigned char**>(reinterpret_cast<unsigned char**>(&reply)),
+ replyLen));
+ if (dsa_sig.get() == NULL) {
+ ALOGW("conversion from DER to DSA_SIG failed");
+ return 0;
+ }
+
+ ALOGV("keystore_dsa_do_sign(%p, %d, %p) => returning %p len %zu", dgst, dlen, dsa,
+ dsa_sig.get(), replyLen);
+ return dsa_sig.release();
+}
+
+static DSA_METHOD keystore_dsa_meth = {
+ kKeystoreEngineId, /* name */
+ keystore_dsa_do_sign, /* dsa_do_sign */
+ NULL, /* dsa_sign_setup */
+ NULL, /* dsa_do_verify */
+ NULL, /* dsa_mod_exp */
+ NULL, /* bn_mod_exp */
+ NULL, /* init */
+ NULL, /* finish */
+ 0, /* flags */
+ NULL, /* app_data */
+ NULL, /* dsa_paramgen */
+ NULL, /* dsa_keygen */
+};
+
+static int register_dsa_methods() {
+ const DSA_METHOD* dsa_meth = DSA_OpenSSL();
+
+ keystore_dsa_meth.dsa_do_verify = dsa_meth->dsa_do_verify;
+
+ return 1;
+}
+
+int dsa_pkey_setup(ENGINE *e, EVP_PKEY *pkey, const char *key_id) {
+ Unique_DSA dsa(EVP_PKEY_get1_DSA(pkey));
+ if (!DSA_set_ex_data(dsa.get(), dsa_key_handle, reinterpret_cast<void*>(strdup(key_id)))) {
+ ALOGW("Could not set ex_data for loaded DSA key");
+ return 0;
+ }
+
+ DSA_set_method(dsa.get(), &keystore_dsa_meth);
+
+ /*
+ * "DSA_set_ENGINE()" should probably be an OpenSSL API. Since it isn't,
+ * and EVP_PKEY_free() calls ENGINE_finish(), we need to call ENGINE_init()
+ * here.
+ */
+ ENGINE_init(e);
+ dsa->engine = e;
+
+ return 1;
+}
+
+int dsa_register(ENGINE* e) {
+ if (!ENGINE_set_DSA(e, &keystore_dsa_meth)
+ || !register_dsa_methods()) {
+ ALOGE("Could not set up keystore DSA methods");
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/security/keystore-engine/ecdsa_meth.cpp b/security/keystore-engine/ecdsa_meth.cpp
new file mode 100644
index 0000000..48f178d
--- /dev/null
+++ b/security/keystore-engine/ecdsa_meth.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <UniquePtr.h>
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "OpenSSL-keystore-ecdsa"
+#include <cutils/log.h>
+
+#include <binder/IServiceManager.h>
+#include <keystore/IKeystoreService.h>
+
+#include <openssl/ecdsa.h>
+#include <openssl/engine.h>
+
+// TODO replace this with real OpenSSL API when it exists
+#include "crypto/ec/ec_lcl.h"
+#include "crypto/ecdsa/ecs_locl.h"
+
+#include "methods.h"
+
+
+using namespace android;
+
+struct ECDSA_SIG_Delete {
+ void operator()(ECDSA_SIG* p) const {
+ ECDSA_SIG_free(p);
+ }
+};
+typedef UniquePtr<ECDSA_SIG, struct ECDSA_SIG_Delete> Unique_ECDSA_SIG;
+
+static ECDSA_SIG* keystore_ecdsa_do_sign(const unsigned char *dgst, int dlen,
+ const BIGNUM*, const BIGNUM*, EC_KEY *eckey) {
+ ALOGV("keystore_ecdsa_do_sign(%p, %d, %p)", dgst, dlen, eckey);
+
+ uint8_t* key_id = reinterpret_cast<uint8_t*>(EC_KEY_get_key_method_data(eckey,
+ ex_data_dup, ex_data_free, ex_data_clear_free));
+ if (key_id == NULL) {
+ ALOGE("key had no key_id!");
+ return 0;
+ }
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
+ sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
+
+ if (service == NULL) {
+ ALOGE("could not contact keystore");
+ return 0;
+ }
+
+ int num = ECDSA_size(eckey);
+
+ uint8_t* reply = NULL;
+ size_t replyLen;
+ int32_t ret = service->sign(String16(reinterpret_cast<const char*>(key_id)), dgst,
+ dlen, &reply, &replyLen);
+ if (ret < 0) {
+ ALOGW("There was an error during dsa_do_sign: could not connect");
+ return 0;
+ } else if (ret != 0) {
+ ALOGW("Error during sign from keystore: %d", ret);
+ return 0;
+ } else if (replyLen <= 0) {
+ ALOGW("No valid signature returned");
+ return 0;
+ } else if (replyLen > (size_t) num) {
+ ALOGW("Signature is too large");
+ return 0;
+ }
+
+ Unique_ECDSA_SIG ecdsa_sig(d2i_ECDSA_SIG(NULL,
+ const_cast<const unsigned char**>(reinterpret_cast<unsigned char**>(&reply)),
+ replyLen));
+ if (ecdsa_sig.get() == NULL) {
+ ALOGW("conversion from DER to ECDSA_SIG failed");
+ return 0;
+ }
+
+ ALOGV("keystore_ecdsa_do_sign(%p, %d, %p) => returning %p len %zu", dgst, dlen, eckey,
+ ecdsa_sig.get(), replyLen);
+ return ecdsa_sig.release();
+}
+
+static ECDSA_METHOD keystore_ecdsa_meth = {
+ kKeystoreEngineId, /* name */
+ keystore_ecdsa_do_sign, /* ecdsa_do_sign */
+ NULL, /* ecdsa_sign_setup */
+ NULL, /* ecdsa_do_verify */
+ 0, /* flags */
+ NULL, /* app_data */
+};
+
+static int register_ecdsa_methods() {
+ const ECDSA_METHOD* ecdsa_meth = ECDSA_OpenSSL();
+
+ keystore_ecdsa_meth.ecdsa_do_verify = ecdsa_meth->ecdsa_do_verify;
+
+ return 1;
+}
+
+int ecdsa_pkey_setup(ENGINE *e, EVP_PKEY *pkey, const char *key_id) {
+ Unique_EC_KEY eckey(EVP_PKEY_get1_EC_KEY(pkey));
+ void* oldData = EC_KEY_insert_key_method_data(eckey.get(),
+ reinterpret_cast<void*>(strdup(key_id)), ex_data_dup, ex_data_free,
+ ex_data_clear_free);
+ if (oldData != NULL) {
+ free(oldData);
+ }
+
+ ECDSA_set_method(eckey.get(), &keystore_ecdsa_meth);
+
+ /*
+ * "ECDSA_set_ENGINE()" should probably be an OpenSSL API. Since it isn't,
+ * and EC_KEY_free() calls ENGINE_finish(), we need to call ENGINE_init()
+ * here.
+ */
+ ECDSA_DATA *ecdsa = ecdsa_check(eckey.get());
+ ENGINE_init(e);
+ ecdsa->engine = e;
+
+ return 1;
+}
+
+int ecdsa_register(ENGINE* e) {
+ if (!ENGINE_set_ECDSA(e, &keystore_ecdsa_meth)
+ || !register_ecdsa_methods()) {
+ ALOGE("Could not set up keystore ECDSA methods");
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/security/keystore-engine/eng_keystore.cpp b/security/keystore-engine/eng_keystore.cpp
new file mode 100644
index 0000000..6feb0f9
--- /dev/null
+++ b/security/keystore-engine/eng_keystore.cpp
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <UniquePtr.h>
+
+#include <sys/socket.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openssl/dsa.h>
+#include <openssl/engine.h>
+#include <openssl/ec.h>
+#include <openssl/evp.h>
+#include <openssl/objects.h>
+#include <openssl/rsa.h>
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "OpenSSL-keystore"
+#include <cutils/log.h>
+
+#include <binder/IServiceManager.h>
+#include <keystore/keystore.h>
+#include <keystore/IKeystoreService.h>
+
+#include "methods.h"
+
+using namespace android;
+
+#define DYNAMIC_ENGINE
+const char* kKeystoreEngineId = "keystore";
+static const char* kKeystoreEngineDesc = "Android keystore engine";
+
+
+/*
+ * ex_data index for keystore's key alias.
+ */
+int rsa_key_handle;
+int dsa_key_handle;
+
+
+/*
+ * Only initialize the *_key_handle once.
+ */
+static pthread_once_t key_handle_control = PTHREAD_ONCE_INIT;
+
+/**
+ * Many OpenSSL APIs take ownership of an argument on success but don't free the argument
+ * on failure. This means we need to tell our scoped pointers when we've transferred ownership,
+ * without triggering a warning by not using the result of release().
+ */
+#define OWNERSHIP_TRANSFERRED(obj) \
+ typeof (obj.release()) _dummy __attribute__((unused)) = obj.release()
+
+
+struct ENGINE_Delete {
+ void operator()(ENGINE* p) const {
+ ENGINE_free(p);
+ }
+};
+typedef UniquePtr<ENGINE, ENGINE_Delete> Unique_ENGINE;
+
+struct EVP_PKEY_Delete {
+ void operator()(EVP_PKEY* p) const {
+ EVP_PKEY_free(p);
+ }
+};
+typedef UniquePtr<EVP_PKEY, EVP_PKEY_Delete> Unique_EVP_PKEY;
+
+/**
+ * Called to initialize RSA's ex_data for the key_id handle. This should
+ * only be called when protected by a lock.
+ */
+static void init_key_handle() {
+ rsa_key_handle = RSA_get_ex_new_index(0, NULL, keyhandle_new, keyhandle_dup, keyhandle_free);
+ dsa_key_handle = DSA_get_ex_new_index(0, NULL, keyhandle_new, keyhandle_dup, keyhandle_free);
+}
+
+static EVP_PKEY* keystore_loadkey(ENGINE* e, const char* key_id, UI_METHOD* ui_method,
+ void* callback_data) {
+#if LOG_NDEBUG
+ (void)ui_method;
+ (void)callback_data;
+#else
+ ALOGV("keystore_loadkey(%p, \"%s\", %p, %p)", e, key_id, ui_method, callback_data);
+#endif
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
+ sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
+
+ if (service == NULL) {
+ ALOGE("could not contact keystore");
+ return 0;
+ }
+
+ uint8_t *pubkey = NULL;
+ size_t pubkeyLen;
+ int32_t ret = service->get_pubkey(String16(key_id), &pubkey, &pubkeyLen);
+ if (ret < 0) {
+ ALOGW("could not contact keystore");
+ free(pubkey);
+ return NULL;
+ } else if (ret != 0) {
+ ALOGW("keystore reports error: %d", ret);
+ free(pubkey);
+ return NULL;
+ }
+
+ const unsigned char* tmp = reinterpret_cast<const unsigned char*>(pubkey);
+ Unique_EVP_PKEY pkey(d2i_PUBKEY(NULL, &tmp, pubkeyLen));
+ free(pubkey);
+ if (pkey.get() == NULL) {
+ ALOGW("Cannot convert pubkey");
+ return NULL;
+ }
+
+ switch (EVP_PKEY_type(pkey->type)) {
+ case EVP_PKEY_DSA: {
+ dsa_pkey_setup(e, pkey.get(), key_id);
+ break;
+ }
+ case EVP_PKEY_RSA: {
+ rsa_pkey_setup(e, pkey.get(), key_id);
+ break;
+ }
+ case EVP_PKEY_EC: {
+ ecdsa_pkey_setup(e, pkey.get(), key_id);
+ break;
+ }
+ default:
+ ALOGE("Unsupported key type %d", EVP_PKEY_type(pkey->type));
+ return NULL;
+ }
+
+ return pkey.release();
+}
+
+static const ENGINE_CMD_DEFN keystore_cmd_defns[] = {
+ {0, NULL, NULL, 0}
+};
+
+static int keystore_engine_setup(ENGINE* e) {
+ ALOGV("keystore_engine_setup");
+
+ if (!ENGINE_set_id(e, kKeystoreEngineId)
+ || !ENGINE_set_name(e, kKeystoreEngineDesc)
+ || !ENGINE_set_load_privkey_function(e, keystore_loadkey)
+ || !ENGINE_set_load_pubkey_function(e, keystore_loadkey)
+ || !ENGINE_set_flags(e, 0)
+ || !ENGINE_set_cmd_defns(e, keystore_cmd_defns)) {
+ ALOGE("Could not set up keystore engine");
+ return 0;
+ }
+
+ /* We need a handle in the keys types as well for keygen if it's not already initialized. */
+ pthread_once(&key_handle_control, init_key_handle);
+ if ((rsa_key_handle < 0) || (dsa_key_handle < 0)) {
+ ALOGE("Could not set up ex_data index");
+ return 0;
+ }
+
+ if (!dsa_register(e)) {
+ ALOGE("DSA registration failed");
+ return 0;
+ } else if (!ecdsa_register(e)) {
+ ALOGE("ECDSA registration failed");
+ return 0;
+ } else if (!rsa_register(e)) {
+ ALOGE("RSA registration failed");
+ return 0;
+ }
+
+ return 1;
+}
+
+ENGINE* ENGINE_keystore() {
+ ALOGV("ENGINE_keystore");
+
+ Unique_ENGINE engine(ENGINE_new());
+ if (engine.get() == NULL) {
+ return NULL;
+ }
+
+ if (!keystore_engine_setup(engine.get())) {
+ return NULL;
+ }
+
+ return engine.release();
+}
+
+static int keystore_bind_fn(ENGINE *e, const char *id) {
+ ALOGV("keystore_bind_fn");
+
+ if (!id) {
+ return 0;
+ }
+
+ if (strcmp(id, kKeystoreEngineId)) {
+ return 0;
+ }
+
+ if (!keystore_engine_setup(e)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+extern "C" {
+#undef OPENSSL_EXPORT
+#define OPENSSL_EXPORT extern __attribute__ ((visibility ("default")))
+
+IMPLEMENT_DYNAMIC_CHECK_FN()
+IMPLEMENT_DYNAMIC_BIND_FN(keystore_bind_fn)
+};
diff --git a/security/keystore-engine/keyhandle.cpp b/security/keystore-engine/keyhandle.cpp
new file mode 100644
index 0000000..aeba896
--- /dev/null
+++ b/security/keystore-engine/keyhandle.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <openssl/engine.h>
+
+#include <string.h>
+
+/**
+ * Makes sure the ex_data for the keyhandle is initially set to NULL.
+ */
+int keyhandle_new(void*, void*, CRYPTO_EX_DATA* ad, int idx, long, void*) {
+ return CRYPTO_set_ex_data(ad, idx, NULL);
+}
+
+/**
+ * Frees a previously allocated keyhandle stored in ex_data.
+ */
+void keyhandle_free(void *, void *ptr, CRYPTO_EX_DATA*, int, long, void*) {
+ char* keyhandle = reinterpret_cast<char*>(ptr);
+ if (keyhandle != NULL) {
+ free(keyhandle);
+ }
+}
+
+/**
+ * Duplicates a keyhandle stored in ex_data in case we copy a key.
+ */
+int keyhandle_dup(CRYPTO_EX_DATA* to, CRYPTO_EX_DATA*, void *ptrRef, int idx, long, void *) {
+ // This appears to be a bug in OpenSSL.
+ void** ptr = reinterpret_cast<void**>(ptrRef);
+ char* keyhandle = reinterpret_cast<char*>(*ptr);
+ if (keyhandle != NULL) {
+ char* keyhandle_copy = strdup(keyhandle);
+ *ptr = keyhandle_copy;
+
+ // Call this in case OpenSSL is fixed in the future.
+ (void) CRYPTO_set_ex_data(to, idx, keyhandle_copy);
+ }
+ return 1;
+}
+
+void *ex_data_dup(void *data) {
+ char* keyhandle = reinterpret_cast<char*>(data);
+ return strdup(keyhandle);
+}
+
+void ex_data_free(void *data) {
+ char* keyhandle = reinterpret_cast<char*>(data);
+ free(keyhandle);
+}
+
+void ex_data_clear_free(void *data) {
+ char* keyhandle = reinterpret_cast<char*>(data);
+ memset(data, '\0', strlen(keyhandle));
+ free(keyhandle);
+}
diff --git a/security/keystore-engine/methods.h b/security/keystore-engine/methods.h
new file mode 100644
index 0000000..fb85942
--- /dev/null
+++ b/security/keystore-engine/methods.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/* For ENGINE method registration purposes. */
+extern const char* kKeystoreEngineId;
+
+extern int dsa_key_handle;
+extern int rsa_key_handle;
+
+struct DSA_Delete {
+ void operator()(DSA* p) const {
+ DSA_free(p);
+ }
+};
+typedef UniquePtr<DSA, struct DSA_Delete> Unique_DSA;
+
+struct EC_KEY_Delete {
+ void operator()(EC_KEY* p) const {
+ EC_KEY_free(p);
+ }
+};
+typedef UniquePtr<EC_KEY, EC_KEY_Delete> Unique_EC_KEY;
+
+struct RSA_Delete {
+ void operator()(RSA* p) const {
+ RSA_free(p);
+ }
+};
+typedef UniquePtr<RSA, struct RSA_Delete> Unique_RSA;
+
+
+/* Keyhandles for ENGINE metadata */
+int keyhandle_new(void*, void*, CRYPTO_EX_DATA* ad, int idx, long, void*);
+void keyhandle_free(void *, void *ptr, CRYPTO_EX_DATA*, int, long, void*);
+int keyhandle_dup(CRYPTO_EX_DATA* to, CRYPTO_EX_DATA*, void *ptrRef, int idx, long, void *);
+
+/* For EC_EX_DATA stuff */
+void *ex_data_dup(void *);
+void ex_data_free(void *);
+void ex_data_clear_free(void *);
+
+/* ECDSA */
+int ecdsa_register(ENGINE *);
+int ecdsa_pkey_setup(ENGINE *, EVP_PKEY*, const char*);
+
+/* DSA */
+int dsa_register(ENGINE *);
+int dsa_pkey_setup(ENGINE *, EVP_PKEY*, const char*);
+
+/* RSA */
+int rsa_register(ENGINE *);
+int rsa_pkey_setup(ENGINE *, EVP_PKEY*, const char*);
diff --git a/security/keystore-engine/rsa_meth.cpp b/security/keystore-engine/rsa_meth.cpp
new file mode 100644
index 0000000..74dfa5c
--- /dev/null
+++ b/security/keystore-engine/rsa_meth.cpp
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <UniquePtr.h>
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "OpenSSL-keystore-rsa"
+#include <cutils/log.h>
+
+#include <binder/IServiceManager.h>
+#include <keystore/IKeystoreService.h>
+
+#include <openssl/rsa.h>
+#include <openssl/engine.h>
+
+#include "methods.h"
+
+
+using namespace android;
+
+
+int keystore_rsa_priv_enc(int flen, const unsigned char* from, unsigned char* to, RSA* rsa,
+ int padding) {
+ ALOGV("keystore_rsa_priv_enc(%d, %p, %p, %p, %d)", flen, from, to, rsa, padding);
+
+ int num = RSA_size(rsa);
+ UniquePtr<uint8_t> padded(new uint8_t[num]);
+ if (padded.get() == NULL) {
+ ALOGE("could not allocate padded signature");
+ return 0;
+ }
+
+ switch (padding) {
+ case RSA_PKCS1_PADDING:
+ if (!RSA_padding_add_PKCS1_type_1(padded.get(), num, from, flen)) {
+ return 0;
+ }
+ break;
+ case RSA_X931_PADDING:
+ if (!RSA_padding_add_X931(padded.get(), num, from, flen)) {
+ return 0;
+ }
+ break;
+ case RSA_NO_PADDING:
+ if (!RSA_padding_add_none(padded.get(), num, from, flen)) {
+ return 0;
+ }
+ break;
+ default:
+ ALOGE("Unknown padding type: %d", padding);
+ return 0;
+ }
+
+ uint8_t* key_id = reinterpret_cast<uint8_t*>(RSA_get_ex_data(rsa, rsa_key_handle));
+ if (key_id == NULL) {
+ ALOGE("key had no key_id!");
+ return 0;
+ }
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
+ sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
+
+ if (service == NULL) {
+ ALOGE("could not contact keystore");
+ return 0;
+ }
+
+ uint8_t* reply = NULL;
+ size_t replyLen;
+ int32_t ret = service->sign(String16(reinterpret_cast<const char*>(key_id)), padded.get(),
+ num, &reply, &replyLen);
+ if (ret < 0) {
+ ALOGW("There was an error during signing: could not connect");
+ free(reply);
+ return 0;
+ } else if (ret != 0) {
+ ALOGW("Error during signing from keystore: %d", ret);
+ free(reply);
+ return 0;
+ } else if (replyLen <= 0) {
+ ALOGW("No valid signature returned");
+ return 0;
+ }
+
+ memcpy(to, reply, replyLen);
+ free(reply);
+
+ ALOGV("rsa=%p keystore_rsa_priv_enc => returning %p len %llu", rsa, to,
+ (unsigned long long) replyLen);
+ return static_cast<int>(replyLen);
+}
+
+int keystore_rsa_priv_dec(int flen, const unsigned char* from, unsigned char* to, RSA* rsa,
+ int padding) {
+ ALOGV("keystore_rsa_priv_dec(%d, %p, %p, %p, %d)", flen, from, to, rsa, padding);
+
+ uint8_t* key_id = reinterpret_cast<uint8_t*>(RSA_get_ex_data(rsa, rsa_key_handle));
+ if (key_id == NULL) {
+ ALOGE("key had no key_id!");
+ return 0;
+ }
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
+ sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
+
+ if (service == NULL) {
+ ALOGE("could not contact keystore");
+ return 0;
+ }
+
+ int num = RSA_size(rsa);
+
+ uint8_t* reply = NULL;
+ size_t replyLen;
+ int32_t ret = service->sign(String16(reinterpret_cast<const char*>(key_id)), from,
+ flen, &reply, &replyLen);
+ if (ret < 0) {
+ ALOGW("There was an error during rsa_mod_exp: could not connect");
+ return 0;
+ } else if (ret != 0) {
+ ALOGW("Error during sign from keystore: %d", ret);
+ return 0;
+ } else if (replyLen <= 0) {
+ ALOGW("No valid signature returned");
+ return 0;
+ }
+
+ /* Trim off the top zero if it's there */
+ uint8_t* alignedReply;
+ if (*reply == 0x00) {
+ alignedReply = reply + 1;
+ replyLen--;
+ } else {
+ alignedReply = reply;
+ }
+
+ int outSize;
+ switch (padding) {
+ case RSA_PKCS1_PADDING:
+ outSize = RSA_padding_check_PKCS1_type_2(to, num, alignedReply, replyLen, num);
+ break;
+ case RSA_X931_PADDING:
+ outSize = RSA_padding_check_X931(to, num, alignedReply, replyLen, num);
+ break;
+ case RSA_NO_PADDING:
+ outSize = RSA_padding_check_none(to, num, alignedReply, replyLen, num);
+ break;
+ default:
+ ALOGE("Unknown padding type: %d", padding);
+ outSize = -1;
+ break;
+ }
+
+ free(reply);
+
+ ALOGV("rsa=%p keystore_rsa_priv_dec => returning %p len %d", rsa, to, outSize);
+ return outSize;
+}
+
+static RSA_METHOD keystore_rsa_meth = {
+ kKeystoreEngineId,
+ NULL, /* rsa_pub_enc (wrap) */
+ NULL, /* rsa_pub_dec (verification) */
+ keystore_rsa_priv_enc, /* rsa_priv_enc (signing) */
+ keystore_rsa_priv_dec, /* rsa_priv_dec (unwrap) */
+ NULL, /* rsa_mod_exp */
+ NULL, /* bn_mod_exp */
+ NULL, /* init */
+ NULL, /* finish */
+ RSA_FLAG_EXT_PKEY | RSA_FLAG_NO_BLINDING, /* flags */
+ NULL, /* app_data */
+ NULL, /* rsa_sign */
+ NULL, /* rsa_verify */
+ NULL, /* rsa_keygen */
+};
+
+static int register_rsa_methods() {
+ const RSA_METHOD* rsa_meth = RSA_PKCS1_SSLeay();
+
+ keystore_rsa_meth.rsa_pub_enc = rsa_meth->rsa_pub_enc;
+ keystore_rsa_meth.rsa_pub_dec = rsa_meth->rsa_pub_dec;
+ keystore_rsa_meth.rsa_mod_exp = rsa_meth->rsa_mod_exp;
+ keystore_rsa_meth.bn_mod_exp = rsa_meth->bn_mod_exp;
+
+ return 1;
+}
+
+int rsa_pkey_setup(ENGINE *e, EVP_PKEY *pkey, const char *key_id) {
+ Unique_RSA rsa(EVP_PKEY_get1_RSA(pkey));
+ if (!RSA_set_ex_data(rsa.get(), rsa_key_handle, reinterpret_cast<void*>(strdup(key_id)))) {
+ ALOGW("Could not set ex_data for loaded RSA key");
+ return 0;
+ }
+
+ RSA_set_method(rsa.get(), &keystore_rsa_meth);
+ RSA_blinding_off(rsa.get());
+
+ /*
+ * "RSA_set_ENGINE()" should probably be an OpenSSL API. Since it isn't,
+ * and EVP_PKEY_free() calls ENGINE_finish(), we need to call ENGINE_init()
+ * here.
+ */
+ ENGINE_init(e);
+ rsa->engine = e;
+ rsa->flags |= RSA_FLAG_EXT_PKEY;
+
+ return 1;
+}
+
+int rsa_register(ENGINE* e) {
+ if (!ENGINE_set_RSA(e, &keystore_rsa_meth)
+ || !register_rsa_methods()) {
+ ALOGE("Could not set up keystore RSA methods");
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/security/keystore/.clang-format b/security/keystore/.clang-format
new file mode 100644
index 0000000..5747e19
--- /dev/null
+++ b/security/keystore/.clang-format
@@ -0,0 +1,10 @@
+BasedOnStyle: LLVM
+IndentWidth: 4
+UseTab: Never
+BreakBeforeBraces: Attach
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: false
+IndentCaseLabels: false
+ColumnLimit: 100
+PointerBindsToType: true
+SpacesBeforeTrailingComments: 2
diff --git a/security/keystore/Android.mk b/security/keystore/Android.mk
new file mode 100644
index 0000000..059bfd4
--- /dev/null
+++ b/security/keystore/Android.mk
@@ -0,0 +1,130 @@
+#
+# Copyright (C) 2009 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+# This has to be lazy-resolved because it depends on the LOCAL_MODULE_CLASS
+# which varies depending on what is being built.
+define keystore_proto_include
+$(call local-generated-sources-dir)/proto/$(LOCAL_PATH)
+endef
+
+include $(CLEAR_VARS)
+ifeq ($(USE_32_BIT_KEYSTORE), true)
+LOCAL_MULTILIB := 32
+endif
+LOCAL_CFLAGS := -Wall -Wextra -Werror -Wunused
+LOCAL_SRC_FILES := \
+ auth_token_table.cpp \
+ blob.cpp \
+ entropy.cpp \
+ key_store_service.cpp \
+ keyblob_utils.cpp \
+ keystore.cpp \
+ keystore_main.cpp \
+ keystore_utils.cpp \
+ operation.cpp \
+ permissions.cpp \
+ user_state.cpp
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ libcutils \
+ libcrypto \
+ libhardware \
+ libkeystore_binder \
+ liblog \
+ libsoftkeymaster \
+ libutils \
+ libselinux \
+ libsoftkeymasterdevice \
+ libkeymaster_messages \
+ libkeymaster1
+LOCAL_MODULE := keystore
+LOCAL_MODULE_TAGS := optional
+LOCAL_INIT_RC := keystore.rc
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+ifeq ($(USE_32_BIT_KEYSTORE), true)
+LOCAL_MULTILIB := 32
+endif
+LOCAL_CFLAGS := -Wall -Wextra -Werror
+LOCAL_SRC_FILES := keystore_cli.cpp
+LOCAL_SHARED_LIBRARIES := libcutils libcrypto libkeystore_binder libutils liblog libbinder
+LOCAL_MODULE := keystore_cli
+LOCAL_MODULE_TAGS := debug
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+ifeq ($(USE_32_BIT_KEYSTORE), true)
+LOCAL_MULTILIB := 32
+endif
+LOCAL_CFLAGS := -Wall -Wextra -Werror -Wno-unused-parameter -DKEYMASTER_NAME_TAGS
+LOCAL_SRC_FILES := keystore_cli_v2.cpp
+LOCAL_SHARED_LIBRARIES := \
+ libchrome \
+ libkeymaster_messages \
+ libkeystore_binder
+LOCAL_MODULE := keystore_cli_v2
+LOCAL_MODULE_TAGS := debug
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include external/gtest/include
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_EXECUTABLE)
+
+# Library for keystore clients
+include $(CLEAR_VARS)
+ifeq ($(USE_32_BIT_KEYSTORE), true)
+LOCAL_MULTILIB := 32
+endif
+LOCAL_CFLAGS := -Wall -Wextra -Werror
+LOCAL_SRC_FILES := \
+ IKeystoreService.cpp \
+ keyblob_utils.cpp \
+ keystore_client.proto \
+ keystore_client_impl.cpp \
+ keystore_get.cpp
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ libkeymaster_messages \
+ liblog \
+ libprotobuf-cpp-lite \
+ libsoftkeymasterdevice \
+ libutils
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+LOCAL_MODULE := libkeystore_binder
+LOCAL_MODULE_TAGS := optional
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(call keystore_proto_include)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := libbinder
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_SHARED_LIBRARY)
+
+# Library for unit tests
+include $(CLEAR_VARS)
+ifeq ($(USE_32_BIT_KEYSTORE), true)
+LOCAL_MULTILIB := 32
+endif
+LOCAL_CFLAGS := -Wall -Wextra -Werror
+LOCAL_SRC_FILES := auth_token_table.cpp
+LOCAL_MODULE := libkeystore_test
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := libgtest_main
+LOCAL_SHARED_LIBRARIES := libkeymaster_messages
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_STATIC_LIBRARY)
diff --git a/security/keystore/IKeystoreService.cpp b/security/keystore/IKeystoreService.cpp
new file mode 100644
index 0000000..635d3a7
--- /dev/null
+++ b/security/keystore/IKeystoreService.cpp
@@ -0,0 +1,1847 @@
+/*
+**
+** Copyright 2008, 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 <stdint.h>
+#include <sys/limits.h>
+#include <sys/types.h>
+
+#define LOG_TAG "KeystoreService"
+#include <utils/Log.h>
+
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+#include <keystore/IKeystoreService.h>
+
+namespace android {
+
+const ssize_t MAX_GENERATE_ARGS = 3;
+static keymaster_key_param_t* readParamList(const Parcel& in, size_t* length);
+
+KeystoreArg::KeystoreArg(const void* data, size_t len)
+ : mData(data), mSize(len) {
+}
+
+KeystoreArg::~KeystoreArg() {
+}
+
+const void *KeystoreArg::data() const {
+ return mData;
+}
+
+size_t KeystoreArg::size() const {
+ return mSize;
+}
+
+OperationResult::OperationResult() : resultCode(0), token(), handle(0), inputConsumed(0),
+ data(NULL), dataLength(0) {
+}
+
+OperationResult::~OperationResult() {
+}
+
+void OperationResult::readFromParcel(const Parcel& in) {
+ resultCode = in.readInt32();
+ token = in.readStrongBinder();
+ handle = static_cast<keymaster_operation_handle_t>(in.readInt64());
+ inputConsumed = in.readInt32();
+ ssize_t length = in.readInt32();
+ dataLength = 0;
+ if (length > 0) {
+ const void* buf = in.readInplace(length);
+ if (buf) {
+ data.reset(reinterpret_cast<uint8_t*>(malloc(length)));
+ if (data.get()) {
+ memcpy(data.get(), buf, length);
+ dataLength = (size_t) length;
+ } else {
+ ALOGE("Failed to allocate OperationResult buffer");
+ }
+ } else {
+ ALOGE("Failed to readInplace OperationResult data");
+ }
+ }
+ outParams.readFromParcel(in);
+}
+
+void OperationResult::writeToParcel(Parcel* out) const {
+ out->writeInt32(resultCode);
+ out->writeStrongBinder(token);
+ out->writeInt64(handle);
+ out->writeInt32(inputConsumed);
+ out->writeInt32(dataLength);
+ if (dataLength && data) {
+ void* buf = out->writeInplace(dataLength);
+ if (buf) {
+ memcpy(buf, data.get(), dataLength);
+ } else {
+ ALOGE("Failed to writeInplace OperationResult data.");
+ }
+ }
+ outParams.writeToParcel(out);
+}
+
+ExportResult::ExportResult() : resultCode(0), exportData(NULL), dataLength(0) {
+}
+
+ExportResult::~ExportResult() {
+}
+
+void ExportResult::readFromParcel(const Parcel& in) {
+ resultCode = in.readInt32();
+ ssize_t length = in.readInt32();
+ dataLength = 0;
+ if (length > 0) {
+ const void* buf = in.readInplace(length);
+ if (buf) {
+ exportData.reset(reinterpret_cast<uint8_t*>(malloc(length)));
+ if (exportData.get()) {
+ memcpy(exportData.get(), buf, length);
+ dataLength = (size_t) length;
+ } else {
+ ALOGE("Failed to allocate ExportData buffer");
+ }
+ } else {
+ ALOGE("Failed to readInplace ExportData data");
+ }
+ }
+}
+
+void ExportResult::writeToParcel(Parcel* out) const {
+ out->writeInt32(resultCode);
+ out->writeInt32(dataLength);
+ if (exportData && dataLength) {
+ void* buf = out->writeInplace(dataLength);
+ if (buf) {
+ memcpy(buf, exportData.get(), dataLength);
+ } else {
+ ALOGE("Failed to writeInplace ExportResult data.");
+ }
+ }
+}
+
+KeymasterArguments::KeymasterArguments() {
+}
+
+KeymasterArguments::~KeymasterArguments() {
+ keymaster_free_param_values(params.data(), params.size());
+}
+
+void KeymasterArguments::readFromParcel(const Parcel& in) {
+ ssize_t length = in.readInt32();
+ size_t ulength = (size_t) length;
+ if (length < 0) {
+ ulength = 0;
+ }
+ keymaster_free_param_values(params.data(), params.size());
+ params.clear();
+ for(size_t i = 0; i < ulength; i++) {
+ keymaster_key_param_t param;
+ if (!readKeymasterArgumentFromParcel(in, ¶m)) {
+ ALOGE("Error reading keymaster argument from parcel");
+ break;
+ }
+ params.push_back(param);
+ }
+}
+
+void KeymasterArguments::writeToParcel(Parcel* out) const {
+ out->writeInt32(params.size());
+ for (auto param : params) {
+ out->writeInt32(1);
+ writeKeymasterArgumentToParcel(param, out);
+ }
+}
+
+KeyCharacteristics::KeyCharacteristics() {
+ memset((void*) &characteristics, 0, sizeof(characteristics));
+}
+
+KeyCharacteristics::~KeyCharacteristics() {
+ keymaster_free_characteristics(&characteristics);
+}
+
+void KeyCharacteristics::readFromParcel(const Parcel& in) {
+ size_t length = 0;
+ keymaster_key_param_t* params = readParamList(in, &length);
+ characteristics.sw_enforced.params = params;
+ characteristics.sw_enforced.length = length;
+
+ params = readParamList(in, &length);
+ characteristics.hw_enforced.params = params;
+ characteristics.hw_enforced.length = length;
+}
+
+void KeyCharacteristics::writeToParcel(Parcel* out) const {
+ if (characteristics.sw_enforced.params) {
+ out->writeInt32(characteristics.sw_enforced.length);
+ for (size_t i = 0; i < characteristics.sw_enforced.length; i++) {
+ out->writeInt32(1);
+ writeKeymasterArgumentToParcel(characteristics.sw_enforced.params[i], out);
+ }
+ } else {
+ out->writeInt32(0);
+ }
+ if (characteristics.hw_enforced.params) {
+ out->writeInt32(characteristics.hw_enforced.length);
+ for (size_t i = 0; i < characteristics.hw_enforced.length; i++) {
+ out->writeInt32(1);
+ writeKeymasterArgumentToParcel(characteristics.hw_enforced.params[i], out);
+ }
+ } else {
+ out->writeInt32(0);
+ }
+}
+
+KeymasterCertificateChain::KeymasterCertificateChain() {
+ memset(&chain, 0, sizeof(chain));
+}
+
+KeymasterCertificateChain::~KeymasterCertificateChain() {
+ keymaster_free_cert_chain(&chain);
+}
+
+static bool readKeymasterBlob(const Parcel& in, keymaster_blob_t* blob) {
+ if (in.readInt32() != 1) {
+ return false;
+ }
+
+ ssize_t length = in.readInt32();
+ if (length <= 0) {
+ return false;
+ }
+
+ blob->data = reinterpret_cast<const uint8_t*>(malloc(length));
+ if (!blob->data)
+ return false;
+
+ const void* buf = in.readInplace(length);
+ if (!buf)
+ return false;
+
+ blob->data_length = static_cast<size_t>(length);
+ memcpy(const_cast<uint8_t*>(blob->data), buf, length);
+
+ return true;
+}
+
+void KeymasterCertificateChain::readFromParcel(const Parcel& in) {
+ keymaster_free_cert_chain(&chain);
+
+ ssize_t count = in.readInt32();
+ size_t ucount = count;
+ if (count <= 0) {
+ return;
+ }
+
+ chain.entries = reinterpret_cast<keymaster_blob_t*>(malloc(sizeof(keymaster_blob_t) * ucount));
+ if (!chain.entries) {
+ ALOGE("Error allocating memory for certificate chain");
+ return;
+ }
+
+ memset(chain.entries, 0, sizeof(keymaster_blob_t) * ucount);
+ for (size_t i = 0; i < ucount; ++i) {
+ if (!readKeymasterBlob(in, &chain.entries[i])) {
+ ALOGE("Error reading certificate from parcel");
+ keymaster_free_cert_chain(&chain);
+ return;
+ }
+ }
+}
+
+void KeymasterCertificateChain::writeToParcel(Parcel* out) const {
+ out->writeInt32(chain.entry_count);
+ for (size_t i = 0; i < chain.entry_count; ++i) {
+ if (chain.entries[i].data) {
+ out->writeInt32(1); // Tell Java side that object is not NULL
+ out->writeInt32(chain.entries[i].data_length);
+ void* buf = out->writeInplace(chain.entries[i].data_length);
+ if (buf) {
+ memcpy(buf, chain.entries[i].data, chain.entries[i].data_length);
+ } else {
+ ALOGE("Failed to writeInplace keymaster cert chain entry");
+ }
+ } else {
+ out->writeInt32(0); // Tell Java side this object is NULL.
+ ALOGE("Found NULL certificate chain entry");
+ }
+ }
+}
+
+void writeKeymasterArgumentToParcel(const keymaster_key_param_t& param, Parcel* out) {
+ switch (keymaster_tag_get_type(param.tag)) {
+ case KM_ENUM:
+ case KM_ENUM_REP: {
+ out->writeInt32(param.tag);
+ out->writeInt32(param.enumerated);
+ break;
+ }
+ case KM_UINT:
+ case KM_UINT_REP: {
+ out->writeInt32(param.tag);
+ out->writeInt32(param.integer);
+ break;
+ }
+ case KM_ULONG:
+ case KM_ULONG_REP: {
+ out->writeInt32(param.tag);
+ out->writeInt64(param.long_integer);
+ break;
+ }
+ case KM_DATE: {
+ out->writeInt32(param.tag);
+ out->writeInt64(param.date_time);
+ break;
+ }
+ case KM_BOOL: {
+ out->writeInt32(param.tag);
+ break;
+ }
+ case KM_BIGNUM:
+ case KM_BYTES: {
+ out->writeInt32(param.tag);
+ out->writeInt32(param.blob.data_length);
+ void* buf = out->writeInplace(param.blob.data_length);
+ if (buf) {
+ memcpy(buf, param.blob.data, param.blob.data_length);
+ } else {
+ ALOGE("Failed to writeInplace keymaster blob param");
+ }
+ break;
+ }
+ default: {
+ ALOGE("Failed to write argument: Unsupported keymaster_tag_t %d", param.tag);
+ }
+ }
+}
+
+
+bool readKeymasterArgumentFromParcel(const Parcel& in, keymaster_key_param_t* out) {
+ if (in.readInt32() == 0) {
+ return false;
+ }
+ keymaster_tag_t tag = static_cast<keymaster_tag_t>(in.readInt32());
+ switch (keymaster_tag_get_type(tag)) {
+ case KM_ENUM:
+ case KM_ENUM_REP: {
+ uint32_t value = in.readInt32();
+ *out = keymaster_param_enum(tag, value);
+ break;
+ }
+ case KM_UINT:
+ case KM_UINT_REP: {
+ uint32_t value = in.readInt32();
+ *out = keymaster_param_int(tag, value);
+ break;
+ }
+ case KM_ULONG:
+ case KM_ULONG_REP: {
+ uint64_t value = in.readInt64();
+ *out = keymaster_param_long(tag, value);
+ break;
+ }
+ case KM_DATE: {
+ uint64_t value = in.readInt64();
+ *out = keymaster_param_date(tag, value);
+ break;
+ }
+ case KM_BOOL: {
+ *out = keymaster_param_bool(tag);
+ break;
+ }
+ case KM_BIGNUM:
+ case KM_BYTES: {
+ ssize_t length = in.readInt32();
+ uint8_t* data = NULL;
+ size_t ulength = 0;
+ if (length >= 0) {
+ ulength = (size_t) length;
+ // use malloc here so we can use keymaster_free_param_values
+ // consistently.
+ data = reinterpret_cast<uint8_t*>(malloc(ulength));
+ const void* buf = in.readInplace(ulength);
+ if (!buf || !data) {
+ ALOGE("Failed to allocate buffer for keymaster blob param");
+ free(data);
+ return false;
+ }
+ memcpy(data, buf, ulength);
+ }
+ *out = keymaster_param_blob(tag, data, ulength);
+ break;
+ }
+ default: {
+ ALOGE("Unsupported keymaster_tag_t %d", tag);
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * Read a byte array from in. The data at *data is still owned by the parcel
+ */
+static void readByteArray(const Parcel& in, const uint8_t** data, size_t* length) {
+ ssize_t slength = in.readInt32();
+ if (slength > 0) {
+ *data = reinterpret_cast<const uint8_t*>(in.readInplace(slength));
+ if (*data) {
+ *length = static_cast<size_t>(slength);
+ } else {
+ *length = 0;
+ }
+ } else {
+ *data = NULL;
+ *length = 0;
+ }
+}
+
+// Read a keymaster_key_param_t* from a Parcel for use in a
+// keymaster_key_characteristics_t. This will be free'd by calling
+// keymaster_free_key_characteristics.
+static keymaster_key_param_t* readParamList(const Parcel& in, size_t* length) {
+ ssize_t slength = in.readInt32();
+ *length = 0;
+ if (slength < 0) {
+ return NULL;
+ }
+ *length = (size_t) slength;
+ if (*length >= UINT_MAX / sizeof(keymaster_key_param_t)) {
+ return NULL;
+ }
+ keymaster_key_param_t* list =
+ reinterpret_cast<keymaster_key_param_t*>(malloc(*length *
+ sizeof(keymaster_key_param_t)));
+ if (!list) {
+ ALOGD("Failed to allocate buffer for generateKey outCharacteristics");
+ goto err;
+ }
+ for (size_t i = 0; i < *length ; i++) {
+ if (!readKeymasterArgumentFromParcel(in, &list[i])) {
+ ALOGE("Failed to read keymaster argument");
+ keymaster_free_param_values(list, i);
+ goto err;
+ }
+ }
+ return list;
+err:
+ free(list);
+ return NULL;
+}
+
+static std::unique_ptr<keymaster_blob_t> readKeymasterBlob(const Parcel& in) {
+ std::unique_ptr<keymaster_blob_t> blob (new keymaster_blob_t);
+ if (!readKeymasterBlob(in, blob.get())) {
+ blob.reset();
+ }
+ return blob;
+}
+
+class BpKeystoreService: public BpInterface<IKeystoreService>
+{
+public:
+ explicit BpKeystoreService(const sp<IBinder>& impl)
+ : BpInterface<IKeystoreService>(impl)
+ {
+ }
+
+ // test ping
+ virtual int32_t getState(int32_t userId)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeInt32(userId);
+ status_t status = remote()->transact(BnKeystoreService::GET_STATE, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("getState() could not contact remote: %d\n", status);
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("getState() caught exception %d\n", err);
+ return -1;
+ }
+ return ret;
+ }
+
+ virtual int32_t get(const String16& name, uint8_t** item, size_t* itemLength)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeString16(name);
+ status_t status = remote()->transact(BnKeystoreService::GET, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("get() could not contact remote: %d\n", status);
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ ssize_t len = reply.readInt32();
+ if (len >= 0 && (size_t) len <= reply.dataAvail()) {
+ size_t ulen = (size_t) len;
+ const void* buf = reply.readInplace(ulen);
+ *item = (uint8_t*) malloc(ulen);
+ if (*item != NULL) {
+ memcpy(*item, buf, ulen);
+ *itemLength = ulen;
+ } else {
+ ALOGE("out of memory allocating output array in get");
+ *itemLength = 0;
+ }
+ } else {
+ *itemLength = 0;
+ }
+ if (err < 0) {
+ ALOGD("get() caught exception %d\n", err);
+ return -1;
+ }
+ return 0;
+ }
+
+ virtual int32_t insert(const String16& name, const uint8_t* item, size_t itemLength, int uid,
+ int32_t flags)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeString16(name);
+ data.writeInt32(itemLength);
+ void* buf = data.writeInplace(itemLength);
+ memcpy(buf, item, itemLength);
+ data.writeInt32(uid);
+ data.writeInt32(flags);
+ status_t status = remote()->transact(BnKeystoreService::INSERT, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("import() could not contact remote: %d\n", status);
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("import() caught exception %d\n", err);
+ return -1;
+ }
+ return ret;
+ }
+
+ virtual int32_t del(const String16& name, int uid)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeString16(name);
+ data.writeInt32(uid);
+ status_t status = remote()->transact(BnKeystoreService::DEL, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("del() could not contact remote: %d\n", status);
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("del() caught exception %d\n", err);
+ return -1;
+ }
+ return ret;
+ }
+
+ virtual int32_t exist(const String16& name, int uid)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeString16(name);
+ data.writeInt32(uid);
+ status_t status = remote()->transact(BnKeystoreService::EXIST, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("exist() could not contact remote: %d\n", status);
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("exist() caught exception %d\n", err);
+ return -1;
+ }
+ return ret;
+ }
+
+ virtual int32_t list(const String16& prefix, int uid, Vector<String16>* matches)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeString16(prefix);
+ data.writeInt32(uid);
+ status_t status = remote()->transact(BnKeystoreService::LIST, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("list() could not contact remote: %d\n", status);
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t numMatches = reply.readInt32();
+ for (int32_t i = 0; i < numMatches; i++) {
+ matches->push(reply.readString16());
+ }
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("list() caught exception %d\n", err);
+ return -1;
+ }
+ return ret;
+ }
+
+ virtual int32_t reset()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ status_t status = remote()->transact(BnKeystoreService::RESET, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("reset() could not contact remote: %d\n", status);
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("reset() caught exception %d\n", err);
+ return -1;
+ }
+ return ret;
+ }
+
+ virtual int32_t onUserPasswordChanged(int32_t userId, const String16& password)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeInt32(userId);
+ data.writeString16(password);
+ status_t status = remote()->transact(BnKeystoreService::ON_USER_PASSWORD_CHANGED, data,
+ &reply);
+ if (status != NO_ERROR) {
+ ALOGD("onUserPasswordChanged() could not contact remote: %d\n", status);
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("onUserPasswordChanged() caught exception %d\n", err);
+ return -1;
+ }
+ return ret;
+ }
+
+ virtual int32_t lock(int32_t userId)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeInt32(userId);
+ status_t status = remote()->transact(BnKeystoreService::LOCK, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("lock() could not contact remote: %d\n", status);
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("lock() caught exception %d\n", err);
+ return -1;
+ }
+ return ret;
+ }
+
+ virtual int32_t unlock(int32_t userId, const String16& password)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeInt32(userId);
+ data.writeString16(password);
+ status_t status = remote()->transact(BnKeystoreService::UNLOCK, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("unlock() could not contact remote: %d\n", status);
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("unlock() caught exception %d\n", err);
+ return -1;
+ }
+ return ret;
+ }
+
+ virtual bool isEmpty(int32_t userId)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeInt32(userId);
+ status_t status = remote()->transact(BnKeystoreService::IS_EMPTY, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("isEmpty() could not contact remote: %d\n", status);
+ return false;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("isEmpty() caught exception %d\n", err);
+ return false;
+ }
+ return ret != 0;
+ }
+
+ virtual int32_t generate(const String16& name, int32_t uid, int32_t keyType, int32_t keySize,
+ int32_t flags, Vector<sp<KeystoreArg> >* args)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeString16(name);
+ data.writeInt32(uid);
+ data.writeInt32(keyType);
+ data.writeInt32(keySize);
+ data.writeInt32(flags);
+ data.writeInt32(1);
+ data.writeInt32(args->size());
+ for (Vector<sp<KeystoreArg> >::iterator it = args->begin(); it != args->end(); ++it) {
+ sp<KeystoreArg> item = *it;
+ size_t keyLength = item->size();
+ data.writeInt32(keyLength);
+ void* buf = data.writeInplace(keyLength);
+ memcpy(buf, item->data(), keyLength);
+ }
+ status_t status = remote()->transact(BnKeystoreService::GENERATE, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("generate() could not contact remote: %d\n", status);
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("generate() caught exception %d\n", err);
+ return -1;
+ }
+ return ret;
+ }
+
+ virtual int32_t import(const String16& name, const uint8_t* key, size_t keyLength, int uid,
+ int flags)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeString16(name);
+ data.writeInt32(keyLength);
+ void* buf = data.writeInplace(keyLength);
+ memcpy(buf, key, keyLength);
+ data.writeInt32(uid);
+ data.writeInt32(flags);
+ status_t status = remote()->transact(BnKeystoreService::IMPORT, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("import() could not contact remote: %d\n", status);
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("import() caught exception %d\n", err);
+ return -1;
+ }
+ return ret;
+ }
+
+ virtual int32_t sign(const String16& name, const uint8_t* in, size_t inLength, uint8_t** out,
+ size_t* outLength)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeString16(name);
+ data.writeInt32(inLength);
+ void* buf = data.writeInplace(inLength);
+ memcpy(buf, in, inLength);
+ status_t status = remote()->transact(BnKeystoreService::SIGN, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("import() could not contact remote: %d\n", status);
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ ssize_t len = reply.readInt32();
+ if (len >= 0 && (size_t) len <= reply.dataAvail()) {
+ size_t ulen = (size_t) len;
+ const void* outBuf = reply.readInplace(ulen);
+ *out = (uint8_t*) malloc(ulen);
+ if (*out != NULL) {
+ memcpy((void*) *out, outBuf, ulen);
+ *outLength = ulen;
+ } else {
+ ALOGE("out of memory allocating output array in sign");
+ *outLength = 0;
+ }
+ } else {
+ *outLength = 0;
+ }
+ if (err < 0) {
+ ALOGD("import() caught exception %d\n", err);
+ return -1;
+ }
+ return 0;
+ }
+
+ virtual int32_t verify(const String16& name, const uint8_t* in, size_t inLength,
+ const uint8_t* signature, size_t signatureLength)
+ {
+ Parcel data, reply;
+ void* buf;
+
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeString16(name);
+ data.writeInt32(inLength);
+ buf = data.writeInplace(inLength);
+ memcpy(buf, in, inLength);
+ data.writeInt32(signatureLength);
+ buf = data.writeInplace(signatureLength);
+ memcpy(buf, signature, signatureLength);
+ status_t status = remote()->transact(BnKeystoreService::VERIFY, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("verify() could not contact remote: %d\n", status);
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("verify() caught exception %d\n", err);
+ return -1;
+ }
+ return ret;
+ }
+
+ virtual int32_t get_pubkey(const String16& name, uint8_t** pubkey, size_t* pubkeyLength)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeString16(name);
+ status_t status = remote()->transact(BnKeystoreService::GET_PUBKEY, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("get_pubkey() could not contact remote: %d\n", status);
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ ssize_t len = reply.readInt32();
+ if (len >= 0 && (size_t) len <= reply.dataAvail()) {
+ size_t ulen = (size_t) len;
+ const void* buf = reply.readInplace(ulen);
+ *pubkey = (uint8_t*) malloc(ulen);
+ if (*pubkey != NULL) {
+ memcpy(*pubkey, buf, ulen);
+ *pubkeyLength = ulen;
+ } else {
+ ALOGE("out of memory allocating output array in get_pubkey");
+ *pubkeyLength = 0;
+ }
+ } else {
+ *pubkeyLength = 0;
+ }
+ if (err < 0) {
+ ALOGD("get_pubkey() caught exception %d\n", err);
+ return -1;
+ }
+ return 0;
+ }
+
+ virtual int32_t grant(const String16& name, int32_t granteeUid)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeString16(name);
+ data.writeInt32(granteeUid);
+ status_t status = remote()->transact(BnKeystoreService::GRANT, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("grant() could not contact remote: %d\n", status);
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("grant() caught exception %d\n", err);
+ return -1;
+ }
+ return ret;
+ }
+
+ virtual int32_t ungrant(const String16& name, int32_t granteeUid)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeString16(name);
+ data.writeInt32(granteeUid);
+ status_t status = remote()->transact(BnKeystoreService::UNGRANT, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("ungrant() could not contact remote: %d\n", status);
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("ungrant() caught exception %d\n", err);
+ return -1;
+ }
+ return ret;
+ }
+
+ int64_t getmtime(const String16& name)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeString16(name);
+ status_t status = remote()->transact(BnKeystoreService::GETMTIME, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("getmtime() could not contact remote: %d\n", status);
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ int64_t ret = reply.readInt64();
+ if (err < 0) {
+ ALOGD("getmtime() caught exception %d\n", err);
+ return -1;
+ }
+ return ret;
+ }
+
+ virtual int32_t duplicate(const String16& srcKey, int32_t srcUid, const String16& destKey,
+ int32_t destUid)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeString16(srcKey);
+ data.writeInt32(srcUid);
+ data.writeString16(destKey);
+ data.writeInt32(destUid);
+ status_t status = remote()->transact(BnKeystoreService::DUPLICATE, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("duplicate() could not contact remote: %d\n", status);
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("duplicate() caught exception %d\n", err);
+ return -1;
+ }
+ return ret;
+ }
+
+ virtual int32_t is_hardware_backed(const String16& keyType)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeString16(keyType);
+ status_t status = remote()->transact(BnKeystoreService::IS_HARDWARE_BACKED, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("is_hardware_backed() could not contact remote: %d\n", status);
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("is_hardware_backed() caught exception %d\n", err);
+ return -1;
+ }
+ return ret;
+ }
+
+ virtual int32_t clear_uid(int64_t uid)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeInt64(uid);
+ status_t status = remote()->transact(BnKeystoreService::CLEAR_UID, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("clear_uid() could not contact remote: %d\n", status);
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("clear_uid() caught exception %d\n", err);
+ return -1;
+ }
+ return ret;
+ }
+
+ virtual int32_t addRngEntropy(const uint8_t* buf, size_t bufLength)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeByteArray(bufLength, buf);
+ status_t status = remote()->transact(BnKeystoreService::ADD_RNG_ENTROPY, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("addRngEntropy() could not contact remote: %d\n", status);
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("addRngEntropy() caught exception %d\n", err);
+ return -1;
+ }
+ return ret;
+ };
+
+ virtual int32_t generateKey(const String16& name, const KeymasterArguments& params,
+ const uint8_t* entropy, size_t entropyLength, int uid, int flags,
+ KeyCharacteristics* outCharacteristics)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeString16(name);
+ data.writeInt32(1);
+ params.writeToParcel(&data);
+ data.writeByteArray(entropyLength, entropy);
+ data.writeInt32(uid);
+ data.writeInt32(flags);
+ status_t status = remote()->transact(BnKeystoreService::GENERATE_KEY, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("generateKey() could not contact remote: %d\n", status);
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("generateKey() caught exception %d\n", err);
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+ if (reply.readInt32() != 0 && outCharacteristics) {
+ outCharacteristics->readFromParcel(reply);
+ }
+ return ret;
+ }
+ virtual int32_t getKeyCharacteristics(const String16& name,
+ const keymaster_blob_t* clientId,
+ const keymaster_blob_t* appData,
+ KeyCharacteristics* outCharacteristics)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeString16(name);
+ if (clientId) {
+ data.writeByteArray(clientId->data_length, clientId->data);
+ } else {
+ data.writeInt32(-1);
+ }
+ if (appData) {
+ data.writeByteArray(appData->data_length, appData->data);
+ } else {
+ data.writeInt32(-1);
+ }
+ status_t status = remote()->transact(BnKeystoreService::GET_KEY_CHARACTERISTICS,
+ data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("getKeyCharacteristics() could not contact remote: %d\n", status);
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("getKeyCharacteristics() caught exception %d\n", err);
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+ if (reply.readInt32() != 0 && outCharacteristics) {
+ outCharacteristics->readFromParcel(reply);
+ }
+ return ret;
+ }
+ virtual int32_t importKey(const String16& name, const KeymasterArguments& params,
+ keymaster_key_format_t format, const uint8_t *keyData,
+ size_t keyLength, int uid, int flags,
+ KeyCharacteristics* outCharacteristics)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeString16(name);
+ data.writeInt32(1);
+ params.writeToParcel(&data);
+ data.writeInt32(format);
+ data.writeByteArray(keyLength, keyData);
+ data.writeInt32(uid);
+ data.writeInt32(flags);
+ status_t status = remote()->transact(BnKeystoreService::IMPORT_KEY, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("importKey() could not contact remote: %d\n", status);
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("importKey() caught exception %d\n", err);
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+ if (reply.readInt32() != 0 && outCharacteristics) {
+ outCharacteristics->readFromParcel(reply);
+ }
+ return ret;
+ }
+
+ virtual void exportKey(const String16& name, keymaster_key_format_t format,
+ const keymaster_blob_t* clientId,
+ const keymaster_blob_t* appData, ExportResult* result)
+ {
+ if (!result) {
+ return;
+ }
+
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeString16(name);
+ data.writeInt32(format);
+ if (clientId) {
+ data.writeByteArray(clientId->data_length, clientId->data);
+ } else {
+ data.writeInt32(-1);
+ }
+ if (appData) {
+ data.writeByteArray(appData->data_length, appData->data);
+ } else {
+ data.writeInt32(-1);
+ }
+ status_t status = remote()->transact(BnKeystoreService::EXPORT_KEY, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("exportKey() could not contact remote: %d\n", status);
+ result->resultCode = KM_ERROR_UNKNOWN_ERROR;
+ return;
+ }
+ int32_t err = reply.readExceptionCode();
+ if (err < 0) {
+ ALOGD("exportKey() caught exception %d\n", err);
+ result->resultCode = KM_ERROR_UNKNOWN_ERROR;
+ return;
+ }
+ if (reply.readInt32() != 0) {
+ result->readFromParcel(reply);
+ }
+ }
+
+ virtual void begin(const sp<IBinder>& appToken, const String16& name,
+ keymaster_purpose_t purpose, bool pruneable,
+ const KeymasterArguments& params, const uint8_t* entropy,
+ size_t entropyLength, OperationResult* result)
+ {
+ if (!result) {
+ return;
+ }
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeStrongBinder(appToken);
+ data.writeString16(name);
+ data.writeInt32(purpose);
+ data.writeInt32(pruneable ? 1 : 0);
+ data.writeInt32(1);
+ params.writeToParcel(&data);
+ data.writeByteArray(entropyLength, entropy);
+ status_t status = remote()->transact(BnKeystoreService::BEGIN, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("begin() could not contact remote: %d\n", status);
+ result->resultCode = KM_ERROR_UNKNOWN_ERROR;
+ return;
+ }
+ int32_t err = reply.readExceptionCode();
+ if (err < 0) {
+ ALOGD("begin() caught exception %d\n", err);
+ result->resultCode = KM_ERROR_UNKNOWN_ERROR;
+ return;
+ }
+ if (reply.readInt32() != 0) {
+ result->readFromParcel(reply);
+ }
+ }
+
+ virtual void update(const sp<IBinder>& token, const KeymasterArguments& params,
+ const uint8_t* opData, size_t dataLength, OperationResult* result)
+ {
+ if (!result) {
+ return;
+ }
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeStrongBinder(token);
+ data.writeInt32(1);
+ params.writeToParcel(&data);
+ data.writeByteArray(dataLength, opData);
+ status_t status = remote()->transact(BnKeystoreService::UPDATE, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("update() could not contact remote: %d\n", status);
+ result->resultCode = KM_ERROR_UNKNOWN_ERROR;
+ return;
+ }
+ int32_t err = reply.readExceptionCode();
+ if (err < 0) {
+ ALOGD("update() caught exception %d\n", err);
+ result->resultCode = KM_ERROR_UNKNOWN_ERROR;
+ return;
+ }
+ if (reply.readInt32() != 0) {
+ result->readFromParcel(reply);
+ }
+ }
+
+ virtual void finish(const sp<IBinder>& token, const KeymasterArguments& params,
+ const uint8_t* signature, size_t signatureLength,
+ const uint8_t* entropy, size_t entropyLength,
+ OperationResult* result)
+ {
+ if (!result) {
+ return;
+ }
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeStrongBinder(token);
+ data.writeInt32(1);
+ params.writeToParcel(&data);
+ data.writeByteArray(signatureLength, signature);
+ data.writeByteArray(entropyLength, entropy);
+ status_t status = remote()->transact(BnKeystoreService::FINISH, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("finish() could not contact remote: %d\n", status);
+ result->resultCode = KM_ERROR_UNKNOWN_ERROR;
+ return;
+ }
+ int32_t err = reply.readExceptionCode();
+ if (err < 0) {
+ ALOGD("finish() caught exception %d\n", err);
+ result->resultCode = KM_ERROR_UNKNOWN_ERROR;
+ return;
+ }
+ if (reply.readInt32() != 0) {
+ result->readFromParcel(reply);
+ }
+ }
+
+ virtual int32_t abort(const sp<IBinder>& token)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeStrongBinder(token);
+ status_t status = remote()->transact(BnKeystoreService::ABORT, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("abort() could not contact remote: %d\n", status);
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("abort() caught exception %d\n", err);
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+ return ret;
+ }
+
+ virtual bool isOperationAuthorized(const sp<IBinder>& token)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeStrongBinder(token);
+ status_t status = remote()->transact(BnKeystoreService::IS_OPERATION_AUTHORIZED, data,
+ &reply);
+ if (status != NO_ERROR) {
+ ALOGD("isOperationAuthorized() could not contact remote: %d\n", status);
+ return false;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("isOperationAuthorized() caught exception %d\n", err);
+ return false;
+ }
+ return ret == 1;
+ }
+
+ virtual int32_t addAuthToken(const uint8_t* token, size_t length)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeByteArray(length, token);
+ status_t status = remote()->transact(BnKeystoreService::ADD_AUTH_TOKEN, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("addAuthToken() could not contact remote: %d\n", status);
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("addAuthToken() caught exception %d\n", err);
+ return -1;
+ }
+ return ret;
+ };
+
+ virtual int32_t onUserAdded(int32_t userId, int32_t parentId)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeInt32(userId);
+ data.writeInt32(parentId);
+ status_t status = remote()->transact(BnKeystoreService::ON_USER_ADDED, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("onUserAdded() could not contact remote: %d\n", status);
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("onUserAdded() caught exception %d\n", err);
+ return -1;
+ }
+ return ret;
+ }
+
+ virtual int32_t onUserRemoved(int32_t userId)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeInt32(userId);
+ status_t status = remote()->transact(BnKeystoreService::ON_USER_REMOVED, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("onUserRemoved() could not contact remote: %d\n", status);
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("onUserRemoved() caught exception %d\n", err);
+ return -1;
+ }
+ return ret;
+ }
+
+ virtual int32_t attestKey(const String16& name, const KeymasterArguments& params,
+ KeymasterCertificateChain* outChain) {
+ if (!outChain)
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+
+ Parcel data, reply;
+ data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+ data.writeString16(name);
+ data.writeInt32(1); // params is not NULL.
+ params.writeToParcel(&data);
+
+ status_t status = remote()->transact(BnKeystoreService::ATTEST_KEY, data, &reply);
+ if (status != NO_ERROR) {
+ ALOGD("attestkey() count not contact remote: %d\n", status);
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+ int32_t err = reply.readExceptionCode();
+ int32_t ret = reply.readInt32();
+ if (err < 0) {
+ ALOGD("attestKey() caught exception %d\n", err);
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+ if (reply.readInt32() != 0) {
+ outChain->readFromParcel(reply);
+ }
+ return ret;
+ }
+
+};
+
+IMPLEMENT_META_INTERFACE(KeystoreService, "android.security.IKeystoreService");
+
+// ----------------------------------------------------------------------
+
+status_t BnKeystoreService::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case GET_STATE: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ int32_t userId = data.readInt32();
+ int32_t ret = getState(userId);
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ } break;
+ case GET: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ String16 name = data.readString16();
+ void* out = NULL;
+ size_t outSize = 0;
+ int32_t ret = get(name, (uint8_t**) &out, &outSize);
+ reply->writeNoException();
+ if (ret == 1) {
+ reply->writeInt32(outSize);
+ void* buf = reply->writeInplace(outSize);
+ memcpy(buf, out, outSize);
+ free(out);
+ } else {
+ reply->writeInt32(-1);
+ }
+ return NO_ERROR;
+ } break;
+ case INSERT: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ String16 name = data.readString16();
+ ssize_t inSize = data.readInt32();
+ const void* in;
+ if (inSize >= 0 && (size_t) inSize <= data.dataAvail()) {
+ in = data.readInplace(inSize);
+ } else {
+ in = NULL;
+ inSize = 0;
+ }
+ int uid = data.readInt32();
+ int32_t flags = data.readInt32();
+ int32_t ret = insert(name, (const uint8_t*) in, (size_t) inSize, uid, flags);
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ } break;
+ case DEL: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ String16 name = data.readString16();
+ int uid = data.readInt32();
+ int32_t ret = del(name, uid);
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ } break;
+ case EXIST: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ String16 name = data.readString16();
+ int uid = data.readInt32();
+ int32_t ret = exist(name, uid);
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ } break;
+ case LIST: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ String16 prefix = data.readString16();
+ int uid = data.readInt32();
+ Vector<String16> matches;
+ int32_t ret = list(prefix, uid, &matches);
+ reply->writeNoException();
+ reply->writeInt32(matches.size());
+ Vector<String16>::const_iterator it = matches.begin();
+ for (; it != matches.end(); ++it) {
+ reply->writeString16(*it);
+ }
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ } break;
+ case RESET: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ int32_t ret = reset();
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ } break;
+ case ON_USER_PASSWORD_CHANGED: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ int32_t userId = data.readInt32();
+ String16 pass = data.readString16();
+ int32_t ret = onUserPasswordChanged(userId, pass);
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ } break;
+ case LOCK: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ int32_t userId = data.readInt32();
+ int32_t ret = lock(userId);
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ } break;
+ case UNLOCK: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ int32_t userId = data.readInt32();
+ String16 pass = data.readString16();
+ int32_t ret = unlock(userId, pass);
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ } break;
+ case IS_EMPTY: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ int32_t userId = data.readInt32();
+ bool ret = isEmpty(userId);
+ reply->writeNoException();
+ reply->writeInt32(ret ? 1 : 0);
+ return NO_ERROR;
+ } break;
+ case GENERATE: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ String16 name = data.readString16();
+ int32_t uid = data.readInt32();
+ int32_t keyType = data.readInt32();
+ int32_t keySize = data.readInt32();
+ int32_t flags = data.readInt32();
+ Vector<sp<KeystoreArg> > args;
+ int32_t argsPresent = data.readInt32();
+ if (argsPresent == 1) {
+ ssize_t numArgs = data.readInt32();
+ if (numArgs > MAX_GENERATE_ARGS) {
+ return BAD_VALUE;
+ }
+ if (numArgs > 0) {
+ for (size_t i = 0; i < (size_t) numArgs; i++) {
+ ssize_t inSize = data.readInt32();
+ if (inSize >= 0 && (size_t) inSize <= data.dataAvail()) {
+ sp<KeystoreArg> arg = new KeystoreArg(data.readInplace(inSize),
+ inSize);
+ args.push_back(arg);
+ } else {
+ args.push_back(NULL);
+ }
+ }
+ }
+ }
+ int32_t ret = generate(name, uid, keyType, keySize, flags, &args);
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ } break;
+ case IMPORT: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ String16 name = data.readString16();
+ ssize_t inSize = data.readInt32();
+ const void* in;
+ if (inSize >= 0 && (size_t) inSize <= data.dataAvail()) {
+ in = data.readInplace(inSize);
+ } else {
+ in = NULL;
+ inSize = 0;
+ }
+ int uid = data.readInt32();
+ int32_t flags = data.readInt32();
+ int32_t ret = import(name, (const uint8_t*) in, (size_t) inSize, uid, flags);
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ } break;
+ case SIGN: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ String16 name = data.readString16();
+ ssize_t inSize = data.readInt32();
+ const void* in;
+ if (inSize >= 0 && (size_t) inSize <= data.dataAvail()) {
+ in = data.readInplace(inSize);
+ } else {
+ in = NULL;
+ inSize = 0;
+ }
+ void* out = NULL;
+ size_t outSize = 0;
+ int32_t ret = sign(name, (const uint8_t*) in, (size_t) inSize, (uint8_t**) &out, &outSize);
+ reply->writeNoException();
+ if (outSize > 0 && out != NULL) {
+ reply->writeInt32(outSize);
+ void* buf = reply->writeInplace(outSize);
+ memcpy(buf, out, outSize);
+ delete[] reinterpret_cast<uint8_t*>(out);
+ } else {
+ reply->writeInt32(-1);
+ }
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ } break;
+ case VERIFY: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ String16 name = data.readString16();
+ ssize_t inSize = data.readInt32();
+ const void* in;
+ if (inSize >= 0 && (size_t) inSize <= data.dataAvail()) {
+ in = data.readInplace(inSize);
+ } else {
+ in = NULL;
+ inSize = 0;
+ }
+ ssize_t sigSize = data.readInt32();
+ const void* sig;
+ if (sigSize >= 0 && (size_t) sigSize <= data.dataAvail()) {
+ sig = data.readInplace(sigSize);
+ } else {
+ sig = NULL;
+ sigSize = 0;
+ }
+ bool ret = verify(name, (const uint8_t*) in, (size_t) inSize, (const uint8_t*) sig,
+ (size_t) sigSize);
+ reply->writeNoException();
+ reply->writeInt32(ret ? 1 : 0);
+ return NO_ERROR;
+ } break;
+ case GET_PUBKEY: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ String16 name = data.readString16();
+ void* out = NULL;
+ size_t outSize = 0;
+ int32_t ret = get_pubkey(name, (unsigned char**) &out, &outSize);
+ reply->writeNoException();
+ if (outSize > 0 && out != NULL) {
+ reply->writeInt32(outSize);
+ void* buf = reply->writeInplace(outSize);
+ memcpy(buf, out, outSize);
+ free(out);
+ } else {
+ reply->writeInt32(-1);
+ }
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ } break;
+ case GRANT: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ String16 name = data.readString16();
+ int32_t granteeUid = data.readInt32();
+ int32_t ret = grant(name, granteeUid);
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ } break;
+ case UNGRANT: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ String16 name = data.readString16();
+ int32_t granteeUid = data.readInt32();
+ int32_t ret = ungrant(name, granteeUid);
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ } break;
+ case GETMTIME: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ String16 name = data.readString16();
+ int64_t ret = getmtime(name);
+ reply->writeNoException();
+ reply->writeInt64(ret);
+ return NO_ERROR;
+ } break;
+ case DUPLICATE: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ String16 srcKey = data.readString16();
+ int32_t srcUid = data.readInt32();
+ String16 destKey = data.readString16();
+ int32_t destUid = data.readInt32();
+ int32_t ret = duplicate(srcKey, srcUid, destKey, destUid);
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ } break;
+ case IS_HARDWARE_BACKED: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ String16 keyType = data.readString16();
+ int32_t ret = is_hardware_backed(keyType);
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ }
+ case CLEAR_UID: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ int64_t uid = data.readInt64();
+ int32_t ret = clear_uid(uid);
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ }
+ case ADD_RNG_ENTROPY: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ const uint8_t* bytes = NULL;
+ size_t size = 0;
+ readByteArray(data, &bytes, &size);
+ int32_t ret = addRngEntropy(bytes, size);
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ }
+ case GENERATE_KEY: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ String16 name = data.readString16();
+ KeymasterArguments args;
+ if (data.readInt32() != 0) {
+ args.readFromParcel(data);
+ }
+ const uint8_t* entropy = NULL;
+ size_t entropyLength = 0;
+ readByteArray(data, &entropy, &entropyLength);
+ int32_t uid = data.readInt32();
+ int32_t flags = data.readInt32();
+ KeyCharacteristics outCharacteristics;
+ int32_t ret = generateKey(name, args, entropy, entropyLength, uid, flags,
+ &outCharacteristics);
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ reply->writeInt32(1);
+ outCharacteristics.writeToParcel(reply);
+ return NO_ERROR;
+ }
+ case GET_KEY_CHARACTERISTICS: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ String16 name = data.readString16();
+ std::unique_ptr<keymaster_blob_t> clientId = readKeymasterBlob(data);
+ std::unique_ptr<keymaster_blob_t> appData = readKeymasterBlob(data);
+ KeyCharacteristics outCharacteristics;
+ int ret = getKeyCharacteristics(name, clientId.get(), appData.get(),
+ &outCharacteristics);
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ reply->writeInt32(1);
+ outCharacteristics.writeToParcel(reply);
+ return NO_ERROR;
+ }
+ case IMPORT_KEY: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ String16 name = data.readString16();
+ KeymasterArguments args;
+ if (data.readInt32() != 0) {
+ args.readFromParcel(data);
+ }
+ keymaster_key_format_t format = static_cast<keymaster_key_format_t>(data.readInt32());
+ const uint8_t* keyData = NULL;
+ size_t keyLength = 0;
+ readByteArray(data, &keyData, &keyLength);
+ int32_t uid = data.readInt32();
+ int32_t flags = data.readInt32();
+ KeyCharacteristics outCharacteristics;
+ int32_t ret = importKey(name, args, format, keyData, keyLength, uid, flags,
+ &outCharacteristics);
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ reply->writeInt32(1);
+ outCharacteristics.writeToParcel(reply);
+
+ return NO_ERROR;
+ }
+ case EXPORT_KEY: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ String16 name = data.readString16();
+ keymaster_key_format_t format = static_cast<keymaster_key_format_t>(data.readInt32());
+ std::unique_ptr<keymaster_blob_t> clientId = readKeymasterBlob(data);
+ std::unique_ptr<keymaster_blob_t> appData = readKeymasterBlob(data);
+ ExportResult result;
+ exportKey(name, format, clientId.get(), appData.get(), &result);
+ reply->writeNoException();
+ reply->writeInt32(1);
+ result.writeToParcel(reply);
+
+ return NO_ERROR;
+ }
+ case BEGIN: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ sp<IBinder> token = data.readStrongBinder();
+ String16 name = data.readString16();
+ keymaster_purpose_t purpose = static_cast<keymaster_purpose_t>(data.readInt32());
+ bool pruneable = data.readInt32() != 0;
+ KeymasterArguments args;
+ if (data.readInt32() != 0) {
+ args.readFromParcel(data);
+ }
+ const uint8_t* entropy = NULL;
+ size_t entropyLength = 0;
+ readByteArray(data, &entropy, &entropyLength);
+ OperationResult result;
+ begin(token, name, purpose, pruneable, args, entropy, entropyLength, &result);
+ reply->writeNoException();
+ reply->writeInt32(1);
+ result.writeToParcel(reply);
+
+ return NO_ERROR;
+ }
+ case UPDATE: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ sp<IBinder> token = data.readStrongBinder();
+ KeymasterArguments args;
+ if (data.readInt32() != 0) {
+ args.readFromParcel(data);
+ }
+ const uint8_t* buf = NULL;
+ size_t bufLength = 0;
+ readByteArray(data, &buf, &bufLength);
+ OperationResult result;
+ update(token, args, buf, bufLength, &result);
+ reply->writeNoException();
+ reply->writeInt32(1);
+ result.writeToParcel(reply);
+
+ return NO_ERROR;
+ }
+ case FINISH: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ sp<IBinder> token = data.readStrongBinder();
+ KeymasterArguments args;
+ if (data.readInt32() != 0) {
+ args.readFromParcel(data);
+ }
+ const uint8_t* signature = NULL;
+ size_t signatureLength = 0;
+ readByteArray(data, &signature, &signatureLength);
+ const uint8_t* entropy = NULL;
+ size_t entropyLength = 0;
+ readByteArray(data, &entropy, &entropyLength);
+ OperationResult result;
+ finish(token, args, signature, signatureLength, entropy, entropyLength, &result);
+ reply->writeNoException();
+ reply->writeInt32(1);
+ result.writeToParcel(reply);
+
+ return NO_ERROR;
+ }
+ case ABORT: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ sp<IBinder> token = data.readStrongBinder();
+ int32_t result = abort(token);
+ reply->writeNoException();
+ reply->writeInt32(result);
+
+ return NO_ERROR;
+ }
+ case IS_OPERATION_AUTHORIZED: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ sp<IBinder> token = data.readStrongBinder();
+ bool result = isOperationAuthorized(token);
+ reply->writeNoException();
+ reply->writeInt32(result ? 1 : 0);
+
+ return NO_ERROR;
+ }
+ case ADD_AUTH_TOKEN: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ const uint8_t* token_bytes = NULL;
+ size_t size = 0;
+ readByteArray(data, &token_bytes, &size);
+ int32_t result = addAuthToken(token_bytes, size);
+ reply->writeNoException();
+ reply->writeInt32(result);
+
+ return NO_ERROR;
+ }
+ case ON_USER_ADDED: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ int32_t userId = data.readInt32();
+ int32_t parentId = data.readInt32();
+ int32_t result = onUserAdded(userId, parentId);
+ reply->writeNoException();
+ reply->writeInt32(result);
+
+ return NO_ERROR;
+ }
+ case ON_USER_REMOVED: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ int32_t userId = data.readInt32();
+ int32_t result = onUserRemoved(userId);
+ reply->writeNoException();
+ reply->writeInt32(result);
+
+ return NO_ERROR;
+ }
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/security/keystore/auth_token_table.cpp b/security/keystore/auth_token_table.cpp
new file mode 100644
index 0000000..c6e5843
--- /dev/null
+++ b/security/keystore/auth_token_table.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2015 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 "auth_token_table.h"
+
+#include <assert.h>
+#include <time.h>
+
+#include <algorithm>
+
+#include <keymaster/android_keymaster_utils.h>
+#include <keymaster/logger.h>
+
+namespace keymaster {
+
+//
+// Some trivial template wrappers around std algorithms, so they take containers not ranges.
+//
+template <typename Container, typename Predicate>
+typename Container::iterator find_if(Container& container, Predicate pred) {
+ return std::find_if(container.begin(), container.end(), pred);
+}
+
+template <typename Container, typename Predicate>
+typename Container::iterator remove_if(Container& container, Predicate pred) {
+ return std::remove_if(container.begin(), container.end(), pred);
+}
+
+template <typename Container> typename Container::iterator min_element(Container& container) {
+ return std::min_element(container.begin(), container.end());
+}
+
+time_t clock_gettime_raw() {
+ struct timespec time;
+ clock_gettime(CLOCK_MONOTONIC_RAW, &time);
+ return time.tv_sec;
+}
+
+void AuthTokenTable::AddAuthenticationToken(const hw_auth_token_t* auth_token) {
+ Entry new_entry(auth_token, clock_function_());
+ RemoveEntriesSupersededBy(new_entry);
+ if (entries_.size() >= max_entries_) {
+ LOG_W("Auth token table filled up; replacing oldest entry", 0);
+ *min_element(entries_) = std::move(new_entry);
+ } else {
+ entries_.push_back(std::move(new_entry));
+ }
+}
+
+inline bool is_secret_key_operation(keymaster_algorithm_t algorithm, keymaster_purpose_t purpose) {
+ if ((algorithm != KM_ALGORITHM_RSA || algorithm != KM_ALGORITHM_EC))
+ return true;
+ if (purpose == KM_PURPOSE_SIGN || purpose == KM_PURPOSE_DECRYPT)
+ return true;
+ return false;
+}
+
+inline bool KeyRequiresAuthentication(const AuthorizationSet& key_info,
+ keymaster_purpose_t purpose) {
+ keymaster_algorithm_t algorithm = KM_ALGORITHM_AES;
+ key_info.GetTagValue(TAG_ALGORITHM, &algorithm);
+ return is_secret_key_operation(algorithm, purpose) && key_info.find(TAG_NO_AUTH_REQUIRED) == -1;
+}
+
+inline bool KeyRequiresAuthPerOperation(const AuthorizationSet& key_info,
+ keymaster_purpose_t purpose) {
+ keymaster_algorithm_t algorithm = KM_ALGORITHM_AES;
+ key_info.GetTagValue(TAG_ALGORITHM, &algorithm);
+ return is_secret_key_operation(algorithm, purpose) && key_info.find(TAG_AUTH_TIMEOUT) == -1;
+}
+
+AuthTokenTable::Error AuthTokenTable::FindAuthorization(const AuthorizationSet& key_info,
+ keymaster_purpose_t purpose,
+ keymaster_operation_handle_t op_handle,
+ const hw_auth_token_t** found) {
+ if (!KeyRequiresAuthentication(key_info, purpose))
+ return AUTH_NOT_REQUIRED;
+
+ hw_authenticator_type_t auth_type = HW_AUTH_NONE;
+ key_info.GetTagValue(TAG_USER_AUTH_TYPE, &auth_type);
+
+ std::vector<uint64_t> key_sids;
+ ExtractSids(key_info, &key_sids);
+
+ if (KeyRequiresAuthPerOperation(key_info, purpose))
+ return FindAuthPerOpAuthorization(key_sids, auth_type, op_handle, found);
+ else
+ return FindTimedAuthorization(key_sids, auth_type, key_info, found);
+}
+
+AuthTokenTable::Error AuthTokenTable::FindAuthPerOpAuthorization(
+ const std::vector<uint64_t>& sids, hw_authenticator_type_t auth_type,
+ keymaster_operation_handle_t op_handle, const hw_auth_token_t** found) {
+ if (op_handle == 0)
+ return OP_HANDLE_REQUIRED;
+
+ auto matching_op = find_if(
+ entries_, [&](Entry& e) { return e.token()->challenge == op_handle && !e.completed(); });
+
+ if (matching_op == entries_.end())
+ return AUTH_TOKEN_NOT_FOUND;
+
+ if (!matching_op->SatisfiesAuth(sids, auth_type))
+ return AUTH_TOKEN_WRONG_SID;
+
+ *found = matching_op->token();
+ return OK;
+}
+
+AuthTokenTable::Error AuthTokenTable::FindTimedAuthorization(const std::vector<uint64_t>& sids,
+ hw_authenticator_type_t auth_type,
+ const AuthorizationSet& key_info,
+ const hw_auth_token_t** found) {
+ Entry* newest_match = NULL;
+ for (auto& entry : entries_)
+ if (entry.SatisfiesAuth(sids, auth_type) && entry.is_newer_than(newest_match))
+ newest_match = &entry;
+
+ if (!newest_match)
+ return AUTH_TOKEN_NOT_FOUND;
+
+ uint32_t timeout;
+ key_info.GetTagValue(TAG_AUTH_TIMEOUT, &timeout);
+ time_t now = clock_function_();
+ if (static_cast<int64_t>(newest_match->time_received()) + timeout < static_cast<int64_t>(now))
+ return AUTH_TOKEN_EXPIRED;
+
+ newest_match->UpdateLastUse(now);
+ *found = newest_match->token();
+ return OK;
+}
+
+void AuthTokenTable::ExtractSids(const AuthorizationSet& key_info, std::vector<uint64_t>* sids) {
+ assert(sids);
+ for (auto& param : key_info)
+ if (param.tag == TAG_USER_SECURE_ID)
+ sids->push_back(param.long_integer);
+}
+
+void AuthTokenTable::RemoveEntriesSupersededBy(const Entry& entry) {
+ entries_.erase(remove_if(entries_, [&](Entry& e) { return entry.Supersedes(e); }),
+ entries_.end());
+}
+
+void AuthTokenTable::Clear() {
+ entries_.clear();
+}
+
+bool AuthTokenTable::IsSupersededBySomeEntry(const Entry& entry) {
+ return std::any_of(entries_.begin(), entries_.end(),
+ [&](Entry& e) { return e.Supersedes(entry); });
+}
+
+void AuthTokenTable::MarkCompleted(const keymaster_operation_handle_t op_handle) {
+ auto found = find_if(entries_, [&](Entry& e) { return e.token()->challenge == op_handle; });
+ if (found == entries_.end())
+ return;
+
+ assert(!IsSupersededBySomeEntry(*found));
+ found->mark_completed();
+
+ if (IsSupersededBySomeEntry(*found))
+ entries_.erase(found);
+}
+
+AuthTokenTable::Entry::Entry(const hw_auth_token_t* token, time_t current_time)
+ : token_(token), time_received_(current_time), last_use_(current_time),
+ operation_completed_(token_->challenge == 0) {
+}
+
+uint32_t AuthTokenTable::Entry::timestamp_host_order() const {
+ return ntoh(token_->timestamp);
+}
+
+hw_authenticator_type_t AuthTokenTable::Entry::authenticator_type() const {
+ hw_authenticator_type_t result = static_cast<hw_authenticator_type_t>(
+ ntoh(static_cast<uint32_t>(token_->authenticator_type)));
+ return result;
+}
+
+bool AuthTokenTable::Entry::SatisfiesAuth(const std::vector<uint64_t>& sids,
+ hw_authenticator_type_t auth_type) {
+ for (auto sid : sids)
+ if ((sid == token_->authenticator_id) ||
+ (sid == token_->user_id && (auth_type & authenticator_type()) != 0))
+ return true;
+ return false;
+}
+
+void AuthTokenTable::Entry::UpdateLastUse(time_t time) {
+ this->last_use_ = time;
+}
+
+bool AuthTokenTable::Entry::Supersedes(const Entry& entry) const {
+ if (!entry.completed())
+ return false;
+
+ return (token_->user_id == entry.token_->user_id &&
+ token_->authenticator_type == entry.token_->authenticator_type &&
+ token_->authenticator_type == entry.token_->authenticator_type &&
+ timestamp_host_order() > entry.timestamp_host_order());
+}
+
+} // namespace keymaster
diff --git a/security/keystore/auth_token_table.h b/security/keystore/auth_token_table.h
new file mode 100644
index 0000000..bcf88fd
--- /dev/null
+++ b/security/keystore/auth_token_table.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2015 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 <memory>
+#include <vector>
+
+#include <hardware/hw_auth_token.h>
+#include <keymaster/authorization_set.h>
+
+#ifndef KEYSTORE_AUTH_TOKEN_TABLE_H_
+#define KEYSTORE_AUTH_TOKEN_TABLE_H_
+
+namespace keymaster {
+
+namespace test {
+class AuthTokenTableTest;
+} // namespace test
+
+time_t clock_gettime_raw();
+
+/**
+ * AuthTokenTable manages a set of received authorization tokens and can provide the appropriate
+ * token for authorizing a key operation.
+ *
+ * To keep the table from growing without bound, superseded entries are removed when possible, and
+ * least recently used entries are automatically pruned when when the table exceeds a size limit,
+ * which is expected to be relatively small, since the implementation uses a linear search.
+ */
+class AuthTokenTable {
+ public:
+ explicit AuthTokenTable(size_t max_entries = 32, time_t (*clock_function)() = clock_gettime_raw)
+ : max_entries_(max_entries), clock_function_(clock_function) {}
+
+ enum Error {
+ OK,
+ AUTH_NOT_REQUIRED = -1,
+ AUTH_TOKEN_EXPIRED = -2, // Found a matching token, but it's too old.
+ AUTH_TOKEN_WRONG_SID = -3, // Found a token with the right challenge, but wrong SID. This
+ // most likely indicates that the authenticator was updated
+ // (e.g. new fingerprint enrolled).
+ OP_HANDLE_REQUIRED = -4, // The key requires auth per use but op_handle was zero.
+ AUTH_TOKEN_NOT_FOUND = -5,
+ };
+
+ /**
+ * Add an authorization token to the table. The table takes ownership of the argument.
+ */
+ void AddAuthenticationToken(const hw_auth_token_t* token);
+
+ /**
+ * Find an authorization token that authorizes the operation specified by \p operation_handle on
+ * a key with the characteristics specified in \p key_info.
+ *
+ * This method is O(n * m), where n is the number of KM_TAG_USER_SECURE_ID entries in key_info
+ * and m is the number of entries in the table. It could be made better, but n and m should
+ * always be small.
+ *
+ * The table retains ownership of the returned object.
+ */
+ Error FindAuthorization(const AuthorizationSet& key_info, keymaster_purpose_t purpose,
+ keymaster_operation_handle_t op_handle, const hw_auth_token_t** found);
+
+ /**
+ * Find an authorization token that authorizes the operation specified by \p operation_handle on
+ * a key with the characteristics specified in \p key_info.
+ *
+ * This method is O(n * m), where n is the number of KM_TAG_USER_SECURE_ID entries in key_info
+ * and m is the number of entries in the table. It could be made better, but n and m should
+ * always be small.
+ *
+ * The table retains ownership of the returned object.
+ */
+ Error FindAuthorization(const keymaster_key_param_t* params, size_t params_count,
+ keymaster_purpose_t purpose, keymaster_operation_handle_t op_handle,
+ const hw_auth_token_t** found) {
+ return FindAuthorization(AuthorizationSet(params, params_count), purpose, op_handle, found);
+ }
+
+ /**
+ * Mark operation completed. This allows tokens associated with the specified operation to be
+ * superseded by new tokens.
+ */
+ void MarkCompleted(const keymaster_operation_handle_t op_handle);
+
+ void Clear();
+
+ size_t size() { return entries_.size(); }
+
+ private:
+ friend class AuthTokenTableTest;
+
+ class Entry {
+ public:
+ Entry(const hw_auth_token_t* token, time_t current_time);
+ Entry(Entry&& entry) { *this = std::move(entry); }
+
+ void operator=(Entry&& rhs) {
+ token_ = std::move(rhs.token_);
+ time_received_ = rhs.time_received_;
+ last_use_ = rhs.last_use_;
+ operation_completed_ = rhs.operation_completed_;
+ }
+
+ bool operator<(const Entry& rhs) const { return last_use_ < rhs.last_use_; }
+
+ void UpdateLastUse(time_t time);
+
+ bool Supersedes(const Entry& entry) const;
+ bool SatisfiesAuth(const std::vector<uint64_t>& sids, hw_authenticator_type_t auth_type);
+
+ bool is_newer_than(const Entry* entry) {
+ if (!entry)
+ return true;
+ return timestamp_host_order() > entry->timestamp_host_order();
+ }
+
+ void mark_completed() { operation_completed_ = true; }
+
+ const hw_auth_token_t* token() { return token_.get(); }
+ time_t time_received() const { return time_received_; }
+ bool completed() const { return operation_completed_; }
+ uint32_t timestamp_host_order() const;
+ hw_authenticator_type_t authenticator_type() const;
+
+ private:
+ std::unique_ptr<const hw_auth_token_t> token_;
+ time_t time_received_;
+ time_t last_use_;
+ bool operation_completed_;
+ };
+
+ Error FindAuthPerOpAuthorization(const std::vector<uint64_t>& sids,
+ hw_authenticator_type_t auth_type,
+ keymaster_operation_handle_t op_handle,
+ const hw_auth_token_t** found);
+ Error FindTimedAuthorization(const std::vector<uint64_t>& sids,
+ hw_authenticator_type_t auth_type,
+ const AuthorizationSet& key_info, const hw_auth_token_t** found);
+ void ExtractSids(const AuthorizationSet& key_info, std::vector<uint64_t>* sids);
+ void RemoveEntriesSupersededBy(const Entry& entry);
+ bool IsSupersededBySomeEntry(const Entry& entry);
+
+ std::vector<Entry> entries_;
+ size_t max_entries_;
+ time_t (*clock_function_)();
+};
+
+} // namespace keymaster
+
+#endif // KEYSTORE_AUTH_TOKEN_TABLE_H_
diff --git a/security/keystore/blob.cpp b/security/keystore/blob.cpp
new file mode 100644
index 0000000..8b08f07
--- /dev/null
+++ b/security/keystore/blob.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#define LOG_TAG "keystore"
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <cutils/log.h>
+
+#include "blob.h"
+#include "entropy.h"
+
+#include "keystore_utils.h"
+
+Blob::Blob(const uint8_t* value, size_t valueLength, const uint8_t* info, uint8_t infoLength,
+ BlobType type) {
+ memset(&mBlob, 0, sizeof(mBlob));
+ if (valueLength > VALUE_SIZE) {
+ valueLength = VALUE_SIZE;
+ ALOGW("Provided blob length too large");
+ }
+ if (infoLength + valueLength > VALUE_SIZE) {
+ infoLength = VALUE_SIZE - valueLength;
+ ALOGW("Provided info length too large");
+ }
+ mBlob.length = valueLength;
+ memcpy(mBlob.value, value, valueLength);
+
+ mBlob.info = infoLength;
+ memcpy(mBlob.value + valueLength, info, infoLength);
+
+ mBlob.version = CURRENT_BLOB_VERSION;
+ mBlob.type = uint8_t(type);
+
+ if (type == TYPE_MASTER_KEY) {
+ mBlob.flags = KEYSTORE_FLAG_ENCRYPTED;
+ } else {
+ mBlob.flags = KEYSTORE_FLAG_NONE;
+ }
+}
+
+Blob::Blob(blob b) {
+ mBlob = b;
+}
+
+Blob::Blob() {
+ memset(&mBlob, 0, sizeof(mBlob));
+}
+
+bool Blob::isEncrypted() const {
+ if (mBlob.version < 2) {
+ return true;
+ }
+
+ return mBlob.flags & KEYSTORE_FLAG_ENCRYPTED;
+}
+
+void Blob::setEncrypted(bool encrypted) {
+ if (encrypted) {
+ mBlob.flags |= KEYSTORE_FLAG_ENCRYPTED;
+ } else {
+ mBlob.flags &= ~KEYSTORE_FLAG_ENCRYPTED;
+ }
+}
+
+void Blob::setFallback(bool fallback) {
+ if (fallback) {
+ mBlob.flags |= KEYSTORE_FLAG_FALLBACK;
+ } else {
+ mBlob.flags &= ~KEYSTORE_FLAG_FALLBACK;
+ }
+}
+
+ResponseCode Blob::writeBlob(const char* filename, AES_KEY* aes_key, State state,
+ Entropy* entropy) {
+ ALOGV("writing blob %s", filename);
+ if (isEncrypted()) {
+ if (state != STATE_NO_ERROR) {
+ ALOGD("couldn't insert encrypted blob while not unlocked");
+ return LOCKED;
+ }
+
+ if (!entropy->generate_random_data(mBlob.vector, AES_BLOCK_SIZE)) {
+ ALOGW("Could not read random data for: %s", filename);
+ return SYSTEM_ERROR;
+ }
+ }
+
+ // data includes the value and the value's length
+ size_t dataLength = mBlob.length + sizeof(mBlob.length);
+ // pad data to the AES_BLOCK_SIZE
+ size_t digestedLength = ((dataLength + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE * AES_BLOCK_SIZE);
+ // encrypted data includes the digest value
+ size_t encryptedLength = digestedLength + MD5_DIGEST_LENGTH;
+ // move info after space for padding
+ memmove(&mBlob.encrypted[encryptedLength], &mBlob.value[mBlob.length], mBlob.info);
+ // zero padding area
+ memset(mBlob.value + mBlob.length, 0, digestedLength - dataLength);
+
+ mBlob.length = htonl(mBlob.length);
+
+ if (isEncrypted()) {
+ MD5(mBlob.digested, digestedLength, mBlob.digest);
+
+ uint8_t vector[AES_BLOCK_SIZE];
+ memcpy(vector, mBlob.vector, AES_BLOCK_SIZE);
+ AES_cbc_encrypt(mBlob.encrypted, mBlob.encrypted, encryptedLength, aes_key, vector,
+ AES_ENCRYPT);
+ }
+
+ size_t headerLength = (mBlob.encrypted - (uint8_t*)&mBlob);
+ size_t fileLength = encryptedLength + headerLength + mBlob.info;
+
+ const char* tmpFileName = ".tmp";
+ int out =
+ TEMP_FAILURE_RETRY(open(tmpFileName, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR));
+ if (out < 0) {
+ ALOGW("could not open file: %s: %s", tmpFileName, strerror(errno));
+ return SYSTEM_ERROR;
+ }
+ size_t writtenBytes = writeFully(out, (uint8_t*)&mBlob, fileLength);
+ if (close(out) != 0) {
+ return SYSTEM_ERROR;
+ }
+ if (writtenBytes != fileLength) {
+ ALOGW("blob not fully written %zu != %zu", writtenBytes, fileLength);
+ unlink(tmpFileName);
+ return SYSTEM_ERROR;
+ }
+ if (rename(tmpFileName, filename) == -1) {
+ ALOGW("could not rename blob to %s: %s", filename, strerror(errno));
+ return SYSTEM_ERROR;
+ }
+ return NO_ERROR;
+}
+
+ResponseCode Blob::readBlob(const char* filename, AES_KEY* aes_key, State state) {
+ ALOGV("reading blob %s", filename);
+ int in = TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
+ if (in < 0) {
+ return (errno == ENOENT) ? KEY_NOT_FOUND : SYSTEM_ERROR;
+ }
+ // fileLength may be less than sizeof(mBlob) since the in
+ // memory version has extra padding to tolerate rounding up to
+ // the AES_BLOCK_SIZE
+ size_t fileLength = readFully(in, (uint8_t*)&mBlob, sizeof(mBlob));
+ if (close(in) != 0) {
+ return SYSTEM_ERROR;
+ }
+
+ if (fileLength == 0) {
+ return VALUE_CORRUPTED;
+ }
+
+ if (isEncrypted() && (state != STATE_NO_ERROR)) {
+ return LOCKED;
+ }
+
+ size_t headerLength = (mBlob.encrypted - (uint8_t*)&mBlob);
+ if (fileLength < headerLength) {
+ return VALUE_CORRUPTED;
+ }
+
+ ssize_t encryptedLength = fileLength - (headerLength + mBlob.info);
+ if (encryptedLength < 0) {
+ return VALUE_CORRUPTED;
+ }
+
+ ssize_t digestedLength;
+ if (isEncrypted()) {
+ if (encryptedLength % AES_BLOCK_SIZE != 0) {
+ return VALUE_CORRUPTED;
+ }
+
+ AES_cbc_encrypt(mBlob.encrypted, mBlob.encrypted, encryptedLength, aes_key, mBlob.vector,
+ AES_DECRYPT);
+ digestedLength = encryptedLength - MD5_DIGEST_LENGTH;
+ uint8_t computedDigest[MD5_DIGEST_LENGTH];
+ MD5(mBlob.digested, digestedLength, computedDigest);
+ if (memcmp(mBlob.digest, computedDigest, MD5_DIGEST_LENGTH) != 0) {
+ return VALUE_CORRUPTED;
+ }
+ } else {
+ digestedLength = encryptedLength;
+ }
+
+ ssize_t maxValueLength = digestedLength - sizeof(mBlob.length);
+ mBlob.length = ntohl(mBlob.length);
+ if (mBlob.length < 0 || mBlob.length > maxValueLength) {
+ return VALUE_CORRUPTED;
+ }
+ if (mBlob.info != 0) {
+ // move info from after padding to after data
+ memmove(&mBlob.value[mBlob.length], &mBlob.value[maxValueLength], mBlob.info);
+ }
+ return ::NO_ERROR;
+}
diff --git a/security/keystore/blob.h b/security/keystore/blob.h
new file mode 100644
index 0000000..95610ad
--- /dev/null
+++ b/security/keystore/blob.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2015 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 KEYSTORE_BLOB_H_
+#define KEYSTORE_BLOB_H_
+
+#include <stdint.h>
+
+#include <openssl/aes.h>
+#include <openssl/md5.h>
+
+#include <keystore/keystore.h>
+
+#define VALUE_SIZE 32768
+
+/* Here is the file format. There are two parts in blob.value, the secret and
+ * the description. The secret is stored in ciphertext, and its original size
+ * can be found in blob.length. The description is stored after the secret in
+ * plaintext, and its size is specified in blob.info. The total size of the two
+ * parts must be no more than VALUE_SIZE bytes. The first field is the version,
+ * the second is the blob's type, and the third byte is flags. Fields other
+ * than blob.info, blob.length, and blob.value are modified by encryptBlob()
+ * and decryptBlob(). Thus they should not be accessed from outside. */
+
+/* ** Note to future implementors of encryption: **
+ * Currently this is the construction:
+ * metadata || Enc(MD5(data) || data)
+ *
+ * This should be the construction used for encrypting if re-implementing:
+ *
+ * Derive independent keys for encryption and MAC:
+ * Kenc = AES_encrypt(masterKey, "Encrypt")
+ * Kmac = AES_encrypt(masterKey, "MAC")
+ *
+ * Store this:
+ * metadata || AES_CTR_encrypt(Kenc, rand_IV, data) ||
+ * HMAC(Kmac, metadata || Enc(data))
+ */
+struct __attribute__((packed)) blob {
+ uint8_t version;
+ uint8_t type;
+ uint8_t flags;
+ uint8_t info;
+ uint8_t vector[AES_BLOCK_SIZE];
+ uint8_t encrypted[0]; // Marks offset to encrypted data.
+ uint8_t digest[MD5_DIGEST_LENGTH];
+ uint8_t digested[0]; // Marks offset to digested data.
+ int32_t length; // in network byte order when encrypted
+ uint8_t value[VALUE_SIZE + AES_BLOCK_SIZE];
+};
+
+static const uint8_t CURRENT_BLOB_VERSION = 2;
+
+typedef enum {
+ TYPE_ANY = 0, // meta type that matches anything
+ TYPE_GENERIC = 1,
+ TYPE_MASTER_KEY = 2,
+ TYPE_KEY_PAIR = 3,
+ TYPE_KEYMASTER_10 = 4,
+} BlobType;
+
+class Entropy;
+
+class Blob {
+ public:
+ Blob(const uint8_t* value, size_t valueLength, const uint8_t* info, uint8_t infoLength,
+ BlobType type);
+ explicit Blob(blob b);
+
+ Blob();
+
+ const uint8_t* getValue() const { return mBlob.value; }
+
+ int32_t getLength() const { return mBlob.length; }
+
+ const uint8_t* getInfo() const { return mBlob.value + mBlob.length; }
+ uint8_t getInfoLength() const { return mBlob.info; }
+
+ uint8_t getVersion() const { return mBlob.version; }
+
+ bool isEncrypted() const;
+ void setEncrypted(bool encrypted);
+
+ bool isFallback() const { return mBlob.flags & KEYSTORE_FLAG_FALLBACK; }
+ void setFallback(bool fallback);
+
+ void setVersion(uint8_t version) { mBlob.version = version; }
+ BlobType getType() const { return BlobType(mBlob.type); }
+ void setType(BlobType type) { mBlob.type = uint8_t(type); }
+
+ ResponseCode writeBlob(const char* filename, AES_KEY* aes_key, State state, Entropy* entropy);
+ ResponseCode readBlob(const char* filename, AES_KEY* aes_key, State state);
+
+ private:
+ struct blob mBlob;
+};
+
+#endif // KEYSTORE_BLOB_H_
diff --git a/security/keystore/defaults.h b/security/keystore/defaults.h
new file mode 100644
index 0000000..9232dd0
--- /dev/null
+++ b/security/keystore/defaults.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2013 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 KEYSTORE_DEFAULTS_H_
+#define KEYSTORE_DEFAULTS_H_
+
+/*
+ * These must be kept in sync with
+ * frameworks/base/keystore/java/android/security/KeyPairGeneratorSpec.java
+ */
+
+/* DSA */
+#define DSA_DEFAULT_KEY_SIZE 1024
+#define DSA_MIN_KEY_SIZE 512
+#define DSA_MAX_KEY_SIZE 8192
+
+/* EC */
+#define EC_DEFAULT_KEY_SIZE 256
+#define EC_MIN_KEY_SIZE 192
+#define EC_MAX_KEY_SIZE 521
+
+/* RSA */
+#define RSA_DEFAULT_KEY_SIZE 2048
+#define RSA_DEFAULT_EXPONENT 0x10001
+#define RSA_MIN_KEY_SIZE 512
+#define RSA_MAX_KEY_SIZE 8192
+
+#endif /* KEYSTORE_DEFAULTS_H_ */
diff --git a/security/keystore/entropy.cpp b/security/keystore/entropy.cpp
new file mode 100644
index 0000000..1bfe9a1
--- /dev/null
+++ b/security/keystore/entropy.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define LOG_TAG "keystore"
+
+#include "entropy.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <cutils/log.h>
+
+#include "keystore_utils.h"
+
+Entropy::~Entropy() {
+ if (mRandom >= 0) {
+ close(mRandom);
+ }
+}
+
+bool Entropy::open() {
+ const char* randomDevice = "/dev/urandom";
+ mRandom = TEMP_FAILURE_RETRY(::open(randomDevice, O_RDONLY));
+ if (mRandom < 0) {
+ ALOGE("open: %s: %s", randomDevice, strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+bool Entropy::generate_random_data(uint8_t* data, size_t size) const {
+ return (readFully(mRandom, data, size) == size);
+}
diff --git a/security/keystore/entropy.h b/security/keystore/entropy.h
new file mode 100644
index 0000000..0e4d1b2
--- /dev/null
+++ b/security/keystore/entropy.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 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 KEYSTORE_ENTROPY_H_
+#define KEYSTORE_ENTROPY_H_
+
+#include <stdint.h>
+
+class Entropy {
+ public:
+ Entropy() : mRandom(-1) {}
+ ~Entropy();
+
+ bool open();
+ bool generate_random_data(uint8_t* data, size_t size) const;
+
+ private:
+ int mRandom;
+};
+
+#endif // KEYSTORE_ENTROPY_H_
diff --git a/security/keystore/include/keystore/IKeystoreService.h b/security/keystore/include/keystore/IKeystoreService.h
new file mode 100644
index 0000000..0f9ee27
--- /dev/null
+++ b/security/keystore/include/keystore/IKeystoreService.h
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2012 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 KEYSTORE_IKEYSTORESERVICE_H
+#define KEYSTORE_IKEYSTORESERVICE_H
+
+#include <hardware/keymaster_defs.h>
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <vector>
+
+namespace android {
+
+class KeystoreArg : public RefBase {
+public:
+ KeystoreArg(const void *data, size_t len);
+ ~KeystoreArg();
+
+ const void* data() const;
+ size_t size() const;
+
+private:
+ const void* mData;
+ size_t mSize;
+};
+
+struct MallocDeleter {
+ void operator()(uint8_t* p) { free(p); }
+};
+
+// struct for serializing/deserializing a list of keymaster_key_param_t's
+struct KeymasterArguments {
+ KeymasterArguments();
+ ~KeymasterArguments();
+ void readFromParcel(const Parcel& in);
+ void writeToParcel(Parcel* out) const;
+
+ std::vector<keymaster_key_param_t> params;
+};
+
+// struct for serializing the results of begin/update/finish
+struct OperationResult {
+ OperationResult();
+ ~OperationResult();
+ void readFromParcel(const Parcel& in);
+ void writeToParcel(Parcel* out) const;
+
+ int resultCode;
+ sp<IBinder> token;
+ keymaster_operation_handle_t handle;
+ int inputConsumed;
+ std::unique_ptr<uint8_t[], MallocDeleter> data;
+ size_t dataLength;
+ KeymasterArguments outParams;
+};
+
+// struct for serializing the results of export
+struct ExportResult {
+ ExportResult();
+ ~ExportResult();
+ void readFromParcel(const Parcel& in);
+ void writeToParcel(Parcel* out) const;
+
+ int resultCode;
+ std::unique_ptr<uint8_t[], MallocDeleter> exportData;
+ size_t dataLength;
+};
+
+// struct for serializing keymaster_key_characteristics_t's
+struct KeyCharacteristics {
+ KeyCharacteristics();
+ ~KeyCharacteristics();
+ void readFromParcel(const Parcel& in);
+ void writeToParcel(Parcel* out) const;
+
+ keymaster_key_characteristics_t characteristics;
+};
+
+// struct for serializing keymaster_cert_chain_t's
+struct KeymasterCertificateChain {
+ KeymasterCertificateChain();
+ ~KeymasterCertificateChain();
+ void readFromParcel(const Parcel& in);
+ void writeToParcel(Parcel* out) const;
+
+ void FreeChain();
+
+ keymaster_cert_chain_t chain;
+};
+
+bool readKeymasterArgumentFromParcel(const Parcel& in, keymaster_key_param_t* out);
+void writeKeymasterArgumentToParcel(const keymaster_key_param_t& param, Parcel* out);
+
+/*
+ * This must be kept manually in sync with frameworks/base's IKeystoreService.java
+ */
+class IKeystoreService: public IInterface {
+public:
+ enum {
+ GET_STATE = IBinder::FIRST_CALL_TRANSACTION + 0,
+ GET = IBinder::FIRST_CALL_TRANSACTION + 1,
+ INSERT = IBinder::FIRST_CALL_TRANSACTION + 2,
+ DEL = IBinder::FIRST_CALL_TRANSACTION + 3,
+ EXIST = IBinder::FIRST_CALL_TRANSACTION + 4,
+ LIST = IBinder::FIRST_CALL_TRANSACTION + 5,
+ RESET = IBinder::FIRST_CALL_TRANSACTION + 6,
+ ON_USER_PASSWORD_CHANGED = IBinder::FIRST_CALL_TRANSACTION + 7,
+ LOCK = IBinder::FIRST_CALL_TRANSACTION + 8,
+ UNLOCK = IBinder::FIRST_CALL_TRANSACTION + 9,
+ IS_EMPTY = IBinder::FIRST_CALL_TRANSACTION + 10,
+ GENERATE = IBinder::FIRST_CALL_TRANSACTION + 11,
+ IMPORT = IBinder::FIRST_CALL_TRANSACTION + 12,
+ SIGN = IBinder::FIRST_CALL_TRANSACTION + 13,
+ VERIFY = IBinder::FIRST_CALL_TRANSACTION + 14,
+ GET_PUBKEY = IBinder::FIRST_CALL_TRANSACTION + 15,
+ GRANT = IBinder::FIRST_CALL_TRANSACTION + 16,
+ UNGRANT = IBinder::FIRST_CALL_TRANSACTION + 17,
+ GETMTIME = IBinder::FIRST_CALL_TRANSACTION + 18,
+ DUPLICATE = IBinder::FIRST_CALL_TRANSACTION + 19,
+ IS_HARDWARE_BACKED = IBinder::FIRST_CALL_TRANSACTION + 20,
+ CLEAR_UID = IBinder::FIRST_CALL_TRANSACTION + 21,
+ ADD_RNG_ENTROPY = IBinder::FIRST_CALL_TRANSACTION + 22,
+ GENERATE_KEY = IBinder::FIRST_CALL_TRANSACTION + 23,
+ GET_KEY_CHARACTERISTICS = IBinder::FIRST_CALL_TRANSACTION + 24,
+ IMPORT_KEY = IBinder::FIRST_CALL_TRANSACTION + 25,
+ EXPORT_KEY = IBinder::FIRST_CALL_TRANSACTION + 26,
+ BEGIN = IBinder::FIRST_CALL_TRANSACTION + 27,
+ UPDATE = IBinder::FIRST_CALL_TRANSACTION + 28,
+ FINISH = IBinder::FIRST_CALL_TRANSACTION + 29,
+ ABORT = IBinder::FIRST_CALL_TRANSACTION + 30,
+ IS_OPERATION_AUTHORIZED = IBinder::FIRST_CALL_TRANSACTION + 31,
+ ADD_AUTH_TOKEN = IBinder::FIRST_CALL_TRANSACTION + 32,
+ ON_USER_ADDED = IBinder::FIRST_CALL_TRANSACTION + 33,
+ ON_USER_REMOVED = IBinder::FIRST_CALL_TRANSACTION + 34,
+ ATTEST_KEY = IBinder::FIRST_CALL_TRANSACTION + 35,
+ };
+
+ DECLARE_META_INTERFACE(KeystoreService);
+
+ virtual int32_t getState(int32_t userId) = 0;
+
+ virtual int32_t get(const String16& name, uint8_t** item, size_t* itemLength) = 0;
+
+ virtual int32_t insert(const String16& name, const uint8_t* item, size_t itemLength, int uid,
+ int32_t flags) = 0;
+
+ virtual int32_t del(const String16& name, int uid) = 0;
+
+ virtual int32_t exist(const String16& name, int uid) = 0;
+
+ virtual int32_t list(const String16& prefix, int uid, Vector<String16>* matches) = 0;
+
+ virtual int32_t reset() = 0;
+
+ virtual int32_t onUserPasswordChanged(int32_t userId, const String16& newPassword) = 0;
+
+ virtual int32_t lock(int32_t userId) = 0;
+
+ virtual int32_t unlock(int32_t userId, const String16& password) = 0;
+
+ virtual bool isEmpty(int32_t userId) = 0;
+
+ virtual int32_t generate(const String16& name, int32_t uid, int32_t keyType, int32_t keySize,
+ int32_t flags, Vector<sp<KeystoreArg> >* args) = 0;
+
+ virtual int32_t import(const String16& name, const uint8_t* data, size_t length, int uid,
+ int32_t flags) = 0;
+
+ virtual int32_t sign(const String16& name, const uint8_t* data, size_t length, uint8_t** out,
+ size_t* outLength) = 0;
+
+ virtual int32_t verify(const String16& name, const uint8_t* data, size_t dataLength,
+ const uint8_t* signature, size_t signatureLength) = 0;
+
+ virtual int32_t get_pubkey(const String16& name, uint8_t** pubkey, size_t* pubkeyLength) = 0;
+
+ virtual int32_t grant(const String16& name, int32_t granteeUid) = 0;
+
+ virtual int32_t ungrant(const String16& name, int32_t granteeUid) = 0;
+
+ virtual int64_t getmtime(const String16& name) = 0;
+
+ virtual int32_t duplicate(const String16& srcKey, int32_t srcUid, const String16& destKey,
+ int32_t destUid) = 0;
+
+ virtual int32_t is_hardware_backed(const String16& keyType) = 0;
+
+ virtual int32_t clear_uid(int64_t uid) = 0;
+
+ virtual int32_t addRngEntropy(const uint8_t* data, size_t dataLength) = 0;
+
+ virtual int32_t generateKey(const String16& name, const KeymasterArguments& params,
+ const uint8_t* entropy, size_t entropyLength, int uid, int flags,
+ KeyCharacteristics* outCharacteristics) = 0;
+
+ virtual int32_t getKeyCharacteristics(const String16& name,
+ const keymaster_blob_t* clientId,
+ const keymaster_blob_t* appData,
+ KeyCharacteristics* outCharacteristics) = 0;
+
+ virtual int32_t importKey(const String16& name, const KeymasterArguments& params,
+ keymaster_key_format_t format, const uint8_t *keyData,
+ size_t keyLength, int uid, int flags,
+ KeyCharacteristics* outCharacteristics) = 0;
+
+ virtual void exportKey(const String16& name, keymaster_key_format_t format,
+ const keymaster_blob_t* clientId,
+ const keymaster_blob_t* appData, ExportResult* result) = 0;
+
+ virtual void begin(const sp<IBinder>& apptoken, const String16& name,
+ keymaster_purpose_t purpose, bool pruneable,
+ const KeymasterArguments& params, const uint8_t* entropy,
+ size_t entropyLength, OperationResult* result) = 0;
+
+ virtual void update(const sp<IBinder>& token, const KeymasterArguments& params,
+ const uint8_t* data, size_t dataLength, OperationResult* result) = 0;
+
+ virtual void finish(const sp<IBinder>& token, const KeymasterArguments& params,
+ const uint8_t* signature, size_t signatureLength,
+ const uint8_t* entropy, size_t entropyLength,
+ OperationResult* result) = 0;
+
+ virtual int32_t abort(const sp<IBinder>& handle) = 0;
+
+ virtual bool isOperationAuthorized(const sp<IBinder>& handle) = 0;
+
+ virtual int32_t addAuthToken(const uint8_t* token, size_t length) = 0;
+
+ virtual int32_t onUserAdded(int32_t userId, int32_t parentId) = 0;
+
+ virtual int32_t onUserRemoved(int32_t userId) = 0;
+
+ virtual int32_t attestKey(const String16& name, const KeymasterArguments& params,
+ KeymasterCertificateChain* outChain) = 0;
+
+};
+
+// ----------------------------------------------------------------------------
+
+class BnKeystoreService: public BnInterface<IKeystoreService> {
+public:
+ virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags = 0);
+};
+
+} // namespace android
+
+#endif
diff --git a/security/keystore/include/keystore/keystore.h b/security/keystore/include/keystore/keystore.h
new file mode 100644
index 0000000..dcb6032
--- /dev/null
+++ b/security/keystore/include/keystore/keystore.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2009 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 __KEYSTORE_H__
+#define __KEYSTORE_H__
+
+#include <stdint.h>
+
+// note state values overlap with ResponseCode for the purposes of the state() API
+enum State {
+ STATE_NO_ERROR = 1,
+ STATE_LOCKED = 2,
+ STATE_UNINITIALIZED = 3,
+};
+
+enum ResponseCode {
+ NO_ERROR = STATE_NO_ERROR, // 1
+ LOCKED = STATE_LOCKED, // 2
+ UNINITIALIZED = STATE_UNINITIALIZED, // 3
+ SYSTEM_ERROR = 4,
+ PROTOCOL_ERROR = 5,
+ PERMISSION_DENIED = 6,
+ KEY_NOT_FOUND = 7,
+ VALUE_CORRUPTED = 8,
+ UNDEFINED_ACTION = 9,
+ WRONG_PASSWORD_0 = 10,
+ WRONG_PASSWORD_1 = 11,
+ WRONG_PASSWORD_2 = 12,
+ WRONG_PASSWORD_3 = 13, // MAX_RETRY = 4
+ SIGNATURE_INVALID = 14,
+ OP_AUTH_NEEDED = 15, // Auth is needed for this operation before it can be used.
+};
+
+/*
+ * All the flags for import and insert calls.
+ */
+enum {
+ KEYSTORE_FLAG_NONE = 0,
+ KEYSTORE_FLAG_ENCRYPTED = 1 << 0,
+ KEYSTORE_FLAG_FALLBACK = 1 << 1,
+};
+
+/**
+ * Returns the size of the softkey magic header value for measuring
+ * and allocating purposes.
+ */
+size_t get_softkey_header_size();
+
+/**
+ * Adds the magic softkey header to a key blob.
+ *
+ * Returns NULL if the destination array is too small. Otherwise it
+ * returns the offset directly after the magic value.
+ */
+uint8_t* add_softkey_header(uint8_t* key_blob, size_t key_blob_length);
+
+/**
+ * Returns true if the key blob has a magic softkey header at the beginning.
+ */
+bool is_softkey(const uint8_t* key_blob, const size_t key_blob_length);
+
+#endif
diff --git a/security/keystore/include/keystore/keystore_client.h b/security/keystore/include/keystore/keystore_client.h
new file mode 100644
index 0000000..cec29f7
--- /dev/null
+++ b/security/keystore/include/keystore/keystore_client.h
@@ -0,0 +1,180 @@
+// Copyright 2015 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 KEYSTORE_KEYSTORE_CLIENT_H_
+#define KEYSTORE_KEYSTORE_CLIENT_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "hardware/keymaster_defs.h"
+#include "keymaster/authorization_set.h"
+
+namespace keystore {
+
+// An abstract class providing a convenient interface to keystore services. This
+// interface is designed to:
+// - hide details of the IPC mechanism (e.g. binder)
+// - use std data types
+// - encourage the use of keymaster::AuthorizationSet[Builder]
+// - be convenient for native services integrating with keystore
+// - be safely mocked for unit testing (e.g. pure virtual methods)
+//
+// Example usage:
+// KeystoreClient* keystore = new KeyStoreClientImpl();
+// keystore->AddRandomNumberGeneratorEntropy("unpredictable");
+//
+// Notes on error codes:
+// Keystore binder methods return a variety of values including ResponseCode
+// values defined in keystore.h, keymaster_error_t values defined in
+// keymaster_defs.h, or just 0 or -1 (both of which conflict with
+// keymaster_error_t). The methods in this class converge on a single success
+// indicator for convenience. KM_ERROR_OK was chosen over ::NO_ERROR for two
+// reasons:
+// 1) KM_ERROR_OK is 0, which is a common convention for success, is the gmock
+// default, and allows error checks like 'if (error) {...'.
+// 2) Although both pollute the global namespace, KM_ERROR_OK has a prefix per
+// C convention and hopefully clients can use this interface without
+// needing to include 'keystore.h' directly.
+class KeystoreClient {
+ public:
+ KeystoreClient() = default;
+ virtual ~KeystoreClient() = default;
+
+ // Encrypts and authenticates |data| with minimal configuration for local
+ // decryption. If a key identified by |key_name| does not already exist it
+ // will be generated. On success returns true and populates |encrypted_data|.
+ // Note: implementations may generate more than one key but they will always
+ // have |key_name| as a prefix.
+ virtual bool encryptWithAuthentication(const std::string& key_name, const std::string& data,
+ std::string* encrypted_data) = 0;
+
+ // Decrypts and authenticates |encrypted_data| as output by
+ // EncryptWithAuthentication using the key(s) identified by |key_name|. On
+ // success returns true and populates |data|.
+ virtual bool decryptWithAuthentication(const std::string& key_name,
+ const std::string& encrypted_data,
+ std::string* data) = 0;
+
+ // Performs a Begin/Update/Finish sequence for an operation. The |purpose|,
+ // |key_name|, |input_parameters|, and |output_parameters| are as in
+ // BeginOperation. The |input_data| is as in UpdateOperation. The
+ // |signature_to_verify| and |output_data| are as in FinishOperation. On
+ // success returns true.
+ virtual bool oneShotOperation(keymaster_purpose_t purpose, const std::string& key_name,
+ const keymaster::AuthorizationSet& input_parameters,
+ const std::string& input_data,
+ const std::string& signature_to_verify,
+ keymaster::AuthorizationSet* output_parameters,
+ std::string* output_data) = 0;
+
+ // Adds |entropy| to the random number generator. Returns KM_ERROR_OK on
+ // success and a Keystore ResponseCode or keymaster_error_t on failure.
+ virtual int32_t addRandomNumberGeneratorEntropy(const std::string& entropy) = 0;
+
+ // Generates a key according to the given |key_parameters| and stores it with
+ // the given |key_name|. The [hardware|software]_enforced_characteristics of
+ // the key are provided on success. Returns KM_ERROR_OK on success. Returns
+ // KM_ERROR_OK on success and a Keystore ResponseCode or keymaster_error_t on
+ // failure.
+ virtual int32_t generateKey(const std::string& key_name,
+ const keymaster::AuthorizationSet& key_parameters,
+ keymaster::AuthorizationSet* hardware_enforced_characteristics,
+ keymaster::AuthorizationSet* software_enforced_characteristics) = 0;
+
+ // Provides the [hardware|software]_enforced_characteristics of a key
+ // identified by |key_name|. Returns KM_ERROR_OK on success and a Keystore
+ // ResponseCode or keymaster_error_t on failure.
+ virtual int32_t
+ getKeyCharacteristics(const std::string& key_name,
+ keymaster::AuthorizationSet* hardware_enforced_characteristics,
+ keymaster::AuthorizationSet* software_enforced_characteristics) = 0;
+
+ // Imports |key_data| in the given |key_format|, applies the given
+ // |key_parameters|, and stores it with the given |key_name|. The
+ // [hardware|software]_enforced_characteristics of the key are provided on
+ // success. Returns KM_ERROR_OK on success and a Keystore ResponseCode or
+ // keymaster_error_t on failure.
+ virtual int32_t importKey(const std::string& key_name,
+ const keymaster::AuthorizationSet& key_parameters,
+ keymaster_key_format_t key_format, const std::string& key_data,
+ keymaster::AuthorizationSet* hardware_enforced_characteristics,
+ keymaster::AuthorizationSet* software_enforced_characteristics) = 0;
+
+ // Exports the public key identified by |key_name| to |export_data| using
+ // |export_format|. Returns KM_ERROR_OK on success and a Keystore ResponseCode
+ // or keymaster_error_t on failure.
+ virtual int32_t exportKey(keymaster_key_format_t export_format, const std::string& key_name,
+ std::string* export_data) = 0;
+
+ // Deletes the key identified by |key_name|. Returns KM_ERROR_OK on success
+ // and a Keystore ResponseCode or keymaster_error_t on failure.
+ virtual int32_t deleteKey(const std::string& key_name) = 0;
+
+ // Deletes all keys owned by the caller. Returns KM_ERROR_OK on success and a
+ // Keystore ResponseCode or keymaster_error_t on failure.
+ virtual int32_t deleteAllKeys() = 0;
+
+ // Begins a cryptographic operation (e.g. encrypt, sign) identified by
+ // |purpose| using the key identified by |key_name| and the given
+ // |input_parameters|. On success, any |output_parameters| and an operation
+ // |handle| are populated. Returns KM_ERROR_OK on success and a Keystore
+ // ResponseCode or keymaster_error_t on failure.
+ virtual int32_t beginOperation(keymaster_purpose_t purpose, const std::string& key_name,
+ const keymaster::AuthorizationSet& input_parameters,
+ keymaster::AuthorizationSet* output_parameters,
+ keymaster_operation_handle_t* handle) = 0;
+
+ // Continues the operation associated with |handle| using the given
+ // |input_parameters| and |input_data|. On success, the
+ // |num_input_bytes_consumed| and any |output_parameters| are populated. Any
+ // |output_data| will be appended. Returns KM_ERROR_OK on success and a
+ // Keystore ResponseCode or keymaster_error_t on failure.
+ virtual int32_t updateOperation(keymaster_operation_handle_t handle,
+ const keymaster::AuthorizationSet& input_parameters,
+ const std::string& input_data, size_t* num_input_bytes_consumed,
+ keymaster::AuthorizationSet* output_parameters,
+ std::string* output_data) = 0;
+
+ // Finishes the operation associated with |handle| using the given
+ // |input_parameters| and, if necessary, a |signature_to_verify|. On success,
+ // any |output_parameters| are populated and |output_data| is appended.
+ // Returns KM_ERROR_OK on success and a Keystore ResponseCode or
+ // keymaster_error_t on failure.
+ virtual int32_t finishOperation(keymaster_operation_handle_t handle,
+ const keymaster::AuthorizationSet& input_parameters,
+ const std::string& signature_to_verify,
+ keymaster::AuthorizationSet* output_parameters,
+ std::string* output_data) = 0;
+
+ // Aborts the operation associated with |handle|. Returns KM_ERROR_OK on
+ // success and a Keystore ResponseCode or keymaster_error_t on failure.
+ virtual int32_t abortOperation(keymaster_operation_handle_t handle) = 0;
+
+ // Returns true if a key identified by |key_name| exists in the caller's
+ // key store. Returns false if an error occurs.
+ virtual bool doesKeyExist(const std::string& key_name) = 0;
+
+ // Provides a |key_name_list| containing all existing key names in the
+ // caller's key store starting with |prefix|. Returns true on success.
+ virtual bool listKeys(const std::string& prefix, std::vector<std::string>* key_name_list) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(KeystoreClient);
+};
+
+} // namespace keystore
+
+#endif // KEYSTORE_KEYSTORE_CLIENT_H_
diff --git a/security/keystore/include/keystore/keystore_client_impl.h b/security/keystore/include/keystore/keystore_client_impl.h
new file mode 100644
index 0000000..21f68f9
--- /dev/null
+++ b/security/keystore/include/keystore/keystore_client_impl.h
@@ -0,0 +1,119 @@
+// Copyright 2015 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 KEYSTORE_KEYSTORE_CLIENT_IMPL_H_
+#define KEYSTORE_KEYSTORE_CLIENT_IMPL_H_
+
+#include "keystore/keystore_client.h"
+
+#include <string>
+#include <map>
+#include <vector>
+
+#include "binder/IBinder.h"
+#include "binder/IServiceManager.h"
+#include "keystore/IKeystoreService.h"
+#include "utils/StrongPointer.h"
+
+namespace keystore {
+
+class KeystoreClientImpl : public KeystoreClient {
+ public:
+ KeystoreClientImpl();
+ ~KeystoreClientImpl() override = default;
+
+ // KeystoreClient methods.
+ bool encryptWithAuthentication(const std::string& key_name, const std::string& data,
+ std::string* encrypted_data) override;
+ bool decryptWithAuthentication(const std::string& key_name, const std::string& encrypted_data,
+ std::string* data) override;
+ bool oneShotOperation(keymaster_purpose_t purpose, const std::string& key_name,
+ const keymaster::AuthorizationSet& input_parameters,
+ const std::string& input_data, const std::string& signature_to_verify,
+ keymaster::AuthorizationSet* output_parameters,
+ std::string* output_data) override;
+ int32_t addRandomNumberGeneratorEntropy(const std::string& entropy) override;
+ int32_t generateKey(const std::string& key_name,
+ const keymaster::AuthorizationSet& key_parameters,
+ keymaster::AuthorizationSet* hardware_enforced_characteristics,
+ keymaster::AuthorizationSet* software_enforced_characteristics) override;
+ int32_t
+ getKeyCharacteristics(const std::string& key_name,
+ keymaster::AuthorizationSet* hardware_enforced_characteristics,
+ keymaster::AuthorizationSet* software_enforced_characteristics) override;
+ int32_t importKey(const std::string& key_name,
+ const keymaster::AuthorizationSet& key_parameters,
+ keymaster_key_format_t key_format, const std::string& key_data,
+ keymaster::AuthorizationSet* hardware_enforced_characteristics,
+ keymaster::AuthorizationSet* software_enforced_characteristics) override;
+ int32_t exportKey(keymaster_key_format_t export_format, const std::string& key_name,
+ std::string* export_data) override;
+ int32_t deleteKey(const std::string& key_name) override;
+ int32_t deleteAllKeys() override;
+ int32_t beginOperation(keymaster_purpose_t purpose, const std::string& key_name,
+ const keymaster::AuthorizationSet& input_parameters,
+ keymaster::AuthorizationSet* output_parameters,
+ keymaster_operation_handle_t* handle) override;
+ int32_t updateOperation(keymaster_operation_handle_t handle,
+ const keymaster::AuthorizationSet& input_parameters,
+ const std::string& input_data, size_t* num_input_bytes_consumed,
+ keymaster::AuthorizationSet* output_parameters,
+ std::string* output_data) override;
+ int32_t finishOperation(keymaster_operation_handle_t handle,
+ const keymaster::AuthorizationSet& input_parameters,
+ const std::string& signature_to_verify,
+ keymaster::AuthorizationSet* output_parameters,
+ std::string* output_data) override;
+ int32_t abortOperation(keymaster_operation_handle_t handle) override;
+ bool doesKeyExist(const std::string& key_name) override;
+ bool listKeys(const std::string& prefix, std::vector<std::string>* key_name_list) override;
+
+ private:
+ // Returns an available virtual operation handle.
+ keymaster_operation_handle_t getNextVirtualHandle();
+
+ // Maps a keystore error code to a code where all success cases use
+ // KM_ERROR_OK (not keystore's NO_ERROR).
+ int32_t mapKeystoreError(int32_t keystore_error);
+
+ // Creates an encryption key suitable for EncryptWithAuthentication or
+ // verifies attributes if the key already exists. Returns true on success.
+ bool createOrVerifyEncryptionKey(const std::string& key_name);
+
+ // Creates an authentication key suitable for EncryptWithAuthentication or
+ // verifies attributes if the key already exists. Returns true on success.
+ bool createOrVerifyAuthenticationKey(const std::string& key_name);
+
+ // Verifies attributes of an encryption key suitable for
+ // EncryptWithAuthentication. Returns true on success and populates |verified|
+ // with the result of the verification.
+ bool verifyEncryptionKeyAttributes(const std::string& key_name, bool* verified);
+
+ // Verifies attributes of an authentication key suitable for
+ // EncryptWithAuthentication. Returns true on success and populates |verified|
+ // with the result of the verification.
+ bool verifyAuthenticationKeyAttributes(const std::string& key_name, bool* verified);
+
+ android::sp<android::IServiceManager> service_manager_;
+ android::sp<android::IBinder> keystore_binder_;
+ android::sp<android::IKeystoreService> keystore_;
+ keymaster_operation_handle_t next_virtual_handle_ = 1;
+ std::map<keymaster_operation_handle_t, android::sp<android::IBinder>> active_operations_;
+
+ DISALLOW_COPY_AND_ASSIGN(KeystoreClientImpl);
+};
+
+} // namespace keystore
+
+#endif // KEYSTORE_KEYSTORE_CLIENT_IMPL_H_
diff --git a/security/keystore/include/keystore/keystore_client_mock.h b/security/keystore/include/keystore/keystore_client_mock.h
new file mode 100644
index 0000000..2d1f499
--- /dev/null
+++ b/security/keystore/include/keystore/keystore_client_mock.h
@@ -0,0 +1,88 @@
+// Copyright 2015 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 KEYSTORE_KEYSTORE_CLIENT_MOCK_H_
+#define KEYSTORE_KEYSTORE_CLIENT_MOCK_H_
+
+#include "gmock/gmock.h"
+#include "keystore/keystore_client.h"
+
+using testing::_;
+
+namespace keystore {
+
+// A mock implementation of KeystoreClient. By default all methods do nothing
+// and return KM_ERROR_OK (or false).
+class KeystoreClientMock : public KeystoreClient {
+ public:
+ KeystoreClientMock() = default;
+ ~KeystoreClientMock() = default;
+
+ MOCK_METHOD3(encryptWithAuthentication,
+ bool(const std::string& key_name, const std::string& data,
+ std::string* encrypted_data));
+ MOCK_METHOD3(decryptWithAuthentication,
+ bool(const std::string& key_name, const std::string& encrypted_data,
+ std::string* data));
+ MOCK_METHOD7(oneShotOperation,
+ bool(keymaster_purpose_t purpose, const std::string& key_name,
+ const keymaster::AuthorizationSet& input_parameters,
+ const std::string& input_data, const std::string& signature_to_verify,
+ keymaster::AuthorizationSet* output_parameters, std::string* output_data));
+ MOCK_METHOD1(addRandomNumberGeneratorEntropy, int32_t(const std::string& entropy));
+ MOCK_METHOD4(generateKey,
+ int32_t(const std::string& key_name,
+ const keymaster::AuthorizationSet& key_parameters,
+ keymaster::AuthorizationSet* hardware_enforced_characteristics,
+ keymaster::AuthorizationSet* software_enforced_characteristics));
+ MOCK_METHOD3(getKeyCharacteristics,
+ int32_t(const std::string& key_name,
+ keymaster::AuthorizationSet* hardware_enforced_characteristics,
+ keymaster::AuthorizationSet* software_enforced_characteristics));
+ MOCK_METHOD6(importKey,
+ int32_t(const std::string& key_name,
+ const keymaster::AuthorizationSet& key_parameters,
+ keymaster_key_format_t key_format, const std::string& key_data,
+ keymaster::AuthorizationSet* hardware_enforced_characteristics,
+ keymaster::AuthorizationSet* software_enforced_characteristics));
+ MOCK_METHOD3(exportKey, int32_t(keymaster_key_format_t export_format,
+ const std::string& key_name, std::string* export_data));
+ MOCK_METHOD1(deleteKey, int32_t(const std::string& key_name));
+ MOCK_METHOD0(deleteAllKeys, int32_t());
+ MOCK_METHOD5(beginOperation, int32_t(keymaster_purpose_t purpose, const std::string& key_name,
+ const keymaster::AuthorizationSet& input_parameters,
+ keymaster::AuthorizationSet* output_parameters,
+ keymaster_operation_handle_t* handle));
+ MOCK_METHOD6(updateOperation,
+ int32_t(keymaster_operation_handle_t handle,
+ const keymaster::AuthorizationSet& input_parameters,
+ const std::string& input_data, size_t* num_input_bytes_consumed,
+ keymaster::AuthorizationSet* output_parameters, std::string* output_data));
+ MOCK_METHOD5(finishOperation,
+ int32_t(keymaster_operation_handle_t handle,
+ const keymaster::AuthorizationSet& input_parameters,
+ const std::string& signature_to_verify,
+ keymaster::AuthorizationSet* output_parameters, std::string* output_data));
+ MOCK_METHOD1(abortOperation, int32_t(keymaster_operation_handle_t handle));
+ MOCK_METHOD1(doesKeyExist, bool(const std::string& key_name));
+ MOCK_METHOD2(listKeys,
+ bool(const std::string& prefix, std::vector<std::string>* key_name_list));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(KeystoreClientMock);
+};
+
+} // namespace keystore
+
+#endif // KEYSTORE_KEYSTORE_CLIENT_MOCK_H_
diff --git a/security/keystore/include/keystore/keystore_get.h b/security/keystore/include/keystore/keystore_get.h
new file mode 100644
index 0000000..4bddd70
--- /dev/null
+++ b/security/keystore/include/keystore/keystore_get.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2009 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 __KEYSTORE_GET_H__
+#define __KEYSTORE_GET_H__
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* This function is provided for native components to get values from keystore.
+ * Users are required to link against libkeystore_binder.
+ *
+ * Keys and values are 8-bit safe. The first two arguments are the key and its
+ * length. The third argument is a pointer to an array that will be malloc()
+ * and the caller is responsible for calling free() on the buffer.
+ */
+ssize_t keystore_get(const char *key, size_t length, uint8_t** value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/security/keystore/key_store_service.cpp b/security/keystore/key_store_service.cpp
new file mode 100644
index 0000000..e3df13a
--- /dev/null
+++ b/security/keystore/key_store_service.cpp
@@ -0,0 +1,1488 @@
+/*
+ * Copyright (C) 2016 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 "key_store_service.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include <sstream>
+
+#include <binder/IPCThreadState.h>
+
+#include <private/android_filesystem_config.h>
+
+#include <hardware/keymaster_defs.h>
+
+#include "defaults.h"
+#include "keystore_utils.h"
+
+namespace android {
+
+const size_t MAX_OPERATIONS = 15;
+
+struct BIGNUM_Delete {
+ void operator()(BIGNUM* p) const { BN_free(p); }
+};
+typedef UniquePtr<BIGNUM, BIGNUM_Delete> Unique_BIGNUM;
+
+void KeyStoreService::binderDied(const wp<IBinder>& who) {
+ auto operations = mOperationMap.getOperationsForToken(who.unsafe_get());
+ for (const auto& token : operations) {
+ abort(token);
+ }
+}
+
+int32_t KeyStoreService::getState(int32_t userId) {
+ if (!checkBinderPermission(P_GET_STATE)) {
+ return ::PERMISSION_DENIED;
+ }
+
+ return mKeyStore->getState(userId);
+}
+
+int32_t KeyStoreService::get(const String16& name, uint8_t** item, size_t* itemLength) {
+ if (!checkBinderPermission(P_GET)) {
+ return ::PERMISSION_DENIED;
+ }
+
+ uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ String8 name8(name);
+ Blob keyBlob;
+
+ ResponseCode responseCode = mKeyStore->getKeyForName(&keyBlob, name8, callingUid, TYPE_GENERIC);
+ if (responseCode != ::NO_ERROR) {
+ *item = NULL;
+ *itemLength = 0;
+ return responseCode;
+ }
+
+ *item = (uint8_t*)malloc(keyBlob.getLength());
+ memcpy(*item, keyBlob.getValue(), keyBlob.getLength());
+ *itemLength = keyBlob.getLength();
+
+ return ::NO_ERROR;
+}
+
+int32_t KeyStoreService::insert(const String16& name, const uint8_t* item, size_t itemLength,
+ int targetUid, int32_t flags) {
+ targetUid = getEffectiveUid(targetUid);
+ int32_t result =
+ checkBinderPermissionAndKeystoreState(P_INSERT, targetUid, flags & KEYSTORE_FLAG_ENCRYPTED);
+ if (result != ::NO_ERROR) {
+ return result;
+ }
+
+ String8 name8(name);
+ String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, targetUid));
+
+ Blob keyBlob(item, itemLength, NULL, 0, ::TYPE_GENERIC);
+ keyBlob.setEncrypted(flags & KEYSTORE_FLAG_ENCRYPTED);
+
+ return mKeyStore->put(filename.string(), &keyBlob, get_user_id(targetUid));
+}
+
+int32_t KeyStoreService::del(const String16& name, int targetUid) {
+ targetUid = getEffectiveUid(targetUid);
+ if (!checkBinderPermission(P_DELETE, targetUid)) {
+ return ::PERMISSION_DENIED;
+ }
+ String8 name8(name);
+ String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, targetUid));
+ return mKeyStore->del(filename.string(), ::TYPE_ANY, get_user_id(targetUid));
+}
+
+int32_t KeyStoreService::exist(const String16& name, int targetUid) {
+ targetUid = getEffectiveUid(targetUid);
+ if (!checkBinderPermission(P_EXIST, targetUid)) {
+ return ::PERMISSION_DENIED;
+ }
+
+ String8 name8(name);
+ String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, targetUid));
+
+ if (access(filename.string(), R_OK) == -1) {
+ return (errno != ENOENT) ? ::SYSTEM_ERROR : ::KEY_NOT_FOUND;
+ }
+ return ::NO_ERROR;
+}
+
+int32_t KeyStoreService::list(const String16& prefix, int targetUid, Vector<String16>* matches) {
+ targetUid = getEffectiveUid(targetUid);
+ if (!checkBinderPermission(P_LIST, targetUid)) {
+ return ::PERMISSION_DENIED;
+ }
+ const String8 prefix8(prefix);
+ String8 filename(mKeyStore->getKeyNameForUid(prefix8, targetUid));
+
+ if (mKeyStore->list(filename, matches, get_user_id(targetUid)) != ::NO_ERROR) {
+ return ::SYSTEM_ERROR;
+ }
+ return ::NO_ERROR;
+}
+
+int32_t KeyStoreService::reset() {
+ if (!checkBinderPermission(P_RESET)) {
+ return ::PERMISSION_DENIED;
+ }
+
+ uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ mKeyStore->resetUser(get_user_id(callingUid), false);
+ return ::NO_ERROR;
+}
+
+int32_t KeyStoreService::onUserPasswordChanged(int32_t userId, const String16& password) {
+ if (!checkBinderPermission(P_PASSWORD)) {
+ return ::PERMISSION_DENIED;
+ }
+
+ const String8 password8(password);
+ // Flush the auth token table to prevent stale tokens from sticking
+ // around.
+ mAuthTokenTable.Clear();
+
+ if (password.size() == 0) {
+ ALOGI("Secure lockscreen for user %d removed, deleting encrypted entries", userId);
+ mKeyStore->resetUser(userId, true);
+ return ::NO_ERROR;
+ } else {
+ switch (mKeyStore->getState(userId)) {
+ case ::STATE_UNINITIALIZED: {
+ // generate master key, encrypt with password, write to file,
+ // initialize mMasterKey*.
+ return mKeyStore->initializeUser(password8, userId);
+ }
+ case ::STATE_NO_ERROR: {
+ // rewrite master key with new password.
+ return mKeyStore->writeMasterKey(password8, userId);
+ }
+ case ::STATE_LOCKED: {
+ ALOGE("Changing user %d's password while locked, clearing old encryption", userId);
+ mKeyStore->resetUser(userId, true);
+ return mKeyStore->initializeUser(password8, userId);
+ }
+ }
+ return ::SYSTEM_ERROR;
+ }
+}
+
+int32_t KeyStoreService::onUserAdded(int32_t userId, int32_t parentId) {
+ if (!checkBinderPermission(P_USER_CHANGED)) {
+ return ::PERMISSION_DENIED;
+ }
+
+ // Sanity check that the new user has an empty keystore.
+ if (!mKeyStore->isEmpty(userId)) {
+ ALOGW("New user %d's keystore not empty. Clearing old entries.", userId);
+ }
+ // Unconditionally clear the keystore, just to be safe.
+ mKeyStore->resetUser(userId, false);
+ if (parentId != -1) {
+ // This profile must share the same master key password as the parent profile. Because the
+ // password of the parent profile is not known here, the best we can do is copy the parent's
+ // master key and master key file. This makes this profile use the same master key as the
+ // parent profile, forever.
+ return mKeyStore->copyMasterKey(parentId, userId);
+ } else {
+ return ::NO_ERROR;
+ }
+}
+
+int32_t KeyStoreService::onUserRemoved(int32_t userId) {
+ if (!checkBinderPermission(P_USER_CHANGED)) {
+ return ::PERMISSION_DENIED;
+ }
+
+ mKeyStore->resetUser(userId, false);
+ return ::NO_ERROR;
+}
+
+int32_t KeyStoreService::lock(int32_t userId) {
+ if (!checkBinderPermission(P_LOCK)) {
+ return ::PERMISSION_DENIED;
+ }
+
+ State state = mKeyStore->getState(userId);
+ if (state != ::STATE_NO_ERROR) {
+ ALOGD("calling lock in state: %d", state);
+ return state;
+ }
+
+ mKeyStore->lock(userId);
+ return ::NO_ERROR;
+}
+
+int32_t KeyStoreService::unlock(int32_t userId, const String16& pw) {
+ if (!checkBinderPermission(P_UNLOCK)) {
+ return ::PERMISSION_DENIED;
+ }
+
+ State state = mKeyStore->getState(userId);
+ if (state != ::STATE_LOCKED) {
+ switch (state) {
+ case ::STATE_NO_ERROR:
+ ALOGI("calling unlock when already unlocked, ignoring.");
+ break;
+ case ::STATE_UNINITIALIZED:
+ ALOGE("unlock called on uninitialized keystore.");
+ break;
+ default:
+ ALOGE("unlock called on keystore in unknown state: %d", state);
+ break;
+ }
+ return state;
+ }
+
+ const String8 password8(pw);
+ // read master key, decrypt with password, initialize mMasterKey*.
+ return mKeyStore->readMasterKey(password8, userId);
+}
+
+bool KeyStoreService::isEmpty(int32_t userId) {
+ if (!checkBinderPermission(P_IS_EMPTY)) {
+ return false;
+ }
+
+ return mKeyStore->isEmpty(userId);
+}
+
+int32_t KeyStoreService::generate(const String16& name, int32_t targetUid, int32_t keyType,
+ int32_t keySize, int32_t flags, Vector<sp<KeystoreArg>>* args) {
+ targetUid = getEffectiveUid(targetUid);
+ int32_t result =
+ checkBinderPermissionAndKeystoreState(P_INSERT, targetUid, flags & KEYSTORE_FLAG_ENCRYPTED);
+ if (result != ::NO_ERROR) {
+ return result;
+ }
+
+ KeymasterArguments params;
+ add_legacy_key_authorizations(keyType, ¶ms.params);
+
+ switch (keyType) {
+ case EVP_PKEY_EC: {
+ params.params.push_back(keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_EC));
+ if (keySize == -1) {
+ keySize = EC_DEFAULT_KEY_SIZE;
+ } else if (keySize < EC_MIN_KEY_SIZE || keySize > EC_MAX_KEY_SIZE) {
+ ALOGI("invalid key size %d", keySize);
+ return ::SYSTEM_ERROR;
+ }
+ params.params.push_back(keymaster_param_int(KM_TAG_KEY_SIZE, keySize));
+ break;
+ }
+ case EVP_PKEY_RSA: {
+ params.params.push_back(keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_RSA));
+ if (keySize == -1) {
+ keySize = RSA_DEFAULT_KEY_SIZE;
+ } else if (keySize < RSA_MIN_KEY_SIZE || keySize > RSA_MAX_KEY_SIZE) {
+ ALOGI("invalid key size %d", keySize);
+ return ::SYSTEM_ERROR;
+ }
+ params.params.push_back(keymaster_param_int(KM_TAG_KEY_SIZE, keySize));
+ unsigned long exponent = RSA_DEFAULT_EXPONENT;
+ if (args->size() > 1) {
+ ALOGI("invalid number of arguments: %zu", args->size());
+ return ::SYSTEM_ERROR;
+ } else if (args->size() == 1) {
+ const sp<KeystoreArg>& expArg = args->itemAt(0);
+ if (expArg != NULL) {
+ Unique_BIGNUM pubExpBn(BN_bin2bn(
+ reinterpret_cast<const unsigned char*>(expArg->data()), expArg->size(), NULL));
+ if (pubExpBn.get() == NULL) {
+ ALOGI("Could not convert public exponent to BN");
+ return ::SYSTEM_ERROR;
+ }
+ exponent = BN_get_word(pubExpBn.get());
+ if (exponent == 0xFFFFFFFFL) {
+ ALOGW("cannot represent public exponent as a long value");
+ return ::SYSTEM_ERROR;
+ }
+ } else {
+ ALOGW("public exponent not read");
+ return ::SYSTEM_ERROR;
+ }
+ }
+ params.params.push_back(keymaster_param_long(KM_TAG_RSA_PUBLIC_EXPONENT, exponent));
+ break;
+ }
+ default: {
+ ALOGW("Unsupported key type %d", keyType);
+ return ::SYSTEM_ERROR;
+ }
+ }
+
+ int32_t rc = generateKey(name, params, NULL, 0, targetUid, flags,
+ /*outCharacteristics*/ NULL);
+ if (rc != ::NO_ERROR) {
+ ALOGW("generate failed: %d", rc);
+ }
+ return translateResultToLegacyResult(rc);
+}
+
+int32_t KeyStoreService::import(const String16& name, const uint8_t* data, size_t length,
+ int targetUid, int32_t flags) {
+ const uint8_t* ptr = data;
+
+ Unique_PKCS8_PRIV_KEY_INFO pkcs8(d2i_PKCS8_PRIV_KEY_INFO(NULL, &ptr, length));
+ if (!pkcs8.get()) {
+ return ::SYSTEM_ERROR;
+ }
+ Unique_EVP_PKEY pkey(EVP_PKCS82PKEY(pkcs8.get()));
+ if (!pkey.get()) {
+ return ::SYSTEM_ERROR;
+ }
+ int type = EVP_PKEY_type(pkey->type);
+ KeymasterArguments params;
+ add_legacy_key_authorizations(type, ¶ms.params);
+ switch (type) {
+ case EVP_PKEY_RSA:
+ params.params.push_back(keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_RSA));
+ break;
+ case EVP_PKEY_EC:
+ params.params.push_back(keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_EC));
+ break;
+ default:
+ ALOGW("Unsupported key type %d", type);
+ return ::SYSTEM_ERROR;
+ }
+ int32_t rc = importKey(name, params, KM_KEY_FORMAT_PKCS8, data, length, targetUid, flags,
+ /*outCharacteristics*/ NULL);
+ if (rc != ::NO_ERROR) {
+ ALOGW("importKey failed: %d", rc);
+ }
+ return translateResultToLegacyResult(rc);
+}
+
+int32_t KeyStoreService::sign(const String16& name, const uint8_t* data, size_t length,
+ uint8_t** out, size_t* outLength) {
+ if (!checkBinderPermission(P_SIGN)) {
+ return ::PERMISSION_DENIED;
+ }
+ return doLegacySignVerify(name, data, length, out, outLength, NULL, 0, KM_PURPOSE_SIGN);
+}
+
+int32_t KeyStoreService::verify(const String16& name, const uint8_t* data, size_t dataLength,
+ const uint8_t* signature, size_t signatureLength) {
+ if (!checkBinderPermission(P_VERIFY)) {
+ return ::PERMISSION_DENIED;
+ }
+ return doLegacySignVerify(name, data, dataLength, NULL, NULL, signature, signatureLength,
+ KM_PURPOSE_VERIFY);
+}
+
+/*
+ * TODO: The abstraction between things stored in hardware and regular blobs
+ * of data stored on the filesystem should be moved down to keystore itself.
+ * Unfortunately the Java code that calls this has naming conventions that it
+ * knows about. Ideally keystore shouldn't be used to store random blobs of
+ * data.
+ *
+ * Until that happens, it's necessary to have a separate "get_pubkey" and
+ * "del_key" since the Java code doesn't really communicate what it's
+ * intentions are.
+ */
+int32_t KeyStoreService::get_pubkey(const String16& name, uint8_t** pubkey, size_t* pubkeyLength) {
+ ExportResult result;
+ exportKey(name, KM_KEY_FORMAT_X509, NULL, NULL, &result);
+ if (result.resultCode != ::NO_ERROR) {
+ ALOGW("export failed: %d", result.resultCode);
+ return translateResultToLegacyResult(result.resultCode);
+ }
+
+ *pubkey = result.exportData.release();
+ *pubkeyLength = result.dataLength;
+ return ::NO_ERROR;
+}
+
+int32_t KeyStoreService::grant(const String16& name, int32_t granteeUid) {
+ uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ int32_t result = checkBinderPermissionAndKeystoreState(P_GRANT);
+ if (result != ::NO_ERROR) {
+ return result;
+ }
+
+ String8 name8(name);
+ String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, callingUid));
+
+ if (access(filename.string(), R_OK) == -1) {
+ return (errno != ENOENT) ? ::SYSTEM_ERROR : ::KEY_NOT_FOUND;
+ }
+
+ mKeyStore->addGrant(filename.string(), granteeUid);
+ return ::NO_ERROR;
+}
+
+int32_t KeyStoreService::ungrant(const String16& name, int32_t granteeUid) {
+ uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ int32_t result = checkBinderPermissionAndKeystoreState(P_GRANT);
+ if (result != ::NO_ERROR) {
+ return result;
+ }
+
+ String8 name8(name);
+ String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, callingUid));
+
+ if (access(filename.string(), R_OK) == -1) {
+ return (errno != ENOENT) ? ::SYSTEM_ERROR : ::KEY_NOT_FOUND;
+ }
+
+ return mKeyStore->removeGrant(filename.string(), granteeUid) ? ::NO_ERROR : ::KEY_NOT_FOUND;
+}
+
+int64_t KeyStoreService::getmtime(const String16& name) {
+ uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ if (!checkBinderPermission(P_GET)) {
+ ALOGW("permission denied for %d: getmtime", callingUid);
+ return -1L;
+ }
+
+ String8 name8(name);
+ String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, callingUid));
+
+ if (access(filename.string(), R_OK) == -1) {
+ ALOGW("could not access %s for getmtime", filename.string());
+ return -1L;
+ }
+
+ int fd = TEMP_FAILURE_RETRY(open(filename.string(), O_NOFOLLOW, O_RDONLY));
+ if (fd < 0) {
+ ALOGW("could not open %s for getmtime", filename.string());
+ return -1L;
+ }
+
+ struct stat s;
+ int ret = fstat(fd, &s);
+ close(fd);
+ if (ret == -1) {
+ ALOGW("could not stat %s for getmtime", filename.string());
+ return -1L;
+ }
+
+ return static_cast<int64_t>(s.st_mtime);
+}
+
+int32_t KeyStoreService::duplicate(const String16& srcKey, int32_t srcUid, const String16& destKey,
+ int32_t destUid) {
+ uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ pid_t spid = IPCThreadState::self()->getCallingPid();
+ if (!has_permission(callingUid, P_DUPLICATE, spid)) {
+ ALOGW("permission denied for %d: duplicate", callingUid);
+ return -1L;
+ }
+
+ State state = mKeyStore->getState(get_user_id(callingUid));
+ if (!isKeystoreUnlocked(state)) {
+ ALOGD("calling duplicate in state: %d", state);
+ return state;
+ }
+
+ if (srcUid == -1 || static_cast<uid_t>(srcUid) == callingUid) {
+ srcUid = callingUid;
+ } else if (!is_granted_to(callingUid, srcUid)) {
+ ALOGD("migrate not granted from source: %d -> %d", callingUid, srcUid);
+ return ::PERMISSION_DENIED;
+ }
+
+ if (destUid == -1) {
+ destUid = callingUid;
+ }
+
+ if (srcUid != destUid) {
+ if (static_cast<uid_t>(srcUid) != callingUid) {
+ ALOGD("can only duplicate from caller to other or to same uid: "
+ "calling=%d, srcUid=%d, destUid=%d",
+ callingUid, srcUid, destUid);
+ return ::PERMISSION_DENIED;
+ }
+
+ if (!is_granted_to(callingUid, destUid)) {
+ ALOGD("duplicate not granted to dest: %d -> %d", callingUid, destUid);
+ return ::PERMISSION_DENIED;
+ }
+ }
+
+ String8 source8(srcKey);
+ String8 sourceFile(mKeyStore->getKeyNameForUidWithDir(source8, srcUid));
+
+ String8 target8(destKey);
+ String8 targetFile(mKeyStore->getKeyNameForUidWithDir(target8, destUid));
+
+ if (access(targetFile.string(), W_OK) != -1 || errno != ENOENT) {
+ ALOGD("destination already exists: %s", targetFile.string());
+ return ::SYSTEM_ERROR;
+ }
+
+ Blob keyBlob;
+ ResponseCode responseCode =
+ mKeyStore->get(sourceFile.string(), &keyBlob, TYPE_ANY, get_user_id(srcUid));
+ if (responseCode != ::NO_ERROR) {
+ return responseCode;
+ }
+
+ return mKeyStore->put(targetFile.string(), &keyBlob, get_user_id(destUid));
+}
+
+int32_t KeyStoreService::is_hardware_backed(const String16& keyType) {
+ return mKeyStore->isHardwareBacked(keyType) ? 1 : 0;
+}
+
+int32_t KeyStoreService::clear_uid(int64_t targetUid64) {
+ uid_t targetUid = getEffectiveUid(targetUid64);
+ if (!checkBinderPermissionSelfOrSystem(P_CLEAR_UID, targetUid)) {
+ return ::PERMISSION_DENIED;
+ }
+
+ String8 prefix = String8::format("%u_", targetUid);
+ Vector<String16> aliases;
+ if (mKeyStore->list(prefix, &aliases, get_user_id(targetUid)) != ::NO_ERROR) {
+ return ::SYSTEM_ERROR;
+ }
+
+ for (uint32_t i = 0; i < aliases.size(); i++) {
+ String8 name8(aliases[i]);
+ String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, targetUid));
+ mKeyStore->del(filename.string(), ::TYPE_ANY, get_user_id(targetUid));
+ }
+ return ::NO_ERROR;
+}
+
+int32_t KeyStoreService::addRngEntropy(const uint8_t* data, size_t dataLength) {
+ const auto* device = mKeyStore->getDevice();
+ const auto* fallback = mKeyStore->getFallbackDevice();
+ int32_t devResult = KM_ERROR_UNIMPLEMENTED;
+ int32_t fallbackResult = KM_ERROR_UNIMPLEMENTED;
+ if (device->common.module->module_api_version >= KEYMASTER_MODULE_API_VERSION_1_0 &&
+ device->add_rng_entropy != NULL) {
+ devResult = device->add_rng_entropy(device, data, dataLength);
+ }
+ if (fallback->add_rng_entropy) {
+ fallbackResult = fallback->add_rng_entropy(fallback, data, dataLength);
+ }
+ if (devResult) {
+ return devResult;
+ }
+ if (fallbackResult) {
+ return fallbackResult;
+ }
+ return ::NO_ERROR;
+}
+
+int32_t KeyStoreService::generateKey(const String16& name, const KeymasterArguments& params,
+ const uint8_t* entropy, size_t entropyLength, int uid,
+ int flags, KeyCharacteristics* outCharacteristics) {
+ uid = getEffectiveUid(uid);
+ int rc = checkBinderPermissionAndKeystoreState(P_INSERT, uid, flags & KEYSTORE_FLAG_ENCRYPTED);
+ if (rc != ::NO_ERROR) {
+ return rc;
+ }
+
+ rc = KM_ERROR_UNIMPLEMENTED;
+ bool isFallback = false;
+ keymaster_key_blob_t blob;
+ keymaster_key_characteristics_t out = {{nullptr, 0}, {nullptr, 0}};
+
+ const auto* device = mKeyStore->getDevice();
+ const auto* fallback = mKeyStore->getFallbackDevice();
+ std::vector<keymaster_key_param_t> opParams(params.params);
+ const keymaster_key_param_set_t inParams = {opParams.data(), opParams.size()};
+ if (device == NULL) {
+ return ::SYSTEM_ERROR;
+ }
+ // TODO: Seed from Linux RNG before this.
+ if (device->common.module->module_api_version >= KEYMASTER_MODULE_API_VERSION_1_0 &&
+ device->generate_key != NULL) {
+ if (!entropy) {
+ rc = KM_ERROR_OK;
+ } else if (device->add_rng_entropy) {
+ rc = device->add_rng_entropy(device, entropy, entropyLength);
+ } else {
+ rc = KM_ERROR_UNIMPLEMENTED;
+ }
+ if (rc == KM_ERROR_OK) {
+ rc =
+ device->generate_key(device, &inParams, &blob, outCharacteristics ? &out : nullptr);
+ }
+ }
+ // If the HW device didn't support generate_key or generate_key failed
+ // fall back to the software implementation.
+ if (rc && fallback->generate_key != NULL) {
+ ALOGW("Primary keymaster device failed to generate key, falling back to SW.");
+ isFallback = true;
+ if (!entropy) {
+ rc = KM_ERROR_OK;
+ } else if (fallback->add_rng_entropy) {
+ rc = fallback->add_rng_entropy(fallback, entropy, entropyLength);
+ } else {
+ rc = KM_ERROR_UNIMPLEMENTED;
+ }
+ if (rc == KM_ERROR_OK) {
+ rc = fallback->generate_key(fallback, &inParams, &blob,
+ outCharacteristics ? &out : nullptr);
+ }
+ }
+
+ if (outCharacteristics) {
+ outCharacteristics->characteristics = out;
+ }
+
+ if (rc) {
+ return rc;
+ }
+
+ String8 name8(name);
+ String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, uid));
+
+ Blob keyBlob(blob.key_material, blob.key_material_size, NULL, 0, ::TYPE_KEYMASTER_10);
+ keyBlob.setFallback(isFallback);
+ keyBlob.setEncrypted(flags & KEYSTORE_FLAG_ENCRYPTED);
+
+ free(const_cast<uint8_t*>(blob.key_material));
+
+ return mKeyStore->put(filename.string(), &keyBlob, get_user_id(uid));
+}
+
+int32_t KeyStoreService::getKeyCharacteristics(const String16& name,
+ const keymaster_blob_t* clientId,
+ const keymaster_blob_t* appData,
+ KeyCharacteristics* outCharacteristics) {
+ if (!outCharacteristics) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+
+ uid_t callingUid = IPCThreadState::self()->getCallingUid();
+
+ Blob keyBlob;
+ String8 name8(name);
+ int rc;
+
+ ResponseCode responseCode =
+ mKeyStore->getKeyForName(&keyBlob, name8, callingUid, TYPE_KEYMASTER_10);
+ if (responseCode != ::NO_ERROR) {
+ return responseCode;
+ }
+ keymaster_key_blob_t key;
+ key.key_material_size = keyBlob.getLength();
+ key.key_material = keyBlob.getValue();
+ auto* dev = mKeyStore->getDeviceForBlob(keyBlob);
+ keymaster_key_characteristics_t out = {{nullptr, 0}, {nullptr, 0}};
+ if (!dev->get_key_characteristics) {
+ ALOGE("device does not implement get_key_characteristics");
+ return KM_ERROR_UNIMPLEMENTED;
+ }
+ rc = dev->get_key_characteristics(dev, &key, clientId, appData, &out);
+ if (rc != KM_ERROR_OK) {
+ return rc;
+ }
+
+ outCharacteristics->characteristics = out;
+ return ::NO_ERROR;
+}
+
+int32_t KeyStoreService::importKey(const String16& name, const KeymasterArguments& params,
+ keymaster_key_format_t format, const uint8_t* keyData,
+ size_t keyLength, int uid, int flags,
+ KeyCharacteristics* outCharacteristics) {
+ uid = getEffectiveUid(uid);
+ int rc = checkBinderPermissionAndKeystoreState(P_INSERT, uid, flags & KEYSTORE_FLAG_ENCRYPTED);
+ if (rc != ::NO_ERROR) {
+ return rc;
+ }
+
+ rc = KM_ERROR_UNIMPLEMENTED;
+ bool isFallback = false;
+ keymaster_key_blob_t blob;
+ keymaster_key_characteristics_t out = {{nullptr, 0}, {nullptr, 0}};
+
+ const auto* device = mKeyStore->getDevice();
+ const auto* fallback = mKeyStore->getFallbackDevice();
+ std::vector<keymaster_key_param_t> opParams(params.params);
+ const keymaster_key_param_set_t inParams = {opParams.data(), opParams.size()};
+ const keymaster_blob_t input = {keyData, keyLength};
+ if (device == NULL) {
+ return ::SYSTEM_ERROR;
+ }
+ if (device->common.module->module_api_version >= KEYMASTER_MODULE_API_VERSION_1_0 &&
+ device->import_key != NULL) {
+ rc = device->import_key(device, &inParams, format, &input, &blob,
+ outCharacteristics ? &out : nullptr);
+ }
+ if (rc && fallback->import_key != NULL) {
+ ALOGW("Primary keymaster device failed to import key, falling back to SW.");
+ isFallback = true;
+ rc = fallback->import_key(fallback, &inParams, format, &input, &blob,
+ outCharacteristics ? &out : nullptr);
+ }
+ if (outCharacteristics) {
+ outCharacteristics->characteristics = out;
+ }
+
+ if (rc) {
+ return rc;
+ }
+
+ String8 name8(name);
+ String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, uid));
+
+ Blob keyBlob(blob.key_material, blob.key_material_size, NULL, 0, ::TYPE_KEYMASTER_10);
+ keyBlob.setFallback(isFallback);
+ keyBlob.setEncrypted(flags & KEYSTORE_FLAG_ENCRYPTED);
+
+ free(const_cast<uint8_t*>(blob.key_material));
+
+ return mKeyStore->put(filename.string(), &keyBlob, get_user_id(uid));
+}
+
+void KeyStoreService::exportKey(const String16& name, keymaster_key_format_t format,
+ const keymaster_blob_t* clientId, const keymaster_blob_t* appData,
+ ExportResult* result) {
+
+ uid_t callingUid = IPCThreadState::self()->getCallingUid();
+
+ Blob keyBlob;
+ String8 name8(name);
+ int rc;
+
+ ResponseCode responseCode =
+ mKeyStore->getKeyForName(&keyBlob, name8, callingUid, TYPE_KEYMASTER_10);
+ if (responseCode != ::NO_ERROR) {
+ result->resultCode = responseCode;
+ return;
+ }
+ keymaster_key_blob_t key;
+ key.key_material_size = keyBlob.getLength();
+ key.key_material = keyBlob.getValue();
+ auto* dev = mKeyStore->getDeviceForBlob(keyBlob);
+ if (!dev->export_key) {
+ result->resultCode = KM_ERROR_UNIMPLEMENTED;
+ return;
+ }
+ keymaster_blob_t output = {NULL, 0};
+ rc = dev->export_key(dev, format, &key, clientId, appData, &output);
+ result->exportData.reset(const_cast<uint8_t*>(output.data));
+ result->dataLength = output.data_length;
+ result->resultCode = rc ? rc : ::NO_ERROR;
+}
+
+void KeyStoreService::begin(const sp<IBinder>& appToken, const String16& name,
+ keymaster_purpose_t purpose, bool pruneable,
+ const KeymasterArguments& params, const uint8_t* entropy,
+ size_t entropyLength, OperationResult* result) {
+ uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ if (!pruneable && get_app_id(callingUid) != AID_SYSTEM) {
+ ALOGE("Non-system uid %d trying to start non-pruneable operation", callingUid);
+ result->resultCode = ::PERMISSION_DENIED;
+ return;
+ }
+ if (!checkAllowedOperationParams(params.params)) {
+ result->resultCode = KM_ERROR_INVALID_ARGUMENT;
+ return;
+ }
+ Blob keyBlob;
+ String8 name8(name);
+ ResponseCode responseCode =
+ mKeyStore->getKeyForName(&keyBlob, name8, callingUid, TYPE_KEYMASTER_10);
+ if (responseCode != ::NO_ERROR) {
+ result->resultCode = responseCode;
+ return;
+ }
+ keymaster_key_blob_t key;
+ key.key_material_size = keyBlob.getLength();
+ key.key_material = keyBlob.getValue();
+ keymaster_operation_handle_t handle;
+ auto* dev = mKeyStore->getDeviceForBlob(keyBlob);
+ keymaster_error_t err = KM_ERROR_UNIMPLEMENTED;
+ std::vector<keymaster_key_param_t> opParams(params.params);
+ Unique_keymaster_key_characteristics characteristics;
+ characteristics.reset(new keymaster_key_characteristics_t);
+ err = getOperationCharacteristics(key, dev, opParams, characteristics.get());
+ if (err) {
+ result->resultCode = err;
+ return;
+ }
+ const hw_auth_token_t* authToken = NULL;
+ int32_t authResult = getAuthToken(characteristics.get(), 0, purpose, &authToken,
+ /*failOnTokenMissing*/ false);
+ // If per-operation auth is needed we need to begin the operation and
+ // the client will need to authorize that operation before calling
+ // update. Any other auth issues stop here.
+ if (authResult != ::NO_ERROR && authResult != ::OP_AUTH_NEEDED) {
+ result->resultCode = authResult;
+ return;
+ }
+ addAuthToParams(&opParams, authToken);
+ // Add entropy to the device first.
+ if (entropy) {
+ if (dev->add_rng_entropy) {
+ err = dev->add_rng_entropy(dev, entropy, entropyLength);
+ } else {
+ err = KM_ERROR_UNIMPLEMENTED;
+ }
+ if (err) {
+ result->resultCode = err;
+ return;
+ }
+ }
+ keymaster_key_param_set_t inParams = {opParams.data(), opParams.size()};
+
+ // Create a keyid for this key.
+ keymaster::km_id_t keyid;
+ if (!enforcement_policy.CreateKeyId(key, &keyid)) {
+ ALOGE("Failed to create a key ID for authorization checking.");
+ result->resultCode = KM_ERROR_UNKNOWN_ERROR;
+ return;
+ }
+
+ // Check that all key authorization policy requirements are met.
+ keymaster::AuthorizationSet key_auths(characteristics->hw_enforced);
+ key_auths.push_back(characteristics->sw_enforced);
+ keymaster::AuthorizationSet operation_params(inParams);
+ err = enforcement_policy.AuthorizeOperation(purpose, keyid, key_auths, operation_params,
+ 0 /* op_handle */, true /* is_begin_operation */);
+ if (err) {
+ result->resultCode = err;
+ return;
+ }
+
+ keymaster_key_param_set_t outParams = {NULL, 0};
+
+ // If there are more than MAX_OPERATIONS, abort the oldest operation that was started as
+ // pruneable.
+ while (mOperationMap.getOperationCount() >= MAX_OPERATIONS) {
+ ALOGD("Reached or exceeded concurrent operations limit");
+ if (!pruneOperation()) {
+ break;
+ }
+ }
+
+ err = dev->begin(dev, purpose, &key, &inParams, &outParams, &handle);
+ if (err != KM_ERROR_OK) {
+ ALOGE("Got error %d from begin()", err);
+ }
+
+ // If there are too many operations abort the oldest operation that was
+ // started as pruneable and try again.
+ while (err == KM_ERROR_TOO_MANY_OPERATIONS && mOperationMap.hasPruneableOperation()) {
+ ALOGE("Ran out of operation handles");
+ if (!pruneOperation()) {
+ break;
+ }
+ err = dev->begin(dev, purpose, &key, &inParams, &outParams, &handle);
+ }
+ if (err) {
+ result->resultCode = err;
+ return;
+ }
+
+ sp<IBinder> operationToken = mOperationMap.addOperation(handle, keyid, purpose, dev, appToken,
+ characteristics.release(), pruneable);
+ if (authToken) {
+ mOperationMap.setOperationAuthToken(operationToken, authToken);
+ }
+ // Return the authentication lookup result. If this is a per operation
+ // auth'd key then the resultCode will be ::OP_AUTH_NEEDED and the
+ // application should get an auth token using the handle before the
+ // first call to update, which will fail if keystore hasn't received the
+ // auth token.
+ result->resultCode = authResult;
+ result->token = operationToken;
+ result->handle = handle;
+ if (outParams.params) {
+ result->outParams.params.assign(outParams.params, outParams.params + outParams.length);
+ free(outParams.params);
+ }
+}
+
+void KeyStoreService::update(const sp<IBinder>& token, const KeymasterArguments& params,
+ const uint8_t* data, size_t dataLength, OperationResult* result) {
+ if (!checkAllowedOperationParams(params.params)) {
+ result->resultCode = KM_ERROR_INVALID_ARGUMENT;
+ return;
+ }
+ const keymaster2_device_t* dev;
+ keymaster_operation_handle_t handle;
+ keymaster_purpose_t purpose;
+ keymaster::km_id_t keyid;
+ const keymaster_key_characteristics_t* characteristics;
+ if (!mOperationMap.getOperation(token, &handle, &keyid, &purpose, &dev, &characteristics)) {
+ result->resultCode = KM_ERROR_INVALID_OPERATION_HANDLE;
+ return;
+ }
+ std::vector<keymaster_key_param_t> opParams(params.params);
+ int32_t authResult = addOperationAuthTokenIfNeeded(token, &opParams);
+ if (authResult != ::NO_ERROR) {
+ result->resultCode = authResult;
+ return;
+ }
+ keymaster_key_param_set_t inParams = {opParams.data(), opParams.size()};
+ keymaster_blob_t input = {data, dataLength};
+ size_t consumed = 0;
+ keymaster_blob_t output = {NULL, 0};
+ keymaster_key_param_set_t outParams = {NULL, 0};
+
+ // Check that all key authorization policy requirements are met.
+ keymaster::AuthorizationSet key_auths(characteristics->hw_enforced);
+ key_auths.push_back(characteristics->sw_enforced);
+ keymaster::AuthorizationSet operation_params(inParams);
+ result->resultCode = enforcement_policy.AuthorizeOperation(
+ purpose, keyid, key_auths, operation_params, handle, false /* is_begin_operation */);
+ if (result->resultCode) {
+ return;
+ }
+
+ keymaster_error_t err =
+ dev->update(dev, handle, &inParams, &input, &consumed, &outParams, &output);
+ result->data.reset(const_cast<uint8_t*>(output.data));
+ result->dataLength = output.data_length;
+ result->inputConsumed = consumed;
+ result->resultCode = err ? (int32_t)err : ::NO_ERROR;
+ if (outParams.params) {
+ result->outParams.params.assign(outParams.params, outParams.params + outParams.length);
+ free(outParams.params);
+ }
+}
+
+void KeyStoreService::finish(const sp<IBinder>& token, const KeymasterArguments& params,
+ const uint8_t* signature, size_t signatureLength,
+ const uint8_t* entropy, size_t entropyLength,
+ OperationResult* result) {
+ if (!checkAllowedOperationParams(params.params)) {
+ result->resultCode = KM_ERROR_INVALID_ARGUMENT;
+ return;
+ }
+ const keymaster2_device_t* dev;
+ keymaster_operation_handle_t handle;
+ keymaster_purpose_t purpose;
+ keymaster::km_id_t keyid;
+ const keymaster_key_characteristics_t* characteristics;
+ if (!mOperationMap.getOperation(token, &handle, &keyid, &purpose, &dev, &characteristics)) {
+ result->resultCode = KM_ERROR_INVALID_OPERATION_HANDLE;
+ return;
+ }
+ std::vector<keymaster_key_param_t> opParams(params.params);
+ int32_t authResult = addOperationAuthTokenIfNeeded(token, &opParams);
+ if (authResult != ::NO_ERROR) {
+ result->resultCode = authResult;
+ return;
+ }
+ keymaster_error_t err;
+ if (entropy) {
+ if (dev->add_rng_entropy) {
+ err = dev->add_rng_entropy(dev, entropy, entropyLength);
+ } else {
+ err = KM_ERROR_UNIMPLEMENTED;
+ }
+ if (err) {
+ result->resultCode = err;
+ return;
+ }
+ }
+
+ keymaster_key_param_set_t inParams = {opParams.data(), opParams.size()};
+ keymaster_blob_t input = {nullptr, 0};
+ keymaster_blob_t sig = {signature, signatureLength};
+ keymaster_blob_t output = {nullptr, 0};
+ keymaster_key_param_set_t outParams = {nullptr, 0};
+
+ // Check that all key authorization policy requirements are met.
+ keymaster::AuthorizationSet key_auths(characteristics->hw_enforced);
+ key_auths.push_back(characteristics->sw_enforced);
+ keymaster::AuthorizationSet operation_params(inParams);
+ err = enforcement_policy.AuthorizeOperation(purpose, keyid, key_auths, operation_params, handle,
+ false /* is_begin_operation */);
+ if (err) {
+ result->resultCode = err;
+ return;
+ }
+
+ err =
+ dev->finish(dev, handle, &inParams, &input /* TODO(swillden): wire up input to finish() */,
+ &sig, &outParams, &output);
+ // Remove the operation regardless of the result
+ mOperationMap.removeOperation(token);
+ mAuthTokenTable.MarkCompleted(handle);
+
+ result->data.reset(const_cast<uint8_t*>(output.data));
+ result->dataLength = output.data_length;
+ result->resultCode = err ? (int32_t)err : ::NO_ERROR;
+ if (outParams.params) {
+ result->outParams.params.assign(outParams.params, outParams.params + outParams.length);
+ free(outParams.params);
+ }
+}
+
+int32_t KeyStoreService::abort(const sp<IBinder>& token) {
+ const keymaster2_device_t* dev;
+ keymaster_operation_handle_t handle;
+ keymaster_purpose_t purpose;
+ keymaster::km_id_t keyid;
+ if (!mOperationMap.getOperation(token, &handle, &keyid, &purpose, &dev, NULL)) {
+ return KM_ERROR_INVALID_OPERATION_HANDLE;
+ }
+ mOperationMap.removeOperation(token);
+ int32_t rc;
+ if (!dev->abort) {
+ rc = KM_ERROR_UNIMPLEMENTED;
+ } else {
+ rc = dev->abort(dev, handle);
+ }
+ mAuthTokenTable.MarkCompleted(handle);
+ if (rc) {
+ return rc;
+ }
+ return ::NO_ERROR;
+}
+
+bool KeyStoreService::isOperationAuthorized(const sp<IBinder>& token) {
+ const keymaster2_device_t* dev;
+ keymaster_operation_handle_t handle;
+ const keymaster_key_characteristics_t* characteristics;
+ keymaster_purpose_t purpose;
+ keymaster::km_id_t keyid;
+ if (!mOperationMap.getOperation(token, &handle, &keyid, &purpose, &dev, &characteristics)) {
+ return false;
+ }
+ const hw_auth_token_t* authToken = NULL;
+ mOperationMap.getOperationAuthToken(token, &authToken);
+ std::vector<keymaster_key_param_t> ignored;
+ int32_t authResult = addOperationAuthTokenIfNeeded(token, &ignored);
+ return authResult == ::NO_ERROR;
+}
+
+int32_t KeyStoreService::addAuthToken(const uint8_t* token, size_t length) {
+ if (!checkBinderPermission(P_ADD_AUTH)) {
+ ALOGW("addAuthToken: permission denied for %d", IPCThreadState::self()->getCallingUid());
+ return ::PERMISSION_DENIED;
+ }
+ if (length != sizeof(hw_auth_token_t)) {
+ return KM_ERROR_INVALID_ARGUMENT;
+ }
+ hw_auth_token_t* authToken = new hw_auth_token_t;
+ memcpy(reinterpret_cast<void*>(authToken), token, sizeof(hw_auth_token_t));
+ // The table takes ownership of authToken.
+ mAuthTokenTable.AddAuthenticationToken(authToken);
+ return ::NO_ERROR;
+}
+
+int32_t KeyStoreService::attestKey(const String16& name, const KeymasterArguments& params,
+ KeymasterCertificateChain* outChain) {
+ if (!outChain)
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+
+ if (!checkAllowedOperationParams(params.params)) {
+ return KM_ERROR_INVALID_ARGUMENT;
+ }
+
+ uid_t callingUid = IPCThreadState::self()->getCallingUid();
+
+ Blob keyBlob;
+ String8 name8(name);
+ ResponseCode responseCode =
+ mKeyStore->getKeyForName(&keyBlob, name8, callingUid, TYPE_KEYMASTER_10);
+ if (responseCode != ::NO_ERROR) {
+ return responseCode;
+ }
+
+ keymaster_key_blob_t key = {keyBlob.getValue(),
+ static_cast<size_t>(std::max(0, keyBlob.getLength()))};
+ auto* dev = mKeyStore->getDeviceForBlob(keyBlob);
+ if (!dev->attest_key)
+ return KM_ERROR_UNIMPLEMENTED;
+
+ const keymaster_key_param_set_t in_params = {
+ const_cast<keymaster_key_param_t*>(params.params.data()), params.params.size()};
+ outChain->chain = {nullptr, 0};
+ int32_t rc = dev->attest_key(dev, &key, &in_params, &outChain->chain);
+ if (rc)
+ return rc;
+ return ::NO_ERROR;
+}
+
+/**
+ * Prune the oldest pruneable operation.
+ */
+bool KeyStoreService::pruneOperation() {
+ sp<IBinder> oldest = mOperationMap.getOldestPruneableOperation();
+ ALOGD("Trying to prune operation %p", oldest.get());
+ size_t op_count_before_abort = mOperationMap.getOperationCount();
+ // We mostly ignore errors from abort() because all we care about is whether at least
+ // one operation has been removed.
+ int abort_error = abort(oldest);
+ if (mOperationMap.getOperationCount() >= op_count_before_abort) {
+ ALOGE("Failed to abort pruneable operation %p, error: %d", oldest.get(), abort_error);
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Get the effective target uid for a binder operation that takes an
+ * optional uid as the target.
+ */
+uid_t KeyStoreService::getEffectiveUid(int32_t targetUid) {
+ if (targetUid == UID_SELF) {
+ return IPCThreadState::self()->getCallingUid();
+ }
+ return static_cast<uid_t>(targetUid);
+}
+
+/**
+ * Check if the caller of the current binder method has the required
+ * permission and if acting on other uids the grants to do so.
+ */
+bool KeyStoreService::checkBinderPermission(perm_t permission, int32_t targetUid) {
+ uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ pid_t spid = IPCThreadState::self()->getCallingPid();
+ if (!has_permission(callingUid, permission, spid)) {
+ ALOGW("permission %s denied for %d", get_perm_label(permission), callingUid);
+ return false;
+ }
+ if (!is_granted_to(callingUid, getEffectiveUid(targetUid))) {
+ ALOGW("uid %d not granted to act for %d", callingUid, targetUid);
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Check if the caller of the current binder method has the required
+ * permission and the target uid is the caller or the caller is system.
+ */
+bool KeyStoreService::checkBinderPermissionSelfOrSystem(perm_t permission, int32_t targetUid) {
+ uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ pid_t spid = IPCThreadState::self()->getCallingPid();
+ if (!has_permission(callingUid, permission, spid)) {
+ ALOGW("permission %s denied for %d", get_perm_label(permission), callingUid);
+ return false;
+ }
+ return getEffectiveUid(targetUid) == callingUid || callingUid == AID_SYSTEM;
+}
+
+/**
+ * Check if the caller of the current binder method has the required
+ * permission or the target of the operation is the caller's uid. This is
+ * for operation where the permission is only for cross-uid activity and all
+ * uids are allowed to act on their own (ie: clearing all entries for a
+ * given uid).
+ */
+bool KeyStoreService::checkBinderPermissionOrSelfTarget(perm_t permission, int32_t targetUid) {
+ uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ if (getEffectiveUid(targetUid) == callingUid) {
+ return true;
+ } else {
+ return checkBinderPermission(permission, targetUid);
+ }
+}
+
+/**
+ * Helper method to check that the caller has the required permission as
+ * well as the keystore is in the unlocked state if checkUnlocked is true.
+ *
+ * Returns NO_ERROR on success, PERMISSION_DENIED on a permission error and
+ * otherwise the state of keystore when not unlocked and checkUnlocked is
+ * true.
+ */
+int32_t KeyStoreService::checkBinderPermissionAndKeystoreState(perm_t permission, int32_t targetUid,
+ bool checkUnlocked) {
+ if (!checkBinderPermission(permission, targetUid)) {
+ return ::PERMISSION_DENIED;
+ }
+ State state = mKeyStore->getState(get_user_id(getEffectiveUid(targetUid)));
+ if (checkUnlocked && !isKeystoreUnlocked(state)) {
+ return state;
+ }
+
+ return ::NO_ERROR;
+}
+
+inline bool KeyStoreService::isKeystoreUnlocked(State state) {
+ switch (state) {
+ case ::STATE_NO_ERROR:
+ return true;
+ case ::STATE_UNINITIALIZED:
+ case ::STATE_LOCKED:
+ return false;
+ }
+ return false;
+}
+
+bool KeyStoreService::isKeyTypeSupported(const keymaster2_device_t* device,
+ keymaster_keypair_t keyType) {
+ const int32_t device_api = device->common.module->module_api_version;
+ if (device_api == KEYMASTER_MODULE_API_VERSION_0_2) {
+ switch (keyType) {
+ case TYPE_RSA:
+ case TYPE_DSA:
+ case TYPE_EC:
+ return true;
+ default:
+ return false;
+ }
+ } else if (device_api >= KEYMASTER_MODULE_API_VERSION_0_3) {
+ switch (keyType) {
+ case TYPE_RSA:
+ return true;
+ case TYPE_DSA:
+ return device->flags & KEYMASTER_SUPPORTS_DSA;
+ case TYPE_EC:
+ return device->flags & KEYMASTER_SUPPORTS_EC;
+ default:
+ return false;
+ }
+ } else {
+ return keyType == TYPE_RSA;
+ }
+}
+
+/**
+ * Check that all keymaster_key_param_t's provided by the application are
+ * allowed. Any parameter that keystore adds itself should be disallowed here.
+ */
+bool KeyStoreService::checkAllowedOperationParams(
+ const std::vector<keymaster_key_param_t>& params) {
+ for (auto param : params) {
+ switch (param.tag) {
+ case KM_TAG_AUTH_TOKEN:
+ return false;
+ default:
+ break;
+ }
+ }
+ return true;
+}
+
+keymaster_error_t KeyStoreService::getOperationCharacteristics(
+ const keymaster_key_blob_t& key, const keymaster2_device_t* dev,
+ const std::vector<keymaster_key_param_t>& params, keymaster_key_characteristics_t* out) {
+ UniquePtr<keymaster_blob_t> appId;
+ UniquePtr<keymaster_blob_t> appData;
+ for (auto param : params) {
+ if (param.tag == KM_TAG_APPLICATION_ID) {
+ appId.reset(new keymaster_blob_t);
+ appId->data = param.blob.data;
+ appId->data_length = param.blob.data_length;
+ } else if (param.tag == KM_TAG_APPLICATION_DATA) {
+ appData.reset(new keymaster_blob_t);
+ appData->data = param.blob.data;
+ appData->data_length = param.blob.data_length;
+ }
+ }
+ keymaster_key_characteristics_t result = {{nullptr, 0}, {nullptr, 0}};
+ if (!dev->get_key_characteristics) {
+ return KM_ERROR_UNIMPLEMENTED;
+ }
+ keymaster_error_t error =
+ dev->get_key_characteristics(dev, &key, appId.get(), appData.get(), &result);
+ if (error == KM_ERROR_OK) {
+ *out = result;
+ }
+ return error;
+}
+
+/**
+ * Get the auth token for this operation from the auth token table.
+ *
+ * Returns ::NO_ERROR if the auth token was set or none was required.
+ * ::OP_AUTH_NEEDED if it is a per op authorization, no
+ * authorization token exists for that operation and
+ * failOnTokenMissing is false.
+ * KM_ERROR_KEY_USER_NOT_AUTHENTICATED if there is no valid auth
+ * token for the operation
+ */
+int32_t KeyStoreService::getAuthToken(const keymaster_key_characteristics_t* characteristics,
+ keymaster_operation_handle_t handle,
+ keymaster_purpose_t purpose,
+ const hw_auth_token_t** authToken, bool failOnTokenMissing) {
+
+ std::vector<keymaster_key_param_t> allCharacteristics;
+ for (size_t i = 0; i < characteristics->sw_enforced.length; i++) {
+ allCharacteristics.push_back(characteristics->sw_enforced.params[i]);
+ }
+ for (size_t i = 0; i < characteristics->hw_enforced.length; i++) {
+ allCharacteristics.push_back(characteristics->hw_enforced.params[i]);
+ }
+ keymaster::AuthTokenTable::Error err = mAuthTokenTable.FindAuthorization(
+ allCharacteristics.data(), allCharacteristics.size(), purpose, handle, authToken);
+ switch (err) {
+ case keymaster::AuthTokenTable::OK:
+ case keymaster::AuthTokenTable::AUTH_NOT_REQUIRED:
+ return ::NO_ERROR;
+ case keymaster::AuthTokenTable::AUTH_TOKEN_NOT_FOUND:
+ case keymaster::AuthTokenTable::AUTH_TOKEN_EXPIRED:
+ case keymaster::AuthTokenTable::AUTH_TOKEN_WRONG_SID:
+ return KM_ERROR_KEY_USER_NOT_AUTHENTICATED;
+ case keymaster::AuthTokenTable::OP_HANDLE_REQUIRED:
+ return failOnTokenMissing ? (int32_t)KM_ERROR_KEY_USER_NOT_AUTHENTICATED
+ : (int32_t)::OP_AUTH_NEEDED;
+ default:
+ ALOGE("Unexpected FindAuthorization return value %d", err);
+ return KM_ERROR_INVALID_ARGUMENT;
+ }
+}
+
+inline void KeyStoreService::addAuthToParams(std::vector<keymaster_key_param_t>* params,
+ const hw_auth_token_t* token) {
+ if (token) {
+ params->push_back(keymaster_param_blob(
+ KM_TAG_AUTH_TOKEN, reinterpret_cast<const uint8_t*>(token), sizeof(hw_auth_token_t)));
+ }
+}
+
+/**
+ * Add the auth token for the operation to the param list if the operation
+ * requires authorization. Uses the cached result in the OperationMap if available
+ * otherwise gets the token from the AuthTokenTable and caches the result.
+ *
+ * Returns ::NO_ERROR if the auth token was added or not needed.
+ * KM_ERROR_KEY_USER_NOT_AUTHENTICATED if the operation is not
+ * authenticated.
+ * KM_ERROR_INVALID_OPERATION_HANDLE if token is not a valid
+ * operation token.
+ */
+int32_t KeyStoreService::addOperationAuthTokenIfNeeded(const sp<IBinder>& token,
+ std::vector<keymaster_key_param_t>* params) {
+ const hw_auth_token_t* authToken = NULL;
+ mOperationMap.getOperationAuthToken(token, &authToken);
+ if (!authToken) {
+ const keymaster2_device_t* dev;
+ keymaster_operation_handle_t handle;
+ const keymaster_key_characteristics_t* characteristics = NULL;
+ keymaster_purpose_t purpose;
+ keymaster::km_id_t keyid;
+ if (!mOperationMap.getOperation(token, &handle, &keyid, &purpose, &dev, &characteristics)) {
+ return KM_ERROR_INVALID_OPERATION_HANDLE;
+ }
+ int32_t result = getAuthToken(characteristics, handle, purpose, &authToken);
+ if (result != ::NO_ERROR) {
+ return result;
+ }
+ if (authToken) {
+ mOperationMap.setOperationAuthToken(token, authToken);
+ }
+ }
+ addAuthToParams(params, authToken);
+ return ::NO_ERROR;
+}
+
+/**
+ * Translate a result value to a legacy return value. All keystore errors are
+ * preserved and keymaster errors become SYSTEM_ERRORs
+ */
+inline int32_t KeyStoreService::translateResultToLegacyResult(int32_t result) {
+ if (result > 0) {
+ return result;
+ }
+ return ::SYSTEM_ERROR;
+}
+
+keymaster_key_param_t*
+KeyStoreService::getKeyAlgorithm(keymaster_key_characteristics_t* characteristics) {
+ for (size_t i = 0; i < characteristics->hw_enforced.length; i++) {
+ if (characteristics->hw_enforced.params[i].tag == KM_TAG_ALGORITHM) {
+ return &characteristics->hw_enforced.params[i];
+ }
+ }
+ for (size_t i = 0; i < characteristics->sw_enforced.length; i++) {
+ if (characteristics->sw_enforced.params[i].tag == KM_TAG_ALGORITHM) {
+ return &characteristics->sw_enforced.params[i];
+ }
+ }
+ return NULL;
+}
+
+void KeyStoreService::addLegacyBeginParams(const String16& name,
+ std::vector<keymaster_key_param_t>& params) {
+ // All legacy keys are DIGEST_NONE/PAD_NONE.
+ params.push_back(keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE));
+ params.push_back(keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE));
+
+ // Look up the algorithm of the key.
+ KeyCharacteristics characteristics;
+ int32_t rc = getKeyCharacteristics(name, NULL, NULL, &characteristics);
+ if (rc != ::NO_ERROR) {
+ ALOGE("Failed to get key characteristics");
+ return;
+ }
+ keymaster_key_param_t* algorithm = getKeyAlgorithm(&characteristics.characteristics);
+ if (!algorithm) {
+ ALOGE("getKeyCharacteristics did not include KM_TAG_ALGORITHM");
+ return;
+ }
+ params.push_back(*algorithm);
+}
+
+int32_t KeyStoreService::doLegacySignVerify(const String16& name, const uint8_t* data,
+ size_t length, uint8_t** out, size_t* outLength,
+ const uint8_t* signature, size_t signatureLength,
+ keymaster_purpose_t purpose) {
+
+ std::basic_stringstream<uint8_t> outBuffer;
+ OperationResult result;
+ KeymasterArguments inArgs;
+ addLegacyBeginParams(name, inArgs.params);
+ sp<IBinder> appToken(new BBinder);
+ sp<IBinder> token;
+
+ begin(appToken, name, purpose, true, inArgs, NULL, 0, &result);
+ if (result.resultCode != ResponseCode::NO_ERROR) {
+ if (result.resultCode == ::KEY_NOT_FOUND) {
+ ALOGW("Key not found");
+ } else {
+ ALOGW("Error in begin: %d", result.resultCode);
+ }
+ return translateResultToLegacyResult(result.resultCode);
+ }
+ inArgs.params.clear();
+ token = result.token;
+ size_t consumed = 0;
+ size_t lastConsumed = 0;
+ do {
+ update(token, inArgs, data + consumed, length - consumed, &result);
+ if (result.resultCode != ResponseCode::NO_ERROR) {
+ ALOGW("Error in update: %d", result.resultCode);
+ return translateResultToLegacyResult(result.resultCode);
+ }
+ if (out) {
+ outBuffer.write(result.data.get(), result.dataLength);
+ }
+ lastConsumed = result.inputConsumed;
+ consumed += lastConsumed;
+ } while (consumed < length && lastConsumed > 0);
+
+ if (consumed != length) {
+ ALOGW("Not all data consumed. Consumed %zu of %zu", consumed, length);
+ return ::SYSTEM_ERROR;
+ }
+
+ finish(token, inArgs, signature, signatureLength, NULL, 0, &result);
+ if (result.resultCode != ResponseCode::NO_ERROR) {
+ ALOGW("Error in finish: %d", result.resultCode);
+ return translateResultToLegacyResult(result.resultCode);
+ }
+ if (out) {
+ outBuffer.write(result.data.get(), result.dataLength);
+ }
+
+ if (out) {
+ auto buf = outBuffer.str();
+ *out = new uint8_t[buf.size()];
+ memcpy(*out, buf.c_str(), buf.size());
+ *outLength = buf.size();
+ }
+
+ return ::NO_ERROR;
+}
+
+} // namespace android
diff --git a/security/keystore/key_store_service.h b/security/keystore/key_store_service.h
new file mode 100644
index 0000000..3efae47
--- /dev/null
+++ b/security/keystore/key_store_service.h
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2016 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 KEYSTORE_KEYSTORE_SERVICE_H_
+#define KEYSTORE_KEYSTORE_SERVICE_H_
+
+#include <keystore/IKeystoreService.h>
+
+#include "auth_token_table.h"
+#include "keystore.h"
+#include "keystore_keymaster_enforcement.h"
+#include "operation.h"
+#include "permissions.h"
+
+namespace android {
+
+class KeyStoreService : public BnKeystoreService, public IBinder::DeathRecipient {
+ public:
+ explicit KeyStoreService(KeyStore* keyStore) : mKeyStore(keyStore), mOperationMap(this) {}
+
+ void binderDied(const wp<IBinder>& who);
+
+ int32_t getState(int32_t userId);
+
+ int32_t get(const String16& name, uint8_t** item, size_t* itemLength);
+ int32_t insert(const String16& name, const uint8_t* item, size_t itemLength, int targetUid,
+ int32_t flags);
+ int32_t del(const String16& name, int targetUid);
+ int32_t exist(const String16& name, int targetUid);
+ int32_t list(const String16& prefix, int targetUid, Vector<String16>* matches);
+
+ int32_t reset();
+
+ int32_t onUserPasswordChanged(int32_t userId, const String16& password);
+ int32_t onUserAdded(int32_t userId, int32_t parentId);
+ int32_t onUserRemoved(int32_t userId);
+
+ int32_t lock(int32_t userId);
+ int32_t unlock(int32_t userId, const String16& pw);
+
+ bool isEmpty(int32_t userId);
+
+ int32_t generate(const String16& name, int32_t targetUid, int32_t keyType, int32_t keySize,
+ int32_t flags, Vector<sp<KeystoreArg>>* args);
+ int32_t import(const String16& name, const uint8_t* data, size_t length, int targetUid,
+ int32_t flags);
+ int32_t sign(const String16& name, const uint8_t* data, size_t length, uint8_t** out,
+ size_t* outLength);
+ int32_t verify(const String16& name, const uint8_t* data, size_t dataLength,
+ const uint8_t* signature, size_t signatureLength);
+
+ /*
+ * TODO: The abstraction between things stored in hardware and regular blobs
+ * of data stored on the filesystem should be moved down to keystore itself.
+ * Unfortunately the Java code that calls this has naming conventions that it
+ * knows about. Ideally keystore shouldn't be used to store random blobs of
+ * data.
+ *
+ * Until that happens, it's necessary to have a separate "get_pubkey" and
+ * "del_key" since the Java code doesn't really communicate what it's
+ * intentions are.
+ */
+ int32_t get_pubkey(const String16& name, uint8_t** pubkey, size_t* pubkeyLength);
+
+ int32_t grant(const String16& name, int32_t granteeUid);
+ int32_t ungrant(const String16& name, int32_t granteeUid);
+
+ int64_t getmtime(const String16& name);
+
+ int32_t duplicate(const String16& srcKey, int32_t srcUid, const String16& destKey,
+ int32_t destUid);
+
+ int32_t is_hardware_backed(const String16& keyType);
+
+ int32_t clear_uid(int64_t targetUid64);
+
+ int32_t addRngEntropy(const uint8_t* data, size_t dataLength);
+ int32_t generateKey(const String16& name, const KeymasterArguments& params,
+ const uint8_t* entropy, size_t entropyLength, int uid, int flags,
+ KeyCharacteristics* outCharacteristics);
+ int32_t getKeyCharacteristics(const String16& name, const keymaster_blob_t* clientId,
+ const keymaster_blob_t* appData,
+ KeyCharacteristics* outCharacteristics);
+ int32_t importKey(const String16& name, const KeymasterArguments& params,
+ keymaster_key_format_t format, const uint8_t* keyData, size_t keyLength,
+ int uid, int flags, KeyCharacteristics* outCharacteristics);
+ void exportKey(const String16& name, keymaster_key_format_t format,
+ const keymaster_blob_t* clientId, const keymaster_blob_t* appData,
+ ExportResult* result);
+ void begin(const sp<IBinder>& appToken, const String16& name, keymaster_purpose_t purpose,
+ bool pruneable, const KeymasterArguments& params, const uint8_t* entropy,
+ size_t entropyLength, OperationResult* result);
+ void update(const sp<IBinder>& token, const KeymasterArguments& params, const uint8_t* data,
+ size_t dataLength, OperationResult* result);
+ void finish(const sp<IBinder>& token, const KeymasterArguments& params,
+ const uint8_t* signature, size_t signatureLength, const uint8_t* entropy,
+ size_t entropyLength, OperationResult* result);
+ int32_t abort(const sp<IBinder>& token);
+
+ bool isOperationAuthorized(const sp<IBinder>& token);
+
+ int32_t addAuthToken(const uint8_t* token, size_t length);
+
+ int32_t attestKey(const String16& name, const KeymasterArguments& params,
+ KeymasterCertificateChain* outChain) override;
+
+ private:
+ static const int32_t UID_SELF = -1;
+
+ /**
+ * Prune the oldest pruneable operation.
+ */
+ bool pruneOperation();
+
+ /**
+ * Get the effective target uid for a binder operation that takes an
+ * optional uid as the target.
+ */
+ uid_t getEffectiveUid(int32_t targetUid);
+
+ /**
+ * Check if the caller of the current binder method has the required
+ * permission and if acting on other uids the grants to do so.
+ */
+ bool checkBinderPermission(perm_t permission, int32_t targetUid = UID_SELF);
+
+ /**
+ * Check if the caller of the current binder method has the required
+ * permission and the target uid is the caller or the caller is system.
+ */
+ bool checkBinderPermissionSelfOrSystem(perm_t permission, int32_t targetUid);
+
+ /**
+ * Check if the caller of the current binder method has the required
+ * permission or the target of the operation is the caller's uid. This is
+ * for operation where the permission is only for cross-uid activity and all
+ * uids are allowed to act on their own (ie: clearing all entries for a
+ * given uid).
+ */
+ bool checkBinderPermissionOrSelfTarget(perm_t permission, int32_t targetUid);
+
+ /**
+ * Helper method to check that the caller has the required permission as
+ * well as the keystore is in the unlocked state if checkUnlocked is true.
+ *
+ * Returns NO_ERROR on success, PERMISSION_DENIED on a permission error and
+ * otherwise the state of keystore when not unlocked and checkUnlocked is
+ * true.
+ */
+ int32_t checkBinderPermissionAndKeystoreState(perm_t permission, int32_t targetUid = -1,
+ bool checkUnlocked = true);
+
+ bool isKeystoreUnlocked(State state);
+
+ bool isKeyTypeSupported(const keymaster2_device_t* device, keymaster_keypair_t keyType);
+
+ /**
+ * Check that all keymaster_key_param_t's provided by the application are
+ * allowed. Any parameter that keystore adds itself should be disallowed here.
+ */
+ bool checkAllowedOperationParams(const std::vector<keymaster_key_param_t>& params);
+
+ keymaster_error_t getOperationCharacteristics(const keymaster_key_blob_t& key,
+ const keymaster2_device_t* dev,
+ const std::vector<keymaster_key_param_t>& params,
+ keymaster_key_characteristics_t* out);
+
+ /**
+ * Get the auth token for this operation from the auth token table.
+ *
+ * Returns ::NO_ERROR if the auth token was set or none was required.
+ * ::OP_AUTH_NEEDED if it is a per op authorization, no
+ * authorization token exists for that operation and
+ * failOnTokenMissing is false.
+ * KM_ERROR_KEY_USER_NOT_AUTHENTICATED if there is no valid auth
+ * token for the operation
+ */
+ int32_t getAuthToken(const keymaster_key_characteristics_t* characteristics,
+ keymaster_operation_handle_t handle, keymaster_purpose_t purpose,
+ const hw_auth_token_t** authToken, bool failOnTokenMissing = true);
+
+ void addAuthToParams(std::vector<keymaster_key_param_t>* params, const hw_auth_token_t* token);
+
+ /**
+ * Add the auth token for the operation to the param list if the operation
+ * requires authorization. Uses the cached result in the OperationMap if available
+ * otherwise gets the token from the AuthTokenTable and caches the result.
+ *
+ * Returns ::NO_ERROR if the auth token was added or not needed.
+ * KM_ERROR_KEY_USER_NOT_AUTHENTICATED if the operation is not
+ * authenticated.
+ * KM_ERROR_INVALID_OPERATION_HANDLE if token is not a valid
+ * operation token.
+ */
+ int32_t addOperationAuthTokenIfNeeded(const sp<IBinder>& token,
+ std::vector<keymaster_key_param_t>* params);
+
+ /**
+ * Translate a result value to a legacy return value. All keystore errors are
+ * preserved and keymaster errors become SYSTEM_ERRORs
+ */
+ int32_t translateResultToLegacyResult(int32_t result);
+
+ keymaster_key_param_t* getKeyAlgorithm(keymaster_key_characteristics_t* characteristics);
+
+ void addLegacyBeginParams(const String16& name, std::vector<keymaster_key_param_t>& params);
+
+ int32_t doLegacySignVerify(const String16& name, const uint8_t* data, size_t length,
+ uint8_t** out, size_t* outLength, const uint8_t* signature,
+ size_t signatureLength, keymaster_purpose_t purpose);
+
+ ::KeyStore* mKeyStore;
+ OperationMap mOperationMap;
+ keymaster::AuthTokenTable mAuthTokenTable;
+ KeystoreKeymasterEnforcement enforcement_policy;
+};
+
+}; // namespace android
+
+#endif // KEYSTORE_KEYSTORE_SERVICE_H_
diff --git a/security/keystore/keyblob_utils.cpp b/security/keystore/keyblob_utils.cpp
new file mode 100644
index 0000000..3616822
--- /dev/null
+++ b/security/keystore/keyblob_utils.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2012 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 <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <keystore/keystore.h>
+
+/**
+ * When a key is being migrated from a software keymaster implementation
+ * to a hardware keymaster implementation, the first 4 bytes of the key_blob
+ * given to the hardware implementation will be equal to SOFT_KEY_MAGIC.
+ * The hardware implementation should import these PKCS#8 format keys which
+ * are encoded like this:
+ *
+ * 4-byte SOFT_KEY_MAGIC
+ *
+ * 4-byte 32-bit integer big endian for public_key_length. This may be zero
+ * length which indicates the public key should be derived from the
+ * private key.
+ *
+ * public_key_length bytes of public key (may be empty)
+ *
+ * 4-byte 32-bit integer big endian for private_key_length
+ *
+ * private_key_length bytes of private key
+ */
+static const uint8_t SOFT_KEY_MAGIC[] = { 'P', 'K', '#', '8' };
+
+size_t get_softkey_header_size() {
+ return sizeof(SOFT_KEY_MAGIC);
+}
+
+uint8_t* add_softkey_header(uint8_t* key_blob, size_t key_blob_length) {
+ if (key_blob_length < sizeof(SOFT_KEY_MAGIC)) {
+ return NULL;
+ }
+
+ memcpy(key_blob, SOFT_KEY_MAGIC, sizeof(SOFT_KEY_MAGIC));
+
+ return key_blob + sizeof(SOFT_KEY_MAGIC);
+}
+
+bool is_softkey(const uint8_t* key_blob, const size_t key_blob_length) {
+ if (key_blob_length < sizeof(SOFT_KEY_MAGIC)) {
+ return false;
+ }
+
+ return !memcmp(key_blob, SOFT_KEY_MAGIC, sizeof(SOFT_KEY_MAGIC));
+}
diff --git a/security/keystore/keystore.cpp b/security/keystore/keystore.cpp
new file mode 100644
index 0000000..3c87fd5
--- /dev/null
+++ b/security/keystore/keystore.cpp
@@ -0,0 +1,756 @@
+/*
+ * Copyright (C) 2016 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 "keystore.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+
+#include <openssl/bio.h>
+
+#include <utils/String16.h>
+
+#include <keystore/IKeystoreService.h>
+
+#include "keystore_utils.h"
+#include "permissions.h"
+
+const char* KeyStore::sOldMasterKey = ".masterkey";
+const char* KeyStore::sMetaDataFile = ".metadata";
+
+const android::String16 KeyStore::sRSAKeyType("RSA");
+const android::String16 KeyStore::sECKeyType("EC");
+
+KeyStore::KeyStore(Entropy* entropy, keymaster2_device_t* device, keymaster2_device_t* fallback)
+ : mEntropy(entropy), mDevice(device), mFallbackDevice(fallback) {
+ memset(&mMetaData, '\0', sizeof(mMetaData));
+}
+
+KeyStore::~KeyStore() {
+ for (android::Vector<grant_t*>::iterator it(mGrants.begin()); it != mGrants.end(); it++) {
+ delete *it;
+ }
+ mGrants.clear();
+
+ for (android::Vector<UserState*>::iterator it(mMasterKeys.begin()); it != mMasterKeys.end();
+ it++) {
+ delete *it;
+ }
+ mMasterKeys.clear();
+}
+
+ResponseCode KeyStore::initialize() {
+ readMetaData();
+ if (upgradeKeystore()) {
+ writeMetaData();
+ }
+
+ return ::NO_ERROR;
+}
+
+ResponseCode KeyStore::initializeUser(const android::String8& pw, uid_t userId) {
+ UserState* userState = getUserState(userId);
+ return userState->initialize(pw, mEntropy);
+}
+
+ResponseCode KeyStore::copyMasterKey(uid_t srcUser, uid_t dstUser) {
+ UserState* userState = getUserState(dstUser);
+ UserState* initState = getUserState(srcUser);
+ return userState->copyMasterKey(initState);
+}
+
+ResponseCode KeyStore::writeMasterKey(const android::String8& pw, uid_t userId) {
+ UserState* userState = getUserState(userId);
+ return userState->writeMasterKey(pw, mEntropy);
+}
+
+ResponseCode KeyStore::readMasterKey(const android::String8& pw, uid_t userId) {
+ UserState* userState = getUserState(userId);
+ return userState->readMasterKey(pw, mEntropy);
+}
+
+/* Here is the encoding of keys. This is necessary in order to allow arbitrary
+ * characters in keys. Characters in [0-~] are not encoded. Others are encoded
+ * into two bytes. The first byte is one of [+-.] which represents the first
+ * two bits of the character. The second byte encodes the rest of the bits into
+ * [0-o]. Therefore in the worst case the length of a key gets doubled. Note
+ * that Base64 cannot be used here due to the need of prefix match on keys. */
+
+static size_t encode_key_length(const android::String8& keyName) {
+ const uint8_t* in = reinterpret_cast<const uint8_t*>(keyName.string());
+ size_t length = keyName.length();
+ for (int i = length; i > 0; --i, ++in) {
+ if (*in < '0' || *in > '~') {
+ ++length;
+ }
+ }
+ return length;
+}
+
+static int encode_key(char* out, const android::String8& keyName) {
+ const uint8_t* in = reinterpret_cast<const uint8_t*>(keyName.string());
+ size_t length = keyName.length();
+ for (int i = length; i > 0; --i, ++in, ++out) {
+ if (*in < '0' || *in > '~') {
+ *out = '+' + (*in >> 6);
+ *++out = '0' + (*in & 0x3F);
+ ++length;
+ } else {
+ *out = *in;
+ }
+ }
+ *out = '\0';
+ return length;
+}
+
+android::String8 KeyStore::getKeyName(const android::String8& keyName) {
+ std::vector<char> encoded(encode_key_length(keyName) + 1); // add 1 for null char
+ encode_key(encoded.data(), keyName);
+ return android::String8(encoded.data());
+}
+
+android::String8 KeyStore::getKeyNameForUid(const android::String8& keyName, uid_t uid) {
+ std::vector<char> encoded(encode_key_length(keyName) + 1); // add 1 for null char
+ encode_key(encoded.data(), keyName);
+ return android::String8::format("%u_%s", uid, encoded.data());
+}
+
+android::String8 KeyStore::getKeyNameForUidWithDir(const android::String8& keyName, uid_t uid) {
+ std::vector<char> encoded(encode_key_length(keyName) + 1); // add 1 for null char
+ encode_key(encoded.data(), keyName);
+ return android::String8::format("%s/%u_%s", getUserStateByUid(uid)->getUserDirName(), uid,
+ encoded.data());
+}
+
+void KeyStore::resetUser(uid_t userId, bool keepUnenryptedEntries) {
+ android::String8 prefix("");
+ android::Vector<android::String16> aliases;
+ UserState* userState = getUserState(userId);
+ if (list(prefix, &aliases, userId) != ::NO_ERROR) {
+ return;
+ }
+ for (uint32_t i = 0; i < aliases.size(); i++) {
+ android::String8 filename(aliases[i]);
+ filename = android::String8::format("%s/%s", userState->getUserDirName(),
+ getKeyName(filename).string());
+ bool shouldDelete = true;
+ if (keepUnenryptedEntries) {
+ Blob blob;
+ ResponseCode rc = get(filename, &blob, ::TYPE_ANY, userId);
+
+ /* get can fail if the blob is encrypted and the state is
+ * not unlocked, only skip deleting blobs that were loaded and
+ * who are not encrypted. If there are blobs we fail to read for
+ * other reasons err on the safe side and delete them since we
+ * can't tell if they're encrypted.
+ */
+ shouldDelete = !(rc == ::NO_ERROR && !blob.isEncrypted());
+ }
+ if (shouldDelete) {
+ del(filename, ::TYPE_ANY, userId);
+ }
+ }
+ if (!userState->deleteMasterKey()) {
+ ALOGE("Failed to delete user %d's master key", userId);
+ }
+ if (!keepUnenryptedEntries) {
+ if (!userState->reset()) {
+ ALOGE("Failed to remove user %d's directory", userId);
+ }
+ }
+}
+
+bool KeyStore::isEmpty(uid_t userId) const {
+ const UserState* userState = getUserState(userId);
+ if (userState == NULL) {
+ return true;
+ }
+
+ DIR* dir = opendir(userState->getUserDirName());
+ if (!dir) {
+ return true;
+ }
+
+ bool result = true;
+ struct dirent* file;
+ while ((file = readdir(dir)) != NULL) {
+ // We only care about files.
+ if (file->d_type != DT_REG) {
+ continue;
+ }
+
+ // Skip anything that starts with a "."
+ if (file->d_name[0] == '.') {
+ continue;
+ }
+
+ result = false;
+ break;
+ }
+ closedir(dir);
+ return result;
+}
+
+void KeyStore::lock(uid_t userId) {
+ UserState* userState = getUserState(userId);
+ userState->zeroizeMasterKeysInMemory();
+ userState->setState(STATE_LOCKED);
+}
+
+ResponseCode KeyStore::get(const char* filename, Blob* keyBlob, const BlobType type, uid_t userId) {
+ UserState* userState = getUserState(userId);
+ ResponseCode rc =
+ keyBlob->readBlob(filename, userState->getDecryptionKey(), userState->getState());
+ if (rc != NO_ERROR) {
+ return rc;
+ }
+
+ const uint8_t version = keyBlob->getVersion();
+ if (version < CURRENT_BLOB_VERSION) {
+ /* If we upgrade the key, we need to write it to disk again. Then
+ * it must be read it again since the blob is encrypted each time
+ * it's written.
+ */
+ if (upgradeBlob(filename, keyBlob, version, type, userId)) {
+ if ((rc = this->put(filename, keyBlob, userId)) != NO_ERROR ||
+ (rc = keyBlob->readBlob(filename, userState->getDecryptionKey(),
+ userState->getState())) != NO_ERROR) {
+ return rc;
+ }
+ }
+ }
+
+ /*
+ * This will upgrade software-backed keys to hardware-backed keys when
+ * the HAL for the device supports the newer key types.
+ */
+ if (rc == NO_ERROR && type == TYPE_KEY_PAIR &&
+ mDevice->common.module->module_api_version >= KEYMASTER_MODULE_API_VERSION_0_2 &&
+ keyBlob->isFallback()) {
+ ResponseCode imported =
+ importKey(keyBlob->getValue(), keyBlob->getLength(), filename, userId,
+ keyBlob->isEncrypted() ? KEYSTORE_FLAG_ENCRYPTED : KEYSTORE_FLAG_NONE);
+
+ // The HAL allowed the import, reget the key to have the "fresh"
+ // version.
+ if (imported == NO_ERROR) {
+ rc = get(filename, keyBlob, TYPE_KEY_PAIR, userId);
+ }
+ }
+
+ // Keymaster 0.3 keys are valid keymaster 1.0 keys, so silently upgrade.
+ if (keyBlob->getType() == TYPE_KEY_PAIR) {
+ keyBlob->setType(TYPE_KEYMASTER_10);
+ rc = this->put(filename, keyBlob, userId);
+ }
+
+ if (type != TYPE_ANY && keyBlob->getType() != type) {
+ ALOGW("key found but type doesn't match: %d vs %d", keyBlob->getType(), type);
+ return KEY_NOT_FOUND;
+ }
+
+ return rc;
+}
+
+ResponseCode KeyStore::put(const char* filename, Blob* keyBlob, uid_t userId) {
+ UserState* userState = getUserState(userId);
+ return keyBlob->writeBlob(filename, userState->getEncryptionKey(), userState->getState(),
+ mEntropy);
+}
+
+ResponseCode KeyStore::del(const char* filename, const BlobType type, uid_t userId) {
+ Blob keyBlob;
+ ResponseCode rc = get(filename, &keyBlob, type, userId);
+ if (rc == ::VALUE_CORRUPTED) {
+ // The file is corrupt, the best we can do is rm it.
+ return (unlink(filename) && errno != ENOENT) ? ::SYSTEM_ERROR : ::NO_ERROR;
+ }
+ if (rc != ::NO_ERROR) {
+ return rc;
+ }
+
+ if (keyBlob.getType() == ::TYPE_KEY_PAIR) {
+ // A device doesn't have to implement delete_key.
+ if (mDevice->delete_key != NULL && !keyBlob.isFallback()) {
+ keymaster_key_blob_t blob = {keyBlob.getValue(),
+ static_cast<size_t>(keyBlob.getLength())};
+ if (mDevice->delete_key(mDevice, &blob)) {
+ rc = ::SYSTEM_ERROR;
+ }
+ }
+ }
+ if (keyBlob.getType() == ::TYPE_KEYMASTER_10) {
+ auto* dev = getDeviceForBlob(keyBlob);
+ if (dev->delete_key) {
+ keymaster_key_blob_t blob;
+ blob.key_material = keyBlob.getValue();
+ blob.key_material_size = keyBlob.getLength();
+ dev->delete_key(dev, &blob);
+ }
+ }
+ if (rc != ::NO_ERROR) {
+ return rc;
+ }
+
+ return (unlink(filename) && errno != ENOENT) ? ::SYSTEM_ERROR : ::NO_ERROR;
+}
+
+/*
+ * Converts from the "escaped" format on disk to actual name.
+ * This will be smaller than the input string.
+ *
+ * Characters that should combine with the next at the end will be truncated.
+ */
+static size_t decode_key_length(const char* in, size_t length) {
+ size_t outLength = 0;
+
+ for (const char* end = in + length; in < end; in++) {
+ /* This combines with the next character. */
+ if (*in < '0' || *in > '~') {
+ continue;
+ }
+
+ outLength++;
+ }
+ return outLength;
+}
+
+static void decode_key(char* out, const char* in, size_t length) {
+ for (const char* end = in + length; in < end; in++) {
+ if (*in < '0' || *in > '~') {
+ /* Truncate combining characters at the end. */
+ if (in + 1 >= end) {
+ break;
+ }
+
+ *out = (*in++ - '+') << 6;
+ *out++ |= (*in - '0') & 0x3F;
+ } else {
+ *out++ = *in;
+ }
+ }
+ *out = '\0';
+}
+
+ResponseCode KeyStore::list(const android::String8& prefix,
+ android::Vector<android::String16>* matches, uid_t userId) {
+
+ UserState* userState = getUserState(userId);
+ size_t n = prefix.length();
+
+ DIR* dir = opendir(userState->getUserDirName());
+ if (!dir) {
+ ALOGW("can't open directory for user: %s", strerror(errno));
+ return ::SYSTEM_ERROR;
+ }
+
+ struct dirent* file;
+ while ((file = readdir(dir)) != NULL) {
+ // We only care about files.
+ if (file->d_type != DT_REG) {
+ continue;
+ }
+
+ // Skip anything that starts with a "."
+ if (file->d_name[0] == '.') {
+ continue;
+ }
+
+ if (!strncmp(prefix.string(), file->d_name, n)) {
+ const char* p = &file->d_name[n];
+ size_t plen = strlen(p);
+
+ size_t extra = decode_key_length(p, plen);
+ char* match = (char*)malloc(extra + 1);
+ if (match != NULL) {
+ decode_key(match, p, plen);
+ matches->push(android::String16(match, extra));
+ free(match);
+ } else {
+ ALOGW("could not allocate match of size %zd", extra);
+ }
+ }
+ }
+ closedir(dir);
+ return ::NO_ERROR;
+}
+
+void KeyStore::addGrant(const char* filename, uid_t granteeUid) {
+ const grant_t* existing = getGrant(filename, granteeUid);
+ if (existing == NULL) {
+ grant_t* grant = new grant_t;
+ grant->uid = granteeUid;
+ grant->filename = reinterpret_cast<const uint8_t*>(strdup(filename));
+ mGrants.add(grant);
+ }
+}
+
+bool KeyStore::removeGrant(const char* filename, uid_t granteeUid) {
+ for (android::Vector<grant_t*>::iterator it(mGrants.begin()); it != mGrants.end(); it++) {
+ grant_t* grant = *it;
+ if (grant->uid == granteeUid &&
+ !strcmp(reinterpret_cast<const char*>(grant->filename), filename)) {
+ mGrants.erase(it);
+ return true;
+ }
+ }
+ return false;
+}
+
+ResponseCode KeyStore::importKey(const uint8_t* key, size_t keyLen, const char* filename,
+ uid_t userId, int32_t flags) {
+ Unique_PKCS8_PRIV_KEY_INFO pkcs8(d2i_PKCS8_PRIV_KEY_INFO(NULL, &key, keyLen));
+ if (!pkcs8.get()) {
+ return ::SYSTEM_ERROR;
+ }
+ Unique_EVP_PKEY pkey(EVP_PKCS82PKEY(pkcs8.get()));
+ if (!pkey.get()) {
+ return ::SYSTEM_ERROR;
+ }
+ int type = EVP_PKEY_type(pkey->type);
+ android::KeymasterArguments params;
+ add_legacy_key_authorizations(type, ¶ms.params);
+ switch (type) {
+ case EVP_PKEY_RSA:
+ params.params.push_back(keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_RSA));
+ break;
+ case EVP_PKEY_EC:
+ params.params.push_back(keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_EC));
+ break;
+ default:
+ ALOGW("Unsupported key type %d", type);
+ return ::SYSTEM_ERROR;
+ }
+
+ std::vector<keymaster_key_param_t> opParams(params.params);
+ const keymaster_key_param_set_t inParams = {opParams.data(), opParams.size()};
+ keymaster_blob_t input = {key, keyLen};
+ keymaster_key_blob_t blob = {nullptr, 0};
+ bool isFallback = false;
+ keymaster_error_t error = mDevice->import_key(mDevice, &inParams, KM_KEY_FORMAT_PKCS8, &input,
+ &blob, NULL /* characteristics */);
+ if (error != KM_ERROR_OK) {
+ ALOGE("Keymaster error %d importing key pair, falling back", error);
+
+ /*
+ * There should be no way to get here. Fallback shouldn't ever really happen
+ * because the main device may be many (SW, KM0/SW hybrid, KM1/SW hybrid), but it must
+ * provide full support of the API. In any case, we'll do the fallback just for
+ * consistency... and I suppose to cover for broken HW implementations.
+ */
+ error = mFallbackDevice->import_key(mFallbackDevice, &inParams, KM_KEY_FORMAT_PKCS8, &input,
+ &blob, NULL /* characteristics */);
+ isFallback = true;
+
+ if (error) {
+ ALOGE("Keymaster error while importing key pair with fallback: %d", error);
+ return SYSTEM_ERROR;
+ }
+ }
+
+ Blob keyBlob(blob.key_material, blob.key_material_size, NULL, 0, TYPE_KEYMASTER_10);
+ free(const_cast<uint8_t*>(blob.key_material));
+
+ keyBlob.setEncrypted(flags & KEYSTORE_FLAG_ENCRYPTED);
+ keyBlob.setFallback(isFallback);
+
+ return put(filename, &keyBlob, userId);
+}
+
+bool KeyStore::isHardwareBacked(const android::String16& keyType) const {
+ if (mDevice == NULL) {
+ ALOGW("can't get keymaster device");
+ return false;
+ }
+
+ if (sRSAKeyType == keyType) {
+ return (mDevice->flags & KEYMASTER_SOFTWARE_ONLY) == 0;
+ } else {
+ return (mDevice->flags & KEYMASTER_SOFTWARE_ONLY) == 0 &&
+ (mDevice->common.module->module_api_version >= KEYMASTER_MODULE_API_VERSION_0_2);
+ }
+}
+
+ResponseCode KeyStore::getKeyForName(Blob* keyBlob, const android::String8& keyName,
+ const uid_t uid, const BlobType type) {
+ android::String8 filepath8(getKeyNameForUidWithDir(keyName, uid));
+ uid_t userId = get_user_id(uid);
+
+ ResponseCode responseCode = get(filepath8.string(), keyBlob, type, userId);
+ if (responseCode == NO_ERROR) {
+ return responseCode;
+ }
+
+ // If this is one of the legacy UID->UID mappings, use it.
+ uid_t euid = get_keystore_euid(uid);
+ if (euid != uid) {
+ filepath8 = getKeyNameForUidWithDir(keyName, euid);
+ responseCode = get(filepath8.string(), keyBlob, type, userId);
+ if (responseCode == NO_ERROR) {
+ return responseCode;
+ }
+ }
+
+ // They might be using a granted key.
+ android::String8 filename8 = getKeyName(keyName);
+ char* end;
+ strtoul(filename8.string(), &end, 10);
+ if (end[0] != '_' || end[1] == 0) {
+ return KEY_NOT_FOUND;
+ }
+ filepath8 = android::String8::format("%s/%s", getUserState(userId)->getUserDirName(),
+ filename8.string());
+ if (!hasGrant(filepath8.string(), uid)) {
+ return responseCode;
+ }
+
+ // It is a granted key. Try to load it.
+ return get(filepath8.string(), keyBlob, type, userId);
+}
+
+UserState* KeyStore::getUserState(uid_t userId) {
+ for (android::Vector<UserState*>::iterator it(mMasterKeys.begin()); it != mMasterKeys.end();
+ it++) {
+ UserState* state = *it;
+ if (state->getUserId() == userId) {
+ return state;
+ }
+ }
+
+ UserState* userState = new UserState(userId);
+ if (!userState->initialize()) {
+ /* There's not much we can do if initialization fails. Trying to
+ * unlock the keystore for that user will fail as well, so any
+ * subsequent request for this user will just return SYSTEM_ERROR.
+ */
+ ALOGE("User initialization failed for %u; subsuquent operations will fail", userId);
+ }
+ mMasterKeys.add(userState);
+ return userState;
+}
+
+UserState* KeyStore::getUserStateByUid(uid_t uid) {
+ uid_t userId = get_user_id(uid);
+ return getUserState(userId);
+}
+
+const UserState* KeyStore::getUserState(uid_t userId) const {
+ for (android::Vector<UserState*>::const_iterator it(mMasterKeys.begin());
+ it != mMasterKeys.end(); it++) {
+ UserState* state = *it;
+ if (state->getUserId() == userId) {
+ return state;
+ }
+ }
+
+ return NULL;
+}
+
+const UserState* KeyStore::getUserStateByUid(uid_t uid) const {
+ uid_t userId = get_user_id(uid);
+ return getUserState(userId);
+}
+
+const grant_t* KeyStore::getGrant(const char* filename, uid_t uid) const {
+ for (android::Vector<grant_t*>::const_iterator it(mGrants.begin()); it != mGrants.end(); it++) {
+ grant_t* grant = *it;
+ if (grant->uid == uid &&
+ !strcmp(reinterpret_cast<const char*>(grant->filename), filename)) {
+ return grant;
+ }
+ }
+ return NULL;
+}
+
+bool KeyStore::upgradeBlob(const char* filename, Blob* blob, const uint8_t oldVersion,
+ const BlobType type, uid_t uid) {
+ bool updated = false;
+ uint8_t version = oldVersion;
+
+ /* From V0 -> V1: All old types were unknown */
+ if (version == 0) {
+ ALOGV("upgrading to version 1 and setting type %d", type);
+
+ blob->setType(type);
+ if (type == TYPE_KEY_PAIR) {
+ importBlobAsKey(blob, filename, uid);
+ }
+ version = 1;
+ updated = true;
+ }
+
+ /* From V1 -> V2: All old keys were encrypted */
+ if (version == 1) {
+ ALOGV("upgrading to version 2");
+
+ blob->setEncrypted(true);
+ version = 2;
+ updated = true;
+ }
+
+ /*
+ * If we've updated, set the key blob to the right version
+ * and write it.
+ */
+ if (updated) {
+ ALOGV("updated and writing file %s", filename);
+ blob->setVersion(version);
+ }
+
+ return updated;
+}
+
+struct BIO_Delete {
+ void operator()(BIO* p) const { BIO_free(p); }
+};
+typedef UniquePtr<BIO, BIO_Delete> Unique_BIO;
+
+ResponseCode KeyStore::importBlobAsKey(Blob* blob, const char* filename, uid_t uid) {
+ // We won't even write to the blob directly with this BIO, so const_cast is okay.
+ Unique_BIO b(BIO_new_mem_buf(const_cast<uint8_t*>(blob->getValue()), blob->getLength()));
+ if (b.get() == NULL) {
+ ALOGE("Problem instantiating BIO");
+ return SYSTEM_ERROR;
+ }
+
+ Unique_EVP_PKEY pkey(PEM_read_bio_PrivateKey(b.get(), NULL, NULL, NULL));
+ if (pkey.get() == NULL) {
+ ALOGE("Couldn't read old PEM file");
+ return SYSTEM_ERROR;
+ }
+
+ Unique_PKCS8_PRIV_KEY_INFO pkcs8(EVP_PKEY2PKCS8(pkey.get()));
+ int len = i2d_PKCS8_PRIV_KEY_INFO(pkcs8.get(), NULL);
+ if (len < 0) {
+ ALOGE("Couldn't measure PKCS#8 length");
+ return SYSTEM_ERROR;
+ }
+
+ UniquePtr<unsigned char[]> pkcs8key(new unsigned char[len]);
+ uint8_t* tmp = pkcs8key.get();
+ if (i2d_PKCS8_PRIV_KEY_INFO(pkcs8.get(), &tmp) != len) {
+ ALOGE("Couldn't convert to PKCS#8");
+ return SYSTEM_ERROR;
+ }
+
+ ResponseCode rc = importKey(pkcs8key.get(), len, filename, get_user_id(uid),
+ blob->isEncrypted() ? KEYSTORE_FLAG_ENCRYPTED : KEYSTORE_FLAG_NONE);
+ if (rc != NO_ERROR) {
+ return rc;
+ }
+
+ return get(filename, blob, TYPE_KEY_PAIR, uid);
+}
+
+void KeyStore::readMetaData() {
+ int in = TEMP_FAILURE_RETRY(open(sMetaDataFile, O_RDONLY));
+ if (in < 0) {
+ return;
+ }
+ size_t fileLength = readFully(in, (uint8_t*)&mMetaData, sizeof(mMetaData));
+ if (fileLength != sizeof(mMetaData)) {
+ ALOGI("Metadata file is %zd bytes (%zd experted); upgrade?", fileLength, sizeof(mMetaData));
+ }
+ close(in);
+}
+
+void KeyStore::writeMetaData() {
+ const char* tmpFileName = ".metadata.tmp";
+ int out =
+ TEMP_FAILURE_RETRY(open(tmpFileName, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR));
+ if (out < 0) {
+ ALOGE("couldn't write metadata file: %s", strerror(errno));
+ return;
+ }
+ size_t fileLength = writeFully(out, (uint8_t*)&mMetaData, sizeof(mMetaData));
+ if (fileLength != sizeof(mMetaData)) {
+ ALOGI("Could only write %zd bytes to metadata file (%zd expected)", fileLength,
+ sizeof(mMetaData));
+ }
+ close(out);
+ rename(tmpFileName, sMetaDataFile);
+}
+
+bool KeyStore::upgradeKeystore() {
+ bool upgraded = false;
+
+ if (mMetaData.version == 0) {
+ UserState* userState = getUserStateByUid(0);
+
+ // Initialize first so the directory is made.
+ userState->initialize();
+
+ // Migrate the old .masterkey file to user 0.
+ if (access(sOldMasterKey, R_OK) == 0) {
+ if (rename(sOldMasterKey, userState->getMasterKeyFileName()) < 0) {
+ ALOGE("couldn't migrate old masterkey: %s", strerror(errno));
+ return false;
+ }
+ }
+
+ // Initialize again in case we had a key.
+ userState->initialize();
+
+ // Try to migrate existing keys.
+ DIR* dir = opendir(".");
+ if (!dir) {
+ // Give up now; maybe we can upgrade later.
+ ALOGE("couldn't open keystore's directory; something is wrong");
+ return false;
+ }
+
+ struct dirent* file;
+ while ((file = readdir(dir)) != NULL) {
+ // We only care about files.
+ if (file->d_type != DT_REG) {
+ continue;
+ }
+
+ // Skip anything that starts with a "."
+ if (file->d_name[0] == '.') {
+ continue;
+ }
+
+ // Find the current file's user.
+ char* end;
+ unsigned long thisUid = strtoul(file->d_name, &end, 10);
+ if (end[0] != '_' || end[1] == 0) {
+ continue;
+ }
+ UserState* otherUser = getUserStateByUid(thisUid);
+ if (otherUser->getUserId() != 0) {
+ unlinkat(dirfd(dir), file->d_name, 0);
+ }
+
+ // Rename the file into user directory.
+ DIR* otherdir = opendir(otherUser->getUserDirName());
+ if (otherdir == NULL) {
+ ALOGW("couldn't open user directory for rename");
+ continue;
+ }
+ if (renameat(dirfd(dir), file->d_name, dirfd(otherdir), file->d_name) < 0) {
+ ALOGW("couldn't rename blob: %s: %s", file->d_name, strerror(errno));
+ }
+ closedir(otherdir);
+ }
+ closedir(dir);
+
+ mMetaData.version = 1;
+ upgraded = true;
+ }
+
+ return upgraded;
+}
diff --git a/security/keystore/keystore.h b/security/keystore/keystore.h
new file mode 100644
index 0000000..62d7294
--- /dev/null
+++ b/security/keystore/keystore.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2016 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 KEYSTORE_KEYSTORE_H_
+#define KEYSTORE_KEYSTORE_H_
+
+#include "user_state.h"
+
+#include <hardware/keymaster2.h>
+
+#include <utils/Vector.h>
+
+#include "blob.h"
+
+typedef struct {
+ uint32_t uid;
+ const uint8_t* filename;
+} grant_t;
+
+class KeyStore {
+ public:
+ KeyStore(Entropy* entropy, keymaster2_device_t* device, keymaster2_device_t* fallback);
+ ~KeyStore();
+
+ keymaster2_device_t* getDevice() const { return mDevice; }
+
+ keymaster2_device_t* getFallbackDevice() const { return mFallbackDevice; }
+
+ keymaster2_device_t* getDeviceForBlob(const Blob& blob) const {
+ return blob.isFallback() ? mFallbackDevice : mDevice;
+ }
+
+ ResponseCode initialize();
+
+ State getState(uid_t userId) { return getUserState(userId)->getState(); }
+
+ ResponseCode initializeUser(const android::String8& pw, uid_t userId);
+
+ ResponseCode copyMasterKey(uid_t srcUser, uid_t dstUser);
+ ResponseCode writeMasterKey(const android::String8& pw, uid_t userId);
+ ResponseCode readMasterKey(const android::String8& pw, uid_t userId);
+
+ android::String8 getKeyName(const android::String8& keyName);
+ android::String8 getKeyNameForUid(const android::String8& keyName, uid_t uid);
+ android::String8 getKeyNameForUidWithDir(const android::String8& keyName, uid_t uid);
+
+ /*
+ * Delete entries owned by userId. If keepUnencryptedEntries is true
+ * then only encrypted entries will be removed, otherwise all entries will
+ * be removed.
+ */
+ void resetUser(uid_t userId, bool keepUnenryptedEntries);
+ bool isEmpty(uid_t userId) const;
+
+ void lock(uid_t userId);
+
+ ResponseCode get(const char* filename, Blob* keyBlob, const BlobType type, uid_t userId);
+ ResponseCode put(const char* filename, Blob* keyBlob, uid_t userId);
+ ResponseCode del(const char* filename, const BlobType type, uid_t userId);
+ ResponseCode list(const android::String8& prefix, android::Vector<android::String16>* matches,
+ uid_t userId);
+
+ void addGrant(const char* filename, uid_t granteeUid);
+ bool removeGrant(const char* filename, uid_t granteeUid);
+ bool hasGrant(const char* filename, const uid_t uid) const {
+ return getGrant(filename, uid) != NULL;
+ }
+
+ ResponseCode importKey(const uint8_t* key, size_t keyLen, const char* filename, uid_t userId,
+ int32_t flags);
+
+ bool isHardwareBacked(const android::String16& keyType) const;
+
+ ResponseCode getKeyForName(Blob* keyBlob, const android::String8& keyName, const uid_t uid,
+ const BlobType type);
+
+ /**
+ * Returns any existing UserState or creates it if it doesn't exist.
+ */
+ UserState* getUserState(uid_t userId);
+
+ /**
+ * Returns any existing UserState or creates it if it doesn't exist.
+ */
+ UserState* getUserStateByUid(uid_t uid);
+
+ /**
+ * Returns NULL if the UserState doesn't already exist.
+ */
+ const UserState* getUserState(uid_t userId) const;
+
+ /**
+ * Returns NULL if the UserState doesn't already exist.
+ */
+ const UserState* getUserStateByUid(uid_t uid) const;
+
+ private:
+ static const char* sOldMasterKey;
+ static const char* sMetaDataFile;
+ static const android::String16 sRSAKeyType;
+ static const android::String16 sECKeyType;
+ Entropy* mEntropy;
+
+ keymaster2_device_t* mDevice;
+ keymaster2_device_t* mFallbackDevice;
+
+ android::Vector<UserState*> mMasterKeys;
+
+ android::Vector<grant_t*> mGrants;
+
+ typedef struct { uint32_t version; } keystore_metadata_t;
+
+ keystore_metadata_t mMetaData;
+
+ const grant_t* getGrant(const char* filename, uid_t uid) const;
+
+ /**
+ * Upgrade the key from the current version to whatever is newest.
+ */
+ bool upgradeBlob(const char* filename, Blob* blob, const uint8_t oldVersion,
+ const BlobType type, uid_t uid);
+
+ /**
+ * Takes a blob that is an PEM-encoded RSA key as a byte array and converts it to a DER-encoded
+ * PKCS#8 for import into a keymaster. Then it overwrites the original blob with the new blob
+ * format that is returned from the keymaster.
+ */
+ ResponseCode importBlobAsKey(Blob* blob, const char* filename, uid_t uid);
+
+ void readMetaData();
+ void writeMetaData();
+
+ bool upgradeKeystore();
+};
+
+#endif // KEYSTORE_KEYSTORE_H_
diff --git a/security/keystore/keystore.rc b/security/keystore/keystore.rc
new file mode 100644
index 0000000..a887594
--- /dev/null
+++ b/security/keystore/keystore.rc
@@ -0,0 +1,4 @@
+service keystore /system/bin/keystore /data/misc/keystore
+ class main
+ user keystore
+ group keystore drmrpc readproc
diff --git a/security/keystore/keystore_cli.cpp b/security/keystore/keystore_cli.cpp
new file mode 100644
index 0000000..bf6f4a0
--- /dev/null
+++ b/security/keystore/keystore_cli.cpp
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2009 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 <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <keystore/IKeystoreService.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+#include <keystore/keystore.h>
+
+using namespace android;
+
+static const char* responses[] = {
+ NULL,
+ /* [NO_ERROR] = */ "No error",
+ /* [LOCKED] = */ "Locked",
+ /* [UNINITIALIZED] = */ "Uninitialized",
+ /* [SYSTEM_ERROR] = */ "System error",
+ /* [PROTOCOL_ERROR] = */ "Protocol error",
+ /* [PERMISSION_DENIED] = */ "Permission denied",
+ /* [KEY_NOT_FOUND] = */ "Key not found",
+ /* [VALUE_CORRUPTED] = */ "Value corrupted",
+ /* [UNDEFINED_ACTION] = */ "Undefined action",
+ /* [WRONG_PASSWORD] = */ "Wrong password (last chance)",
+ /* [WRONG_PASSWORD + 1] = */ "Wrong password (2 tries left)",
+ /* [WRONG_PASSWORD + 2] = */ "Wrong password (3 tries left)",
+ /* [WRONG_PASSWORD + 3] = */ "Wrong password (4 tries left)",
+};
+
+#define NO_ARG_INT_RETURN(cmd) \
+ do { \
+ if (strcmp(argv[1], #cmd) == 0) { \
+ int32_t ret = service->cmd(); \
+ if (ret < 0) { \
+ fprintf(stderr, "%s: could not connect: %d\n", argv[0], ret); \
+ return 1; \
+ } else { \
+ printf(#cmd ": %s (%d)\n", responses[ret], ret); \
+ return 0; \
+ } \
+ } \
+ } while (0)
+
+#define SINGLE_ARG_INT_RETURN(cmd) \
+ do { \
+ if (strcmp(argv[1], #cmd) == 0) { \
+ if (argc < 3) { \
+ fprintf(stderr, "Usage: %s " #cmd " <name>\n", argv[0]); \
+ return 1; \
+ } \
+ int32_t ret = service->cmd(String16(argv[2])); \
+ if (ret < 0) { \
+ fprintf(stderr, "%s: could not connect: %d\n", argv[0], ret); \
+ return 1; \
+ } else { \
+ printf(#cmd ": %s (%d)\n", responses[ret], ret); \
+ return 0; \
+ } \
+ } \
+ } while (0)
+
+#define SINGLE_INT_ARG_INT_RETURN(cmd) \
+ do { \
+ if (strcmp(argv[1], #cmd) == 0) { \
+ if (argc < 3) { \
+ fprintf(stderr, "Usage: %s " #cmd " <name>\n", argv[0]); \
+ return 1; \
+ } \
+ int32_t ret = service->cmd(atoi(argv[2])); \
+ if (ret < 0) { \
+ fprintf(stderr, "%s: could not connect: %d\n", argv[0], ret); \
+ return 1; \
+ } else { \
+ printf(#cmd ": %s (%d)\n", responses[ret], ret); \
+ return 0; \
+ } \
+ } \
+ } while (0)
+
+#define SINGLE_ARG_PLUS_UID_INT_RETURN(cmd) \
+ do { \
+ if (strcmp(argv[1], #cmd) == 0) { \
+ if (argc < 3) { \
+ fprintf(stderr, "Usage: %s " #cmd " <name> <uid>\n", argv[0]); \
+ return 1; \
+ } \
+ int uid = -1; \
+ if (argc > 3) { \
+ uid = atoi(argv[3]); \
+ fprintf(stderr, "Working with uid %d\n", uid); \
+ } \
+ int32_t ret = service->cmd(String16(argv[2]), uid); \
+ if (ret < 0) { \
+ fprintf(stderr, "%s: could not connect: %d\n", argv[0], ret); \
+ return 1; \
+ } else { \
+ printf(#cmd ": %s (%d)\n", responses[ret], ret); \
+ return 0; \
+ } \
+ } \
+ } while (0)
+
+#define STING_ARG_DATA_STDIN_PLUS_UID_PLUS_FLAGS_INT_RETURN(cmd) \
+ do { \
+ if (strcmp(argv[1], #cmd) == 0) { \
+ if (argc < 3) { \
+ fprintf(stderr, "Usage: %s " #cmd " <name> [<uid>, <flags>]\n", argv[0]); \
+ return 1; \
+ } \
+ uint8_t* data; \
+ size_t dataSize; \
+ read_input(&data, &dataSize); \
+ int uid = -1; \
+ if (argc > 3) { \
+ uid = atoi(argv[3]); \
+ fprintf(stderr, "Working with uid %d\n", uid); \
+ } \
+ int32_t flags = 0; \
+ if (argc > 4) { \
+ flags = int32_t(atoi(argv[4])); \
+ fprintf(stderr, "Using flags %04x\n", flags); \
+ } \
+ int32_t ret = service->cmd(String16(argv[2]), data, dataSize, uid, flags); \
+ free(data); \
+ if (ret < 0) { \
+ fprintf(stderr, "%s: could not connect: %d\n", argv[0], ret); \
+ return 1; \
+ } else { \
+ printf(#cmd ": %s (%d)\n", responses[ret], ret); \
+ return 0; \
+ } \
+ } \
+ } while (0)
+
+#define SINGLE_ARG_DATA_RETURN(cmd) \
+ do { \
+ if (strcmp(argv[1], #cmd) == 0) { \
+ if (argc < 3) { \
+ fprintf(stderr, "Usage: %s " #cmd " <name>\n", argv[0]); \
+ return 1; \
+ } \
+ uint8_t* data; \
+ size_t dataSize; \
+ int32_t ret = service->cmd(String16(argv[2]), &data, &dataSize); \
+ if (ret < 0) { \
+ fprintf(stderr, "%s: could not connect: %d\n", argv[0], ret); \
+ return 1; \
+ } else if (ret) { \
+ fprintf(stderr, "%s: " #cmd ": %s (%d)\n", argv[0], responses[ret], ret); \
+ return 1; \
+ } else if (dataSize) { \
+ fwrite(data, dataSize, 1, stdout); \
+ fflush(stdout); \
+ free(data); \
+ return 0; \
+ } else { \
+ return 1; \
+ } \
+ } \
+ } while (0)
+
+static int list(const sp<IKeystoreService>& service, const String16& name, int uid) {
+ Vector<String16> matches;
+ int32_t ret = service->list(name, uid, &matches);
+ if (ret < 0) {
+ fprintf(stderr, "list: could not connect: %d\n", ret);
+ return 1;
+ } else if (ret != ::NO_ERROR) {
+ fprintf(stderr, "list: %s (%d)\n", responses[ret], ret);
+ return 1;
+ } else {
+ Vector<String16>::const_iterator it = matches.begin();
+ for (; it != matches.end(); ++it) {
+ printf("%s\n", String8(*it).string());
+ }
+ return 0;
+ }
+}
+
+#define BUF_SIZE 1024
+static void read_input(uint8_t** data, size_t* dataSize) {
+ char buffer[BUF_SIZE];
+ size_t contentSize = 0;
+ char *content = (char *) malloc(sizeof(char) * BUF_SIZE);
+
+ if (content == NULL) {
+ fprintf(stderr, "read_input: failed to allocate content");
+ exit(1);
+ }
+ content[0] = '\0';
+ while (fgets(buffer, BUF_SIZE, stdin)) {
+ char *old = content;
+ contentSize += strlen(buffer);
+ content = (char *) realloc(content, contentSize);
+ if (content == NULL) {
+ fprintf(stderr, "read_input: failed to reallocate content.");
+ free(old);
+ exit(1);
+ }
+ strcat(content, buffer);
+ }
+
+ if (ferror(stdin)) {
+ free(content);
+ fprintf(stderr, "read_input: error reading from stdin.");
+ exit(1);
+ }
+
+ *data = (uint8_t*) content;
+ *dataSize = contentSize;
+}
+
+int main(int argc, char* argv[])
+{
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s action [parameter ...]\n", argv[0]);
+ return 1;
+ }
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
+ sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
+
+ if (service == NULL) {
+ fprintf(stderr, "%s: error: could not connect to keystore service\n", argv[0]);
+ return 1;
+ }
+
+ /*
+ * All the commands should return a value
+ */
+
+ SINGLE_INT_ARG_INT_RETURN(getState);
+
+ SINGLE_ARG_DATA_RETURN(get);
+
+ STING_ARG_DATA_STDIN_PLUS_UID_PLUS_FLAGS_INT_RETURN(insert);
+
+ SINGLE_ARG_PLUS_UID_INT_RETURN(del);
+
+ SINGLE_ARG_PLUS_UID_INT_RETURN(exist);
+
+ if (strcmp(argv[1], "list") == 0) {
+ return list(service, argc < 3 ? String16("") : String16(argv[2]),
+ argc < 4 ? -1 : atoi(argv[3]));
+ }
+
+ NO_ARG_INT_RETURN(reset);
+
+ // TODO: notifyUserPasswordChanged
+
+ SINGLE_INT_ARG_INT_RETURN(lock);
+
+ // TODO: unlock
+
+ SINGLE_INT_ARG_INT_RETURN(isEmpty);
+
+ // TODO: generate
+
+ SINGLE_ARG_DATA_RETURN(get_pubkey);
+
+ SINGLE_ARG_PLUS_UID_INT_RETURN(grant);
+
+ // TODO: ungrant
+
+ // TODO: getmtime
+
+ fprintf(stderr, "%s: unknown command: %s\n", argv[0], argv[1]);
+ return 1;
+}
diff --git a/security/keystore/keystore_cli_v2.cpp b/security/keystore/keystore_cli_v2.cpp
new file mode 100644
index 0000000..6c229db
--- /dev/null
+++ b/security/keystore/keystore_cli_v2.cpp
@@ -0,0 +1,479 @@
+// Copyright 2015 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 <cstdio>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/strings/string_util.h"
+#include "keymaster/authorization_set.h"
+#include "keymaster/keymaster_tags.h"
+#include "keystore/keystore_client_impl.h"
+
+using base::CommandLine;
+using keymaster::AuthorizationSet;
+using keymaster::AuthorizationSetBuilder;
+using keystore::KeystoreClient;
+
+namespace {
+
+struct TestCase {
+ std::string name;
+ bool required_for_brillo_pts;
+ AuthorizationSet parameters;
+};
+
+void PrintUsageAndExit() {
+ printf("Usage: keystore_client_v2 <command> [options]\n");
+ printf("Commands: brillo-platform-test [--prefix=<test_name_prefix>] [--test_for_0_3]\n"
+ " list-brillo-tests\n"
+ " add-entropy --input=<entropy>\n"
+ " generate --name=<key_name>\n"
+ " get-chars --name=<key_name>\n"
+ " export --name=<key_name>\n"
+ " delete --name=<key_name>\n"
+ " delete-all\n"
+ " exists --name=<key_name>\n"
+ " list [--prefix=<key_name_prefix>]\n"
+ " sign-verify --name=<key_name>\n"
+ " [en|de]crypt --name=<key_name> --in=<file> --out=<file>\n");
+ exit(1);
+}
+
+std::unique_ptr<KeystoreClient> CreateKeystoreInstance() {
+ return std::unique_ptr<KeystoreClient>(new keystore::KeystoreClientImpl);
+}
+
+#ifndef KEYMASTER_NAME_TAGS
+#error KEYMASTER_NAME_TAGS must be defined
+#endif
+
+void PrintTags(const AuthorizationSet& parameters) {
+ const keymaster_key_param_t* iter = nullptr;
+ for (iter = parameters.begin(); iter != parameters.end(); ++iter) {
+ printf(" %s\n", keymaster::StringifyTag(iter->tag));
+ }
+}
+
+void PrintKeyCharacteristics(const AuthorizationSet& hardware_enforced_characteristics,
+ const AuthorizationSet& software_enforced_characteristics) {
+ printf("Hardware:\n");
+ PrintTags(hardware_enforced_characteristics);
+ printf("Software:\n");
+ PrintTags(software_enforced_characteristics);
+}
+
+bool TestKey(const std::string& name, bool required, const AuthorizationSet& parameters) {
+ std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
+ AuthorizationSet hardware_enforced_characteristics;
+ AuthorizationSet software_enforced_characteristics;
+ int32_t result = keystore->generateKey("tmp", parameters, &hardware_enforced_characteristics,
+ &software_enforced_characteristics);
+ const char kBoldRedAbort[] = "\033[1;31mABORT\033[0m";
+ if (result != KM_ERROR_OK) {
+ LOG(ERROR) << "Failed to generate key: " << result;
+ printf("[%s] %s\n", kBoldRedAbort, name.c_str());
+ return false;
+ }
+ result = keystore->deleteKey("tmp");
+ if (result != KM_ERROR_OK) {
+ LOG(ERROR) << "Failed to delete key: " << result;
+ printf("[%s] %s\n", kBoldRedAbort, name.c_str());
+ return false;
+ }
+ printf("===============================================================\n");
+ printf("%s Key Characteristics:\n", name.c_str());
+ PrintKeyCharacteristics(hardware_enforced_characteristics, software_enforced_characteristics);
+ bool hardware_backed = (hardware_enforced_characteristics.size() > 0);
+ if (software_enforced_characteristics.GetTagCount(KM_TAG_ALGORITHM) > 0 ||
+ software_enforced_characteristics.GetTagCount(KM_TAG_KEY_SIZE) > 0 ||
+ software_enforced_characteristics.GetTagCount(KM_TAG_RSA_PUBLIC_EXPONENT) > 0) {
+ VLOG(1) << "Hardware-backed key but required characteristics enforced in software.";
+ hardware_backed = false;
+ }
+ const char kBoldRedFail[] = "\033[1;31mFAIL\033[0m";
+ const char kBoldGreenPass[] = "\033[1;32mPASS\033[0m";
+ const char kBoldYellowWarn[] = "\033[1;33mWARN\033[0m";
+ printf("[%s] %s\n",
+ hardware_backed ? kBoldGreenPass : (required ? kBoldRedFail : kBoldYellowWarn),
+ name.c_str());
+
+ return (hardware_backed || !required);
+}
+
+AuthorizationSet GetRSASignParameters(uint32_t key_size, bool sha256_only) {
+ AuthorizationSetBuilder parameters;
+ parameters.RsaSigningKey(key_size, 65537)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Padding(KM_PAD_RSA_PKCS1_1_5_SIGN)
+ .Padding(KM_PAD_RSA_PSS)
+ .Authorization(keymaster::TAG_NO_AUTH_REQUIRED);
+ if (!sha256_only) {
+ parameters.Digest(KM_DIGEST_SHA_2_224)
+ .Digest(KM_DIGEST_SHA_2_384)
+ .Digest(KM_DIGEST_SHA_2_512);
+ }
+ return parameters.build();
+}
+
+AuthorizationSet GetRSAEncryptParameters(uint32_t key_size) {
+ AuthorizationSetBuilder parameters;
+ parameters.RsaEncryptionKey(key_size, 65537)
+ .Padding(KM_PAD_RSA_PKCS1_1_5_ENCRYPT)
+ .Padding(KM_PAD_RSA_OAEP)
+ .Authorization(keymaster::TAG_NO_AUTH_REQUIRED);
+ return parameters.build();
+}
+
+AuthorizationSet GetECDSAParameters(uint32_t key_size, bool sha256_only) {
+ AuthorizationSetBuilder parameters;
+ parameters.EcdsaSigningKey(key_size)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Authorization(keymaster::TAG_NO_AUTH_REQUIRED);
+ if (!sha256_only) {
+ parameters.Digest(KM_DIGEST_SHA_2_224)
+ .Digest(KM_DIGEST_SHA_2_384)
+ .Digest(KM_DIGEST_SHA_2_512);
+ }
+ return parameters.build();
+}
+
+AuthorizationSet GetAESParameters(uint32_t key_size, bool with_gcm_mode) {
+ AuthorizationSetBuilder parameters;
+ parameters.AesEncryptionKey(key_size).Authorization(keymaster::TAG_NO_AUTH_REQUIRED);
+ if (with_gcm_mode) {
+ parameters.Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(keymaster::TAG_MIN_MAC_LENGTH, 128);
+ } else {
+ parameters.Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_ECB);
+ parameters.Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_CBC);
+ parameters.Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_CTR);
+ parameters.Padding(KM_PAD_NONE);
+ }
+ return parameters.build();
+}
+
+AuthorizationSet GetHMACParameters(uint32_t key_size, keymaster_digest_t digest) {
+ AuthorizationSetBuilder parameters;
+ parameters.HmacKey(key_size)
+ .Digest(digest)
+ .Authorization(keymaster::TAG_MIN_MAC_LENGTH, 224)
+ .Authorization(keymaster::TAG_NO_AUTH_REQUIRED);
+ return parameters.build();
+}
+
+std::vector<TestCase> GetTestCases() {
+ TestCase test_cases[] = {
+ {"RSA-2048 Sign", true, GetRSASignParameters(2048, true)},
+ {"RSA-2048 Sign (more digests)", false, GetRSASignParameters(2048, false)},
+ {"RSA-3072 Sign", false, GetRSASignParameters(3072, false)},
+ {"RSA-4096 Sign", false, GetRSASignParameters(4096, false)},
+ {"RSA-2048 Encrypt", true, GetRSAEncryptParameters(2048)},
+ {"RSA-3072 Encrypt", false, GetRSAEncryptParameters(3072)},
+ {"RSA-4096 Encrypt", false, GetRSAEncryptParameters(4096)},
+ {"ECDSA-P256 Sign", true, GetECDSAParameters(256, true)},
+ {"ECDSA-P256 Sign (more digests)", false, GetECDSAParameters(256, false)},
+ {"ECDSA-P224 Sign", false, GetECDSAParameters(224, false)},
+ {"ECDSA-P384 Sign", false, GetECDSAParameters(384, false)},
+ {"ECDSA-P521 Sign", false, GetECDSAParameters(521, false)},
+ {"AES-128", true, GetAESParameters(128, false)},
+ {"AES-256", true, GetAESParameters(256, false)},
+ {"AES-128-GCM", false, GetAESParameters(128, true)},
+ {"AES-256-GCM", false, GetAESParameters(256, true)},
+ {"HMAC-SHA256-16", true, GetHMACParameters(16, KM_DIGEST_SHA_2_256)},
+ {"HMAC-SHA256-32", true, GetHMACParameters(32, KM_DIGEST_SHA_2_256)},
+ {"HMAC-SHA256-64", false, GetHMACParameters(64, KM_DIGEST_SHA_2_256)},
+ {"HMAC-SHA224-32", false, GetHMACParameters(32, KM_DIGEST_SHA_2_224)},
+ {"HMAC-SHA384-32", false, GetHMACParameters(32, KM_DIGEST_SHA_2_384)},
+ {"HMAC-SHA512-32", false, GetHMACParameters(32, KM_DIGEST_SHA_2_512)},
+ };
+ return std::vector<TestCase>(&test_cases[0], &test_cases[arraysize(test_cases)]);
+}
+
+int BrilloPlatformTest(const std::string& prefix, bool test_for_0_3) {
+ const char kBoldYellowWarning[] = "\033[1;33mWARNING\033[0m";
+ if (test_for_0_3) {
+ printf("%s: Testing for keymaster v0.3. "
+ "This does not meet Brillo requirements.\n", kBoldYellowWarning);
+ }
+ int test_count = 0;
+ int fail_count = 0;
+ std::vector<TestCase> test_cases = GetTestCases();
+ for (const auto& test_case : test_cases) {
+ if (!prefix.empty() &&
+ !base::StartsWith(test_case.name, prefix, base::CompareCase::SENSITIVE)) {
+ continue;
+ }
+ if (test_for_0_3 &&
+ (base::StartsWith(test_case.name, "AES", base::CompareCase::SENSITIVE) ||
+ base::StartsWith(test_case.name, "HMAC", base::CompareCase::SENSITIVE))) {
+ continue;
+ }
+ ++test_count;
+ if (!TestKey(test_case.name, test_case.required_for_brillo_pts, test_case.parameters)) {
+ VLOG(1) << "Test failed: " << test_case.name;
+ ++fail_count;
+ }
+ }
+ return fail_count;
+}
+
+int ListTestCases() {
+ const char kBoldGreenRequired[] = "\033[1;32mREQUIRED\033[0m";
+ const char kBoldYellowRecommended[] = "\033[1;33mRECOMMENDED\033[0m";
+ std::vector<TestCase> test_cases = GetTestCases();
+ for (const auto& test_case : test_cases) {
+ printf("%s : %s\n", test_case.name.c_str(),
+ test_case.required_for_brillo_pts ? kBoldGreenRequired : kBoldYellowRecommended);
+ }
+ return 0;
+}
+
+std::string ReadFile(const std::string& filename) {
+ std::string content;
+ base::FilePath path(filename);
+ if (!base::ReadFileToString(path, &content)) {
+ printf("Failed to read file: %s\n", filename.c_str());
+ exit(1);
+ }
+ return content;
+}
+
+void WriteFile(const std::string& filename, const std::string& content) {
+ base::FilePath path(filename);
+ int size = content.size();
+ if (base::WriteFile(path, content.data(), size) != size) {
+ printf("Failed to write file: %s\n", filename.c_str());
+ exit(1);
+ }
+}
+
+int AddEntropy(const std::string& input) {
+ std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
+ int32_t result = keystore->addRandomNumberGeneratorEntropy(input);
+ printf("AddEntropy: %d\n", result);
+ return result;
+}
+
+int GenerateKey(const std::string& name) {
+ std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
+ AuthorizationSetBuilder params;
+ params.RsaSigningKey(2048, 65537)
+ .Digest(KM_DIGEST_SHA_2_224)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Digest(KM_DIGEST_SHA_2_384)
+ .Digest(KM_DIGEST_SHA_2_512)
+ .Padding(KM_PAD_RSA_PKCS1_1_5_SIGN)
+ .Padding(KM_PAD_RSA_PSS)
+ .Authorization(keymaster::TAG_NO_AUTH_REQUIRED);
+ AuthorizationSet hardware_enforced_characteristics;
+ AuthorizationSet software_enforced_characteristics;
+ int32_t result = keystore->generateKey(name, params.build(), &hardware_enforced_characteristics,
+ &software_enforced_characteristics);
+ printf("GenerateKey: %d\n", result);
+ if (result == KM_ERROR_OK) {
+ PrintKeyCharacteristics(hardware_enforced_characteristics,
+ software_enforced_characteristics);
+ }
+ return result;
+}
+
+int GetCharacteristics(const std::string& name) {
+ std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
+ AuthorizationSet hardware_enforced_characteristics;
+ AuthorizationSet software_enforced_characteristics;
+ int32_t result = keystore->getKeyCharacteristics(name, &hardware_enforced_characteristics,
+ &software_enforced_characteristics);
+ printf("GetCharacteristics: %d\n", result);
+ if (result == KM_ERROR_OK) {
+ PrintKeyCharacteristics(hardware_enforced_characteristics,
+ software_enforced_characteristics);
+ }
+ return result;
+}
+
+int ExportKey(const std::string& name) {
+ std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
+ std::string data;
+ int32_t result = keystore->exportKey(KM_KEY_FORMAT_X509, name, &data);
+ printf("ExportKey: %d (%zu)\n", result, data.size());
+ return result;
+}
+
+int DeleteKey(const std::string& name) {
+ std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
+ int32_t result = keystore->deleteKey(name);
+ printf("DeleteKey: %d\n", result);
+ return result;
+}
+
+int DeleteAllKeys() {
+ std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
+ int32_t result = keystore->deleteAllKeys();
+ printf("DeleteAllKeys: %d\n", result);
+ return result;
+}
+
+int DoesKeyExist(const std::string& name) {
+ std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
+ printf("DoesKeyExist: %s\n", keystore->doesKeyExist(name) ? "yes" : "no");
+ return 0;
+}
+
+int List(const std::string& prefix) {
+ std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
+ std::vector<std::string> key_list;
+ if (!keystore->listKeys(prefix, &key_list)) {
+ printf("ListKeys failed.\n");
+ return 1;
+ }
+ printf("Keys:\n");
+ for (const auto& key_name : key_list) {
+ printf(" %s\n", key_name.c_str());
+ }
+ return 0;
+}
+
+int SignAndVerify(const std::string& name) {
+ std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
+ AuthorizationSetBuilder sign_params;
+ sign_params.Padding(KM_PAD_RSA_PKCS1_1_5_SIGN);
+ sign_params.Digest(KM_DIGEST_SHA_2_256);
+ AuthorizationSet output_params;
+ keymaster_operation_handle_t handle;
+ int32_t result = keystore->beginOperation(KM_PURPOSE_SIGN, name, sign_params.build(),
+ &output_params, &handle);
+ if (result != KM_ERROR_OK) {
+ printf("Sign: BeginOperation failed: %d\n", result);
+ return result;
+ }
+ AuthorizationSet empty_params;
+ size_t num_input_bytes_consumed;
+ std::string output_data;
+ result = keystore->updateOperation(handle, empty_params, "data_to_sign",
+ &num_input_bytes_consumed, &output_params, &output_data);
+ if (result != KM_ERROR_OK) {
+ printf("Sign: UpdateOperation failed: %d\n", result);
+ return result;
+ }
+ result = keystore->finishOperation(handle, empty_params, std::string() /*signature_to_verify*/,
+ &output_params, &output_data);
+ if (result != KM_ERROR_OK) {
+ printf("Sign: FinishOperation failed: %d\n", result);
+ return result;
+ }
+ printf("Sign: %zu bytes.\n", output_data.size());
+ // We have a signature, now verify it.
+ std::string signature_to_verify = output_data;
+ output_data.clear();
+ result = keystore->beginOperation(KM_PURPOSE_VERIFY, name, sign_params.build(), &output_params,
+ &handle);
+ if (result != KM_ERROR_OK) {
+ printf("Verify: BeginOperation failed: %d\n", result);
+ return result;
+ }
+ result = keystore->updateOperation(handle, empty_params, "data_to_sign",
+ &num_input_bytes_consumed, &output_params, &output_data);
+ if (result != KM_ERROR_OK) {
+ printf("Verify: UpdateOperation failed: %d\n", result);
+ return result;
+ }
+ result = keystore->finishOperation(handle, empty_params, signature_to_verify, &output_params,
+ &output_data);
+ if (result == KM_ERROR_VERIFICATION_FAILED) {
+ printf("Verify: Failed to verify signature.\n");
+ return result;
+ }
+ if (result != KM_ERROR_OK) {
+ printf("Verify: FinishOperation failed: %d\n", result);
+ return result;
+ }
+ printf("Verify: OK\n");
+ return 0;
+}
+
+int Encrypt(const std::string& key_name, const std::string& input_filename,
+ const std::string& output_filename) {
+ std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
+ std::string input = ReadFile(input_filename);
+ std::string output;
+ if (!keystore->encryptWithAuthentication(key_name, input, &output)) {
+ printf("EncryptWithAuthentication failed.\n");
+ return 1;
+ }
+ WriteFile(output_filename, output);
+ return 0;
+}
+
+int Decrypt(const std::string& key_name, const std::string& input_filename,
+ const std::string& output_filename) {
+ std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
+ std::string input = ReadFile(input_filename);
+ std::string output;
+ if (!keystore->decryptWithAuthentication(key_name, input, &output)) {
+ printf("DecryptWithAuthentication failed.\n");
+ return 1;
+ }
+ WriteFile(output_filename, output);
+ return 0;
+}
+
+} // namespace
+
+int main(int argc, char** argv) {
+ CommandLine::Init(argc, argv);
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ CommandLine::StringVector args = command_line->GetArgs();
+ if (args.empty()) {
+ PrintUsageAndExit();
+ }
+ if (args[0] == "brillo-platform-test") {
+ return BrilloPlatformTest(command_line->GetSwitchValueASCII("prefix"),
+ command_line->HasSwitch("test_for_0_3"));
+ } else if (args[0] == "list-brillo-tests") {
+ return ListTestCases();
+ } else if (args[0] == "add-entropy") {
+ return AddEntropy(command_line->GetSwitchValueASCII("input"));
+ } else if (args[0] == "generate") {
+ return GenerateKey(command_line->GetSwitchValueASCII("name"));
+ } else if (args[0] == "get-chars") {
+ return GetCharacteristics(command_line->GetSwitchValueASCII("name"));
+ } else if (args[0] == "export") {
+ return ExportKey(command_line->GetSwitchValueASCII("name"));
+ } else if (args[0] == "delete") {
+ return DeleteKey(command_line->GetSwitchValueASCII("name"));
+ } else if (args[0] == "delete-all") {
+ return DeleteAllKeys();
+ } else if (args[0] == "exists") {
+ return DoesKeyExist(command_line->GetSwitchValueASCII("name"));
+ } else if (args[0] == "list") {
+ return List(command_line->GetSwitchValueASCII("prefix"));
+ } else if (args[0] == "sign-verify") {
+ return SignAndVerify(command_line->GetSwitchValueASCII("name"));
+ } else if (args[0] == "encrypt") {
+ return Encrypt(command_line->GetSwitchValueASCII("name"),
+ command_line->GetSwitchValueASCII("in"),
+ command_line->GetSwitchValueASCII("out"));
+ } else if (args[0] == "decrypt") {
+ return Decrypt(command_line->GetSwitchValueASCII("name"),
+ command_line->GetSwitchValueASCII("in"),
+ command_line->GetSwitchValueASCII("out"));
+ } else {
+ PrintUsageAndExit();
+ }
+ return 0;
+}
diff --git a/security/keystore/keystore_client.proto b/security/keystore/keystore_client.proto
new file mode 100644
index 0000000..cd520dc
--- /dev/null
+++ b/security/keystore/keystore_client.proto
@@ -0,0 +1,26 @@
+// Copyright 2015 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.
+
+package keystore;
+
+option optimize_for = LITE_RUNTIME;
+
+// Holds encrypted, authenticated data.
+message EncryptedData {
+ // The initialization vector used during encryption.
+ optional bytes init_vector = 1;
+ // MAC of (init_vector + encrypted_data).
+ optional bytes authentication_data = 2;
+ optional bytes encrypted_data = 3;
+}
diff --git a/security/keystore/keystore_client_impl.cpp b/security/keystore/keystore_client_impl.cpp
new file mode 100644
index 0000000..a46dfc7
--- /dev/null
+++ b/security/keystore/keystore_client_impl.cpp
@@ -0,0 +1,556 @@
+// Copyright 2015 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.
+
+#define LOG_TAG "keystore_client"
+
+#include "keystore/keystore_client_impl.h"
+
+#include <string>
+#include <vector>
+
+#include "binder/IBinder.h"
+#include "binder/IInterface.h"
+#include "binder/IServiceManager.h"
+#include "keystore/IKeystoreService.h"
+#include "keystore/keystore.h"
+#include "log/log.h"
+#include "utils/String16.h"
+#include "utils/String8.h"
+
+#include "keystore_client.pb.h"
+
+using android::ExportResult;
+using android::KeyCharacteristics;
+using android::KeymasterArguments;
+using android::OperationResult;
+using android::String16;
+using keymaster::AuthorizationSet;
+using keymaster::AuthorizationSetBuilder;
+
+namespace {
+
+// Use the UID of the current process.
+const int kDefaultUID = -1;
+const char kEncryptSuffix[] = "_ENC";
+const char kAuthenticateSuffix[] = "_AUTH";
+const uint32_t kAESKeySize = 256; // bits
+const uint32_t kHMACKeySize = 256; // bits
+const uint32_t kHMACOutputSize = 256; // bits
+
+const uint8_t* StringAsByteArray(const std::string& s) {
+ return reinterpret_cast<const uint8_t*>(s.data());
+}
+
+std::string ByteArrayAsString(const uint8_t* data, size_t data_size) {
+ return std::string(reinterpret_cast<const char*>(data), data_size);
+}
+
+void CopyParameters(const AuthorizationSet& in, std::vector<keymaster_key_param_t>* out) {
+ keymaster_key_param_set_t tmp;
+ in.CopyToParamSet(&tmp);
+ out->assign(&tmp.params[0], &tmp.params[tmp.length]);
+ free(tmp.params);
+}
+
+} // namespace
+
+namespace keystore {
+
+KeystoreClientImpl::KeystoreClientImpl() {
+ service_manager_ = android::defaultServiceManager();
+ keystore_binder_ = service_manager_->getService(String16("android.security.keystore"));
+ keystore_ = android::interface_cast<android::IKeystoreService>(keystore_binder_);
+}
+
+bool KeystoreClientImpl::encryptWithAuthentication(const std::string& key_name,
+ const std::string& data,
+ std::string* encrypted_data) {
+ // The encryption algorithm is AES-256-CBC with PKCS #7 padding and a random
+ // IV. The authentication algorithm is HMAC-SHA256 and is computed over the
+ // cipher-text (i.e. Encrypt-then-MAC approach). This was chosen over AES-GCM
+ // because hardware support for GCM is not mandatory for all Brillo devices.
+ std::string encryption_key_name = key_name + kEncryptSuffix;
+ if (!createOrVerifyEncryptionKey(encryption_key_name)) {
+ return false;
+ }
+ std::string authentication_key_name = key_name + kAuthenticateSuffix;
+ if (!createOrVerifyAuthenticationKey(authentication_key_name)) {
+ return false;
+ }
+ AuthorizationSetBuilder encrypt_params;
+ encrypt_params.Padding(KM_PAD_PKCS7);
+ encrypt_params.Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_CBC);
+ AuthorizationSet output_params;
+ std::string raw_encrypted_data;
+ if (!oneShotOperation(KM_PURPOSE_ENCRYPT, encryption_key_name, encrypt_params.build(), data,
+ std::string(), /* signature_to_verify */
+ &output_params, &raw_encrypted_data)) {
+ ALOGE("Encrypt: AES operation failed.");
+ return false;
+ }
+ keymaster_blob_t init_vector_blob;
+ if (!output_params.GetTagValue(keymaster::TAG_NONCE, &init_vector_blob)) {
+ ALOGE("Encrypt: Missing initialization vector.");
+ return false;
+ }
+ std::string init_vector =
+ ByteArrayAsString(init_vector_blob.data, init_vector_blob.data_length);
+
+ AuthorizationSetBuilder authenticate_params;
+ authenticate_params.Digest(KM_DIGEST_SHA_2_256);
+ authenticate_params.Authorization(keymaster::TAG_MAC_LENGTH, kHMACOutputSize);
+ std::string raw_authentication_data;
+ if (!oneShotOperation(KM_PURPOSE_SIGN, authentication_key_name, authenticate_params.build(),
+ init_vector + raw_encrypted_data, std::string(), /* signature_to_verify */
+ &output_params, &raw_authentication_data)) {
+ ALOGE("Encrypt: HMAC operation failed.");
+ return false;
+ }
+ EncryptedData protobuf;
+ protobuf.set_init_vector(init_vector);
+ protobuf.set_authentication_data(raw_authentication_data);
+ protobuf.set_encrypted_data(raw_encrypted_data);
+ if (!protobuf.SerializeToString(encrypted_data)) {
+ ALOGE("Encrypt: Failed to serialize EncryptedData protobuf.");
+ return false;
+ }
+ return true;
+}
+
+bool KeystoreClientImpl::decryptWithAuthentication(const std::string& key_name,
+ const std::string& encrypted_data,
+ std::string* data) {
+ EncryptedData protobuf;
+ if (!protobuf.ParseFromString(encrypted_data)) {
+ ALOGE("Decrypt: Failed to parse EncryptedData protobuf.");
+ }
+ // Verify authentication before attempting decryption.
+ std::string authentication_key_name = key_name + kAuthenticateSuffix;
+ AuthorizationSetBuilder authenticate_params;
+ authenticate_params.Digest(KM_DIGEST_SHA_2_256);
+ AuthorizationSet output_params;
+ std::string output_data;
+ if (!oneShotOperation(KM_PURPOSE_VERIFY, authentication_key_name, authenticate_params.build(),
+ protobuf.init_vector() + protobuf.encrypted_data(),
+ protobuf.authentication_data(), &output_params, &output_data)) {
+ ALOGE("Decrypt: HMAC operation failed.");
+ return false;
+ }
+ std::string encryption_key_name = key_name + kEncryptSuffix;
+ AuthorizationSetBuilder encrypt_params;
+ encrypt_params.Padding(KM_PAD_PKCS7);
+ encrypt_params.Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_CBC);
+ encrypt_params.Authorization(keymaster::TAG_NONCE, protobuf.init_vector().data(),
+ protobuf.init_vector().size());
+ if (!oneShotOperation(KM_PURPOSE_DECRYPT, encryption_key_name, encrypt_params.build(),
+ protobuf.encrypted_data(), std::string(), /* signature_to_verify */
+ &output_params, data)) {
+ ALOGE("Decrypt: AES operation failed.");
+ return false;
+ }
+ return true;
+}
+
+bool KeystoreClientImpl::oneShotOperation(keymaster_purpose_t purpose, const std::string& key_name,
+ const keymaster::AuthorizationSet& input_parameters,
+ const std::string& input_data,
+ const std::string& signature_to_verify,
+ keymaster::AuthorizationSet* output_parameters,
+ std::string* output_data) {
+ keymaster_operation_handle_t handle;
+ int32_t result =
+ beginOperation(purpose, key_name, input_parameters, output_parameters, &handle);
+ if (result != KM_ERROR_OK) {
+ ALOGE("BeginOperation failed: %d", result);
+ return false;
+ }
+ AuthorizationSet empty_params;
+ size_t num_input_bytes_consumed;
+ AuthorizationSet ignored_params;
+ result = updateOperation(handle, empty_params, input_data, &num_input_bytes_consumed,
+ &ignored_params, output_data);
+ if (result != KM_ERROR_OK) {
+ ALOGE("UpdateOperation failed: %d", result);
+ return false;
+ }
+ result =
+ finishOperation(handle, empty_params, signature_to_verify, &ignored_params, output_data);
+ if (result != KM_ERROR_OK) {
+ ALOGE("FinishOperation failed: %d", result);
+ return false;
+ }
+ return true;
+}
+
+int32_t KeystoreClientImpl::addRandomNumberGeneratorEntropy(const std::string& entropy) {
+ return mapKeystoreError(keystore_->addRngEntropy(StringAsByteArray(entropy), entropy.size()));
+}
+
+int32_t KeystoreClientImpl::generateKey(const std::string& key_name,
+ const AuthorizationSet& key_parameters,
+ AuthorizationSet* hardware_enforced_characteristics,
+ AuthorizationSet* software_enforced_characteristics) {
+ String16 key_name16(key_name.data(), key_name.size());
+ KeymasterArguments key_arguments;
+ CopyParameters(key_parameters, &key_arguments.params);
+ KeyCharacteristics characteristics;
+ int32_t result =
+ keystore_->generateKey(key_name16, key_arguments, NULL /*entropy*/, 0 /*entropyLength*/,
+ kDefaultUID, KEYSTORE_FLAG_NONE, &characteristics);
+ hardware_enforced_characteristics->Reinitialize(characteristics.characteristics.hw_enforced);
+ software_enforced_characteristics->Reinitialize(characteristics.characteristics.sw_enforced);
+ return mapKeystoreError(result);
+}
+
+int32_t
+KeystoreClientImpl::getKeyCharacteristics(const std::string& key_name,
+ AuthorizationSet* hardware_enforced_characteristics,
+ AuthorizationSet* software_enforced_characteristics) {
+ String16 key_name16(key_name.data(), key_name.size());
+ keymaster_blob_t client_id_blob = {nullptr, 0};
+ keymaster_blob_t app_data_blob = {nullptr, 0};
+ KeyCharacteristics characteristics;
+ int32_t result = keystore_->getKeyCharacteristics(key_name16, &client_id_blob, &app_data_blob,
+ &characteristics);
+ hardware_enforced_characteristics->Reinitialize(characteristics.characteristics.hw_enforced);
+ software_enforced_characteristics->Reinitialize(characteristics.characteristics.sw_enforced);
+ return mapKeystoreError(result);
+}
+
+int32_t KeystoreClientImpl::importKey(const std::string& key_name,
+ const AuthorizationSet& key_parameters,
+ keymaster_key_format_t key_format,
+ const std::string& key_data,
+ AuthorizationSet* hardware_enforced_characteristics,
+ AuthorizationSet* software_enforced_characteristics) {
+ String16 key_name16(key_name.data(), key_name.size());
+ KeymasterArguments key_arguments;
+ CopyParameters(key_parameters, &key_arguments.params);
+ KeyCharacteristics characteristics;
+ int32_t result =
+ keystore_->importKey(key_name16, key_arguments, key_format, StringAsByteArray(key_data),
+ key_data.size(), kDefaultUID, KEYSTORE_FLAG_NONE, &characteristics);
+ hardware_enforced_characteristics->Reinitialize(characteristics.characteristics.hw_enforced);
+ software_enforced_characteristics->Reinitialize(characteristics.characteristics.sw_enforced);
+ return mapKeystoreError(result);
+}
+
+int32_t KeystoreClientImpl::exportKey(keymaster_key_format_t export_format,
+ const std::string& key_name, std::string* export_data) {
+ String16 key_name16(key_name.data(), key_name.size());
+ keymaster_blob_t client_id_blob = {nullptr, 0};
+ keymaster_blob_t app_data_blob = {nullptr, 0};
+ ExportResult export_result;
+ keystore_->exportKey(key_name16, export_format, &client_id_blob, &app_data_blob,
+ &export_result);
+ *export_data = ByteArrayAsString(export_result.exportData.get(), export_result.dataLength);
+ return mapKeystoreError(export_result.resultCode);
+}
+
+int32_t KeystoreClientImpl::deleteKey(const std::string& key_name) {
+ String16 key_name16(key_name.data(), key_name.size());
+ return mapKeystoreError(keystore_->del(key_name16, kDefaultUID));
+}
+
+int32_t KeystoreClientImpl::deleteAllKeys() {
+ return mapKeystoreError(keystore_->clear_uid(kDefaultUID));
+}
+
+int32_t KeystoreClientImpl::beginOperation(keymaster_purpose_t purpose, const std::string& key_name,
+ const AuthorizationSet& input_parameters,
+ AuthorizationSet* output_parameters,
+ keymaster_operation_handle_t* handle) {
+ android::sp<android::IBinder> token(new android::BBinder);
+ String16 key_name16(key_name.data(), key_name.size());
+ KeymasterArguments input_arguments;
+ CopyParameters(input_parameters, &input_arguments.params);
+ OperationResult result;
+ keystore_->begin(token, key_name16, purpose, true /*pruneable*/, input_arguments,
+ NULL /*entropy*/, 0 /*entropyLength*/, &result);
+ int32_t error_code = mapKeystoreError(result.resultCode);
+ if (error_code == KM_ERROR_OK) {
+ *handle = getNextVirtualHandle();
+ active_operations_[*handle] = result.token;
+ if (!result.outParams.params.empty()) {
+ output_parameters->Reinitialize(&*result.outParams.params.begin(),
+ result.outParams.params.size());
+ }
+ }
+ return error_code;
+}
+
+int32_t KeystoreClientImpl::updateOperation(keymaster_operation_handle_t handle,
+ const AuthorizationSet& input_parameters,
+ const std::string& input_data,
+ size_t* num_input_bytes_consumed,
+ AuthorizationSet* output_parameters,
+ std::string* output_data) {
+ if (active_operations_.count(handle) == 0) {
+ return KM_ERROR_INVALID_OPERATION_HANDLE;
+ }
+ KeymasterArguments input_arguments;
+ CopyParameters(input_parameters, &input_arguments.params);
+ OperationResult result;
+ keystore_->update(active_operations_[handle], input_arguments, StringAsByteArray(input_data),
+ input_data.size(), &result);
+ int32_t error_code = mapKeystoreError(result.resultCode);
+ if (error_code == KM_ERROR_OK) {
+ *num_input_bytes_consumed = result.inputConsumed;
+ if (!result.outParams.params.empty()) {
+ output_parameters->Reinitialize(&*result.outParams.params.begin(),
+ result.outParams.params.size());
+ }
+ output_data->append(ByteArrayAsString(result.data.get(), result.dataLength));
+ }
+ return error_code;
+}
+
+int32_t KeystoreClientImpl::finishOperation(keymaster_operation_handle_t handle,
+ const AuthorizationSet& input_parameters,
+ const std::string& signature_to_verify,
+ AuthorizationSet* output_parameters,
+ std::string* output_data) {
+ if (active_operations_.count(handle) == 0) {
+ return KM_ERROR_INVALID_OPERATION_HANDLE;
+ }
+ KeymasterArguments input_arguments;
+ CopyParameters(input_parameters, &input_arguments.params);
+ OperationResult result;
+ keystore_->finish(active_operations_[handle], input_arguments,
+ StringAsByteArray(signature_to_verify), signature_to_verify.size(),
+ NULL /*entropy*/, 0 /*entropyLength*/, &result);
+ int32_t error_code = mapKeystoreError(result.resultCode);
+ if (error_code == KM_ERROR_OK) {
+ if (!result.outParams.params.empty()) {
+ output_parameters->Reinitialize(&*result.outParams.params.begin(),
+ result.outParams.params.size());
+ }
+ output_data->append(ByteArrayAsString(result.data.get(), result.dataLength));
+ active_operations_.erase(handle);
+ }
+ return error_code;
+}
+
+int32_t KeystoreClientImpl::abortOperation(keymaster_operation_handle_t handle) {
+ if (active_operations_.count(handle) == 0) {
+ return KM_ERROR_INVALID_OPERATION_HANDLE;
+ }
+ int32_t error_code = mapKeystoreError(keystore_->abort(active_operations_[handle]));
+ if (error_code == KM_ERROR_OK) {
+ active_operations_.erase(handle);
+ }
+ return error_code;
+}
+
+bool KeystoreClientImpl::doesKeyExist(const std::string& key_name) {
+ String16 key_name16(key_name.data(), key_name.size());
+ int32_t error_code = mapKeystoreError(keystore_->exist(key_name16, kDefaultUID));
+ return (error_code == KM_ERROR_OK);
+}
+
+bool KeystoreClientImpl::listKeys(const std::string& prefix,
+ std::vector<std::string>* key_name_list) {
+ String16 prefix16(prefix.data(), prefix.size());
+ android::Vector<String16> matches;
+ int32_t error_code = mapKeystoreError(keystore_->list(prefix16, kDefaultUID, &matches));
+ if (error_code == KM_ERROR_OK) {
+ for (const auto& match : matches) {
+ android::String8 key_name(match);
+ key_name_list->push_back(prefix + std::string(key_name.string(), key_name.size()));
+ }
+ return true;
+ }
+ return false;
+}
+
+keymaster_operation_handle_t KeystoreClientImpl::getNextVirtualHandle() {
+ return next_virtual_handle_++;
+}
+
+int32_t KeystoreClientImpl::mapKeystoreError(int32_t keystore_error) {
+ // See notes in keystore_client.h for rationale.
+ if (keystore_error == ::NO_ERROR) {
+ return KM_ERROR_OK;
+ }
+ return keystore_error;
+}
+
+bool KeystoreClientImpl::createOrVerifyEncryptionKey(const std::string& key_name) {
+ bool key_exists = doesKeyExist(key_name);
+ if (key_exists) {
+ bool verified = false;
+ if (!verifyEncryptionKeyAttributes(key_name, &verified)) {
+ return false;
+ }
+ if (!verified) {
+ int32_t result = deleteKey(key_name);
+ if (result != KM_ERROR_OK) {
+ ALOGE("Failed to delete invalid encryption key: %d", result);
+ return false;
+ }
+ key_exists = false;
+ }
+ }
+ if (!key_exists) {
+ AuthorizationSetBuilder key_parameters;
+ key_parameters.AesEncryptionKey(kAESKeySize)
+ .Padding(KM_PAD_PKCS7)
+ .Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_CBC)
+ .Authorization(keymaster::TAG_NO_AUTH_REQUIRED);
+ AuthorizationSet hardware_enforced_characteristics;
+ AuthorizationSet software_enforced_characteristics;
+ int32_t result =
+ generateKey(key_name, key_parameters.build(), &hardware_enforced_characteristics,
+ &software_enforced_characteristics);
+ if (result != KM_ERROR_OK) {
+ ALOGE("Failed to generate encryption key: %d", result);
+ return false;
+ }
+ if (hardware_enforced_characteristics.size() == 0) {
+ ALOGW("WARNING: Encryption key is not hardware-backed.");
+ }
+ }
+ return true;
+}
+
+bool KeystoreClientImpl::createOrVerifyAuthenticationKey(const std::string& key_name) {
+ bool key_exists = doesKeyExist(key_name);
+ if (key_exists) {
+ bool verified = false;
+ if (!verifyAuthenticationKeyAttributes(key_name, &verified)) {
+ return false;
+ }
+ if (!verified) {
+ int32_t result = deleteKey(key_name);
+ if (result != KM_ERROR_OK) {
+ ALOGE("Failed to delete invalid authentication key: %d", result);
+ return false;
+ }
+ key_exists = false;
+ }
+ }
+ if (!key_exists) {
+ AuthorizationSetBuilder key_parameters;
+ key_parameters.HmacKey(kHMACKeySize)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Authorization(keymaster::TAG_MIN_MAC_LENGTH, kHMACOutputSize)
+ .Authorization(keymaster::TAG_NO_AUTH_REQUIRED);
+ AuthorizationSet hardware_enforced_characteristics;
+ AuthorizationSet software_enforced_characteristics;
+ int32_t result =
+ generateKey(key_name, key_parameters.build(), &hardware_enforced_characteristics,
+ &software_enforced_characteristics);
+ if (result != KM_ERROR_OK) {
+ ALOGE("Failed to generate authentication key: %d", result);
+ return false;
+ }
+ if (hardware_enforced_characteristics.size() == 0) {
+ ALOGW("WARNING: Authentication key is not hardware-backed.");
+ }
+ }
+ return true;
+}
+
+bool KeystoreClientImpl::verifyEncryptionKeyAttributes(const std::string& key_name,
+ bool* verified) {
+ AuthorizationSet hardware_enforced_characteristics;
+ AuthorizationSet software_enforced_characteristics;
+ int32_t result = getKeyCharacteristics(key_name, &hardware_enforced_characteristics,
+ &software_enforced_characteristics);
+ if (result != KM_ERROR_OK) {
+ ALOGE("Failed to query encryption key: %d", result);
+ return false;
+ }
+ *verified = true;
+ keymaster_algorithm_t algorithm = KM_ALGORITHM_RSA;
+ if ((!hardware_enforced_characteristics.GetTagValue(keymaster::TAG_ALGORITHM, &algorithm) &&
+ !software_enforced_characteristics.GetTagValue(keymaster::TAG_ALGORITHM, &algorithm)) ||
+ algorithm != KM_ALGORITHM_AES) {
+ ALOGW("Found encryption key with invalid algorithm.");
+ *verified = false;
+ }
+ uint32_t key_size = 0;
+ if ((!hardware_enforced_characteristics.GetTagValue(keymaster::TAG_KEY_SIZE, &key_size) &&
+ !software_enforced_characteristics.GetTagValue(keymaster::TAG_KEY_SIZE, &key_size)) ||
+ key_size != kAESKeySize) {
+ ALOGW("Found encryption key with invalid size.");
+ *verified = false;
+ }
+ keymaster_block_mode_t block_mode = KM_MODE_ECB;
+ if ((!hardware_enforced_characteristics.GetTagValue(keymaster::TAG_BLOCK_MODE, &block_mode) &&
+ !software_enforced_characteristics.GetTagValue(keymaster::TAG_BLOCK_MODE, &block_mode)) ||
+ block_mode != KM_MODE_CBC) {
+ ALOGW("Found encryption key with invalid block mode.");
+ *verified = false;
+ }
+ keymaster_padding_t padding_mode = KM_PAD_NONE;
+ if ((!hardware_enforced_characteristics.GetTagValue(keymaster::TAG_PADDING, &padding_mode) &&
+ !software_enforced_characteristics.GetTagValue(keymaster::TAG_PADDING, &padding_mode)) ||
+ padding_mode != KM_PAD_PKCS7) {
+ ALOGW("Found encryption key with invalid padding mode.");
+ *verified = false;
+ }
+ if (hardware_enforced_characteristics.size() == 0) {
+ ALOGW("WARNING: Encryption key is not hardware-backed.");
+ }
+ return true;
+}
+
+bool KeystoreClientImpl::verifyAuthenticationKeyAttributes(const std::string& key_name,
+ bool* verified) {
+ AuthorizationSet hardware_enforced_characteristics;
+ AuthorizationSet software_enforced_characteristics;
+ int32_t result = getKeyCharacteristics(key_name, &hardware_enforced_characteristics,
+ &software_enforced_characteristics);
+ if (result != KM_ERROR_OK) {
+ ALOGE("Failed to query authentication key: %d", result);
+ return false;
+ }
+ *verified = true;
+ keymaster_algorithm_t algorithm = KM_ALGORITHM_RSA;
+ if ((!hardware_enforced_characteristics.GetTagValue(keymaster::TAG_ALGORITHM, &algorithm) &&
+ !software_enforced_characteristics.GetTagValue(keymaster::TAG_ALGORITHM, &algorithm)) ||
+ algorithm != KM_ALGORITHM_HMAC) {
+ ALOGW("Found authentication key with invalid algorithm.");
+ *verified = false;
+ }
+ uint32_t key_size = 0;
+ if ((!hardware_enforced_characteristics.GetTagValue(keymaster::TAG_KEY_SIZE, &key_size) &&
+ !software_enforced_characteristics.GetTagValue(keymaster::TAG_KEY_SIZE, &key_size)) ||
+ key_size != kHMACKeySize) {
+ ALOGW("Found authentication key with invalid size.");
+ *verified = false;
+ }
+ uint32_t mac_size = 0;
+ if ((!hardware_enforced_characteristics.GetTagValue(keymaster::TAG_MIN_MAC_LENGTH, &mac_size) &&
+ !software_enforced_characteristics.GetTagValue(keymaster::TAG_MIN_MAC_LENGTH,
+ &mac_size)) ||
+ mac_size != kHMACOutputSize) {
+ ALOGW("Found authentication key with invalid minimum mac size.");
+ *verified = false;
+ }
+ keymaster_digest_t digest = KM_DIGEST_NONE;
+ if ((!hardware_enforced_characteristics.GetTagValue(keymaster::TAG_DIGEST, &digest) &&
+ !software_enforced_characteristics.GetTagValue(keymaster::TAG_DIGEST, &digest)) ||
+ digest != KM_DIGEST_SHA_2_256) {
+ ALOGW("Found authentication key with invalid digest list.");
+ *verified = false;
+ }
+ if (hardware_enforced_characteristics.size() == 0) {
+ ALOGW("WARNING: Authentication key is not hardware-backed.");
+ }
+ return true;
+}
+
+} // namespace keystore
diff --git a/security/keystore/keystore_get.cpp b/security/keystore/keystore_get.cpp
new file mode 100644
index 0000000..45ad415
--- /dev/null
+++ b/security/keystore/keystore_get.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2012 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 <keystore/IKeystoreService.h>
+#include <binder/IServiceManager.h>
+
+#include <keystore/keystore_get.h>
+
+using namespace android;
+
+ssize_t keystore_get(const char *key, size_t keyLength, uint8_t** value) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
+ sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
+
+ if (service == NULL) {
+ return -1;
+ }
+
+ size_t valueLength;
+ int32_t ret = service->get(String16(key, keyLength), value, &valueLength);
+ if (ret < 0) {
+ return -1;
+ } else if (ret != ::NO_ERROR) {
+ return -1;
+ } else {
+ return valueLength;
+ }
+}
diff --git a/security/keystore/keystore_keymaster_enforcement.h b/security/keystore/keystore_keymaster_enforcement.h
new file mode 100644
index 0000000..d20d7a6
--- /dev/null
+++ b/security/keystore/keystore_keymaster_enforcement.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2015 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 KEYSTORE_KEYMASTER_ENFORCEMENT_H_
+#define KEYSTORE_KEYMASTER_ENFORCEMENT_H_
+
+#include <time.h>
+
+#include <keymaster/keymaster_enforcement.h>
+
+/**
+ * This is a specialization of the KeymasterEnforcement class to be used by Keystore to enforce
+ * keymaster requirements on all key operation.
+ */
+class KeystoreKeymasterEnforcement : public keymaster::KeymasterEnforcement {
+ public:
+ KeystoreKeymasterEnforcement() : KeymasterEnforcement(64, 64) {}
+
+ uint32_t get_current_time() const override {
+ struct timespec tp;
+ int err = clock_gettime(CLOCK_MONOTONIC, &tp);
+ if (err || tp.tv_sec < 0)
+ return 0;
+ return static_cast<uint32_t>(tp.tv_sec);
+ }
+
+ bool activation_date_valid(uint64_t activation_date) const override {
+ time_t now = time(NULL);
+ if (now == static_cast<time_t>(-1)) {
+ // Failed to obtain current time -- fail safe: activation_date hasn't yet occurred.
+ return false;
+ } else if (now < 0) {
+ // Current time is prior to start of the epoch -- activation_date hasn't yet occurred.
+ return false;
+ }
+
+ // time(NULL) returns seconds since epoch and "loses" milliseconds information. We thus add
+ // 999 ms to now_date to avoid a situation where an activation_date of up to 999ms in the
+ // past may still be considered to still be in the future. This can be removed once
+ // time(NULL) is replaced by a millisecond-precise source of time.
+ uint64_t now_date = static_cast<uint64_t>(now) * 1000 + 999;
+ return now_date >= activation_date;
+ }
+
+ bool expiration_date_passed(uint64_t expiration_date) const override {
+ time_t now = time(NULL);
+ if (now == static_cast<time_t>(-1)) {
+ // Failed to obtain current time -- fail safe: expiration_date has passed.
+ return true;
+ } else if (now < 0) {
+ // Current time is prior to start of the epoch: expiration_date hasn't yet occurred.
+ return false;
+ }
+
+ // time(NULL) returns seconds since epoch and "loses" milliseconds information. As a result,
+ // expiration_date of up to 999 ms in the past may still be considered in the future. This
+ // is OK.
+ uint64_t now_date = static_cast<uint64_t>(now) * 1000;
+ return now_date > expiration_date;
+ }
+
+ bool auth_token_timed_out(const hw_auth_token_t&, uint32_t) const {
+ // Assume the token has not timed out, because AuthTokenTable would not have returned it if
+ // the timeout were past. Secure hardware will also check timeouts if it supports them.
+ return false;
+ }
+
+ bool ValidateTokenSignature(const hw_auth_token_t&) const override {
+ // Non-secure world cannot validate token signatures because it doesn't have access to the
+ // signing key. Assume the token is good.
+ return true;
+ }
+};
+
+#endif // KEYSTORE_KEYMASTER_ENFORCEMENT_H_
diff --git a/security/keystore/keystore_main.cpp b/security/keystore/keystore_main.cpp
new file mode 100644
index 0000000..a2b75f6
--- /dev/null
+++ b/security/keystore/keystore_main.cpp
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "keystore"
+
+#include <keymaster/soft_keymaster_device.h>
+#include <keymaster/soft_keymaster_logger.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+#include <cutils/log.h>
+
+#include "entropy.h"
+#include "key_store_service.h"
+#include "keystore.h"
+#include "permissions.h"
+
+/* KeyStore is a secured storage for key-value pairs. In this implementation,
+ * each file stores one key-value pair. Keys are encoded in file names, and
+ * values are encrypted with checksums. The encryption key is protected by a
+ * user-defined password. To keep things simple, buffers are always larger than
+ * the maximum space we needed, so boundary checks on buffers are omitted. */
+
+using keymaster::SoftKeymasterDevice;
+
+static int keymaster0_device_initialize(const hw_module_t* mod, keymaster2_device_t** dev) {
+ assert(mod->module_api_version < KEYMASTER_MODULE_API_VERSION_1_0);
+ ALOGI("Found keymaster0 module %s, version %x", mod->name, mod->module_api_version);
+
+ UniquePtr<SoftKeymasterDevice> soft_keymaster(new SoftKeymasterDevice);
+ keymaster0_device_t* km0_device = NULL;
+ keymaster_error_t error = KM_ERROR_OK;
+
+ int rc = keymaster0_open(mod, &km0_device);
+ if (rc) {
+ ALOGE("Error opening keystore keymaster0 device.");
+ goto err;
+ }
+
+ if (km0_device->flags & KEYMASTER_SOFTWARE_ONLY) {
+ ALOGI("Keymaster0 module is software-only. Using SoftKeymasterDevice instead.");
+ km0_device->common.close(&km0_device->common);
+ km0_device = NULL;
+ // SoftKeymasterDevice will be deleted by keymaster_device_release()
+ *dev = soft_keymaster.release()->keymaster2_device();
+ return 0;
+ }
+
+ ALOGD("Wrapping keymaster0 module %s with SoftKeymasterDevice", mod->name);
+ error = soft_keymaster->SetHardwareDevice(km0_device);
+ km0_device = NULL; // SoftKeymasterDevice has taken ownership.
+ if (error != KM_ERROR_OK) {
+ ALOGE("Got error %d from SetHardwareDevice", error);
+ rc = error;
+ goto err;
+ }
+
+ // SoftKeymasterDevice will be deleted by keymaster_device_release()
+ *dev = soft_keymaster.release()->keymaster2_device();
+ return 0;
+
+err:
+ if (km0_device)
+ km0_device->common.close(&km0_device->common);
+ *dev = NULL;
+ return rc;
+}
+
+static int keymaster1_device_initialize(const hw_module_t* mod, keymaster2_device_t** dev) {
+ assert(mod->module_api_version >= KEYMASTER_MODULE_API_VERSION_1_0);
+ ALOGI("Found keymaster1 module %s, version %x", mod->name, mod->module_api_version);
+
+ UniquePtr<SoftKeymasterDevice> soft_keymaster(new SoftKeymasterDevice);
+ keymaster1_device_t* km1_device = nullptr;
+ keymaster_error_t error = KM_ERROR_OK;
+
+ int rc = keymaster1_open(mod, &km1_device);
+ if (rc) {
+ ALOGE("Error %d opening keystore keymaster1 device", rc);
+ goto err;
+ }
+
+ ALOGD("Wrapping keymaster1 module %s with SofKeymasterDevice", mod->name);
+ error = soft_keymaster->SetHardwareDevice(km1_device);
+ km1_device = nullptr; // SoftKeymasterDevice has taken ownership.
+ if (error != KM_ERROR_OK) {
+ ALOGE("Got error %d from SetHardwareDevice", error);
+ rc = error;
+ goto err;
+ }
+
+ // SoftKeymasterDevice will be deleted by keymaster_device_release()
+ *dev = soft_keymaster.release()->keymaster2_device();
+ return 0;
+
+err:
+ if (km1_device)
+ km1_device->common.close(&km1_device->common);
+ *dev = NULL;
+ return rc;
+}
+
+static int keymaster2_device_initialize(const hw_module_t* mod, keymaster2_device_t** dev) {
+ assert(mod->module_api_version >= KEYMASTER_MODULE_API_VERSION_2_0);
+ ALOGI("Found keymaster2 module %s, version %x", mod->name, mod->module_api_version);
+
+ UniquePtr<SoftKeymasterDevice> soft_keymaster(new SoftKeymasterDevice);
+ keymaster2_device_t* km2_device = nullptr;
+
+ int rc = keymaster2_open(mod, &km2_device);
+ if (rc) {
+ ALOGE("Error %d opening keystore keymaster2 device", rc);
+ goto err;
+ }
+
+ *dev = km2_device;
+ return 0;
+
+err:
+ if (km2_device)
+ km2_device->common.close(&km2_device->common);
+ *dev = nullptr;
+ return rc;
+}
+
+static int keymaster_device_initialize(keymaster2_device_t** dev) {
+ const hw_module_t* mod;
+
+ int rc = hw_get_module_by_class(KEYSTORE_HARDWARE_MODULE_ID, NULL, &mod);
+ if (rc) {
+ ALOGI("Could not find any keystore module, using software-only implementation.");
+ // SoftKeymasterDevice will be deleted by keymaster_device_release()
+ *dev = (new SoftKeymasterDevice)->keymaster2_device();
+ return 0;
+ }
+
+ if (mod->module_api_version < KEYMASTER_MODULE_API_VERSION_1_0) {
+ return keymaster0_device_initialize(mod, dev);
+ } else if (mod->module_api_version == KEYMASTER_MODULE_API_VERSION_1_0) {
+ return keymaster1_device_initialize(mod, dev);
+ } else {
+ return keymaster2_device_initialize(mod, dev);
+ }
+}
+
+// softkeymaster_logger appears not to be used in keystore, but it installs itself as the
+// logger used by SoftKeymasterDevice.
+static keymaster::SoftKeymasterLogger softkeymaster_logger;
+
+static int fallback_keymaster_device_initialize(keymaster2_device_t** dev) {
+ *dev = (new SoftKeymasterDevice)->keymaster2_device();
+ // SoftKeymasterDevice will be deleted by keymaster_device_release()
+ return 0;
+}
+
+static void keymaster_device_release(keymaster2_device_t* dev) {
+ dev->common.close(&dev->common);
+}
+
+int main(int argc, char* argv[]) {
+ if (argc < 2) {
+ ALOGE("A directory must be specified!");
+ return 1;
+ }
+ if (chdir(argv[1]) == -1) {
+ ALOGE("chdir: %s: %s", argv[1], strerror(errno));
+ return 1;
+ }
+
+ Entropy entropy;
+ if (!entropy.open()) {
+ return 1;
+ }
+
+ keymaster2_device_t* dev;
+ if (keymaster_device_initialize(&dev)) {
+ ALOGE("keystore keymaster could not be initialized; exiting");
+ return 1;
+ }
+
+ keymaster2_device_t* fallback;
+ if (fallback_keymaster_device_initialize(&fallback)) {
+ ALOGE("software keymaster could not be initialized; exiting");
+ return 1;
+ }
+
+ if (configure_selinux() == -1) {
+ return -1;
+ }
+
+ KeyStore keyStore(&entropy, dev, fallback);
+ keyStore.initialize();
+ android::sp<android::IServiceManager> sm = android::defaultServiceManager();
+ android::sp<android::KeyStoreService> service = new android::KeyStoreService(&keyStore);
+ android::status_t ret = sm->addService(android::String16("android.security.keystore"), service);
+ if (ret != android::OK) {
+ ALOGE("Couldn't register binder service!");
+ return -1;
+ }
+
+ /*
+ * We're the only thread in existence, so we're just going to process
+ * Binder transaction as a single-threaded program.
+ */
+ android::IPCThreadState::self()->joinThreadPool();
+
+ keymaster_device_release(dev);
+ return 1;
+}
diff --git a/security/keystore/keystore_utils.cpp b/security/keystore/keystore_utils.cpp
new file mode 100644
index 0000000..bfcb43a
--- /dev/null
+++ b/security/keystore/keystore_utils.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define LOG_TAG "keystore"
+
+#include "keystore_utils.h"
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <cutils/log.h>
+#include <private/android_filesystem_config.h>
+
+#include <keymaster/android_keymaster_utils.h>
+
+size_t readFully(int fd, uint8_t* data, size_t size) {
+ size_t remaining = size;
+ while (remaining > 0) {
+ ssize_t n = TEMP_FAILURE_RETRY(read(fd, data, remaining));
+ if (n <= 0) {
+ return size - remaining;
+ }
+ data += n;
+ remaining -= n;
+ }
+ return size;
+}
+
+size_t writeFully(int fd, uint8_t* data, size_t size) {
+ size_t remaining = size;
+ while (remaining > 0) {
+ ssize_t n = TEMP_FAILURE_RETRY(write(fd, data, remaining));
+ if (n < 0) {
+ ALOGW("write failed: %s", strerror(errno));
+ return size - remaining;
+ }
+ data += n;
+ remaining -= n;
+ }
+ return size;
+}
+
+void add_legacy_key_authorizations(int keyType, std::vector<keymaster_key_param_t>* params) {
+ params->push_back(keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_SIGN));
+ params->push_back(keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_VERIFY));
+ params->push_back(keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_ENCRYPT));
+ params->push_back(keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_DECRYPT));
+ params->push_back(keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE));
+ if (keyType == EVP_PKEY_RSA) {
+ params->push_back(keymaster_param_enum(KM_TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_SIGN));
+ params->push_back(keymaster_param_enum(KM_TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_ENCRYPT));
+ params->push_back(keymaster_param_enum(KM_TAG_PADDING, KM_PAD_RSA_PSS));
+ params->push_back(keymaster_param_enum(KM_TAG_PADDING, KM_PAD_RSA_OAEP));
+ }
+ params->push_back(keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE));
+ params->push_back(keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_MD5));
+ params->push_back(keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_SHA1));
+ params->push_back(keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_SHA_2_224));
+ params->push_back(keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_SHA_2_256));
+ params->push_back(keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_SHA_2_384));
+ params->push_back(keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_SHA_2_512));
+ params->push_back(keymaster_param_bool(KM_TAG_ALL_USERS));
+ params->push_back(keymaster_param_bool(KM_TAG_NO_AUTH_REQUIRED));
+ params->push_back(keymaster_param_date(KM_TAG_ORIGINATION_EXPIRE_DATETIME, LLONG_MAX));
+ params->push_back(keymaster_param_date(KM_TAG_USAGE_EXPIRE_DATETIME, LLONG_MAX));
+ params->push_back(keymaster_param_date(KM_TAG_ACTIVE_DATETIME, 0));
+ uint64_t now = keymaster::java_time(time(NULL));
+ params->push_back(keymaster_param_date(KM_TAG_CREATION_DATETIME, now));
+}
+
+uid_t get_app_id(uid_t uid) {
+ return uid % AID_USER;
+}
+
+uid_t get_user_id(uid_t uid) {
+ return uid / AID_USER;
+}
diff --git a/security/keystore/keystore_utils.h b/security/keystore/keystore_utils.h
new file mode 100644
index 0000000..eaa5eb3
--- /dev/null
+++ b/security/keystore/keystore_utils.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 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 KEYSTORE_KEYSTORE_UTILS_H_
+#define KEYSTORE_KEYSTORE_UTILS_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+
+#include <hardware/keymaster_defs.h>
+
+#include <UniquePtr.h>
+
+size_t readFully(int fd, uint8_t* data, size_t size);
+size_t writeFully(int fd, uint8_t* data, size_t size);
+
+void add_legacy_key_authorizations(int keyType, std::vector<keymaster_key_param_t>* params);
+
+/**
+ * Returns the app ID (in the Android multi-user sense) for the current
+ * UNIX UID.
+ */
+uid_t get_app_id(uid_t uid);
+
+/**
+ * Returns the user ID (in the Android multi-user sense) for the current
+ * UNIX UID.
+ */
+uid_t get_user_id(uid_t uid);
+
+struct EVP_PKEY_Delete {
+ void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); }
+};
+typedef UniquePtr<EVP_PKEY, EVP_PKEY_Delete> Unique_EVP_PKEY;
+
+struct PKCS8_PRIV_KEY_INFO_Delete {
+ void operator()(PKCS8_PRIV_KEY_INFO* p) const { PKCS8_PRIV_KEY_INFO_free(p); }
+};
+typedef UniquePtr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_Delete> Unique_PKCS8_PRIV_KEY_INFO;
+
+#endif // KEYSTORE_KEYSTORE_UTILS_H_
diff --git a/security/keystore/operation.cpp b/security/keystore/operation.cpp
new file mode 100644
index 0000000..e8ae8b7
--- /dev/null
+++ b/security/keystore/operation.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+#define LOG_TAG "KeystoreOperation"
+
+#include "operation.h"
+
+#include <algorithm>
+
+namespace android {
+OperationMap::OperationMap(IBinder::DeathRecipient* deathRecipient)
+ : mDeathRecipient(deathRecipient) {}
+
+sp<IBinder> OperationMap::addOperation(keymaster_operation_handle_t handle, uint64_t keyid,
+ keymaster_purpose_t purpose, const keymaster2_device_t* dev,
+ const sp<IBinder>& appToken,
+ keymaster_key_characteristics_t* characteristics,
+ bool pruneable) {
+ sp<IBinder> token = new BBinder();
+ mMap[token] = Operation(handle, keyid, purpose, dev, characteristics, appToken);
+ if (pruneable) {
+ mLru.push_back(token);
+ }
+ if (mAppTokenMap.find(appToken) == mAppTokenMap.end()) {
+ appToken->linkToDeath(mDeathRecipient);
+ }
+ mAppTokenMap[appToken].push_back(token);
+ return token;
+}
+
+bool OperationMap::getOperation(const sp<IBinder>& token, keymaster_operation_handle_t* outHandle,
+ uint64_t* outKeyid, keymaster_purpose_t* outPurpose,
+ const keymaster2_device_t** outDevice,
+ const keymaster_key_characteristics_t** outCharacteristics) {
+ if (!outHandle || !outDevice) {
+ return false;
+ }
+ auto entry = mMap.find(token);
+ if (entry == mMap.end()) {
+ return false;
+ }
+ updateLru(token);
+
+ *outHandle = entry->second.handle;
+ *outKeyid = entry->second.keyid;
+ *outPurpose = entry->second.purpose;
+ *outDevice = entry->second.device;
+ if (outCharacteristics) {
+ *outCharacteristics = entry->second.characteristics.get();
+ }
+ return true;
+}
+
+void OperationMap::updateLru(const sp<IBinder>& token) {
+ auto lruEntry = std::find(mLru.begin(), mLru.end(), token);
+ if (lruEntry != mLru.end()) {
+ mLru.erase(lruEntry);
+ mLru.push_back(token);
+ }
+}
+
+bool OperationMap::removeOperation(const sp<IBinder>& token) {
+ auto entry = mMap.find(token);
+ if (entry == mMap.end()) {
+ return false;
+ }
+ sp<IBinder> appToken = entry->second.appToken;
+ mMap.erase(entry);
+ auto lruEntry = std::find(mLru.begin(), mLru.end(), token);
+ if (lruEntry != mLru.end()) {
+ mLru.erase(lruEntry);
+ }
+ removeOperationTracking(token, appToken);
+ return true;
+}
+
+void OperationMap::removeOperationTracking(const sp<IBinder>& token, const sp<IBinder>& appToken) {
+ auto appEntry = mAppTokenMap.find(appToken);
+ if (appEntry == mAppTokenMap.end()) {
+ ALOGE("Entry for %p contains unmapped application token %p", token.get(), appToken.get());
+ return;
+ }
+ auto tokenEntry = std::find(appEntry->second.begin(), appEntry->second.end(), token);
+ appEntry->second.erase(tokenEntry);
+ // Stop listening for death if all operations tied to the token have finished.
+ if (appEntry->second.size() == 0) {
+ appToken->unlinkToDeath(mDeathRecipient);
+ mAppTokenMap.erase(appEntry);
+ }
+}
+
+bool OperationMap::hasPruneableOperation() const {
+ return mLru.size() != 0;
+}
+
+size_t OperationMap::getPruneableOperationCount() const {
+ return mLru.size();
+}
+
+sp<IBinder> OperationMap::getOldestPruneableOperation() {
+ if (!hasPruneableOperation()) {
+ return sp<IBinder>(NULL);
+ }
+ return mLru[0];
+}
+
+bool OperationMap::getOperationAuthToken(const sp<IBinder>& token, const hw_auth_token_t** outToken) {
+ auto entry = mMap.find(token);
+ if (entry == mMap.end()) {
+ return false;
+ }
+ *outToken = entry->second.authToken.get();
+ return true;
+}
+
+bool OperationMap::setOperationAuthToken(const sp<IBinder>& token, const hw_auth_token_t* authToken) {
+ auto entry = mMap.find(token);
+ if (entry == mMap.end()) {
+ return false;
+ }
+ entry->second.authToken.reset(new hw_auth_token_t);
+ *entry->second.authToken = *authToken;
+ return true;
+}
+
+std::vector<sp<IBinder>> OperationMap::getOperationsForToken(const sp<IBinder>& appToken) {
+ auto appEntry = mAppTokenMap.find(appToken);
+ if (appEntry != mAppTokenMap.end()) {
+ return appEntry->second;
+ } else {
+ return std::vector<sp<IBinder>>();
+ }
+}
+
+OperationMap::Operation::Operation(keymaster_operation_handle_t handle_, uint64_t keyid_,
+ keymaster_purpose_t purpose_, const keymaster2_device_t* device_,
+ keymaster_key_characteristics_t* characteristics_,
+ sp<IBinder> appToken_)
+ : handle(handle_), keyid(keyid_), purpose(purpose_), device(device_),
+ characteristics(characteristics_), appToken(appToken_) {}
+
+OperationMap::Operation::Operation() : handle(0), device(NULL), characteristics(), appToken(NULL) {}
+
+} // namespace android
diff --git a/security/keystore/operation.h b/security/keystore/operation.h
new file mode 100644
index 0000000..263b5c9
--- /dev/null
+++ b/security/keystore/operation.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2015 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 KEYSTORE_OPERATION_H_
+#define KEYSTORE_OPERATION_H_
+
+#include <hardware/hw_auth_token.h>
+#include <hardware/keymaster2.h>
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <utils/LruCache.h>
+#include <utils/StrongPointer.h>
+#include <map>
+#include <vector>
+
+namespace android {
+
+struct keymaster_key_characteristics_t_Delete {
+ void operator()(keymaster_key_characteristics_t* characteristics) const {
+ keymaster_free_characteristics(characteristics);
+ delete characteristics;
+ }
+};
+typedef std::unique_ptr<keymaster_key_characteristics_t, keymaster_key_characteristics_t_Delete>
+ Unique_keymaster_key_characteristics;
+
+/**
+ * OperationMap handles the translation of keymaster_operation_handle_t's and
+ * keymaster2_device_t's to opaque binder tokens that can be used to reference
+ * that operation at a later time by applications. It also does LRU tracking
+ * for operation pruning and keeps a mapping of clients to operations to allow
+ * for graceful handling of application death.
+ */
+class OperationMap {
+public:
+ explicit OperationMap(IBinder::DeathRecipient* deathRecipient);
+ sp<IBinder> addOperation(keymaster_operation_handle_t handle, uint64_t keyid,
+ keymaster_purpose_t purpose, const keymaster2_device_t* dev,
+ const sp<IBinder>& appToken, keymaster_key_characteristics_t* characteristics,
+ bool pruneable);
+ bool getOperation(const sp<IBinder>& token, keymaster_operation_handle_t* outHandle,
+ uint64_t* outKeyid, keymaster_purpose_t* outPurpose,
+ const keymaster2_device_t** outDev,
+ const keymaster_key_characteristics_t** outCharacteristics);
+ bool removeOperation(const sp<IBinder>& token);
+ bool hasPruneableOperation() const;
+ size_t getOperationCount() const { return mMap.size(); }
+ size_t getPruneableOperationCount() const;
+ bool getOperationAuthToken(const sp<IBinder>& token, const hw_auth_token_t** outToken);
+ bool setOperationAuthToken(const sp<IBinder>& token, const hw_auth_token_t* authToken);
+ sp<IBinder> getOldestPruneableOperation();
+ std::vector<sp<IBinder>> getOperationsForToken(const sp<IBinder>& appToken);
+
+private:
+ void updateLru(const sp<IBinder>& token);
+ void removeOperationTracking(const sp<IBinder>& token, const sp<IBinder>& appToken);
+ struct Operation {
+ Operation();
+ Operation(keymaster_operation_handle_t handle, uint64_t keyid, keymaster_purpose_t purpose,
+ const keymaster2_device_t* device,
+ keymaster_key_characteristics_t* characteristics, sp<IBinder> appToken);
+ keymaster_operation_handle_t handle;
+ uint64_t keyid;
+ keymaster_purpose_t purpose;
+ const keymaster2_device_t* device;
+ Unique_keymaster_key_characteristics characteristics;
+ sp<IBinder> appToken;
+ std::unique_ptr<hw_auth_token_t> authToken;
+ };
+ std::map<sp<IBinder>, struct Operation> mMap;
+ std::vector<sp<IBinder>> mLru;
+ std::map<sp<IBinder>, std::vector<sp<IBinder>>> mAppTokenMap;
+ IBinder::DeathRecipient* mDeathRecipient;
+};
+} // namespace android
+#endif
diff --git a/security/keystore/permissions.cpp b/security/keystore/permissions.cpp
new file mode 100644
index 0000000..feacd8f
--- /dev/null
+++ b/security/keystore/permissions.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define LOG_TAG "keystore"
+
+#include "permissions.h"
+
+#include <cutils/log.h>
+#include <cutils/sockets.h>
+#include <private/android_filesystem_config.h>
+
+#include <selinux/android.h>
+
+#include "keystore_utils.h"
+
+/* perm_labels associcated with keystore_key SELinux class verbs. */
+const char* perm_labels[] = {
+ "get_state", "get", "insert", "delete", "exist", "list",
+ "reset", "password", "lock", "unlock", "is_empty", "sign",
+ "verify", "grant", "duplicate", "clear_uid", "add_auth", "user_changed",
+};
+
+struct user_euid {
+ uid_t uid;
+ uid_t euid;
+};
+
+user_euid user_euids[] = {
+ {AID_VPN, AID_SYSTEM}, {AID_WIFI, AID_SYSTEM}, {AID_ROOT, AID_SYSTEM},
+};
+
+struct user_perm {
+ uid_t uid;
+ perm_t perms;
+};
+
+static user_perm user_perms[] = {
+ {AID_SYSTEM, static_cast<perm_t>((uint32_t)(~0))},
+ {AID_VPN, static_cast<perm_t>(P_GET | P_SIGN | P_VERIFY)},
+ {AID_WIFI, static_cast<perm_t>(P_GET | P_SIGN | P_VERIFY)},
+ {AID_ROOT, static_cast<perm_t>(P_GET)},
+};
+
+static const perm_t DEFAULT_PERMS = static_cast<perm_t>(P_GET_STATE | P_GET | P_INSERT | P_DELETE |
+ P_EXIST | P_LIST | P_SIGN | P_VERIFY);
+
+struct audit_data {
+ pid_t pid;
+ uid_t uid;
+};
+
+const char* get_perm_label(perm_t perm) {
+ unsigned int index = ffs(perm);
+ if (index > 0 && index <= (sizeof(perm_labels) / sizeof(perm_labels[0]))) {
+ return perm_labels[index - 1];
+ } else {
+ ALOGE("Keystore: Failed to retrieve permission label.\n");
+ abort();
+ }
+}
+
+static int audit_callback(void* data, security_class_t /* cls */, char* buf, size_t len) {
+ struct audit_data* ad = reinterpret_cast<struct audit_data*>(data);
+ if (!ad) {
+ ALOGE("No keystore audit data");
+ return 0;
+ }
+
+ snprintf(buf, len, "pid=%d uid=%d", ad->pid, ad->uid);
+ return 0;
+}
+
+static char* tctx;
+static int ks_is_selinux_enabled;
+
+int configure_selinux() {
+ ks_is_selinux_enabled = is_selinux_enabled();
+ if (ks_is_selinux_enabled) {
+ union selinux_callback cb;
+ cb.func_audit = audit_callback;
+ selinux_set_callback(SELINUX_CB_AUDIT, cb);
+ cb.func_log = selinux_log_callback;
+ selinux_set_callback(SELINUX_CB_LOG, cb);
+ if (getcon(&tctx) != 0) {
+ ALOGE("SELinux: Could not acquire target context. Aborting keystore.\n");
+ return -1;
+ }
+ } else {
+ ALOGI("SELinux: Keystore SELinux is disabled.\n");
+ }
+
+ return 0;
+}
+
+static bool keystore_selinux_check_access(uid_t uid, perm_t perm, pid_t spid) {
+ if (!ks_is_selinux_enabled) {
+ return true;
+ }
+
+ audit_data ad;
+ char* sctx = NULL;
+ const char* selinux_class = "keystore_key";
+ const char* str_perm = get_perm_label(perm);
+
+ if (!str_perm) {
+ return false;
+ }
+
+ if (getpidcon(spid, &sctx) != 0) {
+ ALOGE("SELinux: Failed to get source pid context.\n");
+ return false;
+ }
+
+ ad.pid = spid;
+ ad.uid = uid;
+
+ bool allowed = selinux_check_access(sctx, tctx, selinux_class, str_perm,
+ reinterpret_cast<void*>(&ad)) == 0;
+ freecon(sctx);
+ return allowed;
+}
+
+/**
+ * Returns the UID that the callingUid should act as. This is here for
+ * legacy support of the WiFi and VPN systems and should be removed
+ * when WiFi can operate in its own namespace.
+ */
+uid_t get_keystore_euid(uid_t uid) {
+ for (size_t i = 0; i < sizeof(user_euids) / sizeof(user_euids[0]); i++) {
+ struct user_euid user = user_euids[i];
+ if (user.uid == uid) {
+ return user.euid;
+ }
+ }
+
+ return uid;
+}
+
+bool has_permission(uid_t uid, perm_t perm, pid_t spid) {
+ // All system users are equivalent for multi-user support.
+ if (get_app_id(uid) == AID_SYSTEM) {
+ uid = AID_SYSTEM;
+ }
+
+ for (size_t i = 0; i < sizeof(user_perms) / sizeof(user_perms[0]); i++) {
+ struct user_perm user = user_perms[i];
+ if (user.uid == uid) {
+ return (user.perms & perm) && keystore_selinux_check_access(uid, perm, spid);
+ }
+ }
+
+ return (DEFAULT_PERMS & perm) && keystore_selinux_check_access(uid, perm, spid);
+}
+
+/**
+ * Returns true if the callingUid is allowed to interact in the targetUid's
+ * namespace.
+ */
+bool is_granted_to(uid_t callingUid, uid_t targetUid) {
+ if (callingUid == targetUid) {
+ return true;
+ }
+ for (size_t i = 0; i < sizeof(user_euids) / sizeof(user_euids[0]); i++) {
+ struct user_euid user = user_euids[i];
+ if (user.euid == callingUid && user.uid == targetUid) {
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/security/keystore/permissions.h b/security/keystore/permissions.h
new file mode 100644
index 0000000..f5f1831
--- /dev/null
+++ b/security/keystore/permissions.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 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 KEYSTORE_PERMISSIONS_H_
+#define KEYSTORE_PERMISSIONS_H_
+
+#include <unistd.h>
+
+/* Here are the permissions, actions, users, and the main function. */
+enum perm_t {
+ P_GET_STATE = 1 << 0,
+ P_GET = 1 << 1,
+ P_INSERT = 1 << 2,
+ P_DELETE = 1 << 3,
+ P_EXIST = 1 << 4,
+ P_LIST = 1 << 5,
+ P_RESET = 1 << 6,
+ P_PASSWORD = 1 << 7,
+ P_LOCK = 1 << 8,
+ P_UNLOCK = 1 << 9,
+ P_IS_EMPTY = 1 << 10,
+ P_SIGN = 1 << 11,
+ P_VERIFY = 1 << 12,
+ P_GRANT = 1 << 13,
+ P_DUPLICATE = 1 << 14,
+ P_CLEAR_UID = 1 << 15,
+ P_ADD_AUTH = 1 << 16,
+ P_USER_CHANGED = 1 << 17,
+};
+
+const char* get_perm_label(perm_t perm);
+
+/**
+ * Returns the UID that the callingUid should act as. This is here for
+ * legacy support of the WiFi and VPN systems and should be removed
+ * when WiFi can operate in its own namespace.
+ */
+uid_t get_keystore_euid(uid_t uid);
+
+bool has_permission(uid_t uid, perm_t perm, pid_t spid);
+
+/**
+ * Returns true if the callingUid is allowed to interact in the targetUid's
+ * namespace.
+ */
+bool is_granted_to(uid_t callingUid, uid_t targetUid);
+
+int configure_selinux();
+
+#endif // KEYSTORE_PERMISSIONS_H_
diff --git a/security/keystore/test-keystore b/security/keystore/test-keystore
new file mode 100755
index 0000000..071cfcd
--- /dev/null
+++ b/security/keystore/test-keystore
@@ -0,0 +1,297 @@
+#!/bin/bash
+#
+# Copyright 2011, 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.
+
+set -e
+
+prefix=$0
+log_file=$prefix.log
+baseline_file=$prefix.baseline
+
+function cleanup_output() {
+ rm -f $log_file
+ rm -f $baseline_file
+}
+
+function log() {
+ echo "$@"
+ append $log_file \# "$@"
+ append $baseline_file \# "$@"
+}
+
+function expect() {
+ append $baseline_file "$@"
+}
+
+function append() {
+ declare -r file=$1
+ shift
+ echo "$@" >> $file
+}
+
+function run() {
+ # strip out carriage returns from adb
+ # strip out date/time from ls -l
+ "$@" | tr -d '\r' | sed -E 's/[0-9]{4}-[0-9]{2}-[0-9]{2} +[0-9]{1,2}:[0-9]{2} //' >> $log_file
+}
+
+function keystore() {
+ declare -r user=$1
+ shift
+ run adb shell su $user keystore_cli "$@"
+}
+
+function keystore_in() {
+ declare -r user=$1
+ declare -r input=$2
+ shift; shift
+ run adb shell "echo '$input' | su $user keystore_cli $@"
+}
+
+function list_keystore_directory() {
+ run adb shell ls -al /data/misc/keystore$@
+}
+
+function compare() {
+ log "comparing $baseline_file and $log_file"
+ diff $baseline_file $log_file || (log $tag FAILED && exit 1)
+}
+
+function test_basic() {
+
+ #
+ # reset
+ #
+ log "reset keystore as system user"
+ keystore system reset
+ expect "reset: No error (1)"
+ list_keystore_directory
+ expect "-rw------- keystore keystore 4 .metadata"
+ expect "drwx------ keystore keystore user_0"
+
+ #
+ # basic tests as system/root
+ #
+ log "root does not have permission to run test"
+ keystore root test
+ expect "test: Permission denied (6)"
+
+ log "but system user does"
+ keystore system test
+ expect "test: Uninitialized (3)"
+ list_keystore_directory
+ expect "-rw------- keystore keystore 4 .metadata"
+ expect "drwx------ keystore keystore user_0"
+
+ log "password is now bar"
+ keystore system password bar
+ expect "password: No error (1)"
+ list_keystore_directory /user_0
+ expect "-rw------- keystore keystore 84 .masterkey"
+
+ log "no error implies initialized and unlocked"
+ keystore system test
+ expect "test: No error (1)"
+
+ log "saw with no argument"
+ keystore system saw
+
+ log "saw nothing"
+ keystore system saw ""
+
+ log "add key baz"
+ keystore_in system quux insert baz
+ expect "insert: No error (1)"
+
+ log "1000 is uid of system"
+ list_keystore_directory /user_0
+ expect "-rw------- keystore keystore 84 .masterkey"
+ expect "-rw------- keystore keystore 52 1000_baz"
+
+ log "saw baz"
+ keystore system saw
+ expect "baz"
+
+ log "get baz"
+ keystore system get baz
+ expect "quux"
+
+ log "root can read system user keys (as can wifi or vpn users)"
+ keystore root get baz
+ expect "quux"
+
+ #
+ # app user tests
+ #
+
+ # u0_a0 has uid 10000, as seen below
+ log "other uses cannot see the system keys"
+ keystore u0_a0 get baz
+
+ log "app user cannot use reset, password, lock, unlock"
+ keystore u0_a0 reset
+ expect "reset: Permission denied (6)"
+ keystore u0_a0 password some_pass
+ expect "password: Permission denied (6)"
+ keystore u0_a0 lock
+ expect "lock: Permission denied (6)"
+ keystore u0_a0 unlock some_pass
+ expect "unlock: Permission denied (6)"
+
+ log "install u0_a0 key"
+ keystore_in u0_a0 deadbeef insert 0x
+ expect "insert: No error (1)"
+ list_keystore_directory /user_0
+ expect "-rw------- keystore keystore 84 .masterkey"
+ expect "-rw------- keystore keystore 52 10000_0x"
+ expect "-rw------- keystore keystore 52 1000_baz"
+
+ log "get with no argument"
+ keystore u0_a0 get
+ expect "Usage: keystore_cli get <name>"
+
+ log "few get tests for an app"
+ keystore u0_a0 get 0x
+ expect "deadbeef"
+
+ keystore_in u0_a0 barney insert fred
+ expect "insert: No error (1)"
+
+ keystore u0_a0 saw
+ expect "0x"
+ expect "fred"
+
+ log "note that saw returns the suffix of prefix matches"
+ keystore u0_a0 saw fr # fred
+ expect "ed" # fred
+
+ #
+ # lock tests
+ #
+ log "lock the store as system"
+ keystore system lock
+ expect "lock: No error (1)"
+ keystore system test
+ expect "test: Locked (2)"
+
+ log "saw works while locked"
+ keystore u0_a0 saw
+ expect "0x"
+ expect "fred"
+
+ log "...and app can read keys..."
+ keystore u0_a0 get 0x
+ expect "deadbeef"
+
+ log "...but they cannot be deleted."
+ keystore u0_a0 exist 0x
+ expect "exist: No error (1)"
+ keystore u0_a0 del_key 0x
+ expect "del_key: Key not found (7)"
+
+ #
+ # password
+ #
+ log "wrong password"
+ keystore system unlock foo
+ expect "unlock: Wrong password (4 tries left) (13)"
+ log "right password"
+ keystore system unlock bar
+ expect "unlock: No error (1)"
+
+ log "make the password foo"
+ keystore system password foo
+ expect "password: No error (1)"
+
+ #
+ # final reset
+ #
+ log "reset wipes everything for all users"
+ keystore system reset
+ expect "reset: No error (1)"
+ list_keystore_directory
+ expect "-rw------- keystore keystore 4 .metadata"
+ expect "drwx------ keystore keystore user_0"
+ list_keystore_directory /user_0
+
+ keystore system test
+ expect "test: Uninitialized (3)"
+}
+
+function test_grant() {
+ log "test granting"
+ keystore system reset
+ expect "reset: No error (1)"
+ keystore system password test_pass
+ expect "password: No error (1)"
+
+ keystore_in system granted_key_value insert granted_key
+ expect "insert: No error (1)"
+
+ # Cannot read before grant.
+ keystore u10_a0 get granted_key
+
+ # Grant and read.
+ log "System grants to u0_a1"
+ keystore system grant granted_key 10001
+ expect "Working with uid 10001"
+ expect "grant: No error (1)"
+ keystore u0_a1 get 1000_granted_key
+ expect "granted_key_value"
+}
+
+function test_4599735() {
+ # http://b/4599735
+ log "start regression test for b/4599735"
+ keystore system reset
+ expect "reset: No error (1)"
+ list_keystore_directory /user_0
+
+ keystore system password foo
+ expect "password: No error (1)"
+
+ keystore_in system quux insert baz
+ expect "insert: No error (1)"
+
+ keystore root get baz
+ expect "quux"
+
+ keystore system lock
+ expect "lock: No error (1)"
+
+ keystore system password foo
+ expect "password: No error (1)"
+
+ log "after unlock, regression led to result of '8 Value corrupted'"
+ keystore root get baz
+ expect "quux"
+
+ keystore system reset
+ expect "reset: No error (1)"
+ log "end regression test for b/4599735"
+}
+
+function main() {
+ cleanup_output
+ log $tag START
+ test_basic
+ test_4599735
+ test_grant
+ compare
+ log $tag PASSED
+ cleanup_output
+}
+
+main
diff --git a/security/keystore/tests/Android.mk b/security/keystore/tests/Android.mk
new file mode 100644
index 0000000..be8c426
--- /dev/null
+++ b/security/keystore/tests/Android.mk
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2015 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+# Unit test for AuthTokenTable
+include $(CLEAR_VARS)
+ifeq ($(USE_32_BIT_KEYSTORE), true)
+LOCAL_MULTILIB := 32
+endif
+LOCAL_CFLAGS := -Wall -Wextra -Werror
+LOCAL_SRC_FILES := auth_token_table_test.cpp
+LOCAL_MODULE := auth_token_table_test
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := libgtest_main libkeystore_test
+LOCAL_SHARED_LIBRARIES := libkeymaster_messages
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_NATIVE_TEST)
diff --git a/security/keystore/tests/Makefile b/security/keystore/tests/Makefile
new file mode 100644
index 0000000..5c1117f
--- /dev/null
+++ b/security/keystore/tests/Makefile
@@ -0,0 +1,116 @@
+##########
+# This makefile builds local unit tests that run locally on the development machine. Note
+# that it may be necessary to install some libraries on the dev maching to make the tests
+# build.
+#
+# The same unit tests are also built by Android.mk to run on the target device. The tests
+# should always build and pass in both places. The on-device test is what really matters,
+# of course, but debugging is often somewhat easier on the dev platform.
+##########
+
+BASE=../../../..
+SUBS=system/core \
+ system/keymaster\
+ hardware/libhardware \
+ external/gtest
+GTEST=$(BASE)/external/gtest
+KEYMASTER=$(BASE)/system/keymaster
+
+INCLUDES=$(foreach dir,$(SUBS),-I $(BASE)/$(dir)/include) \
+ -I $(BASE)/libnativehelper/include/nativehelper \
+ -I $(GTEST) -Iinclude
+
+# Add USE_CLANG=1 to the make command line to build with clang, which has better error
+# reporting and diagnoses some conditions that GCC doesn't.
+ifdef USE_CLANG
+CC=/usr/bin/clang
+CXX=/usr/bin/clang
+CLANG_TEST_DEFINE=-DKEYMASTER_CLANG_TEST_BUILD
+COMPILER_SPECIFIC_ARGS=-std=c++11 $(CLANG_TEST_DEFINE)
+else
+COMPILER_SPECIFIC_ARGS=-std=c++0x -fprofile-arcs
+endif
+
+CPPFLAGS=$(INCLUDES) -g -O0 -MD
+CXXFLAGS=-Wall -Werror -Wno-unused -Winit-self -Wpointer-arith -Wunused-parameter \
+ -Werror=sign-compare -Wmissing-declarations -ftest-coverage -fno-permissive \
+ -Wno-deprecated-declarations -fno-exceptions -DKEYMASTER_NAME_TAGS \
+ $(COMPILER_SPECIFIC_ARGS)
+
+# Uncomment to enable debug logging.
+# CXXFLAGS += -DDEBUG
+
+LDLIBS=-lpthread -lstdc++ -lgcov
+
+# This list of sources is used for dependency generation and cleanup. Add each new source
+# file here (not headers).
+CPPSRCS=\
+ ../auth_token_table.cpp \
+ auth_token_table_test.cpp
+
+# This list of binaries determes what gets built and run. Add each new test binary here.
+BINARIES=\
+ auth_token_table_test
+
+.PHONY: coverage memcheck massif clean run
+
+%.run: %
+ ./$<
+ touch $@
+
+run: $(BINARIES:=.run)
+
+auth_token_table_test: auth_token_table_test.o \
+ ../auth_token_table.o \
+ $(GTEST)/src/gtest-all.o \
+ $(KEYMASTER)/authorization_set.o \
+ $(KEYMASTER)/logger.o \
+ $(KEYMASTER)/serializable.o
+
+coverage: coverage.info
+ genhtml coverage.info --output-directory coverage
+
+coverage.info: run
+ lcov --capture --directory=. --directory=.. -b . --output-file coverage.info
+
+%.coverage : %
+ $(MAKE) clean && $(MAKE) $<
+ ./$<
+ lcov --capture --directory=. --output-file coverage.info
+ genhtml coverage.info --output-directory coverage
+#UNINIT_OPTS=--track-origins=yes
+UNINIT_OPTS=--undef-value-errors=no
+
+MEMCHECK_OPTS=--leak-check=full \
+ --show-reachable=yes \
+ --vgdb=full \
+ $(UNINIT_OPTS) \
+ --error-exitcode=1
+
+MASSIF_OPTS=--tool=massif \
+ --stacks=yes
+
+%.memcheck : %
+ valgrind $(MEMCHECK_OPTS) ./$< && \
+ touch $@
+
+%.massif : %
+ valgrind $(MASSIF_OPTS) --massif-out-file=$@ ./$<
+
+memcheck: $(BINARIES:=.memcheck)
+
+massif: $(BINARIES:=.massif)
+
+OBJS=$(CPPSRCS:.cpp=.o)
+DEPS=$(CPPSRCS:.cpp=.d)
+GCOV=$(CPPSRCS:.cpp=.gcov) $(CPPSRCS:.cpp=.gcda) $(CPPSRCS:.cpp=.gcno)
+
+clean:
+ rm -f $(OBJS) $(DEPS) $(BINARIES) $(GCOV) \
+ $(BINARIES:=.run) $(BINARIES:=.memcheck) $(BINARIES:=.massif) \
+ *gcov *gcno *gcda coverage.info
+ rm -rf coverage
+
+-include $(CPPSRCS:.cpp=.d)
+-include $(CCSRCS:.cc=.d)
+
diff --git a/security/keystore/tests/auth_token_table_test.cpp b/security/keystore/tests/auth_token_table_test.cpp
new file mode 100644
index 0000000..b1c0f49
--- /dev/null
+++ b/security/keystore/tests/auth_token_table_test.cpp
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2015 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 <gtest/gtest.h>
+
+#include <keymaster/android_keymaster_utils.h>
+#include <keymaster/logger.h>
+
+#include "../auth_token_table.h"
+
+using std::vector;
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ int result = RUN_ALL_TESTS();
+}
+
+inline bool operator==(const hw_auth_token_t& a, const hw_auth_token_t& b) {
+ return (memcmp(&a, &b, sizeof(a)) == 0);
+}
+
+namespace keymaster {
+namespace test {
+
+class StdoutLogger : public Logger {
+ public:
+ StdoutLogger() { set_instance(this); }
+
+ int log_msg(LogLevel level, const char* fmt, va_list args) const {
+ int output_len = 0;
+ switch (level) {
+ case DEBUG_LVL:
+ output_len = printf("DEBUG: ");
+ break;
+ case INFO_LVL:
+ output_len = printf("INFO: ");
+ break;
+ case WARNING_LVL:
+ output_len = printf("WARNING: ");
+ break;
+ case ERROR_LVL:
+ output_len = printf("ERROR: ");
+ break;
+ case SEVERE_LVL:
+ output_len = printf("SEVERE: ");
+ break;
+ }
+
+ output_len += vprintf(fmt, args);
+ output_len += printf("\n");
+ return output_len;
+ }
+};
+
+StdoutLogger logger;
+
+TEST(AuthTokenTableTest, Create) {
+ AuthTokenTable table;
+}
+
+static hw_auth_token_t* make_token(uint64_t rsid, uint64_t ssid = 0, uint64_t challenge = 0,
+ uint64_t timestamp = 0) {
+ hw_auth_token_t* token = new hw_auth_token_t;
+ token->user_id = rsid;
+ token->authenticator_id = ssid;
+ token->authenticator_type = hton(static_cast<uint32_t>(HW_AUTH_PASSWORD));
+ token->challenge = challenge;
+ token->timestamp = hton(timestamp);
+ return token;
+}
+
+static AuthorizationSet make_set(uint64_t rsid, uint32_t timeout = 10000) {
+ AuthorizationSetBuilder builder;
+ builder.Authorization(TAG_USER_ID, 10)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD)
+ .Authorization(TAG_USER_SECURE_ID, rsid);
+ // Use timeout == 0 to indicate tags that require auth per operation.
+ if (timeout != 0)
+ builder.Authorization(TAG_AUTH_TIMEOUT, timeout);
+ return builder.build();
+}
+
+// Tests obviously run so fast that a real-time clock with a one-second granularity rarely changes
+// output during a test run. This test clock "ticks" one second every time it's called.
+static time_t monotonic_clock() {
+ static time_t time = 0;
+ return time++;
+}
+
+TEST(AuthTokenTableTest, SimpleAddAndFindTokens) {
+ AuthTokenTable table;
+
+ table.AddAuthenticationToken(make_token(1, 2));
+ table.AddAuthenticationToken(make_token(3, 4));
+ EXPECT_EQ(2U, table.size());
+
+ const hw_auth_token_t* found;
+
+ ASSERT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(1), 0, &found));
+ EXPECT_EQ(1U, found->user_id);
+ EXPECT_EQ(2U, found->authenticator_id);
+
+ ASSERT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(2), 0, &found));
+ EXPECT_EQ(1U, found->user_id);
+ EXPECT_EQ(2U, found->authenticator_id);
+
+ ASSERT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(3), 0, &found));
+ EXPECT_EQ(3U, found->user_id);
+ EXPECT_EQ(4U, found->authenticator_id);
+
+ ASSERT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(4), 0, &found));
+ EXPECT_EQ(3U, found->user_id);
+ EXPECT_EQ(4U, found->authenticator_id);
+
+ ASSERT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+ table.FindAuthorization(make_set(5), 0, &found));
+}
+
+TEST(AuthTokenTableTest, FlushTable) {
+ AuthTokenTable table(3, monotonic_clock);
+
+ table.AddAuthenticationToken(make_token(1));
+ table.AddAuthenticationToken(make_token(2));
+ table.AddAuthenticationToken(make_token(3));
+
+ const hw_auth_token_t* found;
+
+ // All three should be in the table.
+ EXPECT_EQ(3U, table.size());
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(1), 0, &found));
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(2), 0, &found));
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(3), 0, &found));
+
+ table.Clear();
+ EXPECT_EQ(0U, table.size());
+}
+
+TEST(AuthTokenTableTest, TableOverflow) {
+ AuthTokenTable table(3, monotonic_clock);
+
+ table.AddAuthenticationToken(make_token(1));
+ table.AddAuthenticationToken(make_token(2));
+ table.AddAuthenticationToken(make_token(3));
+
+ const hw_auth_token_t* found;
+
+ // All three should be in the table.
+ EXPECT_EQ(3U, table.size());
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(1), 0, &found));
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(2), 0, &found));
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(3), 0, &found));
+
+ table.AddAuthenticationToken(make_token(4));
+
+ // Oldest should be gone.
+ EXPECT_EQ(3U, table.size());
+ EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+ table.FindAuthorization(make_set(1), 0, &found));
+
+ // Others should be there, including the new one (4). Search for it first, then the others, so
+ // 4 becomes the least recently used.
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(4), 0, &found));
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(2), 0, &found));
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(3), 0, &found));
+
+ table.AddAuthenticationToken(make_token(5));
+
+ // 5 should have replaced 4.
+ EXPECT_EQ(3U, table.size());
+ EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+ table.FindAuthorization(make_set(4), 0, &found));
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(2), 0, &found));
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(5), 0, &found));
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(3), 0, &found));
+
+ table.AddAuthenticationToken(make_token(6));
+ table.AddAuthenticationToken(make_token(7));
+
+ // 2 and 5 should be gone
+ EXPECT_EQ(3U, table.size());
+ EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+ table.FindAuthorization(make_set(2), 0, &found));
+ EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+ table.FindAuthorization(make_set(5), 0, &found));
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(6), 0, &found));
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(7), 0, &found));
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(3), 0, &found));
+
+ table.AddAuthenticationToken(make_token(8));
+ table.AddAuthenticationToken(make_token(9));
+ table.AddAuthenticationToken(make_token(10));
+
+ // Only the three most recent should be there.
+ EXPECT_EQ(3U, table.size());
+ EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+ table.FindAuthorization(make_set(1), 0, &found));
+ EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+ table.FindAuthorization(make_set(2), 0, &found));
+ EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+ table.FindAuthorization(make_set(3), 0, &found));
+ EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+ table.FindAuthorization(make_set(4), 0, &found));
+ EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+ table.FindAuthorization(make_set(5), 0, &found));
+ EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+ table.FindAuthorization(make_set(6), 0, &found));
+ EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+ table.FindAuthorization(make_set(7), 0, &found));
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(8), 0, &found));
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(9), 0, &found));
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(10), 0, &found));
+}
+
+TEST(AuthTokenTableTest, AuthenticationNotRequired) {
+ AuthTokenTable table;
+ const hw_auth_token_t* found;
+
+ EXPECT_EQ(AuthTokenTable::AUTH_NOT_REQUIRED,
+ table.FindAuthorization(AuthorizationSetBuilder().Authorization(TAG_NO_AUTH_REQUIRED),
+ 0 /* no challenge */, &found));
+}
+
+TEST(AuthTokenTableTest, OperationHandleNotFound) {
+ AuthTokenTable table;
+ const hw_auth_token_t* found;
+
+ table.AddAuthenticationToken(make_token(1, 0, 1, 5));
+ EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+ table.FindAuthorization(make_set(1, 0 /* no timeout */),
+ 2 /* non-matching challenge */, &found));
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(1, 0 /* no timeout */),
+ 1 /* matching challenge */, &found));
+ table.MarkCompleted(1);
+ EXPECT_EQ(
+ AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+ table.FindAuthorization(make_set(1, 0 /* no timeout */), 1 /* used challenge */, &found));
+}
+
+TEST(AuthTokenTableTest, OperationHandleRequired) {
+ AuthTokenTable table;
+ const hw_auth_token_t* found;
+
+ table.AddAuthenticationToken(make_token(1));
+ EXPECT_EQ(
+ AuthTokenTable::OP_HANDLE_REQUIRED,
+ table.FindAuthorization(make_set(1, 0 /* no timeout */), 0 /* no op handle */, &found));
+}
+
+TEST(AuthTokenTableTest, AuthSidChanged) {
+ AuthTokenTable table;
+ const hw_auth_token_t* found;
+
+ table.AddAuthenticationToken(make_token(1, 3, /* op handle */ 1));
+ EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_WRONG_SID,
+ table.FindAuthorization(make_set(2, 0 /* no timeout */), 1 /* op handle */, &found));
+}
+
+TEST(AuthTokenTableTest, TokenExpired) {
+ AuthTokenTable table(5, monotonic_clock);
+ const hw_auth_token_t* found;
+
+ auto key_info = make_set(1, 5 /* five second timeout */);
+
+ // monotonic_clock "ticks" one second each time it's called, which is once per request, so the
+ // sixth request should fail, since key_info says the key is good for five seconds.
+ //
+ // Note that this tests the decision of the AuthTokenTable to reject a request it knows is
+ // expired. An additional check of the secure timestamp (in the token) will be made by
+ // keymaster when the found token is passed to it.
+ table.AddAuthenticationToken(make_token(1, 0));
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(key_info, 0 /* no op handle */, &found));
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(key_info, 0 /* no op handle */, &found));
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(key_info, 0 /* no op handle */, &found));
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(key_info, 0 /* no op handle */, &found));
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(key_info, 0 /* no op handle */, &found));
+ EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_EXPIRED,
+ table.FindAuthorization(key_info, 0 /* no op handle */, &found));
+}
+
+TEST(AuthTokenTableTest, MarkNonexistentEntryCompleted) {
+ AuthTokenTable table;
+ // Marking a nonexistent entry completed is ignored. This test is mainly for code coverage.
+ table.MarkCompleted(1);
+}
+
+TEST(AuthTokenTableTest, SupersededEntries) {
+ AuthTokenTable table;
+ const hw_auth_token_t* found;
+
+ // Add two identical tokens, without challenges. The second should supersede the first, based
+ // on timestamp (fourth arg to make_token).
+ table.AddAuthenticationToken(make_token(1, 0, 0, 0));
+ table.AddAuthenticationToken(make_token(1, 0, 0, 1));
+ EXPECT_EQ(1U, table.size());
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(1), 0, &found));
+ EXPECT_EQ(1U, ntoh(found->timestamp));
+
+ // Add a third token, this with a different RSID. It should not be superseded.
+ table.AddAuthenticationToken(make_token(2, 0, 0, 2));
+ EXPECT_EQ(2U, table.size());
+
+ // Add two more, superseding each of the two in the table.
+ table.AddAuthenticationToken(make_token(1, 0, 0, 3));
+ table.AddAuthenticationToken(make_token(2, 0, 0, 4));
+ EXPECT_EQ(2U, table.size());
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(1), 0, &found));
+ EXPECT_EQ(3U, ntoh(found->timestamp));
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(2), 0, &found));
+ EXPECT_EQ(4U, ntoh(found->timestamp));
+
+ // Add another, this one with a challenge value. It should supersede the old one since it is
+ // newer, and matches other than the challenge.
+ table.AddAuthenticationToken(make_token(1, 0, 1, 5));
+ EXPECT_EQ(2U, table.size());
+
+ // And another, also with a challenge. Because of the challenge values, the one just added
+ // cannot be superseded.
+ table.AddAuthenticationToken(make_token(1, 0, 2, 6));
+ EXPECT_EQ(3U, table.size());
+
+ // Should be able to find each of them, by specifying their challenge, with a key that is not
+ // timed (timed keys don't care about challenges).
+ EXPECT_EQ(AuthTokenTable::OK,
+ table.FindAuthorization(make_set(1, 0 /* no timeout*/), 1 /* challenge */, &found));
+ EXPECT_EQ(5U, ntoh(found->timestamp));
+ EXPECT_EQ(AuthTokenTable::OK,
+ table.FindAuthorization(make_set(1, 0 /* no timeout */), 2 /* challenge */, &found));
+ EXPECT_EQ(6U, ntoh(found->timestamp));
+
+ // Add another, without a challenge, and the same timestamp as the last one. This new one
+ // actually could be considered already-superseded, but the table doesn't handle that case,
+ // since it seems unlikely to occur in practice.
+ table.AddAuthenticationToken(make_token(1, 0, 0, 6));
+ EXPECT_EQ(4U, table.size());
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(1), 0 /* challenge */, &found));
+ EXPECT_EQ(6U, ntoh(found->timestamp));
+
+ // Add another without a challenge but an increased timestamp. This should supersede the
+ // previous challenge-free entry.
+ table.AddAuthenticationToken(make_token(1, 0, 0, 7));
+ EXPECT_EQ(4U, table.size());
+ EXPECT_EQ(AuthTokenTable::OK,
+ table.FindAuthorization(make_set(1, 0 /* no timeout */), 2 /* challenge */, &found));
+ EXPECT_EQ(6U, ntoh(found->timestamp));
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(1), 0 /* challenge */, &found));
+ EXPECT_EQ(7U, ntoh(found->timestamp));
+
+ // Mark the entry with challenge 2 as complete. Since there's a newer challenge-free entry, the
+ // challenge entry will be superseded.
+ table.MarkCompleted(2);
+ EXPECT_EQ(3U, table.size());
+ EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+ table.FindAuthorization(make_set(1, 0 /* no timeout */), 2 /* challenge */, &found));
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(1), 0 /* challenge */, &found));
+ EXPECT_EQ(7U, ntoh(found->timestamp));
+
+ // Add another SID 1 entry with a challenge. It supersedes the previous SID 1 entry with
+ // no challenge (timestamp 7), but not the one with challenge 1 (timestamp 5).
+ table.AddAuthenticationToken(make_token(1, 0, 3, 8));
+ EXPECT_EQ(3U, table.size());
+
+ EXPECT_EQ(AuthTokenTable::OK,
+ table.FindAuthorization(make_set(1, 0 /* no timeout */), 1 /* challenge */, &found));
+ EXPECT_EQ(5U, ntoh(found->timestamp));
+
+ EXPECT_EQ(AuthTokenTable::OK,
+ table.FindAuthorization(make_set(1, 0 /* no timeout */), 3 /* challenge */, &found));
+ EXPECT_EQ(8U, ntoh(found->timestamp));
+
+ // SID 2 entry is still there.
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(2), 0 /* challenge */, &found));
+ EXPECT_EQ(4U, ntoh(found->timestamp));
+
+ // Mark the entry with challenge 3 as complete. Since the older challenge 1 entry is
+ // incomplete, nothing is superseded.
+ table.MarkCompleted(3);
+ EXPECT_EQ(3U, table.size());
+
+ EXPECT_EQ(AuthTokenTable::OK,
+ table.FindAuthorization(make_set(1, 0 /* no timeout */), 1 /* challenge */, &found));
+ EXPECT_EQ(5U, ntoh(found->timestamp));
+
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(1), 0 /* challenge */, &found));
+ EXPECT_EQ(8U, ntoh(found->timestamp));
+
+ // Mark the entry with challenge 1 as complete. Since there's a newer one (with challenge 3,
+ // completed), the challenge 1 entry is superseded and removed.
+ table.MarkCompleted(1);
+ EXPECT_EQ(2U, table.size());
+ EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+ table.FindAuthorization(make_set(1, 0 /* no timeout */), 1 /* challenge */, &found));
+ EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(1), 0 /* challenge */, &found));
+ EXPECT_EQ(8U, ntoh(found->timestamp));
+}
+
+} // namespace keymaster
+} // namespace test
diff --git a/security/keystore/user_state.cpp b/security/keystore/user_state.cpp
new file mode 100644
index 0000000..3da88c2
--- /dev/null
+++ b/security/keystore/user_state.cpp
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define LOG_TAG "keystore"
+
+#include "user_state.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include <openssl/evp.h>
+
+#include <cutils/log.h>
+
+#include "blob.h"
+#include "keystore_utils.h"
+
+UserState::UserState(uid_t userId) : mUserId(userId), mRetry(MAX_RETRY) {
+ asprintf(&mUserDir, "user_%u", mUserId);
+ asprintf(&mMasterKeyFile, "%s/.masterkey", mUserDir);
+}
+
+UserState::~UserState() {
+ free(mUserDir);
+ free(mMasterKeyFile);
+}
+
+bool UserState::initialize() {
+ if ((mkdir(mUserDir, S_IRUSR | S_IWUSR | S_IXUSR) < 0) && (errno != EEXIST)) {
+ ALOGE("Could not create directory '%s'", mUserDir);
+ return false;
+ }
+
+ if (access(mMasterKeyFile, R_OK) == 0) {
+ setState(STATE_LOCKED);
+ } else {
+ setState(STATE_UNINITIALIZED);
+ }
+
+ return true;
+}
+
+void UserState::setState(State state) {
+ mState = state;
+ if (mState == STATE_NO_ERROR || mState == STATE_UNINITIALIZED) {
+ mRetry = MAX_RETRY;
+ }
+}
+
+void UserState::zeroizeMasterKeysInMemory() {
+ memset(mMasterKey, 0, sizeof(mMasterKey));
+ memset(mSalt, 0, sizeof(mSalt));
+ memset(&mMasterKeyEncryption, 0, sizeof(mMasterKeyEncryption));
+ memset(&mMasterKeyDecryption, 0, sizeof(mMasterKeyDecryption));
+}
+
+bool UserState::deleteMasterKey() {
+ setState(STATE_UNINITIALIZED);
+ zeroizeMasterKeysInMemory();
+ return unlink(mMasterKeyFile) == 0 || errno == ENOENT;
+}
+
+ResponseCode UserState::initialize(const android::String8& pw, Entropy* entropy) {
+ if (!generateMasterKey(entropy)) {
+ return SYSTEM_ERROR;
+ }
+ ResponseCode response = writeMasterKey(pw, entropy);
+ if (response != NO_ERROR) {
+ return response;
+ }
+ setupMasterKeys();
+ return ::NO_ERROR;
+}
+
+ResponseCode UserState::copyMasterKey(UserState* src) {
+ if (mState != STATE_UNINITIALIZED) {
+ return ::SYSTEM_ERROR;
+ }
+ if (src->getState() != STATE_NO_ERROR) {
+ return ::SYSTEM_ERROR;
+ }
+ memcpy(mMasterKey, src->mMasterKey, MASTER_KEY_SIZE_BYTES);
+ setupMasterKeys();
+ return copyMasterKeyFile(src);
+}
+
+ResponseCode UserState::copyMasterKeyFile(UserState* src) {
+ /* Copy the master key file to the new user. Unfortunately we don't have the src user's
+ * password so we cannot generate a new file with a new salt.
+ */
+ int in = TEMP_FAILURE_RETRY(open(src->getMasterKeyFileName(), O_RDONLY));
+ if (in < 0) {
+ return ::SYSTEM_ERROR;
+ }
+ blob rawBlob;
+ size_t length = readFully(in, (uint8_t*)&rawBlob, sizeof(rawBlob));
+ if (close(in) != 0) {
+ return ::SYSTEM_ERROR;
+ }
+ int out =
+ TEMP_FAILURE_RETRY(open(mMasterKeyFile, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR));
+ if (out < 0) {
+ return ::SYSTEM_ERROR;
+ }
+ size_t outLength = writeFully(out, (uint8_t*)&rawBlob, length);
+ if (close(out) != 0) {
+ return ::SYSTEM_ERROR;
+ }
+ if (outLength != length) {
+ ALOGW("blob not fully written %zu != %zu", outLength, length);
+ unlink(mMasterKeyFile);
+ return ::SYSTEM_ERROR;
+ }
+ return ::NO_ERROR;
+}
+
+ResponseCode UserState::writeMasterKey(const android::String8& pw, Entropy* entropy) {
+ uint8_t passwordKey[MASTER_KEY_SIZE_BYTES];
+ generateKeyFromPassword(passwordKey, MASTER_KEY_SIZE_BYTES, pw, mSalt);
+ AES_KEY passwordAesKey;
+ AES_set_encrypt_key(passwordKey, MASTER_KEY_SIZE_BITS, &passwordAesKey);
+ Blob masterKeyBlob(mMasterKey, sizeof(mMasterKey), mSalt, sizeof(mSalt), TYPE_MASTER_KEY);
+ return masterKeyBlob.writeBlob(mMasterKeyFile, &passwordAesKey, STATE_NO_ERROR, entropy);
+}
+
+ResponseCode UserState::readMasterKey(const android::String8& pw, Entropy* entropy) {
+ int in = TEMP_FAILURE_RETRY(open(mMasterKeyFile, O_RDONLY));
+ if (in < 0) {
+ return SYSTEM_ERROR;
+ }
+
+ // We read the raw blob to just to get the salt to generate the AES key, then we create the Blob
+ // to use with decryptBlob
+ blob rawBlob;
+ size_t length = readFully(in, (uint8_t*)&rawBlob, sizeof(rawBlob));
+ if (close(in) != 0) {
+ return SYSTEM_ERROR;
+ }
+ // find salt at EOF if present, otherwise we have an old file
+ uint8_t* salt;
+ if (length > SALT_SIZE && rawBlob.info == SALT_SIZE) {
+ salt = (uint8_t*)&rawBlob + length - SALT_SIZE;
+ } else {
+ salt = NULL;
+ }
+ uint8_t passwordKey[MASTER_KEY_SIZE_BYTES];
+ generateKeyFromPassword(passwordKey, MASTER_KEY_SIZE_BYTES, pw, salt);
+ AES_KEY passwordAesKey;
+ AES_set_decrypt_key(passwordKey, MASTER_KEY_SIZE_BITS, &passwordAesKey);
+ Blob masterKeyBlob(rawBlob);
+ ResponseCode response = masterKeyBlob.readBlob(mMasterKeyFile, &passwordAesKey, STATE_NO_ERROR);
+ if (response == SYSTEM_ERROR) {
+ return response;
+ }
+ if (response == NO_ERROR && masterKeyBlob.getLength() == MASTER_KEY_SIZE_BYTES) {
+ // If salt was missing, generate one and write a new master key file with the salt.
+ if (salt == NULL) {
+ if (!generateSalt(entropy)) {
+ return SYSTEM_ERROR;
+ }
+ response = writeMasterKey(pw, entropy);
+ }
+ if (response == NO_ERROR) {
+ memcpy(mMasterKey, masterKeyBlob.getValue(), MASTER_KEY_SIZE_BYTES);
+ setupMasterKeys();
+ }
+ return response;
+ }
+ if (mRetry <= 0) {
+ reset();
+ return UNINITIALIZED;
+ }
+ --mRetry;
+ switch (mRetry) {
+ case 0:
+ return WRONG_PASSWORD_0;
+ case 1:
+ return WRONG_PASSWORD_1;
+ case 2:
+ return WRONG_PASSWORD_2;
+ case 3:
+ return WRONG_PASSWORD_3;
+ default:
+ return WRONG_PASSWORD_3;
+ }
+}
+
+bool UserState::reset() {
+ DIR* dir = opendir(getUserDirName());
+ if (!dir) {
+ // If the directory doesn't exist then nothing to do.
+ if (errno == ENOENT) {
+ return true;
+ }
+ ALOGW("couldn't open user directory: %s", strerror(errno));
+ return false;
+ }
+
+ struct dirent* file;
+ while ((file = readdir(dir)) != NULL) {
+ // skip . and ..
+ if (!strcmp(".", file->d_name) || !strcmp("..", file->d_name)) {
+ continue;
+ }
+
+ unlinkat(dirfd(dir), file->d_name, 0);
+ }
+ closedir(dir);
+ return true;
+}
+
+void UserState::generateKeyFromPassword(uint8_t* key, ssize_t keySize, const android::String8& pw,
+ uint8_t* salt) {
+ size_t saltSize;
+ if (salt != NULL) {
+ saltSize = SALT_SIZE;
+ } else {
+ // Pre-gingerbread used this hardwired salt, readMasterKey will rewrite these when found
+ salt = (uint8_t*)"keystore";
+ // sizeof = 9, not strlen = 8
+ saltSize = sizeof("keystore");
+ }
+
+ PKCS5_PBKDF2_HMAC_SHA1(reinterpret_cast<const char*>(pw.string()), pw.length(), salt, saltSize,
+ 8192, keySize, key);
+}
+
+bool UserState::generateSalt(Entropy* entropy) {
+ return entropy->generate_random_data(mSalt, sizeof(mSalt));
+}
+
+bool UserState::generateMasterKey(Entropy* entropy) {
+ if (!entropy->generate_random_data(mMasterKey, sizeof(mMasterKey))) {
+ return false;
+ }
+ if (!generateSalt(entropy)) {
+ return false;
+ }
+ return true;
+}
+
+void UserState::setupMasterKeys() {
+ AES_set_encrypt_key(mMasterKey, MASTER_KEY_SIZE_BITS, &mMasterKeyEncryption);
+ AES_set_decrypt_key(mMasterKey, MASTER_KEY_SIZE_BITS, &mMasterKeyDecryption);
+ setState(STATE_NO_ERROR);
+}
diff --git a/security/keystore/user_state.h b/security/keystore/user_state.h
new file mode 100644
index 0000000..902719c
--- /dev/null
+++ b/security/keystore/user_state.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016 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 KEYSTORE_USER_STATE_H_
+#define KEYSTORE_USER_STATE_H_
+
+#include <sys/types.h>
+
+#include <openssl/aes.h>
+
+#include <utils/String8.h>
+
+#include <keystore/keystore.h>
+
+#include "entropy.h"
+
+class UserState {
+ public:
+ explicit UserState(uid_t userId);
+ ~UserState();
+
+ bool initialize();
+
+ uid_t getUserId() const { return mUserId; }
+ const char* getUserDirName() const { return mUserDir; }
+
+ const char* getMasterKeyFileName() const { return mMasterKeyFile; }
+
+ void setState(State state);
+ State getState() const { return mState; }
+
+ int8_t getRetry() const { return mRetry; }
+
+ void zeroizeMasterKeysInMemory();
+ bool deleteMasterKey();
+
+ ResponseCode initialize(const android::String8& pw, Entropy* entropy);
+
+ ResponseCode copyMasterKey(UserState* src);
+ ResponseCode copyMasterKeyFile(UserState* src);
+ ResponseCode writeMasterKey(const android::String8& pw, Entropy* entropy);
+ ResponseCode readMasterKey(const android::String8& pw, Entropy* entropy);
+
+ AES_KEY* getEncryptionKey() { return &mMasterKeyEncryption; }
+ AES_KEY* getDecryptionKey() { return &mMasterKeyDecryption; }
+
+ bool reset();
+
+ private:
+ static const int MASTER_KEY_SIZE_BYTES = 16;
+ static const int MASTER_KEY_SIZE_BITS = MASTER_KEY_SIZE_BYTES * 8;
+
+ static const int MAX_RETRY = 4;
+ static const size_t SALT_SIZE = 16;
+
+ void generateKeyFromPassword(uint8_t* key, ssize_t keySize, const android::String8& pw,
+ uint8_t* salt);
+ bool generateSalt(Entropy* entropy);
+ bool generateMasterKey(Entropy* entropy);
+ void setupMasterKeys();
+
+ uid_t mUserId;
+
+ char* mUserDir;
+ char* mMasterKeyFile;
+
+ State mState;
+ int8_t mRetry;
+
+ uint8_t mMasterKey[MASTER_KEY_SIZE_BYTES];
+ uint8_t mSalt[SALT_SIZE];
+
+ AES_KEY mMasterKeyEncryption;
+ AES_KEY mMasterKeyDecryption;
+};
+
+#endif // KEYSTORE_USER_STATE_H_
diff --git a/security/softkeymaster/Android.mk b/security/softkeymaster/Android.mk
new file mode 100644
index 0000000..eb32c87
--- /dev/null
+++ b/security/softkeymaster/Android.mk
@@ -0,0 +1,44 @@
+# Copyright (C) 2012 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+ifeq ($(USE_32_BIT_KEYSTORE), true)
+LOCAL_MULTILIB := 32
+endif
+LOCAL_MODULE := keystore.default
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_SRC_FILES := module.cpp
+LOCAL_C_INCLUDES := system/security/keystore
+LOCAL_CFLAGS = -fvisibility=hidden -Wall -Werror
+LOCAL_SHARED_LIBRARIES := libcrypto liblog libkeystore_binder libsoftkeymaster
+LOCAL_MODULE_TAGS := optional
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+ifeq ($(USE_32_BIT_KEYSTORE), true)
+LOCAL_MULTILIB := 32
+endif
+LOCAL_MODULE := libsoftkeymaster
+LOCAL_SRC_FILES := keymaster_openssl.cpp
+LOCAL_C_INCLUDES := system/security/keystore \
+ $(LOCAL_PATH)/include
+LOCAL_CFLAGS = -fvisibility=hidden -Wall -Werror
+LOCAL_SHARED_LIBRARIES := libcrypto liblog libkeystore_binder
+LOCAL_MODULE_TAGS := optional
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_SHARED_LIBRARY)
diff --git a/security/softkeymaster/include/keymaster/softkeymaster.h b/security/softkeymaster/include/keymaster/softkeymaster.h
new file mode 100644
index 0000000..e86ba3d
--- /dev/null
+++ b/security/softkeymaster/include/keymaster/softkeymaster.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2013 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 <hardware/keymaster0.h>
+
+#ifndef SOFTKEYMASTER_INCLUDE_KEYMASTER_SOFTKEYMASTER_H_
+#define SOFTKEYMASTER_INCLUDE_KEYMASTER_SOFTKEYMASTER_H_
+
+int openssl_generate_keypair(const keymaster0_device_t* dev, const keymaster_keypair_t key_type,
+ const void* key_params, uint8_t** keyBlob, size_t* keyBlobLength);
+
+int openssl_import_keypair(const keymaster0_device_t* dev, const uint8_t* key,
+ const size_t key_length, uint8_t** key_blob, size_t* key_blob_length);
+
+int openssl_get_keypair_public(const struct keymaster0_device* dev, const uint8_t* key_blob,
+ const size_t key_blob_length, uint8_t** x509_data,
+ size_t* x509_data_length);
+
+int openssl_sign_data(const keymaster0_device_t* dev, const void* params, const uint8_t* keyBlob,
+ const size_t keyBlobLength, const uint8_t* data, const size_t dataLength,
+ uint8_t** signedData, size_t* signedDataLength);
+
+int openssl_verify_data(const keymaster0_device_t* dev, const void* params, const uint8_t* keyBlob,
+ const size_t keyBlobLength, const uint8_t* signedData,
+ const size_t signedDataLength, const uint8_t* signature,
+ const size_t signatureLength);
+
+int openssl_open(const hw_module_t* module, const char* name, hw_device_t** device);
+
+extern struct keystore_module softkeymaster_module;
+
+#endif // SOFTKEYMASTER_INCLUDE_KEYMASTER_SOFTKEYMASTER_H_
diff --git a/security/softkeymaster/keymaster_openssl.cpp b/security/softkeymaster/keymaster_openssl.cpp
new file mode 100644
index 0000000..927b4a6
--- /dev/null
+++ b/security/softkeymaster/keymaster_openssl.cpp
@@ -0,0 +1,785 @@
+/*
+ * Copyright (C) 2012 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 <errno.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <keystore/keystore.h>
+#include <keymaster/softkeymaster.h>
+
+#include <hardware/hardware.h>
+#include <hardware/keymaster0.h>
+
+#include <openssl/evp.h>
+#include <openssl/bio.h>
+#include <openssl/rsa.h>
+#include <openssl/err.h>
+#include <openssl/x509.h>
+
+#include <UniquePtr.h>
+
+// For debugging
+// #define LOG_NDEBUG 0
+
+#define LOG_TAG "OpenSSLKeyMaster"
+#include <cutils/log.h>
+
+struct BIGNUM_Delete {
+ void operator()(BIGNUM* p) const { BN_free(p); }
+};
+typedef UniquePtr<BIGNUM, BIGNUM_Delete> Unique_BIGNUM;
+
+struct EVP_PKEY_Delete {
+ void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); }
+};
+typedef UniquePtr<EVP_PKEY, EVP_PKEY_Delete> Unique_EVP_PKEY;
+
+struct PKCS8_PRIV_KEY_INFO_Delete {
+ void operator()(PKCS8_PRIV_KEY_INFO* p) const { PKCS8_PRIV_KEY_INFO_free(p); }
+};
+typedef UniquePtr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_Delete> Unique_PKCS8_PRIV_KEY_INFO;
+
+struct DSA_Delete {
+ void operator()(DSA* p) const { DSA_free(p); }
+};
+typedef UniquePtr<DSA, DSA_Delete> Unique_DSA;
+
+struct EC_KEY_Delete {
+ void operator()(EC_KEY* p) const { EC_KEY_free(p); }
+};
+typedef UniquePtr<EC_KEY, EC_KEY_Delete> Unique_EC_KEY;
+
+struct EC_GROUP_Delete {
+ void operator()(EC_GROUP* p) const { EC_GROUP_free(p); }
+};
+typedef UniquePtr<EC_GROUP, EC_GROUP_Delete> Unique_EC_GROUP;
+
+struct RSA_Delete {
+ void operator()(RSA* p) const { RSA_free(p); }
+};
+typedef UniquePtr<RSA, RSA_Delete> Unique_RSA;
+
+struct Malloc_Free {
+ void operator()(void* p) const { free(p); }
+};
+
+typedef UniquePtr<keymaster0_device_t> Unique_keymaster_device_t;
+
+/**
+ * Many OpenSSL APIs take ownership of an argument on success but
+ * don't free the argument on failure. This means we need to tell our
+ * scoped pointers when we've transferred ownership, without
+ * triggering a warning by not using the result of release().
+ */
+template <typename T, typename Delete_T>
+inline void release_because_ownership_transferred(UniquePtr<T, Delete_T>& p) {
+ T* val __attribute__((unused)) = p.release();
+}
+
+/*
+ * Checks this thread's OpenSSL error queue and logs if
+ * necessary.
+ */
+static void logOpenSSLError(const char* location) {
+ int error = ERR_get_error();
+
+ if (error != 0) {
+ char message[256];
+ ERR_error_string_n(error, message, sizeof(message));
+ ALOGE("OpenSSL error in %s %d: %s", location, error, message);
+ }
+
+ ERR_clear_error();
+ ERR_remove_thread_state(NULL);
+}
+
+static int wrap_key(EVP_PKEY* pkey, int type, uint8_t** keyBlob, size_t* keyBlobLength) {
+ /*
+ * Find the length of each size. Public key is not needed anymore
+ * but must be kept for alignment purposes.
+ */
+ int publicLen = 0;
+ int privateLen = i2d_PrivateKey(pkey, NULL);
+
+ if (privateLen <= 0) {
+ ALOGE("private key size was too big");
+ return -1;
+ }
+
+ /* int type + int size + private key data + int size + public key data */
+ *keyBlobLength = get_softkey_header_size() + sizeof(type) + sizeof(publicLen) + privateLen +
+ sizeof(privateLen) + publicLen;
+
+ // derData will be returned to the caller, so allocate it with malloc.
+ UniquePtr<unsigned char, Malloc_Free> derData(
+ static_cast<unsigned char*>(malloc(*keyBlobLength)));
+ if (derData.get() == NULL) {
+ ALOGE("could not allocate memory for key blob");
+ return -1;
+ }
+ unsigned char* p = derData.get();
+
+ /* Write the magic value for software keys. */
+ p = add_softkey_header(p, *keyBlobLength);
+
+ /* Write key type to allocated buffer */
+ for (int i = sizeof(type) - 1; i >= 0; i--) {
+ *p++ = (type >> (8 * i)) & 0xFF;
+ }
+
+ /* Write public key to allocated buffer */
+ for (int i = sizeof(publicLen) - 1; i >= 0; i--) {
+ *p++ = (publicLen >> (8 * i)) & 0xFF;
+ }
+
+ /* Write private key to allocated buffer */
+ for (int i = sizeof(privateLen) - 1; i >= 0; i--) {
+ *p++ = (privateLen >> (8 * i)) & 0xFF;
+ }
+ if (i2d_PrivateKey(pkey, &p) != privateLen) {
+ logOpenSSLError("wrap_key");
+ return -1;
+ }
+
+ *keyBlob = derData.release();
+
+ return 0;
+}
+
+static EVP_PKEY* unwrap_key(const uint8_t* keyBlob, const size_t keyBlobLength) {
+ long publicLen = 0;
+ long privateLen = 0;
+ const uint8_t* p = keyBlob;
+ const uint8_t* const end = keyBlob + keyBlobLength;
+
+ if (keyBlob == NULL) {
+ ALOGE("supplied key blob was NULL");
+ return NULL;
+ }
+
+ int type = 0;
+ if (keyBlobLength < (get_softkey_header_size() + sizeof(type) + sizeof(publicLen) + 1 +
+ sizeof(privateLen) + 1)) {
+ ALOGE("key blob appears to be truncated");
+ return NULL;
+ }
+
+ if (!is_softkey(p, keyBlobLength)) {
+ ALOGE("cannot read key; it was not made by this keymaster");
+ return NULL;
+ }
+ p += get_softkey_header_size();
+
+ for (size_t i = 0; i < sizeof(type); i++) {
+ type = (type << 8) | *p++;
+ }
+
+ for (size_t i = 0; i < sizeof(type); i++) {
+ publicLen = (publicLen << 8) | *p++;
+ }
+ if (p + publicLen > end) {
+ ALOGE("public key length encoding error: size=%ld, end=%td", publicLen, end - p);
+ return NULL;
+ }
+
+ p += publicLen;
+ if (end - p < 2) {
+ ALOGE("private key truncated");
+ return NULL;
+ }
+ for (size_t i = 0; i < sizeof(type); i++) {
+ privateLen = (privateLen << 8) | *p++;
+ }
+ if (p + privateLen > end) {
+ ALOGE("private key length encoding error: size=%ld, end=%td", privateLen, end - p);
+ return NULL;
+ }
+
+ Unique_EVP_PKEY pkey(d2i_PrivateKey(type, nullptr, &p, privateLen));
+ if (pkey.get() == NULL) {
+ logOpenSSLError("unwrap_key");
+ return NULL;
+ }
+
+ return pkey.release();
+}
+
+static int generate_dsa_keypair(EVP_PKEY* pkey, const keymaster_dsa_keygen_params_t* dsa_params) {
+ if (dsa_params->key_size < 512) {
+ ALOGI("Requested DSA key size is too small (<512)");
+ return -1;
+ }
+
+ Unique_DSA dsa(DSA_new());
+
+ if (dsa_params->generator_len == 0 || dsa_params->prime_p_len == 0 ||
+ dsa_params->prime_q_len == 0 || dsa_params->generator == NULL ||
+ dsa_params->prime_p == NULL || dsa_params->prime_q == NULL) {
+ if (DSA_generate_parameters_ex(dsa.get(), dsa_params->key_size, NULL, 0, NULL, NULL,
+ NULL) != 1) {
+ logOpenSSLError("generate_dsa_keypair");
+ return -1;
+ }
+ } else {
+ dsa->g = BN_bin2bn(dsa_params->generator, dsa_params->generator_len, NULL);
+ if (dsa->g == NULL) {
+ logOpenSSLError("generate_dsa_keypair");
+ return -1;
+ }
+
+ dsa->p = BN_bin2bn(dsa_params->prime_p, dsa_params->prime_p_len, NULL);
+ if (dsa->p == NULL) {
+ logOpenSSLError("generate_dsa_keypair");
+ return -1;
+ }
+
+ dsa->q = BN_bin2bn(dsa_params->prime_q, dsa_params->prime_q_len, NULL);
+ if (dsa->q == NULL) {
+ logOpenSSLError("generate_dsa_keypair");
+ return -1;
+ }
+ }
+
+ if (DSA_generate_key(dsa.get()) != 1) {
+ logOpenSSLError("generate_dsa_keypair");
+ return -1;
+ }
+
+ if (EVP_PKEY_assign_DSA(pkey, dsa.get()) == 0) {
+ logOpenSSLError("generate_dsa_keypair");
+ return -1;
+ }
+ release_because_ownership_transferred(dsa);
+
+ return 0;
+}
+
+static int generate_ec_keypair(EVP_PKEY* pkey, const keymaster_ec_keygen_params_t* ec_params) {
+ Unique_EC_GROUP group;
+ switch (ec_params->field_size) {
+ case 224:
+ group.reset(EC_GROUP_new_by_curve_name(NID_secp224r1));
+ break;
+ case 256:
+ group.reset(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+ break;
+ case 384:
+ group.reset(EC_GROUP_new_by_curve_name(NID_secp384r1));
+ break;
+ case 521:
+ group.reset(EC_GROUP_new_by_curve_name(NID_secp521r1));
+ break;
+ default:
+ break;
+ }
+
+ if (group.get() == NULL) {
+ logOpenSSLError("generate_ec_keypair");
+ return -1;
+ }
+
+#if !defined(OPENSSL_IS_BORINGSSL)
+ EC_GROUP_set_point_conversion_form(group.get(), POINT_CONVERSION_UNCOMPRESSED);
+ EC_GROUP_set_asn1_flag(group.get(), OPENSSL_EC_NAMED_CURVE);
+#endif
+
+ /* initialize EC key */
+ Unique_EC_KEY eckey(EC_KEY_new());
+ if (eckey.get() == NULL) {
+ logOpenSSLError("generate_ec_keypair");
+ return -1;
+ }
+
+ if (EC_KEY_set_group(eckey.get(), group.get()) != 1) {
+ logOpenSSLError("generate_ec_keypair");
+ return -1;
+ }
+
+ if (EC_KEY_generate_key(eckey.get()) != 1 || EC_KEY_check_key(eckey.get()) < 0) {
+ logOpenSSLError("generate_ec_keypair");
+ return -1;
+ }
+
+ if (EVP_PKEY_assign_EC_KEY(pkey, eckey.get()) == 0) {
+ logOpenSSLError("generate_ec_keypair");
+ return -1;
+ }
+ release_because_ownership_transferred(eckey);
+
+ return 0;
+}
+
+static int generate_rsa_keypair(EVP_PKEY* pkey, const keymaster_rsa_keygen_params_t* rsa_params) {
+ Unique_BIGNUM bn(BN_new());
+ if (bn.get() == NULL) {
+ logOpenSSLError("generate_rsa_keypair");
+ return -1;
+ }
+
+ if (BN_set_word(bn.get(), rsa_params->public_exponent) == 0) {
+ logOpenSSLError("generate_rsa_keypair");
+ return -1;
+ }
+
+ /* initialize RSA */
+ Unique_RSA rsa(RSA_new());
+ if (rsa.get() == NULL) {
+ logOpenSSLError("generate_rsa_keypair");
+ return -1;
+ }
+
+ if (!RSA_generate_key_ex(rsa.get(), rsa_params->modulus_size, bn.get(), NULL) ||
+ RSA_check_key(rsa.get()) < 0) {
+ logOpenSSLError("generate_rsa_keypair");
+ return -1;
+ }
+
+ if (EVP_PKEY_assign_RSA(pkey, rsa.get()) == 0) {
+ logOpenSSLError("generate_rsa_keypair");
+ return -1;
+ }
+ release_because_ownership_transferred(rsa);
+
+ return 0;
+}
+
+__attribute__((visibility("default"))) int openssl_generate_keypair(
+ const keymaster0_device_t*, const keymaster_keypair_t key_type, const void* key_params,
+ uint8_t** keyBlob, size_t* keyBlobLength) {
+ Unique_EVP_PKEY pkey(EVP_PKEY_new());
+ if (pkey.get() == NULL) {
+ logOpenSSLError("openssl_generate_keypair");
+ return -1;
+ }
+
+ if (key_params == NULL) {
+ ALOGW("key_params == null");
+ return -1;
+ } else if (key_type == TYPE_DSA) {
+ const keymaster_dsa_keygen_params_t* dsa_params =
+ (const keymaster_dsa_keygen_params_t*)key_params;
+ generate_dsa_keypair(pkey.get(), dsa_params);
+ } else if (key_type == TYPE_EC) {
+ const keymaster_ec_keygen_params_t* ec_params =
+ (const keymaster_ec_keygen_params_t*)key_params;
+ generate_ec_keypair(pkey.get(), ec_params);
+ } else if (key_type == TYPE_RSA) {
+ const keymaster_rsa_keygen_params_t* rsa_params =
+ (const keymaster_rsa_keygen_params_t*)key_params;
+ generate_rsa_keypair(pkey.get(), rsa_params);
+ } else {
+ ALOGW("Unsupported key type %d", key_type);
+ return -1;
+ }
+
+ if (wrap_key(pkey.get(), EVP_PKEY_type(pkey->type), keyBlob, keyBlobLength)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+__attribute__((visibility("default"))) int openssl_import_keypair(const keymaster0_device_t*,
+ const uint8_t* key,
+ const size_t key_length,
+ uint8_t** key_blob,
+ size_t* key_blob_length) {
+ if (key == NULL) {
+ ALOGW("input key == NULL");
+ return -1;
+ } else if (key_blob == NULL || key_blob_length == NULL) {
+ ALOGW("output key blob or length == NULL");
+ return -1;
+ }
+
+ Unique_PKCS8_PRIV_KEY_INFO pkcs8(d2i_PKCS8_PRIV_KEY_INFO(NULL, &key, key_length));
+ if (pkcs8.get() == NULL) {
+ logOpenSSLError("openssl_import_keypair");
+ return -1;
+ }
+
+ /* assign to EVP */
+ Unique_EVP_PKEY pkey(EVP_PKCS82PKEY(pkcs8.get()));
+ if (pkey.get() == NULL) {
+ logOpenSSLError("openssl_import_keypair");
+ return -1;
+ }
+
+ if (wrap_key(pkey.get(), EVP_PKEY_type(pkey->type), key_blob, key_blob_length)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+__attribute__((visibility("default"))) int openssl_get_keypair_public(const keymaster0_device_t*,
+ const uint8_t* key_blob,
+ const size_t key_blob_length,
+ uint8_t** x509_data,
+ size_t* x509_data_length) {
+ if (x509_data == NULL || x509_data_length == NULL) {
+ ALOGW("output public key buffer == NULL");
+ return -1;
+ }
+
+ Unique_EVP_PKEY pkey(unwrap_key(key_blob, key_blob_length));
+ if (pkey.get() == NULL) {
+ return -1;
+ }
+
+ int len = i2d_PUBKEY(pkey.get(), NULL);
+ if (len <= 0) {
+ logOpenSSLError("openssl_get_keypair_public");
+ return -1;
+ }
+
+ UniquePtr<uint8_t, Malloc_Free> key(static_cast<uint8_t*>(malloc(len)));
+ if (key.get() == NULL) {
+ ALOGE("Could not allocate memory for public key data");
+ return -1;
+ }
+
+ unsigned char* tmp = reinterpret_cast<unsigned char*>(key.get());
+ if (i2d_PUBKEY(pkey.get(), &tmp) != len) {
+ logOpenSSLError("openssl_get_keypair_public");
+ return -1;
+ }
+
+ ALOGV("Length of x509 data is %d", len);
+ *x509_data_length = len;
+ *x509_data = key.release();
+
+ return 0;
+}
+
+static int sign_dsa(EVP_PKEY* pkey, keymaster_dsa_sign_params_t* sign_params, const uint8_t* data,
+ const size_t dataLength, uint8_t** signedData, size_t* signedDataLength) {
+ if (sign_params->digest_type != DIGEST_NONE) {
+ ALOGW("Cannot handle digest type %d", sign_params->digest_type);
+ return -1;
+ }
+
+ Unique_DSA dsa(EVP_PKEY_get1_DSA(pkey));
+ if (dsa.get() == NULL) {
+ logOpenSSLError("openssl_sign_dsa");
+ return -1;
+ }
+
+ unsigned int dsaSize = DSA_size(dsa.get());
+ UniquePtr<uint8_t, Malloc_Free> signedDataPtr(reinterpret_cast<uint8_t*>(malloc(dsaSize)));
+ if (signedDataPtr.get() == NULL) {
+ logOpenSSLError("openssl_sign_dsa");
+ return -1;
+ }
+
+ unsigned char* tmp = reinterpret_cast<unsigned char*>(signedDataPtr.get());
+ if (DSA_sign(0, data, dataLength, tmp, &dsaSize, dsa.get()) <= 0) {
+ logOpenSSLError("openssl_sign_dsa");
+ return -1;
+ }
+
+ *signedDataLength = dsaSize;
+ *signedData = signedDataPtr.release();
+
+ return 0;
+}
+
+static int sign_ec(EVP_PKEY* pkey, keymaster_ec_sign_params_t* sign_params, const uint8_t* data,
+ const size_t dataLength, uint8_t** signedData, size_t* signedDataLength) {
+ if (sign_params->digest_type != DIGEST_NONE) {
+ ALOGW("Cannot handle digest type %d", sign_params->digest_type);
+ return -1;
+ }
+
+ Unique_EC_KEY eckey(EVP_PKEY_get1_EC_KEY(pkey));
+ if (eckey.get() == NULL) {
+ logOpenSSLError("openssl_sign_ec");
+ return -1;
+ }
+
+ unsigned int ecdsaSize = ECDSA_size(eckey.get());
+ UniquePtr<uint8_t, Malloc_Free> signedDataPtr(reinterpret_cast<uint8_t*>(malloc(ecdsaSize)));
+ if (signedDataPtr.get() == NULL) {
+ logOpenSSLError("openssl_sign_ec");
+ return -1;
+ }
+
+ unsigned char* tmp = reinterpret_cast<unsigned char*>(signedDataPtr.get());
+ if (ECDSA_sign(0, data, dataLength, tmp, &ecdsaSize, eckey.get()) <= 0) {
+ logOpenSSLError("openssl_sign_ec");
+ return -1;
+ }
+
+ *signedDataLength = ecdsaSize;
+ *signedData = signedDataPtr.release();
+
+ return 0;
+}
+
+static int sign_rsa(EVP_PKEY* pkey, keymaster_rsa_sign_params_t* sign_params, const uint8_t* data,
+ const size_t dataLength, uint8_t** signedData, size_t* signedDataLength) {
+ if (sign_params->digest_type != DIGEST_NONE) {
+ ALOGW("Cannot handle digest type %d", sign_params->digest_type);
+ return -1;
+ } else if (sign_params->padding_type != PADDING_NONE) {
+ ALOGW("Cannot handle padding type %d", sign_params->padding_type);
+ return -1;
+ }
+
+ Unique_RSA rsa(EVP_PKEY_get1_RSA(pkey));
+ if (rsa.get() == NULL) {
+ logOpenSSLError("openssl_sign_rsa");
+ return -1;
+ }
+
+ UniquePtr<uint8_t, Malloc_Free> signedDataPtr(reinterpret_cast<uint8_t*>(malloc(dataLength)));
+ if (signedDataPtr.get() == NULL) {
+ logOpenSSLError("openssl_sign_rsa");
+ return -1;
+ }
+
+ unsigned char* tmp = reinterpret_cast<unsigned char*>(signedDataPtr.get());
+ if (RSA_private_encrypt(dataLength, data, tmp, rsa.get(), RSA_NO_PADDING) <= 0) {
+ logOpenSSLError("openssl_sign_rsa");
+ return -1;
+ }
+
+ *signedDataLength = dataLength;
+ *signedData = signedDataPtr.release();
+
+ return 0;
+}
+
+__attribute__((visibility("default"))) int openssl_sign_data(
+ const keymaster0_device_t*, const void* params, const uint8_t* keyBlob,
+ const size_t keyBlobLength, const uint8_t* data, const size_t dataLength, uint8_t** signedData,
+ size_t* signedDataLength) {
+ if (data == NULL) {
+ ALOGW("input data to sign == NULL");
+ return -1;
+ } else if (signedData == NULL || signedDataLength == NULL) {
+ ALOGW("output signature buffer == NULL");
+ return -1;
+ }
+
+ Unique_EVP_PKEY pkey(unwrap_key(keyBlob, keyBlobLength));
+ if (pkey.get() == NULL) {
+ return -1;
+ }
+
+ int type = EVP_PKEY_type(pkey->type);
+ if (type == EVP_PKEY_DSA) {
+ const keymaster_dsa_sign_params_t* sign_params =
+ reinterpret_cast<const keymaster_dsa_sign_params_t*>(params);
+ return sign_dsa(pkey.get(), const_cast<keymaster_dsa_sign_params_t*>(sign_params), data,
+ dataLength, signedData, signedDataLength);
+ } else if (type == EVP_PKEY_EC) {
+ const keymaster_ec_sign_params_t* sign_params =
+ reinterpret_cast<const keymaster_ec_sign_params_t*>(params);
+ return sign_ec(pkey.get(), const_cast<keymaster_ec_sign_params_t*>(sign_params), data,
+ dataLength, signedData, signedDataLength);
+ } else if (type == EVP_PKEY_RSA) {
+ const keymaster_rsa_sign_params_t* sign_params =
+ reinterpret_cast<const keymaster_rsa_sign_params_t*>(params);
+ return sign_rsa(pkey.get(), const_cast<keymaster_rsa_sign_params_t*>(sign_params), data,
+ dataLength, signedData, signedDataLength);
+ } else {
+ ALOGW("Unsupported key type");
+ return -1;
+ }
+}
+
+static int verify_dsa(EVP_PKEY* pkey, keymaster_dsa_sign_params_t* sign_params,
+ const uint8_t* signedData, const size_t signedDataLength,
+ const uint8_t* signature, const size_t signatureLength) {
+ if (sign_params->digest_type != DIGEST_NONE) {
+ ALOGW("Cannot handle digest type %d", sign_params->digest_type);
+ return -1;
+ }
+
+ Unique_DSA dsa(EVP_PKEY_get1_DSA(pkey));
+ if (dsa.get() == NULL) {
+ logOpenSSLError("openssl_verify_dsa");
+ return -1;
+ }
+
+ if (DSA_verify(0, signedData, signedDataLength, signature, signatureLength, dsa.get()) <= 0) {
+ logOpenSSLError("openssl_verify_dsa");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int verify_ec(EVP_PKEY* pkey, keymaster_ec_sign_params_t* sign_params,
+ const uint8_t* signedData, const size_t signedDataLength,
+ const uint8_t* signature, const size_t signatureLength) {
+ if (sign_params->digest_type != DIGEST_NONE) {
+ ALOGW("Cannot handle digest type %d", sign_params->digest_type);
+ return -1;
+ }
+
+ Unique_EC_KEY eckey(EVP_PKEY_get1_EC_KEY(pkey));
+ if (eckey.get() == NULL) {
+ logOpenSSLError("openssl_verify_ec");
+ return -1;
+ }
+
+ if (ECDSA_verify(0, signedData, signedDataLength, signature, signatureLength, eckey.get()) <=
+ 0) {
+ logOpenSSLError("openssl_verify_ec");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int verify_rsa(EVP_PKEY* pkey, keymaster_rsa_sign_params_t* sign_params,
+ const uint8_t* signedData, const size_t signedDataLength,
+ const uint8_t* signature, const size_t signatureLength) {
+ if (sign_params->digest_type != DIGEST_NONE) {
+ ALOGW("Cannot handle digest type %d", sign_params->digest_type);
+ return -1;
+ } else if (sign_params->padding_type != PADDING_NONE) {
+ ALOGW("Cannot handle padding type %d", sign_params->padding_type);
+ return -1;
+ } else if (signatureLength != signedDataLength) {
+ ALOGW("signed data length must be signature length");
+ return -1;
+ }
+
+ Unique_RSA rsa(EVP_PKEY_get1_RSA(pkey));
+ if (rsa.get() == NULL) {
+ logOpenSSLError("openssl_verify_data");
+ return -1;
+ }
+
+ UniquePtr<uint8_t[]> dataPtr(new uint8_t[signedDataLength]);
+ if (dataPtr.get() == NULL) {
+ logOpenSSLError("openssl_verify_data");
+ return -1;
+ }
+
+ unsigned char* tmp = reinterpret_cast<unsigned char*>(dataPtr.get());
+ if (!RSA_public_decrypt(signatureLength, signature, tmp, rsa.get(), RSA_NO_PADDING)) {
+ logOpenSSLError("openssl_verify_data");
+ return -1;
+ }
+
+ int result = 0;
+ for (size_t i = 0; i < signedDataLength; i++) {
+ result |= tmp[i] ^ signedData[i];
+ }
+
+ return result == 0 ? 0 : -1;
+}
+
+__attribute__((visibility("default"))) int openssl_verify_data(
+ const keymaster0_device_t*, const void* params, const uint8_t* keyBlob,
+ const size_t keyBlobLength, const uint8_t* signedData, const size_t signedDataLength,
+ const uint8_t* signature, const size_t signatureLength) {
+ if (signedData == NULL || signature == NULL) {
+ ALOGW("data or signature buffers == NULL");
+ return -1;
+ }
+
+ Unique_EVP_PKEY pkey(unwrap_key(keyBlob, keyBlobLength));
+ if (pkey.get() == NULL) {
+ return -1;
+ }
+
+ int type = EVP_PKEY_type(pkey->type);
+ if (type == EVP_PKEY_DSA) {
+ const keymaster_dsa_sign_params_t* sign_params =
+ reinterpret_cast<const keymaster_dsa_sign_params_t*>(params);
+ return verify_dsa(pkey.get(), const_cast<keymaster_dsa_sign_params_t*>(sign_params),
+ signedData, signedDataLength, signature, signatureLength);
+ } else if (type == EVP_PKEY_RSA) {
+ const keymaster_rsa_sign_params_t* sign_params =
+ reinterpret_cast<const keymaster_rsa_sign_params_t*>(params);
+ return verify_rsa(pkey.get(), const_cast<keymaster_rsa_sign_params_t*>(sign_params),
+ signedData, signedDataLength, signature, signatureLength);
+ } else if (type == EVP_PKEY_EC) {
+ const keymaster_ec_sign_params_t* sign_params =
+ reinterpret_cast<const keymaster_ec_sign_params_t*>(params);
+ return verify_ec(pkey.get(), const_cast<keymaster_ec_sign_params_t*>(sign_params),
+ signedData, signedDataLength, signature, signatureLength);
+ } else {
+ ALOGW("Unsupported key type %d", type);
+ return -1;
+ }
+}
+
+/* Close an opened OpenSSL instance */
+static int openssl_close(hw_device_t* dev) {
+ delete dev;
+ return 0;
+}
+
+/*
+ * Generic device handling
+ */
+__attribute__((visibility("default"))) int openssl_open(const hw_module_t* module, const char* name,
+ hw_device_t** device) {
+ if (strcmp(name, KEYSTORE_KEYMASTER) != 0)
+ return -EINVAL;
+
+ Unique_keymaster_device_t dev(new keymaster0_device_t);
+ if (dev.get() == NULL)
+ return -ENOMEM;
+
+ dev->common.tag = HARDWARE_DEVICE_TAG;
+ dev->common.version = 1;
+ dev->common.module = (struct hw_module_t*)module;
+ dev->common.close = openssl_close;
+
+ dev->flags = KEYMASTER_SOFTWARE_ONLY | KEYMASTER_BLOBS_ARE_STANDALONE | KEYMASTER_SUPPORTS_DSA |
+ KEYMASTER_SUPPORTS_EC;
+
+ dev->generate_keypair = openssl_generate_keypair;
+ dev->import_keypair = openssl_import_keypair;
+ dev->get_keypair_public = openssl_get_keypair_public;
+ dev->delete_keypair = NULL;
+ dev->delete_all = NULL;
+ dev->sign_data = openssl_sign_data;
+ dev->verify_data = openssl_verify_data;
+
+ ERR_load_crypto_strings();
+ ERR_load_BIO_strings();
+
+ *device = reinterpret_cast<hw_device_t*>(dev.release());
+
+ return 0;
+}
+
+static struct hw_module_methods_t keystore_module_methods = {
+ .open = openssl_open,
+};
+
+struct keystore_module softkeymaster_module __attribute__((visibility("default"))) = {
+ .common =
+ {
+ .tag = HARDWARE_MODULE_TAG,
+ .module_api_version = KEYMASTER_MODULE_API_VERSION_0_2,
+ .hal_api_version = HARDWARE_HAL_API_VERSION,
+ .id = KEYSTORE_HARDWARE_MODULE_ID,
+ .name = "Keymaster OpenSSL HAL",
+ .author = "The Android Open Source Project",
+ .methods = &keystore_module_methods,
+ .dso = 0,
+ .reserved = {},
+ },
+};
diff --git a/security/softkeymaster/module.cpp b/security/softkeymaster/module.cpp
new file mode 100644
index 0000000..0dcbadd
--- /dev/null
+++ b/security/softkeymaster/module.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2012 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 <keymaster/softkeymaster.h>
+
+#include <keystore/keystore.h>
+
+#include <hardware/hardware.h>
+#include <hardware/keymaster0.h>
+
+struct keystore_module HAL_MODULE_INFO_SYM __attribute__((visibility("default")))
+ = softkeymaster_module;