Blob Blame History Raw
diff -up openssh-7.6p1/ssh-pkcs11-client.c.pkcs11-ecdsa openssh-7.6p1/ssh-pkcs11-client.c
--- openssh-7.6p1/ssh-pkcs11-client.c.pkcs11-ecdsa	2018-02-16 13:25:59.426469253 +0100
+++ openssh-7.6p1/ssh-pkcs11-client.c	2018-02-16 13:25:59.428469265 +0100
@@ -31,6 +31,15 @@
 #include <errno.h>
 
 #include <openssl/rsa.h>
+#ifdef OPENSSL_HAS_ECC
+#include <openssl/ecdsa.h>
+#if ((defined(LIBRESSL_VERSION_NUMBER) && \
+	(LIBRESSL_VERSION_NUMBER >= 0x20010002L))) || \
+	(defined(ECDSA_F_ECDSA_METHOD_NEW)) || \
+	(OPENSSL_VERSION_NUMBER >= 0x00010100L)
+#define ENABLE_PKCS11_ECDSA 1
+#endif
+#endif
 
 #include "pathnames.h"
 #include "xmalloc.h"
@@ -139,9 +147,9 @@ pkcs11_rsa_private_encrypt(int flen, con
 	return (ret);
 }
 
-/* redirect the private key encrypt operation to the ssh-pkcs11-helper */
+/* redirect the RSA private key encrypt operation to the ssh-pkcs11-helper */
 static int
-wrap_key(RSA *rsa)
+wrap_rsa_key(RSA *rsa)
 {
 	static RSA_METHOD helper_rsa;
 
@@ -152,6 +160,81 @@ wrap_key(RSA *rsa)
 	return (0);
 }
 
+#ifdef ENABLE_PKCS11_ECDSA
+static ECDSA_SIG *
+pkcs11_ecdsa_private_sign(const unsigned char *from, int flen,
+    const BIGNUM *inv, const BIGNUM *rp, EC_KEY * ecdsa)
+{
+	Key key;
+	u_char *blob, *signature = NULL;
+	u_int blen, slen = 0;
+	Buffer msg;
+	ECDSA_SIG *ret = NULL;
+	BIGNUM *r = NULL, *s = NULL;
+
+	key.type = KEY_ECDSA;
+	key.ecdsa = ecdsa;
+	key.ecdsa_nid = sshkey_ecdsa_key_to_nid(ecdsa);
+	if (key_to_blob(&key, &blob, &blen) == 0)
+		return NULL;
+	buffer_init(&msg);
+	buffer_put_char(&msg, SSH2_AGENTC_SIGN_REQUEST);
+	buffer_put_string(&msg, blob, blen);
+	buffer_put_string(&msg, from, flen);
+	buffer_put_int(&msg, 0);
+	free(blob);
+	send_msg(&msg);
+	buffer_clear(&msg);
+
+	if (recv_msg(&msg) == SSH2_AGENT_SIGN_RESPONSE) {
+		signature = buffer_get_string(&msg, &slen);
+		if (slen <= (u_int)ECDSA_size(ecdsa)) {
+			int nlen = slen / 2;
+			ret = ECDSA_SIG_new();
+			r = BN_new();
+			s = BN_new();
+			BN_bin2bn(&signature[0], nlen, r);
+			BN_bin2bn(&signature[nlen], nlen, s);
+			ECDSA_SIG_set0(ret, r, s);
+		}
+		free(signature);
+	}
+	buffer_free(&msg);
+	return (ret);
+}
+
+/* redirect the ECDSA private key encrypt operation to the ssh-pkcs11-helper */
+static int
+wrap_ecdsa_key(EC_KEY *ecdsa) {
+#if (OPENSSL_VERSION_NUMBER >= 0x00010100L)
+	static EC_KEY_METHOD *helper_ecdsa = NULL;
+	if (helper_ecdsa == NULL) {
+		const EC_KEY_METHOD *def = EC_KEY_get_default_method();
+		helper_ecdsa = EC_KEY_METHOD_new(def);
+		EC_KEY_METHOD_set_sign(helper_ecdsa, NULL, NULL, pkcs11_ecdsa_private_sign);
+	}
+	EC_KEY_set_method(ecdsa, helper_ecdsa);
+#else
+	static ECDSA_METHOD *helper_ecdsa = NULL;
+	if(helper_ecdsa == NULL) {
+		const ECDSA_METHOD *def = ECDSA_get_default_method();
+# ifdef ECDSA_F_ECDSA_METHOD_NEW
+		helper_ecdsa = ECDSA_METHOD_new((ECDSA_METHOD *)def);
+		ECDSA_METHOD_set_name(helper_ecdsa, "ssh-pkcs11-helper-ecdsa");
+		ECDSA_METHOD_set_sign(helper_ecdsa, pkcs11_ecdsa_private_sign);
+# else
+		helper_ecdsa = xcalloc(1, sizeof(*helper_ecdsa));
+		memcpy(helper_ecdsa, def, sizeof(*helper_ecdsa));
+		helper_ecdsa->name = "ssh-pkcs11-helper-ecdsa";
+		helper_ecdsa->ecdsa_do_sign = pkcs11_ecdsa_private_sign;
+# endif
+	}
+	ECDSA_set_method(ecdsa, helper_ecdsa);
+#endif
+	return (0);
+}
+#endif
+
 static int
 pkcs11_start_helper(void)
 {
@@ -212,7 +281,15 @@ pkcs11_add_provider(char *name, char *pi
 			blob = buffer_get_string(&msg, &blen);
 			free(buffer_get_string(&msg, NULL));
 			k = key_from_blob(blob, blen);
-			wrap_key(k->rsa);
+			if(k->type == KEY_RSA) {
+				 wrap_rsa_key(k->rsa);
+#ifdef ENABLE_PKCS11_ECDSA
+			} else if(k->type == KEY_ECDSA) {
+				 wrap_ecdsa_key(k->ecdsa);
+#endif /* ENABLE_PKCS11_ECDSA */
+			} else {
+				/* Unsupported type */
+			}
 			(*keysp)[i] = k;
 			free(blob);
 		}
diff -up openssh-7.6p1/ssh-pkcs11.c.pkcs11-ecdsa openssh-7.6p1/ssh-pkcs11.c
--- openssh-7.6p1/ssh-pkcs11.c.pkcs11-ecdsa	2018-02-16 13:25:59.427469259 +0100
+++ openssh-7.6p1/ssh-pkcs11.c	2018-02-16 13:44:51.270554797 +0100
@@ -32,6 +32,16 @@
 #include "openbsd-compat/sys-queue.h"
 
 #include <openssl/x509.h>
+#include <openssl/rsa.h>
+#ifdef OPENSSL_HAS_ECC
+#include <openssl/ecdsa.h>
+#if ((defined(LIBRESSL_VERSION_NUMBER) && \
+	(LIBRESSL_VERSION_NUMBER >= 0x20010002L))) || \
+	(defined(ECDSA_F_ECDSA_METHOD_NEW)) || \
+	(OPENSSL_VERSION_NUMBER >= 0x00010100L)
+#define ENABLE_PKCS11_ECDSA 1
+#endif
+#endif
 
 #define CRYPTOKI_COMPAT
 #include "pkcs11.h"
