summaryrefslogtreecommitdiff
path: root/src/libstrongswan/plugins/pkcs11
diff options
context:
space:
mode:
authorYves-Alexis Perez <corsac@corsac.net>2012-06-28 21:16:07 +0200
committerYves-Alexis Perez <corsac@corsac.net>2012-06-28 21:16:07 +0200
commitb34738ed08c2227300d554b139e2495ca5da97d6 (patch)
tree62f33b52820f2e49f0e53c0f8c636312037c8054 /src/libstrongswan/plugins/pkcs11
parent0a9d51a49042a68daa15b0c74a2b7f152f52606b (diff)
downloadvyos-strongswan-b34738ed08c2227300d554b139e2495ca5da97d6.tar.gz
vyos-strongswan-b34738ed08c2227300d554b139e2495ca5da97d6.zip
Imported Upstream version 4.6.4
Diffstat (limited to 'src/libstrongswan/plugins/pkcs11')
-rw-r--r--src/libstrongswan/plugins/pkcs11/Makefile.am2
-rw-r--r--src/libstrongswan/plugins/pkcs11/Makefile.in14
-rw-r--r--src/libstrongswan/plugins/pkcs11/pkcs11.h24
-rw-r--r--src/libstrongswan/plugins/pkcs11/pkcs11_dh.c446
-rw-r--r--src/libstrongswan/plugins/pkcs11/pkcs11_dh.h51
-rw-r--r--src/libstrongswan/plugins/pkcs11/pkcs11_hasher.c3
-rw-r--r--src/libstrongswan/plugins/pkcs11/pkcs11_library.c203
-rw-r--r--src/libstrongswan/plugins/pkcs11/pkcs11_library.h46
-rw-r--r--src/libstrongswan/plugins/pkcs11/pkcs11_manager.c19
-rw-r--r--src/libstrongswan/plugins/pkcs11/pkcs11_manager.h10
-rw-r--r--src/libstrongswan/plugins/pkcs11/pkcs11_plugin.c217
-rw-r--r--src/libstrongswan/plugins/pkcs11/pkcs11_private_key.c184
-rw-r--r--src/libstrongswan/plugins/pkcs11/pkcs11_private_key.h16
-rw-r--r--src/libstrongswan/plugins/pkcs11/pkcs11_public_key.c597
-rw-r--r--src/libstrongswan/plugins/pkcs11/pkcs11_public_key.h2
-rw-r--r--src/libstrongswan/plugins/pkcs11/pkcs11_rng.c137
-rw-r--r--src/libstrongswan/plugins/pkcs11/pkcs11_rng.h47
17 files changed, 1796 insertions, 222 deletions
diff --git a/src/libstrongswan/plugins/pkcs11/Makefile.am b/src/libstrongswan/plugins/pkcs11/Makefile.am
index 199039d95..d032b879a 100644
--- a/src/libstrongswan/plugins/pkcs11/Makefile.am
+++ b/src/libstrongswan/plugins/pkcs11/Makefile.am
@@ -16,6 +16,8 @@ libstrongswan_pkcs11_la_SOURCES = \
pkcs11_private_key.h pkcs11_private_key.c \
pkcs11_public_key.h pkcs11_public_key.c \
pkcs11_hasher.h pkcs11_hasher.c \
+ pkcs11_rng.h pkcs11_rng.c \
+ pkcs11_dh.h pkcs11_dh.c \
pkcs11_manager.h pkcs11_manager.c
libstrongswan_pkcs11_la_LDFLAGS = -module -avoid-version
diff --git a/src/libstrongswan/plugins/pkcs11/Makefile.in b/src/libstrongswan/plugins/pkcs11/Makefile.in
index 1a67f88cc..2ead77f5a 100644
--- a/src/libstrongswan/plugins/pkcs11/Makefile.in
+++ b/src/libstrongswan/plugins/pkcs11/Makefile.in
@@ -77,7 +77,8 @@ LTLIBRARIES = $(noinst_LTLIBRARIES) $(plugin_LTLIBRARIES)
libstrongswan_pkcs11_la_LIBADD =
am_libstrongswan_pkcs11_la_OBJECTS = pkcs11_plugin.lo \
pkcs11_library.lo pkcs11_creds.lo pkcs11_private_key.lo \
- pkcs11_public_key.lo pkcs11_hasher.lo pkcs11_manager.lo
+ pkcs11_public_key.lo pkcs11_hasher.lo pkcs11_rng.lo \
+ pkcs11_dh.lo pkcs11_manager.lo
libstrongswan_pkcs11_la_OBJECTS = \
$(am_libstrongswan_pkcs11_la_OBJECTS)
libstrongswan_pkcs11_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
@@ -195,6 +196,9 @@ am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
+attest_plugins = @attest_plugins@
+axis2c_CFLAGS = @axis2c_CFLAGS@
+axis2c_LIBS = @axis2c_LIBS@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
@@ -203,6 +207,7 @@ build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
c_plugins = @c_plugins@
+clearsilver_LIBS = @clearsilver_LIBS@
datadir = @datadir@
datarootdir = @datarootdir@
dbusservicedir = @dbusservicedir@
@@ -219,11 +224,13 @@ host_cpu = @host_cpu@
host_os = @host_os@
host_vendor = @host_vendor@
htmldir = @htmldir@
+imcvdir = @imcvdir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
ipsecdir = @ipsecdir@
ipsecgroup = @ipsecgroup@
+ipseclibdir = @ipseclibdir@
ipsecuser = @ipsecuser@
libcharon_plugins = @libcharon_plugins@
libdir = @libdir@
@@ -267,6 +274,7 @@ sharedstatedir = @sharedstatedir@
soup_CFLAGS = @soup_CFLAGS@
soup_LIBS = @soup_LIBS@
srcdir = @srcdir@
+starter_plugins = @starter_plugins@
strongswan_conf = @strongswan_conf@
sysconfdir = @sysconfdir@
systemdsystemunitdir = @systemdsystemunitdir@
@@ -288,6 +296,8 @@ libstrongswan_pkcs11_la_SOURCES = \
pkcs11_private_key.h pkcs11_private_key.c \
pkcs11_public_key.h pkcs11_public_key.c \
pkcs11_hasher.h pkcs11_hasher.c \
+ pkcs11_rng.h pkcs11_rng.c \
+ pkcs11_dh.h pkcs11_dh.c \
pkcs11_manager.h pkcs11_manager.c
libstrongswan_pkcs11_la_LDFLAGS = -module -avoid-version
@@ -375,12 +385,14 @@ distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs11_creds.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs11_dh.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs11_hasher.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs11_library.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs11_manager.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs11_plugin.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs11_private_key.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs11_public_key.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs11_rng.Plo@am__quote@
.c.o:
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
diff --git a/src/libstrongswan/plugins/pkcs11/pkcs11.h b/src/libstrongswan/plugins/pkcs11/pkcs11.h
index 2e6a1e3ed..da29a77d0 100644
--- a/src/libstrongswan/plugins/pkcs11/pkcs11.h
+++ b/src/libstrongswan/plugins/pkcs11/pkcs11.h
@@ -179,6 +179,14 @@ extern "C" {
#define unlock_mutex UnlockMutex
#define reserved pReserved
+#define ck_ec_kdf_type_t CK_EC_KDF_TYPE
+
+#define ck_ecdh1_derive_args _CK_ECDH1_DERIVE_PARAMS
+#define shared_data_len ulSharedDataLen
+#define shared_data pSharedData
+#define public_data_len ulPublicDataLen
+#define public_data pPublicData
+
#endif /* CRYPTOKI_COMPAT */
@@ -1090,6 +1098,19 @@ struct ck_c_initialize_args
void *reserved;
};
+typedef unsigned long ck_ec_kdf_type_t;
+
+#define CKD_NULL (1)
+#define CKD_SHA1_DKF (2)
+
+struct ck_ecdh1_derive_params
+{
+ ck_ec_kdf_type_t kdf;
+ unsigned long shared_data_len;
+ void *shared_data;
+ unsigned long public_data_len;
+ void *public_data;
+};
#define CKF_LIBRARY_CANT_CREATE_OS_THREADS (1 << 0)
#define CKF_OS_LOCKING_OK (1 << 1)
@@ -1260,6 +1281,9 @@ typedef struct ck_function_list **CK_FUNCTION_LIST_PTR_PTR;
typedef struct ck_c_initialize_args CK_C_INITIALIZE_ARGS;
typedef struct ck_c_initialize_args *CK_C_INITIALIZE_ARGS_PTR;
+typedef struct ck_ecdh1_derive_params CK_ECDH1_DERIVE_PARAMS;
+typedef struct ck_ecdh1_derive_params *CK_ECDH1_DERIVE_PARAMS_PTR;
+
#define NULL_PTR NULL
/* Delete the helper macros defined at the top of the file. */
diff --git a/src/libstrongswan/plugins/pkcs11/pkcs11_dh.c b/src/libstrongswan/plugins/pkcs11/pkcs11_dh.c
new file mode 100644
index 000000000..c870370c8
--- /dev/null
+++ b/src/libstrongswan/plugins/pkcs11/pkcs11_dh.c
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2011 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "pkcs11_dh.h"
+
+#include <debug.h>
+#include <library.h>
+#include <asn1/asn1.h>
+#include <asn1/oid.h>
+
+#include "pkcs11_manager.h"
+
+typedef struct private_pkcs11_dh_t private_pkcs11_dh_t;
+
+/**
+ * Private data of an pkcs11_dh_t object.
+ */
+struct private_pkcs11_dh_t {
+
+ /**
+ * Public pkcs11_dh_t interface
+ */
+ pkcs11_dh_t public;
+
+ /**
+ * PKCS#11 library
+ */
+ pkcs11_library_t *lib;
+
+ /**
+ * Session handle for this objct
+ */
+ CK_SESSION_HANDLE session;
+
+ /**
+ * Diffie Hellman group number.
+ */
+ u_int16_t group;
+
+ /**
+ * Handle for own private value
+ */
+ CK_OBJECT_HANDLE pri_key;
+
+ /**
+ * Own public value
+ */
+ chunk_t pub_key;
+
+ /**
+ * Shared secret
+ */
+ chunk_t secret;
+
+ /**
+ * Mechanism to use to generate a key pair
+ */
+ CK_MECHANISM_TYPE mech_key;
+
+ /**
+ * Mechanism to use to derive a shared secret
+ */
+ CK_MECHANISM_TYPE mech_derive;
+
+};
+
+/**
+ * Derive a DH/ECDH shared secret.
+ *
+ * If this succeeds the shared secret is stored in this->secret.
+ */
+static void derive_secret(private_pkcs11_dh_t *this, chunk_t other)
+{
+ CK_OBJECT_CLASS klass = CKO_SECRET_KEY;
+ CK_KEY_TYPE type = CKK_GENERIC_SECRET;
+ CK_ATTRIBUTE attr[] = {
+ { CKA_CLASS, &klass, sizeof(klass) },
+ { CKA_KEY_TYPE, &type, sizeof(type) },
+ };
+ CK_MECHANISM mech = {
+ this->mech_derive,
+ other.ptr,
+ other.len,
+ };
+ CK_OBJECT_HANDLE secret;
+ CK_RV rv;
+
+ rv = this->lib->f->C_DeriveKey(this->session, &mech, this->pri_key,
+ attr, countof(attr), &secret);
+ if (rv != CKR_OK)
+ {
+ DBG1(DBG_CFG, "C_DeriveKey() error: %N", ck_rv_names, rv);
+ return;
+ }
+ if (!this->lib->get_ck_attribute(this->lib, this->session, secret,
+ CKA_VALUE, &this->secret))
+ {
+ chunk_free(&this->secret);
+ return;
+ }
+}
+
+METHOD(diffie_hellman_t, set_other_public_value, void,
+ private_pkcs11_dh_t *this, chunk_t value)
+{
+ switch (this->group)
+ {
+ case ECP_192_BIT:
+ case ECP_224_BIT:
+ case ECP_256_BIT:
+ case ECP_384_BIT:
+ case ECP_521_BIT:
+ { /* we expect the public value to just be the concatenated x and y
+ * coordinates, so we tag the value as an uncompressed ECPoint */
+ chunk_t tag = chunk_from_chars(0x04);
+ chunk_t pubkey = chunk_cata("cc", tag, value);
+ CK_ECDH1_DERIVE_PARAMS params = {
+ CKD_NULL,
+ 0,
+ NULL,
+ pubkey.len,
+ pubkey.ptr,
+ };
+
+ if (!lib->settings->get_bool(lib->settings,
+ "libstrongswan.ecp_x_coordinate_only", TRUE))
+ { /* we only get the x coordinate back */
+ return;
+ }
+ value = chunk_from_thing(params);
+ break;
+ }
+ default:
+ break;
+ }
+ derive_secret(this, value);
+}
+
+METHOD(diffie_hellman_t, get_my_public_value, void,
+ private_pkcs11_dh_t *this, chunk_t *value)
+{
+ *value = chunk_clone(this->pub_key);
+}
+
+METHOD(diffie_hellman_t, get_shared_secret, status_t,
+ private_pkcs11_dh_t *this, chunk_t *secret)
+{
+ if (!this->secret.ptr)
+ {
+ return FAILED;
+ }
+ *secret = chunk_clone(this->secret);
+ return SUCCESS;
+}
+
+METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t,
+ private_pkcs11_dh_t *this)
+{
+ return this->group;
+}
+
+METHOD(diffie_hellman_t, destroy, void,
+ private_pkcs11_dh_t *this)
+{
+ this->lib->f->C_CloseSession(this->session);
+ chunk_clear(&this->pub_key);
+ chunk_clear(&this->secret);
+ free(this);
+}
+
+/**
+ * Generate a DH/ECDH key pair.
+ *
+ * If this succeeds, this->pri_key has a handle to the private key and
+ * this->pub_key stores the public key.
+ */
+static bool generate_key_pair(private_pkcs11_dh_t *this, CK_ATTRIBUTE_PTR pub,
+ int pub_len, CK_ATTRIBUTE_PTR pri, int pri_len,
+ CK_ATTRIBUTE_TYPE attr)
+{
+ CK_MECHANISM mech = {
+ this->mech_key,
+ NULL,
+ 0,
+ };
+ CK_OBJECT_HANDLE pub_key;
+ CK_RV rv;
+
+ rv = this->lib->f->C_GenerateKeyPair(this->session, &mech, pub, pub_len,
+ pri, pri_len, &pub_key, &this->pri_key);
+ if (rv != CKR_OK)
+ {
+ DBG1(DBG_CFG, "C_GenerateKeyPair() error: %N", ck_rv_names, rv);
+ return FALSE;
+ }
+ if (!this->lib->get_ck_attribute(this->lib, this->session, pub_key,
+ attr, &this->pub_key))
+ {
+ chunk_free(&this->pub_key);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * Generate DH key pair.
+ */
+static bool generate_key_pair_modp(private_pkcs11_dh_t *this, size_t exp_len,
+ chunk_t g, chunk_t p)
+{
+ CK_BBOOL ck_true = CK_TRUE;
+ CK_ATTRIBUTE pub_attr[] = {
+ { CKA_DERIVE, &ck_true, sizeof(ck_true) },
+ { CKA_PRIME, p.ptr, p.len },
+ { CKA_BASE, g.ptr, g.len },
+ };
+ CK_ULONG bits = exp_len * 8;
+ CK_ATTRIBUTE pri_attr[] = {
+ { CKA_DERIVE, &ck_true, sizeof(ck_true) },
+ { CKA_VALUE_BITS, &bits, sizeof(bits) },
+ };
+ return generate_key_pair(this, pub_attr, countof(pub_attr), pri_attr,
+ countof(pri_attr), CKA_VALUE);
+}
+
+/**
+ * Generate ECDH key pair.
+ */
+static bool generate_key_pair_ecp(private_pkcs11_dh_t *this,
+ chunk_t ecparams)
+{
+ CK_BBOOL ck_true = CK_TRUE;
+ CK_ATTRIBUTE pub_attr[] = {
+ { CKA_DERIVE, &ck_true, sizeof(ck_true) },
+ { CKA_EC_PARAMS, ecparams.ptr, ecparams.len },
+ };
+ CK_ATTRIBUTE pri_attr[] = {
+ { CKA_DERIVE, &ck_true, sizeof(ck_true) },
+ };
+ chunk_t pub_key;
+ if (!generate_key_pair(this, pub_attr, countof(pub_attr), pri_attr,
+ countof(pri_attr), CKA_EC_POINT))
+ {
+ return FALSE;
+ }
+ if (this->pub_key.len <= 0 || this->pub_key.ptr[0] != 0x04)
+ { /* we currently only support the point in uncompressed form which
+ * looks like this: 0x04 || x || y */
+ chunk_clear(&this->pub_key);
+ return FALSE;
+ }
+ pub_key = chunk_clone(chunk_skip(this->pub_key, 1));
+ chunk_clear(&this->pub_key);
+ this->pub_key = pub_key;
+ return TRUE;
+}
+
+/**
+ * Find a token we can use for DH/ECDH algorithm
+ */
+static pkcs11_library_t *find_token(private_pkcs11_dh_t *this,
+ CK_SESSION_HANDLE *session)
+{
+ enumerator_t *tokens, *mechs;
+ pkcs11_manager_t *manager;
+ pkcs11_library_t *current, *found = NULL;
+ CK_MECHANISM_TYPE type;
+ CK_SLOT_ID slot;
+
+ manager = lib->get(lib, "pkcs11-manager");
+ if (!manager)
+ {
+ return NULL;
+ }
+ tokens = manager->create_token_enumerator(manager);
+ while (tokens->enumerate(tokens, &current, &slot))
+ {
+ mechs = current->create_mechanism_enumerator(current, slot);
+ while (mechs->enumerate(mechs, &type, NULL))
+ { /* we assume we can generate key pairs if the derive mechanism
+ * is supported */
+ if (type == this->mech_derive)
+ {
+ if (current->f->C_OpenSession(slot, CKF_SERIAL_SESSION,
+ NULL, NULL, session) == CKR_OK)
+ {
+ found = current;
+ break;
+ }
+ }
+ }
+ mechs->destroy(mechs);
+ if (found)
+ {
+ break;
+ }
+ }
+ tokens->destroy(tokens);
+ return found;
+}
+
+/**
+ * Generic internal constructor
+ */
+static private_pkcs11_dh_t *create_generic(diffie_hellman_group_t group,
+ CK_MECHANISM_TYPE key,
+ CK_MECHANISM_TYPE derive)
+{
+ private_pkcs11_dh_t *this;
+
+ INIT(this,
+ .public = {
+ .dh = {
+ .get_shared_secret = _get_shared_secret,
+ .set_other_public_value = _set_other_public_value,
+ .get_my_public_value = _get_my_public_value,
+ .get_dh_group = _get_dh_group,
+ .destroy = _destroy,
+ },
+ },
+ .group = group,
+ .mech_key = key,
+ .mech_derive = derive,
+ );
+
+ this->lib = find_token(this, &this->session);
+ if (!this->lib)
+ {
+ free(this);
+ return NULL;
+ }
+ return this;
+}
+
+static pkcs11_dh_t *create_ecp(diffie_hellman_group_t group, chunk_t ecparam)
+{
+ private_pkcs11_dh_t *this = create_generic(group, CKM_EC_KEY_PAIR_GEN,
+ CKM_ECDH1_DERIVE);
+
+ if (this)
+ {
+ if (generate_key_pair_ecp(this, ecparam))
+ {
+ chunk_free(&ecparam);
+ return &this->public;
+ }
+ chunk_free(&ecparam);
+ free(this);
+ }
+ return NULL;
+}
+
+/**
+ * Constructor for MODP DH
+ */
+static pkcs11_dh_t *create_modp(diffie_hellman_group_t group, size_t exp_len,
+ chunk_t g, chunk_t p)
+{
+ private_pkcs11_dh_t *this = create_generic(group, CKM_DH_PKCS_KEY_PAIR_GEN,
+ CKM_DH_PKCS_DERIVE);
+
+ if (this)
+ {
+ if (generate_key_pair_modp(this, exp_len, g, p))
+ {
+ return &this->public;
+ }
+ free(this);
+ }
+ return NULL;
+}
+
+/**
+ * Lookup the EC params for the given group.
+ */
+static chunk_t ecparams_lookup(diffie_hellman_group_t group)
+{
+ switch (group)
+ {
+ case ECP_192_BIT:
+ return asn1_build_known_oid(OID_PRIME192V1);
+ case ECP_224_BIT:
+ return asn1_build_known_oid(OID_SECT224R1);
+ case ECP_256_BIT:
+ return asn1_build_known_oid(OID_PRIME256V1);
+ case ECP_384_BIT:
+ return asn1_build_known_oid(OID_SECT384R1);
+ case ECP_521_BIT:
+ return asn1_build_known_oid(OID_SECT521R1);
+ default:
+ break;
+ }
+ return chunk_empty;
+}
+
+/**
+ * Described in header.
+ */
+pkcs11_dh_t *pkcs11_dh_create(diffie_hellman_group_t group,
+ chunk_t g, chunk_t p)
+{
+ switch (group)
+ {
+ case MODP_CUSTOM:
+ {
+ return create_modp(group, p.len, g, p);
+ }
+ case ECP_192_BIT:
+ case ECP_224_BIT:
+ case ECP_256_BIT:
+ case ECP_384_BIT:
+ case ECP_521_BIT:
+ {
+ chunk_t params = ecparams_lookup(group);
+ if (params.ptr)
+ {
+ return create_ecp(group, params);
+ }
+ break;
+ }
+ default:
+ {
+ diffie_hellman_params_t *params = diffie_hellman_get_params(group);
+ if (params)
+ {
+ return create_modp(group, params->exp_len, params->generator,
+ params->prime);
+ }
+ break;
+ }
+ }
+ return NULL;
+}
+
diff --git a/src/libstrongswan/plugins/pkcs11/pkcs11_dh.h b/src/libstrongswan/plugins/pkcs11/pkcs11_dh.h
new file mode 100644
index 000000000..2654130c0
--- /dev/null
+++ b/src/libstrongswan/plugins/pkcs11/pkcs11_dh.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2011 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup pkcs11_dh pkcs11_dh
+ * @{ @ingroup pkcs11
+ */
+
+#ifndef PKCS11_DH_H_
+#define PKCS11_DH_H_
+
+typedef struct pkcs11_dh_t pkcs11_dh_t;
+
+#include <library.h>
+
+/**
+ * Implementation of the Diffie-Hellman algorithm via PKCS#11.
+ */
+struct pkcs11_dh_t {
+
+ /**
+ * Implements diffie_hellman_t interface.
+ */
+ diffie_hellman_t dh;
+};
+
+/**
+ * Creates a new pkcs11_dh_t object.
+ *
+ * @param group Diffie Hellman group number to use
+ * @param g generator in case group is MODP_CUSTOM
+ * @param p prime in case group is MODP_CUSTOM
+ * @return pkcs11_dh_t object, NULL if not supported
+ */
+pkcs11_dh_t *pkcs11_dh_create(diffie_hellman_group_t group,
+ chunk_t g, chunk_t p);
+
+#endif /** PKCS11_DH_H_ @}*/
+
diff --git a/src/libstrongswan/plugins/pkcs11/pkcs11_hasher.c b/src/libstrongswan/plugins/pkcs11/pkcs11_hasher.c
index 6d327be40..069fa98b6 100644
--- a/src/libstrongswan/plugins/pkcs11/pkcs11_hasher.c
+++ b/src/libstrongswan/plugins/pkcs11/pkcs11_hasher.c
@@ -260,7 +260,7 @@ static pkcs11_library_t* find_token(hash_algorithm_t algo,
{
return NULL;
}
- manager = pkcs11_manager_get();
+ manager = lib->get(lib, "pkcs11-manager");
if (!manager)
{
return NULL;
@@ -315,6 +315,7 @@ pkcs11_hasher_t *pkcs11_hasher_create(hash_algorithm_t algo)
this->lib = find_token(algo, &this->session, &this->mech, &this->size);
if (!this->lib)
{
+ this->mutex->destroy(this->mutex);
free(this);
return NULL;
}
diff --git a/src/libstrongswan/plugins/pkcs11/pkcs11_library.c b/src/libstrongswan/plugins/pkcs11/pkcs11_library.c
index 6f7926808..97c3d2fcf 100644
--- a/src/libstrongswan/plugins/pkcs11/pkcs11_library.c
+++ b/src/libstrongswan/plugins/pkcs11/pkcs11_library.c
@@ -1,4 +1,7 @@
/*
+ * Copyright (C) 2011 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
* Copyright (C) 2010 Martin Willi
* Copyright (C) 2010 revosec AG
*
@@ -447,6 +450,123 @@ ENUM_NEXT(ck_mech_names, CKM_DSA_PARAMETER_GEN, CKM_X9_42_DH_PARAMETER_GEN,
"X9_42_DH_PARAMETER_GEN");
ENUM_END(ck_mech_names, CKM_X9_42_DH_PARAMETER_GEN);
+
+ENUM_BEGIN(ck_attr_names, CKA_CLASS, CKA_LABEL,
+ "CLASS",
+ "TOKEN",
+ "PRIVATE",
+ "LABEL");
+ENUM_NEXT(ck_attr_names, CKA_APPLICATION, CKA_OBJECT_ID, CKA_LABEL,
+ "APPLICATION",
+ "VALUE",
+ "OBJECT_ID");
+ENUM_NEXT(ck_attr_names, CKA_CERTIFICATE_TYPE, CKA_HASH_OF_ISSUER_PUBLIC_KEY,
+ CKA_OBJECT_ID,
+ "CERTIFICATE_TYPE",
+ "ISSUER",
+ "SERIAL_NUMBER",
+ "AC_ISSUER",
+ "OWNER",
+ "ATTR_TYPES",
+ "TRUSTED",
+ "CERTIFICATE_CATEGORY",
+ "JAVA_MIDP_SECURITY_DOMAIN",
+ "URL",
+ "HASH_OF_SUBJECT_PUBLIC_KEY",
+ "HASH_OF_ISSUER_PUBLIC_KEY");
+ENUM_NEXT(ck_attr_names, CKA_CHECK_VALUE, CKA_CHECK_VALUE,
+ CKA_HASH_OF_ISSUER_PUBLIC_KEY,
+ "CHECK_VALUE");
+ENUM_NEXT(ck_attr_names, CKA_KEY_TYPE, CKA_DERIVE, CKA_CHECK_VALUE,
+ "KEY_TYPE",
+ "SUBJECT",
+ "ID",
+ "SENSITIVE",
+ "ENCRYPT",
+ "DECRYPT",
+ "WRAP",
+ "UNWRAP",
+ "SIGN",
+ "SIGN_RECOVER",
+ "VERIFY",
+ "VERIFY_RECOVER",
+ "DERIVE");
+ENUM_NEXT(ck_attr_names, CKA_START_DATE, CKA_END_DATE, CKA_DERIVE,
+ "START_DATE",
+ "END_DATE");
+ENUM_NEXT(ck_attr_names, CKA_MODULUS, CKA_COEFFICIENT, CKA_END_DATE,
+ "MODULUS",
+ "MODULUS_BITS",
+ "PUBLIC_EXPONENT",
+ "PRIVATE_EXPONENT",
+ "PRIME_1",
+ "PRIME_2",
+ "EXPONENT_1",
+ "EXPONENT_2",
+ "COEFFICIENT");
+ENUM_NEXT(ck_attr_names, CKA_PRIME, CKA_SUB_PRIME_BITS, CKA_COEFFICIENT,
+ "PRIME",
+ "SUBPRIME",
+ "BASE",
+ "PRIME_BITS",
+ "SUB_PRIME_BITS");
+ENUM_NEXT(ck_attr_names, CKA_VALUE_BITS, CKA_KEY_GEN_MECHANISM,
+ CKA_SUB_PRIME_BITS,
+ "VALUE_BITS",
+ "VALUE_LEN",
+ "EXTRACTABLE",
+ "LOCAL",
+ "NEVER_EXTRACTABLE",
+ "ALWAYS_SENSITIVE",
+ "KEY_GEN_MECHANISM");
+ENUM_NEXT(ck_attr_names, CKA_MODIFIABLE, CKA_MODIFIABLE, CKA_KEY_GEN_MECHANISM,
+ "MODIFIABLE");
+ENUM_NEXT(ck_attr_names, CKA_EC_PARAMS, CKA_EC_POINT, CKA_MODIFIABLE,
+ "EC_PARAMS",
+ "EC_POINT");
+ENUM_NEXT(ck_attr_names, CKA_SECONDARY_AUTH, CKA_ALWAYS_AUTHENTICATE,
+ CKA_EC_POINT,
+ "SECONDARY_AUTH",
+ "AUTH_PIN_FLAGS",
+ "ALWAYS_AUTHENTICATE");
+ENUM_NEXT(ck_attr_names, CKA_WRAP_WITH_TRUSTED, CKA_WRAP_WITH_TRUSTED,
+ CKA_ALWAYS_AUTHENTICATE,
+ "WRAP_WITH_TRUSTED");
+ENUM_NEXT(ck_attr_names, CKA_HW_FEATURE_TYPE, CKA_HAS_RESET,
+ CKA_WRAP_WITH_TRUSTED,
+ "HW_FEATURE_TYPE",
+ "RESET_ON_INIT",
+ "HAS_RESET");
+ENUM_NEXT(ck_attr_names, CKA_PIXEL_X, CKA_BITS_PER_PIXEL, CKA_HAS_RESET,
+ "PIXEL_X",
+ "RESOLUTION",
+ "CHAR_ROWS",
+ "CHAR_COLUMNS",
+ "COLOR",
+ "BITS_PER_PIXEL");
+ENUM_NEXT(ck_attr_names, CKA_CHAR_SETS, CKA_MIME_TYPES, CKA_BITS_PER_PIXEL,
+ "CHAR_SETS",
+ "ENCODING_METHODS",
+ "MIME_TYPES");
+ENUM_NEXT(ck_attr_names, CKA_MECHANISM_TYPE, CKA_SUPPORTED_CMS_ATTRIBUTES,
+ CKA_MIME_TYPES,
+ "MECHANISM_TYPE",
+ "REQUIRED_CMS_ATTRIBUTES",
+ "DEFAULT_CMS_ATTRIBUTES",
+ "SUPPORTED_CMS_ATTRIBUTES");
+ENUM_NEXT(ck_attr_names, CKA_WRAP_TEMPLATE, CKA_UNWRAP_TEMPLATE,
+ CKA_SUPPORTED_CMS_ATTRIBUTES,
+ "WRAP_TEMPLATE",
+ "UNWRAP_TEMPLATE");
+ENUM_NEXT(ck_attr_names, CKA_ALLOWED_MECHANISMS, CKA_ALLOWED_MECHANISMS,
+ CKA_UNWRAP_TEMPLATE,
+ "ALLOWED_MECHANISMS");
+ENUM_END(ck_attr_names, CKA_ALLOWED_MECHANISMS);
+/* the values in an enum_name_t are stored as int, thus CKA_VENDOR_DEFINED
+ * will overflow and is thus not defined here */
+
+
+
/**
* Private data of an pkcs11_library_t object.
*/
@@ -495,10 +615,12 @@ typedef struct {
CK_SESSION_HANDLE session;
/* pkcs11 library */
pkcs11_library_t *lib;
- /* attributes to retreive */
+ /* attributes to retrieve */
CK_ATTRIBUTE_PTR attr;
/* number of attributes */
CK_ULONG count;
+ /* object handle in case of a single object */
+ CK_OBJECT_HANDLE object;
/* currently allocated attributes, to free */
linked_list_t *freelist;
} object_enumerator_t;
@@ -552,7 +674,7 @@ static bool get_attributes(object_enumerator_t *this, CK_OBJECT_HANDLE object)
if (rv != CKR_OK)
{
free_attrs(this);
- DBG1(DBG_CFG, "C_GetAttributeValue(NULL) error: %N", ck_rv_names, rv);
+ DBG1(DBG_CFG, "C_GetAttributeValue() error: %N", ck_rv_names, rv);
return FALSE;
}
return TRUE;
@@ -565,11 +687,19 @@ METHOD(enumerator_t, object_enumerate, bool,
CK_ULONG found;
CK_RV rv;
- rv = this->lib->f->C_FindObjects(this->session, &object, 1, &found);
- if (rv != CKR_OK)
+ if (!this->object)
{
- DBG1(DBG_CFG, "C_FindObjects() failed: %N", ck_rv_names, rv);
- return FALSE;
+ rv = this->lib->f->C_FindObjects(this->session, &object, 1, &found);
+ if (rv != CKR_OK)
+ {
+ DBG1(DBG_CFG, "C_FindObjects() failed: %N", ck_rv_names, rv);
+ return FALSE;
+ }
+ }
+ else
+ {
+ object = this->object;
+ found = 1;
}
if (found)
{
@@ -580,7 +710,10 @@ METHOD(enumerator_t, object_enumerate, bool,
return FALSE;
}
}
- *out = object;
+ if (out)
+ {
+ *out = object;
+ }
return TRUE;
}
return FALSE;
@@ -589,7 +722,10 @@ METHOD(enumerator_t, object_enumerate, bool,
METHOD(enumerator_t, object_destroy, void,
object_enumerator_t *this)
{
- this->lib->f->C_FindObjectsFinal(this->session);
+ if (!this->object)
+ {
+ this->lib->f->C_FindObjectsFinal(this->session);
+ }
free_attrs(this);
this->freelist->destroy(this->freelist);
free(this);
@@ -624,6 +760,27 @@ METHOD(pkcs11_library_t, create_object_enumerator, enumerator_t*,
return &enumerator->public;
}
+METHOD(pkcs11_library_t, create_object_attr_enumerator, enumerator_t*,
+ private_pkcs11_library_t *this, CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE object, CK_ATTRIBUTE_PTR attr, CK_ULONG count)
+{
+ object_enumerator_t *enumerator;
+
+ INIT(enumerator,
+ .public = {
+ .enumerate = (void*)_object_enumerate,
+ .destroy = _object_destroy,
+ },
+ .session = session,
+ .lib = &this->public,
+ .attr = attr,
+ .count = count,
+ .object = object,
+ .freelist = linked_list_create(),
+ );
+ return &enumerator->public;
+}
+
/**
* Enumerator over mechanisms
*/
@@ -707,6 +864,32 @@ METHOD(pkcs11_library_t, create_mechanism_enumerator, enumerator_t*,
return &enumerator->public;
}
+METHOD(pkcs11_library_t, get_ck_attribute, bool,
+ private_pkcs11_library_t *this, CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_TYPE type, chunk_t *data)
+{
+ CK_ATTRIBUTE attr = { type, NULL, 0 };
+ CK_RV rv;
+ rv = this->public.f->C_GetAttributeValue(session, obj, &attr, 1);
+ if (rv != CKR_OK)
+ {
+ DBG1(DBG_CFG, "C_GetAttributeValue(%N) error: %N", ck_attr_names, type,
+ ck_rv_names, rv);
+ return FALSE;
+ }
+ *data = chunk_alloc(attr.ulValueLen);
+ attr.pValue = data->ptr;
+ rv = this->public.f->C_GetAttributeValue(session, obj, &attr, 1);
+ if (rv != CKR_OK)
+ {
+ DBG1(DBG_CFG, "C_GetAttributeValue(%N) error: %N", ck_attr_names, type,
+ ck_rv_names, rv);
+ chunk_free(data);
+ return FALSE;
+ }
+ return TRUE;
+}
+
METHOD(pkcs11_library_t, destroy, void,
private_pkcs11_library_t *this)
{
@@ -739,7 +922,7 @@ void pkcs11_library_trim(char *str, int len)
*/
static CK_RV CreateMutex(CK_VOID_PTR_PTR data)
{
- *data = mutex_create(MUTEX_TYPE_DEFAULT);
+ *data = mutex_create(MUTEX_TYPE_RECURSIVE);
return CKR_OK;
}
@@ -889,7 +1072,9 @@ pkcs11_library_t *pkcs11_library_create(char *name, char *file, bool os_locking)
.get_name = _get_name,
.get_features = _get_features,
.create_object_enumerator = _create_object_enumerator,
+ .create_object_attr_enumerator = _create_object_attr_enumerator,
.create_mechanism_enumerator = _create_mechanism_enumerator,
+ .get_ck_attribute = _get_ck_attribute,
.destroy = _destroy,
},
.name = name,
diff --git a/src/libstrongswan/plugins/pkcs11/pkcs11_library.h b/src/libstrongswan/plugins/pkcs11/pkcs11_library.h
index abe023448..e76e65e07 100644
--- a/src/libstrongswan/plugins/pkcs11/pkcs11_library.h
+++ b/src/libstrongswan/plugins/pkcs11/pkcs11_library.h
@@ -1,4 +1,7 @@
/*
+ * Copyright (C) 2011 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
* Copyright (C) 2010 Martin Willi
* Copyright (C) 2010 revosec AG
*
@@ -27,6 +30,7 @@ typedef struct pkcs11_library_t pkcs11_library_t;
#include "pkcs11.h"
#include <enum.h>
+#include <chunk.h>
#include <utils/enumerator.h>
/**
@@ -72,7 +76,7 @@ struct pkcs11_library_t {
*
* @param session session to use
* @param tmpl search template
- * @param tcount number of attributes in the search template
+ * @param tcount number of attributes in the search template
* @param attr attributes to read from object
* @param acount number of attributes to read
*/
@@ -81,6 +85,24 @@ struct pkcs11_library_t {
CK_ATTRIBUTE_PTR attr, CK_ULONG acount);
/**
+ * This is very similar to the object enumerator but is only used to
+ * easily retrieve multiple attributes from a single object for which
+ * a handle is already known.
+ *
+ * The given attribute array is automatically filled in with the
+ * associated attributes. If the value of an output attribute is NULL,
+ * the required memory gets allocated/freed during enumeration.
+ *
+ * @param session session to use
+ * @param object object handle
+ * @param attr attributes to read from object
+ * @param count number of attributes to read
+ */
+ enumerator_t* (*create_object_attr_enumerator)(pkcs11_library_t *this,
+ CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object,
+ CK_ATTRIBUTE_PTR attr, CK_ULONG count);
+
+ /**
* Create an enumerator over supported mechanisms of a token.
*
* The resulting enumerator enumerates over the mechanism type, and if
@@ -93,6 +115,21 @@ struct pkcs11_library_t {
CK_SLOT_ID slot);
/**
+ * Retrieve a single attribute from the given object.
+ *
+ * Memory for the data is allocated.
+ *
+ * @param session session with the PKCS#11 library
+ * @param obj object handle
+ * @param type attribute type to extract
+ * @param data extracted data
+ * @return TRUE if successful
+ */
+ bool (*get_ck_attribute)(pkcs11_library_t *this, CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_TYPE type,
+ chunk_t *data);
+
+ /**
* Destroy a pkcs11_library_t.
*/
void (*destroy)(pkcs11_library_t *this);
@@ -109,7 +146,12 @@ extern enum_name_t *ck_rv_names;
extern enum_name_t *ck_mech_names;
/**
- * Trim/null terminate a string returned by the varius PKCS#11 functions.
+ * Enum names for CK_ATTRIBUTE_TYPE values
+ */
+extern enum_name_t *ck_attr_names;
+
+/**
+ * Trim/null terminate a string returned by the various PKCS#11 functions.
*
* @param str string to trim
* @param len max length of the string
diff --git a/src/libstrongswan/plugins/pkcs11/pkcs11_manager.c b/src/libstrongswan/plugins/pkcs11/pkcs11_manager.c
index 431cd6a2c..5b321b26e 100644
--- a/src/libstrongswan/plugins/pkcs11/pkcs11_manager.c
+++ b/src/libstrongswan/plugins/pkcs11/pkcs11_manager.c
@@ -323,17 +323,11 @@ METHOD(pkcs11_manager_t, create_token_enumerator, enumerator_t*,
return &enumerator->public;
}
-/**
- * Singleton instance
- */
-static private_pkcs11_manager_t *singleton = NULL;
-
METHOD(pkcs11_manager_t, destroy, void,
private_pkcs11_manager_t *this)
{
this->libs->destroy_function(this->libs, (void*)lib_entry_destroy);
free(this);
- singleton = NULL;
}
/**
@@ -386,14 +380,12 @@ pkcs11_manager_t *pkcs11_manager_create(pkcs11_manager_token_event_t cb,
}
enumerator->destroy(enumerator);
- singleton = this;
-
enumerator = this->libs->create_enumerator(this->libs);
while (enumerator->enumerate(enumerator, &entry))
{
query_slots(entry);
- entry->job = callback_job_create((void*)dispatch_slot_events,
- entry, (void*)end_dispatch, NULL);
+ entry->job = callback_job_create_with_prio((void*)dispatch_slot_events,
+ entry, (void*)end_dispatch, NULL, JOB_PRIO_CRITICAL);
lib->processor->queue_job(lib->processor, (job_t*)entry->job);
}
enumerator->destroy(enumerator);
@@ -401,10 +393,3 @@ pkcs11_manager_t *pkcs11_manager_create(pkcs11_manager_token_event_t cb,
return &this->public;
}
-/**
- * See header
- */
-pkcs11_manager_t *pkcs11_manager_get()
-{
- return (pkcs11_manager_t*)singleton;
-}
diff --git a/src/libstrongswan/plugins/pkcs11/pkcs11_manager.h b/src/libstrongswan/plugins/pkcs11/pkcs11_manager.h
index b80d67324..2f51fb30e 100644
--- a/src/libstrongswan/plugins/pkcs11/pkcs11_manager.h
+++ b/src/libstrongswan/plugins/pkcs11/pkcs11_manager.h
@@ -32,7 +32,7 @@ typedef struct pkcs11_manager_t pkcs11_manager_t;
*
* @param data user supplied data, as passed to pkcs11_manager_create()
* @param p11 loaded PKCS#11 library token belongs to
- * @param slot slot number the event occured in
+ * @param slot slot number the event occurred in
* @param add TRUE if token was added to the slot, FALSE if removed
*/
typedef void (*pkcs11_manager_token_event_t)(void *data, pkcs11_library_t *p11,
@@ -67,12 +67,4 @@ struct pkcs11_manager_t {
pkcs11_manager_t *pkcs11_manager_create(pkcs11_manager_token_event_t cb,
void *data);
-
-/**
- * Get the singleton instance of the manager
- *
- * @return instance, NULL if none available
- */
-pkcs11_manager_t *pkcs11_manager_get();
-
#endif /** PKCS11_MANAGER_H_ @}*/
diff --git a/src/libstrongswan/plugins/pkcs11/pkcs11_plugin.c b/src/libstrongswan/plugins/pkcs11/pkcs11_plugin.c
index 7b537cfa7..183fce53a 100644
--- a/src/libstrongswan/plugins/pkcs11/pkcs11_plugin.c
+++ b/src/libstrongswan/plugins/pkcs11/pkcs11_plugin.c
@@ -1,4 +1,7 @@
/*
+ * Copyright (C) 2011 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
* Copyright (C) 2010 Martin Willi
* Copyright (C) 2010 revosec AG
*
@@ -19,12 +22,15 @@
#include <debug.h>
#include <utils/linked_list.h>
#include <threading/mutex.h>
+#include <threading/rwlock.h>
#include "pkcs11_manager.h"
#include "pkcs11_creds.h"
#include "pkcs11_private_key.h"
#include "pkcs11_public_key.h"
#include "pkcs11_hasher.h"
+#include "pkcs11_rng.h"
+#include "pkcs11_dh.h"
typedef struct private_pkcs11_plugin_t private_pkcs11_plugin_t;
@@ -52,6 +58,16 @@ struct private_pkcs11_plugin_t {
* mutex to lock list
*/
mutex_t *mutex;
+
+ /**
+ * TRUE if events from tokens are to be handled
+ */
+ bool handle_events;
+
+ /**
+ * Lock for the above flag
+ */
+ rwlock_t *handle_events_lock;
};
/**
@@ -61,9 +77,10 @@ static void token_event_cb(private_pkcs11_plugin_t *this, pkcs11_library_t *p11,
CK_SLOT_ID slot, bool add)
{
enumerator_t *enumerator;
- pkcs11_creds_t *creds, *found = NULL;;
+ pkcs11_creds_t *creds, *found = NULL;
- if (add)
+ this->handle_events_lock->read_lock(this->handle_events_lock);
+ if (add && this->handle_events)
{
creds = pkcs11_creds_create(p11, slot);
if (creds)
@@ -74,7 +91,7 @@ static void token_event_cb(private_pkcs11_plugin_t *this, pkcs11_library_t *p11,
lib->credmgr->add_set(lib->credmgr, &creds->set);
}
}
- else
+ else if (this->handle_events)
{
this->mutex->lock(this->mutex);
enumerator = this->creds->create_enumerator(this->creds);
@@ -99,6 +116,7 @@ static void token_event_cb(private_pkcs11_plugin_t *this, pkcs11_library_t *p11,
lib->credmgr->flush_cache(lib->credmgr, CERT_X509);
}
}
+ this->handle_events_lock->unlock(this->handle_events_lock);
}
METHOD(plugin_t, get_name, char*,
@@ -107,23 +125,158 @@ METHOD(plugin_t, get_name, char*,
return "pkcs11";
}
-METHOD(plugin_t, destroy, void,
- private_pkcs11_plugin_t *this)
+/**
+ * Load/unload certificates from tokens.
+ */
+static bool handle_certs(private_pkcs11_plugin_t *this,
+ plugin_feature_t *feature, bool reg, void *data)
{
- pkcs11_creds_t *creds;
+ this->handle_events_lock->write_lock(this->handle_events_lock);
+ this->handle_events = reg;
+ this->handle_events_lock->unlock(this->handle_events_lock);
+
+ if (reg)
+ {
+ enumerator_t *enumerator;
+ pkcs11_library_t *p11;
+ CK_SLOT_ID slot;
+
+ enumerator = this->manager->create_token_enumerator(this->manager);
+ while (enumerator->enumerate(enumerator, &p11, &slot))
+ {
+ token_event_cb(this, p11, slot, TRUE);
+ }
+ enumerator->destroy(enumerator);
+ }
+ else
+ {
+ pkcs11_creds_t *creds;
- lib->creds->remove_builder(lib->creds,
- (builder_function_t)pkcs11_private_key_connect);
- while (this->creds->remove_last(this->creds, (void**)&creds) == SUCCESS)
+ while (this->creds->remove_last(this->creds, (void**)&creds) == SUCCESS)
+ {
+ lib->credmgr->remove_set(lib->credmgr, &creds->set);
+ creds->destroy(creds);
+ }
+ }
+ return TRUE;
+}
+/**
+ * Add a set of features
+ */
+static inline void add_features(plugin_feature_t *f, plugin_feature_t *n,
+ int count, int *pos)
+{
+ int i;
+ for (i = 0; i < count; i++)
{
- lib->credmgr->remove_set(lib->credmgr, &creds->set);
- creds->destroy(creds);
+ f[(*pos)++] = n[i];
}
- lib->crypto->remove_hasher(lib->crypto,
- (hasher_constructor_t)pkcs11_hasher_create);
- this->creds->destroy(this->creds);
+}
+
+METHOD(plugin_t, get_features, int,
+ private_pkcs11_plugin_t *this, plugin_feature_t *features[])
+{
+ static plugin_feature_t f_hash[] = {
+ PLUGIN_REGISTER(HASHER, pkcs11_hasher_create),
+ PLUGIN_PROVIDE(HASHER, HASH_MD2),
+ PLUGIN_PROVIDE(HASHER, HASH_MD5),
+ PLUGIN_PROVIDE(HASHER, HASH_SHA1),
+ PLUGIN_PROVIDE(HASHER, HASH_SHA256),
+ PLUGIN_PROVIDE(HASHER, HASH_SHA384),
+ PLUGIN_PROVIDE(HASHER, HASH_SHA512),
+ };
+ static plugin_feature_t f_dh[] = {
+ PLUGIN_REGISTER(DH, pkcs11_dh_create),
+ PLUGIN_PROVIDE(DH, MODP_2048_BIT),
+ PLUGIN_PROVIDE(DH, MODP_2048_224),
+ PLUGIN_PROVIDE(DH, MODP_2048_256),
+ PLUGIN_PROVIDE(DH, MODP_1536_BIT),
+ PLUGIN_PROVIDE(DH, MODP_3072_BIT),
+ PLUGIN_PROVIDE(DH, MODP_4096_BIT),
+ PLUGIN_PROVIDE(DH, MODP_6144_BIT),
+ PLUGIN_PROVIDE(DH, MODP_8192_BIT),
+ PLUGIN_PROVIDE(DH, MODP_1024_BIT),
+ PLUGIN_PROVIDE(DH, MODP_1024_160),
+ PLUGIN_PROVIDE(DH, MODP_768_BIT),
+ PLUGIN_PROVIDE(DH, MODP_CUSTOM),
+ };
+ static plugin_feature_t f_ecdh[] = {
+ PLUGIN_REGISTER(DH, pkcs11_dh_create),
+ PLUGIN_PROVIDE(DH, ECP_192_BIT),
+ PLUGIN_PROVIDE(DH, ECP_224_BIT),
+ PLUGIN_PROVIDE(DH, ECP_256_BIT),
+ PLUGIN_PROVIDE(DH, ECP_384_BIT),
+ PLUGIN_PROVIDE(DH, ECP_521_BIT),
+ };
+ static plugin_feature_t f_rng[] = {
+ PLUGIN_REGISTER(RNG, pkcs11_rng_create),
+ PLUGIN_PROVIDE(RNG, RNG_STRONG),
+ PLUGIN_PROVIDE(RNG, RNG_TRUE),
+ };
+ static plugin_feature_t f_privkey[] = {
+ PLUGIN_REGISTER(PRIVKEY, pkcs11_private_key_connect, FALSE),
+ PLUGIN_PROVIDE(PRIVKEY, KEY_ANY),
+ };
+ static plugin_feature_t f_pubkey[] = {
+ PLUGIN_REGISTER(PUBKEY, pkcs11_public_key_load, TRUE),
+ PLUGIN_PROVIDE(PUBKEY, KEY_RSA),
+ PLUGIN_PROVIDE(PUBKEY, KEY_ECDSA),
+ };
+ static plugin_feature_t f_manager[] = {
+ PLUGIN_CALLBACK((plugin_feature_callback_t)handle_certs, NULL),
+ PLUGIN_PROVIDE(CUSTOM, "pkcs11-certs"),
+ PLUGIN_DEPENDS(CERT_DECODE, CERT_X509),
+ };
+ static plugin_feature_t f[countof(f_hash) + countof(f_dh) + countof(f_rng) +
+ countof(f_ecdh) + countof(f_privkey) +
+ countof(f_pubkey) + countof(f_manager)] = {};
+ static int count = 0;
+
+ if (!count)
+ { /* initialize only once */
+ bool use_ecc = lib->settings->get_bool(lib->settings,
+ "libstrongswan.plugins.pkcs11.use_ecc", FALSE);
+ add_features(f, f_manager, countof(f_manager), &count);
+ /* private key handling for EC keys is not disabled by use_ecc */
+ add_features(f, f_privkey, countof(f_privkey), &count);
+ if (lib->settings->get_bool(lib->settings,
+ "libstrongswan.plugins.pkcs11.use_pubkey", FALSE))
+ {
+ add_features(f, f_pubkey, countof(f_pubkey) - (use_ecc ? 0 : 1),
+ &count);
+ }
+ if (lib->settings->get_bool(lib->settings,
+ "libstrongswan.plugins.pkcs11.use_hasher", FALSE))
+ {
+ add_features(f, f_hash, countof(f_hash), &count);
+ }
+ if (lib->settings->get_bool(lib->settings,
+ "libstrongswan.plugins.pkcs11.use_rng", FALSE))
+ {
+ add_features(f, f_rng, countof(f_rng), &count);
+ }
+ if (lib->settings->get_bool(lib->settings,
+ "libstrongswan.plugins.pkcs11.use_dh", FALSE))
+ {
+ add_features(f, f_dh, countof(f_dh), &count);
+ if (use_ecc)
+ {
+ add_features(f, f_ecdh, countof(f_ecdh), &count);
+ }
+ }
+ }
+ *features = f;
+ return count;
+}
+
+METHOD(plugin_t, destroy, void,
+ private_pkcs11_plugin_t *this)
+{
+ lib->set(lib, "pkcs11-manager", NULL);
this->manager->destroy(this->manager);
+ this->creds->destroy(this->creds);
this->mutex->destroy(this->mutex);
+ this->handle_events_lock->destroy(this->handle_events_lock);
free(this);
}
@@ -133,52 +286,22 @@ METHOD(plugin_t, destroy, void,
plugin_t *pkcs11_plugin_create()
{
private_pkcs11_plugin_t *this;
- enumerator_t *enumerator;
- pkcs11_library_t *p11;
- CK_SLOT_ID slot;
INIT(this,
.public = {
.plugin = {
.get_name = _get_name,
- .reload = (void*)return_false,
+ .get_features = _get_features,
.destroy = _destroy,
},
},
.creds = linked_list_create(),
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+ .handle_events_lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
);
this->manager = pkcs11_manager_create((void*)token_event_cb, this);
-
- if (lib->settings->get_bool(lib->settings,
- "libstrongswan.plugins.pkcs11.use_hasher", FALSE))
- {
- lib->crypto->add_hasher(lib->crypto, HASH_MD2, get_name(this),
- (hasher_constructor_t)pkcs11_hasher_create);
- lib->crypto->add_hasher(lib->crypto, HASH_MD5, get_name(this),
- (hasher_constructor_t)pkcs11_hasher_create);
- lib->crypto->add_hasher(lib->crypto, HASH_SHA1, get_name(this),
- (hasher_constructor_t)pkcs11_hasher_create);
- lib->crypto->add_hasher(lib->crypto, HASH_SHA256, get_name(this),
- (hasher_constructor_t)pkcs11_hasher_create);
- lib->crypto->add_hasher(lib->crypto, HASH_SHA384, get_name(this),
- (hasher_constructor_t)pkcs11_hasher_create);
- lib->crypto->add_hasher(lib->crypto, HASH_SHA512, get_name(this),
- (hasher_constructor_t)pkcs11_hasher_create);
- }
-
- lib->creds->add_builder(lib->creds, CRED_PRIVATE_KEY, KEY_ANY, FALSE,
- (builder_function_t)pkcs11_private_key_connect);
- lib->creds->add_builder(lib->creds, CRED_PUBLIC_KEY, KEY_RSA, TRUE,
- (builder_function_t)pkcs11_public_key_load);
-
- enumerator = this->manager->create_token_enumerator(this->manager);
- while (enumerator->enumerate(enumerator, &p11, &slot))
- {
- token_event_cb(this, p11, slot, TRUE);
- }
- enumerator->destroy(enumerator);
+ lib->set(lib, "pkcs11-manager", this->manager);
return &this->public.plugin;
}
diff --git a/src/libstrongswan/plugins/pkcs11/pkcs11_private_key.c b/src/libstrongswan/plugins/pkcs11/pkcs11_private_key.c
index b4cc7a805..b616abc38 100644
--- a/src/libstrongswan/plugins/pkcs11/pkcs11_private_key.c
+++ b/src/libstrongswan/plugins/pkcs11/pkcs11_private_key.c
@@ -1,4 +1,7 @@
/*
+ * Copyright (C) 2011 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
* Copyright (C) 2010 Martin Willi
* Copyright (C) 2010 revosec AG
*
@@ -19,7 +22,6 @@
#include "pkcs11_manager.h"
#include <debug.h>
-#include <threading/mutex.h>
typedef struct private_pkcs11_private_key_t private_pkcs11_private_key_t;
@@ -39,14 +41,14 @@ struct private_pkcs11_private_key_t {
pkcs11_library_t *lib;
/**
- * Token session
+ * Slot the token is in
*/
- CK_SESSION_HANDLE session;
+ CK_SLOT_ID slot;
/**
- * Mutex to lock session
+ * Token session
*/
- mutex_t *mutex;
+ CK_SESSION_HANDLE session;
/**
* Key object on the token
@@ -72,12 +74,24 @@ struct private_pkcs11_private_key_t {
* References to this key
*/
refcount_t ref;
+
+ /**
+ * Type of this private key
+ */
+ key_type_t type;
};
+/**
+ * Implemented in pkcs11_public_key.c
+ */
+public_key_t *pkcs11_public_key_connect(pkcs11_library_t *p11,
+ int slot, key_type_t type, chunk_t keyid);
+
+
METHOD(private_key_t, get_type, key_type_t,
private_pkcs11_private_key_t *this)
{
- return this->pubkey->get_type(this->pubkey);
+ return this->type;
}
METHOD(private_key_t, get_keysize, int,
@@ -89,18 +103,45 @@ METHOD(private_key_t, get_keysize, int,
/**
* See header.
*/
-CK_MECHANISM_PTR pkcs11_signature_scheme_to_mech(signature_scheme_t scheme)
+CK_MECHANISM_PTR pkcs11_signature_scheme_to_mech(signature_scheme_t scheme,
+ key_type_t type, size_t keylen,
+ hash_algorithm_t *hash)
{
static struct {
signature_scheme_t scheme;
CK_MECHANISM mechanism;
+ key_type_t type;
+ size_t keylen;
+ hash_algorithm_t hash;
} mappings[] = {
- {SIGN_RSA_EMSA_PKCS1_NULL, {CKM_RSA_PKCS, NULL, 0}},
- {SIGN_RSA_EMSA_PKCS1_SHA1, {CKM_SHA1_RSA_PKCS, NULL, 0}},
- {SIGN_RSA_EMSA_PKCS1_SHA256, {CKM_SHA256_RSA_PKCS, NULL, 0}},
- {SIGN_RSA_EMSA_PKCS1_SHA384, {CKM_SHA384_RSA_PKCS, NULL, 0}},
- {SIGN_RSA_EMSA_PKCS1_SHA512, {CKM_SHA512_RSA_PKCS, NULL, 0}},
- {SIGN_RSA_EMSA_PKCS1_MD5, {CKM_MD5_RSA_PKCS, NULL, 0}},
+ {SIGN_RSA_EMSA_PKCS1_NULL, {CKM_RSA_PKCS, NULL, 0},
+ KEY_RSA, 0, HASH_UNKNOWN},
+ {SIGN_RSA_EMSA_PKCS1_SHA1, {CKM_SHA1_RSA_PKCS, NULL, 0},
+ KEY_RSA, 0, HASH_UNKNOWN},
+ {SIGN_RSA_EMSA_PKCS1_SHA256, {CKM_SHA256_RSA_PKCS, NULL, 0},
+ KEY_RSA, 0, HASH_UNKNOWN},
+ {SIGN_RSA_EMSA_PKCS1_SHA384, {CKM_SHA384_RSA_PKCS, NULL, 0},
+ KEY_RSA, 0, HASH_UNKNOWN},
+ {SIGN_RSA_EMSA_PKCS1_SHA512, {CKM_SHA512_RSA_PKCS, NULL, 0},
+ KEY_RSA, 0, HASH_UNKNOWN},
+ {SIGN_RSA_EMSA_PKCS1_MD5, {CKM_MD5_RSA_PKCS, NULL, 0},
+ KEY_RSA, 0, HASH_UNKNOWN},
+ {SIGN_ECDSA_WITH_NULL, {CKM_ECDSA, NULL, 0},
+ KEY_ECDSA, 0, HASH_UNKNOWN},
+ {SIGN_ECDSA_WITH_SHA1_DER, {CKM_ECDSA_SHA1, NULL, 0},
+ KEY_ECDSA, 0, HASH_UNKNOWN},
+ {SIGN_ECDSA_WITH_SHA256_DER, {CKM_ECDSA, NULL, 0},
+ KEY_ECDSA, 0, HASH_SHA256},
+ {SIGN_ECDSA_WITH_SHA384_DER, {CKM_ECDSA, NULL, 0},
+ KEY_ECDSA, 0, HASH_SHA384},
+ {SIGN_ECDSA_WITH_SHA512_DER, {CKM_ECDSA, NULL, 0},
+ KEY_ECDSA, 0, HASH_SHA512},
+ {SIGN_ECDSA_256, {CKM_ECDSA, NULL, 0},
+ KEY_ECDSA, 256, HASH_SHA256},
+ {SIGN_ECDSA_384, {CKM_ECDSA, NULL, 0},
+ KEY_ECDSA, 384, HASH_SHA384},
+ {SIGN_ECDSA_521, {CKM_ECDSA, NULL, 0},
+ KEY_ECDSA, 521, HASH_SHA512},
};
int i;
@@ -108,6 +149,15 @@ CK_MECHANISM_PTR pkcs11_signature_scheme_to_mech(signature_scheme_t scheme)
{
if (mappings[i].scheme == scheme)
{
+ size_t len = mappings[i].keylen;
+ if (mappings[i].type != type || (len && keylen != len))
+ {
+ return NULL;
+ }
+ if (hash)
+ {
+ *hash = mappings[i].hash;
+ }
return &mappings[i].mechanism;
}
}
@@ -141,7 +191,8 @@ CK_MECHANISM_PTR pkcs11_encryption_scheme_to_mech(encryption_scheme_t scheme)
/**
* Reauthenticate to do a signature
*/
-static bool reauth(private_pkcs11_private_key_t *this)
+static bool reauth(private_pkcs11_private_key_t *this,
+ CK_SESSION_HANDLE session)
{
enumerator_t *enumerator;
shared_key_t *shared;
@@ -155,7 +206,7 @@ static bool reauth(private_pkcs11_private_key_t *this)
{
found = TRUE;
pin = shared->get_key(shared);
- rv = this->lib->f->C_Login(this->session, CKU_CONTEXT_SPECIFIC,
+ rv = this->lib->f->C_Login(session, CKU_CONTEXT_SPECIFIC,
pin.ptr, pin.len);
if (rv == CKR_OK)
{
@@ -179,33 +230,61 @@ METHOD(private_key_t, sign, bool,
chunk_t data, chunk_t *signature)
{
CK_MECHANISM_PTR mechanism;
+ CK_SESSION_HANDLE session;
CK_BYTE_PTR buf;
CK_ULONG len;
CK_RV rv;
+ hash_algorithm_t hash_alg;
+ chunk_t hash = chunk_empty;
- mechanism = pkcs11_signature_scheme_to_mech(scheme);
+ mechanism = pkcs11_signature_scheme_to_mech(scheme, this->type,
+ get_keysize(this), &hash_alg);
if (!mechanism)
{
DBG1(DBG_LIB, "signature scheme %N not supported",
signature_scheme_names, scheme);
return FALSE;
}
- this->mutex->lock(this->mutex);
- rv = this->lib->f->C_SignInit(this->session, mechanism, this->object);
- if (this->reauth && !reauth(this))
+ rv = this->lib->f->C_OpenSession(this->slot, CKF_SERIAL_SESSION, NULL, NULL,
+ &session);
+ if (rv != CKR_OK)
{
+ DBG1(DBG_CFG, "opening PKCS#11 session failed: %N", ck_rv_names, rv);
+ return FALSE;
+ }
+ rv = this->lib->f->C_SignInit(session, mechanism, this->object);
+ if (this->reauth && !reauth(this, session))
+ {
+ this->lib->f->C_CloseSession(session);
return FALSE;
}
if (rv != CKR_OK)
{
- this->mutex->unlock(this->mutex);
+ this->lib->f->C_CloseSession(session);
DBG1(DBG_LIB, "C_SignInit() failed: %N", ck_rv_names, rv);
return FALSE;
}
+ if (hash_alg != HASH_UNKNOWN)
+ {
+ hasher_t *hasher = lib->crypto->create_hasher(lib->crypto, hash_alg);
+ if (!hasher)
+ {
+ this->lib->f->C_CloseSession(session);
+ return FALSE;
+ }
+ hasher->allocate_hash(hasher, data, &hash);
+ hasher->destroy(hasher);
+ data = hash;
+ }
len = (get_keysize(this) + 7) / 8;
+ if (this->type == KEY_ECDSA)
+ { /* signature is twice the length of the base point order */
+ len *= 2;
+ }
buf = malloc(len);
- rv = this->lib->f->C_Sign(this->session, data.ptr, data.len, buf, &len);
- this->mutex->unlock(this->mutex);
+ rv = this->lib->f->C_Sign(session, data.ptr, data.len, buf, &len);
+ this->lib->f->C_CloseSession(session);
+ chunk_free(&hash);
if (rv != CKR_OK)
{
DBG1(DBG_LIB, "C_Sign() failed: %N", ck_rv_names, rv);
@@ -221,6 +300,7 @@ METHOD(private_key_t, decrypt, bool,
chunk_t crypt, chunk_t *plain)
{
CK_MECHANISM_PTR mechanism;
+ CK_SESSION_HANDLE session;
CK_BYTE_PTR buf;
CK_ULONG len;
CK_RV rv;
@@ -232,22 +312,29 @@ METHOD(private_key_t, decrypt, bool,
encryption_scheme_names, scheme);
return FALSE;
}
- this->mutex->lock(this->mutex);
- rv = this->lib->f->C_DecryptInit(this->session, mechanism, this->object);
- if (this->reauth && !reauth(this))
+ rv = this->lib->f->C_OpenSession(this->slot, CKF_SERIAL_SESSION, NULL, NULL,
+ &session);
+ if (rv != CKR_OK)
{
+ DBG1(DBG_CFG, "opening PKCS#11 session failed: %N", ck_rv_names, rv);
+ return FALSE;
+ }
+ rv = this->lib->f->C_DecryptInit(session, mechanism, this->object);
+ if (this->reauth && !reauth(this, session))
+ {
+ this->lib->f->C_CloseSession(session);
return FALSE;
}
if (rv != CKR_OK)
{
- this->mutex->unlock(this->mutex);
+ this->lib->f->C_CloseSession(session);
DBG1(DBG_LIB, "C_DecryptInit() failed: %N", ck_rv_names, rv);
return FALSE;
}
len = (get_keysize(this) + 7) / 8;
buf = malloc(len);
- rv = this->lib->f->C_Decrypt(this->session, crypt.ptr, crypt.len, buf, &len);
- this->mutex->unlock(this->mutex);
+ rv = this->lib->f->C_Decrypt(session, crypt.ptr, crypt.len, buf, &len);
+ this->lib->f->C_CloseSession(session);
if (rv != CKR_OK)
{
DBG1(DBG_LIB, "C_Decrypt() failed: %N", ck_rv_names, rv);
@@ -294,7 +381,6 @@ METHOD(private_key_t, destroy, void,
{
this->pubkey->destroy(this->pubkey);
}
- this->mutex->destroy(this->mutex);
this->keyid->destroy(this->keyid);
this->lib->f->C_CloseSession(this->session);
free(this);
@@ -311,7 +397,7 @@ static pkcs11_library_t* find_lib(char *module)
pkcs11_library_t *p11, *found = NULL;
CK_SLOT_ID slot;
- manager = pkcs11_manager_get();
+ manager = lib->get(lib, "pkcs11-manager");
if (!manager)
{
return NULL;
@@ -339,7 +425,7 @@ static pkcs11_library_t* find_lib_by_keyid(chunk_t keyid, int *slot)
pkcs11_library_t *p11, *found = NULL;
CK_SLOT_ID current;
- manager = pkcs11_manager_get();
+ manager = lib->get(lib, "pkcs11-manager");
if (!manager)
{
return NULL;
@@ -404,13 +490,11 @@ static bool find_key(private_pkcs11_private_key_t *this, chunk_t keyid)
CK_BBOOL reauth = FALSE;
CK_ATTRIBUTE attr[] = {
{CKA_KEY_TYPE, &type, sizeof(type)},
- {CKA_MODULUS, NULL, 0},
- {CKA_PUBLIC_EXPONENT, NULL, 0},
{CKA_ALWAYS_AUTHENTICATE, &reauth, sizeof(reauth)},
};
enumerator_t *enumerator;
- chunk_t modulus, pubexp;
int count = countof(attr);
+ bool found = FALSE;
/* do not use CKA_ALWAYS_AUTHENTICATE if not supported */
if (!(this->lib->get_features(this->lib) & PKCS11_ALWAYS_AUTH_KEYS))
@@ -421,26 +505,16 @@ static bool find_key(private_pkcs11_private_key_t *this, chunk_t keyid)
this->session, tmpl, countof(tmpl), attr, count);
if (enumerator->enumerate(enumerator, &object))
{
+ this->type = KEY_RSA;
switch (type)
{
+ case CKK_ECDSA:
+ this->type = KEY_ECDSA;
+ /* fall-through */
case CKK_RSA:
- if (attr[1].ulValueLen == -1 || attr[2].ulValueLen == -1)
- {
- DBG1(DBG_CFG, "reading modulus/exponent from PKCS#1 failed");
- break;
- }
- modulus = chunk_create(attr[1].pValue, attr[1].ulValueLen);
- pubexp = chunk_create(attr[2].pValue, attr[2].ulValueLen);
- this->pubkey = lib->creds->create(lib->creds, CRED_PUBLIC_KEY,
- KEY_RSA, BUILD_RSA_MODULUS, modulus,
- BUILD_RSA_PUB_EXP, pubexp, BUILD_END);
- if (!this->pubkey)
- {
- DBG1(DBG_CFG, "extracting public key from PKCS#11 RSA "
- "private key failed");
- }
this->reauth = reauth;
this->object = object;
+ found = TRUE;
break;
default:
DBG1(DBG_CFG, "PKCS#11 key type %d not supported", type);
@@ -448,7 +522,7 @@ static bool find_key(private_pkcs11_private_key_t *this, chunk_t keyid)
}
}
enumerator->destroy(enumerator);
- return this->pubkey != NULL;
+ return found;
}
/**
@@ -587,7 +661,7 @@ pkcs11_private_key_t *pkcs11_private_key_connect(key_type_t type, va_list args)
return NULL;
}
- this->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
+ this->slot = slot;
this->keyid = identification_create_from_encoding(ID_KEY_ID, keyid);
if (!login(this, slot))
@@ -602,5 +676,13 @@ pkcs11_private_key_t *pkcs11_private_key_connect(key_type_t type, va_list args)
return NULL;
}
+ this->pubkey = pkcs11_public_key_connect(this->lib, slot, this->type,
+ keyid);
+ if (!this->pubkey)
+ {
+ destroy(this);
+ return NULL;
+ }
+
return &this->public;
}
diff --git a/src/libstrongswan/plugins/pkcs11/pkcs11_private_key.h b/src/libstrongswan/plugins/pkcs11/pkcs11_private_key.h
index 428913f0a..6d3a9556e 100644
--- a/src/libstrongswan/plugins/pkcs11/pkcs11_private_key.h
+++ b/src/libstrongswan/plugins/pkcs11/pkcs11_private_key.h
@@ -1,4 +1,7 @@
/*
+ * Copyright (C) 2011 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
* Copyright (C) 2010 Martin Willi
* Copyright (C) 2010 revosec AG
*
@@ -46,14 +49,23 @@ struct pkcs11_private_key_t {
*
* @param type type of the key
* @param args builder_part_t argument list
- * @return loaded key, NULL on failure
+ * @return loaded key, NULL on failure
*/
pkcs11_private_key_t *pkcs11_private_key_connect(key_type_t type, va_list args);
/**
* Get the Cryptoki mechanism for a signature scheme.
+ *
+ * Verifies that the given key is usable for this scheme.
+ *
+ * @param scheme signature scheme
+ * @param type key type
+ * @param keylen key length in bits
+ * @param hash hash algorithm to apply first (HASH_UNKNOWN if none)
*/
-CK_MECHANISM_PTR pkcs11_signature_scheme_to_mech(signature_scheme_t scheme);
+CK_MECHANISM_PTR pkcs11_signature_scheme_to_mech(signature_scheme_t scheme,
+ key_type_t type, size_t keylen,
+ hash_algorithm_t *hash);
/**
* Get the Cryptoki mechanism for a encryption scheme.
diff --git a/src/libstrongswan/plugins/pkcs11/pkcs11_public_key.c b/src/libstrongswan/plugins/pkcs11/pkcs11_public_key.c
index 8d32d9a3f..d4ec9235d 100644
--- a/src/libstrongswan/plugins/pkcs11/pkcs11_public_key.c
+++ b/src/libstrongswan/plugins/pkcs11/pkcs11_public_key.c
@@ -1,4 +1,7 @@
/*
+ * Copyright (C) 2011 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
* Copyright (C) 2010 Martin Willi
* Copyright (C) 2010 revosec AG
*
@@ -19,8 +22,10 @@
#include "pkcs11_private_key.h"
#include "pkcs11_manager.h"
+#include <asn1/oid.h>
+#include <asn1/asn1.h>
+#include <asn1/asn1_parser.h>
#include <debug.h>
-#include <threading/mutex.h>
typedef struct private_pkcs11_public_key_t private_pkcs11_public_key_t;
@@ -40,7 +45,7 @@ struct private_pkcs11_public_key_t {
key_type_t type;
/**
- * Key size in bytes
+ * Key size in bits
*/
size_t k;
@@ -65,16 +70,121 @@ struct private_pkcs11_public_key_t {
CK_OBJECT_HANDLE object;
/**
- * Mutex to lock session
- */
- mutex_t *mutex;
-
- /**
* References to this key
*/
refcount_t ref;
};
+/**
+ * Helper function that returns the base point order length in bits of the
+ * given named curve.
+ *
+ * Currently only a subset of defined curves is supported (namely the 5 curves
+ * over Fp recommended by NIST). IKEv2 only supports 3 out of these.
+ *
+ * 0 is returned if the given curve is not supported.
+ */
+static size_t basepoint_order_len(int oid)
+{
+ switch (oid)
+ {
+ case OID_PRIME192V1:
+ return 192;
+ case OID_SECT224R1:
+ return 224;
+ case OID_PRIME256V1:
+ return 256;
+ case OID_SECT384R1:
+ return 384;
+ case OID_SECT521R1:
+ return 521;
+ default:
+ return 0;
+ }
+}
+
+/**
+ * Parses the given ecParameters (ASN.1) and returns the key length.
+ */
+static bool keylen_from_ecparams(chunk_t ecparams, size_t *keylen)
+{
+ if (!asn1_parse_simple_object(&ecparams, ASN1_OID, 0, "named curve"))
+ {
+ return FALSE;
+ }
+ *keylen = basepoint_order_len(asn1_known_oid(ecparams));
+ return *keylen > 0;
+}
+
+/**
+ * ASN.1 definition of a subjectPublicKeyInfo structure when used with ECDSA
+ * we currently only support named curves.
+ */
+static const asn1Object_t pkinfoObjects[] = {
+ { 0, "subjectPublicKeyInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
+ { 1, "algorithmIdentifier", ASN1_SEQUENCE, ASN1_NONE }, /* 1 */
+ { 2, "algorithm", ASN1_OID, ASN1_BODY }, /* 2 */
+ { 2, "namedCurve", ASN1_OID, ASN1_RAW }, /* 3 */
+ { 1, "subjectPublicKey", ASN1_BIT_STRING, ASN1_BODY }, /* 4 */
+ { 0, "exit", ASN1_EOC, ASN1_EXIT }
+};
+#define PKINFO_SUBJECT_PUBLIC_KEY_ALGORITHM 2
+#define PKINFO_SUBJECT_PUBLIC_KEY_NAMEDCURVE 3
+#define PKINFO_SUBJECT_PUBLIC_KEY 4
+
+/**
+ * Extract the DER encoded Parameters and ECPoint from the given DER encoded
+ * subjectPublicKeyInfo.
+ */
+static bool parse_ecdsa_public_key(chunk_t blob, chunk_t *ecparams,
+ chunk_t *ecpoint, size_t *keylen)
+{
+ asn1_parser_t *parser;
+ chunk_t object;
+ int objectID;
+ bool success = FALSE;
+
+ parser = asn1_parser_create(pkinfoObjects, blob);
+
+ while (parser->iterate(parser, &objectID, &object))
+ {
+ switch (objectID)
+ {
+ case PKINFO_SUBJECT_PUBLIC_KEY_ALGORITHM:
+ {
+ if (asn1_known_oid(object) != OID_EC_PUBLICKEY)
+ {
+ goto end;
+ }
+ break;
+ }
+ case PKINFO_SUBJECT_PUBLIC_KEY_NAMEDCURVE:
+ {
+ *ecparams = object;
+ if (!keylen_from_ecparams(object, keylen))
+ {
+ goto end;
+ }
+ break;
+ }
+ case PKINFO_SUBJECT_PUBLIC_KEY:
+ {
+ if (object.len > 0 && *object.ptr == 0x00)
+ { /* skip initial bit string octet defining 0 unused bits */
+ object = chunk_skip(object, 1);
+ }
+ *ecpoint = object;
+ break;
+ }
+ }
+ }
+ success = parser->success(parser);
+end:
+ parser->destroy(parser);
+ return success;
+}
+
+
METHOD(public_key_t, get_type, key_type_t,
private_pkcs11_public_key_t *this)
{
@@ -84,7 +194,7 @@ METHOD(public_key_t, get_type, key_type_t,
METHOD(public_key_t, get_keysize, int,
private_pkcs11_public_key_t *this)
{
- return this->k * 8;
+ return this->k;
}
METHOD(public_key_t, verify, bool,
@@ -92,9 +202,13 @@ METHOD(public_key_t, verify, bool,
chunk_t data, chunk_t sig)
{
CK_MECHANISM_PTR mechanism;
+ CK_SESSION_HANDLE session;
CK_RV rv;
+ hash_algorithm_t hash_alg;
+ chunk_t hash = chunk_empty;
- mechanism = pkcs11_signature_scheme_to_mech(scheme);
+ mechanism = pkcs11_signature_scheme_to_mech(scheme, this->type, this->k,
+ &hash_alg);
if (!mechanism)
{
DBG1(DBG_LIB, "signature scheme %N not supported",
@@ -105,17 +219,35 @@ METHOD(public_key_t, verify, bool,
{ /* trim leading zero byte in sig */
sig = chunk_skip(sig, 1);
}
- this->mutex->lock(this->mutex);
- rv = this->lib->f->C_VerifyInit(this->session, mechanism, this->object);
+ rv = this->lib->f->C_OpenSession(this->slot, CKF_SERIAL_SESSION, NULL, NULL,
+ &session);
if (rv != CKR_OK)
{
- this->mutex->unlock(this->mutex);
+ DBG1(DBG_CFG, "opening PKCS#11 session failed: %N", ck_rv_names, rv);
+ return FALSE;
+ }
+ rv = this->lib->f->C_VerifyInit(session, mechanism, this->object);
+ if (rv != CKR_OK)
+ {
+ this->lib->f->C_CloseSession(session);
DBG1(DBG_LIB, "C_VerifyInit() failed: %N", ck_rv_names, rv);
return FALSE;
}
- rv = this->lib->f->C_Verify(this->session, data.ptr, data.len,
- sig.ptr, sig.len);
- this->mutex->unlock(this->mutex);
+ if (hash_alg != HASH_UNKNOWN)
+ {
+ hasher_t *hasher = lib->crypto->create_hasher(lib->crypto, hash_alg);
+ if (!hasher)
+ {
+ this->lib->f->C_CloseSession(session);
+ return FALSE;
+ }
+ hasher->allocate_hash(hasher, data, &hash);
+ hasher->destroy(hasher);
+ data = hash;
+ }
+ rv = this->lib->f->C_Verify(session, data.ptr, data.len, sig.ptr, sig.len);
+ this->lib->f->C_CloseSession(session);
+ chunk_free(&hash);
if (rv != CKR_OK)
{
DBG1(DBG_LIB, "C_Verify() failed: %N", ck_rv_names, rv);
@@ -129,6 +261,7 @@ METHOD(public_key_t, encrypt, bool,
chunk_t plain, chunk_t *crypt)
{
CK_MECHANISM_PTR mechanism;
+ CK_SESSION_HANDLE session;
CK_BYTE_PTR buf;
CK_ULONG len;
CK_RV rv;
@@ -140,18 +273,24 @@ METHOD(public_key_t, encrypt, bool,
encryption_scheme_names, scheme);
return FALSE;
}
- this->mutex->lock(this->mutex);
- rv = this->lib->f->C_EncryptInit(this->session, mechanism, this->object);
+ rv = this->lib->f->C_OpenSession(this->slot, CKF_SERIAL_SESSION, NULL, NULL,
+ &session);
if (rv != CKR_OK)
{
- this->mutex->unlock(this->mutex);
+ DBG1(DBG_CFG, "opening PKCS#11 session failed: %N", ck_rv_names, rv);
+ return FALSE;
+ }
+ rv = this->lib->f->C_EncryptInit(session, mechanism, this->object);
+ if (rv != CKR_OK)
+ {
+ this->lib->f->C_CloseSession(session);
DBG1(DBG_LIB, "C_EncryptInit() failed: %N", ck_rv_names, rv);
return FALSE;
}
len = (get_keysize(this) + 7) / 8;
buf = malloc(len);
- rv = this->lib->f->C_Encrypt(this->session, plain.ptr, plain.len, buf, &len);
- this->mutex->unlock(this->mutex);
+ rv = this->lib->f->C_Encrypt(session, plain.ptr, plain.len, buf, &len);
+ this->lib->f->C_CloseSession(session);
if (rv != CKR_OK)
{
DBG1(DBG_LIB, "C_Encrypt() failed: %N", ck_rv_names, rv);
@@ -163,40 +302,119 @@ METHOD(public_key_t, encrypt, bool,
}
/**
+ * Encode ECDSA key using a given encoding type
+ */
+static bool encode_ecdsa(private_pkcs11_public_key_t *this,
+ cred_encoding_type_t type, chunk_t *encoding)
+{
+ enumerator_t *enumerator;
+ bool success = FALSE;
+ CK_ATTRIBUTE attr[] = {
+ {CKA_EC_PARAMS, NULL, 0},
+ {CKA_EC_POINT, NULL, 0},
+ };
+
+ if (type != PUBKEY_SPKI_ASN1_DER && type != PUBKEY_PEM)
+ {
+ return FALSE;
+ }
+
+ enumerator = this->lib->create_object_attr_enumerator(this->lib,
+ this->session, this->object, attr, countof(attr));
+ if (enumerator && enumerator->enumerate(enumerator, NULL) &&
+ attr[0].ulValueLen > 0 && attr[1].ulValueLen > 0)
+ {
+ chunk_t ecparams, ecpoint;
+ ecparams = chunk_create(attr[0].pValue, attr[0].ulValueLen);
+ ecpoint = chunk_create(attr[1].pValue, attr[1].ulValueLen);
+ /* encode as subjectPublicKeyInfo */
+ *encoding = asn1_wrap(ASN1_SEQUENCE, "mm",
+ asn1_wrap(ASN1_SEQUENCE, "mc",
+ asn1_build_known_oid(OID_EC_PUBLICKEY), ecparams),
+ asn1_bitstring("c", ecpoint));
+ success = TRUE;
+ if (type == PUBKEY_PEM)
+ {
+ chunk_t asn1 = *encoding;
+ success = lib->encoding->encode(lib->encoding, PUBKEY_PEM,
+ NULL, encoding, CRED_PART_ECDSA_PUB_ASN1_DER,
+ asn1, CRED_PART_END);
+ chunk_clear(&asn1);
+ }
+ }
+ DESTROY_IF(enumerator);
+ return success;
+}
+
+/**
+ * Compute fingerprint of an ECDSA key
+ */
+static bool fingerprint_ecdsa(private_pkcs11_public_key_t *this,
+ cred_encoding_type_t type, chunk_t *fp)
+{
+ hasher_t *hasher;
+ chunk_t asn1;
+
+ switch (type)
+ {
+ case KEYID_PUBKEY_SHA1:
+ if (!this->lib->get_ck_attribute(this->lib, this->session,
+ this->object, CKA_EC_POINT, &asn1))
+ {
+ return FALSE;
+ }
+ break;
+ case KEYID_PUBKEY_INFO_SHA1:
+ if (!encode_ecdsa(this, PUBKEY_SPKI_ASN1_DER, &asn1))
+ {
+ return FALSE;
+ }
+ break;
+ default:
+ return FALSE;
+ }
+ hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
+ if (!hasher)
+ {
+ chunk_clear(&asn1);
+ return FALSE;
+ }
+ hasher->allocate_hash(hasher, asn1, fp);
+ hasher->destroy(hasher);
+ chunk_clear(&asn1);
+ lib->encoding->cache(lib->encoding, type, this, *fp);
+ return TRUE;
+}
+
+/**
* Encode RSA key using a given encoding type
*/
static bool encode_rsa(private_pkcs11_public_key_t *this,
cred_encoding_type_t type, void *cache, chunk_t *encoding)
{
- CK_RV rv;
+ enumerator_t *enumerator;
bool success = FALSE;
- chunk_t n, e;
CK_ATTRIBUTE attr[] = {
{CKA_MODULUS, NULL, 0},
{CKA_PUBLIC_EXPONENT, NULL, 0},
};
- rv = this->lib->f->C_GetAttributeValue(this->session, this->object,
- attr, countof(attr));
- if (rv != CKR_OK ||
- attr[0].ulValueLen == 0 || attr[0].ulValueLen == -1 ||
- attr[1].ulValueLen == 0 || attr[1].ulValueLen == -1)
- {
- return FALSE;
- }
- attr[0].pValue = malloc(attr[0].ulValueLen);
- attr[1].pValue = malloc(attr[1].ulValueLen);
- rv = this->lib->f->C_GetAttributeValue(this->session, this->object,
- attr, countof(attr));
- if (rv == CKR_OK)
+ enumerator = this->lib->create_object_attr_enumerator(this->lib,
+ this->session, this->object, attr, countof(attr));
+ if (enumerator && enumerator->enumerate(enumerator, NULL) &&
+ attr[0].ulValueLen > 0 && attr[1].ulValueLen > 0)
{
+ chunk_t n, e;
n = chunk_create(attr[0].pValue, attr[0].ulValueLen);
+ if (n.ptr[0] & 0x80)
+ { /* add leading 0x00, encoders expect it already like this */
+ n = chunk_cata("cc", chunk_from_chars(0x00), n);
+ }
e = chunk_create(attr[1].pValue, attr[1].ulValueLen);
success = lib->encoding->encode(lib->encoding, type, cache, encoding,
CRED_PART_RSA_MODULUS, n, CRED_PART_RSA_PUB_EXP, e, CRED_PART_END);
}
- free(attr[0].pValue);
- free(attr[1].pValue);
+ DESTROY_IF(enumerator);
return success;
}
@@ -208,6 +426,8 @@ METHOD(public_key_t, get_encoding, bool,
{
case KEY_RSA:
return encode_rsa(this, type, NULL, encoding);
+ case KEY_ECDSA:
+ return encode_ecdsa(this, type, encoding);
default:
return FALSE;
}
@@ -224,6 +444,8 @@ METHOD(public_key_t, get_fingerprint, bool,
{
case KEY_RSA:
return encode_rsa(this, type, this, fp);
+ case KEY_ECDSA:
+ return fingerprint_ecdsa(this, type, fp);
default:
return FALSE;
}
@@ -243,7 +465,6 @@ METHOD(public_key_t, destroy, void,
{
lib->encoding->clear_cache(lib->encoding, this);
this->lib->f->C_CloseSession(this->session);
- this->mutex->destroy(this->mutex);
free(this);
}
}
@@ -278,7 +499,6 @@ static private_pkcs11_public_key_t *create(key_type_t type, size_t k,
.slot = slot,
.session = session,
.object = object,
- .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
.ref = 1,
);
@@ -288,7 +508,8 @@ static private_pkcs11_public_key_t *create(key_type_t type, size_t k,
/**
* Find a key object, including PKCS11 library and slot
*/
-static private_pkcs11_public_key_t* find_rsa_key(chunk_t n, chunk_t e)
+static private_pkcs11_public_key_t* find_key(key_type_t type, size_t keylen,
+ CK_ATTRIBUTE_PTR tmpl, int count)
{
private_pkcs11_public_key_t *this = NULL;
pkcs11_manager_t *manager;
@@ -296,7 +517,7 @@ static private_pkcs11_public_key_t* find_rsa_key(chunk_t n, chunk_t e)
pkcs11_library_t *p11;
CK_SLOT_ID slot;
- manager = pkcs11_manager_get();
+ manager = lib->get(lib, "pkcs11-manager");
if (!manager)
{
return NULL;
@@ -305,14 +526,6 @@ static private_pkcs11_public_key_t* find_rsa_key(chunk_t n, chunk_t e)
enumerator = manager->create_token_enumerator(manager);
while (enumerator->enumerate(enumerator, &p11, &slot))
{
- CK_OBJECT_CLASS class = CKO_PUBLIC_KEY;
- CK_KEY_TYPE type = CKK_RSA;
- CK_ATTRIBUTE tmpl[] = {
- {CKA_CLASS, &class, sizeof(class)},
- {CKA_KEY_TYPE, &type, sizeof(type)},
- {CKA_MODULUS, n.ptr, n.len},
- {CKA_PUBLIC_EXPONENT, e.ptr, e.len},
- };
CK_OBJECT_HANDLE object;
CK_SESSION_HANDLE session;
CK_RV rv;
@@ -324,11 +537,11 @@ static private_pkcs11_public_key_t* find_rsa_key(chunk_t n, chunk_t e)
DBG1(DBG_CFG, "opening PKCS#11 session failed: %N", ck_rv_names, rv);
continue;
}
- keys = p11->create_object_enumerator(p11, session,
- tmpl, countof(tmpl), NULL, 0);
+ keys = p11->create_object_enumerator(p11, session, tmpl, count,
+ NULL, 0);
if (keys->enumerate(keys, &object))
{
- this = create(KEY_RSA, n.len, p11, slot, session, object);
+ this = create(type, keylen, p11, slot, session, object);
keys->destroy(keys);
break;
}
@@ -340,9 +553,46 @@ static private_pkcs11_public_key_t* find_rsa_key(chunk_t n, chunk_t e)
}
/**
+ * Find an RSA key object
+ */
+static private_pkcs11_public_key_t* find_rsa_key(chunk_t n, chunk_t e,
+ size_t keylen)
+{
+ CK_OBJECT_CLASS class = CKO_PUBLIC_KEY;
+ CK_KEY_TYPE type = CKK_RSA;
+ CK_ATTRIBUTE tmpl[] = {
+ {CKA_CLASS, &class, sizeof(class)},
+ {CKA_KEY_TYPE, &type, sizeof(type)},
+ {CKA_MODULUS, n.ptr, n.len},
+ {CKA_PUBLIC_EXPONENT, e.ptr, e.len},
+ };
+ return find_key(KEY_RSA, keylen, tmpl, countof(tmpl));
+}
+
+/**
+ * Find an ECDSA key object
+ */
+static private_pkcs11_public_key_t* find_ecdsa_key(chunk_t ecparams,
+ chunk_t ecpoint,
+ size_t keylen)
+{
+ CK_OBJECT_CLASS class = CKO_PUBLIC_KEY;
+ CK_KEY_TYPE type = CKK_ECDSA;
+ CK_ATTRIBUTE tmpl[] = {
+ {CKA_CLASS, &class, sizeof(class)},
+ {CKA_KEY_TYPE, &type, sizeof(type)},
+ {CKA_EC_PARAMS, ecparams.ptr, ecparams.len},
+ {CKA_EC_POINT, ecpoint.ptr, ecpoint.len},
+ };
+ return find_key(KEY_ECDSA, keylen, tmpl, countof(tmpl));
+}
+
+/**
* Create a key object in a suitable token session
*/
-static private_pkcs11_public_key_t* create_rsa_key(chunk_t n, chunk_t e)
+static private_pkcs11_public_key_t* create_key(key_type_t type, size_t keylen,
+ CK_MECHANISM_TYPE_PTR mechanisms, int mcount,
+ CK_ATTRIBUTE_PTR tmpl, int count)
{
private_pkcs11_public_key_t *this = NULL;
pkcs11_manager_t *manager;
@@ -350,7 +600,7 @@ static private_pkcs11_public_key_t* create_rsa_key(chunk_t n, chunk_t e)
pkcs11_library_t *p11;
CK_SLOT_ID slot;
- manager = pkcs11_manager_get();
+ manager = lib->get(lib, "pkcs11-manager");
if (!manager)
{
return NULL;
@@ -361,14 +611,6 @@ static private_pkcs11_public_key_t* create_rsa_key(chunk_t n, chunk_t e)
{
CK_MECHANISM_TYPE mech;
CK_MECHANISM_INFO info;
- CK_OBJECT_CLASS class = CKO_PUBLIC_KEY;
- CK_KEY_TYPE type = CKK_RSA;
- CK_ATTRIBUTE tmpl[] = {
- {CKA_CLASS, &class, sizeof(class)},
- {CKA_KEY_TYPE, &type, sizeof(type)},
- {CKA_MODULUS, n.ptr, n.len},
- {CKA_PUBLIC_EXPONENT, e.ptr, e.len}
- };
CK_OBJECT_HANDLE object;
CK_SESSION_HANDLE session;
CK_RV rv;
@@ -376,21 +618,23 @@ static private_pkcs11_public_key_t* create_rsa_key(chunk_t n, chunk_t e)
mechs = p11->create_mechanism_enumerator(p11, slot);
while (mechs->enumerate(mechs, &mech, &info))
{
+ bool found = FALSE;
+ int i;
if (!(info.flags & CKF_VERIFY))
{
continue;
}
- switch (mech)
+ for (i = 0; i < mcount; i++)
{
- case CKM_RSA_PKCS:
- case CKM_SHA1_RSA_PKCS:
- case CKM_SHA256_RSA_PKCS:
- case CKM_SHA384_RSA_PKCS:
- case CKM_SHA512_RSA_PKCS:
- case CKM_MD5_RSA_PKCS:
+ if (mechanisms[i] == mech)
+ {
+ found = TRUE;
break;
- default:
- continue;
+ }
+ }
+ if (!found)
+ {
+ continue;
}
rv = p11->f->C_OpenSession(slot, CKF_SERIAL_SESSION, NULL, NULL,
&session);
@@ -400,20 +644,21 @@ static private_pkcs11_public_key_t* create_rsa_key(chunk_t n, chunk_t e)
ck_rv_names, rv);
continue;
}
- rv = p11->f->C_CreateObject(session, tmpl, countof(tmpl), &object);
+ rv = p11->f->C_CreateObject(session, tmpl, count, &object);
if (rv == CKR_OK)
{
- this = create(KEY_RSA, n.len, p11, slot, session, object);
- DBG2(DBG_CFG, "created RSA public key on token '%s':%d ",
- p11->get_name(p11), slot);
- break;
+ this = create(type, keylen, p11, slot, session, object);
+ DBG2(DBG_CFG, "created %N public key on token '%s':%d ",
+ key_type_names, type, p11->get_name(p11), slot);
}
else
{
- DBG1(DBG_CFG, "creating RSA public key on token '%s':%d "
- "failed: %N", p11->get_name(p11), slot, ck_rv_names, rv);
+ DBG1(DBG_CFG, "creating %N public key on token '%s':%d "
+ "failed: %N", key_type_names, type, p11->get_name(p11),
+ slot, ck_rv_names, rv);
p11->f->C_CloseSession(session);
}
+ break;
}
mechs->destroy(mechs);
if (this)
@@ -426,18 +671,71 @@ static private_pkcs11_public_key_t* create_rsa_key(chunk_t n, chunk_t e)
}
/**
+ * Create an RSA key object in a suitable token session
+ */
+static private_pkcs11_public_key_t* create_rsa_key(chunk_t n, chunk_t e,
+ size_t keylen)
+{
+ CK_OBJECT_CLASS class = CKO_PUBLIC_KEY;
+ CK_KEY_TYPE type = CKK_RSA;
+ CK_ATTRIBUTE tmpl[] = {
+ {CKA_CLASS, &class, sizeof(class)},
+ {CKA_KEY_TYPE, &type, sizeof(type)},
+ {CKA_MODULUS, n.ptr, n.len},
+ {CKA_PUBLIC_EXPONENT, e.ptr, e.len},
+ };
+ CK_MECHANISM_TYPE mechs[] = {
+ CKM_RSA_PKCS,
+ CKM_SHA1_RSA_PKCS,
+ CKM_SHA256_RSA_PKCS,
+ CKM_SHA384_RSA_PKCS,
+ CKM_SHA512_RSA_PKCS,
+ CKM_MD5_RSA_PKCS,
+ };
+ return create_key(KEY_RSA, keylen, mechs, countof(mechs), tmpl,
+ countof(tmpl));
+}
+
+/**
+ * Create an ECDSA key object in a suitable token session
+ */
+static private_pkcs11_public_key_t* create_ecdsa_key(chunk_t ecparams,
+ chunk_t ecpoint,
+ size_t keylen)
+{
+ CK_OBJECT_CLASS class = CKO_PUBLIC_KEY;
+ CK_KEY_TYPE type = CKK_ECDSA;
+ CK_ATTRIBUTE tmpl[] = {
+ {CKA_CLASS, &class, sizeof(class)},
+ {CKA_KEY_TYPE, &type, sizeof(type)},
+ {CKA_EC_PARAMS, ecparams.ptr, ecparams.len},
+ {CKA_EC_POINT, ecpoint.ptr, ecpoint.len},
+ };
+ CK_MECHANISM_TYPE mechs[] = {
+ CKM_ECDSA,
+ CKM_ECDSA_SHA1,
+ };
+ return create_key(KEY_ECDSA, keylen, mechs,
+ countof(mechs), tmpl, countof(tmpl));
+}
+
+/**
* See header
*/
pkcs11_public_key_t *pkcs11_public_key_load(key_type_t type, va_list args)
{
private_pkcs11_public_key_t *this;
- chunk_t n, e;
+ chunk_t n, e, blob;
+ size_t keylen = 0;
- n = e = chunk_empty;
+ n = e = blob = chunk_empty;
while (TRUE)
{
switch (va_arg(args, builder_part_t))
{
+ case BUILD_BLOB_ASN1_DER:
+ blob = va_arg(args, chunk_t);
+ continue;
case BUILD_RSA_MODULUS:
n = va_arg(args, chunk_t);
continue;
@@ -457,17 +755,152 @@ pkcs11_public_key_t *pkcs11_public_key_load(key_type_t type, va_list args)
{ /* trim leading zero byte in modulus */
n = chunk_skip(n, 1);
}
- this = find_rsa_key(n, e);
+ keylen = n.len * 8;
+ this = find_rsa_key(n, e, keylen);
if (this)
{
return &this->public;
}
- this = create_rsa_key(n, e);
+ this = create_rsa_key(n, e, keylen);
if (this)
{
return &this->public;
}
}
+ else if (type == KEY_ECDSA && blob.ptr)
+ {
+ chunk_t ecparams, ecpoint;
+ ecparams = ecpoint = chunk_empty;
+ if (parse_ecdsa_public_key(blob, &ecparams, &ecpoint, &keylen))
+ {
+ this = find_ecdsa_key(ecparams, ecpoint, keylen);
+ if (this)
+ {
+ return &this->public;
+ }
+ this = create_ecdsa_key(ecparams, ecpoint, keylen);
+ if (this)
+ {
+ return &this->public;
+ }
+ }
+ }
return NULL;
}
+static private_pkcs11_public_key_t *find_key_by_keyid(pkcs11_library_t *p11,
+ int slot, key_type_t key_type,
+ chunk_t keyid)
+{
+ CK_OBJECT_CLASS class = CKO_PUBLIC_KEY;
+ CK_KEY_TYPE type;
+ CK_ATTRIBUTE tmpl[] = {
+ {CKA_CLASS, &class, sizeof(class)},
+ {CKA_ID, keyid.ptr, keyid.len},
+ {CKA_KEY_TYPE, &type, sizeof(type)},
+ };
+ CK_OBJECT_HANDLE object;
+ CK_ATTRIBUTE attr[] = {
+ {CKA_KEY_TYPE, &type, sizeof(type)},
+ };
+ CK_SESSION_HANDLE session;
+ CK_RV rv;
+ enumerator_t *enumerator;
+ int count = countof(tmpl);
+ bool found = FALSE;
+ size_t keylen;
+
+ switch (key_type)
+ {
+ case KEY_RSA:
+ type = CKK_RSA;
+ break;
+ case KEY_ECDSA:
+ type = CKK_ECDSA;
+ break;
+ default:
+ /* don't specify key type on KEY_ANY */
+ count--;
+ break;
+ }
+
+ rv = p11->f->C_OpenSession(slot, CKF_SERIAL_SESSION, NULL, NULL, &session);
+ if (rv != CKR_OK)
+ {
+ DBG1(DBG_CFG, "opening public key session on '%s':%d failed: %N",
+ p11->get_name(p11), slot, ck_rv_names, rv);
+ return NULL;
+ }
+
+ enumerator = p11->create_object_enumerator(p11, session, tmpl, count, attr,
+ countof(attr));
+ if (enumerator->enumerate(enumerator, &object))
+ {
+ switch (type)
+ {
+ case CKK_ECDSA:
+ {
+ chunk_t ecparams;
+ if (p11->get_ck_attribute(p11, session, object, CKA_EC_PARAMS,
+ &ecparams) &&
+ keylen_from_ecparams(ecparams, &keylen))
+ {
+ chunk_free(&ecparams);
+ key_type = KEY_ECDSA;
+ found = TRUE;
+ }
+ break;
+ }
+ case CKK_RSA:
+ {
+ chunk_t n;
+ if (p11->get_ck_attribute(p11, session, object, CKA_MODULUS,
+ &n) && n.len > 0)
+ {
+ keylen = n.len * 8;
+ chunk_free(&n);
+ key_type = KEY_RSA;
+ found = TRUE;
+ }
+ break;
+ }
+ default:
+ DBG1(DBG_CFG, "PKCS#11 key type %d not supported", type);
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ if (found)
+ {
+ return create(key_type, keylen, p11, slot, session, object);
+ }
+ p11->f->C_CloseSession(session);
+ return NULL;
+}
+
+/**
+ * Find a public key on the given token with a specific keyid.
+ *
+ * Used by pkcs11_private_key_t.
+ *
+ * TODO: if no public key is found, we should perhaps search for a certificate
+ * with the given keyid and extract the key from there
+ *
+ * @param p11 PKCS#11 module
+ * @param slot slot id
+ * @param type type of the key
+ * @param keyid key id
+ */
+pkcs11_public_key_t *pkcs11_public_key_connect(pkcs11_library_t *p11,
+ int slot, key_type_t type, chunk_t keyid)
+{
+ private_pkcs11_public_key_t *this;
+
+ this = find_key_by_keyid(p11, slot, type, keyid);
+ if (!this)
+ {
+ return NULL;
+ }
+ return &this->public;
+}
diff --git a/src/libstrongswan/plugins/pkcs11/pkcs11_public_key.h b/src/libstrongswan/plugins/pkcs11/pkcs11_public_key.h
index 4fd94620e..b3ea725a2 100644
--- a/src/libstrongswan/plugins/pkcs11/pkcs11_public_key.h
+++ b/src/libstrongswan/plugins/pkcs11/pkcs11_public_key.h
@@ -42,7 +42,7 @@ struct pkcs11_public_key_t {
*
* @param type type of the key
* @param args builder_part_t argument list
- * @return loaded key, NULL on failure
+ * @return loaded key, NULL on failure
*/
pkcs11_public_key_t *pkcs11_public_key_load(key_type_t type, va_list args);
diff --git a/src/libstrongswan/plugins/pkcs11/pkcs11_rng.c b/src/libstrongswan/plugins/pkcs11/pkcs11_rng.c
new file mode 100644
index 000000000..45cf0b7c2
--- /dev/null
+++ b/src/libstrongswan/plugins/pkcs11/pkcs11_rng.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2011 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "pkcs11_rng.h"
+
+#include <debug.h>
+
+#include "pkcs11_manager.h"
+
+typedef struct private_pkcs11_rng_t private_pkcs11_rng_t;
+
+/**
+ * Private data of an pkcs11_rng_t object.
+ */
+struct private_pkcs11_rng_t {
+
+ /**
+ * Public interface.
+ */
+ pkcs11_rng_t public;
+
+ /**
+ * PKCS#11 library
+ */
+ pkcs11_library_t *lib;
+
+ /**
+ * Mechanism for this rng
+ */
+ CK_SESSION_HANDLE session;
+
+};
+
+METHOD(rng_t, get_bytes, void,
+ private_pkcs11_rng_t *this, size_t bytes, u_int8_t *buffer)
+{
+ CK_RV rv;
+ rv = this->lib->f->C_GenerateRandom(this->session, buffer, bytes);
+ if (rv != CKR_OK)
+ {
+ DBG1(DBG_CFG, "C_GenerateRandom() failed: %N", ck_rv_names, rv);
+ abort();
+ }
+}
+
+METHOD(rng_t, allocate_bytes, void,
+ private_pkcs11_rng_t *this, size_t bytes, chunk_t *chunk)
+{
+ *chunk = chunk_alloc(bytes);
+ get_bytes(this, chunk->len, chunk->ptr);
+}
+
+METHOD(rng_t, destroy, void,
+ private_pkcs11_rng_t *this)
+{
+ this->lib->f->C_CloseSession(this->session);
+ free(this);
+}
+
+/**
+ * Find a token with its own RNG
+ */
+static pkcs11_library_t *find_token(CK_SESSION_HANDLE *session)
+{
+ enumerator_t *tokens;
+ pkcs11_manager_t *manager;
+ pkcs11_library_t *current, *found = NULL;
+ CK_SLOT_ID slot;
+
+ manager = lib->get(lib, "pkcs11-manager");
+ if (!manager)
+ {
+ return NULL;
+ }
+ tokens = manager->create_token_enumerator(manager);
+ while (tokens->enumerate(tokens, &current, &slot))
+ {
+ CK_TOKEN_INFO info;
+ CK_RV rv;
+ rv = current->f->C_GetTokenInfo(slot, &info);
+ if (rv != CKR_OK)
+ {
+ continue;
+ }
+ if (info.flags & CKF_RNG)
+ {
+ if (current->f->C_OpenSession(slot, CKF_SERIAL_SESSION,
+ NULL, NULL, session) == CKR_OK)
+ {
+ found = current;
+ break;
+ }
+ }
+ }
+ tokens->destroy(tokens);
+ return found;
+}
+
+/*
+ * Described in header.
+ */
+pkcs11_rng_t *pkcs11_rng_create(rng_quality_t quality)
+{
+ private_pkcs11_rng_t *this;
+
+ INIT(this,
+ .public = {
+ .rng = {
+ .get_bytes = _get_bytes,
+ .allocate_bytes = _allocate_bytes,
+ .destroy = _destroy,
+ },
+ },
+ );
+
+ this->lib = find_token(&this->session);
+ if (!this->lib)
+ {
+ free(this);
+ return NULL;
+ }
+
+ return &this->public;
+}
+
diff --git a/src/libstrongswan/plugins/pkcs11/pkcs11_rng.h b/src/libstrongswan/plugins/pkcs11/pkcs11_rng.h
new file mode 100644
index 000000000..998631f7e
--- /dev/null
+++ b/src/libstrongswan/plugins/pkcs11/pkcs11_rng.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2011 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup pkcs11_rng pkcs11_rng
+ * @{ @ingroup pkcs11
+ */
+
+#ifndef PKCS11_RNG_H_
+#define PKCS11_RNG_H_
+
+typedef struct pkcs11_rng_t pkcs11_rng_t;
+
+#include <library.h>
+
+/**
+ * rng_t implementation via PKCS#11
+ */
+struct pkcs11_rng_t {
+
+ /**
+ * Implements rng_t.
+ */
+ rng_t rng;
+};
+
+/**
+ * Creates a pkcs11_rng_t instance.
+ *
+ * @param quality required quality of randomness
+ * @return created pkcs11_rng_t
+ */
+pkcs11_rng_t *pkcs11_rng_create(rng_quality_t quality);
+
+#endif /** PKCS11_RNG_H_ @} */