Attached are mods to engine_pkcs11 and libp11 to allow the use of ECDSA sign operations. A new p11_ec.c files is added. The code still has a lot of debuging statements, fprintf(stderr,.. and needs some polishing.
There is an issue with OpenSSL where ECDSA_METHOD is defined in an internal header file, ecs_locl.c, but ECDSA_METHOD needs to be overridden by libp11. This has been reported as OpenSSL bug #2459 on 2/23/2011. The code has been tested with OpenSSL-1.0.0a, using the OpenSSL engine and req commands to generate a cert request using the pub key from the card, and signing the request using the private key on the card. So if anyone is interested, these mods can be used by building libp11: export CPPFLAGS="-DBUILD_WITH_EC -I/some.path/openssl-1.0.0a/crypto/ecdsa/" ./configure The code will then find #include "ecs_locl.h"; When OpenSSL addresses #r2459 the modification can be updated. This meets my needs to allow creation of EC keys and certificate to test ECDH and key derivation. -- Douglas E. Engert <deeng...@anl.gov> Argonne National Laboratory 9700 South Cass Avenue Argonne, Illinois 60439 (630) 252-5444
Index: src/hw_pkcs11.c =================================================================== --- src/hw_pkcs11.c (revision 129) +++ src/hw_pkcs11.c (working copy) @@ -62,6 +62,8 @@ #include <config.h> #include <stdio.h> #include <string.h> +#include <openssl/opensslv.h> +#include <openssl/opensslconf.h> #include <openssl/crypto.h> #include <openssl/objects.h> #include <openssl/engine.h> @@ -188,6 +190,11 @@ #ifndef OPENSSL_NO_DH !ENGINE_set_DH(e, DH_get_default_method()) || #endif +#ifndef OPENSSL_NO_EC +#ifndef OPENSSL_NO_ECDSA + !ENGINE_set_ECDSA(e, PKCS11_get_ecdsa_method()) || +#endif +#endif !ENGINE_set_RAND(e, RAND_SSLeay()) || #if 0 !ENGINE_set_BN_mod_exp(e, BN_mod_exp) ||
Index: src/Makefile.mak =================================================================== --- src/Makefile.mak (revision 197) +++ src/Makefile.mak (working copy) @@ -10,7 +10,7 @@ TARGET = libp11.dll OBJECTS = libpkcs11.obj p11_attr.obj p11_cert.obj p11_err.obj \ - p11_key.obj p11_load.obj p11_misc.obj p11_rsa.obj p11_slot.obj p11_ops.obj + p11_key.obj p11_load.obj p11_misc.obj p11_rsa.obj p11_ec.obj p11_slot.obj p11_ops.obj all: $(TARGET) versioninfo.res Index: src/p11_ec.c =================================================================== --- src/p11_ec.c (revision 0) +++ src/p11_ec.c (revision 0) @@ -0,0 +1,244 @@ +/* libp11, a simple layer on to of PKCS#11 API + * Copyright (C) 2005 Olaf Kirch <o...@lst.de> + * Copyright (C) 2011 Douglas E. Engert <deeng...@anl.gov> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * This file implements the handling of EC keys stored on a + * PKCS11 token + */ + +#include <config.h> +#include <string.h> +#include <openssl/opensslv.h> +#include <openssl/opensslconf.h> + +#if defined(BUILD_WITH_EC) && !defined(OPENSSL_NO_EC) && !defined(OPENSSL_NO_ECDSA) +/* OpenSSL has ECDSA_METHOD defined in internal header file ecs_locl.h + * For now: + * CPPFLAGS="-DBUILD_WITH_EC -I/path.to.openssl-1.0.0a/crypto/ecdh" + * See OpenSSL bug report #2459 02/23/2011 + * When this is fixed, the BUILD_WITH_EC test can be removed + * + * TODO ECDH_METHOD is in ech_locl.h too! + */ + +#include "ecs_locl.h" +#include <openssl/evp.h> +#include <openssl/ec.h> +#include <openssl/ecdsa.h> +#include "libp11-int.h" + +static int pkcs11_get_ec_public(PKCS11_KEY *, EVP_PKEY *); +static int pkcs11_get_ec_private(PKCS11_KEY *, EVP_PKEY *); + + +/* + * Get EC key material and stach pointer in ex_data + * Note we get called twice, once for private key, and once for public + * We need to get the EC_PARAMS and EC_POINT into both, + * as lib11 dates from RSA only where all the pub key components + * were also part of the privite key. With EC the point + * is not in the privite key, and the params may or may not be. + * + */ +static int pkcs11_get_ec_private(PKCS11_KEY * key, EVP_PKEY * pk) +{ + CK_BBOOL sensitive, extractable; + EC_KEY * ec = NULL; + CK_RV ckrv; + int rv; + size_t ec_paramslen = 0; + CK_BYTE * ec_params = NULL; + size_t ec_pointlen = 0; + CK_BYTE * ec_point = NULL; + PKCS11_KEY * prkey; + PKCS11_KEY * pubkey; + + if (key->isPrivate) { /* Are we being called for the prive or pub key */ + prkey = key; + pubkey = PKCS11_find_key_from_key(key); + } else { + pubkey = key; + prkey = PKCS11_find_key_from_key(key); + } + +fprintf(stderr,"%s:%d %p %p\n",__FUNCTION__,__LINE__,key, pk); +fprintf(stderr,"%s:%d %p %p\n",__FUNCTION__,__LINE__, prkey, pubkey); + + if (!(ec = EVP_PKEY_get1_EC_KEY(pk))) { + ERR_clear_error(); /* the above flags an error */ + ec = EC_KEY_new(); +fprintf(stderr,"%s:%d \n",__FUNCTION__,__LINE__); + EVP_PKEY_set1_EC_KEY(pk, ec); + } + + if (prkey) { + if (key_getattr(prkey, CKA_SENSITIVE, &sensitive, sizeof(sensitive)) + || key_getattr(prkey, CKA_EXTRACTABLE, &extractable, sizeof(extractable))) { + EC_KEY_free(ec); +fprintf(stderr,"%s:%d \n",__FUNCTION__,__LINE__); + return -1; + } + + /* For Openssl req we need at least the + * EC_KEY_get0_group(ec_key)) to return the group. + * Even if it fails will continue as a sign only does not need + * need this if the pkcs11 or card can figure this out. + */ + + if (key_getattr_var(prkey, CKA_EC_PARAMS, NULL, &ec_paramslen) == CKR_OK && + ec_paramslen > 0) { + ec_params = malloc(ec_paramslen); + ckrv = key_getattr_var(prkey, CKA_EC_PARAMS, ec_params, &ec_paramslen); +fprintf(stderr,"%s:%d 0x%08x %p %d\n",__FUNCTION__,__LINE__,ckrv,ec_params, ec_paramslen); + if (ckrv == CKR_OK) { + const unsigned char * a = ec_params; + /* convert to OpenSSL parmas */ + d2i_ECParameters(&ec, &a, ec_paramslen); + } + } + } + + /* Now get the ec_point */ + + if (pubkey) { + if (key_getattr_var(pubkey, CKA_EC_POINT, NULL, &ec_pointlen) == CKR_OK && + ec_pointlen > 0) { + ec_point = malloc(ec_pointlen); + ckrv = key_getattr_var(pubkey, CKA_EC_POINT, ec_point, &ec_pointlen); +fprintf(stderr,"%s:%d 0x%08x %p %d\n",__FUNCTION__,__LINE__,ckrv,ec_point, ec_pointlen); + if (ckrv == CKR_OK) { + /* PKCS#11 returns ASN1 octstring*/ + const unsigned char * a; + /* TODO we have asn1 octet string, need to strip off 04 len */ + + a = ec_point + 2; + o2i_ECPublicKey(&ec, &a, ec_pointlen-2); +EC_KEY_print_fp(stderr, ec, 5); +fprintf(stderr,"%s:%d\n",__FUNCTION__,__LINE__); + } + } + } +fprintf(stderr,"%s:%d %p \n",__FUNCTION__,__LINE__,ec); + + /* If the key is not extractable, create a key object + * that will use the card's functions to sign & decrypt + */ + if (ec_point) + free(ec_point); + if (ec_params) + free(ec_params); + + if (sensitive || !extractable) { + ECDSA_set_ex_data(ec, 0, key); +fprintf(stderr,"%s:%d %p \n",__FUNCTION__,__LINE__,ec); + return 0; + } + + return -1; +} + +static int pkcs11_get_ec_public(PKCS11_KEY * key, EVP_PKEY * pk) +{ + /* TBD */ +fprintf(stderr,"%s:%d %p %p \n",__FUNCTION__,__LINE__,key,pk); + return pkcs11_get_ec_private(key, pk); +} + +/* TODO Looks like this is never called */ +static int pkcs11_ecdsa_do_sign_setup(EC_KEY *ec, BN_CTX *ctx_in, + BIGNUM **kinvp, BIGNUM **rp) { + +fprintf(stderr,"%s:%d \n",__FUNCTION__,__LINE__); + + if (*kinvp != NULL) + BN_clear_free(*kinvp); + *kinvp = BN_new(); + + if (*rp != NULL) + BN_clear_free(*rp); + *rp = BN_new(); + return 1; +} + +static ECDSA_SIG * pkcs11_ecdsa_do_sign(const unsigned char *dgst, int dlen, + const BIGNUM *inv, const BIGNUM *r, EC_KEY * ec) +{ + int type; + unsigned char sigret[512]; /* HACK for now */ + ECDSA_SIG * sig = NULL; + PKCS11_KEY * key = NULL; + int siglen; + int nLen = 48; /* HACK */; + int rv; + +fprintf(stderr,"%s:%d dgst %p:%d %p\n",__FUNCTION__,__LINE__,dgst,dlen,ec); + key = (PKCS11_KEY *) ECDSA_get_ex_data(ec, 0); + if (key == NULL) + return NULL; + + siglen = sizeof(sigret); + + rv = PKCS11_ecdsa_sign(dgst,dlen,sigret,&siglen, key); + nLen = siglen / 2; + if (rv > 0) { + sig = ECDSA_SIG_new(); + if (sig) { + BN_bin2bn(&sigret[0], nLen, sig->r); + BN_bin2bn(&sigret[nLen], nLen, sig->s); + } + } + return sig; +} + +/* + * Overload the default OpenSSL methods for ECDSA + */ +ECDSA_METHOD *PKCS11_get_ecdsa_method(void) +{ + static ECDSA_METHOD ops; + +fprintf(stderr,"%s:%d\n",__FUNCTION__,__LINE__); + if (!ops.ecdsa_do_sign) { + ops = *ECDSA_get_default_method(); + ops.ecdsa_do_sign = pkcs11_ecdsa_do_sign; + ops.ecdsa_sign_setup = pkcs11_ecdsa_do_sign_setup; + } + return &ops; +} + +PKCS11_KEY_ops pkcs11_ec_ops = { + EVP_PKEY_EC, + pkcs11_get_ec_public, + pkcs11_get_ec_private +}; + +#else +/* if not built with EC or OpenSSL does not support ECDSA + * add these so engine_pocs11 can be built now and not + * require further changes */ +void * PKCS11_get_ecdsa_method(void) +{ +#if !defined(OPENSSL_NO_EC) && !defined(OPENSSL_NO_ECDSA) + return ECDSA_get_default_method(); +#else +fprintf(stderr,"ECDSA support not built with libp11\n"); + return NULL; +#endif +} +#endif /*BUILD_WITH_EC OPENSSL_NO_EC */ Index: src/p11_key.c =================================================================== --- src/p11_key.c (revision 197) +++ src/p11_key.c (working copy) @@ -51,6 +51,7 @@ { PKCS11_TOKEN_private *priv = PRIVTOKEN(token); +fprintf(stderr,"%s %s:%d\n",__FILE__,__FUNCTION__,__LINE__); if (priv->nkeys < 0) { priv->nkeys = 0; if (pkcs11_find_keys(token, CKO_PRIVATE_KEY)) { @@ -78,6 +79,7 @@ PKCS11_KEY *key; unsigned int n, count; +fprintf(stderr,"%s %s:%d\n",__FILE__,__FUNCTION__,__LINE__); cpriv = PRIVCERT(cert); if (PKCS11_enumerate_keys(CERT2TOKEN(cert), &key, &count)) return NULL; @@ -91,6 +93,35 @@ } /* + * Find key matching a key of the other type pub vs priv + */ +PKCS11_KEY *PKCS11_find_key_from_key(PKCS11_KEY * keyin) +{ + PKCS11_TOKEN_private *tpriv; + PKCS11_KEY_private *kinpriv; + PKCS11_KEY_private *kpriv; + PKCS11_KEY *key; + int isprivate; + unsigned int n, count; + +fprintf(stderr,"%s %s:%d\n",__FILE__,__FUNCTION__,__LINE__); + kinpriv = PRIVKEY(keyin); + tpriv = KEY2TOKEN(keyin); + PKCS11_enumerate_keys(KEY2TOKEN(keyin), &key, &count); + /* We want to use all the keys, the above only returns count for private */ + count = tpriv->nkeys; + if (count < 2) /* must be at least two key to have a match */ + return; + for (n = 0; n < count; n++, key++) { + kpriv = PRIVKEY(key); + if (keyin->isPrivate != key->isPrivate + && kinpriv->id_len == kpriv->id_len + && !memcmp(kinpriv->id, kpriv->id, kinpriv->id_len)) + return key; + } + return NULL; +} +/* * Store a private key on the token */ int PKCS11_store_private_key(PKCS11_TOKEN * token, EVP_PKEY * pk, char *label, unsigned char *id, size_t id_len) @@ -168,6 +199,7 @@ { PKCS11_KEY_private *priv = PRIVKEY(key); +fprintf(stderr,"%s %s:%d\n",__FILE__,__FUNCTION__,__LINE__); if (key->evp_key == NULL) { EVP_PKEY *pk = EVP_PKEY_new(); if (pk == NULL) @@ -185,6 +217,7 @@ EVP_PKEY *PKCS11_get_public_key(PKCS11_KEY * key) { +fprintf(stderr,"%s %s:%d\n",__FILE__,__FUNCTION__,__LINE__); return PKCS11_get_private_key(key); } @@ -199,6 +232,7 @@ CK_SESSION_HANDLE session; int rv, res = -1; +fprintf(stderr,"%s %s:%d\n",__FILE__,__FUNCTION__,__LINE__); /* Make sure we have a session */ if (!PRIVSLOT(slot)->haveSession && PKCS11_open_session(slot, 0)) return -1; @@ -225,6 +259,7 @@ CK_ULONG count; int rv; +fprintf(stderr,"%s %s:%d\n",__FILE__,__FUNCTION__,__LINE__); /* Get the next matching object */ rv = CRYPTOKI_call(ctx, C_FindObjects(session, &obj, 1, &count)); CRYPTOKI_checkerr(PKCS11_F_PKCS11_ENUM_KEYS, rv); @@ -251,6 +286,7 @@ PKCS11_KEY_ops *ops; size_t size; +fprintf(stderr,"%s %s:%d\n",__FILE__,__FUNCTION__,__LINE__); size = sizeof(key_type); if (pkcs11_getattr_var(token, obj, CKA_KEY_TYPE, &key_type, &size)) return -1; @@ -259,6 +295,9 @@ case CKK_RSA: ops = &pkcs11_rsa_ops; break; + case CKK_EC: + ops = &pkcs11_ec_ops; + break; default: /* Ignore any keys we don't understand */ return 0; @@ -307,6 +346,7 @@ { PKCS11_TOKEN_private *priv = PRIVTOKEN(token); +fprintf(stderr,"%s %s:%d\n",__FILE__,__FUNCTION__,__LINE__); while (priv->nkeys > 0) { PKCS11_KEY *key = &priv->keys[--(priv->nkeys)]; Index: src/libp11.exports =================================================================== --- src/libp11.exports (revision 197) +++ src/libp11.exports (working copy) @@ -34,4 +34,5 @@ PKCS11_seed_random PKCS11_generate_random PKCS11_get_rsa_method +PKCS11_get_ecdsa_method ERR_load_PKCS11_strings Index: src/p11_ops.c =================================================================== --- src/p11_ops.c (revision 197) +++ src/p11_ops.c (working copy) @@ -24,6 +24,49 @@ #include <string.h> #include "libp11-int.h" +#ifdef BUILD_WITH_EC +int +PKCS11_ecdsa_sign(const unsigned char *m, unsigned int m_len, + unsigned char *sigret, unsigned int *siglen, const PKCS11_KEY * key) +{ +/* signature size is the issue, will assume caller has a big buffer ! */ +/* No padding or other stuff needed, we can cal PKCS11 from here */ + int rv; + PKCS11_KEY_private *priv; + PKCS11_SLOT *slot; + PKCS11_CTX *ctx; + CK_SESSION_HANDLE session; + CK_MECHANISM mechanism; + CK_ULONG ck_sigsize; + + ctx = KEY2CTX(key); + priv = PRIVKEY(key); + slot = TOKEN2SLOT(priv->parent); + session = PRIVSLOT(slot)->session; + + ck_sigsize = *siglen; + + memset(&mechanism, 0, sizeof(mechanism)); + mechanism.mechanism = CKM_ECDSA; + + if((rv = CRYPTOKI_call(ctx, C_SignInit + (session, &mechanism, priv->object))) == 0) { + rv = CRYPTOKI_call(ctx, C_Sign + (session, (CK_BYTE *) m, m_len, + sigret, &ck_sigsize)); + } + + if (rv) { + PKCS11err(PKCS11_F_PKCS11_EC_KEY_SIGN, pkcs11_map_err(rv)); + return -1; + } + *siglen = ck_sigsize; + + return ck_sigsize; +} +#endif /* BUILD_WITH_EC */ + +/* Following used for RSA */ int PKCS11_sign(int type, const unsigned char *m, unsigned int m_len, unsigned char *sigret, unsigned int *siglen, const PKCS11_KEY * key) Index: src/libp11-int.h =================================================================== --- src/libp11-int.h (revision 197) +++ src/libp11-int.h (working copy) @@ -143,6 +143,9 @@ #define key_getattr_bn(key, t, bn) \ pkcs11_getattr_bn(KEY2TOKEN((key)), PRIVKEY((key))->object, (t), (bn)) +#define key_getattr_var(key, t, p, s) \ + pkcs11_getattr_var(KEY2TOKEN((key)), PRIVKEY((key))->object, (t), (p), (s)) + typedef int (*pkcs11_i2d_fn) (void *, unsigned char **); extern void pkcs11_addattr(CK_ATTRIBUTE_PTR, int, const void *, size_t); extern void pkcs11_addattr_int(CK_ATTRIBUTE_PTR, int, unsigned long); @@ -155,5 +158,6 @@ extern void *memdup(const void *, size_t); extern PKCS11_KEY_ops pkcs11_rsa_ops; +extern PKCS11_KEY_ops pkcs11_ec_ops; #endif Index: src/Makefile.am =================================================================== --- src/Makefile.am (revision 197) +++ src/Makefile.am (working copy) @@ -9,7 +9,7 @@ pkgconfig_DATA = libp11.pc libp11_la_SOURCES = libpkcs11.c p11_attr.c p11_cert.c p11_err.c p11_key.c \ - p11_load.c p11_misc.c p11_ops.c p11_rsa.c p11_slot.c \ + p11_load.c p11_misc.c p11_ops.c p11_rsa.c p11_ec.c p11_slot.c \ libp11.exports if WIN32 libp11_la_SOURCES += versioninfo.rc Index: src/libp11.h =================================================================== --- src/libp11.h (revision 197) +++ src/libp11.h (working copy) @@ -258,6 +258,9 @@ /* Find the corresponding key (if any) */ extern PKCS11_KEY *PKCS11_find_key(PKCS11_CERT *); +/* Find the corresponding key (if any) pub <-> priv base on label */ +extern PKCS11_KEY *PKCS11_find_key_from_key(PKCS11_KEY *); + /* Get a list of all certificates associated with this token */ extern int PKCS11_enumerate_certs(PKCS11_TOKEN *, PKCS11_CERT **, unsigned int *); @@ -413,6 +416,8 @@ #define PKCS11_F_PKCS11_GENERATE_RANDOM 21 #define PKCS11_F_PKCS11_CHANGE_PIN 22 #define PKCS11_F_PKCS11_GETATTR 40 +#define PKCS11_F_PKCS11_EC_KEY_SIGN 41 +#define PKCS11_F_PKCS11_EC_KEY_VERIFY 42 #define PKCS11_ERR_BASE 1024 #define PKCS11_LOAD_MODULE_ERROR (PKCS11_ERR_BASE+1)
_______________________________________________ opensc-devel mailing list opensc-devel@lists.opensc-project.org http://www.opensc-project.org/mailman/listinfo/opensc-devel