diff options
author | Yves-Alexis Perez <corsac@corsac.net> | 2012-06-28 21:16:07 +0200 |
---|---|---|
committer | Yves-Alexis Perez <corsac@corsac.net> | 2012-06-28 21:16:07 +0200 |
commit | b34738ed08c2227300d554b139e2495ca5da97d6 (patch) | |
tree | 62f33b52820f2e49f0e53c0f8c636312037c8054 /src/libstrongswan/plugins/pkcs11 | |
parent | 0a9d51a49042a68daa15b0c74a2b7f152f52606b (diff) | |
download | vyos-strongswan-b34738ed08c2227300d554b139e2495ca5da97d6.tar.gz vyos-strongswan-b34738ed08c2227300d554b139e2495ca5da97d6.zip |
Imported Upstream version 4.6.4
Diffstat (limited to 'src/libstrongswan/plugins/pkcs11')
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, ¤t, &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, ¤t, &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_ @} */ |