]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Add p11-kit-1 support
authorAki Tuomi <cmouse@desteem.org>
Mon, 24 Feb 2014 10:33:02 +0000 (12:33 +0200)
committerAki Tuomi <cmouse@cmouse.fi>
Fri, 9 May 2014 19:45:34 +0000 (22:45 +0300)
Skeleton for PKCS#11 signers

Add exceptions for key format

Rudimentary version, still needs cleanup

Some small tweaks for pkcs11

Remove dnssecinfra.cc from zone2*

Use pkcs11 when such file is detected

Code cleanup

Added pkcs11signers to test suite builds

Added few comments

Support key creation

Add p11-kit-1 support

Skeleton for PKCS#11 signers

Add exceptions for key format

Rudimentary version, still needs cleanup

Some small tweaks for pkcs11

Code cleanup

Added few comments

Support key creation

Avoid base64 for PKCS11 attributes

hsm assign and hsm create-key methods

configure.ac
m4/pdns_enable_p11kit.m4 [new file with mode: 0644]
modules/remotebackend/Makefile.am
pdns/Makefile.am
pdns/dnssecinfra.cc
pdns/dnssecinfra.hh
pdns/pdnssec.cc
pdns/pkcs11signers.cc [new file with mode: 0644]
pdns/pkcs11signers.hh [new file with mode: 0644]

index 3433fd9c13a2687215510414ad9d2655df34d669..819cf63a8f7e557393c82eff583cb1a2c07fee6f 100644 (file)
@@ -158,6 +158,7 @@ AC_SUBST(DYNLINKFLAGS)
 PDNS_ENABLE_VERBOSE_LOGGING
 PDNS_WITH_SYSTEM_POLARSSL
 PDNS_ENABLE_BOTAN
+PDNS_ENABLE_PKCS11
 PDNS_WITH_CRYPTOPP
 PDNS_ENABLE_REMOTEBACKEND_HTTP
 PDNS_ENABLE_REMOTEBACKEND_ZEROMQ
diff --git a/m4/pdns_enable_p11kit.m4 b/m4/pdns_enable_p11kit.m4
new file mode 100644 (file)
index 0000000..cce64eb
--- /dev/null
@@ -0,0 +1,18 @@
+AC_DEFUN([PDNS_ENABLE_PKCS11],[
+  AC_MSG_CHECKING([whether to enable PKCS11 support])
+  AC_ARG_ENABLE([experimental-pkcs11],
+    [AS_HELP_STRING([--enable-experimental-pkcs11],[enable experimental PKCS11 support @<:@default=no@:>@])],
+    [enable_pkcs11=yes],
+    [enable_pkcs11=no]
+  )
+  AC_MSG_RESULT([$enable_pkcs11])
+  AM_CONDITIONAL([PKCS11], [test "x$enable_pkcs11" = "xyes"])
+
+  AS_IF([test "x$enable_pkcs11" = "xyes"], [
+    PKG_CHECK_MODULES([P11KIT1], [p11-kit-1],
+      [AC_DEFINE([HAVE_P11KIT1],[1],[Define to 1 if you have p11-kit-1])],
+      [AC_MSG_ERROR([Could not find p11-kit-1])]
+    )]
+  )
+
+])
index 82920da2a6127d7312695b53fbd70447258d7b95..f6b74cc466116d8b5d48251269dc527e63cade9d 100644 (file)
@@ -35,8 +35,13 @@ libtestremotebackend_la_SOURCES=../../pdns/dnsbackend.hh ../../pdns/dnsbackend.c
         ../../pdns/dns.hh ../../pdns/dns.cc ../../pdns/json.hh ../../pdns/json.cc \
         remotebackend.hh remotebackend.cc unixconnector.cc httpconnector.cc pipeconnector.cc zmqconnector.cc
 
-libtestremotebackend_la_CFLAGS=$(BOOST_CPPFLAGS) @THREADFLAGS@ $(POLARSSL_CFLAGS) $(LIBCURL_CFLAGS) $(LIBZMQ_CFLAGS) -g -O0 -I../../pdns
-libtestremotebackend_la_CXXFLAGS=$(BOOST_CPPFLAGS) @THREADFLAGS@ $(POLARSSL_CFLAGS) $(LIBCURL_CFLAGS) $(LIBZMQ_CFLAGS) -g -O0 -I../../pdns
+if PKCS11
+libtestremotebackend_la_SOURCES += ../../pdns/pkcs11signers.hh ../../pdns/pkcs11signers.cc
+libtestremotebackend_la_LIBADD=$(P11KIT1_LIBS)
+endif
+
+libtestremotebackend_la_CFLAGS=$(BOOST_CPPFLAGS) @THREADFLAGS@ $(POLARSSL_CFLAGS) $(LIBCURL_CFLAGS) $(LIBZMQ_CFLAGS) $(P11KIT1_CFLAGS) -g -O0 -I../../pdns
+libtestremotebackend_la_CXXFLAGS=$(BOOST_CPPFLAGS) @THREADFLAGS@ $(POLARSSL_CFLAGS) $(LIBCURL_CFLAGS) $(LIBZMQ_CFLAGS) $(P11KIT1_CFLAGS) -g -O0 -I../../pdns
 
 test_remotebackend_pipe_SOURCES=test-remotebackend.cc test-remotebackend-pipe.cc test-remotebackend-keys.hh 
 test_remotebackend_unix_SOURCES=test-remotebackend.cc test-remotebackend-unix.cc test-remotebackend-keys.hh
index 0bf2fd040c1792430ce089fd857032b1d7cd1c92..9c5ac51f4870e0791231afe1bdfbebc6128932b2 100644 (file)
@@ -1,4 +1,3 @@
-
 AM_CXXFLAGS=-DSYSCONFDIR=\"@sysconfdir@\" -DLIBDIR=\"@libdir@\" -DLOCALSTATEDIR=\"@socketdir@\" @THREADFLAGS@ $(LUA_CFLAGS) $(SQLITE3_CFLAGS) $(POLARSSL_CFLAGS) -Iext/rapidjson/include -Iext/yahttp
 
 YAHTTP_LIBS = -Lext/yahttp/yahttp -lyahttp
@@ -15,6 +14,10 @@ if BOTAN18
 AM_CPPFLAGS += $(BOTAN18_CFLAGS)
 endif
 
+if PKCS11
+AM_CPPFLAGS += $(P11KIT1_CFLAGS)
+endif
+
 EXTRA_DIST = dnslabeltext.rl dnslabeltext.cc mtasker.cc inflighter.cc docs/pdns_control.8  \
        docs/pdns_server.8 docs/zone2sql.8 docs/zone2ldap.8 docs/pdnssec.8 \
        docs/dnsreplay.8 docs/dnsscope.8 docs/dnswasher.8 docs/pdnssec.8 docs/zone2ldap.8 \
@@ -92,6 +95,11 @@ if ORACLE
 pdns_server_LDADD += $(ORACLE_LIBS)
 endif
 
+if PKCS11
+pdns_server_SOURCES += pkcs11signers.cc pkcs11signers.hh
+pdns_server_LDADD += $(P11KIT1_LIBS)
+endif
+
 pdnssec_SOURCES=pdnssec.cc dbdnsseckeeper.cc sstuff.hh dnsparser.cc dnsparser.hh dnsrecords.cc dnswriter.cc dnswriter.hh \
         misc.cc misc.hh rcpgenerator.cc rcpgenerator.hh base64.cc base64.hh unix_utility.cc \
        logger.cc statbag.cc qtype.cc sillyrecords.cc nsecrecords.cc dnssecinfra.cc dnssecinfra.hh \
