diff --git a/keystore-engine/Android.mk b/keystore-engine/Android.mk index b975629d..e7cab53f 100644 --- a/keystore-engine/Android.mk +++ b/keystore-engine/Android.mk @@ -25,13 +25,15 @@ LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/ssl/engines LOCAL_SRC_FILES := \ eng_keystore.cpp \ keyhandle.cpp \ + ecdsa_meth.cpp \ dsa_meth.cpp \ rsa_meth.cpp LOCAL_CFLAGS := -fvisibility=hidden -Wall -Werror LOCAL_C_INCLUDES += \ - external/openssl/include + external/openssl/include \ + external/openssl LOCAL_SHARED_LIBRARIES += \ libcrypto \ diff --git a/keystore-engine/ecdsa_meth.cpp b/keystore-engine/ecdsa_meth.cpp new file mode 100644 index 00000000..7b673a2b --- /dev/null +++ b/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 + +//#define LOG_NDEBUG 0 +#define LOG_TAG "OpenSSL-keystore-ecdsa" +#include + +#include +#include + +#include +#include + +// 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 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(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 sm = defaultServiceManager(); + sp binder = sm->getService(String16("android.security.keystore")); + sp service = interface_cast(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(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(reinterpret_cast(&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 %llu", 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(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/keystore-engine/eng_keystore.cpp b/keystore-engine/eng_keystore.cpp index 9397e53b..6f5b01a6 100644 --- a/keystore-engine/eng_keystore.cpp +++ b/keystore-engine/eng_keystore.cpp @@ -66,11 +66,6 @@ int dsa_key_handle; */ static pthread_once_t key_handle_control = PTHREAD_ONCE_INIT; -/* - * Used for generic EVP_PKEY* handling (only for EC stuff currently) - */ -static EVP_PKEY_METHOD* keystore_pkey_ec_methods; - /** * 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, @@ -103,33 +98,6 @@ static void init_key_handle() { dsa_key_handle = DSA_get_ex_new_index(0, NULL, keyhandle_new, keyhandle_dup, keyhandle_free); } -static int pkey_setup(ENGINE *e, EVP_PKEY *pkey, const char *key_id) { - int ret = 1; - switch (EVP_PKEY_type(pkey->type)) { - case EVP_PKEY_EC: { - Unique_EC_KEY eckey(EVP_PKEY_get1_EC_KEY(pkey)); - void* oldData = EC_KEY_insert_key_method_data(eckey.get(), - reinterpret_cast(strdup(key_id)), ex_data_dup, ex_data_free, - ex_data_clear_free); - if (oldData != NULL) { - free(oldData); - } - } break; - default: - ALOGW("Unsupported key type during setup %d", EVP_PKEY_type(pkey->type)); - return 0; - } - - if (ret != 1) { - return ret; - } - - ENGINE_init(e); - pkey->engine = e; - - return 1; -} - static EVP_PKEY* keystore_loadkey(ENGINE* e, const char* key_id, UI_METHOD* ui_method, void* callback_data) { #if LOG_NDEBUG @@ -179,7 +147,7 @@ static EVP_PKEY* keystore_loadkey(ENGINE* e, const char* key_id, UI_METHOD* ui_m break; } case EVP_PKEY_EC: { - pkey_setup(e, pkey.get(), key_id); + ecdsa_pkey_setup(e, pkey.get(), key_id); break; } default: @@ -194,107 +162,11 @@ static const ENGINE_CMD_DEFN keystore_cmd_defns[] = { {0, NULL, NULL, 0} }; -static uint8_t* get_key_id(EVP_PKEY* pkey) { - switch (EVP_PKEY_type(pkey->type)) { - case EVP_PKEY_EC: { - Unique_EC_KEY eckey(EVP_PKEY_get1_EC_KEY(pkey)); - return reinterpret_cast(EC_KEY_get_key_method_data(eckey.get(), - ex_data_dup, ex_data_free, ex_data_clear_free)); - } break; - } - - return NULL; -} - -static int keystore_pkey_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, - const unsigned char *tbs, size_t tbs_len) { - EVP_PKEY* pkey = EVP_PKEY_CTX_get0_pkey(ctx); - - const uint8_t* key_id = get_key_id(pkey); - if (key_id == NULL) { - ALOGW("key_id is empty"); - return 0; - } - - sp sm = defaultServiceManager(); - sp binder = sm->getService(String16("android.security.keystore")); - sp service = interface_cast(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(key_id)), tbs, tbs_len, - &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(sig, reply, replyLen); - free(reply); - *siglen = replyLen; - - return 1; -} - -static int register_pkey_methods(EVP_PKEY_METHOD** meth, int nid) { - *meth = EVP_PKEY_meth_new(nid, 0); - if (*meth == NULL) { - ALOGE("Failure allocating PKEY methods for NID %d", nid); - return 0; - } - - const EVP_PKEY_METHOD* orig = EVP_PKEY_meth_find(nid); - EVP_PKEY_meth_copy(*meth, orig); - - EVP_PKEY_meth_set_sign(*meth, NULL, keystore_pkey_sign); - - return 1; -} - -static int keystore_nids[] = { - EVP_PKEY_EC, -}; - -static int keystore_pkey_meths(ENGINE*, EVP_PKEY_METHOD** meth, const int **nids, int nid) { - if (meth == NULL) { - *nids = keystore_nids; - return sizeof(keystore_nids) / sizeof(keystore_nids[0]); - } - - switch (nid) { - case EVP_PKEY_EC: - *meth = keystore_pkey_ec_methods; - return 1; - } - - *meth = NULL; - return 0; -} - static int keystore_engine_setup(ENGINE* e) { ALOGV("keystore_engine_setup"); - if (!register_pkey_methods(&keystore_pkey_ec_methods, EVP_PKEY_EC)) { - ALOGE("Could not set up keystore engine"); - return 0; - } - if (!ENGINE_set_id(e, kKeystoreEngineId) || !ENGINE_set_name(e, kKeystoreEngineDesc) - || !ENGINE_set_pkey_meths(e, keystore_pkey_meths) || !ENGINE_set_load_privkey_function(e, keystore_loadkey) || !ENGINE_set_load_pubkey_function(e, keystore_loadkey) || !ENGINE_set_flags(e, 0) @@ -313,6 +185,9 @@ static int keystore_engine_setup(ENGINE* e) { 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; diff --git a/keystore-engine/methods.h b/keystore-engine/methods.h index 8535ac99..fb85942d 100644 --- a/keystore-engine/methods.h +++ b/keystore-engine/methods.h @@ -61,6 +61,10 @@ 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*);