Skip to content

Instantly share code, notes, and snippets.

@kyontan
Last active April 14, 2019 15:47
Show Gist options
  • Save kyontan/763952e7be68a2e96d5c3f0ad0d3bce8 to your computer and use it in GitHub Desktop.
Save kyontan/763952e7be68a2e96d5c3f0ad0d3bce8 to your computer and use it in GitHub Desktop.
Enabling ECDSA in PKCS#11 support for ssh-agent for OpenSSH 7.9p1 (ref: https://bugzilla.mindrot.org/show_bug.cgi?id=2474)

Patch to use ECDSA keys in PKCS#11

This is updated version of https://bugzilla.mindrot.org/show_bug.cgi?id=2474

Tested under macOS Mojave 10.14.4 with Homebrew to use YubiKey 5C

# openssl version
OpenSSL 1.0.2r  26 Feb 2019
# ssh -V
OpenSSH_7.9p1, OpenSSL 1.0.2r  26 Feb 2019

If you use Homebrew, edit fomulae by brew edit openssh and add below:

# Add support ECDSA for PKCS11, ref: https://bugzilla.mindrot.org/show_bug.cgi?id=2474
patch do
  url "https://gist.githubusercontent.com/kyontan/763952e7be68a2e96d5c3f0ad0d3bce8/raw/2ad5f854eba7704fbb7af2182ac57ee82b9a8f61/openssh-7.9p1-pkcs11-ecdsa.patch"
  sha256 "93b4e48321db94d785833a9414b12467a4741156cec90fa3052a4092acec8938"
