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@xxxxxxxxxxxxxxxx> 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@xxxxxxxxxxxxxxxx Thu Dec 18 18:05:54 2014 > Return-Path: <pedro@xxxxxxxxxxxxxxxx> > 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@xxxxxxxxxxxxxxxx> > To: djm@xxxxxxxxxxx, miod@xxxxxxxxxxx, markus@xxxxxxxxxxx > Subject: PKCS#11 ECDSA support in OpenSSH, and related LibreSSL changes > Message-ID: <20141218170508.GA25923@xxxxxxxxxxxxxxxxxxxxxxxxx> > 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@xxxxxxxxxxxxxxxx> > 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@xxxxxxxxxxxxxxxx> > 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@xxxxxxxxxxxxxxxx> > 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); _______________________________________________ openssh-unix-dev mailing list openssh-unix-dev@xxxxxxxxxxx https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev