Implemented PKCS#11 RSA public key for keys found on a token
authorMartin Willi <martin@revosec.ch>
Fri, 6 Aug 2010 15:02:41 +0000 (17:02 +0200)
committerMartin Willi <martin@revosec.ch>
Fri, 6 Aug 2010 15:02:41 +0000 (17:02 +0200)
src/libstrongswan/plugins/pkcs11/Makefile.am
src/libstrongswan/plugins/pkcs11/pkcs11_plugin.c
src/libstrongswan/plugins/pkcs11/pkcs11_public_key.c [new file with mode: 0644]
src/libstrongswan/plugins/pkcs11/pkcs11_public_key.h [new file with mode: 0644]

index 8da6a25..7d395e0 100644 (file)
@@ -15,6 +15,7 @@ libstrongswan_pkcs11_la_SOURCES = \
        pkcs11_library.h pkcs11_library.c \
        pkcs11_creds.h pkcs11_creds.c \
        pkcs11_private_key.h pkcs11_private_key.c \
+       pkcs11_public_key.h pkcs11_public_key.c \
        pkcs11_hasher.h pkcs11_hasher.c \
        pkcs11_manager.h pkcs11_manager.c
 
index 40970b3..5e527f4 100644 (file)
@@ -23,6 +23,7 @@
 #include "pkcs11_manager.h"
 #include "pkcs11_creds.h"
 #include "pkcs11_private_key.h"
+#include "pkcs11_public_key.h"
 #include "pkcs11_hasher.h"
 
 typedef struct private_pkcs11_plugin_t private_pkcs11_plugin_t;
@@ -157,6 +158,8 @@ plugin_t *pkcs11_plugin_create()
 
        lib->creds->add_builder(lib->creds, CRED_PRIVATE_KEY, KEY_ANY,
                                                        (builder_function_t)pkcs11_private_key_connect);
+       lib->creds->add_builder(lib->creds, CRED_PUBLIC_KEY, KEY_RSA,
+                                                       (builder_function_t)pkcs11_public_key_load);
 
        enumerator = this->manager->create_token_enumerator(this->manager);
        while (enumerator->enumerate(enumerator, &p11, &slot))
diff --git a/src/libstrongswan/plugins/pkcs11/pkcs11_public_key.c b/src/libstrongswan/plugins/pkcs11/pkcs11_public_key.c
new file mode 100644 (file)
index 0000000..d98f922
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ *
+ * 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_public_key.h"
+
+#include "pkcs11.h"
+#include "pkcs11_private_key.h"
+#include "pkcs11_manager.h"
+
+#include <debug.h>
+#include <threading/mutex.h>
+
+typedef struct private_pkcs11_public_key_t private_pkcs11_public_key_t;
+
+/**
+ * Private data of an pkcs11_public_key_t object.
+ */
+struct private_pkcs11_public_key_t {
+
+       /**
+        * Public pkcs11_public_key_t interface.
+        */
+       pkcs11_public_key_t public;
+
+       /**
+        * Type of the key
+        */
+       key_type_t type;
+
+       /**
+        * Key size in bytes
+        */
+       size_t k;
+
+       /**
+        * PKCS#11 library this key uses
+        */
+       pkcs11_library_t *lib;
+
+       /**
+        * Slot the token is in
+        */
+       CK_SLOT_ID slot;
+
+       /**
+        * Session we use
+        */
+       CK_SESSION_HANDLE session;
+
+       /**
+        * Object handle to the key
+        */
+       CK_OBJECT_HANDLE object;
+
+       /**
+        * Mutex to lock session
+        */
+       mutex_t *mutex;
+
+       /**
+        * References to this key
+        */
+       refcount_t ref;
+};
+
+METHOD(public_key_t, get_type, key_type_t,
+       private_pkcs11_public_key_t *this)
+{
+       return this->type;
+}
+
+METHOD(public_key_t, verify, bool,
+       private_pkcs11_public_key_t *this, signature_scheme_t scheme,
+       chunk_t data, chunk_t sig)
+{
+       CK_MECHANISM_PTR mechanism;
+       CK_RV rv;
+
+       mechanism = pkcs11_scheme_to_mechanism(scheme);
+       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_VerifyInit(this->session, mechanism, this->object);
+       if (rv != CKR_OK)
+       {
+               this->mutex->unlock(this->mutex);
+               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 (rv != CKR_OK)
+       {
+               DBG1(DBG_LIB, "C_Verify() failed: %N", ck_rv_names, rv);
+               return FALSE;
+       }
+       return TRUE;
+}
+
+METHOD(public_key_t, encrypt, bool,
+       private_pkcs11_public_key_t *this, chunk_t plain, chunk_t *crypto)
+{
+       return FALSE;
+}
+
+METHOD(public_key_t, get_keysize, size_t,
+       private_pkcs11_public_key_t *this)
+{
+       return this->k;
+}
+
+/**
+ * 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;
+       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)
+       {
+               n = chunk_create(attr[0].pValue, attr[0].ulValueLen);
+               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);
+       return success;
+}
+
+METHOD(public_key_t, get_encoding, bool,
+       private_pkcs11_public_key_t *this, cred_encoding_type_t type,
+       chunk_t *encoding)
+{
+       switch (this->type)
+       {
+               case KEY_RSA:
+                       return encode_rsa(this, type, NULL, encoding);
+               default:
+                       return FALSE;
+       }
+}
+
+METHOD(public_key_t, get_fingerprint, bool,
+       private_pkcs11_public_key_t *this, cred_encoding_type_t type, chunk_t *fp)
+{
+       if (lib->encoding->get_cache(lib->encoding, type, this, fp))
+       {
+               return TRUE;
+       }
+       switch (this->type)
+       {
+               case KEY_RSA:
+                       return encode_rsa(this, type, this, fp);
+               default:
+                       return FALSE;
+       }
+}
+
+METHOD(public_key_t, get_ref, public_key_t*,
+       private_pkcs11_public_key_t *this)
+{
+       ref_get(&this->ref);
+       return &this->public.key;
+}
+
+METHOD(public_key_t, destroy, void,
+       private_pkcs11_public_key_t *this)
+{
+       if (ref_put(&this->ref))
+       {
+               lib->encoding->clear_cache(lib->encoding, this);
+               this->lib->f->C_CloseSession(this->session);
+               this->mutex->destroy(this->mutex);
+               free(this);
+       }
+}
+
+/**
+ * Create an empty PKCS#11 public key
+ */
+static private_pkcs11_public_key_t *create(key_type_t type, size_t k,
+                                                       pkcs11_library_t *p11, CK_SLOT_ID slot,
+                                                       CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object)
+{
+       private_pkcs11_public_key_t *this;
+
+       INIT(this,
+               .public = {
+                       .key = {
+                               .get_type = _get_type,
+                               .verify = _verify,
+                               .encrypt = _encrypt,
+                               .equals = public_key_equals,
+                               .get_keysize = _get_keysize,
+                               .get_fingerprint = _get_fingerprint,
+                               .has_fingerprint = public_key_has_fingerprint,
+                               .get_encoding = _get_encoding,
+                               .get_ref = _get_ref,
+                               .destroy = _destroy,
+                       },
+               },
+               .type = type,
+               .k = k,
+               .lib = p11,
+               .slot = slot,
+               .session = session,
+               .object = object,
+               .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+       );
+
+       return this;
+}
+
+/**
+ * Find a key object, including PKCS11 library and slot
+ */
+static private_pkcs11_public_key_t* find_rsa_key(chunk_t n, chunk_t e)
+{
+       private_pkcs11_public_key_t *this = NULL;
+       pkcs11_manager_t *manager;
+       enumerator_t *enumerator, *keys;
+       pkcs11_library_t *p11;
+       CK_SLOT_ID slot;
+
+       manager = pkcs11_manager_get();
+       if (!manager)
+       {
+               return NULL;
+       }
+
+       if (n.len && n.ptr[0] == 0)
+       {       /* trim leading zero byte in modulus */
+               n = chunk_skip(n, 1);
+       }
+
+       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;
+
+               rv = p11->f->C_OpenSession(slot, CKF_SERIAL_SESSION, NULL, NULL,
+                                                                  &session);
+               if (rv != CKR_OK)
+               {
+                       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);
+               if (keys->enumerate(keys, &object))
+               {
+                       this = create(KEY_RSA, n.len, p11, slot, session, object);
+                       keys->destroy(keys);
+                       break;
+               }
+               keys->destroy(keys);
+               p11->f->C_CloseSession(session);
+       }
+       enumerator->destroy(enumerator);
+       return this;
+}
+
+/**
+ * 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;
+
+       n = e = chunk_empty;
+       while (TRUE)
+       {
+               switch (va_arg(args, builder_part_t))
+               {
+                       case BUILD_RSA_MODULUS:
+                               n = va_arg(args, chunk_t);
+                               continue;
+                       case BUILD_RSA_PUB_EXP:
+                               e = va_arg(args, chunk_t);
+                               continue;
+                       case BUILD_END:
+                               break;
+                       default:
+                               return NULL;
+               }
+               break;
+       }
+       if (type == KEY_RSA && e.ptr && n.ptr)
+       {
+               this = find_rsa_key(n, e);
+               if (this)
+               {
+                       return &this->public;
+               }
+       }
+       return NULL;
+}
+
diff --git a/src/libstrongswan/plugins/pkcs11/pkcs11_public_key.h b/src/libstrongswan/plugins/pkcs11/pkcs11_public_key.h
new file mode 100644 (file)
index 0000000..4fd9462
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ *
+ * 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_public_key pkcs11_public_key
+ * @{ @ingroup pkcs11
+ */
+
+#ifndef PKCS11_PUBLIC_KEY_H_
+#define PKCS11_PUBLIC_KEY_H_
+
+typedef struct pkcs11_public_key_t pkcs11_public_key_t;
+
+#include <credentials/builder.h>
+#include <credentials/keys/private_key.h>
+
+/**
+ * PKCS#11 based public key implementation.
+ */
+struct pkcs11_public_key_t {
+
+       /**
+        * Implements public_key_t.
+        */
+       public_key_t key;
+};
+
+/**
+ * Create a public key in a PKCS#11 session.
+ *
+ * @param type         type of the key
+ * @param args         builder_part_t argument list
+ * @return                     loaded key, NULL on failure
+ */
+pkcs11_public_key_t *pkcs11_public_key_load(key_type_t type, va_list args);
+
+#endif /** PKCS11_PUBLIC_KEY_H_ @}*/