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