Wanted: smartcard with ECDSA support

Thomas Calderon calderon.thomas at gmail.com
Wed Apr 1 08:34:59 AEDT 2015


Hi Pedro, hi Markus,

Haha ! Great that you guys had already worked on supporting other signature
mechanisms !
In any case, coding an alternative version allowed me to understand how the
PKCS#11 support was implemented in OpenSSH.

We followed a similar approach, but I made it work with fewer modifications
to ssh-pkcs11.c but it is less complete. I saw that you split the code to
isolate pkcs11 functions by the type of object that should be fetched, it
makes more sense and improve code readability.

I will try your version tomorrow and look more closely at differences. I
will also check if we can manipulate the CCRYPTO_ex_data as pointed by
Douglas to have a valid freeing mechanism.

Cheers,

Thomas

On Tue, Mar 31, 2015 at 8:26 PM, Pedro Martelletto <pedro at ambientworks.net>
wrote:

> Hi,
>
> Here's a copy of the mail sent to Markus, Damien and Miod back in
> December. It is also available at the following URL:
>
> http://ambientworks.net/ecdsa-ssh.txt
>
> The OpenSC work mentioned by Markus can be found at:
>
> https://github.com/OpenSC/OpenSC/pull/283
>
> -p.
>
> From pedro at ambientworks.net Thu Dec 18 18:05:54 2014
> Return-Path: <pedro at ambientworks.net>
> Received: from triangle.ambientworks.net (ambientworks.net.
> [195.154.11.238])
>        by mx.google.com with ESMTPSA id dp8sm25399482wib.20.2014.12.
> 18.09.05.53
>        (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);
>        Thu, 18 Dec 2014 09:05:54 -0800 (PST)
> Date: Thu, 18 Dec 2014 18:05:09 +0100
> From: pedro martelletto <pedro at ambientworks.net>
> To: djm at openbsd.org, miod at openbsd.org, markus at openbsd.org
> Subject: PKCS#11 ECDSA support in OpenSSH, and related LibreSSL changes
> Message-ID: <20141218170508.GA25923 at triangle.ambientworks.net>
> MIME-Version: 1.0
> Content-Type: text/plain; charset=us-ascii; format=flowed
> Content-Disposition: inline
>
> Hi Damien and Miod,
>
> I have recently worked with Markus on implementing PKCS#11 ECDSA support
> in OpenSSH. In order to do this, we needed to be able to wrap ECDSA key
> objects, providing them with a customised callback for signing. This,
> however, wasn't possible due to the definition of ECDSA_METHOD not being
> exported by LibreSSL. We then looked around and found this OpenSSL bug
> report [1] which, after remaining inactive for years, was finally
> updated yesterday, 2014-12-17.
>
> As can be seen in the commits linked from the bug report [2,3], the
> course taken by OpenSSL was diferent than the one adopted by LibreSSL [4].
> Whether or not OpenSSL's decision makes sense, I am not sure. It would
> arguably make sense though, for the sake of a similar API, for LibreSSL
> to provide the same set of functions.
>
> In the first of the three patches inlined below, I took the interim
> decision of reverting Miod's commit to expose ECDSA_METHOD, with the
> understanding that it would be best to copy the OpenSSL commits verbatim
> and leave any merging decisions up to you.
>
> The second diff consists of an amalgamation of the two OpenSSL commits
> [2,3]. Finally, the third diff implements the PKCS#11 ECDSA bits in
> OpenSSH.
>
> Warm regards and season's greetings! :)
>
> -p.
>
> [1] http://rt.openssl.org/Ticket/Display.html?id=2459&user=
> guest&pass=guest
> [2] https://github.com/openssl/openssl/commit/94c2f77a
> [3] https://github.com/openssl/openssl/commit/387b844f
> [4] http://freshbsd.org/commit/openbsd/1c8c6abf87634651e430450659b62b
> 34a2cb63aa
>
> commit 1623ab45684d7bd034001d518d70ec250de7d398
> Author: pedro martelletto <pedro at ambientworks.net>
> Date:   Thu Dec 18 17:13:19 2014 +0100
>
>    Revert "Make the ECDSA_SIG bowels public. This matches RSA_SIG and
> DSA_SIG, and we"
>       This reverts commit 7075e35e197bdac9ca93b09442bc1b2436fc94e8.
>
> diff --git lib/libssl/src/crypto/ecdsa/ecdsa.h
> lib/libssl/src/crypto/ecdsa/ecdsa.h
> index ad716c2..a29e5f6 100644
> --- lib/libssl/src/crypto/ecdsa/ecdsa.h
> +++ lib/libssl/src/crypto/ecdsa/ecdsa.h
> @@ -1,4 +1,4 @@
> -/* $OpenBSD: ecdsa.h,v 1.3 2014/11/17 20:25:50 miod Exp $ */
> +/* $OpenBSD: ecdsa.h,v 1.2 2014/06/12 15:49:29 deraadt Exp $ */
> /**
>  * \file   crypto/ecdsa/ecdsa.h Include file for the OpenSSL ECDSA
> functions
>  * \author Written by Nils Larsch for the OpenSSL project
> @@ -75,36 +75,11 @@
> extern "C" {
> #endif
>
> -typedef struct ECDSA_SIG_st ECDSA_SIG;
> -
> -struct ecdsa_method {
> -       const char *name;
> -       ECDSA_SIG *(*ecdsa_do_sign)(const unsigned char *dgst, int
> dgst_len, -      const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey);
> -       int (*ecdsa_sign_setup)(EC_KEY *eckey, BN_CTX *ctx, BIGNUM **kinv,
> -        BIGNUM **r);
> -       int (*ecdsa_do_verify)(const unsigned char *dgst, int dgst_len, -
>          const ECDSA_SIG *sig, EC_KEY *eckey);
> -#if 0
> -       int (*init)(EC_KEY *eckey);
> -       int (*finish)(EC_KEY *eckey);
> -#endif
> -       int flags;
> -       char *app_data;
> -};
> -
> -/* If this flag is set the ECDSA method is FIPS compliant and can be used
> - * in FIPS mode. This is set in the validated module method. If an
> - * application sets this flag in its own methods it is its responsibility
> - * to ensure the result is compliant.
> - */
> -
> -#define ECDSA_FLAG_FIPS_METHOD  0x1
> -
> -struct ECDSA_SIG_st {
> +typedef struct ECDSA_SIG_st
> +       {
>         BIGNUM *r;
>         BIGNUM *s;
> -};
> +       } ECDSA_SIG;
>
> /** Allocates and initialize a ECDSA_SIG structure
>  *  \return pointer to a ECDSA_SIG structure or NULL if an error occurred
> diff --git lib/libssl/src/crypto/ecdsa/ecs_locl.h
> lib/libssl/src/crypto/ecdsa/ecs_locl.h
> index e47f679..ceae6a2 100644
> --- lib/libssl/src/crypto/ecdsa/ecs_locl.h
> +++ lib/libssl/src/crypto/ecdsa/ecs_locl.h
> @@ -1,4 +1,4 @@
> -/* $OpenBSD: ecs_locl.h,v 1.3 2014/11/17 20:25:50 miod Exp $ */
> +/* $OpenBSD: ecs_locl.h,v 1.2 2014/06/12 15:49:29 deraadt Exp $ */
> /*
>  * Written by Nils Larsch for the OpenSSL project
>  */
> @@ -65,6 +65,31 @@
> extern "C" {
> #endif
>
> +struct ecdsa_method +  {
> +       const char *name;
> +       ECDSA_SIG *(*ecdsa_do_sign)(const unsigned char *dgst, int
> dgst_len, +                  const BIGNUM *inv, const BIGNUM *rp, EC_KEY
> *eckey);
> +       int (*ecdsa_sign_setup)(EC_KEY *eckey, BN_CTX *ctx, BIGNUM **kinv,
> +                    BIGNUM **r);
> +       int (*ecdsa_do_verify)(const unsigned char *dgst, int dgst_len, +
>                      const ECDSA_SIG *sig, EC_KEY *eckey);
> +#if 0
> +       int (*init)(EC_KEY *eckey);
> +       int (*finish)(EC_KEY *eckey);
> +#endif
> +       int flags;
> +       char *app_data;
> +       };
> +
> +/* If this flag is set the ECDSA method is FIPS compliant and can be used
> + * in FIPS mode. This is set in the validated module method. If an
> + * application sets this flag in its own methods it is its responsibility
> + * to ensure the result is compliant.
> + */
> +
> +#define ECDSA_FLAG_FIPS_METHOD 0x1
> +
> typedef struct ecdsa_data_st {
>         /* EC_KEY_METH_DATA part */
>         int (*init)(EC_KEY *);
>
> commit d5a744b7d8b98997f0f69be3afbcde5b369f2634
> Author: pedro martelletto <pedro at ambientworks.net>
> Date:   Thu Dec 18 10:43:16 2014 +0100
>
>    Add functions to set ECDSA_METHOD structure.
>       From OpenSSL commits 94c2f77a and 387b844f, issue #2459:
>       "Add various functions to allocate and set the fields of an
> ECDSA_METHOD
>    structure."
>
> diff --git lib/libssl/src/crypto/ecdsa/ecdsa.h
> lib/libssl/src/crypto/ecdsa/ecdsa.h
> index a29e5f6..a495984 100644
> --- lib/libssl/src/crypto/ecdsa/ecdsa.h
> +++ lib/libssl/src/crypto/ecdsa/ecdsa.h
> @@ -229,6 +229,74 @@ int          ECDSA_set_ex_data(EC_KEY *d, int idx,
> void *arg);
> void      *ECDSA_get_ex_data(EC_KEY *d, int idx);
>
>
> +/** Allocates and initialize a ECDSA_METHOD structure
> + *  \param ecdsa_method pointer to ECDSA_METHOD to copy.  (May be NULL)
> + *  \return pointer to a ECDSA_METHOD structure or NULL if an error
> occurred
> + */
> +
> +ECDSA_METHOD *ECDSA_METHOD_new(ECDSA_METHOD *ecdsa_method);
> +
> +/** frees a ECDSA_METHOD structure
> + *  \param  ecdsa_method  pointer to the ECDSA_METHOD structure
> + */
> +void ECDSA_METHOD_free(ECDSA_METHOD *ecdsa_method);
> +
> +/**  Sets application specific data in the ECDSA_METHOD
> + *   \param  ecdsa_method pointer to existing ECDSA_METHOD
> + *   \param  app application specific data to set
> + */
> +
> +void ECDSA_METHOD_set_app_data(ECDSA_METHOD *ecdsa_method, void *app);
> +
> +/** Returns application specific data from a ECDSA_METHOD structure
> + *  \param ecdsa_method pointer to ECDSA_METHOD structure
> + *  \return pointer to application specific data.
> + */
> +
> +
> +void * ECDSA_METHOD_get_app_data(ECDSA_METHOD *ecdsa_method);
> +
> +/**  Set the ECDSA_do_sign function in the ECDSA_METHOD
> + *   \param  ecdsa_method  pointer to existing ECDSA_METHOD
> + *   \param  ecdsa_do_sign a funtion of type ECDSA_do_sign
> + */
> +
> +void ECDSA_METHOD_set_sign(ECDSA_METHOD *ecdsa_method,
> +        ECDSA_SIG *(*ecdsa_do_sign)(const unsigned char *dgst, int
> dgst_len,
> +                const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey));
> +
> +/**  Set the  ECDSA_sign_setup function in the ECDSA_METHOD
> + *   \param  ecdsa_method  pointer to existing ECDSA_METHOD
> + *   \param  ecdsa_sign_setup a funtion of type ECDSA_sign_setup
> + */
> +
> +void ECDSA_METHOD_set_sign_setup(ECDSA_METHOD *ecdsa_method,
> +        int (*ecdsa_sign_setup)(EC_KEY *eckey, BN_CTX *ctx, BIGNUM **kinv,
> +                BIGNUM **r));
> +
> +/**  Set the ECDSA_do_verify function in the ECDSA_METHOD
> + *   \param  ecdsa_method  pointer to existing ECDSA_METHOD
> + *   \param  ecdsa_do_verify a funtion of type ECDSA_do_verify
> + */
> +
> +void ECDSA_METHOD_set_verify(ECDSA_METHOD *ecdsa_method,
> +        int (*ecdsa_do_verify)(const unsigned char *dgst, int dgst_len,
> +                const ECDSA_SIG *sig, EC_KEY *eckey));
> +
> +void ECDSA_METHOD_set_flags(ECDSA_METHOD *ecdsa_method, int flags);
> +
> +/**  Set the flags field in the ECDSA_METHOD
> + *   \param  ecdsa_method  pointer to existing ECDSA_METHOD
> + *   \param  flags flags value to set
> + */
> +
> +void ECDSA_METHOD_set_name(ECDSA_METHOD *ecdsa_method, char *name);
> +
> +/**  Set the name field in the ECDSA_METHOD
> + *   \param  ecdsa_method  pointer to existing ECDSA_METHOD
> + *   \param  name name to set
> + */
> +
> /* BEGIN ERROR CODES */
> /* The following lines are auto generated by the script mkerr.pl. Any
> changes
>  * made after this point may be overwritten when the script is next run.
> @@ -242,6 +310,7 @@ void ERR_load_ECDSA_strings(void);
> #define ECDSA_F_ECDSA_DATA_NEW_METHOD                    100
> #define ECDSA_F_ECDSA_DO_SIGN                            101
> #define ECDSA_F_ECDSA_DO_VERIFY                          102
> +#define ECDSA_F_ECDSA_METHOD_NEW                        105
> #define ECDSA_F_ECDSA_SIGN_SETUP                         103
>
> /* Reason codes. */
> diff --git lib/libssl/src/crypto/ecdsa/ecs_err.c
> lib/libssl/src/crypto/ecdsa/ecs_err.c
> index 721b53c..1400f34 100644
> --- lib/libssl/src/crypto/ecdsa/ecs_err.c
> +++ lib/libssl/src/crypto/ecdsa/ecs_err.c
> @@ -77,6 +77,7 @@ static ERR_STRING_DATA ECDSA_str_functs[]=
> {ERR_FUNC(ECDSA_F_ECDSA_DATA_NEW_METHOD),       "ECDSA_DATA_NEW_METHOD"},
> {ERR_FUNC(ECDSA_F_ECDSA_DO_SIGN),       "ECDSA_do_sign"},
> {ERR_FUNC(ECDSA_F_ECDSA_DO_VERIFY),     "ECDSA_do_verify"},
> +{ERR_FUNC(ECDSA_F_ECDSA_METHOD_NEW),   "ECDSA_METHOD_new"},
> {ERR_FUNC(ECDSA_F_ECDSA_SIGN_SETUP),    "ECDSA_sign_setup"},
> {0,NULL}
>         };
> diff --git lib/libssl/src/crypto/ecdsa/ecs_lib.c
> lib/libssl/src/crypto/ecdsa/ecs_lib.c
> index a92d611..38756fa 100644
> --- lib/libssl/src/crypto/ecdsa/ecs_lib.c
> +++ lib/libssl/src/crypto/ecdsa/ecs_lib.c
> @@ -266,3 +266,76 @@ void *ECDSA_get_ex_data(EC_KEY *d, int idx)
>                 return NULL;
>         return(CRYPTO_get_ex_data(&ecdsa->ex_data,idx));
> }
> +
> +ECDSA_METHOD *ECDSA_METHOD_new(ECDSA_METHOD *ecdsa_meth)
> +       {
> +       ECDSA_METHOD  *ret;
> +
> +       ret = malloc(sizeof(ECDSA_METHOD));
> +       if (ret == NULL)
> +               {
> +               ECDSAerr(ECDSA_F_ECDSA_METHOD_NEW, ERR_R_MALLOC_FAILURE);
> +               return NULL;
> +               }
> +
> +       if (ecdsa_meth)
> +               *ret = *ecdsa_meth;
> +       else
> +               {
> +               ret->ecdsa_sign_setup = 0;
> +               ret->ecdsa_do_sign = 0;
> +               ret->ecdsa_do_verify = 0;
> +               ret->name = NULL;
> +               ret->flags = 0;
> +               }
> +       ret->flags |= ECDSA_METHOD_FLAG_ALLOCATED;
> +       return ret;
> +       }
> +
> +
> +void ECDSA_METHOD_set_sign(ECDSA_METHOD *ecdsa_method,
> +       ECDSA_SIG *(*ecdsa_do_sign)(const unsigned char *dgst, int
> dgst_len,
> +               const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey))
> +       {
> +       ecdsa_method->ecdsa_do_sign = ecdsa_do_sign;
> +       }
> +
> +void ECDSA_METHOD_set_sign_setup(ECDSA_METHOD *ecdsa_method,
> +       int (*ecdsa_sign_setup)(EC_KEY *eckey, BN_CTX *ctx, BIGNUM **kinv,
> +               BIGNUM **r))
> +       {
> +       ecdsa_method->ecdsa_sign_setup = ecdsa_sign_setup;
> +       }
> +
> +void ECDSA_METHOD_set_verify(ECDSA_METHOD *ecdsa_method,
> +       int (*ecdsa_do_verify)(const unsigned char *dgst, int dgst_len,
> +               const ECDSA_SIG *sig, EC_KEY *eckey))
> +       {
> +       ecdsa_method->ecdsa_do_verify = ecdsa_do_verify;
> +       }
> +
> +void ECDSA_METHOD_set_flags(ECDSA_METHOD *ecdsa_method, int flags)
> +       {
> +       ecdsa_method->flags = flags | ECDSA_METHOD_FLAG_ALLOCATED;
> +       }
> +
> +void ECDSA_METHOD_set_name(ECDSA_METHOD *ecdsa_method, char *name)
> +       {
> +       ecdsa_method->name = name;
> +       }
> +
> +void ECDSA_METHOD_free(ECDSA_METHOD *ecdsa_method)
> +       {
> +       if (ecdsa_method->flags & ECDSA_METHOD_FLAG_ALLOCATED)
> +               free(ecdsa_method);
> +       }
> +
> +void ECDSA_METHOD_set_app_data(ECDSA_METHOD *ecdsa_method, void *app)
> +       {
> +       ecdsa_method->app_data = app;
> +       }
> +
> +void * ECDSA_METHOD_get_app_data(ECDSA_METHOD *ecdsa_method)
> +       {
> +       return ecdsa_method->app_data;
> +       }
> diff --git lib/libssl/src/crypto/ecdsa/ecs_locl.h
> lib/libssl/src/crypto/ecdsa/ecs_locl.h
> index ceae6a2..b1cf71e 100644
> --- lib/libssl/src/crypto/ecdsa/ecs_locl.h
> +++ lib/libssl/src/crypto/ecdsa/ecs_locl.h
> @@ -79,9 +79,14 @@ struct ecdsa_method
>         int (*finish)(EC_KEY *eckey);
> #endif
>         int flags;
> -       char *app_data;
> +       void *app_data;
>         };
>
> +/* The ECDSA_METHOD was allocated and can be freed */
> +
> +#define ECDSA_METHOD_FLAG_ALLOCATED 0x2
> +
> +
> /* If this flag is set the ECDSA method is FIPS compliant and can be used
>  * in FIPS mode. This is set in the validated module method. If an
>  * application sets this flag in its own methods it is its responsibility
>
> commit b9051ac84c79c2659f01367db90680183845d9ae
> Author: pedro martelletto <pedro at ambientworks.net>
> Date:   Thu Dec 18 13:48:05 2014 +0100
>
>    Implement PKCS#11 ECDSA support in SSH
>       Most of the changes take place in ssh-pkcs11.c, which has been
> instrumented on
>    loading ECDSA keys. Minor changes were reflected in other files such as
>    ssh-pkcs11-client.c and ssh-pkcs11-helper.c. Finally, this change also
> adds
>    ECDSA support for ssh-keygen conversions.
>       Joint work with Markus Friedl.
>
> diff --git usr.bin/ssh/ssh-keygen.c usr.bin/ssh/ssh-keygen.c
> index 9031683..1a89153 100644
> --- usr.bin/ssh/ssh-keygen.c
> +++ usr.bin/ssh/ssh-keygen.c
> @@ -343,7 +343,10 @@ do_convert_to_pem(Key *k)
>                         fatal("PEM_write_DSAPublicKey failed");
>                 break;
> #endif
> -       /* XXX ECDSA? */
> +       case KEY_ECDSA:
> +               if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa))
> +                       fatal("PEM_write_EC_PUBKEY failed");
> +               break;
>         default:
>                 fatal("%s: unsupported key type %s", __func__,
> key_type(k));
>         }
> @@ -623,6 +626,7 @@ do_convert_from_pem(Key **k, int *private)
> #ifdef notyet
>         DSA *dsa;
> #endif
> +       EC_KEY *ec;
>
>         if ((fp = fopen(identity_file, "r")) == NULL)
>                 fatal("%s: %s: %s", __progname, identity_file,
> strerror(errno));
> @@ -642,8 +646,17 @@ do_convert_from_pem(Key **k, int *private)
>                 fclose(fp);
>                 return;
>         }
> -       /* XXX ECDSA */
> #endif
> +       if ((ec = PEM_read_EC_PUBKEY(fp, NULL, NULL, NULL)) != NULL) {
> +               *k = key_new(KEY_UNSPEC);
> +               (*k)->type = KEY_ECDSA;
> +               (*k)->ecdsa = ec;
> +               (*k)->ecdsa_nid = key_ecdsa_key_to_nid(ec);
> +               if ((*k)->ecdsa_nid < 0)
> +                       fatal("%s: couldn't get curve nid", __func__);
> +               fclose(fp);
> +               return;
> +       }
>         fatal("%s: unrecognised raw private key format", __func__);
> }
>
> diff --git usr.bin/ssh/ssh-pkcs11-client.c usr.bin/ssh/ssh-pkcs11-client.c
> index 2dc5f17..d2f2aa9 100644
> --- usr.bin/ssh/ssh-pkcs11-client.c
> +++ usr.bin/ssh/ssh-pkcs11-client.c
> @@ -1,6 +1,7 @@
> /* $OpenBSD: ssh-pkcs11-client.c,v 1.5 2014/06/24 01:13:21 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
> @@ -24,6 +25,7 @@
> #include <unistd.h>
> #include <errno.h>
>
> +#include <openssl/ecdsa.h>
> #include <openssl/rsa.h>
>
> #include "pathnames.h"
> @@ -97,8 +99,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)
> {
>         Key key;
>         u_char *blob, *signature = NULL;
> @@ -133,16 +134,98 @@ 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 int
> -wrap_key(RSA *rsa)
> +ecdsa_sign_setup(EC_KEY *ec, BN_CTX *ctx, BIGNUM **kinvp, BIGNUM **rp)
> {
> -       static RSA_METHOD helper_rsa;
> +       error("%s called, returning -1", __func__);
> +       return (-1);
> +}
> +
> +static ECDSA_SIG *
> +ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv,
> +    const BIGNUM *rp, EC_KEY *ec)
> +{
> +       Key key;
> +       u_char *blob, *signature = NULL;
> +       u_int blen, slen = 0;
> +       ECDSA_SIG *ret = NULL;
> +       Buffer msg;
> +
> +       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 (key_to_blob(&key, &blob, &blen) == 0)
> +               return (NULL);
> +       buffer_init(&msg);
> +       buffer_put_char(&msg, SSH2_AGENTC_SIGN_REQUEST);
> +       buffer_put_string(&msg, blob, blen);
> +       buffer_put_string(&msg, dgst, dgst_len);
> +       buffer_put_int(&msg, 0);
> +       free(blob);
> +       send_msg(&msg);
> +       buffer_clear(&msg);
> +
> +       if (recv_msg(&msg) == SSH2_AGENT_SIGN_RESPONSE) {
> +               signature = buffer_get_string(&msg, &slen);
> +               if (signature == NULL) {
> +                       error("%s: buffer_get_string failed", __func__);
> +                       goto out;
> +               }
> +               ret = d2i_ECDSA_SIG(NULL, (const u_char **)&signature,
> slen);
> +               free(signature);
> +       }
> +out:
> +       buffer_free(&msg);
> +       return (ret);
> +}
> +
> +static int
> +ecdsa_do_verify(const unsigned char *dgst, int dgst_len, const ECDSA_SIG
> *sig,
> +    EC_KEY *ec)
> +{
> +       error("%s called, returning -1", __func__);
> +       return (-1);
> +}
> +
> +static RSA_METHOD       helper_rsa;
> +static ECDSA_METHOD    *helper_ecdsa;
> +
> +/* redirect private key crypto operations to the ssh-pkcs11-helper */
> +static void
> +wrap_key(Key *k)
> +{
> +       if (k->type == KEY_RSA)
> +               RSA_set_method(k->rsa, &helper_rsa);
> +       else if (k->type == KEY_ECDSA)
> +               ECDSA_set_method(k->ecdsa, helper_ecdsa);
> +       else
> +               fatal("%s: unknown key type", __func__);
> +}
> +
> +static int
> +pkcs11_start_helper_methods(void)
> +{
> +       if (helper_ecdsa != NULL)
> +               return (0);
> +
> +       helper_ecdsa = ECDSA_METHOD_new(NULL);
> +       if (helper_ecdsa == NULL) {
> +               error("ECDSA_METHOD_new() failed");
> +               return (-1);
> +       }
> +
> +       ECDSA_METHOD_set_sign_setup(helper_ecdsa, ecdsa_sign_setup);
> +       ECDSA_METHOD_set_sign(helper_ecdsa, ecdsa_do_sign);
> +       ECDSA_METHOD_set_verify(helper_ecdsa, ecdsa_do_verify);
>
>         memcpy(&helper_rsa, RSA_get_default_method(), sizeof(helper_rsa));
>         helper_rsa.name = "ssh-pkcs11-helper";
> -       helper_rsa.rsa_priv_enc = pkcs11_rsa_private_encrypt;
> -       RSA_set_method(rsa, &helper_rsa);
> +       helper_rsa.rsa_priv_enc = rsa_encrypt;
> +
>         return (0);
> }
>
> @@ -151,6 +234,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);
> @@ -203,7 +291,7 @@ pkcs11_add_provider(char *name, char *pin, Key
> ***keysp)
>                         blob = buffer_get_string(&msg, &blen);
>                         free(buffer_get_string(&msg, NULL));
>                         k = key_from_blob(blob, blen);
> -                       wrap_key(k->rsa);
> +                       wrap_key(k);
>                         (*keysp)[i] = k;
>                         free(blob);
>                 }
> diff --git usr.bin/ssh/ssh-pkcs11-helper.c usr.bin/ssh/ssh-pkcs11-helper.c
> index 3c2a0f2..64282de 100644
> --- usr.bin/ssh/ssh-pkcs11-helper.c
> +++ usr.bin/ssh/ssh-pkcs11-helper.c
> @@ -175,13 +175,26 @@ process_sign(void)
> #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 (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) {
> +                               slen = ECDSA_size(key->ecdsa);
> +                               signature = xmalloc(slen);
> +                               /* "The parameter type is ignored." */
> +                               ret = ECDSA_sign(-1, data, dlen, signature,
> +                                   &slen, found->ecdsa);
> +                               if (ret != -1)
> +                                       ok = 0;
> +                       } else
> +                               error("%s: don't know how to sign with key
> "
> +                                   "type %d", __func__, (int)key->type);
> #endif /* WITH_OPENSSL */
>                 }
>                 key_free(key);
> diff --git usr.bin/ssh/ssh-pkcs11.c usr.bin/ssh/ssh-pkcs11.c
> index 11a3370..e7ad949 100644
> --- usr.bin/ssh/ssh-pkcs11.c
> +++ usr.bin/ssh/ssh-pkcs11.c
> @@ -1,6 +1,7 @@
> /* $OpenBSD: ssh-pkcs11.c,v 1.14 2014/06/24 01:13:21 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
> @@ -23,6 +24,7 @@
> #include <string.h>
> #include <dlfcn.h>
>
> +#include <openssl/ecdsa.h>
> #include <openssl/x509.h>
>
> #define CRYPTOKI_COMPAT
> @@ -60,6 +62,7 @@ struct pkcs11_key {
>         CK_ULONG                slotidx;
>         int                     (*orig_finish)(RSA *rsa);
>         RSA_METHOD              rsa_method;
> +       ECDSA_METHOD            *ecdsa_method;
>         char                    *keyid;
>         int                     keyid_len;
> };
> @@ -210,40 +213,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, &private_key_class, sizeof(private_key_class)
> },
> -               {CKA_ID, NULL, 0},
> -               {CKA_SIGN, &true_val, 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, prompt[1024];
> -       int                     rval = -1;
>
> -       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");
> @@ -263,23 +253,75 @@ pkcs11_rsa_private_encrypt(int flen, const u_char
> *from, u_char *to, RSA *rsa,
>                 free(pin);
>                 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);
> }
>
> @@ -317,6 +359,132 @@ pkcs11_rsa_wrap(struct pkcs11_provider *provider,
> CK_ULONG slotidx,
>         return (0);
> }
>
> +/* ~*kingdom of unimplemented callbacks*~ */
> +
> +static void *
> +ecdsa_k11_dup(void *k11)
> +{
> +       fatal("%s called", __func__);
> +}
> +
> +static void
> +ecdsa_k11_free(void *k11)
> +{
> +       fatal("%s called", __func__);
> +}
> +
> +static void
> +ecdsa_k11_clear_free(void *k11)
> +{
> +       fatal("%s called", __func__);
> +}
> +
> +static int
> +ecdsa_sign_setup(EC_KEY *ec, BN_CTX *ctx, BIGNUM **kinvp, BIGNUM **rp)
> +{
> +       error("%s called, returning -1", __func__);
> +       return (-1);
> +}
> +
> +/* 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;
> +       CK_RV                   rv;
> +       ECDSA_SIG               *ret = NULL;
> +       u_char                  *sig;
> +       const u_char            *cp;
> +
> +       if ((k11 = EC_KEY_get_key_method_data(ec, ecdsa_k11_dup,
> ecdsa_k11_free,
> +           ecdsa_k11_clear_free)) == NULL) {
> +               error("EC_KEY_get_key_method_data failed for ec %p", 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) {
> +               cp = sig;
> +               ret = d2i_ECDSA_SIG(NULL, &cp, siglen);
> +       } else
> +               error("C_Sign failed: %lu", rv);
> +
> +       free(sig);
> +
> +       return (ret);
> +}
> +
> +static int
> +ecdsa_do_verify(const unsigned char *dgst, int dgst_len, const ECDSA_SIG
> *sig,
> +    EC_KEY *ec)
> +{
> +       error("%s called, returning -1", __func__);
> +       return (-1);
> +}
> +
> +static ECDSA_METHOD *ecdsa_method;
> +
> +static int
> +pkcs11_ecdsa_start_wrapper(void)
> +{
> +       if (ecdsa_method != NULL)
> +               return (0);
> +
> +       ecdsa_method = ECDSA_METHOD_new(NULL);
> +       if (ecdsa_method == NULL) {
> +               error("ECDSA_METHOD_new() failed");
> +               return (-1);
> +       }
> +
> +       ECDSA_METHOD_set_sign_setup(ecdsa_method, ecdsa_sign_setup);
> +       ECDSA_METHOD_set_sign(ecdsa_method, ecdsa_do_sign);
> +       ECDSA_METHOD_set_verify(ecdsa_method, ecdsa_do_verify);
> +
> +       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->ecdsa_method = ecdsa_method;
> +
> +       ECDSA_set_method(ec, k11->ecdsa_method);
> +       EC_KEY_insert_key_method_data(ec, k11, ecdsa_k11_dup,
> ecdsa_k11_free,
> +           ecdsa_k11_clear_free);
> +
> +       return (0);
> +}
> +
> /* remove trailing spaces */
> static void
> rmspace(u_char *buf, size_t len)
> @@ -370,46 +538,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], Key ***, int *)
> -       __attribute__((__bounded__(__minbytes__,4, 3 *
> sizeof(CK_ATTRIBUTE))));
> -
> -static int
> -pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx,
> -    Key ***keysp, int *nkeys)
> -{
> -       CK_OBJECT_CLASS         pubkey_class = CKO_PUBLIC_KEY;
> -       CK_OBJECT_CLASS         cert_class = CKO_CERTIFICATE;
> -       CK_ATTRIBUTE            pubkey_filter[] = {
> -               { CKA_CLASS, &pubkey_class, sizeof(pubkey_class) }
> -       };
> -       CK_ATTRIBUTE            cert_filter[] = {
> -               { CKA_CLASS, &cert_class, 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 }
> -       };
> -
> -       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(Key ***keysp, int *nkeys, Key *key)
> {
> @@ -421,114 +549,522 @@ pkcs11_key_included(Key ***keysp, int *nkeys, Key
> *key)
>         return (0);
> }
>
> -static int
> -pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx,
> -    CK_ATTRIBUTE filter[], CK_ATTRIBUTE attribs[3],
> -    Key ***keysp, int *nkeys)
> +static Key *
> +pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
> +    CK_OBJECT_HANDLE *obj)
> {
> -       Key                     *key;
> -       RSA                     *rsa;
> -       X509                    *x509;
> +       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;
> +       Key                     *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);
> +       }
> +
> +       /* check that none of the attributes are zero length */
> +       if (key_attr[0].ulValueLen == 0 ||
> +           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++)
> +               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 || EC_KEY_set_group(ec, group) == 0) {
> +               error("d2i_ECPKParameters 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 + 2;
> +       if (o2i_ECPublicKey(&ec, &attrp, key_attr[1].ulValueLen - 2) ==
> NULL) {
> +               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 = key_new(KEY_UNSPEC);
> +       if (key == NULL) {
> +               error("key_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 Key *
> +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;
> +       Key                     *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);
> +       }
> +
> +       /* check that none of the attributes are zero length */
> +       if (key_attr[0].ulValueLen == 0 ||
> +           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++)
> +               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 (pkcs11_rsa_wrap(p, slotidx, &key_attr[0], rsa))
> +               goto fail;
> +
> +       key = key_new(KEY_UNSPEC);
> +       if (key == NULL) {
> +               error("key_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);
> +       if (rsa)
> +               RSA_free(rsa);
> +
> +       return (key);
> +}
> +
> +static Key *
> +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;
> -       int                     i;
> -       const u_char            *cp;
> -       CK_RV                   rv;
> -       CK_OBJECT_HANDLE        obj;
> -       CK_ULONG                nfound;
> -       CK_SESSION_HANDLE       session;
> -       CK_FUNCTION_LIST        *f;
> +       RSA                     *rsa = NULL;
> +       EC_KEY                  *ec = NULL;
> +       Key                     *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);
> +       }
> +
> +       /* check that none of the attributes are zero length */
> +       if (cert_attr[0].ulValueLen == 0 ||
> +           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++)
> +               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->type == EVP_PKEY_RSA) {
> +               if (evp->pkey.rsa == NULL) {
> +                       error("invalid x509; no rsa key");
> +                       goto fail;
> +               }
> +               if ((rsa = RSAPublicKey_dup(evp->pkey.rsa)) == NULL) {
> +                       error("RSAPublicKey_dup failed");
> +                       goto fail;
> +               }
> +
> +               if (pkcs11_rsa_wrap(p, slotidx, &cert_attr[0], rsa))
> +                       goto fail;
> +
> +               key = key_new(KEY_UNSPEC);
> +               if (key == NULL) {
> +                       error("key_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->type == EVP_PKEY_EC) {
> +               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 = key_new(KEY_UNSPEC);
> +               if (key == NULL) {
> +                       error("key_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);
> +       if (x509)
> +               X509_free(x509);
> +       if (rsa)
> +               RSA_free(rsa);
> +       if (ec)
> +               EC_KEY_free(ec);
> +
> +       return (key);
> +}
> +
> +/*
> + * 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_certs(struct pkcs11_provider *p, CK_ULONG slotidx, Key
> ***keysp,
> +    int *nkeys)
> +{
> +       Key                     *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);
> +
>         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;
> +               }
> +
> +               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");
>                 }
> -               /* check that none of the attributes are zero length */
> -               if (attribs[0].ulValueLen == 0 ||
> -                   attribs[1].ulValueLen == 0 ||
> -                   attribs[2].ulValueLen == 0) {
> +
> +               if (key == NULL) {
> +                       error("failed to fetch key");
>                         continue;
>                 }
> -               /* allocate buffers for attributes */
> -               for (i = 0; i < 3; i++)
> -                       attribs[i].pValue = xmalloc(attribs[i].ulValueLen)
> ;
> -               /*
> -                * 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) {
> -                       error("C_GetAttributeValue failed: %lu", rv);
> -               } else if (attribs[1].type == CKA_MODULUS ) {
> -                       if ((rsa = RSA_new()) == NULL) {
> -                               error("RSA_new failed");
> -                       } else {
> -                               rsa->n = BN_bin2bn(attribs[1].pValue,
> -                                   attribs[1].ulValueLen, NULL);
> -                               rsa->e = BN_bin2bn(attribs[2].pValue,
> -                                   attribs[2].ulValueLen, NULL);
> -                       }
> +
> +               if (pkcs11_key_included(keysp, nkeys, key)) {
> +                       key_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->type != EVP_PKEY_RSA ||
> -                           evp->pkey.rsa == NULL) {
> -                               debug("X509_get_pubkey failed or no rsa");
> -                       } else if ((rsa = RSAPublicKey_dup(evp->pkey.rsa))
> -                           == NULL) {
> -                               error("RSAPublicKey_dup");
> -                       }
> -                       if (x509)
> -                               X509_free(x509);
> +                       /* expand key array and add key */
> +                       *keysp = xrealloc(*keysp, *nkeys + 1, sizeof(Key
> *));
> +                       (*keysp)[*nkeys] = key;
> +                       *nkeys = *nkeys + 1;
> +                       debug("have %d keys", *nkeys);
>                 }
> -               if (rsa && rsa->n && rsa->e &&
> -                   pkcs11_rsa_wrap(p, slotidx, &attribs[0], rsa) == 0) {
> -                       key = key_new(KEY_UNSPEC);
> -                       key->rsa = rsa;
> -                       key->type = KEY_RSA;
> -                       key->flags |= SSHKEY_FLAG_EXT;
> -                       if (pkcs11_key_included(keysp, nkeys, key)) {
> -                               key_free(key);
> -                       } else {
> -                               /* expand key array and add key */
> -                               *keysp = xrealloc(*keysp, *nkeys + 1,
> -                                   sizeof(Key *));
> -                               (*keysp)[*nkeys] = key;
> -                               *nkeys = *nkeys + 1;
> -                               debug("have %d keys", *nkeys);
> -                       }
> -               } else if (rsa) {
> -                       RSA_free(rsa);
> +       }
> +
> +       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, Key
> ***keysp,
> +    int *nkeys)
> +{
> +       Key                     *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);
> +                       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)) {
> +                       key_free(key);
> +               } else {
> +                       /* expand key array and add key */
> +                       *keysp = xrealloc(*keysp, *nkeys + 1, sizeof(Key
> *));
> +                       (*keysp)[*nkeys] = key;
> +                       *nkeys = *nkeys + 1;
> +                       debug("have %d keys", *nkeys);
>                 }
> -               for (i = 0; i < 3; i++)
> -                       free(attribs[i].pValue);
>         }
> -       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);
> }
>
> #ifdef HAVE_DLOPEN
> @@ -620,8 +1156,10 @@ pkcs11_add_provider(char *provider_id, char *pin,
> Key ***keyp)
>                     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)
> +               if (pkcs11_open_session(p, i, pin) == 0) {
>                         pkcs11_fetch_keys(p, i, keyp, &nkeys);
> +                       pkcs11_fetch_certs(p, i, keyp, &nkeys);


More information about the openssh-unix-dev mailing list