@@ -67,6 +76,7 @@ TAILQ_HEAD(, pkcs11_provider) pkcs11_pro
 struct pkcs11_key {
 	struct pkcs11_provider	*provider;
 	CK_ULONG		slotidx;
+	CK_ULONG		key_type;
 	int			(*orig_finish)(RSA *rsa);
 	RSA_METHOD		rsa_method;
 	char			*keyid;
@@ -75,6 +85,9 @@ struct pkcs11_key {
 };
 
 int pkcs11_interactive = 0;
+#ifdef ENABLE_PKCS11_ECDSA
+static int pkcs11_key_idx = -1;
+#endif /* ENABLE_PKCS11_ECDSA */
 
 /*
  * This can't be in the ssh-pkcs11-uri, becase we can not depend on
@@ -289,6 +302,40 @@ pkcs11_find(struct pkcs11_provider *p, C
 	return (ret);
 }
 
+int pkcs11_login(struct pkcs11_key *k11, CK_FUNCTION_LIST *f, struct pkcs11_slotinfo *si) {
+	char			*pin = NULL, prompt[1024];
+	CK_RV			rv;
+	if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) {
+		if (!pkcs11_interactive) {
+			error("need pin entry%s", (si->token.flags &
+			    CKF_PROTECTED_AUTHENTICATION_PATH) ?
+			    " on reader keypad" : "");
+			return (-1);
+		}
+		if (si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH)
+			verbose("Deferring PIN entry to reader keypad.");
+		else {
+			snprintf(prompt, sizeof(prompt),
+			    "Enter PIN for '%s': ", si->token.label);
+			pin = read_passphrase(prompt, RP_ALLOW_EOF);
+			if (pin == NULL)
+				return (-1);	/* bail out */
+		}
+		rv = f->C_Login(si->session, CKU_USER, (u_char *)pin,
+		    (pin != NULL) ? strlen(pin) : 0);
+		if (pin != NULL) {
+			explicit_bzero(pin, strlen(pin));
+			free(pin);
+		}
+		if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) {
+			error("C_Login failed: %lu", rv);
+			return (-1);
+		}
+		si->logged_in = 1;
+	}
+	return 0;
+}
+
 /* openssl callback doing the actual signing operation */
 static int
 pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
