[pkg-opensc-commit] [libp11] 178/239: ECDSA support
Eric Dorland
eric at moszumanska.debian.org
Sat Oct 17 06:21:31 UTC 2015
This is an automated email from the git hooks/post-receive script.
eric pushed a commit to branch master
in repository libp11.
commit ff1b48cc803e2ea76a82a62c7172e3dbe6123895
Author: Doug Engert <deengert at anl.gov>
Date: Thu Sep 19 13:51:33 2013 -0500
ECDSA support
Support for ECDSA is added for use with OpenSSL >= 1.0.2.
OpenSSL had an outstanding bug report, #2459, that requests
the structure ecdsa_method be exposed. An engine needs to create
a modified version of the structure, which has pointers into
functions within the engine. Code as suggested in #2459 was
added in OpenSSL master and in OpenSSL-1.0.2-beta.
Engines using RSA do not have this problem, because the
rsa_method_st is exposed in rsa.h This allows an engine such
as the combination of libp11 and engine_pkcs11 to compile in
a static version of the rsa_method_st.
These libp11 modifications are designed to allow building
libp11 using either the ESDA_METHOD_new or (for backward
compatibility with older OpenSSL versions) use the internal OpenSSL
crypto/ecdsa/ecs_locl.h
By default the ECDSA_METHOD_new will be used if present.
To build using the ecs_locl.h, one must have access to the
OpenSSL source and add to the libp11 build process,
-DBUILD_WITH_ECS_LOCL_H -I/<path.to.OpenSSL.source>/crypto/edcsa
(Note: that using an internal header file may require libp11
to be rebuilt to match the specific version of OpenSSL being
used, and is not recommened.)
---
src/Makefile.am | 2 +-
src/Makefile.mak | 2 +-
src/libp11-int.h | 4 +
src/libp11.exports | 2 +
src/libp11.h | 7 ++
src/p11_ec.c | 318 +++++++++++++++++++++++++++++++++++++++++++++++++++++
src/p11_key.c | 31 ++++++
src/p11_ops.c | 41 +++++++
8 files changed, 405 insertions(+), 2 deletions(-)
diff --git a/src/Makefile.am b/src/Makefile.am
index 0910f44..1b35442 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -9,7 +9,7 @@ lib_LTLIBRARIES = libp11.la
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
diff --git a/src/Makefile.mak b/src/Makefile.mak
index fa0809c..5025fbc 100644
--- a/src/Makefile.mak
+++ b/src/Makefile.mak
@@ -7,7 +7,7 @@ LINKFLAGS = /DEBUG /NOLOGO /INCREMENTAL:NO /MACHINE:IX86
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
diff --git a/src/libp11-int.h b/src/libp11-int.h
index be3965f..1ebf01e 100644
--- a/src/libp11-int.h
+++ b/src/libp11-int.h
@@ -143,6 +143,9 @@ extern int pkcs11_getattr_bn(PKCS11_TOKEN *, CK_OBJECT_HANDLE,
#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 pkcs11_zap_attrs(CK_ATTRIBUTE_PTR, unsigned int);
extern void *memdup(const void *, size_t);
extern PKCS11_KEY_ops pkcs11_rsa_ops;
+extern PKCS11_KEY_ops pkcs11_ec_ops;
#endif
diff --git a/src/libp11.exports b/src/libp11.exports
index aecbdba..3ed185e 100644
--- a/src/libp11.exports
+++ b/src/libp11.exports
@@ -34,4 +34,6 @@ PKCS11_verify
PKCS11_seed_random
PKCS11_generate_random
PKCS11_get_rsa_method
+PKCS11_get_ecdsa_method
+PKCS11_ecdsa_method_free
ERR_load_PKCS11_strings
diff --git a/src/libp11.h b/src/libp11.h
index 89604ff..96e65ce 100644
--- a/src/libp11.h
+++ b/src/libp11.h
@@ -258,6 +258,9 @@ extern PKCS11_CERT *PKCS11_find_certificate(PKCS11_KEY *);
/* Find the corresponding key (if any) */
extern PKCS11_KEY *PKCS11_find_key(PKCS11_CERT *);
+/* Find the corresponding key (if any) pub <-> priv base on ID */
+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 *);
@@ -378,6 +381,8 @@ extern int PKCS11_generate_random(PKCS11_SLOT *, unsigned char *r, unsigned int
/* using with openssl method mechanism */
RSA_METHOD *PKCS11_get_rsa_method(void);
+ECDSA_METHOD *PKCS11_get_ecdsa_method(void);
+void PKCS11_ecdsa_method_free(void);
/**
* Load PKCS11 error strings
@@ -413,6 +418,8 @@ extern void ERR_load_PKCS11_strings(void);
#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)
diff --git a/src/p11_ec.c b/src/p11_ec.c
new file mode 100644
index 0000000..05b50dc
--- /dev/null
+++ b/src/p11_ec.c
@@ -0,0 +1,318 @@
+/* libp11, a simple layer on to of PKCS#11 API
+ * Copyright (C) 2005 Olaf Kirch <okir at lst.de>
+ * Copyright (C) 2011, 2013 Douglas E. Engert <deengert at anl.gov>
+ * Copyright (C) 2014 Douglas E. Engert <deengert at gmail.com>
+
+ *
+ * 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>
+
+#define LIBP11_BUILD_WITHOUT_ECDSA
+#if !defined(OPENSSL_NO_EC) && !defined(OPENSSL_NO_ECDSA)
+#undef LIBP11_BUILD_WITHOUT_ECDSA
+
+#include <openssl/evp.h>
+#include <openssl/ec.h>
+#include <openssl/ecdsa.h>
+
+/* To build this mode,
+ * OpenSSL has ECDSA_METHOD defined in internal header file ecs_locl.h
+ * Until this is resolved use something like:
+ * CPPFLAGS="-DBUILD_WITH_ECS_LOCL_H -I/path.to.openssl-1.0.1e/src/crypto/ecdsa"
+ * See OpenSSL bug report #2459 02/23/2011
+ * Once OpenSSL addresses the issues this code will be changed.
+ *
+ * OpenSSL mods were submitted 09/2013 that will set ECDSA_F_ECDSA_METHOD_NEW
+ * and define the ECDSA_METHOD_new and friends functions
+ * These mods are in OpenSSL-1.0.2-beta
+ * We will try both methods.
+ */
+
+#if defined(ECDSA_F_ECDSA_METHOD_NEW) && defined(BUILD_WITH_ECS_LOCL_H)
+ #warning "Both BUILD_WITH_ECS_LOCL_H and ECDSA_F_ECDSA_METHOD_NEW defined"
+ #warning "Consider not using BUILD_WITH_ECS_LOCL_H"
+#endif
+
+#if defined(BUILD_WITH_ECS_LOCL_H)
+ #warning "Consider not using BUILD_WITH_ECS_LOCL_H"
+ #warning "newer version of OpenSSL >-1.0.2 does not need BUILD_WITH_ECS_LOCL_H"
+ #include "ecs_locl.h"
+
+ #if !defined(HEADER_ECS_LOCL_H)
+ #warning "Unable to find OpenSSL src/crypto/ecs_locl.h"
+ #warning "add to CPPFLAGS: -I/path/to/source/openssl-n.n.n/src/crypto/ecdsa"
+ #warning "or copy ecs_locl.h or create symlink to it"
+ #if defined(ECDSA_F_ECDSA_METHOD_NEW)
+ #warning "Will build instead using ECDSA_F_ECDSA_METHOD_NEW"
+ #else
+ #error " Unable to build with ECDSA support"
+ #endif
+ #else
+ #define LIBP11_BUILD_WITH_ECS_LOCL_H
+ #endif
+#else
+ #if !defined(ECDSA_F_ECDSA_METHOD_NEW)
+ #define LIBP11_BUILD_WITHOUT_ECDSA
+ #endif
+#endif
+
+#endif /* OpenSSL EC tests and version */
+
+#if !defined(LIBP11_BUILD_WITHOUT_ECDSA)
+
+#include "libp11-int.h"
+
+static int pkcs11_get_ec_public(PKCS11_KEY *, EVP_PKEY *);
+static int pkcs11_get_ec_private(PKCS11_KEY *, EVP_PKEY *);
+
+static ECDSA_METHOD *ops = NULL;
+
+/*
+ * 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;
+ ASN1_OCTET_STRING *os=NULL;
+
+ 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);
+ }
+
+
+ if (!(ec = EVP_PKEY_get1_EC_KEY(pk))) {
+ ERR_clear_error(); /* the above flags an error */
+ ec = EC_KEY_new();
+ 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);
+ 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 = OPENSSL_malloc(ec_paramslen);
+ if (ec_params) {
+ ckrv = key_getattr_var(prkey, CKA_EC_PARAMS, 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 = OPENSSL_malloc(ec_pointlen);
+ if (ec_point) {
+ ckrv = key_getattr_var(pubkey, CKA_EC_POINT, ec_point, &ec_pointlen);
+ if (ckrv == CKR_OK) {
+ /* PKCS#11 returns ASN1 octstring*/
+ const unsigned char * a;
+ /* we have asn1 octet string, need to strip off 04 len */
+
+ a = ec_point;
+ os = d2i_ASN1_OCTET_STRING(NULL, &a, ec_pointlen);
+ if (os) {
+ a = os->data;
+ o2i_ECPublicKey(&ec, &a, os->length);
+ }
+/* EC_KEY_print_fp(stderr, ec, 5); */
+ }
+ }
+ }
+ }
+
+ /* If the key is not extractable, create a key object
+ * that will use the card's functions to sign & decrypt
+ */
+ if (os)
+ M_ASN1_OCTET_STRING_free(os);
+ if (ec_point)
+ OPENSSL_free(ec_point);
+ if (ec_params)
+ OPENSSL_free(ec_params);
+
+ if (sensitive || !extractable) {
+ ECDSA_set_ex_data(ec, 0, key);
+ return 0;
+ }
+
+ return -1;
+}
+
+static int pkcs11_get_ec_public(PKCS11_KEY * key, EVP_PKEY * pk)
+{
+ /* TBD */
+ return pkcs11_get_ec_private(key, pk);
+}
+
+/* TODO Looks like this is never called */
+static int pkcs11_ecdsa_sign_setup(EC_KEY *ec, BN_CTX *ctx_in,
+ BIGNUM **kinvp, BIGNUM **rp) {
+
+
+ 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;
+
+ 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
+ * If OpenSSL supports ECDSA_METHOD_new we will use it.
+ * Otherwise we expect the ecs_locl.h to be present.
+ */
+#if !defined(LIBP11_BUILD_WITH_ECS_LOCL_H)
+
+/* New way to allocate a ECDSA_METOD... */
+ECDSA_METHOD *PKCS11_get_ecdsa_method(void)
+{
+
+ if (ops == NULL) {
+ ops = ECDSA_METHOD_new(NULL);
+ ECDSA_METHOD_set_sign(ops, pkcs11_ecdsa_do_sign);
+ ECDSA_METHOD_set_sign_setup(ops, pkcs11_ecdsa_sign_setup);
+ }
+ return ops;
+}
+
+void PKCS11_ecdsa_method_free(void)
+{
+ if (ops) {
+ ECDSA_METHOD_free(ops);
+ ops = NULL;
+ }
+}
+
+#else
+
+/* old way using ecs_locl.h */
+ECDSA_METHOD *PKCS11_get_ecdsa_method(void)
+{
+ static struct ecdsa_method sops;
+
+ if (!sops.ecdsa_do_sign) {
+/* question if compiler is copying each member of struct or not */
+ sops = *ECDSA_get_default_method();
+ sops.ecdsa_do_sign = pkcs11_ecdsa_do_sign;
+ sops.ecdsa_sign_setup = pkcs11_ecdsa_sign_setup;
+ }
+ return &sops;
+}
+
+void PKCS11_ecdsa_method_free(void)
+{
+ /* in old method it is static */
+}
+#endif
+
+PKCS11_KEY_ops pkcs11_ec_ops = {
+ EVP_PKEY_EC,
+ pkcs11_get_ec_public,
+ pkcs11_get_ec_private
+};
+
+#else /* LIBP11_BUILD_WITHOUT_ECDSA */
+/* if not built with EC or OpenSSL does not support ECDSA
+ * add these routines so engine_pkcs11 can be built now and not
+ * require further changes */
+#warning "ECDSA support not built with libp11"
+void * PKCS11_get_ecdsa_method(void)
+{
+ return NULL;
+}
+void PKCS11_ecdsa_method_free(void)
+{
+ /* no op, as it is static in old code. */
+}
+#endif /* OPENSSL_NO_EC */
diff --git a/src/p11_key.c b/src/p11_key.c
index 47ed71b..0efd862 100644
--- a/src/p11_key.c
+++ b/src/p11_key.c
@@ -91,6 +91,34 @@ PKCS11_KEY *PKCS11_find_key(PKCS11_CERT *cert)
}
/*
+ * 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;
+
+ kinpriv = PRIVKEY(keyin);
+ tpriv = PRIVTOKEN(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)
@@ -262,6 +290,9 @@ static int pkcs11_init_key(PKCS11_CTX * ctx, PKCS11_TOKEN * token,
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;
diff --git a/src/p11_ops.c b/src/p11_ops.c
index 821094e..8d9cbfc 100644
--- a/src/p11_ops.c
+++ b/src/p11_ops.c
@@ -25,6 +25,47 @@
#include "libp11-int.h"
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;
+}
+
+/* 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)
{
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-opensc/libp11.git
More information about the pkg-opensc-commit
mailing list