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/1c8c6abf87634651e430450659b62b34a2cb63aa
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);
+ }
}
if (nkeys > 0) {
TAILQ_INSERT_TAIL(&pkcs11_providers, p, next);
diff --git usr.bin/ssh/sshkey.h usr.bin/ssh/sshkey.h
index 4127db2..b351f45 100644
--- usr.bin/ssh/sshkey.h
+++ usr.bin/ssh/sshkey.h
@@ -32,6 +32,7 @@
#include <openssl/rsa.h>
#include <openssl/dsa.h>
#include <openssl/ec.h>
+#include <openssl/ecdsa.h>
#else /* OPENSSL */
#define RSA void
#define DSA void