[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