@@ -310,7 +357,6 @@ pkcs11_rsa_private_encrypt(int flen, con
 		{CKA_ID, NULL, 0},
 		{CKA_SIGN, NULL, sizeof(true_val) }
 	};
-	char			*pin = NULL, prompt[1024];
 	int			rval = -1;
 
 	key_filter[0].pValue = &private_key_class;
@@ -326,33 +372,8 @@ pkcs11_rsa_private_encrypt(int flen, con
 	}
 	f = k11->provider->module->function_list;
 	si = &k11->provider->module->slotinfo[k11->slotidx];
-	if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) {
-		if (!pkcs11_interactive) {
-			error("need pin entry%s", (si->token.flags &
-			    CKF_PROTECTED_AUTHENTICATION_PATH) ?
-			    " on reader keypad" : "");
-			return (-1);
-		}
-		if (si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH)
-			verbose("Deferring PIN entry to reader keypad.");
-		else {
-			snprintf(prompt, sizeof(prompt),
-			    "Enter PIN for '%s': ", si->token.label);
-			pin = read_passphrase(prompt, RP_ALLOW_EOF);
-			if (pin == NULL)
-				return (-1);	/* bail out */
-		}
-		rv = f->C_Login(si->session, CKU_USER, (u_char *)pin,
-		    (pin != NULL) ? strlen(pin) : 0);
-		if (pin != NULL) {
-			explicit_bzero(pin, strlen(pin));
-			free(pin);
-		}
-		if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) {
-			error("C_Login failed: %lu", rv);
-			return (-1);
-		}
-		si->logged_in = 1;
+	if(pkcs11_login(k11, f, si)) {
+		return (-1);
 	}
 	key_filter[1].pValue = k11->keyid;
 	key_filter[1].ulValueLen = k11->keyid_len;
