|
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) |