@@ -105,14 +113,14 @@ pdnssec_SOURCES=pdnssec.cc dbdnsseckeeper.cc sstuff.hh dnsparser.cc dnsparser.hh
        serialtweaker.cc randomhelper.cc
 
 pdnssec_LDFLAGS=@moduleobjects@ @modulelibs@ @DYNLINKFLAGS@ @LIBDL@ @THREADFLAGS@ $(BOOST_PROGRAM_OPTIONS_LDFLAGS) $(BOOST_SERIALIZATION_LDFLAGS) 
-pdnssec_LDADD= $(POLARSSL_LIBS) $(BOOST_PROGRAM_OPTIONS_LIBS) $(BOOST_SERIALIZATION_LIBS) $(SQLITE3_LIBS)
+pdnssec_LDADD= $(POLARSSL_LIBS) $(BOOST_PROGRAM_OPTIONS_LIBS) $(BOOST_SERIALIZATION_LIBS) $(SQLITE3_LIBS) 
 
 zone2sql_SOURCES=bindparser.yy bindlexer.l bind-dnssec.schema.sqlite3.sql.h \
        arguments.cc logger.cc zone2sql.cc statbag.cc misc.cc \
        unix_utility.cc qtype.cc dns.cc \
        zoneparser-tng.cc dnsrecords.cc sillyrecords.cc \
        dnswriter.cc dnslabeltext.cc rcpgenerator.cc dnsparser.cc base64.cc \
-       nsecrecords.cc dnssecinfra.cc base32.cc bindparserclasses.hh \
+       nsecrecords.cc base32.cc bindparserclasses.hh \
        dns_random.cc json.cc json.hh
 
 zone2sql_LDFLAGS=@THREADFLAGS@
@@ -122,7 +130,7 @@ zone2json_SOURCES=bindparser.yy bindlexer.l bind-dnssec.schema.sqlite3.sql.h \
        arguments.cc logger.cc zone2json.cc statbag.cc misc.cc \
        unix_utility.cc qtype.cc zoneparser-tng.cc dnsrecords.cc \
        dnswriter.cc dnslabeltext.cc rcpgenerator.cc dnsparser.cc base64.cc sillyrecords.cc \
-       nsecrecords.cc dnssecinfra.cc base32.cc bindparserclasses.hh
+       nsecrecords.cc base32.cc bindparserclasses.hh
 
 zone2json_LDFLAGS=@THREADFLAGS@
 zone2json_LDADD= $(POLARSSL_LIBS)
@@ -131,7 +139,7 @@ zone2ldap_SOURCES=bindparser.yy bindlexer.l bind-dnssec.schema.sqlite3.sql.h \
        arguments.cc logger.cc zone2ldap.cc statbag.cc misc.cc \
        unix_utility.cc qtype.cc zoneparser-tng.cc dnsrecords.cc \
        dnswriter.cc dnslabeltext.cc rcpgenerator.cc dnsparser.cc base64.cc sillyrecords.cc \
-       nsecrecords.cc dnssecinfra.cc base32.cc bindparserclasses.hh \
+       nsecrecords.cc base32.cc bindparserclasses.hh \
        dns_random.cc
 
 zone2ldap_LDFLAGS=@THREADFLAGS@
@@ -160,6 +168,11 @@ if ORACLE
 pdnssec_LDADD += $(ORACLE_LIBS)
 endif
 
+if PKCS11
+pdnssec_SOURCES +=  pkcs11signers.cc pkcs11signers.hh
+pdnssec_LDADD += $(P11KIT1_LIBS)
+endif
+
 sdig_SOURCES=sdig.cc sstuff.hh dnsparser.cc dnsparser.hh dnsrecords.cc dnswriter.cc dnslabeltext.cc dnswriter.hh \
        misc.cc misc.hh rcpgenerator.cc rcpgenerator.hh base64.cc base64.hh unix_utility.cc \
        logger.cc statbag.cc qtype.cc sillyrecords.cc nsecrecords.cc base32.cc
@@ -283,6 +296,11 @@ testrunner_SOURCES=testrunner.cc test-misc_hh.cc test-nameserver_cc.cc test-dnsr
 testrunner_LDFLAGS= @DYNLINKFLAGS@ @THREADFLAGS@ $(BOOST_UNIT_TEST_FRAMEWORK_LDFLAGS)
 testrunner_LDADD= $(POLARSSL_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIBS)
 
+if PKCS11
+testrunner_SOURCES += pkcs11signers.cc pkcs11signers.hh
+testrunner_LDADD += $(P11KIT1_LIBS)
+endif
+
 pdns_recursor_SOURCES=syncres.cc resolver.hh misc.cc unix_utility.cc qtype.cc \
 logger.cc statbag.cc arguments.cc  lwres.cc pdns_recursor.cc reczones.cc lwres.hh \
 mtasker.hh syncres.hh recursor_cache.cc recursor_cache.hh dnsparser.cc \
index a8a77fd8358f7520c50363de30b6b1680b5d4b1c..94d98a0fed2840fd7be336f7d42e976a4db9ed58 100644 (file)
 #include "base64.hh"
 #include "sha.hh"
 #include "namespaces.hh"
+#ifdef HAVE_P11KIT1
+#include "pkcs11signers.hh"
+#endif
+
 using namespace boost::assign;
 
 DNSCryptoKeyEngine* DNSCryptoKeyEngine::makeFromISCFile(DNSKEYRecordContent& drc, const char* fname)
