pkcs11: Added support for DH.
authorTobias Brunner <tobias@strongswan.org>
Tue, 25 Oct 2011 08:29:07 +0000 (10:29 +0200)
committerTobias Brunner <tobias@strongswan.org>
Mon, 31 Oct 2011 17:45:36 +0000 (18:45 +0100)
src/libstrongswan/plugins/pkcs11/Makefile.am
src/libstrongswan/plugins/pkcs11/pkcs11_dh.c [new file with mode: 0644]
src/libstrongswan/plugins/pkcs11/pkcs11_dh.h [new file with mode: 0644]
src/libstrongswan/plugins/pkcs11/pkcs11_plugin.c

index fbe6a70..d032b87 100644 (file)
@@ -17,6 +17,7 @@ libstrongswan_pkcs11_la_SOURCES = \
        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/pkcs11_dh.c b/src/libstrongswan/plugins/pkcs11/pkcs11_dh.c
new file mode 100644 (file)
index 0000000..e6ca3eb
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * 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 "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;
+
+};
+
+/**
+ * Retrieve a CKA_VALUE from a CK_OBJECT_HANDLE, memory gets allocated
+ */
+static bool get_cka_value(private_pkcs11_dh_t *this, CK_OBJECT_HANDLE obj,
+                                                 chunk_t *value)
+{
+       CK_ATTRIBUTE attr = { CKA_VALUE, NULL, 0 };
+       CK_RV rv;
+       rv = this->lib->f->C_GetAttributeValue(this->session, obj, &attr, 1);
+       if (rv != CKR_OK)
+       {
+               DBG1(DBG_CFG, "C_GetAttributeValue(NULL) error: %N", ck_rv_names, rv);
+               return FALSE;
+       }
+       *value = chunk_alloc(attr.ulValueLen);
+       attr.pValue = value->ptr;
+       rv = this->lib->f->C_GetAttributeValue(this->session, obj, &attr, 1);
+       if (rv != CKR_OK)
+       {
+               DBG1(DBG_CFG, "C_GetAttributeValue() error: %N", ck_rv_names, rv);
+               chunk_free(value);
+               return FALSE;
+       }
+       return TRUE;
+}
+
+METHOD(diffie_hellman_t, set_other_public_value, void,
+       private_pkcs11_dh_t *this, chunk_t value)
+{
+       CK_ATTRIBUTE attr[] = {
+       };
+       CK_MECHANISM mech = {
+               CKM_DH_PKCS_DERIVE,
+               value.ptr,
+               value.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 (!get_cka_value(this, secret, &this->secret))
+       {
+               return;
+       }
+}
+
+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 DH key pair
+ */
+static bool generate_key_pair(private_pkcs11_dh_t *this, size_t exp_len,
+                                                         chunk_t g, chunk_t p)
+{
+       CK_ULONG bits = exp_len * 8;
+       CK_ATTRIBUTE pub_attr[] = {
+               { CKA_PRIME, p.ptr, p.len },
+               { CKA_BASE, g.ptr, g.len },
+       };
+       CK_ATTRIBUTE pri_attr[] = {
+               { CKA_VALUE_BITS, &bits, sizeof(bits) },
+       };
+       CK_MECHANISM mech = {
+               CKM_DH_PKCS_KEY_PAIR_GEN,
+               NULL,
+               0,
+       };
+       CK_OBJECT_HANDLE pub_key;
+       CK_RV rv;
+
+       rv = this->lib->f->C_GenerateKeyPair(this->session, &mech, pub_attr,
+                                                       countof(pub_attr), pri_attr, countof(pri_attr),
+                                                       &pub_key, &this->pri_key);
+       if (rv != CKR_OK)
+       {
+               DBG1(DBG_CFG, "C_GenerateKeyPair() error: %N", ck_rv_names, rv);
+               return FALSE;
+       }
+
+       if (!get_cka_value(this, pub_key, &this->pub_key))
+       {
+               return FALSE;
+       }
+       return TRUE;
+}
+
+/**
+ * Find a token we can use for DH algorithm
+ */
+static pkcs11_library_t *find_token(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 CKM_DH_PKCS_DERIVE is supported too */
+                       if (type == CKM_DH_PKCS_KEY_PAIR_GEN)
+                       {
+                               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
+ */
+pkcs11_dh_t *create_generic(diffie_hellman_group_t group, size_t exp_len,
+                                                       chunk_t g, chunk_t p)
+{
+       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,
+       );
+
+       this->lib = find_token(&this->session);
+       if (!this->lib)
+       {
+               free(this);
+               return NULL;
+       }
+
+       if (!generate_key_pair(this, exp_len, g, p))
+       {
+               free(this);
+               return NULL;
+       }
+       return &this->public;
+}
+
+
+/*
+ * Described in header.
+ */
+pkcs11_dh_t *pkcs11_dh_create(diffie_hellman_group_t group)
+{
+
+       diffie_hellman_params_t *params;
+
+       params = diffie_hellman_get_params(group);
+       if (!params)
+       {
+               return NULL;
+       }
+       return create_generic(group, params->exp_len,
+                                                 params->generator, params->prime);
+}
+
+/*
+ * Described in header.
+ */
+pkcs11_dh_t *pkcs11_dh_create_custom(diffie_hellman_group_t group,
+                                                                        chunk_t g, chunk_t p)
+{
+       if (group == MODP_CUSTOM)
+       {
+               return create_generic(group, p.len, g, p);
+       }
+       return NULL;
+}
diff --git a/src/libstrongswan/plugins/pkcs11/pkcs11_dh.h b/src/libstrongswan/plugins/pkcs11/pkcs11_dh.h
new file mode 100644 (file)
index 0000000..9926534
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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
+ * @return                             pkcs11_dh_t object, NULL if not supported
+ */
+pkcs11_dh_t *pkcs11_dh_create(diffie_hellman_group_t group);
+
+/**
+ * Creates a new pkcs11_dh_t object for MODP_CUSTOM.
+ *
+ * @param group                        MODP_CUSTOM
+ * @param g                            generator
+ * @param p                            prime
+ * @return                             pkcs11_dh_t object, NULL if not supported
+ */
+pkcs11_dh_t *pkcs11_dh_create_custom(diffie_hellman_group_t group,
+                                                                        chunk_t g, chunk_t p);
+
+#endif /** PKCS11_DH_H_ @}*/
+
index 824da9d..73fe78b 100644 (file)
@@ -29,6 +29,7 @@
 #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;
 
@@ -126,6 +127,8 @@ METHOD(plugin_t, destroy, void,
        lib->crypto->remove_hasher(lib->crypto,
                                                        (hasher_constructor_t)pkcs11_hasher_create);
        lib->crypto->remove_rng(lib->crypto, (rng_constructor_t)pkcs11_rng_create);
+       lib->crypto->remove_dh(lib->crypto, (dh_constructor_t)pkcs11_dh_create_custom);
+       lib->crypto->remove_dh(lib->crypto, (dh_constructor_t)pkcs11_dh_create);
        this->creds->destroy(this->creds);
        lib->set(lib, "pkcs11-manager", NULL);
        this->manager->destroy(this->manager);
@@ -192,6 +195,23 @@ plugin_t *pkcs11_plugin_create()
        lib->creds->add_builder(lib->creds, CRED_PUBLIC_KEY, KEY_RSA, TRUE,
                                                        (builder_function_t)pkcs11_public_key_load);
 
+       lib->crypto->add_dh(lib->crypto, MODP_3072_BIT, get_name(this),
+                                       (dh_constructor_t)pkcs11_dh_create);
+       lib->crypto->add_dh(lib->crypto, MODP_4096_BIT, get_name(this),
+                                       (dh_constructor_t)pkcs11_dh_create);
+       lib->crypto->add_dh(lib->crypto, MODP_6144_BIT, get_name(this),
+                                       (dh_constructor_t)pkcs11_dh_create);
+       lib->crypto->add_dh(lib->crypto, MODP_8192_BIT, get_name(this),
+                                       (dh_constructor_t)pkcs11_dh_create);
+       lib->crypto->add_dh(lib->crypto, MODP_1024_BIT, get_name(this),
+                                       (dh_constructor_t)pkcs11_dh_create);
+       lib->crypto->add_dh(lib->crypto, MODP_1024_160, get_name(this),
+                                       (dh_constructor_t)pkcs11_dh_create);
+       lib->crypto->add_dh(lib->crypto, MODP_768_BIT, get_name(this),
+                                       (dh_constructor_t)pkcs11_dh_create);
+       lib->crypto->add_dh(lib->crypto, MODP_CUSTOM, get_name(this),
+                                       (dh_constructor_t)pkcs11_dh_create_custom);
+
        enumerator = this->manager->create_token_enumerator(this->manager);
        while (enumerator->enumerate(enumerator, &p11, &slot))
        {