end
diff --git a/ssh-pkcs11-client.c b/ssh-pkcs11-client.c
index d1241ce..897520e 100644
--- a/ssh-pkcs11-client.c
+++ b/ssh-pkcs11-client.c
@@ -31,6 +31,14 @@
#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))
+#define ENABLE_PKCS11_ECDSA 1
+#endif
+#endif
#include "openbsd-compat/openssl-compat.h"
@@ -154,9 +162,9 @@ pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
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;
@@ -169,6 +177,73 @@ 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)
+{
+ struct sshkey key;
+ u_char *blob, *signature = NULL;
+ u_int blen, slen = 0;
+ int r;
+ struct sshbuf *msg;
+ ECDSA_SIG *ret = NULL;
+
+ key.type = KEY_ECDSA;
+ key.ecdsa = ecdsa;
+ key.ecdsa_nid = sshkey_ecdsa_key_to_nid(ecdsa);
+ if ((r = sshkey_to_blob(&key, &blob, &blen)) != 0) {
+ error("%s: sshkey_to_blob: %s", __func__, ssh_err(r));
+ return NULL;
+ }
+ if ((msg = sshbuf_new()) == NULL)
+ fatal("%s: sshbuf_new failed", __func__);
+ if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 ||
+ (r = sshbuf_put_string(msg, blob, blen)) != 0 ||
+ (r = sshbuf_put_string(msg, from, flen)) != 0 ||
+ (r = sshbuf_put_u32(msg, 0)) != 0)
+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
+ free(blob);
+ send_msg(msg);
+ sshbuf_reset(msg);
+
+ if (recv_msg(msg) == SSH2_AGENT_SIGN_RESPONSE) {
+ if ((r = sshbuf_get_string(msg, &signature, &slen)) != 0)
+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
+ if (slen <= (size_t)ECDSA_size(ecdsa)) {
+ int nlen = slen / 2;
+ ret = ECDSA_SIG_new();
+ BN_bin2bn(&signature[0], nlen, ret->r);
+ BN_bin2bn(&signature[nlen], nlen, ret->s);
+ }
+ free(signature);
+ }
+ sshbuf_free(&msg);
+ return (ret);
+}
+
+/* redirect the ECDSA private key encrypt operation to the ssh-pkcs11-helper */
+static int
+wrap_ecdsa_key(EC_KEY *ecdsa) {
+ 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);
+ return (0);
+}
+#endif
+
static int
pkcs11_start_helper(void)
{
@@ -234,7 +309,15 @@ pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp)
__func__, ssh_err(r));
if ((r = sshkey_from_blob(blob, blen, &k)) != 0)
fatal("%s: bad key: %s", __func__, ssh_err(r));
- 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 --git a/ssh-pkcs11-helper.c b/ssh-pkcs11-helper.c
index 6301033..23c4d2d 100644
--- a/ssh-pkcs11-helper.c
+++ b/ssh-pkcs11-helper.c
@@ -24,6 +24,16 @@
#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))
+#define ENABLE_PKCS11_ECDSA 1
+#endif
+#endif
+
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
@@ -172,6 +182,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)
{
@@ -192,14 +216,36 @@ 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;
+ 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 */
+ u_int o_len = EC_KEY_order_size(found->ecdsa);
+ u_int r_len = BN_num_bytes(sig->r);
+ u_int s_len = BN_num_bytes(sig->s);
+ if (o_len > 0 && r_len <= o_len && s_len <= o_len) {
+ signature = xcalloc(2, o_len);
+ BN_bn2bin(sig->r, signature + o_len - r_len);
+ BN_bn2bin(sig->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 --git a/ssh-pkcs11.c b/ssh-pkcs11.c
index 775de96..a507d6f 100644
--- a/ssh-pkcs11.c
+++ b/ssh-pkcs11.c
@@ -33,6 +33,15 @@
#include "openbsd-compat/openssl-compat.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))
+#define ENABLE_PKCS11_ECDSA 1
+#endif
+#endif
#define CRYPTOKI_COMPAT
#include "pkcs11.h"
@@ -67,6 +76,7 @@ TAILQ_HEAD(, pkcs11_provider) pkcs11_providers;
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;
@@ -74,6 +84,9 @@ struct pkcs11_key {
};
int pkcs11_interactive = 0;
+#ifdef ENABLE_PKCS11_ECDSA
+static int pkcs11_key_idx = -1;
+#endif /* ENABLE_PKCS11_ECDSA */
int
pkcs11_init(int interactive)
@@ -218,6 +231,40 @@ pkcs11_find(struct pkcs11_provider *p, CK_ULONG slotidx, CK_ATTRIBUTE *attr,
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,
@@ -239,7 +286,6 @@ pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
{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;
@@ -255,33 +301,8 @@ pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
}
f = k11->provider->function_list;
si = &k11->provider->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;
@@ -295,9 +316,9 @@ pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
/* XXX handle CKR_BUFFER_TOO_SMALL */
tlen = RSA_size(rsa);
rv = f->C_Sign(si->session, (CK_BYTE *)from, flen, to, &tlen);
- if (rv == CKR_OK)
+ if (rv == CKR_OK)
rval = tlen;
- else
+ else
error("C_Sign failed: %lu", rv);
}
return (rval);
@@ -319,6 +340,7 @@ pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
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;
@@ -344,6 +366,148 @@ pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
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 ((k11 = (struct pkcs11_key *)ECDSA_get_ex_data(ecdsa, pkcs11_key_idx)) == NULL) {
+ error("ECDSA_get_ex_data failed for ecdsa %p", ecdsa);
+ return NULL;
+ }
+ if (!k11->provider || !k11->provider->valid) {
+ error("no pkcs11 (valid) provider for ecdsa %p", ecdsa);
+ return NULL;
+ }
+ f = k11->provider->function_list;
+ si = &k11->provider->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;
+ 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) {
+ 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, rval->r);
+ BN_bin2bn(&buf[nlen], nlen, rval->s);
+ }
+ free(buf);
+ }
+ return (rval);
+}
+
+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
+ }
+ return pkcs11_ecdsa_method;
+}
+
+static int
+pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
+ CK_ATTRIBUTE *keyid_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);
+ }
+ ECDSA_set_method(ecdsa, get_pkcs11_ecdsa_method());
+ ECDSA_set_ex_data(ecdsa, pkcs11_key_idx, k11);
+ 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 *)
+ ECDSA_get_ex_data(key->ecdsa, pkcs11_key_idx);
+ if (k11 == NULL) {
+ error("ECDSA_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)
@@ -404,17 +568,19 @@ pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin)
* keysp points to an (possibly empty) array with *nkeys keys.
*/
static int pkcs11_fetch_keys_filter(struct pkcs11_provider *, CK_ULONG,
- CK_ATTRIBUTE [], CK_ATTRIBUTE [3], struct sshkey ***, int *)
+ CK_ATTRIBUTE [], int, CK_ATTRIBUTE [3], struct sshkey ***, int *)
__attribute__((__bounded__(__minbytes__,4, 3 * sizeof(CK_ATTRIBUTE))));
static int
pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx,
struct sshkey ***keysp, int *nkeys)
{
- CK_OBJECT_CLASS pubkey_class = CKO_PUBLIC_KEY;
- CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE;
+ 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_CLASS, NULL, sizeof(pubkey_class) },
+ { CKA_KEY_TYPE, NULL, sizeof(pubkey_type) }
};
CK_ATTRIBUTE cert_filter[] = {
{ CKA_CLASS, NULL, sizeof(cert_class) }
@@ -429,12 +595,31 @@ pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx,
{ 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) }
+ };
+ CK_ATTRIBUTE ecdsa_attribs[] = {
+ { CKA_ID, 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 (pkcs11_fetch_keys_filter(p, slotidx, pubkey_filter, pubkey_attribs,
+ if (pkcs11_fetch_keys_filter(p, slotidx, pubkey_filter, 2, pubkey_attribs,
keysp, nkeys) < 0 ||
- pkcs11_fetch_keys_filter(p, slotidx, cert_filter, cert_attribs,
+#ifdef ENABLE_PKCS11_ECDSA
+ pkcs11_fetch_keys_filter(p, slotidx, ecdsa_filter, 2, ecdsa_attribs,
+ keysp, nkeys) < 0||
+#endif /* ENABLE_PKCS11_ECDSA */
+ pkcs11_fetch_keys_filter(p, slotidx, cert_filter, 1, cert_attribs,
keysp, nkeys) < 0)
return (-1);
return (0);
@@ -462,11 +647,16 @@ have_rsa_key(const RSA *rsa)
static int
pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx,
- CK_ATTRIBUTE filter[], CK_ATTRIBUTE attribs[3],
+ CK_ATTRIBUTE filter[], int nfilter, CK_ATTRIBUTE attribs[3],
struct sshkey ***keysp, int *nkeys)
{
struct sshkey *key;
RSA *rsa;
+#ifdef ENABLE_PKCS11_ECDSA
+ EC_KEY *ecdsa;
+#else
+ void *ecdsa;
+#endif /* ENABLE_PKCS11_ECDSA */
X509 *x509;
EVP_PKEY *evp;
int i;
@@ -480,7 +670,7 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx,
f = p->function_list;
session = p->slotinfo[slotidx].session;
/* setup a filter the looks for public keys */
- if ((rv = f->C_FindObjectsInit(session, filter, 1)) != CKR_OK) {
+ if ((rv = f->C_FindObjectsInit(session, filter, nfilter)) != CKR_OK) {
error("C_FindObjectsInit failed: %lu", rv);
return (-1);
}
@@ -521,6 +711,9 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx,
* or ID, 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, 3))
!= CKR_OK) {
error("C_GetAttributeValue failed: %lu", rv);
@@ -543,6 +736,45 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx,
BN_free(rsa_n);
BN_free(rsa_e);
}
+#ifdef ENABLE_PKCS11_ECDSA
+ } else if (attribs[1].type == CKA_EC_PARAMS ) {
+ if ((ecdsa = EC_KEY_new()) == NULL) {
+ error("EC_KEY_new failed");
+ } else {
+ const unsigned char *ptr1 = attribs[1].pValue;
+ const unsigned char *ptr2 = attribs[2].pValue;
+ CK_ULONG len1 = attribs[1].ulValueLen;
+ CK_ULONG len2 = attribs[2].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[2].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) {
+ M_ASN1_OCTET_STRING_free(point);
+ }
+ }
+#endif /* ENABLE_PKCS11_ECDSA */
} else {
cp = attribs[2].pValue;
if ((x509 = X509_new()) == NULL) {
@@ -557,16 +789,53 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx,
} else if ((rsa = RSAPublicKey_dup(
EVP_PKEY_get0_RSA(evp))) == 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.ec == NULL)
+ debug("Missing EC(DSA) key");
+ else if ((ecdsa = EC_KEY_dup(
+ evp->pkey.ec)) == NULL)
+ error("EC_KEY_dup failed");
+ break;
+ default:
+ debug("not a RSA or ECDSA key");
+ break;
+ }
}
X509_free(x509);
}
- if (rsa && have_rsa_key(rsa) &&
- pkcs11_rsa_wrap(p, slotidx, &attribs[0], 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 && have_rsa_key(rsa) &&
+ pkcs11_rsa_wrap(p, slotidx, &attribs[0], 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], 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 {
@@ -579,6 +848,10 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx,
}
} 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 < 3; i++)
free(attribs[i].pValue);
diff --git a/ssh-pkcs11.h b/ssh-pkcs11.h
index 0ced74f..cd2ad3f 100644
--- a/ssh-pkcs11.h
+++ b/ssh-pkcs11.h
@@ -17,6 +17,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_del_provider(char *);
#if !defined(WITH_OPENSSL) && defined(ENABLE_PKCS11)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment