]> git.ipfire.org Git - thirdparty/openssh-portable.git/commitdiff
upstream: add support for ECDSA keys in PKCS#11 tokens
authordjm@openbsd.org <djm@openbsd.org>
Sun, 20 Jan 2019 22:51:37 +0000 (22:51 +0000)
committerDamien Miller <djm@mindrot.org>
Sun, 20 Jan 2019 23:54:37 +0000 (10:54 +1100)
Work by markus@ and Pedro Martelletto, feedback and ok me@

OpenBSD-Commit-ID: a37d651e221341376636056512bddfc16efb4424

ssh-pkcs11-client.c
ssh-pkcs11-helper.c
ssh-pkcs11.c
ssh-pkcs11.h
sshkey.h

index d1241ce67f32638df34815ed28ab79251aa44436..6e16b2f9adddb1a0409875d492754b6ab6cb52ed 100644 (file)
@@ -1,6 +1,7 @@
-/* $OpenBSD: ssh-pkcs11-client.c,v 1.10 2018/07/09 21:59:10 markus Exp $ */
+/* $OpenBSD: ssh-pkcs11-client.c,v 1.12 2019/01/20 22:51:37 djm Exp $ */
 /*
  * Copyright (c) 2010 Markus Friedl.  All rights reserved.
+ * Copyright (c) 2014 Pedro Martelletto. All rights reserved.
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -30,6 +31,7 @@
 #include <unistd.h>
 #include <errno.h>
 
+#include <openssl/ecdsa.h>
 #include <openssl/rsa.h>
 
 #include "openbsd-compat/openssl-compat.h"
@@ -113,8 +115,7 @@ pkcs11_terminate(void)
 }
 
 static int
-pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
-    int padding)
+rsa_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, int padding)
 {
        struct sshkey key;      /* XXX */
        u_char *blob, *signature = NULL;
@@ -154,18 +155,89 @@ 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 */
+static ECDSA_SIG *
+ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv,
+    const BIGNUM *rp, EC_KEY *ec)
+{
+       struct sshkey key; /* XXX */
+       u_char *blob, *signature = NULL;
+       const u_char *cp;
+       size_t blen, slen = 0;
+       ECDSA_SIG *ret = NULL;
+       struct sshbuf *msg;
+       int r;
+
+       key.type = KEY_ECDSA;
+       key.ecdsa = ec;
+       key.ecdsa_nid = sshkey_ecdsa_key_to_nid(ec);
+       if (key.ecdsa_nid < 0) {
+               error("%s: couldn't get curve nid", __func__);
+               return (NULL);
+       }
+       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, dgst, dgst_len)) != 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));
+               cp = signature;
+               ret = d2i_ECDSA_SIG(NULL, &cp, slen);
+               free(signature);
+       }
+
+       sshbuf_free(msg);
+       return (ret);
+}
+
+static RSA_METHOD      *helper_rsa;
+static EC_KEY_METHOD   *helper_ecdsa;
+
+/* redirect private key crypto operations to the ssh-pkcs11-helper */
+static void
+wrap_key(struct sshkey *k)
+{
+       if (k->type == KEY_RSA)
+               RSA_set_method(k->rsa, helper_rsa);
+       else if (k->type == KEY_ECDSA)
+               EC_KEY_set_method(k->ecdsa, helper_ecdsa);
+       else
+               fatal("%s: unknown key type", __func__);
+}
+
 static int
-wrap_key(RSA *rsa)
+pkcs11_start_helper_methods(void)
 {
-       static RSA_METHOD *helper_rsa;
+       if (helper_ecdsa != NULL)
+               return (0);
+
+       int (*orig_sign)(int, const unsigned char *, int, unsigned char *,
+           unsigned int *, const BIGNUM *, const BIGNUM *, EC_KEY *) = NULL;
+       if (helper_ecdsa != NULL)
+               return (0);
+       helper_ecdsa = EC_KEY_METHOD_new(EC_KEY_OpenSSL());
+       if (helper_ecdsa == NULL)
+               return (-1);
+       EC_KEY_METHOD_get_sign(helper_ecdsa, &orig_sign, NULL, NULL);
+       EC_KEY_METHOD_set_sign(helper_ecdsa, orig_sign, NULL, ecdsa_do_sign);
 
        if ((helper_rsa = RSA_meth_dup(RSA_get_default_method())) == NULL)
                fatal("%s: RSA_meth_dup failed", __func__);
        if (!RSA_meth_set1_name(helper_rsa, "ssh-pkcs11-helper") ||
-           !RSA_meth_set_priv_enc(helper_rsa, pkcs11_rsa_private_encrypt))
+           !RSA_meth_set_priv_enc(helper_rsa, rsa_encrypt))
                fatal("%s: failed to prepare method", __func__);
-       RSA_set_method(rsa, helper_rsa);
+
        return (0);
 }
 
@@ -174,6 +246,11 @@ pkcs11_start_helper(void)
 {
        int pair[2];
 
+       if (pkcs11_start_helper_methods() == -1) {
+               error("pkcs11_start_helper_methods failed");
+               return (-1);
+       }
+
        if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
                error("socketpair: %s", strerror(errno));
                return (-1);
@@ -204,7 +281,7 @@ int
 pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp)
 {
        struct sshkey *k;
-       int r;
+       int r, type;
        u_char *blob;
        size_t blen;
        u_int nkeys, i;
@@ -222,7 +299,8 @@ pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp)
        send_msg(msg);
        sshbuf_reset(msg);
 
-       if (recv_msg(msg) == SSH2_AGENT_IDENTITIES_ANSWER) {
+       type = recv_msg(msg);
+       if (type == SSH2_AGENT_IDENTITIES_ANSWER) {
                if ((r = sshbuf_get_u32(msg, &nkeys)) != 0)
                        fatal("%s: buffer error: %s", __func__, ssh_err(r));
                *keysp = xcalloc(nkeys, sizeof(struct sshkey *));
@@ -234,10 +312,13 @@ 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);
+                       wrap_key(k);
                        (*keysp)[i] = k;
                        free(blob);
                }
+       } else if (type == SSH2_AGENT_FAILURE) {
+               if ((r = sshbuf_get_u32(msg, &nkeys)) != 0)
+                       nkeys = -1;
        } else {
                nkeys = -1;
        }
index 6301033c513ae6ee5f2cbcee20f9eb515df75f9b..92c6728ba654f1ddc8bf814c6711bb14f91c0464 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-pkcs11-helper.c,v 1.14 2018/01/08 15:18:46 markus Exp $ */
+/* $OpenBSD: ssh-pkcs11-helper.c,v 1.15 2019/01/20 22:51:37 djm Exp $ */
 /*
  * Copyright (c) 2010 Markus Friedl.  All rights reserved.
  *
@@ -110,7 +110,7 @@ static void
 process_add(void)
 {
        char *name, *pin;
-       struct sshkey **keys;
+       struct sshkey **keys = NULL;
        int r, i, nkeys;
        u_char *blob;
        size_t blen;
@@ -139,11 +139,13 @@ process_add(void)
                        free(blob);
                        add_key(keys[i], name);
                }
-               free(keys);
        } else {
                if ((r = sshbuf_put_u8(msg, SSH_AGENT_FAILURE)) != 0)
                        fatal("%s: buffer error: %s", __func__, ssh_err(r));
+               if ((r = sshbuf_put_u32(msg, -nkeys)) != 0)
+                       fatal("%s: buffer error: %s", __func__, ssh_err(r));
        }
+       free(keys);
        free(pin);
        free(name);
        send_msg(msg);
@@ -192,15 +194,33 @@ process_sign(void)
        else {
                if ((found = lookup_key(key)) != NULL) {
 #ifdef WITH_OPENSSL
+                       u_int xslen;
                        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 (key->type == KEY_RSA) {
+                               slen = RSA_size(key->rsa);
+                               signature = xmalloc(slen);
+                               ret = RSA_private_encrypt(dlen, data, signature,
+                                   found->rsa, RSA_PKCS1_PADDING);
+                               if (ret != -1) {
+                                       slen = ret;
+                                       ok = 0;
+                               }
+                       } else if (key->type == KEY_ECDSA) {
+                               xslen = ECDSA_size(key->ecdsa);
+                               signature = xmalloc(xslen);
+                               /* "The parameter type is ignored." */
+                               ret = ECDSA_sign(-1, data, dlen, signature,
+                                   &xslen, found->ecdsa);
+                               if (ret != 0)
+                                       ok = 0;
+                               else
+                                       error("%s: ECDSA_sign"
+                                           " returns %d", __func__, ret);
+                               slen = xslen;
+                       } else
+                               error("%s: don't know how to sign with key "
+                                   "type %d", __func__, (int)key->type);
 #endif /* WITH_OPENSSL */
                }
                sshkey_free(key);
index 775de964210ff63af440e9f060061fb3b15423e1..01f968a9b9dacc189bb3b81d4d93a48a3de5e995 100644 (file)
@@ -1,6 +1,7 @@
-/* $OpenBSD: ssh-pkcs11.c,v 1.26 2018/02/07 02:06:51 jsing Exp $ */
+/* $OpenBSD: ssh-pkcs11.c,v 1.28 2019/01/20 22:51:37 djm Exp $ */
 /*
  * Copyright (c) 2010 Markus Friedl.  All rights reserved.
+ * Copyright (c) 2014 Pedro Martelletto. All rights reserved.
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
 
 #ifdef ENABLE_PKCS11
 
-#include <sys/types.h>
 #ifdef HAVE_SYS_TIME_H
 # include <sys/time.h>
 #endif
+
+#include <sys/types.h>
 #include <stdarg.h>
 #include <stdio.h>
 
+#include <ctype.h>
 #include <string.h>
 #include <dlfcn.h>
 
 #include "openbsd-compat/sys-queue.h"
 #include "openbsd-compat/openssl-compat.h"
 
+#include <openssl/ecdsa.h>
 #include <openssl/x509.h>
+#include <openssl/err.h>
 
 #define CRYPTOKI_COMPAT
 #include "pkcs11.h"
@@ -69,12 +74,25 @@ struct pkcs11_key {
        CK_ULONG                slotidx;
        int                     (*orig_finish)(RSA *rsa);
        RSA_METHOD              *rsa_method;
+       EC_KEY_METHOD           *ec_key_method;
        char                    *keyid;
        int                     keyid_len;
 };
 
 int pkcs11_interactive = 0;
 
+#ifdef HAVE_DLOPEN
+static void
+ossl_error(const char *msg)
+{
+        unsigned long    e;
+
+        while ((e = ERR_get_error()) != 0)
+                error("%s: %s: %.100s", __func__, msg,
+                    ERR_error_string(e, NULL));
+}
+#endif
+
 int
 pkcs11_init(int interactive)
 {
@@ -84,9 +102,9 @@ pkcs11_init(int interactive)
 }
 
 /*
- * finalize a provider shared libarary, it's no longer usable.
+ * finalize a provider shared library, it's no longer usable.
  * however, there might still be keys referencing this provider,
- * so the actuall freeing of memory is handled by pkcs11_provider_unref().
+ * so the actual freeing of memory is handled by pkcs11_provider_unref().
  * this is called when a provider gets unregistered.
  */
 static void
@@ -123,6 +141,7 @@ pkcs11_provider_unref(struct pkcs11_provider *p)
        if (--p->refcount <= 0) {
                if (p->valid)
                        error("pkcs11_provider_unref: %p still valid", p);
+               free(p->name);
                free(p->slotlist);
                free(p->slotinfo);
                free(p);
@@ -218,43 +237,27 @@ pkcs11_find(struct pkcs11_provider *p, CK_ULONG slotidx, CK_ATTRIBUTE *attr,
        return (ret);
 }
 
-/* openssl callback doing the actual signing operation */
 static int
-pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
-    int padding)
+pkcs11_get_key(struct pkcs11_key *k11, CK_MECHANISM_TYPE mech_type)
 {
-       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_RSA_PKCS, NULL_PTR, 0
-       };
-       CK_ATTRIBUTE            key_filter[] = {
-               {CKA_CLASS, NULL, sizeof(private_key_class) },
-               {CKA_ID, NULL, 0},
-               {CKA_SIGN, NULL, sizeof(true_val) }
-       };
+       CK_OBJECT_HANDLE         obj;
+       CK_RV                    rv;
+       CK_OBJECT_CLASS          private_key_class;
+       CK_BBOOL                 true_val;
+       CK_MECHANISM             mech;
+       CK_ATTRIBUTE             key_filter[3];
        char                    *pin = NULL, prompt[1024];
-       int                     rval = -1;
-
-       key_filter[0].pValue = &private_key_class;
-       key_filter[2].pValue = &true_val;
 
-       if ((k11 = RSA_get_app_data(rsa)) == NULL) {
-               error("RSA_get_app_data failed for rsa %p", rsa);
-               return (-1);
-       }
        if (!k11->provider || !k11->provider->valid) {
-               error("no pkcs11 (valid) provider for rsa %p", rsa);
+               error("no pkcs11 (valid) provider found");
                return (-1);
        }
+
        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 &
@@ -283,23 +286,75 @@ pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
                }
                si->logged_in = 1;
        }
+
+       memset(&key_filter, 0, sizeof(key_filter));
+       private_key_class = CKO_PRIVATE_KEY;
+       key_filter[0].type = CKA_CLASS;
+       key_filter[0].pValue = &private_key_class;
+       key_filter[0].ulValueLen = sizeof(private_key_class);
+
+       key_filter[1].type = CKA_ID;
        key_filter[1].pValue = k11->keyid;
        key_filter[1].ulValueLen = k11->keyid_len;
+
+       true_val = CK_TRUE;
+       key_filter[2].type = CKA_SIGN;
+       key_filter[2].pValue = &true_val;
+       key_filter[2].ulValueLen = sizeof(true_val);
+
        /* 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) {
+               return (-1);
+       }
+
+       memset(&mech, 0, sizeof(mech));
+       mech.mechanism = mech_type;
+       mech.pParameter = NULL_PTR;
+       mech.ulParameterLen = 0;
+
+       if ((rv = f->C_SignInit(si->session, &mech, obj)) != CKR_OK) {
                error("C_SignInit failed: %lu", rv);
-       } else {
-               /* 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) 
-                       rval = tlen;
-               else 
-                       error("C_Sign failed: %lu", rv);
+               return (-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,
+    int padding)
+{
+       struct pkcs11_key       *k11;
+       struct pkcs11_slotinfo  *si;
+       CK_FUNCTION_LIST        *f;
+       CK_ULONG                tlen = 0;
+       CK_RV                   rv;
+       int                     rval = -1;
+
+       if ((k11 = RSA_get_app_data(rsa)) == NULL) {
+               error("RSA_get_app_data failed for rsa %p", rsa);
+               return (-1);
+       }
+
+       if (pkcs11_get_key(k11, CKM_RSA_PKCS) == -1) {
+               error("pkcs11_get_key failed");
+               return (-1);
        }
+
+       f = k11->provider->function_list;
+       si = &k11->provider->slotinfo[k11->slotidx];
+       tlen = RSA_size(rsa);
+
+       /* XXX handle CKR_BUFFER_TOO_SMALL */
+       rv = f->C_Sign(si->session, (CK_BYTE *)from, flen, to, &tlen);
+       if (rv == CKR_OK)
+               rval = tlen;
+       else
+               error("C_Sign failed: %lu", rv);
+
        return (rval);
 }
 
@@ -344,6 +399,115 @@ pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
        return (0);
 }
 
+/* openssl callback doing the actual signing operation */
+static ECDSA_SIG *
+ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv,
+    const BIGNUM *rp, EC_KEY *ec)
+{
+       struct pkcs11_key       *k11;
+       struct pkcs11_slotinfo  *si;
+       CK_FUNCTION_LIST        *f;
+       CK_ULONG                siglen = 0, bnlen;
+       CK_RV                   rv;
+       ECDSA_SIG               *ret = NULL;
+       u_char                  *sig;
+       const u_char            *cp;
+
+       if ((k11 = EC_KEY_get_ex_data(ec, 0)) == NULL) {
+               ossl_error("EC_KEY_get_key_method_data failed for ec");
+               return (NULL);
+       }
+
+       if (pkcs11_get_key(k11, CKM_ECDSA) == -1) {
+               error("pkcs11_get_key failed");
+               return (NULL);
+       }
+
+       f = k11->provider->function_list;
+       si = &k11->provider->slotinfo[k11->slotidx];
+
+       siglen = ECDSA_size(ec);
+       sig = xmalloc(siglen);
+
+       /* XXX handle CKR_BUFFER_TOO_SMALL */
+       rv = f->C_Sign(si->session, (CK_BYTE *)dgst, dgst_len, sig, &siglen);
+       if (rv != CKR_OK) {
+               error("C_Sign failed: %lu", rv);
+               goto done;
+       }
+       cp = sig;
+       ret = d2i_ECDSA_SIG(NULL, &cp, siglen);
+       if (ret == NULL) {
+               /*
+                * d2i_ECDSA_SIG failed, so sig does not point to a DER-encoded
+                * sequence, but to the concatenation r|s.
+                */
+               if (siglen < 64 || siglen > 132 || siglen % 2) {
+                       ossl_error("d2i_ECDSA_SIG failed");
+                       goto done;
+               }
+               bnlen = siglen/2;
+               if ((ret = ECDSA_SIG_new()) == NULL) {
+                       error("ECDSA_SIG_new failed");
+                       goto done;
+               }
+               if (BN_bin2bn(sig, bnlen, ret->r) == NULL ||
+                   BN_bin2bn(sig+bnlen, bnlen, ret->s) == NULL) {
+                       ossl_error("d2i_ECDSA_SIG failed");
+                       ECDSA_SIG_free(ret);
+                       ret = NULL;
+                       goto done;
+               }
+       }
+ done:
+       free(sig);
+
+       return (ret);
+}
+
+static EC_KEY_METHOD *ec_key_method;
+
+static int
+pkcs11_ecdsa_start_wrapper(void)
+{
+       int (*orig_sign)(int, const unsigned char *, int, unsigned char *,
+           unsigned int *, const BIGNUM *, const BIGNUM *, EC_KEY *) = NULL;
+
+       if (ec_key_method != NULL)
+               return (0);
+       ec_key_method = EC_KEY_METHOD_new(EC_KEY_OpenSSL());
+       if (ec_key_method == NULL)
+               return (-1);
+       EC_KEY_METHOD_get_sign(ec_key_method, &orig_sign, NULL, NULL);
+       EC_KEY_METHOD_set_sign(ec_key_method, orig_sign, NULL, ecdsa_do_sign);
+       return (0);
+}
+
+static int
+pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
+    CK_ATTRIBUTE *keyid_attrib, EC_KEY *ec)
+{
+       struct pkcs11_key       *k11;
+
+       if (pkcs11_ecdsa_start_wrapper() == -1)
+               return (-1);
+
+       k11 = xcalloc(1, sizeof(*k11));
+       k11->provider = provider;
+       provider->refcount++;   /* provider referenced by ECDSA key */
+       k11->slotidx = slotidx;
+       /* identify key object on smartcard */
+       k11->keyid_len = keyid_attrib->ulValueLen;
+       k11->keyid = xmalloc(k11->keyid_len);
+       memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len);
+       k11->ec_key_method = ec_key_method;
+
+       EC_KEY_set_method(ec, k11->ec_key_method);
+       EC_KEY_set_ex_data(ec, 0, k11);
+
+       return (0);
+}
+
 /* remove trailing spaces */
 static void
 rmspace(u_char *buf, size_t len)
@@ -364,18 +528,19 @@ rmspace(u_char *buf, size_t len)
  * if pin == NULL we delay login until key use
  */
 static int
-pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin)
+pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin,
+    CK_ULONG user)
 {
        CK_RV                   rv;
        CK_FUNCTION_LIST        *f;
        CK_SESSION_HANDLE       session;
-       int                     login_required;
+       int                     login_required, ret;
 
        f = p->function_list;
        login_required = p->slotinfo[slotidx].token.flags & CKF_LOGIN_REQUIRED;
        if (pin && login_required && !strlen(pin)) {
                error("pin required");
-               return (-1);
+               return (-SSH_PKCS11_ERR_PIN_REQUIRED);
        }
        if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION|
            CKF_SERIAL_SESSION, NULL, NULL, &session))
@@ -384,13 +549,16 @@ pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin)
                return (-1);
        }
        if (login_required && pin) {
-               rv = f->C_Login(session, CKU_USER,
+               rv = f->C_Login(session, user,
                    (u_char *)pin, strlen(pin));
                if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) {
                        error("C_Login failed: %lu", rv);
+                       ret = (rv == CKR_PIN_LOCKED) ?
+                           -SSH_PKCS11_ERR_PIN_LOCKED :
+                           -SSH_PKCS11_ERR_LOGIN_FAIL;
                        if ((rv = f->C_CloseSession(session)) != CKR_OK)
                                error("C_CloseSession failed: %lu", rv);
-                       return (-1);
+                       return (ret);
                }
                p->slotinfo[slotidx].logged_in = 1;
        }
@@ -398,48 +566,6 @@ pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin)
        return (0);
 }
 
-/*
- * lookup public keys for token in slot identified by slotidx,
- * add 'wrapped' public keys to the 'keysp' array and increment nkeys.
- * 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 *)
-       __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_ATTRIBUTE            pubkey_filter[] = {
-               { CKA_CLASS, NULL, sizeof(pubkey_class) }
-       };
-       CK_ATTRIBUTE            cert_filter[] = {
-               { CKA_CLASS, NULL, sizeof(cert_class) }
-       };
-       CK_ATTRIBUTE            pubkey_attribs[] = {
-               { CKA_ID, NULL, 0 },
-               { CKA_MODULUS, NULL, 0 },
-               { CKA_PUBLIC_EXPONENT, NULL, 0 }
-       };
-       CK_ATTRIBUTE            cert_attribs[] = {
-               { CKA_ID, NULL, 0 },
-               { CKA_SUBJECT, NULL, 0 },
-               { CKA_VALUE, NULL, 0 }
-       };
-       pubkey_filter[0].pValue = &pubkey_class;
-       cert_filter[0].pValue = &cert_class;
-
-       if (pkcs11_fetch_keys_filter(p, slotidx, pubkey_filter, pubkey_attribs,
-           keysp, nkeys) < 0 ||
-           pkcs11_fetch_keys_filter(p, slotidx, cert_filter, cert_attribs,
-           keysp, nkeys) < 0)
-               return (-1);
-       return (0);
-}
-
 static int
 pkcs11_key_included(struct sshkey ***keysp, int *nkeys, struct sshkey *key)
 {
@@ -451,6 +577,355 @@ pkcs11_key_included(struct sshkey ***keysp, int *nkeys, struct sshkey *key)
        return (0);
 }
 
+static struct sshkey *
+pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
+    CK_OBJECT_HANDLE *obj)
+{
+       CK_ATTRIBUTE             key_attr[3];
+       CK_SESSION_HANDLE        session;
+       CK_FUNCTION_LIST        *f = NULL;
+       CK_RV                    rv;
+       EC_KEY                  *ec = NULL;
+       EC_GROUP                *group = NULL;
+       struct sshkey           *key = NULL;
+       const unsigned char     *attrp = NULL;
+       int                      i;
+       int                      nid;
+
+       memset(&key_attr, 0, sizeof(key_attr));
+       key_attr[0].type = CKA_ID;
+       key_attr[1].type = CKA_EC_POINT;
+       key_attr[2].type = CKA_EC_PARAMS;
+
+       session = p->slotinfo[slotidx].session;
+       f = p->function_list;
+
+       /* figure out size of the attributes */
+       rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
+       if (rv != CKR_OK) {
+               error("C_GetAttributeValue failed: %lu", rv);
+               return (NULL);
+       }
+
+       /*
+        * Allow CKA_ID (always first attribute) to be empty, but
+        * ensure that none of the others are zero length.
+        * XXX assumes CKA_ID is always first.
+        */
+       if (key_attr[1].ulValueLen == 0 ||
+           key_attr[2].ulValueLen == 0) {
+               error("invalid attribute length");
+               return (NULL);
+       }
+
+       /* allocate buffers for attributes */
+       for (i = 0; i < 3; i++)
+               if (key_attr[i].ulValueLen > 0)
+                       key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen);
+
+       /* retrieve ID, public point and curve parameters of EC key */
+       rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
+       if (rv != CKR_OK) {
+               error("C_GetAttributeValue failed: %lu", rv);
+               goto fail;
+       }
+
+       ec = EC_KEY_new();
+       if (ec == NULL) {
+               error("EC_KEY_new failed");
+               goto fail;
+       }
+
+       attrp = key_attr[2].pValue;
+       group = d2i_ECPKParameters(NULL, &attrp, key_attr[2].ulValueLen);
+       if (group == NULL) {
+               ossl_error("d2i_ECPKParameters failed");
+               goto fail;
+       }
+
+       if (EC_KEY_set_group(ec, group) == 0) {
+               ossl_error("EC_KEY_set_group failed");
+               goto fail;
+       }
+
+       if (key_attr[1].ulValueLen <= 2) {
+               error("CKA_EC_POINT too small");
+               goto fail;
+       }
+
+       attrp = (const unsigned char *)key_attr[1].pValue;
+       if (o2i_ECPublicKey(&ec, &attrp, key_attr[1].ulValueLen) == NULL) {
+               /* try to skip DER header (octet string type and length byte) */
+               attrp = (const unsigned char *)key_attr[1].pValue + 2;
+               if (o2i_ECPublicKey(&ec, &attrp, key_attr[1].ulValueLen - 2)
+                   == NULL) {
+                       ossl_error("o2i_ECPublicKey failed");
+                       goto fail;
+               }
+       }
+
+       nid = sshkey_ecdsa_key_to_nid(ec);
+       if (nid < 0) {
+               error("couldn't get curve nid");
+               goto fail;
+       }
+
+       if (pkcs11_ecdsa_wrap(p, slotidx, &key_attr[0], ec))
+               goto fail;
+
+       key = sshkey_new(KEY_UNSPEC);
+       if (key == NULL) {
+               error("sshkey_new failed");
+               goto fail;
+       }
+
+       key->ecdsa = ec;
+       key->ecdsa_nid = nid;
+       key->type = KEY_ECDSA;
+       key->flags |= SSHKEY_FLAG_EXT;
+       ec = NULL;      /* now owned by key */
+
+fail:
+       for (i = 0; i < 3; i++)
+               free(key_attr[i].pValue);
+       if (ec)
+               EC_KEY_free(ec);
+       if (group)
+               EC_GROUP_free(group);
+
+       return (key);
+}
+
+static struct sshkey *
+pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
+    CK_OBJECT_HANDLE *obj)
+{
+       CK_ATTRIBUTE             key_attr[3];
+       CK_SESSION_HANDLE        session;
+       CK_FUNCTION_LIST        *f = NULL;
+       CK_RV                    rv;
+       RSA                     *rsa = NULL;
+       BIGNUM                  *rsa_n, *rsa_e;
+       struct sshkey           *key = NULL;
+       int                      i;
+
+       memset(&key_attr, 0, sizeof(key_attr));
+       key_attr[0].type = CKA_ID;
+       key_attr[1].type = CKA_MODULUS;
+       key_attr[2].type = CKA_PUBLIC_EXPONENT;
+
+       session = p->slotinfo[slotidx].session;
+       f = p->function_list;
+
+       /* figure out size of the attributes */
+       rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
+       if (rv != CKR_OK) {
+               error("C_GetAttributeValue failed: %lu", rv);
+               return (NULL);
+       }
+
+       /*
+        * Allow CKA_ID (always first attribute) to be empty, but
+        * ensure that none of the others are zero length.
+        * XXX assumes CKA_ID is always first.
+        */
+       if (key_attr[1].ulValueLen == 0 ||
+           key_attr[2].ulValueLen == 0) {
+               error("invalid attribute length");
+               return (NULL);
+       }
+
+       /* allocate buffers for attributes */
+       for (i = 0; i < 3; i++)
+               if (key_attr[i].ulValueLen > 0)
+                       key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen);
+
+       /* retrieve ID, modulus and public exponent of RSA key */
+       rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
+       if (rv != CKR_OK) {
+               error("C_GetAttributeValue failed: %lu", rv);
+               goto fail;
+       }
+
+       rsa = RSA_new();
+       if (rsa == NULL) {
+               error("RSA_new failed");
+               goto fail;
+       }
+
+       rsa_n = BN_bin2bn(key_attr[1].pValue, key_attr[1].ulValueLen, NULL);
+       rsa_e = BN_bin2bn(key_attr[2].pValue, key_attr[2].ulValueLen, NULL);
+       if (rsa_n == NULL || rsa_e == NULL) {
+               error("BN_bin2bn failed");
+               goto fail;
+       }
+       if (!RSA_set0_key(rsa, rsa_n, rsa_e, NULL))
+               fatal("%s: set key", __func__);
+       rsa_n = rsa_e = NULL; /* transferred */
+
+       if (pkcs11_rsa_wrap(p, slotidx, &key_attr[0], rsa))
+               goto fail;
+
+       key = sshkey_new(KEY_UNSPEC);
+       if (key == NULL) {
+               error("sshkey_new failed");
+               goto fail;
+       }
+
+       key->rsa = rsa;
+       key->type = KEY_RSA;
+       key->flags |= SSHKEY_FLAG_EXT;
+       rsa = NULL;     /* now owned by key */
+
+fail:
+       for (i = 0; i < 3; i++)
+               free(key_attr[i].pValue);
+       RSA_free(rsa);
+
+       return (key);
+}
+
+static struct sshkey *
+pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
+    CK_OBJECT_HANDLE *obj)
+{
+       CK_ATTRIBUTE             cert_attr[3];
+       CK_SESSION_HANDLE        session;
+       CK_FUNCTION_LIST        *f = NULL;
+       CK_RV                    rv;
+       X509                    *x509 = NULL;
+       EVP_PKEY                *evp;
+       RSA                     *rsa = NULL;
+       EC_KEY                  *ec = NULL;
+       struct sshkey           *key = NULL;
+       int                      i;
+       int                      nid;
+       const u_char             *cp;
+
+       memset(&cert_attr, 0, sizeof(cert_attr));
+       cert_attr[0].type = CKA_ID;
+       cert_attr[1].type = CKA_SUBJECT;
+       cert_attr[2].type = CKA_VALUE;
+
+       session = p->slotinfo[slotidx].session;
+       f = p->function_list;
+
+       /* figure out size of the attributes */
+       rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3);
+       if (rv != CKR_OK) {
+               error("C_GetAttributeValue failed: %lu", rv);
+               return (NULL);
+       }
+
+       /*
+        * Allow CKA_ID (always first attribute) to be empty, but
+        * ensure that none of the others are zero length.
+        * XXX assumes CKA_ID is always first.
+        */
+       if (cert_attr[1].ulValueLen == 0 ||
+           cert_attr[2].ulValueLen == 0) {
+               error("invalid attribute length");
+               return (NULL);
+       }
+
+       /* allocate buffers for attributes */
+       for (i = 0; i < 3; i++)
+               if (cert_attr[i].ulValueLen > 0)
+                       cert_attr[i].pValue = xcalloc(1, cert_attr[i].ulValueLen);
+
+       /* retrieve ID, subject and value of certificate */
+       rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3);
+       if (rv != CKR_OK) {
+               error("C_GetAttributeValue failed: %lu", rv);
+               goto fail;
+       }
+
+       x509 = X509_new();
+       if (x509 == NULL) {
+               error("x509_new failed");
+               goto fail;
+       }
+
+       cp = cert_attr[2].pValue;
+       if (d2i_X509(&x509, &cp, cert_attr[2].ulValueLen) == NULL) {
+               error("d2i_x509 failed");
+               goto fail;
+       }
+
+       evp = X509_get_pubkey(x509);
+       if (evp == NULL) {
+               error("X509_get_pubkey failed");
+               goto fail;
+       }
+
+       if (EVP_PKEY_base_id(evp) == EVP_PKEY_RSA) {
+               if (EVP_PKEY_get0_RSA(evp) == NULL) {
+                       error("invalid x509; no rsa key");
+                       goto fail;
+               }
+               if ((rsa = RSAPublicKey_dup(EVP_PKEY_get0_RSA(evp))) == NULL) {
+                       error("RSAPublicKey_dup failed");
+                       goto fail;
+               }
+
+               if (pkcs11_rsa_wrap(p, slotidx, &cert_attr[0], rsa))
+                       goto fail;
+
+               key = sshkey_new(KEY_UNSPEC);
+               if (key == NULL) {
+                       error("sshkey_new failed");
+                       goto fail;
+               }
+
+               key->rsa = rsa;
+               key->type = KEY_RSA;
+               key->flags |= SSHKEY_FLAG_EXT;
+               rsa = NULL;     /* now owned by key */
+       } else if (EVP_PKEY_base_id(evp) == EVP_PKEY_EC) {
+               /* XXX XXX fix accessor */
+               if (evp->pkey.ec == NULL) {
+                       error("invalid x509; no ec key");
+                       goto fail;
+               }
+               if ((ec = EC_KEY_dup(evp->pkey.ec)) == NULL) {
+                       error("EC_KEY_dup failed");
+                       goto fail;
+               }
+
+               nid = sshkey_ecdsa_key_to_nid(ec);
+               if (nid < 0) {
+                       error("couldn't get curve nid");
+                       goto fail;
+               }
+
+               if (pkcs11_ecdsa_wrap(p, slotidx, &cert_attr[0], ec))
+                       goto fail;
+
+               key = sshkey_new(KEY_UNSPEC);
+               if (key == NULL) {
+                       error("sshkey_new failed");
+                       goto fail;
+               }
+
+               key->ecdsa = ec;
+               key->ecdsa_nid = nid;
+               key->type = KEY_ECDSA;
+               key->flags |= SSHKEY_FLAG_EXT;
+               ec = NULL;      /* now owned by key */
+       } else
+               error("unknown certificate key type");
+
+fail:
+       for (i = 0; i < 3; i++)
+               free(cert_attr[i].pValue);
+       X509_free(x509);
+       RSA_free(rsa);
+       EC_KEY_free(ec);
+
+       return (key);
+}
+
+#if 0
 static int
 have_rsa_key(const RSA *rsa)
 {
@@ -459,140 +934,398 @@ have_rsa_key(const RSA *rsa)
        RSA_get0_key(rsa, &rsa_n, &rsa_e, NULL);
        return rsa_n != NULL && rsa_e != NULL;
 }
+#endif
 
+/*
+ * lookup certificates for token in slot identified by slotidx,
+ * add 'wrapped' public keys to the 'keysp' array and increment nkeys.
+ * keysp points to an (possibly empty) array with *nkeys keys.
+ */
 static int
-pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx,
-    CK_ATTRIBUTE filter[], CK_ATTRIBUTE attribs[3],
+pkcs11_fetch_certs(struct pkcs11_provider *p, CK_ULONG slotidx,
     struct sshkey ***keysp, int *nkeys)
 {
-       struct sshkey           *key;
-       RSA                     *rsa;
-       X509                    *x509;
-       EVP_PKEY                *evp;
-       int                     i;
-       const u_char            *cp;
-       CK_RV                   rv;
-       CK_OBJECT_HANDLE        obj;
-       CK_ULONG                nfound;
-       CK_SESSION_HANDLE       session;
-       CK_FUNCTION_LIST        *f;
+       struct sshkey           *key = NULL;
+       CK_OBJECT_CLASS          key_class;
+       CK_ATTRIBUTE             key_attr[1];
+       CK_SESSION_HANDLE        session;
+       CK_FUNCTION_LIST        *f = NULL;
+       CK_RV                    rv;
+       CK_OBJECT_HANDLE         obj;
+       CK_ULONG                 n = 0;
+       int                      ret = -1;
+
+       memset(&key_attr, 0, sizeof(key_attr));
+       memset(&obj, 0, sizeof(obj));
+
+       key_class = CKO_CERTIFICATE;
+       key_attr[0].type = CKA_CLASS;
+       key_attr[0].pValue = &key_class;
+       key_attr[0].ulValueLen = sizeof(key_class);
 
-       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) {
+       f = p->function_list;
+
+       rv = f->C_FindObjectsInit(session, key_attr, 1);
+       if (rv != CKR_OK) {
                error("C_FindObjectsInit failed: %lu", rv);
-               return (-1);
+               goto fail;
        }
+
        while (1) {
-               /* XXX 3 attributes in attribs[] */
-               for (i = 0; i < 3; i++) {
-                       attribs[i].pValue = NULL;
-                       attribs[i].ulValueLen = 0;
+               CK_CERTIFICATE_TYPE     ck_cert_type;
+
+               rv = f->C_FindObjects(session, &obj, 1, &n);
+               if (rv != CKR_OK) {
+                       error("C_FindObjects failed: %lu", rv);
+                       goto fail;
                }
-               if ((rv = f->C_FindObjects(session, &obj, 1, &nfound)) != CKR_OK
-                   || nfound == 0)
+               if (n == 0)
                        break;
-               /* found a key, so figure out size of the attributes */
-               if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3))
-                   != CKR_OK) {
+
+               memset(&ck_cert_type, 0, sizeof(ck_cert_type));
+               memset(&key_attr, 0, sizeof(key_attr));
+               key_attr[0].type = CKA_CERTIFICATE_TYPE;
+               key_attr[0].pValue = &ck_cert_type;
+               key_attr[0].ulValueLen = sizeof(ck_cert_type);
+
+               rv = f->C_GetAttributeValue(session, obj, key_attr, 1);
+               if (rv != CKR_OK) {
                        error("C_GetAttributeValue failed: %lu", rv);
-                       continue;
+                       goto fail;
                }
-               /*
-                * Allow CKA_ID (always first attribute) to be empty, but
-                * ensure that none of the others are zero length.
-                * XXX assumes CKA_ID is always first.
-                */
-               if (attribs[1].ulValueLen == 0 ||
-                   attribs[2].ulValueLen == 0) {
+
+               switch (ck_cert_type) {
+               case CKC_X_509:
+                       key = pkcs11_fetch_x509_pubkey(p, slotidx, &obj);
+                       break;
+               default:
+                       /* XXX print key type? */
+                       error("skipping unsupported certificate type");
+               }
+
+               if (key == NULL) {
+                       error("failed to fetch key");
                        continue;
                }
-               /* allocate buffers for attributes */
-               for (i = 0; i < 3; i++) {
-                       if (attribs[i].ulValueLen > 0) {
-                               attribs[i].pValue = xmalloc(
-                                   attribs[i].ulValueLen);
-                       }
+
+               if (pkcs11_key_included(keysp, nkeys, key)) {
+                       sshkey_free(key);
+               } else {
+                       /* expand key array and add key */
+                       *keysp = xrecallocarray(*keysp, *nkeys,
+                           *nkeys + 1, sizeof(struct sshkey *));
+                       (*keysp)[*nkeys] = key;
+                       *nkeys = *nkeys + 1;
+                       debug("have %d keys", *nkeys);
                }
+       }
 
-               /*
-                * retrieve ID, modulus and public exponent of RSA key,
-                * or ID, subject and value for certificates.
-                */
-               rsa = NULL;
-               if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3))
-                   != CKR_OK) {
+       ret = 0;
+fail:
+       rv = f->C_FindObjectsFinal(session);
+       if (rv != CKR_OK) {
+               error("C_FindObjectsFinal failed: %lu", rv);
+               ret = -1;
+       }
+
+       return (ret);
+}
+
+/*
+ * lookup public keys for token in slot identified by slotidx,
+ * add 'wrapped' public keys to the 'keysp' array and increment nkeys.
+ * keysp points to an (possibly empty) array with *nkeys keys.
+ */
+static int
+pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx,
+    struct sshkey ***keysp, int *nkeys)
+{
+       struct sshkey           *key = NULL;
+       CK_OBJECT_CLASS          key_class;
+       CK_ATTRIBUTE             key_attr[1];
+       CK_SESSION_HANDLE        session;
+       CK_FUNCTION_LIST        *f = NULL;
+       CK_RV                    rv;
+       CK_OBJECT_HANDLE         obj;
+       CK_ULONG                 n = 0;
+       int                      ret = -1;
+
+       memset(&key_attr, 0, sizeof(key_attr));
+       memset(&obj, 0, sizeof(obj));
+
+       key_class = CKO_PUBLIC_KEY;
+       key_attr[0].type = CKA_CLASS;
+       key_attr[0].pValue = &key_class;
+       key_attr[0].ulValueLen = sizeof(key_class);
+
+       session = p->slotinfo[slotidx].session;
+       f = p->function_list;
+
+       rv = f->C_FindObjectsInit(session, key_attr, 1);
+       if (rv != CKR_OK) {
+               error("C_FindObjectsInit failed: %lu", rv);
+               goto fail;
+       }
+
+       while (1) {
+               CK_KEY_TYPE     ck_key_type;
+
+               rv = f->C_FindObjects(session, &obj, 1, &n);
+               if (rv != CKR_OK) {
+                       error("C_FindObjects failed: %lu", rv);
+                       goto fail;
+               }
+               if (n == 0)
+                       break;
+
+               memset(&ck_key_type, 0, sizeof(ck_key_type));
+               memset(&key_attr, 0, sizeof(key_attr));
+               key_attr[0].type = CKA_KEY_TYPE;
+               key_attr[0].pValue = &ck_key_type;
+               key_attr[0].ulValueLen = sizeof(ck_key_type);
+
+               rv = f->C_GetAttributeValue(session, obj, key_attr, 1);
+               if (rv != CKR_OK) {
                        error("C_GetAttributeValue failed: %lu", rv);
-               } else if (attribs[1].type == CKA_MODULUS ) {
-                       if ((rsa = RSA_new()) == NULL) {
-                               error("RSA_new failed");
-                       } else {
-                               BIGNUM *rsa_n, *rsa_e;
-
-                               rsa_n = BN_bin2bn(attribs[1].pValue,
-                                   attribs[1].ulValueLen, NULL);
-                               rsa_e = BN_bin2bn(attribs[2].pValue,
-                                   attribs[2].ulValueLen, NULL);
-                               if (rsa_n != NULL && rsa_e != NULL) {
-                                       if (!RSA_set0_key(rsa,
-                                           rsa_n, rsa_e, NULL))
-                                               fatal("%s: set key", __func__);
-                                       rsa_n = rsa_e = NULL; /* transferred */
-                               }
-                               BN_free(rsa_n);
-                               BN_free(rsa_e);
-                       }
+                       goto fail;
+               }
+
+               switch (ck_key_type) {
+               case CKK_RSA:
+                       key = pkcs11_fetch_rsa_pubkey(p, slotidx, &obj);
+                       break;
+               case CKK_ECDSA:
+                       key = pkcs11_fetch_ecdsa_pubkey(p, slotidx, &obj);
+                       break;
+               default:
+                       /* XXX print key type? */
+                       error("skipping unsupported key type");
+               }
+
+               if (key == NULL) {
+                       error("failed to fetch key");
+                       continue;
+               }
+
+               if (pkcs11_key_included(keysp, nkeys, key)) {
+                       sshkey_free(key);
                } else {
-                       cp = attribs[2].pValue;
-                       if ((x509 = X509_new()) == NULL) {
-                               error("X509_new failed");
-                       } else if (d2i_X509(&x509, &cp, attribs[2].ulValueLen)
-                           == NULL) {
-                               error("d2i_X509 failed");
-                       } else if ((evp = X509_get_pubkey(x509)) == NULL ||
-                           EVP_PKEY_base_id(evp) != EVP_PKEY_RSA ||
-                           EVP_PKEY_get0_RSA(evp) == NULL) {
-                               debug("X509_get_pubkey failed or no rsa");
-                       } else if ((rsa = RSAPublicKey_dup(
-                           EVP_PKEY_get0_RSA(evp))) == NULL) {
-                               error("RSAPublicKey_dup");
-                       }
-                       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;
-                       if (pkcs11_key_included(keysp, nkeys, key)) {
-                               sshkey_free(key);
-                       } else {
-                               /* expand key array and add key */
-                               *keysp = xrecallocarray(*keysp, *nkeys,
-                                   *nkeys + 1, sizeof(struct sshkey *));
-                               (*keysp)[*nkeys] = key;
-                               *nkeys = *nkeys + 1;
-                               debug("have %d keys", *nkeys);
-                       }
-               } else if (rsa) {
-                       RSA_free(rsa);
-               }
-               for (i = 0; i < 3; i++)
-                       free(attribs[i].pValue);
+                       /* expand key array and add key */
+                       *keysp = xrecallocarray(*keysp, *nkeys,
+                           *nkeys + 1, sizeof(struct sshkey *));
+                       (*keysp)[*nkeys] = key;
+                       *nkeys = *nkeys + 1;
+                       debug("have %d keys", *nkeys);
+               }
        }