@@ -35,6 +39,7 @@ DNSCryptoKeyEngine* DNSCryptoKeyEngine::makeFromISCFile(DNSKEYRecordContent& drc
 
 DNSCryptoKeyEngine* DNSCryptoKeyEngine::makeFromISCString(DNSKEYRecordContent& drc, const std::string& content)
 {
+  bool pkcs11=false;
   int algorithm = 0;
   string sline, key, value, raw;
   std::istringstream str(content);
@@ -47,6 +52,20 @@ DNSCryptoKeyEngine* DNSCryptoKeyEngine::makeFromISCString(DNSKEYRecordContent& d
       algorithm = atoi(value.c_str());
       stormap["algorithm"]=lexical_cast<string>(algorithm);
       continue;
+    } else if (pdns_iequals(key,"pin")) {
+      stormap["pin"]=value;
+      continue;
+    } else if (pdns_iequals(key,"engine")) {
+      stormap["engine"]=value;
+      pkcs11=true;
+      continue;
+    } else if (pdns_iequals(key,"slot")) {
+      int slot = atoi(value.c_str());
+      stormap["slot"]=lexical_cast<string>(slot);
+      continue;
+    }  else if (pdns_iequals(key,"label")) {
+      stormap["label"]=value;
+      continue;
     }
     else if(pdns_iequals(key, "Private-key-format"))
       continue;
@@ -54,7 +73,17 @@ DNSCryptoKeyEngine* DNSCryptoKeyEngine::makeFromISCString(DNSKEYRecordContent& d
     B64Decode(value, raw);
     stormap[toLower(key)]=raw;
   }
-  DNSCryptoKeyEngine* dpk=make(algorithm);
+  DNSCryptoKeyEngine* dpk;
+
+  if (pkcs11) {
+#ifdef HAVE_P11KIT1
+    dpk = PKCS11DNSCryptoKeyEngine::maker(algorithm); 
+#else
+    throw new PDNSException("Cannot load PKCS#11 key without support for it");
+#endif
+  } else {
+    dpk=make(algorithm);
+  }
   dpk->fromISCMap(drc, stormap);
   return dpk;
 }
@@ -66,7 +95,9 @@ std::string DNSCryptoKeyEngine::convertToISC() const
   ostringstream ret;
   ret<<"Private-key-format: v1.2\n";
   BOOST_FOREACH(const stormap_t::value_type& value, stormap) {
-    if(value.first != "Algorithm") 
+    if(value.first != "Algorithm" && value.first != "PIN" && 
+       value.first != "Slot" && value.first != "Engine" &&
+       value.first != "Label") 
       ret<<value.first<<": "<<Base64Encode(value.second)<<"\n";
     else
       ret<<value.first<<": "<<value.second<<"\n";
@@ -168,6 +199,19 @@ pair<unsigned int, unsigned int> DNSCryptoKeyEngine::testMakers(unsigned int alg
         algorithm = atoi(value.c_str());
         stormap["algorithm"]=lexical_cast<string>(algorithm);
         continue;
+      } else if (pdns_iequals(key,"pin")) {
+        stormap["pin"]=value;
+        continue;
+      } else if (pdns_iequals(key,"engine")) {
+        stormap["engine"]=value;
+        continue;
+      } else if (pdns_iequals(key,"slot")) {
+        int slot = atoi(value.c_str());
+        stormap["slot"]=lexical_cast<string>(slot);
+        continue;
+      }  else if (pdns_iequals(key,"label")) {
+        stormap["label"]=value;
+        continue;
       }
       else if(pdns_iequals(key, "Private-key-format"))
         continue;
@@ -262,7 +306,6 @@ DSRecordContent makeDSFromDNSKey(const std::string& qname, const DNSKEYRecordCon
   string toHash;
   toHash.assign(toLower(simpleCompress(qname)));
   toHash.append(const_cast<DNSKEYRecordContent&>(drc).serialize("", true, true));
-
   
   DSRecordContent dsrc;
   if(digest==1) {
@@ -287,6 +330,7 @@ DSRecordContent makeDSFromDNSKey(const std::string& qname, const DNSKEYRecordCon
   dsrc.d_algorithm= drc.d_algorithm;
   dsrc.d_digesttype=digest;
   dsrc.d_tag=const_cast<DNSKEYRecordContent&>(drc).getTag();
+
   return dsrc;
 }
 
@@ -294,12 +338,13 @@ DSRecordContent makeDSFromDNSKey(const std::string& qname, const DNSKEYRecordCon
 DNSKEYRecordContent makeDNSKEYFromDNSCryptoKeyEngine(const DNSCryptoKeyEngine* pk, uint8_t algorithm, uint16_t flags)
 {
   DNSKEYRecordContent drc;
-  
+
   drc.d_protocol=3;
   drc.d_algorithm = algorithm;
 
   drc.d_flags=flags;
   drc.d_key = pk->getPublicKeyString();
+
   return drc;
 }
 
@@ -594,3 +639,4 @@ void addTSIG(DNSPacketWriter& pw, TSIGRecordContent* trc, const string& tsigkeyn
   trc->toPacket(pw);
   pw.commit();
 }
+
index 96362a44cbc15f458b85487472b509fd1f166c50..d81e7bb4d956c62e7e182fdba03fd3fe779f2c2a 100644 (file)
@@ -1,5 +1,6 @@
 #ifndef PDNS_DNSSECINFRA_HH
 #define PDNS_DNSSECINFRA_HH
+
 #include "dnsrecords.hh"
 #include <boost/shared_ptr.hpp>
 #include <string>
@@ -28,7 +29,7 @@ class DNSCryptoKeyEngine
     virtual std::string getPubKeyHash()const =0;
     virtual std::string getPublicKeyString()const =0;
     virtual int getBits() const =0;
-    
     virtual void fromISCMap(DNSKEYRecordContent& drc, stormap_t& stormap)=0;
     virtual void fromPEMString(DNSKEYRecordContent& drc, const std::string& raw)
     {
index 2f4affd3b56100f3f964a6c313aeb5eb4e03f9c7..19cd43394a11bff8838663eff39dff4ab8621ace 100644 (file)
@@ -5,6 +5,7 @@
 #include "base64.hh"
 #include <boost/foreach.hpp>
 #include <boost/program_options.hpp>
+#include <boost/assign/std/vector.hpp>
 #include <boost/assign/list_of.hpp>
 #include "dnsbackend.hh"
 #include "ueberbackend.hh"
@@ -1116,6 +1117,8 @@ try
     cerr<<"                                   Generate a ZSK or KSK to stdout with specified algo&bits"<<endl;
     cerr<<"get-meta ZONE [kind kind ..]       Get zone metadata. If no KIND given, lists all known"<<endl;
     cerr<<"hash-zone-record ZONE RNAME        Calculate the NSEC3 hash for RNAME in ZONE"<<endl;
+    cerr<<"hsm assign zone module slot pin    Assign a hardware signing module to a ZONE"<<endl;
+    cerr<<"hsm create-key zone                Create a key using hardware signing module for ZONE (assign first)"<<endl;
     cerr<<"increase-serial ZONE               Increases the SOA-serial by 1. Uses SOA-EDIT"<<endl;
     cerr<<"import-tsig-key NAME ALGORITHM KEY Import TSIG key"<<endl;
     cerr<<"import-zone-key ZONE FILE          Import from a file a private key, ZSK or KSK"<<endl;
@@ -1845,6 +1848,84 @@ try
     } else {
       cout << "Set '" << zone << "' meta " << kind << " = " << boost::join(meta, ", ") << endl;
     }
+  } else if (cmds[0]=="hsm") {
+    UeberBackend B("default");
+    if (cmds[1] == "assign") {
+      DNSCryptoKeyEngine::storvector_t storvect;
+      DomainInfo di;
+      string zone = cmds[2];
+      // verify zone
+      if (!B.getDomainInfo(zone, di)) {
+        cerr << "Unable to assign module to unknown zone '" << zone << "'" << std::endl;
+        return 1;
+      }
+
+      int algorithm = shorthand2algorithm(cmds[3]);
+      bool keyOrZone = (cmds[4] == "ksk" ? true : false);
+      string module = cmds[5];
+      string slot = cmds[6];
+      string pin = cmds[7];
+      string label = cmds[8];
+
+      std::ostringstream iscString;
+      iscString << "Private-key-format: v1.2" << std::endl << 
+        "Algorithm: " << algorithm << std::endl << 
+        "Engine: " << module << std::endl <<
+        "Slot: " << slot << std::endl <<
+        "PIN: " << pin << std::endl << 
+        "Label: " << label << std::endl;
+
+     DNSKEYRecordContent drc; 
+     DNSSECPrivateKey dpk;
+     dpk.d_flags = (keyOrZone ? 257 : 256);
+     dpk.setKey(shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromISCString(drc, iscString.str())));
+
+     if (!dk.addKey(zone, dpk)) {
+       cerr << "Unable to assign module slot to zone" << std::endl;
+       return 1;
+     }
+
+     cerr << "Module " << module << " slot " << slot << " assigned to " << zone << endl;
+     return 0;
+    } else if (cmds[1] == "create-key") {
+      DomainInfo di;
+      string zone = cmds[2];
+      unsigned int id;
+
+      // verify zone
+      if (!B.getDomainInfo(zone, di)) {
+        cerr << "Unable to create key for unknown zone '" << zone << "'" << std::endl;
+        return 1;
+      }
+      id = boost::lexical_cast<unsigned int>(cmds[3]);
+      std::vector<DNSBackend::KeyData> keys; 
+      if (!B.getDomainKeys(zone, 0, keys)) {
+        cerr << "No keys found for zone " << zone << std::endl;
+        return 1;
+      } 
+
+      DNSCryptoKeyEngine *dke = NULL;
+      // lookup correct key      
+      BOOST_FOREACH(DNSBackend::KeyData &kd, keys) {
+        if (kd.id == id) {
+          // found our key. 
+          DNSKEYRecordContent dkrc;
+          dke = DNSCryptoKeyEngine::makeFromISCString(dkrc, kd.content);
+        }
+      }
+
+      if (!dke) {
+        cerr << "Could not find key with ID " << id << endl;
+        return 1;
+      }
+
+      dke->create(2048);
+
+      cerr << "Created key i think" << std::endl;
+      return 0;
+    }
   } else {
     cerr<<"Unknown command '"<<cmds[0] <<"'"<< endl;
     return 1;
diff --git a/pdns/pkcs11signers.cc b/pdns/pkcs11signers.cc
new file mode 100644 (file)
index 0000000..e992af0
--- /dev/null
@@ -0,0 +1,842 @@
+#include <polarssl/rsa.h>
+#include <polarssl/base64.h>
+#include <sha.hh>
+#include <polarssl/entropy.h>
+#include <polarssl/ctr_drbg.h>
+#include <boost/assign/std/vector.hpp> // for 'operator+=()'
+#include <boost/assign/list_of.hpp>
+#include <boost/foreach.hpp>
+#include <p11-kit/p11-kit.h>
+
+#include "dnssecinfra.hh"
+#include "pkcs11signers.hh"
+#include "pdnsexception.hh"
+
+/* TODO
+
+  - list possible tokens and supported modes
+  - Engine: <name>, Slot: <slot>, PIN: <pin>
+  - fallback if not supported to polarssl (for hashing)
+  - ECDSA support (how to test?)
+  - Physical token testing?
+  - module-slot locking (as they do not support parallel)
+
+NB! If you do use this, here is a simple way to get softhsm working
+
+create /etc/pkcs11/modules/softhsm.module
+
+put 
+
+module: /usr/lib/softhsm/libsofthsm.so
+managed: yes
+
+in it. you need to use softhsm tools to manage this all.
+
+*/
+
+
+
+// map for signing algorithms
+static std::map<unsigned int,CK_MECHANISM_TYPE> dnssec2smech = boost::assign::map_list_of
+(5, CKM_SHA1_RSA_PKCS)
+(7, CKM_SHA1_RSA_PKCS)
+(8, CKM_SHA256_RSA_PKCS)
+(10, CKM_SHA512_RSA_PKCS)
+(13, CKM_ECDSA)
+(14, CKM_ECDSA);
+
+// map for hashing algorithms
+static std::map<unsigned int,CK_MECHANISM_TYPE> dnssec2hmech = boost::assign::map_list_of
+(5, CKM_SHA_1)
+(7, CKM_SHA_1)
+(8, CKM_SHA256)
+(10, CKM_SHA512)
+(13, CKM_SHA256)
+(14, CKM_SHA384);
+
+// p11 handler for modules
+
+class P11KitModule;
+class P11KitSlot;
+
+typedef enum { Attribute_Byte, Attribute_Long, Attribute_String } CkaValueType;
+
+// Attribute handling
+class P11KitAttribute {
+private:
+  CK_ATTRIBUTE_TYPE type;
+  CK_BYTE ckByte;
+  CK_ULONG ckLong;
+  std::string ckString;
+  CkaValueType ckType;
+  unsigned char *buffer;
+  CK_ULONG buflen;
+protected:
+  void Init() {
+    buffer = NULL;
+    buflen = 0;
+  };
+public:
+  P11KitAttribute(CK_ATTRIBUTE_TYPE type, const std::string& value) {
+    Init();
+    this->type = type;
+    setString(value);
+  }
+
+  P11KitAttribute(CK_ATTRIBUTE_TYPE type, char value) {
+    Init();
+    this->type = type;
+    setByte(value);
+  }
+
+  P11KitAttribute(CK_ATTRIBUTE_TYPE type, unsigned char value) {
+    Init();
+    this->type = type;
+    setByte(value);
+  }
+
+  P11KitAttribute(CK_ATTRIBUTE_TYPE type, unsigned long value) {
+    Init();
+    this->type = type;
+    setLong(value);
+  }
+
+  CkaValueType valueType() const {
+    return ckType;
+  }
+
+  const std::string &str() const {
+    return ckString;
+  };
+
+  unsigned char byte() const {
+    return ckByte;
+  }
+
+  unsigned long ulong() const {
+    return ckLong;
+  }
+
+  void setString(const std::string& value) {
+    this->ckString = value;
+    this->ckType = Attribute_String;
+  }
+
+  void setByte(char value) {
+    this->ckByte = value;
+    this->ckType = Attribute_Byte;
+  }
+
+  void setByte(unsigned char value) {
+    this->ckByte = value;
+    this->ckType = Attribute_Byte;
+  }
+
+  void setLong(unsigned long value) {
+    this->ckLong = value;
+    this->ckType = Attribute_Long;
+  }
+
+// this bit is used for getting attribute from object
+// we provide a pointer for GetAttributeValue to write to
+  CK_BYTE_PTR allocate(CK_ULONG amount) {
+    buffer = new unsigned char[amount];
+    buflen = amount;
+    return buffer;
+  }
+
+// and here we copy the results back and delete buffer
+  void commit(CK_ULONG amount) {
+    if (buffer) {
+      this->ckString.assign((char*)buffer, amount);
+      delete [] buffer;
+    }
+    buffer = NULL;
+    buflen = 0;
+  }
+
+// this is *writable* attribute (you write into it)
+  void wattr(CK_ATTRIBUTE_PTR attr) {
+    attr->type = type;
+    switch(ckType) {
+      case Attribute_Byte: {
+        attr->pValue = (void*)&ckByte;
+        attr->ulValueLen = 1;
+        break;
+      }
+      case Attribute_Long: {
+        attr->pValue = (void*)&ckLong;
+        attr->ulValueLen = sizeof(CK_ULONG);
+        break;
+      }
+      case Attribute_String: {
+        attr->pValue = buffer;
+        attr->ulValueLen = buflen;
+      }
+    };
+  };
+
+// this is *readable* attribute (you read from it)
+  void rattr(CK_ATTRIBUTE_PTR attr) const {
+    attr->type = type;
+    switch(ckType) {
+      case Attribute_Byte: {
+        attr->pValue = (void*)&ckByte;
+        attr->ulValueLen = 1;
+        break;
+      }
+      case Attribute_Long: {
+        attr->pValue = (void*)&ckLong;
+        attr->ulValueLen = sizeof(CK_ULONG);
+        break;
+      }
+      case Attribute_String: {
+        attr->pValue = (void*)ckString.c_str();
+        attr->ulValueLen = ckString.size();
+      }
+    };
+  };
+};
+
+// representation of slot
+class P11KitSlot {
+private:
+  CK_SESSION_HANDLE d_session;
+  CK_SLOT_ID d_slot;
+  P11KitModule *d_module;
+
+public:
+  P11KitSlot();
+  P11KitSlot(CK_SLOT_ID slot, P11KitModule *module);
+  P11KitSlot(const P11KitSlot& rhs);
+  ~P11KitSlot();
+
+  void SetSlot(CK_SLOT_ID slot);
+  void SetModule(P11KitModule *module);
+
+  CK_RV OpenSession(CK_FLAGS flags);
+  CK_RV CloseSession();
+
+  CK_RV GetInfo(CK_SLOT_INFO_PTR info) const;
+  CK_RV GetTokenInfo(CK_TOKEN_INFO_PTR info) const;
+
+  CK_RV Login(const std::string& pin, CK_USER_TYPE user);
+  CK_RV Logout();
+
+  CK_RV GenerateKeyPair(CK_MECHANISM_PTR mechanism, std::vector<P11KitAttribute>& pubAttributes, std::vector<P11KitAttribute>& privAttributes, CK_OBJECT_HANDLE_PTR pubKey, CK_OBJECT_HANDLE_PTR privKey);
+  CK_RV FindObjects(const std::vector<P11KitAttribute>& attributes, std::vector<CK_OBJECT_HANDLE>& objects, CK_ULONG maxobjects) const;
+  CK_RV SetAttributeValue(CK_OBJECT_HANDLE object, const std::vector<P11KitAttribute>& attributes);
+  CK_RV GetAttributeValue(CK_OBJECT_HANDLE object, std::vector<P11KitAttribute>& attributes) const;
+
+  CK_RV Sign(const std::string& data, std::string& result, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) const;
+  CK_RV Verify(const std::string& data, const std::string& result, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) const;
+
+  CK_RV Digest(const std::string& data, std::string& result, CK_MECHANISM_PTR mechanism) const;
+
+  CK_RV DigestInit(CK_MECHANISM_PTR mechanism) const;
+  CK_RV DigestUpdate(const std::string& data) const;
+  CK_RV DigestUpdate(CK_OBJECT_HANDLE key) const;
+  CK_RV DigestFinal(std::string& result) const;
+};
+
+// representation of module (or Engine)
+class P11KitModule
+{
+  public:
+    std::string d_module;
+    CK_FUNCTION_LIST_PTR functions;
+
+    P11KitModule() {}
+
+    P11KitModule(const std::string& module) {
+      this->d_module = module;
+    }
+
+    P11KitModule(const P11KitModule& rhs) {
+      functions = rhs.functions;
+      d_module = rhs.d_module;
+    }
+
+    void setModule(const std::string& module) {
+      this->d_module = module;
+    };
+
+// basically get the function list
+    bool initialize() {
+      functions = p11_kit_registered_name_to_module(d_module.c_str());
+      if (functions == NULL) return false;
+      return true;
+    };
+
+// convenience method + checking that slot exists
+    bool GetSlot(CK_SLOT_ID slotId, P11KitSlot &slot) {
+      _CK_SLOT_INFO info;
+      if (this->functions->C_GetSlotInfo(slotId, &info)) {
+        return false;
+      }
+      slot.SetSlot(slotId);
+      slot.SetModule(this);
+      return true;
+    };
+};
+
+P11KitSlot::P11KitSlot() { d_module = NULL; };
+
+P11KitSlot::P11KitSlot(CK_SLOT_ID slot, P11KitModule *module)
+{
+  this->d_slot = slot;
+  this->d_module = module;
+}
+
+P11KitSlot::P11KitSlot(const P11KitSlot &rhs)
+{
+  this->d_slot = rhs.d_slot;
+  this->d_module = rhs.d_module;
+  this->d_session = rhs.d_session;
+}
+
+P11KitSlot::~P11KitSlot()
+{
+  if (this->d_module)
+    this->d_module->functions->C_CloseAllSessions(this->d_slot);
+}
+
+// DO NOT CALL THIS ON YOUR OWN
+void P11KitSlot::SetSlot(CK_SLOT_ID slot) {
+  this->d_slot = slot;
+}
+
+// DO NOT CALL THIS ON YOUR OWN
+void P11KitSlot::SetModule(P11KitModule *module) {
+  this->d_module = module;
+}
+
+// Create new session, mostly uses CKF_SERIAL_SESSION (which is mandatory flag)
+// Another flag you can pass is CFK_RW_SESSION
+CK_RV P11KitSlot::OpenSession(CK_FLAGS flags)
+{
+  if (!this->d_module) return 0xff;
+  return this->d_module->functions->C_OpenSession(this->d_slot, flags, 0, 0, &(this->d_session));
+}
+
+CK_RV P11KitSlot::CloseSession()
+{
+  if (!this->d_module) return 0xff;
+  CK_RV rv = this->d_module->functions->C_CloseSession(this->d_session);
+  this->d_session = 0;
+  return rv;
+}
+
+CK_RV P11KitSlot::GetInfo(CK_SLOT_INFO_PTR info) const
+{
+  if (!this->d_module) return 0xff;
+  return this->d_module->functions->C_GetSlotInfo(this->d_slot, info);
+}
+
+CK_RV P11KitSlot::GetTokenInfo(CK_TOKEN_INFO_PTR info) const
+{  
+  if (!this->d_module) return 0xff;
+  return this->d_module->functions->C_GetTokenInfo(this->d_slot, info);
+}
+
+CK_RV P11KitSlot::Login(const std::string& pin, CK_USER_TYPE user)
+{
+  if (!this->d_module) return 0xff;
+  CK_RV rv;
+  unsigned char *pPin;
+  pPin = new unsigned char[pin.size()];
+  pin.copy(reinterpret_cast<char*>(pPin), pin.size());
+  rv = this->d_module->functions->C_Login(this->d_session, user, pPin, pin.size());
+  delete [] pPin;
+  return rv;
+}
+
+CK_RV P11KitSlot::Logout()
+{
+  if (!this->d_module) return 0xff;
+  return this->d_module->functions->C_Logout(this->d_slot);
+}
+
+CK_RV P11KitSlot::GenerateKeyPair(CK_MECHANISM_PTR mechanism, std::vector<P11KitAttribute>& pubAttributes, std::vector<P11KitAttribute>& privAttributes, CK_OBJECT_HANDLE_PTR pubKey, CK_OBJECT_HANDLE_PTR privKey) {
+  if (!this->d_module) return 0xff;
+
+  CK_RV rv;
+  size_t k;
+  CK_ATTRIBUTE_PTR pubAttr, privAttr;
+  pubAttr = new CK_ATTRIBUTE[pubAttributes.size()];
+  privAttr = new CK_ATTRIBUTE[privAttributes.size()];
+
+  k = 0;
+  BOOST_FOREACH(P11KitAttribute& attribute, pubAttributes) {
+    attribute.rattr(pubAttr+k);
+    k++;
+  }
+
+  k = 0;
+  BOOST_FOREACH(P11KitAttribute& attribute, privAttributes) {
+    attribute.rattr(privAttr+k);
+    k++;
+  }
+
+  rv = this->d_module->functions->C_GenerateKeyPair(d_session, mechanism, pubAttr, pubAttributes.size(), privAttr, privAttributes.size(), pubKey, privKey); 
+
+  delete [] pubAttr;
+  delete [] privAttr;
+
+  return rv;
+}
+
+// Finds object(s) that match exactly to attributes
+CK_RV P11KitSlot::FindObjects(const std::vector<P11KitAttribute>& attributes, std::vector<CK_OBJECT_HANDLE>& objects, unsigned long maxobjects) const 
+{
+  if (!this->d_module) return 0xff;
+  CK_RV rv;
+  size_t k;
+  unsigned long count;
+  CK_ATTRIBUTE_PTR attr;
+  CK_OBJECT_HANDLE_PTR handles = new CK_OBJECT_HANDLE[maxobjects];
+  attr = new CK_ATTRIBUTE[attributes.size()];
+
+  k = 0;
+  BOOST_FOREACH(const P11KitAttribute& attribute, attributes) {
+    attribute.rattr(attr+k);
+    k++;
+  }
+
+  // perform search
+  rv = this->d_module->functions->C_FindObjectsInit(d_session, attr, k);
+
+  if (rv) {
+    delete [] attr;
+    delete [] handles;
+    return rv;
+  }
+
+  count = maxobjects;
+  rv = this->d_module->functions->C_FindObjects(d_session, handles, maxobjects, &count);
+
+  if (!rv) {
+    objects.clear();
+    for(k=0;k<count;k++) {
+      objects.push_back(handles[k]);
+    }
+  }
+
+  delete [] attr;
+  delete [] handles;
+
+  this->d_module->functions->C_FindObjectsFinal(d_session);
+
+  return rv;
+};
+
+// TODO: Untested codepath
+CK_RV P11KitSlot::SetAttributeValue(CK_OBJECT_HANDLE object, const std::vector<P11KitAttribute>& attributes)
+{
+  if (!this->d_module) return 0xff;
+  CK_RV rv;
+  size_t k;
+  CK_ATTRIBUTE_PTR attr;
+  attr = new CK_ATTRIBUTE[attributes.size()];
+
+  k = 0;
+  BOOST_FOREACH(const P11KitAttribute &attribute, attributes) {
+    attribute.rattr(attr+k);
+    k++;
+  }
+
+  rv = this->d_module->functions->C_SetAttributeValue(d_session, object, attr, attributes.size());
+
+  delete [] attr;
+  return rv;
+}
+
+CK_RV P11KitSlot::GetAttributeValue(CK_OBJECT_HANDLE object, std::vector<P11KitAttribute>& attributes) const
+{
+  if (!this->d_module) return 0xff;
+  CK_RV rv;
+  size_t k;
+  CK_ATTRIBUTE_PTR attr;
+  attr = new CK_ATTRIBUTE[attributes.size()];
+
+  k = 0;
+  BOOST_FOREACH(P11KitAttribute &attribute, attributes) {
+    attribute.wattr(attr+k);
+    k++;
+  }
+
+  // round 1 - get attribute sizes
+  rv = this->d_module->functions->C_GetAttributeValue(d_session, object, attr, attributes.size());
+
+  if (rv) {
+    delete [] attr;
+    return rv;
+  }
+
+  // then allocate memory
+  for(size_t k=0; k < attributes.size(); k++) {
+    if (attributes[k].valueType() == Attribute_String) {
+      attr[k].pValue = attributes[k].allocate(attr[k].ulValueLen);
+    }
+  }
+
+  // round 2 - get actual values
+  rv = this->d_module->functions->C_GetAttributeValue(d_session, object, attr, attributes.size());
+
+  // copy values to map and release allocated memory
+  for(size_t k=0; k < attributes.size(); k++) {
+    if (attributes[k].valueType() == Attribute_String) {
+      attributes[k].commit(attr[k].ulValueLen);
+    }
+  }
+
+  delete [] attr;
+
+  return rv;
+};
+
+CK_RV P11KitSlot::Sign(const std::string& data, std::string& result, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) const
+{
+  if (!this->d_module) return 0xff;
+  CK_RV rv;
+  CK_BYTE buffer[1024];
+  CK_ULONG buflen = sizeof buffer; // should be enough for most signatures.
+
+  // perform signature
+  if ((rv = this->d_module->functions->C_SignInit(d_session, mechanism, key))) return rv; // well that failed.
+  rv = this->d_module->functions->C_Sign(d_session, (unsigned char*)data.c_str(), data.size(), buffer, &buflen);
+  if (!rv) {
+    result.assign((char*)buffer, buflen);
+  }
+  memset(buffer,0,sizeof buffer);
+
+  return rv;
+};
+
+CK_RV P11KitSlot::Verify(const std::string& data, const std::string& signature, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) const
+{
+  if (!this->d_module) return 0xff;
+  CK_RV rv;
+  if ((rv = this->d_module->functions->C_VerifyInit(d_session, mechanism, key))) return rv;
+  rv = this->d_module->functions->C_Verify(d_session, (unsigned char*)data.c_str(), data.size(), (unsigned char*)signature.c_str(), signature.size());
+  return rv;
+};
+
+CK_RV P11KitSlot::Digest(const std::string& data, std::string& result, CK_MECHANISM_PTR mechanism) const
+{ 
+  if (!this->d_module) return 0xff;
+  CK_RV rv;
+  CK_BYTE buffer[1024];
+  CK_ULONG buflen = sizeof buffer; // should be enough for most digests
+  if ((rv = this->d_module->functions->C_DigestInit(d_session, mechanism))) return rv;
+  rv = this->d_module->functions->C_Digest(d_session, (unsigned char*)data.c_str(), data.size(), buffer, &buflen);
+  if (!rv) {
+    result.assign((char*)buffer, buflen);
+  }
+  memset(buffer,0,sizeof buffer);
+
+  return rv;
+};
+
+CK_RV P11KitSlot::DigestInit(CK_MECHANISM_PTR mechanism) const
+{
+  if (!this->d_module) return 0xff;
+  return this->d_module->functions->C_DigestInit(d_session, mechanism);
+}
+
+CK_RV P11KitSlot::DigestUpdate(const std::string& data) const
+{
+  if (!this->d_module) return 0xff;
+  return this->d_module->functions->C_DigestUpdate(d_session, (unsigned char*)data.c_str(), data.size());
+}
+
+CK_RV P11KitSlot::DigestUpdate(CK_OBJECT_HANDLE key) const
+{
+  if (!this->d_module) return 0xff;
+  return this->d_module->functions->C_DigestKey(d_session, key);
+}
+
+CK_RV P11KitSlot::DigestFinal(std::string& result) const
+{
+  if (!this->d_module) return 0xff;
+  CK_RV rv;
+  CK_BYTE buffer[1024];
+  CK_ULONG buflen = sizeof buffer; // should be enough for most digests
+  rv = this->d_module->functions->C_DigestFinal(d_session, buffer, &buflen);
+  if (!rv) {
+    result.assign((char*)buffer, buflen);
+  }
+  memset(buffer,0,sizeof buffer);
+  return rv;
+}
+
+// map between engine names and engines
+static std::map<std::string, P11KitModule> pkcs11_engines;
+// map between engine names and slots (not used now)
+//static std::map<std::string, CK_SLOT_ID> pkcs11_slots;
+
+static bool pkcs11_GetSlot(const std::string& engine, CK_SLOT_ID slotId, const std::string& pin, CK_FLAGS flags, P11KitSlot& slot) 
+{
+  CK_RV rv;
+  if (engine.empty()) return false;
+
+  // open module if necessary
+  if (pkcs11_engines.find(engine) == pkcs11_engines.end()) {
+    P11KitModule module(engine);
+    if (module.initialize() == false) {
+      throw PDNSException("Cannot initialize or unknown PKCS#11 engine " + engine);
+    }
+    pkcs11_engines[engine] = module;
+  }
+  pkcs11_engines[engine].GetSlot(slotId, slot);
+  rv = slot.OpenSession(flags);
+  if (rv) { 
+    return false; 
+  };
+  rv = slot.Login(pin, CKU_USER);
+//  if (rv) {
+//    std::cerr << "Login gave " << rv << std::endl;
+//  };
+  return rv == 0;
+}
+
+PKCS11DNSCryptoKeyEngine::PKCS11DNSCryptoKeyEngine(unsigned int algorithm): DNSCryptoKeyEngine(algorithm) {}
+PKCS11DNSCryptoKeyEngine::~PKCS11DNSCryptoKeyEngine() {}
+PKCS11DNSCryptoKeyEngine::PKCS11DNSCryptoKeyEngine(const PKCS11DNSCryptoKeyEngine& orig) : DNSCryptoKeyEngine(orig.d_algorithm) {}
+
+void PKCS11DNSCryptoKeyEngine::create(unsigned int bits) {
+  std::vector<P11KitAttribute> pubAttr;
+  std::vector<P11KitAttribute> privAttr;
+  CK_MECHANISM mech;
+  CK_OBJECT_HANDLE pubKey, privKey;
+  CK_RV rv;
+  P11KitSlot d_slot;
+  pkcs11_GetSlot(d_engine, d_slot_id, d_pin, CKF_SERIAL_SESSION|CKF_RW_SESSION, d_slot);
+  std::string pubExp("\000\001\000\001", 4); // 65537
+
+  pubAttr.push_back(P11KitAttribute(CKA_ENCRYPT, (char)CK_TRUE));
+  pubAttr.push_back(P11KitAttribute(CKA_VERIFY, (char)CK_TRUE));
+  pubAttr.push_back(P11KitAttribute(CKA_WRAP, (char)CK_TRUE));
+  pubAttr.push_back(P11KitAttribute(CKA_MODULUS_BITS, (unsigned long)bits));
+  pubAttr.push_back(P11KitAttribute(CKA_PUBLIC_EXPONENT, pubExp)); 
+  pubAttr.push_back(P11KitAttribute(CKA_LABEL, d_label));
+
+  privAttr.push_back(P11KitAttribute(CKA_TOKEN, (char)CK_TRUE));
+  privAttr.push_back(P11KitAttribute(CKA_PRIVATE, (char)CK_TRUE));
+//  privAttr.push_back(P11KitAttribute(CKA_SUBJECT, "CN=keygen"));
+  privAttr.push_back(P11KitAttribute(CKA_ID, "\x01\x02\x03\x04")); // this is mandatory if you want to export anything
+  privAttr.push_back(P11KitAttribute(CKA_SENSITIVE, (char)CK_TRUE));
+  privAttr.push_back(P11KitAttribute(CKA_DECRYPT, (char)CK_TRUE));
+  privAttr.push_back(P11KitAttribute(CKA_SIGN, (char)CK_TRUE));
+  privAttr.push_back(P11KitAttribute(CKA_UNWRAP, (char)CK_TRUE));
+  privAttr.push_back(P11KitAttribute(CKA_LABEL, d_label));
+
+  mech.mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
+  mech.pParameter = NULL;
+  mech.ulParameterLen = 0;
+
+  if ((rv = d_slot.GenerateKeyPair(&mech, pubAttr, privAttr, &pubKey, &privKey))) {
+    std::ostringstream error;
+    error << "Keypair generation failed with " << rv;
+    throw new PDNSException(error.str());
+  }
+};
+
+std::string PKCS11DNSCryptoKeyEngine::sign(const std::string& msg) const { 
+  std::string result;
+  std::vector<CK_OBJECT_HANDLE> key;
+  std::vector<P11KitAttribute> attr;
+  P11KitSlot d_slot;
+  pkcs11_GetSlot(d_engine, d_slot_id, d_pin, CKF_SERIAL_SESSION, d_slot);
+  // find key that can be used for signing 
+  attr.push_back(P11KitAttribute(CKA_SIGN, (char)CK_TRUE));
+  attr.push_back(P11KitAttribute(CKA_LABEL, d_label));
+  d_slot.FindObjects(attr, key, 1);
+  // hopefully we have a key
+  if (key.size() == 0) return "";
+  // choose mech
+  CK_MECHANISM mech;
+  mech.mechanism = dnssec2smech[d_algorithm];
+  mech.pParameter = NULL;
+  mech.ulParameterLen = 0;
+  d_slot.Sign(msg, result, &mech, key[0]);
+  return result;
+};
+
+std::string PKCS11DNSCryptoKeyEngine::hash(const std::string& msg) const {
+  std::string result;
+  CK_MECHANISM mech;
+  mech.mechanism = dnssec2hmech[d_algorithm];
+  mech.pParameter = NULL;
+  mech.ulParameterLen = 0;
+  P11KitSlot d_slot;
+  pkcs11_GetSlot(d_engine, d_slot_id, d_pin, CKF_SERIAL_SESSION, d_slot);
+
+  if (d_slot.Digest(msg, result, &mech)) {
+    // FINE! I'll do this myself, then, shall I?
+    switch(d_algorithm) {
+    case 5: {
+      SHA1Summer sha;
+      sha.feed(msg);
+      return sha.get();
+    }
+    case 8: {
+      SHA256Summer sha;
+      sha.feed(msg);
+      return sha.get();
+    }
+    case 10: {
+      SHA512Summer sha;
+      sha.feed(msg);
+      return sha.get();
+    }
+    case 13: {
+      SHA256Summer sha;
+      sha.feed(msg);
+      return sha.get();
+    }
+    case 14: {
+      SHA384Summer sha;
+      sha.feed(msg);
+      return sha.get();
+    }
+    };
+  };
+  return result;
+};
+
+bool PKCS11DNSCryptoKeyEngine::verify(const std::string& msg, const std::string& signature) const {
+  bool result;
+  std::vector<CK_OBJECT_HANDLE> key;
+  std::vector<P11KitAttribute> attr;
+  // find a key that can be used to verify signatures
+  attr.push_back(P11KitAttribute(CKA_VERIFY, (char)CK_TRUE));
+  attr.push_back(P11KitAttribute(CKA_LABEL, d_label));
+  P11KitSlot d_slot;
+  pkcs11_GetSlot(d_engine, d_slot_id, d_pin, CKF_SERIAL_SESSION, d_slot);
+
+  d_slot.FindObjects(attr, key, 1);
+  // hopefully we have a key
+  if (key.size() == 0) return "";
+  // choose mech
+  CK_MECHANISM mech;
+  mech.mechanism = dnssec2smech[d_algorithm];
+  mech.pParameter = NULL;
+  mech.ulParameterLen = 0;
+  result = d_slot.Verify(msg, signature, &mech, key[0]);
+  return result;
+};
+
+std::string PKCS11DNSCryptoKeyEngine::getPubKeyHash() const { 
+  std::string result = "";
+  std::vector<CK_OBJECT_HANDLE> key;
+  std::vector<P11KitAttribute> attr;
+  // find us a public key
+  //attr.push_back(P11KitAttribute(CKA_CLASS, CKO_PUBLIC_KEY));
+  attr.push_back(P11KitAttribute(CKA_LABEL, d_label));
+  P11KitSlot d_slot;
+  pkcs11_GetSlot(d_engine, d_slot_id, d_pin, CKF_SERIAL_SESSION, d_slot);
+
+  d_slot.FindObjects(attr, key, 1);
+  if (key.size() > 0) {
+     attr.clear();
+     attr.push_back(P11KitAttribute(CKA_MODULUS, ""));
+     attr.push_back(P11KitAttribute(CKA_PUBLIC_EXPONENT, ""));
+     if (d_slot.GetAttributeValue(key[0], attr) == 0) {
+       CK_MECHANISM mech;
+       mech.mechanism = CKM_SHA_1;
+       d_slot.DigestInit(&mech);
+       d_slot.DigestUpdate(attr[0].str());
+       d_slot.DigestUpdate(attr[1].str());
+       d_slot.DigestFinal(result);
+     }
+  }
+  return result; 
+};
+
+std::string PKCS11DNSCryptoKeyEngine::getPublicKeyString() const {
+  std::string result("");
+  std::vector<CK_OBJECT_HANDLE> key;
+  std::vector<P11KitAttribute> attr;
+  attr.push_back(P11KitAttribute(CKA_LABEL, d_label));
+  P11KitSlot d_slot;
+  pkcs11_GetSlot(d_engine, d_slot_id, d_pin, CKF_SERIAL_SESSION, d_slot);
+
+  d_slot.FindObjects(attr, key, 1);
+  if (key.size() > 0) {
+     attr.clear();
+     attr.push_back(P11KitAttribute(CKA_MODULUS, ""));
+     attr.push_back(P11KitAttribute(CKA_PUBLIC_EXPONENT, ""));
+     if (d_slot.GetAttributeValue(key[0], attr) == 0) {
+       if(attr[1].str().length() < 255)
+         result.assign(1, (char) (unsigned int) attr[1].str().length());
+       else {
+         result.assign(1, 0);
+         uint16_t len=htons(attr[1].str().length());
+         result.append((char*)&len, 2);
+       } 
+       result.append(attr[1].str());
+       result.append(attr[0].str());
+     }
+//   } else {
+//     std::cerr << "Could not find key" << std::endl;
+   }
+   return result;
+};
+
+int PKCS11DNSCryptoKeyEngine::getBits() const {
+  int bits = -1;
+  std::vector<CK_OBJECT_HANDLE> key;
+  std::vector<P11KitAttribute> attr;
+  //attr.push_back(P11KitAttribute(CKA_VERIFY, (char)CK_TRUE));
+  attr.push_back(P11KitAttribute(CKA_LABEL, d_label));
+  P11KitSlot d_slot;
+  pkcs11_GetSlot(d_engine, d_slot_id, d_pin, CKF_SERIAL_SESSION, d_slot);
+
+//  std::cerr << "Looking for " << d_label << " from " << d_slot_id << std::endl;
+
+  d_slot.FindObjects(attr, key, 1);
+  if (key.size() > 0) {
+     attr.clear(); 
+     attr.push_back(P11KitAttribute(CKA_MODULUS_BITS, 0UL));
+     if (d_slot.GetAttributeValue(key[0], attr) == 0) {
+       bits = static_cast<int>(attr[0].ulong());
+     }
+//  } else {
+//     std::cerr << "Could not find key" << std::endl;
+  }
+  return bits;
+};
+
+DNSCryptoKeyEngine::storvector_t PKCS11DNSCryptoKeyEngine::convertToISCVector() const { 
+  storvector_t storvect;
+  typedef std::vector<std::pair<std::string, std::string> > outputs_t;
+  outputs_t outputs;
+
+  boost::assign::push_back(storvect)
+   (make_pair("Algorithm", boost::lexical_cast<std::string>(d_algorithm)))
+   (make_pair("Engine", d_engine))
+   (make_pair("Slot", boost::lexical_cast<std::string>(d_slot_id)))
+   (make_pair("PIN", d_pin))
+   (make_pair("Label", d_label));
+  return storvect;
+};
+
+DNSCryptoKeyEngine* PKCS11DNSCryptoKeyEngine::maker(unsigned int algorithm)
+{
+  return new PKCS11DNSCryptoKeyEngine(algorithm);
+}
+
+// this is called during program startup
+namespace {
+struct LoaderStruct
+{
+  LoaderStruct()
+  {
+    p11_kit_initialize_registered();
+  };
+  ~LoaderStruct() {
+    p11_kit_finalize_registered();
+  };
+} loaderPkcs11;
+}
diff --git a/pdns/pkcs11signers.hh b/pdns/pkcs11signers.hh
new file mode 100644 (file)
index 0000000..37850ff
--- /dev/null
@@ -0,0 +1,50 @@
+class PKCS11DNSCryptoKeyEngine : public DNSCryptoKeyEngine
+{
+  protected:
+    std::string d_engine;
+    unsigned long d_slot_id;
+    std::string d_pin;
+    std::string d_label;
+
+  public:
+    PKCS11DNSCryptoKeyEngine(unsigned int algorithm);
+    ~PKCS11DNSCryptoKeyEngine();
+
+    bool operator<(const PKCS11DNSCryptoKeyEngine& rhs) const
+    {
+      return false;
+    }
+    PKCS11DNSCryptoKeyEngine(const PKCS11DNSCryptoKeyEngine& orig);
+
+    string getName() const { return "P11 Kit PKCS#11"; };
+
+    void create(unsigned int bits);
+
+    storvector_t convertToISCVector() const;
+
+    std::string sign(const std::string& msg) const;
+
+    std::string hash(const std::string& msg) const;
+
+    bool verify(const std::string& msg, const std::string& signature) const;
+
+    std::string getPubKeyHash() const;
+
+    std::string getPublicKeyString() const;
+
+    int getBits() const;
+
+    void fromISCMap(DNSKEYRecordContent& drc, stormap_t& stormap) {
+      drc.d_algorithm = atoi(stormap["algorithm"].c_str());
+      d_engine = stormap["engine"];
+      d_slot_id = atoi(stormap["slot"].c_str());
+      d_pin = stormap["pin"];
+      d_label = stormap["label"];
+    };
+
+    void fromPEMString(DNSKEYRecordContent& drc, const std::string& raw) { throw "Unimplemented"; };
+    void fromPublicKeyString(const std::string& content) { throw "Unimplemented"; };
+
+    static DNSCryptoKeyEngine* maker(unsigned int algorithm);
+};
+