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;