@@ -390,6 +411,7 @@ pkcs11_rsa_wrap(struct pkcs11_provider *
 	const RSA_METHOD	*def = RSA_get_default_method();
 
 	k11 = xcalloc(1, sizeof(*k11));
+	k11->key_type = CKK_RSA;
 	k11->provider = provider;
 	provider->refcount++;	/* provider referenced by RSA key */
 	k11->slotidx = slotidx;
@@ -415,6 +437,184 @@ pkcs11_rsa_wrap(struct pkcs11_provider *
 	return (0);
 }
 
+#ifdef ENABLE_PKCS11_ECDSA
+static ECDSA_SIG *pkcs11_ecdsa_sign(const unsigned char *dgst, int dgst_len,
+                                    const BIGNUM *inv, const BIGNUM *rp,
+                                    EC_KEY *ecdsa) {
+	struct pkcs11_key	*k11;
+	struct pkcs11_slotinfo	*si;
+	CK_FUNCTION_LIST	*f;
+	CK_OBJECT_HANDLE	obj;
+	CK_ULONG		tlen = 0;
+	CK_RV			rv;
+	CK_OBJECT_CLASS	private_key_class = CKO_PRIVATE_KEY;
+	CK_BBOOL		true_val = CK_TRUE;
+	CK_MECHANISM		mech = {
+		CKM_ECDSA, NULL_PTR, 0
+	};
+	CK_ATTRIBUTE		key_filter[] = {
+		{CKA_CLASS, NULL, sizeof(private_key_class) },
+		{CKA_ID, NULL, 0},
+		{CKA_SIGN, NULL, sizeof(true_val) }
+	};
+	ECDSA_SIG  		*rval = NULL;
+	key_filter[0].pValue = &private_key_class;
+	key_filter[2].pValue = &true_val;
+
+ #if (OPENSSL_VERSION_NUMBER >= 0x00010100L)
+	if ((k11 = (struct pkcs11_key *)EC_KEY_get_ex_data(ecdsa, pkcs11_key_idx)) == NULL) {
+		error("EC_KEY_get_ex_data failed for ecdsa %p", ecdsa);
+ #else
+	if ((k11 = (struct pkcs11_key *)ECDSA_get_ex_data(ecdsa, pkcs11_key_idx)) == NULL) {
+		error("ECDSA_get_ex_data failed for ecdsa %p", ecdsa);
+ #endif
+		return NULL;
+	}
+	if (!k11->provider || !k11->provider->valid) {
+		error("no pkcs11 (valid) provider for ecdsa %p", ecdsa);
+		return NULL;
+	}
+	f = k11->provider->module->function_list;
+	si = &k11->provider->module->slotinfo[k11->slotidx];
+	if(pkcs11_login(k11, f, si)) {
+		return NULL;
+	}
+	key_filter[1].pValue = k11->keyid;
+	key_filter[1].ulValueLen = k11->keyid_len;
+	/* try to find object w/CKA_SIGN first, retry w/o */
+	if (pkcs11_find(k11->provider, k11->slotidx, key_filter, 3, &obj) < 0 &&
+	    pkcs11_find(k11->provider, k11->slotidx, key_filter, 2, &obj) < 0) {
+		error("cannot find private key");
+	} else if ((rv = f->C_SignInit(si->session, &mech, obj)) != CKR_OK) {
+		error("C_SignInit failed: %lu", rv);
+	} else {
+		CK_BYTE_PTR buf = NULL;
+		BIGNUM *r = NULL, *s = NULL;
+		int nlen;
+		/* Make a call to C_Sign to find out the size of the signature */
+		rv = f->C_Sign(si->session, (CK_BYTE *)dgst, dgst_len, NULL, &tlen);
+		if (rv != CKR_OK) {
+			error("C_Sign failed: %lu", rv);
+			return NULL;
+		}
+		if ((buf = xmalloc(tlen)) == NULL) {
+			error("failure to allocate signature buffer");
+			return NULL;
+		}
+		rv = f->C_Sign(si->session, (CK_BYTE *)dgst, dgst_len, buf, &tlen);
+		if (rv != CKR_OK) {
+			error("C_Sign failed: %lu", rv);
+		}
+
+		if ((rval = ECDSA_SIG_new()) == NULL ||
+		    (r = BN_new()) == NULL ||
+		    (s = BN_new()) == NULL) {
+			error("failure to allocate ECDSA signature");
+		} else {
+			/*
+			 * ECDSA signature is 2 large integers of same size returned
+			 * concatenated by PKCS#11, we separate them to create an
+			 * ECDSA_SIG for OpenSSL.
+			 */
+			nlen = tlen / 2;
+			BN_bin2bn(&buf[0], nlen, r);
+			BN_bin2bn(&buf[nlen], nlen, s);
+			ECDSA_SIG_set0(rval, r, s);
+		}
+		free(buf);
+	}
+	return (rval);
+}
+
+#if (OPENSSL_VERSION_NUMBER >= 0x00010100L)
+static EC_KEY_METHOD *get_pkcs11_ecdsa_method(void) {
+	static EC_KEY_METHOD *pkcs11_ecdsa_method = NULL;
+	if(pkcs11_key_idx == -1) {
+		pkcs11_key_idx = EC_KEY_get_ex_new_index(0, NULL, NULL, NULL, 0);
+	}
+	if (pkcs11_ecdsa_method == NULL) {
+		const EC_KEY_METHOD *def = EC_KEY_get_default_method();
+		pkcs11_ecdsa_method = EC_KEY_METHOD_new(def);
+		EC_KEY_METHOD_set_sign(pkcs11_ecdsa_method, NULL, NULL, pkcs11_ecdsa_sign);
+	}
+#else
+static ECDSA_METHOD *get_pkcs11_ecdsa_method(void) {
+	static ECDSA_METHOD *pkcs11_ecdsa_method = NULL;
+	if(pkcs11_key_idx == -1) {
+		pkcs11_key_idx = ECDSA_get_ex_new_index(0, NULL, NULL, NULL, 0);
+	}
+	if(pkcs11_ecdsa_method == NULL) {
+		const ECDSA_METHOD *def = ECDSA_get_default_method();
+ #ifdef ECDSA_F_ECDSA_METHOD_NEW
+		pkcs11_ecdsa_method = ECDSA_METHOD_new((ECDSA_METHOD *)def);
+		ECDSA_METHOD_set_name(pkcs11_ecdsa_method, "pkcs11");
+		ECDSA_METHOD_set_sign(pkcs11_ecdsa_method, pkcs11_ecdsa_sign);
+ #else
+		pkcs11_ecdsa_method = xcalloc(1, sizeof(*pkcs11_ecdsa_method));
+		memcpy(pkcs11_ecdsa_method, def, sizeof(*pkcs11_ecdsa_method));
+		pkcs11_ecdsa_method->name = "pkcs11";
+		pkcs11_ecdsa_method->ecdsa_do_sign = pkcs11_ecdsa_sign;
+ #endif
+	}
+#endif
+	return pkcs11_ecdsa_method;
+}
+
+static int
+pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
+                  CK_ATTRIBUTE *keyid_attrib, CK_ATTRIBUTE *label_attrib, EC_KEY *ecdsa)
+{
+	struct pkcs11_key *k11;
+	k11 = xcalloc(1, sizeof(*k11));
+	k11->key_type = CKK_EC;
+	k11->provider = provider;
+	provider->refcount++; /* provider referenced by ECDSA key */
+	k11->slotidx = slotidx;
+	/* identify key object on smartcard */
+	k11->keyid_len = keyid_attrib->ulValueLen;
+	if (k11->keyid_len > 0) {
+		k11->keyid = xmalloc(k11->keyid_len);
+		memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len);
+	}
+	if (label_attrib->ulValueLen > 0 ) {
+		k11->label = xmalloc(label_attrib->ulValueLen+1);
+		memcpy(k11->label, label_attrib->pValue, label_attrib->ulValueLen);
+		k11->label[label_attrib->ulValueLen] = 0;
+	}
+ #if (OPENSSL_VERSION_NUMBER >= 0x00010100L)
+	EC_KEY_set_method(ecdsa, get_pkcs11_ecdsa_method());
+	EC_KEY_set_ex_data(ecdsa, pkcs11_key_idx, k11);
+ #else
+	ECDSA_set_method(ecdsa, get_pkcs11_ecdsa_method());
+	ECDSA_set_ex_data(ecdsa, pkcs11_key_idx, k11);
+ #endif
+	return (0);
+}
+#endif /* ENABLE_PKCS11_ECDSA */
+
+int pkcs11_del_key(struct sshkey *key) {
+#ifdef ENABLE_PKCS11_ECDSA
+	if(key->type == KEY_ECDSA) {
+		struct pkcs11_key *k11 = (struct pkcs11_key *)
+ #if (OPENSSL_VERSION_NUMBER >= 0x00010100L)
+			EC_KEY_get_ex_data(key->ecdsa, pkcs11_key_idx);
+ #else
+			ECDSA_get_ex_data(key->ecdsa, pkcs11_key_idx);
+ #endif
+		if (k11 == NULL) {
+			error("EC_KEY_get_ex_data failed for ecdsa %p", key->ecdsa);
+		} else {
+			if (k11->provider)
+				pkcs11_provider_unref(k11->provider);
+			free(k11->keyid);
+			free(k11);
+		}
+	}
+#endif /* ENABLE_PKCS11_ECDSA */
+	sshkey_free(key);
+	return (0);
+}
+
 /* remove trailing spaces */
 static void
 rmspace(u_char *buf, size_t len)
@@ -482,11 +646,13 @@ static int
 pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx,
     struct sshkey ***keysp, int *nkeys, struct pkcs11_uri *uri)
 {
-	size_t filter_size = 1;
+	size_t filter_size = 2;
+	CK_KEY_TYPE pubkey_type = CKK_RSA;
 	CK_OBJECT_CLASS	pubkey_class = CKO_PUBLIC_KEY;
 	CK_OBJECT_CLASS	cert_class = CKO_CERTIFICATE;
 	CK_ATTRIBUTE		pubkey_filter[] = {
 		{ CKA_CLASS, NULL, sizeof(pubkey_class) },
+		{ CKA_KEY_TYPE, NULL, sizeof(pubkey_type) },
 		{ CKA_ID, NULL, 0 },
 		{ CKA_LABEL, NULL, 0 }
 	};
@@ -507,29 +673,60 @@ pkcs11_fetch_keys(struct pkcs11_provider
 		{ CKA_SUBJECT, NULL, 0 },
 		{ CKA_VALUE, NULL, 0 }
 	};
+#ifdef ENABLE_PKCS11_ECDSA
+	CK_KEY_TYPE	        ecdsa_type = CKK_EC;
+	CK_ATTRIBUTE		ecdsa_filter[] = {
+		{ CKA_CLASS, NULL, sizeof(pubkey_class) },
+		{ CKA_KEY_TYPE, NULL, sizeof(ecdsa_type) },
+		{ CKA_ID, NULL, 0 },
+		{ CKA_LABEL, NULL, 0 }
+	};
+	CK_ATTRIBUTE		ecdsa_attribs[] = {
+		{ CKA_ID, NULL, 0 },
+		{ CKA_LABEL, NULL, 0 },
+		{ CKA_EC_PARAMS, NULL, 0 },
+		{ CKA_EC_POINT, NULL, 0 }
+	};
+	ecdsa_filter[0].pValue = &pubkey_class;
+	ecdsa_filter[1].pValue = &ecdsa_type;
+#endif /* ENABLE_PKCS11_ECDSA */
 	pubkey_filter[0].pValue = &pubkey_class;
+	pubkey_filter[1].pValue = &pubkey_type;
 	cert_filter[0].pValue = &cert_class;
 
 	if (uri->id != NULL) {
 		pubkey_filter[filter_size].pValue = uri->id;
 		pubkey_filter[filter_size].ulValueLen = uri->id_len;
-		cert_filter[filter_size].pValue = uri->id;
-		cert_filter[filter_size].ulValueLen = uri->id_len;
+#ifdef ENABLE_PKCS11_ECDSA
+		ecdsa_filter[filter_size].pValue = uri->id;
+		ecdsa_filter[filter_size].ulValueLen = uri->id_len;
+#endif /* ENABLE_PKCS11_ECDSA */
+		cert_filter[filter_size-1].pValue = uri->id;
+		cert_filter[filter_size-1].ulValueLen = uri->id_len;
 		filter_size++;
 	}
 	if (uri->object != NULL) {
 		pubkey_filter[filter_size].pValue = uri->object;
 		pubkey_filter[filter_size].ulValueLen = strlen(uri->object);
 		pubkey_filter[filter_size].type = CKA_LABEL;
-		cert_filter[filter_size].pValue = uri->object;
-		cert_filter[filter_size].ulValueLen = strlen(uri->object);
-		cert_filter[filter_size].type = CKA_LABEL;
+#ifdef ENABLE_PKCS11_ECDSA
+		ecdsa_filter[filter_size].pValue = uri->object;
+		ecdsa_filter[filter_size].ulValueLen = strlen(uri->object);
+		ecdsa_filter[filter_size].type = CKA_LABEL;
+#endif /* ENABLE_PKCS11_ECDSA */
+		cert_filter[filter_size-1].pValue = uri->object;
+		cert_filter[filter_size-1].ulValueLen = strlen(uri->object);
+		cert_filter[filter_size-1].type = CKA_LABEL;
 		filter_size++;
 	}
 
 	if (pkcs11_fetch_keys_filter(p, slotidx, pubkey_filter, filter_size,
 	    pubkey_attribs, keysp, nkeys) < 0 ||
-	    pkcs11_fetch_keys_filter(p, slotidx, cert_filter, filter_size,
+#ifdef ENABLE_PKCS11_ECDSA
+	    pkcs11_fetch_keys_filter(p, slotidx, ecdsa_filter, filter_size,
+	    ecdsa_attribs, keysp, nkeys) < 0||
+#endif /* ENABLE_PKCS11_ECDSA */
+	    pkcs11_fetch_keys_filter(p, slotidx, cert_filter, filter_size - 1,
 	    cert_attribs, keysp, nkeys) < 0)
 		return (-1);
 	return (0);
@@ -553,6 +746,11 @@ pkcs11_fetch_keys_filter(struct pkcs11_p
 {
 	struct sshkey		*key;
 	RSA			*rsa;
+#ifdef ENABLE_PKCS11_ECDSA
+	EC_KEY			*ecdsa;
+#else
+	void			*ecdsa;
+#endif /* ENABLE_PKCS11_ECDSA */
 	X509 			*x509;
 	EVP_PKEY		*evp = NULL;
 	int			i;
@@ -608,6 +806,9 @@ pkcs11_fetch_keys_filter(struct pkcs11_p
 		 * or ID, label, subject and value for certificates.
 		 */
 		rsa = NULL;
+#ifdef ENABLE_PKCS11_ECDSA
+		ecdsa = NULL;
+#endif /* ENABLE_PKCS11_ECDSA */
 		if ((rv = f->C_GetAttributeValue(session, obj, attribs, nattribs))
 		    != CKR_OK) {
 			error("C_GetAttributeValue failed: %lu", rv);
@@ -620,6 +821,45 @@ pkcs11_fetch_keys_filter(struct pkcs11_p
 				rsa->e = BN_bin2bn(attribs[3].pValue,
 				    attribs[3].ulValueLen, NULL);
 			}
+#ifdef ENABLE_PKCS11_ECDSA
+		} else if (attribs[2].type == CKA_EC_PARAMS ) {
+			if ((ecdsa = EC_KEY_new()) == NULL) {
+				error("EC_KEY_new failed");
+			} else {
+				const unsigned char *ptr1 = attribs[2].pValue;
+				const unsigned char *ptr2 = attribs[3].pValue;
+				CK_ULONG len1 = attribs[2].ulValueLen;
+				CK_ULONG len2 = attribs[3].ulValueLen;
+				ASN1_OCTET_STRING *point = NULL;
+
+				/*
+				 * CKA_EC_PARAMS contains the curve parameters of the key
+				 * either referenced as an OID or directly with all values.
+				 * CKA_EC_POINT contains the point (public key) on the curve.
+				 * The point is should be returned inside a DER-encoded
+				 * ASN.1 OCTET STRING value (but some implementation).
+				 */
+				if ((point = d2i_ASN1_OCTET_STRING(NULL, &ptr2, len2))) {
+					/* Pointing to OCTET STRING content */
+					ptr2 = point->data;
+					len2 = point->length;
+				} else {
+					/* No OCTET STRING */
+					ptr2 = attribs[3].pValue;
+				}
+
+				if((d2i_ECParameters(&ecdsa, &ptr1, len1) == NULL) ||
+				   (o2i_ECPublicKey(&ecdsa, &ptr2, len2) == NULL)) {
+					EC_KEY_free(ecdsa);
+					ecdsa = NULL;
+					error("EC public key parsing failed");
+				}
+
+				if(point) {
+					ASN1_OCTET_STRING_free(point);
+				}
+			}
+#endif /* ENABLE_PKCS11_ECDSA */
 		} else {
 			cp = attribs[3].pValue;
 			if ((x509 = X509_new()) == NULL) {
@@ -639,13 +879,28 @@ pkcs11_fetch_keys_filter(struct pkcs11_p
 			X509_free(x509);
 			EVP_PKEY_free(evp);
 		}
-		if (rsa && rsa->n && rsa->e &&
-		    pkcs11_rsa_wrap(p, slotidx, &attribs[0], &attribs[1], rsa) == 0) {
-			if ((key = sshkey_new(KEY_UNSPEC)) == NULL)
-				fatal("sshkey_new failed");
-			key->rsa = rsa;
-			key->type = KEY_RSA;
-			key->flags |= SSHKEY_FLAG_EXT;
+		key = NULL;
+		if (rsa || ecdsa) {
+			if (rsa && rsa->n && rsa->e &&
+			    pkcs11_rsa_wrap(p, slotidx, &attribs[0], &attribs[1], rsa) == 0) {
+				if ((key = sshkey_new(KEY_UNSPEC)) == NULL)
+					fatal("sshkey_new failed");
+				key->rsa = rsa;
+				key->type = KEY_RSA;
+				key->flags |= SSHKEY_FLAG_EXT;
+#ifdef ENABLE_PKCS11_ECDSA
+			} else if(ecdsa && pkcs11_ecdsa_wrap(p, slotidx, &attribs[0], &attribs[1], ecdsa) == 0) {
+				if ((key = sshkey_new(KEY_UNSPEC)) == NULL)
+					fatal("sshkey_new failed");
+				key->ecdsa = ecdsa;
+				key->ecdsa_nid = sshkey_ecdsa_key_to_nid(ecdsa);
+				key->type = KEY_ECDSA;
+				key->flags |= SSHKEY_FLAG_EXT;
+#endif /* ENABLE_PKCS11_ECDSA */
+			}
+		}
+
+		if(key) {
 			if (pkcs11_key_included(keysp, nkeys, key)) {
 				sshkey_free(key);
 			} else {
@@ -658,6 +913,10 @@ pkcs11_fetch_keys_filter(struct pkcs11_p
 			}
 		} else if (rsa) {
 			RSA_free(rsa);
+#ifdef ENABLE_PKCS11_ECDSA
+		} else if (ecdsa) {
+			EC_KEY_free(ecdsa);
+#endif /* ENABLE_PKCS11_ECDSA */
 		}
 		for (i = 0; i < nattribs; i++)
 			free(attribs[i].pValue);
diff -up openssh-7.6p1/ssh-pkcs11-helper.c.pkcs11-ecdsa openssh-7.6p1/ssh-pkcs11-helper.c
--- openssh-7.6p1/ssh-pkcs11-helper.c.pkcs11-ecdsa	2017-10-02 21:34:26.000000000 +0200
+++ openssh-7.6p1/ssh-pkcs11-helper.c	2018-02-16 13:25:59.428469265 +0100
@@ -24,6 +24,17 @@
 
 #include "openbsd-compat/sys-queue.h"
 
+#include <openssl/rsa.h>
+#ifdef OPENSSL_HAS_ECC
+#include <openssl/ecdsa.h>
+#if ((defined(LIBRESSL_VERSION_NUMBER) && \
+	(LIBRESSL_VERSION_NUMBER >= 0x20010002L))) || \
+	(defined(ECDSA_F_ECDSA_METHOD_NEW)) || \
+	(OPENSSL_VERSION_NUMBER >= 0x00010100L)
+#define ENABLE_PKCS11_ECDSA 1
+#endif
+#endif
+
 #include <stdarg.h>
 #include <string.h>
 #include <unistd.h>
@@ -80,7 +90,7 @@ del_keys_by_name(char *name)
 		if (!strcmp(ki->providername, name)) {
 			TAILQ_REMOVE(&pkcs11_keylist, ki, next);
 			free(ki->providername);
-			sshkey_free(ki->key);
+			pkcs11_del_key(ki->key);
 			free(ki);
 		}
 	}
@@ -164,6 +174,20 @@ process_del(void)
 	sshbuf_free(msg);
 }
 
+#ifdef ENABLE_PKCS11_ECDSA
+static u_int EC_KEY_order_size(EC_KEY *key)
+{
+	const EC_GROUP *group = EC_KEY_get0_group(key);
+	BIGNUM *order = BN_new();
+	u_int nbytes = 0;
+	if ((group != NULL) && (order != NULL) && EC_GROUP_get_order(group, order, NULL)) {
+		nbytes = BN_num_bytes(order);
+	}
+	BN_clear_free(order);
+	return nbytes;
+}
+#endif /* ENABLE_PKCS11_ECDSA */
+
 static void
 process_sign(void)
 {
@@ -180,14 +204,38 @@ process_sign(void)
 	else {
 		if ((found = lookup_key(key)) != NULL) {
 #ifdef WITH_OPENSSL
-			int ret;
-
-			slen = RSA_size(key->rsa);
-			signature = xmalloc(slen);
-			if ((ret = RSA_private_encrypt(dlen, data, signature,
-			    found->rsa, RSA_PKCS1_PADDING)) != -1) {
-				slen = ret;
-				ok = 0;
+			if(found->type == KEY_RSA) {
+				int ret;
+				slen = RSA_size(key->rsa);
+				signature = xmalloc(slen);
+				if ((ret = RSA_private_encrypt(dlen, data, signature,
+											   found->rsa, RSA_PKCS1_PADDING)) != -1) {
+					slen = ret;
+					ok = 0;
+				}
+#ifdef ENABLE_PKCS11_ECDSA
+			} else if(found->type == KEY_ECDSA) {
+				ECDSA_SIG *sig;
+				const BIGNUM *r = NULL, *s = NULL;
+				if ((sig = ECDSA_do_sign(data, dlen, found->ecdsa)) != NULL) {
+					/* PKCS11 2.3.1 recommends both r and s to have the order size for
+					   backward compatiblity */
+					ECDSA_SIG_get0(sig, &r, &s);
+					u_int o_len = EC_KEY_order_size(found->ecdsa);
+					u_int r_len = BN_num_bytes(r);
+					u_int s_len = BN_num_bytes(s);
+					if (o_len > 0 && r_len <= o_len && s_len <= o_len) {
+						signature = xcalloc(2, o_len);
+						BN_bn2bin(r, signature + o_len - r_len);
+						BN_bn2bin(s, signature + (2 * o_len) - s_len);
+						slen = 2 * o_len;
+						ok = 0;
+					}
+					ECDSA_SIG_free(sig);
+				}
+#endif /* ENABLE_PKCS11_ECDSA */
+			} else {
+				/* Unsupported type */
 			}
 #endif /* WITH_OPENSSL */
 		}
diff -up openssh-7.6p1/ssh-pkcs11.h.pkcs11-ecdsa openssh-7.6p1/ssh-pkcs11.h
--- openssh-7.6p1/ssh-pkcs11.h.pkcs11-ecdsa	2018-02-16 13:25:59.429469272 +0100
+++ openssh-7.6p1/ssh-pkcs11.h	2018-02-16 13:45:29.623800048 +0100
@@ -20,6 +20,7 @@
 int	pkcs11_init(int);
 void	pkcs11_terminate(void);
 int	pkcs11_add_provider(char *, char *, struct sshkey ***);
+int	pkcs11_del_key(struct sshkey *);
 int	pkcs11_add_provider_by_uri(struct pkcs11_uri *, char *, struct sshkey ***);
 int	pkcs11_del_provider(char *);
 int	pkcs11_uri_write(const struct sshkey *, FILE *);



diff -up openssh-7.6p1/ssh-pkcs11.c.old openssh-7.6p1/ssh-pkcs11.c
--- openssh-7.6p1/ssh-pkcs11.c.old	2018-02-16 16:43:08.861520255 +0100
+++ openssh-7.6p1/ssh-pkcs11.c	2018-02-16 16:56:35.312601451 +0100
@@ -917,13 +917,28 @@ pkcs11_fetch_keys_filter(struct pkcs11_p
 			} else if (d2i_X509(&x509, &cp, attribs[3].ulValueLen)
 			    == NULL) {
 				error("d2i_X509 failed");
-			} else if ((evp = X509_get_pubkey(x509)) == NULL ||
-			    evp->type != EVP_PKEY_RSA ||
-			    evp->pkey.rsa == NULL) {
-				debug("X509_get_pubkey failed or no rsa");
-			} else if ((rsa = RSAPublicKey_dup(evp->pkey.rsa))
-			    == NULL) {
-				error("RSAPublicKey_dup");
+			} else if ((evp = X509_get_pubkey(x509)) == NULL) {
+				debug("X509_get_pubkey failed");
+			} else {
+				switch (evp->type) {
+				case EVP_PKEY_RSA:
+					if (evp->pkey.rsa == NULL)
+						debug("Missing RSA key");
+					else if ((rsa = RSAPublicKey_dup(
+					    evp->pkey.rsa)) == NULL)
+						error("RSAPublicKey_dup failed");
+					break;
+				case EVP_PKEY_EC:
+					if (evp->pkey.ecdsa == NULL)
+						debug("Missing ECDSA key");
+					else if ((ecdsa = EC_KEY_dup(
+					    evp->pkey.ecdsa)) == NULL)
+						error("EC_KEY_dup failed");
+					break;
+				default:
+					debug("not a RSA or ECDSA key");
+					break;
+				}
 			}
 			X509_free(x509);
 			EVP_PKEY_free(evp);