-       if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK)
+
+       ret = 0;
+fail:
+       rv = f->C_FindObjectsFinal(session);
+       if (rv != CKR_OK) {
                error("C_FindObjectsFinal failed: %lu", rv);
-       return (0);
+               ret = -1;
+       }
+
+       return (ret);
 }
 
-/* register a new provider, fails if provider already exists */
-int
-pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp)
+#ifdef WITH_PKCS11_KEYGEN
+#define FILL_ATTR(attr, idx, typ, val, len) \
+       { (attr[idx]).type=(typ); (attr[idx]).pValue=(val); (attr[idx]).ulValueLen=len; idx++; }
+
+static struct sshkey *
+pkcs11_rsa_generate_private_key(struct pkcs11_provider *p, CK_ULONG slotidx,
+    char *label, CK_ULONG bits, CK_BYTE keyid, u_int32_t *err)
+{
+       struct pkcs11_slotinfo  *si;
+       char                    *plabel = label ? label : "";
+       int                      npub = 0, npriv = 0;
+       CK_RV                    rv;
+       CK_FUNCTION_LIST        *f;
+       CK_SESSION_HANDLE        session;
+       CK_BBOOL                 true_val = CK_TRUE, false_val = CK_FALSE;
+       CK_OBJECT_HANDLE         pubKey, privKey;
+       CK_ATTRIBUTE             tpub[16], tpriv[16];
+       CK_MECHANISM             mech = {
+           CKM_RSA_PKCS_KEY_PAIR_GEN, NULL_PTR, 0
+       };
+       CK_BYTE                  pubExponent[] = {
+           0x01, 0x00, 0x01 /* RSA_F4 in bytes */
+       };
+       pubkey_filter[0].pValue = &pubkey_class;
+       cert_filter[0].pValue = &cert_class;
+
+       *err = 0;
+
+       FILL_ATTR(tpub, npub, CKA_TOKEN, &true_val, sizeof(true_val));
+       FILL_ATTR(tpub, npub, CKA_LABEL, plabel, strlen(plabel));
+       FILL_ATTR(tpub, npub, CKA_ENCRYPT, &false_val, sizeof(false_val));
+       FILL_ATTR(tpub, npub, CKA_VERIFY, &true_val, sizeof(true_val));
+       FILL_ATTR(tpub, npub, CKA_VERIFY_RECOVER, &false_val,
+           sizeof(false_val));
+       FILL_ATTR(tpub, npub, CKA_WRAP, &false_val, sizeof(false_val));
+       FILL_ATTR(tpub, npub, CKA_DERIVE, &false_val, sizeof(false_val));
+       FILL_ATTR(tpub, npub, CKA_MODULUS_BITS, &bits, sizeof(bits));
+       FILL_ATTR(tpub, npub, CKA_PUBLIC_EXPONENT, pubExponent,
+           sizeof(pubExponent));
+       FILL_ATTR(tpub, npub, CKA_ID, &keyid, sizeof(keyid));
+
+       FILL_ATTR(tpriv, npriv, CKA_TOKEN,  &true_val, sizeof(true_val));
+       FILL_ATTR(tpriv, npriv, CKA_LABEL,  plabel, strlen(plabel));
+       FILL_ATTR(tpriv, npriv, CKA_PRIVATE,  &true_val, sizeof(true_val));
+       FILL_ATTR(tpriv, npriv, CKA_SENSITIVE,  &true_val, sizeof(true_val));
+       FILL_ATTR(tpriv, npriv, CKA_DECRYPT,  &false_val, sizeof(false_val));
+       FILL_ATTR(tpriv, npriv, CKA_SIGN,  &true_val, sizeof(true_val));
+       FILL_ATTR(tpriv, npriv, CKA_SIGN_RECOVER,  &false_val,
+           sizeof(false_val));
+       FILL_ATTR(tpriv, npriv, CKA_UNWRAP,  &false_val, sizeof(false_val));
+       FILL_ATTR(tpriv, npriv, CKA_DERIVE,  &false_val, sizeof(false_val));
+       FILL_ATTR(tpriv, npriv, CKA_ID, &keyid, sizeof(keyid));
+
+       f = p->function_list;
+       si = &p->slotinfo[slotidx];
+       session = si->session;
+
+       if ((rv = f->C_GenerateKeyPair(session, &mech, tpub, npub, tpriv, npriv,
+           &pubKey, &privKey)) != CKR_OK) {
+               error("%s: key generation failed: error 0x%lx", __func__, rv);
+               *err = rv;
+               return NULL;
+       }
+
+       return pkcs11_fetch_rsa_pubkey(p, slotidx, &pubKey);
+}
+
+static int
+pkcs11_decode_hex(const char *hex, unsigned char **dest, size_t *rlen)
+{
+       size_t  i, len;
+       char    ptr[3];
+
+       if (dest)
+               *dest = NULL;
+       if (rlen)
+               *rlen = 0;
+
+       if ((len = strlen(hex)) % 2)
+               return -1;
+       len /= 2;
+
+       *dest = xmalloc(len);
+
+       ptr[2] = '\0';
+       for (i = 0; i < len; i++) {
+               ptr[0] = hex[2 * i];
+               ptr[1] = hex[(2 * i) + 1];
+               if (!isxdigit(ptr[0]) || !isxdigit(ptr[1]))
+                       return -1;
+               (*dest)[i] = (unsigned char)strtoul(ptr, NULL, 16);
+       }
+
+       if (rlen)
+               *rlen = len;
+
+       return 0;
+}
+
+static struct ec_curve_info {
+       const char      *name;
+       const char      *oid;
+       const char      *oid_encoded;
+       size_t           size;
+} ec_curve_infos[] = {
+       {"prime256v1",  "1.2.840.10045.3.1.7",  "06082A8648CE3D030107", 256},
+       {"secp384r1",   "1.3.132.0.34",         "06052B81040022",       384},
+       {"secp521r1",   "1.3.132.0.35",         "06052B81040023",       521},
+        {NULL,         NULL,                   NULL,                   0},
+};
+
+static struct sshkey *
+pkcs11_ecdsa_generate_private_key(struct pkcs11_provider *p, CK_ULONG slotidx,
+    char *label, CK_ULONG bits, CK_BYTE keyid, u_int32_t *err)
+{
+       struct pkcs11_slotinfo  *si;
+       char                    *plabel = label ? label : "";
+       int                      i;
+       size_t                   ecparams_size;
+       unsigned char           *ecparams = NULL;
+       int                      npub = 0, npriv = 0;
+       CK_RV                    rv;
+       CK_FUNCTION_LIST        *f;
+       CK_SESSION_HANDLE        session;
+       CK_BBOOL                 true_val = CK_TRUE, false_val = CK_FALSE;
+       CK_OBJECT_HANDLE         pubKey, privKey;
+       CK_MECHANISM             mech = {
+           CKM_EC_KEY_PAIR_GEN, NULL_PTR, 0
+       };
+       CK_ATTRIBUTE             tpub[16], tpriv[16];
+
+       *err = 0;
+
+       for (i = 0; ec_curve_infos[i].name; i++) {
+               if (ec_curve_infos[i].size == bits)
+                       break;
+       }
+       if (!ec_curve_infos[i].name) {
+               error("%s: invalid key size %lu", __func__, bits);
+               return NULL;
+       }
+       if (pkcs11_decode_hex(ec_curve_infos[i].oid_encoded, &ecparams,
+           &ecparams_size) == -1) {
+               error("%s: invalid oid", __func__);
+               return NULL;
+       }
+
+       FILL_ATTR(tpub, npub, CKA_TOKEN, &true_val, sizeof(true_val));
+       FILL_ATTR(tpub, npub, CKA_LABEL, plabel, strlen(plabel));
+       FILL_ATTR(tpub, npub, CKA_ENCRYPT, &false_val, sizeof(false_val));
+       FILL_ATTR(tpub, npub, CKA_VERIFY, &true_val, sizeof(true_val));
+       FILL_ATTR(tpub, npub, CKA_VERIFY_RECOVER, &false_val,
+           sizeof(false_val));
+       FILL_ATTR(tpub, npub, CKA_WRAP, &false_val, sizeof(false_val));
+       FILL_ATTR(tpub, npub, CKA_DERIVE, &false_val, sizeof(false_val));
+       FILL_ATTR(tpub, npub, CKA_EC_PARAMS, ecparams, ecparams_size);
+       FILL_ATTR(tpub, npub, CKA_ID, &keyid, sizeof(keyid));
+
+       FILL_ATTR(tpriv, npriv, CKA_TOKEN, &true_val, sizeof(true_val));
+       FILL_ATTR(tpriv, npriv, CKA_LABEL, plabel, strlen(plabel));
+       FILL_ATTR(tpriv, npriv, CKA_PRIVATE, &true_val, sizeof(true_val));
+       FILL_ATTR(tpriv, npriv, CKA_SENSITIVE, &true_val, sizeof(true_val));
+       FILL_ATTR(tpriv, npriv, CKA_DECRYPT, &false_val, sizeof(false_val));
+       FILL_ATTR(tpriv, npriv, CKA_SIGN, &true_val, sizeof(true_val));
+       FILL_ATTR(tpriv, npriv, CKA_SIGN_RECOVER, &false_val,
+           sizeof(false_val));
+       FILL_ATTR(tpriv, npriv, CKA_UNWRAP, &false_val, sizeof(false_val));
+       FILL_ATTR(tpriv, npriv, CKA_DERIVE, &false_val, sizeof(false_val));
+       FILL_ATTR(tpriv, npriv, CKA_ID, &keyid, sizeof(keyid));
+
+       f = p->function_list;
+       si = &p->slotinfo[slotidx];
+       session = si->session;
+
+       if ((rv = f->C_GenerateKeyPair(session, &mech, tpub, npub, tpriv, npriv,
+           &pubKey, &privKey)) != CKR_OK) {
+               error("%s: key generation failed: error 0x%lx", __func__, rv);
+               *err = rv;
+               return NULL;
+       }
+
+       return pkcs11_fetch_ecdsa_pubkey(p, slotidx, &pubKey);
+}
+#endif /* WITH_PKCS11_KEYGEN */
+
+/*
+ * register a new provider, fails if provider already exists. if
+ * keyp is provided, fetch keys.
+ */
+static int
+pkcs11_register_provider(char *provider_id, char *pin, struct sshkey ***keyp,
+    struct pkcs11_provider **providerp, CK_ULONG user)
 {
        int nkeys, need_finalize = 0;
+       int ret = -1;
        struct pkcs11_provider *p = NULL;
        void *handle = NULL;
        CK_RV (*getfunctionlist)(CK_FUNCTION_LIST **);
@@ -601,13 +1334,19 @@ pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp)
        CK_TOKEN_INFO *token;
        CK_ULONG i;
 
-       *keyp = NULL;
+       if (providerp == NULL)
+               goto fail;
+       *providerp = NULL;
+
+       if (keyp != NULL)
+               *keyp = NULL;
+
        if (pkcs11_provider_lookup(provider_id) != NULL) {
                debug("%s: provider already registered: %s",
                    __func__, provider_id);
                goto fail;
        }
-       /* open shared pkcs11-libarary */
+       /* open shared pkcs11-library */
        if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) {
                error("dlopen %s failed: %s", provider_id, dlerror());
                goto fail;
@@ -653,8 +1392,9 @@ pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp)
                goto fail;
        }
        if (p->nslots == 0) {
-               debug("%s: provider %s returned no slots", __func__,
+               error("%s: provider %s returned no slots", __func__,
                    provider_id);
+               ret = -SSH_PKCS11_ERR_NO_SLOTS;
                goto fail;
        }
        p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID));
@@ -690,43 +1430,251 @@ pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp)
                    provider_id, (unsigned long)i,
                    token->label, token->manufacturerID, token->model,
                    token->serialNumber, token->flags);
-               /* open session, login with pin and retrieve public keys */
-               if (pkcs11_open_session(p, i, pin) == 0)
+               /*
+                * open session, login with pin and retrieve public
+                * keys (if keyp is provided)
+                */
+               if ((ret = pkcs11_open_session(p, i, pin, user)) == 0) {
+                       if (keyp == NULL)
+                               continue;
                        pkcs11_fetch_keys(p, i, keyp, &nkeys);
+                       pkcs11_fetch_certs(p, i, keyp, &nkeys);
+               }
        }
-       if (nkeys > 0) {
-               TAILQ_INSERT_TAIL(&pkcs11_providers, p, next);
-               p->refcount++;  /* add to provider list */
-               return (nkeys);
-       }
-       debug("%s: provider %s returned no keys", __func__, provider_id);
-       /* don't add the provider, since it does not have any keys */
+
+       /* now owned by caller */
+       *providerp = p;
+
+       TAILQ_INSERT_TAIL(&pkcs11_providers, p, next);
+       p->refcount++;  /* add to provider list */
+
+       return (nkeys);
 fail:
        if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK)
                error("C_Finalize for provider %s failed: %lu",
                    provider_id, rv);
        if (p) {
+               free(p->name);
                free(p->slotlist);
                free(p->slotinfo);
                free(p);
        }
        if (handle)
                dlclose(handle);
-       return (-1);
+       return (ret);
 }
 
-#else
+/*
+ * register a new provider and get number of keys hold by the token,
+ * fails if provider already exists
+ */
+int
+pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp)
+{
+       struct pkcs11_provider *p = NULL;
+       int nkeys;
+
+       nkeys = pkcs11_register_provider(provider_id, pin, keyp, &p, CKU_USER);
 
+       /* no keys found or some other error, de-register provider */
+       if (nkeys <= 0 && p != NULL) {
+               TAILQ_REMOVE(&pkcs11_providers, p, next);
+               pkcs11_provider_finalize(p);
+               pkcs11_provider_unref(p);
+       }
+       if (nkeys == 0)
+               debug("%s: provider %s returned no keys", __func__,
+                   provider_id);
+
+       return (nkeys);
+}
+
+#ifdef WITH_PKCS11_KEYGEN
+struct sshkey *
+pkcs11_gakp(char *provider_id, char *pin, unsigned int slotidx, char *label,
+    unsigned int type, unsigned int bits, unsigned char keyid, u_int32_t *err)
+{
+       struct pkcs11_provider  *p = NULL;
+       struct pkcs11_slotinfo  *si;
+       CK_FUNCTION_LIST        *f;
+       CK_SESSION_HANDLE        session;
+       struct sshkey           *k = NULL;
+       int                      ret = -1, reset_pin = 0, reset_provider = 0;
+       CK_RV                    rv;
+
+       *err = 0;
+
+       if ((p = pkcs11_provider_lookup(provider_id)) != NULL)
+               debug("%s: provider \"%s\" available", __func__, provider_id);
+       else if ((ret = pkcs11_register_provider(provider_id, pin, NULL, &p,
+           CKU_SO)) < 0) {
+               debug("%s: could not register provider %s", __func__,
+                   provider_id);
+               goto out;
+       } else
+               reset_provider = 1;
+
+       f = p->function_list;
+       si = &p->slotinfo[slotidx];
+       session = si->session;
+
+       if ((rv = f->C_SetOperationState(session , pin, strlen(pin),
+           CK_INVALID_HANDLE, CK_INVALID_HANDLE)) != CKR_OK) {
+               debug("%s: could not supply SO pin: %lu", __func__, rv);
+               reset_pin = 0;
+       } else
+               reset_pin = 1;
+
+       switch (type) {
+       case KEY_RSA:
+               if ((k = pkcs11_rsa_generate_private_key(p, slotidx, label,
+                   bits, keyid, err)) == NULL) {
+                       debug("%s: failed to generate RSA key", __func__);
+                       goto out;
+               }
+               break;
+       case KEY_ECDSA:
+               if ((k = pkcs11_ecdsa_generate_private_key(p, slotidx, label,
+                   bits, keyid, err)) == NULL) {
+                       debug("%s: failed to generate ECDSA key", __func__);
+                       goto out;
+               }
+               break;
+       default:
+               *err = SSH_PKCS11_ERR_GENERIC;
+               debug("%s: unknown type %d", __func__, type);
+               goto out;
+       }
+
+out:
+       if (reset_pin)
+               f->C_SetOperationState(session , NULL, 0, CK_INVALID_HANDLE,
+                   CK_INVALID_HANDLE);
+
+       if (reset_provider)
+               pkcs11_del_provider(provider_id);
+
+       return (k);
+}
+
+struct sshkey *
+pkcs11_destroy_keypair(char *provider_id, char *pin, unsigned long slotidx,
+    unsigned char keyid, u_int32_t *err)
+{
+       struct pkcs11_provider  *p = NULL;
+       struct pkcs11_slotinfo  *si;
+       struct sshkey           *k = NULL;
+       int                      reset_pin = 0, reset_provider = 0;
+       CK_ULONG                 nattrs;
+       CK_FUNCTION_LIST        *f;
+       CK_SESSION_HANDLE        session;
+       CK_ATTRIBUTE             attrs[16];
+       CK_OBJECT_CLASS          key_class;
+       CK_KEY_TYPE              key_type;
+       CK_OBJECT_HANDLE         obj = CK_INVALID_HANDLE;
+       CK_RV                    rv;
+
+       *err = 0;
+
+       if ((p = pkcs11_provider_lookup(provider_id)) != NULL) {
+               debug("%s: using provider \"%s\"", __func__, provider_id);
+       } else if (pkcs11_register_provider(provider_id, pin, NULL, &p,
+           CKU_SO) < 0) {
+               debug("%s: could not register provider %s", __func__,
+                   provider_id);
+               goto out;
+       } else
+               reset_provider = 1;
+
+       f = p->function_list;
+       si = &p->slotinfo[slotidx];
+       session = si->session;
+
+       if ((rv = f->C_SetOperationState(session , pin, strlen(pin),
+           CK_INVALID_HANDLE, CK_INVALID_HANDLE)) != CKR_OK) {
+               debug("%s: could not supply SO pin: %lu", __func__, rv);
+               reset_pin = 0;
+       } else
+               reset_pin = 1;
+
+       /* private key */
+       nattrs = 0;
+       key_class = CKO_PRIVATE_KEY;
+       FILL_ATTR(attrs, nattrs, CKA_CLASS, &key_class, sizeof(key_class));
+       FILL_ATTR(attrs, nattrs, CKA_ID, &keyid, sizeof(keyid));
+
+       if (pkcs11_find(p, slotidx, attrs, nattrs, &obj) == 0 &&
+           obj != CK_INVALID_HANDLE) {
+               if ((rv = f->C_DestroyObject(session, obj)) != CKR_OK) {
+                       debug("%s: could not destroy private key 0x%hhx",
+                           __func__, keyid);
+                       *err = rv;
+                       goto out;
+               }
+       }
+
+       /* public key */
+       nattrs = 0;
+       key_class = CKO_PUBLIC_KEY;
+       FILL_ATTR(attrs, nattrs, CKA_CLASS, &key_class, sizeof(key_class));
+       FILL_ATTR(attrs, nattrs, CKA_ID, &keyid, sizeof(keyid));
+
+       if (pkcs11_find(p, slotidx, attrs, nattrs, &obj) == 0 &&
+           obj != CK_INVALID_HANDLE) {
+
+               /* get key type */
+               nattrs = 0;
+               FILL_ATTR(attrs, nattrs, CKA_KEY_TYPE, &key_type,
+                   sizeof(key_type));
+               rv = f->C_GetAttributeValue(session, obj, attrs, nattrs);
+               if (rv != CKR_OK) {
+                       debug("%s: could not get key type of public key 0x%hhx",
+                           __func__, keyid);
+                       *err = rv;
+                       key_type = -1;
+               }
+               if (key_type == CKK_RSA)
+                       k = pkcs11_fetch_rsa_pubkey(p, slotidx, &obj);
+               else if (key_type == CKK_ECDSA)
+                       k = pkcs11_fetch_ecdsa_pubkey(p, slotidx, &obj);
+
+               if ((rv = f->C_DestroyObject(session, obj)) != CKR_OK) {
+                       debug("%s: could not destroy public key 0x%hhx",
+                           __func__, keyid);
+                       *err = rv;
+                       goto out;
+               }
+       }
+
+out:
+       if (reset_pin)
+               f->C_SetOperationState(session , NULL, 0, CK_INVALID_HANDLE,
+                   CK_INVALID_HANDLE);
+
+       if (reset_provider)
+               pkcs11_del_provider(provider_id);
+
+       return (k);
+}
+#endif /* WITH_PKCS11_KEYGEN */
+#else /* HAVE_DLOPEN */
 int
 pkcs11_init(int interactive)
 {
-       return (0);
+       error("%s: dlopen() not supported", __func__);
+       return (-1);
+}
+
+int
+pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp)
+{
+       error("%s: dlopen() not supported", __func__);
+       return (-1);
 }
 
 void
 pkcs11_terminate(void)
 {
-       return;
+       error("%s: dlopen() not supported", __func__);
 }
-
-#endif /* ENABLE_PKCS11 */
+#endif /* HAVE_DLOPEN */
index 0ced74f29ce732859bfdb1942005a191a30e9118..b9038450da8cf9ee908a07159b68e59fa3570c93 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-pkcs11.h,v 1.4 2015/01/15 09:40:00 djm Exp $ */
+/* $OpenBSD: ssh-pkcs11.h,v 1.5 2019/01/20 22:51:37 djm Exp $ */
 /*
  * Copyright (c) 2010 Markus Friedl.  All rights reserved.
  *
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
+
+/* Errors for pkcs11_add_provider() */
+#define        SSH_PKCS11_ERR_GENERIC                  1
+#define        SSH_PKCS11_ERR_LOGIN_FAIL               2
+#define        SSH_PKCS11_ERR_NO_SLOTS                 3
+#define        SSH_PKCS11_ERR_PIN_REQUIRED             4
+#define        SSH_PKCS11_ERR_PIN_LOCKED               5
+
 int    pkcs11_init(int);
 void   pkcs11_terminate(void);
 int    pkcs11_add_provider(char *, char *, struct sshkey ***);
 int    pkcs11_del_provider(char *);
+#ifdef WITH_PKCS11_KEYGEN
+struct sshkey *
+       pkcs11_gakp(char *, char *, unsigned int, char *, unsigned int,
+           unsigned int, unsigned char, u_int32_t *);
+struct sshkey *
+       pkcs11_destroy_keypair(char *, char *, unsigned long, unsigned char,
+           u_int32_t *);
+#endif
 
 #if !defined(WITH_OPENSSL) && defined(ENABLE_PKCS11)
 #undef ENABLE_PKCS11
index f6a007fdff35c41b7300c2eb0e3cbc13916083aa..a91e60436358f49370a9962b70a2e7d204ac92f5 100644 (file)
--- a/sshkey.h
+++ b/sshkey.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshkey.h,v 1.30 2018/09/14 04:17:44 djm Exp $ */
+/* $OpenBSD: sshkey.h,v 1.31 2019/01/20 22:51:37 djm Exp $ */
 
 /*
  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
@@ -33,6 +33,7 @@
 #include <openssl/dsa.h>
 # ifdef OPENSSL_HAS_ECC
 #  include <openssl/ec.h>
+#  include <openssl/ecdsa.h>
 # else /* OPENSSL_HAS_ECC */
 #  define EC_KEY       void
 #  define EC_GROUP     void