Blob Blame Raw
From e9ef042fc45d2004c99dd7642d5032fd5832b270 Mon Sep 17 00:00:00 2001
From: Tomas Hozza <thozza@redhat.com>
Date: Thu, 21 May 2015 10:52:03 +0200
Subject: [PATCH] native PKCS#11

Signed-off-by: Tomas Hozza <thozza@redhat.com>
---
 acconfig.h                                   |    7 +-
 bin/check/Makefile.in                        |    3 +-
 bin/dig/Makefile.in                          |   13 +-
 bin/dig/dighost.c                            |   11 +-
 bin/dnssec/Makefile.in                       |    3 +-
 bin/dnssec/dnssec-dsfromkey.c                |    9 +-
 bin/dnssec/dnssec-importkey.c                |   93 +-
 bin/dnssec/dnssec-importkey.docbook          |   58 +-
 bin/dnssec/dnssec-keyfromlabel.c             |   31 +-
 bin/dnssec/dnssec-keyfromlabel.docbook       |   43 +-
 bin/dnssec/dnssec-keygen.c                   |   21 +-
 bin/dnssec/dnssec-keygen.docbook             |   13 +-
 bin/dnssec/dnssec-revoke.c                   |   14 +-
 bin/dnssec/dnssec-revoke.docbook             |   11 +-
 bin/dnssec/dnssec-settime.c                  |   14 +-
 bin/dnssec/dnssec-settime.docbook            |   11 +-
 bin/dnssec/dnssec-signzone.c                 |   14 +-
 bin/dnssec/dnssec-signzone.docbook           |   15 +-
 bin/dnssec/dnssec-verify.c                   |   14 +-
 bin/dnssec/dnssec-verify.docbook             |   17 +
 bin/dnssec/dnssectool.c                      |    2 +-
 bin/named/Makefile.in                        |    2 +-
 bin/named/include/named/globals.h            |    4 +-
 bin/named/main.c                             |    6 +
 bin/named/named.docbook                      |   16 +-
 bin/named/server.c                           |    9 +-
 bin/nsupdate/Makefile.in                     |    6 +-
 bin/pkcs11/Makefile.in                       |   67 +-
 bin/pkcs11/pkcs11-destroy.8                  |   10 +-
 bin/pkcs11/pkcs11-destroy.c                  |  153 +-
 bin/pkcs11/pkcs11-destroy.docbook            |   19 +-
 bin/pkcs11/pkcs11-destroy.html               |   22 +-
 bin/pkcs11/pkcs11-keygen.8                   |   69 +-
 bin/pkcs11/pkcs11-keygen.c                   |  687 +++++++--
 bin/pkcs11/pkcs11-keygen.docbook             |  115 +-
 bin/pkcs11/pkcs11-keygen.html                |   90 +-
 bin/pkcs11/pkcs11-list.c                     |  118 +-
 bin/pkcs11/pkcs11-tokens.8                   |   51 +
 bin/pkcs11/pkcs11-tokens.c                   |  106 ++
 bin/pkcs11/pkcs11-tokens.docbook             |   86 ++
 bin/pkcs11/pkcs11-tokens.html                |   58 +
 bin/rndc/Makefile.in                         |    3 +-
 bin/tests/Makefile.in                        |   76 +-
 bin/tests/dst/dst_test.c                     |    7 +-
 bin/tests/dst/t_dst.c                        |   14 +-
 bin/tests/pkcs11/Makefile.in                 |   49 +
 bin/tests/pkcs11/benchmarks/Makefile.in      |   79 +
 bin/tests/pkcs11/benchmarks/create.c         |  260 ++++
 bin/tests/pkcs11/benchmarks/find.c           |  227 +++
 bin/tests/pkcs11/benchmarks/genrsa.c         |  295 ++++
 bin/tests/pkcs11/benchmarks/login.c          |  249 ++++
 bin/tests/pkcs11/benchmarks/privrsa.c        |  360 +++++
 bin/tests/pkcs11/benchmarks/pubrsa.c         |  281 ++++
 bin/tests/pkcs11/benchmarks/random.c         |  192 +++
 bin/tests/pkcs11/benchmarks/session.c        |  213 +++
 bin/tests/pkcs11/benchmarks/sha1.c           |  214 +++
 bin/tests/pkcs11/benchmarks/sign.c           |  368 +++++
 bin/tests/pkcs11/benchmarks/verify.c         |  292 ++++
 bin/tests/pkcs11/pkcs11-hmacmd5.c            |  332 +++++
 bin/tests/pkcs11/pkcs11-md5sum.c             |  235 +++
 bin/tests/system/autosign/prereq.sh          |    3 +-
 bin/tests/system/cleanpkcs11.sh              |    6 +-
 bin/tests/system/conf.sh.in                  |    8 +-
 bin/tests/system/dnssec/prereq.sh            |    3 +-
 bin/tests/system/ecdsa/prereq.sh.in          |   15 +-
 bin/tests/system/gost/prereq.sh.in           |   15 +-
 bin/tests/system/inline/clean.sh             |    2 +-
 bin/tests/system/metadata/prereq.sh          |    3 +-
 bin/tests/system/pending/prereq.sh           |   23 +-
 bin/tests/system/pkcs11/clean.sh             |    5 +-
 bin/tests/system/pkcs11/ns1/named.conf       |   10 +-
 bin/tests/system/pkcs11/setup.sh             |   64 +-
 bin/tests/system/pkcs11/tests.sh             |   72 +-
 bin/tests/system/pkcs11ssl/clean.sh          |   20 +
 bin/tests/system/pkcs11ssl/ns1/example.db.in |   29 +
 bin/tests/system/pkcs11ssl/ns1/named.conf    |   52 +
 bin/tests/system/pkcs11ssl/prereq.sh         |   34 +
 bin/tests/system/pkcs11ssl/setup.sh          |   46 +
 bin/tests/system/pkcs11ssl/tests.sh          |   71 +
 bin/tests/system/pkcs11ssl/usepkcs11         |    1 +
 bin/tests/system/rsabigexponent/Makefile.in  |    2 +-
 bin/tests/system/rsabigexponent/bigkey.c     |   18 +-
 bin/tests/system/rsabigexponent/prereq.sh    |    3 +-
 bin/tests/system/smartsign/prereq.sh         |    3 +-
 bin/tests/system/tkey/keycreate.c            |    1 +
 bin/tests/system/tkey/prereq.sh              |    3 +-
 config.h.in                                  |   19 +-
 configure.in                                 |  383 ++++-
 doc/arm/pkcs11.xml                           |  694 +++++----
 lib/dns/Makefile.in                          |   16 +-
 lib/dns/dnssec.c                             |   12 +-
 lib/dns/ds.c                                 |   44 +-
 lib/dns/dst_api.c                            |   73 +-
 lib/dns/dst_gost.h                           |   57 +
 lib/dns/dst_internal.h                       |   28 +-
 lib/dns/dst_parse.c                          |   36 +-
 lib/dns/dst_parse.h                          |    6 +-
 lib/dns/dst_pkcs11.h                         |   43 +
 lib/dns/dst_result.c                         |    3 +-
 lib/dns/gssapi_link.c                        |    1 +
 lib/dns/hmac_link.c                          |   47 +-
 lib/dns/include/dst/dst.h                    |   10 +
 lib/dns/include/dst/result.h                 |    3 +-
 lib/dns/openssldh_link.c                     |    7 +
 lib/dns/openssldsa_link.c                    |   36 +-
 lib/dns/opensslecdsa_link.c                  |   73 +-
 lib/dns/opensslgost_link.c                   |  180 ++-
 lib/dns/opensslrsa_link.c                    |   35 +-
 lib/dns/pkcs11.c                             |   50 +
 lib/dns/pkcs11dh_link.c                      | 1140 +++++++++++++++
 lib/dns/pkcs11dsa_link.c                     | 1130 +++++++++++++++
 lib/dns/pkcs11ecdsa_link.c                   | 1189 ++++++++++++++++
 lib/dns/pkcs11gost_link.c                    |  949 +++++++++++++
 lib/dns/pkcs11rsa_link.c                     | 1583 +++++++++++++++++++++
 lib/dns/rdata/generic/dlv_32769.c            |    9 +
 lib/dns/rdata/generic/ds_43.c                |   10 +
 lib/dns/tests/Makefile.in                    |   14 +-
 lib/dns/tests/gost_test.c                    |  232 +++
 lib/dns/tkey.c                               |   10 +-
 lib/dns/tsig.c                               |   14 +-
 lib/export/dns/Makefile.in                   |    4 +-
 lib/export/isc/Makefile.in                   |    5 +-
 lib/export/isc/unix/Makefile.in              |    4 +
 lib/isc/Makefile.in                          |   14 +-
 lib/isc/entropy.c                            |    8 +
 lib/isc/hmacmd5.c                            |  166 +++
 lib/isc/hmacsha.c                            |  375 +++++
 lib/isc/include/Makefile.in                  |    2 +-
 lib/isc/include/isc/hmacmd5.h                |    5 +
 lib/isc/include/isc/hmacsha.h                |    9 +
 lib/isc/include/isc/md5.h                    |    5 +
 lib/isc/include/isc/resultclass.h            |    2 +-
 lib/isc/include/isc/sha1.h                   |    5 +
 lib/isc/include/isc/sha2.h                   |    6 +
 lib/isc/include/pk11/Makefile.in             |   38 +
 lib/isc/include/pk11/constants.h             |  107 ++
 lib/isc/include/pk11/internal.h              |   46 +
 lib/isc/include/pk11/pk11.h                  |  295 ++++
 lib/isc/include/pk11/result.h                |   56 +
 lib/isc/include/pkcs11/Makefile.in           |   40 +
 lib/isc/include/pkcs11/pkcs11.h              |  299 ++++
 lib/isc/include/pkcs11/pkcs11f.h             |  912 ++++++++++++
 lib/isc/include/pkcs11/pkcs11t.h             | 1977 ++++++++++++++++++++++++++
 lib/isc/md5.c                                |   50 +
 lib/isc/pk11.c                               | 1327 +++++++++++++++++
 lib/isc/pk11_result.c                        |   85 ++
 lib/isc/sha1.c                               |   50 +-
 lib/isc/sha2.c                               |  279 ++++
 lib/isc/unix/Makefile.in                     |    4 +-
 lib/isc/unix/include/Makefile.in             |    2 +-
 lib/isc/unix/include/pkcs11/Makefile.in      |   33 +
 lib/isc/unix/include/pkcs11/cryptoki.h       |   66 +
 lib/isc/unix/pk11_api.c                      |  673 +++++++++
 153 files changed, 20271 insertions(+), 1183 deletions(-)
 create mode 100644 bin/pkcs11/pkcs11-tokens.8
 create mode 100644 bin/pkcs11/pkcs11-tokens.c
 create mode 100644 bin/pkcs11/pkcs11-tokens.docbook
 create mode 100644 bin/pkcs11/pkcs11-tokens.html
 create mode 100644 bin/tests/pkcs11/Makefile.in
 create mode 100644 bin/tests/pkcs11/benchmarks/Makefile.in
 create mode 100644 bin/tests/pkcs11/benchmarks/create.c
 create mode 100644 bin/tests/pkcs11/benchmarks/find.c
 create mode 100644 bin/tests/pkcs11/benchmarks/genrsa.c
 create mode 100644 bin/tests/pkcs11/benchmarks/login.c
 create mode 100644 bin/tests/pkcs11/benchmarks/privrsa.c
 create mode 100644 bin/tests/pkcs11/benchmarks/pubrsa.c
 create mode 100644 bin/tests/pkcs11/benchmarks/random.c
 create mode 100644 bin/tests/pkcs11/benchmarks/session.c
 create mode 100644 bin/tests/pkcs11/benchmarks/sha1.c
 create mode 100644 bin/tests/pkcs11/benchmarks/sign.c
 create mode 100644 bin/tests/pkcs11/benchmarks/verify.c
 create mode 100644 bin/tests/pkcs11/pkcs11-hmacmd5.c
 create mode 100644 bin/tests/pkcs11/pkcs11-md5sum.c
 create mode 100644 bin/tests/system/pkcs11ssl/clean.sh
 create mode 100644 bin/tests/system/pkcs11ssl/ns1/example.db.in
 create mode 100644 bin/tests/system/pkcs11ssl/ns1/named.conf
 create mode 100644 bin/tests/system/pkcs11ssl/prereq.sh
 create mode 100644 bin/tests/system/pkcs11ssl/setup.sh
 create mode 100644 bin/tests/system/pkcs11ssl/tests.sh
 create mode 100644 bin/tests/system/pkcs11ssl/usepkcs11
 create mode 100644 lib/dns/dst_gost.h
 create mode 100644 lib/dns/dst_pkcs11.h
 create mode 100644 lib/dns/pkcs11.c
 create mode 100644 lib/dns/pkcs11dh_link.c
 create mode 100644 lib/dns/pkcs11dsa_link.c
 create mode 100644 lib/dns/pkcs11ecdsa_link.c
 create mode 100644 lib/dns/pkcs11gost_link.c
 create mode 100644 lib/dns/pkcs11rsa_link.c
 create mode 100644 lib/dns/tests/gost_test.c
 create mode 100644 lib/isc/include/pk11/Makefile.in
 create mode 100644 lib/isc/include/pk11/constants.h
 create mode 100644 lib/isc/include/pk11/internal.h
 create mode 100644 lib/isc/include/pk11/pk11.h
 create mode 100644 lib/isc/include/pk11/result.h
 create mode 100644 lib/isc/include/pkcs11/Makefile.in
 create mode 100644 lib/isc/include/pkcs11/pkcs11.h
 create mode 100644 lib/isc/include/pkcs11/pkcs11f.h
 create mode 100644 lib/isc/include/pkcs11/pkcs11t.h
 create mode 100644 lib/isc/pk11.c
 create mode 100644 lib/isc/pk11_result.c
 create mode 100644 lib/isc/unix/include/pkcs11/Makefile.in
 create mode 100644 lib/isc/unix/include/pkcs11/cryptoki.h
 create mode 100644 lib/isc/unix/pk11_api.c

diff --git a/acconfig.h b/acconfig.h
index 3d412d9..c8e832a 100644
--- a/acconfig.h
+++ b/acconfig.h
@@ -132,14 +132,11 @@ int sigwait(const unsigned int *set, int *sig);
 /** define if you have strerror in the C library. */
 #undef HAVE_STRERROR
 
-/** Define if you are running under Compaq TruCluster. */
-#undef HAVE_TRUCLUSTER
-
 /* Define if OpenSSL includes DSA support */
 #undef HAVE_OPENSSL_DSA
 
-/* Define if OpenSSL includes ECDSA support */
-#undef HAVE_OPENSSL_ECDSA
+/* Define if you have getpassphrase in the C library. */
+#undef HAVE_GETPASSPHRASE
 
 /* Define to the length type used by the socket API (socklen_t, size_t, int). */
 #undef ISC_SOCKADDR_LEN_T
diff --git a/bin/check/Makefile.in b/bin/check/Makefile.in
index c191605..d585480 100644
--- a/bin/check/Makefile.in
+++ b/bin/check/Makefile.in
@@ -75,7 +75,8 @@ named-checkconf@EXEEXT@: named-checkconf.@O@ check-tool.@O@ ${ISCDEPLIBS} \
 	export LIBS0="${BIND9LIBS} ${ISCCFGLIBS} ${DNSLIBS}"; \
 	${FINALBUILDCMD}
 
-named-checkzone@EXEEXT@: named-checkzone.@O@ check-tool.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+named-checkzone@EXEEXT@: named-checkzone.@O@ check-tool.@O@ \
+		${ISCDEPLIBS} ${DNSDEPLIBS}
 	export BASEOBJS="named-checkzone.@O@ check-tool.@O@"; \
 	export LIBS0="${ISCCFGLIBS} ${DNSLIBS}"; \
 	${FINALBUILDCMD}
diff --git a/bin/dig/Makefile.in b/bin/dig/Makefile.in
index 3864e06..43dd061 100644
--- a/bin/dig/Makefile.in
+++ b/bin/dig/Makefile.in
@@ -28,7 +28,7 @@ READLINE_LIB = @READLINE_LIB@
 CINCLUDES =	-I${srcdir}/include ${DNS_INCLUDES} ${BIND9_INCLUDES} \
 		${ISC_INCLUDES} ${LWRES_INCLUDES} ${ISCCFG_INCLUDES}
 
-CDEFINES =	-DVERSION=\"${VERSION}\"
+CDEFINES =	-DVERSION=\"${VERSION}\" @CRYPTO@
 CWARNINGS =
 
 ISCCFGLIBS =	../../lib/isccfg/libisccfg.@A@
@@ -44,13 +44,13 @@ BIND9DEPLIBS =	../../lib/bind9/libbind9.@A@
 ISCDEPLIBS =	../../lib/isc/libisc.@A@
 LWRESDEPLIBS =	../../lib/lwres/liblwres.@A@
 
-DEPLIBS =	${DNSDEPLIBS} ${BIND9DEPLIBS} ${ISCDEPLIBS} ${ISCCFGDEPLIBS} \
-		${LWRESDEPLIBS}
+DEPLIBS =	${DNSDEPLIBS} ${BIND9DEPLIBS} ${ISCDEPLIBS} \
+		${ISCCFGDEPLIBS} ${LWRESDEPLIBS}
 
-LIBS =		${LWRESLIBS} ${DNSLIBS} ${BIND9LIBS} ${ISCCFGLIBS} \
+LIBS =		${LWRESLIBS} ${BIND9LIBS} ${ISCCFGLIBS} \
 		${ISCLIBS} @IDNLIBS@ @LIBS@ -lidn
 
-NOSYMLIBS =	${LWRESLIBS} ${DNSLIBS} ${BIND9LIBS} ${ISCCFGLIBS} \
+NOSYMLIBS =	${LWRESLIBS} ${BIND9LIBS} ${ISCCFGLIBS} \
 		${ISCNOSYMLIBS} @IDNLIBS@ @LIBS@ -lidn
 
 SUBDIRS =
@@ -75,14 +75,17 @@ EXT_CFLAGS = -DWITH_LIBIDN
 
 dig@EXEEXT@: dig.@O@ dighost.@O@ ${UOBJS} ${DEPLIBS}
 	export BASEOBJS="dig.@O@ dighost.@O@ ${UOBJS}"; \
+	export LIBS0="${DNSLIBS}"; \
 	${FINALBUILDCMD}
 
 host@EXEEXT@: host.@O@ dighost.@O@ ${UOBJS} ${DEPLIBS}
 	export BASEOBJS="host.@O@ dighost.@O@ ${UOBJS}"; \
+	export LIBS0="${DNSLIBS}"; \
 	${FINALBUILDCMD}
 
 nslookup@EXEEXT@: nslookup.@O@ dighost.@O@ ${UOBJS} ${DEPLIBS}
 	export BASEOBJS="nslookup.@O@ dighost.@O@ ${READLINE_LIB} ${UOBJS}"; \
+	export LIBS0="${DNSLIBS}"; \
 	${FINALBUILDCMD}
 
 doc man:: ${MANOBJS}
diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c
index e2bb110..5c03d95 100644
--- a/bin/dig/dighost.c
+++ b/bin/dig/dighost.c
@@ -105,6 +105,10 @@
 
 #include <dig/dig.h>
 
+#ifdef PKCS11CRYPTO
+#include <pk11/result.h>
+#endif
+
 #if ! defined(NS_INADDRSZ)
 #define NS_INADDRSZ	 4
 #endif
@@ -1347,6 +1351,11 @@ setup_libs(void) {
 
 	debug("setup_libs()");
 
+#ifdef PKCS11CRYPTO
+	pk11_result_register();
+#endif
+	dns_result_register();
+
 	result = isc_net_probeipv4();
 	if (result == ISC_R_SUCCESS)
 		have_ipv4 = ISC_TRUE;
@@ -1403,8 +1412,6 @@ setup_libs(void) {
 
 	result = isc_mutex_init(&lookup_lock);
 	check_result(result, "isc_mutex_init");
-
-	dns_result_register();
 }
 
 /*%
diff --git a/bin/dnssec/Makefile.in b/bin/dnssec/Makefile.in
index ecb0fae..64e1846 100644
--- a/bin/dnssec/Makefile.in
+++ b/bin/dnssec/Makefile.in
@@ -25,7 +25,8 @@ top_srcdir =	@top_srcdir@
 
 CINCLUDES =	${DNS_INCLUDES} ${ISC_INCLUDES}
 
-CDEFINES =	-DVERSION=\"${VERSION}\" @USE_PKCS11@
+CDEFINES =	-DVERSION=\"${VERSION}\" @USE_PKCS11@ @PKCS11_ENGINE@ \
+		@CRYPTO@ -DPK11_LIB_LOCATION=\"@PKCS11_PROVIDER@\"
 CWARNINGS =
 
 DNSLIBS =	../../lib/dns/libdns.@A@ @DNS_CRYPTO_LIBS@
diff --git a/bin/dnssec/dnssec-dsfromkey.c b/bin/dnssec/dnssec-dsfromkey.c
index bfedae8..df616ac 100644
--- a/bin/dnssec/dnssec-dsfromkey.c
+++ b/bin/dnssec/dnssec-dsfromkey.c
@@ -49,6 +49,10 @@
 
 #include <dst/dst.h>
 
+#ifdef PKCS11CRYPTO
+#include <pk11/result.h>
+#endif
+
 #include "dnssectool.h"
 
 #ifndef PATH_MAX
@@ -370,6 +374,9 @@ main(int argc, char **argv) {
 	if (result != ISC_R_SUCCESS)
 		fatal("out of memory");
 
+#ifdef PKCS11CRYPTO
+	pk11_result_register();
+#endif
 	dns_result_register();
 
 	isc_commandline_errprint = ISC_FALSE;
@@ -448,7 +455,7 @@ main(int argc, char **argv) {
 		else if (strcasecmp(algname, "SHA256") == 0 ||
 			 strcasecmp(algname, "SHA-256") == 0)
 			dtype = DNS_DSDIGEST_SHA256;
-#ifdef HAVE_OPENSSL_GOST
+#if defined(HAVE_OPENSSL_GOST) || defined(HAVE_PKCS11_GOST)
 		else if (strcasecmp(algname, "GOST") == 0)
 			dtype = DNS_DSDIGEST_GOST;
 #endif
diff --git a/bin/dnssec/dnssec-importkey.c b/bin/dnssec/dnssec-importkey.c
index 00f9200..73166c4 100644
--- a/bin/dnssec/dnssec-importkey.c
+++ b/bin/dnssec/dnssec-importkey.c
@@ -47,6 +47,10 @@
 
 #include <dst/dst.h>
 
+#ifdef PKCS11CRYPTO
+#include <pk11/result.h>
+#endif
+
 #include "dnssectool.h"
 
 #ifndef PATH_MAX
@@ -61,7 +65,9 @@ static dns_fixedname_t	fixed;
 static dns_name_t	*name = NULL;
 static isc_mem_t	*mctx = NULL;
 static isc_boolean_t	setpub = ISC_FALSE, setdel = ISC_FALSE;
+static isc_boolean_t	setttl = ISC_FALSE;
 static isc_stdtime_t	pub = 0, del = 0;
+static dns_ttl_t	ttl = 0;
 
 static isc_result_t
 initname(char *setname) {
@@ -190,9 +196,10 @@ static void
 emit(const char *dir, dns_rdata_t *rdata) {
 	isc_result_t result;
 	char keystr[DST_KEY_FORMATSIZE];
-	char newname[1024];
+	char pubname[1024];
+	char priname[1024];
 	isc_buffer_t buf;
-	dst_key_t *key = NULL;
+	dst_key_t *key = NULL, *tmp = NULL;
 
 	isc_buffer_init(&buf, rdata->data, rdata->length);
 	isc_buffer_add(&buf, rdata->length);
@@ -201,18 +208,36 @@ emit(const char *dir, dns_rdata_t *rdata) {
 		fatal("dst_key_fromdns: %s", isc_result_totext(result));
 	}
 
-	dst_key_setexternal(key, ISC_TRUE);
-	if (setpub)
-		dst_key_settime(key, DST_TIME_PUBLISH, pub);
-	if (setdel)
-		dst_key_settime(key, DST_TIME_DELETE, del);
-
-	isc_buffer_init(&buf, newname, sizeof(newname));
+	isc_buffer_init(&buf, pubname, sizeof(pubname));
 	result = dst_key_buildfilename(key, DST_TYPE_PUBLIC, dir, &buf);
 	if (result != ISC_R_SUCCESS) {
 		fatal("Failed to build public key filename: %s",
 		      isc_result_totext(result));
 	}
+	isc_buffer_init(&buf, priname, sizeof(priname));
+	result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, dir, &buf);
+	if (result != ISC_R_SUCCESS) {
+		fatal("Failed to build private key filename: %s",
+		      isc_result_totext(result));
+	}
+
+	result = dst_key_fromfile(dst_key_name(key), dst_key_id(key),
+				  dst_key_alg(key),
+				  DST_TYPE_PUBLIC | DST_TYPE_PRIVATE,
+				  dir, mctx, &tmp);
+	if (result == ISC_R_SUCCESS) {
+		if (dst_key_isprivate(tmp) && !dst_key_isexternal(tmp))
+			fatal("Private key already exists in %s", priname);
+		dst_key_free(&tmp);
+	}
+
+	dst_key_setexternal(key, ISC_TRUE);
+	if (setpub)
+		dst_key_settime(key, DST_TIME_PUBLISH, pub);
+	if (setdel)
+		dst_key_settime(key, DST_TIME_DELETE, del);
+	if (setttl)
+		dst_key_setttl(key, ttl);
 
 	result = dst_key_tofile(key, DST_TYPE_PUBLIC|DST_TYPE_PRIVATE,
 				dir);
@@ -221,8 +246,7 @@ emit(const char *dir, dns_rdata_t *rdata) {
 		fatal("Failed to write key %s: %s", keystr,
 		      isc_result_totext(result));
 	}
-
-	printf("%s\n", newname);
+	printf("%s\n", pubname);
 
 	isc_buffer_clear(&buf);
 	result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, dir, &buf);
@@ -230,7 +254,7 @@ emit(const char *dir, dns_rdata_t *rdata) {
 		fatal("Failed to build private key filename: %s",
 		      isc_result_totext(result));
 	}
-	printf("%s\n", newname);
+	printf("%s\n", priname);
 	dst_key_free(&key);
 }
 
@@ -240,13 +264,21 @@ usage(void) ISC_PLATFORM_NORETURN_POST;
 static void
 usage(void) {
 	fprintf(stderr, "Usage:\n");
-	fprintf(stderr,	"    %s options [-K dir] file\n\n", program);
+	fprintf(stderr,	"    %s options [-K dir] keyfile\n\n", program);
+	fprintf(stderr, "    %s options -f file [keyname]\n\n", program);
 	fprintf(stderr, "Version: %s\n", VERSION);
 	fprintf(stderr, "Options:\n");
-	fprintf(stderr, "    -v <verbose level>\n");
+	fprintf(stderr, "    -f file: read key from zone file\n");
 	fprintf(stderr, "    -K <directory>: directory in which to store "
-			"the keyset files\n");
-	fprintf(stderr, "    -f file: read keyset from zone file\n");
+				"the key files\n");
+	fprintf(stderr, "    -L ttl:             set default key TTL\n");
+	fprintf(stderr, "    -v <verbose level>\n");
+	fprintf(stderr, "    -h: print usage and exit\n");
+	fprintf(stderr, "Timing options:\n");
+	fprintf(stderr, "    -P date/[+-]offset/none: set/unset key "
+						     "publication date\n");
+	fprintf(stderr, "    -D date/[+-]offset/none: set/unset key "
+						     "deletion date\n");
 
 	exit (-1);
 }
@@ -274,15 +306,19 @@ main(int argc, char **argv) {
 	if (result != ISC_R_SUCCESS)
 		fatal("out of memory");
 
+#ifdef PKCS11CRYPTO
+	pk11_result_register();
+#endif
 	dns_result_register();
 
 	isc_commandline_errprint = ISC_FALSE;
 
-	while ((ch = isc_commandline_parse(argc, argv, "D:f:hK:P:v:")) != -1) {
+#define CMDLINE_FLAGS "D:f:hK:L:P:v:"
+	while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
 		switch (ch) {
-                case 'D':
-                        if (setdel)
-                                fatal("-D specified more than once");
+		case 'D':
+			if (setdel)
+				fatal("-D specified more than once");
 
 			setdel = ISC_TRUE;
 			del = strtotime(isc_commandline_argument, now, now);
@@ -292,9 +328,16 @@ main(int argc, char **argv) {
 			if (strlen(dir) == 0U)
 				fatal("directory must be non-empty string");
 			break;
-                case 'P':
-                        if (setpub)
-                                fatal("-P specified more than once");
+		case 'L':
+			if (strcmp(isc_commandline_argument, "none") == 0)
+				ttl = 0;
+			else
+				ttl = strtottl(isc_commandline_argument);
+			setttl = ISC_TRUE;
+			break;
+		case 'P':
+			if (setpub)
+				fatal("-P specified more than once");
 
 			setpub = ISC_TRUE;
 			pub = strtotime(isc_commandline_argument, now, now);
@@ -346,8 +389,8 @@ main(int argc, char **argv) {
 	dns_rdataset_init(&rdataset);
 
 	if (filename != NULL) {
-		if (argc < isc_commandline_index + 1 && filename != NULL) {
-			/* using zone name as the zone file name */
+		if (argc < isc_commandline_index + 1) {
+			/* using filename as zone name */
 			namestr = filename;
 		} else
 			namestr = argv[isc_commandline_index];
diff --git a/bin/dnssec/dnssec-importkey.docbook b/bin/dnssec/dnssec-importkey.docbook
index b8d160c..853db30 100644
--- a/bin/dnssec/dnssec-importkey.docbook
+++ b/bin/dnssec/dnssec-importkey.docbook
@@ -44,22 +44,45 @@
   <refsynopsisdiv>
     <cmdsynopsis>
       <command>dnssec-importkey</command>
-      <arg><option>-f <replaceable class="parameter">filename</replaceable></option></arg>
       <arg><option>-K <replaceable class="parameter">directory</replaceable></option></arg>
+      <arg><option>-L <replaceable class="parameter">ttl</replaceable></option></arg>
       <arg><option>-P <replaceable class="parameter">date/offset</replaceable></option></arg>
       <arg><option>-D <replaceable class="parameter">date/offset</replaceable></option></arg>
       <arg><option>-h</option></arg>
       <arg><option>-v <replaceable class="parameter">level</replaceable></option></arg>
-      <arg><option>keyname</option></arg>
+      <arg choice="req"><option>keyfile</option></arg>
+    </cmdsynopsis>
+    <cmdsynopsis>
+      <command>dnssec-importkey</command>
+      <arg choice="req"><option>-f <replaceable class="parameter">filename</replaceable></option></arg>
+      <arg><option>-K <replaceable class="parameter">directory</replaceable></option></arg>
+      <arg><option>-L <replaceable class="parameter">ttl</replaceable></option></arg>
+      <arg><option>-P <replaceable class="parameter">date/offset</replaceable></option></arg>
+      <arg><option>-D <replaceable class="parameter">date/offset</replaceable></option></arg>
+      <arg><option>-h</option></arg>
+      <arg><option>-v <replaceable class="parameter">level</replaceable></option></arg>
+      <arg><option>dnsname</option></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
 
   <refsect1>
     <title>DESCRIPTION</title>
     <para><command>dnssec-importkey</command>
-      read a DNSKEY record and generated a .key/.private key pair.
-      Publication (<option>-P</option>) and deletions (<option>-D</option>)
-      times can be set for the key.
+      reads a public DNSKEY record and generates a pair of
+      .key/.private files.  The DNSKEY record may be read from an
+      existing .key file, in which case a corresponding .private file
+      will be generated, or it may be read from any other file or
+      from the standard input, in which case both .key and .private
+      files will be generated.
+    </para>
+    <para>
+      The newly-created .private file does <emphasis>not</command>
+      contain private key data, and cannot be used for signing.
+      However, having a .private file makes it possible to set
+      publication (<option>-P</option>) and deletion
+      (<option>-D</option>) times for the key, which means the
+      public key can be added to and removed from the DNSKEY RRset
+      on schedule even if the true private key is stored offline.
     </para>
   </refsect1>
 
@@ -70,9 +93,16 @@
       <varlistentry>
 	<term>-f <replaceable class="parameter">filename</replaceable></term>
         <listitem>
-	  <para>
-	    Filename to read the key from.
-	  </para>
+          <para>
+            Zone file mode: instead of a public keyfile name, the argument
+	    is the DNS domain name of a zone master file, which can be read
+            from <option>file</option>.  If the domain name is the same as
+            <option>file</option>, then it may be omitted.
+          </para>
+          <para>
+            If <option>file</option> is set to <literal>"-"</literal>, then
+            the zone data is read from the standard input.
+          </para>
         </listitem>
       </varlistentry>
   
@@ -93,7 +123,7 @@
             into a DNSKEY RR.  If the key is imported into a zone,
             this is the TTL that will be used for it, unless there was
             already a DNSKEY RRset in place, in which case the existing TTL
-            would take precedence.  importkey the default TTL to
+            would take precedence.  Setting the default TTL to
             <literal>0</literal> or <literal>none</literal> removes it.
           </para>
         </listitem>
@@ -160,6 +190,16 @@
   </refsect1>
 
   <refsect1>
+    <title>FILES</title>
+    <para>
+      A keyfile can be designed by the key identification
+      <filename>Knnnn.+aaa+iiiii</filename> or the full file name
+      <filename>Knnnn.+aaa+iiiii.key</filename> as generated by
+      <refentrytitle>dnssec-keygen</refentrytitle><manvolnum>8</manvolnum>.
+    </para>
+  </refsect1>
+
+  <refsect1>
     <title>SEE ALSO</title>
     <para><citerefentry>
         <refentrytitle>dnssec-keygen</refentrytitle><manvolnum>8</manvolnum>
diff --git a/bin/dnssec/dnssec-keyfromlabel.c b/bin/dnssec/dnssec-keyfromlabel.c
index 3ad00d7..cc212e1 100644
--- a/bin/dnssec/dnssec-keyfromlabel.c
+++ b/bin/dnssec/dnssec-keyfromlabel.c
@@ -43,6 +43,10 @@
 
 #include <dst/dst.h>
 
+#ifdef PKCS11CRYPTO
+#include <pk11/result.h>
+#endif
+
 #include "dnssectool.h"
 
 #define MAX_RSA 4096 /* should be long enough... */
@@ -76,10 +80,15 @@ usage(void) {
 			       "NSEC3RSASHA1 if using -3)\n");
 	fprintf(stderr, "    -3: use NSEC3-capable algorithm\n");
 	fprintf(stderr, "    -c class (default: IN)\n");
-#ifdef USE_PKCS11
-	fprintf(stderr, "    -E enginename (default: pkcs11)\n");
+	fprintf(stderr, "    -E <engine>:\n");
+#if defined(PKCS11CRYPTO)
+	fprintf(stderr, "        path to PKCS#11 provider library "
+				"(default is %s)\n", PK11_LIB_LOCATION);
+#elif defined(USE_PKCS11)
+	fprintf(stderr, "        name of an OpenSSL engine to use "
+				"(default is \"pkcs11\")\n");
 #else
-	fprintf(stderr, "    -E enginename\n");
+	fprintf(stderr, "        name of an OpenSSL engine to use\n");
 #endif
 	fprintf(stderr, "    -f keyflag: KSK | REVOKE\n");
 	fprintf(stderr, "    -K directory: directory in which to place "
@@ -116,7 +125,7 @@ main(int argc, char **argv) {
 	char		*nametype = NULL, *type = NULL;
 	const char	*directory = NULL;
 #ifdef USE_PKCS11
-	const char	*engine = "pkcs11";
+	const char	*engine = PKCS11_ENGINE;
 #else
 	const char	*engine = NULL;
 #endif
@@ -161,6 +170,9 @@ main(int argc, char **argv) {
 
 	RUNTIME_CHECK(isc_mem_create(0, 0, &mctx) == ISC_R_SUCCESS);
 
+#ifdef PKCS11CRYPTO
+	pk11_result_register();
+#endif
 	dns_result_register();
 
 	isc_commandline_errprint = ISC_FALSE;
@@ -334,16 +346,15 @@ main(int argc, char **argv) {
 	if (argc > isc_commandline_index + 1)
 		fatal("extraneous arguments");
 
-	if (strchr(label, ':') == NULL &&
-	    engine != NULL && strlen(engine) != 0U) {
+	if (strchr(label, ':') == NULL) {
 		char *l;
 		int len;
 
-		len = strlen(label) + strlen(engine) + 2;
+		len = strlen(label) + 8;
 		l = isc_mem_allocate(mctx, len);
 		if (l == NULL)
 			fatal("cannot allocate memory");
-		snprintf(l, len, "%s:%s", engine, label);
+		snprintf(l, len, "pkcs11:%s", label);
 		isc_mem_free(mctx, label);
 		label = l;
 	}
@@ -460,7 +471,7 @@ main(int argc, char **argv) {
 
 	/* associate the key */
 	ret = dst_key_fromlabel(name, alg, flags, protocol,
-				rdclass, engine, label, NULL, mctx, &key);
+				rdclass, "pkcs11", label, NULL, mctx, &key);
 	isc_entropy_stopcallbacksources(ectx);
 
 	if (ret != ISC_R_SUCCESS) {
@@ -468,7 +479,7 @@ main(int argc, char **argv) {
 		char algstr[DNS_SECALG_FORMATSIZE];
 		dns_name_format(name, namestr, sizeof(namestr));
 		dns_secalg_format(alg, algstr, sizeof(algstr));
-		fatal("failed to get key %s/%s: %s\n",
+		fatal("failed to get key %s/%s: %s",
 		      namestr, algstr, isc_result_totext(ret));
 		/* NOTREACHED */
 		exit(-1);
diff --git a/bin/dnssec/dnssec-keyfromlabel.docbook b/bin/dnssec/dnssec-keyfromlabel.docbook
index 0dd3c0e..c48a100 100644
--- a/bin/dnssec/dnssec-keyfromlabel.docbook
+++ b/bin/dnssec/dnssec-keyfromlabel.docbook
@@ -133,8 +133,15 @@
         <term>-E <replaceable class="parameter">engine</replaceable></term>
         <listitem>
           <para>
-            Specifies the name of the crypto hardware (OpenSSL engine).
-            When compiled with PKCS#11 support it defaults to "pkcs11".
+            Specifies the cryptographic hardware to use.
+          </para>
+          <para>
+            When BIND is built with OpenSSL PKCS#11 support, this defaults
+            to the string "pkcs11", which identifies an OpenSSL engine
+            that can drive a cryptographic accelerator or hardware service
+            module.  When BIND is built with native PKCS#11 cryptography
+            (--enable-native-pkcs11), it defaults to the path of the PKCS#11
+            provider library specified via "--with-pkcs11".
           </para>
         </listitem>
       </varlistentry>
@@ -143,9 +150,32 @@
         <term>-l <replaceable class="parameter">label</replaceable></term>
         <listitem>
           <para>
-            Specifies the label of the key pair in the crypto hardware.
-            The label may be preceded by an optional OpenSSL engine name,
-            separated by a colon, as in "pkcs11:keylabel".
+            Specifies the label for a key pair in the crypto hardware.
+          </para>
+          <para>
+            When <acronym>BIND</acronym> 9 is built with OpenSSL-based
+            PKCS#11 support, the label is an arbitrary string that
+            identifies a particular key.  It may be preceded by an
+            optional OpenSSL engine name, followed by a colon, as in
+            "pkcs11:<replaceable>keylabel<replaceable>".
+          </para>
+          <para>
+            When <acronym>BIND</acronym> 9 is built with native PKCS#11
+            support, the label is a PKCS#11 URI string in the format
+            "pkcs11:<option>keyword</option>=<replaceable>value</replaceable><optional>;<option>keyword</option>=<replaceable>value</replaceable>;...</optional>"
+            Keywords include "token", which identifies the HSM; "object", which
+            identifies the key; and "pin-source", which identifies a file from
+            which the HSM's PIN code can be obtained.  The label will be
+            stored in the on-disk "private" file.
+          </para>
+          <para>
+            If the label contains a
+            <option>pin-source</option> field, tools using the generated
+            key files will be able to use the HSM for signing and other
+            operations without any need for an operator to manually enter
+            a PIN.  Note: Making the HSM's PIN accessible in this manner
+            may reduce the security advantage of using an HSM; be sure
+            this is what you want to do before making use of this feature.
           </para>
         </listitem>
       </varlistentry>
@@ -429,7 +459,8 @@
         <refentrytitle>dnssec-signzone</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       <citetitle>BIND 9 Administrator Reference Manual</citetitle>,
-      <citetitle>RFC 4034</citetitle>.
+      <citetitle>RFC 4034</citetitle>,
+      <citetitle>The PKCS#11 URI Scheme (draft-pechanec-pkcs11uri-13)</citetitle>.
     </para>
   </refsect1>
 
diff --git a/bin/dnssec/dnssec-keygen.c b/bin/dnssec/dnssec-keygen.c
index 7061829..97b96ee 100644
--- a/bin/dnssec/dnssec-keygen.c
+++ b/bin/dnssec/dnssec-keygen.c
@@ -58,6 +58,10 @@
 
 #include <dst/dst.h>
 
+#ifdef PKCS11CRYPTO
+#include <pk11/result.h>
+#endif
+
 #include "dnssectool.h"
 
 #define MAX_RSA 4096 /* should be long enough... */
@@ -119,10 +123,15 @@ usage(void) {
 	fprintf(stderr, "        (DNSKEY generation defaults to ZONE)\n");
 	fprintf(stderr, "    -c <class>: (default: IN)\n");
 	fprintf(stderr, "    -d <digest bits> (0 => max, default)\n");
-#ifdef USE_PKCS11
-	fprintf(stderr, "    -E <engine name> (default \"pkcs11\")\n");
+	fprintf(stderr, "    -E <engine>:\n");
+#if defined(PKCS11CRYPTO)
+	fprintf(stderr, "        path to PKCS#11 provider library "
+				"(default is %s)\n", PK11_LIB_LOCATION);
+#elif defined(USE_PKCS11)
+	fprintf(stderr, "        name of an OpenSSL engine to use "
+				"(default is \"pkcs11\")\n");
 #else
-	fprintf(stderr, "    -E <engine name>\n");
+	fprintf(stderr, "        name of an OpenSSL engine to use\n");
 #endif
 	fprintf(stderr, "    -f <keyflag>: KSK | REVOKE\n");
 	fprintf(stderr, "    -g <generator>: use specified generator "
@@ -134,7 +143,6 @@ usage(void) {
 			"records with (default: 0)\n");
 	fprintf(stderr, "    -T <rrtype>: DNSKEY | KEY (default: DNSKEY; "
 			"use KEY for SIG(0))\n");
-	fprintf(stderr, "        ECCGOST:\tignored\n");
 	fprintf(stderr, "    -t <type>: "
 			"AUTHCONF | NOAUTHCONF | NOAUTH | NOCONF "
 			"(default: AUTHCONF)\n");
@@ -223,7 +231,7 @@ main(int argc, char **argv) {
 	isc_log_t	*log = NULL;
 	isc_entropy_t	*ectx = NULL;
 #ifdef USE_PKCS11
-	const char	*engine = "pkcs11";
+	const char	*engine = PKCS11_ENGINE;
 #else
 	const char	*engine = NULL;
 #endif
@@ -250,6 +258,9 @@ main(int argc, char **argv) {
 	if (argc == 1)
 		usage();
 
+#ifdef PKCS11CRYPTO
+	pk11_result_register();
+#endif
 	dns_result_register();
 
 	isc_commandline_errprint = ISC_FALSE;
diff --git a/bin/dnssec/dnssec-keygen.docbook b/bin/dnssec/dnssec-keygen.docbook
index bc50c02..4c693eb 100644
--- a/bin/dnssec/dnssec-keygen.docbook
+++ b/bin/dnssec/dnssec-keygen.docbook
@@ -224,10 +224,15 @@
         <term>-E <replaceable class="parameter">engine</replaceable></term>
         <listitem>
           <para>
-            Uses a crypto hardware (OpenSSL engine) for random number
-            and, when supported, key generation. When compiled with PKCS#11
-            support it defaults to pkcs11; the empty name resets it to
-            no engine.
+            Specifies the cryptographic hardware to use, when applicable.
+          </para>
+          <para>
+            When BIND is built with OpenSSL PKCS#11 support, this defaults
+            to the string "pkcs11", which identifies an OpenSSL engine
+            that can drive a cryptographic accelerator or hardware service
+            module.  When BIND is built with native PKCS#11 cryptography
+            (--enable-native-pkcs11), it defaults to the path of the PKCS#11
+            provider library specified via "--with-pkcs11".
           </para>
         </listitem>
       </varlistentry>
diff --git a/bin/dnssec/dnssec-revoke.c b/bin/dnssec/dnssec-revoke.c
index 7b11581..7b5aaee 100644
--- a/bin/dnssec/dnssec-revoke.c
+++ b/bin/dnssec/dnssec-revoke.c
@@ -38,6 +38,10 @@
 
 #include <dst/dst.h>
 
+#ifdef PKCS11CRYPTO
+#include <pk11/result.h>
+#endif
+
 #include "dnssectool.h"
 
 const char *program = "dnssec-revoke";
@@ -53,7 +57,10 @@ usage(void) {
 	fprintf(stderr, "Usage:\n");
 	fprintf(stderr,	"    %s [options] keyfile\n\n", program);
 	fprintf(stderr, "Version: %s\n", VERSION);
-#ifdef USE_PKCS11
+#if defined(PKCS11CRYPTO)
+	fprintf(stderr, "    -E engine:    specify PKCS#11 provider "
+					"(default: %s)\n", PK11_LIB_LOCATION);
+#elif defined(USE_PKCS11)
 	fprintf(stderr, "    -E engine:    specify OpenSSL engine "
 					   "(default \"pkcs11\")\n");
 #else
@@ -76,7 +83,7 @@ int
 main(int argc, char **argv) {
 	isc_result_t result;
 #ifdef USE_PKCS11
-	const char *engine = "pkcs11";
+	const char *engine = PKCS11_ENGINE;
 #else
 	const char *engine = NULL;
 #endif
@@ -100,6 +107,9 @@ main(int argc, char **argv) {
 	if (result != ISC_R_SUCCESS)
 		fatal("Out of memory");
 
+#ifdef PKCS11CRYPTO
+	pk11_result_register();
+#endif
 	dns_result_register();
 
 	isc_commandline_errprint = ISC_FALSE;
diff --git a/bin/dnssec/dnssec-revoke.docbook b/bin/dnssec/dnssec-revoke.docbook
index 4062f5e..0c1dd4e 100644
--- a/bin/dnssec/dnssec-revoke.docbook
+++ b/bin/dnssec/dnssec-revoke.docbook
@@ -109,8 +109,15 @@
         <term>-E <replaceable class="parameter">engine</replaceable></term>
         <listitem>
           <para>
-            Use the given OpenSSL engine. When compiled with PKCS#11 support
-            it defaults to pkcs11; the empty name resets it to no engine.
+            Specifies the cryptographic hardware to use, when applicable.
+          </para>
+          <para>
+            When BIND is built with OpenSSL PKCS#11 support, this defaults
+            to the string "pkcs11", which identifies an OpenSSL engine
+            that can drive a cryptographic accelerator or hardware service
+            module.  When BIND is built with native PKCS#11 cryptography
+            (--enable-native-pkcs11), it defaults to the path of the PKCS#11
+            provider library specified via "--with-pkcs11".
           </para>
         </listitem>
       </varlistentry>
diff --git a/bin/dnssec/dnssec-settime.c b/bin/dnssec/dnssec-settime.c
index 108d803..c71cac7 100644
--- a/bin/dnssec/dnssec-settime.c
+++ b/bin/dnssec/dnssec-settime.c
@@ -41,6 +41,10 @@
 
 #include <dst/dst.h>
 
+#ifdef PKCS11CRYPTO
+#include <pk11/result.h>
+#endif
+
 #include "dnssectool.h"
 
 const char *program = "dnssec-settime";
@@ -57,7 +61,10 @@ usage(void) {
 	fprintf(stderr,	"    %s [options] keyfile\n\n", program);
 	fprintf(stderr, "Version: %s\n", VERSION);
 	fprintf(stderr, "General options:\n");
-#ifdef USE_PKCS11
+#if defined(PKCS11CRYPTO)
+	fprintf(stderr, "    -E engine:          specify PKCS#11 provider "
+					"(default: %s)\n", PK11_LIB_LOCATION);
+#elif defined(USE_PKCS11)
 	fprintf(stderr, "    -E engine:          specify OpenSSL engine "
 						 "(default \"pkcs11\")\n");
 #else
@@ -119,7 +126,7 @@ int
 main(int argc, char **argv) {
 	isc_result_t	result;
 #ifdef USE_PKCS11
-	const char	*engine = "pkcs11";
+	const char	*engine = PKCS11_ENGINE;
 #else
 	const char	*engine = NULL;
 #endif
@@ -165,6 +172,9 @@ main(int argc, char **argv) {
 
 	setup_logging(verbose, mctx, &log);
 
+#ifdef PKCS11CRYPTO
+	pk11_result_register();
+#endif
 	dns_result_register();
 
 	isc_commandline_errprint = ISC_FALSE;
diff --git a/bin/dnssec/dnssec-settime.docbook b/bin/dnssec/dnssec-settime.docbook
index bc6870b..3540bf2 100644
--- a/bin/dnssec/dnssec-settime.docbook
+++ b/bin/dnssec/dnssec-settime.docbook
@@ -153,8 +153,15 @@
         <term>-E <replaceable class="parameter">engine</replaceable></term>
         <listitem>
           <para>
-            Use the given OpenSSL engine. When compiled with PKCS#11 support
-            it defaults to pkcs11; the empty name resets it to no engine.
+            Specifies the cryptographic hardware to use, when applicable.
+          </para>
+          <para>
+            When BIND is built with OpenSSL PKCS#11 support, this defaults
+            to the string "pkcs11", which identifies an OpenSSL engine
+            that can drive a cryptographic accelerator or hardware service
+            module.  When BIND is built with native PKCS#11 cryptography
+            (--enable-native-pkcs11), it defaults to the path of the PKCS#11
+            provider library specified via "--with-pkcs11".
           </para>
         </listitem>
       </varlistentry>
diff --git a/bin/dnssec/dnssec-signzone.c b/bin/dnssec/dnssec-signzone.c
index 83456a7..128c753 100644
--- a/bin/dnssec/dnssec-signzone.c
+++ b/bin/dnssec/dnssec-signzone.c
@@ -86,6 +86,10 @@
 
 #include <dst/dst.h>
 
+#ifdef PKCS11CRYPTO
+#include <pk11/result.h>
+#endif
+
 #include "dnssectool.h"
 
 #ifndef PATH_MAX
@@ -2938,7 +2942,10 @@ usage(void) {
 	fprintf(stderr, "verify generated signatures\n");
 	fprintf(stderr, "\t-c class (IN)\n");
 	fprintf(stderr, "\t-E engine:\n");
-#ifdef USE_PKCS11
+#if defined(PKCS11CRYPTO)
+	fprintf(stderr, "\t\tpath to PKCS#11 provider library "
+		"(default is %s)\n", PK11_LIB_LOCATION);
+#elif defined(USE_PKCS11)
 	fprintf(stderr, "\t\tname of an OpenSSL engine to use "
 				"(default is \"pkcs11\")\n");
 #else
@@ -3033,7 +3040,7 @@ main(int argc, char *argv[]) {
 	isc_log_t *log = NULL;
 	isc_boolean_t pseudorandom = ISC_FALSE;
 #ifdef USE_PKCS11
-	const char *engine = "pkcs11";
+	const char *engine = PKCS11_ENGINE;
 #else
 	const char *engine = NULL;
 #endif
@@ -3085,6 +3092,9 @@ main(int argc, char *argv[]) {
 	if (result != ISC_R_SUCCESS)
 		fatal("out of memory");
 
+#ifdef PKCS11CRYPTO
+	pk11_result_register();
+#endif
 	dns_result_register();
 
 	isc_commandline_errprint = ISC_FALSE;
diff --git a/bin/dnssec/dnssec-signzone.docbook b/bin/dnssec/dnssec-signzone.docbook
index e427fc1..c46f43c 100644
--- a/bin/dnssec/dnssec-signzone.docbook
+++ b/bin/dnssec/dnssec-signzone.docbook
@@ -176,10 +176,17 @@
         <term>-E <replaceable class="parameter">engine</replaceable></term>
         <listitem>
           <para>
-            Uses a crypto hardware (OpenSSL engine) for the crypto operations
-            it supports, for instance signing with private keys from
-            a secure key store. When compiled with PKCS#11 support
-            it defaults to pkcs11; the empty name resets it to no engine.
+            When applicable, specifies the hardware to use for
+            cryptographic operations, such as a secure key store used
+            for signing.
+          </para>
+          <para>
+            When BIND is built with OpenSSL PKCS#11 support, this defaults
+            to the string "pkcs11", which identifies an OpenSSL engine
+            that can drive a cryptographic accelerator or hardware service
+            module.  When BIND is built with native PKCS#11 cryptography
+            (--enable-native-pkcs11), it defaults to the path of the PKCS#11
+            provider library specified via "--with-pkcs11".
           </para>
         </listitem>
       </varlistentry>
diff --git a/bin/dnssec/dnssec-verify.c b/bin/dnssec/dnssec-verify.c
index 682896c..817e380 100644
--- a/bin/dnssec/dnssec-verify.c
+++ b/bin/dnssec/dnssec-verify.c
@@ -69,6 +69,10 @@
 
 #include <dst/dst.h>
 
+#ifdef PKCS11CRYPTO
+#include <pk11/result.h>
+#endif
+
 #include "dnssectool.h"
 
 const char *program = "dnssec-verify";
@@ -137,7 +141,10 @@ usage(void) {
 	fprintf(stderr, "\t\tfile format of input zonefile (text)\n");
 	fprintf(stderr, "\t-c class (IN)\n");
 	fprintf(stderr, "\t-E engine:\n");
-#ifdef USE_PKCS11
+#if defined(PKCS11CRYPTO)
+	fprintf(stderr, "\t\tpath to PKCS#11 provider library "
+		"(default is %s)\n", PK11_LIB_LOCATION);
+#elif defined(USE_PKCS11)
 	fprintf(stderr, "\t\tname of an OpenSSL engine to use "
 				"(default is \"pkcs11\")\n");
 #else
@@ -156,7 +163,7 @@ main(int argc, char *argv[]) {
 	isc_result_t result;
 	isc_log_t *log = NULL;
 #ifdef USE_PKCS11
-	const char *engine = "pkcs11";
+	const char *engine = PKCS11_ENGINE;
 #else
 	const char *engine = NULL;
 #endif
@@ -195,6 +202,9 @@ main(int argc, char *argv[]) {
 	if (result != ISC_R_SUCCESS)
 		fatal("out of memory");
 
+#ifdef PKCS11CRYPTO
+	pk11_result_register();
+#endif
 	dns_result_register();
 
 	isc_commandline_errprint = ISC_FALSE;
diff --git a/bin/dnssec/dnssec-verify.docbook b/bin/dnssec/dnssec-verify.docbook
index 0835df1..875f3ed 100644
--- a/bin/dnssec/dnssec-verify.docbook
+++ b/bin/dnssec/dnssec-verify.docbook
@@ -78,6 +78,23 @@
       </varlistentry>
 
       <varlistentry>
+        <term>-E <replaceable class="parameter">engine</replaceable></term>
+        <listitem>
+          <para>
+            Specifies the cryptographic hardware to use, when applicable.
+          </para>
+          <para>
+            When BIND is built with OpenSSL PKCS#11 support, this defaults
+            to the string "pkcs11", which identifies an OpenSSL engine
+            that can drive a cryptographic accelerator or hardware service
+            module.  When BIND is built with native PKCS#11 cryptography
+            (--enable-native-pkcs11), it defaults to the path of the PKCS#11
+            provider library specified via "--with-pkcs11".
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term>-I <replaceable class="parameter">input-format</replaceable></term>
         <listitem>
           <para>
diff --git a/bin/dnssec/dnssectool.c b/bin/dnssec/dnssectool.c
index 5f5f7d8..c68ae69 100644
--- a/bin/dnssec/dnssectool.c
+++ b/bin/dnssec/dnssectool.c
@@ -319,7 +319,7 @@ strtotime(const char *str, isc_int64_t now, isc_int64_t base) {
 	isc_result_t result;
 	const char *orig = str;
 	char *endp;
-	int n;
+	size_t n;
 
 	if ((str[0] == '0' || str[0] == '-') && str[1] == '\0')
 		return ((isc_stdtime_t) 0);
diff --git a/bin/named/Makefile.in b/bin/named/Makefile.in
index 68c42a8..cd65777 100644
--- a/bin/named/Makefile.in
+++ b/bin/named/Makefile.in
@@ -51,7 +51,7 @@ CINCLUDES =	-I${srcdir}/include -I${srcdir}/unix/include -I. \
 		${ISCCFG_INCLUDES} ${ISCCC_INCLUDES} ${ISC_INCLUDES} \
 		${DLZDRIVER_INCLUDES} ${DBDRIVER_INCLUDES} @DST_OPENSSL_INC@
 
-CDEFINES =      @CONTRIB_DLZ@ @USE_PKCS11@ @USE_OPENSSL@
+CDEFINES =      @CONTRIB_DLZ@ @USE_PKCS11@ @PKCS11_ENGINE@ @CRYPTO@
 
 CWARNINGS =
 
diff --git a/bin/named/include/named/globals.h b/bin/named/include/named/globals.h
index cbc14d8..aad462d 100644
--- a/bin/named/include/named/globals.h
+++ b/bin/named/include/named/globals.h
@@ -146,8 +146,8 @@ EXTERN const char *		lwresd_g_defaultpidfile INIT(NS_LOCALSTATEDIR
 
 EXTERN const char *		ns_g_username		INIT(NULL);
 
-#ifdef USE_PKCS11
-EXTERN const char *		ns_g_engine		INIT("pkcs11");
+#if defined(USE_PKCS11)
+EXTERN const char *		ns_g_engine		INIT(PKCS11_ENGINE);
 #else
 EXTERN const char *		ns_g_engine		INIT(NULL);
 #endif
diff --git a/bin/named/main.c b/bin/named/main.c
index 15905ef..a00687f 100644
--- a/bin/named/main.c
+++ b/bin/named/main.c
@@ -51,6 +51,9 @@
 #include <dns/view.h>
 
 #include <dst/result.h>
+#ifdef PKCS11CRYPTO
+#include <pk11/result.h>
+#endif
 
 #include <dlz/dlz_dlopen_driver.h>
 
@@ -1081,6 +1084,9 @@ main(int argc, char *argv[]) {
 	dns_result_register();
 	dst_result_register();
 	isccc_result_register();
+#ifdef PKCS11CRYPTO
+	pk11_result_register();
+#endif
 
 	parse_command_line(argc, argv);
 
diff --git a/bin/named/named.docbook b/bin/named/named.docbook
index 1f08e19..8f46aac 100644
--- a/bin/named/named.docbook
+++ b/bin/named/named.docbook
@@ -153,11 +153,17 @@
         <term>-E <replaceable class="parameter">engine-name</replaceable></term>
         <listitem>
           <para>
-            Use a crypto hardware (OpenSSL engine) for the crypto operations
-            it supports, for instance re-signing with private keys from
-            a secure key store. When compiled with PKCS#11 support
-            <replaceable class="parameter">engine-name</replaceable>
-            defaults to pkcs11, the empty name resets it to no engine.
+            When applicable, specifies the hardware to use for
+            cryptographic operations, such as a secure key store used
+            for signing.
+          </para>
+          <para>
+            When BIND is built with OpenSSL PKCS#11 support, this defaults
+            to the string "pkcs11", which identifies an OpenSSL engine
+            that can drive a cryptographic accelerator or hardware service
+            module.  When BIND is built with native PKCS#11 cryptography
+            (--enable-native-pkcs11), it defaults to the path of the PKCS#11
+            provider library specified via "--with-pkcs11".
           </para>
         </listitem>
       </varlistentry>
diff --git a/bin/named/server.c b/bin/named/server.c
index 56df6bf..227c646 100644
--- a/bin/named/server.c
+++ b/bin/named/server.c
@@ -6215,6 +6215,11 @@ ns_server_create(isc_mem_t *mctx, ns_server_t **serverp) {
 	server->in_roothints = NULL;
 	server->blackholeacl = NULL;
 
+	/* Must be first. */
+	CHECKFATAL(dst_lib_init2(ns_g_mctx, ns_g_entropy,
+				 ns_g_engine, ISC_ENTROPY_GOODONLY),
+		   "initializing DST");
+
 	CHECKFATAL(dns_rootns_create(mctx, dns_rdataclass_in, NULL,
 				     &server->in_roothints),
 		   "setting up root hints");
@@ -6231,10 +6236,6 @@ ns_server_create(isc_mem_t *mctx, ns_server_t **serverp) {
 		   ISC_R_NOMEMORY : ISC_R_SUCCESS,
 		   "allocating reload event");
 
-	CHECKFATAL(dst_lib_init2(ns_g_mctx, ns_g_entropy,
-				 ns_g_engine, ISC_ENTROPY_GOODONLY),
-		   "initializing DST");
-
 	server->tkeyctx = NULL;
 	CHECKFATAL(dns_tkeyctx_create(ns_g_mctx, ns_g_entropy,
 				      &server->tkeyctx),
diff --git a/bin/nsupdate/Makefile.in b/bin/nsupdate/Makefile.in
index 09e6c14..e258ffc 100644
--- a/bin/nsupdate/Makefile.in
+++ b/bin/nsupdate/Makefile.in
@@ -46,9 +46,11 @@ ISCCFGDEPLIBS =	../../lib/isccfg/libisccfg.@A@
 
 DEPLIBS =	${DNSDEPLIBS} ${BIND9DEPLIBS} ${ISCDEPLIBS} ${ISCCFGDEPLIBS}
 
-LIBS =		${LWRESLIBS} ${DNSLIBS} ${BIND9LIBS} ${ISCCFGLIBS} ${ISCLIBS} @LIBS@
+LIBS =		${LWRESLIBS} ${DNSLIBS} ${BIND9LIBS} ${ISCCFGLIBS} \
+		${ISCLIBS} @LIBS@
 
-NOSYMLIBS =	${LWRESLIBS} ${DNSLIBS} ${BIND9LIBS} ${ISCCFGLIBS} ${ISCNOSYMLIBS} @LIBS@
+NOSYMLIBS =	${LWRESLIBS} ${DNSLIBS} ${BIND9LIBS} ${ISCCFGLIBS} \
+		${ISCNOSYMLIBS} @LIBS@
 
 SUBDIRS =
 
diff --git a/bin/pkcs11/Makefile.in b/bin/pkcs11/Makefile.in
index 407d977..15d3fb5 100644
--- a/bin/pkcs11/Makefile.in
+++ b/bin/pkcs11/Makefile.in
@@ -20,38 +20,51 @@ top_srcdir =	@top_srcdir@
 
 @BIND9_MAKE_INCLUDES@
 
-PROVIDER =	@PKCS11_PROVIDER@
+CINCLUDES =	${ISC_INCLUDES}
 
-CINCLUDES =	-I${srcdir}/include -I${srcdir}/unix
+CDEFINES =
 
-CDEFINES =	-DPK11_LIB_LOCATION=\"${PROVIDER}\"
+ISCLIBS =	../../lib/isc/libisc.@A@
+
+ISCDEPLIBS =	../../lib/isc/libisc.@A@
+
+DEPLIBS =	${ISCDEPLIBS}
 
 # if FORCE_STATIC_PROVIDER: LIBS = ${PROVIDER}
-LIBS =		-ldl
+LIBS =		${ISCLIBS} @LIBS@
 
-SUBDIRS =
+SUBDIRS =	benchmarks
 
-TARGETS =	pkcs11-keygen@EXEEXT@ pkcs11-list@EXEEXT@ \
-		pkcs11-destroy@EXEEXT@
-SRCS =		pkcs11-keygen.c pkcs11-list.c pkcs11-destroy.c
+TARGETS =	pkcs11-list@EXEEXT@ pkcs11-destroy@EXEEXT@ \
+		pkcs11-keygen@EXEEXT@ pkcs11-tokens@EXEEXT@
+SRCS =		pkcs11-list.c pkcs11-destroy.c \
+		pkcs11-keygen.c pkcs11-tokens.c
+OBJS =		pkcs11-list.@O@ pkcs11-destroy.@O@ \
+		pkcs11-keygen.@O@ pkcs11-tokens.@O@
 
-MANPAGES =	pkcs11-keygen.8 pkcs11-list.8 pkcs11-destroy.8
-HTMLPAGES =	pkcs11-keygen.html pkcs11-list.html pkcs11-destroy.html
+MANPAGES =	pkcs11-list.8 pkcs11-destroy.8 \
+		pkcs11-keygen.8 pkcs11-tokens.8
+HTMLPAGES =	pkcs11-list.html pkcs11-destroy.html \
+		pkcs11-keygen.html pkcs11-tokens.html
 MANOBJS =	${MANPAGES} ${HTMLPAGES}
 
 @BIND9_MAKE_RULES@
 
-pkcs11-keygen@EXEEXT@: @srcdir@/pkcs11-keygen.c
-	${CC} ${ALL_CFLAGS} ${LDFLAGS} \
-	-o $@ @srcdir@/pkcs11-keygen.c ${LIBS}
-
-pkcs11-list@EXEEXT@: @srcdir@/pkcs11-list.c
-	${CC} ${ALL_CFLAGS} ${LDFLAGS} \
-	-o $@ @srcdir@/pkcs11-list.c ${LIBS}
-
-pkcs11-destroy@EXEEXT@: @srcdir@/pkcs11-destroy.c
-	${CC} ${ALL_CFLAGS} ${LDFLAGS} \
-	-o $@ @srcdir@/pkcs11-destroy.c ${LIBS}
+pkcs11-list@EXEEXT@: @srcdir@/pkcs11-list.@O@ ${DEPLIBS}
+	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${ALL_CFLAGS} ${LDFLAGS} \
+	-o $@ @srcdir@/pkcs11-list.@O@ ${LIBS}
+
+pkcs11-destroy@EXEEXT@: @srcdir@/pkcs11-destroy.@O@ ${DEPLIBS}
+	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${ALL_CFLAGS} ${LDFLAGS} \
+	-o $@ @srcdir@/pkcs11-destroy.@O@ ${LIBS}
+ 
+pkcs11-keygen@EXEEXT@: @srcdir@/pkcs11-keygen.@O@ ${DEPLIBS}
+	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${ALL_CFLAGS} ${LDFLAGS} \
+	-o $@ @srcdir@/pkcs11-keygen.@O@ ${LIBS}
+ 
+pkcs11-tokens@EXEEXT@: @srcdir@/pkcs11-tokens.@O@ ${DEPLIBS}
+	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${ALL_CFLAGS} ${LDFLAGS} \
+	-o $@ @srcdir@/pkcs11-tokens.@O@ ${LIBS}
 
 doc man:: ${MANOBJS}
 
@@ -63,12 +76,14 @@ installdirs:
 	$(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${mandir}/man8
 
 install:: ${TARGETS} installdirs
-	${INSTALL_PROGRAM} pkcs11-keygen@EXEEXT@ ${DESTDIR}${sbindir}
-	${INSTALL_PROGRAM} pkcs11-list@EXEEXT@ ${DESTDIR}${sbindir}
-	${INSTALL_PROGRAM} pkcs11-destroy@EXEEXT@ ${DESTDIR}${sbindir}
-	${INSTALL_DATA} ${srcdir}/pkcs11-keygen.8 ${DESTDIR}${mandir}/man8
+	${LIBTOOL_MODE_INSTALL} ${INSTALL_PROGRAM} pkcs11-list@EXEEXT@ ${DESTDIR}${sbindir}
+	${LIBTOOL_MODE_INSTALL} ${INSTALL_PROGRAM} pkcs11-destroy@EXEEXT@ ${DESTDIR}${sbindir}
+	${LIBTOOL_MODE_INSTALL} ${INSTALL_PROGRAM} pkcs11-keygen@EXEEXT@ ${DESTDIR}${sbindir}
+	${LIBTOOL_MODE_INSTALL} ${INSTALL_PROGRAM} pkcs11-tokens@EXEEXT@ ${DESTDIR}${sbindir}
 	${INSTALL_DATA} ${srcdir}/pkcs11-list.8 ${DESTDIR}${mandir}/man8
 	${INSTALL_DATA} ${srcdir}/pkcs11-destroy.8 ${DESTDIR}${mandir}/man8
+	${INSTALL_DATA} ${srcdir}/pkcs11-keygen.8 ${DESTDIR}${mandir}/man8
+	${INSTALL_DATA} ${srcdir}/pkcs11-tokens.8 ${DESTDIR}${mandir}/man8
 
 clean distclean::
-	rm -f ${TARGETS}
+	rm -f ${OBJS} ${TARGETS}
diff --git a/bin/pkcs11/pkcs11-destroy.8 b/bin/pkcs11/pkcs11-destroy.8
index aff35b3..25323ca 100644
--- a/bin/pkcs11/pkcs11-destroy.8
+++ b/bin/pkcs11/pkcs11-destroy.8
@@ -32,7 +32,7 @@
 pkcs11\-destroy \- destroy PKCS#11 objects
 .SH "SYNOPSIS"
 .HP 15
-\fBpkcs11\-destroy\fR [\fB\-m\ \fR\fB\fImodule\fR\fR] [\fB\-s\ \fR\fB\fIslot\fR\fR] {\-i\ \fIID\fR | \-l\ \fIlabel\fR} [\fB\-p\ \fR\fB\fIPIN\fR\fR]
+\fBpkcs11\-destroy\fR [\fB\-m\ \fR\fB\fImodule\fR\fR] [\fB\-s\ \fR\fB\fIslot\fR\fR] {\-i\ \fIID\fR | \-l\ \fIlabel\fR} [\fB\-p\ \fR\fB\fIPIN\fR\fR] [\fB\-w\ \fR\fB\fIseconds\fR\fR]
 .SH "DESCRIPTION"
 .PP
 \fBpkcs11\-destroy\fR
@@ -41,7 +41,7 @@ destroys keys stored in a PKCS#11 device, identified by their
 or
 \fBlabel\fR.
 .PP
-Matching keys are displayed before being destroyed. There is a five second delay to allow the user to interrupt the process before the destruction takes place.
+Matching keys are displayed before being destroyed. By default, there is a five second delay to allow the user to interrupt the process before the destruction takes place.
 .SH "ARGUMENTS"
 .PP
 \-m \fImodule\fR
@@ -70,6 +70,12 @@ Specify the PIN for the device. If no PIN is provided on the command line,
 \fBpkcs11\-destroy\fR
 will prompt for it.
 .RE
+.PP
+\-w \fIseconds\fR
+.RS 4
+Specify how long to pause before carrying out key destruction. The default is five seconds. If set to
+0, destruction will be immediate.
+.RE
 .SH "SEE ALSO"
 .PP
 \fBpkcs11\-list\fR(3),
diff --git a/bin/pkcs11/pkcs11-destroy.c b/bin/pkcs11/pkcs11-destroy.c
index 0f46a89..2905395 100644
--- a/bin/pkcs11/pkcs11-destroy.c
+++ b/bin/pkcs11/pkcs11-destroy.c
@@ -40,7 +40,10 @@
 
 /* $Id: pkcs11-destroy.c,v 1.8 2010/01/13 21:19:52 fdupont Exp $ */
 
-/* pkcs11-destroy [-m module] [-s $slot] [-i $id | -l $label] [-p $pin] */
+/*
+ * pkcs11-destroy [-m module] [-s $slot] [-i $id | -l $label]
+ *                 [-p $pin] [ -w $wait ]
+ */
 
 /*! \file */
 
@@ -52,74 +55,70 @@
 #include <errno.h>
 #include <string.h>
 #include <sys/types.h>
-#include "cryptoki.h"
 
-#ifdef WIN32
-#define sleep(x)	Sleep(x)
-#include "win32.c"
-#else
-#ifndef FORCE_STATIC_PROVIDER
-#include "unix.c"
-#endif
-#endif
+#include <isc/commandline.h>
+#include <isc/result.h>
+#include <isc/types.h>
+
+#include <pk11/pk11.h>
+#include <pk11/result.h>
 
 #if !(defined(HAVE_GETPASSPHRASE) || (defined (__SVR4) && defined (__sun)))
 #define getpassphrase(x)	getpass(x)
 #endif
 
 int
-main(int argc, char *argv[])
-{
+main(int argc, char *argv[]) {
+	isc_result_t result;
 	CK_RV rv;
 	CK_SLOT_ID slot = 0;
 	CK_SESSION_HANDLE hSession;
-	CK_UTF8CHAR *pin = NULL;
 	CK_BYTE attr_id[2];
 	CK_OBJECT_HANDLE akey[50];
+	pk11_context_t pctx;
+	char *lib_name = NULL;
 	char *label = NULL;
+	char *pin = NULL;
 	int error = 0;
-	unsigned int id = 0, i = 0;
+	unsigned int id = 0, i = 0, wait = 5;
 	int c, errflg = 0;
 	CK_ULONG ulObjectCount;
 	CK_ATTRIBUTE search_template[] = {
 		{CKA_ID, &attr_id, sizeof(attr_id)}
 	};
-	char *pk11_provider;
 	unsigned int j, len;
-	extern char *optarg;
-	extern int optopt;
-
-	pk11_provider = getenv("PKCS11_PROVIDER");
-	if (pk11_provider != NULL)
-		pk11_libname = pk11_provider;
 
-	while ((c = getopt(argc, argv, ":m:s:i:l:p:")) != -1) {
+	while ((c = isc_commandline_parse(argc, argv, ":m:s:i:l:p:w:")) != -1) {
 		switch (c) {
 		case 'm':
-			pk11_libname = optarg;
+			lib_name = isc_commandline_argument;
 			break;
 		case 's':
-			slot = atoi(optarg);
+			slot = atoi(isc_commandline_argument);
 			break;
 		case 'i':
-			id = atoi(optarg);
+			id = atoi(isc_commandline_argument);
 			id &= 0xffff;
 			break;
 		case 'l':
-			label = optarg;
+			label = isc_commandline_argument;
 			break;
 		case 'p':
-			pin = (CK_UTF8CHAR *)optarg;
+			pin = isc_commandline_argument;
+			break;
+		case 'w':
+			wait = atoi(isc_commandline_argument);
 			break;
 		case ':':
 			fprintf(stderr,
 				"Option -%c requires an operand\n",
-				optopt);
+				isc_commandline_option);
 			errflg++;
 			break;
 		case '?':
 		default:
-			fprintf(stderr, "Unrecognised option: -%c\n", optopt);
+			fprintf(stderr, "Unrecognised option: -%c\n",
+				isc_commandline_option);
 			errflg++;
 		}
 	}
@@ -127,56 +126,49 @@ main(int argc, char *argv[])
 	if (errflg || (id && (label != NULL))) {
 		fprintf(stderr, "Usage:\n");
 		fprintf(stderr, "\tpkcs11-destroy [-m module] [-s slot] "
-				"[-i id | -l label] [-p pin]\n");
+				"[-i id | -l label] [-p pin] [-w waittime]\n");
 		exit(1);
 	}
 
 	if (id) {
-		printf("id %i\n", id);
 		attr_id[0] = (id >> 8) & 0xff;
 		attr_id[1] = id & 0xff;
 	} else if (label) {
-		printf("label %s\n", label);
 		search_template[0].type = CKA_LABEL;
 		search_template[0].pValue = label;
 		search_template[0].ulValueLen = strlen(label);
 	}
 
-	/* Initialize the CRYPTOKI library */
-	rv = C_Initialize(NULL_PTR);
-	if (rv != CKR_OK) {
-		if (rv == 0xfe)
-			fprintf(stderr,
-				"Can't load or link module \"%s\"\n",
-				pk11_libname);
-		else
-			fprintf(stderr, "C_Initialize: Error = 0x%.8lX\n", rv);
-		exit(1);
-	}
+	pk11_result_register();
 
-	/* Open a session on the slot found */
-	rv = C_OpenSession(slot, CKF_RW_SESSION+CKF_SERIAL_SESSION,
-			   NULL_PTR, NULL_PTR, &hSession);
-	if (rv != CKR_OK) {
-		fprintf(stderr, "C_OpenSession: Error = 0x%.8lX\n", rv);
-		error = 1;
-		goto exit_program;
-	}
+	/* Initialize the CRYPTOKI library */
+	if (lib_name != NULL)
+		pk11_set_lib_name(lib_name);
 
 	if (pin == NULL)
-		pin = (CK_UTF8CHAR *)getpassphrase("Enter Pin: ");
+		pin = getpassphrase("Enter Pin: ");
 
 	/* Login to the Token (Keystore) */
-	rv = C_Login(hSession, CKU_USER, pin, strlen((char *)pin));
-	memset(pin, 0, strlen((char *)pin));
-	if (rv != CKR_OK) {
-		fprintf(stderr, "C_Login: Error = 0x%.8lX\n", rv);
-		error = 1;
-		goto exit_session;
+	result = pk11_get_session(&pctx, OP_ANY, ISC_FALSE, ISC_TRUE,
+				  ISC_TRUE, (const char *) pin, slot);
+	if (result == PK11_R_NORANDOMSERVICE ||
+	    result == PK11_R_NODIGESTSERVICE ||
+	    result == PK11_R_NOAESSERVICE) {
+		fprintf(stderr, "Warning: %s\n", isc_result_totext(result));
+		fprintf(stderr, "This HSM will not work with BIND 9 "
+				"using native PKCS#11.\n");
+	} else if (result != ISC_R_SUCCESS) {
+		fprintf(stderr, "Unrecoverable error initializing "
+				"PKCS#11: %s\n", isc_result_totext(result));
+		exit(1);
 	}
 
-	rv = C_FindObjectsInit(hSession, search_template,
-		   ((id != 0) || (label != NULL)) ? 1 : 0); 
+	memset(pin, 0, strlen(pin));
+
+	hSession = pctx.session;
+
+	rv = pkcs_C_FindObjectsInit(hSession, search_template,
+				    ((id != 0) || (label != NULL)) ? 1 : 0);
 
 	if (rv != CKR_OK) {
 		fprintf(stderr, "C_FindObjectsInit: Error = 0x%.8lX\n", rv);
@@ -184,13 +176,19 @@ main(int argc, char *argv[])
 		goto exit_session;
 	}
 	
-	rv = C_FindObjects(hSession, akey, 50, &ulObjectCount);
+	rv = pkcs_C_FindObjects(hSession, akey, 50, &ulObjectCount);
 	if (rv != CKR_OK) {
 		fprintf(stderr, "C_FindObjects: Error = 0x%.8lX\n", rv);
 		error = 1;
 		goto exit_search;
 	}
 
+	if (ulObjectCount == 0) {
+		printf("No matching key objects found.\n");
+		goto exit_search;
+	} else
+		printf("Key object%s found:\n", ulObjectCount > 1 ? "s" : "");
+
 	for (i = 0; i < ulObjectCount; i++) {
 		CK_OBJECT_CLASS oclass = 0;
 		CK_BYTE labelbuf[64 + 1];
@@ -204,7 +202,8 @@ main(int argc, char *argv[])
 		memset(labelbuf, 0, sizeof(labelbuf));
 		memset(idbuf, 0, sizeof(idbuf));
 
-		rv = C_GetAttributeValue(hSession, akey[i], attr_template, 3);
+		rv = pkcs_C_GetAttributeValue(hSession, akey[i],
+					      attr_template, 3);
 		if (rv != CKR_OK) {
 			fprintf(stderr,
 				"C_GetAttributeValue[%u]: rv = 0x%.8lX\n",
@@ -213,7 +212,7 @@ main(int argc, char *argv[])
 			goto exit_search;
 		}
 		len = attr_template[2].ulValueLen;
-		printf("object[%u]: class %lu label '%s' id[%lu] ",
+		printf("  object[%u]: class %lu, label '%s', id[%lu] ",
 		       i, oclass, labelbuf, attr_template[2].ulValueLen);
 		if (len > 4)
 			len = 4;
@@ -227,32 +226,40 @@ main(int argc, char *argv[])
 			printf("\n");
 	}
 
-	/* give a chance to kill this */
-	printf("sleeping 5 seconds...\n");
-	sleep(5);
+	if (wait != 0) {
+		printf("WARNING: This action is irreversible! "
+		       "Destroying key objects in %d seconds\n  ", wait);
+		for (i = 0; i < wait; i++) {
+			printf(".");
+			fflush(stdout);
+			sleep(1);
+		}
+		printf("\n");
+	}
 
 	for (i = 0; i < ulObjectCount; i++) {
-		rv = C_DestroyObject(hSession, akey[i]);
+		rv = pkcs_C_DestroyObject(hSession, akey[i]);
 		if (rv != CKR_OK) {
 			fprintf(stderr,
-				"C_DestroyObject[%u]: rv = 0x%.8lX\n",
+				"C_DestroyObject[%u] failed: rv = 0x%.8lX\n",
 				i, rv);
 			error = 1;
 		}
 	}
 
+	if (error == 0)
+		printf("Destruction complete.\n");
+
  exit_search:
-	rv = C_FindObjectsFinal(hSession);
+	rv = pkcs_C_FindObjectsFinal(hSession);
 	if (rv != CKR_OK) {
 		fprintf(stderr, "C_FindObjectsFinal: Error = 0x%.8lX\n", rv);
 		error = 1;
 	}
 
  exit_session:
-	(void)C_CloseSession(hSession);
-
- exit_program:
-	(void)C_Finalize(NULL_PTR);
+	pk11_return_session(&pctx);
+	(void) pk11_finalize();
 
 	exit(error);
 }
diff --git a/bin/pkcs11/pkcs11-destroy.docbook b/bin/pkcs11/pkcs11-destroy.docbook
index b4c2048..45c0208 100644
--- a/bin/pkcs11/pkcs11-destroy.docbook
+++ b/bin/pkcs11/pkcs11-destroy.docbook
@@ -37,6 +37,7 @@
   <docinfo>
     <copyright>
       <year>2009</year>
+      <year>2014</year>
       <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
     </copyright>
   </docinfo>
@@ -51,6 +52,7 @@
         <arg choice="plain">-l <replaceable class="parameter">label</replaceable></arg>
       </group>
       <arg><option>-p <replaceable class="parameter">PIN</replaceable></option></arg>
+      <arg><option>-w <replaceable class="parameter">seconds</replaceable></option></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
 
@@ -62,9 +64,9 @@
       <option>label</option>.
     </para>
     <para>
-      Matching keys are displayed before being destroyed.  There is a
-      five second delay to allow the user to interrupt the process
-      before the destruction takes place.
+      Matching keys are displayed before being destroyed.  By default,
+      there is a five second delay to allow the user to interrupt the
+      process before the destruction takes place.
     </para>
   </refsect1>
 
@@ -119,6 +121,17 @@
           </para>
         </listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term>-w <replaceable class="parameter">seconds</replaceable></term>
+        <listitem>
+          <para>
+            Specify how long to pause before carrying out key destruction.
+            The default is five seconds.  If set to <literal>0</literal>,
+            destruction will be immediate.
+          </para>
+        </listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
diff --git a/bin/pkcs11/pkcs11-destroy.html b/bin/pkcs11/pkcs11-destroy.html
index afc6e36..899b3e9 100644
--- a/bin/pkcs11/pkcs11-destroy.html
+++ b/bin/pkcs11/pkcs11-destroy.html
@@ -29,23 +29,23 @@
 </div>
 <div class="refsynopsisdiv">
 <h2>Synopsis</h2>
-<div class="cmdsynopsis"><p><code class="command">pkcs11-destroy</code>  [<code class="option">-m <em class="replaceable"><code>module</code></em></code>] [<code class="option">-s <em class="replaceable"><code>slot</code></em></code>] { -i <em class="replaceable"><code>ID</code></em>  |   -l <em class="replaceable"><code>label</code></em> } [<code class="option">-p <em class="replaceable"><code>PIN</code></em></code>]</p></div>
+<div class="cmdsynopsis"><p><code class="command">pkcs11-destroy</code>  [<code class="option">-m <em class="replaceable"><code>module</code></em></code>] [<code class="option">-s <em class="replaceable"><code>slot</code></em></code>] { -i <em class="replaceable"><code>ID</code></em>  |   -l <em class="replaceable"><code>label</code></em> } [<code class="option">-p <em class="replaceable"><code>PIN</code></em></code>] [<code class="option">-w <em class="replaceable"><code>seconds</code></em></code>]</p></div>
 </div>
 <div class="refsect1" lang="en">
-<a name="id2543384"></a><h2>DESCRIPTION</h2>
+<a name="id2543393"></a><h2>DESCRIPTION</h2>
 <p>
       <span><strong class="command">pkcs11-destroy</strong></span> destroys keys stored in a
       PKCS#11 device, identified by their <code class="option">ID</code> or
       <code class="option">label</code>.
     </p>
 <p>
-      Matching keys are displayed before being destroyed.  There is a
-      five second delay to allow the user to interrupt the process
-      before the destruction takes place.
+      Matching keys are displayed before being destroyed.  By default,
+      there is a five second delay to allow the user to interrupt the
+      process before the destruction takes place.
     </p>
 </div>
 <div class="refsect1" lang="en">
-<a name="id2543406"></a><h2>ARGUMENTS</h2>
+<a name="id2543415"></a><h2>ARGUMENTS</h2>
 <div class="variablelist"><dl>
 <dt><span class="term">-m <em class="replaceable"><code>module</code></em></span></dt>
 <dd><p>
@@ -71,17 +71,23 @@
             Specify the PIN for the device.  If no PIN is provided on the
             command line, <span><strong class="command">pkcs11-destroy</strong></span> will prompt for it.
           </p></dd>
+<dt><span class="term">-w <em class="replaceable"><code>seconds</code></em></span></dt>
+<dd><p>
+            Specify how long to pause before carrying out key destruction.
+            The default is five seconds.  If set to <code class="literal">0</code>,
+            destruction will be immediate.
+          </p></dd>
 </dl></div>
 </div>
 <div class="refsect1" lang="en">
-<a name="id2543507"></a><h2>SEE ALSO</h2>
+<a name="id2543537"></a><h2>SEE ALSO</h2>
 <p>
       <span class="citerefentry"><span class="refentrytitle">pkcs11-list</span>(3)</span>,
       <span class="citerefentry"><span class="refentrytitle">pkcs11-keygen</span>(3)</span>
     </p>
 </div>
 <div class="refsect1" lang="en">
-<a name="id2543533"></a><h2>AUTHOR</h2>
+<a name="id2543563"></a><h2>AUTHOR</h2>
 <p><span class="corpauthor">Internet Systems Consortium</span>
     </p>
 </div>
diff --git a/bin/pkcs11/pkcs11-keygen.8 b/bin/pkcs11/pkcs11-keygen.8
index 568e862..53de464 100644
--- a/bin/pkcs11/pkcs11-keygen.8
+++ b/bin/pkcs11/pkcs11-keygen.8
@@ -23,80 +23,91 @@
 .\"    Manual: BIND9
 .\"    Source: BIND9
 .\"
-.TH "PKCS11\-KEYGEN" "8" "Sep 18, 2009" "BIND9" "BIND9"
+.TH "PKCS11\-ECGEN" "8" "Feb 30, 2012" "BIND9" "BIND9"
 .\" disable hyphenation
 .nh
 .\" disable justification (adjust text to left margin only)
 .ad l
 .SH "NAME"
-pkcs11\-keygen \- generate RSA keys on a PKCS#11 device
+pkcs11\-keygen \- generate keys on a PKCS#11 device
 .SH "SYNOPSIS"
 .HP 14
-\fBpkcs11\-keygen\fR [\fB\-P\fR] [\fB\-m\ \fR\fB\fImodule\fR\fR] [\fB\-s\ \fR\fB\fIslot\fR\fR] [\fB\-e\fR] {\-b\ \fIkeysize\fR} {\-l\ \fIlabel\fR} [\fB\-i\ \fR\fB\fIid\fR\fR] [\fB\-p\ \fR\fB\fIPIN\fR\fR]
+\fBpkcs11\-keygen\fR {\-a\ \fIalgorithm\fR} [\fB\-b\ \fR\fB\fIkeysize\fR\fR] [\fB\-e\fR] [\fB\-i\ \fR\fB\fIid\fR\fR] [\fB\-m\ \fR\fB\fImodule\fR\fR] [\fB\-P\fR] [\fB\-p\ \fR\fB\fIPIN\fR\fR] [\fB\-q\fR] [\fB\-S\fR] [\fB\-s\ \fR\fB\fIslot\fR\fR] {label}
 .SH "DESCRIPTION"
 .PP
 \fBpkcs11\-keygen\fR
-causes a PKCS#11 device to generate a new RSA key pair with the specified
+causes a PKCS#11 device to generate a new key pair with the given
 \fBlabel\fR
-and with
+(which must be unique) and with
 \fBkeysize\fR
-bits of modulus.
+bits of prime.
 .SH "ARGUMENTS"
 .PP
-\-P
-.RS 4
-Set the new private key to be non\-sensitive and extractable. The allows the private key data to be read from the PKCS#11 device. The default is for private keys to be sensitive and non\-extractable.
-.RE
-.PP
-\-m \fImodule\fR
+\-a \fIalgorithm\fR
 .RS 4
-Specify the PKCS#11 provider module. This must be the full path to a shared library object implementing the PKCS#11 API for the device.
+Specify the key algorithm class: Supported classes are RSA, DSA, DH, and ECC. In addition to these strings, the
+\fBalgorithm\fR
+can be specified as a DNSSEC signing algorithm that will be used with this key; for example, NSEC3RSASHA1 maps to RSA, and ECDSAP256SHA256 maps to ECC. The default class is "RSA".
 .RE
 .PP
-\-s \fIslot\fR
+\-b \fIkeysize\fR
 .RS 4
-Open the session with the given PKCS#11 slot. The default is slot 0.
+Create the key pair with
+\fBkeysize\fR
+bits of prime. For ECC keys, the only valid values are 256 and 384, and the default is 256.
 .RE
 .PP
 \-e
 .RS 4
-Use a large exponent.
+For RSA keys only, use a large exponent.
 .RE
 .PP
-\-b \fIkeysize\fR
+\-i \fIid\fR
 .RS 4
-Create the key pair with
-\fBkeysize\fR
-bits of modulus.
+Create key objects with id. The id is either an unsigned short 2 byte or an unsigned long 4 byte number.
 .RE
 .PP
-\-l \fIlabel\fR
+\-m \fImodule\fR
 .RS 4
-Create key objects with the given label. This name must be unique.
+Specify the PKCS#11 provider module. This must be the full path to a shared library object implementing the PKCS#11 API for the device.
 .RE
 .PP
-\-i \fIid\fR
+\-P
 .RS 4
-Create key objects with id. The id is either an unsigned short 2 byte or an unsigned long 4 byte number.
+Set the new private key to be non\-sensitive and extractable. The allows the private key data to be read from the PKCS#11 device. The default is for private keys to be sensitive and non\-extractable.
 .RE
 .PP
 \-p \fIPIN\fR
 .RS 4
 Specify the PIN for the device. If no PIN is provided on the command line,
-\fBpkcs11\-keygen\fR
+\fBpkcs11\-ecgen\fR
 will prompt for it.
 .RE
+.PP
+\-e
+.RS 4
+Quiet mode: suppress unnecessary output.
+.RE
+.PP
+\-S
+.RS 4
+For Diffie\-Hellman (DH) keys only, use a special prime of 768, 1024 or 1536 bit size and base (aka generator) 2. If not specified, bit size will default to 1024.
+.RE
+.PP
+\-s \fIslot\fR
+.RS 4
+Open the session with the given PKCS#11 slot. The default is slot 0.
+.RE
 .SH "SEE ALSO"
 .PP
+\fBpkcs11\-rsagen\fR(3),
+\fBpkcs11\-dsagen\fR(3),
 \fBpkcs11\-list\fR(3),
 \fBpkcs11\-destroy\fR(3),
 \fBdnssec\-keyfromlabel\fR(3),
-.SH "CAVEAT"
-.PP
-Some PKCS#11 providers crash with big public exponent.
 .SH "AUTHOR"
 .PP
 Internet Systems Consortium
 .SH "COPYRIGHT"
-Copyright \(co 2009 Internet Systems Consortium, Inc. ("ISC")
+Copyright \(co 2012 Internet Systems Consortium, Inc. ("ISC")
 .br
diff --git a/bin/pkcs11/pkcs11-keygen.c b/bin/pkcs11/pkcs11-keygen.c
index 1ffb343..cc91776 100644
--- a/bin/pkcs11/pkcs11-keygen.c
+++ b/bin/pkcs11/pkcs11-keygen.c
@@ -40,18 +40,19 @@
 
 /* $Id: pkcs11-keygen.c,v 1.9 2009/10/26 23:36:53 each Exp $ */
 
-/* pkcs11-keygen - pkcs11 rsa key generator
-*
-* create RSASHA1 key in the keystore of an SCA6000
-* The calculation of key tag is left to the script
-* that converts the key into a DNSKEY RR and inserts 
-* it into a zone file.
-*
-* usage:
-* pkcs11-keygen [-P] [-m module] [-s slot] [-e] -b keysize
-*               -l label [-i id] [-p pin] 
-*
-*/
+/* pkcs11-keygen - PKCS#11 key generator
+ *
+ * Create a key in the keystore of an HSM
+ *
+ * The calculation of key tag is left to the script
+ * that converts the key into a DNSKEY RR and inserts 
+ * it into a zone file.
+ *
+ * usage:
+ * pkcs11-keygen [-P] [-m module] [-s slot] [-e] [-b keysize]
+ *               [-i id] [-p pin] -l label
+ *
+ */
 
 /*! \file */
 
@@ -63,15 +64,16 @@
 #include <errno.h>
 #include <string.h>
 #include <sys/types.h>
-#include "cryptoki.h"
 
-#ifdef WIN32
-#include "win32.c"
-#else
-#ifndef FORCE_STATIC_PROVIDER
-#include "unix.c"
-#endif
-#endif
+#include <isc/commandline.h>
+#include <isc/result.h>
+#include <isc/types.h>
+
+#include <pk11/pk11.h>
+#include <pk11/result.h>
+#define WANT_DH_PRIMES
+#define WANT_ECC_CURVES
+#include <pk11/constants.h>
 
 #if !(defined(HAVE_GETPASSPHRASE) || (defined (__SVR4) && defined (__sun)))
 #define getpassphrase(x)	getpass(x)
@@ -81,188 +83,478 @@
 static CK_BBOOL truevalue = TRUE;
 static CK_BBOOL falsevalue = FALSE;
 
+/* Key class: RSA, ECC, DSA, DH, or unknown */
+typedef enum {
+	key_unknown,
+	key_rsa,
+	key_dsa,
+	key_dh,
+	key_ecc
+} key_class_t;
+
+/*
+ * Private key template: usable for most key classes without
+ * modificaton; override CKA_SIGN with CKA_DERIVE for DH
+ */
+#define PRIVATE_LABEL 0
+#define PRIVATE_SIGN 1
+#define PRIVATE_DERIVE 1
+#define PRIVATE_TOKEN 2
+#define PRIVATE_PRIVATE 3
+#define PRIVATE_SENSITIVE 4
+#define PRIVATE_EXTRACTABLE 5
+#define PRIVATE_ID 6
+#define PRIVATE_ATTRS 7
+static CK_ATTRIBUTE private_template[] = {
+	{CKA_LABEL, NULL_PTR, 0},
+	{CKA_SIGN, &truevalue, sizeof(truevalue)},
+	{CKA_TOKEN, &truevalue, sizeof(truevalue)},
+	{CKA_PRIVATE, &truevalue, sizeof(truevalue)},
+	{CKA_SENSITIVE, &truevalue, sizeof(truevalue)},
+	{CKA_EXTRACTABLE, &falsevalue, sizeof(falsevalue)},
+	{CKA_ID, NULL_PTR, 0}
+};
+
+/*
+ * Public key template for RSA keys
+ */
+#define RSA_LABEL 0
+#define RSA_VERIFY 1
+#define RSA_TOKEN 2
+#define RSA_PRIVATE 3
+#define RSA_MODULUS_BITS 4
+#define RSA_PUBLIC_EXPONENT 5
+#define RSA_ID 6
+#define RSA_ATTRS 7
+static CK_ATTRIBUTE rsa_template[] = {
+	{CKA_LABEL, NULL_PTR, 0},
+	{CKA_VERIFY, &truevalue, sizeof(truevalue)},
+	{CKA_TOKEN, &truevalue, sizeof(truevalue)},
+	{CKA_PRIVATE, &falsevalue, sizeof(falsevalue)},
+	{CKA_MODULUS_BITS, NULL_PTR, 0},
+	{CKA_PUBLIC_EXPONENT, NULL_PTR, 0},
+	{CKA_ID, NULL_PTR, 0}
+};
+
+/*
+ * Public key template for ECC keys
+ */
+#define ECC_LABEL 0
+#define ECC_VERIFY 1
+#define ECC_TOKEN 2
+#define ECC_PRIVATE 3
+#define ECC_PARAMS 4
+#define ECC_ID 5
+#define ECC_ATTRS 6
+static CK_ATTRIBUTE ecc_template[] = {
+	{CKA_LABEL, NULL_PTR, 0},
+	{CKA_VERIFY, &truevalue, sizeof(truevalue)},
+	{CKA_TOKEN, &truevalue, sizeof(truevalue)},
+	{CKA_PRIVATE, &falsevalue, sizeof(falsevalue)},
+	{CKA_EC_PARAMS, NULL_PTR, 0},
+	{CKA_ID, NULL_PTR, 0}
+};
+
+/*
+ * Public key template for DSA keys
+ */
+#define DSA_LABEL 0
+#define DSA_VERIFY 1
+#define DSA_TOKEN 2
+#define DSA_PRIVATE 3
+#define DSA_PRIME 4
+#define DSA_SUBPRIME 5
+#define DSA_BASE 6
+#define DSA_ID 7
+#define DSA_ATTRS 8
+static CK_ATTRIBUTE dsa_template[] = {
+	{CKA_LABEL, NULL_PTR, 0},
+	{CKA_VERIFY, &truevalue, sizeof(truevalue)},
+	{CKA_TOKEN, &truevalue, sizeof(truevalue)},
+	{CKA_PRIVATE, &falsevalue, sizeof(falsevalue)},
+	{CKA_PRIME, NULL_PTR, 0},
+	{CKA_SUBPRIME, NULL_PTR, 0},
+	{CKA_BASE, NULL_PTR, 0},
+	{CKA_ID, NULL_PTR, 0}
+};
+#define DSA_PARAM_PRIME 0
+#define DSA_PARAM_SUBPRIME 1
+#define DSA_PARAM_BASE 2
+#define DSA_PARAM_ATTRS 3
+static CK_ATTRIBUTE dsa_param_template[] = {
+	{CKA_PRIME, NULL_PTR, 0},
+	{CKA_SUBPRIME, NULL_PTR, 0},
+	{CKA_BASE, NULL_PTR, 0},
+};
+#define DSA_DOMAIN_PRIMEBITS 0
+#define DSA_DOMAIN_PRIVATE 1
+#define DSA_DOMAIN_ATTRS 2
+static CK_ATTRIBUTE dsa_domain_template[] = {
+	{CKA_PRIME_BITS, NULL_PTR, 0},
+	{CKA_PRIVATE, &falsevalue, sizeof(falsevalue)},
+};
+
+/*
+ * Public key template for DH keys
+ */
+#define DH_LABEL 0
+#define DH_VERIFY 1
+#define DH_TOKEN 2
+#define DH_PRIVATE 3
+#define DH_PRIME 4
+#define DH_BASE 5
+#define DH_ID 6
+#define DH_ATTRS 7
+static CK_ATTRIBUTE dh_template[] = {
+	{CKA_LABEL, NULL_PTR, 0},
+	{CKA_VERIFY, &truevalue, sizeof(truevalue)},
+	{CKA_TOKEN, &truevalue, sizeof(truevalue)},
+	{CKA_PRIVATE, &falsevalue, sizeof(falsevalue)},
+	{CKA_PRIME, NULL_PTR, 0},
+	{CKA_BASE, NULL_PTR, 0},
+	{CKA_ID, NULL_PTR, 0}
+};
+#define DH_PARAM_PRIME 0
+#define DH_PARAM_BASE 1
+#define DH_PARAM_ATTRS 2
+static CK_ATTRIBUTE dh_param_template[] = {
+	{CKA_PRIME, NULL_PTR, 0},
+	{CKA_BASE, NULL_PTR, 0},
+};
+#define DH_DOMAIN_PRIMEBITS 0
+#define DH_DOMAIN_ATTRS 1
+static CK_ATTRIBUTE dh_domain_template[] = {
+	{CKA_PRIME_BITS, NULL_PTR, 0},
+};
+
+/*
+ * Convert from text to key class.  Accepts the names of DNSSEC
+ * signing algorithms, so e.g., ECDSAP256SHA256 maps to ECC and
+ * NSEC3RSASHA1 maps to RSA.
+ */
+static key_class_t
+keyclass_fromtext(const char *name) {
+	if (name == NULL)
+		return (key_unknown);
+
+	if (strncasecmp(name, "rsa", 3) == 0 ||
+	    strncasecmp(name, "nsec3rsa", 8) == 0)
+		return (key_rsa);
+	else if (strncasecmp(name, "dsa", 3) == 0 ||
+		 strncasecmp(name, "nsec3dsa", 8) == 0)
+		return (key_dsa);
+	else if (strcasecmp(name, "dh") == 0)
+		return (key_dh);
+	else if (strncasecmp(name, "ecc", 3) == 0 ||
+		 strncasecmp(name, "ecdsa", 5) == 0)
+		return (key_ecc);
+	else
+		return (key_unknown);
+}
+
+static void
+usage() {
+	fprintf(stderr,
+		"Usage:\n"
+		"\tpkcs11-keygen -a algorithm -b keysize -l label\n"
+		"\t              [-P] [-m module] "
+			"[-s slot] [-e] [-S] [-i id] [-p PIN]\n");
+	exit(2);
+}
+
 int
-main(int argc, char *argv[])
-{
+main(int argc, char *argv[]) {
+	isc_result_t result;
 	CK_RV rv;
 	CK_SLOT_ID slot = 0;
-	CK_MECHANISM genmech;
+	CK_MECHANISM mech, dpmech;
 	CK_SESSION_HANDLE hSession;
-	CK_UTF8CHAR *pin = NULL;
-	CK_ULONG modulusbits = 0;
+	char *lib_name = NULL;
+	char *pin = NULL;
+	CK_ULONG bits = 0;
 	CK_CHAR *label = NULL;
-	CK_OBJECT_HANDLE privatekey, publickey;
-	CK_BYTE public_exponent[5];
-	CK_ULONG expsize = 3;
+	CK_OBJECT_HANDLE privatekey, publickey, domainparams;
+	CK_BYTE exponent[5];
+	CK_ULONG expsize = 0;
+	pk11_context_t pctx;
 	int error = 0;
 	int c, errflg = 0;
-	int hide = 1;
-	int idlen = 0;
+	int hide = 1, special = 0, quiet = 0;
+	int idlen = 0, id_offset = 0;
+	unsigned int i;
 	unsigned long id = 0;
 	CK_BYTE idbuf[4];
 	CK_ULONG ulObjectCount;
-	/* Set search template */
 	CK_ATTRIBUTE search_template[] = {
 		{CKA_LABEL, NULL_PTR, 0}
 	};
-	CK_ATTRIBUTE publickey_template[] = {
-		{CKA_LABEL, NULL_PTR, 0},
-		{CKA_VERIFY, &truevalue, sizeof(truevalue)},
-		{CKA_TOKEN, &truevalue, sizeof(truevalue)},
-		{CKA_MODULUS_BITS, &modulusbits, sizeof(modulusbits)},
-		{CKA_PUBLIC_EXPONENT, &public_exponent, expsize},
-		{CKA_ID, &idbuf, idlen}
-	};
-	CK_ULONG publickey_attrcnt = 6;
-	CK_ATTRIBUTE privatekey_template[] = {
-		{CKA_LABEL, NULL_PTR, 0},
-		{CKA_SIGN, &truevalue, sizeof(truevalue)},
-		{CKA_TOKEN, &truevalue, sizeof(truevalue)},
-		{CKA_PRIVATE, &truevalue, sizeof(truevalue)},
-		{CKA_SENSITIVE, &truevalue, sizeof(truevalue)},
-		{CKA_EXTRACTABLE, &falsevalue, sizeof(falsevalue)},
-		{CKA_ID, &idbuf, idlen}
-	};
-	CK_ULONG privatekey_attrcnt = 7;
-	char *pk11_provider;
-	extern char *optarg;
-	extern int optopt;
 
-	pk11_provider = getenv("PKCS11_PROVIDER");
-	if (pk11_provider != NULL)
-		pk11_libname = pk11_provider;
+	CK_ATTRIBUTE *public_template = NULL;
+	CK_ATTRIBUTE *domain_template = NULL;
+	CK_ATTRIBUTE *param_template = NULL;
+	CK_ULONG public_attrcnt = 0, private_attrcnt = PRIVATE_ATTRS;
+	CK_ULONG domain_attrcnt = 0, param_attrcnt = 0;
+	key_class_t keyclass = key_rsa;
+	pk11_optype_t op_type = OP_ANY;
 
-	while ((c = getopt(argc, argv, ":Pm:s:b:ei:l:p:")) != -1) {
+#define OPTIONS ":a:b:ei:l:m:Pp:qSs:"
+	while ((c = isc_commandline_parse(argc, argv, OPTIONS)) != -1) {
 		switch (c) {
+		case 'a':
+			keyclass = keyclass_fromtext(isc_commandline_argument);
+			break;
 		case 'P':
 			hide = 0;
 			break;
 		case 'm':
-			pk11_libname = optarg;
+			lib_name = isc_commandline_argument;
 			break;
 		case 's':
-			slot = atoi(optarg);
+			slot = atoi(isc_commandline_argument);
 			break;
 		case 'e':
 			expsize = 5;
 			break;
 		case 'b':
-			modulusbits = atoi(optarg);
+			bits = atoi(isc_commandline_argument);
 			break;
 		case 'l':
-			label = (CK_CHAR *)optarg;
+			/* -l option is retained for backward compatibility * */
+			label = (CK_CHAR *)isc_commandline_argument;
 			break;
 		case 'i':
-			id = strtoul(optarg, NULL, 0);
+			id = strtoul(isc_commandline_argument, NULL, 0);
 			idlen = 4;
 			break;
 		case 'p':
-			pin = (CK_UTF8CHAR *)optarg;
+			pin = isc_commandline_argument;
+			break;
+		case 'q':
+			quiet = 1;
+			break;
+		case 'S':
+			special = 1;
 			break;
 		case ':':
 			fprintf(stderr,
 				"Option -%c requires an operand\n",
-				optopt);
+				isc_commandline_option);
 			errflg++;
 			break;
 		case '?':
 		default:
-			fprintf(stderr, "Unrecognised option: -%c\n", optopt);
+			fprintf(stderr, "Unrecognised option: -%c\n",
+				isc_commandline_option);
 			errflg++;
 		}
 	}
 
-	if (errflg || !modulusbits || (label == NULL)) {
-		fprintf(stderr, "Usage:\n");
-		fprintf(stderr, "\tpkcs11-keygen -b keysize -l label\n");
-		fprintf(stderr, "\t              [-P] [-m module] "
-				"[-s slot] [-e] [-i id] [-p PIN]\n");
+	if (label == NULL && isc_commandline_index < argc)
+		label = (CK_CHAR *)argv[isc_commandline_index];
+
+	if (errflg || (label == NULL))
+		usage();
+
+	if (expsize != 0 && keyclass != key_rsa) {
+		fprintf(stderr, "The -e option is only compatible "
+				"with RSA key generation\n");
 		exit(2);
 	}
+
+	if (special != 0 && keyclass != key_dh) {
+		fprintf(stderr, "The -S option is only compatible "
+				"with Diffie-Hellman key generation\n");
+		exit(2);
+	}
+
+	switch (keyclass) {
+	case key_rsa:
+		op_type = OP_RSA;
+		if (expsize == 0)
+			expsize = 3;
+		if (bits == 0)
+			usage();
+
+		mech.mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
+		mech.pParameter = NULL;
+		mech.ulParameterLen = 0;
+
+		public_template = rsa_template;
+		public_attrcnt = RSA_ATTRS;
+		id_offset = RSA_ID;
+
+		/* Set public exponent to F4 or F5 */
+		exponent[0] = 0x01;
+		exponent[1] = 0x00;
+		if (expsize == 3)
+			exponent[2] = 0x01;
+		else {
+			exponent[2] = 0x00;
+			exponent[3] = 0x00;
+			exponent[4] = 0x01;
+		}
+
+		public_template[RSA_MODULUS_BITS].pValue = &bits;
+		public_template[RSA_MODULUS_BITS].ulValueLen = sizeof(bits);
+		public_template[RSA_PUBLIC_EXPONENT].pValue = &exponent;
+		public_template[RSA_PUBLIC_EXPONENT].ulValueLen = expsize;
+		break;
+	case key_ecc:
+		op_type = OP_EC;
+		if (bits == 0)
+			bits = 256;
+		else if (bits != 256 && bits != 384) {
+			fprintf(stderr, "ECC keys only support bit sizes of "
+					"256 and 384\n");
+			exit(2);
+		}
+
+		mech.mechanism = CKM_EC_KEY_PAIR_GEN;
+		mech.pParameter = NULL;
+		mech.ulParameterLen = 0;
+
+		public_template = ecc_template;
+		public_attrcnt = ECC_ATTRS;
+		id_offset = ECC_ID;
+
+		if (bits == 256) {
+			public_template[4].pValue = pk11_ecc_prime256v1;
+			public_template[4].ulValueLen =
+				sizeof(pk11_ecc_prime256v1);
+		} else {
+			public_template[4].pValue = pk11_ecc_secp384r1;
+			public_template[4].ulValueLen =
+				sizeof(pk11_ecc_secp384r1);
+		}
+
+		break;
+	case key_dsa:
+		op_type = OP_DSA;
+		if (bits == 0)
+			usage();
+
+		dpmech.mechanism = CKM_DSA_PARAMETER_GEN;
+		dpmech.pParameter = NULL;
+		dpmech.ulParameterLen = 0;
+		mech.mechanism = CKM_DSA_KEY_PAIR_GEN;
+		mech.pParameter = NULL;
+		mech.ulParameterLen = 0;
+
+		public_template = dsa_template;
+		public_attrcnt = DSA_ATTRS;
+		id_offset = DSA_ID;
+
+		domain_template = dsa_domain_template;
+		domain_attrcnt = DSA_DOMAIN_ATTRS;
+		param_template = dsa_param_template;
+		param_attrcnt = DSA_PARAM_ATTRS;
+
+		domain_template[DSA_DOMAIN_PRIMEBITS].pValue = &bits;
+		domain_template[DSA_DOMAIN_PRIMEBITS].ulValueLen = sizeof(bits);
+		break;
+	case key_dh:
+		op_type = OP_DH;
+		if (special && bits == 0)
+			bits = 1024;
+		else if (special &&
+			 bits != 768 && bits != 1024 && bits != 1536)
+		{
+			fprintf(stderr, "When using the special prime (-S) "
+				"option, only key sizes of\n"
+				"768, 1024 or 1536 are supported.\n");
+			exit(2);
+		} else if (bits == 0)
+			usage();
+
+		dpmech.mechanism = CKM_DH_PKCS_PARAMETER_GEN;
+		dpmech.pParameter = NULL;
+		dpmech.ulParameterLen = 0;
+		mech.mechanism = CKM_DH_PKCS_KEY_PAIR_GEN;
+		mech.pParameter = NULL;
+		mech.ulParameterLen = 0;
+
+		/* Override CKA_SIGN attribute */
+		private_template[PRIVATE_DERIVE].type = CKA_DERIVE;
+
+		public_template = dh_template;
+		public_attrcnt = DH_ATTRS;
+		id_offset = DH_ID;
+
+		domain_template = dh_domain_template;
+		domain_attrcnt = DH_DOMAIN_ATTRS;
+		param_template = dh_param_template;
+		param_attrcnt = DH_PARAM_ATTRS;
+
+		domain_template[DH_DOMAIN_PRIMEBITS].pValue = &bits;
+		domain_template[DH_DOMAIN_PRIMEBITS].ulValueLen = sizeof(bits);
+		break;
+	case key_unknown:
+		usage();
+	}
 	
 	search_template[0].pValue = label;
 	search_template[0].ulValueLen = strlen((char *)label);
-	publickey_template[0].pValue = label;
-	publickey_template[0].ulValueLen = strlen((char *)label);
-	privatekey_template[0].pValue = label;
-	privatekey_template[0].ulValueLen = strlen((char *)label);
-
-	/* Set public exponent to F4 or F5 */
-	public_exponent[0] = 0x01;
-	public_exponent[1] = 0x00;
-	if (expsize == 3)
-		public_exponent[2] = 0x01;
-	else {
-		publickey_template[4].ulValueLen = expsize;
-		public_exponent[2] = 0x00;
-		public_exponent[3] = 0x00;
-		public_exponent[4] = 0x01;
-	}
-
-	/* Set up mechanism for generating key pair */
-	genmech.mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
-	genmech.pParameter = NULL_PTR;
-	genmech.ulParameterLen = 0;
+	public_template[0].pValue = label;
+	public_template[0].ulValueLen = strlen((char *)label);
+	private_template[0].pValue = label;
+	private_template[0].ulValueLen = strlen((char *)label);
 
 	if (idlen == 0) {
-		publickey_attrcnt--;
-		privatekey_attrcnt--;
-	} else if (id <= 0xffff) {
-		idlen = 2;
-		publickey_template[5].ulValueLen = idlen;
-		privatekey_template[6].ulValueLen = idlen;
-		idbuf[0] = (CK_BYTE)(id >> 8);
-		idbuf[1] = (CK_BYTE)id;
+		public_attrcnt--;
+		private_attrcnt--;
 	} else {
-		idbuf[0] = (CK_BYTE)(id >> 24);
-		idbuf[1] = (CK_BYTE)(id >> 16);
-		idbuf[2] = (CK_BYTE)(id >> 8);
-		idbuf[3] = (CK_BYTE)id;
-	}
-
-	/* Initialize the CRYPTOKI library */
-	rv = C_Initialize(NULL_PTR);
+		if (id <= 0xffff) {
+			idlen = 2;
+			idbuf[0] = (CK_BYTE)(id >> 8);
+			idbuf[1] = (CK_BYTE)id;
+		} else {
+			idbuf[0] = (CK_BYTE)(id >> 24);
+			idbuf[1] = (CK_BYTE)(id >> 16);
+			idbuf[2] = (CK_BYTE)(id >> 8);
+			idbuf[3] = (CK_BYTE)id;
+		}
 
-	if (rv != CKR_OK) {
-		if (rv == 0xfe)
-			fprintf(stderr,
-				"Can't load or link module \"%s\"\n",
-				pk11_libname);
-		else
-			fprintf(stderr, "C_Initialize: Error = 0x%.8lX\n", rv);
-		exit(1);
+		public_template[id_offset].pValue = idbuf;
+		public_template[id_offset].ulValueLen = idlen;
+		private_template[PRIVATE_ID].pValue = idbuf;
+		private_template[PRIVATE_ID].ulValueLen = idlen;
 	}
 
-	/* Open a session on the slot found */
-	rv = C_OpenSession(slot, CKF_RW_SESSION+CKF_SERIAL_SESSION,
-			   NULL_PTR, NULL_PTR, &hSession);
+	pk11_result_register();
 
-	if (rv != CKR_OK) {
-		fprintf(stderr, "C_OpenSession: Error = 0x%.8lX\n", rv);
-		error = 1;
-		goto exit_program;
-	}
+	/* Initialize the CRYPTOKI library */
+	if (lib_name != NULL)
+		pk11_set_lib_name(lib_name);
 
-	/* Login to the Token (Keystore) */
 	if (pin == NULL)
-		pin = (CK_UTF8CHAR *)getpassphrase("Enter Pin: ");
+		pin = getpassphrase("Enter Pin: ");
 
-	rv = C_Login(hSession, CKU_USER, pin, strlen((char *)pin));
-	memset(pin, 0, strlen((char *)pin));
-	if (rv != CKR_OK) {
-		fprintf(stderr, "C_Login: Error = 0x%.8lX\n", rv);
-		error = 1;
-		goto exit_session;
+	result = pk11_get_session(&pctx, op_type, ISC_FALSE, ISC_TRUE,
+				  ISC_TRUE, (const char *) pin, slot);
+	if (result == PK11_R_NORANDOMSERVICE ||
+	    result == PK11_R_NODIGESTSERVICE ||
+	    result == PK11_R_NOAESSERVICE) {
+		fprintf(stderr, "Warning: %s\n", isc_result_totext(result));
+		fprintf(stderr, "This HSM will not work with BIND 9 "
+				"using native PKCS#11.\n");
+	} else if (result != ISC_R_SUCCESS) {
+		fprintf(stderr, "Unrecoverable error initializing "
+				"PKCS#11: %s\n", isc_result_totext(result));
+		exit(1);
 	}
 
+	/* Login to the Token (Keystore) */
+	memset(pin, 0, strlen(pin));
+	hSession = pctx.session;
+
 	/* check if a key with the same id already exists */
-	rv = C_FindObjectsInit(hSession, search_template, 1); 
+	rv = pkcs_C_FindObjectsInit(hSession, search_template, 1);
 	if (rv != CKR_OK) {
 		fprintf(stderr, "C_FindObjectsInit: Error = 0x%.8lX\n", rv);
 		error = 1;
 		goto exit_session;
 	}
-	rv = C_FindObjects(hSession, &privatekey, 1, &ulObjectCount);
+	rv = pkcs_C_FindObjects(hSession, &privatekey, 1, &ulObjectCount);
 	if (rv != CKR_OK) {
 		fprintf(stderr, "C_FindObjects: Error = 0x%.8lX\n", rv);
 		error = 1;
@@ -276,33 +568,140 @@ main(int argc, char *argv[])
 
 	/* Set attributes if the key is not to be hidden */
 	if (!hide) {
-		privatekey_template[4].pValue = &falsevalue;
-		privatekey_template[5].pValue = &truevalue;
+		private_template[4].pValue = &falsevalue;
+		private_template[5].pValue = &truevalue;
+	}
+
+	if (keyclass == key_rsa || keyclass == key_ecc)
+		goto generate_keys;
+
+	/*
+	 * Special setup for Diffie-Hellman keys
+	 */
+	if (special != 0) {
+		public_template[DH_BASE].pValue = pk11_dh_bn2;
+		public_template[DH_BASE].ulValueLen = sizeof(pk11_dh_bn2);
+		if (bits == 768) {
+			public_template[DH_PRIME].pValue = pk11_dh_bn768;
+			public_template[DH_PRIME].ulValueLen =
+				sizeof(pk11_dh_bn768);
+		} else if (bits == 1024) {
+			public_template[DH_PRIME].pValue = pk11_dh_bn1024;
+			public_template[DH_PRIME].ulValueLen =
+				sizeof(pk11_dh_bn1024);
+		} else {
+			public_template[DH_PRIME].pValue = pk11_dh_bn1536;
+			public_template[DH_PRIME].ulValueLen =
+				sizeof(pk11_dh_bn1536);
+		}
+		param_attrcnt = 0;
+		goto generate_keys;
+	}
+
+	/* Generate Domain parameters */
+	rv = pkcs_C_GenerateKey(hSession, &dpmech, domain_template,
+			   domain_attrcnt, &domainparams);
+
+	if (rv != CKR_OK) {
+		fprintf(stderr,
+			"C_GenerateKey: Error = 0x%.8lX\n", rv);
+		error = 1;
+		goto exit_search;
+	}
+
+	/* Get Domain parameters */
+	rv = pkcs_C_GetAttributeValue(hSession, domainparams,
+				 param_template, param_attrcnt);
+
+	if (rv != CKR_OK) {
+		fprintf(stderr,
+			"C_GetAttributeValue0: Error = 0x%.8lX\n", rv);
+		error = 1;
+		goto exit_domain;
+	}
+
+	/* Allocate space for parameter attributes */
+	for (i = 0; i < param_attrcnt; i++)
+		param_template[i].pValue = malloc(param_template[i].ulValueLen);
+
+	rv = pkcs_C_GetAttributeValue(hSession, domainparams,
+				 dsa_param_template, DSA_PARAM_ATTRS);
+
+	if (rv != CKR_OK) {
+		fprintf(stderr,
+			"C_GetAttributeValue1: Error = 0x%.8lX\n", rv);
+		error = 1;
+		goto exit_params;
+	}
+
+	switch (keyclass) {
+	case key_dsa:
+		public_template[DSA_PRIME].pValue =
+			param_template[DSA_PARAM_PRIME].pValue;
+		public_template[DSA_PRIME].ulValueLen =
+			param_template[DSA_PARAM_PRIME].ulValueLen;
+		public_template[DSA_SUBPRIME].pValue =
+			param_template[DSA_PARAM_SUBPRIME].pValue;
+		public_template[DSA_SUBPRIME].ulValueLen =
+			param_template[DSA_PARAM_SUBPRIME].ulValueLen;
+		public_template[DSA_BASE].pValue =
+			param_template[DSA_PARAM_BASE].pValue;
+		public_template[DSA_BASE].ulValueLen =
+			param_template[DSA_PARAM_BASE].ulValueLen;
+		break;
+	case key_dh:
+		public_template[DH_PRIME].pValue =
+			param_template[DH_PARAM_PRIME].pValue;
+		public_template[DH_PRIME].ulValueLen =
+			param_template[DH_PARAM_PRIME].ulValueLen;
+		public_template[DH_BASE].pValue =
+			param_template[DH_PARAM_BASE].pValue;
+		public_template[DH_BASE].ulValueLen =
+			param_template[DH_PARAM_BASE].ulValueLen;
+	default:
+		break;
 	}
 
+ generate_keys:
 	/* Generate Key pair for signing/verifying */
-	rv = C_GenerateKeyPair(hSession, &genmech,
-			       publickey_template, publickey_attrcnt,
-			       privatekey_template, privatekey_attrcnt,
+	rv = pkcs_C_GenerateKeyPair(hSession, &mech,
+			       public_template, public_attrcnt,
+			       private_template, private_attrcnt,
 			       &publickey, &privatekey);
 	
 	if (rv != CKR_OK) {
 		fprintf(stderr, "C_GenerateKeyPair: Error = 0x%.8lX\n", rv);
 		error = 1;
+	 } else if (!quiet)
+		printf("Key pair generation complete.\n");
+
+ exit_params:
+	/* Free parameter attributes */
+	if (keyclass == key_dsa || keyclass == key_dh)
+		for (i = 0; i < param_attrcnt; i++)
+			free(param_template[i].pValue);
+
+ exit_domain:
+	/* Destroy domain parameters */
+	if (keyclass == key_dsa || (keyclass == key_dh && !special)) {
+		rv = pkcs_C_DestroyObject(hSession, domainparams);
+		if (rv != CKR_OK) {
+			fprintf(stderr,
+				"C_DestroyObject: Error = 0x%.8lX\n", rv);
+			error = 1;
+		}
 	}
-	
+
  exit_search:
-	rv = C_FindObjectsFinal(hSession);
+	rv = pkcs_C_FindObjectsFinal(hSession);
 	if (rv != CKR_OK) {
 		fprintf(stderr, "C_FindObjectsFinal: Error = 0x%.8lX\n", rv);
 		error = 1;
 	}
 
  exit_session:
-	(void)C_CloseSession(hSession);
-
- exit_program:
-	(void)C_Finalize(NULL_PTR);
+	pk11_return_session(&pctx);
+	(void) pk11_finalize();
 
 	exit(error);
 }
diff --git a/bin/pkcs11/pkcs11-keygen.docbook b/bin/pkcs11/pkcs11-keygen.docbook
index 7c4ba08..d62ba2f 100644
--- a/bin/pkcs11/pkcs11-keygen.docbook
+++ b/bin/pkcs11/pkcs11-keygen.docbook
@@ -18,25 +18,26 @@
 -->
 
 <!-- $Id: pkcs11-keygen.docbook,v 1.3 2009/10/05 12:23:11 fdupont Exp $ -->
-<refentry id="man.pkcs11-keygen">
+<refentry id="man.pkcs11-ecgen">
   <refentryinfo>
-    <date>Sep 18, 2009</date>
+    <date>Feb 30, 2012</date>
   </refentryinfo>
 
   <refmeta>
-    <refentrytitle><application>pkcs11-keygen</application></refentrytitle>
+    <refentrytitle><application>pkcs11-ecgen</application></refentrytitle>
     <manvolnum>8</manvolnum>
     <refmiscinfo>BIND9</refmiscinfo>
   </refmeta>
 
   <refnamediv>
     <refname><application>pkcs11-keygen</application></refname>
-    <refpurpose>generate RSA keys on a PKCS#11 device</refpurpose>
+    <refpurpose>generate keys on a PKCS#11 device</refpurpose>
   </refnamediv>
 
   <docinfo>
     <copyright>
       <year>2009</year>
+      <year>2014</year>
       <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
     </copyright>
   </docinfo>
@@ -44,14 +45,17 @@
   <refsynopsisdiv>
     <cmdsynopsis>
       <command>pkcs11-keygen</command>
-      <arg><option>-P</option></arg>
-      <arg><option>-m <replaceable class="parameter">module</replaceable></option></arg>
-      <arg><option>-s <replaceable class="parameter">slot</replaceable></option></arg>
+      <arg choice="req">-a <replaceable class="parameter">algorithm</replaceable></arg>
+      <arg><option>-b <replaceable class="parameter">keysize</replaceable></option></arg>
       <arg><option>-e</option></arg>
-      <arg choice="req">-b <replaceable class="parameter">keysize</replaceable></arg>
-      <arg choice="req">-l <replaceable class="parameter">label</replaceable></arg>
       <arg><option>-i <replaceable class="parameter">id</replaceable></option></arg>
+      <arg><option>-m <replaceable class="parameter">module</replaceable></option></arg>
+      <arg><option>-P</option></arg>
       <arg><option>-p <replaceable class="parameter">PIN</replaceable></option></arg>
+      <arg><option>-q</option></arg>
+      <arg><option>-S</option></arg>
+      <arg><option>-s <replaceable class="parameter">slot</replaceable></option></arg>
+      <arg choice="req">label</arg>
     </cmdsynopsis>
   </refsynopsisdiv>
 
@@ -59,8 +63,8 @@
     <title>DESCRIPTION</title>
     <para>
       <command>pkcs11-keygen</command> causes a PKCS#11 device to generate
-      a new RSA key pair with the specified <option>label</option> and
-      with <option>keysize</option> bits of modulus.
+      a new key pair with the given <option>label</option> (which must be
+      unique) and with <option>keysize</option> bits of prime.
     </para>
   </refsect1>
 
@@ -68,83 +72,109 @@
     <title>ARGUMENTS</title>
     <variablelist>
       <varlistentry>
-        <term>-P</term>
+        <term>-a <replaceable class="parameter">algorithm</replaceable></term>
         <listitem>
           <para>
-            Set the new private key to be non-sensitive and extractable.
-            The allows the private key data to be read from the PKCS#11
-            device.  The default is for private keys to be sensitive and
-            non-extractable.
+            Specify the key algorithm class: Supported classes are RSA,
+            DSA, DH, and ECC. In addition to these strings, the
+            <option>algorithm</option> can be specified as a DNSSEC
+            signing algorithm that will be used with this key; for
+            example, NSEC3RSASHA1 maps to RSA, and ECDSAP256SHA256 maps
+            to ECC.  The default class is "RSA".
           </para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
-        <term>-m <replaceable class="parameter">module</replaceable></term>
+        <term>-b <replaceable class="parameter">keysize</replaceable></term>
         <listitem>
           <para>
-            Specify the PKCS#11 provider module.  This must be the full
-            path to a shared library object implementing the PKCS#11 API
-            for the device.
+            Create the key pair with <option>keysize</option> bits of
+            prime. For ECC keys, the only valid values are 256 and 384,
+            and the default is 256.
           </para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
-        <term>-s <replaceable class="parameter">slot</replaceable></term>
+        <term>-e</term>
         <listitem>
           <para>
-            Open the session with the given PKCS#11 slot.  The default is
-            slot 0.
+            For RSA keys only, use a large exponent.
           </para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
-        <term>-e</term>
+        <term>-i <replaceable class="parameter">id</replaceable></term>
         <listitem>
           <para>
-            Use a large exponent.
+            Create key objects with id. The id is either
+            an unsigned short 2 byte or an unsigned long 4 byte number.
           </para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
-        <term>-b <replaceable class="parameter">keysize</replaceable></term>
+        <term>-m <replaceable class="parameter">module</replaceable></term>
         <listitem>
           <para>
-            Create the key pair with <option>keysize</option> bits of
-            modulus.
+            Specify the PKCS#11 provider module.  This must be the full
+            path to a shared library object implementing the PKCS#11 API
+            for the device.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-P</term>
+        <listitem>
+          <para>
+            Set the new private key to be non-sensitive and extractable.
+            The allows the private key data to be read from the PKCS#11
+            device.  The default is for private keys to be sensitive and
+            non-extractable.
           </para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
-        <term>-l <replaceable class="parameter">label</replaceable></term>
+        <term>-p <replaceable class="parameter">PIN</replaceable></term>
         <listitem>
           <para>
-            Create key objects with the given label.
-            This name must be unique.
+            Specify the PIN for the device.  If no PIN is provided on
+            the command line, <command>pkcs11-ecgen</command> will
+            prompt for it.
           </para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
-        <term>-i <replaceable class="parameter">id</replaceable></term>
+		<term>-q</term>
         <listitem>
           <para>
-            Create key objects with id. The id is either
-            an unsigned short 2 byte or an unsigned long 4 byte number.
+		    Quiet mode: suppress unnecessary output.
+          </para>
+        </listitem>
+      </varlistentry>
+
+	  <varlistentry>
+        <term>-S</term>
+        <listitem>
+          <para>
+            For Diffie-Hellman (DH) keys only, use a special prime of
+            768, 1024 or 1536 bit size and base (aka generator) 2.
+	    If not specified, bit size will default to 1024.
           </para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
-        <term>-p <replaceable class="parameter">PIN</replaceable></term>
+        <term>-s <replaceable class="parameter">slot</replaceable></term>
         <listitem>
           <para>
-            Specify the PIN for the device.  If no PIN is provided on the
-            command line, <command>pkcs11-keygen</command> will prompt for it.
+            Open the session with the given PKCS#11 slot.  The default is
+            slot 0.
           </para>
         </listitem>
       </varlistentry>
@@ -155,6 +185,12 @@
     <title>SEE ALSO</title>
     <para>
       <citerefentry>
+        <refentrytitle>pkcs11-rsagen</refentrytitle><manvolnum>3</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>pkcs11-dsagen</refentrytitle><manvolnum>3</manvolnum>
+      </citerefentry>,
+      <citerefentry>
         <refentrytitle>pkcs11-list</refentrytitle><manvolnum>3</manvolnum>
       </citerefentry>,
       <citerefentry>
@@ -167,11 +203,6 @@
   </refsect1>
 
   <refsect1>
-    <title>CAVEAT</title>
-    <para>Some PKCS#11 providers crash with big public exponent.</para>
-  </refsect1>
-
-  <refsect1>
     <title>AUTHOR</title>
     <para><corpauthor>Internet Systems Consortium</corpauthor>
     </para>
diff --git a/bin/pkcs11/pkcs11-keygen.html b/bin/pkcs11/pkcs11-keygen.html
index 41378fc..c7fdecf 100644
--- a/bin/pkcs11/pkcs11-keygen.html
+++ b/bin/pkcs11/pkcs11-keygen.html
@@ -18,36 +18,53 @@
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
-<title>pkcs11-keygen</title>
+<title>pkcs11-ecgen</title>
 <meta name="generator" content="DocBook XSL Stylesheets V1.71.1">
 </head>
 <body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="refentry" lang="en">
-<a name="man.pkcs11-keygen"></a><div class="titlepage"></div>
+<a name="man.pkcs11-ecgen"></a><div class="titlepage"></div>
 <div class="refnamediv">
 <h2>Name</h2>
-<p><span class="application">pkcs11-keygen</span> &#8212; generate RSA keys on a PKCS#11 device</p>
+<p><span class="application">pkcs11-keygen</span> &#8212; generate keys on a PKCS#11 device</p>
 </div>
 <div class="refsynopsisdiv">
 <h2>Synopsis</h2>
-<div class="cmdsynopsis"><p><code class="command">pkcs11-keygen</code>  [<code class="option">-P</code>] [<code class="option">-m <em class="replaceable"><code>module</code></em></code>] [<code class="option">-s <em class="replaceable"><code>slot</code></em></code>] [<code class="option">-e</code>] {-b <em class="replaceable"><code>keysize</code></em>} {-l <em class="replaceable"><code>label</code></em>} [<code class="option">-i <em class="replaceable"><code>id</code></em></code>] [<code class="option">-p <em class="replaceable"><code>PIN</code></em></code>]</p></div>
+<div class="cmdsynopsis"><p><code class="command">pkcs11-keygen</code>  {-a <em class="replaceable"><code>algorithm</code></em>} [<code class="option">-b <em class="replaceable"><code>keysize</code></em></code>] [<code class="option">-e</code>] [<code class="option">-i <em class="replaceable"><code>id</code></em></code>] [<code class="option">-m <em class="replaceable"><code>module</code></em></code>] [<code class="option">-P</code>] [<code class="option">-p <em class="replaceable"><code>PIN</code></em></code>] [<code class="option">-q</code>] [<code class="option">-S</code>] [<code class="option">-s <em class="replaceable"><code>slot</code></em></code>] {label}</p></div>
 </div>
 <div class="refsect1" lang="en">
-<a name="id2543397"></a><h2>DESCRIPTION</h2>
+<a name="id2543410"></a><h2>DESCRIPTION</h2>
 <p>
       <span><strong class="command">pkcs11-keygen</strong></span> causes a PKCS#11 device to generate
-      a new RSA key pair with the specified <code class="option">label</code> and
-      with <code class="option">keysize</code> bits of modulus.
+      a new key pair with the given <code class="option">label</code> (which must be
+      unique) and with <code class="option">keysize</code> bits of prime.
     </p>
 </div>
 <div class="refsect1" lang="en">
-<a name="id2543416"></a><h2>ARGUMENTS</h2>
+<a name="id2543430"></a><h2>ARGUMENTS</h2>
 <div class="variablelist"><dl>
-<dt><span class="term">-P</span></dt>
+<dt><span class="term">-a <em class="replaceable"><code>algorithm</code></em></span></dt>
 <dd><p>
-            Set the new private key to be non-sensitive and extractable.
-            The allows the private key data to be read from the PKCS#11
-            device.  The default is for private keys to be sensitive and
-            non-extractable.
+            Specify the key algorithm class: Supported classes are RSA,
+            DSA, DH, and ECC. In addition to these strings, the
+            <code class="option">algorithm</code> can be specified as a DNSSEC
+            signing algorithm that will be used with this key; for
+            example, NSEC3RSASHA1 maps to RSA, and ECDSAP256SHA256 maps
+            to ECC.  The default class is "RSA".
+          </p></dd>
+<dt><span class="term">-b <em class="replaceable"><code>keysize</code></em></span></dt>
+<dd><p>
+            Create the key pair with <code class="option">keysize</code> bits of
+            prime. For ECC keys, the only valid values are 256 and 384,
+            and the default is 256.
+          </p></dd>
+<dt><span class="term">-e</span></dt>
+<dd><p>
+            For RSA keys only, use a large exponent.
+          </p></dd>
+<dt><span class="term">-i <em class="replaceable"><code>id</code></em></span></dt>
+<dd><p>
+            Create key objects with id. The id is either
+            an unsigned short 2 byte or an unsigned long 4 byte number.
           </p></dd>
 <dt><span class="term">-m <em class="replaceable"><code>module</code></em></span></dt>
 <dd><p>
@@ -55,51 +72,48 @@
             path to a shared library object implementing the PKCS#11 API
             for the device.
           </p></dd>
-<dt><span class="term">-s <em class="replaceable"><code>slot</code></em></span></dt>
-<dd><p>
-            Open the session with the given PKCS#11 slot.  The default is
-            slot 0.
-          </p></dd>
-<dt><span class="term">-e</span></dt>
+<dt><span class="term">-P</span></dt>
 <dd><p>
-            Use a large exponent.
+            Set the new private key to be non-sensitive and extractable.
+            The allows the private key data to be read from the PKCS#11
+            device.  The default is for private keys to be sensitive and
+            non-extractable.
           </p></dd>
-<dt><span class="term">-b <em class="replaceable"><code>keysize</code></em></span></dt>
+<dt><span class="term">-p <em class="replaceable"><code>PIN</code></em></span></dt>
 <dd><p>
-            Create the key pair with <code class="option">keysize</code> bits of
-            modulus.
+            Specify the PIN for the device.  If no PIN is provided on
+            the command line, <span><strong class="command">pkcs11-ecgen</strong></span> will
+            prompt for it.
           </p></dd>
-<dt><span class="term">-l <em class="replaceable"><code>label</code></em></span></dt>
+<dt><span class="term">-q</span></dt>
 <dd><p>
-            Create key objects with the given label.
-            This name must be unique.
+            Quiet mode: suppress unnecessary output.
           </p></dd>
-<dt><span class="term">-i <em class="replaceable"><code>id</code></em></span></dt>
+<dt><span class="term">-S</span></dt>
 <dd><p>
-            Create key objects with id. The id is either
-            an unsigned short 2 byte or an unsigned long 4 byte number.
+            For Diffie-Hellman (DH) keys only, use a special prime of
+            768, 1024 or 1536 bit size and base (aka generator) 2.
+	    	If not specified, bit size will default to 1024.
           </p></dd>
-<dt><span class="term">-p <em class="replaceable"><code>PIN</code></em></span></dt>
+<dt><span class="term">-s <em class="replaceable"><code>slot</code></em></span></dt>
 <dd><p>
-            Specify the PIN for the device.  If no PIN is provided on the
-            command line, <span><strong class="command">pkcs11-keygen</strong></span> will prompt for it.
+            Open the session with the given PKCS#11 slot.  The default is
+            slot 0.
           </p></dd>
 </dl></div>
 </div>
 <div class="refsect1" lang="en">
-<a name="id2543563"></a><h2>SEE ALSO</h2>
+<a name="id2543605"></a><h2>SEE ALSO</h2>
 <p>
+      <span class="citerefentry"><span class="refentrytitle">pkcs11-rsagen</span>(3)</span>,
+      <span class="citerefentry"><span class="refentrytitle">pkcs11-dsagen</span>(3)</span>,
       <span class="citerefentry"><span class="refentrytitle">pkcs11-list</span>(3)</span>,
       <span class="citerefentry"><span class="refentrytitle">pkcs11-destroy</span>(3)</span>,
       <span class="citerefentry"><span class="refentrytitle">dnssec-keyfromlabel</span>(3)</span>,
     </p>
 </div>
 <div class="refsect1" lang="en">
-<a name="id2543598"></a><h2>CAVEAT</h2>
-<p>Some PKCS#11 providers crash with big public exponent.</p>
-</div>
-<div class="refsect1" lang="en">
-<a name="id2543609"></a><h2>AUTHOR</h2>
+<a name="id2543657"></a><h2>AUTHOR</h2>
 <p><span class="corpauthor">Internet Systems Consortium</span>
     </p>
 </div>
diff --git a/bin/pkcs11/pkcs11-list.c b/bin/pkcs11/pkcs11-list.c
index 336bf41..bc6ad28 100644
--- a/bin/pkcs11/pkcs11-list.c
+++ b/bin/pkcs11/pkcs11-list.c
@@ -52,74 +52,68 @@
 #include <errno.h>
 #include <string.h>
 #include <sys/types.h>
-#include "cryptoki.h"
 
-#ifdef WIN32
-#include "win32.c"
-#else
-#ifndef FORCE_STATIC_PROVIDER
-#include "unix.c"
-#endif
-#endif
+#include <isc/commandline.h>
+#include <isc/result.h>
+#include <isc/types.h>
+
+#include <pk11/pk11.h>
+#include <pk11/result.h>
 
 #if !(defined(HAVE_GETPASSPHRASE) || (defined (__SVR4) && defined (__sun)))
 #define getpassphrase(x)		getpass(x)
 #endif
 
 int
-main(int argc, char *argv[])
-{
+main(int argc, char *argv[]) {
+	isc_result_t result;
 	CK_RV rv;
 	CK_SLOT_ID slot = 0;
 	CK_SESSION_HANDLE hSession;
-	CK_UTF8CHAR *pin = NULL;
 	CK_BYTE attr_id[2];
 	CK_OBJECT_HANDLE akey[50];
+	pk11_context_t pctx;
+	char *lib_name = NULL;
 	char *label = NULL;
-	int error = 0, public = 0, all = 0;
+	char *pin = NULL;
+	isc_boolean_t error = ISC_FALSE, logon = ISC_TRUE, all = ISC_FALSE;
 	unsigned int i = 0, id = 0;
 	int c, errflg = 0;
 	CK_ULONG ulObjectCount;
 	CK_ATTRIBUTE search_template[] = {
 		{CKA_ID, &attr_id, sizeof(attr_id)}
 	};
-	char *pk11_provider;
-	extern char *optarg;
-	extern int optopt;
 
-	pk11_provider = getenv("PKCS11_PROVIDER");
-	if (pk11_provider != NULL)
-		pk11_libname = pk11_provider;
-
-	while ((c = getopt(argc, argv, ":m:s:i:l:p:P")) != -1) {
+	while ((c = isc_commandline_parse(argc, argv, ":m:s:i:l:p:P")) != -1) {
 		switch (c) {
 		case 'P':
-			public = 1;
+			logon = ISC_FALSE;
 			break;
 		case 'm':
-			pk11_libname = optarg;
+			lib_name = isc_commandline_argument;
 			break;
 		case 's':
-			slot = atoi(optarg);
+			slot = atoi(isc_commandline_argument);
 			break;
 		case 'i':
-			id = atoi(optarg);
+			id = atoi(isc_commandline_argument);
 			id &= 0xffff;
 			break;
 		case 'l':
-			label = optarg;
+			label = isc_commandline_argument;
 			break;
 		case 'p':
-			pin = (CK_UTF8CHAR *)optarg;
+			pin = isc_commandline_argument;
 			break;
 		case ':':
 			fprintf(stderr, "Option -%c requires an operand\n",
-				optopt);
+				isc_commandline_option);
 			errflg++;
 			break;
 		case '?':
 		default:
-			fprintf(stderr, "Unrecognised option: -%c\n", optopt);
+			fprintf(stderr, "Unrecognised option: -%c\n",
+				isc_commandline_option);
 			errflg++;
 		}
 	}
@@ -132,7 +126,7 @@ main(int argc, char *argv[])
 	}
 
 	if (!id && (label == NULL))
-		all = 1;
+		all = ISC_TRUE;
 
 	if (slot)
 		printf("slot %lu\n", slot);
@@ -148,41 +142,37 @@ main(int argc, char *argv[])
 		search_template[0].ulValueLen = strlen(label);
 	}
 
+	pk11_result_register();
+
 	/* Initialize the CRYPTOKI library */
-	rv = C_Initialize(NULL_PTR);
-	if (rv != CKR_OK) {
-		if (rv == 0xfe)
-			fprintf(stderr,
-				"Can't load or link module \"%s\"\n",
-				pk11_libname);
-		else
-			fprintf(stderr, "C_Initialize: Error = 0x%.8lX\n", rv);
+	if (lib_name != NULL)
+		pk11_set_lib_name(lib_name);
+
+	if (logon && pin == NULL)
+		pin = getpassphrase("Enter Pin: ");
+
+	result = pk11_get_session(&pctx, OP_ANY, ISC_FALSE, ISC_FALSE,
+				  logon, pin, slot);
+	if (result == PK11_R_NORANDOMSERVICE ||
+	    result == PK11_R_NODIGESTSERVICE ||
+	    result == PK11_R_NOAESSERVICE) {
+		fprintf(stderr, "Warning: %s\n", isc_result_totext(result));
+		fprintf(stderr, "This HSM will not work with BIND 9 "
+				"using native PKCS#11.\n");
+	} else if (result != ISC_R_SUCCESS) {
+		fprintf(stderr, "Unrecoverable error initializing "
+			"PKCS#11: %s\n", isc_result_totext(result));
+		fprintf(stderr, "Unrecoverable error initializing "
+				"PKCS#11: %s\n", isc_result_totext(result));
 		exit(1);
 	}
 
-	/* Open a session on the slot found */
-	rv = C_OpenSession(slot, CKF_SERIAL_SESSION,
-			   NULL_PTR, NULL_PTR, &hSession);
-	if (rv != CKR_OK) {
-		fprintf(stderr, "C_OpenSession: Error = 0x%.8lX\n", rv);
-		error = 1;
-		goto exit_program;
-	}
+	if (pin != NULL)
+		memset(pin, 0, strlen(pin));
 
-	/* Login to the Token (Keystore) */
-	if (!public) {
-		if (pin == NULL)
-			pin = (CK_UTF8CHAR *)getpassphrase("Enter Pin: ");
-		rv = C_Login(hSession, CKU_USER, pin, strlen((char *)pin));
-		memset(pin, 0, strlen((char *)pin));
-		if (rv != CKR_OK) {
-			fprintf(stderr, "C_Login: Error = 0x%.8lX\n", rv);
-			error = 1;
-			goto exit_session;
-		}
-	}
+	hSession = pctx.session;
 
-	rv = C_FindObjectsInit(hSession, search_template, all ? 0 : 1); 
+	rv = pkcs_C_FindObjectsInit(hSession, search_template, all ? 0 : 1); 
 	if (rv != CKR_OK) {
 		fprintf(stderr, "C_FindObjectsInit: Error = 0x%.8lX\n", rv);
 		error = 1;
@@ -191,7 +181,7 @@ main(int argc, char *argv[])
 	
 	ulObjectCount = 1;
 	while (ulObjectCount) {
-		rv = C_FindObjects(hSession, akey, 50, &ulObjectCount);
+		rv = pkcs_C_FindObjects(hSession, akey, 50, &ulObjectCount);
 		if (rv != CKR_OK) {
 			fprintf(stderr,
 				"C_FindObjects: Error = 0x%.8lX\n",
@@ -215,7 +205,7 @@ main(int argc, char *argv[])
 			memset(labelbuf, 0, sizeof(labelbuf));
 			memset(idbuf, 0, sizeof(idbuf));
 
-			rv = C_GetAttributeValue(hSession, akey[i],
+			rv = pkcs_C_GetAttributeValue(hSession, akey[i],
 						 template, 3);
 			if (rv != CKR_OK) {
 				fprintf(stderr,
@@ -260,17 +250,15 @@ main(int argc, char *argv[])
 	}
 
  exit_search:
-	rv = C_FindObjectsFinal(hSession);
+	rv = pkcs_C_FindObjectsFinal(hSession);
 	if (rv != CKR_OK) {
 		fprintf(stderr, "C_FindObjectsFinal: Error = 0x%.8lX\n", rv);
 		error = 1;
 	}
 
  exit_session:
-	(void)C_CloseSession(hSession);
-
- exit_program:
-	(void)C_Finalize(NULL_PTR);
+	pk11_return_session(&pctx);
+	(void) pk11_finalize();
 
 	exit(error);
 }
diff --git a/bin/pkcs11/pkcs11-tokens.8 b/bin/pkcs11/pkcs11-tokens.8
new file mode 100644
index 0000000..7c2be83
--- /dev/null
+++ b/bin/pkcs11/pkcs11-tokens.8
@@ -0,0 +1,51 @@
+.\" Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+.\" 
+.\" Permission to use, copy, modify, and/or distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\" 
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+.\" REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+.\" AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+.\" INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+.\" LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+.\" OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+.\" PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" $Id$
+.\"
+.hy 0
+.ad l
+.\"     Title: pkcs11\-tokens
+.\"    Author: 
+.\" Generator: DocBook XSL Stylesheets v1.71.1 <http://docbook.sf.net/>
+.\"      Date: August 25, 2013
+.\"    Manual: BIND9
+.\"    Source: BIND9
+.\"
+.TH "PKCS11\-TOKENS" "8" "August 25, 2013" "BIND9" "BIND9"
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.SH "NAME"
+pkcs11\-tokens \- list PKCS#11 available tokens
+.SH "SYNOPSIS"
+.HP 14
+\fBpkcs11\-tokens\fR [\fB\-m\ \fR\fB\fImodule\fR\fR]
+.SH "DESCRIPTION"
+.PP
+\fBpkcs11\-tokens\fR
+lists the PKCS#11 available tokens with defaults from the slot/token scan performed at application initialization.
+.SH "ARGUMENTS"
+.PP
+\-m \fImodule\fR
+.RS 4
+Specify the PKCS#11 provider module. This must be the full path to a shared library object implementing the PKCS#11 API for the device.
+.RE
+.SH "AUTHOR"
+.PP
+Internet Systems Consortium
+.SH "COPYRIGHT"
+Copyright \(co 2013 Internet Systems Consortium, Inc. ("ISC")
+.br
diff --git a/bin/pkcs11/pkcs11-tokens.c b/bin/pkcs11/pkcs11-tokens.c
new file mode 100644
index 0000000..ff4e030
--- /dev/null
+++ b/bin/pkcs11/pkcs11-tokens.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id$ */
+
+/* pkcs11-tokens [-m module] */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <isc/commandline.h>
+#include <isc/mem.h>
+#include <isc/result.h>
+#include <isc/types.h>
+
+#include <pk11/pk11.h>
+#include <pk11/result.h>
+
+int
+main(int argc, char *argv[]) {
+	isc_result_t result;
+	char *lib_name = NULL;
+	int c, errflg = 0;
+	isc_mem_t *mctx = NULL;
+	pk11_context_t pctx;
+
+	while ((c = isc_commandline_parse(argc, argv, ":m:")) != -1) {
+		switch (c) {
+		case 'm':
+			lib_name = isc_commandline_argument;
+			break;
+		case ':':
+			fprintf(stderr, "Option -%c requires an operand\n",
+				isc_commandline_option);
+			errflg++;
+			break;
+		case '?':
+		default:
+			fprintf(stderr, "Unrecognised option: -%c\n",
+				isc_commandline_option);
+			errflg++;
+		}
+	}
+
+	if (errflg) {
+		fprintf(stderr, "Usage:\n");
+		fprintf(stderr, "\tpkcs11-tokens [-m module]\n");
+		exit(1);
+	}
+
+	if (isc_mem_create(0, 0, &mctx) != ISC_R_SUCCESS) {
+		fprintf(stderr, "isc_mem_create() failed\n");
+		exit(1);
+	}
+
+	pk11_result_register();
+
+	/* Initialize the CRYPTOKI library */
+	if (lib_name != NULL)
+		pk11_set_lib_name(lib_name);
+
+	result = pk11_get_session(&pctx, OP_ANY, ISC_FALSE, ISC_FALSE,
+				  ISC_FALSE, NULL, 0);
+	if (result == PK11_R_NORANDOMSERVICE ||
+	    result == PK11_R_NODIGESTSERVICE ||
+	    result == PK11_R_NOAESSERVICE) {
+		fprintf(stderr, "Warning: %s\n", isc_result_totext(result));
+		fprintf(stderr, "This HSM will not work with BIND 9 "
+				"using native PKCS#11.\n\n");
+	} else if (result != ISC_R_SUCCESS) {
+		fprintf(stderr, "Unrecoverable error initializing "
+				"PKCS#11: %s\n", isc_result_totext(result));
+		exit(1);
+	}
+
+	pk11_dump_tokens();
+
+	if (pctx.handle != NULL)
+		pk11_return_session(&pctx);
+	(void) pk11_finalize();
+
+	isc_mem_destroy(&mctx);
+
+	exit(0);
+}
diff --git a/bin/pkcs11/pkcs11-tokens.docbook b/bin/pkcs11/pkcs11-tokens.docbook
new file mode 100644
index 0000000..44dc7cd
--- /dev/null
+++ b/bin/pkcs11/pkcs11-tokens.docbook
@@ -0,0 +1,86 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+               "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
+	       [<!ENTITY mdash "&#8212;">]>
+<!--
+ - Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+ -
+ - Permission to use, copy, modify, and/or distribute this software for any
+ - purpose with or without fee is hereby granted, provided that the above
+ - copyright notice and this permission notice appear in all copies.
+ -
+ - THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ - AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ - PERFORMANCE OF THIS SOFTWARE.
+-->
+
+<!-- $Id$ -->
+<refentry id="man.pkcs11-tokens">
+  <refentryinfo>
+    <date>August 25, 2013</date>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle><application>pkcs11-tokens</application></refentrytitle>
+    <manvolnum>8</manvolnum>
+    <refmiscinfo>BIND9</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname><application>pkcs11-tokens</application></refname>
+    <refpurpose>list PKCS#11 available tokens</refpurpose>
+  </refnamediv>
+
+  <docinfo>
+    <copyright>
+      <year>2013</year>
+      <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
+    </copyright>
+  </docinfo>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>pkcs11-tokens</command>
+      <arg><option>-m <replaceable class="parameter">module</replaceable></option></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>DESCRIPTION</title>
+    <para>
+      <command>pkcs11-tokens</command>
+      lists the PKCS#11 available tokens with defaults from the slot/token
+      scan performed at application initialization.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>ARGUMENTS</title>
+    <variablelist>
+      <varlistentry>
+        <term>-m <replaceable class="parameter">module</replaceable></term>
+        <listitem>
+          <para>
+            Specify the PKCS#11 provider module.  This must be the full
+            path to a shared library object implementing the PKCS#11 API
+            for the device.
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>AUTHOR</title>
+    <para><corpauthor>Internet Systems Consortium</corpauthor>
+    </para>
+  </refsect1>
+
+</refentry><!--
+ - Local variables:
+ - mode: sgml
+ - End:
+-->
diff --git a/bin/pkcs11/pkcs11-tokens.html b/bin/pkcs11/pkcs11-tokens.html
new file mode 100644
index 0000000..45d7243
--- /dev/null
+++ b/bin/pkcs11/pkcs11-tokens.html
@@ -0,0 +1,58 @@
+<!--
+ - Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+ - 
+ - Permission to use, copy, modify, and/or distribute this software for any
+ - purpose with or without fee is hereby granted, provided that the above
+ - copyright notice and this permission notice appear in all copies.
+ - 
+ - THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ - AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ - PERFORMANCE OF THIS SOFTWARE.
+-->
+<!-- $Id$ -->
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+<title>pkcs11-tokens</title>
+<meta name="generator" content="DocBook XSL Stylesheets V1.71.1">
+</head>
+<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="refentry" lang="en">
+<a name="man.pkcs11-tokens"></a><div class="titlepage"></div>
+<div class="refnamediv">
+<h2>Name</h2>
+<p><span class="application">pkcs11-tokens</span> &#8212; list PKCS#11 available tokens</p>
+</div>
+<div class="refsynopsisdiv">
+<h2>Synopsis</h2>
+<div class="cmdsynopsis"><p><code class="command">pkcs11-tokens</code>  [<code class="option">-m <em class="replaceable"><code>module</code></em></code>]</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543342"></a><h2>DESCRIPTION</h2>
+<p>
+      <span><strong class="command">pkcs11-tokens</strong></span>
+      lists the PKCS#11 available tokens with defaults from the slot/token
+      scan performed at application initialization.
+    </p>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543355"></a><h2>ARGUMENTS</h2>
+<div class="variablelist"><dl>
+<dt><span class="term">-m <em class="replaceable"><code>module</code></em></span></dt>
+<dd><p>
+            Specify the PKCS#11 provider module.  This must be the full
+            path to a shared library object implementing the PKCS#11 API
+            for the device.
+          </p></dd>
+</dl></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543382"></a><h2>AUTHOR</h2>
+<p><span class="corpauthor">Internet Systems Consortium</span>
+    </p>
+</div>
+</div></body>
+</html>
diff --git a/bin/rndc/Makefile.in b/bin/rndc/Makefile.in
index f6100df..bc0657a 100644
--- a/bin/rndc/Makefile.in
+++ b/bin/rndc/Makefile.in
@@ -45,7 +45,8 @@ BIND9DEPLIBS =	../../lib/bind9/libbind9.@A@
 LIBS =		${ISCLIBS} @LIBS@
 NOSYMLIBS =	${ISCNOSYMLIBS} @LIBS@
 
-RNDCDEPLIBS =	${ISCCFGDEPLIBS} ${ISCCCDEPLIBS} ${BIND9DEPLIBS} ${DNSDEPLIBS} ${ISCDEPLIBS}
+RNDCDEPLIBS =	${ISCCFGDEPLIBS} ${ISCCCDEPLIBS} ${BIND9DEPLIBS} \
+		${DNSDEPLIBS} ${ISCDEPLIBS}
 
 CONFDEPLIBS =	${DNSDEPLIBS} ${ISCDEPLIBS}
 
diff --git a/bin/tests/Makefile.in b/bin/tests/Makefile.in
index bc040a3..2020bf4 100644
--- a/bin/tests/Makefile.in
+++ b/bin/tests/Makefile.in
@@ -42,7 +42,7 @@ LWRESDEPLIBS =	../../lib/lwres/liblwres.@A@
 LIBS =		@LIBS@
 
 SUBDIRS =	atomic db dst master mem hashes names net rbt resolver \
-		sockaddr tasks timers system
+		sockaddr tasks timers system @PKCS11_TOOLS@
 
 # Test programs that are built by default:
 # cfg_test is needed for regenerating doc/misc/options
@@ -173,139 +173,139 @@ backtrace_test@EXEEXT@: backtrace_test_nosymtbl@EXEEXT@
 nsecify@EXEEXT@: nsecify.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ nsecify.@O@ \
 		${DNSLIBS} ${ISCLIBS} ${LIBS}
-
+ 
 byaddr_test@EXEEXT@: byaddr_test.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ byaddr_test.@O@ \
 		${DNSLIBS} ${ISCLIBS} ${LIBS}
-
+ 
 byname_test@EXEEXT@: byname_test.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ byname_test.@O@ \
 		${DNSLIBS} ${ISCLIBS} ${LIBS}
-
+ 
 lex_test@EXEEXT@: lex_test.@O@ ${ISCDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ lex_test.@O@ \
 		${ISCLIBS} ${LIBS}
-
+ 
 lfsr_test@EXEEXT@: lfsr_test.@O@ ${ISCDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ lfsr_test.@O@ \
 		${ISCLIBS} ${LIBS}
-
+ 
 log_test@EXEEXT@: log_test.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ log_test.@O@ \
 		${DNSLIBS} ${ISCLIBS} ${LIBS}
-
+ 
 name_test@EXEEXT@: name_test.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ name_test.@O@ \
 		${DNSLIBS} ${ISCLIBS} ${LIBS}
-
+ 
 hash_test@EXEEXT@: hash_test.@O@ ${ISCDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ hash_test.@O@ \
 		${ISCLIBS} ${LIBS}
-
+ 
 entropy_test@EXEEXT@: entropy_test.@O@ ${ISCDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ entropy_test.@O@ \
 		${ISCLIBS} ${LIBS}
-
+ 
 entropy2_test@EXEEXT@: entropy2_test.@O@ ${ISCDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ entropy2_test.@O@ \
 		${ISCLIBS} ${LIBS}
-
+ 
 sock_test@EXEEXT@: sock_test.@O@ ${ISCDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ sock_test.@O@ \
 		${ISCLIBS} ${LIBS}
-
+ 
 sym_test@EXEEXT@: sym_test.@O@ ${ISCDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ sym_test.@O@ \
 		${ISCLIBS} ${LIBS}
-
+ 
 task_test@EXEEXT@: task_test.@O@ ${ISCDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ task_test.@O@ \
 		${ISCLIBS} ${LIBS}
-
+ 
 shutdown_test@EXEEXT@: shutdown_test.@O@ ${ISCDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ shutdown_test.@O@ \
 		${ISCLIBS} ${LIBS}
-
+ 
 timer_test@EXEEXT@: timer_test.@O@ ${ISCDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ timer_test.@O@ \
 		${ISCLIBS} ${LIBS}
-
+ 
 ratelimiter_test@EXEEXT@: ratelimiter_test.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ ratelimiter_test.@O@ \
 		${DNSLIBS} ${ISCLIBS} ${LIBS}
-
+ 
 rbt_test@EXEEXT@: rbt_test.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ rbt_test.@O@ \
 		${DNSLIBS} ${ISCLIBS} ${LIBS}
-
+ 
 rdata_test@EXEEXT@: rdata_test.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ rdata_test.@O@ \
 		${DNSLIBS} ${ISCLIBS} ${LIBS}
-
+ 
 rwlock_test@EXEEXT@: rwlock_test.@O@ ${ISCDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ rwlock_test.@O@ \
 		${ISCLIBS} ${LIBS}
-
+ 
 wire_test@EXEEXT@: wire_test.@O@ printmsg.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ wire_test.@O@ printmsg.@O@ \
 		${DNSLIBS} ${ISCLIBS} ${LIBS}
-
+ 
 master_test@EXEEXT@: master_test.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ master_test.@O@ \
 		${DNSLIBS} ${ISCLIBS} ${LIBS}
-
+ 
 db_test@EXEEXT@: db_test.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ db_test.@O@ \
 		${DNSLIBS} ${ISCLIBS} ${LIBS}
-
+ 
 compress_test@EXEEXT@: compress_test.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ compress_test.@O@ \
 		${DNSLIBS} ${ISCLIBS} ${LIBS}
-
+ 
 mempool_test@EXEEXT@: mempool_test.@O@ ${ISCDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ mempool_test.@O@ \
 		${ISCLIBS} ${LIBS}
-
+ 
 serial_test@EXEEXT@: serial_test.@O@ ${ISCDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ serial_test.@O@ \
 		${ISCLIBS} ${LIBS}
-
+ 
 zone_test@EXEEXT@: zone_test.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ zone_test.@O@ \
 		${DNSLIBS} ${ISCLIBS} ${LIBS}
-
+ 
 fsaccess_test@EXEEXT@: fsaccess_test.@O@ ${ISCDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ fsaccess_test.@O@ \
 		${ISCLIBS} ${LIBS}
-
+ 
 inter_test@EXEEXT@: inter_test.@O@ ${ISCDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ inter_test.@O@ \
 		${ISCLIBS} ${LIBS}
-
+ 
 keyboard_test@EXEEXT@: keyboard_test.@O@ ${ISCDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ keyboard_test.@O@ \
 		${ISCLIBS} ${LIBS}
-
+ 
 lwresconf_test@EXEEXT@: lwresconf_test.@O@ ${ISCDEPLIBS} ${LWRESDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ lwresconf_test.@O@ \
 		${LWRESLIBS} ${ISCLIBS} ${LIBS}
-
+ 
 lwres_test@EXEEXT@: lwres_test.@O@ ${ISCDEPLIBS} ${LWRESDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ lwres_test.@O@ \
-		${LWRESLIBS} ${ISCLIBS} ${LIBS}
-
-gxbn_test@EXEEXT@: gxbn_test.@O@ ${LWRESDEPLIBS}
+		${LWRESLIBS} ${ISCLIBS}  ${LIBS}
+ 
+ gxbn_test@EXEEXT@: gxbn_test.@O@ ${LWRESDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ gxbn_test.@O@ \
 		${LWRESLIBS} ${ISCLIBS} ${LIBS}
-
-gxba_test@EXEEXT@: gxba_test.@O@ ${LWRESDEPLIBS}
+ 
+ gxba_test@EXEEXT@: gxba_test.@O@ ${LWRESDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ gxba_test.@O@ \
 		${LWRESLIBS} ${ISCLIBS} ${LIBS}
-
+ 
 sig0_test@EXEEXT@: sig0_test.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ sig0_test.@O@ \
 		${DNSLIBS} ${ISCLIBS} ${LIBS}
-
+ 
 cfg_test@EXEEXT@: cfg_test.@O@ ${ISCCFGDEPLIBS} ${ISCDEPLIBS}
 	${LIBTOOL_MODE_LINK} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ cfg_test.@O@ \
 		${ISCCFGLIBS} ${DNSLIBS} ${ISCLIBS} ${LIBS}
diff --git a/bin/tests/dst/dst_test.c b/bin/tests/dst/dst_test.c
index bf305d8..240dc6f 100644
--- a/bin/tests/dst/dst_test.c
+++ b/bin/tests/dst/dst_test.c
@@ -30,6 +30,7 @@
 #include <isc/string.h>		/* Required for HP/UX (and others?) */
 
 #include <dns/fixedname.h>
+#include <dns/log.h>
 #include <dns/name.h>
 #include <dns/result.h>
 
@@ -58,7 +59,8 @@ use(dst_key_t *key, isc_mem_t *mctx) {
 	isc_buffer_add(&databuf, strlen(data));
 	isc_buffer_usedregion(&databuf, &datareg);
 
-	ret = dst_context_create(key, mctx, &ctx);
+	ret = dst_context_create3(key, mctx,
+				  DNS_LOGCATEGORY_GENERAL, ISC_TRUE, &ctx);
 	if (ret != ISC_R_SUCCESS) {
 		printf("contextcreate(%d) returned: %s\n", dst_key_alg(key),
 		       isc_result_totext(ret));
@@ -78,7 +80,8 @@ use(dst_key_t *key, isc_mem_t *mctx) {
 
 	isc_buffer_forward(&sigbuf, 1);
 	isc_buffer_remainingregion(&sigbuf, &sigreg);
-	ret = dst_context_create(key, mctx, &ctx);
+	ret = dst_context_create3(key, mctx,
+				  DNS_LOGCATEGORY_GENERAL, ISC_FALSE, &ctx);
 	if (ret != ISC_R_SUCCESS) {
 		printf("contextcreate(%d) returned: %s\n", dst_key_alg(key),
 		       isc_result_totext(ret));
diff --git a/bin/tests/dst/t_dst.c b/bin/tests/dst/t_dst.c
index e431c95..59c7835 100644
--- a/bin/tests/dst/t_dst.c
+++ b/bin/tests/dst/t_dst.c
@@ -108,7 +108,8 @@ use(dst_key_t *key, isc_mem_t *mctx, isc_result_t exp_result, int *nfails) {
 	isc_buffer_add(&databuf, strlen(data));
 	isc_buffer_usedregion(&databuf, &datareg);
 
-	ret = dst_context_create(key, mctx, &ctx);
+	ret = dst_context_create3(key, mctx,
+				  DNS_LOGCATEGORY_GENERAL, ISC_TRUE, &ctx);
 	if (ret != exp_result) {
 		t_info("dst_context_create(%d) returned (%s) expected (%s)\n",
 		       dst_key_alg(key), dst_result_totext(ret),
@@ -137,7 +138,8 @@ use(dst_key_t *key, isc_mem_t *mctx, isc_result_t exp_result, int *nfails) {
 	dst_context_destroy(&ctx);
 
 	isc_buffer_remainingregion(&sigbuf, &sigreg);
-	ret = dst_context_create(key, mctx, &ctx);
+	ret = dst_context_create3(key, mctx,
+				  DNS_LOGCATEGORY_GENERAL, ISC_FALSE, &ctx);
 	if (ret != ISC_R_SUCCESS) {
 		t_info("dst_context_create(%d) returned (%s)\n",
 		       dst_key_alg(key), dst_result_totext(ret));
@@ -783,7 +785,9 @@ t2_sigchk(char *datapath, char *sigpath, char *keyname,
 	memset(sig, 0, sizeof(sig));
 	isc_buffer_init(&sigbuf, sig, sizeof(sig));
 
-	isc_result = dst_context_create(key, mctx, &ctx);
+	isc_result = dst_context_create3(key, mctx,
+					 DNS_LOGCATEGORY_GENERAL,
+					 ISC_TRUE, &ctx);
 	if (isc_result != ISC_R_SUCCESS) {
 		t_info("dst_context_create(%d) failed %s\n",
 		       dst_result_totext(isc_result));
@@ -849,7 +853,9 @@ t2_sigchk(char *datapath, char *sigpath, char *keyname,
 	if (strstr(expected_result, "!"))
 		exp_res = 1;
 
-	isc_result = dst_context_create(key, mctx, &ctx);
+	isc_result = dst_context_create3(key, mctx,
+					 DNS_LOGCATEGORY_GENERAL,
+					 ISC_FALSE, &ctx);
 	if (isc_result != ISC_R_SUCCESS) {
 		t_info("dst_context_create returned %s\n",
 			isc_result_totext(isc_result));
diff --git a/bin/tests/pkcs11/Makefile.in b/bin/tests/pkcs11/Makefile.in
new file mode 100644
index 0000000..0a6281f
--- /dev/null
+++ b/bin/tests/pkcs11/Makefile.in
@@ -0,0 +1,49 @@
+# Copyright (C) 2009, 2012  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+srcdir =	@srcdir@
+VPATH =		@srcdir@
+top_srcdir =	@top_srcdir@
+
+@BIND9_MAKE_INCLUDES@
+
+PROVIDER =	@PKCS11_PROVIDER@
+
+CINCLUDES =	${ISC_INCLUDES}
+
+CDEFINES =	-DPK11_LIB_LOCATION=\"${PROVIDER}\"
+
+ISCLIBS =	../../../lib/isc/libisc.@A@
+
+LIBS =		${ISCLIBS} @LIBS@
+
+SUBDIRS =	benchmarks
+
+TARGETS =	pkcs11-md5sum@EXEEXT@ pkcs11-hmacmd5@EXEEXT@
+SRCS =		pkcs11-md5sum.c pkcs11-hmacmd5.c
+
+@BIND9_MAKE_RULES@
+
+pkcs11-md5sum@EXEEXT@: @srcdir@/pkcs11-md5sum.c
+	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${ALL_CFLAGS} ${LDFLAGS} \
+	-o $@ @srcdir@/pkcs11-md5sum.c ${LIBS}
+
+pkcs11-hmacmd5@EXEEXT@: @srcdir@/pkcs11-hmacmd5.c
+	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${ALL_CFLAGS} ${LDFLAGS} \
+	-o $@ @srcdir@/pkcs11-hmacmd5.c ${LIBS}
+
+test:
+
+clean distclean::
+	rm -f ${TARGETS}
diff --git a/bin/tests/pkcs11/benchmarks/Makefile.in b/bin/tests/pkcs11/benchmarks/Makefile.in
new file mode 100644
index 0000000..cd0347c
--- /dev/null
+++ b/bin/tests/pkcs11/benchmarks/Makefile.in
@@ -0,0 +1,79 @@
+# Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# $Id$
+
+srcdir =	@srcdir@
+VPATH =		@srcdir@
+top_srcdir =	@top_srcdir@
+
+@BIND9_MAKE_INCLUDES@
+
+PROVIDER =	@PKCS11_PROVIDER@
+
+CINCLUDES =	${ISC_INCLUDES}
+
+CDEFINES =	-DPK11_LIB_LOCATION=\"${PROVIDER}\"
+
+ISCLIBS =	../../../../lib/isc/libisc.@A@
+
+LIBS =		${ISCLIBS} @LIBS@
+
+SUBDIRS =
+
+TARGETS =	session@EXEEXT@ login@EXEEXT@ random@EXEEXT@ \
+		sha1@EXEEXT@ create@EXEEXT@ find@EXEEXT@ \
+		pubrsa@EXEEXT@ privrsa@EXEEXT@ genrsa@EXEEXT@ \
+		sign@EXEEXT@ verify@EXEEXT@
+
+SRCS =		session.c login.c random.c sha1.c create.c find.c \
+		pubrsa.c privrsa.c genrsa.c sign.c verify.c
+
+@BIND9_MAKE_RULES@
+
+session@EXEEXT@: @srcdir@/session.c
+	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${ALL_CFLAGS} ${LDFLAGS} -o $@ @srcdir@/session.c ${LIBS}
+
+login@EXEEXT@: @srcdir@/login.c
+	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${ALL_CFLAGS} ${LDFLAGS} -o $@ @srcdir@/login.c ${LIBS}
+
+random@EXEEXT@: @srcdir@/random.c
+	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${ALL_CFLAGS} ${LDFLAGS} -o $@ @srcdir@/random.c ${LIBS}
+
+sha1@EXEEXT@: @srcdir@/sha1.c
+	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${ALL_CFLAGS} ${LDFLAGS} -o $@ @srcdir@/sha1.c ${LIBS}
+
+create@EXEEXT@: @srcdir@/create.c
+	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${ALL_CFLAGS} ${LDFLAGS} -o $@ @srcdir@/create.c ${LIBS}
+
+find@EXEEXT@: @srcdir@/find.c
+	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${ALL_CFLAGS} ${LDFLAGS} -o $@ @srcdir@/find.c ${LIBS}
+
+pubrsa@EXEEXT@: @srcdir@/pubrsa.c
+	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${ALL_CFLAGS} ${LDFLAGS} -o $@ @srcdir@/pubrsa.c ${LIBS}
+
+privrsa@EXEEXT@: @srcdir@/privrsa.c
+	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${ALL_CFLAGS} ${LDFLAGS} -o $@ @srcdir@/privrsa.c ${LIBS}
+
+genrsa@EXEEXT@: @srcdir@/genrsa.c
+	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${ALL_CFLAGS} ${LDFLAGS} -o $@ @srcdir@/genrsa.c ${LIBS}
+
+sign@EXEEXT@: @srcdir@/sign.c
+	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${ALL_CFLAGS} ${LDFLAGS} -o $@ @srcdir@/sign.c ${LIBS}
+
+verify@EXEEXT@: @srcdir@/verify.c
+	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${ALL_CFLAGS} ${LDFLAGS} -o $@ @srcdir@/verify.c ${LIBS}
+
+clean distclean::
+	rm -f ${TARGETS}
diff --git a/bin/tests/pkcs11/benchmarks/create.c b/bin/tests/pkcs11/benchmarks/create.c
new file mode 100644
index 0000000..d0d8c77
--- /dev/null
+++ b/bin/tests/pkcs11/benchmarks/create.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Portions copyright (c) 2008 Nominet UK.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+/* create [-m module] [-s $slot] [-p pin] [-t] [-n count] */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <isc/commandline.h>
+#include <isc/result.h>
+#include <isc/types.h>
+
+#include <pk11/pk11.h>
+#include <pk11/result.h>
+
+#if !(defined(HAVE_GETPASSPHRASE) || (defined (__SVR4) && defined (__sun)))
+#define getpassphrase(x)	getpass(x)
+#endif
+
+#ifndef HAVE_CLOCK_GETTIME
+#ifndef CLOCK_REALTIME
+#define CLOCK_REALTIME 0
+#endif
+
+int
+clock_gettime(int32_t id, struct timespec *tp)
+{
+	struct timeval tv;
+	int result;
+
+	result = gettimeofday(&tv, NULL);
+	if (result)
+		return (result);
+	tp->tv_sec = tv.tv_sec;
+	tp->tv_nsec = (long) tv.tv_usec * 1000;
+	return (result);
+}
+#endif
+
+CK_BYTE buf[1024];
+char label[16];
+
+static CK_BBOOL truevalue = TRUE;
+static CK_BBOOL falsevalue = FALSE;
+
+int
+main(int argc, char *argv[]) {
+	isc_result_t result;
+	CK_RV rv;
+	CK_SLOT_ID slot = 0;
+	CK_SESSION_HANDLE hSession = CK_INVALID_HANDLE;
+	CK_OBJECT_HANDLE *hKey;
+	CK_OBJECT_CLASS kClass = CKO_DATA;
+	CK_ULONG len = sizeof(buf);
+	CK_ATTRIBUTE kTemplate[] =
+	{
+		{ CKA_CLASS, &kClass, (CK_ULONG) sizeof(kClass) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_LABEL, (CK_BYTE_PTR) label, (CK_ULONG) sizeof(label) },
+		{ CKA_VALUE, buf, (CK_ULONG) sizeof(buf) }
+	};
+	pk11_context_t pctx;
+	char *lib_name = NULL;
+	char *pin = NULL;
+	int error = 0;
+	int c, errflg = 0;
+	int ontoken  = 0;
+	unsigned int count = 1000;
+	unsigned int i;
+	struct timespec starttime;
+	struct timespec endtime;
+
+	while ((c = isc_commandline_parse(argc, argv, ":m:s:p:tn:")) != -1) {
+		switch (c) {
+		case 'm':
+			lib_name = isc_commandline_argument;
+			break;
+		case 's':
+			slot = atoi(isc_commandline_argument);
+			break;
+		case 't':
+			ontoken = 1;
+			break;
+		case 'p':
+			pin = isc_commandline_argument;
+			break;
+		case 'n':
+			count = atoi(isc_commandline_argument);
+			break;
+		case ':':
+			fprintf(stderr,
+				"Option -%c requires an operand\n",
+				isc_commandline_option);
+			errflg++;
+			break;
+		case '?':
+		default:
+			fprintf(stderr, "Unrecognised option: -%c\n",
+				isc_commandline_option);
+			errflg++;
+		}
+	}
+
+	if (errflg) {
+		fprintf(stderr, "Usage:\n");
+		fprintf(stderr,
+			"\tcreate [-m module] [-s slot] [-t] [-n count]\n");
+		exit(1);
+	}
+
+	pk11_result_register();
+
+	/* Allocate hanles */
+	hKey = (CK_SESSION_HANDLE *)
+		malloc(count * sizeof(CK_SESSION_HANDLE));
+	if (hKey == NULL) {
+		perror("malloc");
+		exit(1);
+	}
+	for (i = 0; i < count; i++)
+		hKey[i] = CK_INVALID_HANDLE;
+
+	/* Initialize the CRYPTOKI library */
+	if (lib_name != NULL)
+		pk11_set_lib_name(lib_name);
+
+	if (pin == NULL)
+		pin = getpassphrase("Enter Pin: ");
+
+	result = pk11_get_session(&pctx, OP_ANY, ISC_TRUE, ISC_TRUE,
+				  ISC_TRUE, (const char *) pin, slot);
+	if ((result != ISC_R_SUCCESS) &&
+	    (result != PK11_R_NORANDOMSERVICE) &&
+	    (result != PK11_R_NODIGESTSERVICE) &&
+	    (result != PK11_R_NOAESSERVICE)) {
+		fprintf(stderr, "Error initializing PKCS#11: %s\n",
+			isc_result_totext(result));
+		exit(1);
+	}
+
+	if (pin != NULL)
+		memset(pin, 0, strlen((char *)pin));
+
+	hSession = pctx.session;
+
+	/* Randomize the buffer */
+	rv = pkcs_C_GenerateRandom(hSession, buf, len);
+	if (rv != CKR_OK) {
+		fprintf(stderr, "C_GenerateRandom: Error = 0x%.8lX\n", rv);
+		goto exit_objects;
+	}
+
+	if (ontoken)
+		kTemplate[1].pValue = &truevalue;
+
+	if (clock_gettime(CLOCK_REALTIME, &starttime) < 0) {
+		perror("clock_gettime(start)");
+		goto exit_objects;
+	}
+
+	for (i = 0; i < count; i++) {
+		(void) snprintf(label, sizeof(label), "obj%u", i);
+		kTemplate[3].ulValueLen = strlen(label);
+		rv = pkcs_C_CreateObject(hSession, kTemplate, 5, &hKey[i]);
+		if (rv != CKR_OK) {
+			fprintf(stderr,
+				"C_CreateObject[%u]: Error = 0x%.8lX\n",
+				i, rv);
+			error = 1;
+			if (i == 0)
+				goto exit_objects;
+			break;
+		}
+	}
+
+	if (clock_gettime(CLOCK_REALTIME, &endtime) < 0) {
+		perror("clock_gettime(end)");
+		goto exit_objects;
+	}
+
+	endtime.tv_sec -= starttime.tv_sec;
+	endtime.tv_nsec -= starttime.tv_nsec;
+	while (endtime.tv_nsec < 0) {
+		endtime.tv_sec -= 1;
+		endtime.tv_nsec += 1000000000;
+	}
+	printf("%u created objects in %ld.%09lds\n", i,
+	       endtime.tv_sec, endtime.tv_nsec);
+	if (i > 0)
+		printf("%g created objects/s\n",
+		       1024 * i / ((double) endtime.tv_sec +
+				   (double) endtime.tv_nsec / 1000000000.));
+
+    exit_objects:
+	for (i = 0; i < count; i++) {
+		/* Destroy objects */
+		if (hKey[i] == CK_INVALID_HANDLE)
+			continue;
+		rv = pkcs_C_DestroyObject(hSession, hKey[i]);
+		if ((rv != CKR_OK) && !errflg) {
+			fprintf(stderr,
+				"C_DestroyObject[%u]: Error = 0x%.8lX\n",
+				i, rv);
+			errflg = 1;
+		}
+	}
+
+	free(hKey);
+	pk11_return_session(&pctx);
+	(void) pk11_finalize();
+
+	exit(error);
+}
diff --git a/bin/tests/pkcs11/benchmarks/find.c b/bin/tests/pkcs11/benchmarks/find.c
new file mode 100644
index 0000000..e22b17e
--- /dev/null
+++ b/bin/tests/pkcs11/benchmarks/find.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Portions copyright (c) 2008 Nominet UK.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* find [-m module] [-s $slot] [-p pin] [-n count] */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <isc/commandline.h>
+#include <isc/result.h>
+#include <isc/types.h>
+
+#include <pk11/pk11.h>
+#include <pk11/result.h>
+
+#if !(defined(HAVE_GETPASSPHRASE) || (defined (__SVR4) && defined (__sun)))
+#define getpassphrase(x)	getpass(x)
+#endif
+
+#ifndef HAVE_CLOCK_GETTIME
+#ifndef CLOCK_REALTIME
+#define CLOCK_REALTIME 0
+#endif
+
+int
+clock_gettime(int32_t id, struct timespec *tp)
+{
+	struct timeval tv;
+	int result;
+
+	result = gettimeofday(&tv, NULL);
+	if (result)
+		return (result);
+	tp->tv_sec = tv.tv_sec;
+	tp->tv_nsec = (long) tv.tv_usec * 1000;
+	return (result);
+}
+#endif
+
+CK_BYTE label[] = "foo??bar!!";
+
+int
+main(int argc, char *argv[]) {
+	isc_result_t result;
+	CK_RV rv;
+	CK_SLOT_ID slot = 0;
+	CK_SESSION_HANDLE hSession = CK_INVALID_HANDLE;
+	CK_ATTRIBUTE sTemplate[] =
+	{
+		{ CKA_LABEL, label, (CK_ULONG) sizeof(label) },
+	};
+	CK_OBJECT_HANDLE sKey = CK_INVALID_HANDLE;
+	CK_ULONG found = 0;
+	pk11_context_t pctx;
+	pk11_optype_t op_type = OP_RSA;
+	char *lib_name = NULL;
+	char *pin = NULL;
+	int error = 0;
+	int c, errflg = 0;
+	unsigned int count = 1000;
+	unsigned int i;
+	struct timespec starttime;
+	struct timespec endtime;
+
+	while ((c = isc_commandline_parse(argc, argv, ":m:s:p:n:")) != -1) {
+		switch (c) {
+		case 'm':
+			lib_name = isc_commandline_argument;
+			break;
+		case 's':
+			op_type = OP_ANY;
+			slot = atoi(isc_commandline_argument);
+			break;
+		case 'p':
+			pin = isc_commandline_argument;
+			break;
+		case 'n':
+			count = atoi(isc_commandline_argument);
+			break;
+		case ':':
+			fprintf(stderr,
+				"Option -%c requires an operand\n",
+				isc_commandline_option);
+			errflg++;
+			break;
+		case '?':
+		default:
+			fprintf(stderr, "Unrecognised option: -%c\n",
+				isc_commandline_option);
+			errflg++;
+		}
+	}
+
+	if (errflg) {
+		fprintf(stderr, "Usage:\n");
+		fprintf(stderr,
+			"\tfind [-m module] [-s slot] [-p pin] [-n count]\n");
+		exit(1);
+	}
+
+	pk11_result_register();
+
+	/* Initialize the CRYPTOKI library */
+	if (lib_name != NULL)
+		pk11_set_lib_name(lib_name);
+
+	if (pin == NULL)
+		pin = getpassphrase("Enter Pin: ");
+
+	result = pk11_get_session(&pctx, op_type, ISC_FALSE, ISC_FALSE,
+				  ISC_TRUE, (const char *) pin, slot);
+	if ((result != ISC_R_SUCCESS) &&
+	    (result != PK11_R_NORANDOMSERVICE) &&
+	    (result != PK11_R_NODIGESTSERVICE) &&
+	    (result != PK11_R_NOAESSERVICE)) {
+		fprintf(stderr, "Error initializing PKCS#11: %s\n",
+			isc_result_totext(result));
+		exit(1);
+	}
+
+	if (pin != NULL)
+		memset(pin, 0, strlen((char *)pin));
+
+	hSession = pctx.session;
+
+	if (clock_gettime(CLOCK_REALTIME, &starttime) < 0) {
+		perror("clock_gettime(start)");
+		goto exit_objects;
+	}
+
+	for (i = 0; !error && (i < count); i++) {
+		rv = pkcs_C_FindObjectsInit(hSession, sTemplate, 1);
+		if (rv != CKR_OK) {
+			fprintf(stderr,
+				"C_FindObjectsInit[%u]: Error = 0x%.8lX\n",
+				i, rv);
+			error = 1;
+			break;
+		}
+
+		rv = pkcs_C_FindObjects(hSession, &sKey, 1, &found);
+		if (rv != CKR_OK) {
+			fprintf(stderr,
+				"C_FindObjects[%u]: Error = 0x%.8lX\n",
+				i, rv);
+			error = 1;
+			/* no break here! */
+		}
+
+		rv = pkcs_C_FindObjectsFinal(hSession);
+		if (rv != CKR_OK) {
+			fprintf(stderr,
+				"C_FindObjectsFinal[%u]: Error = 0x%.8lX\n",
+				i, rv);
+			error = 1;
+			break;
+		}
+	}
+
+	if (clock_gettime(CLOCK_REALTIME, &endtime) < 0) {
+		perror("clock_gettime(end)");
+		goto exit_objects;
+	}
+
+	endtime.tv_sec -= starttime.tv_sec;
+	endtime.tv_nsec -= starttime.tv_nsec;
+	while (endtime.tv_nsec < 0) {
+		endtime.tv_sec -= 1;
+		endtime.tv_nsec += 1000000000;
+	}
+	printf("%u object searches in %ld.%09lds\n", i,
+	       endtime.tv_sec, endtime.tv_nsec);
+	if (i > 0)
+		printf("%g object searches/s\n",
+		       1024 * i / ((double) endtime.tv_sec +
+				   (double) endtime.tv_nsec / 1000000000.));
+
+    exit_objects:
+	pk11_return_session(&pctx);
+	(void) pk11_finalize();
+		
+	exit(error);
+}
diff --git a/bin/tests/pkcs11/benchmarks/genrsa.c b/bin/tests/pkcs11/benchmarks/genrsa.c
new file mode 100644
index 0000000..e9d3c2a
--- /dev/null
+++ b/bin/tests/pkcs11/benchmarks/genrsa.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Portions copyright (c) 2008 Nominet UK.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+/* genrsa [-m module] [-s $slot] [-p pin] [-t] [-b bits] [-n count] */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <isc/commandline.h>
+#include <isc/result.h>
+#include <isc/types.h>
+
+#include <pk11/pk11.h>
+#include <pk11/result.h>
+
+#if !(defined(HAVE_GETPASSPHRASE) || (defined (__SVR4) && defined (__sun)))
+#define getpassphrase(x)	getpass(x)
+#endif
+
+#ifndef HAVE_CLOCK_GETTIME
+#ifndef CLOCK_REALTIME
+#define CLOCK_REALTIME 0
+#endif
+
+int
+clock_gettime(int32_t id, struct timespec *tp)
+{
+	struct timeval tv;
+	int result;
+
+	result = gettimeofday(&tv, NULL);
+	if (result)
+		return (result);
+	tp->tv_sec = tv.tv_sec;
+	tp->tv_nsec = (long) tv.tv_usec * 1000;
+	return (result);
+}
+#endif
+
+static CK_BBOOL truevalue = TRUE;
+static CK_BBOOL falsevalue = FALSE;
+
+int
+main(int argc, char *argv[]) {
+	isc_result_t result;
+	CK_RV rv;
+	CK_SLOT_ID slot = 0;
+	CK_SESSION_HANDLE hSession = CK_INVALID_HANDLE;
+	CK_MECHANISM mech = { CKM_RSA_PKCS_KEY_PAIR_GEN, NULL, 0 };
+	CK_OBJECT_HANDLE *pubKey;
+	CK_OBJECT_HANDLE *privKey;
+	CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY;
+	CK_OBJECT_CLASS privClass = CKO_PRIVATE_KEY;
+	CK_KEY_TYPE kType = CKK_RSA;
+	CK_ULONG bits = 1024;
+	CK_BYTE exponent[] = { 0x01, 0x00, 0x01 };
+	CK_ATTRIBUTE pubTemplate[] =
+	{
+		{ CKA_CLASS, &pubClass, (CK_ULONG) sizeof(pubClass) },
+		{ CKA_KEY_TYPE, &kType, (CK_ULONG) sizeof(kType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_VERIFY, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_MODULUS_BITS, &bits, (CK_ULONG) sizeof(bits) },
+		{ CKA_PUBLIC_EXPONENT, exponent, (CK_ULONG) sizeof(exponent) }
+	};
+	CK_ATTRIBUTE privTemplate[] =
+	{
+		{ CKA_CLASS, &privClass, (CK_ULONG) sizeof(privClass) },
+		{ CKA_KEY_TYPE, &kType, (CK_ULONG) sizeof(kType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+	};
+	pk11_context_t pctx;
+	pk11_optype_t op_type = OP_RSA;
+	char *lib_name = NULL;
+	char *pin = NULL;
+	int error = 0;
+	int c, errflg = 0;
+	int ontoken  = 0;
+	unsigned int count = 1000;
+	unsigned int i;
+	struct timespec starttime;
+	struct timespec endtime;
+
+	while ((c = isc_commandline_parse(argc, argv, ":m:s:p:tb:n:")) != -1) {
+		switch (c) {
+		case 'm':
+			lib_name = isc_commandline_argument;
+			break;
+		case 's':
+			op_type = OP_ANY;
+			slot = atoi(isc_commandline_argument);
+			break;
+		case 'p':
+			pin = isc_commandline_argument;
+			break;
+		case 't':
+			ontoken = 1;
+			break;
+		case 'b':
+			bits = (CK_ULONG)atoi(isc_commandline_argument);
+			break;
+		case 'n':
+			count = atoi(isc_commandline_argument);
+			break;
+		case ':':
+			fprintf(stderr,
+				"Option -%c requires an operand\n",
+				isc_commandline_option);
+			errflg++;
+			break;
+		case '?':
+		default:
+			fprintf(stderr, "Unrecognised option: -%c\n",
+				isc_commandline_option);
+			errflg++;
+		}
+	}
+
+	if (errflg) {
+		fprintf(stderr, "Usage:\n");
+		fprintf(stderr,
+			"\tgenrsa [-m module] [-s slot] [-p pin] "
+			"[-t] [-b bits] [-n count]\n");
+		exit(1);
+	}
+
+	pk11_result_register();
+
+	/* Allocate hanles */
+	pubKey = (CK_SESSION_HANDLE *)
+		malloc(count * sizeof(CK_SESSION_HANDLE));
+	if (pubKey == NULL) {
+		perror("malloc");
+		exit(1);
+	}
+	privKey = (CK_SESSION_HANDLE *)
+		malloc(count * sizeof(CK_SESSION_HANDLE));
+	if (privKey == NULL) {
+		free(pubKey);
+		perror("malloc");
+		exit(1);
+	}
+	for (i = 0; i < count; i++) {
+		pubKey[i] = CK_INVALID_HANDLE;
+		privKey[i] = CK_INVALID_HANDLE;
+	}
+
+	/* Initialize the CRYPTOKI library */
+	if (lib_name != NULL)
+		pk11_set_lib_name(lib_name);
+
+	if (pin == NULL)
+		pin = getpassphrase("Enter Pin: ");
+
+	result = pk11_get_session(&pctx, op_type, ISC_FALSE, ISC_TRUE,
+				  ISC_TRUE, (const char *) pin, slot);
+	if ((result != ISC_R_SUCCESS) &&
+	    (result != PK11_R_NORANDOMSERVICE) &&
+	    (result != PK11_R_NODIGESTSERVICE) &&
+	    (result != PK11_R_NOAESSERVICE)) {
+		fprintf(stderr, "Error initializing PKCS#11: %s\n",
+			isc_result_totext(result));
+		exit(1);
+	}
+
+	if (pin != NULL)
+		memset(pin, 0, strlen((char *)pin));
+
+	hSession = pctx.session;
+
+	if (ontoken) {
+		pubTemplate[2].pValue = &truevalue;
+		privTemplate[2].pValue = &truevalue;
+	}
+
+	if (clock_gettime(CLOCK_REALTIME, &starttime) < 0) {
+		perror("clock_gettime(start)");
+		goto exit_keys;
+	}
+
+	for (i = 0; i < count; i++) {
+		rv = pkcs_C_GenerateKeyPair(hSession, &mech,
+				       pubTemplate, 7,
+				       privTemplate, 5,
+				       &pubKey[i], &privKey[i]);
+		if (rv != CKR_OK) {
+			fprintf(stderr,
+				"C_GenerateKeyPair[%u]: Error = 0x%.8lX\n",
+				i, rv);
+			error = 1;
+			if (i == 0)
+				goto exit_keys;
+			break;
+		}
+	}
+
+	if (clock_gettime(CLOCK_REALTIME, &endtime) < 0) {
+		perror("clock_gettime(end)");
+		goto exit_keys;
+	}
+
+	endtime.tv_sec -= starttime.tv_sec;
+	endtime.tv_nsec -= starttime.tv_nsec;
+	while (endtime.tv_nsec < 0) {
+		endtime.tv_sec -= 1;
+		endtime.tv_nsec += 1000000000;
+	}
+	printf("%u generated RSA in %ld.%09lds\n", i,
+	       endtime.tv_sec, endtime.tv_nsec);
+	if (i > 0)
+		printf("%g generated RSA/s\n",
+		       1024 * i / ((double) endtime.tv_sec +
+				   (double) endtime.tv_nsec / 1000000000.));
+
+    exit_keys:
+	for (i = 0; i < count; i++) {
+		/* Destroy keys */
+		if (pubKey[i] == CK_INVALID_HANDLE)
+			goto destroy_priv;
+		rv = pkcs_C_DestroyObject(hSession, pubKey[i]);
+		if ((rv != CKR_OK) && !errflg) {
+			fprintf(stderr,
+				"C_DestroyObject[pub%u]: Error = 0x%.8lX\n",
+				i, rv);
+			errflg = 1;
+		}
+	    destroy_priv:
+		if (privKey[i] == CK_INVALID_HANDLE)
+			continue;
+		rv = pkcs_C_DestroyObject(hSession, privKey[i]);
+		if ((rv != CKR_OK) && !errflg) {
+			fprintf(stderr,
+				"C_DestroyObject[priv%u]: Error = 0x%.8lX\n",
+				i, rv);
+			errflg = 1;
+		}
+	}
+
+	free(pubKey);
+	free(privKey);
+
+	pk11_return_session(&pctx);
+	(void) pk11_finalize();
+		
+	exit(error);
+}
diff --git a/bin/tests/pkcs11/benchmarks/login.c b/bin/tests/pkcs11/benchmarks/login.c
new file mode 100644
index 0000000..fe597fa
--- /dev/null
+++ b/bin/tests/pkcs11/benchmarks/login.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Portions copyright (c) 2008 Nominet UK.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+/* login [-m module] [-s $slot] [-p pin] [-n count] */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <isc/commandline.h>
+#include <isc/result.h>
+#include <isc/types.h>
+
+#include <pk11/pk11.h>
+#include <pk11/internal.h>
+
+#if !(defined(HAVE_GETPASSPHRASE) || (defined (__SVR4) && defined (__sun)))
+#define getpassphrase(x)	getpass(x)
+#endif
+
+#ifndef HAVE_CLOCK_GETTIME
+#ifndef CLOCK_REALTIME
+#define CLOCK_REALTIME 0
+#endif
+
+int
+clock_gettime(int32_t id, struct timespec *tp)
+{
+	struct timeval tv;
+	int result;
+
+	result = gettimeofday(&tv, NULL);
+	if (result)
+		return (result);
+	tp->tv_sec = tv.tv_sec;
+	tp->tv_nsec = (long) tv.tv_usec * 1000;
+	return (result);
+}
+#endif
+
+int
+main(int argc, char *argv[]) {
+	CK_RV rv;
+	CK_SLOT_ID slot = 0;
+	CK_SESSION_HANDLE *hSession;
+	CK_UTF8CHAR *pin = NULL;
+	char *lib_name = NULL;
+	int error = 0;
+	int c, errflg = 0;
+	unsigned int count = 1000;
+	unsigned int i, j;
+	struct timespec starttime;
+	struct timespec endtime;
+
+	while ((c = isc_commandline_parse(argc, argv, ":m:s:p:n:")) != -1) {
+		switch (c) {
+		case 'm':
+			lib_name = isc_commandline_argument;
+			break;
+		case 's':
+			slot = atoi(isc_commandline_argument);
+			break;
+		case 'p':
+			pin = (CK_UTF8CHAR *)isc_commandline_argument;
+			break;
+		case 'n':
+			count = atoi(isc_commandline_argument);
+			break;
+		case ':':
+			fprintf(stderr,
+				"Option -%c requires an operand\n",
+				isc_commandline_option);
+			errflg++;
+			break;
+		case '?':
+		default:
+			fprintf(stderr, "Unrecognised option: -%c\n",
+				isc_commandline_option);
+			errflg++;
+		}
+	}
+
+	if (errflg) {
+		fprintf(stderr, "Usage:\n");
+		fprintf(stderr,
+			"\tlogin [-m module] [-s slot] [-p pin] [-n count]\n");
+		exit(1);
+	}
+
+	/* allocate sessions */
+	hSession = (CK_SESSION_HANDLE *)
+		malloc(count * sizeof(CK_SESSION_HANDLE));
+	if (hSession == NULL) {
+		perror("malloc");
+		exit(1);
+	}
+	for (i = 0; i < count; i++)
+		hSession[i] = CK_INVALID_HANDLE;
+
+	/* Initialize the CRYPTOKI library */
+	if (lib_name != NULL)
+		pk11_set_lib_name(lib_name);
+
+	if (pin == NULL)
+		pin = (CK_UTF8CHAR *)getpassphrase("Enter Pin: ");
+
+	rv = pkcs_C_Initialize(NULL_PTR);
+	if (rv != CKR_OK) {
+		if (rv == 0xfe)
+			fprintf(stderr,
+				"Can't load or link module \"%s\"\n",
+				pk11_get_lib_name());
+		else
+			fprintf(stderr, "C_Initialize: Error = 0x%.8lX\n", rv);
+		free(hSession);
+		exit(1);
+	}
+
+	if (clock_gettime(CLOCK_REALTIME, &starttime) < 0) {
+		perror("clock_gettime(start)");
+		goto exit_program;
+	}
+
+	/* loop */
+	for (i = 0; i < count; i++) {
+		/* Open sessions */
+		rv = pkcs_C_OpenSession(slot, CKF_SERIAL_SESSION,
+					NULL_PTR, NULL_PTR, &hSession[i]);
+		if (rv != CKR_OK) {
+			fprintf(stderr,
+				"C_OpenSession[%u]: Error = 0x%.8lX\n",
+				i, rv);
+			error = 1;
+			if (i == 0)
+				goto exit_program;
+			break;
+		}
+
+		/* Logon */
+		rv = pkcs_C_Login(hSession[i], CKU_USER,
+				  pin, strlen((char *)pin));
+		if (rv != CKR_OK) {
+			fprintf(stderr,
+				"C_Login[%u]: Error = 0x%.8lX\n",
+				i, rv);
+			error = 1;
+			if (i == 0)
+				goto exit_program;
+			break;
+		}
+
+		/* Logoff */
+		rv = pkcs_C_Logout(hSession[i]);
+		if (rv != CKR_OK) {
+			fprintf(stderr,
+				"C_Logout[%u]: Error = 0x%.8lX\n",
+				i, rv);
+			error = 1;
+			if (i == 0)
+				goto exit_program;
+			break;
+		}
+	}
+
+	if (clock_gettime(CLOCK_REALTIME, &endtime) < 0) {
+		perror("clock_gettime(end)");
+		goto exit_program;
+	}
+
+	endtime.tv_sec -= starttime.tv_sec;
+	endtime.tv_nsec -= starttime.tv_nsec;
+	while (endtime.tv_nsec < 0) {
+		endtime.tv_sec -= 1;
+		endtime.tv_nsec += 1000000000;
+	}
+	printf("%u logins in %ld.%09lds\n", i,
+	       endtime.tv_sec, endtime.tv_nsec);
+	if (i > 0)
+		printf("%g logins/s\n",
+		       i / ((double) endtime.tv_sec +
+			    (double) endtime.tv_nsec / 1000000000.));
+
+	for (j = 0; j < i; j++) {
+		if (hSession[j] == CK_INVALID_HANDLE)
+			continue;
+		/* Close sessions */
+		rv = pkcs_C_CloseSession(hSession[j]);
+		if ((rv != CKR_OK) && !errflg) {
+			fprintf(stderr,
+				"C_CloseSession[%u]: Error = 0x%.8lX\n",
+				j, rv);
+			errflg = 1;
+		}
+	}
+
+    exit_program:
+	free(hSession);
+
+	rv = pkcs_C_Finalize(NULL_PTR);
+	if (rv != CKR_OK)
+		fprintf(stderr, "C_Finalize: Error = 0x%.8lX\n", rv);
+		
+	exit(error);
+}
diff --git a/bin/tests/pkcs11/benchmarks/privrsa.c b/bin/tests/pkcs11/benchmarks/privrsa.c
new file mode 100644
index 0000000..c50d8d2
--- /dev/null
+++ b/bin/tests/pkcs11/benchmarks/privrsa.c
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Portions copyright (c) 2008 Nominet UK.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+/* privrsa [-m module] [-s $slot] [-p pin] [-t] [-n count] */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <isc/commandline.h>
+#include <isc/result.h>
+#include <isc/types.h>
+
+#include <pk11/pk11.h>
+#include <pk11/result.h>
+
+#if !(defined(HAVE_GETPASSPHRASE) || (defined (__SVR4) && defined (__sun)))
+#define getpassphrase(x)	getpass(x)
+#endif
+
+#ifndef HAVE_CLOCK_GETTIME
+#ifndef CLOCK_REALTIME
+#define CLOCK_REALTIME 0
+#endif
+
+int
+clock_gettime(int32_t id, struct timespec *tp)
+{
+	struct timeval tv;
+	int result;
+
+	result = gettimeofday(&tv, NULL);
+	if (result)
+		return (result);
+	tp->tv_sec = tv.tv_sec;
+	tp->tv_nsec = (long) tv.tv_usec * 1000;
+	return (result);
+}
+#endif
+
+CK_BYTE modulus[] = {
+	0x00, 0xb7, 0x9c, 0x1f, 0x05, 0xa3, 0xc2, 0x99,
+	0x44, 0x82, 0x20, 0x78, 0x43, 0x7f, 0x5f, 0x3b,
+	0x10, 0xd7, 0x9e, 0x61, 0x42, 0xd2, 0x7a, 0x90,
+	0x50, 0x8a, 0x99, 0x33, 0xe7, 0xca, 0xc8, 0x5f,
+	0x16, 0x1c, 0x56, 0xf8, 0xc1, 0x06, 0x2f, 0x96,
+	0xe7, 0x54, 0xf2, 0x85, 0x89, 0x41, 0x36, 0xf5,
+	0x4c, 0xa4, 0x0d, 0x62, 0xd3, 0x42, 0x51, 0x6b,
+	0x9f, 0xdc, 0x36, 0xcb, 0xad, 0x56, 0xf4, 0xbd,
+	0x2a, 0x60, 0x33, 0xb1, 0x7a, 0x99, 0xad, 0x08,
+	0x9f, 0x95, 0xe8, 0xe5, 0x14, 0xd9, 0x68, 0x79,
+	0xca, 0x4e, 0x72, 0xeb, 0xfb, 0x2c, 0xf1, 0x45,
+	0xd3, 0x33, 0x65, 0xe7, 0xc5, 0x11, 0xdd, 0xe7,
+	0x09, 0x83, 0x13, 0xd5, 0x17, 0x1b, 0xf4, 0xbd,
+	0x49, 0xdd, 0x8a, 0x3c, 0x3c, 0xf7, 0xa1, 0x5d,
+	0x7b, 0xb4, 0xd3, 0x80, 0x25, 0xf4, 0x05, 0x8f,
+	0xbc, 0x2c, 0x2a, 0x47, 0xff, 0xd1, 0xc8, 0x34,
+	0xbf
+};
+CK_BYTE pubexp[] = { 0x01, 0x00, 0x01 };
+CK_BYTE privexp[] = {
+	0x00, 0xae, 0x02, 0xf1, 0x47, 0xa8, 0x07, 0x02,
+	0xb8, 0xf1, 0xd6, 0x92, 0x03, 0xee, 0x50, 0x33,
+	0xab, 0x67, 0x9e, 0x3b, 0xb1, 0x57, 0xc7, 0x3e,
+	0xc4, 0x86, 0x46, 0x61, 0xf1, 0xf8, 0xb6, 0x63,
+	0x9f, 0x91, 0xe6, 0x3f, 0x44, 0xb8, 0x77, 0x1b,
+	0xbe, 0x4c, 0x3c, 0xb8, 0x9f, 0xf7, 0x45, 0x7d,
+	0xbf, 0x4f, 0xef, 0x3b, 0xcc, 0xda, 0x1a, 0x4e,
+	0x34, 0xa8, 0x40, 0xea, 0x51, 0x72, 0x8a, 0xea,
+	0x47, 0x06, 0x04, 0xd0, 0x62, 0x31, 0xa0, 0x6c,
+	0x09, 0x60, 0xf9, 0xc7, 0x95, 0x88, 0x4a, 0xd7,
+	0x19, 0xce, 0x89, 0x08, 0x87, 0x14, 0xef, 0xcc,
+	0x0a, 0xef, 0x72, 0xb9, 0x21, 0xf5, 0xf0, 0xcd,
+	0x6d, 0xe5, 0xfa, 0x15, 0x7f, 0xae, 0x33, 0x9f,
+	0x26, 0xac, 0x2e, 0x52, 0x02, 0x07, 0xfb, 0x1d,
+	0x4b, 0xec, 0x9a, 0x6b, 0x3b, 0x26, 0x1f, 0x52,
+	0xfc, 0x47, 0xf8, 0x66, 0x33, 0xfa, 0x50, 0x6c,
+	0x41
+};
+CK_BYTE prime1[] = {
+	0x00, 0xe8, 0x98, 0xeb, 0xa1, 0xf0, 0xce, 0xde,
+	0xc2, 0x74, 0x01, 0x18, 0x2b, 0xd3, 0x8f, 0x58,
+	0xcd, 0xe9, 0x8e, 0x97, 0xbe, 0xfe, 0xe8, 0x6f,
+	0xd6, 0x0c, 0x0a, 0x47, 0xf8, 0x56, 0x84, 0x36,
+	0x15, 0xe6, 0x75, 0x1c, 0x69, 0x48, 0x8b, 0xf5,
+	0x0f, 0x84, 0xd2, 0x60, 0x8b, 0xa2, 0x2a, 0xa1,
+	0xeb, 0xed, 0xbe, 0x2d, 0xe9, 0x41, 0x0b, 0xed,
+	0x17, 0x7c, 0xd3, 0xa6, 0x35, 0x6e, 0xa6, 0xd8,
+	0x21
+};
+CK_BYTE prime2[] = {
+	0x00, 0xca, 0x15, 0x6a, 0x43, 0x5e, 0x83, 0xc9,
+	0x09, 0xeb, 0x14, 0x1e, 0x46, 0x46, 0x97, 0xfa,
+	0xfa, 0x3c, 0x61, 0x7e, 0xc1, 0xf8, 0x8c, 0x5e,
+	0xcb, 0xbf, 0xe4, 0xb9, 0x78, 0x7f, 0x4f, 0xab,
+	0x82, 0x15, 0x53, 0xaa, 0x04, 0xee, 0x11, 0x21,
+	0x2e, 0x23, 0x08, 0xa0, 0x14, 0x6d, 0x3a, 0x88,
+	0xe6, 0xf8, 0xbe, 0x61, 0x38, 0x99, 0xca, 0x36,
+	0x0d, 0x3e, 0x42, 0x0f, 0x63, 0x4d, 0x73, 0xf0,
+	0xdf
+};
+CK_BYTE exp_1[] = {
+	0x66, 0x2d, 0xb7, 0x65, 0xbe, 0x99, 0xc2, 0x35,
+	0xfe, 0x2b, 0xf4, 0xe8, 0x5b, 0xd9, 0xdf, 0x13,
+	0x26, 0x04, 0xe4, 0x18, 0x9d, 0x76, 0x92, 0x9a,
+	0x9f, 0x53, 0x6c, 0xe6, 0x65, 0x6b, 0x53, 0x2f,
+	0x2f, 0xbc, 0x46, 0xac, 0xe1, 0x97, 0xca, 0x21,
+	0xf5, 0x21, 0x4e, 0x14, 0x49, 0x3b, 0x1d, 0x42,
+	0xbd, 0x80, 0x0c, 0x3f, 0x29, 0xba, 0x09, 0x7f,
+	0x85, 0xf0, 0x9c, 0x55, 0x60, 0xb4, 0x9e, 0xc1
+};
+CK_BYTE exp_2[] = {
+	0x00, 0x87, 0x22, 0x74, 0xf1, 0xe2, 0x15, 0x3c,
+	0x6d, 0xde, 0x7e, 0x90, 0x94, 0x2c, 0x06, 0xdb,
+	0xb5, 0x54, 0x85, 0x59, 0xcf, 0x7a, 0x56, 0xdb,
+	0xd9, 0x62, 0x54, 0x20, 0x56, 0xdc, 0xc3, 0xb9,
+	0x0b, 0xff, 0x18, 0xf8, 0x7b, 0xdd, 0x7b, 0x24,
+	0xf6, 0x06, 0x45, 0x71, 0x4e, 0xd7, 0x90, 0x2a,
+	0x16, 0x52, 0x46, 0x75, 0x1a, 0xf5, 0x74, 0x8c,
+	0x5a, 0xa4, 0xc4, 0x66, 0x27, 0xe0, 0x96, 0x64,
+	0x7f
+};
+CK_BYTE coeff[] = {
+	0x00, 0xd0, 0x1f, 0xb3, 0x47, 0x40, 0x93, 0x8b,
+	0x99, 0xd7, 0xb5, 0xc6, 0x09, 0x82, 0x65, 0x94,
+	0x9d, 0x56, 0x0a, 0x05, 0x55, 0x7d, 0x93, 0x04,
+	0xa4, 0x26, 0xee, 0x42, 0x86, 0xa3, 0xf1, 0xd5,
+	0x7a, 0x42, 0x84, 0x3c, 0x21, 0x96, 0x9a, 0xd9,
+	0x36, 0xd4, 0x62, 0x01, 0xb0, 0x8b, 0x77, 0xe5,
+	0xcc, 0x1b, 0xd2, 0x12, 0xd2, 0x9c, 0x89, 0x67,
+	0x0c, 0x00, 0x09, 0x56, 0x8c, 0x33, 0x57, 0xf9,
+	0x8c
+};
+
+char label[16];
+
+static CK_BBOOL truevalue = TRUE;
+static CK_BBOOL falsevalue = FALSE;
+
+int
+main(int argc, char *argv[]) {
+	isc_result_t result;
+	CK_RV rv;
+	CK_SLOT_ID slot = 0;
+	CK_SESSION_HANDLE hSession = CK_INVALID_HANDLE;
+	CK_OBJECT_HANDLE *hKey;
+	CK_OBJECT_CLASS kClass = CKO_PRIVATE_KEY;
+	CK_KEY_TYPE kType = CKK_RSA;
+	CK_ATTRIBUTE kTemplate[] =
+	{
+		{ CKA_CLASS, &kClass, (CK_ULONG) sizeof(kClass) },
+		{ CKA_KEY_TYPE, &kType, (CK_ULONG) sizeof(kType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_LABEL, (CK_BYTE_PTR) label, (CK_ULONG) sizeof(label) },
+		{ CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_MODULUS, modulus, (CK_ULONG) sizeof(modulus) },
+		{ CKA_PUBLIC_EXPONENT, pubexp, (CK_ULONG) sizeof(pubexp) },
+		{ CKA_PRIVATE_EXPONENT, privexp, (CK_ULONG) sizeof(privexp) },
+		{ CKA_PRIME_1, prime1, (CK_ULONG) sizeof(prime1) },
+		{ CKA_PRIME_2, prime2, (CK_ULONG) sizeof(prime2) },
+		{ CKA_EXPONENT_1, exp_1, (CK_ULONG) sizeof(exp_1) },
+		{ CKA_EXPONENT_2, exp_2, (CK_ULONG) sizeof(exp_2) },
+		{ CKA_COEFFICIENT, coeff, (CK_ULONG) sizeof(coeff) }
+	};
+	pk11_context_t pctx;
+	pk11_optype_t op_type = OP_RSA;
+	char *lib_name = NULL;
+	char *pin = NULL;
+	int error = 0;
+	int c, errflg = 0;
+	int ontoken  = 0;
+	unsigned int count = 1000;
+	unsigned int i;
+	struct timespec starttime;
+	struct timespec endtime;
+
+	while ((c = isc_commandline_parse(argc, argv, ":m:s:p:tn:")) != -1) {
+		switch (c) {
+		case 'm':
+			lib_name = isc_commandline_argument;
+			break;
+		case 's':
+			op_type = OP_ANY;
+			slot = atoi(isc_commandline_argument);
+			break;
+		case 'p':
+			pin = isc_commandline_argument;
+			break;
+		case 't':
+			ontoken = 1;
+			break;
+		case 'n':
+			count = atoi(isc_commandline_argument);
+			break;
+		case ':':
+			fprintf(stderr,
+				"Option -%c requires an operand\n",
+				isc_commandline_option);
+			errflg++;
+			break;
+		case '?':
+		default:
+			fprintf(stderr, "Unrecognised option: -%c\n",
+				isc_commandline_option);
+			errflg++;
+		}
+	}
+
+	if (errflg) {
+		fprintf(stderr, "Usage:\n");
+		fprintf(stderr,
+			"\tprivrsa [-m module] [-s slot] [-p pin] "
+			"[-t] [-n count]\n");
+		exit(1);
+	}
+
+	pk11_result_register();
+
+	/* Allocate hanles */
+	hKey = (CK_SESSION_HANDLE *)
+		malloc(count * sizeof(CK_SESSION_HANDLE));
+	if (hKey == NULL) {
+		perror("malloc");
+		exit(1);
+	}
+	for (i = 0; i < count; i++)
+		hKey[i] = CK_INVALID_HANDLE;
+
+	/* Initialize the CRYPTOKI library */
+	if (lib_name != NULL)
+		pk11_set_lib_name(lib_name);
+
+	if (pin == NULL)
+		pin = getpassphrase("Enter Pin: ");
+
+	result = pk11_get_session(&pctx, op_type, ISC_FALSE, ISC_TRUE,
+				  ISC_TRUE, (const char *) pin, slot);
+	if ((result != ISC_R_SUCCESS) &&
+	    (result != PK11_R_NORANDOMSERVICE) &&
+	    (result != PK11_R_NODIGESTSERVICE) &&
+	    (result != PK11_R_NOAESSERVICE)) {
+		fprintf(stderr, "Error initializing PKCS#11: %s\n",
+			isc_result_totext(result));
+		free(hKey);
+		exit(1);
+	}
+
+	if (pin != NULL)
+		memset(pin, 0, strlen((char *)pin));
+
+	hSession = pctx.session;
+
+	if (ontoken)
+		kTemplate[2].pValue = &truevalue;
+
+	if (clock_gettime(CLOCK_REALTIME, &starttime) < 0) {
+		perror("clock_gettime(start)");
+		goto exit_objects;
+	}
+
+	for (i = 0; i < count; i++) {
+		(void) snprintf(label, sizeof(label), "obj%u", i);
+		kTemplate[4].ulValueLen = strlen(label);
+		rv = pkcs_C_CreateObject(hSession, kTemplate, 14, &hKey[i]);
+		if (rv != CKR_OK) {
+			fprintf(stderr,
+				"C_CreateObject[%u]: Error = 0x%.8lX\n",
+				i, rv);
+			error = 1;
+			if (i == 0)
+				goto exit_objects;
+			break;
+		}
+	}
+
+	if (clock_gettime(CLOCK_REALTIME, &endtime) < 0) {
+		perror("clock_gettime(end)");
+		goto exit_objects;
+	}
+
+	endtime.tv_sec -= starttime.tv_sec;
+	endtime.tv_nsec -= starttime.tv_nsec;
+	while (endtime.tv_nsec < 0) {
+		endtime.tv_sec -= 1;
+		endtime.tv_nsec += 1000000000;
+	}
+	printf("%u private RSA keys in %ld.%09lds\n", i,
+	       endtime.tv_sec, endtime.tv_nsec);
+	if (i > 0)
+		printf("%g private RSA keys/s\n",
+		       1024 * i / ((double) endtime.tv_sec +
+				   (double) endtime.tv_nsec / 1000000000.));
+
+    exit_objects:
+	for (i = 0; i < count; i++) {
+		/* Destroy objects */
+		if (hKey[i] == CK_INVALID_HANDLE)
+			continue;
+		rv = pkcs_C_DestroyObject(hSession, hKey[i]);
+		if ((rv != CKR_OK) && !errflg) {
+			fprintf(stderr,
+				"C_DestroyObject[%u]: Error = 0x%.8lX\n",
+				i, rv);
+			errflg = 1;
+		}
+	}
+
+	free(hKey);
+
+	pk11_return_session(&pctx);
+	(void) pk11_finalize();
+
+	exit(error);
+}
diff --git a/bin/tests/pkcs11/benchmarks/pubrsa.c b/bin/tests/pkcs11/benchmarks/pubrsa.c
new file mode 100644
index 0000000..b27a999
--- /dev/null
+++ b/bin/tests/pkcs11/benchmarks/pubrsa.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Portions copyright (c) 2008 Nominet UK.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+/* pubrsa [-m module] [-s $slot] [-p pin] [-t] [-n count] */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <isc/commandline.h>
+#include <isc/result.h>
+#include <isc/types.h>
+
+#include <pk11/pk11.h>
+#include <pk11/result.h>
+
+#if !(defined(HAVE_GETPASSPHRASE) || (defined (__SVR4) && defined (__sun)))
+#define getpassphrase(x)	getpass(x)
+#endif
+
+#ifndef HAVE_CLOCK_GETTIME
+#ifndef CLOCK_REALTIME
+#define CLOCK_REALTIME 0
+#endif
+
+int
+clock_gettime(int32_t id, struct timespec *tp)
+{
+	struct timeval tv;
+	int result;
+
+	result = gettimeofday(&tv, NULL);
+	if (result)
+		return (result);
+	tp->tv_sec = tv.tv_sec;
+	tp->tv_nsec = (long) tv.tv_usec * 1000;
+	return (result);
+}
+#endif
+
+CK_BYTE modulus[] = {
+	0x00, 0xb7, 0x9c, 0x1f, 0x05, 0xa3, 0xc2, 0x99,
+	0x44, 0x82, 0x20, 0x78, 0x43, 0x7f, 0x5f, 0x3b,
+	0x10, 0xd7, 0x9e, 0x61, 0x42, 0xd2, 0x7a, 0x90,
+	0x50, 0x8a, 0x99, 0x33, 0xe7, 0xca, 0xc8, 0x5f,
+	0x16, 0x1c, 0x56, 0xf8, 0xc1, 0x06, 0x2f, 0x96,
+	0xe7, 0x54, 0xf2, 0x85, 0x89, 0x41, 0x36, 0xf5,
+	0x4c, 0xa4, 0x0d, 0x62, 0xd3, 0x42, 0x51, 0x6b,
+	0x9f, 0xdc, 0x36, 0xcb, 0xad, 0x56, 0xf4, 0xbd,
+	0x2a, 0x60, 0x33, 0xb1, 0x7a, 0x99, 0xad, 0x08,
+	0x9f, 0x95, 0xe8, 0xe5, 0x14, 0xd9, 0x68, 0x79,
+	0xca, 0x4e, 0x72, 0xeb, 0xfb, 0x2c, 0xf1, 0x45,
+	0xd3, 0x33, 0x65, 0xe7, 0xc5, 0x11, 0xdd, 0xe7,
+	0x09, 0x83, 0x13, 0xd5, 0x17, 0x1b, 0xf4, 0xbd,
+	0x49, 0xdd, 0x8a, 0x3c, 0x3c, 0xf7, 0xa1, 0x5d,
+	0x7b, 0xb4, 0xd3, 0x80, 0x25, 0xf4, 0x05, 0x8f,
+	0xbc, 0x2c, 0x2a, 0x47, 0xff, 0xd1, 0xc8, 0x34,
+	0xbf
+};
+CK_BYTE exponent[] = { 0x01, 0x00, 0x01 };
+
+char label[16];
+
+static CK_BBOOL truevalue = TRUE;
+static CK_BBOOL falsevalue = FALSE;
+
+int
+main(int argc, char *argv[]) {
+	isc_result_t result;
+	CK_RV rv;
+	CK_SLOT_ID slot = 0;
+	CK_SESSION_HANDLE hSession = CK_INVALID_HANDLE;
+	CK_OBJECT_HANDLE *hKey;
+	CK_OBJECT_CLASS kClass = CKO_PUBLIC_KEY;
+	CK_KEY_TYPE kType = CKK_RSA;
+	CK_ATTRIBUTE kTemplate[] =
+	{
+		{ CKA_CLASS, &kClass, (CK_ULONG) sizeof(kClass) },
+		{ CKA_KEY_TYPE, &kType, (CK_ULONG) sizeof(kType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_LABEL, (CK_BYTE_PTR) label, (CK_ULONG) sizeof(label) },
+		{ CKA_VERIFY, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_MODULUS, modulus, (CK_ULONG) sizeof(modulus) },
+		{ CKA_PUBLIC_EXPONENT, exponent, (CK_ULONG) sizeof(exponent) }
+	};
+	pk11_context_t pctx;
+	pk11_optype_t op_type = OP_RSA;
+	char *lib_name = NULL;
+	char *pin = NULL;
+	int error = 0;
+	int c, errflg = 0;
+	int ontoken  = 0;
+	unsigned int count = 1000;
+	unsigned int i;
+	struct timespec starttime;
+	struct timespec endtime;
+
+	while ((c = isc_commandline_parse(argc, argv, ":m:s:p:tn:")) != -1) {
+		switch (c) {
+		case 'm':
+			lib_name = isc_commandline_argument;
+			break;
+		case 's':
+			op_type = OP_ANY;
+			slot = atoi(isc_commandline_argument);
+			break;
+		case 'p':
+			pin = isc_commandline_argument;
+			break;
+		case 't':
+			ontoken = 1;
+			break;
+		case 'n':
+			count = atoi(isc_commandline_argument);
+			break;
+		case ':':
+			fprintf(stderr,
+				"Option -%c requires an operand\n",
+				isc_commandline_option);
+			errflg++;
+			break;
+		case '?':
+		default:
+			fprintf(stderr, "Unrecognised option: -%c\n",
+				isc_commandline_option);
+			errflg++;
+		}
+	}
+
+	if (errflg) {
+		fprintf(stderr, "Usage:\n");
+		fprintf(stderr,
+			"\tpubrsa [-m module] [-s slot] [-p pin] "
+			"[-t] [-n count]\n");
+		exit(1);
+	}
+
+	pk11_result_register();
+
+	/* Allocate hanles */
+	hKey = (CK_SESSION_HANDLE *)
+		malloc(count * sizeof(CK_SESSION_HANDLE));
+	if (hKey == NULL) {
+		perror("malloc");
+		exit(1);
+	}
+	for (i = 0; i < count; i++)
+		hKey[i] = CK_INVALID_HANDLE;
+
+	/* Initialize the CRYPTOKI library */
+	if (lib_name != NULL)
+		pk11_set_lib_name(lib_name);
+
+	if (pin == NULL)
+		pin = getpassphrase("Enter Pin: ");
+
+	result = pk11_get_session(&pctx, op_type, ISC_FALSE, ISC_TRUE,
+				  ISC_TRUE, (const char *) pin, slot);
+	if ((result != ISC_R_SUCCESS) &&
+	    (result != PK11_R_NORANDOMSERVICE) &&
+	    (result != PK11_R_NODIGESTSERVICE) &&
+	    (result != PK11_R_NOAESSERVICE)) {
+		fprintf(stderr, "Error initializing PKCS#11: %s\n",
+			isc_result_totext(result));
+		free(hKey);
+		exit(1);
+	}
+
+	if (pin != NULL)
+		memset(pin, 0, strlen((char *)pin));
+
+	hSession = pctx.session;
+
+	if (ontoken)
+		kTemplate[2].pValue = &truevalue;
+
+	if (clock_gettime(CLOCK_REALTIME, &starttime) < 0) {
+		perror("clock_gettime(start)");
+		goto exit_objects;
+	}
+
+	for (i = 0; i < count; i++) {
+		(void) snprintf(label, sizeof(label), "obj%u", i);
+		kTemplate[4].ulValueLen = strlen(label);
+		rv = pkcs_C_CreateObject(hSession, kTemplate, 8, &hKey[i]);
+		if (rv != CKR_OK) {
+			fprintf(stderr,
+				"C_CreateObject[%u]: Error = 0x%.8lX\n",
+				i, rv);
+			error = 1;
+			if (i == 0)
+				goto exit_objects;
+			break;
+		}
+	}
+
+	if (clock_gettime(CLOCK_REALTIME, &endtime) < 0) {
+		perror("clock_gettime(end)");
+		goto exit_objects;
+	}
+
+	endtime.tv_sec -= starttime.tv_sec;
+	endtime.tv_nsec -= starttime.tv_nsec;
+	while (endtime.tv_nsec < 0) {
+		endtime.tv_sec -= 1;
+		endtime.tv_nsec += 1000000000;
+	}
+	printf("%u public RSA keys in %ld.%09lds\n", i,
+	       endtime.tv_sec, endtime.tv_nsec);
+	if (i > 0)
+		printf("%g public RSA keys/s\n",
+		       1024 * i / ((double) endtime.tv_sec +
+				   (double) endtime.tv_nsec / 1000000000.));
+
+    exit_objects:
+	for (i = 0; i < count; i++) {
+		/* Destroy objects */
+		if (hKey[i] == CK_INVALID_HANDLE)
+			continue;
+		rv = pkcs_C_DestroyObject(hSession, hKey[i]);
+		if ((rv != CKR_OK) && !errflg) {
+			fprintf(stderr,
+				"C_DestroyObject[%u]: Error = 0x%.8lX\n",
+				i, rv);
+			errflg = 1;
+		}
+	}
+
+	free(hKey);
+
+	pk11_return_session(&pctx);
+	(void) pk11_finalize();
+
+	exit(error);
+}
diff --git a/bin/tests/pkcs11/benchmarks/random.c b/bin/tests/pkcs11/benchmarks/random.c
new file mode 100644
index 0000000..10d6db0
--- /dev/null
+++ b/bin/tests/pkcs11/benchmarks/random.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Portions copyright (c) 2008 Nominet UK.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+/* random [-m module] [-s $slot] [-n count] */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <isc/commandline.h>
+#include <isc/result.h>
+#include <isc/types.h>
+
+#include <pk11/pk11.h>
+#include <pk11/result.h>
+
+#ifndef HAVE_CLOCK_GETTIME
+#ifndef CLOCK_REALTIME
+#define CLOCK_REALTIME 0
+#endif
+
+int
+clock_gettime(int32_t id, struct timespec *tp)
+{
+	struct timeval tv;
+	int result;
+
+	result = gettimeofday(&tv, NULL);
+	if (result)
+		return (result);
+	tp->tv_sec = tv.tv_sec;
+	tp->tv_nsec = (long) tv.tv_usec * 1000;
+	return (result);
+}
+#endif
+
+CK_BYTE buf[1024];
+
+int
+main(int argc, char *argv[]) {
+	isc_result_t result;
+	CK_RV rv;
+	CK_SLOT_ID slot = 0;
+	CK_SESSION_HANDLE hSession = CK_INVALID_HANDLE;
+	CK_ULONG len = sizeof(buf);
+	pk11_context_t pctx;
+	pk11_optype_t op_type = OP_RAND;
+	char *lib_name = NULL;
+	int error = 0;
+	int c, errflg = 0;
+	unsigned int count = 1000;
+	unsigned int i;
+	struct timespec starttime;
+	struct timespec endtime;
+
+	while ((c = isc_commandline_parse(argc, argv, ":m:s:n:")) != -1) {
+		switch (c) {
+		case 'm':
+			lib_name = isc_commandline_argument;
+			break;
+		case 's':
+			op_type = OP_ANY;
+			slot = atoi(isc_commandline_argument);
+			break;
+		case 'n':
+			count = atoi(isc_commandline_argument);
+			break;
+		case ':':
+			fprintf(stderr,
+				"Option -%c requires an operand\n",
+				isc_commandline_option);
+			errflg++;
+			break;
+		case '?':
+		default:
+			fprintf(stderr, "Unrecognised option: -%c\n",
+				isc_commandline_option);
+			errflg++;
+		}
+	}
+
+	if (errflg) {
+		fprintf(stderr, "Usage:\n");
+		fprintf(stderr,
+			"\trandom [-m module] [-s slot] [-n count]\n");
+		exit(1);
+	}
+
+	pk11_result_register();
+
+	/* Initialize the CRYPTOKI library */
+	if (lib_name != NULL)
+		pk11_set_lib_name(lib_name);
+
+	result = pk11_get_session(&pctx, op_type, ISC_FALSE, ISC_FALSE,
+				  ISC_FALSE, NULL, slot);
+	if ((result != ISC_R_SUCCESS) &&
+	    (result != PK11_R_NODIGESTSERVICE) &&
+	    (result != PK11_R_NOAESSERVICE)) {
+		fprintf(stderr, "Error initializing PKCS#11: %s\n",
+			isc_result_totext(result));
+		exit(1);
+	}
+
+	hSession = pctx.session;
+
+	if (clock_gettime(CLOCK_REALTIME, &starttime) < 0) {
+		perror("clock_gettime(start)");
+		goto exit_session;
+	}
+
+	for (i = 0; i < count; i++) {
+		/* Get random bytes */
+		rv = pkcs_C_GenerateRandom(hSession, buf, len);
+		if (rv != CKR_OK) {
+			fprintf(stderr,
+				"C_GenerateRandom[%u]: Error = 0x%.8lX\n",
+				i, rv);
+			error = 1;
+			break;
+		}
+	}
+
+	if (clock_gettime(CLOCK_REALTIME, &endtime) < 0) {
+		perror("clock_gettime(end)");
+		goto exit_session;
+	}
+
+	endtime.tv_sec -= starttime.tv_sec;
+	endtime.tv_nsec -= starttime.tv_nsec;
+	while (endtime.tv_nsec < 0) {
+		endtime.tv_sec -= 1;
+		endtime.tv_nsec += 1000000000;
+	}
+	printf("%uK random bytes in %ld.%09lds\n", i,
+	       endtime.tv_sec, endtime.tv_nsec);
+	if (i > 0)
+		printf("%g random bytes/s\n",
+		       1024 * i / ((double) endtime.tv_sec +
+				   (double) endtime.tv_nsec / 1000000000.));
+
+    exit_session:
+	pk11_return_session(&pctx);
+	(void) pk11_finalize();
+
+	exit(error);
+}
diff --git a/bin/tests/pkcs11/benchmarks/session.c b/bin/tests/pkcs11/benchmarks/session.c
new file mode 100644
index 0000000..74bd63a
--- /dev/null
+++ b/bin/tests/pkcs11/benchmarks/session.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Portions copyright (c) 2008 Nominet UK.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+/* session [-m module] [-s $slot] [-n count] */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <isc/commandline.h>
+#include <isc/result.h>
+#include <isc/types.h>
+
+#include <pk11/pk11.h>
+#include <pk11/internal.h>
+
+#ifndef HAVE_CLOCK_GETTIME
+#ifndef CLOCK_REALTIME
+#define CLOCK_REALTIME 0
+#endif
+
+int
+clock_gettime(int32_t id, struct timespec *tp)
+{
+	struct timeval tv;
+	int result;
+
+	result = gettimeofday(&tv, NULL);
+	if (result)
+		return (result);
+	tp->tv_sec = tv.tv_sec;
+	tp->tv_nsec = (long) tv.tv_usec * 1000;
+	return (result);
+}
+#endif
+
+int
+main(int argc, char *argv[]) {
+	CK_RV rv;
+	CK_SLOT_ID slot = 0;
+	CK_SESSION_HANDLE *hSession;
+	char *lib_name = NULL;
+	int error = 0;
+	int c, errflg = 0;
+	unsigned int count = 1000;
+	unsigned int i;
+	struct timespec starttime;
+	struct timespec endtime;
+
+	while ((c = isc_commandline_parse(argc, argv, ":m:s:n:")) != -1) {
+		switch (c) {
+		case 'm':
+			lib_name = isc_commandline_argument;
+			break;
+		case 's':
+			slot = atoi(isc_commandline_argument);
+			break;
+		case 'n':
+			count = atoi(isc_commandline_argument);
+			break;
+		case ':':
+			fprintf(stderr,
+				"Option -%c requires an operand\n",
+				isc_commandline_option);
+			errflg++;
+			break;
+		case '?':
+		default:
+			fprintf(stderr, "Unrecognised option: -%c\n",
+				isc_commandline_option);
+			errflg++;
+		}
+	}
+
+	if (errflg) {
+		fprintf(stderr, "Usage:\n");
+		fprintf(stderr,
+			"\tsession [-m module] [-s slot] [-n count]\n");
+		exit(1);
+	}
+
+	/* Allocate sessions */
+	hSession = (CK_SESSION_HANDLE *)
+		malloc(count * sizeof(CK_SESSION_HANDLE));
+	if (hSession == NULL) {
+		perror("malloc");
+		exit(1);
+	}
+	for (i = 0; i < count; i++)
+		hSession[i] = CK_INVALID_HANDLE;
+
+	/* Initialize the CRYPTOKI library */
+	if (lib_name != NULL)
+		pk11_set_lib_name(lib_name);
+
+	rv = pkcs_C_Initialize(NULL_PTR);
+	if (rv != CKR_OK) {
+		if (rv == 0xfe)
+			fprintf(stderr,
+				"Can't load or link module \"%s\"\n",
+				pk11_get_lib_name());
+		else
+			fprintf(stderr, "C_Initialize: Error = 0x%.8lX\n", rv);
+		free(hSession);
+		exit(1);
+	}
+
+	if (clock_gettime(CLOCK_REALTIME, &starttime) < 0) {
+		perror("clock_gettime(start)");
+		goto exit_program;
+	}
+
+	/* loop */
+	for (i = 0; i < count; i++) {
+		/* Open sessions */
+		rv = pkcs_C_OpenSession(slot, CKF_SERIAL_SESSION,
+					NULL_PTR, NULL_PTR, &hSession[i]);
+		if (rv != CKR_OK) {
+			fprintf(stderr,
+				"C_OpenSession[%u]: Error = 0x%.8lX\n",
+				i, rv);
+			error = 1;
+			if (i == 0)
+				goto exit_program;
+			break;
+		}
+	}
+
+	if (clock_gettime(CLOCK_REALTIME, &endtime) < 0) {
+		perror("clock_gettime(end)");
+		goto exit_program;
+	}
+
+	endtime.tv_sec -= starttime.tv_sec;
+	endtime.tv_nsec -= starttime.tv_nsec;
+	while (endtime.tv_nsec < 0) {
+		endtime.tv_sec -= 1;
+		endtime.tv_nsec += 1000000000;
+	}
+	printf("%u sessions in %ld.%09lds\n", i,
+	       endtime.tv_sec, endtime.tv_nsec);
+	if (i > 0)
+		printf("%g sessions/s\n",
+		       i / ((double) endtime.tv_sec +
+			    (double) endtime.tv_nsec / 1000000000.));
+
+	for (i = 0; i < count; i++) {
+		/* Close sessions */
+		if (hSession[i] == CK_INVALID_HANDLE)
+			continue;
+		rv = pkcs_C_CloseSession(hSession[i]);
+		if ((rv != CKR_OK) && !errflg) {
+			fprintf(stderr,
+				"C_CloseSession[%u]: Error = 0x%.8lX\n",
+				i, rv);
+			errflg = 1;
+		}
+	}
+
+    exit_program:
+	free(hSession);
+
+	rv = pkcs_C_Finalize(NULL_PTR);
+	if (rv != CKR_OK)
+		fprintf(stderr, "C_Finalize: Error = 0x%.8lX\n", rv);
+		
+	exit(error);
+}
diff --git a/bin/tests/pkcs11/benchmarks/sha1.c b/bin/tests/pkcs11/benchmarks/sha1.c
new file mode 100644
index 0000000..756aadb
--- /dev/null
+++ b/bin/tests/pkcs11/benchmarks/sha1.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Portions copyright (c) 2008 Nominet UK.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+/* sha1 [-m module] [-s $slot] [-n count] */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <isc/commandline.h>
+#include <isc/result.h>
+#include <isc/types.h>
+
+#include <pk11/pk11.h>
+#include <pk11/result.h>
+
+#ifndef HAVE_CLOCK_GETTIME
+#ifndef CLOCK_REALTIME
+#define CLOCK_REALTIME 0
+#endif
+
+int
+clock_gettime(int32_t id, struct timespec *tp)
+{
+	struct timeval tv;
+	int result;
+
+	result = gettimeofday(&tv, NULL);
+	if (result)
+		return (result);
+	tp->tv_sec = tv.tv_sec;
+	tp->tv_nsec = (long) tv.tv_usec * 1000;
+	return (result);
+}
+#endif
+
+CK_BYTE buf[1024];
+
+int
+main(int argc, char *argv[]) {
+	isc_result_t result;
+	CK_RV rv;
+	CK_SLOT_ID slot = 0;
+	CK_SESSION_HANDLE hSession = CK_INVALID_HANDLE;
+	CK_MECHANISM mech = { CKM_SHA_1, NULL, 0 };
+	CK_ULONG len = sizeof(buf);
+	pk11_context_t pctx;
+	pk11_optype_t op_type = OP_DIGEST;
+	char *lib_name = NULL;
+	int error = 0;
+	int c, errflg = 0;
+	unsigned int count = 1000;
+	unsigned int i;
+	struct timespec starttime;
+	struct timespec endtime;
+
+	while ((c = isc_commandline_parse(argc, argv, ":m:s:n:")) != -1) {
+		switch (c) {
+		case 'm':
+			lib_name = isc_commandline_argument;
+			break;
+		case 's':
+			op_type = OP_ANY;
+			slot = atoi(isc_commandline_argument);
+			break;
+		case 'n':
+			count = atoi(isc_commandline_argument);
+			break;
+		case ':':
+			fprintf(stderr,
+				"Option -%c requires an operand\n",
+				isc_commandline_option);
+			errflg++;
+			break;
+		case '?':
+		default:
+			fprintf(stderr, "Unrecognised option: -%c\n",
+				isc_commandline_option);
+			errflg++;
+		}
+	}
+
+	if (errflg) {
+		fprintf(stderr, "Usage:\n");
+		fprintf(stderr,
+			"\tssha1 [-m module] [-s slot] [-n count]\n");
+		exit(1);
+	}
+
+	pk11_result_register();
+
+	/* Initialize the CRYPTOKI library */
+	if (lib_name != NULL)
+		pk11_set_lib_name(lib_name);
+
+	result = pk11_get_session(&pctx, op_type, ISC_FALSE, ISC_FALSE,
+				  ISC_FALSE, NULL, slot);
+	if ((result != ISC_R_SUCCESS) &&
+	    (result != PK11_R_NORANDOMSERVICE) &&
+	    (result != PK11_R_NOAESSERVICE)) {
+		fprintf(stderr, "Error initializing PKCS#11: %s\n",
+			isc_result_totext(result));
+		exit(1);
+	}
+
+	hSession = pctx.session;
+
+	/* Randomize the buffer */
+	rv = pkcs_C_GenerateRandom(hSession, buf, len);
+	if (rv != CKR_OK) {
+		fprintf(stderr, "C_GenerateRandom: Error = 0x%.8lX\n", rv);
+		goto exit_session;
+	}
+
+	if (clock_gettime(CLOCK_REALTIME, &starttime) < 0) {
+		perror("clock_gettime(start)");
+		goto exit_session;
+	}
+
+	/* Initialize Digest */
+	rv = pkcs_C_DigestInit(hSession, &mech);
+	if (rv != CKR_OK) {
+		fprintf(stderr, "C_DigestInit: Error = 0x%.8lX\n", rv);
+		goto exit_session;
+	}
+
+
+	for (i = 0; i < count; i++) {
+		/* Digest buffer */
+		rv = pkcs_C_DigestUpdate(hSession, buf, len);
+		if (rv != CKR_OK) {
+			fprintf(stderr,
+				"C_DigestUpdate[%u]: Error = 0x%.8lX\n",
+				i, rv);
+			error = 1;
+			break;
+		}
+	}
+
+	/* Finalize Digest (unconditionally) */
+	len = 20U;
+	rv = pkcs_C_DigestFinal(hSession, buf, &len);
+	if ((rv != CKR_OK) && !error)
+		fprintf(stderr, "C_DigestFinal: Error = 0x%.8lX\n", rv);
+
+	if (clock_gettime(CLOCK_REALTIME, &endtime) < 0) {
+		perror("clock_gettime(end)");
+		goto exit_session;
+	}
+
+	endtime.tv_sec -= starttime.tv_sec;
+	endtime.tv_nsec -= starttime.tv_nsec;
+	while (endtime.tv_nsec < 0) {
+		endtime.tv_sec -= 1;
+		endtime.tv_nsec += 1000000000;
+	}
+	printf("%uK digested bytes in %ld.%09lds\n", i,
+	       endtime.tv_sec, endtime.tv_nsec);
+	if (i > 0)
+		printf("%g digested bytes/s\n",
+		       1024 * i / ((double) endtime.tv_sec +
+				   (double) endtime.tv_nsec / 1000000000.));
+
+    exit_session:
+	pk11_return_session(&pctx);
+	(void) pk11_finalize();
+
+	exit(error);
+}
diff --git a/bin/tests/pkcs11/benchmarks/sign.c b/bin/tests/pkcs11/benchmarks/sign.c
new file mode 100644
index 0000000..8425ba9
--- /dev/null
+++ b/bin/tests/pkcs11/benchmarks/sign.c
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Portions copyright (c) 2008 Nominet UK.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+/* signrsa [-m module] [-s $slot] [-p pin] [-t] [-n count] */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <isc/commandline.h>
+#include <isc/result.h>
+#include <isc/types.h>
+
+#include <pk11/pk11.h>
+#include <pk11/result.h>
+
+#if !(defined(HAVE_GETPASSPHRASE) || (defined (__SVR4) && defined (__sun)))
+#define getpassphrase(x)	getpass(x)
+#endif
+
+#ifndef HAVE_CLOCK_GETTIME
+#ifndef CLOCK_REALTIME
+#define CLOCK_REALTIME 0
+#endif
+
+int
+clock_gettime(int32_t id, struct timespec *tp)
+{
+	struct timeval tv;
+	int result;
+
+	result = gettimeofday(&tv, NULL);
+	if (result)
+		return (result);
+	tp->tv_sec = tv.tv_sec;
+	tp->tv_nsec = (long) tv.tv_usec * 1000;
+	return (result);
+}
+#endif
+
+CK_BYTE modulus[] = {
+	0x00, 0xb7, 0x9c, 0x1f, 0x05, 0xa3, 0xc2, 0x99,
+	0x44, 0x82, 0x20, 0x78, 0x43, 0x7f, 0x5f, 0x3b,
+	0x10, 0xd7, 0x9e, 0x61, 0x42, 0xd2, 0x7a, 0x90,
+	0x50, 0x8a, 0x99, 0x33, 0xe7, 0xca, 0xc8, 0x5f,
+	0x16, 0x1c, 0x56, 0xf8, 0xc1, 0x06, 0x2f, 0x96,
+	0xe7, 0x54, 0xf2, 0x85, 0x89, 0x41, 0x36, 0xf5,
+	0x4c, 0xa4, 0x0d, 0x62, 0xd3, 0x42, 0x51, 0x6b,
+	0x9f, 0xdc, 0x36, 0xcb, 0xad, 0x56, 0xf4, 0xbd,
+	0x2a, 0x60, 0x33, 0xb1, 0x7a, 0x99, 0xad, 0x08,
+	0x9f, 0x95, 0xe8, 0xe5, 0x14, 0xd9, 0x68, 0x79,
+	0xca, 0x4e, 0x72, 0xeb, 0xfb, 0x2c, 0xf1, 0x45,
+	0xd3, 0x33, 0x65, 0xe7, 0xc5, 0x11, 0xdd, 0xe7,
+	0x09, 0x83, 0x13, 0xd5, 0x17, 0x1b, 0xf4, 0xbd,
+	0x49, 0xdd, 0x8a, 0x3c, 0x3c, 0xf7, 0xa1, 0x5d,
+	0x7b, 0xb4, 0xd3, 0x80, 0x25, 0xf4, 0x05, 0x8f,
+	0xbc, 0x2c, 0x2a, 0x47, 0xff, 0xd1, 0xc8, 0x34,
+	0xbf
+};
+CK_BYTE pubexp[] = { 0x01, 0x00, 0x01 };
+CK_BYTE privexp[] = {
+	0x00, 0xae, 0x02, 0xf1, 0x47, 0xa8, 0x07, 0x02,
+	0xb8, 0xf1, 0xd6, 0x92, 0x03, 0xee, 0x50, 0x33,
+	0xab, 0x67, 0x9e, 0x3b, 0xb1, 0x57, 0xc7, 0x3e,
+	0xc4, 0x86, 0x46, 0x61, 0xf1, 0xf8, 0xb6, 0x63,
+	0x9f, 0x91, 0xe6, 0x3f, 0x44, 0xb8, 0x77, 0x1b,
+	0xbe, 0x4c, 0x3c, 0xb8, 0x9f, 0xf7, 0x45, 0x7d,
+	0xbf, 0x4f, 0xef, 0x3b, 0xcc, 0xda, 0x1a, 0x4e,
+	0x34, 0xa8, 0x40, 0xea, 0x51, 0x72, 0x8a, 0xea,
+	0x47, 0x06, 0x04, 0xd0, 0x62, 0x31, 0xa0, 0x6c,
+	0x09, 0x60, 0xf9, 0xc7, 0x95, 0x88, 0x4a, 0xd7,
+	0x19, 0xce, 0x89, 0x08, 0x87, 0x14, 0xef, 0xcc,
+	0x0a, 0xef, 0x72, 0xb9, 0x21, 0xf5, 0xf0, 0xcd,
+	0x6d, 0xe5, 0xfa, 0x15, 0x7f, 0xae, 0x33, 0x9f,
+	0x26, 0xac, 0x2e, 0x52, 0x02, 0x07, 0xfb, 0x1d,
+	0x4b, 0xec, 0x9a, 0x6b, 0x3b, 0x26, 0x1f, 0x52,
+	0xfc, 0x47, 0xf8, 0x66, 0x33, 0xfa, 0x50, 0x6c,
+	0x41
+};
+CK_BYTE prime1[] = {
+	0x00, 0xe8, 0x98, 0xeb, 0xa1, 0xf0, 0xce, 0xde,
+	0xc2, 0x74, 0x01, 0x18, 0x2b, 0xd3, 0x8f, 0x58,
+	0xcd, 0xe9, 0x8e, 0x97, 0xbe, 0xfe, 0xe8, 0x6f,
+	0xd6, 0x0c, 0x0a, 0x47, 0xf8, 0x56, 0x84, 0x36,
+	0x15, 0xe6, 0x75, 0x1c, 0x69, 0x48, 0x8b, 0xf5,
+	0x0f, 0x84, 0xd2, 0x60, 0x8b, 0xa2, 0x2a, 0xa1,
+	0xeb, 0xed, 0xbe, 0x2d, 0xe9, 0x41, 0x0b, 0xed,
+	0x17, 0x7c, 0xd3, 0xa6, 0x35, 0x6e, 0xa6, 0xd8,
+	0x21
+};
+CK_BYTE prime2[] = {
+	0x00, 0xca, 0x15, 0x6a, 0x43, 0x5e, 0x83, 0xc9,
+	0x09, 0xeb, 0x14, 0x1e, 0x46, 0x46, 0x97, 0xfa,
+	0xfa, 0x3c, 0x61, 0x7e, 0xc1, 0xf8, 0x8c, 0x5e,
+	0xcb, 0xbf, 0xe4, 0xb9, 0x78, 0x7f, 0x4f, 0xab,
+	0x82, 0x15, 0x53, 0xaa, 0x04, 0xee, 0x11, 0x21,
+	0x2e, 0x23, 0x08, 0xa0, 0x14, 0x6d, 0x3a, 0x88,
+	0xe6, 0xf8, 0xbe, 0x61, 0x38, 0x99, 0xca, 0x36,
+	0x0d, 0x3e, 0x42, 0x0f, 0x63, 0x4d, 0x73, 0xf0,
+	0xdf
+};
+CK_BYTE exp_1[] = {
+	0x66, 0x2d, 0xb7, 0x65, 0xbe, 0x99, 0xc2, 0x35,
+	0xfe, 0x2b, 0xf4, 0xe8, 0x5b, 0xd9, 0xdf, 0x13,
+	0x26, 0x04, 0xe4, 0x18, 0x9d, 0x76, 0x92, 0x9a,
+	0x9f, 0x53, 0x6c, 0xe6, 0x65, 0x6b, 0x53, 0x2f,
+	0x2f, 0xbc, 0x46, 0xac, 0xe1, 0x97, 0xca, 0x21,
+	0xf5, 0x21, 0x4e, 0x14, 0x49, 0x3b, 0x1d, 0x42,
+	0xbd, 0x80, 0x0c, 0x3f, 0x29, 0xba, 0x09, 0x7f,
+	0x85, 0xf0, 0x9c, 0x55, 0x60, 0xb4, 0x9e, 0xc1
+};
+CK_BYTE exp_2[] = {
+	0x00, 0x87, 0x22, 0x74, 0xf1, 0xe2, 0x15, 0x3c,
+	0x6d, 0xde, 0x7e, 0x90, 0x94, 0x2c, 0x06, 0xdb,
+	0xb5, 0x54, 0x85, 0x59, 0xcf, 0x7a, 0x56, 0xdb,
+	0xd9, 0x62, 0x54, 0x20, 0x56, 0xdc, 0xc3, 0xb9,
+	0x0b, 0xff, 0x18, 0xf8, 0x7b, 0xdd, 0x7b, 0x24,
+	0xf6, 0x06, 0x45, 0x71, 0x4e, 0xd7, 0x90, 0x2a,
+	0x16, 0x52, 0x46, 0x75, 0x1a, 0xf5, 0x74, 0x8c,
+	0x5a, 0xa4, 0xc4, 0x66, 0x27, 0xe0, 0x96, 0x64,
+	0x7f
+};
+CK_BYTE coeff[] = {
+	0x00, 0xd0, 0x1f, 0xb3, 0x47, 0x40, 0x93, 0x8b,
+	0x99, 0xd7, 0xb5, 0xc6, 0x09, 0x82, 0x65, 0x94,
+	0x9d, 0x56, 0x0a, 0x05, 0x55, 0x7d, 0x93, 0x04,
+	0xa4, 0x26, 0xee, 0x42, 0x86, 0xa3, 0xf1, 0xd5,
+	0x7a, 0x42, 0x84, 0x3c, 0x21, 0x96, 0x9a, 0xd9,
+	0x36, 0xd4, 0x62, 0x01, 0xb0, 0x8b, 0x77, 0xe5,
+	0xcc, 0x1b, 0xd2, 0x12, 0xd2, 0x9c, 0x89, 0x67,
+	0x0c, 0x00, 0x09, 0x56, 0x8c, 0x33, 0x57, 0xf9,
+	0x8c
+};
+
+CK_BYTE buf[1024];
+CK_BYTE sig[128];
+
+static CK_BBOOL truevalue = TRUE;
+static CK_BBOOL falsevalue = FALSE;
+
+int
+main(int argc, char *argv[]) {
+	isc_result_t result;
+	CK_RV rv;
+	CK_SLOT_ID slot = 0;
+	CK_SESSION_HANDLE hSession = CK_INVALID_HANDLE;
+	CK_ULONG len;
+	CK_ULONG slen; 
+	CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE;
+	CK_OBJECT_CLASS kClass = CKO_PRIVATE_KEY;
+	CK_KEY_TYPE kType = CKK_RSA;
+	CK_ATTRIBUTE kTemplate[] =
+	{
+		{ CKA_CLASS, &kClass, (CK_ULONG) sizeof(kClass) },
+		{ CKA_KEY_TYPE, &kType, (CK_ULONG) sizeof(kType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_MODULUS, modulus, (CK_ULONG) sizeof(modulus) },
+		{ CKA_PUBLIC_EXPONENT, pubexp, (CK_ULONG) sizeof(pubexp) },
+		{ CKA_PRIVATE_EXPONENT, privexp, (CK_ULONG) sizeof(privexp) },
+		{ CKA_PRIME_1, prime1, (CK_ULONG) sizeof(prime1) },
+		{ CKA_PRIME_2, prime2, (CK_ULONG) sizeof(prime2) },
+		{ CKA_EXPONENT_1, exp_1, (CK_ULONG) sizeof(exp_1) },
+		{ CKA_EXPONENT_2, exp_2, (CK_ULONG) sizeof(exp_2) },
+		{ CKA_COEFFICIENT, coeff, (CK_ULONG) sizeof(coeff) }
+	};
+	CK_MECHANISM mech = { CKM_SHA1_RSA_PKCS, NULL, 0 };
+	pk11_context_t pctx;
+	pk11_optype_t op_type = OP_RSA;
+	char *lib_name = NULL;
+	char *pin = NULL;
+	int error = 0;
+	int c, errflg = 0;
+	int ontoken  = 0;
+	unsigned int count = 1000;
+	unsigned int i;
+	struct timespec starttime;
+	struct timespec endtime;
+
+	while ((c = isc_commandline_parse(argc, argv, ":m:s:p:tn:")) != -1) {
+		switch (c) {
+		case 'm':
+			lib_name = isc_commandline_argument;
+			break;
+		case 's':
+			op_type = OP_ANY;
+			slot = atoi(isc_commandline_argument);
+			break;
+		case 'p':
+			pin = isc_commandline_argument;
+			break;
+		case 't':
+			ontoken = 1;
+			break;
+		case 'n':
+			count = atoi(isc_commandline_argument);
+			break;
+		case ':':
+			fprintf(stderr,
+				"Option -%c requires an operand\n",
+				isc_commandline_option);
+			errflg++;
+			break;
+		case '?':
+		default:
+			fprintf(stderr, "Unrecognised option: -%c\n",
+				isc_commandline_option);
+			errflg++;
+		}
+	}
+
+	if (errflg) {
+		fprintf(stderr, "Usage:\n");
+		fprintf(stderr,
+			"\tsign [-m module] [-s slot] [-p pin] "
+			"[-t] [-n count]\n");
+		exit(1);
+	}
+
+	pk11_result_register();
+
+	/* Initialize the CRYPTOKI library */
+	if (lib_name != NULL)
+		pk11_set_lib_name(lib_name);
+
+	if (pin == NULL)
+		pin = getpassphrase("Enter Pin: ");
+
+	result = pk11_get_session(&pctx, op_type, ISC_FALSE, ISC_TRUE,
+				  ISC_TRUE, (const char *) pin, slot);
+	if ((result != ISC_R_SUCCESS) &&
+	    (result != PK11_R_NORANDOMSERVICE) &&
+	    (result != PK11_R_NODIGESTSERVICE) &&
+	    (result != PK11_R_NOAESSERVICE)) {
+		fprintf(stderr, "Error initializing PKCS#11: %s\n",
+			isc_result_totext(result));
+		exit(1);
+	}
+
+	if (pin != NULL)
+		memset(pin, 0, strlen((char *)pin));
+
+	hSession = pctx.session;
+
+	/* Create the private RSA key */
+	if (ontoken)
+		kTemplate[2].pValue = &truevalue;
+
+	rv = pkcs_C_CreateObject(hSession, kTemplate, 13, &hKey);
+	if (rv != CKR_OK) {
+		fprintf(stderr, "C_CreateObject: Error = 0x%.8lX\n", rv);
+		goto exit_key;
+	}
+
+	/* Randomize the buffer */
+	len = (CK_ULONG) sizeof(buf);
+	rv = pkcs_C_GenerateRandom(hSession, buf, len);
+	if (rv != CKR_OK) {
+		fprintf(stderr, "C_GenerateRandom: Error = 0x%.8lX\n", rv);
+		goto exit_key;
+	}
+
+	if (clock_gettime(CLOCK_REALTIME, &starttime) < 0) {
+		perror("clock_gettime(start)");
+		goto exit_key;
+	}
+
+	for (i = 0; i < count; i++) {
+		/* Initialize Sign */
+		rv = pkcs_C_SignInit(hSession, &mech, hKey);
+		if (rv != CKR_OK) {
+			fprintf(stderr,
+				"C_SignInit[%u]: Error = 0x%.8lX\n",
+				i, rv);
+			error = 1;
+			break;
+		}
+
+		/* Perform Sign */
+		slen = (CK_ULONG) sizeof(sig);
+		rv = pkcs_C_Sign(hSession, buf, len, sig, &slen);
+		if (rv != CKR_OK) {
+			fprintf(stderr,
+				"C_Sign[%u]: Error = 0x%.8lX\n",
+				i, rv);
+			error = 1;
+			break;
+		}
+	}
+
+	if (clock_gettime(CLOCK_REALTIME, &endtime) < 0) {
+		perror("clock_gettime(end)");
+		goto exit_key;
+	}
+
+	endtime.tv_sec -= starttime.tv_sec;
+	endtime.tv_nsec -= starttime.tv_nsec;
+	while (endtime.tv_nsec < 0) {
+		endtime.tv_sec -= 1;
+		endtime.tv_nsec += 1000000000;
+	}
+	printf("%u RSA signs in %ld.%09lds\n", i,
+	       endtime.tv_sec, endtime.tv_nsec);
+	if (i > 0)
+		printf("%g RSA signs/s\n",
+		       1024 * i / ((double) endtime.tv_sec +
+				   (double) endtime.tv_nsec / 1000000000.));
+
+    exit_key:
+	if (hKey != CK_INVALID_HANDLE) {
+		rv = pkcs_C_DestroyObject(hSession, hKey);
+		if (rv != CKR_OK)
+			fprintf(stderr,
+				"C_DestroyObject: Error = 0x%.8lX\n",
+				rv);
+	}
+
+	pk11_return_session(&pctx);
+	(void) pk11_finalize();
+
+	exit(error);
+}
diff --git a/bin/tests/pkcs11/benchmarks/verify.c b/bin/tests/pkcs11/benchmarks/verify.c
new file mode 100644
index 0000000..0a8f2c2
--- /dev/null
+++ b/bin/tests/pkcs11/benchmarks/verify.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Portions copyright (c) 2008 Nominet UK.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+/* verify [-m module] [-s $slot] [-p pin] [-t] [-n count] */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <isc/commandline.h>
+#include <isc/result.h>
+#include <isc/types.h>
+
+#include <pk11/pk11.h>
+#include <pk11/result.h>
+
+#if !(defined(HAVE_GETPASSPHRASE) || (defined (__SVR4) && defined (__sun)))
+#define getpassphrase(x)	getpass(x)
+#endif
+
+#ifndef HAVE_CLOCK_GETTIME
+#ifndef CLOCK_REALTIME
+#define CLOCK_REALTIME 0
+#endif
+
+int
+clock_gettime(int32_t id, struct timespec *tp)
+{
+	struct timeval tv;
+	int result;
+
+	result = gettimeofday(&tv, NULL);
+	if (result)
+		return (result);
+	tp->tv_sec = tv.tv_sec;
+	tp->tv_nsec = (long) tv.tv_usec * 1000;
+	return (result);
+}
+#endif
+
+CK_BYTE modulus[] = {
+	0x00, 0xb7, 0x9c, 0x1f, 0x05, 0xa3, 0xc2, 0x99,
+	0x44, 0x82, 0x20, 0x78, 0x43, 0x7f, 0x5f, 0x3b,
+	0x10, 0xd7, 0x9e, 0x61, 0x42, 0xd2, 0x7a, 0x90,
+	0x50, 0x8a, 0x99, 0x33, 0xe7, 0xca, 0xc8, 0x5f,
+	0x16, 0x1c, 0x56, 0xf8, 0xc1, 0x06, 0x2f, 0x96,
+	0xe7, 0x54, 0xf2, 0x85, 0x89, 0x41, 0x36, 0xf5,
+	0x4c, 0xa4, 0x0d, 0x62, 0xd3, 0x42, 0x51, 0x6b,
+	0x9f, 0xdc, 0x36, 0xcb, 0xad, 0x56, 0xf4, 0xbd,
+	0x2a, 0x60, 0x33, 0xb1, 0x7a, 0x99, 0xad, 0x08,
+	0x9f, 0x95, 0xe8, 0xe5, 0x14, 0xd9, 0x68, 0x79,
+	0xca, 0x4e, 0x72, 0xeb, 0xfb, 0x2c, 0xf1, 0x45,
+	0xd3, 0x33, 0x65, 0xe7, 0xc5, 0x11, 0xdd, 0xe7,
+	0x09, 0x83, 0x13, 0xd5, 0x17, 0x1b, 0xf4, 0xbd,
+	0x49, 0xdd, 0x8a, 0x3c, 0x3c, 0xf7, 0xa1, 0x5d,
+	0x7b, 0xb4, 0xd3, 0x80, 0x25, 0xf4, 0x05, 0x8f,
+	0xbc, 0x2c, 0x2a, 0x47, 0xff, 0xd1, 0xc8, 0x34,
+	0xbf
+};
+CK_BYTE exponent[] = { 0x01, 0x00, 0x01 };
+
+CK_BYTE buf[1024];
+CK_BYTE sig[128];
+
+static CK_BBOOL truevalue = TRUE;
+static CK_BBOOL falsevalue = FALSE;
+
+int
+main(int argc, char *argv[]) {
+	isc_result_t result;
+	CK_RV rv;
+	CK_SLOT_ID slot = 0;
+	CK_SESSION_HANDLE hSession = CK_INVALID_HANDLE;
+	CK_ULONG len;
+	CK_ULONG slen; 
+	CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE;
+	CK_OBJECT_CLASS kClass = CKO_PUBLIC_KEY;
+	CK_KEY_TYPE kType = CKK_RSA;
+	CK_ATTRIBUTE kTemplate[] =
+	{
+		{ CKA_CLASS, &kClass, (CK_ULONG) sizeof(kClass) },
+		{ CKA_KEY_TYPE, &kType, (CK_ULONG) sizeof(kType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_VERIFY, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_MODULUS, modulus, (CK_ULONG) sizeof(modulus) },
+		{ CKA_PUBLIC_EXPONENT, exponent, (CK_ULONG) sizeof(exponent) }
+	};
+	CK_MECHANISM mech = { CKM_SHA1_RSA_PKCS, NULL, 0 };
+	pk11_context_t pctx;
+	pk11_optype_t op_type = OP_RSA;
+	char *lib_name = NULL;
+	char *pin = NULL;
+	int error = 0;
+	int c, errflg = 0;
+	int ontoken  = 0;
+	unsigned int count = 1000;
+	unsigned int i;
+	struct timespec starttime;
+	struct timespec endtime;
+
+	while ((c = isc_commandline_parse(argc, argv, ":m:s:p:tn:")) != -1) {
+		switch (c) {
+		case 'm':
+			lib_name = isc_commandline_argument;
+			break;
+		case 's':
+			op_type = OP_ANY;
+			slot = atoi(isc_commandline_argument);
+			break;
+		case 'p':
+			pin = isc_commandline_argument;
+			break;
+		case 't':
+			ontoken = 1;
+			break;
+		case 'n':
+			count = atoi(isc_commandline_argument);
+			break;
+		case ':':
+			fprintf(stderr,
+				"Option -%c requires an operand\n",
+				isc_commandline_option);
+			errflg++;
+			break;
+		case '?':
+		default:
+			fprintf(stderr, "Unrecognised option: -%c\n",
+				isc_commandline_option);
+			errflg++;
+		}
+	}
+
+	if (errflg) {
+		fprintf(stderr, "Usage:\n");
+		fprintf(stderr,
+			"\tverify [-m module] [-s slot] [-p pin] "
+			"[-t] [-n count]\n");
+		exit(1);
+	}
+
+	pk11_result_register();
+
+	/* Initialize the CRYPTOKI library */
+	if (lib_name != NULL)
+		pk11_set_lib_name(lib_name);
+
+	if (pin == NULL)
+		pin = getpassphrase("Enter Pin: ");
+
+	result = pk11_get_session(&pctx, op_type, ISC_FALSE, ISC_TRUE,
+				  ISC_TRUE, (const char *) pin, slot);
+	if ((result != ISC_R_SUCCESS) &&
+	    (result != PK11_R_NORANDOMSERVICE) &&
+	    (result != PK11_R_NODIGESTSERVICE) &&
+	    (result != PK11_R_NOAESSERVICE)) {
+		fprintf(stderr, "Error initializing PKCS#11: %s\n",
+			isc_result_totext(result));
+		exit(1);
+	}
+
+	if (pin != NULL)
+		memset(pin, 0, strlen((char *)pin));
+
+	hSession = pctx.session;
+
+	/* Create the private RSA key */
+	if (ontoken)
+		kTemplate[2].pValue = &truevalue;
+
+	rv = pkcs_C_CreateObject(hSession, kTemplate, 7, &hKey);
+	if (rv != CKR_OK) {
+		fprintf(stderr, "C_CreateObject: Error = 0x%.8lX\n", rv);
+		error = 1;
+		goto exit_key;
+	}
+
+	/* Randomize the buffer */
+	len = (CK_ULONG) sizeof(buf);
+	rv = pkcs_C_GenerateRandom(hSession, buf, len);
+	if (rv != CKR_OK) {
+		fprintf(stderr, "C_GenerateRandom: Error = 0x%.8lX\n", rv);
+		goto exit_key;
+	}
+
+	if (clock_gettime(CLOCK_REALTIME, &starttime) < 0) {
+		perror("clock_gettime(start)");
+		goto exit_key;
+	}
+
+	for (i = 0; i < count; i++) {
+		/* Initialize Verify */
+		rv = pkcs_C_VerifyInit(hSession, &mech, hKey);
+		if (rv != CKR_OK) {
+			fprintf(stderr,
+				"C_VerifyInit[%u]: Error = 0x%.8lX\n",
+				i, rv);
+			error = 1;
+			break;
+		}
+
+		/* Perform Verify */
+		slen = (CK_ULONG) sizeof(sig);
+		rv = pkcs_C_Verify(hSession, buf, len, sig, slen);
+		if ((rv != CKR_OK) && (rv != CKR_SIGNATURE_INVALID)) {
+			fprintf(stderr,
+				"C_Verify[%u]: Error = 0x%.8lX\n",
+				i, rv);
+			error = 1;
+			break;
+		}
+	}
+
+	if (clock_gettime(CLOCK_REALTIME, &endtime) < 0) {
+		perror("clock_gettime(end)");
+		goto exit_key;
+	}
+
+	endtime.tv_sec -= starttime.tv_sec;
+	endtime.tv_nsec -= starttime.tv_nsec;
+	while (endtime.tv_nsec < 0) {
+		endtime.tv_sec -= 1;
+		endtime.tv_nsec += 1000000000;
+	}
+	printf("%u RSA verify in %ld.%09lds\n", i,
+	       endtime.tv_sec, endtime.tv_nsec);
+	if (i > 0)
+		printf("%g RSA verify/s\n",
+		       1024 * i / ((double) endtime.tv_sec +
+				   (double) endtime.tv_nsec / 1000000000.));
+
+    exit_key:
+	if (hKey != CK_INVALID_HANDLE) {
+		rv = pkcs_C_DestroyObject(hSession, hKey);
+		if (rv != CKR_OK) {
+			fprintf(stderr,
+				"C_DestroyObject: Error = 0x%.8lX\n",
+				rv);
+			errflg = 1;
+		}
+	}
+
+	pk11_return_session(&pctx);
+	(void) pk11_finalize();
+
+	exit(error);
+}
diff --git a/bin/tests/pkcs11/pkcs11-hmacmd5.c b/bin/tests/pkcs11/pkcs11-hmacmd5.c
new file mode 100644
index 0000000..00a1df1
--- /dev/null
+++ b/bin/tests/pkcs11/pkcs11-hmacmd5.c
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Portions copyright (c) 2008 Nominet UK.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+/*
+ * pkcs11-hmacmd5
+ *
+ * Prints the MD5 HMAC of the standard input, using the PKCS#11 device.
+ *
+ * Usage:
+ * pkcs11-hmacmd5 [-m module] [-s $slot] [-n] [-p $pin]
+ *  -m: PKCS#11 provider module.  This must be the full
+ *      path to a shared library object implementing the
+ *      PKCS#11 API for a device.
+ *  -s: Slot
+ *  -p: PIN
+ *  -n: don't log in to the PKCS#11 device
+ *  -k: key name for the HMAC
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <isc/commandline.h>
+#include <isc/result.h>
+#include <isc/types.h>
+
+#include <pk11/pk11.h>
+#include <pk11/result.h>
+
+#if !(defined(HAVE_GETPASSPHRASE) || (defined (__SVR4) && defined (__sun)))
+#define getpassphrase(x)	getpass(x)
+#endif
+
+/* Define static key template values */
+static CK_BBOOL truevalue = TRUE;
+static CK_BBOOL falsevalue = FALSE;
+
+#define BLOCKSIZE	32768
+
+char buffer[BLOCKSIZE + 72];
+char digest[16];
+
+int
+main(int argc, char *argv[]) {
+	isc_result_t result;
+	CK_RV rv;
+	CK_SLOT_ID slot = 0;
+	CK_SESSION_HANDLE hSession;
+	CK_MECHANISM mech = { CKM_MD5_HMAC, NULL, 0 };
+	CK_ULONG len;
+	CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE;
+	CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
+	CK_KEY_TYPE keyType = CKK_MD5_HMAC;
+	CK_ATTRIBUTE keyTemplate[] =
+	{
+		{ CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_VALUE, NULL, 0 }
+	};
+	pk11_context_t pctx;
+	pk11_optype_t op_type = OP_DIGEST;
+	char *lib_name = NULL;
+	char *pin = NULL;
+	int error = 0;
+	isc_boolean_t logon = ISC_TRUE;
+	int c, errflg = 0;
+	char *key = NULL;
+	size_t sum = 0;
+	unsigned int i;
+
+	while ((c = isc_commandline_parse(argc, argv, ":m:s:np:k:")) != -1) {
+		switch (c) {
+		case 'm':
+			lib_name = isc_commandline_argument;
+			break;
+		case 's':
+			op_type = OP_ANY;
+			slot = atoi(isc_commandline_argument);
+			break;
+		case 'n':
+			logon = ISC_FALSE;
+			break;
+		case 'p':
+			pin = isc_commandline_argument;
+			break;
+		case 'k':
+			key = isc_commandline_argument;
+			break;
+		case ':':
+			fprintf(stderr,
+				"Option -%c requires an operand\n",
+				isc_commandline_option);
+			errflg++;
+			break;
+		case '?':
+		default:
+			fprintf(stderr, "Unrecognised option: -%c\n",
+				isc_commandline_option);
+			errflg++;
+		}
+	}
+
+	if (errflg || (key == NULL)) {
+		fprintf(stderr, "Usage:\n");
+		fprintf(stderr,
+			"\tpkcs11-hmacmd5 [-m module] [-s slot] "
+			"[-n|-p pin] -k key\n");
+		exit(1);
+	}
+
+	/* Decode the key */
+	for (i = 0; i < BLOCKSIZE / 2; i++) {
+		switch (c = *key++) {
+		case 0:
+			goto key_done;
+		case '0':
+		case '1':
+		case '2':
+		case '3':
+		case '4':
+		case '5':
+		case '6':
+		case '7':
+		case '8':
+		case '9':
+			if ((i & 1) == 0)
+				buffer[i >> 1] = (c - '0') << 4;
+			else
+				buffer[i >> 1] |= c - '0';
+			break;
+		case 'A':
+		case 'B':
+		case 'C':
+		case 'D':
+		case 'E':
+		case 'F':
+			if ((i & 1) == 0)
+				buffer[i >> 1] = (c - 'A' + 10) << 4;
+			else
+				buffer[i >> 1] |= c - 'A' + 10;
+			break;
+		case 'a':
+		case 'b':
+		case 'c':
+		case 'd':
+		case 'e':
+		case 'f':
+			if ((i & 1) == 0)
+				buffer[i >> 1] = (c - 'a' + 10) << 4;
+			else
+				buffer[i >> 1] |= c - 'a' + 10;
+			break;
+		default:
+			fprintf(stderr, "Not hexdigit '%c' in key\n", c);
+			exit(1);
+		}
+	}
+    key_done:
+	if ((i & 1) != 0) {
+		fprintf(stderr, "Even number of hexdigits in key\n");
+		exit(1);
+	}
+	len = i >> 1;
+	keyTemplate[5].pValue = buffer;
+	keyTemplate[5].ulValueLen = (CK_ULONG) len;
+
+	pk11_result_register();
+
+	/* Initialize the CRYPTOKI library */
+	if (lib_name != NULL)
+		pk11_set_lib_name(lib_name);
+
+	if (logon && pin == NULL)
+		pin = getpassphrase("Enter Pin: ");
+
+	result = pk11_get_session(&pctx, op_type, ISC_FALSE, ISC_FALSE, logon,
+				  (const char *) pin, slot);
+	if ((result != ISC_R_SUCCESS) &&
+	    (result != PK11_R_NORANDOMSERVICE) &&
+	    (result != PK11_R_NOAESSERVICE)) {
+		fprintf(stderr, "Error initializing PKCS#11: %s\n",
+			isc_result_totext(result));
+		exit(1);
+	}
+
+	if (pin != NULL)
+		memset(pin, 0, strlen((char *)pin));
+
+	hSession = pctx.session;
+
+	rv = pkcs_C_CreateObject(hSession, keyTemplate, (CK_ULONG) 6, &hKey);
+	if (rv != CKR_OK) {
+		fprintf(stderr, "C_CreateObject: Error = 0x%.8lX\n", rv);
+		error = 1;
+		goto exit_session;
+	}
+	if (hKey == CK_INVALID_HANDLE) {
+		fprintf(stderr, "C_CreateObject failed\n");
+		error = 1;
+		goto exit_session;
+	}
+
+	rv = pkcs_C_SignInit(hSession, &mech, hKey);
+	if (rv != CKR_OK) {
+		fprintf(stderr, "C_SignInit: Error = 0x%.8lX\n", rv);
+		error = 1;
+		goto exit_sign;
+	}
+	
+	for (;;) {
+		size_t n;
+
+		for (;;) {
+			n = fread(buffer + sum, 1, BLOCKSIZE - sum, stdin);
+			sum += n;
+			if (sum == BLOCKSIZE)
+				break;
+			if (n == 0) {
+				if (ferror(stdin)) {
+					fprintf(stderr, "fread failed\n");
+					error = 1;
+					goto exit_sign;
+				}
+				goto partial_block;
+			}
+			if (feof(stdin))
+				goto partial_block;
+		}
+
+		rv = pkcs_C_SignUpdate(hSession, (CK_BYTE_PTR) buffer,
+				       (CK_ULONG) BLOCKSIZE);
+		if (rv != CKR_OK) {
+			fprintf(stderr,
+				"C_SignUpdate: Error = 0x%.8lX\n",
+				rv);
+			error = 1;
+			goto exit_sign;
+		}
+	}
+
+partial_block:
+	if (sum > 0) {
+		rv = pkcs_C_SignUpdate(hSession, (CK_BYTE_PTR) buffer,
+				       (CK_ULONG) sum);
+		if (rv != CKR_OK) {
+			fprintf(stderr,
+				"C_SignUpdate: Error = 0x%.8lX\n",
+				rv);
+			error = 1;
+			goto exit_sign;
+		}
+	}
+
+	len = 16;
+	rv = pkcs_C_SignFinal(hSession, (CK_BYTE_PTR) digest, &len);
+	if (rv != CKR_OK) {
+		fprintf(stderr, "C_SignFinal: Error = 0x%.8lX\n", rv);
+		error = 1;
+		goto exit_sign;
+	}
+	if (len != 16) {
+		fprintf(stderr, "C_SignFinal: bad length = %lu\n", len);
+		error = 1;
+	}
+
+	for (i = 0; i < 16; i++)
+		printf("%02x", digest[i] & 0xff);
+	printf("\n");
+
+    exit_sign:
+	rv = pkcs_C_DestroyObject(hSession, hKey);
+	if ((error == 0) && (rv != CKR_OK)) {
+		fprintf(stderr, "C_DestroyObject: Error = 0x%.8lX\n", rv);
+		error = 1;
+	}
+
+    exit_session:
+	pk11_return_session(&pctx);
+	(void) pk11_finalize();
+
+	exit(error);
+}
diff --git a/bin/tests/pkcs11/pkcs11-md5sum.c b/bin/tests/pkcs11/pkcs11-md5sum.c
new file mode 100644
index 0000000..fd50648
--- /dev/null
+++ b/bin/tests/pkcs11/pkcs11-md5sum.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Portions copyright (c) 2008 Nominet UK.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+/*
+ * pkcs11-md5sum
+ *
+ * Prints the MD5 checksum of the standard input, using the PKCS#11 device.
+ *
+ * Usage:
+ * pkcs11-md5sum [-m module] [-s $slot] [-n] [-p $pin]
+ *  -m: PKCS#11 provider module.  This must be the full
+ *      path to a shared library object implementing the
+ *      PKCS#11 API for a device.
+ *  -s: Slot
+ *  -p: PIN
+ *  -n: don't log in to the PKCS#11 device
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <isc/commandline.h>
+#include <isc/result.h>
+#include <isc/types.h>
+
+#include <pk11/pk11.h>
+#include <pk11/result.h>
+
+#if !(defined(HAVE_GETPASSPHRASE) || (defined (__SVR4) && defined (__sun)))
+#define getpassphrase(x)	getpass(x)
+#endif
+
+#define BLOCKSIZE	32768
+
+char buffer[BLOCKSIZE + 72];
+char digest[16];
+
+int
+main(int argc, char *argv[]) {
+	isc_result_t result;
+	CK_RV rv;
+	CK_SLOT_ID slot = 0;
+	CK_SESSION_HANDLE hSession;
+	CK_MECHANISM mech = { CKM_MD5, NULL, 0 };
+	CK_ULONG len;
+	pk11_context_t pctx;
+	pk11_optype_t op_type = OP_DIGEST;
+	char *lib_name = NULL;
+	char *pin = NULL;
+	int error = 0;
+	isc_boolean_t logon = ISC_TRUE;
+	int c, errflg = 0;
+	size_t sum = 0;
+	unsigned int i;
+
+	while ((c = isc_commandline_parse(argc, argv, ":m:s:np:")) != -1) {
+		switch (c) {
+		case 'm':
+			lib_name = isc_commandline_argument;
+			break;
+		case 's':
+			op_type = OP_ANY;
+			slot = atoi(isc_commandline_argument);
+			break;
+		case 'n':
+			logon = ISC_FALSE;
+			break;
+		case 'p':
+			pin = isc_commandline_argument;
+			break;
+		case ':':
+			fprintf(stderr,
+				"Option -%c requires an operand\n",
+				isc_commandline_option);
+			errflg++;
+			break;
+		case '?':
+		default:
+			fprintf(stderr, "Unrecognised option: -%c\n",
+				isc_commandline_option);
+			errflg++;
+		}
+	}
+
+	if (errflg) {
+		fprintf(stderr, "Usage:\n");
+		fprintf(stderr,
+			"\tpkcs11-md5sum [-m module] [-s slot] [-n|-p pin]\n");
+		exit(1);
+	}
+
+	pk11_result_register();
+
+	/* Initialize the CRYPTOKI library */
+	if (lib_name != NULL)
+		pk11_set_lib_name(lib_name);
+
+	if (logon && pin == NULL)
+		pin = getpassphrase("Enter Pin: ");
+
+	result = pk11_get_session(&pctx, op_type, ISC_FALSE, ISC_FALSE, logon,
+				  (const char *) pin, slot);
+	if ((result != ISC_R_SUCCESS) &&
+	    (result != PK11_R_NORANDOMSERVICE) &&
+	    (result != PK11_R_NOAESSERVICE)) {
+		fprintf(stderr, "Error initializing PKCS#11: %s\n",
+			isc_result_totext(result));
+		exit(1);
+	}
+
+	if (pin != NULL)
+		memset(pin, 0, strlen((char *)pin));
+
+	hSession = pctx.session;
+
+	rv = pkcs_C_DigestInit(hSession, &mech);
+	if (rv != CKR_OK) {
+		fprintf(stderr, "C_DigestInit: Error = 0x%.8lX\n", rv);
+		error = 1;
+		goto exit_session;
+	}
+	
+	for (;;) {
+		size_t n;
+
+		for (;;) {
+			n = fread(buffer + sum, 1, BLOCKSIZE - sum, stdin);
+			sum += n;
+			if (sum == BLOCKSIZE)
+				break;
+			if (n == 0) {
+				if (ferror(stdin)) {
+					fprintf(stderr, "fread failed\n");
+					error = 1;
+					goto exit_session;
+				}
+				goto partial_block;
+			}
+			if (feof(stdin))
+				goto partial_block;
+		}
+
+		rv = pkcs_C_DigestUpdate(hSession, (CK_BYTE_PTR) buffer,
+					 (CK_ULONG) BLOCKSIZE);
+		if (rv != CKR_OK) {
+			fprintf(stderr,
+				"C_DigestUpdate: Error = 0x%.8lX\n",
+				rv);
+			error = 1;
+			goto exit_session;
+		}
+	}
+
+partial_block:
+	if (sum > 0) {
+		rv = pkcs_C_DigestUpdate(hSession, (CK_BYTE_PTR) buffer,
+					 (CK_ULONG) sum);
+		if (rv != CKR_OK) {
+			fprintf(stderr,
+				"C_DigestUpdate: Error = 0x%.8lX\n",
+				rv);
+			error = 1;
+			goto exit_session;
+		}
+	}
+
+	len = 16;
+	rv = pkcs_C_DigestFinal(hSession, (CK_BYTE_PTR) digest, &len);
+	if (rv != CKR_OK) {
+		fprintf(stderr, "C_DigestFinal: Error = 0x%.8lX\n", rv);
+		error = 1;
+		goto exit_session;
+	}
+	if (len != 16) {
+		fprintf(stderr, "C_DigestFinal: bad length = %lu\n", len);
+		error = 1;
+	}
+
+	for (i = 0; i < 16; i++)
+		printf("%02x", digest[i] & 0xff);
+	printf("\n");
+
+    exit_session:
+	pk11_return_session(&pctx);
+	(void) pk11_finalize();
+
+	exit(error);
+}
diff --git a/bin/tests/system/autosign/prereq.sh b/bin/tests/system/autosign/prereq.sh
index 34cd4a1..53807a2 100644
--- a/bin/tests/system/autosign/prereq.sh
+++ b/bin/tests/system/autosign/prereq.sh
@@ -25,6 +25,7 @@ if $KEYGEN -q -a RSAMD5 -b 512 -n zone -r random.data foo > /dev/null 2>&1
 then
     rm -f Kfoo*
 else
-    echo "I:This test requires that --with-openssl was used." >&2
+    echo "I:This test requires cryptography" >&2
+    echo "I:--with-openssl, or --with-pkcs11 and --enable-native-pkcs11" >&2
     exit 1
 fi
diff --git a/bin/tests/system/cleanpkcs11.sh b/bin/tests/system/cleanpkcs11.sh
index e1cbc6f..ba541ed 100644
--- a/bin/tests/system/cleanpkcs11.sh
+++ b/bin/tests/system/cleanpkcs11.sh
@@ -16,6 +16,10 @@
 
 # $Id: cleanpkcs11.sh,v 1.3 2010/06/08 23:50:24 tbox Exp $
 
+SYSTEMTESTTOP=.
+. $SYSTEMTESTTOP/conf.sh
+
+
 if [ ! -x ../../pkcs11/pkcs11-destroy ]; then exit 1; fi
 
-../../pkcs11/pkcs11-destroy -s ${SLOT:-0} -p 1234
+$PK11DEL -w0 > /dev/null 2>&1
diff --git a/bin/tests/system/conf.sh.in b/bin/tests/system/conf.sh.in
index d6e902f..c40e8f1 100644
--- a/bin/tests/system/conf.sh.in
+++ b/bin/tests/system/conf.sh.in
@@ -47,9 +47,9 @@ CHECKDS=$TOP/bin/python/dnssec-checkds
 COVERAGE=$TOP/bin/python/dnssec-coverage
 CHECKZONE=$TOP/bin/check/named-checkzone
 CHECKCONF=$TOP/bin/check/named-checkconf
-PK11GEN="$TOP/bin/pkcs11/pkcs11-keygen -s ${SLOT:-0} -p 1234"
-PK11LIST="$TOP/bin/pkcs11/pkcs11-list -s ${SLOT:-0} -p 1234"
-PK11DEL="$TOP/bin/pkcs11/pkcs11-destroy -s ${SLOT:-0} -p 1234"
+PK11GEN="$TOP/bin/pkcs11/pkcs11-keygen -q -s ${SLOT:-0} -p ${HSMPIN:-1234}"
+PK11LIST="$TOP/bin/pkcs11/pkcs11-list -s ${SLOT:-0} -p ${HSMPIN:-1234}"
+PK11DEL="$TOP/bin/pkcs11/pkcs11-destroy -s ${SLOT:-0} -p ${HSMPIN:-1234} -w 0"
 JOURNALPRINT=$TOP/bin/tools/named-journalprint
 VERIFY=$TOP/bin/dnssec/dnssec-verify
 ARPANAME=$TOP/bin/tools/arpaname
@@ -63,7 +63,7 @@ SUBDIRS="acl additional allow_query addzone autosign builtin
          database dlv dlvauto dlz dlzexternal dname dns64 dnssec ecdsa
          formerr forward glue gost ixfr inline limits logfileconfig
          lwresd masterfile masterformat metadata notify nsupdate pending
-	 pkcs11 redirect resolver rndc rpz rrl rrsetorder rsabigexponent
+	 @PKCS11_TEST@ redirect resolver rndc rpz rrl rrsetorder rsabigexponent
 	 smartsign sortlist spf staticstub stub tkey tsig tsiggss unknown
 	 upforwd verify views wildcard xfer xferquota zero zonechecks"
 
diff --git a/bin/tests/system/dnssec/prereq.sh b/bin/tests/system/dnssec/prereq.sh
index cb7c0c7..113e372 100644
--- a/bin/tests/system/dnssec/prereq.sh
+++ b/bin/tests/system/dnssec/prereq.sh
@@ -23,6 +23,7 @@ if $KEYGEN -q -a RSAMD5 -b 512 -n zone -r random.data foo > /dev/null 2>&1
 then
     rm -f Kfoo*
 else
-    echo "I:This test requires that --with-openssl was used." >&2
+    echo "I:This test requires cryptography" >&2
+    echo "I:--with-openssl, or --with-pkcs11 and --enable-native-pkcs11" >&2
     exit 1
 fi
diff --git a/bin/tests/system/ecdsa/prereq.sh.in b/bin/tests/system/ecdsa/prereq.sh.in
index 434b53c..4214a30 100644
--- a/bin/tests/system/ecdsa/prereq.sh.in
+++ b/bin/tests/system/ecdsa/prereq.sh.in
@@ -16,9 +16,16 @@
 
 # $Id$
 
-OPENSSL_ECDSA="@OPENSSL_ECDSA@"
-if test -z "$OPENSSL_ECDSA"
-then
-    echo "I:This test requires a openssl version with ecdsa support." >&2
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
+../../../tools/genrandom 400 random.data
+
+fail=0
+$KEYGEN -q -a ecdsap256sha256 test > /dev/null 2>&1 || fail=1
+rm -f Ktest* random.data
+
+if [ $fail != 0 ]
+ then
+    echo "I:This test requires support for ECDSA cryptography." >&2
     exit 255
 fi
diff --git a/bin/tests/system/gost/prereq.sh.in b/bin/tests/system/gost/prereq.sh.in
index 98ec507..0e4079e 100644
--- a/bin/tests/system/gost/prereq.sh.in
+++ b/bin/tests/system/gost/prereq.sh.in
@@ -16,9 +16,16 @@
 
 # $Id: prereq.sh.in,v 1.4 2010/12/27 13:38:43 marka Exp $
 
-OPENSSL_GOST="@OPENSSL_GOST@"
-if test -z "$OPENSSL_GOST"
-then
-    echo "I:This test requires a openssl version with gost support." >&2
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
+../../../tools/genrandom 400 random.data
+
+fail=0
+$KEYGEN -q -a eccgost test > /dev/null 2>&1 || fail=1
+rm -f Ktest* random.data
+
+if [ $fail != 0 ]
+ then
+    echo "I:This test requires support for GOST cryptography." >&2
     exit 255
 fi
diff --git a/bin/tests/system/inline/clean.sh b/bin/tests/system/inline/clean.sh
index dae21e5..b6dda0b 100644
--- a/bin/tests/system/inline/clean.sh
+++ b/bin/tests/system/inline/clean.sh
@@ -74,7 +74,7 @@ rm -f ns5/bits.bk.signed
 rm -f ns5/bits.bk.signed.jnl
 rm -f */*.jbk
 rm -f random.data
-rm -f dig.out.ns*.test*
+rm -f dig.out.ns*
 rm -f signing.out*
 rm -f freeze.test*
 rm -f thaw.test*
diff --git a/bin/tests/system/metadata/prereq.sh b/bin/tests/system/metadata/prereq.sh
index b7ce1ea..006bcf5 100644
--- a/bin/tests/system/metadata/prereq.sh
+++ b/bin/tests/system/metadata/prereq.sh
@@ -22,6 +22,7 @@ if $KEYGEN -q -r random.data foo > /dev/null 2>&1
 then
     rm -f Kfoo*
 else
-    echo "I:This test requires that --with-openssl was used." >&2
+    echo "I:This test requires cryptography" >&2
+    echo "I:--with-openssl, or --with-pkcs11 and --enable-native-pkcs11" >&2
     exit 1
 fi
diff --git a/bin/tests/system/pending/prereq.sh b/bin/tests/system/pending/prereq.sh
index 0b6998e..f0848d7 100644
--- a/bin/tests/system/pending/prereq.sh
+++ b/bin/tests/system/pending/prereq.sh
@@ -16,12 +16,25 @@
 
 # $Id: prereq.sh,v 1.3 2009/11/18 23:48:06 tbox Exp $
 
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
 ../../../tools/genrandom 400 random.data
 
-if $KEYGEN -q -a RSAMD5 -b 512 -n zone -r random.data foo > /dev/null 2>&1
-then
-    rm -f Kfoo*
+rsafail=0 eccfail=0
+
+$KEYGEN -q -r random.data foo > /dev/null 2>&1 || rsafail=1
+rm -f Kfoo*
+
+$KEYGEN -q -a ECDSAP256SHA256 -r random.data foo > /dev/null 2>&1 || eccfail=1
+rm -f Kfoo*
+
+if [ $rsafail = 0 -a $eccfail = 0 ]; then
+	echo both > supported
+elif [ $rsafail = 1 -a $eccfail = 1 ]; then
+	echo "I:This test requires PKCS#11 support for either RSA or ECDSA cryptography." >&2
+	exit 255
+elif [ $rsafail = 0 ]; then
+	echo rsaonly > supported
 else
-    echo "I:This test requires that --with-openssl was used." >&2
-    exit 1
+    echo ecconly > supported
 fi
diff --git a/bin/tests/system/pkcs11/clean.sh b/bin/tests/system/pkcs11/clean.sh
index d7a557b..29d0149 100644
--- a/bin/tests/system/pkcs11/clean.sh
+++ b/bin/tests/system/pkcs11/clean.sh
@@ -17,5 +17,6 @@
 # $Id: clean.sh,v 1.3 2010/06/08 23:50:24 tbox Exp $
 
 rm -f K* ns1/K* keyset-* dsset-* ns1/*.db ns1/*.signed ns1/*.jnl
-rm -f dig.out random.data
-rm -f ns1/key ns1/named.memstats
+rm -f dig.out random.data pin
+rm -f ns1/*.key ns1/named.memstats
+rm -f supported
diff --git a/bin/tests/system/pkcs11/ns1/named.conf b/bin/tests/system/pkcs11/ns1/named.conf
index 09a850f..48b8adf 100644
--- a/bin/tests/system/pkcs11/ns1/named.conf
+++ b/bin/tests/system/pkcs11/ns1/named.conf
@@ -39,8 +39,14 @@ controls {
 	inet 10.53.0.1 port 9953 allow { any; } keys { rndc_key; };
 };
 
-zone "example." {
+zone "rsa.example." {
+ 	type master;
+	file "rsa.example.db.signed";
+	allow-update { any; };
+};
+
+zone "ecc.example." {
 	type master;
-	file "example.db.signed";
+	file "ecc.example.db.signed";
 	allow-update { any; };
 };
diff --git a/bin/tests/system/pkcs11/setup.sh b/bin/tests/system/pkcs11/setup.sh
index c044d75..a17a83d 100644
--- a/bin/tests/system/pkcs11/setup.sh
+++ b/bin/tests/system/pkcs11/setup.sh
@@ -21,21 +21,59 @@ SYSTEMTESTTOP=..
 
 RANDFILE=random.data
 
-zone=example
 infile=ns1/example.db.in
-zonefile=ns1/example.db
 
-$PK11GEN -b 1024 -l robie-zsk1 -i 01
-$PK11GEN -b 1024 -l robie-zsk2 -i 02
-$PK11GEN -b 2048 -l robie-ksk
+/bin/echo -n ${HSMPIN:-1234}> pin
+PWD=`pwd`
 
-zsk1=`$KEYFRLAB -a RSASHA1 -l robie-zsk1 example`
-zsk2=`$KEYFRLAB -a RSASHA1 -l robie-zsk2 example`
-ksk=`$KEYFRLAB -a RSASHA1 -f ksk -l robie-ksk example`
+supported=`cat supported`
 
-cat $infile $zsk1.key $ksk.key > $zonefile
-$SIGNER -a -P -g -r $RANDFILE -o $zone $zonefile > /dev/null 2> signer.err || cat signer.err
-rm -f signer.err
+zone=rsa.example
+zonefile=ns1/rsa.example.db
+if [ "$supported" != "ecconly" ]; then
+    $PK11GEN -a RSA -b 1024 -l robie-rsa-zsk1 -i 01
+    $PK11GEN -a RSA -b 1024 -l robie-rsa-zsk2 -i 02
+    $PK11GEN -a RSA -b 2048 -l robie-rsa-ksk
+
+    rsazsk1=`$KEYFRLAB -a RSASHA1 \
+            -l "object=robie-rsa-zsk1;pin-source=$PWD/pin" rsa.example`
+    rsazsk2=`$KEYFRLAB -a RSASHA1 \
+            -l "object=robie-rsa-zsk2;pin-source=$PWD/pin" rsa.example`
+    rsaksk=`$KEYFRLAB -a RSASHA1 -f ksk \
+            -l "object=robie-rsa-ksk;pin-source=$PWD/pin" rsa.example`
+
+    cat $infile $rsazsk1.key $rsaksk.key > $zonefile
+    $SIGNER -a -P -g -r $RANDFILE -o $zone $zonefile \
+            > /dev/null 2> signer.err || cat signer.err
+    cp $rsazsk2.key ns1/rsa.key
+    mv Krsa* ns1
+else
+    # RSA not available and will not be tested; make a placeholder
+    cp $infile ${zonefile}.signed
+fi
+
+zone=ecc.example
+zonefile=ns1/ecc.example.db
+if [ "$supported" != "rsaonly" ]; then
+    $PK11GEN -a ECC -b 256 -l robie-ecc-zsk1 -i 03
+    $PK11GEN -a ECC -b 256 -l robie-ecc-zsk2 -i 04
+    $PK11GEN -a ECC -b 384 -l robie-ecc-ksk
 
-cp $zsk2.key ns1/key
-mv Kexample* ns1
+    ecczsk1=`$KEYFRLAB -a ECDSAP256SHA256 \
+            -l "object=robie-ecc-zsk1;pin-source=$PWD/pin" ecc.example`
+    ecczsk2=`$KEYFRLAB -a ECDSAP256SHA256 \
+            -l "object=robie-ecc-zsk2;pin-source=$PWD/pin" ecc.example`
+    eccksk=`$KEYFRLAB -a ECDSAP384SHA384 -f ksk \
+            -l "object=robie-ecc-ksk;pin-source=$PWD/pin" ecc.example`
+
+    cat $infile $ecczsk1.key $eccksk.key > $zonefile
+    $SIGNER -a -P -g -r $RANDFILE -o $zone $zonefile \
+        > /dev/null 2> signer.err || cat signer.err
+    cp $ecczsk2.key ns1/ecc.key
+    mv Kecc* ns1
+else
+    # ECC not available and will not be tested; make a placeholder
+    cp $infile ${zonefile}.signed
+fi
+
+rm -f signer.err
diff --git a/bin/tests/system/pkcs11/tests.sh b/bin/tests/system/pkcs11/tests.sh
index 4694afc..01f1523 100644
--- a/bin/tests/system/pkcs11/tests.sh
+++ b/bin/tests/system/pkcs11/tests.sh
@@ -26,47 +26,59 @@ DIGOPTS="+tcp +noadd +nosea +nostat +nocmd +dnssec -p 5300"
 status=0
 ret=0
 
-zonefile=ns1/example.db
+supported=`cat supported`
+case $supported in
+    rsaonly) algs="rsa" ;;
+    ecconly) algs="ecc" ;;
+    both) algs="rsa ecc" ;;
+esac
 
-echo "I:testing PKCS#11 key generation"
+for alg in $algs; do
+    zonefile=ns1/$alg.example.db 
+    echo "I:testing PKCS#11 key generation ($alg)"
+    count=`$PK11LIST | grep robie-$alg-ksk | wc -l`
+    if [ $count != 2 ]; then echo "I:failed"; status=1; fi
 
-count=`$PK11LIST | grep robie-ksk | wc -l`
-if [ $count != 2 ]; then echo "I:failed"; status=1; fi
+    echo "I:testing offline signing with PKCS#11 keys ($alg)"
 
-echo "I:testing offline signing with PKCS#11 keys"
+    count=`grep RRSIG $zonefile.signed | wc -l`
+    if [ $count != 12 ]; then echo "I:failed"; status=1; fi
 
-count=`grep RRSIG $zonefile.signed | wc -l`
-if [ $count != 12 ]; then echo "I:failed"; status=1; fi
+    echo "I:testing inline signing with PKCS#11 keys ($alg)"
 
-echo "I:testing inline signing with PKCS#11 keys"
+    $NSUPDATE > /dev/null <<END || status=1
 
-$NSUPDATE > /dev/null <<END || status=1
 server 10.53.0.1 5300
 ttl 300
-zone example.
-update add `grep -v ';' ns1/key`
+zone $alg.example.
+update add `grep -v ';' ns1/${alg}.key`
 send
 END
 
-echo "I:waiting 20 seconds for key changes to take effect"
-sleep 20
-
-$DIG $DIGOPTS ns.example. @10.53.0.1 a > dig.out || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
-status=`expr $status + $ret`
-count=`grep RRSIG dig.out | wc -l`
-if [ $count != 4 ]; then echo "I:failed"; status=1; fi
-
-echo "I:testing PKCS#11 key destroy"
-
-ret=0
-$PK11DEL -l robie-zsk1 || ret=1
-$PK11DEL -i 02 || ret=1
-if [ $ret != 0 ]; then echo "I:failed"; fi
-status=`expr $status + $ret`
-count=`$PK11LIST | grep robie-zsk | wc -l`
-if [ $count != 0 ]; then echo "I:failed"; fi
-status=`expr $status + $count`
+    echo "I:waiting 20 seconds for key changes to take effect"
+    sleep 20
+
+    $DIG $DIGOPTS ns.$alg.example. @10.53.0.1 a > dig.out || ret=1
+    if [ $ret != 0 ]; then echo "I:failed"; fi
+    status=`expr $status + $ret`
+    count=`grep RRSIG dig.out | wc -l`
+    if [ $count != 4 ]; then echo "I:failed"; status=1; fi
+
+    echo "I:testing PKCS#11 key destroy ($alg)"
+    ret=0
+    $PK11DEL -l robie-$alg-ksk -w0 > /dev/null 2>&1 || ret=1
+    $PK11DEL -l robie-$alg-zsk1 -w0 > /dev/null 2>&1 || ret=1
+    case $alg in
+        rsa) id=02 ;;
+        ecc) id=04 ;;
+    esac
+    $PK11DEL -i $id -w0 > /dev/null 2>&1 || ret=1
+    if [ $ret != 0 ]; then echo "I:failed"; fi
+    status=`expr $status + $ret`
+    count=`$PK11LIST | grep robie-$alg | wc -l`
+    if [ $count != 0 ]; then echo "I:failed"; fi
+    status=`expr $status + $count`
+done
 
 echo "I:exit status: $status"
 exit $status
diff --git a/bin/tests/system/pkcs11ssl/clean.sh b/bin/tests/system/pkcs11ssl/clean.sh
new file mode 100644
index 0000000..14ec725
--- /dev/null
+++ b/bin/tests/system/pkcs11ssl/clean.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+#
+# Copyright (C) 2010, 2012  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+rm -f K* ns1/K* keyset-* dsset-* ns1/*.db ns1/*.signed ns1/*.jnl
+rm -f dig.out random.data pin
+rm -f ns1/*.key ns1/named.memstats
+rm -f supported
diff --git a/bin/tests/system/pkcs11ssl/ns1/example.db.in b/bin/tests/system/pkcs11ssl/ns1/example.db.in
new file mode 100644
index 0000000..7166fa8
--- /dev/null
+++ b/bin/tests/system/pkcs11ssl/ns1/example.db.in
@@ -0,0 +1,29 @@
+; Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+;
+; Permission to use, copy, modify, and/or distribute this software for any
+; purpose with or without fee is hereby granted, provided that the above
+; copyright notice and this permission notice appear in all copies.
+;
+; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+; AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+; PERFORMANCE OF THIS SOFTWARE.
+
+; $Id: example.db.in,v 1.3 2010/06/08 23:50:24 tbox Exp $
+
+$TTL 300	; 5 minutes
+@			IN SOA	ns root (
+				2000082401 ; serial
+				1800       ; refresh (30 minutes)
+				1800       ; retry (30 minutes)
+				1814400    ; expire (3 weeks)
+				3600       ; minimum (1 hour)
+				)
+			NS	ns
+ns			A	10.53.0.1
+
+txt			TXT	"recursed"
+
diff --git a/bin/tests/system/pkcs11ssl/ns1/named.conf b/bin/tests/system/pkcs11ssl/ns1/named.conf
new file mode 100644
index 0000000..90b8117
--- /dev/null
+++ b/bin/tests/system/pkcs11ssl/ns1/named.conf
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: named.conf,v 1.3 2010/06/08 23:50:24 tbox Exp $ */
+
+controls { /* empty */ };
+
+options {
+	query-source address 10.53.0.1;
+	notify-source 10.53.0.1;
+	transfer-source 10.53.0.1;
+	port 5300;
+	pid-file "named.pid";
+	listen-on { 10.53.0.1; };
+	listen-on-v6 { none; };
+	recursion no;
+	notify no;
+};
+
+key rndc_key {
+	secret "1234abcd8765";
+	algorithm hmac-md5;
+};
+
+controls {
+	inet 10.53.0.1 port 9953 allow { any; } keys { rndc_key; };
+};
+
+zone "rsa.example." {
+	type master;
+	file "rsa.example.db.signed";
+	allow-update { any; };
+};
+
+zone "ecc.example." {
+	type master;
+	file "ecc.example.db.signed";
+	allow-update { any; };
+};
diff --git a/bin/tests/system/pkcs11ssl/prereq.sh b/bin/tests/system/pkcs11ssl/prereq.sh
new file mode 100644
index 0000000..b5133f4
--- /dev/null
+++ b/bin/tests/system/pkcs11ssl/prereq.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+#
+# Copyright (C) 2010, 2012  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# $Id: prereq.sh,v 1.3 2010/06/08 23:50:24 tbox Exp $
+
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
+../../../tools/genrandom 400 random.data
+
+echo rsaonly > supported
+exit 0
+
+rsafail=0 eccfail=0
+
+$KEYGEN -q -r random.data foo > /dev/null 2>&1 || rsafail=1
+rm -f Kfoo*
+
+if [ $rsafail = 1 ]; then
+	echo "I:This test requires OpenSSL built with PKCS#11 support." >&2
+	exit 255
+fi
diff --git a/bin/tests/system/pkcs11ssl/setup.sh b/bin/tests/system/pkcs11ssl/setup.sh
new file mode 100644
index 0000000..c13b275
--- /dev/null
+++ b/bin/tests/system/pkcs11ssl/setup.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+#
+# Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
+
+RANDFILE=random.data
+infile=ns1/example.db.in
+
+/bin/echo -n ${HSMPIN:-1234}> pin
+PWD=`pwd`
+
+zone=rsa.example
+zonefile=ns1/rsa.example.db
+
+$PK11GEN -a RSA -b 1024 -l robie-rsa-zsk1 -i 01
+$PK11GEN -a RSA -b 1024 -l robie-rsa-zsk2 -i 02
+$PK11GEN -a RSA -b 2048 -l robie-rsa-ksk
+
+rsazsk1=`$KEYFRLAB -a RSASHA1 \
+        -l "robie-rsa-zsk1" rsa.example`
+rsazsk2=`$KEYFRLAB -a RSASHA1 \
+        -l "robie-rsa-zsk2" rsa.example`
+rsaksk=`$KEYFRLAB -a RSASHA1 -f ksk \
+        -l "robie-rsa-ksk" rsa.example`
+
+cat $infile $rsazsk1.key $rsaksk.key > $zonefile
+$SIGNER -a -P -g -r $RANDFILE -o $zone $zonefile \
+        > /dev/null 2> signer.err || cat signer.err
+cp $rsazsk2.key ns1/rsa.key
+mv Krsa* ns1
+
+rm -f signer.err
diff --git a/bin/tests/system/pkcs11ssl/tests.sh b/bin/tests/system/pkcs11ssl/tests.sh
new file mode 100644
index 0000000..7785d5a
--- /dev/null
+++ b/bin/tests/system/pkcs11ssl/tests.sh
@@ -0,0 +1,71 @@
+#!/bin/sh
+#
+# Copyright (C) 2010, 2012  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# $Id: tests.sh,v 1.3 2010/06/08 23:50:24 tbox Exp $
+
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
+
+RANDFILE=random.data
+
+DIGOPTS="+tcp +noadd +nosea +nostat +nocmd +dnssec -p 5300"
+
+status=0
+ret=0
+
+alg=rsa
+zonefile=ns1/rsa.example.db 
+echo "I:testing PKCS#11 key generation (rsa)"
+count=`$PK11LIST | grep robie-rsa-ksk | wc -l`
+if [ $count != 2 ]; then echo "I:failed"; status=1; fi
+
+echo "I:testing offline signing with PKCS#11 keys (rsa)"
+
+count=`grep RRSIG $zonefile.signed | wc -l`
+if [ $count != 12 ]; then echo "I:failed"; status=1; fi
+
+echo "I:testing inline signing with PKCS#11 keys (rsa)"
+
+$NSUPDATE > /dev/null <<END || status=1
+server 10.53.0.1 5300
+ttl 300
+zone rsa.example.
+update add `grep -v ';' ns1/${alg}.key`
+send
+END
+
+echo "I:waiting 20 seconds for key changes to take effect"
+sleep 20
+
+$DIG $DIGOPTS ns.rsa.example. @10.53.0.1 a > dig.out || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+count=`grep RRSIG dig.out | wc -l`
+if [ $count != 4 ]; then echo "I:failed"; status=1; fi
+
+echo "I:testing PKCS#11 key destroy (rsa)"
+ret=0
+$PK11DEL -l robie-rsa-ksk -w0 > /dev/null 2>&1 || ret=1
+$PK11DEL -l robie-rsa-zsk1 -w0 > /dev/null 2>&1 || ret=1
+$PK11DEL -i $id -w0 > /dev/null 2>&1 || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+count=`$PK11LIST | grep robie-rsa | wc -l`
+if [ $count != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $count`
+
+echo "I:exit status: $status"
+exit $status
diff --git a/bin/tests/system/pkcs11ssl/usepkcs11 b/bin/tests/system/pkcs11ssl/usepkcs11
new file mode 100644
index 0000000..ef46412
--- /dev/null
+++ b/bin/tests/system/pkcs11ssl/usepkcs11
@@ -0,0 +1 @@
+This test relies on PKCS#11!
diff --git a/bin/tests/system/rsabigexponent/Makefile.in b/bin/tests/system/rsabigexponent/Makefile.in
index d32eb15..ce8958b 100644
--- a/bin/tests/system/rsabigexponent/Makefile.in
+++ b/bin/tests/system/rsabigexponent/Makefile.in
@@ -24,7 +24,7 @@ top_srcdir =	@top_srcdir@
 
 CINCLUDES =	${DNS_INCLUDES} ${ISC_INCLUDES} @DST_OPENSSL_INC@
 
-CDEFINES =	@USE_OPENSSL@
+CDEFINES =	@CRYPTO@
 CWARNINGS =
 
 DNSLIBS =	../../../../lib/dns/libdns.@A@ @DNS_CRYPTO_LIBS@
diff --git a/bin/tests/system/rsabigexponent/bigkey.c b/bin/tests/system/rsabigexponent/bigkey.c
index aa2e8ec..436254c 100644
--- a/bin/tests/system/rsabigexponent/bigkey.c
+++ b/bin/tests/system/rsabigexponent/bigkey.c
@@ -16,7 +16,7 @@
 
 /* $Id$ */
 
-#ifdef OPENSSL
+#if defined(OPENSSL) || defined(PKCS11CRYPTO)
 #include <config.h>
 
 #include <stdio.h>
@@ -44,8 +44,16 @@
 #include <dst/dst.h>
 #include <dst/result.h>
 
+#ifdef OPENSSL
 #include <openssl/opensslv.h>
 #if OPENSSL_VERSION_NUMBER <= 0x00908000L
+#define USE_FIX_KEY_FILES
+#endif
+#else
+#define USE_FIX_KEY_FILES
+#endif
+
+#ifdef USE_FIX_KEY_FILES
 
 /*
  * Use a fixed key file pair if OpenSSL doesn't support > 32 bit exponents.
@@ -235,16 +243,16 @@ main(int argc, char **argv) {
 }
 #endif
 
-#else /* OPENSSL */
+#else /* OPENSSL || PKCS11CRYPTO */
 
 #include <stdio.h>
 #include <stdlib.h>
 
 int
-main(int argc, char **argv) {
-	fprintf(stderr, "Compiled without OpenSSL\n");
+main() {
+	fprintf(stderr, "Compiled without Crypto\n");
 	exit(1);
 }
 
-#endif /* OPENSSL */
+#endif /* OPENSSL || PKCS11CRYPTO */
 /*! \file */
diff --git a/bin/tests/system/rsabigexponent/prereq.sh b/bin/tests/system/rsabigexponent/prereq.sh
index 8edbf1d..6259fb6 100644
--- a/bin/tests/system/rsabigexponent/prereq.sh
+++ b/bin/tests/system/rsabigexponent/prereq.sh
@@ -22,6 +22,7 @@ if ./bigkey > /dev/null 2>&1
 then
     rm -f Kexample.*
 else
-    echo "I:This test requires that --with-openssl was used." >&2
+    echo "I:This test requires cryptography" >&2
+    echo "I:--with-openssl, or --with-pkcs11 and --enable-native-pkcs11" >&2
     exit 1
 fi
diff --git a/bin/tests/system/smartsign/prereq.sh b/bin/tests/system/smartsign/prereq.sh
index e47b769..9ed2fa8 100644
--- a/bin/tests/system/smartsign/prereq.sh
+++ b/bin/tests/system/smartsign/prereq.sh
@@ -22,6 +22,7 @@ if $KEYGEN -q -r random.data foo > /dev/null 2>&1
 then
     rm -f Kfoo*
 else
-    echo "I:This test requires that --with-openssl was used." >&2
+    echo "I:This test requires cryptography" >&2
+    echo "I:--with-openssl, or --with-pkcs11 and --enable-native-pkcs11" >&2
     exit 1
 fi
diff --git a/bin/tests/system/tkey/keycreate.c b/bin/tests/system/tkey/keycreate.c
index ff2c2ee..af17582 100644
--- a/bin/tests/system/tkey/keycreate.c
+++ b/bin/tests/system/tkey/keycreate.c
@@ -228,6 +228,7 @@ main(int argc, char *argv[]) {
 	dns_result_register();
 
 	mctx = NULL;
+	isc_mem_debugging = ISC_MEM_DEBUGRECORD;
 	RUNCHECK(isc_mem_create(0, 0, &mctx));
 
 	ectx = NULL;
diff --git a/bin/tests/system/tkey/prereq.sh b/bin/tests/system/tkey/prereq.sh
index fca4a27..66295fe 100644
--- a/bin/tests/system/tkey/prereq.sh
+++ b/bin/tests/system/tkey/prereq.sh
@@ -23,6 +23,7 @@ if $KEYGEN -a RSAMD5 -b 512 -n zone -r random.data foo > /dev/null 2>&1
 then
     rm -f foo*
 else
-    echo "I:This test requires that --with-openssl was used." >&2
+    echo "I:This test requires cryptography" >&2
+    echo "I:--with-openssl, or --with-pkcs11 and --enable-native-pkcs11" >&2
     exit 1
 fi
diff --git a/config.h.in b/config.h.in
index 4139e1d..f2eb59a 100644
--- a/config.h.in
+++ b/config.h.in
@@ -132,14 +132,11 @@ int sigwait(const unsigned int *set, int *sig);
 /** define if you have strerror in the C library. */
 #undef HAVE_STRERROR
 
-/** Define if you are running under Compaq TruCluster. */
-#undef HAVE_TRUCLUSTER
-
 /* Define if OpenSSL includes DSA support */
 #undef HAVE_OPENSSL_DSA
 
-/* Define if OpenSSL includes ECDSA support */
-#undef HAVE_OPENSSL_ECDSA
+/* Define if you have getpassphrase in the C library. */
+#undef HAVE_GETPASSPHRASE
 
 /* Define to the length type used by the socket API (socklen_t, size_t, int). */
 #undef ISC_SOCKADDR_LEN_T
@@ -187,6 +184,9 @@ int sigwait(const unsigned int *set, int *sig);
 /* Define to 1 if you have the `chroot' function. */
 #undef HAVE_CHROOT
 
+/* Define if clock_gettime is available. */
+#undef HAVE_CLOCK_GETTIME
+
 /* Define to 1 if you have the <devpoll.h> header file. */
 #undef HAVE_DEVPOLL_H
 
@@ -292,6 +292,12 @@ int sigwait(const unsigned int *set, int *sig);
 /* Define if your OpenSSL version supports GOST. */
 #undef HAVE_OPENSSL_GOST
 
+/* Define if your PKCS11 provider supports ECDSA. */
+#undef HAVE_PKCS11_ECDSA
+
+/* Define if your PKCS11 provider supports GOST. */
+#undef HAVE_PKCS11_GOST
+
 /* Define to 1 if you have the `readline' function. */
 #undef HAVE_READLINE
 
@@ -418,6 +424,9 @@ int sigwait(const unsigned int *set, int *sig);
    (O_NDELAY/O_NONBLOCK). */
 #undef PORT_NONBLOCK
 
+/* Define if GOST private keys are encoded in ASN.1. */
+#undef PREFER_GOSTASN1
+
 /* The size of `void *', as computed by sizeof. */
 /* #undef SIZEOF_VOID_P */
 
diff --git a/configure.in b/configure.in
index 8a06905..24eafb7 100644
--- a/configure.in
+++ b/configure.in
@@ -640,25 +640,76 @@ AC_ARG_WITH(openssl,
 			  (Required for DNSSEC)],
     use_openssl="$withval", use_openssl="auto")
 
+#
+# was --enable-native-pkcs11 specified?
+#  (note it implies both --without-openssl and --with-pkcs11)
+#
+AC_ARG_ENABLE(native-pkcs11,
+	[  --enable-native-pkcs11  use native PKCS11 for all crypto [[default=no]]],
+	want_native_pkcs11="$enableval", want_native_pkcs11="no")
+
+AC_ARG_WITH(pkcs11,
+[  --with-pkcs11[=PATH]      Build with PKCS11 support [yes|no|path]
+                          (PATH is for the PKCS11 provider)],
+   use_pkcs11="$withval", use_pkcs11="auto")
+
 openssldirs="/usr /usr/local /usr/local/ssl /usr/pkg /usr/sfw"
 if test "$use_openssl" = "auto"
 then
-	for d in $openssldirs
-	do
-		if test -f $d/include/openssl/opensslv.h
-		then
-			use_openssl=$d
-			break
-		fi
-	done
+    if test "$want_native_pkcs11" = "yes"
+    then
+        use_openssl="native_pkcs11"
+    else
+	    for d in $openssldirs
+    	do
+	    	if test -f $d/include/openssl/opensslv.h
+		    then
+			    use_openssl=$d
+    			break
+		    fi
+    	done
+    fi
 fi
 OPENSSL_ECDSA=""
 OPENSSL_GOST=""
+AC_ARG_WITH(gost,
+[  --with-gost             Crypto GOST [yes|no|raw|asn1].],
+                    with_gost="$withval", with_gost="auto")
+AC_ARG_WITH(ecdsa, [  --with-ecdsa            OpenSSL ECDSA],
+	            with_ecdsa="$withval", with_ecdsa="auto")
+
+gosttype="raw"
+case "$with_gost" in
+	raw)
+		with_gost="yes"
+		;;
+	asn1)
+		AC_DEFINE(PREFER_GOSTASN1, 1,
+			  [Define if GOST private keys are encoded in ASN.1.])
+                gosttype="asn1"
+		with_gost="yes"
+		;;
+	auto|yes|no)
+		;;
+	*)
+		AC_MSG_ERROR(unknown GOST private key encoding)
+		;;
+esac
+
 case "$use_openssl" in
+    native_pkcs11)
+		AC_MSG_RESULT(disabled because of native PKCS11)
+		DST_OPENSSL_INC=""
+		CRYPTO=""
+		OPENSSLGOSTLINKOBJS=""
+		OPENSSLGOSTLINKSRS=""
+		OPENSSLLINKOBJS=""
+		OPENSSLLINKSRCS=""
+		;;
 	no)
 		AC_MSG_RESULT(no)
 		DST_OPENSSL_INC=""
-		USE_OPENSSL=""
+		CRYPTO=""
 		OPENSSLGOSTLINKOBJS=""
 		OPENSSLGOSTLINKSRS=""
 		OPENSSLLINKOBJS=""
@@ -666,7 +717,7 @@ case "$use_openssl" in
 		;;
 	auto)
 		DST_OPENSSL_INC=""
-		USE_OPENSSL=""
+		CRYPTO=""
 		OPENSSLGOSTLINKOBJS=""
 		OPENSSLGOSTLINKSRS=""
 		OPENSSLLINKOBJS=""
@@ -676,6 +727,11 @@ case "$use_openssl" in
 If you don't want OpenSSL, use --without-openssl])
 		;;
 	*)
+		if test "$want_native_pkcs11" = "yes"
+		then
+                        AC_MSG_RESULT()
+			AC_MSG_ERROR([OpenSSL and native PKCS11 cannot be used together.])
+		fi
 		if test "$use_openssl" = "yes"
 		then
 			# User did not specify a path - guess it
@@ -697,7 +753,7 @@ If you don't want OpenSSL, use --without-openssl])
 		then
 			AC_MSG_ERROR(["$use_openssl/include/openssl/opensslv.h" not found])
 		fi
-		USE_OPENSSL='-DOPENSSL'
+		CRYPTO='-DOPENSSL'
 		if test "$use_openssl" = "/usr"
 		then
 			DST_OPENSSL_INC=""
@@ -733,6 +789,7 @@ If you don't want OpenSSL, use --without-openssl])
 		fi
 		AC_MSG_RESULT(using OpenSSL from $use_openssl/lib and $use_openssl/include)
 
+		saved_cc="$CC"
 		saved_cflags="$CFLAGS"
 		saved_libs="$LIBS"
 		CFLAGS="$CFLAGS $DST_OPENSSL_INC"
@@ -839,8 +896,7 @@ int main() {
         [AC_MSG_RESULT(no)
         have_ecdsa="no"],
         [AC_MSG_RESULT(using --with-ecdsa)])
-        AC_ARG_WITH(ecdsa, [  --with-ecdsa            OpenSSL ECDSA],
-	            with_ecdsa="$withval", with_ecdsa="auto")
+        
         case "$with_ecdsa" in
         yes)
             case "$have_ecdsa" in
@@ -869,6 +925,15 @@ int main() {
 
         AC_MSG_CHECKING(for OpenSSL GOST support)
         have_gost=""
+		case "$use_pkcs11" in
+                auto|no)
+                        ;;
+                *)
+                        if $use_threads; then
+                                CC="$CC -pthread"
+                        fi
+                        ;;
+        esac
         AC_TRY_RUN([
 #include <openssl/conf.h>
 #include <openssl/engine.h>
@@ -896,8 +961,7 @@ int main() {
         [AC_MSG_RESULT(no)
         have_gost="no"],
         [AC_MSG_RESULT(using --with-gost)])
-        AC_ARG_WITH(gost, [  --with-gost             OpenSSL GOST],
-                    with_gost="$withval", with_gost="auto")
+        
         case "$with_gost" in
         yes)
             case "$have_gost" in
@@ -910,7 +974,7 @@ int main() {
         *)
             case "$have_gost" in
             yes|no) ;;
-            *) AC_MSG_ERROR([need --with-gost=[[yes or no]]]) ;;
+            *) AC_MSG_ERROR([need --with-gost=[[yes, no, raw or asn1]]]) ;;
             esac
             ;;
         esac
@@ -938,7 +1002,6 @@ esac
 # it as needed) if it is found.
 #
 
-AC_SUBST(USE_OPENSSL)
 AC_SUBST(DST_OPENSSL_INC)
 AC_SUBST(OPENSSLGOSTLINKOBJS)
 AC_SUBST(OPENSSLGOSTLINKSRCS)
@@ -958,7 +1021,7 @@ AC_ARG_ENABLE(openssl-hash,
 	want_openssl_hash="$enableval", want_openssl_hash="no")
 case $want_openssl_hash in
 	yes)
-		if test "$USE_OPENSSL" = ""
+		if test "$CRYPTO" = ""
 		then
 			AC_MSG_ERROR([No OpenSSL for hash functions])
 		fi
@@ -973,6 +1036,41 @@ esac
 AC_SUBST(ISC_PLATFORM_OPENSSLHASH)
 AC_SUBST(ISC_OPENSSL_INC)
 
+AC_ARG_WITH(libtool,
+	    [  --with-libtool          use GNU libtool],
+	    use_libtool="$withval", use_libtool="no")
+
+case $use_libtool in
+	yes)
+		AM_PROG_LIBTOOL
+		O=lo
+		A=la
+		LIBTOOL_MKDEP_SED='s;\.o;\.lo;'
+		LIBTOOL_MODE_COMPILE='--mode=compile --tag=CC'
+		LIBTOOL_MODE_INSTALL='--mode=install --tag=CC'
+		LIBTOOL_MODE_LINK='--mode=link --tag=CC'
+		case "$host" in
+		*) LIBTOOL_ALLOW_UNDEFINED= ;;
+		esac
+		case "$host" in
+		*-ibm-aix*) LIBTOOL_IN_MAIN="-Wl,-bI:T_testlist.imp" ;;
+		*) LIBTOOL_IN_MAIN= ;;
+		esac;
+		;;
+	*)
+		O=o
+		A=a
+		LIBTOOL=
+		AC_SUBST(LIBTOOL)
+		LIBTOOL_MKDEP_SED=
+		LIBTOOL_MODE_COMPILE=
+		LIBTOOL_MODE_INSTALL=
+		LIBTOOL_MODE_LINK=
+		LIBTOOL_ALLOW_UNDEFINED=
+		LIBTOOL_IN_MAIN=
+		;;
+esac
+
 #
 # PKCS11 (aka crypto hardware) support
 #
@@ -980,25 +1078,102 @@ AC_SUBST(ISC_OPENSSL_INC)
 #
 
 AC_MSG_CHECKING(for PKCS11 support)
-AC_ARG_WITH(pkcs11,
-[  --with-pkcs11[=PATH]      Build with PKCS11 support [yes|no|path]
-                          (PATH is for the PKCS11 provider)],
-   use_pkcs11="$withval", use_pkcs11="no")
+
+if test "$use_pkcs11" = "auto"
+then
+	if test "$want_native_pkcs11" = "yes"
+	then
+		use_pkcs11="yes"
+	else
+		use_pkcs11="no"
+	fi
+fi
 
 case "$use_pkcs11" in
 	no|'')
-		AC_MSG_RESULT(disabled)
-		USE_PKCS11=''
-		PKCS11_TOOLS=''
+		AC_MSG_RESULT(no)
+		USE_PKCS11=""
+		PKCS11_TEST=""
+		PKCS11_TOOLS=""
+		ISC_PK11_C=""
+		ISC_PK11_O=""
+		ISC_PK11_API_C=""
+		ISC_PK11_API_O=""
+		ISC_PK11_RESULT_C=""
+		ISC_PK11_RESULT_O=""
+		ISC_ISCPK11_API_C=""
+		ISC_ISCPK11_API_O=""
 		;;
 	yes|*)
-		AC_MSG_RESULT(using OpenSSL with PKCS11 support)
+        AC_MSG_RESULT(yes)
+                if ! $use_threads; then
+			AC_MSG_ERROR([PKCS11 requires thread support])
+                fi
+		if test "$CRYPTO" != ""
+		then
+			AC_MSG_CHECKING(for OpenSSL with PKCS11 support)
+                        saved_cc="$CC"
+			saved_cflags="$CFLAGS"
+			saved_libs="$LIBS"
+                        CC="$CC -pthread"
+			CFLAGS="$CFLAGS $DST_OPENSSL_INC"
+			LIBS="$LIBS $DNS_OPENSSL_LIBS"
+			AC_TRY_RUN([
+#include <openssl/conf.h>
+#include <openssl/engine.h>
+int main() {
+	ENGINE *e;
+
+	OPENSSL_config(NULL);
+	e = ENGINE_by_id("pkcs11");
+	if (e == NULL)
+		return (1);
+	if (ENGINE_init(e) <= 0)
+		return (1);
+	return (0);
+}
+],
+			[AC_MSG_RESULT(yes)
+			PKCS11_TEST=pkcs11ssl
+			PKCS11_ENGINE='-DPKCS11_ENGINE="\"pkcs11\""'],
+			[AC_MSG_RESULT(no)
+			PKCS11_TEST=''
+			PKCS11_ENGINE='-DPKCS11_ENGINE=NULL'],
+			[AC_MSG_RESULT(cross compile, defaulting to no)
+			PKCS11_TEST=''
+			PKCS11_ENGINE='-DPKCS11_ENGINE=NULL'])
+                        CC="$saved_cc"
+			CFLAGS="$saved_cflags"
+			LIBS="$saved_libs"
+		else
+			PKCS11_TEST=''
+			PKCS11_ENGINE='-DPKCS11_ENGINE=NULL'
+
+		fi
 		USE_PKCS11='-DUSE_PKCS11'
 		PKCS11_TOOLS=pkcs11
-		;;
+		AC_CHECK_FUNC(getpassphrase, AC_DEFINE(HAVE_GETPASSPHRASE),)
+		ISC_PK11_C="pk11.c"
+		ISC_PK11_O="pk11.$O"
+		ISC_PK11_API_C="pk11_api.c"
+		ISC_PK11_API_O="pk11_api.$O"
+		ISC_PK11_RESULT_C="pk11_result.c"
+		ISC_PK11_RESULT_O="pk11_result.$O"
+		ISC_ISCPK11_API_C="unix/pk11_api.c"
+		ISC_ISCPK11_API_O="unix/pk11_api.$O"
+ 		;;
 esac
 AC_SUBST(USE_PKCS11)
 AC_SUBST(PKCS11_TOOLS)
+AC_SUBST(PKCS11_ENGINE)
+AC_SUBST(ISC_PK11_C)
+AC_SUBST(ISC_PK11_O)
+AC_SUBST(ISC_PK11_API_C)
+AC_SUBST(ISC_PK11_API_O)
+AC_SUBST(ISC_PK11_RESULT_C)
+AC_SUBST(ISC_PK11_RESULT_O)
+AC_SUBST(ISC_ISCPK11_API_C)
+AC_SUBST(ISC_ISCPK11_API_O)
 
 AC_MSG_CHECKING(for PKCS11 tools)
 case "$use_pkcs11" in
@@ -1006,13 +1181,77 @@ case "$use_pkcs11" in
 		AC_MSG_RESULT(disabled)
 		PKCS11_PROVIDER="undefined"
 		;;
-       *)
-		AC_MSG_RESULT(PKCS11 provider is "$use_pkcs11")
+    yes|'')
+		PKCS11_PROVIDER="undefined"
+		AC_MSG_RESULT(enabled)
+		;;
+ 	*)
 		PKCS11_PROVIDER="$use_pkcs11"
+		AC_MSG_RESULT([enabled, PKCS11 provider is $PKCS11_PROVIDER])
 		;;
 esac
 AC_SUBST(PKCS11_PROVIDER)
 
+
+PKCS11_ECDSA=""
+PKCS11_GOST=""
+AC_MSG_CHECKING(for native PKCS11)
+
+case "$want_native_pkcs11" in
+	yes)
+		AC_MSG_RESULT(using native PKCS11 crypto)
+		CRYPTO="-DPKCS11CRYPTO"
+		PKCS11LINKOBJS='${PKCS11LINKOBJS}'
+		PKCS11LINKSRCS='${PKCS11LINKSRCS}'
+                PKCS11_TEST=pkcs11
+		AC_MSG_CHECKING(for PKCS11 ECDSA)
+		case "$with_ecdsa" in
+		no)
+			AC_MSG_RESULT([disabled])
+ 			;;
+		*)
+			AC_MSG_RESULT(enabled)
+			PKCS11_ECDSA="yes"
+			AC_DEFINE(HAVE_PKCS11_ECDSA, 1,
+			          [Define if your PKCS11 provider supports ECDSA.])
+ 			;;
+ 		esac
+		AC_MSG_CHECKING(for PKCS11 GOST)
+		case "$with_gost" in
+		yes)
+			AC_MSG_RESULT(enabled)
+			PKCS11_GOST="yes"
+			AC_DEFINE(HAVE_PKCS11_GOST, 1,
+			          [Define if your PKCS11 provider supports GOST.])
+ 			;;
+		*)
+			AC_MSG_RESULT([disabled])
+ 			;;
+ 		esac
+ 		;;
+	no|'')
+		AC_MSG_RESULT(disabled)
+		;;
+esac
+
+AC_SUBST(PKCS11LINKOBJS)
+AC_SUBST(PKCS11LINKSRCS)
+AC_SUBST(CRYPTO)
+AC_SUBST(PKCS11_ECDSA)
+AC_SUBST(PKCS11_GOST)
+AC_SUBST(PKCS11_TEST)
+
+# for PKCS11 benchmarks
+have_clock_gt=no
+AC_CHECK_FUNC(clock_gettime,have_clock_gt=yes,)
+if test "$have_clock_gt" = "no"; then
+	AC_CHECK_LIB(rt,clock_gettime,have_clock_gt=ye,,)
+ fi
+if test "$have_clock_gt" = "yes"; then
+	AC_DEFINE(HAVE_CLOCK_GETTIME, 1, [Define if clock_gettime is available.])
+fi
+
+
 AC_MSG_CHECKING(for GSSAPI library)
 AC_ARG_WITH(gssapi,
 [  --with-gssapi=PATH      Specify path for system-supplied GSSAPI [[default=yes]]],
@@ -1245,6 +1484,21 @@ case "$use_randomdev" in
 esac
 
 #
+# Only check dsa signature generation on these platforms when performing
+# system tests.
+#
+CHECK_DSA=0
+if grep "#define PATH_RANDOMDEV " confdefs.h > /dev/null
+then
+	case "$host" in
+	*darwin*|*freebsd*)
+		CHECK_DSA=1
+		;;
+	esac
+fi
+AC_SUBST(CHECK_DSA)
+
+#
 # Do we have arc4random() ?
 #
 AC_CHECK_FUNC(arc4random, AC_DEFINE(HAVE_ARC4RANDOM))
@@ -1633,41 +1887,6 @@ esac
 AC_SUBST(PURIFY)
 
 
-AC_ARG_WITH(libtool,
-	    [  --with-libtool          use GNU libtool],
-	    use_libtool="$withval", use_libtool="no")
-
-case $use_libtool in
-	yes)
-		AM_PROG_LIBTOOL
-		O=lo
-		A=la
-		LIBTOOL_MKDEP_SED='s;\.o;\.lo;'
-		LIBTOOL_MODE_COMPILE='--mode=compile --tag=CC'
-		LIBTOOL_MODE_INSTALL='--mode=install --tag=CC'
-		LIBTOOL_MODE_LINK='--mode=link --tag=CC'
-		case "$host" in
-		*) LIBTOOL_ALLOW_UNDEFINED= ;;
-		esac
-		case "$host" in
-		*-ibm-aix*) LIBTOOL_IN_MAIN="-Wl,-bI:T_testlist.imp" ;;
-		*) LIBTOOL_IN_MAIN= ;;
-		esac;
-		;;
-	*)
-		O=o
-		A=a
-		LIBTOOL=
-		AC_SUBST(LIBTOOL)
-		LIBTOOL_MKDEP_SED=
-		LIBTOOL_MODE_COMPILE=
-		LIBTOOL_MODE_INSTALL=
-		LIBTOOL_MODE_LINK=
-		LIBTOOL_ALLOW_UNDEFINED=
-		LIBTOOL_IN_MAIN=
-		;;
-esac
-
 #
 # enable/disable dumping stack backtrace.  Also check if the system supports
 # glibc-compatible backtrace() function.
@@ -3419,6 +3638,9 @@ BIND9_CONFIGARGS="`echo $BIND9_CONFIGARGS | sed 's/^ //'`"
 BIND9_CONFIGARGS="CONFIGARGS=${BIND9_CONFIGARGS}"
 AC_SUBST(BIND9_CONFIGARGS)
 
+AC_SUBST_FILE(LIBISCPK11_API)
+LIBISCPK11_API="$srcdir/lib/iscpk11/api"
+
 AC_SUBST_FILE(LIBISC_API)
 LIBISC_API="$srcdir/lib/isc/api"
 
@@ -3728,6 +3950,8 @@ AC_CONFIG_FILES([
 	bin/tests/mem/Makefile
 	bin/tests/names/Makefile
 	bin/tests/net/Makefile
+    bin/tests/pkcs11/Makefile
+	bin/tests/pkcs11/benchmarks/Makefile
 	bin/tests/rbt/Makefile
 	bin/tests/resolver/Makefile
 	bin/tests/sockaddr/Makefile
@@ -3811,11 +4035,14 @@ AC_CONFIG_FILES([
 	lib/isc/include/Makefile
 	lib/isc/include/isc/Makefile
 	lib/isc/include/isc/platform.h
+	lib/isc/include/pk11/Makefile
+	lib/isc/include/pkcs11/Makefile
 	lib/isc/tests/Makefile
 	lib/isc/nls/Makefile
 	lib/isc/unix/Makefile
 	lib/isc/unix/include/Makefile
 	lib/isc/unix/include/isc/Makefile
+	lib/isc/unix/include/pkcs11/Makefile
 	lib/isccc/Makefile
 	lib/isccc/include/Makefile
 	lib/isccc/include/isccc/Makefile
@@ -3885,12 +4112,8 @@ test "$use_pkcs11" = "no" || echo "    PKCS#11/Cryptoki support (--with-pkcs11)"
 if test "$enable_full_report" = "yes"; then
     test "$enable_ipv6" = "no" -o "$found_ipv6" = "no" || \
         echo "    IPv6 support (--enable-ipv6)"
-    test "X$USE_OPENSSL" = "X" || \
+    test "X$CRYPTO" = "X" -o "$want_native_pkcs11" = "yes" || \
             echo "    OpenSSL cryptography/DNSSEC (--with-openssl)"
-    test "$OPENSSL_GOST" != "yes" || \
-            echo "    GOST algorithm support (--with-gost)"
-    test "$OPENSSL_ECDSA" != "yes" || \
-            echo "    ECDSA algorithm support (--with-ecdsa)"
     test "X$PYTHON" = "X" || echo "    Python tools (--with-python)"
     test "X$libxml2_libs" = "X" || echo "    XML statistics (--with-libxml2)"
 fi
@@ -3923,24 +4146,28 @@ test "$enable_filter" = "yes" || \
 test "$use_gssapi" = "no" && echo "    GSS-API (--with-gssapi)"
 test "$want_backtrace" = "yes" || \
     echo "    Print backtrace on crash (--enable-backtrace)"
-test "$use_pkcs11" = "no" && echo "    PKCS#11/Cryptoki support (--with-pkcs11)"
 
-test "$enable_ipv6" = "no" -o "$found_ipv6" = "no" && \
-        echo "    IPv6 support (--enable-ipv6)"
-test "X$USE_OPENSSL" = "X" && \
-        echo "    OpenSSL cryptography/DNSSEC (--with-openssl)"
-test "X$USE_OPENSSL" != "X" -a "$OPENSSL_GOST" != "yes" && \
+test "X$CRYPTO" = "X" -o "$want_native_pkcs11" = "yes" && \
+    echo "    OpenSSL cryptography/DNSSEC (--with-openssl)"
+test "$want_native_pkcs11" != "yes" && \
+    echo "    Native PKCS#11 cryptography/DNSSEC (--enable-native-pkcs11)"
+test "X$CRYPTO" = "X" -o "$OPENSSL_GOST" = "yes" -o "$PKCS11_GOST" = "yes" || \
     echo "    GOST algorithm support (--with-gost)"
-test "X$USE_OPENSSL" != "X" -a "$OPENSSL_ECDSA" != "yes" && \
+test "X$CRYPTO" = "X" -o "$OPENSSL_ECDSA" = "yes" -o "$PKCS11_ECDSA" = "yes" || \
     echo "    ECDSA algorithm support (--with-ecdsa)"
+test "$use_pkcs11" = "no" && echo "    PKCS#11/Cryptoki support (--with-pkcs11)"
+test "$enable_ipv6" = "no" -o "$found_ipv6" = "no" && \
+        echo "    IPv6 support (--enable-ipv6)"
 test "X$PYTHON" = "X" && echo "    Python tools (--with-python)"
 test "X$libxml2_libs" = "X" && echo "    XML statistics (--with-libxml2)"
 
 echo "========================================================================"
 
-if test "X$USE_OPENSSL" = "X"; then
+if test "X$CRYPTO" = "X"; then
 cat << \EOF
-BIND is being built without OpenSSL. This means it will not have DNSSEC support.
+BIND 9 is being built without cryptography support. This means it will
+not have DNSSEC support. Use --with-openssl, or --with-pkcs11 and
+--enable-native-pkcs11 to enable cryptography.
 EOF
 fi
 
diff --git a/doc/arm/pkcs11.xml b/doc/arm/pkcs11.xml
index b4e22bb..5388a29 100644
--- a/doc/arm/pkcs11.xml
+++ b/doc/arm/pkcs11.xml
@@ -20,162 +20,259 @@
 <!-- $Id: pkcs11.xml,v 1.7 2012/01/16 22:50:12 each Exp $ -->
 
 <sect1 id="pkcs11">
-  <title>PKCS #11 (Cryptoki) support</title>
-  <para>PKCS #11 (Public Key Cryptography Standard #11) defines a
-  platform- independent API for the control of hardware security
-  modules (HSMs) and other cryptographic support devices.</para>
-  <para>BIND 9 is known to work with two HSMs: The Sun SCA 6000
-  cryptographic acceleration board, tested under Solaris x86, and
-  the AEP Keyper network-attached key storage device, tested with
-  Debian Linux, Solaris x86 and Windows Server 2003.</para>
+  <title>PKCS#11 (Cryptoki) support</title>
+  <para>
+    PKCS#11 (Public Key Cryptography Standard #11) defines a
+    platform-independent API for the control of hardware security
+    modules (HSMs) and other cryptographic support devices.
+  </para>
+  <para>
+    BIND 9 is known to work with three HSMs: The AEP Keyper, which has
+    been tested with Debian Linux, Solaris x86 and Windows Server 2003;
+    the Thales nShield, tested with Debian Linux; and the Sun SCA 6000
+    cryptographic acceleration board, tested with Solaris x86.  In
+    addition, BIND can be used with SoftHSM, a software-based HSM
+    simulator produced by the OpenDNSSEC project.
+  </para>
+  <para>
+    PKCS#11 makes use of a "provider library": a dynamically loadable
+    library which provides a low-level PKCS#11 interface to drive the HSM
+    hardware.  The PKCS#11 provider library comes from the HSM vendor, and
+    it is specific to the HSM to be controlled.
+  </para>
+  <para>
+    There are two available mechanisms for PKCS#11 support in BIND 9:
+    OpenSSL-based PKCS#11 and native PKCS#11.  When using the first
+    mechanism, BIND uses a modified version of OpenSSL, which loads
+    the provider library and operates the HSM indirectly; any
+    cryptographic operations not supported by the HSM can be carried
+    out by OpenSSL instead.  The second mechanism enables BIND to bypass
+    OpenSSL completely; BIND loads the provider library itself, and uses
+    the PKCS#11 API to drive the HSM directly.
+  </para>
   <sect2>
     <title>Prerequisites</title>
-    <para>See the HSM vendor documentation for information about
-    installing, initializing, testing and troubleshooting the
-    HSM.</para>
-    <para>BIND 9 uses OpenSSL for cryptography, but stock OpenSSL
-    does not yet fully support PKCS #11. However, a PKCS #11 engine
-    for OpenSSL is available from the OpenSolaris project. It has
-    been modified by ISC to work with with BIND 9, and to provide
-    new features such as PIN management and key by
-    reference.</para>
-    <para>The patched OpenSSL depends on a "PKCS #11 provider".
-    This is a shared library object, providing a low-level PKCS #11
-    interface to the HSM hardware. It is dynamically loaded by
-    OpenSSL at runtime. The PKCS #11 provider comes from the HSM
-    vendor, and is specific to the HSM to be controlled.</para>
-    <para>There are two "flavors" of PKCS #11 support provided by
-    the patched OpenSSL, one of which must be chosen at
-    configuration time. The correct choice depends on the HSM
-    hardware:</para>
+    <para>
+      See the documentation provided by your HSM vendor for
+      information about installing, initializing, testing and
+      troubleshooting the HSM.
+    </para>
+  </sect2>
+  <sect2>
+    <title>Native PKCS#11</title>
+    <para>
+      Native PKCS#11 mode will only work with an HSM capable of carrying
+      out <emphasis>every</emphasis> cryptographic operation BIND 9 may
+      need. The HSM's provider library must have a complete implementation
+      of the PKCS#11 API, so that all these functions are accessible. As of
+      this writing, only the Thales nShield HSM and the latest development
+      version of SoftHSM can be used in this fashion.  For other HSM's,
+      including the AEP Keyper, Sun SCA 6000 and older versions of SoftHSM,
+      use OpenSSL-based PKCS#11. (Note: As more HSMs become capable of
+      supporting native PKCS#11, it is expected that OpenSSL-based
+      PKCS#11 will eventually be deprecated.)
+    </para>
+    <para>
+      To build BIND with native PKCS#11, configure as follows:
+    </para>
+    <screen>
+$ <userinput>cd bind9</userinput>
+$ <userinput>./configure --enable-native-pkcs11 \
+    --with-pkcs11=<replaceable>provider-library-path</replaceable></userinput>
+    </screen>
+    <para>
+      This will cause all BIND tools, including <command>named</command>
+      and the <command>dnssec-*</command> and <command>pkcs11-*</command>
+      tools, to use the PKCS#11 provider library specified in
+      <replaceable>provider-library-path</replaceable> for cryptography.
+      (The provider library path can be overridden using the
+      <option>-E</option> in <command>named</command> and the
+      <command>dnssec-*</command> tools, or the <option>-m</option> in
+      the <command>pkcs11-*</command> tools.)
+    </para>
+  </sect2>
+  <sect2>
+    <title>OpenSSL-based PKCS#11</title>
+    <para>
+      OpenSSL-based PKCS#11 mode uses a modified version of the
+      OpenSSL library; stock OpenSSL does not fully support PKCS#11.
+      ISC provides a patch to OpenSSL to correct this.  This patch is
+      based on work originally done by the OpenSolaris project; it has been
+      modified by ISC to provide new features such as PIN management and
+      key-by-reference.
+    </para>
+    <para>
+      There are two "flavors" of PKCS#11 support provided by
+      the patched OpenSSL, one of which must be chosen at
+      configuration time. The correct choice depends on the HSM
+      hardware:
+    </para>
     <itemizedlist>
       <listitem>
-        <para>Use 'crypto-accelerator' with HSMs that have hardware
-        cryptographic acceleration features, such as the SCA 6000
-        board. This causes OpenSSL to run all supported
-        cryptographic operations in the HSM.</para>
+        <para>
+          Use 'crypto-accelerator' with HSMs that have hardware
+          cryptographic acceleration features, such as the SCA 6000
+          board. This causes OpenSSL to run all supported
+          cryptographic operations in the HSM.
+        </para>
       </listitem>
       <listitem>
-        <para>Use 'sign-only' with HSMs that are designed to
-        function primarily as secure key storage devices, but lack
-        hardware acceleration. These devices are highly secure, but
-        are not necessarily any faster at cryptography than the
-        system CPU &mdash; often, they are slower. It is therefore
-        most efficient to use them only for those cryptographic
-        functions that require access to the secured private key,
-        such as zone signing, and to use the system CPU for all
-        other computationally-intensive operations. The AEP Keyper
-        is an example of such a device.</para>
+        <para>
+          Use 'sign-only' with HSMs that are designed to
+          function primarily as secure key storage devices, but lack
+          hardware acceleration. These devices are highly secure, but
+          are not necessarily any faster at cryptography than the
+          system CPU &mdash; often, they are slower. It is therefore
+          most efficient to use them only for those cryptographic
+          functions that require access to the secured private key,
+          such as zone signing, and to use the system CPU for all
+          other computationally-intensive operations. The AEP Keyper
+          is an example of such a device.
+        </para>
       </listitem>
     </itemizedlist>
-    <para>The modified OpenSSL code is included in the BIND 9 release,
-        in the form of a context diff against the latest verions of
-        OpenSSL.  OpenSSL 0.9.8, 1.0.0 and 1.0.1 are supported; there are
-        separate diffs for each version.  In the examples to follow,
-        we use OpenSSL 0.9.8, but the same methods work with OpenSSL 1.0.0
-	and 1.0.1.
+    <para>
+      The modified OpenSSL code is included in the BIND 9 release,
+      in the form of a context diff against the latest verions of
+      OpenSSL.  OpenSSL 0.9.8, 1.0.0, and 1.0.1 are supported; there are
+      separate diffs for each version.  In the examples to follow,
+      we use OpenSSL 0.9.8, but the same methods work with OpenSSL
+      1.0.0 and 1.0.1.
     </para>
     <note>
-      The latest OpenSSL versions at the time of the BIND release
-      are 0.9.8y, 1.0.0k and 1.0.1e.
-      ISC will provide an updated patch as new versions of OpenSSL
+      The latest OpenSSL versions as of this writing (January 2014)
+      are 0.9.8y, 1.0.0l, and 1.0.1f.
+      ISC will provide updated patches as new versions of OpenSSL
       are released. The version number in the following examples
-      is expected to change.</note>
+      is expected to change.
+    </note>
     <para>
-    Before building BIND 9 with PKCS #11 support, it will be
-    necessary to build OpenSSL with this patch in place and inform
-    it of the path to the HSM-specific PKCS #11 provider
-    library.</para>
-    <para>Obtain OpenSSL 0.9.8s:</para>
-    <screen>
-$ <userinput>wget <ulink>http://www.openssl.org/source/openssl-0.9.8s.tar.gz</ulink></userinput>
-</screen>
-    <para>Extract the tarball:</para>
-    <screen>
-$ <userinput>tar zxf openssl-0.9.8s.tar.gz</userinput>
+      Before building BIND 9 with PKCS#11 support, it will be
+      necessary to build OpenSSL with the patch in place, and configure
+      it with the path to your HSM's PKCS#11 provider library.
+    </para>
+    </sect3>
+      <title>Patching OpenSSL</title>
+      <screen>
+$ <userinput>wget <ulink>http://www.openssl.org/source/openssl-0.9.8y.tar.gz</ulink></userinput>
+  </screen>
+      <para>Extract the tarball:</para>
+      <screen>
+$ <userinput>tar zxf openssl-0.9.8y.tar.gz</userinput>
 </screen>
-    <para>Apply the patch from the BIND 9 release:</para>
-    <screen>
-$ <userinput>patch -p1 -d openssl-0.9.8s \
-            &lt; bind9/bin/pkcs11/openssl-0.9.8s-patch</userinput>
+      <para>Apply the patch from the BIND 9 release:</para>
+      <screen>
+$ <userinput>patch -p1 -d openssl-0.9.8y \
+              &lt; bind9/bin/pkcs11/openssl-0.9.8y-patch</userinput>
 </screen>
-    <note>(Note that the patch file may not be compatible with the
-    "patch" utility on all operating systems. You may need to
-    install GNU patch.)</note>
-    <para>When building OpenSSL, place it in a non-standard
-    location so that it does not interfere with OpenSSL libraries
-    elsewhere on the system. In the following examples, we choose
-    to install into "/opt/pkcs11/usr". We will use this location
-    when we configure BIND 9.</para>
+      <note>
+        Note that the patch file may not be compatible with the
+        "patch" utility on all operating systems. You may need to
+        install GNU patch.
+      </note>
+      <para>
+        When building OpenSSL, place it in a non-standard
+        location so that it does not interfere with OpenSSL libraries
+        elsewhere on the system. In the following examples, we choose
+        to install into "/opt/pkcs11/usr". We will use this location
+        when we configure BIND 9.
+      </para>
+      <para>
+        Later, when building BIND 9, the location of the custom-built
+        OpenSSL library will need to be specified via configure.
+      </para>
+    </sect3>
     <sect3>
       <!-- Example 1 -->
       <title>Building OpenSSL for the AEP Keyper on Linux</title>
-      <para>The AEP Keyper is a highly secure key storage device,
-      but does not provide hardware cryptographic acceleration. It
-      can carry out cryptographic operations, but it is probably
-      slower than your system's CPU. Therefore, we choose the
-      'sign-only' flavor when building OpenSSL.</para>
-      <para>The Keyper-specific PKCS #11 provider library is
-      delivered with the Keyper software. In this example, we place
-      it /opt/pkcs11/usr/lib:</para>
+      <para>
+        The AEP Keyper is a highly secure key storage device,
+        but does not provide hardware cryptographic acceleration. It
+        can carry out cryptographic operations, but it is probably
+        slower than your system's CPU. Therefore, we choose the
+        'sign-only' flavor when building OpenSSL.
+      </para>
+      <para>
+        The Keyper-specific PKCS#11 provider library is
+        delivered with the Keyper software. In this example, we place
+        it /opt/pkcs11/usr/lib:
+      </para>
       <screen>
 $ <userinput>cp pkcs11.GCC4.0.2.so.4.05 /opt/pkcs11/usr/lib/libpkcs11.so</userinput>
 </screen>
-      <para>This library is only available for Linux as a 32-bit
-      binary. If we are compiling on a 64-bit Linux system, it is
-      necessary to force a 32-bit build, by specifying -m32 in the
-      build options.</para>
-      <para>Finally, the Keyper library requires threads, so we
-      must specify -pthread.</para>
+      <para>
+        This library is only available for Linux as a 32-bit
+        binary. If we are compiling on a 64-bit Linux system, it is
+        necessary to force a 32-bit build, by specifying -m32 in the
+        build options.
+      </para>
+      <para>
+        Finally, the Keyper library requires threads, so we
+        must specify -pthread.
+      </para>
       <screen>
-$ <userinput>cd openssl-0.9.8s</userinput>
+$ <userinput>cd openssl-0.9.8y</userinput>
 $ <userinput>./Configure linux-generic32 -m32 -pthread \
             --pk11-libname=/opt/pkcs11/usr/lib/libpkcs11.so \
             --pk11-flavor=sign-only \
             --prefix=/opt/pkcs11/usr</userinput>
 </screen>
-      <para>After configuring, run "<command>make</command>"
-      and "<command>make test</command>". If "<command>make
-      test</command>" fails with "pthread_atfork() not found", you forgot to
-      add the -pthread above.</para>
+      <para>
+        After configuring, run "<command>make</command>"
+        and "<command>make test</command>". If "<command>make
+        test</command>" fails with "pthread_atfork() not found", you forgot to
+        add the -pthread above.
+      </para>
     </sect3>
     <sect3>
       <!-- Example 2 -->
       <title>Building OpenSSL for the SCA 6000 on Solaris</title>
-      <para>The SCA-6000 PKCS #11 provider is installed as a system
-      library, libpkcs11. It is a true crypto accelerator, up to 4
-      times faster than any CPU, so the flavor shall be
-      'crypto-accelerator'.</para>
-      <para>In this example, we are building on Solaris x86 on an
-      AMD64 system.</para>
+      <para>
+        The SCA-6000 PKCS#11 provider is installed as a system
+        library, libpkcs11. It is a true crypto accelerator, up to 4
+        times faster than any CPU, so the flavor shall be
+        'crypto-accelerator'.
+      </para>
+      <para>
+        In this example, we are building on Solaris x86 on an
+        AMD64 system.
+      </para>
       <screen>
-$ <userinput>cd openssl-0.9.8s</userinput>
+$ <userinput>cd openssl-0.9.8y</userinput>
 $ <userinput>./Configure solaris64-x86_64-cc \
             --pk11-libname=/usr/lib/64/libpkcs11.so \
             --pk11-flavor=crypto-accelerator \
             --prefix=/opt/pkcs11/usr</userinput>
 </screen>
-      <para>(For a 32-bit build, use "solaris-x86-cc" and
-      /usr/lib/libpkcs11.so.)</para>
-      <para>After configuring, run 
-      <command>make</command> and 
-      <command>make test</command>.</para>
+      <para>
+        (For a 32-bit build, use "solaris-x86-cc" and /usr/lib/libpkcs11.so.)
+      </para>
+      <para>
+        After configuring, run 
+        <command>make</command> and 
+        <command>make test</command>.
+      </para>
     </sect3>
     <sect3>
       <!-- Example 3 -->
       <title>Building OpenSSL for SoftHSM</title>
-      <para>SoftHSM is a software library provided by the OpenDNSSEC
-      project (http://www.opendnssec.org) which provides a PKCS#11
-      interface to a virtual HSM, implemented in the form of encrypted
-      data on the local filesystem.  It uses the Botan library for
-      encryption and SQLite3 for data storage.  Though less secure
-      than a true HSM, it can provide more secure key storage than
-      traditional key files, and can allow you to experiment with
-      PKCS#11 when an HSM is not available.</para>
-      <para>The SoftHSM cryptographic store must be installed and
-      initialized before using it with OpenSSL, and the SOFTHSM_CONF
-      environment variable must always point to the SoftHSM configuration
-      file:</para>
+      <para>
+        SoftHSM is a software library provided by the OpenDNSSEC
+        project (http://www.opendnssec.org) which provides a PKCS#11
+        interface to a virtual HSM, implemented in the form of encrypted
+        data on the local filesystem.  SoftHSM can be configured to use
+        either OpenSSL or the Botan library for encryption, and SQLite3
+        for data storage.  Though less secure than a true HSM, it can
+        provide more secure key storage than traditional key files,
+        and can allow you to experiment with PKCS#11 when an HSM is
+        not available.
+      </para>
+      <para>
+        The SoftHSM cryptographic store must be installed and
+        initialized before using it with OpenSSL, and the SOFTHSM_CONF
+        environment variable must always point to the SoftHSM configuration
+        file:
+      </para>
       <screen>
 $ <userinput> cd softhsm-1.3.0 </userinput>
 $ <userinput> configure --prefix=/opt/pkcs11/usr </userinput>
@@ -185,25 +282,31 @@ $ <userinput> export SOFTHSM_CONF=/opt/pkcs11/softhsm.conf </userinput>
 $ <userinput> echo "0:/opt/pkcs11/softhsm.db" > $SOFTHSM_CONF </userinput>
 $ <userinput> /opt/pkcs11/usr/bin/softhsm --init-token 0 --slot 0 --label softhsm </userinput>
 </screen>
-      <para>SoftHSM can perform all cryptographic operations, but
-      since it only uses your system CPU, there is no need to use it
-      for anything but signing.  Therefore, we choose the 'sign-only'
-      flavor when building OpenSSL.</para>
+      <para>
+        SoftHSM can perform all cryptographic operations, but
+        since it only uses your system CPU, there is no advantage to using
+        it for anything but signing.  Therefore, we choose the 'sign-only'
+        flavor when building OpenSSL.
+      </para>
       <screen>
-$ <userinput>cd openssl-0.9.8s</userinput>
+$ <userinput>cd openssl-0.9.8y</userinput>
 $ <userinput>./Configure linux-x86_64 -pthread \
-            --pk11-libname=/opt/pkcs11/usr/lib/libpkcs11.so \
+            --pk11-libname=/opt/pkcs11/usr/lib/libsofthsm.so \
             --pk11-flavor=sign-only \
             --prefix=/opt/pkcs11/usr</userinput>
 </screen>
-      <para>After configuring, run "<command>make</command>"
-      and "<command>make test</command>".</para>
+     <para>
+       After configuring, run "<command>make</command>"
+       and "<command>make test</command>".
+     </para>
     </sect3>
-    <para>Once you have built OpenSSL, run
-    "<command>apps/openssl engine pkcs11</command>" to confirm
-    that PKCS #11 support was compiled in correctly. The output
-    should be one of the following lines, depending on the flavor
-    selected:</para>
+    <para>
+      Once you have built OpenSSL, run
+      "<command>apps/openssl engine pkcs11</command>" to confirm
+      that PKCS#11 support was compiled in correctly. The output
+      should be one of the following lines, depending on the flavor
+      selected:
+    </para>
     <screen>
         (pkcs11) PKCS #11 engine support (sign only)
 </screen>
@@ -211,29 +314,31 @@ $ <userinput>./Configure linux-x86_64 -pthread \
     <screen>
         (pkcs11) PKCS #11 engine support (crypto accelerator)
 </screen>
-    <para>Next, run
-    "<command>apps/openssl engine pkcs11 -t</command>". This will
-    attempt to initialize the PKCS #11 engine. If it is able to
-    do so successfully, it will report
-    <quote><literal>[ available ]</literal></quote>.</para>
-    <para>If the output is correct, run
-    "<command>make install</command>" which will install the
-    modified OpenSSL suite to 
-    <filename>/opt/pkcs11/usr</filename>.</para>
-  </sect2>
-  <sect2>
-    <title>Building BIND 9 with PKCS#11</title>
-    <para>When building BIND 9, the location of the custom-built
-    OpenSSL library must be specified via configure.</para>
+    <para>
+      Next, run
+      "<command>apps/openssl engine pkcs11 -t</command>". This will
+      attempt to initialize the PKCS#11 engine. If it is able to
+      do so successfully, it will report
+      <quote><literal>[ available ]</literal></quote>.
+    </para>
+    <para>
+      If the output is correct, run
+      "<command>make install</command>" which will install the
+      modified OpenSSL suite to <filename>/opt/pkcs11/usr</filename>.
+    </para>
     <sect3>
       <!-- Example 4 -->
       <title>Configuring BIND 9 for Linux with the AEP Keyper</title>
-      <para>To link with the PKCS #11 provider, threads must be
-      enabled in the BIND 9 build.</para>
-      <para>The PKCS #11 library for the AEP Keyper is currently
-      only available as a 32-bit binary. If we are building on a
-      64-bit host, we must force a 32-bit build by adding "-m32" to
-      the CC options on the "configure" command line.</para>
+      <para>
+        To link with the PKCS#11 provider, threads must be
+        enabled in the BIND 9 build.
+      </para>
+      <para>
+        The PKCS#11 library for the AEP Keyper is currently
+        only available as a 32-bit binary. If we are building on a
+        64-bit host, we must force a 32-bit build by adding "-m32" to
+        the CC options on the "configure" command line.
+      </para>
       <screen>
 $ <userinput>cd ../bind9</userinput>
 $ <userinput>./configure CC="gcc -m32" --enable-threads \
@@ -244,8 +349,10 @@ $ <userinput>./configure CC="gcc -m32" --enable-threads \
     <sect3>
       <!-- Example 5 -->
       <title>Configuring BIND 9 for Solaris with the SCA 6000</title>
-      <para>To link with the PKCS #11 provider, threads must be
-      enabled in the BIND 9 build.</para>
+      <para>
+        To link with the PKCS#11 provider, threads must be
+        enabled in the BIND 9 build.
+      </para>
       <screen>
 $ <userinput>cd ../bind9</userinput>
 $ <userinput>./configure CC="cc -xarch=amd64" --enable-threads \
@@ -253,11 +360,13 @@ $ <userinput>./configure CC="cc -xarch=amd64" --enable-threads \
             --with-pkcs11=/usr/lib/64/libpkcs11.so</userinput>
 </screen>
       <para>(For a 32-bit build, omit CC="cc -xarch=amd64".)</para>
-      <para>If configure complains about OpenSSL not working, you
-      may have a 32/64-bit architecture mismatch. Or, you may have
-      incorrectly specified the path to OpenSSL (it should be the
-      same as the --prefix argument to the OpenSSL
-      Configure).</para>
+      <para>
+        If configure complains about OpenSSL not working, you
+        may have a 32/64-bit architecture mismatch. Or, you may have
+        incorrectly specified the path to OpenSSL (it should be the
+        same as the --prefix argument to the OpenSSL
+        Configure).
+      </para>
     </sect3>
     <sect3>
       <!-- Example 6 -->
@@ -266,63 +375,85 @@ $ <userinput>./configure CC="cc -xarch=amd64" --enable-threads \
 $ <userinput>cd ../bind9</userinput>
 $ <userinput>./configure --enable-threads \
            --with-openssl=/opt/pkcs11/usr \
-           --with-pkcs11=/opt/pkcs11/usr/lib/libpkcs11.so</userinput>
+           --with-pkcs11=/opt/pkcs11/usr/lib/libsofthsm.so</userinput>
 </screen>
     </sect3>
-    <para>After configuring, run
-    "<command>make</command>",
-    "<command>make test</command>" and
-    "<command>make install</command>".</para>
-    <para>(Note: If "make test" fails in the "pkcs11" system test, you may
-    have forgotten to set the SOFTHSM_CONF environment variable.)</para>
+    <para>
+      After configuring, run
+      "<command>make</command>",
+      "<command>make test</command>" and
+      "<command>make install</command>".
+    </para>
+    <para>
+      (Note: If "make test" fails in the "pkcs11" system test, you may
+      have forgotten to set the SOFTHSM_CONF environment variable.)
+    </para>
   </sect2>
   <sect2>
-    <title>PKCS #11 Tools</title>
-    <para>BIND 9 includes a minimal set of tools to operate the
-    HSM, including 
-    <command>pkcs11-keygen</command> to generate a new key pair
-    within the HSM, 
-    <command>pkcs11-list</command> to list objects currently
-    available, and 
-    <command>pkcs11-destroy</command> to remove objects.</para>
-    <para>In UNIX/Linux builds, these tools are built only if BIND
-    9 is configured with the --with-pkcs11 option. (NOTE: If
-    --with-pkcs11 is set to "yes", rather than to the path of the
-    PKCS #11 provider, then the tools will be built but the
-    provider will be left undefined. Use the -m option or the
-    PKCS11_PROVIDER environment variable to specify the path to the
-    provider.)</para>
+    <title>PKCS#11 Tools</title>
+    <para>
+      BIND 9 includes a minimal set of tools to operate the
+      HSM, including 
+      <command>pkcs11-keygen</command> to generate a new key pair
+      within the HSM, 
+      <command>pkcs11-list</command> to list objects currently
+      available,
+      <command>pkcs11-destroy</command> to remove objects, and
+      <command>pkcs11-tokens</command> to list available tokens.
+    </para>
+    <para>
+      In UNIX/Linux builds, these tools are built only if BIND
+      9 is configured with the --with-pkcs11 option. (Note: If
+      --with-pkcs11 is set to "yes", rather than to the path of the
+      PKCS#11 provider, then the tools will be built but the
+      provider will be left undefined. Use the -m option or the
+      PKCS11_PROVIDER environment variable to specify the path to the
+      provider.)
+    </para>
   </sect2>
   <sect2>
     <title>Using the HSM</title>
-    <para>First, we must set up the runtime environment so the
-    OpenSSL and PKCS #11 libraries can be loaded:</para>
+    <para>
+      For OpenSSL-based PKCS#11, we must first set up the runtime
+      environment so the OpenSSL and PKCS#11 libraries can be loaded:
+    </para>
     <screen>
 $ <userinput>export LD_LIBRARY_PATH=/opt/pkcs11/usr/lib:${LD_LIBRARY_PATH}</userinput>
 </screen>
-    <para>When operating an AEP Keyper, it is also necessary to
-    specify the location of the "machine" file, which stores
-    information about the Keyper for use by PKCS #11 provider
-    library. If the machine file is in 
-    <filename>/opt/Keyper/PKCS11Provider/machine</filename>,
-    use:</para>
+    <para>
+      This causes <command>named</command> and other binaries to load
+      the OpenSSL library from <filename>/opt/pkcs11/usr/lib</filename>
+      rather than from the default location.  This step is not necessary
+      when using native PKCS#11.
+    </para>
+    <para>
+      Some HSMs require other environment variables to be set.
+      For example, when operating an AEP Keyper, it is necessary to
+      specify the location of the "machine" file, which stores
+      information about the Keyper for use by the provider
+      library. If the machine file is in 
+      <filename>/opt/Keyper/PKCS11Provider/machine</filename>,
+      use:
+    </para>
     <screen>
 $ <userinput>export KEYPER_LIBRARY_PATH=/opt/Keyper/PKCS11Provider</userinput>
 </screen>
-    <!-- TODO: why not defined at compile time? -->
-    <para>These environment variables must be set whenever running
-    any tool that uses the HSM, including 
-    <command>pkcs11-keygen</command>, 
-    <command>pkcs11-list</command>, 
-    <command>pkcs11-destroy</command>, 
-    <command>dnssec-keyfromlabel</command>, 
-    <command>dnssec-signzone</command>, 
-    <command>dnssec-keygen</command>(which will use the HSM for
-    random number generation), and 
-    <command>named</command>.</para>
-    <para>We can now create and use keys in the HSM. In this case,
-    we will create a 2048 bit key and give it the label
-    "sample-ksk":</para>
+    <para>
+      Such environment variables must be set whenever running
+      any tool that uses the HSM, including 
+      <command>pkcs11-keygen</command>, 
+      <command>pkcs11-list</command>, 
+      <command>pkcs11-destroy</command>, 
+      <command>dnssec-keyfromlabel</command>, 
+      <command>dnssec-signzone</command>, 
+      <command>dnssec-keygen</command>, and
+      <command>named</command>.
+    </para>
+    <para>
+      We can now create and use keys in the HSM. In this case,
+      we will create a 2048 bit key and give it the label
+      "sample-ksk":
+    </para>
     <screen>
 $ <userinput>pkcs11-keygen -b 2048 -l sample-ksk</userinput>
 </screen>
@@ -333,44 +464,56 @@ Enter PIN:
 object[0]: handle 2147483658 class 3 label[8] 'sample-ksk' id[0]
 object[1]: handle 2147483657 class 2 label[8] 'sample-ksk' id[0]
 </screen>
-    <para>Before using this key to sign a zone, we must create a
-    pair of BIND 9 key files. The "dnssec-keyfromlabel" utility
-    does this. In this case, we will be using the HSM key
-    "sample-ksk" as the key-signing key for "example.net":</para>
+    <para>
+      Before using this key to sign a zone, we must create a
+      pair of BIND 9 key files. The "dnssec-keyfromlabel" utility
+      does this. In this case, we will be using the HSM key
+      "sample-ksk" as the key-signing key for "example.net":
+    </para>
     <screen>
 $ <userinput>dnssec-keyfromlabel -l sample-ksk -f KSK example.net</userinput>
 </screen>
-    <para>The resulting K*.key and K*.private files can now be used
-    to sign the zone. Unlike normal K* files, which contain both
-    public and private key data, these files will contain only the
-    public key data, plus an identifier for the private key which
-    remains stored within the HSM. The HSM handles signing with the
-    private key.</para>
-    <para>If you wish to generate a second key in the HSM for use
-    as a zone-signing key, follow the same procedure above, using a
-    different keylabel, a smaller key size, and omitting "-f KSK"
-    from the dnssec-keyfromlabel arguments:</para>
+    <para>
+      The resulting K*.key and K*.private files can now be used
+      to sign the zone. Unlike normal K* files, which contain both
+      public and private key data, these files will contain only the
+      public key data, plus an identifier for the private key which
+      remains stored within the HSM. Signing with the private key takes
+      place inside the HSM.
+    </para>
+    <para>
+      If you wish to generate a second key in the HSM for use
+      as a zone-signing key, follow the same procedure above, using a
+      different keylabel, a smaller key size, and omitting "-f KSK"
+      from the dnssec-keyfromlabel arguments:
+    </para>
     <screen>
 $ <userinput>pkcs11-keygen -b 1024 -l sample-zsk</userinput>
 $ <userinput>dnssec-keyfromlabel -l sample-zsk example.net</userinput>
 </screen>
-    <para>Alternatively, you may prefer to generate a conventional
-    on-disk key, using dnssec-keygen:</para>
+    <para>
+      Alternatively, you may prefer to generate a conventional
+      on-disk key, using dnssec-keygen:
+    </para>
     <screen>
 $ <userinput>dnssec-keygen example.net</userinput>
 </screen>
-    <para>This provides less security than an HSM key, but since
-    HSMs can be slow or cumbersome to use for security reasons, it
-    may be more efficient to reserve HSM keys for use in the less
-    frequent key-signing operation. The zone-signing key can be
-    rolled more frequently, if you wish, to compensate for a
-    reduction in key security.</para>
-    <para>Now you can sign the zone. (Note: If not using the -S
-    option to 
-    <command>dnssec-signzone</command>, it will be necessary to add
-    the contents of both 
-    <filename>K*.key</filename> files to the zone master file before
-    signing it.)</para>
+    <para>
+      This provides less security than an HSM key, but since
+      HSMs can be slow or cumbersome to use for security reasons, it
+      may be more efficient to reserve HSM keys for use in the less
+      frequent key-signing operation. The zone-signing key can be
+      rolled more frequently, if you wish, to compensate for a
+      reduction in key security.  (Note: When using native PKCS#11,
+      there is no speed advantage to using on-disk keys, as cryptographic
+      operations will be done by the HSM regardless.)
+    </para>
+    <para>
+      Now you can sign the zone. (Note: If not using the -S
+      option to <command>dnssec-signzone</command>, it will be
+      necessary to add the contents of both <filename>K*.key</filename>
+      files to the zone master file before signing it.)
+    </para>
     <screen>
 $ <userinput>dnssec-signzone -S example.net</userinput>
 Enter PIN:
@@ -383,36 +526,50 @@ example.net.signed
   </sect2>
   <sect2>
     <title>Specifying the engine on the command line</title>
-    <para>The OpenSSL engine can be specified in 
-    <command>named</command> and all of the BIND 
-    <command>dnssec-*</command> tools by using the "-E
-    &lt;engine&gt;" command line option. If BIND 9 is built with
-    the --with-pkcs11 option, this option defaults to "pkcs11".
-    Specifying the engine will generally not be necessary unless
-    for some reason you wish to use a different OpenSSL
-    engine.</para>
-    <para>If you wish to disable use of the "pkcs11" engine &mdash;
-    for troubleshooting purposes, or because the HSM is unavailable
-    &mdash; set the engine to the empty string. For example:</para>
+    <para>
+      When using OpenSSL-based PKCS#11, the "engine" to be used by
+      OpenSSL can be specified in <command>named</command> and all of
+      the BIND <command>dnssec-*</command> tools by using the "-E
+      &lt;engine&gt;" command line option. If BIND 9 is built with
+      the --with-pkcs11 option, this option defaults to "pkcs11".
+      Specifying the engine will generally not be necessary unless
+      for some reason you wish to use a different OpenSSL
+      engine.
+    </para>
+    <para>
+      If you wish to disable use of the "pkcs11" engine &mdash;
+      for troubleshooting purposes, or because the HSM is unavailable
+      &mdash; set the engine to the empty string. For example:
+    </para>
     <screen>
 $ <userinput>dnssec-signzone -E '' -S example.net</userinput>
 </screen>
-    <para>This causes 
-    <command>dnssec-signzone</command> to run as if it were compiled
-    without the --with-pkcs11 option.</para>
+    <para>
+      This causes 
+      <command>dnssec-signzone</command> to run as if it were compiled
+      without the --with-pkcs11 option.
+    </para>
+    <para>
+      When built with native PKCS#11 mode, the "engine" option has a
+      different meaning: it specifies the path to the PKCS#11 provider
+      library.  This may be useful when testing a new provider library.
+    </para>
   </sect2>
   <sect2>
     <title>Running named with automatic zone re-signing</title>
-    <para>If you want 
-    <command>named</command> to dynamically re-sign zones using HSM
-    keys, and/or to to sign new records inserted via nsupdate, then
-    named must have access to the HSM PIN. This can be accomplished
-    by placing the PIN into the openssl.cnf file (in the above
-    examples, 
-    <filename>/opt/pkcs11/usr/ssl/openssl.cnf</filename>).</para>
-    <para>The location of the openssl.cnf file can be overridden by
-    setting the OPENSSL_CONF environment variable before running
-    named.</para>
+    <para>
+      If you want <command>named</command> to dynamically re-sign zones
+      using HSM keys, and/or to to sign new records inserted via nsupdate,
+      then named must have access to the HSM PIN. In OpenSSL-based PKCS#11,
+      this is accomplished by placing the PIN into the openssl.cnf file
+      (in the above examples, 
+      <filename>/opt/pkcs11/usr/ssl/openssl.cnf</filename>).
+    </para>
+    <para>
+      The location of the openssl.cnf file can be overridden by
+      setting the OPENSSL_CONF environment variable before running
+      named.
+    </para>
     <para>Sample openssl.cnf:</para>
     <programlisting>
         openssl_conf = openssl_def
@@ -423,22 +580,25 @@ $ <userinput>dnssec-signzone -E '' -S example.net</userinput>
         [ pkcs11_section ]
         PIN = <replaceable>&lt;PLACE PIN HERE&gt;</replaceable>
 </programlisting>
-    <para>This will also allow the dnssec-* tools to access the HSM
-    without PIN entry. (The pkcs11-* tools access the HSM directly,
-    not via OpenSSL, so a PIN will still be required to use
-    them.)</para>
-<!-- 
-If the PIN is not known, I believe the first time named needs the
-PIN to open a key, it'll ask you to type in the PIN, which will be
-a problem because it probably won't be running on a terminal
--->
+    <para>
+      This will also allow the dnssec-* tools to access the HSM
+      without PIN entry. (The pkcs11-* tools access the HSM directly,
+      not via OpenSSL, so a PIN will still be required to use
+      them.)
+    </para>
+    <para>
+      In native PKCS#11 mode, the PIN can be provided in a file specified
+      as an attribute of the key's label.  For example, if a key had the label
+      <userinput>pkcs11:object=local-zsk;pin-source=/etc/hsmpin"</userinput>,
+      then the PIN would be read from the file
+      <filename>/etc/hsmpin</filename>.
+    </para>
     <warning>
-      <para>Placing the HSM's PIN in a text file in
-      this manner may reduce the security advantage of using an
-      HSM. Be sure this is what you want to do before configuring
-      OpenSSL in this way.</para>
+      <para>
+        Placing the HSM's PIN in a text file in this manner may reduce the
+        security advantage of using an HSM. Be sure this is what you want to
+        do before configuring OpenSSL in this way.
+      </para>
     </warning>
   </sect2>
-  <!-- TODO: what is alternative then for named dynamic re-signing? -->
-  <!-- TODO: what happens if PIN is not known? named will log about it? -->
 </sect1>
diff --git a/lib/dns/Makefile.in b/lib/dns/Makefile.in
index 0c5e93b..41fac95 100644
--- a/lib/dns/Makefile.in
+++ b/lib/dns/Makefile.in
@@ -27,10 +27,10 @@ top_srcdir =	@top_srcdir@
 
 USE_ISC_SPNEGO = @USE_ISC_SPNEGO@
 
-CINCLUDES =	-I. -Iinclude ${DNS_INCLUDES} \
-		${ISC_INCLUDES} @DST_OPENSSL_INC@ @DST_GSSAPI_INC@
+CINCLUDES =	-I. -I${top_srcdir}/lib/dns -Iinclude ${DNS_INCLUDES} ${ISC_INCLUDES} \
+		@DST_OPENSSL_INC@ @DST_GSSAPI_INC@
 
-CDEFINES =	-DUSE_MD5 @USE_OPENSSL@ @USE_GSSAPI@ ${USE_ISC_SPNEGO}
+CDEFINES =	-DUSE_MD5 @CRYPTO@ @USE_GSSAPI@ ${USE_ISC_SPNEGO}
 
 CWARNINGS =
 
@@ -47,7 +47,10 @@ OPENSSLLINKOBJS = openssl_link.@O@ openssldh_link.@O@ openssldsa_link.@O@ \
 		  opensslecdsa_link.@O@ @OPENSSLGOSTLINKOBJS@ \
 		  opensslrsa_link.@O@
 
-DSTOBJS =	@DST_EXTRA_OBJS@ @OPENSSLLINKOBJS@ \
+PKCS11LINKOBJS	= pkcs11dh_link.@O@ pkcs11dsa_link.@O@ pkcs11rsa_link.@O@ \
+		pkcs11ecdsa_link.@O@ pkcs11gost_link.@O@ pkcs11.@O@
+
+DSTOBJS =	@DST_EXTRA_OBJS@ @OPENSSLLINKOBJS@ @PKCS11LINKOBJS@ \
 		dst_api.@O@ dst_lib.@O@ dst_parse.@O@ dst_result.@O@ \
 		gssapi_link.@O@ gssapictx.@O@ hmac_link.@O@ key.@O@ 
 
@@ -79,7 +82,10 @@ OPENSSLGOSTLINKSRCS = opensslgost_link.c
 OPENSSLLINKSRCS = openssl_link.c openssldh_link.c openssldsa_link.c \
 		  opensslecdsa_link.c @OPENSSLGOSTLINKSRCS@ opensslrsa_link.c
 
-DSTSRCS =	@DST_EXTRA_SRCS@ @OPENSSLLINKSRCS@ \
+PKCS11LINKSRCS	= pkcs11dh_link.c pkcs11dsa_link.c pkcs11rsa_link.c \
+		pkcs11ecdsa_link.c pkcs11gost_link.c pkcs11.c
+
+DSTSRCS =	@DST_EXTRA_SRCS@ @OPENSSLLINKSRCS@ @PKCS11LINKSRCS@ \
 		dst_api.c dst_lib.c dst_parse.c \
 		dst_result.c gssapi_link.c gssapictx.c \
 		hmac_link.c key.c
diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c
index cf97404..00a0080 100644
--- a/lib/dns/dnssec.c
+++ b/lib/dns/dnssec.c
@@ -275,7 +275,8 @@ dns_dnssec_sign(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
 	if (ret != ISC_R_SUCCESS)
 		goto cleanup_databuf;
 
-	ret = dst_context_create2(key, mctx, DNS_LOGCATEGORY_DNSSEC, &ctx);
+	ret = dst_context_create3(key, mctx,
+				  DNS_LOGCATEGORY_DNSSEC, ISC_TRUE, &ctx);
 	if (ret != ISC_R_SUCCESS)
 		goto cleanup_databuf;
 
@@ -470,7 +471,8 @@ dns_dnssec_verify3(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
 	}
 
  again:
-	ret = dst_context_create2(key, mctx, DNS_LOGCATEGORY_DNSSEC, &ctx);
+	ret = dst_context_create4(key, mctx, DNS_LOGCATEGORY_DNSSEC,
+				  ISC_FALSE, maxbits, &ctx);
 	if (ret != ISC_R_SUCCESS)
 		goto cleanup_struct;
 
@@ -872,7 +874,8 @@ dns_dnssec_signmessage(dns_message_t *msg, dst_key_t *key) {
 
 	isc_buffer_init(&databuf, data, sizeof(data));
 
-	RETERR(dst_context_create2(key, mctx, DNS_LOGCATEGORY_DNSSEC, &ctx));
+	RETERR(dst_context_create3(key, mctx,
+				   DNS_LOGCATEGORY_DNSSEC, ISC_TRUE, &ctx));
 
 	/*
 	 * Digest the fields of the SIG - we can cheat and use
@@ -1022,7 +1025,8 @@ dns_dnssec_verifymessage(isc_buffer_t *source, dns_message_t *msg,
 		goto failure;
 	}
 
-	RETERR(dst_context_create2(key, mctx, DNS_LOGCATEGORY_DNSSEC, &ctx));
+	RETERR(dst_context_create3(key, mctx,
+				   DNS_LOGCATEGORY_DNSSEC, ISC_FALSE, &ctx));
 
 	/*
 	 * Digest the SIG(0) record, except for the signature.
diff --git a/lib/dns/ds.c b/lib/dns/ds.c
index e72ecbb..b51476b 100644
--- a/lib/dns/ds.c
+++ b/lib/dns/ds.c
@@ -38,11 +38,8 @@
 
 #include <dst/dst.h>
 
-#ifdef HAVE_OPENSSL_GOST
-#include <dst/result.h>
-#include <openssl/evp.h>
-
-extern const EVP_MD * EVP_gost(void);
+#if defined(HAVE_OPENSSL_GOST) || defined(HAVE_PKCS11_GOST)
+#include "dst_gost.h"
 #endif
 
 isc_result_t
@@ -59,9 +56,8 @@ dns_ds_buildrdata(dns_name_t *owner, dns_rdata_t *key,
 	isc_sha1_t sha1;
 	isc_sha256_t sha256;
 	isc_sha384_t sha384;
-#ifdef HAVE_OPENSSL_GOST
-	EVP_MD_CTX ctx;
-	const EVP_MD *md;
+#if defined(HAVE_OPENSSL_GOST) || defined(HAVE_PKCS11_GOST)
+	isc_gost_t gost;
 #endif
 
 	REQUIRE(key != NULL);
@@ -88,29 +84,23 @@ dns_ds_buildrdata(dns_name_t *owner, dns_rdata_t *key,
 		isc_sha1_final(&sha1, digest);
 		break;
 
-#ifdef HAVE_OPENSSL_GOST
-#define CHECK(x)					\
-	if ((x) != 1) {					\
-		EVP_MD_CTX_cleanup(&ctx);		\
-		return (DST_R_CRYPTOFAILURE);		\
-	}
+#if defined(HAVE_OPENSSL_GOST) || defined(HAVE_PKCS11_GOST)
+#define RETERR(x) do {					\
+	isc_result_t ret = (x);				\
+	if (ret != ISC_R_SUCCESS) {			\
+		isc_gost_invalidate(&gost);		\
+		return (ret);				\
+	}						\
+} while (0)
 
 	case DNS_DSDIGEST_GOST:
-		md = EVP_gost();
-		if (md == NULL)
-			return (DST_R_CRYPTOFAILURE);
-		EVP_MD_CTX_init(&ctx);
-		CHECK(EVP_DigestInit(&ctx, md));
+		RETERR(isc_gost_init(&gost));
 		dns_name_toregion(name, &r);
-		CHECK(EVP_DigestUpdate(&ctx,
-				       (const void *) r.base,
-				       (size_t) r.length));
+		RETERR(isc_gost_update(&gost, r.base, r.length));
 		dns_rdata_toregion(key, &r);
 		INSIST(r.length >= 4);
-		CHECK(EVP_DigestUpdate(&ctx,
-				       (const void *) r.base,
-				       (size_t) r.length));
-		CHECK(EVP_DigestFinal(&ctx, digest, NULL));
+		RETERR(isc_gost_update(&gost, r.base, r.length));
+		RETERR(isc_gost_final(&gost, digest));
 		break;
 #endif
 
@@ -147,7 +137,7 @@ dns_ds_buildrdata(dns_name_t *owner, dns_rdata_t *key,
 		ds.length = ISC_SHA1_DIGESTLENGTH;
 		break;
 
-#ifdef HAVE_OPENSSL_GOST
+#if defined(HAVE_OPENSSL_GOST) || defined(HAVE_PKCS11_GOST)
 	case DNS_DSDIGEST_GOST:
 		ds.length = ISC_GOST_DIGESTLENGTH;
 		break;
diff --git a/lib/dns/dst_api.c b/lib/dns/dst_api.c
index 6416273..d96473f 100644
--- a/lib/dns/dst_api.c
+++ b/lib/dns/dst_api.c
@@ -75,9 +75,7 @@
 #define DST_AS_STR(t) ((t).value.as_textregion.base)
 
 static dst_func_t *dst_t_func[DST_MAX_ALGS];
-#ifdef BIND9
 static isc_entropy_t *dst_entropy_pool = NULL;
-#endif
 static unsigned int dst_entropy_flags = 0;
 static isc_boolean_t dst_initialized = ISC_FALSE;
 
@@ -169,7 +167,7 @@ dst_lib_init2(isc_mem_t *mctx, isc_entropy_t *ectx,
 #endif
 	REQUIRE(dst_initialized == ISC_FALSE);
 
-#ifndef OPENSSL
+#if !defined(OPENSSL) && !defined(PKCS11CRYPTO)
 	UNUSED(engine);
 #endif
 
@@ -234,7 +232,24 @@ dst_lib_init2(isc_mem_t *mctx, isc_entropy_t *ectx,
 	RETERR(dst__opensslecdsa_init(&dst_t_func[DST_ALG_ECDSA256]));
 	RETERR(dst__opensslecdsa_init(&dst_t_func[DST_ALG_ECDSA384]));
 #endif
-#endif /* OPENSSL */
+#elif PKCS11CRYPTO
+	RETERR(dst__pkcs11_init(mctx, engine));
+	RETERR(dst__pkcs11rsa_init(&dst_t_func[DST_ALG_RSAMD5]));
+	RETERR(dst__pkcs11rsa_init(&dst_t_func[DST_ALG_RSASHA1]));
+	RETERR(dst__pkcs11rsa_init(&dst_t_func[DST_ALG_NSEC3RSASHA1]));
+	RETERR(dst__pkcs11rsa_init(&dst_t_func[DST_ALG_RSASHA256]));
+	RETERR(dst__pkcs11rsa_init(&dst_t_func[DST_ALG_RSASHA512]));
+	RETERR(dst__pkcs11dsa_init(&dst_t_func[DST_ALG_DSA]));
+	RETERR(dst__pkcs11dsa_init(&dst_t_func[DST_ALG_NSEC3DSA]));
+	RETERR(dst__pkcs11dh_init(&dst_t_func[DST_ALG_DH]));
+#ifdef HAVE_PKCS11_ECDSA
+	RETERR(dst__pkcs11ecdsa_init(&dst_t_func[DST_ALG_ECDSA256]));
+	RETERR(dst__pkcs11ecdsa_init(&dst_t_func[DST_ALG_ECDSA384]));
+#endif
+#ifdef HAVE_PKCS11_GOST
+	RETERR(dst__pkcs11gost_init(&dst_t_func[DST_ALG_ECCGOST]));
+#endif
+#endif /* if OPENSSL, elif PKCS11CRYPTO */
 #ifdef GSSAPI
 	RETERR(dst__gssapi_init(&dst_t_func[DST_ALG_GSSAPI]));
 #endif
@@ -259,7 +274,9 @@ dst_lib_destroy(void) {
 			dst_t_func[i]->cleanup();
 #ifdef OPENSSL
 	dst__openssl_destroy();
-#endif
+#elif PKCS11CRYPTO
+	(void) dst__pkcs11_destroy();
+#endif /* if OPENSSL, elif PKCS11CRYPTO */
 	if (dst__memory_pool != NULL)
 		isc_mem_detach(&dst__memory_pool);
 #ifdef BIND9
@@ -279,13 +296,31 @@ dst_algorithm_supported(unsigned int alg) {
 
 isc_result_t
 dst_context_create(dst_key_t *key, isc_mem_t *mctx, dst_context_t **dctxp) {
-	return (dst_context_create2(key, mctx,
-				    DNS_LOGCATEGORY_GENERAL, dctxp));
+	return (dst_context_create4(key, mctx, DNS_LOGCATEGORY_GENERAL,
+				    ISC_TRUE, 0, dctxp));
 }
 
 isc_result_t
 dst_context_create2(dst_key_t *key, isc_mem_t *mctx,
-		    isc_logcategory_t *category, dst_context_t **dctxp) {
+		    isc_logcategory_t *category, dst_context_t **dctxp)
+{
+	return (dst_context_create4(key, mctx, category, ISC_TRUE, 0, dctxp));
+}
+
+isc_result_t
+dst_context_create3(dst_key_t *key, isc_mem_t *mctx,
+		    isc_logcategory_t *category, isc_boolean_t useforsigning,
+		    dst_context_t **dctxp)
+{
+	return (dst_context_create4(key, mctx, category,
+				    useforsigning, 0, dctxp));
+}
+
+isc_result_t
+dst_context_create4(dst_key_t *key, isc_mem_t *mctx,
+		    isc_logcategory_t *category, isc_boolean_t useforsigning,
+		    int maxbits, dst_context_t **dctxp)
+{
 	dst_context_t *dctx;
 	isc_result_t result;
 
@@ -294,7 +329,7 @@ dst_context_create2(dst_key_t *key, isc_mem_t *mctx,
 	REQUIRE(mctx != NULL);
 	REQUIRE(dctxp != NULL && *dctxp == NULL);
 
-	if (key->func->createctx == NULL)
+	if (key->func->createctx == NULL && key->func->createctx2 == NULL)
 		return (DST_R_UNSUPPORTEDALG);
 	if (key->keydata.generic == NULL)
 		return (DST_R_NULLKEY);
@@ -305,7 +340,14 @@ dst_context_create2(dst_key_t *key, isc_mem_t *mctx,
 	dctx->key = key;
 	dctx->mctx = mctx;
 	dctx->category = category;
-	result = key->func->createctx(key, dctx);
+	if (useforsigning)
+		dctx->use = DO_SIGN;
+	else
+		dctx->use = DO_VERIFY;
+	if (key->func->createctx2 != NULL)
+		result = key->func->createctx2(key, maxbits, dctx);
+	else
+		result = key->func->createctx(key, dctx);
 	if (result != ISC_R_SUCCESS) {
 		isc_mem_put(mctx, dctx, sizeof(dst_context_t));
 		return (result);
@@ -1796,7 +1838,7 @@ algorithm_status(unsigned int alg) {
 
 	if (dst_algorithm_supported(alg))
 		return (ISC_R_SUCCESS);
-#ifndef OPENSSL
+#if !defined(OPENSSL) && !defined(PKCS11CRYPTO)
 	if (alg == DST_ALG_RSAMD5 || alg == DST_ALG_RSASHA1 ||
 	    alg == DST_ALG_DSA || alg == DST_ALG_DH ||
 	    alg == DST_ALG_HMACMD5 || alg == DST_ALG_NSEC3DSA ||
@@ -1842,11 +1884,18 @@ dst__entropy_getdata(void *buf, unsigned int len, isc_boolean_t pseudo) {
 
 	if (len == 0)
 		return (ISC_R_SUCCESS);
+
+#ifdef PKCS11CRYPTO
+	UNUSED(pseudo);
+	UNUSED(flags);
+	return (pk11_rand_bytes(buf, len));
+#else /* PKCS11CRYPTO */
 	if (pseudo)
 		flags &= ~ISC_ENTROPY_GOODONLY;
 	else
 		flags |= ISC_ENTROPY_BLOCKING;
 	return (isc_entropy_getdata(dst_entropy_pool, buf, len, NULL, flags));
+#endif /* PKCS11CRYPTO */
 #else
 	UNUSED(buf);
 	UNUSED(len);
@@ -1858,7 +1907,7 @@ dst__entropy_getdata(void *buf, unsigned int len, isc_boolean_t pseudo) {
 
 unsigned int
 dst__entropy_status(void) {
-#ifdef BIND9
+#ifndef PKCS11CRYPTO
 #ifdef GSSAPI
 	unsigned int flags = dst_entropy_flags;
 	isc_result_t ret;
diff --git a/lib/dns/dst_gost.h b/lib/dns/dst_gost.h
new file mode 100644
index 0000000..37a4200
--- /dev/null
+++ b/lib/dns/dst_gost.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef DST_GOST_H
+#define DST_GOST_H 1
+
+#include <isc/lang.h>
+#include <isc/log.h>
+#include <dst/result.h>
+
+#define ISC_GOST_DIGESTLENGTH 32U
+
+#ifdef HAVE_OPENSSL_GOST
+#include <openssl/evp.h>
+
+typedef EVP_MD_CTX isc_gost_t;
+#endif
+#ifdef HAVE_PKCS11_GOST
+#include <pk11/pk11.h>
+
+typedef pk11_context_t isc_gost_t;
+#endif
+
+ISC_LANG_BEGINDECLS
+
+#if defined(HAVE_OPENSSL_GOST) || defined(HAVE_PKCS11_GOST)
+
+isc_result_t
+isc_gost_init(isc_gost_t *ctx);
+
+void
+isc_gost_invalidate(isc_gost_t *ctx);
+
+isc_result_t
+isc_gost_update(isc_gost_t *ctx, const unsigned char *data, unsigned int len);
+
+isc_result_t
+isc_gost_final(isc_gost_t *ctx, unsigned char *digest);
+
+ISC_LANG_ENDDECLS
+
+#endif /* HAVE_OPENSSL_GOST || HAVE_PKCS11_GOST */
+
+#endif /* DST_GOST_H */
diff --git a/lib/dns/dst_internal.h b/lib/dns/dst_internal.h
index 49ca424..b15135e 100644
--- a/lib/dns/dst_internal.h
+++ b/lib/dns/dst_internal.h
@@ -84,6 +84,12 @@ typedef struct dst_hmacsha256_key dst_hmacsha256_key_t;
 typedef struct dst_hmacsha384_key dst_hmacsha384_key_t;
 typedef struct dst_hmacsha512_key dst_hmacsha512_key_t;
 
+/*%
+ * Indicate whether a DST context will be used for signing
+ * or for verification
+ */
+typedef enum { DO_SIGN, DO_VERIFY } dst_use_t;
+
 /*% DST Key Structure */
 struct dst_key {
 	unsigned int	magic;
@@ -112,6 +118,8 @@ struct dst_key {
 		DSA *dsa;
 		DH *dh;
 		EVP_PKEY *pkey;
+#elif PKCS11CRYPTO
+		pk11_object_t *pkey;
 #endif
 		dst_hmacmd5_key_t *hmacmd5;
 		dst_hmacsha1_key_t *hmacsha1;
@@ -139,6 +147,7 @@ struct dst_key {
 
 struct dst_context {
 	unsigned int magic;
+	dst_use_t use;
 	dst_key_t *key;
 	isc_mem_t *mctx;
 	isc_logcategory_t *category;
@@ -157,6 +166,8 @@ struct dst_context {
 		isc_hmacsha512_t *hmacsha512ctx;
 #ifdef OPENSSL
 		EVP_MD_CTX *evp_md_ctx;
+#elif PKCS11CRYPTO
+		pk11_context_t *pk11_ctx;
 #endif
 	} ctxdata;
 };
@@ -166,6 +177,8 @@ struct dst_func {
 	 * Context functions
 	 */
 	isc_result_t (*createctx)(dst_key_t *key, dst_context_t *dctx);
+	isc_result_t (*createctx2)(dst_key_t *key, int maxbits,
+				   dst_context_t *dctx);
 	void (*destroyctx)(dst_context_t *dctx);
 	isc_result_t (*adddata)(dst_context_t *dctx, const isc_region_t *data);
 
@@ -209,6 +222,7 @@ struct dst_func {
  * Initializers
  */
 isc_result_t dst__openssl_init(const char *engine);
+#define dst__pkcs11_init pk11_initialize
 
 isc_result_t dst__hmacmd5_init(struct dst_func **funcp);
 isc_result_t dst__hmacsha1_init(struct dst_func **funcp);
@@ -218,20 +232,30 @@ isc_result_t dst__hmacsha384_init(struct dst_func **funcp);
 isc_result_t dst__hmacsha512_init(struct dst_func **funcp);
 isc_result_t dst__opensslrsa_init(struct dst_func **funcp,
 				  unsigned char algorithm);
+isc_result_t dst__pkcs11rsa_init(struct dst_func **funcp);
 isc_result_t dst__openssldsa_init(struct dst_func **funcp);
+isc_result_t dst__pkcs11dsa_init(struct dst_func **funcp);
 isc_result_t dst__openssldh_init(struct dst_func **funcp);
+isc_result_t dst__pkcs11dh_init(struct dst_func **funcp);
 isc_result_t dst__gssapi_init(struct dst_func **funcp);
+#ifdef HAVE_OPENSSL_ECDSA
+isc_result_t dst__opensslecdsa_init(struct dst_func **funcp);
+#endif
+#ifdef HAVE_PKCS11_ECDSA
+isc_result_t dst__pkcs11ecdsa_init(struct dst_func **funcp);
+#endif
 #ifdef HAVE_OPENSSL_GOST
 isc_result_t dst__opensslgost_init(struct dst_func **funcp);
 #endif
-#ifdef HAVE_OPENSSL_ECDSA
-isc_result_t dst__opensslecdsa_init(struct dst_func **funcp);
+#ifdef HAVE_PKCS11_GOST
+isc_result_t dst__pkcs11gost_init(struct dst_func **funcp);
 #endif
 
 /*%
  * Destructors
  */
 void dst__openssl_destroy(void);
+#define dst__pkcs11_destroy pk11_finalize
 
 /*%
  * Memory allocators using the DST memory pool.
diff --git a/lib/dns/dst_parse.c b/lib/dns/dst_parse.c
index 6348cc1..ec622d9 100644
--- a/lib/dns/dst_parse.c
+++ b/lib/dns/dst_parse.c
@@ -93,7 +93,6 @@ static struct parse_map map[] = {
 	{TAG_RSA_COEFFICIENT, "Coefficient:"},
 	{TAG_RSA_ENGINE, "Engine:" },
 	{TAG_RSA_LABEL, "Label:" },
-	{TAG_RSA_PIN, "PIN:" },
 
 	{TAG_DH_PRIME, "Prime(p):"},
 	{TAG_DH_GENERATOR, "Generator(g):"},
@@ -107,8 +106,11 @@ static struct parse_map map[] = {
 	{TAG_DSA_PUBLIC, "Public_value(y):"},
 
 	{TAG_GOST_PRIVASN1, "GostAsn1:"},
+	{TAG_GOST_PRIVRAW, "PrivateKey:"},
 
 	{TAG_ECDSA_PRIVATEKEY, "PrivateKey:"},
+	{TAG_ECDSA_ENGINE, "Engine:" },
+	{TAG_ECDSA_LABEL, "Label:" },
 
 	{TAG_HMACMD5_KEY, "Key:"},
 	{TAG_HMACMD5_BITS, "Bits:"},
@@ -262,22 +264,42 @@ check_gost(const dst_private_t *priv, isc_boolean_t external) {
 
 	if (priv->nelements != GOST_NTAGS)
 		return (-1);
-	if (priv->elements[0].tag != TAG(DST_ALG_ECCGOST, 0))
+	if ((priv->elements[0].tag != TAG(DST_ALG_ECCGOST, 0)) &&
+	    (priv->elements[0].tag != TAG(DST_ALG_ECCGOST, 1)))
 		return (-1);
 	return (0);
 }
 
 static int
 check_ecdsa(const dst_private_t *priv, isc_boolean_t external) {
+	int i, j;
+	isc_boolean_t have[ECDSA_NTAGS];
+	isc_boolean_t ok;
+	unsigned int mask;
 
 	if (external)
 		return ((priv->nelements == 0) ? 0 : -1);
 
-	if (priv->nelements != ECDSA_NTAGS)
-		return (-1);
-	if (priv->elements[0].tag != TAG(DST_ALG_ECDSA256, 0))
-		return (-1);
-	return (0);
+	for (i = 0; i < ECDSA_NTAGS; i++)
+		have[i] = ISC_FALSE;
+	for (j = 0; j < priv->nelements; j++) {
+		for (i = 0; i < ECDSA_NTAGS; i++)
+			if (priv->elements[j].tag == TAG(DST_ALG_ECDSA256, i))
+				break;
+		if (i == ECDSA_NTAGS)
+			return (-1);
+		have[i] = ISC_TRUE;
+	}
+
+	mask = ~0;
+	mask <<= sizeof(mask) * 8 - TAG_SHIFT;
+	mask >>= sizeof(mask) * 8 - TAG_SHIFT;
+
+	if (have[TAG_ECDSA_ENGINE & mask])
+		ok = have[TAG_ECDSA_LABEL & mask];
+	else
+		ok = have[TAG_ECDSA_PRIVATEKEY & mask];
+	return (ok ? 0 : -1 );
 }
 
 static int
diff --git a/lib/dns/dst_parse.h b/lib/dns/dst_parse.h
index f048bf0..a8a5641 100644
--- a/lib/dns/dst_parse.h
+++ b/lib/dns/dst_parse.h
@@ -63,7 +63,6 @@
 #define TAG_RSA_COEFFICIENT	((DST_ALG_RSAMD5 << TAG_SHIFT) + 7)
 #define TAG_RSA_ENGINE		((DST_ALG_RSAMD5 << TAG_SHIFT) + 8)
 #define TAG_RSA_LABEL		((DST_ALG_RSAMD5 << TAG_SHIFT) + 9)
-#define TAG_RSA_PIN		((DST_ALG_RSAMD5 << TAG_SHIFT) + 10)
 
 #define DH_NTAGS		4
 #define TAG_DH_PRIME		((DST_ALG_DH << TAG_SHIFT) + 0)
@@ -80,9 +79,12 @@
 
 #define GOST_NTAGS		1
 #define TAG_GOST_PRIVASN1	((DST_ALG_ECCGOST << TAG_SHIFT) + 0)
+#define TAG_GOST_PRIVRAW	((DST_ALG_ECCGOST << TAG_SHIFT) + 1)
 
-#define ECDSA_NTAGS		1
+#define ECDSA_NTAGS		4
 #define TAG_ECDSA_PRIVATEKEY	((DST_ALG_ECDSA256 << TAG_SHIFT) + 0)
+#define TAG_ECDSA_ENGINE	((DST_ALG_ECDSA256 << TAG_SHIFT) + 1)
+#define TAG_ECDSA_LABEL		((DST_ALG_ECDSA256 << TAG_SHIFT) + 2)
 
 #define OLD_HMACMD5_NTAGS	1
 #define HMACMD5_NTAGS		2
diff --git a/lib/dns/dst_pkcs11.h b/lib/dns/dst_pkcs11.h
new file mode 100644
index 0000000..1c35b6b
--- /dev/null
+++ b/lib/dns/dst_pkcs11.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef DST_PKCS11_H
+#define DST_PKCS11_H 1
+
+#include <isc/lang.h>
+#include <isc/log.h>
+#include <isc/result.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dst__pkcs11_toresult(const char *funcname, const char *file, int line,
+		     isc_result_t fallback, CK_RV rv);
+
+#define PK11_CALL(func, args, fallback) \
+	((void) (((rv = (func) args) == CKR_OK) || \
+		 ((ret = dst__pkcs11_toresult(#func, __FILE__, __LINE__, \
+					      fallback, rv)), 0)))
+
+#define PK11_RET(func, args, fallback) \
+	((void) (((rv = (func) args) == CKR_OK) || \
+		 ((ret = dst__pkcs11_toresult(#func, __FILE__, __LINE__, \
+					      fallback, rv)), 0)));	\
+	if (rv != CKR_OK) goto err;
+
+ISC_LANG_ENDDECLS
+
+#endif /* DST_PKCS11_H */
diff --git a/lib/dns/dst_result.c b/lib/dns/dst_result.c
index 30aa1fa..79fa7d3 100644
--- a/lib/dns/dst_result.c
+++ b/lib/dns/dst_result.c
@@ -50,7 +50,8 @@ static const char *text[DST_R_NRESULTS] = {
 	"failure computing a shared secret",	/*%< 18 */
 	"no randomness available",		/*%< 19 */
 	"bad key type",				/*%< 20 */
-	"no engine"				/*%< 21 */
+	"no engine",				/*%< 21 */
+	"illegal operation for an external key",/*%< 22 */
 };
 
 #define DST_RESULT_RESULTSET			2
diff --git a/lib/dns/gssapi_link.c b/lib/dns/gssapi_link.c
index 5ad81cd..1c35959 100644
--- a/lib/dns/gssapi_link.c
+++ b/lib/dns/gssapi_link.c
@@ -358,6 +358,7 @@ gssapi_dump(dst_key_t *key, isc_mem_t *mctx, char **buffer, int *length) {
 
 static dst_func_t gssapi_functions = {
 	gssapi_create_signverify_ctx,
+	NULL, /*%< createctx2 */
 	gssapi_destroy_signverify_ctx,
 	gssapi_adddata,
 	gssapi_sign,
diff --git a/lib/dns/hmac_link.c b/lib/dns/hmac_link.c
index 1f1a0ca..7a56c79 100644
--- a/lib/dns/hmac_link.c
+++ b/lib/dns/hmac_link.c
@@ -282,6 +282,9 @@ hmacmd5_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
 	if (result != ISC_R_SUCCESS)
 		return (result);
 
+	if (key->external)
+		result = DST_R_EXTERNALKEY;
+
 	key->key_bits = 0;
 	for (i = 0; i < priv.nelements && result == ISC_R_SUCCESS; i++) {
 		switch (priv.elements[i].tag) {
@@ -310,6 +313,7 @@ hmacmd5_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
 
 static dst_func_t hmacmd5_functions = {
 	hmacmd5_createctx,
+	NULL, /*%< createctx2 */
 	hmacmd5_destroyctx,
 	hmacmd5_adddata,
 	hmacmd5_sign,
@@ -528,6 +532,9 @@ hmacsha1_tofile(const dst_key_t *key, const char *directory) {
 	if (key->keydata.hmacsha1 == NULL)
 		return (DST_R_NULLKEY);
 
+	if (key->external)
+		return (DST_R_EXTERNALKEY);
+
 	hkey = key->keydata.hmacsha1;
 
 	priv.elements[cnt].tag = TAG_HMACSHA1_KEY;
@@ -559,8 +566,11 @@ hmacsha1_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
 	if (result != ISC_R_SUCCESS)
 		return (result);
 
+	if (key->external)
+		result = DST_R_EXTERNALKEY;
+
 	key->key_bits = 0;
-	for (i = 0; i < priv.nelements; i++) {
+	for (i = 0; i < priv.nelements && result == ISC_R_SUCCESS; i++) {
 		switch (priv.elements[i].tag) {
 		case TAG_HMACSHA1_KEY:
 			isc_buffer_init(&b, priv.elements[i].data,
@@ -587,6 +597,7 @@ hmacsha1_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
 
 static dst_func_t hmacsha1_functions = {
 	hmacsha1_createctx,
+	NULL, /*%< createctx2 */
 	hmacsha1_destroyctx,
 	hmacsha1_adddata,
 	hmacsha1_sign,
@@ -807,6 +818,9 @@ hmacsha224_tofile(const dst_key_t *key, const char *directory) {
 	if (key->keydata.hmacsha224 == NULL)
 		return (DST_R_NULLKEY);
 
+	if (key->external)
+		return (DST_R_EXTERNALKEY);
+
 	hkey = key->keydata.hmacsha224;
 
 	priv.elements[cnt].tag = TAG_HMACSHA224_KEY;
@@ -838,6 +852,9 @@ hmacsha224_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
 	if (result != ISC_R_SUCCESS)
 		return (result);
 
+	if (key->external)
+		result = DST_R_EXTERNALKEY;
+
 	key->key_bits = 0;
 	for (i = 0; i < priv.nelements; i++) {
 		switch (priv.elements[i].tag) {
@@ -866,6 +883,7 @@ hmacsha224_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
 
 static dst_func_t hmacsha224_functions = {
 	hmacsha224_createctx,
+	NULL, /*%< createctx2 */
 	hmacsha224_destroyctx,
 	hmacsha224_adddata,
 	hmacsha224_sign,
@@ -1086,6 +1104,9 @@ hmacsha256_tofile(const dst_key_t *key, const char *directory) {
 	if (key->keydata.hmacsha256 == NULL)
 		return (DST_R_NULLKEY);
 
+	if (key->external)
+		return (DST_R_EXTERNALKEY);
+
 	hkey = key->keydata.hmacsha256;
 
 	priv.elements[cnt].tag = TAG_HMACSHA256_KEY;
@@ -1117,8 +1138,11 @@ hmacsha256_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
 	if (result != ISC_R_SUCCESS)
 		return (result);
 
+	if (key->external)
+		result = DST_R_EXTERNALKEY;
+
 	key->key_bits = 0;
-	for (i = 0; i < priv.nelements; i++) {
+	for (i = 0; i < priv.nelements && result == ISC_R_SUCCESS; i++) {
 		switch (priv.elements[i].tag) {
 		case TAG_HMACSHA256_KEY:
 			isc_buffer_init(&b, priv.elements[i].data,
@@ -1145,6 +1169,7 @@ hmacsha256_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
 
 static dst_func_t hmacsha256_functions = {
 	hmacsha256_createctx,
+	NULL, /*%< createctx2 */
 	hmacsha256_destroyctx,
 	hmacsha256_adddata,
 	hmacsha256_sign,
@@ -1365,6 +1390,9 @@ hmacsha384_tofile(const dst_key_t *key, const char *directory) {
 	if (key->keydata.hmacsha384 == NULL)
 		return (DST_R_NULLKEY);
 
+	if (key->external)
+		return (DST_R_EXTERNALKEY);
+
 	hkey = key->keydata.hmacsha384;
 
 	priv.elements[cnt].tag = TAG_HMACSHA384_KEY;
@@ -1396,8 +1424,11 @@ hmacsha384_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
 	if (result != ISC_R_SUCCESS)
 		return (result);
 
+	if (key->external)
+		result = DST_R_EXTERNALKEY;
+
 	key->key_bits = 0;
-	for (i = 0; i < priv.nelements; i++) {
+	for (i = 0; i < priv.nelements && result == ISC_R_SUCCESS; i++) {
 		switch (priv.elements[i].tag) {
 		case TAG_HMACSHA384_KEY:
 			isc_buffer_init(&b, priv.elements[i].data,
@@ -1424,6 +1455,7 @@ hmacsha384_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
 
 static dst_func_t hmacsha384_functions = {
 	hmacsha384_createctx,
+	NULL, /*%< createctx2 */
 	hmacsha384_destroyctx,
 	hmacsha384_adddata,
 	hmacsha384_sign,
@@ -1644,6 +1676,9 @@ hmacsha512_tofile(const dst_key_t *key, const char *directory) {
 	if (key->keydata.hmacsha512 == NULL)
 		return (DST_R_NULLKEY);
 
+	if (key->external)
+		return (DST_R_EXTERNALKEY);
+
 	hkey = key->keydata.hmacsha512;
 
 	priv.elements[cnt].tag = TAG_HMACSHA512_KEY;
@@ -1675,8 +1710,11 @@ hmacsha512_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
 	if (result != ISC_R_SUCCESS)
 		return (result);
 
+	if (key->external)
+		result = DST_R_EXTERNALKEY;
+
 	key->key_bits = 0;
-	for (i = 0; i < priv.nelements; i++) {
+	for (i = 0; i < priv.nelements && result == ISC_R_SUCCESS; i++) {
 		switch (priv.elements[i].tag) {
 		case TAG_HMACSHA512_KEY:
 			isc_buffer_init(&b, priv.elements[i].data,
@@ -1703,6 +1741,7 @@ hmacsha512_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
 
 static dst_func_t hmacsha512_functions = {
 	hmacsha512_createctx,
+	NULL, /*%< createctx2 */
 	hmacsha512_destroyctx,
 	hmacsha512_adddata,
 	hmacsha512_sign,
diff --git a/lib/dns/include/dst/dst.h b/lib/dns/include/dst/dst.h
index 1fdce4c..bdbd269 100644
--- a/lib/dns/include/dst/dst.h
+++ b/lib/dns/include/dst/dst.h
@@ -175,6 +175,16 @@ isc_result_t
 dst_context_create2(dst_key_t *key, isc_mem_t *mctx,
 		    isc_logcategory_t *category, dst_context_t **dctxp);
 
+isc_result_t
+dst_context_create3(dst_key_t *key, isc_mem_t *mctx,
+		    isc_logcategory_t *category, isc_boolean_t useforsigning,
+		    dst_context_t **dctxp);
+
+isc_result_t
+dst_context_create4(dst_key_t *key, isc_mem_t *mctx,
+		    isc_logcategory_t *category, isc_boolean_t useforsigning,
+		    int maxbits, dst_context_t **dctxp);
+
 /*%<
  * Creates a context to be used for a sign or verify operation.
  *
diff --git a/lib/dns/include/dst/result.h b/lib/dns/include/dst/result.h
index 00640a1..cf9428f 100644
--- a/lib/dns/include/dst/result.h
+++ b/lib/dns/include/dst/result.h
@@ -57,8 +57,9 @@
 #define DST_R_NORANDOMNESS		(ISC_RESULTCLASS_DST + 19)
 #define DST_R_BADKEYTYPE		(ISC_RESULTCLASS_DST + 20)
 #define DST_R_NOENGINE			(ISC_RESULTCLASS_DST + 21)
+#define DST_R_EXTERNALKEY		(ISC_RESULTCLASS_DST + 22)
 
-#define DST_R_NRESULTS			22	/* Number of results */
+#define DST_R_NRESULTS			23	/* Number of results */
 
 ISC_LANG_BEGINDECLS
 
diff --git a/lib/dns/openssldh_link.c b/lib/dns/openssldh_link.c
index 36b8a41..55752da 100644
--- a/lib/dns/openssldh_link.c
+++ b/lib/dns/openssldh_link.c
@@ -463,6 +463,9 @@ openssldh_tofile(const dst_key_t *key, const char *directory) {
 	if (key->keydata.dh == NULL)
 		return (DST_R_NULLKEY);
 
+	if (key->external)
+		return (DST_R_EXTERNALKEY);
+
 	dh = key->keydata.dh;
 
 	memset(bufs, 0, sizeof(bufs));
@@ -528,6 +531,9 @@ openssldh_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
 	if (ret != ISC_R_SUCCESS)
 		return (ret);
 
+	if (key->external)
+		DST_RET(DST_R_EXTERNALKEY);
+
 	dh = DH_new();
 	if (dh == NULL)
 		DST_RET(ISC_R_NOMEMORY);
@@ -630,6 +636,7 @@ openssldh_cleanup(void) {
 
 static dst_func_t openssldh_functions = {
 	NULL, /*%< createctx */
+	NULL, /*%< createctx2 */
 	NULL, /*%< destroyctx */
 	NULL, /*%< adddata */
 	NULL, /*%< openssldh_sign */
diff --git a/lib/dns/openssldsa_link.c b/lib/dns/openssldsa_link.c
index a24baae..fd6e91e 100644
--- a/lib/dns/openssldsa_link.c
+++ b/lib/dns/openssldsa_link.c
@@ -522,7 +522,7 @@ openssldsa_tofile(const dst_key_t *key, const char *directory) {
 
 	if (key->keydata.dsa == NULL)
 		return (DST_R_NULLKEY);
-	
+
 	if (key->external) {
 		priv.nelements = 0;
 		return (dst__privstruct_writefile(key, &priv, directory));
@@ -573,20 +573,31 @@ openssldsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
 	isc_mem_t *mctx = key->mctx;
 #define DST_RET(a) {ret = a; goto err;}
 
-	UNUSED(pub);
-
 	/* read private key file */
 	ret = dst__privstruct_parse(key, DST_ALG_DSA, lexer, mctx, &priv);
 	if (ret != ISC_R_SUCCESS)
 		return (ret);
 
+	if (key->external) {
+		if (priv.nelements != 0)
+			DST_RET(DST_R_INVALIDPRIVATEKEY);
+		if (pub == NULL)
+			DST_RET(DST_R_INVALIDPRIVATEKEY);
+		key->keydata.pkey = pub->keydata.pkey;
+		pub->keydata.pkey = NULL;
+		key->key_size = pub->key_size;
+		dst__privstruct_free(&priv, mctx);
+		memset(&priv, 0, sizeof(priv));
+		return (ISC_R_SUCCESS);
+	}
+
 	dsa = DSA_new();
 	if (dsa == NULL)
 		DST_RET(ISC_R_NOMEMORY);
 	dsa->flags &= ~DSA_FLAG_CACHE_MONT_P;
 	key->keydata.dsa = dsa;
 
-	for (i=0; i < priv.nelements; i++) {
+	for (i = 0; i < priv.nelements; i++) {
 		BIGNUM *bn;
 		bn = BN_bin2bn(priv.elements[i].data,
 			       priv.elements[i].length, NULL);
@@ -612,22 +623,8 @@ openssldsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
 		}
 	}
 	dst__privstruct_free(&priv, mctx);
-
-	if (key->external) {
-		if (pub == NULL)
-			DST_RET(DST_R_INVALIDPRIVATEKEY);
-		dsa->q = pub->keydata.dsa->q;
-		pub->keydata.dsa->q = NULL;
-		dsa->p = pub->keydata.dsa->p;
-		pub->keydata.dsa->p = NULL;
-		dsa->g = pub->keydata.dsa->g;
-		pub->keydata.dsa->g =  NULL;
-		dsa->pub_key = pub->keydata.dsa->pub_key;
-		pub->keydata.dsa->pub_key = NULL;
-	}
-
+	memset(&priv, 0, sizeof(priv));
 	key->key_size = BN_num_bits(dsa->p);
-
 	return (ISC_R_SUCCESS);
 
  err:
@@ -639,6 +636,7 @@ openssldsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
 
 static dst_func_t openssldsa_functions = {
 	openssldsa_createctx,
+	NULL, /*%< createctx2 */
 	openssldsa_destroyctx,
 	openssldsa_adddata,
 	openssldsa_sign,
diff --git a/lib/dns/opensslecdsa_link.c b/lib/dns/opensslecdsa_link.c
index 7eff9a0..c64cc55 100644
--- a/lib/dns/opensslecdsa_link.c
+++ b/lib/dns/opensslecdsa_link.c
@@ -18,7 +18,7 @@
 
 #include <config.h>
 
-#ifdef HAVE_OPENSSL_ECDSA
+#if defined(OPENSSL) && defined(HAVE_OPENSSL_ECDSA)
 
 #if !defined(HAVE_EVP_SHA256) || !defined(HAVE_EVP_SHA384)
 #error "ECDSA without EVP for SHA2?"
@@ -474,7 +474,7 @@ opensslecdsa_tofile(const dst_key_t *key, const char *directory) {
 	priv.elements[0].length = BN_num_bytes(privkey);
 	BN_bn2bin(privkey, buf);
 	priv.elements[0].data = buf;
-	priv.nelements = ECDSA_NTAGS;
+	priv.nelements = 1;
 	ret = dst__privstruct_writefile(key, &priv, directory);
 
  err:
@@ -519,60 +519,50 @@ static isc_result_t
 opensslecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
 	dst_private_t priv;
 	isc_result_t ret;
-	EVP_PKEY *pkey, *pubpkey;
-	EC_KEY *eckey = NULL, *pubeckey = NULL;
-	const EC_POINT *pubkey;
-	BIGNUM *privkey;
+	EVP_PKEY *pkey;
+	EC_KEY *eckey = NULL;
+	BIGNUM *privkey = NULL;
 	int group_nid;
 	isc_mem_t *mctx = key->mctx;
 
 	REQUIRE(key->key_alg == DST_ALG_ECDSA256 ||
 		key->key_alg == DST_ALG_ECDSA384);
 
-	if (key->key_alg == DST_ALG_ECDSA256)
-		group_nid = NID_X9_62_prime256v1;
-	else
-		group_nid = NID_secp384r1;
-
-	eckey = EC_KEY_new_by_curve_name(group_nid);
-	if (eckey == NULL)
-		return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
-
 	/* read private key file */
 	ret = dst__privstruct_parse(key, DST_ALG_ECDSA256, lexer, mctx, &priv);
 	if (ret != ISC_R_SUCCESS)
 		goto err;
 
 	if (key->external) {
-		/*
-		 * Copy the public key to this new key.
-		 */
-		if (pub == NULL)
-			DST_RET(DST_R_INVALIDPRIVATEKEY);
-		pubpkey = pub->keydata.pkey;
-		pubeckey = EVP_PKEY_get1_EC_KEY(pubpkey);
-		if (pubeckey == NULL)
-			DST_RET(DST_R_INVALIDPRIVATEKEY);
-		pubkey = EC_KEY_get0_public_key(pubeckey);
-		if (pubkey == NULL)
-			DST_RET(DST_R_INVALIDPRIVATEKEY);
-		if (EC_KEY_set_public_key(eckey, pubkey) != 1)
-			DST_RET(DST_R_INVALIDPRIVATEKEY);
-		if (EC_KEY_check_key(eckey) != 1)
+		if (priv.nelements != 0)
 			DST_RET(DST_R_INVALIDPRIVATEKEY);
-	} else {
-		privkey = BN_bin2bn(priv.elements[0].data,
-				    priv.elements[0].length, NULL);
-		if (privkey == NULL)
-			DST_RET(ISC_R_NOMEMORY);
-		if (!EC_KEY_set_private_key(eckey, privkey))
-			DST_RET(ISC_R_NOMEMORY);
-		if (ecdsa_check(eckey, pub) != ISC_R_SUCCESS)
+		if (pub == NULL)
 			DST_RET(DST_R_INVALIDPRIVATEKEY);
+		key->keydata.pkey = pub->keydata.pkey;
+		pub->keydata.pkey = NULL;
 		dst__privstruct_free(&priv, mctx);
 		memset(&priv, 0, sizeof(priv));
+		return (ISC_R_SUCCESS);
 	}
- 
+
+	if (key->key_alg == DST_ALG_ECDSA256)
+		group_nid = NID_X9_62_prime256v1;
+	else
+		group_nid = NID_secp384r1;
+
+	eckey = EC_KEY_new_by_curve_name(group_nid);
+	if (eckey == NULL)
+		return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+
+	privkey = BN_bin2bn(priv.elements[0].data,
+			    priv.elements[0].length, NULL);
+	if (privkey == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	if (!EC_KEY_set_private_key(eckey, privkey))
+		DST_RET(ISC_R_NOMEMORY);
+	if (ecdsa_check(eckey, pub) != ISC_R_SUCCESS)
+		DST_RET(DST_R_INVALIDPRIVATEKEY);
+
 	pkey = EVP_PKEY_new();
 	if (pkey == NULL)
 		DST_RET (ISC_R_NOMEMORY);
@@ -584,10 +574,10 @@ opensslecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
 	ret = ISC_R_SUCCESS;
 
  err:
+	if (privkey != NULL)
+		BN_clear_free(privkey);
 	if (eckey != NULL)
 		EC_KEY_free(eckey);
-	if (pubeckey != NULL)
-		EC_KEY_free(pubeckey);
 	dst__privstruct_free(&priv, mctx);
 	memset(&priv, 0, sizeof(priv));
 	return (ret);
@@ -595,6 +585,7 @@ opensslecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
 
 static dst_func_t opensslecdsa_functions = {
 	opensslecdsa_createctx,
+	NULL, /*%< createctx2 */
 	opensslecdsa_destroyctx,
 	opensslecdsa_adddata,
 	opensslecdsa_sign,
diff --git a/lib/dns/opensslgost_link.c b/lib/dns/opensslgost_link.c
index 325a7c0..9b4ff55 100644
--- a/lib/dns/opensslgost_link.c
+++ b/lib/dns/opensslgost_link.c
@@ -30,6 +30,7 @@
 #include "dst_internal.h"
 #include "dst_openssl.h"
 #include "dst_parse.h"
+#include "dst_gost.h"
 
 #include <openssl/err.h>
 #include <openssl/objects.h>
@@ -44,6 +45,60 @@ const EVP_MD *EVP_gost(void) {
 	return (opensslgost_digest);
 }
 
+/* ISC methods */
+
+isc_result_t
+isc_gost_init(isc_gost_t *ctx) {
+	const EVP_MD *md;
+	int ret;
+
+	INSIST(ctx != NULL);
+
+	md = EVP_gost();
+	if (md == NULL)
+		return (DST_R_CRYPTOFAILURE);
+	EVP_MD_CTX_init(ctx);
+	ret = EVP_DigestInit(ctx, md);
+	if (ret != 1)
+		return (DST_R_CRYPTOFAILURE);
+	return (ISC_R_SUCCESS);
+}
+
+void
+isc_gost_invalidate(isc_gost_t *ctx) {
+	EVP_MD_CTX_cleanup(ctx);
+}
+
+isc_result_t
+isc_gost_update(isc_gost_t *ctx, const unsigned char *data,
+		unsigned int len)
+{
+	int ret;
+
+	INSIST(ctx != NULL);
+	INSIST(data != NULL);
+
+	ret = EVP_DigestUpdate(ctx, (const void *) data, (size_t) len);
+	if (ret != 1)
+		return (DST_R_CRYPTOFAILURE);
+	return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_gost_final(isc_gost_t *ctx, unsigned char *digest) {
+	int ret;
+
+	INSIST(ctx != NULL);
+	INSIST(digest != NULL);
+
+	ret = EVP_DigestFinal(ctx, digest, NULL);
+	if (ret != 1)
+		return (DST_R_CRYPTOFAILURE);
+	return (ISC_R_SUCCESS);
+}
+
+/* DST methods */
+
 #define DST_RET(a) {ret = a; goto err;}
 
 static isc_result_t opensslgost_todns(const dst_key_t *key,
@@ -285,6 +340,8 @@ opensslgost_fromdns(dst_key_t *key, isc_buffer_t *data) {
 	return (ISC_R_SUCCESS);
 }
 
+#ifdef PREFER_GOSTASN1
+
 static isc_result_t
 opensslgost_tofile(const dst_key_t *key, const char *directory) {
 	EVP_PKEY *pkey;
@@ -318,7 +375,7 @@ opensslgost_tofile(const dst_key_t *key, const char *directory) {
 	priv.elements[0].tag = TAG_GOST_PRIVASN1;
 	priv.elements[0].length = len;
 	priv.elements[0].data = der;
-	priv.nelements = GOST_NTAGS;
+	priv.nelements = 1;
 
 	result = dst__privstruct_writefile(key, &priv, directory);
  fail:
@@ -327,42 +384,146 @@ opensslgost_tofile(const dst_key_t *key, const char *directory) {
 	return (result);
 }
 
+#else
+
+static isc_result_t
+opensslgost_tofile(const dst_key_t *key, const char *directory) {
+	EVP_PKEY *pkey;
+	EC_KEY *eckey;
+	const BIGNUM *privkey;
+	dst_private_t priv;
+	isc_result_t ret;
+	unsigned char *buf = NULL;
+
+	if (key->keydata.pkey == NULL)
+		return (DST_R_NULLKEY);
+
+	if (key->external) {
+		priv.nelements = 0;
+		return (dst__privstruct_writefile(key, &priv, directory));
+	}
+
+	pkey = key->keydata.pkey;
+	eckey = EVP_PKEY_get0(pkey);
+	if (eckey == NULL)
+		return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+	privkey = EC_KEY_get0_private_key(eckey);
+	if (privkey == NULL)
+		return (ISC_R_FAILURE);
+
+	buf = isc_mem_get(key->mctx, BN_num_bytes(privkey));
+	if (buf == NULL)
+		return (ISC_R_NOMEMORY);
+
+	priv.elements[0].tag = TAG_GOST_PRIVRAW;
+	priv.elements[0].length = BN_num_bytes(privkey);
+	BN_bn2bin(privkey, buf);
+	priv.elements[0].data = buf;
+	priv.nelements = 1;
+
+	ret = dst__privstruct_writefile(key, &priv, directory);
+
+	if (buf != NULL)
+		isc_mem_put(key->mctx, buf, BN_num_bytes(privkey));
+	return (ret);
+}
+#endif
+
+static unsigned char gost_dummy_key[71] = {
+	0x30, 0x45, 0x02, 0x01, 0x00, 0x30, 0x1c, 0x06,
+	0x06, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x13, 0x30,
+	0x12, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02,
+	0x23, 0x01, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02,
+	0x02, 0x1e, 0x01, 0x04, 0x22, 0x02, 0x20, 0x1b,
+	0x3f, 0x94, 0xf7, 0x1a, 0x5f, 0x2f, 0xe7, 0xe5,
+	0x74, 0x0b, 0x8c, 0xd4, 0xb7, 0x18, 0xdd, 0x65,
+	0x68, 0x26, 0xd1, 0x54, 0xfb, 0x77, 0xba, 0x63,
+	0x72, 0xd9, 0xf0, 0x63, 0x87, 0xe0, 0xd6
+};
+
 static isc_result_t
 opensslgost_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
 	dst_private_t priv;
 	isc_result_t ret;
 	isc_mem_t *mctx = key->mctx;
 	EVP_PKEY *pkey = NULL;
+	EC_KEY *eckey;
+	const EC_POINT *pubkey = NULL;
+	BIGNUM *privkey = NULL;
 	const unsigned char *p;
 
-	UNUSED(pub);
-
 	/* read private key file */
 	ret = dst__privstruct_parse(key, DST_ALG_ECCGOST, lexer, mctx, &priv);
 	if (ret != ISC_R_SUCCESS)
 		return (ret);
 
 	if (key->external) {
-		INSIST(priv.nelements == 0);
+		if (priv.nelements != 0)
+			DST_RET(DST_R_INVALIDPRIVATEKEY);
 		if (pub == NULL)
 			DST_RET(DST_R_INVALIDPRIVATEKEY);
 		key->keydata.pkey = pub->keydata.pkey;
 		pub->keydata.pkey = NULL;
-	} else {
-		INSIST(priv.elements[0].tag == TAG_GOST_PRIVASN1);
+		key->key_size = pub->key_size;
+		dst__privstruct_free(&priv, mctx);
+		memset(&priv, 0, sizeof(priv));
+		return (ISC_R_SUCCESS);
+	}
+
+	INSIST((priv.elements[0].tag == TAG_GOST_PRIVASN1) ||
+	       (priv.elements[0].tag == TAG_GOST_PRIVRAW));
+
+	if (priv.elements[0].tag == TAG_GOST_PRIVASN1) {
 		p = priv.elements[0].data;
 		if (d2i_PrivateKey(NID_id_GostR3410_2001, &pkey, &p,
 				   (long) priv.elements[0].length) == NULL)
-			DST_RET(dst__openssl_toresult2("d2i_PrivateKey",
-						     DST_R_INVALIDPRIVATEKEY));
-		key->keydata.pkey = pkey;
+			DST_RET(dst__openssl_toresult2(
+					    "d2i_PrivateKey",
+					    DST_R_INVALIDPRIVATEKEY));
+	} else {
+		if ((pub != NULL) && (pub->keydata.pkey != NULL)) {
+			eckey = EVP_PKEY_get0(pub->keydata.pkey);
+			pubkey = EC_KEY_get0_public_key(eckey);
+		}
+
+		privkey = BN_bin2bn(priv.elements[0].data,
+				    priv.elements[0].length, NULL);
+		if (privkey == NULL)
+			DST_RET(ISC_R_NOMEMORY);
+
+		/* can't create directly the whole key */
+		p = gost_dummy_key;
+		if (d2i_PrivateKey(NID_id_GostR3410_2001, &pkey, &p,
+				   (long) sizeof(gost_dummy_key)) == NULL)
+			DST_RET(dst__openssl_toresult2(
+					    "d2i_PrivateKey",
+					    DST_R_INVALIDPRIVATEKEY));
+
+		eckey = EVP_PKEY_get0(pkey);
+		if (eckey == NULL)
+			return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+		if (!EC_KEY_set_private_key(eckey, privkey))
+			DST_RET(ISC_R_NOMEMORY);
+
+		/* have to (re)set the public key */
+#ifdef notyet
+		(void) gost2001_compute_public(eckey);
+#else
+		if ((pubkey != NULL) && !EC_KEY_set_public_key(eckey, pubkey))
+			DST_RET(ISC_R_NOMEMORY);
+#endif
+		BN_clear_free(privkey);
+		privkey = NULL;
 	}
+	key->keydata.pkey = pkey;
 	key->key_size = EVP_PKEY_bits(pkey);
 	dst__privstruct_free(&priv, mctx);
 	memset(&priv, 0, sizeof(priv));
 	return (ISC_R_SUCCESS);
 
  err:
+	if (privkey != NULL)
+		BN_clear_free(privkey);
 	if (pkey != NULL)
 		EVP_PKEY_free(pkey);
 	opensslgost_destroy(key);
@@ -382,6 +543,7 @@ opensslgost_cleanup(void) {
 
 static dst_func_t opensslgost_functions = {
 	opensslgost_createctx,
+	NULL, /*%< createctx2 */
 	opensslgost_destroyctx,
 	opensslgost_adddata,
 	opensslgost_sign,
diff --git a/lib/dns/opensslrsa_link.c b/lib/dns/opensslrsa_link.c
index 894c7ae..1edeb8d 100644
--- a/lib/dns/opensslrsa_link.c
+++ b/lib/dns/opensslrsa_link.c
@@ -1196,6 +1196,24 @@ opensslrsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
 	EVP_PKEY *pkey = NULL;
 #endif
 
+	/* read private key file */
+	ret = dst__privstruct_parse(key, DST_ALG_RSA, lexer, mctx, &priv);
+	if (ret != ISC_R_SUCCESS)
+		goto err;
+
+	if (key->external) {
+		if (priv.nelements != 0)
+			DST_RET(DST_R_INVALIDPRIVATEKEY);
+		if (pub == NULL)
+			DST_RET(DST_R_INVALIDPRIVATEKEY);
+		key->keydata.pkey = pub->keydata.pkey;
+		pub->keydata.pkey = NULL;
+		key->key_size = pub->key_size;
+		dst__privstruct_free(&priv, mctx);
+		memset(&priv, 0, sizeof(priv));
+		return (ISC_R_SUCCESS);
+	}
+
 #if USE_EVP
 	if (pub != NULL && pub->keydata.pkey != NULL)
 		pubrsa = EVP_PKEY_get1_RSA(pub->keydata.pkey);
@@ -1206,14 +1224,6 @@ opensslrsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
 	}
 #endif
 
-	/* read private key file */
-	ret = dst__privstruct_parse(key, DST_ALG_RSA, lexer, mctx, &priv);
-	if (ret != ISC_R_SUCCESS)
-		goto err;
-
-	if (key->external && priv.nelements != 0)
-		DST_RET(DST_R_INVALIDPRIVATEKEY);
-
 	for (i = 0; i < priv.nelements; i++) {
 		switch (priv.elements[i].tag) {
 		case TAG_RSA_ENGINE:
@@ -1297,8 +1307,6 @@ opensslrsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
 			continue;
 		case TAG_RSA_LABEL:
 			continue;
-		case TAG_RSA_PIN:
-			continue;
 		default:
 			bn = BN_bin2bn(priv.elements[i].data,
 				       priv.elements[i].length, NULL);
@@ -1338,10 +1346,8 @@ opensslrsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
 
 	if (rsa_check(rsa, pubrsa) != ISC_R_SUCCESS)
 		DST_RET(DST_R_INVALIDPRIVATEKEY);
-	if (!key->external) {
-		if (BN_num_bits(rsa->e) > RSA_MAX_PUBEXP_BITS)
-			DST_RET(ISC_R_RANGE);
-	}
+	if (BN_num_bits(rsa->e) > RSA_MAX_PUBEXP_BITS)
+		DST_RET(ISC_R_RANGE);
 	key->key_size = BN_num_bits(rsa->n);
 	if (pubrsa != NULL)
 		RSA_free(pubrsa);
@@ -1448,6 +1454,7 @@ opensslrsa_fromlabel(dst_key_t *key, const char *engine, const char *label,
 
 static dst_func_t opensslrsa_functions = {
 	opensslrsa_createctx,
+	NULL, /*%< createctx2 */
 	opensslrsa_destroyctx,
 	opensslrsa_adddata,
 	opensslrsa_sign,
diff --git a/lib/dns/pkcs11.c b/lib/dns/pkcs11.c
new file mode 100644
index 0000000..7aa15fa
--- /dev/null
+++ b/lib/dns/pkcs11.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifdef PKCS11CRYPTO
+
+#include <config.h>
+
+#include <dns/log.h>
+#include <dns/result.h>
+
+#include <pk11/pk11.h>
+#include <pk11/internal.h>
+
+#include "dst_pkcs11.h"
+
+isc_result_t
+dst__pkcs11_toresult(const char *funcname, const char *file, int line,
+		     isc_result_t fallback, CK_RV rv)
+{
+	isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+		      DNS_LOGMODULE_CRYPTO, ISC_LOG_WARNING,
+		      "%s:%d: %s: Error = 0x%.8lX\n",
+		      file, line, funcname, rv);
+	if (rv == CKR_HOST_MEMORY)
+		return (ISC_R_NOMEMORY);
+	return (fallback);
+}
+
+
+#else /* PKCS11CRYPTO */
+
+#include <isc/util.h>
+
+EMPTY_TRANSLATION_UNIT
+
+#endif /* PKCS11CRYPTO */
+/*! \file */
diff --git a/lib/dns/pkcs11dh_link.c b/lib/dns/pkcs11dh_link.c
new file mode 100644
index 0000000..87afc02
--- /dev/null
+++ b/lib/dns/pkcs11dh_link.c
@@ -0,0 +1,1140 @@
+/*
+ * Portions Copyright (C) 20012  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Portions Copyright (C) 1995-2000 by Network Associates, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id$ */
+
+#ifdef PKCS11CRYPTO
+
+#include <config.h>
+
+#include <ctype.h>
+
+#include <isc/mem.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dst/result.h>
+
+#include "dst_internal.h"
+#include "dst_parse.h"
+#include "dst_pkcs11.h"
+
+#include <pk11/pk11.h>
+#include <pk11/internal.h>
+#define WANT_DH_PRIMES
+#include <pk11/constants.h>
+
+#include <pkcs11/pkcs11.h>
+
+/*
+ * PKCS#3 DH keys:
+ *  mechanisms:
+ *    CKM_DH_PKCS_PARAMETER_GEN,
+ *    CKM_DH_PKCS_KEY_PAIR_GEN,
+ *    CKM_DH_PKCS_DERIVE
+ *  domain parameters:
+ *    object class CKO_DOMAIN_PARAMETERS
+ *    key type CKK_DH
+ *    attribute CKA_PRIME (prime p)
+ *    attribute CKA_BASE (base g)
+ *    optional attribute CKA_PRIME_BITS (p length in bits)
+ *  public key:
+ *    object class CKO_PUBLIC_KEY
+ *    key type CKK_DH
+ *    attribute CKA_PRIME (prime p)
+ *    attribute CKA_BASE (base g)
+ *    attribute CKA_VALUE (public value y)
+ *  private key:
+ *    object class CKO_PRIVATE_KEY
+ *    key type CKK_DH
+ *    attribute CKA_PRIME (prime p)
+ *    attribute CKA_BASE (base g)
+ *    attribute CKA_VALUE (private value x)
+ *    optional attribute CKA_VALUE_BITS (x length in bits)
+ *  reuse CKA_PRIVATE_EXPONENT for key pair private value
+ */
+
+#define CKA_VALUE2	CKA_PRIVATE_EXPONENT
+
+static CK_BBOOL truevalue = TRUE;
+static CK_BBOOL falsevalue = FALSE;
+
+#define DST_RET(a) {ret = a; goto err;}
+
+static void pkcs11dh_destroy(dst_key_t *key);
+static isc_result_t pkcs11dh_todns(const dst_key_t *key, isc_buffer_t *data);
+
+static isc_result_t
+pkcs11dh_loadpriv(const dst_key_t *key,
+		  CK_SESSION_HANDLE session,
+		  CK_OBJECT_HANDLE *hKey)
+{
+	CK_RV rv;
+	CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
+	CK_KEY_TYPE keyType = CKK_DH;
+	CK_ATTRIBUTE keyTemplate[] =
+	{
+		{ CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_DERIVE, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_PRIME, NULL, 0 },
+		{ CKA_BASE, NULL, 0 },
+		{ CKA_VALUE, NULL, 0 }
+	};
+	CK_ATTRIBUTE *attr;
+	const pk11_object_t *priv;
+	isc_result_t ret;
+	unsigned int i;
+
+	priv = key->keydata.pkey;
+	if ((priv->object != CK_INVALID_HANDLE) && priv->ontoken) {
+		*hKey = priv->object;
+		return (ISC_R_SUCCESS);
+	}
+
+	attr = pk11_attribute_bytype(priv, CKA_PRIME);
+	if (attr == NULL)
+		return (DST_R_INVALIDPRIVATEKEY);
+	keyTemplate[6].pValue = isc_mem_get(key->mctx, attr->ulValueLen);
+	if (keyTemplate[6].pValue == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memcpy(keyTemplate[6].pValue, attr->pValue, attr->ulValueLen);
+	keyTemplate[6].ulValueLen = attr->ulValueLen;
+
+	attr = pk11_attribute_bytype(priv, CKA_BASE);
+	if (attr == NULL)
+		DST_RET(DST_R_INVALIDPRIVATEKEY);
+	keyTemplate[7].pValue = isc_mem_get(key->mctx, attr->ulValueLen);
+	if (keyTemplate[7].pValue == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memcpy(keyTemplate[7].pValue, attr->pValue, attr->ulValueLen);
+	keyTemplate[7].ulValueLen = attr->ulValueLen;
+
+	attr = pk11_attribute_bytype(priv, CKA_VALUE2);
+	if (attr == NULL)
+		DST_RET(DST_R_INVALIDPRIVATEKEY);
+	keyTemplate[8].pValue = isc_mem_get(key->mctx, attr->ulValueLen);
+	if (keyTemplate[8].pValue == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memcpy(keyTemplate[8].pValue, attr->pValue, attr->ulValueLen);
+	keyTemplate[8].ulValueLen = attr->ulValueLen;
+
+	PK11_CALL(pkcs_C_CreateObject,
+		  (session, keyTemplate, (CK_ULONG) 9, hKey),
+		  DST_R_COMPUTESECRETFAILURE);
+	if (rv == CKR_OK)
+		ret = ISC_R_SUCCESS;
+
+    err:
+	for (i = 6; i <= 8; i++)
+		if (keyTemplate[i].pValue != NULL) {
+			memset(keyTemplate[i].pValue, 0,
+			       keyTemplate[i].ulValueLen);
+			isc_mem_put(key->mctx,
+				    keyTemplate[i].pValue,
+				    keyTemplate[i].ulValueLen);
+		}
+	return (ret);
+}
+
+static isc_result_t
+pkcs11dh_computesecret(const dst_key_t *pub, const dst_key_t *priv,
+		       isc_buffer_t *secret)
+{
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_DH_PKCS_DERIVE, NULL, 0 };
+	CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
+	CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
+	CK_OBJECT_HANDLE hDerived = CK_INVALID_HANDLE;
+	CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE;
+	CK_ATTRIBUTE *attr;
+	CK_ULONG secLen;
+	CK_ATTRIBUTE keyTemplate[] =
+	{
+		{ CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_EXTRACTABLE, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_VALUE_LEN, &secLen, (CK_ULONG) sizeof(secLen) }
+	};
+	CK_ATTRIBUTE valTemplate[] =
+	{
+		{ CKA_VALUE, NULL, 0 }
+	};
+	CK_BYTE *secValue;
+	pk11_context_t ctx;
+	isc_result_t ret;
+	unsigned int i;
+	isc_region_t r;
+
+	REQUIRE(pub->keydata.pkey != NULL);
+	REQUIRE(priv->keydata.pkey != NULL);
+	REQUIRE(priv->keydata.pkey->repr != NULL);
+	attr = pk11_attribute_bytype(pub->keydata.pkey, CKA_PRIME);
+	if (attr == NULL)
+		return (DST_R_INVALIDPUBLICKEY);
+	REQUIRE(attr != NULL);
+	secLen = attr->ulValueLen;
+	attr = pk11_attribute_bytype(pub->keydata.pkey, CKA_VALUE);
+	if (attr == NULL)
+		return (DST_R_INVALIDPUBLICKEY);
+
+	ret = pk11_get_session(&ctx, OP_DH, ISC_TRUE, ISC_FALSE, ISC_FALSE,
+			       NULL, pk11_get_best_token(OP_DH));
+	if (ret != ISC_R_SUCCESS)
+		return (ret);
+
+	mech.ulParameterLen = attr->ulValueLen;
+	mech.pParameter = isc_mem_get(pub->mctx, mech.ulParameterLen);
+	if (mech.pParameter == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memcpy(mech.pParameter, attr->pValue, mech.ulParameterLen);
+
+	ret = pkcs11dh_loadpriv(priv, ctx.session, &hKey);
+	if (ret != ISC_R_SUCCESS)
+		goto err;
+
+	PK11_RET(pkcs_C_DeriveKey,
+		 (ctx.session, &mech, hKey,
+		  keyTemplate, (CK_ULONG) 6, &hDerived),
+		 DST_R_COMPUTESECRETFAILURE);
+
+	attr = valTemplate;
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (ctx.session, hDerived, attr, (CK_ULONG) 1),
+		 DST_R_CRYPTOFAILURE);
+	attr->pValue = isc_mem_get(pub->mctx, attr->ulValueLen);
+	if (attr->pValue == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(attr->pValue, 0, attr->ulValueLen);
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (ctx.session, hDerived, attr, (CK_ULONG) 1),
+		 DST_R_CRYPTOFAILURE);
+
+	/* strip leading zeros */
+	secValue = (CK_BYTE_PTR) attr->pValue;
+	for (i = 0; i < attr->ulValueLen; i++)
+		if (secValue[i] != 0)
+			break;
+	isc_buffer_availableregion(secret, &r);
+	if (r.length < attr->ulValueLen - i)
+		DST_RET(ISC_R_NOSPACE);
+	memcpy(r.base, secValue + i, attr->ulValueLen - i);
+	isc_buffer_add(secret, attr->ulValueLen - i);
+	ret = ISC_R_SUCCESS;
+
+    err:
+	if (hDerived != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(ctx.session, hDerived);
+	if (valTemplate[0].pValue != NULL) {
+		memset(valTemplate[0].pValue, 0, valTemplate[0].ulValueLen);
+		isc_mem_put(pub->mctx,
+			    valTemplate[0].pValue,
+			    valTemplate[0].ulValueLen);
+	}
+	if ((hKey != CK_INVALID_HANDLE) && !priv->keydata.pkey->ontoken)
+		(void) pkcs_C_DestroyObject(ctx.session, hKey);
+	if (mech.pParameter != NULL) {
+		memset(mech.pParameter, 0, mech.ulParameterLen);
+		isc_mem_put(pub->mctx, mech.pParameter, mech.ulParameterLen);
+	}
+	pk11_return_session(&ctx);
+	return (ret);
+}
+
+static isc_boolean_t
+pkcs11dh_compare(const dst_key_t *key1, const dst_key_t *key2) {
+	pk11_object_t *dh1, *dh2;
+	CK_ATTRIBUTE *attr1, *attr2;
+
+	dh1 = key1->keydata.pkey;
+	dh2 = key2->keydata.pkey;
+
+	if ((dh1 == NULL) && (dh2 == NULL))
+		return (ISC_TRUE);
+	else if ((dh1 == NULL) || (dh2 == NULL))
+		return (ISC_FALSE);
+
+	attr1 = pk11_attribute_bytype(dh1, CKA_PRIME);
+	attr2 = pk11_attribute_bytype(dh2, CKA_PRIME);
+	if ((attr1 == NULL) && (attr2 == NULL))
+		return (ISC_TRUE);
+	else if ((attr1 == NULL) || (attr2 == NULL) ||
+		 (attr1->ulValueLen != attr2->ulValueLen) ||
+		 memcmp(attr1->pValue, attr2->pValue, attr1->ulValueLen))
+		return (ISC_FALSE);
+
+	attr1 = pk11_attribute_bytype(dh1, CKA_BASE);
+	attr2 = pk11_attribute_bytype(dh2, CKA_BASE);
+	if ((attr1 == NULL) && (attr2 == NULL))
+		return (ISC_TRUE);
+	else if ((attr1 == NULL) || (attr2 == NULL) ||
+		 (attr1->ulValueLen != attr2->ulValueLen) ||
+		 memcmp(attr1->pValue, attr2->pValue, attr1->ulValueLen))
+		return (ISC_FALSE);
+
+	attr1 = pk11_attribute_bytype(dh1, CKA_VALUE);
+	attr2 = pk11_attribute_bytype(dh2, CKA_VALUE);
+	if ((attr1 == NULL) && (attr2 == NULL))
+		return (ISC_TRUE);
+	else if ((attr1 == NULL) || (attr2 == NULL) ||
+		 (attr1->ulValueLen != attr2->ulValueLen) ||
+		 memcmp(attr1->pValue, attr2->pValue, attr1->ulValueLen))
+		return (ISC_FALSE);
+
+	attr1 = pk11_attribute_bytype(dh1, CKA_VALUE2);
+	attr2 = pk11_attribute_bytype(dh2, CKA_VALUE2);
+	if (((attr1 != NULL) || (attr2 != NULL)) &&
+	    ((attr1 == NULL) || (attr2 == NULL) ||
+	     (attr1->ulValueLen != attr2->ulValueLen) ||
+	     memcmp(attr1->pValue, attr2->pValue, attr1->ulValueLen)))
+		return (ISC_FALSE);
+
+	if (!dh1->ontoken && !dh2->ontoken)
+		return (ISC_TRUE);
+	else if (dh1->ontoken || dh2->ontoken ||
+		 (dh1->object != dh2->object))
+		return (ISC_FALSE);
+
+	return (ISC_TRUE);
+}
+
+static isc_boolean_t
+pkcs11dh_paramcompare(const dst_key_t *key1, const dst_key_t *key2) {
+	pk11_object_t *dh1, *dh2;
+	CK_ATTRIBUTE *attr1, *attr2;
+
+	dh1 = key1->keydata.pkey;
+	dh2 = key2->keydata.pkey;
+
+	if ((dh1 == NULL) && (dh2 == NULL))
+		return (ISC_TRUE);
+	else if ((dh1 == NULL) || (dh2 == NULL))
+		return (ISC_FALSE);
+
+	attr1 = pk11_attribute_bytype(dh1, CKA_PRIME);
+	attr2 = pk11_attribute_bytype(dh2, CKA_PRIME);
+	if ((attr1 == NULL) && (attr2 == NULL))
+		return (ISC_TRUE);
+	else if ((attr1 == NULL) || (attr2 == NULL) ||
+		 (attr1->ulValueLen != attr2->ulValueLen) ||
+		 memcmp(attr1->pValue, attr2->pValue, attr1->ulValueLen))
+		return (ISC_FALSE);
+
+	attr1 = pk11_attribute_bytype(dh1, CKA_BASE);
+	attr2 = pk11_attribute_bytype(dh2, CKA_BASE);
+	if ((attr1 == NULL) && (attr2 == NULL))
+		return (ISC_TRUE);
+	else if ((attr1 == NULL) || (attr2 == NULL) ||
+		 (attr1->ulValueLen != attr2->ulValueLen) ||
+		 memcmp(attr1->pValue, attr2->pValue, attr1->ulValueLen))
+		return (ISC_FALSE);
+
+	return (ISC_TRUE);
+}
+
+static isc_result_t
+pkcs11dh_generate(dst_key_t *key, int generator, void (*callback)(int)) {
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_DH_PKCS_PARAMETER_GEN, NULL, 0 };
+	CK_OBJECT_HANDLE domainparams = CK_INVALID_HANDLE;
+	CK_OBJECT_CLASS dClass = CKO_DOMAIN_PARAMETERS;
+	CK_KEY_TYPE keyType = CKK_DH;
+	CK_ULONG bits = 0;
+	CK_ATTRIBUTE dTemplate[] =
+	{
+		{ CKA_CLASS, &dClass, (CK_ULONG) sizeof(dClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIME_BITS, &bits, (CK_ULONG) sizeof(bits) }
+	};
+	CK_ATTRIBUTE pTemplate[] =
+	{
+		{ CKA_PRIME, NULL, 0 },
+		{ CKA_BASE, NULL, 0 }
+	};
+	CK_OBJECT_HANDLE pub = CK_INVALID_HANDLE;
+	CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY;
+	CK_ATTRIBUTE pubTemplate[] =
+	{
+		{ CKA_CLASS, &pubClass, (CK_ULONG) sizeof(pubClass) },
+		{ CKA_KEY_TYPE,&keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIME, NULL, 0 },
+		{ CKA_BASE, NULL, 0 },
+	};
+	CK_OBJECT_HANDLE priv = CK_INVALID_HANDLE;
+	CK_OBJECT_HANDLE privClass = CKO_PRIVATE_KEY;
+	CK_ATTRIBUTE privTemplate[] =
+	{
+		{ CKA_CLASS, &privClass, (CK_ULONG) sizeof(privClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_EXTRACTABLE, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_DERIVE, &truevalue, (CK_ULONG) sizeof(truevalue) },
+	};
+	CK_ATTRIBUTE *attr;
+	pk11_object_t *dh = NULL;
+	pk11_context_t *pk11_ctx;
+	isc_result_t ret;
+
+	UNUSED(callback);
+
+	pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx,
+						  sizeof(*pk11_ctx));
+	if (pk11_ctx == NULL)
+		return (ISC_R_NOMEMORY);
+	ret = pk11_get_session(pk11_ctx, OP_DH, ISC_TRUE, ISC_FALSE,
+			       ISC_FALSE, NULL, pk11_get_best_token(OP_DH));
+	if (ret != ISC_R_SUCCESS)
+		goto err;
+
+	bits = key->key_size;
+	if ((generator == 0) &&
+	    ((bits == 768) || (bits == 1024) || (bits == 1536))) {
+		if (bits == 768) {
+			pubTemplate[4].pValue =
+				isc_mem_get(key->mctx, sizeof(pk11_dh_bn768));
+			if (pubTemplate[4].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(pubTemplate[4].pValue,
+			       pk11_dh_bn768, sizeof(pk11_dh_bn768));
+			pubTemplate[4].ulValueLen = sizeof(pk11_dh_bn768);
+		} else if (bits == 1024) {
+			pubTemplate[4].pValue =
+				isc_mem_get(key->mctx, sizeof(pk11_dh_bn1024));
+			if (pubTemplate[4].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(pubTemplate[4].pValue,
+			       pk11_dh_bn1024, sizeof(pk11_dh_bn1024));
+			pubTemplate[4].ulValueLen = sizeof(pk11_dh_bn1024);
+		} else {
+			pubTemplate[4].pValue =
+				isc_mem_get(key->mctx, sizeof(pk11_dh_bn1536));
+			if (pubTemplate[4].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(pubTemplate[4].pValue,
+			       pk11_dh_bn1536, sizeof(pk11_dh_bn1536));
+			pubTemplate[4].ulValueLen = sizeof(pk11_dh_bn1536);
+		}
+		pubTemplate[5].pValue = isc_mem_get(key->mctx,
+						    sizeof(pk11_dh_bn2));
+		if (pubTemplate[5].pValue == NULL)
+			DST_RET(ISC_R_NOMEMORY);
+		memcpy(pubTemplate[5].pValue, pk11_dh_bn2, sizeof(pk11_dh_bn2));
+		pubTemplate[5].ulValueLen = sizeof(pk11_dh_bn2);
+	} else {
+		PK11_RET(pkcs_C_GenerateKey,
+			 (pk11_ctx->session, &mech,
+			  dTemplate, (CK_ULONG) 5, &domainparams),
+			 DST_R_CRYPTOFAILURE);
+		PK11_RET(pkcs_C_GetAttributeValue,
+			 (pk11_ctx->session, domainparams,
+			  pTemplate, (CK_ULONG) 2),
+			 DST_R_CRYPTOFAILURE);
+		pTemplate[0].pValue = isc_mem_get(key->mctx,
+						  pTemplate[0].ulValueLen);
+		if (pTemplate[0].pValue == NULL)
+			DST_RET(ISC_R_NOMEMORY);
+		memset(pTemplate[0].pValue, 0, pTemplate[0].ulValueLen);
+		pTemplate[1].pValue = isc_mem_get(key->mctx,
+						  pTemplate[1].ulValueLen);
+		if (pTemplate[1].pValue == NULL)
+			DST_RET(ISC_R_NOMEMORY);
+		memset(pTemplate[1].pValue, 0, pTemplate[1].ulValueLen);
+		PK11_RET(pkcs_C_GetAttributeValue,
+			 (pk11_ctx->session, domainparams,
+			  pTemplate, (CK_ULONG) 2),
+			 DST_R_CRYPTOFAILURE);
+
+		pubTemplate[4].pValue = pTemplate[0].pValue;
+		pubTemplate[4].ulValueLen = pTemplate[0].ulValueLen;
+		pTemplate[0].pValue = NULL;
+		pubTemplate[5].pValue = pTemplate[1].pValue;
+		pubTemplate[5].ulValueLen = pTemplate[1].ulValueLen;
+		pTemplate[1].pValue = NULL;
+	}
+
+	mech.mechanism = CKM_DH_PKCS_KEY_PAIR_GEN;
+	PK11_RET(pkcs_C_GenerateKeyPair,
+		 (pk11_ctx->session, &mech,
+		  pubTemplate, (CK_ULONG) 6,
+		  privTemplate, (CK_ULONG) 7,
+		  &pub, &priv),
+		 DST_R_CRYPTOFAILURE);
+
+	dh = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*dh));
+	if (dh == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(dh, 0,  sizeof(*dh));
+	key->keydata.pkey = dh;
+	dh->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 4);
+	if (dh->repr == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(dh->repr, 0, sizeof(*attr) * 4);
+	dh->attrcnt = 4;
+
+	attr = dh->repr;
+	attr[0].type = CKA_PRIME;
+	attr[0].pValue = pubTemplate[4].pValue;
+	attr[0].ulValueLen = pubTemplate[4].ulValueLen;
+	pubTemplate[4].pValue = NULL;
+
+	attr[1].type = CKA_BASE;
+	attr[1].pValue = pubTemplate[5].pValue;
+	attr[1].ulValueLen = pubTemplate[5].ulValueLen;
+	pubTemplate[5].pValue =NULL;
+
+	attr += 2;
+	attr->type = CKA_VALUE;
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, pub, attr, 1),
+		 DST_R_CRYPTOFAILURE);
+	attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen);
+	if (attr->pValue == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(attr->pValue, 0, attr->ulValueLen);
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, pub, attr, 1),
+		 DST_R_CRYPTOFAILURE);
+
+	attr++;
+	attr->type = CKA_VALUE;
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, priv, attr, 1),
+		 DST_R_CRYPTOFAILURE);
+	attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen);
+	if (attr->pValue == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(attr->pValue, 0, attr->ulValueLen);
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, priv, attr, 1),
+		 DST_R_CRYPTOFAILURE);
+	attr->type = CKA_VALUE2;
+
+	(void) pkcs_C_DestroyObject(pk11_ctx->session, priv);
+	(void) pkcs_C_DestroyObject(pk11_ctx->session, pub);
+	(void) pkcs_C_DestroyObject(pk11_ctx->session, domainparams);
+	pk11_return_session(pk11_ctx);
+	memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+	isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+	return (ISC_R_SUCCESS);
+
+    err:
+	pkcs11dh_destroy(key);
+	if (priv != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(pk11_ctx->session, priv);
+	if (pub != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(pk11_ctx->session, pub);
+	if (domainparams != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(pk11_ctx->session, domainparams);
+
+	if (pubTemplate[4].pValue != NULL) {
+		memset(pubTemplate[4].pValue, 0, pubTemplate[4].ulValueLen);
+		isc_mem_put(key->mctx,
+			    pubTemplate[4].pValue,
+			    pubTemplate[4].ulValueLen);
+	}
+	if (pubTemplate[5].pValue != NULL) {
+		memset(pubTemplate[5].pValue, 0, pubTemplate[5].ulValueLen);
+		isc_mem_put(key->mctx,
+			    pubTemplate[5].pValue,
+			    pubTemplate[5].ulValueLen);
+	}
+	if (pTemplate[0].pValue != NULL) {
+		memset(pTemplate[0].pValue, 0, pTemplate[0].ulValueLen);
+		isc_mem_put(key->mctx,
+			    pTemplate[0].pValue,
+			    pTemplate[0].ulValueLen);
+	}
+	if (pTemplate[1].pValue != NULL) {
+		memset(pTemplate[1].pValue, 0, pTemplate[1].ulValueLen);
+		isc_mem_put(key->mctx,
+			    pTemplate[1].pValue,
+			    pTemplate[1].ulValueLen);
+	}
+
+	pk11_return_session(pk11_ctx);
+	memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+	isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+	return (ret);
+}
+
+static isc_boolean_t
+pkcs11dh_isprivate(const dst_key_t *key) {
+	pk11_object_t *dh = key->keydata.pkey;
+	CK_ATTRIBUTE *attr;
+
+	if (dh == NULL)
+		return (ISC_FALSE);
+	attr = pk11_attribute_bytype(dh, CKA_VALUE2);
+	return (ISC_TF((attr != NULL) || dh->ontoken));
+}
+
+static void
+pkcs11dh_destroy(dst_key_t *key) {
+	pk11_object_t *dh = key->keydata.pkey;
+	CK_ATTRIBUTE *attr;
+
+	if (dh == NULL)
+		return;
+
+	INSIST((dh->object == CK_INVALID_HANDLE) || dh->ontoken);
+
+	for (attr = pk11_attribute_first(dh);
+	     attr != NULL;
+	     attr = pk11_attribute_next(dh, attr))
+		switch (attr->type) {
+		case CKA_VALUE:
+		case CKA_VALUE2:
+		case CKA_PRIME:
+		case CKA_BASE:
+			if (attr->pValue != NULL) {
+				memset(attr->pValue, 0, attr->ulValueLen);
+				isc_mem_put(key->mctx,
+					    attr->pValue,
+					    attr->ulValueLen);
+			}
+			break;
+		}
+	if (dh->repr != NULL) {
+		memset(dh->repr, 0, dh->attrcnt * sizeof(*attr));
+		isc_mem_put(key->mctx, dh->repr, dh->attrcnt * sizeof(*attr));
+	}
+	memset(dh, 0, sizeof(*dh));
+	isc_mem_put(key->mctx, dh, sizeof(*dh));
+	key->keydata.pkey = NULL;
+}
+
+static void
+uint16_toregion(isc_uint16_t val, isc_region_t *region) {
+	*region->base++ = (val & 0xff00) >> 8;
+	*region->base++ = (val & 0x00ff);
+}
+
+static isc_uint16_t
+uint16_fromregion(isc_region_t *region) {
+	isc_uint16_t val;
+	unsigned char *cp = region->base;
+
+	val = ((unsigned int)(cp[0])) << 8;
+	val |= ((unsigned int)(cp[1]));
+
+	region->base += 2;
+	return (val);
+}
+
+static isc_result_t
+pkcs11dh_todns(const dst_key_t *key, isc_buffer_t *data) {
+	pk11_object_t *dh;
+	CK_ATTRIBUTE *attr;
+	isc_region_t r;
+	isc_uint16_t dnslen, plen = 0, glen = 0, publen = 0;
+	CK_BYTE *prime = NULL, *base = NULL, *pub = NULL;
+
+	REQUIRE(key->keydata.pkey != NULL);
+
+	dh = key->keydata.pkey;
+
+	for (attr = pk11_attribute_first(dh);
+	     attr != NULL;
+	     attr = pk11_attribute_next(dh, attr))
+		switch (attr->type) {
+		case CKA_VALUE:
+			pub = (CK_BYTE *) attr->pValue;
+			publen = (isc_uint16_t) attr->ulValueLen;
+			break;
+		case CKA_PRIME:
+			prime = (CK_BYTE *) attr->pValue;
+			plen = (isc_uint16_t) attr->ulValueLen;
+			break;
+		case CKA_BASE:
+			base = (CK_BYTE *) attr->pValue;
+			glen = (isc_uint16_t) attr->ulValueLen;
+			break;
+		}
+	REQUIRE((prime != NULL) && (base != NULL) && (pub != NULL));
+
+	isc_buffer_availableregion(data, &r);
+
+	if ((glen == 1) && (memcmp(pk11_dh_bn2, base, glen) == 0) &&
+	    (((plen == sizeof(pk11_dh_bn768)) &&
+	      (memcmp(pk11_dh_bn768, prime, plen) == 0)) ||
+	     ((plen == sizeof(pk11_dh_bn1024)) &&
+	      (memcmp(pk11_dh_bn1024, prime, plen) == 0)) ||
+	     ((plen == sizeof(pk11_dh_bn1536)) &&
+	      (memcmp(pk11_dh_bn1536, prime, plen) == 0)))) {
+		plen = 1;
+		glen = 0;
+	}
+
+	dnslen = plen + glen + publen + 6;
+	if (r.length < (unsigned int) dnslen)
+		return (ISC_R_NOSPACE);
+
+	uint16_toregion(plen, &r);
+	if (plen == 1) {
+		if (memcmp(pk11_dh_bn768, prime, sizeof(pk11_dh_bn768)) == 0)
+			*r.base = 1;
+		else if (memcmp(pk11_dh_bn1024, prime,
+				sizeof(pk11_dh_bn1024)) == 0)
+			*r.base = 2;
+		else
+			*r.base = 3;
+	}
+	else
+		memcpy(r.base, prime, plen);
+	r.base += plen;
+
+	uint16_toregion(glen, &r);
+	if (glen > 0)
+		memcpy(r.base, base, glen);
+	r.base += glen;
+
+	uint16_toregion(publen, &r);
+	memcpy(r.base, pub, publen);
+	r.base += publen;
+
+	isc_buffer_add(data, dnslen);
+
+	return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+pkcs11dh_fromdns(dst_key_t *key, isc_buffer_t *data) {
+	pk11_object_t *dh;
+	isc_region_t r;
+	isc_uint16_t plen, glen, plen_, glen_, publen;
+	CK_BYTE *prime = NULL, *base = NULL, *pub = NULL;
+	CK_ATTRIBUTE *attr;
+	int special = 0;
+
+	isc_buffer_remainingregion(data, &r);
+	if (r.length == 0)
+		return (ISC_R_SUCCESS);
+
+	dh = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*dh));
+	if (dh == NULL)
+		return (ISC_R_NOMEMORY);
+	memset(dh, 0, sizeof(*dh));
+
+	/*
+	 * Read the prime length.  1 & 2 are table entries, > 16 means a
+	 * prime follows, otherwise an error.
+	 */
+	if (r.length < 2) {
+		memset(dh, 0, sizeof(*dh));
+		isc_mem_put(key->mctx, dh, sizeof(*dh));
+		return (DST_R_INVALIDPUBLICKEY);
+	}
+	plen = uint16_fromregion(&r);
+	if (plen < 16 && plen != 1 && plen != 2) {
+		memset(dh, 0, sizeof(*dh));
+		isc_mem_put(key->mctx, dh, sizeof(*dh));
+		return (DST_R_INVALIDPUBLICKEY);
+	}
+	if (r.length < plen) {
+		memset(dh, 0, sizeof(*dh));
+		isc_mem_put(key->mctx, dh, sizeof(*dh));
+		return (DST_R_INVALIDPUBLICKEY);
+	}
+	plen_ = plen;
+	if (plen == 1 || plen == 2) {
+		if (plen == 1)
+			special = *r.base++;
+		else
+			special = uint16_fromregion(&r);
+		switch (special) {
+			case 1:
+				prime = pk11_dh_bn768;
+				plen_ = sizeof(pk11_dh_bn768);
+				break;
+			case 2:
+				prime = pk11_dh_bn1024;
+				plen_ = sizeof(pk11_dh_bn1024);
+				break;
+			case 3:
+				prime = pk11_dh_bn1536;
+				plen_ = sizeof(pk11_dh_bn1536);
+				break;
+			default:
+				memset(dh, 0, sizeof(*dh));
+				isc_mem_put(key->mctx, dh, sizeof(*dh));
+				return (DST_R_INVALIDPUBLICKEY);
+		}
+	}
+	else {
+		prime = r.base;
+		r.base += plen;
+	}
+
+	/*
+	 * Read the generator length.  This should be 0 if the prime was
+	 * special, but it might not be.  If it's 0 and the prime is not
+	 * special, we have a problem.
+	 */
+	if (r.length < 2) {
+		memset(dh, 0, sizeof(*dh));
+		isc_mem_put(key->mctx, dh, sizeof(*dh));
+		return (DST_R_INVALIDPUBLICKEY);
+	}
+	glen = uint16_fromregion(&r);
+	if (r.length < glen) {
+		memset(dh, 0, sizeof(*dh));
+		isc_mem_put(key->mctx, dh, sizeof(*dh));
+		return (DST_R_INVALIDPUBLICKEY);
+	}
+	glen_ = glen;
+	if (special != 0) {
+		if (glen == 0) {
+			base = pk11_dh_bn2;
+			glen_ = sizeof(pk11_dh_bn2);
+		}
+		else {
+			base = r.base;
+			if (memcmp(base, pk11_dh_bn2, glen) == 0) {
+				base = pk11_dh_bn2;
+				glen_ = sizeof(pk11_dh_bn2);
+			}
+			else {
+				memset(dh, 0, sizeof(*dh));
+				isc_mem_put(key->mctx, dh, sizeof(*dh));
+				return (DST_R_INVALIDPUBLICKEY);
+			}
+		}
+	}
+	else {
+		if (glen == 0) {
+			memset(dh, 0, sizeof(*dh));
+			isc_mem_put(key->mctx, dh, sizeof(*dh));
+			return (DST_R_INVALIDPUBLICKEY);
+		}
+		base = r.base;
+	}
+	r.base += glen;
+
+	if (r.length < 2) {
+		memset(dh, 0, sizeof(*dh));
+		isc_mem_put(key->mctx, dh, sizeof(*dh));
+		return (DST_R_INVALIDPUBLICKEY);
+	}
+	publen = uint16_fromregion(&r);
+	if (r.length < publen) {
+		memset(dh, 0, sizeof(*dh));
+		isc_mem_put(key->mctx, dh, sizeof(*dh));
+		return (DST_R_INVALIDPUBLICKEY);
+	}
+	pub = r.base;
+	r.base += publen;
+
+	key->key_size = pk11_numbits(prime, plen_);
+
+	dh->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 3);
+	if (dh->repr == NULL)
+		goto nomemory;
+	memset(dh->repr, 0, sizeof(*attr) * 3);
+	dh->attrcnt = 3;
+
+	attr = dh->repr;
+	attr[0].type = CKA_PRIME;
+	attr[0].pValue = isc_mem_get(key->mctx, plen_);
+	if (attr[0].pValue == NULL)
+		goto nomemory;
+	memcpy(attr[0].pValue, prime, plen_);
+	attr[0].ulValueLen = (CK_ULONG) plen_;
+
+	attr[1].type = CKA_BASE;
+	attr[1].pValue = isc_mem_get(key->mctx, glen_);
+	if (attr[1].pValue == NULL)
+		goto nomemory;
+	memcpy(attr[1].pValue, base, glen_);
+	attr[1].ulValueLen = (CK_ULONG) glen_;
+
+	attr[2].type = CKA_VALUE;
+	attr[2].pValue = isc_mem_get(key->mctx, publen);
+	if (attr[2].pValue == NULL)
+		goto nomemory;
+	memcpy(attr[2].pValue, pub, publen);
+	attr[2].ulValueLen = (CK_ULONG) publen;
+
+	isc_buffer_forward(data, plen + glen + publen + 6);
+
+	key->keydata.pkey = dh;
+
+	return (ISC_R_SUCCESS);
+
+    nomemory:
+	for (attr = pk11_attribute_first(dh);
+	     attr != NULL;
+	     attr = pk11_attribute_next(dh, attr))
+		switch (attr->type) {
+		case CKA_VALUE:
+		case CKA_PRIME:
+		case CKA_BASE:
+			if (attr->pValue != NULL) {
+				memset(attr->pValue, 0, attr->ulValueLen);
+				isc_mem_put(key->mctx,
+					    attr->pValue,
+					    attr->ulValueLen);
+			}
+			break;
+		}
+	if (dh->repr != NULL) {
+		memset(dh->repr, 0, dh->attrcnt * sizeof(*attr));
+		isc_mem_put(key->mctx, dh->repr, dh->attrcnt * sizeof(*attr));
+	}
+	memset(dh, 0, sizeof(*dh));
+	isc_mem_put(key->mctx, dh, sizeof(*dh));
+	return (ISC_R_NOMEMORY);
+}
+
+static isc_result_t
+pkcs11dh_tofile(const dst_key_t *key, const char *directory) {
+	int i;
+	pk11_object_t *dh;
+	CK_ATTRIBUTE *attr;
+	CK_ATTRIBUTE *prime = NULL, *base = NULL, *pub = NULL, *prv = NULL;
+	dst_private_t priv;
+	unsigned char *bufs[4];
+	isc_result_t result;
+
+	if (key->keydata.pkey == NULL)
+		return (DST_R_NULLKEY);
+
+	if (key->external)
+		return (DST_R_EXTERNALKEY);
+
+	dh = key->keydata.pkey;
+
+	for (attr = pk11_attribute_first(dh);
+	     attr != NULL;
+	     attr = pk11_attribute_next(dh, attr))
+		switch (attr->type) {
+		case CKA_VALUE:
+			pub = attr;
+			break;
+		case CKA_VALUE2:
+			prv = attr;
+			break;
+		case CKA_PRIME:
+			prime = attr;
+			break;
+		case CKA_BASE:
+			base = attr;
+			break;
+		}
+	if ((prime == NULL) || (base == NULL) ||
+	    (pub == NULL) || (prv == NULL))
+		return (DST_R_NULLKEY);
+
+	memset(bufs, 0, sizeof(bufs));
+	for (i = 0; i < 4; i++) {
+		bufs[i] = isc_mem_get(key->mctx, prime->ulValueLen);
+		if (bufs[i] == NULL) {
+			result = ISC_R_NOMEMORY;
+			goto fail;
+		}
+		memset(bufs[i], 0, prime->ulValueLen);
+	}
+
+	i = 0;
+
+	priv.elements[i].tag = TAG_DH_PRIME;
+	priv.elements[i].length = (unsigned short) prime->ulValueLen;
+	memcpy(bufs[i], prime->pValue, prime->ulValueLen);
+	priv.elements[i].data = bufs[i];
+	i++;
+
+	priv.elements[i].tag = TAG_DH_GENERATOR;
+	priv.elements[i].length = (unsigned short) base->ulValueLen;
+	memcpy(bufs[i], base->pValue, base->ulValueLen);
+	priv.elements[i].data = bufs[i];
+	i++;
+
+	priv.elements[i].tag = TAG_DH_PRIVATE;
+	priv.elements[i].length = (unsigned short) prv->ulValueLen;
+	memcpy(bufs[i], prv->pValue, prv->ulValueLen);
+	priv.elements[i].data = bufs[i];
+	i++;
+
+	priv.elements[i].tag = TAG_DH_PUBLIC;
+	priv.elements[i].length = (unsigned short) pub->ulValueLen;
+	memcpy(bufs[i], pub->pValue, pub->ulValueLen);
+	priv.elements[i].data = bufs[i];
+	i++;
+
+	priv.nelements = i;
+	result = dst__privstruct_writefile(key, &priv, directory);
+ fail:
+	for (i = 0; i < 4; i++) {
+		if (bufs[i] == NULL)
+			break;
+		memset(bufs[i], 0, prime->ulValueLen);
+		isc_mem_put(key->mctx, bufs[i], prime->ulValueLen);
+	}
+	return (result);
+}
+
+static isc_result_t
+pkcs11dh_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
+	dst_private_t priv;
+	isc_result_t ret;
+	int i;
+	pk11_object_t *dh = NULL;
+	CK_ATTRIBUTE *attr;
+	isc_mem_t *mctx;
+
+	UNUSED(pub);
+	mctx = key->mctx;
+
+	/* read private key file */
+	ret = dst__privstruct_parse(key, DST_ALG_DH, lexer, mctx, &priv);
+	if (ret != ISC_R_SUCCESS)
+		return (ret);
+
+	if (key->external)
+		DST_RET(DST_R_EXTERNALKEY);
+
+	dh = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*dh));
+	if (dh == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(dh, 0, sizeof(*dh));
+	key->keydata.pkey = dh;
+	dh->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 4);
+	if (dh->repr == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(dh->repr, 0, sizeof(*attr) * 4);
+	dh->attrcnt = 4;
+	attr = dh->repr;
+	attr[0].type = CKA_PRIME;
+	attr[1].type = CKA_BASE;
+	attr[2].type = CKA_VALUE;
+	attr[3].type = CKA_VALUE2;
+
+	for (i = 0; i < priv.nelements; i++) {
+		CK_BYTE *bn;
+
+		bn = isc_mem_get(key->mctx, priv.elements[i].length);
+		if (bn == NULL)
+			DST_RET(ISC_R_NOMEMORY);
+		memcpy(bn, priv.elements[i].data, priv.elements[i].length);
+
+		switch (priv.elements[i].tag) {
+			case TAG_DH_PRIME:
+				attr = pk11_attribute_bytype(dh, CKA_PRIME);
+				INSIST(attr != NULL);
+				attr->pValue = bn;
+				attr->ulValueLen = priv.elements[i].length;
+				break;
+			case TAG_DH_GENERATOR:
+				attr = pk11_attribute_bytype(dh, CKA_BASE);
+				INSIST(attr != NULL);
+				attr->pValue = bn;
+				attr->ulValueLen = priv.elements[i].length;
+				break;
+			case TAG_DH_PRIVATE:
+				attr = pk11_attribute_bytype(dh, CKA_VALUE2);
+				INSIST(attr != NULL);
+				attr->pValue = bn;
+				attr->ulValueLen = priv.elements[i].length;
+				break;
+			case TAG_DH_PUBLIC:
+				attr = pk11_attribute_bytype(dh, CKA_VALUE);
+				INSIST(attr != NULL);
+				attr->pValue = bn;
+				attr->ulValueLen = priv.elements[i].length;
+				break;
+		}
+	}
+	dst__privstruct_free(&priv, mctx);
+
+	attr = pk11_attribute_bytype(dh, CKA_PRIME);
+	INSIST(attr != NULL);
+	key->key_size = pk11_numbits(attr->pValue, attr->ulValueLen);
+
+	return (ISC_R_SUCCESS);
+
+ err:
+	pkcs11dh_destroy(key);
+	dst__privstruct_free(&priv, mctx);
+	memset(&priv, 0, sizeof(priv));
+	return (ret);
+}
+
+static dst_func_t pkcs11dh_functions = {
+	NULL, /*%< createctx */
+	NULL, /*%< createctx2 */
+	NULL, /*%< destroyctx */
+	NULL, /*%< adddata */
+	NULL, /*%< sign */
+	NULL, /*%< verify */
+	NULL, /*%< verify2 */
+	pkcs11dh_computesecret,
+	pkcs11dh_compare,
+	pkcs11dh_paramcompare,
+	pkcs11dh_generate,
+	pkcs11dh_isprivate,
+	pkcs11dh_destroy,
+	pkcs11dh_todns,
+	pkcs11dh_fromdns,
+	pkcs11dh_tofile,
+	pkcs11dh_parse,
+	NULL, /*%< cleanup */
+	NULL, /*%< fromlabel */
+	NULL, /*%< dump */
+	NULL, /*%< restore */
+};
+
+isc_result_t
+dst__pkcs11dh_init(dst_func_t **funcp) {
+	REQUIRE(funcp != NULL);
+	if (*funcp == NULL)
+		*funcp = &pkcs11dh_functions;
+	return (ISC_R_SUCCESS);
+}
+
+#else /* PKCS11CRYPTO */
+
+#include <isc/util.h>
+
+EMPTY_TRANSLATION_UNIT
+
+#endif /* PKCS11CRYPTO */
+/*! \file */
diff --git a/lib/dns/pkcs11dsa_link.c b/lib/dns/pkcs11dsa_link.c
new file mode 100644
index 0000000..6c8e46c
--- /dev/null
+++ b/lib/dns/pkcs11dsa_link.c
@@ -0,0 +1,1130 @@
+/*
+ * Portions Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Portions Copyright (C) 1995-2000 by Network Associates, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id$ */
+
+#ifdef PKCS11CRYPTO
+
+#include <config.h>
+
+#include <string.h>
+
+#include <isc/entropy.h>
+#include <isc/mem.h>
+#include <isc/sha1.h>
+#include <isc/util.h>
+
+#include <dst/result.h>
+
+#include "dst_internal.h"
+#include "dst_parse.h"
+#include "dst_pkcs11.h"
+
+#include <pk11/internal.h>
+
+/*
+ * FIPS 186-2 DSA keys:
+ *  mechanisms:
+ *    CKM_DSA_SHA1,
+ *    CKM_DSA_KEY_PAIR_GEN,
+ *    CKM_DSA_PARAMETER_GEN
+ *  domain parameters:
+ *    object class CKO_DOMAIN_PARAMETERS
+ *    key type CKK_DSA
+ *    attribute CKA_PRIME (prime p)
+ *    attribute CKA_SUBPRIME (subprime q)
+ *    attribute CKA_BASE (base g)
+ *    optional attribute CKA_PRIME_BITS (p length in bits)
+ *  public keys:
+ *    object class CKO_PUBLIC_KEY
+ *    key type CKK_DSA
+ *    attribute CKA_PRIME (prime p)
+ *    attribute CKA_SUBPRIME (subprime q)
+ *    attribute CKA_BASE (base g)
+ *    attribute CKA_VALUE (public value y)
+ *  private keys:
+ *    object class CKO_PRIVATE_KEY
+ *    key type CKK_DSA
+ *    attribute CKA_PRIME (prime p)
+ *    attribute CKA_SUBPRIME (subprime q)
+ *    attribute CKA_BASE (base g)
+ *    attribute CKA_VALUE (private value x)
+ *  reuse CKA_PRIVATE_EXPONENT for key pair private value
+ */
+
+#define CKA_VALUE2	CKA_PRIVATE_EXPONENT
+
+#define DST_RET(a) {ret = a; goto err;}
+
+static CK_BBOOL truevalue = TRUE;
+static CK_BBOOL falsevalue = FALSE;
+
+static isc_result_t pkcs11dsa_todns(const dst_key_t *key, isc_buffer_t *data);
+static void pkcs11dsa_destroy(dst_key_t *key);
+
+static isc_result_t
+pkcs11dsa_createctx_sign(dst_key_t *key, dst_context_t *dctx) {
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_DSA_SHA1, NULL, 0 };
+	CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
+	CK_KEY_TYPE keyType = CKK_DSA;
+	CK_ATTRIBUTE keyTemplate[] =
+	{
+		{ CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_PRIME, NULL, 0 },
+		{ CKA_SUBPRIME, NULL, 0 },
+		{ CKA_BASE, NULL, 0 },
+		{ CKA_VALUE, NULL, 0 }
+	};
+	CK_ATTRIBUTE *attr;
+	pk11_object_t *dsa;
+	pk11_context_t *pk11_ctx;
+	isc_result_t ret;
+	unsigned int i;
+
+	pk11_ctx = (pk11_context_t *) isc_mem_get(dctx->mctx,
+						  sizeof(*pk11_ctx));
+	if (pk11_ctx == NULL)
+		return (ISC_R_NOMEMORY);
+	ret = pk11_get_session(pk11_ctx, OP_DSA, ISC_TRUE, ISC_FALSE,
+			       ISC_FALSE, NULL, pk11_get_best_token(OP_DSA));
+	if (ret != ISC_R_SUCCESS)
+		goto err;
+
+	dsa = key->keydata.pkey;
+	if (dsa->ontoken && (dsa->object != CK_INVALID_HANDLE)) {
+		pk11_ctx->ontoken = dsa->ontoken;
+		pk11_ctx->object = dsa->object;
+		goto token_key;
+	}
+
+	for (attr = pk11_attribute_first(dsa);
+	     attr != NULL;
+	     attr = pk11_attribute_next(dsa, attr))
+		switch (attr->type) {
+		case CKA_PRIME:
+			INSIST(keyTemplate[6].type == attr->type);
+			keyTemplate[6].pValue = isc_mem_get(dctx->mctx,
+							    attr->ulValueLen);
+			if (keyTemplate[6].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(keyTemplate[6].pValue,
+			       attr->pValue,
+			       attr->ulValueLen);
+			keyTemplate[6].ulValueLen = attr->ulValueLen;
+			break;
+		case CKA_SUBPRIME:
+			INSIST(keyTemplate[7].type == attr->type);
+			keyTemplate[7].pValue = isc_mem_get(dctx->mctx,
+							    attr->ulValueLen);
+			if (keyTemplate[7].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(keyTemplate[7].pValue,
+			       attr->pValue,
+			       attr->ulValueLen);
+			keyTemplate[7].ulValueLen = attr->ulValueLen;
+			break;
+		case CKA_BASE:
+			INSIST(keyTemplate[8].type == attr->type);
+			keyTemplate[8].pValue = isc_mem_get(dctx->mctx,
+							    attr->ulValueLen);
+			if (keyTemplate[8].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(keyTemplate[8].pValue,
+			       attr->pValue,
+			       attr->ulValueLen);
+			keyTemplate[8].ulValueLen = attr->ulValueLen;
+			break;
+		case CKA_VALUE2:
+			INSIST(keyTemplate[9].type == CKA_VALUE);
+			keyTemplate[9].pValue = isc_mem_get(dctx->mctx,
+							    attr->ulValueLen);
+			if (keyTemplate[9].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(keyTemplate[9].pValue,
+			       attr->pValue,
+			       attr->ulValueLen);
+			keyTemplate[9].ulValueLen = attr->ulValueLen;
+			break;
+		}
+	pk11_ctx->object = CK_INVALID_HANDLE;
+	pk11_ctx->ontoken = ISC_FALSE;
+	PK11_RET(pkcs_C_CreateObject,
+		 (pk11_ctx->session,
+		  keyTemplate, (CK_ULONG) 10,
+		  &pk11_ctx->object),
+		 ISC_R_FAILURE);
+
+    token_key:
+
+	PK11_RET(pkcs_C_SignInit,
+		 (pk11_ctx->session, &mech, pk11_ctx->object),
+		 ISC_R_FAILURE);
+
+	dctx->ctxdata.pk11_ctx = pk11_ctx;
+
+	for (i = 6; i <= 9; i++)
+		if (keyTemplate[i].pValue != NULL) {
+			memset(keyTemplate[i].pValue, 0,
+			       keyTemplate[i].ulValueLen);
+			isc_mem_put(dctx->mctx,
+				    keyTemplate[i].pValue,
+				    keyTemplate[i].ulValueLen);
+		}
+
+	return (ISC_R_SUCCESS);
+
+    err:
+	if (!pk11_ctx->ontoken && (pk11_ctx->object != CK_INVALID_HANDLE))
+		(void) pkcs_C_DestroyObject(pk11_ctx->session, pk11_ctx->object);
+	for (i = 6; i <= 9; i++)
+		if (keyTemplate[i].pValue != NULL) {
+			memset(keyTemplate[i].pValue, 0,
+			       keyTemplate[i].ulValueLen);
+			isc_mem_put(dctx->mctx,
+				    keyTemplate[i].pValue,
+				    keyTemplate[i].ulValueLen);
+		}
+	pk11_return_session(pk11_ctx);
+	memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+	isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+	return (ret);
+}
+
+static isc_result_t
+pkcs11dsa_createctx_verify(dst_key_t *key, dst_context_t *dctx) {
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_DSA_SHA1, NULL, 0 };
+	CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY;
+	CK_KEY_TYPE keyType = CKK_DSA;
+	CK_ATTRIBUTE keyTemplate[] =
+	{
+		{ CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_VERIFY, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_PRIME, NULL, 0 },
+		{ CKA_SUBPRIME, NULL, 0 },
+		{ CKA_BASE, NULL, 0 },
+		{ CKA_VALUE, NULL, 0 }
+	};
+	CK_ATTRIBUTE *attr;
+	pk11_object_t *dsa;
+	pk11_context_t *pk11_ctx;
+	isc_result_t ret;
+	unsigned int i;
+
+	pk11_ctx = (pk11_context_t *) isc_mem_get(dctx->mctx,
+						  sizeof(*pk11_ctx));
+	if (pk11_ctx == NULL)
+		return (ISC_R_NOMEMORY);
+	ret = pk11_get_session(pk11_ctx, OP_DSA, ISC_TRUE, ISC_FALSE,
+			       ISC_FALSE, NULL, pk11_get_best_token(OP_DSA));
+	if (ret != ISC_R_SUCCESS)
+		goto err;
+
+	dsa = key->keydata.pkey;
+	if (dsa->ontoken && (dsa->object != CK_INVALID_HANDLE)) {
+		pk11_ctx->ontoken = dsa->ontoken;
+		pk11_ctx->object = dsa->object;
+		goto token_key;
+	}
+
+	for (attr = pk11_attribute_first(dsa);
+	     attr != NULL;
+	     attr = pk11_attribute_next(dsa, attr))
+		switch (attr->type) {
+		case CKA_PRIME:
+			INSIST(keyTemplate[5].type == attr->type);
+			keyTemplate[5].pValue = isc_mem_get(dctx->mctx,
+							    attr->ulValueLen);
+			if (keyTemplate[5].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(keyTemplate[5].pValue,
+			       attr->pValue,
+			       attr->ulValueLen);
+			keyTemplate[5].ulValueLen = attr->ulValueLen;
+			break;
+		case CKA_SUBPRIME:
+			INSIST(keyTemplate[6].type == attr->type);
+			keyTemplate[6].pValue = isc_mem_get(dctx->mctx,
+							    attr->ulValueLen);
+			if (keyTemplate[6].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(keyTemplate[6].pValue,
+			       attr->pValue,
+			       attr->ulValueLen);
+			keyTemplate[6].ulValueLen = attr->ulValueLen;
+			break;
+		case CKA_BASE:
+			INSIST(keyTemplate[7].type == attr->type);
+			keyTemplate[7].pValue = isc_mem_get(dctx->mctx,
+							    attr->ulValueLen);
+			if (keyTemplate[7].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(keyTemplate[7].pValue,
+			       attr->pValue,
+			       attr->ulValueLen);
+			keyTemplate[7].ulValueLen = attr->ulValueLen;
+			break;
+		case CKA_VALUE:
+			INSIST(keyTemplate[8].type == attr->type);
+			keyTemplate[8].pValue = isc_mem_get(dctx->mctx,
+							    attr->ulValueLen);
+			if (keyTemplate[8].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(keyTemplate[8].pValue,
+			       attr->pValue,
+			       attr->ulValueLen);
+			keyTemplate[8].ulValueLen = attr->ulValueLen;
+			break;
+		}
+	pk11_ctx->object = CK_INVALID_HANDLE;
+	pk11_ctx->ontoken = ISC_FALSE;
+	PK11_RET(pkcs_C_CreateObject,
+		 (pk11_ctx->session,
+		  keyTemplate, (CK_ULONG) 9,
+		  &pk11_ctx->object),
+		 ISC_R_FAILURE);
+
+    token_key:
+
+	PK11_RET(pkcs_C_VerifyInit,
+		 (pk11_ctx->session, &mech, pk11_ctx->object),
+		 ISC_R_FAILURE);
+
+	dctx->ctxdata.pk11_ctx = pk11_ctx;
+
+	for (i = 5; i <= 8; i++)
+		if (keyTemplate[i].pValue != NULL) {
+			memset(keyTemplate[i].pValue, 0,
+			       keyTemplate[i].ulValueLen);
+			isc_mem_put(dctx->mctx,
+				    keyTemplate[i].pValue,
+				    keyTemplate[i].ulValueLen);
+		}
+
+	return (ISC_R_SUCCESS);
+
+    err:
+	if (!pk11_ctx->ontoken && (pk11_ctx->object != CK_INVALID_HANDLE))
+		(void) pkcs_C_DestroyObject(pk11_ctx->session, pk11_ctx->object);
+	for (i = 5; i <= 8; i++)
+		if (keyTemplate[i].pValue != NULL) {
+			memset(keyTemplate[i].pValue, 0,
+			       keyTemplate[i].ulValueLen);
+			isc_mem_put(dctx->mctx,
+				    keyTemplate[i].pValue,
+				    keyTemplate[i].ulValueLen);
+		}
+	pk11_return_session(pk11_ctx);
+	memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+	isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+	return (ret);
+}
+
+static isc_result_t
+pkcs11dsa_createctx(dst_key_t *key, dst_context_t *dctx) {
+	if (dctx->use == DO_SIGN)
+		return (pkcs11dsa_createctx_sign(key, dctx));
+	else
+		return (pkcs11dsa_createctx_verify(key, dctx));
+}
+
+static void
+pkcs11dsa_destroyctx(dst_context_t *dctx) {
+	pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+
+	if (pk11_ctx != NULL) {
+		if (!pk11_ctx->ontoken &&
+		    (pk11_ctx->object != CK_INVALID_HANDLE))
+			(void) pkcs_C_DestroyObject(pk11_ctx->session,
+					       pk11_ctx->object);
+		pk11_return_session(pk11_ctx);
+		memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+		isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+		dctx->ctxdata.pk11_ctx = NULL;
+	}
+}
+
+static isc_result_t
+pkcs11dsa_adddata(dst_context_t *dctx, const isc_region_t *data) {
+	CK_RV rv;
+	pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+	isc_result_t ret = ISC_R_SUCCESS;
+
+	if (dctx->use == DO_SIGN)
+		PK11_CALL(pkcs_C_SignUpdate,
+			  (pk11_ctx->session,
+			   (CK_BYTE_PTR) data->base,
+			   (CK_ULONG) data->length),
+			  ISC_R_FAILURE);
+	else
+		PK11_CALL(pkcs_C_VerifyUpdate,
+			  (pk11_ctx->session,
+			   (CK_BYTE_PTR) data->base,
+			   (CK_ULONG) data->length),
+			  ISC_R_FAILURE);
+	return (ret);
+}
+
+static isc_result_t
+pkcs11dsa_sign(dst_context_t *dctx, isc_buffer_t *sig) {
+	CK_RV rv;
+	CK_ULONG siglen = ISC_SHA1_DIGESTLENGTH * 2;
+	isc_region_t r;
+	pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+	isc_result_t ret = ISC_R_SUCCESS;
+
+	isc_buffer_availableregion(sig, &r);
+	if (r.length < ISC_SHA1_DIGESTLENGTH * 2 + 1)
+		return (ISC_R_NOSPACE);
+
+	PK11_RET(pkcs_C_SignFinal,
+		 (pk11_ctx->session, (CK_BYTE_PTR) r.base + 1, &siglen),
+		 DST_R_SIGNFAILURE);
+	if (siglen != ISC_SHA1_DIGESTLENGTH * 2)
+		return (DST_R_SIGNFAILURE);
+
+	*r.base = (dctx->key->key_size - 512)/64;
+	isc_buffer_add(sig, ISC_SHA1_DIGESTLENGTH * 2 + 1);
+
+    err:
+	return (ret);
+}
+
+static isc_result_t
+pkcs11dsa_verify(dst_context_t *dctx, const isc_region_t *sig) {
+	CK_RV rv;
+	pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+	isc_result_t ret = ISC_R_SUCCESS;
+
+	PK11_CALL(pkcs_C_VerifyFinal,
+		  (pk11_ctx->session,
+		   (CK_BYTE_PTR) sig->base + 1,
+		   (CK_ULONG) sig->length - 1),
+		  DST_R_VERIFYFAILURE);
+	return (ret);
+}
+
+static isc_boolean_t
+pkcs11dsa_compare(const dst_key_t *key1, const dst_key_t *key2) {
+	pk11_object_t *dsa1, *dsa2;
+	CK_ATTRIBUTE *attr1, *attr2;
+
+	dsa1 = key1->keydata.pkey;
+	dsa2 = key2->keydata.pkey;
+
+	if ((dsa1 == NULL) && (dsa2 == NULL))
+		return (ISC_TRUE);
+	else if ((dsa1 == NULL) || (dsa2 == NULL))
+		return (ISC_FALSE);
+
+	attr1 = pk11_attribute_bytype(dsa1, CKA_PRIME);
+	attr2 = pk11_attribute_bytype(dsa2, CKA_PRIME);
+	if ((attr1 == NULL) && (attr2 == NULL))
+		return (ISC_TRUE);
+	else if ((attr1 == NULL) || (attr2 == NULL) ||
+		 (attr1->ulValueLen != attr2->ulValueLen) ||
+		 memcmp(attr1->pValue, attr2->pValue, attr1->ulValueLen))
+		return (ISC_FALSE);
+
+	attr1 = pk11_attribute_bytype(dsa1, CKA_SUBPRIME);
+	attr2 = pk11_attribute_bytype(dsa2, CKA_SUBPRIME);
+	if ((attr1 == NULL) && (attr2 == NULL))
+		return (ISC_TRUE);
+	else if ((attr1 == NULL) || (attr2 == NULL) ||
+		 (attr1->ulValueLen != attr2->ulValueLen) ||
+		 memcmp(attr1->pValue, attr2->pValue, attr1->ulValueLen))
+		return (ISC_FALSE);
+
+	attr1 = pk11_attribute_bytype(dsa1, CKA_BASE);
+	attr2 = pk11_attribute_bytype(dsa2, CKA_BASE);
+	if ((attr1 == NULL) && (attr2 == NULL))
+		return (ISC_TRUE);
+	else if ((attr1 == NULL) || (attr2 == NULL) ||
+		 (attr1->ulValueLen != attr2->ulValueLen) ||
+		 memcmp(attr1->pValue, attr2->pValue, attr1->ulValueLen))
+		return (ISC_FALSE);
+
+	attr1 = pk11_attribute_bytype(dsa1, CKA_VALUE);
+	attr2 = pk11_attribute_bytype(dsa2, CKA_VALUE);
+	if ((attr1 == NULL) && (attr2 == NULL))
+		return (ISC_TRUE);
+	else if ((attr1 == NULL) || (attr2 == NULL) ||
+		 (attr1->ulValueLen != attr2->ulValueLen) ||
+		 memcmp(attr1->pValue, attr2->pValue, attr1->ulValueLen))
+		return (ISC_FALSE);
+
+	attr1 = pk11_attribute_bytype(dsa1, CKA_VALUE2);
+	attr2 = pk11_attribute_bytype(dsa2, CKA_VALUE2);
+	if (((attr1 != NULL) || (attr2 != NULL)) &&
+	    ((attr1 == NULL) || (attr2 == NULL) ||
+	     (attr1->ulValueLen != attr2->ulValueLen) ||
+	     memcmp(attr1->pValue, attr2->pValue, attr1->ulValueLen)))
+		return (ISC_FALSE);
+
+	if (!dsa1->ontoken && !dsa2->ontoken)
+		return (ISC_TRUE);
+	else if (dsa1->ontoken || dsa2->ontoken ||
+		 (dsa1->object != dsa2->object))
+		return (ISC_FALSE);
+
+	return (ISC_TRUE);
+}
+
+static isc_result_t
+pkcs11dsa_generate(dst_key_t *key, int unused, void (*callback)(int)) {
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_DSA_PARAMETER_GEN, NULL, 0 };
+	CK_OBJECT_HANDLE dp = CK_INVALID_HANDLE;
+	CK_OBJECT_CLASS dpClass = CKO_DOMAIN_PARAMETERS;
+	CK_KEY_TYPE  keyType = CKK_DSA;
+	CK_ULONG bits = 0;
+	CK_ATTRIBUTE dpTemplate[] =
+	{
+		{ CKA_CLASS, &dpClass, (CK_ULONG) sizeof(dpClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIME_BITS, &bits, (CK_ULONG) sizeof(bits) },
+	};
+	CK_OBJECT_HANDLE pub = CK_INVALID_HANDLE;
+	CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY;
+	CK_ATTRIBUTE pubTemplate[] =
+	{
+		{ CKA_CLASS, &pubClass, (CK_ULONG) sizeof(pubClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_VERIFY, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_PRIME, NULL, 0 },
+		{ CKA_SUBPRIME, NULL, 0 },
+		{ CKA_BASE, NULL, 0 }
+	};
+	CK_OBJECT_HANDLE priv = CK_INVALID_HANDLE;
+	CK_OBJECT_HANDLE privClass = CKO_PRIVATE_KEY;
+	CK_ATTRIBUTE privTemplate[] =
+	{
+		{ CKA_CLASS, &privClass, (CK_ULONG) sizeof(privClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_EXTRACTABLE, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+	};
+	CK_ATTRIBUTE *attr;
+	pk11_object_t *dsa;
+	pk11_context_t *pk11_ctx;
+	isc_result_t ret;
+	unsigned int i;
+
+	UNUSED(unused);
+	UNUSED(callback);
+
+	pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx,
+						  sizeof(*pk11_ctx));
+	if (pk11_ctx == NULL)
+		return (ISC_R_NOMEMORY);
+	ret = pk11_get_session(pk11_ctx, OP_DSA, ISC_TRUE, ISC_FALSE,
+			       ISC_FALSE, NULL, pk11_get_best_token(OP_DSA));
+	if (ret != ISC_R_SUCCESS)
+		goto err;
+
+	bits = key->key_size;
+	PK11_RET(pkcs_C_GenerateKey,
+		 (pk11_ctx->session, &mech, dpTemplate, (CK_ULONG) 5, &dp),
+		 DST_R_CRYPTOFAILURE);
+
+	dsa = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*dsa));
+	if (dsa == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(dsa, 0, sizeof(*dsa));
+	key->keydata.pkey = dsa;
+	dsa->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 5);
+	if (dsa->repr == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(dsa->repr, 0, sizeof(*attr) * 5);
+	dsa->attrcnt = 5;
+
+	attr = dsa->repr;
+	attr[0].type = CKA_PRIME;
+	attr[1].type = CKA_SUBPRIME;
+	attr[2].type = CKA_BASE;
+	attr[3].type = CKA_VALUE;
+	attr[4].type = CKA_VALUE2;
+
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, dp, attr, 3),
+		 DST_R_CRYPTOFAILURE);
+
+	for (i = 0; i <= 2; i++) {
+		attr[i].pValue = isc_mem_get(key->mctx, attr[i].ulValueLen);
+		if (attr[i].pValue == NULL)
+			DST_RET(ISC_R_NOMEMORY);
+		memset(attr[i].pValue, 0, attr[i].ulValueLen);
+	}
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, dp, attr, 3),
+		 DST_R_CRYPTOFAILURE);
+	pubTemplate[5].pValue = attr[0].pValue;
+	pubTemplate[5].ulValueLen = attr[0].ulValueLen;
+	pubTemplate[6].pValue = attr[1].pValue;
+	pubTemplate[6].ulValueLen = attr[1].ulValueLen;
+	pubTemplate[7].pValue = attr[2].pValue;
+	pubTemplate[7].ulValueLen = attr[2].ulValueLen;
+
+	mech.mechanism = CKM_DSA_KEY_PAIR_GEN;
+	PK11_RET(pkcs_C_GenerateKeyPair,
+		 (pk11_ctx->session, &mech,
+		  pubTemplate, (CK_ULONG) 8,
+		  privTemplate, (CK_ULONG) 7,
+		  &pub, &priv),
+		 DST_R_CRYPTOFAILURE);
+
+	attr = dsa->repr;
+	attr += 3;
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, pub, attr, 1),
+		 DST_R_CRYPTOFAILURE);
+	attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen);
+	if (attr->pValue == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(attr->pValue, 0, attr->ulValueLen);
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, pub, attr, 1),
+		 DST_R_CRYPTOFAILURE);
+
+	attr++;
+	attr->type = CKA_VALUE;
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, priv, attr, 1),
+		 DST_R_CRYPTOFAILURE);
+	attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen);
+	if (attr->pValue == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(attr->pValue, 0, attr->ulValueLen);
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, priv, attr, 1),
+		 DST_R_CRYPTOFAILURE);
+	attr->type = CKA_VALUE2;
+
+	(void) pkcs_C_DestroyObject(pk11_ctx->session, priv);
+	(void) pkcs_C_DestroyObject(pk11_ctx->session, pub);
+	(void) pkcs_C_DestroyObject(pk11_ctx->session, dp);
+	pk11_return_session(pk11_ctx);
+	memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+	isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+	return (ISC_R_SUCCESS);
+
+    err:
+	pkcs11dsa_destroy(key);
+	if (priv != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(pk11_ctx->session, priv);
+	if (pub != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(pk11_ctx->session, pub);
+	if (dp != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(pk11_ctx->session, dp);
+	pk11_return_session(pk11_ctx);
+	memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+	isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+	return (ret);
+}
+
+static isc_boolean_t
+pkcs11dsa_isprivate(const dst_key_t *key) {
+	pk11_object_t *dsa = key->keydata.pkey;
+	CK_ATTRIBUTE *attr;
+
+	if (dsa == NULL)
+		return (ISC_FALSE);
+	attr = pk11_attribute_bytype(dsa, CKA_VALUE2);
+	return (ISC_TF((attr != NULL) || dsa->ontoken));
+}
+
+static void
+pkcs11dsa_destroy(dst_key_t *key) {
+	pk11_object_t *dsa = key->keydata.pkey;
+	CK_ATTRIBUTE *attr;
+
+	if (dsa == NULL)
+		return;
+
+	INSIST((dsa->object == CK_INVALID_HANDLE) || dsa->ontoken);
+
+	for (attr = pk11_attribute_first(dsa);
+	     attr != NULL;
+	     attr = pk11_attribute_next(dsa, attr))
+		switch (attr->type) {
+		case CKA_PRIME:
+		case CKA_SUBPRIME:
+		case CKA_BASE:
+		case CKA_VALUE:
+		case CKA_VALUE2:
+			if (attr->pValue != NULL) {
+				memset(attr->pValue, 0, attr->ulValueLen);
+				isc_mem_put(key->mctx,
+					    attr->pValue,
+					    attr->ulValueLen);
+			}
+			break;
+		}
+	if (dsa->repr != NULL) {
+		memset(dsa->repr, 0, dsa->attrcnt * sizeof(*attr));
+		isc_mem_put(key->mctx,
+			    dsa->repr,
+			    dsa->attrcnt * sizeof(*attr));
+	}
+	memset(dsa, 0, sizeof(*dsa));
+	isc_mem_put(key->mctx, dsa, sizeof(*dsa));
+	key->keydata.pkey = NULL;
+}
+
+
+static isc_result_t
+pkcs11dsa_todns(const dst_key_t *key, isc_buffer_t *data) {
+	pk11_object_t *dsa;
+	CK_ATTRIBUTE *attr;
+	isc_region_t r;
+	int dnslen;
+	unsigned int t, p_bytes;
+	CK_ATTRIBUTE *prime = NULL, *subprime = NULL;
+	CK_ATTRIBUTE *base = NULL, *pub_key = NULL;
+	CK_BYTE *cp;
+
+	REQUIRE(key->keydata.pkey != NULL);
+
+	dsa = key->keydata.pkey;
+
+	for (attr = pk11_attribute_first(dsa);
+	     attr != NULL;
+	     attr = pk11_attribute_next(dsa, attr))
+		switch (attr->type) {
+		case CKA_PRIME:
+			prime = attr;
+			break;
+		case CKA_SUBPRIME:
+			subprime = attr;
+			break;
+		case CKA_BASE:
+			base = attr;
+			break;
+		case CKA_VALUE:
+			pub_key = attr;
+			break;
+		}
+	REQUIRE((prime != NULL) && (subprime != NULL) &&
+		(base != NULL) && (pub_key != NULL));
+
+	isc_buffer_availableregion(data, &r);
+
+	t = (prime->ulValueLen - 64) / 8;
+	if (t > 8)
+		return (DST_R_INVALIDPUBLICKEY);
+	p_bytes = 64 + 8 * t;
+
+	dnslen = 1 + (key->key_size * 3)/8 + ISC_SHA1_DIGESTLENGTH;
+	if (r.length < (unsigned int) dnslen)
+		return (ISC_R_NOSPACE);
+
+	memset(r.base, 0, dnslen);
+	*r.base++ = t;
+	cp = (CK_BYTE *) subprime->pValue;
+	memcpy(r.base + ISC_SHA1_DIGESTLENGTH - subprime->ulValueLen,
+	       cp, subprime->ulValueLen);
+	r.base += ISC_SHA1_DIGESTLENGTH;
+	cp = (CK_BYTE *) prime->pValue;
+	memcpy(r.base + key->key_size/8 - prime->ulValueLen,
+	       cp, prime->ulValueLen);
+	r.base += p_bytes;
+	cp = (CK_BYTE *) base->pValue;
+	memcpy(r.base + key->key_size/8 - base->ulValueLen,
+	       cp, base->ulValueLen);
+	r.base += p_bytes;
+	cp = (CK_BYTE *) pub_key->pValue;
+	memcpy(r.base + key->key_size/8 - pub_key->ulValueLen,
+	       cp, pub_key->ulValueLen);
+	r.base += p_bytes;
+
+	isc_buffer_add(data, dnslen);
+
+	return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+pkcs11dsa_fromdns(dst_key_t *key, isc_buffer_t *data) {
+	pk11_object_t *dsa;
+	isc_region_t r;
+	unsigned int t, p_bytes;
+	CK_BYTE *prime, *subprime, *base, *pub_key;
+	CK_ATTRIBUTE *attr;
+
+	isc_buffer_remainingregion(data, &r);
+	if (r.length == 0)
+		return (ISC_R_SUCCESS);
+
+	dsa = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*dsa));
+	if (dsa == NULL)
+		return (ISC_R_NOMEMORY);
+	memset(dsa, 0, sizeof(*dsa));
+
+	t = (unsigned int) *r.base++;
+	if (t > 8) {
+		memset(dsa, 0, sizeof(*dsa));
+		isc_mem_put(key->mctx, dsa, sizeof(*dsa));
+		return (DST_R_INVALIDPUBLICKEY);
+	}
+	p_bytes = 64 + 8 * t;
+
+	if (r.length < 1 + ISC_SHA1_DIGESTLENGTH + 3 * p_bytes) {
+		memset(dsa, 0, sizeof(*dsa));
+		isc_mem_put(key->mctx, dsa, sizeof(*dsa));
+		return (DST_R_INVALIDPUBLICKEY);
+	}
+
+	subprime = r.base;
+	r.base += ISC_SHA1_DIGESTLENGTH;
+
+	prime = r.base;
+	r.base += p_bytes;
+
+	base = r.base;
+	r.base += p_bytes;
+
+	pub_key = r.base;
+	r.base += p_bytes;
+
+	key->key_size = p_bytes * 8;
+
+	isc_buffer_forward(data, 1 + ISC_SHA1_DIGESTLENGTH + 3 * p_bytes);
+
+	dsa->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 4);
+	if (dsa->repr == NULL)
+		goto nomemory;
+	memset(dsa->repr, 0, sizeof(*attr) * 4);
+	dsa->attrcnt = 4;
+
+	attr = dsa->repr;
+	attr[0].type = CKA_PRIME;
+	attr[0].pValue = isc_mem_get(key->mctx, p_bytes);
+	if (attr[0].pValue == NULL)
+		goto nomemory;
+	memcpy(attr[0].pValue, prime, p_bytes);
+	attr[0].ulValueLen = p_bytes;
+
+	attr[1].type = CKA_SUBPRIME;
+	attr[1].pValue = isc_mem_get(key->mctx, ISC_SHA1_DIGESTLENGTH);
+	if (attr[1].pValue == NULL)
+		goto nomemory;
+	memcpy(attr[1].pValue, subprime, ISC_SHA1_DIGESTLENGTH);
+	attr[1].ulValueLen = ISC_SHA1_DIGESTLENGTH;
+
+	attr[2].type = CKA_BASE;
+	attr[2].pValue = isc_mem_get(key->mctx, p_bytes);
+	if (attr[2].pValue == NULL)
+		goto nomemory;
+	memcpy(attr[2].pValue, base, p_bytes);
+	attr[2].ulValueLen = p_bytes;
+
+	attr[3].type = CKA_VALUE;
+	attr[3].pValue = isc_mem_get(key->mctx, p_bytes);
+	if (attr[3].pValue == NULL)
+		goto nomemory;
+	memcpy(attr[3].pValue, pub_key, p_bytes);
+	attr[3].ulValueLen = p_bytes;
+
+	key->keydata.pkey = dsa;
+
+	return (ISC_R_SUCCESS);
+
+    nomemory:
+	for (attr = pk11_attribute_first(dsa);
+	     attr != NULL;
+	     attr = pk11_attribute_next(dsa, attr))
+		switch (attr->type) {
+		case CKA_PRIME:
+		case CKA_SUBPRIME:
+		case CKA_BASE:
+		case CKA_VALUE:
+			if (attr->pValue != NULL) {
+				memset(attr->pValue, 0, attr->ulValueLen);
+				isc_mem_put(key->mctx,
+					    attr->pValue,
+					    attr->ulValueLen);
+			}
+			break;
+		}
+	if (dsa->repr != NULL) {
+		memset(dsa->repr, 0, dsa->attrcnt * sizeof(*attr));
+		isc_mem_put(key->mctx,
+			    dsa->repr,
+			    dsa->attrcnt * sizeof(*attr));
+	}
+	memset(dsa, 0, sizeof(*dsa));
+	isc_mem_put(key->mctx, dsa, sizeof(*dsa));
+	return (ISC_R_NOMEMORY);
+}
+
+static isc_result_t
+pkcs11dsa_tofile(const dst_key_t *key, const char *directory) {
+	int cnt = 0;
+	pk11_object_t *dsa;
+	CK_ATTRIBUTE *attr;
+	CK_ATTRIBUTE *prime = NULL, *subprime = NULL, *base = NULL;
+	CK_ATTRIBUTE *pub_key = NULL, *priv_key = NULL;
+	dst_private_t priv;
+	unsigned char bufs[5][128];
+
+	if (key->keydata.pkey == NULL)
+		return (DST_R_NULLKEY);
+
+	if (key->external) {
+		priv.nelements = 0;
+		return (dst__privstruct_writefile(key, &priv, directory));
+	}
+
+	dsa = key->keydata.pkey;
+
+	for (attr = pk11_attribute_first(dsa);
+	     attr != NULL;
+	     attr = pk11_attribute_next(dsa, attr))
+		switch (attr->type) {
+		case CKA_PRIME:
+			prime = attr;
+			break;
+		case CKA_SUBPRIME:
+			subprime = attr;
+			break;
+		case CKA_BASE:
+			base = attr;
+			break;
+		case CKA_VALUE:
+			pub_key = attr;
+			break;
+		case CKA_VALUE2:
+			priv_key = attr;
+			break;
+		}
+	if ((prime == NULL) || (subprime == NULL) || (base == NULL) ||
+	    (pub_key == NULL) || (priv_key ==NULL))
+		return (DST_R_NULLKEY);
+
+	priv.elements[cnt].tag = TAG_DSA_PRIME;
+	priv.elements[cnt].length = (unsigned short) prime->ulValueLen;
+	memcpy(bufs[cnt], prime->pValue, prime->ulValueLen);
+	priv.elements[cnt].data = bufs[cnt];
+	cnt++;
+
+	priv.elements[cnt].tag = TAG_DSA_SUBPRIME;
+	priv.elements[cnt].length = (unsigned short) subprime->ulValueLen;
+	memcpy(bufs[cnt], subprime->pValue, subprime->ulValueLen);
+	priv.elements[cnt].data = bufs[cnt];
+	cnt++;
+
+	priv.elements[cnt].tag = TAG_DSA_BASE;
+	priv.elements[cnt].length = (unsigned short) base->ulValueLen;
+	memcpy(bufs[cnt], base->pValue, base->ulValueLen);
+	priv.elements[cnt].data = bufs[cnt];
+	cnt++;
+
+	priv.elements[cnt].tag = TAG_DSA_PRIVATE;
+	priv.elements[cnt].length = (unsigned short) priv_key->ulValueLen;
+	memcpy(bufs[cnt], priv_key->pValue, priv_key->ulValueLen);
+	priv.elements[cnt].data = bufs[cnt];
+	cnt++;
+
+	priv.elements[cnt].tag = TAG_DSA_PUBLIC;
+	priv.elements[cnt].length = (unsigned short) pub_key->ulValueLen;
+	memcpy(bufs[cnt], pub_key->pValue, pub_key->ulValueLen);
+	priv.elements[cnt].data = bufs[cnt];
+	cnt++;
+
+	priv.nelements = cnt;
+	return (dst__privstruct_writefile(key, &priv, directory));
+}
+
+static isc_result_t
+pkcs11dsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
+	dst_private_t priv;
+	isc_result_t ret;
+	int i;
+	pk11_object_t *dsa = NULL;
+	CK_ATTRIBUTE *attr;
+	isc_mem_t *mctx = key->mctx;
+
+	/* read private key file */
+	ret = dst__privstruct_parse(key, DST_ALG_DSA, lexer, mctx, &priv);
+	if (ret != ISC_R_SUCCESS)
+		return (ret);
+
+	if (key->external) {
+		if (priv.nelements != 0)
+			DST_RET(DST_R_INVALIDPRIVATEKEY);
+		if (pub == NULL)
+			DST_RET(DST_R_INVALIDPRIVATEKEY);
+
+		key->keydata.pkey = pub->keydata.pkey;
+		pub->keydata.pkey = NULL;
+		key->key_size = pub->key_size;
+
+		dst__privstruct_free(&priv, mctx);
+		memset(&priv, 0, sizeof(priv));
+
+		return (ISC_R_SUCCESS);
+	}
+
+	dsa = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*dsa));
+	if (dsa == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(dsa, 0, sizeof(*dsa));
+	key->keydata.pkey = dsa;
+
+	dsa->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 5);
+	if (dsa->repr == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(dsa->repr, 0, sizeof(*attr) * 5);
+	dsa->attrcnt = 5;
+	attr = dsa->repr;
+	attr[0].type = CKA_PRIME;
+	attr[1].type = CKA_SUBPRIME;
+	attr[2].type = CKA_BASE;
+	attr[3].type = CKA_VALUE;
+	attr[4].type = CKA_VALUE2;
+
+	for (i = 0; i < priv.nelements; i++) {
+		CK_BYTE *bn;
+
+		bn = isc_mem_get(key->mctx, priv.elements[i].length);
+		if (bn == NULL)
+			DST_RET(ISC_R_NOMEMORY);
+		memcpy(bn,
+		       priv.elements[i].data,
+		       priv.elements[i].length);
+
+		switch (priv.elements[i].tag) {
+			case TAG_DSA_PRIME:
+				attr = pk11_attribute_bytype(dsa, CKA_PRIME);
+				INSIST(attr != NULL);
+				attr->pValue = bn;
+				attr->ulValueLen = priv.elements[i].length;
+				break;
+			case TAG_DSA_SUBPRIME:
+				attr = pk11_attribute_bytype(dsa,
+							     CKA_SUBPRIME);
+				INSIST(attr != NULL);
+				attr->pValue = bn;
+				attr->ulValueLen = priv.elements[i].length;
+				break;
+			case TAG_DSA_BASE:
+				attr = pk11_attribute_bytype(dsa, CKA_BASE);
+				INSIST(attr != NULL);
+				attr->pValue = bn;
+				attr->ulValueLen = priv.elements[i].length;
+				break;
+			case TAG_DSA_PRIVATE:
+				attr = pk11_attribute_bytype(dsa, CKA_VALUE2);
+				INSIST(attr != NULL);
+				attr->pValue = bn;
+				attr->ulValueLen = priv.elements[i].length;
+				break;
+			case TAG_DSA_PUBLIC:
+				attr = pk11_attribute_bytype(dsa, CKA_VALUE);
+				INSIST(attr != NULL);
+				attr->pValue = bn;
+				attr->ulValueLen = priv.elements[i].length;
+				break;
+		}
+	}
+	dst__privstruct_free(&priv, mctx);
+
+	attr = pk11_attribute_bytype(dsa, CKA_PRIME);
+	INSIST(attr != NULL);
+	key->key_size = pk11_numbits(attr->pValue, attr->ulValueLen);
+
+	return (ISC_R_SUCCESS);
+
+ err:
+	pkcs11dsa_destroy(key);
+	dst__privstruct_free(&priv, mctx);
+	memset(&priv, 0, sizeof(priv));
+	return (ret);
+}
+
+static dst_func_t pkcs11dsa_functions = {
+	pkcs11dsa_createctx,
+	NULL, /*%< createctx2 */
+	pkcs11dsa_destroyctx,
+	pkcs11dsa_adddata,
+	pkcs11dsa_sign,
+	pkcs11dsa_verify,
+	NULL, /*%< verify2 */
+	NULL, /*%< computesecret */
+	pkcs11dsa_compare,
+	NULL, /*%< paramcompare */
+	pkcs11dsa_generate,
+	pkcs11dsa_isprivate,
+	pkcs11dsa_destroy,
+	pkcs11dsa_todns,
+	pkcs11dsa_fromdns,
+	pkcs11dsa_tofile,
+	pkcs11dsa_parse,
+	NULL, /*%< cleanup */
+	NULL, /*%< fromlabel */
+	NULL, /*%< dump */
+	NULL, /*%< restore */
+};
+
+isc_result_t
+dst__pkcs11dsa_init(dst_func_t **funcp) {
+	REQUIRE(funcp != NULL);
+	if (*funcp == NULL)
+		*funcp = &pkcs11dsa_functions;
+	return (ISC_R_SUCCESS);
+}
+
+#else /* PKCS11CRYPTO */
+
+#include <isc/util.h>
+
+EMPTY_TRANSLATION_UNIT
+
+#endif /* PKCS11CRYPTO */
+/*! \file */
diff --git a/lib/dns/pkcs11ecdsa_link.c b/lib/dns/pkcs11ecdsa_link.c
new file mode 100644
index 0000000..4f56050
--- /dev/null
+++ b/lib/dns/pkcs11ecdsa_link.c
@@ -0,0 +1,1189 @@
+/*
+ * Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id$ */
+
+#include <config.h>
+
+#if defined(PKCS11CRYPTO) && defined(HAVE_PKCS11_ECDSA)
+
+#include <isc/entropy.h>
+#include <isc/mem.h>
+#include <isc/sha2.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/keyvalues.h>
+#include <dst/result.h>
+
+#include "dst_internal.h"
+#include "dst_parse.h"
+#include "dst_pkcs11.h"
+
+#include <pk11/pk11.h>
+#include <pk11/internal.h>
+#define WANT_ECC_CURVES
+#include <pk11/constants.h>
+
+#include <pkcs11/pkcs11.h>
+
+/*
+ * FIPS 186-3 ECDSA keys:
+ *  mechanisms:
+ *    CKM_ECDSA,
+ *    CKM_EC_KEY_PAIR_GEN
+ *  domain parameters:
+ *    CKA_EC_PARAMS (choice with OID namedCurve)
+ *  public keys:
+ *    object class CKO_PUBLIC_KEY
+ *    key type CKK_EC
+ *    attribute CKA_EC_PARAMS (choice with OID namedCurve)
+ *    attribute CKA_EC_POINT (point Q)
+ *  private keys:
+ *    object class CKO_PRIVATE_KEY
+ *    key type CKK_EC
+ *    attribute CKA_EC_PARAMS (choice with OID namedCurve)
+ *    attribute CKA_VALUE (big int d)
+ *  point format: 0x04 (octet-string) <2*size+1> 0x4 (uncompressed) <x> <y>
+ */
+
+#define TAG_OCTECT_STRING	0x04
+#define UNCOMPRESSED		0x04
+
+#define DST_RET(a) {ret = a; goto err;}
+
+static CK_BBOOL truevalue = TRUE;
+static CK_BBOOL falsevalue = FALSE;
+
+static isc_result_t pkcs11ecdsa_todns(const dst_key_t *key,
+				      isc_buffer_t *data);
+static void pkcs11ecdsa_destroy(dst_key_t *key);
+static isc_result_t pkcs11ecdsa_fetch(dst_key_t *key, const char *engine,
+				      const char *label, dst_key_t *pub);
+
+static isc_result_t
+pkcs11ecdsa_createctx(dst_key_t *key, dst_context_t *dctx) {
+	CK_RV rv;
+	CK_MECHANISM mech = {0, NULL, 0 };
+	CK_SLOT_ID slotid;
+	pk11_context_t *pk11_ctx;
+	pk11_object_t *ec = key->keydata.pkey;
+	isc_result_t ret;
+
+	UNUSED(key);
+	REQUIRE(dctx->key->key_alg == DST_ALG_ECDSA256 ||
+		dctx->key->key_alg == DST_ALG_ECDSA384);
+
+	if (dctx->key->key_alg == DST_ALG_ECDSA256)
+		mech.mechanism = CKM_SHA256;
+	else
+		mech.mechanism = CKM_SHA384;
+
+	pk11_ctx = (pk11_context_t *) isc_mem_get(dctx->mctx,
+						  sizeof(*pk11_ctx));
+	if (pk11_ctx == NULL)
+		return (ISC_R_NOMEMORY);
+	memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+	if (ec->ontoken && (dctx->use == DO_SIGN))
+		slotid = ec->slot;
+	else
+		slotid = pk11_get_best_token(OP_EC);
+	ret = pk11_get_session(pk11_ctx, OP_EC, ISC_TRUE, ISC_FALSE, ISC_FALSE,
+			       NULL, slotid);
+	if (ret != ISC_R_SUCCESS)
+		goto err;
+
+	PK11_RET(pkcs_C_DigestInit, (pk11_ctx->session, &mech), ISC_R_FAILURE);
+	dctx->ctxdata.pk11_ctx = pk11_ctx;
+	return (ISC_R_SUCCESS);
+
+ err:
+	pk11_return_session(pk11_ctx);
+	memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+	isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+	return (ret);
+}
+
+static void
+pkcs11ecdsa_destroyctx(dst_context_t *dctx) {
+	CK_BYTE garbage[ISC_SHA384_DIGESTLENGTH];
+	CK_ULONG len = ISC_SHA384_DIGESTLENGTH;
+	pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+
+	REQUIRE(dctx->key->key_alg == DST_ALG_ECDSA256 ||
+		dctx->key->key_alg == DST_ALG_ECDSA384);
+
+	if (pk11_ctx != NULL) {
+		(void) pkcs_C_DigestFinal(pk11_ctx->session, garbage, &len);
+		memset(garbage, 0, sizeof(garbage));
+		pk11_return_session(pk11_ctx);
+		memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+		isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+		dctx->ctxdata.pk11_ctx = NULL;
+	}
+}
+
+static isc_result_t
+pkcs11ecdsa_adddata(dst_context_t *dctx, const isc_region_t *data) {
+	CK_RV rv;
+	pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+	isc_result_t ret = ISC_R_SUCCESS;
+
+	REQUIRE(dctx->key->key_alg == DST_ALG_ECDSA256 ||
+		dctx->key->key_alg == DST_ALG_ECDSA384);
+
+	PK11_CALL(pkcs_C_DigestUpdate,
+		  (pk11_ctx->session,
+		   (CK_BYTE_PTR) data->base,
+		   (CK_ULONG) data->length),
+		  ISC_R_FAILURE);
+
+	return (ret);
+}
+
+static isc_result_t
+pkcs11ecdsa_sign(dst_context_t *dctx, isc_buffer_t *sig) {
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_ECDSA, NULL, 0 };
+	CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE;
+	CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
+	CK_KEY_TYPE keyType = CKK_EC;
+	CK_ATTRIBUTE keyTemplate[] =
+	{
+		{ CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_EC_PARAMS, NULL, 0 },
+		{ CKA_VALUE, NULL, 0 }
+	};
+	CK_ATTRIBUTE *attr;
+	CK_BYTE digest[ISC_SHA384_DIGESTLENGTH];
+	CK_ULONG dgstlen;
+	CK_ULONG siglen;
+	pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+	dst_key_t *key = dctx->key;
+	pk11_object_t *ec = key->keydata.pkey;
+	isc_region_t r;
+	isc_result_t ret = ISC_R_SUCCESS;
+	unsigned int i;
+
+	REQUIRE(key->key_alg == DST_ALG_ECDSA256 ||
+		key->key_alg == DST_ALG_ECDSA384);
+	REQUIRE(ec != NULL);
+
+	if (key->key_alg == DST_ALG_ECDSA256) {
+		dgstlen = ISC_SHA256_DIGESTLENGTH;
+		siglen = DNS_SIG_ECDSA256SIZE;
+	} else {
+		siglen = DNS_SIG_ECDSA384SIZE;
+		dgstlen = ISC_SHA384_DIGESTLENGTH;
+	}
+
+	PK11_RET(pkcs_C_DigestFinal,
+		 (pk11_ctx->session, digest, &dgstlen),
+		 ISC_R_FAILURE);
+
+	isc_buffer_availableregion(sig, &r);
+	if (r.length < siglen)
+		DST_RET(ISC_R_NOSPACE);
+
+	if (ec->ontoken && (ec->object != CK_INVALID_HANDLE)) {
+		pk11_ctx->ontoken = ec->ontoken;
+		pk11_ctx->object = ec->object;
+		goto token_key;
+	}
+
+	for (attr = pk11_attribute_first(ec);
+	     attr != NULL;
+	     attr = pk11_attribute_next(ec, attr))
+		switch (attr->type) {
+		case CKA_EC_PARAMS:
+			INSIST(keyTemplate[5].type == attr->type);
+			keyTemplate[5].pValue = isc_mem_get(dctx->mctx,
+							    attr->ulValueLen);
+			if (keyTemplate[5].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(keyTemplate[5].pValue,
+			       attr->pValue,
+			       attr->ulValueLen);
+			keyTemplate[5].ulValueLen = attr->ulValueLen;
+			break;
+		case CKA_VALUE:
+			INSIST(keyTemplate[6].type == attr->type);
+			keyTemplate[6].pValue = isc_mem_get(dctx->mctx,
+							    attr->ulValueLen);
+			if (keyTemplate[6].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(keyTemplate[6].pValue,
+			       attr->pValue,
+			       attr->ulValueLen);
+			keyTemplate[6].ulValueLen = attr->ulValueLen;
+			break;
+		}
+	pk11_ctx->object = CK_INVALID_HANDLE;
+	pk11_ctx->ontoken = ISC_FALSE;
+	PK11_RET(pkcs_C_CreateObject,
+		 (pk11_ctx->session,
+		  keyTemplate, (CK_ULONG) 7,
+		  &hKey),
+		 ISC_R_FAILURE);
+
+ token_key:
+
+	PK11_RET(pkcs_C_SignInit,
+		 (pk11_ctx->session, &mech,
+		  pk11_ctx->ontoken ? pk11_ctx->object : hKey),
+		 ISC_R_FAILURE);
+
+	PK11_RET(pkcs_C_Sign,
+		 (pk11_ctx->session,
+		  digest, dgstlen,
+		  (CK_BYTE_PTR) r.base, &siglen),
+		 DST_R_SIGNFAILURE);
+
+	isc_buffer_add(sig, (unsigned int) siglen);
+
+ err:
+
+	if (hKey != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(pk11_ctx->session, hKey);
+	for (i = 5; i <= 6; i++)
+		if (keyTemplate[i].pValue != NULL) {
+			memset(keyTemplate[i].pValue, 0,
+			       keyTemplate[i].ulValueLen);
+			isc_mem_put(dctx->mctx,
+				    keyTemplate[i].pValue,
+				    keyTemplate[i].ulValueLen);
+		}
+	pk11_return_session(pk11_ctx);
+	memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+	isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+	dctx->ctxdata.pk11_ctx = NULL;
+
+	return (ret);
+}
+
+static isc_result_t
+pkcs11ecdsa_verify(dst_context_t *dctx, const isc_region_t *sig) {
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_ECDSA, NULL, 0 };
+	CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE;
+	CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY;
+	CK_KEY_TYPE keyType = CKK_EC;
+	CK_ATTRIBUTE keyTemplate[] =
+	{
+		{ CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_VERIFY, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_EC_PARAMS, NULL, 0 },
+		{ CKA_EC_POINT, NULL, 0 }
+	};
+	CK_ATTRIBUTE *attr;
+	CK_BYTE digest[ISC_SHA384_DIGESTLENGTH];
+	CK_ULONG dgstlen;
+	pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+	dst_key_t *key = dctx->key;
+	pk11_object_t *ec = key->keydata.pkey;
+	isc_result_t ret = ISC_R_SUCCESS;
+	unsigned int i;
+
+	REQUIRE(key->key_alg == DST_ALG_ECDSA256 ||
+		key->key_alg == DST_ALG_ECDSA384);
+	REQUIRE(ec != NULL);
+
+	if (key->key_alg == DST_ALG_ECDSA256)
+		dgstlen = ISC_SHA256_DIGESTLENGTH;
+	else
+		dgstlen = ISC_SHA384_DIGESTLENGTH;
+
+	PK11_RET(pkcs_C_DigestFinal,
+		 (pk11_ctx->session, digest, &dgstlen),
+		 ISC_R_FAILURE);
+
+	for (attr = pk11_attribute_first(ec);
+	     attr != NULL;
+	     attr = pk11_attribute_next(ec, attr))
+		switch (attr->type) {
+		case CKA_EC_PARAMS:
+			INSIST(keyTemplate[5].type == attr->type);
+			keyTemplate[5].pValue = isc_mem_get(dctx->mctx,
+							    attr->ulValueLen);
+			if (keyTemplate[5].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(keyTemplate[5].pValue,
+			       attr->pValue,
+			       attr->ulValueLen);
+			keyTemplate[5].ulValueLen = attr->ulValueLen;
+			break;
+		case CKA_EC_POINT:
+			INSIST(keyTemplate[6].type == attr->type);
+			keyTemplate[6].pValue = isc_mem_get(dctx->mctx,
+							    attr->ulValueLen);
+			if (keyTemplate[6].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(keyTemplate[6].pValue,
+			       attr->pValue,
+			       attr->ulValueLen);
+			keyTemplate[6].ulValueLen = attr->ulValueLen;
+			break;
+		}
+	pk11_ctx->object = CK_INVALID_HANDLE;
+	pk11_ctx->ontoken = ISC_FALSE;
+	PK11_RET(pkcs_C_CreateObject,
+		 (pk11_ctx->session,
+		  keyTemplate, (CK_ULONG) 7,
+		  &hKey),
+		 ISC_R_FAILURE);
+
+	PK11_RET(pkcs_C_VerifyInit,
+		 (pk11_ctx->session, &mech, hKey),
+		 ISC_R_FAILURE);
+
+	PK11_RET(pkcs_C_Verify,
+		 (pk11_ctx->session,
+		  digest, dgstlen,
+		  (CK_BYTE_PTR) sig->base, (CK_ULONG) sig->length),
+		 DST_R_SIGNFAILURE);
+
+ err:
+
+	if (hKey != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(pk11_ctx->session, hKey);
+	for (i = 5; i <= 6; i++)
+		if (keyTemplate[i].pValue != NULL) {
+			memset(keyTemplate[i].pValue, 0,
+			       keyTemplate[i].ulValueLen);
+			isc_mem_put(dctx->mctx,
+				    keyTemplate[i].pValue,
+				    keyTemplate[i].ulValueLen);
+		}
+	pk11_return_session(pk11_ctx);
+	memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+	isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+	dctx->ctxdata.pk11_ctx = NULL;
+
+	return (ret);
+}
+
+static isc_boolean_t
+pkcs11ecdsa_compare(const dst_key_t *key1, const dst_key_t *key2) {
+	pk11_object_t *ec1, *ec2;
+	CK_ATTRIBUTE *attr1, *attr2;
+
+	ec1 = key1->keydata.pkey;
+	ec2 = key2->keydata.pkey;
+
+	if ((ec1 == NULL) && (ec2 == NULL))
+		return (ISC_TRUE);
+	else if ((ec1 == NULL) || (ec2 == NULL))
+		return (ISC_FALSE);
+
+	attr1 = pk11_attribute_bytype(ec1, CKA_EC_PARAMS);
+	attr2 = pk11_attribute_bytype(ec2, CKA_EC_PARAMS);
+	if ((attr1 == NULL) && (attr2 == NULL))
+		return (ISC_TRUE);
+	else if ((attr1 == NULL) || (attr2 == NULL) ||
+		 (attr1->ulValueLen != attr2->ulValueLen) ||
+		 memcmp(attr1->pValue, attr2->pValue, attr1->ulValueLen))
+		return (ISC_FALSE);
+
+	attr1 = pk11_attribute_bytype(ec1, CKA_EC_POINT);
+	attr2 = pk11_attribute_bytype(ec2, CKA_EC_POINT);
+	if ((attr1 == NULL) && (attr2 == NULL))
+		return (ISC_TRUE);
+	else if ((attr1 == NULL) || (attr2 == NULL) ||
+		 (attr1->ulValueLen != attr2->ulValueLen) ||
+		 memcmp(attr1->pValue, attr2->pValue, attr1->ulValueLen))
+		return (ISC_FALSE);
+
+	attr1 = pk11_attribute_bytype(ec1, CKA_VALUE);
+	attr2 = pk11_attribute_bytype(ec2, CKA_VALUE);
+	if (((attr1 != NULL) || (attr2 != NULL)) &&
+	    ((attr1 == NULL) || (attr2 == NULL) ||
+	     (attr1->ulValueLen != attr2->ulValueLen) ||
+	     memcmp(attr1->pValue, attr2->pValue, attr1->ulValueLen)))
+		return (ISC_FALSE);
+
+	if (!ec1->ontoken && !ec2->ontoken)
+		return (ISC_TRUE);
+	else if (ec1->ontoken || ec2->ontoken ||
+		 (ec1->object != ec2->object))
+		return (ISC_FALSE);
+
+	return (ISC_TRUE);
+}
+
+#define SETCURVE() \
+	if (key->key_alg == DST_ALG_ECDSA256) { \
+		attr->pValue = isc_mem_get(key->mctx, \
+					   sizeof(pk11_ecc_prime256v1)); \
+		if (attr->pValue == NULL) \
+			DST_RET(ISC_R_NOMEMORY); \
+		memcpy(attr->pValue, \
+		       pk11_ecc_prime256v1, sizeof(pk11_ecc_prime256v1)); \
+		attr->ulValueLen = sizeof(pk11_ecc_prime256v1); \
+	} else { \
+		attr->pValue = isc_mem_get(key->mctx, \
+					   sizeof(pk11_ecc_secp384r1)); \
+		if (attr->pValue == NULL) \
+			DST_RET(ISC_R_NOMEMORY); \
+		memcpy(attr->pValue, \
+		       pk11_ecc_secp384r1, sizeof(pk11_ecc_secp384r1)); \
+		attr->ulValueLen = sizeof(pk11_ecc_secp384r1); \
+	}
+
+#define FREECURVE() \
+	if (attr->pValue != NULL) { \
+		memset(attr->pValue, 0, attr->ulValueLen); \
+		isc_mem_put(key->mctx, attr->pValue, attr->ulValueLen); \
+		attr->pValue = NULL; \
+	}
+
+static isc_result_t
+pkcs11ecdsa_generate(dst_key_t *key, int unused, void (*callback)(int)) {
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_EC_KEY_PAIR_GEN, NULL, 0 };
+	CK_OBJECT_HANDLE pub = CK_INVALID_HANDLE;
+	CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY;
+	CK_KEY_TYPE  keyType = CKK_EC;
+	CK_ATTRIBUTE pubTemplate[] =
+	{
+		{ CKA_CLASS, &pubClass, (CK_ULONG) sizeof(pubClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_VERIFY, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_EC_PARAMS, NULL, 0 }
+	};
+	CK_OBJECT_HANDLE priv = CK_INVALID_HANDLE;
+	CK_OBJECT_HANDLE privClass = CKO_PRIVATE_KEY;
+	CK_ATTRIBUTE privTemplate[] =
+	{
+		{ CKA_CLASS, &privClass, (CK_ULONG) sizeof(privClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_EXTRACTABLE, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) }
+	};
+	CK_ATTRIBUTE *attr;
+	pk11_object_t *ec;
+	pk11_context_t *pk11_ctx;
+	isc_result_t ret;
+
+	REQUIRE(key->key_alg == DST_ALG_ECDSA256 ||
+		key->key_alg == DST_ALG_ECDSA384);
+	UNUSED(unused);
+	UNUSED(callback);
+
+	pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx,
+						  sizeof(*pk11_ctx));
+	if (pk11_ctx == NULL)
+		return (ISC_R_NOMEMORY);
+	ret = pk11_get_session(pk11_ctx, OP_EC, ISC_TRUE, ISC_FALSE,
+			       ISC_FALSE, NULL, pk11_get_best_token(OP_EC));
+	if (ret != ISC_R_SUCCESS)
+		goto err;
+
+	ec = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*ec));
+	if (ec == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(ec, 0, sizeof(*ec));
+	key->keydata.pkey = ec;
+	ec->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 3);
+	if (ec->repr == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(ec->repr, 0, sizeof(*attr) * 3);
+	ec->attrcnt = 3;
+
+	attr = ec->repr;
+	attr[0].type = CKA_EC_PARAMS;
+	attr[1].type = CKA_EC_POINT;
+	attr[2].type = CKA_VALUE;
+
+	attr = &pubTemplate[5];
+	SETCURVE();
+
+	PK11_RET(pkcs_C_GenerateKeyPair,
+		 (pk11_ctx->session, &mech,
+		  pubTemplate, (CK_ULONG) 6,
+		  privTemplate, (CK_ULONG) 7,
+		  &pub, &priv),
+		 DST_R_CRYPTOFAILURE);
+
+	attr = &pubTemplate[5];
+	FREECURVE();
+
+	attr = ec->repr;
+	SETCURVE();
+
+	attr++;
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, pub, attr, 1),
+		 DST_R_CRYPTOFAILURE);
+	attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen);
+	if (attr->pValue == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(attr->pValue, 0, attr->ulValueLen);
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, pub, attr, 1),
+		 DST_R_CRYPTOFAILURE);
+
+	attr++;
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, priv, attr, 1),
+		 DST_R_CRYPTOFAILURE);
+	attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen);
+	if (attr->pValue == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(attr->pValue, 0, attr->ulValueLen);
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, priv, attr, 1),
+		 DST_R_CRYPTOFAILURE);
+
+	(void) pkcs_C_DestroyObject(pk11_ctx->session, priv);
+	(void) pkcs_C_DestroyObject(pk11_ctx->session, pub);
+	pk11_return_session(pk11_ctx);
+	memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+	isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+	return (ISC_R_SUCCESS);
+
+ err:
+	pkcs11ecdsa_destroy(key);
+	if (priv != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(pk11_ctx->session, priv);
+	if (pub != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(pk11_ctx->session, pub);
+	pk11_return_session(pk11_ctx);
+	memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+	isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+	return (ret);
+}
+
+static isc_boolean_t
+pkcs11ecdsa_isprivate(const dst_key_t *key) {
+	pk11_object_t *ec = key->keydata.pkey;
+	CK_ATTRIBUTE *attr;
+
+	if (ec == NULL)
+		return (ISC_FALSE);
+	attr = pk11_attribute_bytype(ec, CKA_VALUE);
+	return (ISC_TF((attr != NULL) || ec->ontoken));
+}
+
+static void
+pkcs11ecdsa_destroy(dst_key_t *key) {
+	pk11_object_t *ec = key->keydata.pkey;
+	CK_ATTRIBUTE *attr;
+
+	if (ec == NULL)
+		return;
+
+	INSIST((ec->object == CK_INVALID_HANDLE) || ec->ontoken);
+
+	for (attr = pk11_attribute_first(ec);
+	     attr != NULL;
+	     attr = pk11_attribute_next(ec, attr))
+		switch (attr->type) {
+		case CKA_LABEL:
+		case CKA_ID:
+		case CKA_EC_PARAMS:
+		case CKA_EC_POINT:
+		case CKA_VALUE:
+			FREECURVE();
+			break;
+		}
+	if (ec->repr != NULL) {
+		memset(ec->repr, 0, ec->attrcnt * sizeof(*attr));
+		isc_mem_put(key->mctx,
+			    ec->repr,
+			    ec->attrcnt * sizeof(*attr));
+	}
+	memset(ec, 0, sizeof(*ec));
+	isc_mem_put(key->mctx, ec, sizeof(*ec));
+	key->keydata.pkey = NULL;
+}
+
+static isc_result_t
+pkcs11ecdsa_todns(const dst_key_t *key, isc_buffer_t *data) {
+	pk11_object_t *ec;
+	isc_region_t r;
+	unsigned int len;
+	CK_ATTRIBUTE *attr;
+
+	REQUIRE(key->keydata.pkey != NULL);
+
+	if (key->key_alg == DST_ALG_ECDSA256)
+		len = DNS_KEY_ECDSA256SIZE;
+	else
+		len = DNS_KEY_ECDSA384SIZE;
+
+	ec = key->keydata.pkey;
+	attr = pk11_attribute_bytype(ec, CKA_EC_POINT);
+	if ((attr == NULL) ||
+	    (attr->ulValueLen != len + 3) ||
+	    (((CK_BYTE_PTR) attr->pValue)[0] != TAG_OCTECT_STRING) ||
+	    (((CK_BYTE_PTR) attr->pValue)[1] != len + 1) ||
+	    (((CK_BYTE_PTR) attr->pValue)[2] != UNCOMPRESSED))
+		return (ISC_R_FAILURE);
+
+	isc_buffer_availableregion(data, &r);
+	if (r.length < len)
+		return (ISC_R_NOSPACE);
+	memcpy(r.base, (CK_BYTE_PTR) attr->pValue + 3, len);
+	isc_buffer_add(data, len);
+
+	return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+pkcs11ecdsa_fromdns(dst_key_t *key, isc_buffer_t *data) {
+	pk11_object_t *ec;
+	isc_region_t r;
+	unsigned int len;
+	CK_ATTRIBUTE *attr;
+
+	REQUIRE(key->key_alg == DST_ALG_ECDSA256 ||
+		key->key_alg == DST_ALG_ECDSA384);
+
+	if (key->key_alg == DST_ALG_ECDSA256)
+		len = DNS_KEY_ECDSA256SIZE;
+	else
+		len = DNS_KEY_ECDSA384SIZE;
+
+	isc_buffer_remainingregion(data, &r);
+	if (r.length == 0)
+		return (ISC_R_SUCCESS);
+	if (r.length != len)
+		return (DST_R_INVALIDPUBLICKEY);
+
+	ec = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*ec));
+	if (ec == NULL)
+		return (ISC_R_NOMEMORY);
+	memset(ec, 0, sizeof(*ec));
+	ec->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 2);
+	if (ec->repr == NULL)
+		goto nomemory;
+	ec->attrcnt = 2;
+
+	attr = ec->repr;
+	attr->type = CKA_EC_PARAMS;
+	if (key->key_alg == DST_ALG_ECDSA256) {
+		attr->pValue =
+			isc_mem_get(key->mctx, sizeof(pk11_ecc_prime256v1));
+		if (attr->pValue == NULL)
+			goto nomemory;
+		memcpy(attr->pValue,
+		       pk11_ecc_prime256v1, sizeof(pk11_ecc_prime256v1));
+		attr->ulValueLen = sizeof(pk11_ecc_prime256v1);
+	} else {
+		attr->pValue =
+			isc_mem_get(key->mctx, sizeof(pk11_ecc_secp384r1));
+		if (attr->pValue == NULL)
+			goto nomemory;
+		memcpy(attr->pValue,
+		       pk11_ecc_secp384r1, sizeof(pk11_ecc_secp384r1));
+		attr->ulValueLen = sizeof(pk11_ecc_secp384r1);
+	}
+
+	attr++;
+	attr->type = CKA_EC_POINT;
+	attr->pValue = isc_mem_get(key->mctx, len + 3);
+	if (attr->pValue == NULL)
+		goto nomemory;
+	((CK_BYTE_PTR) attr->pValue)[0] = TAG_OCTECT_STRING;
+	((CK_BYTE_PTR) attr->pValue)[1] = len + 1;
+	((CK_BYTE_PTR) attr->pValue)[2] = UNCOMPRESSED;
+	memcpy((CK_BYTE_PTR) attr->pValue + 3, r.base, len);
+	attr->ulValueLen = len + 3;
+
+	isc_buffer_forward(data, len);
+	key->keydata.pkey = ec;
+	return (ISC_R_SUCCESS);
+
+ nomemory:
+	for (attr = pk11_attribute_first(ec);
+	     attr != NULL;
+	     attr = pk11_attribute_next(ec, attr))
+		switch (attr->type) {
+		case CKA_EC_PARAMS:
+		case CKA_EC_POINT:
+			FREECURVE();
+			break;
+		}
+	if (ec->repr != NULL) {
+		memset(ec->repr, 0, ec->attrcnt * sizeof(*attr));
+		isc_mem_put(key->mctx,
+			    ec->repr,
+			    ec->attrcnt * sizeof(*attr));
+	}
+	memset(ec, 0, sizeof(*ec));
+	isc_mem_put(key->mctx, ec, sizeof(*ec));
+	return (ISC_R_NOMEMORY);
+}
+
+static isc_result_t
+pkcs11ecdsa_tofile(const dst_key_t *key, const char *directory) {
+	isc_result_t ret;
+	pk11_object_t *ec;
+	dst_private_t priv;
+	unsigned char *buf = NULL;
+	unsigned int i = 0;
+	CK_ATTRIBUTE *attr;
+
+	if (key->keydata.pkey == NULL)
+		return (DST_R_NULLKEY);
+
+	if (key->external) {
+		priv.nelements = 0;
+		return (dst__privstruct_writefile(key, &priv, directory));
+	}
+
+	ec = key->keydata.pkey;
+	attr = pk11_attribute_bytype(ec, CKA_VALUE);
+	if (attr != NULL) {
+		buf = isc_mem_get(key->mctx, attr->ulValueLen);
+		if (buf == NULL)
+			return (ISC_R_NOMEMORY);
+		priv.elements[i].tag = TAG_ECDSA_PRIVATEKEY;
+		priv.elements[i].length = (unsigned short) attr->ulValueLen;
+		memcpy(buf, attr->pValue, attr->ulValueLen);
+		priv.elements[i].data = buf;
+		i++;
+	}
+
+	if (key->engine != NULL) {
+		priv.elements[i].tag = TAG_ECDSA_ENGINE;
+		priv.elements[i].length = strlen(key->engine) + 1;
+		priv.elements[i].data = (unsigned char *)key->engine;
+		i++;
+	}
+
+	if (key->label != NULL) {
+		priv.elements[i].tag = TAG_ECDSA_LABEL;
+		priv.elements[i].length = strlen(key->label) + 1;
+		priv.elements[i].data = (unsigned char *)key->label;
+		i++;
+	}
+
+	priv.nelements = i;
+	ret = dst__privstruct_writefile(key, &priv, directory);
+
+	if (buf != NULL) {
+		memset(buf, 0, attr->ulValueLen);
+		isc_mem_put(key->mctx, buf, attr->ulValueLen);
+	}
+	return (ret);
+}
+
+static isc_result_t
+pkcs11ecdsa_fetch(dst_key_t *key, const char *engine, const char *label,
+		  dst_key_t *pub)
+{
+	CK_RV rv;
+	CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
+	CK_KEY_TYPE keyType = CKK_EC;
+	CK_ATTRIBUTE searchTemplate[] =
+	{
+		{ CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_LABEL, NULL, 0 }
+	};
+	CK_ULONG cnt;
+	CK_ATTRIBUTE *attr;
+	CK_ATTRIBUTE *pubattr;
+	pk11_object_t *ec;
+	pk11_object_t *pubec;
+	pk11_context_t *pk11_ctx = NULL;
+	isc_result_t ret;
+
+	if (label == NULL)
+		return (DST_R_NOENGINE);
+
+	ec = key->keydata.pkey;
+	pubec = pub->keydata.pkey;
+
+	ec->object = CK_INVALID_HANDLE;
+	ec->ontoken = ISC_TRUE;
+	ec->reqlogon = ISC_TRUE;
+	ec->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 2);
+	if (ec->repr == NULL)
+		return (ISC_R_NOMEMORY);
+	memset(ec->repr, 0, sizeof(*attr) * 2);
+	ec->attrcnt = 2;
+	attr = ec->repr;
+
+	attr->type = CKA_EC_PARAMS;
+	pubattr = pk11_attribute_bytype(pubec, CKA_EC_PARAMS);
+	attr->pValue = isc_mem_get(key->mctx, pubattr->ulValueLen);
+	if (attr->pValue == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memcpy(attr->pValue, pubattr->pValue, pubattr->ulValueLen);
+	attr->ulValueLen = pubattr->ulValueLen;
+	attr++;
+
+	attr->type = CKA_EC_POINT;
+	pubattr = pk11_attribute_bytype(pubec, CKA_EC_POINT);
+	attr->pValue = isc_mem_get(key->mctx, pubattr->ulValueLen);
+	if (attr->pValue == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memcpy(attr->pValue, pubattr->pValue, pubattr->ulValueLen);
+	attr->ulValueLen = pubattr->ulValueLen;
+
+	ret = pk11_parse_uri(ec, label, key->mctx, OP_EC);
+	if (ret != ISC_R_SUCCESS)
+		goto err;
+
+	pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx,
+						  sizeof(*pk11_ctx));
+	if (pk11_ctx == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	ret = pk11_get_session(pk11_ctx, OP_EC, ISC_TRUE, ISC_FALSE,
+			       ec->reqlogon, NULL, ec->slot);
+	if (ret != ISC_R_SUCCESS)
+		goto err;
+
+	attr = pk11_attribute_bytype(ec, CKA_LABEL);
+	if (attr == NULL) {
+		attr = pk11_attribute_bytype(ec, CKA_ID);
+		INSIST(attr != NULL);
+		searchTemplate[3].type = CKA_ID;
+	}
+	searchTemplate[3].pValue = attr->pValue;
+	searchTemplate[3].ulValueLen = attr->ulValueLen;
+
+	PK11_RET(pkcs_C_FindObjectsInit,
+		 (pk11_ctx->session, searchTemplate, (CK_ULONG) 4),
+		 DST_R_CRYPTOFAILURE);
+	PK11_RET(pkcs_C_FindObjects,
+		 (pk11_ctx->session, &ec->object, (CK_ULONG) 1, &cnt),
+		 DST_R_CRYPTOFAILURE);
+	(void) pkcs_C_FindObjectsFinal(pk11_ctx->session);
+	if (cnt == 0)
+		DST_RET(ISC_R_NOTFOUND);
+	if (cnt > 1)
+		DST_RET(ISC_R_EXISTS);
+
+	if (engine != NULL) {
+		key->engine = isc_mem_strdup(key->mctx, engine);
+		if (key->engine == NULL)
+			DST_RET(ISC_R_NOMEMORY);
+	}
+
+	key->label = isc_mem_strdup(key->mctx, label);
+	if (key->label == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+
+	pk11_return_session(pk11_ctx);
+	memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+	isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+	return (ISC_R_SUCCESS);
+
+ err:
+	if (pk11_ctx != NULL) {
+		pk11_return_session(pk11_ctx);
+		memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+		isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+	}
+	return (ret);
+}
+
+static isc_result_t
+pkcs11ecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
+	dst_private_t priv;
+	isc_result_t ret;
+	pk11_object_t *ec = NULL;
+	CK_ATTRIBUTE *attr, *pattr;
+	isc_mem_t *mctx = key->mctx;
+	unsigned int i;
+	const char *engine = NULL, *label = NULL;
+
+	REQUIRE(key->key_alg == DST_ALG_ECDSA256 ||
+		key->key_alg == DST_ALG_ECDSA384);
+
+	if ((pub == NULL) || (pub->keydata.pkey == NULL))
+		DST_RET(DST_R_INVALIDPRIVATEKEY);
+
+	/* read private key file */
+	ret = dst__privstruct_parse(key, DST_ALG_ECDSA256, lexer, mctx, &priv);
+	if (ret != ISC_R_SUCCESS)
+		return (ret);
+
+	if (key->external) {
+		if (priv.nelements != 0)
+			DST_RET(DST_R_INVALIDPRIVATEKEY);
+
+		key->keydata.pkey = pub->keydata.pkey;
+		pub->keydata.pkey = NULL;
+		key->key_size = pub->key_size;
+
+		dst__privstruct_free(&priv, mctx);
+		memset(&priv, 0, sizeof(priv));
+
+		return (ISC_R_SUCCESS);
+	}
+
+	for (i = 0; i < priv.nelements; i++) {
+		switch (priv.elements[i].tag) {
+		case TAG_ECDSA_ENGINE:
+			engine = (char *)priv.elements[i].data;
+			break;
+		case TAG_ECDSA_LABEL:
+			label = (char *)priv.elements[i].data;
+			break;
+		default:
+			break;
+		}
+	}
+	ec = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*ec));
+	if (ec == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(ec, 0, sizeof(*ec));
+	key->keydata.pkey = ec;
+
+	/* Is this key is stored in a HSM? See if we can fetch it. */
+	if ((label != NULL) || (engine != NULL)) {
+		ret = pkcs11ecdsa_fetch(key, engine, label, pub);
+		if (ret != ISC_R_SUCCESS)
+			goto err;
+		dst__privstruct_free(&priv, mctx);
+		memset(&priv, 0, sizeof(priv));
+		return (ret);
+	}
+
+	ec->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 3);
+	if (ec->repr == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(ec->repr, 0, sizeof(*attr) * 3);
+	ec->attrcnt = 3;
+
+	attr = ec->repr;
+	attr->type = CKA_EC_PARAMS;
+	pattr = pk11_attribute_bytype(pub->keydata.pkey, CKA_EC_PARAMS);
+	INSIST(pattr != NULL);
+	attr->pValue = isc_mem_get(key->mctx, pattr->ulValueLen);
+	if (attr->pValue == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memcpy(attr->pValue, pattr->pValue, pattr->ulValueLen);
+	attr->ulValueLen = pattr->ulValueLen;
+
+	attr++;
+	attr->type = CKA_EC_POINT;
+	pattr = pk11_attribute_bytype(pub->keydata.pkey, CKA_EC_POINT);
+	INSIST(pattr != NULL);
+	attr->pValue = isc_mem_get(key->mctx, pattr->ulValueLen);
+	if (attr->pValue == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memcpy(attr->pValue, pattr->pValue, pattr->ulValueLen);
+	attr->ulValueLen = pattr->ulValueLen;
+
+	attr++;
+	attr->type = CKA_VALUE;
+	attr->pValue = isc_mem_get(key->mctx, priv.elements[0].length);
+	if (attr->pValue == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memcpy(attr->pValue, priv.elements[0].data, priv.elements[0].length);
+	attr->ulValueLen = priv.elements[0].length;
+
+	dst__privstruct_free(&priv, mctx);
+	memset(&priv, 0, sizeof(priv));
+
+	return (ISC_R_SUCCESS);
+
+ err:
+	pkcs11ecdsa_destroy(key);
+	dst__privstruct_free(&priv, mctx);
+	memset(&priv, 0, sizeof(priv));
+	return (ret);
+}
+
+static isc_result_t
+pkcs11ecdsa_fromlabel(dst_key_t *key, const char *engine, const char *label,
+		      const char *pin)
+{
+	CK_RV rv;
+	CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE;
+	CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY;
+	CK_KEY_TYPE keyType = CKK_EC;
+	CK_ATTRIBUTE searchTemplate[] =
+	{
+		{ CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_LABEL, NULL, 0 }
+	};
+	CK_ULONG cnt;
+	CK_ATTRIBUTE *attr;
+	pk11_object_t *ec;
+	pk11_context_t *pk11_ctx = NULL;
+	isc_result_t ret;
+	unsigned int i;
+
+	UNUSED(pin);
+
+	ec = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*ec));
+	if (ec == NULL)
+		return (ISC_R_NOMEMORY);
+	memset(ec, 0, sizeof(*ec));
+	ec->object = CK_INVALID_HANDLE;
+	ec->ontoken = ISC_TRUE;
+	ec->reqlogon = ISC_TRUE;
+	key->keydata.pkey = ec;
+
+	ec->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 2);
+	if (ec->repr == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(ec->repr, 0, sizeof(*attr) * 2);
+	ec->attrcnt = 2;
+	attr = ec->repr;
+	attr[0].type = CKA_EC_PARAMS;
+	attr[1].type = CKA_EC_POINT;
+
+	ret = pk11_parse_uri(ec, label, key->mctx, OP_EC);
+	if (ret != ISC_R_SUCCESS)
+		goto err;
+
+	pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx,
+						  sizeof(*pk11_ctx));
+	if (pk11_ctx == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	ret = pk11_get_session(pk11_ctx, OP_EC, ISC_TRUE, ISC_FALSE,
+			       ec->reqlogon, NULL, ec->slot);
+	if (ret != ISC_R_SUCCESS)
+		goto err;
+
+	attr = pk11_attribute_bytype(ec, CKA_LABEL);
+	if (attr == NULL) {
+		attr = pk11_attribute_bytype(ec, CKA_ID);
+		INSIST(attr != NULL);
+		searchTemplate[3].type = CKA_ID;
+	}
+	searchTemplate[3].pValue = attr->pValue;
+	searchTemplate[3].ulValueLen = attr->ulValueLen;
+
+	PK11_RET(pkcs_C_FindObjectsInit,
+		 (pk11_ctx->session, searchTemplate, (CK_ULONG) 4),
+		 DST_R_CRYPTOFAILURE);
+	PK11_RET(pkcs_C_FindObjects,
+		 (pk11_ctx->session, &hKey, (CK_ULONG) 1, &cnt),
+		 DST_R_CRYPTOFAILURE);
+	(void) pkcs_C_FindObjectsFinal(pk11_ctx->session);
+	if (cnt == 0)
+		DST_RET(ISC_R_NOTFOUND);
+	if (cnt > 1)
+		DST_RET(ISC_R_EXISTS);
+
+	attr = ec->repr;
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, hKey, attr, 2),
+		 DST_R_CRYPTOFAILURE);
+	for (i = 0; i <= 1; i++) {
+		attr[i].pValue = isc_mem_get(key->mctx, attr[i].ulValueLen);
+		if (attr[i].pValue == NULL)
+			DST_RET(ISC_R_NOMEMORY);
+		memset(attr[i].pValue, 0, attr[i].ulValueLen);
+	}
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, hKey, attr, 2),
+		 DST_R_CRYPTOFAILURE);
+
+	keyClass = CKO_PRIVATE_KEY;
+	PK11_RET(pkcs_C_FindObjectsInit,
+		 (pk11_ctx->session, searchTemplate, (CK_ULONG) 4),
+		 DST_R_CRYPTOFAILURE);
+	PK11_RET(pkcs_C_FindObjects,
+		 (pk11_ctx->session, &ec->object, (CK_ULONG) 1, &cnt),
+		 DST_R_CRYPTOFAILURE);
+	(void) pkcs_C_FindObjectsFinal(pk11_ctx->session);
+	if (cnt == 0)
+		DST_RET(ISC_R_NOTFOUND);
+	if (cnt > 1)
+		DST_RET(ISC_R_EXISTS);
+
+	if (engine != NULL) {
+		key->engine = isc_mem_strdup(key->mctx, engine);
+		if (key->engine == NULL)
+			DST_RET(ISC_R_NOMEMORY);
+	}
+
+	key->label = isc_mem_strdup(key->mctx, label);
+	if (key->label == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+
+	pk11_return_session(pk11_ctx);
+	memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+	isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+	return (ISC_R_SUCCESS);
+
+ err:
+	pkcs11ecdsa_destroy(key);
+	if (pk11_ctx != NULL) {
+		pk11_return_session(pk11_ctx);
+		memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+		isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+	}
+	return (ret);
+}
+
+static dst_func_t pkcs11ecdsa_functions = {
+	pkcs11ecdsa_createctx,
+	NULL, /*%< createctx2 */
+	pkcs11ecdsa_destroyctx,
+	pkcs11ecdsa_adddata,
+	pkcs11ecdsa_sign,
+	pkcs11ecdsa_verify,
+	NULL, /*%< verify2 */
+	NULL, /*%< computesecret */
+	pkcs11ecdsa_compare,
+	NULL, /*%< paramcompare */
+	pkcs11ecdsa_generate,
+	pkcs11ecdsa_isprivate,
+	pkcs11ecdsa_destroy,
+	pkcs11ecdsa_todns,
+	pkcs11ecdsa_fromdns,
+	pkcs11ecdsa_tofile,
+	pkcs11ecdsa_parse,
+	NULL, /*%< cleanup */
+	pkcs11ecdsa_fromlabel,
+	NULL, /*%< dump */
+	NULL, /*%< restore */
+};
+
+isc_result_t
+dst__pkcs11ecdsa_init(dst_func_t **funcp) {
+	REQUIRE(funcp != NULL);
+	if (*funcp == NULL)
+		*funcp = &pkcs11ecdsa_functions;
+	return (ISC_R_SUCCESS);
+}
+
+#else /* PKCS11CRYPTO && HAVE_PKCS11_ECDSA */
+
+#include <isc/util.h>
+
+EMPTY_TRANSLATION_UNIT
+
+#endif /* PKCS11CRYPTO && HAVE_PKCS11_ECDSA */
+/*! \file */
diff --git a/lib/dns/pkcs11gost_link.c b/lib/dns/pkcs11gost_link.c
new file mode 100644
index 0000000..c03b285
--- /dev/null
+++ b/lib/dns/pkcs11gost_link.c
@@ -0,0 +1,949 @@
+/*
+ * Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id$ */
+
+#include <config.h>
+
+#if defined(PKCS11CRYPTO) && defined(HAVE_PKCS11_GOST)
+
+#include <isc/entropy.h>
+#include <isc/mem.h>
+#include <isc/sha2.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/keyvalues.h>
+#include <dns/log.h>
+#include <dst/result.h>
+
+#include "dst_internal.h"
+#include "dst_parse.h"
+#include "dst_pkcs11.h"
+#include "dst_gost.h"
+
+#include <pk11/pk11.h>
+#include <pk11/internal.h>
+#define WANT_GOST_PARAMS
+#include <pk11/constants.h>
+
+#include <pkcs11/pkcs11.h>
+
+/*
+ * RU CryptoPro GOST keys:
+ *  mechanisms:
+ *    CKM_GOSTR3411
+ *    CKM_GOSTR3410_WITH_GOSTR3411
+ *    CKM_GOSTR3410_KEY_PAIR_GEN
+ *  domain parameters:
+ *    CKA_GOSTR3410_PARAMS (fixed BER OID 1.2.643.2.2.35.1)
+ *    CKA_GOSTR3411_PARAMS (fixed BER OID 1.2.643.2.2.30.1)
+ *    CKA_GOST28147_PARAMS (optional, don't use)
+ *  public keys:
+ *    object class CKO_PUBLIC_KEY
+ *    key type CKK_GOSTR3410
+ *    attribute CKA_VALUE (point Q)
+ *    attribute CKA_GOSTR3410_PARAMS
+ *    attribute CKA_GOSTR3411_PARAMS
+ *    attribute CKA_GOST28147_PARAMS
+ *  private keys:
+ *    object class CKO_PRIVATE_KEY
+ *    key type CKK_GOSTR3410
+ *    attribute CKA_VALUE (big int d)
+ *    attribute CKA_GOSTR3410_PARAMS
+ *    attribute CKA_GOSTR3411_PARAMS
+ *    attribute CKA_GOST28147_PARAMS
+ *  point format: <x> <y> (little endian)
+ */
+
+#define CKA_VALUE2			CKA_PRIVATE_EXPONENT
+
+#define ISC_GOST_SIGNATURELENGTH	64
+#define ISC_GOST_PUBKEYLENGTH		64
+
+/* HASH methods */
+
+isc_result_t
+isc_gost_init(isc_gost_t *ctx) {
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_GOSTR3411, NULL, 0 };
+	int ret = ISC_R_SUCCESS;
+
+	ret = pk11_get_session(ctx, OP_GOST, ISC_TRUE, ISC_FALSE,
+			       ISC_FALSE, NULL, 0);
+	if (ret != ISC_R_SUCCESS)
+		return (ret);
+	PK11_CALL(pkcs_C_DigestInit, (ctx->session, &mech), ISC_R_FAILURE);
+	return (ret);
+}
+
+void
+isc_gost_invalidate(isc_gost_t *ctx) {
+	CK_BYTE garbage[ISC_GOST_DIGESTLENGTH];
+	CK_ULONG len = ISC_GOST_DIGESTLENGTH;
+
+	if (ctx->handle == NULL)
+		return;
+	(void) pkcs_C_DigestFinal(ctx->session, garbage, &len);
+	memset(garbage, 0, sizeof(garbage));
+	pk11_return_session(ctx);
+}
+
+isc_result_t
+isc_gost_update(isc_gost_t *ctx, const unsigned char *buf, unsigned int len) {
+	CK_RV rv;
+	CK_BYTE_PTR pPart;
+	int ret = ISC_R_SUCCESS;
+
+	DE_CONST(buf, pPart);
+	PK11_CALL(pkcs_C_DigestUpdate,
+		  (ctx->session, pPart, (CK_ULONG) len),
+		  ISC_R_FAILURE);
+	return (ret);
+}
+
+isc_result_t
+isc_gost_final(isc_gost_t *ctx, unsigned char *digest) {
+	CK_RV rv;
+	CK_ULONG len = ISC_GOST_DIGESTLENGTH;
+	int ret = ISC_R_SUCCESS;
+
+	PK11_CALL(pkcs_C_DigestFinal,
+		  (ctx->session, (CK_BYTE_PTR) digest, &len),
+		  ISC_R_FAILURE);
+	pk11_return_session(ctx);
+	return (ret);
+}
+
+/* DST methods */
+
+static CK_BBOOL truevalue = TRUE;
+static CK_BBOOL falsevalue = FALSE;
+
+#define DST_RET(a) {ret = a; goto err;}
+
+static isc_result_t pkcs11gost_todns(const dst_key_t *key, isc_buffer_t *data);
+static void pkcs11gost_destroy(dst_key_t *key);
+
+static isc_result_t
+pkcs11gost_createctx_sign(dst_key_t *key, dst_context_t *dctx) {
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_GOSTR3410_WITH_GOSTR3411, NULL, 0 };
+	CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
+	CK_KEY_TYPE keyType = CKK_GOSTR3410;
+	CK_ATTRIBUTE keyTemplate[] =
+	{
+		{ CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_VALUE, NULL, 0 },
+		{ CKA_GOSTR3410_PARAMS, pk11_gost_a_paramset,
+		  (CK_ULONG) sizeof(pk11_gost_a_paramset) },
+		{ CKA_GOSTR3411_PARAMS, pk11_gost_paramset,
+		  (CK_ULONG) sizeof(pk11_gost_paramset) }
+	};
+	CK_ATTRIBUTE *attr;
+	pk11_object_t *gost;
+	pk11_context_t *pk11_ctx;
+	isc_result_t ret;
+	unsigned int i;
+
+	pk11_ctx = (pk11_context_t *) isc_mem_get(dctx->mctx,
+						  sizeof(*pk11_ctx));
+	if (pk11_ctx == NULL)
+		return (ISC_R_NOMEMORY);
+	ret = pk11_get_session(pk11_ctx, OP_GOST, ISC_TRUE, ISC_FALSE,
+			       ISC_FALSE, NULL, pk11_get_best_token(OP_GOST));
+	if (ret != ISC_R_SUCCESS)
+		goto err;
+
+	gost = key->keydata.pkey;
+	if (gost->ontoken && (gost->object != CK_INVALID_HANDLE)) {
+		pk11_ctx->ontoken = gost->ontoken;
+		pk11_ctx->object = gost->object;
+		goto token_key;
+	}
+
+	for (attr = pk11_attribute_first(gost);
+	     attr != NULL;
+	     attr = pk11_attribute_next(gost, attr))
+		switch (attr->type) {
+		case CKA_VALUE2:
+			INSIST(keyTemplate[6].type == CKA_VALUE);
+			keyTemplate[6].pValue = isc_mem_get(dctx->mctx,
+							    attr->ulValueLen);
+			if (keyTemplate[6].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(keyTemplate[6].pValue,
+			       attr->pValue,
+			       attr->ulValueLen);
+			keyTemplate[6].ulValueLen = attr->ulValueLen;
+			break;
+		}
+	pk11_ctx->object = CK_INVALID_HANDLE;
+	pk11_ctx->ontoken = ISC_FALSE;
+	PK11_RET(pkcs_C_CreateObject,
+		 (pk11_ctx->session,
+		  keyTemplate, (CK_ULONG) 9,
+		  &pk11_ctx->object),
+		 ISC_R_FAILURE);
+
+    token_key:
+
+	PK11_RET(pkcs_C_SignInit,
+		 (pk11_ctx->session, &mech, pk11_ctx->object),
+		 ISC_R_FAILURE);
+
+	dctx->ctxdata.pk11_ctx = pk11_ctx;
+
+	for (i = 6; i <= 6; i++)
+		if (keyTemplate[i].pValue != NULL) {
+			memset(keyTemplate[i].pValue, 0,
+			       keyTemplate[i].ulValueLen);
+			isc_mem_put(dctx->mctx,
+				    keyTemplate[i].pValue,
+				    keyTemplate[i].ulValueLen);
+		}
+
+	return (ISC_R_SUCCESS);
+
+    err:
+	if (!pk11_ctx->ontoken && (pk11_ctx->object != CK_INVALID_HANDLE))
+		(void) pkcs_C_DestroyObject(pk11_ctx->session, pk11_ctx->object);
+	for (i = 6; i <= 6; i++)
+		if (keyTemplate[i].pValue != NULL) {
+			memset(keyTemplate[i].pValue, 0,
+			       keyTemplate[i].ulValueLen);
+			isc_mem_put(dctx->mctx,
+				    keyTemplate[i].pValue,
+				    keyTemplate[i].ulValueLen);
+		}
+	pk11_return_session(pk11_ctx);
+	memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+	isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+	return (ret);
+}
+
+static isc_result_t
+pkcs11gost_createctx_verify(dst_key_t *key, dst_context_t *dctx) {
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_GOSTR3410_WITH_GOSTR3411, NULL, 0 };
+	CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY;
+	CK_KEY_TYPE keyType = CKK_GOSTR3410;
+	CK_ATTRIBUTE keyTemplate[] =
+	{
+		{ CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_VERIFY, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_VALUE, NULL, 0 },
+		{ CKA_GOSTR3410_PARAMS, pk11_gost_a_paramset,
+		  (CK_ULONG) sizeof(pk11_gost_a_paramset) },
+		{ CKA_GOSTR3411_PARAMS, pk11_gost_paramset,
+		  (CK_ULONG) sizeof(pk11_gost_paramset) }
+	};
+	CK_ATTRIBUTE *attr;
+	pk11_object_t *gost;
+	pk11_context_t *pk11_ctx;
+	isc_result_t ret;
+	unsigned int i;
+
+	pk11_ctx = (pk11_context_t *) isc_mem_get(dctx->mctx,
+						  sizeof(*pk11_ctx));
+	if (pk11_ctx == NULL)
+		return (ISC_R_NOMEMORY);
+	ret = pk11_get_session(pk11_ctx, OP_GOST, ISC_TRUE, ISC_FALSE,
+			       ISC_FALSE, NULL, pk11_get_best_token(OP_GOST));
+	if (ret != ISC_R_SUCCESS)
+		goto err;
+
+	gost = key->keydata.pkey;
+	if (gost->ontoken && (gost->object != CK_INVALID_HANDLE)) {
+		pk11_ctx->ontoken = gost->ontoken;
+		pk11_ctx->object = gost->object;
+		goto token_key;
+	}
+
+	for (attr = pk11_attribute_first(gost);
+	     attr != NULL;
+	     attr = pk11_attribute_next(gost, attr))
+		switch (attr->type) {
+		case CKA_VALUE:
+			INSIST(keyTemplate[5].type == attr->type);
+			keyTemplate[5].pValue = isc_mem_get(dctx->mctx,
+							    attr->ulValueLen);
+			if (keyTemplate[5].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(keyTemplate[5].pValue,
+			       attr->pValue,
+			       attr->ulValueLen);
+			keyTemplate[5].ulValueLen = attr->ulValueLen;
+			break;
+		}
+	pk11_ctx->object = CK_INVALID_HANDLE;
+	pk11_ctx->ontoken = ISC_FALSE;
+	PK11_RET(pkcs_C_CreateObject,
+		 (pk11_ctx->session,
+		  keyTemplate, (CK_ULONG) 8,
+		  &pk11_ctx->object),
+		 ISC_R_FAILURE);
+
+    token_key:
+
+	PK11_RET(pkcs_C_VerifyInit,
+		 (pk11_ctx->session, &mech, pk11_ctx->object),
+		 ISC_R_FAILURE);
+
+	dctx->ctxdata.pk11_ctx = pk11_ctx;
+
+	for (i = 5; i <= 5; i++)
+		if (keyTemplate[i].pValue != NULL) {
+			memset(keyTemplate[i].pValue, 0,
+			       keyTemplate[i].ulValueLen);
+			isc_mem_put(dctx->mctx,
+				    keyTemplate[i].pValue,
+				    keyTemplate[i].ulValueLen);
+		}
+
+	return (ISC_R_SUCCESS);
+
+    err:
+	if (!pk11_ctx->ontoken && (pk11_ctx->object != CK_INVALID_HANDLE))
+		(void) pkcs_C_DestroyObject(pk11_ctx->session, pk11_ctx->object);
+	for (i = 5; i <= 5; i++)
+		if (keyTemplate[i].pValue != NULL) {
+			memset(keyTemplate[i].pValue, 0,
+			       keyTemplate[i].ulValueLen);
+			isc_mem_put(dctx->mctx,
+				    keyTemplate[i].pValue,
+				    keyTemplate[i].ulValueLen);
+		}
+	pk11_return_session(pk11_ctx);
+	memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+	isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+	return (ret);
+}
+
+static isc_result_t
+pkcs11gost_createctx(dst_key_t *key, dst_context_t *dctx) {
+	if (dctx->use == DO_SIGN)
+		return (pkcs11gost_createctx_sign(key, dctx));
+	else
+		return (pkcs11gost_createctx_verify(key, dctx));
+}
+
+static void
+pkcs11gost_destroyctx(dst_context_t *dctx) {
+	pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+
+	if (pk11_ctx != NULL) {
+		if (!pk11_ctx->ontoken &&
+		    (pk11_ctx->object != CK_INVALID_HANDLE))
+			(void) pkcs_C_DestroyObject(pk11_ctx->session,
+					       pk11_ctx->object);
+		pk11_return_session(pk11_ctx);
+		memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+		isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+		dctx->ctxdata.pk11_ctx = NULL;
+	}
+}
+
+static isc_result_t
+pkcs11gost_adddata(dst_context_t *dctx, const isc_region_t *data) {
+	CK_RV rv;
+	pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+	isc_result_t ret = ISC_R_SUCCESS;
+
+	if (dctx->use == DO_SIGN)
+		PK11_CALL(pkcs_C_SignUpdate,
+			  (pk11_ctx->session,
+			   (CK_BYTE_PTR) data->base,
+			   (CK_ULONG) data->length),
+			  ISC_R_FAILURE);
+	else
+		PK11_CALL(pkcs_C_VerifyUpdate,
+			  (pk11_ctx->session,
+			   (CK_BYTE_PTR) data->base,
+			   (CK_ULONG) data->length),
+			  ISC_R_FAILURE);
+	return (ret);
+}
+
+static isc_result_t
+pkcs11gost_sign(dst_context_t *dctx, isc_buffer_t *sig) {
+	CK_RV rv;
+	CK_ULONG siglen = ISC_GOST_SIGNATURELENGTH;
+	isc_region_t r;
+	pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+	isc_result_t ret = ISC_R_SUCCESS;
+
+	isc_buffer_availableregion(sig, &r);
+	if (r.length < ISC_GOST_SIGNATURELENGTH)
+		return (ISC_R_NOSPACE);
+
+	PK11_RET(pkcs_C_SignFinal,
+		 (pk11_ctx->session, (CK_BYTE_PTR) r.base, &siglen),
+		 DST_R_SIGNFAILURE);
+	if (siglen != ISC_GOST_SIGNATURELENGTH)
+		return (DST_R_SIGNFAILURE);
+
+	isc_buffer_add(sig, ISC_GOST_SIGNATURELENGTH);
+
+    err:
+	return (ret);
+}
+
+static isc_result_t
+pkcs11gost_verify(dst_context_t *dctx, const isc_region_t *sig) {
+	CK_RV rv;
+	pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+	isc_result_t ret = ISC_R_SUCCESS;
+
+	PK11_CALL(pkcs_C_VerifyFinal,
+		  (pk11_ctx->session,
+		   (CK_BYTE_PTR) sig->base,
+		   (CK_ULONG) sig->length),
+		  DST_R_VERIFYFAILURE);
+	return (ret);
+}
+
+static isc_boolean_t
+pkcs11gost_compare(const dst_key_t *key1, const dst_key_t *key2) {
+	pk11_object_t *gost1, *gost2;
+	CK_ATTRIBUTE *attr1, *attr2;
+
+	gost1 = key1->keydata.pkey;
+	gost2 = key2->keydata.pkey;
+
+	if ((gost1 == NULL) && (gost2 == NULL))
+		return (ISC_TRUE);
+	else if ((gost1 == NULL) || (gost2 == NULL))
+		return (ISC_FALSE);
+
+	attr1 = pk11_attribute_bytype(gost1, CKA_VALUE);
+	attr2 = pk11_attribute_bytype(gost2, CKA_VALUE);
+	if ((attr1 == NULL) && (attr2 == NULL))
+		return (ISC_TRUE);
+	else if ((attr1 == NULL) || (attr2 == NULL) ||
+		 (attr1->ulValueLen != attr2->ulValueLen) ||
+		 memcmp(attr1->pValue, attr2->pValue, attr1->ulValueLen))
+		return (ISC_FALSE);
+
+	attr1 = pk11_attribute_bytype(gost1, CKA_VALUE2);
+	attr2 = pk11_attribute_bytype(gost2, CKA_VALUE2);
+	if (((attr1 != NULL) || (attr2 != NULL)) &&
+	    ((attr1 == NULL) || (attr2 == NULL) ||
+	     (attr1->ulValueLen != attr2->ulValueLen) ||
+	     memcmp(attr1->pValue, attr2->pValue, attr1->ulValueLen)))
+		return (ISC_FALSE);
+
+	if (!gost1->ontoken && !gost2->ontoken)
+		return (ISC_TRUE);
+	else if (gost1->ontoken || gost2->ontoken ||
+		 (gost1->object != gost2->object))
+		return (ISC_FALSE);
+
+	return (ISC_TRUE);
+}
+
+static isc_result_t
+pkcs11gost_generate(dst_key_t *key, int unused, void (*callback)(int)) {
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_GOSTR3410_KEY_PAIR_GEN, NULL, 0 };
+	CK_KEY_TYPE  keyType = CKK_GOSTR3410;
+	CK_OBJECT_HANDLE pub = CK_INVALID_HANDLE;
+	CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY;
+	CK_ATTRIBUTE pubTemplate[] =
+	{
+		{ CKA_CLASS, &pubClass, (CK_ULONG) sizeof(pubClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_VERIFY, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_GOSTR3410_PARAMS, pk11_gost_a_paramset,
+		  (CK_ULONG) sizeof(pk11_gost_a_paramset) },
+		{ CKA_GOSTR3411_PARAMS, pk11_gost_paramset,
+		  (CK_ULONG) sizeof(pk11_gost_paramset) }
+	};
+	CK_OBJECT_HANDLE priv = CK_INVALID_HANDLE;
+	CK_OBJECT_HANDLE privClass = CKO_PRIVATE_KEY;
+	CK_ATTRIBUTE privTemplate[] =
+	{
+		{ CKA_CLASS, &privClass, (CK_ULONG) sizeof(privClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_EXTRACTABLE, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+	};
+	CK_ATTRIBUTE *attr;
+	pk11_object_t *gost;
+	pk11_context_t *pk11_ctx;
+	isc_result_t ret;
+
+	UNUSED(unused);
+	UNUSED(callback);
+
+	pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx,
+						  sizeof(*pk11_ctx));
+	if (pk11_ctx == NULL)
+		return (ISC_R_NOMEMORY);
+	ret = pk11_get_session(pk11_ctx, OP_GOST, ISC_TRUE, ISC_FALSE,
+			       ISC_FALSE, NULL, pk11_get_best_token(OP_GOST));
+	if (ret != ISC_R_SUCCESS)
+		goto err;
+
+	PK11_RET(pkcs_C_GenerateKeyPair,
+		 (pk11_ctx->session, &mech,
+		  pubTemplate, (CK_ULONG) 7,
+		  privTemplate, (CK_ULONG) 7,
+		  &pub, &priv),
+		 DST_R_CRYPTOFAILURE);
+
+	gost = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*gost));
+	if (gost == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(gost, 0, sizeof(*gost));
+	key->keydata.pkey = gost;
+	gost->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx,
+						  sizeof(*attr) * 2);
+	if (gost->repr == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(gost->repr, 0, sizeof(*attr) * 2);
+	gost->attrcnt = 2;
+
+	attr = gost->repr;
+	attr[0].type = CKA_VALUE;
+	attr[1].type = CKA_VALUE2;
+
+	attr = gost->repr;
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, pub, attr, 1),
+		 DST_R_CRYPTOFAILURE);
+	attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen);
+	if (attr->pValue == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(attr->pValue, 0, attr->ulValueLen);
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, pub, attr, 1),
+		 DST_R_CRYPTOFAILURE);
+
+	attr++;
+	attr->type = CKA_VALUE;
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, priv, attr, 1),
+		 DST_R_CRYPTOFAILURE);
+	attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen);
+	if (attr->pValue == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(attr->pValue, 0, attr->ulValueLen);
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, priv, attr, 1),
+		 DST_R_CRYPTOFAILURE);
+	attr->type = CKA_VALUE2;
+
+	(void) pkcs_C_DestroyObject(pk11_ctx->session, priv);
+	(void) pkcs_C_DestroyObject(pk11_ctx->session, pub);
+	pk11_return_session(pk11_ctx);
+	memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+	isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+	return (ISC_R_SUCCESS);
+
+    err:
+	pkcs11gost_destroy(key);
+	if (priv != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(pk11_ctx->session, priv);
+	if (pub != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(pk11_ctx->session, pub);
+	pk11_return_session(pk11_ctx);
+	memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+	isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+	return (ret);
+}
+
+static isc_boolean_t
+pkcs11gost_isprivate(const dst_key_t *key) {
+	pk11_object_t *gost = key->keydata.pkey;
+	CK_ATTRIBUTE *attr;
+
+	if (gost == NULL)
+		return (ISC_FALSE);
+	attr = pk11_attribute_bytype(gost, CKA_VALUE2);
+	return (ISC_TF((attr != NULL) || gost->ontoken));
+}
+
+static void
+pkcs11gost_destroy(dst_key_t *key) {
+	pk11_object_t *gost = key->keydata.pkey;
+	CK_ATTRIBUTE *attr;
+
+	if (gost == NULL)
+		return;
+
+	INSIST((gost->object == CK_INVALID_HANDLE) || gost->ontoken);
+
+	for (attr = pk11_attribute_first(gost);
+	     attr != NULL;
+	     attr = pk11_attribute_next(gost, attr))
+		switch (attr->type) {
+		case CKA_VALUE:
+		case CKA_VALUE2:
+			if (attr->pValue != NULL) {
+				memset(attr->pValue, 0, attr->ulValueLen);
+				isc_mem_put(key->mctx,
+					    attr->pValue,
+					    attr->ulValueLen);
+			}
+			break;
+		}
+	if (gost->repr != NULL) {
+		memset(gost->repr, 0, gost->attrcnt * sizeof(*attr));
+		isc_mem_put(key->mctx,
+			    gost->repr,
+			    gost->attrcnt * sizeof(*attr));
+	}
+	memset(gost, 0, sizeof(*gost));
+	isc_mem_put(key->mctx, gost, sizeof(*gost));
+	key->keydata.pkey = NULL;
+}
+
+static isc_result_t
+pkcs11gost_todns(const dst_key_t *key, isc_buffer_t *data) {
+	pk11_object_t *gost;
+	isc_region_t r;
+	CK_ATTRIBUTE *attr;
+
+	REQUIRE(key->keydata.pkey != NULL);
+
+	gost = key->keydata.pkey;
+	attr = pk11_attribute_bytype(gost, CKA_VALUE);
+	if ((attr == NULL) || (attr->ulValueLen != ISC_GOST_PUBKEYLENGTH))
+		return (ISC_R_FAILURE);
+
+	isc_buffer_availableregion(data, &r);
+	if (r.length < ISC_GOST_PUBKEYLENGTH)
+		return (ISC_R_NOSPACE);
+	memcpy(r.base, (CK_BYTE_PTR) attr->pValue, ISC_GOST_PUBKEYLENGTH);
+	isc_buffer_add(data, ISC_GOST_PUBKEYLENGTH);
+
+	return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+pkcs11gost_fromdns(dst_key_t *key, isc_buffer_t *data) {
+	pk11_object_t *gost;
+	isc_region_t r;
+	CK_ATTRIBUTE *attr;
+
+	isc_buffer_remainingregion(data, &r);
+	if (r.length == 0)
+		return (ISC_R_SUCCESS);
+	if (r.length != ISC_GOST_PUBKEYLENGTH)
+		return (DST_R_INVALIDPUBLICKEY);
+
+	gost = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*gost));
+	if (gost == NULL)
+		return (ISC_R_NOMEMORY);
+	memset(gost, 0, sizeof(*gost));
+	gost->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr));
+	if (gost->repr == NULL)
+		goto nomemory;
+	gost->attrcnt = 1;
+
+	attr = gost->repr;
+	attr->type = CKA_VALUE;
+	attr->pValue = isc_mem_get(key->mctx, ISC_GOST_PUBKEYLENGTH);
+	if (attr->pValue == NULL)
+		goto nomemory;
+	memcpy((CK_BYTE_PTR) attr->pValue, r.base, ISC_GOST_PUBKEYLENGTH);
+	attr->ulValueLen = ISC_GOST_PUBKEYLENGTH;
+
+	isc_buffer_forward(data, ISC_GOST_PUBKEYLENGTH);
+	key->keydata.pkey = gost;
+	return (ISC_R_SUCCESS);
+
+ nomemory:
+	for (attr = pk11_attribute_first(gost);
+	     attr != NULL;
+	     attr = pk11_attribute_next(gost, attr))
+		switch (attr->type) {
+		case CKA_VALUE:
+			if (attr->pValue != NULL) {
+				memset(attr->pValue, 0, attr->ulValueLen);
+				isc_mem_put(key->mctx,
+					    attr->pValue,
+					    attr->ulValueLen);
+			}
+			break;
+		}
+	if (gost->repr != NULL) {
+		memset(gost->repr, 0, gost->attrcnt * sizeof(*attr));
+		isc_mem_put(key->mctx,
+			    gost->repr,
+			    gost->attrcnt * sizeof(*attr));
+	}
+	memset(gost, 0, sizeof(*gost));
+	isc_mem_put(key->mctx, gost, sizeof(*gost));
+	return (ISC_R_NOMEMORY);
+}
+
+static unsigned char gost_private_der[39] = {
+	0x30, 0x45, 0x02, 0x01, 0x00, 0x30, 0x1c, 0x06,
+	0x06, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x13, 0x30,
+	0x12, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02,
+	0x23, 0x01, 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02,
+	0x02, 0x1e, 0x01, 0x04, 0x22, 0x02, 0x20
+};
+
+#ifdef PREFER_GOSTASN1
+
+static isc_result_t
+pkcs11gost_tofile(const dst_key_t *key, const char *directory) {
+	isc_result_t ret;
+	pk11_object_t *gost;
+	dst_private_t priv;
+	unsigned char *buf = NULL;
+	unsigned int i = 0;
+	CK_ATTRIBUTE *attr;
+	int adj;
+
+	if (key->keydata.pkey == NULL)
+		return (DST_R_NULLKEY);
+
+	if (key->external) {
+		priv.nelements = 0;
+		return (dst__privstruct_writefile(key, &priv, directory));
+	}
+
+	gost = key->keydata.pkey;
+	attr = pk11_attribute_bytype(gost, CKA_VALUE2);
+	if (attr != NULL) {
+		buf = isc_mem_get(key->mctx, attr->ulValueLen + 39);
+		if (buf == NULL)
+			return (ISC_R_NOMEMORY);
+		priv.elements[i].tag = TAG_GOST_PRIVASN1;
+		priv.elements[i].length =
+			(unsigned short) attr->ulValueLen + 39;
+		memcpy(buf, gost_private_der, 39);
+		memcpy(buf +39, attr->pValue, attr->ulValueLen);
+		adj = (int) attr->ulValueLen - 32;
+		if (adj != 0) {
+			buf[1] += adj;
+			buf[36] += adj;
+			buf[38] += adj;
+		}
+		priv.elements[i].data = buf;
+		i++;
+	} else
+		return (DST_R_CRYPTOFAILURE);
+
+	priv.nelements = i;
+	ret = dst__privstruct_writefile(key, &priv, directory);
+
+	if (buf != NULL) {
+		memset(buf, 0, attr->ulValueLen);
+		isc_mem_put(key->mctx, buf, attr->ulValueLen);
+	}
+	return (ret);
+}
+
+#else
+
+static isc_result_t
+pkcs11gost_tofile(const dst_key_t *key, const char *directory) {
+	isc_result_t ret;
+	pk11_object_t *gost;
+	dst_private_t priv;
+	unsigned char *buf = NULL;
+	unsigned int i = 0;
+	CK_ATTRIBUTE *attr;
+
+	if (key->keydata.pkey == NULL)
+		return (DST_R_NULLKEY);
+
+	if (key->external) {
+		priv.nelements = 0;
+		return (dst__privstruct_writefile(key, &priv, directory));
+	}
+
+	gost = key->keydata.pkey;
+	attr = pk11_attribute_bytype(gost, CKA_VALUE2);
+	if (attr != NULL) {
+		buf = isc_mem_get(key->mctx, attr->ulValueLen);
+		if (buf == NULL)
+			return (ISC_R_NOMEMORY);
+		priv.elements[i].tag = TAG_GOST_PRIVRAW;
+		priv.elements[i].length = (unsigned short) attr->ulValueLen;
+		memcpy(buf, attr->pValue, attr->ulValueLen);
+		priv.elements[i].data = buf;
+		i++;
+	} else
+		return (DST_R_CRYPTOFAILURE);
+
+	priv.nelements = i;
+	ret = dst__privstruct_writefile(key, &priv, directory);
+
+	if (buf != NULL) {
+		memset(buf, 0, attr->ulValueLen);
+		isc_mem_put(key->mctx, buf, attr->ulValueLen);
+	}
+	return (ret);
+}
+#endif
+
+static isc_result_t
+pkcs11gost_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
+	dst_private_t priv;
+	isc_result_t ret;
+	pk11_object_t *gost = NULL;
+	CK_ATTRIBUTE *attr, *pattr;
+	isc_mem_t *mctx = key->mctx;
+
+	if ((pub == NULL) || (pub->keydata.pkey == NULL))
+		DST_RET(DST_R_INVALIDPRIVATEKEY);
+
+	/* read private key file */
+	ret = dst__privstruct_parse(key, DST_ALG_ECDSA256, lexer, mctx, &priv);
+	if (ret != ISC_R_SUCCESS)
+		return (ret);
+
+	if (key->external) {
+		if (priv.nelements != 0)
+			DST_RET(DST_R_INVALIDPRIVATEKEY);
+
+		key->keydata.pkey = pub->keydata.pkey;
+		pub->keydata.pkey = NULL;
+		key->key_size = pub->key_size;
+
+		dst__privstruct_free(&priv, mctx);
+		memset(&priv, 0, sizeof(priv));
+
+		return (ISC_R_SUCCESS);
+	}
+
+	if (priv.elements[0].tag == TAG_GOST_PRIVASN1) {
+		int adj = (int) priv.elements[0].length - (39 + 32);
+		unsigned char buf[39];
+
+		if ((adj > 0) || (adj < -31))
+			DST_RET(DST_R_INVALIDPRIVATEKEY);
+		memcpy(buf, gost_private_der, 39);
+		if (adj != 0) {
+			buf[1] += adj;
+			buf[36] += adj;
+			buf[38] += adj;
+		}
+		if (memcmp(priv.elements[0].data, buf, 39) != 0)
+			DST_RET(DST_R_INVALIDPRIVATEKEY);
+		priv.elements[0].tag = TAG_GOST_PRIVRAW;
+		priv.elements[0].length -= 39;
+		memmove(priv.elements[0].data,
+			priv.elements[0].data + 39,
+			32 + adj);
+	}
+
+	gost = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*gost));
+	if (gost == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(gost, 0, sizeof(*gost));
+	key->keydata.pkey = gost;
+
+	gost->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx,
+						  sizeof(*attr) * 2);
+	if (gost->repr == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(gost->repr, 0, sizeof(*attr) * 2);
+	gost->attrcnt = 2;
+
+	attr = gost->repr;
+	attr->type = CKA_VALUE;
+	pattr = pk11_attribute_bytype(pub->keydata.pkey, CKA_VALUE);
+	INSIST(pattr != NULL);
+	attr->pValue = isc_mem_get(key->mctx, pattr->ulValueLen);
+	if (attr->pValue == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memcpy(attr->pValue, pattr->pValue, pattr->ulValueLen);
+	attr->ulValueLen = pattr->ulValueLen;
+
+	attr++;
+	attr->type = CKA_VALUE2;
+	attr->pValue = isc_mem_get(key->mctx, priv.elements[0].length);
+	if (attr->pValue == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memcpy(attr->pValue, priv.elements[0].data, priv.elements[0].length);
+	attr->ulValueLen = priv.elements[0].length;
+
+	dst__privstruct_free(&priv, mctx);
+	memset(&priv, 0, sizeof(priv));
+
+	return (ISC_R_SUCCESS);
+
+ err:
+	pkcs11gost_destroy(key);
+	dst__privstruct_free(&priv, mctx);
+	memset(&priv, 0, sizeof(priv));
+	return (ret);
+}
+
+static dst_func_t pkcs11gost_functions = {
+	pkcs11gost_createctx,
+	NULL, /*%< createctx2 */
+	pkcs11gost_destroyctx,
+	pkcs11gost_adddata,
+	pkcs11gost_sign,
+	pkcs11gost_verify,
+	NULL, /*%< verify2 */
+	NULL, /*%< computesecret */
+	pkcs11gost_compare,
+	NULL, /*%< paramcompare */
+	pkcs11gost_generate,
+	pkcs11gost_isprivate,
+	pkcs11gost_destroy,
+	pkcs11gost_todns,
+	pkcs11gost_fromdns,
+	pkcs11gost_tofile,
+	pkcs11gost_parse,
+	NULL, /*%< cleanup */
+	NULL, /*%< fromlabel */
+	NULL, /*%< dump */
+	NULL, /*%< restore */
+};
+
+isc_result_t
+dst__pkcs11gost_init(dst_func_t **funcp) {
+	REQUIRE(funcp != NULL);
+	if (*funcp == NULL)
+		*funcp = &pkcs11gost_functions;
+	return (ISC_R_SUCCESS);
+}
+
+#else /* PKCS11CRYPTO && HAVE_PKCS11_GOST */
+
+#include <isc/util.h>
+
+EMPTY_TRANSLATION_UNIT
+
+#endif /* PKCS11CRYPTO && HAVE_PKCS11_GOST */
+/*! \file */
diff --git a/lib/dns/pkcs11rsa_link.c b/lib/dns/pkcs11rsa_link.c
new file mode 100644
index 0000000..010d4b6
--- /dev/null
+++ b/lib/dns/pkcs11rsa_link.c
@@ -0,0 +1,1583 @@
+/*
+ * Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id$ */
+
+#ifdef PKCS11CRYPTO
+
+#include <config.h>
+
+#include <isc/entropy.h>
+#include <isc/md5.h>
+#include <isc/sha1.h>
+#include <isc/sha2.h>
+#include <isc/mem.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dst/result.h>
+
+#include "dst_internal.h"
+#include "dst_parse.h"
+#include "dst_pkcs11.h"
+
+#include <pk11/internal.h>
+
+/*
+ * Limit the size of public exponents.
+ */
+#ifndef RSA_MAX_PUBEXP_BITS
+#define RSA_MAX_PUBEXP_BITS    35
+#endif
+
+#define DST_RET(a) {ret = a; goto err;}
+
+static CK_BBOOL truevalue = TRUE;
+static CK_BBOOL falsevalue = FALSE;
+
+static isc_result_t pkcs11rsa_todns(const dst_key_t *key, isc_buffer_t *data);
+static void pkcs11rsa_destroy(dst_key_t *key);
+static isc_result_t pkcs11rsa_fetch(dst_key_t *key, const char *engine,
+				    const char *label, dst_key_t *pub);
+
+static isc_result_t
+pkcs11rsa_createctx_sign(dst_key_t *key, dst_context_t *dctx) {
+	CK_RV rv;
+	CK_MECHANISM mech = { 0, NULL, 0 };
+	CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
+	CK_KEY_TYPE keyType = CKK_RSA;
+	CK_ATTRIBUTE keyTemplate[] =
+	{
+		{ CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_MODULUS, NULL, 0 },
+		{ CKA_PUBLIC_EXPONENT, NULL, 0 },
+		{ CKA_PRIVATE_EXPONENT, NULL, 0 },
+		{ CKA_PRIME_1, NULL, 0 },
+		{ CKA_PRIME_2, NULL, 0 },
+		{ CKA_EXPONENT_1, NULL, 0 },
+		{ CKA_EXPONENT_2, NULL, 0 },
+		{ CKA_COEFFICIENT, NULL, 0 }
+	};
+	CK_ATTRIBUTE *attr;
+	CK_SLOT_ID slotid;
+	pk11_object_t *rsa;
+	pk11_context_t *pk11_ctx;
+	isc_result_t ret;
+	unsigned int i;
+
+	REQUIRE(key->key_alg == DST_ALG_RSAMD5 ||
+		key->key_alg == DST_ALG_RSASHA1 ||
+		key->key_alg == DST_ALG_NSEC3RSASHA1 ||
+		key->key_alg == DST_ALG_RSASHA256 ||
+		key->key_alg == DST_ALG_RSASHA512);
+
+	rsa = key->keydata.pkey;
+
+	pk11_ctx = (pk11_context_t *) isc_mem_get(dctx->mctx,
+						  sizeof(*pk11_ctx));
+	if (pk11_ctx == NULL)
+		return (ISC_R_NOMEMORY);
+	memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+	if (rsa->ontoken)
+		slotid = rsa->slot;
+	else
+		slotid = pk11_get_best_token(OP_RSA);
+	ret = pk11_get_session(pk11_ctx, OP_RSA, ISC_TRUE, ISC_FALSE,
+			       rsa->reqlogon, NULL, slotid);
+	if (ret != ISC_R_SUCCESS)
+		goto err;
+
+	if (rsa->ontoken && (rsa->object != CK_INVALID_HANDLE)) {
+		pk11_ctx->ontoken = rsa->ontoken;
+		pk11_ctx->object = rsa->object;
+		goto token_key;
+	}
+
+	for (attr = pk11_attribute_first(rsa);
+	     attr != NULL;
+	     attr = pk11_attribute_next(rsa, attr))
+		switch (attr->type) {
+		case CKA_MODULUS:
+			INSIST(keyTemplate[6].type == attr->type);
+			keyTemplate[6].pValue = isc_mem_get(dctx->mctx,
+							    attr->ulValueLen);
+			if (keyTemplate[6].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(keyTemplate[6].pValue,
+			       attr->pValue,
+			       attr->ulValueLen);
+			keyTemplate[6].ulValueLen = attr->ulValueLen;
+			break;
+		case CKA_PUBLIC_EXPONENT:
+			INSIST(keyTemplate[7].type == attr->type);
+			keyTemplate[7].pValue = isc_mem_get(dctx->mctx,
+							    attr->ulValueLen);
+			if (keyTemplate[7].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(keyTemplate[7].pValue,
+			       attr->pValue,
+			       attr->ulValueLen);
+			keyTemplate[7].ulValueLen = attr->ulValueLen;
+			break;
+		case CKA_PRIVATE_EXPONENT:
+			INSIST(keyTemplate[8].type == attr->type);
+			keyTemplate[8].pValue = isc_mem_get(dctx->mctx,
+							    attr->ulValueLen);
+			if (keyTemplate[8].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(keyTemplate[8].pValue,
+			       attr->pValue,
+			       attr->ulValueLen);
+			keyTemplate[8].ulValueLen = attr->ulValueLen;
+			break;
+		case CKA_PRIME_1:
+			INSIST(keyTemplate[9].type == attr->type);
+			keyTemplate[9].pValue = isc_mem_get(dctx->mctx,
+							    attr->ulValueLen);
+			if (keyTemplate[9].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(keyTemplate[9].pValue,
+			       attr->pValue,
+			       attr->ulValueLen);
+			keyTemplate[9].ulValueLen = attr->ulValueLen;
+			break;
+		case CKA_PRIME_2:
+			INSIST(keyTemplate[10].type == attr->type);
+			keyTemplate[10].pValue = isc_mem_get(dctx->mctx,
+							    attr->ulValueLen);
+			if (keyTemplate[10].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(keyTemplate[10].pValue,
+			       attr->pValue,
+			       attr->ulValueLen);
+			keyTemplate[10].ulValueLen = attr->ulValueLen;
+			break;
+		case CKA_EXPONENT_1:
+			INSIST(keyTemplate[11].type == attr->type);
+			keyTemplate[11].pValue = isc_mem_get(dctx->mctx,
+							     attr->ulValueLen);
+			if (keyTemplate[11].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(keyTemplate[11].pValue,
+			       attr->pValue,
+			       attr->ulValueLen);
+			keyTemplate[11].ulValueLen = attr->ulValueLen;
+			break;
+		case CKA_EXPONENT_2:
+			INSIST(keyTemplate[12].type == attr->type);
+			keyTemplate[12].pValue = isc_mem_get(dctx->mctx,
+							     attr->ulValueLen);
+			if (keyTemplate[12].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(keyTemplate[12].pValue,
+			       attr->pValue,
+			       attr->ulValueLen);
+			keyTemplate[12].ulValueLen = attr->ulValueLen;
+			break;
+		case CKA_COEFFICIENT:
+			INSIST(keyTemplate[13].type == attr->type);
+			keyTemplate[13].pValue = isc_mem_get(dctx->mctx,
+							     attr->ulValueLen);
+			if (keyTemplate[13].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(keyTemplate[13].pValue,
+			       attr->pValue,
+			       attr->ulValueLen);
+			keyTemplate[13].ulValueLen = attr->ulValueLen;
+			break;
+		}
+	pk11_ctx->object = CK_INVALID_HANDLE;
+	pk11_ctx->ontoken = ISC_FALSE;
+	PK11_RET(pkcs_C_CreateObject,
+		 (pk11_ctx->session,
+		  keyTemplate, (CK_ULONG) 14,
+		  &pk11_ctx->object),
+		 ISC_R_FAILURE);
+
+    token_key:
+
+	switch (dctx->key->key_alg) {
+	case DST_ALG_RSAMD5:
+		mech.mechanism = CKM_MD5_RSA_PKCS;
+		break;
+	case DST_ALG_RSASHA1:
+	case DST_ALG_NSEC3RSASHA1:
+		mech.mechanism = CKM_SHA1_RSA_PKCS;
+		break;
+	case DST_ALG_RSASHA256:
+		mech.mechanism = CKM_SHA256_RSA_PKCS;
+		break;
+	case DST_ALG_RSASHA512:
+		mech.mechanism = CKM_SHA512_RSA_PKCS;
+		break;
+	default:
+		INSIST(0);
+	}
+
+	PK11_RET(pkcs_C_SignInit,
+		 (pk11_ctx->session, &mech, pk11_ctx->object),
+		 ISC_R_FAILURE);
+
+	dctx->ctxdata.pk11_ctx = pk11_ctx;
+
+	for (i = 6; i <= 13; i++)
+		if (keyTemplate[i].pValue != NULL) {
+			memset(keyTemplate[i].pValue, 0,
+			       keyTemplate[i].ulValueLen);
+			isc_mem_put(dctx->mctx,
+				    keyTemplate[i].pValue,
+				    keyTemplate[i].ulValueLen);
+		}
+
+	return (ISC_R_SUCCESS);
+
+    err:
+	if (!pk11_ctx->ontoken && (pk11_ctx->object != CK_INVALID_HANDLE))
+		(void) pkcs_C_DestroyObject(pk11_ctx->session,
+					    pk11_ctx->object);
+	for (i = 6; i <= 13; i++)
+		if (keyTemplate[i].pValue != NULL) {
+			memset(keyTemplate[i].pValue, 0,
+			       keyTemplate[i].ulValueLen);
+			isc_mem_put(dctx->mctx,
+				    keyTemplate[i].pValue,
+				    keyTemplate[i].ulValueLen);
+		}
+	pk11_return_session(pk11_ctx);
+	memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+	isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+	return (ret);
+}
+
+static isc_result_t
+pkcs11rsa_createctx_verify(dst_key_t *key, unsigned int maxbits,
+			   dst_context_t *dctx) {
+	CK_RV rv;
+	CK_MECHANISM mech = { 0, NULL, 0 };
+	CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY;
+	CK_KEY_TYPE keyType = CKK_RSA;
+	CK_ATTRIBUTE keyTemplate[] =
+	{
+		{ CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_VERIFY, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_MODULUS, NULL, 0 },
+		{ CKA_PUBLIC_EXPONENT, NULL, 0 },
+	};
+	CK_ATTRIBUTE *attr;
+	pk11_object_t *rsa;
+	pk11_context_t *pk11_ctx;
+	isc_result_t ret;
+	unsigned int i;
+
+	REQUIRE(key->key_alg == DST_ALG_RSAMD5 ||
+		key->key_alg == DST_ALG_RSASHA1 ||
+		key->key_alg == DST_ALG_NSEC3RSASHA1 ||
+		key->key_alg == DST_ALG_RSASHA256 ||
+		key->key_alg == DST_ALG_RSASHA512);
+
+	rsa = key->keydata.pkey;
+
+	pk11_ctx = (pk11_context_t *) isc_mem_get(dctx->mctx,
+						  sizeof(*pk11_ctx));
+	if (pk11_ctx == NULL)
+		return (ISC_R_NOMEMORY);
+	ret = pk11_get_session(pk11_ctx, OP_RSA, ISC_TRUE, ISC_FALSE,
+			       rsa->reqlogon, NULL,
+			       pk11_get_best_token(OP_RSA));
+	if (ret != ISC_R_SUCCESS)
+		goto err;
+
+	for (attr = pk11_attribute_first(rsa);
+	     attr != NULL;
+	     attr = pk11_attribute_next(rsa, attr))
+		switch (attr->type) {
+		case CKA_MODULUS:
+			INSIST(keyTemplate[5].type == attr->type);
+			keyTemplate[5].pValue = isc_mem_get(dctx->mctx,
+							    attr->ulValueLen);
+			if (keyTemplate[5].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(keyTemplate[5].pValue,
+			       attr->pValue,
+			       attr->ulValueLen);
+			keyTemplate[5].ulValueLen = attr->ulValueLen;
+			break;
+		case CKA_PUBLIC_EXPONENT:
+			INSIST(keyTemplate[6].type == attr->type);
+			keyTemplate[6].pValue = isc_mem_get(dctx->mctx,
+							    attr->ulValueLen);
+			if (keyTemplate[6].pValue == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(keyTemplate[6].pValue,
+			       attr->pValue,
+			       attr->ulValueLen);
+			keyTemplate[6].ulValueLen = attr->ulValueLen;
+			if (pk11_numbits(attr->pValue,
+					 attr->ulValueLen) > maxbits &&
+			    maxbits != 0)
+				DST_RET(DST_R_VERIFYFAILURE);
+			break;
+		}
+	pk11_ctx->object = CK_INVALID_HANDLE;
+	pk11_ctx->ontoken = ISC_FALSE;
+	PK11_RET(pkcs_C_CreateObject,
+		 (pk11_ctx->session,
+		  keyTemplate, (CK_ULONG) 7,
+		  &pk11_ctx->object),
+		 ISC_R_FAILURE);
+
+	switch (dctx->key->key_alg) {
+	case DST_ALG_RSAMD5:
+		mech.mechanism = CKM_MD5_RSA_PKCS;
+		break;
+	case DST_ALG_RSASHA1:
+	case DST_ALG_NSEC3RSASHA1:
+		mech.mechanism = CKM_SHA1_RSA_PKCS;
+		break;
+	case DST_ALG_RSASHA256:
+		mech.mechanism = CKM_SHA256_RSA_PKCS;
+		break;
+	case DST_ALG_RSASHA512:
+		mech.mechanism = CKM_SHA512_RSA_PKCS;
+		break;
+	default:
+		INSIST(0);
+	}
+
+	PK11_RET(pkcs_C_VerifyInit,
+		 (pk11_ctx->session, &mech, pk11_ctx->object),
+		 ISC_R_FAILURE);
+
+	dctx->ctxdata.pk11_ctx = pk11_ctx;
+
+	for (i = 5; i <= 6; i++)
+		if (keyTemplate[i].pValue != NULL) {
+			memset(keyTemplate[i].pValue, 0,
+			       keyTemplate[i].ulValueLen);
+			isc_mem_put(dctx->mctx,
+				    keyTemplate[i].pValue,
+				    keyTemplate[i].ulValueLen);
+		}
+
+	return (ISC_R_SUCCESS);
+
+    err:
+	if (!pk11_ctx->ontoken && (pk11_ctx->object != CK_INVALID_HANDLE))
+		(void) pkcs_C_DestroyObject(pk11_ctx->session,
+					    pk11_ctx->object);
+	for (i = 5; i <= 6; i++)
+		if (keyTemplate[i].pValue != NULL) {
+			memset(keyTemplate[i].pValue, 0,
+			       keyTemplate[i].ulValueLen);
+			isc_mem_put(dctx->mctx,
+				    keyTemplate[i].pValue,
+				    keyTemplate[i].ulValueLen);
+		}
+	pk11_return_session(pk11_ctx);
+	memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+	isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+	return (ret);
+}
+
+static isc_result_t
+pkcs11rsa_createctx(dst_key_t *key, dst_context_t *dctx) {
+	if (dctx->use == DO_SIGN)
+		return (pkcs11rsa_createctx_sign(key, dctx));
+	else
+		return (pkcs11rsa_createctx_verify(key, 0U, dctx));
+}
+
+static isc_result_t
+pkcs11rsa_createctx2(dst_key_t *key, int maxbits, dst_context_t *dctx) {
+	if (dctx->use == DO_SIGN)
+		return (pkcs11rsa_createctx_sign(key, dctx));
+	else
+		return (pkcs11rsa_createctx_verify(key,
+						   (unsigned) maxbits, dctx));
+}
+
+static void
+pkcs11rsa_destroyctx(dst_context_t *dctx) {
+	pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+
+	if (pk11_ctx != NULL) {
+		if (!pk11_ctx->ontoken &&
+		    (pk11_ctx->object != CK_INVALID_HANDLE))
+			(void) pkcs_C_DestroyObject(pk11_ctx->session,
+						    pk11_ctx->object);
+		pk11_return_session(pk11_ctx);
+		memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+		isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx));
+		dctx->ctxdata.pk11_ctx = NULL;
+	}
+}
+
+static isc_result_t
+pkcs11rsa_adddata(dst_context_t *dctx, const isc_region_t *data) {
+	CK_RV rv;
+	pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+	isc_result_t ret = ISC_R_SUCCESS;
+
+	if (dctx->use == DO_SIGN)
+		PK11_CALL(pkcs_C_SignUpdate,
+			  (pk11_ctx->session,
+			   (CK_BYTE_PTR) data->base,
+			   (CK_ULONG) data->length),
+			  ISC_R_FAILURE);
+	else
+		PK11_CALL(pkcs_C_VerifyUpdate,
+			  (pk11_ctx->session,
+			   (CK_BYTE_PTR) data->base,
+			   (CK_ULONG) data->length),
+			  ISC_R_FAILURE);
+	return (ret);
+}
+
+static isc_result_t
+pkcs11rsa_sign(dst_context_t *dctx, isc_buffer_t *sig) {
+	CK_RV rv;
+	CK_ULONG siglen = 0;
+	isc_region_t r;
+	pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+	isc_result_t ret = ISC_R_SUCCESS;
+
+	PK11_RET(pkcs_C_SignFinal,
+		 (pk11_ctx->session, NULL, &siglen),
+		 DST_R_SIGNFAILURE);
+
+	isc_buffer_availableregion(sig, &r);
+
+	if (r.length < (unsigned int) siglen)
+		return (ISC_R_NOSPACE);
+
+	PK11_RET(pkcs_C_SignFinal,
+		 (pk11_ctx->session, (CK_BYTE_PTR) r.base, &siglen),
+		 DST_R_SIGNFAILURE);
+
+	isc_buffer_add(sig, (unsigned int) siglen);
+
+    err:
+	return (ret);
+}
+
+static isc_result_t
+pkcs11rsa_verify(dst_context_t *dctx, const isc_region_t *sig) {
+	CK_RV rv;
+	pk11_context_t *pk11_ctx = dctx->ctxdata.pk11_ctx;
+	isc_result_t ret = ISC_R_SUCCESS;
+
+	PK11_CALL(pkcs_C_VerifyFinal,
+		  (pk11_ctx->session,
+		   (CK_BYTE_PTR) sig->base,
+		   (CK_ULONG) sig->length),
+		  DST_R_VERIFYFAILURE);
+	return (ret);
+}
+
+static isc_boolean_t
+pkcs11rsa_compare(const dst_key_t *key1, const dst_key_t *key2) {
+	pk11_object_t *rsa1, *rsa2;
+	CK_ATTRIBUTE *attr1, *attr2;
+
+	rsa1 = key1->keydata.pkey;
+	rsa2 = key2->keydata.pkey;
+
+	if ((rsa1 == NULL) && (rsa2 == NULL))
+		return (ISC_TRUE);
+	else if ((rsa1 == NULL) || (rsa2 == NULL))
+		return (ISC_FALSE);
+
+	attr1 = pk11_attribute_bytype(rsa1, CKA_MODULUS);
+	attr2 = pk11_attribute_bytype(rsa2, CKA_MODULUS);
+	if ((attr1 == NULL) && (attr2 == NULL))
+		return (ISC_TRUE);
+	else if ((attr1 == NULL) || (attr2 == NULL) ||
+		 (attr1->ulValueLen != attr2->ulValueLen) ||
+		 memcmp(attr1->pValue, attr2->pValue, attr1->ulValueLen))
+		return (ISC_FALSE);
+
+	attr1 = pk11_attribute_bytype(rsa1, CKA_PUBLIC_EXPONENT);
+	attr2 = pk11_attribute_bytype(rsa2, CKA_PUBLIC_EXPONENT);
+	if ((attr1 == NULL) && (attr2 == NULL))
+		return (ISC_TRUE);
+	else if ((attr1 == NULL) || (attr2 == NULL) ||
+		 (attr1->ulValueLen != attr2->ulValueLen) ||
+		 memcmp(attr1->pValue, attr2->pValue, attr1->ulValueLen))
+		return (ISC_FALSE);
+
+	attr1 = pk11_attribute_bytype(rsa1, CKA_PRIVATE_EXPONENT);
+	attr2 = pk11_attribute_bytype(rsa2, CKA_PRIVATE_EXPONENT);
+	if (((attr1 != NULL) || (attr2 != NULL)) &&
+	    ((attr1 == NULL) || (attr2 == NULL) ||
+	     (attr1->ulValueLen != attr2->ulValueLen) ||
+	     memcmp(attr1->pValue, attr2->pValue, attr1->ulValueLen)))
+		return (ISC_FALSE);
+
+	if (!rsa1->ontoken && !rsa2->ontoken)
+		return (ISC_TRUE);
+	else if (rsa1->ontoken || rsa2->ontoken ||
+		 (rsa1->object != rsa2->object))
+		return (ISC_FALSE);
+
+	return (ISC_TRUE);
+}
+
+static isc_result_t
+pkcs11rsa_generate(dst_key_t *key, int exp, void (*callback)(int)) {
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_RSA_PKCS_KEY_PAIR_GEN, NULL, 0 };
+	CK_OBJECT_HANDLE pub = CK_INVALID_HANDLE;
+	CK_ULONG bits = 0;
+	CK_BYTE pubexp[5];
+	CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY;
+	CK_KEY_TYPE  keyType = CKK_RSA;
+	CK_ATTRIBUTE pubTemplate[] =
+	{
+		{ CKA_CLASS, &pubClass, (CK_ULONG) sizeof(pubClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_VERIFY, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_MODULUS_BITS, &bits, (CK_ULONG) sizeof(bits) },
+		{ CKA_PUBLIC_EXPONENT, &pubexp, (CK_ULONG) sizeof(pubexp) }
+	};
+	CK_OBJECT_HANDLE priv = CK_INVALID_HANDLE;
+	CK_OBJECT_CLASS privClass = CKO_PRIVATE_KEY;
+	CK_ATTRIBUTE privTemplate[] =
+	{
+		{ CKA_CLASS, &privClass, (CK_ULONG) sizeof(privClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_EXTRACTABLE, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+	};
+	CK_ATTRIBUTE *attr;
+	pk11_object_t *rsa;
+	pk11_context_t *pk11_ctx;
+	isc_result_t ret;
+	unsigned int i;
+
+	UNUSED(callback);
+
+	pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx,
+						  sizeof(*pk11_ctx));
+	if (pk11_ctx == NULL)
+		return (ISC_R_NOMEMORY);
+	ret = pk11_get_session(pk11_ctx, OP_RSA, ISC_TRUE, ISC_FALSE,
+			       ISC_FALSE, NULL, pk11_get_best_token(OP_RSA));
+	if (ret != ISC_R_SUCCESS)
+		goto err;
+
+	bits = key->key_size;
+	if (exp == 0) {
+		/* RSA_F4 0x10001 */
+		pubexp[0] = 1;
+		pubexp[1] = 0;
+		pubexp[2] = 1;
+		pubTemplate[6].ulValueLen = 3;
+	} else {
+		/* F5 0x100000001 */
+		pubexp[0] = 1;
+		pubexp[1] = 0;
+		pubexp[2] = 0;
+		pubexp[3] = 0;
+		pubexp[4] = 1;
+		pubTemplate[6].ulValueLen = 5;
+	}
+
+	PK11_RET(pkcs_C_GenerateKeyPair,
+		 (pk11_ctx->session, &mech,
+		  pubTemplate, (CK_ULONG) 7,
+		  privTemplate, (CK_ULONG) 7,
+		  &pub, &priv),
+		 DST_R_CRYPTOFAILURE);
+
+	rsa = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*rsa));
+	if (rsa == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(rsa, 0, sizeof(*rsa));
+	key->keydata.pkey = rsa;
+	rsa->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 8);
+	if (rsa->repr == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(rsa->repr, 0, sizeof(*attr) * 8);
+	rsa->attrcnt = 8;
+
+	attr = rsa->repr;
+	attr[0].type = CKA_MODULUS;
+	attr[1].type = CKA_PUBLIC_EXPONENT;
+	attr[2].type = CKA_PRIVATE_EXPONENT;
+	attr[3].type = CKA_PRIME_1;
+	attr[4].type = CKA_PRIME_2;
+	attr[5].type = CKA_EXPONENT_1;
+	attr[6].type = CKA_EXPONENT_2;
+	attr[7].type = CKA_COEFFICIENT;
+
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, pub, attr, 2),
+		 DST_R_CRYPTOFAILURE);
+	for (i = 0; i <= 1; i++) {
+		attr[i].pValue = isc_mem_get(key->mctx, attr[i].ulValueLen);
+		if (attr[i].pValue == NULL)
+			DST_RET(ISC_R_NOMEMORY);
+		memset(attr[i].pValue, 0, attr[i].ulValueLen);
+	}
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, pub, attr, 2),
+		 DST_R_CRYPTOFAILURE);
+
+	attr += 2;
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, priv, attr, 6),
+		 DST_R_CRYPTOFAILURE);
+	for (i = 0; i <= 5; i++) {
+		attr[i].pValue = isc_mem_get(key->mctx, attr[i].ulValueLen);
+		if (attr[i].pValue == NULL)
+			DST_RET(ISC_R_NOMEMORY);
+		memset(attr[i].pValue, 0, attr[i].ulValueLen);
+	}
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, priv, attr, 6),
+		 DST_R_CRYPTOFAILURE);
+
+	(void) pkcs_C_DestroyObject(pk11_ctx->session, priv);
+	(void) pkcs_C_DestroyObject(pk11_ctx->session, pub);
+	pk11_return_session(pk11_ctx);
+	memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+	isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+	return (ISC_R_SUCCESS);
+
+    err:
+	pkcs11rsa_destroy(key);
+	if (priv != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(pk11_ctx->session, priv);
+	if (pub != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(pk11_ctx->session, pub);
+	pk11_return_session(pk11_ctx);
+	memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+	isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+	return (ret);
+}
+
+static isc_boolean_t
+pkcs11rsa_isprivate(const dst_key_t *key) {
+	pk11_object_t *rsa = key->keydata.pkey;
+	CK_ATTRIBUTE *attr;
+
+	if (rsa == NULL)
+		return (ISC_FALSE);
+	attr = pk11_attribute_bytype(rsa, CKA_PRIVATE_EXPONENT);
+	return (ISC_TF((attr != NULL) || rsa->ontoken));
+}
+
+static void
+pkcs11rsa_destroy(dst_key_t *key) {
+	pk11_object_t *rsa = key->keydata.pkey;
+	CK_ATTRIBUTE *attr;
+
+	if (rsa == NULL)
+		return;
+
+	INSIST((rsa->object == CK_INVALID_HANDLE) || rsa->ontoken);
+
+	for (attr = pk11_attribute_first(rsa);
+	     attr != NULL;
+	     attr = pk11_attribute_next(rsa, attr))
+		switch (attr->type) {
+		case CKA_LABEL:
+		case CKA_ID:
+		case CKA_MODULUS:
+		case CKA_PUBLIC_EXPONENT:
+		case CKA_PRIVATE_EXPONENT:
+		case CKA_PRIME_1:
+		case CKA_PRIME_2:
+		case CKA_EXPONENT_1:
+		case CKA_EXPONENT_2:
+		case CKA_COEFFICIENT:
+			if (attr->pValue != NULL) {
+				memset(attr->pValue, 0, attr->ulValueLen);
+				isc_mem_put(key->mctx,
+					    attr->pValue,
+					    attr->ulValueLen);
+			}
+			break;
+		}
+	if (rsa->repr != NULL) {
+		memset(rsa->repr, 0, rsa->attrcnt * sizeof(*attr));
+		isc_mem_put(key->mctx,
+			    rsa->repr,
+			    rsa->attrcnt * sizeof(*attr));
+	}
+	memset(rsa, 0, sizeof(*rsa));
+	isc_mem_put(key->mctx, rsa, sizeof(*rsa));
+	key->keydata.pkey = NULL;
+}
+
+static isc_result_t
+pkcs11rsa_todns(const dst_key_t *key, isc_buffer_t *data) {
+	pk11_object_t *rsa;
+	CK_ATTRIBUTE *attr;
+	isc_region_t r;
+	unsigned int e_bytes = 0, mod_bytes = 0;
+	CK_BYTE *exponent = NULL, *modulus = NULL;
+
+	REQUIRE(key->keydata.pkey != NULL);
+
+	rsa = key->keydata.pkey;
+
+	for (attr = pk11_attribute_first(rsa);
+	     attr != NULL;
+	     attr = pk11_attribute_next(rsa, attr))
+		switch (attr->type) {
+		case CKA_PUBLIC_EXPONENT:
+			exponent = (CK_BYTE *) attr->pValue;
+			e_bytes = (unsigned int) attr->ulValueLen;
+			break;
+		case CKA_MODULUS:
+			modulus = (CK_BYTE *) attr->pValue;
+			mod_bytes = (unsigned int) attr->ulValueLen;
+			break;
+		}
+	REQUIRE((exponent != NULL) && (modulus != NULL));
+
+	isc_buffer_availableregion(data, &r);
+
+	if (e_bytes < 256) {	/*%< key exponent is <= 2040 bits */
+		if (r.length < 1)
+			return (ISC_R_NOSPACE);
+		isc_buffer_putuint8(data, (isc_uint8_t) e_bytes);
+		isc_region_consume(&r, 1);
+	} else {
+		if (r.length < 3)
+			return (ISC_R_NOSPACE);
+		isc_buffer_putuint8(data, 0);
+		isc_buffer_putuint16(data, (isc_uint16_t) e_bytes);
+		isc_region_consume(&r, 3);
+	}
+
+	if (r.length < e_bytes + mod_bytes)
+		return (ISC_R_NOSPACE);
+
+	memcpy(r.base, exponent, e_bytes);
+	isc_region_consume(&r, e_bytes);
+	memcpy(r.base, modulus, mod_bytes);
+
+	isc_buffer_add(data, e_bytes + mod_bytes);
+
+	return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+pkcs11rsa_fromdns(dst_key_t *key, isc_buffer_t *data) {
+	pk11_object_t *rsa;
+	isc_region_t r;
+	unsigned int e_bytes, mod_bytes;
+	CK_BYTE *exponent = NULL, *modulus = NULL;
+	CK_ATTRIBUTE *attr;
+
+	isc_buffer_remainingregion(data, &r);
+	if (r.length == 0)
+		return (ISC_R_SUCCESS);
+
+	rsa = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*rsa));
+	if (rsa == NULL)
+		return (ISC_R_NOMEMORY);
+	memset(rsa, 0, sizeof(*rsa));
+
+	if (r.length < 1) {
+		memset(rsa, 0, sizeof(*rsa));
+		isc_mem_put(key->mctx, rsa, sizeof(*rsa));
+		return (DST_R_INVALIDPUBLICKEY);
+	}
+	e_bytes = *r.base++;
+	r.length--;
+
+	if (e_bytes == 0) {
+		if (r.length < 2) {
+			memset(rsa, 0, sizeof(*rsa));
+			isc_mem_put(key->mctx, rsa, sizeof(*rsa));
+			return (DST_R_INVALIDPUBLICKEY);
+		}
+		e_bytes = ((*r.base++) << 8);
+		e_bytes += *r.base++;
+		r.length -= 2;
+	}
+
+	if (r.length < e_bytes) {
+		memset(rsa, 0, sizeof(*rsa));
+		isc_mem_put(key->mctx, rsa, sizeof(*rsa));
+		return (DST_R_INVALIDPUBLICKEY);
+	}
+	exponent = r.base;
+	r.base += e_bytes;
+	r.length -= e_bytes;
+	modulus = r.base;
+	mod_bytes = r.length;
+
+	key->key_size = pk11_numbits(modulus, mod_bytes);
+
+	isc_buffer_forward(data, r.length);
+
+	rsa->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 2);
+	if (rsa->repr == NULL)
+		goto nomemory;
+	memset(rsa->repr, 0, sizeof(*attr) * 2);
+	rsa->attrcnt = 2;
+	attr = rsa->repr;
+	attr[0].type = CKA_MODULUS;
+	attr[0].pValue = isc_mem_get(key->mctx, mod_bytes);
+	if (attr[0].pValue == NULL)
+		goto nomemory;
+	memcpy(attr[0].pValue, modulus, mod_bytes);
+	attr[0].ulValueLen = (CK_ULONG) mod_bytes;
+	attr[1].type = CKA_PUBLIC_EXPONENT;
+	attr[1].pValue = isc_mem_get(key->mctx, e_bytes);
+	if (attr[1].pValue == NULL)
+		goto nomemory;
+	memcpy(attr[1].pValue, exponent, e_bytes);
+	attr[1].ulValueLen = (CK_ULONG) e_bytes;
+
+	key->keydata.pkey = rsa;
+
+	return (ISC_R_SUCCESS);
+
+    nomemory:
+	for (attr = pk11_attribute_first(rsa);
+	     attr != NULL;
+	     attr = pk11_attribute_next(rsa, attr))
+		switch (attr->type) {
+		case CKA_MODULUS:
+		case CKA_PUBLIC_EXPONENT:
+			if (attr->pValue != NULL) {
+				memset(attr->pValue, 0, attr->ulValueLen);
+				isc_mem_put(key->mctx,
+					    attr->pValue,
+					    attr->ulValueLen);
+			}
+			break;
+		}
+	if (rsa->repr != NULL) {
+		memset(rsa->repr, 0, rsa->attrcnt * sizeof(*attr));
+		isc_mem_put(key->mctx,
+			    rsa->repr,
+			    rsa->attrcnt * sizeof(*attr));
+	}
+	memset(rsa, 0, sizeof(*rsa));
+	isc_mem_put(key->mctx, rsa, sizeof(*rsa));
+	return (ISC_R_NOMEMORY);
+}
+
+static isc_result_t
+pkcs11rsa_tofile(const dst_key_t *key, const char *directory) {
+	int i;
+	pk11_object_t *rsa;
+	CK_ATTRIBUTE *attr;
+	CK_ATTRIBUTE *modulus = NULL, *exponent = NULL;
+	CK_ATTRIBUTE  *d = NULL, *p = NULL, *q = NULL;
+	CK_ATTRIBUTE *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL;
+	dst_private_t priv;
+	unsigned char *bufs[10];
+	isc_result_t result;
+
+	if (key->keydata.pkey == NULL)
+		return (DST_R_NULLKEY);
+
+	if (key->external) {
+		priv.nelements = 0;
+		return (dst__privstruct_writefile(key, &priv, directory));
+	}
+
+	rsa = key->keydata.pkey;
+
+	for (attr = pk11_attribute_first(rsa);
+	     attr != NULL;
+	     attr = pk11_attribute_next(rsa, attr))
+		switch (attr->type) {
+		case CKA_MODULUS:
+			modulus = attr;
+			break;
+		case CKA_PUBLIC_EXPONENT:
+			exponent = attr;
+			break;
+		case CKA_PRIVATE_EXPONENT:
+			d = attr;
+			break;
+		case CKA_PRIME_1:
+			p = attr;
+			break;
+		case CKA_PRIME_2:
+			q = attr;
+			break;
+		case CKA_EXPONENT_1:
+			dmp1 = attr;
+			break;
+		case CKA_EXPONENT_2:
+			dmq1 = attr;
+			break;
+		case CKA_COEFFICIENT:
+			iqmp = attr;
+			break;
+		}
+	if ((modulus == NULL) || (exponent == NULL))
+		return (DST_R_NULLKEY);
+
+	memset(bufs, 0, sizeof(bufs));
+
+	for (i = 0; i < 10; i++) {
+		bufs[i] = isc_mem_get(key->mctx, modulus->ulValueLen);
+		if (bufs[i] == NULL) {
+			result = ISC_R_NOMEMORY;
+			goto fail;
+		}
+		memset(bufs[i], 0, modulus->ulValueLen);
+	}
+
+	i = 0;
+
+	priv.elements[i].tag = TAG_RSA_MODULUS;
+	priv.elements[i].length = (unsigned short) modulus->ulValueLen;
+	memcpy(bufs[i], modulus->pValue, modulus->ulValueLen);
+	priv.elements[i].data = bufs[i];
+	i++;
+
+	priv.elements[i].tag = TAG_RSA_PUBLICEXPONENT;
+	priv.elements[i].length = (unsigned short) exponent->ulValueLen;
+	memcpy(bufs[i], exponent->pValue, exponent->ulValueLen);
+	priv.elements[i].data = bufs[i];
+	i++;
+
+	if (d != NULL) {
+		priv.elements[i].tag = TAG_RSA_PRIVATEEXPONENT;
+		priv.elements[i].length = (unsigned short) d->ulValueLen;
+		memcpy(bufs[i], d->pValue, d->ulValueLen);
+		priv.elements[i].data = bufs[i];
+		i++;
+	}
+
+	if (p != NULL) {
+		priv.elements[i].tag = TAG_RSA_PRIME1;
+		priv.elements[i].length = (unsigned short) p->ulValueLen;
+		memcpy(bufs[i], p->pValue, p->ulValueLen);
+		priv.elements[i].data = bufs[i];
+		i++;
+	}
+
+	if (q != NULL) {
+		priv.elements[i].tag = TAG_RSA_PRIME2;
+		priv.elements[i].length = (unsigned short) q->ulValueLen;
+		memcpy(bufs[i], q->pValue, q->ulValueLen);
+		priv.elements[i].data = bufs[i];
+		i++;
+	}
+
+	if (dmp1 != NULL) {
+		priv.elements[i].tag = TAG_RSA_EXPONENT1;
+		priv.elements[i].length = (unsigned short) dmp1->ulValueLen;
+		memcpy(bufs[i], dmp1->pValue, dmp1->ulValueLen);
+		priv.elements[i].data = bufs[i];
+		i++;
+	}
+
+	if (dmq1 != NULL) {
+		priv.elements[i].tag = TAG_RSA_EXPONENT2;
+		priv.elements[i].length = (unsigned short) dmq1->ulValueLen;
+		memcpy(bufs[i], dmq1->pValue, dmq1->ulValueLen);
+		priv.elements[i].data = bufs[i];
+		i++;
+	}
+
+	if (iqmp != NULL) {
+		priv.elements[i].tag = TAG_RSA_COEFFICIENT;
+		priv.elements[i].length = (unsigned short) iqmp->ulValueLen;
+		memcpy(bufs[i], iqmp->pValue, iqmp->ulValueLen);
+		priv.elements[i].data = bufs[i];
+		i++;
+	}
+
+	if (key->engine != NULL) {
+		priv.elements[i].tag = TAG_RSA_ENGINE;
+		priv.elements[i].length = strlen(key->engine) + 1;
+		priv.elements[i].data = (unsigned char *)key->engine;
+		i++;
+	}
+
+	if (key->label != NULL) {
+		priv.elements[i].tag = TAG_RSA_LABEL;
+		priv.elements[i].length = strlen(key->label) + 1;
+		priv.elements[i].data = (unsigned char *)key->label;
+		i++;
+	}
+
+	priv.nelements = i;
+	result = dst__privstruct_writefile(key, &priv, directory);
+ fail:
+	for (i = 0; i < 10; i++) {
+		if (bufs[i] == NULL)
+			break;
+		memset(bufs[i], 0, modulus->ulValueLen);
+		isc_mem_put(key->mctx, bufs[i], modulus->ulValueLen);
+	}
+	return (result);
+}
+
+static isc_result_t
+pkcs11rsa_fetch(dst_key_t *key, const char *engine, const char *label,
+		dst_key_t *pub)
+{
+	CK_RV rv;
+	CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
+	CK_KEY_TYPE keyType = CKK_RSA;
+	CK_ATTRIBUTE searchTemplate[] =
+	{
+		{ CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_LABEL, NULL, 0 }
+	};
+	CK_ULONG cnt;
+	CK_ATTRIBUTE *attr;
+	CK_ATTRIBUTE *pubattr;
+	pk11_object_t *rsa;
+	pk11_object_t *pubrsa;
+	pk11_context_t *pk11_ctx = NULL;
+	isc_result_t ret;
+
+	if (label == NULL)
+		return (DST_R_NOENGINE);
+
+	rsa = key->keydata.pkey;
+	pubrsa = pub->keydata.pkey;
+
+	rsa->object = CK_INVALID_HANDLE;
+	rsa->ontoken = ISC_TRUE;
+	rsa->reqlogon = ISC_TRUE;
+	rsa->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 2);
+	if (rsa->repr == NULL)
+		return (ISC_R_NOMEMORY);
+	memset(rsa->repr, 0, sizeof(*attr) * 2);
+	rsa->attrcnt = 2;
+	attr = rsa->repr;
+
+	attr->type = CKA_MODULUS;
+	pubattr = pk11_attribute_bytype(pubrsa, CKA_MODULUS);
+	attr->pValue = isc_mem_get(key->mctx, pubattr->ulValueLen);
+	if (attr->pValue == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memcpy(attr->pValue, pubattr->pValue, pubattr->ulValueLen);
+	attr->ulValueLen = pubattr->ulValueLen;
+	attr++;
+
+	attr->type = CKA_PUBLIC_EXPONENT;
+	pubattr = pk11_attribute_bytype(pubrsa, CKA_PUBLIC_EXPONENT);
+	attr->pValue = isc_mem_get(key->mctx, pubattr->ulValueLen);
+	if (attr->pValue == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memcpy(attr->pValue, pubattr->pValue, pubattr->ulValueLen);
+	attr->ulValueLen = pubattr->ulValueLen;
+
+	ret = pk11_parse_uri(rsa, label, key->mctx, OP_RSA);
+	if (ret != ISC_R_SUCCESS)
+		goto err;
+
+	pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx,
+						  sizeof(*pk11_ctx));
+	if (pk11_ctx == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	ret = pk11_get_session(pk11_ctx, OP_RSA, ISC_TRUE, ISC_FALSE,
+			       rsa->reqlogon, NULL, rsa->slot);
+	if (ret != ISC_R_SUCCESS)
+		goto err;
+
+	attr = pk11_attribute_bytype(rsa, CKA_LABEL);
+	if (attr == NULL) {
+		attr = pk11_attribute_bytype(rsa, CKA_ID);
+		INSIST(attr != NULL);
+		searchTemplate[3].type = CKA_ID;
+	}
+	searchTemplate[3].pValue = attr->pValue;
+	searchTemplate[3].ulValueLen = attr->ulValueLen;
+
+	PK11_RET(pkcs_C_FindObjectsInit,
+		 (pk11_ctx->session, searchTemplate, (CK_ULONG) 4),
+		 DST_R_CRYPTOFAILURE);
+	PK11_RET(pkcs_C_FindObjects,
+		 (pk11_ctx->session, &rsa->object, (CK_ULONG) 1, &cnt),
+		 DST_R_CRYPTOFAILURE);
+	(void) pkcs_C_FindObjectsFinal(pk11_ctx->session);
+	if (cnt == 0)
+		DST_RET(ISC_R_NOTFOUND);
+	if (cnt > 1)
+		DST_RET(ISC_R_EXISTS);
+
+	if (engine != NULL) {
+		key->engine = isc_mem_strdup(key->mctx, engine);
+		if (key->engine == NULL)
+			DST_RET(ISC_R_NOMEMORY);
+	}
+
+	key->label = isc_mem_strdup(key->mctx, label);
+	if (key->label == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+
+	pk11_return_session(pk11_ctx);
+	memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+	isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+	attr = pk11_attribute_bytype(rsa, CKA_MODULUS);
+	INSIST(attr != NULL);
+	key->key_size = pk11_numbits(attr->pValue, attr->ulValueLen);
+
+	return (ISC_R_SUCCESS);
+
+    err:
+	if (pk11_ctx != NULL) {
+		pk11_return_session(pk11_ctx);
+		memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+		isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+	}
+
+	return (ret);
+}
+
+static isc_result_t
+rsa_check(pk11_object_t *rsa, pk11_object_t *pubrsa) {
+	CK_ATTRIBUTE *pubattr, *privattr;
+	CK_BYTE *priv_exp = NULL, *priv_mod = NULL;
+	CK_BYTE *pub_exp = NULL, *pub_mod = NULL;
+	unsigned int priv_explen = 0, priv_modlen = 0;
+	unsigned int pub_explen = 0, pub_modlen = 0;
+
+	REQUIRE(rsa != NULL && pubrsa != NULL);
+
+	privattr = pk11_attribute_bytype(rsa, CKA_PUBLIC_EXPONENT);
+	INSIST(privattr != NULL);
+	priv_exp = privattr->pValue;
+	priv_explen = privattr->ulValueLen;
+
+	pubattr = pk11_attribute_bytype(pubrsa, CKA_PUBLIC_EXPONENT);
+	INSIST(pubattr != NULL);
+	pub_exp = pubattr->pValue;
+	pub_explen = pubattr->ulValueLen;
+
+	if (priv_exp != NULL) {
+		if (priv_explen != pub_explen)
+			return (DST_R_INVALIDPRIVATEKEY);
+		if (memcmp(priv_exp, pub_exp, pub_explen) != 0)
+			return (DST_R_INVALIDPRIVATEKEY);
+	} else {
+		privattr->pValue = pub_exp;
+		privattr->ulValueLen = pub_explen;
+		pubattr->pValue = NULL;
+		pubattr->ulValueLen = 0;
+	}
+
+	if (privattr->pValue == NULL)
+		return (DST_R_INVALIDPRIVATEKEY);
+
+	privattr = pk11_attribute_bytype(rsa, CKA_MODULUS);
+	INSIST(privattr != NULL);
+	priv_mod = privattr->pValue;
+	priv_modlen = privattr->ulValueLen;
+
+	pubattr = pk11_attribute_bytype(pubrsa, CKA_MODULUS);
+	INSIST(pubattr != NULL);
+	pub_mod = pubattr->pValue;
+	pub_modlen = pubattr->ulValueLen;
+
+	if (priv_mod != NULL) {
+		if (priv_modlen != pub_modlen)
+			return (DST_R_INVALIDPRIVATEKEY);
+		if (memcmp(priv_mod, pub_mod, pub_modlen) != 0)
+			return (DST_R_INVALIDPRIVATEKEY);
+	} else {
+		privattr->pValue = pub_mod;
+		privattr->ulValueLen = pub_modlen;
+		pubattr->pValue = NULL;
+		pubattr->ulValueLen = 0;
+	}
+
+	if (privattr->pValue == NULL)
+		return (DST_R_INVALIDPRIVATEKEY);
+
+	return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+pkcs11rsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
+	dst_private_t priv;
+	isc_result_t ret;
+	int i;
+	pk11_object_t *rsa;
+	CK_ATTRIBUTE *attr;
+	isc_mem_t *mctx = key->mctx;
+	const char *engine = NULL, *label = NULL;
+
+	/* read private key file */
+	ret = dst__privstruct_parse(key, DST_ALG_RSA, lexer, mctx, &priv);
+	if (ret != ISC_R_SUCCESS)
+		return (ret);
+
+	if (key->external) {
+		if (priv.nelements != 0)
+			DST_RET(DST_R_INVALIDPRIVATEKEY);
+		if (pub == NULL)
+			DST_RET(DST_R_INVALIDPRIVATEKEY);
+
+		key->keydata.pkey = pub->keydata.pkey;
+		pub->keydata.pkey = NULL;
+		key->key_size = pub->key_size;
+
+		dst__privstruct_free(&priv, mctx);
+		memset(&priv, 0, sizeof(priv));
+
+		return (ISC_R_SUCCESS);
+	}
+
+	for (i = 0; i < priv.nelements; i++) {
+		switch (priv.elements[i].tag) {
+		case TAG_RSA_ENGINE:
+			engine = (char *)priv.elements[i].data;
+			break;
+		case TAG_RSA_LABEL:
+			label = (char *)priv.elements[i].data;
+			break;
+		default:
+			break;
+		}
+	}
+	rsa = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*rsa));
+	if (rsa == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(rsa, 0, sizeof(*rsa));
+	key->keydata.pkey = rsa;
+
+	/* Is this key is stored in a HSM? See if we can fetch it. */
+	if ((label != NULL) || (engine != NULL)) {
+		ret = pkcs11rsa_fetch(key, engine, label, pub);
+		if (ret != ISC_R_SUCCESS)
+			goto err;
+		dst__privstruct_free(&priv, mctx);
+		memset(&priv, 0, sizeof(priv));
+		return (ret);
+	}
+
+	rsa->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 8);
+	if (rsa->repr == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(rsa->repr, 0, sizeof(*attr) * 8);
+	rsa->attrcnt = 8;
+	attr = rsa->repr;
+	attr[0].type = CKA_MODULUS;
+	attr[1].type = CKA_PUBLIC_EXPONENT;
+	attr[2].type = CKA_PRIVATE_EXPONENT;
+	attr[3].type = CKA_PRIME_1;
+	attr[4].type = CKA_PRIME_2;
+	attr[5].type = CKA_EXPONENT_1;
+	attr[6].type = CKA_EXPONENT_2;
+	attr[7].type = CKA_COEFFICIENT;
+
+	for (i = 0; i < priv.nelements; i++) {
+		CK_BYTE *bn;
+
+		switch (priv.elements[i].tag) {
+		case TAG_RSA_ENGINE:
+			continue;
+		case TAG_RSA_LABEL:
+			continue;
+		default:
+			bn = isc_mem_get(key->mctx, priv.elements[i].length);
+			if (bn == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			memcpy(bn,
+			       priv.elements[i].data,
+			       priv.elements[i].length);
+		}
+
+		switch (priv.elements[i].tag) {
+			case TAG_RSA_MODULUS:
+				attr = pk11_attribute_bytype(rsa, CKA_MODULUS);
+				INSIST(attr != NULL);
+				attr->pValue = bn;
+				attr->ulValueLen = priv.elements[i].length;
+				break;
+			case TAG_RSA_PUBLICEXPONENT:
+				attr = pk11_attribute_bytype(rsa,
+						CKA_PUBLIC_EXPONENT);
+				INSIST(attr != NULL);
+				attr->pValue = bn;
+				attr->ulValueLen = priv.elements[i].length;
+				break;
+			case TAG_RSA_PRIVATEEXPONENT:
+				attr = pk11_attribute_bytype(rsa,
+						CKA_PRIVATE_EXPONENT);
+				INSIST(attr != NULL);
+				attr->pValue = bn;
+				attr->ulValueLen = priv.elements[i].length;
+				break;
+			case TAG_RSA_PRIME1:
+				attr = pk11_attribute_bytype(rsa, CKA_PRIME_1);
+				INSIST(attr != NULL);
+				attr->pValue = bn;
+				attr->ulValueLen = priv.elements[i].length;
+				break;
+			case TAG_RSA_PRIME2:
+				attr = pk11_attribute_bytype(rsa, CKA_PRIME_2);
+				INSIST(attr != NULL);
+				attr->pValue = bn;
+				attr->ulValueLen = priv.elements[i].length;
+				break;
+			case TAG_RSA_EXPONENT1:
+				attr = pk11_attribute_bytype(rsa,
+							     CKA_EXPONENT_1);
+				INSIST(attr != NULL);
+				attr->pValue = bn;
+				attr->ulValueLen = priv.elements[i].length;
+				break;
+			case TAG_RSA_EXPONENT2:
+				attr = pk11_attribute_bytype(rsa,
+							     CKA_EXPONENT_2);
+				INSIST(attr != NULL);
+				attr->pValue = bn;
+				attr->ulValueLen = priv.elements[i].length;
+				break;
+			case TAG_RSA_COEFFICIENT:
+				attr = pk11_attribute_bytype(rsa,
+							     CKA_COEFFICIENT);
+				INSIST(attr != NULL);
+				attr->pValue = bn;
+				attr->ulValueLen = priv.elements[i].length;
+				break;
+		}
+	}
+
+	if (rsa_check(rsa, pub->keydata.pkey) != ISC_R_SUCCESS)
+		DST_RET(DST_R_INVALIDPRIVATEKEY);
+
+	attr = pk11_attribute_bytype(rsa, CKA_MODULUS);
+	INSIST(attr != NULL);
+	key->key_size = pk11_numbits(attr->pValue, attr->ulValueLen);
+
+	attr = pk11_attribute_bytype(rsa, CKA_PUBLIC_EXPONENT);
+	INSIST(attr != NULL);
+	if (pk11_numbits(attr->pValue, attr->ulValueLen) > RSA_MAX_PUBEXP_BITS)
+		DST_RET(ISC_R_RANGE);
+
+	dst__privstruct_free(&priv, mctx);
+	memset(&priv, 0, sizeof(priv));
+
+	return (ISC_R_SUCCESS);
+
+ err:
+	pkcs11rsa_destroy(key);
+	dst__privstruct_free(&priv, mctx);
+	memset(&priv, 0, sizeof(priv));
+	return (ret);
+}
+
+static isc_result_t
+pkcs11rsa_fromlabel(dst_key_t *key, const char *engine, const char *label,
+		    const char *pin)
+{
+	CK_RV rv;
+	CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE;
+	CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY;
+	CK_KEY_TYPE keyType = CKK_RSA;
+	CK_ATTRIBUTE searchTemplate[] =
+	{
+		{ CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_LABEL, NULL, 0 }
+	};
+	CK_ULONG cnt;
+	CK_ATTRIBUTE *attr;
+	pk11_object_t *rsa;
+	pk11_context_t *pk11_ctx = NULL;
+	isc_result_t ret;
+	unsigned int i;
+
+	UNUSED(pin);
+
+	rsa = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*rsa));
+	if (rsa == NULL)
+		return (ISC_R_NOMEMORY);
+	memset(rsa, 0, sizeof(*rsa));
+	rsa->object = CK_INVALID_HANDLE;
+	rsa->ontoken = ISC_TRUE;
+	rsa->reqlogon = ISC_TRUE;
+	key->keydata.pkey = rsa;
+
+	rsa->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 2);
+	if (rsa->repr == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	memset(rsa->repr, 0, sizeof(*attr) * 2);
+	rsa->attrcnt = 2;
+	attr = rsa->repr;
+	attr[0].type = CKA_MODULUS;
+	attr[1].type = CKA_PUBLIC_EXPONENT;
+
+	ret = pk11_parse_uri(rsa, label, key->mctx, OP_RSA);
+	if (ret != ISC_R_SUCCESS)
+		goto err;
+
+	pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx,
+						  sizeof(*pk11_ctx));
+	if (pk11_ctx == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+	ret = pk11_get_session(pk11_ctx, OP_RSA, ISC_TRUE, ISC_FALSE,
+			       rsa->reqlogon, NULL, rsa->slot);
+	if (ret != ISC_R_SUCCESS)
+		goto err;
+
+	attr = pk11_attribute_bytype(rsa, CKA_LABEL);
+	if (attr == NULL) {
+		attr = pk11_attribute_bytype(rsa, CKA_ID);
+		INSIST(attr != NULL);
+		searchTemplate[3].type = CKA_ID;
+	}
+	searchTemplate[3].pValue = attr->pValue;
+	searchTemplate[3].ulValueLen = attr->ulValueLen;
+
+	PK11_RET(pkcs_C_FindObjectsInit,
+		 (pk11_ctx->session, searchTemplate, (CK_ULONG) 4),
+		 DST_R_CRYPTOFAILURE);
+	PK11_RET(pkcs_C_FindObjects,
+		 (pk11_ctx->session, &hKey, (CK_ULONG) 1, &cnt),
+		 DST_R_CRYPTOFAILURE);
+	(void) pkcs_C_FindObjectsFinal(pk11_ctx->session);
+	if (cnt == 0)
+		DST_RET(ISC_R_NOTFOUND);
+	if (cnt > 1)
+		DST_RET(ISC_R_EXISTS);
+
+	attr = rsa->repr;
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, hKey, attr, 2),
+		 DST_R_CRYPTOFAILURE);
+	for (i = 0; i <= 1; i++) {
+		attr[i].pValue = isc_mem_get(key->mctx, attr[i].ulValueLen);
+		if (attr[i].pValue == NULL)
+			DST_RET(ISC_R_NOMEMORY);
+		memset(attr[i].pValue, 0, attr[i].ulValueLen);
+	}
+	PK11_RET(pkcs_C_GetAttributeValue,
+		 (pk11_ctx->session, hKey, attr, 2),
+		 DST_R_CRYPTOFAILURE);
+
+	keyClass = CKO_PRIVATE_KEY;
+	PK11_RET(pkcs_C_FindObjectsInit,
+		 (pk11_ctx->session, searchTemplate, (CK_ULONG) 4),
+		 DST_R_CRYPTOFAILURE);
+	PK11_RET(pkcs_C_FindObjects,
+		 (pk11_ctx->session, &rsa->object, (CK_ULONG) 1, &cnt),
+		 DST_R_CRYPTOFAILURE);
+	(void) pkcs_C_FindObjectsFinal(pk11_ctx->session);
+	if (cnt == 0)
+		DST_RET(ISC_R_NOTFOUND);
+	if (cnt > 1)
+		DST_RET(ISC_R_EXISTS);
+
+	if (engine != NULL) {
+		key->engine = isc_mem_strdup(key->mctx, engine);
+		if (key->engine == NULL)
+			DST_RET(ISC_R_NOMEMORY);
+	}
+
+	key->label = isc_mem_strdup(key->mctx, label);
+	if (key->label == NULL)
+		DST_RET(ISC_R_NOMEMORY);
+
+	attr = pk11_attribute_bytype(rsa, CKA_PUBLIC_EXPONENT);
+	INSIST(attr != NULL);
+	if (pk11_numbits(attr->pValue, attr->ulValueLen) > RSA_MAX_PUBEXP_BITS)
+		DST_RET(ISC_R_RANGE);
+
+	attr = pk11_attribute_bytype(rsa, CKA_MODULUS);
+	INSIST(attr != NULL);
+	key->key_size = pk11_numbits(attr->pValue, attr->ulValueLen);
+
+	pk11_return_session(pk11_ctx);
+	memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+	isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+
+	return (ISC_R_SUCCESS);
+
+    err:
+	pkcs11rsa_destroy(key);
+	if (pk11_ctx != NULL) {
+		pk11_return_session(pk11_ctx);
+		memset(pk11_ctx, 0, sizeof(*pk11_ctx));
+		isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx));
+	}
+
+	return (ret);
+}
+
+static dst_func_t pkcs11rsa_functions = {
+	pkcs11rsa_createctx,
+	pkcs11rsa_createctx2,
+	pkcs11rsa_destroyctx,
+	pkcs11rsa_adddata,
+	pkcs11rsa_sign,
+	pkcs11rsa_verify,
+	NULL, /*%< verify2 */
+	NULL, /*%< computesecret */
+	pkcs11rsa_compare,
+	NULL, /*%< paramcompare */
+	pkcs11rsa_generate,
+	pkcs11rsa_isprivate,
+	pkcs11rsa_destroy,
+	pkcs11rsa_todns,
+	pkcs11rsa_fromdns,
+	pkcs11rsa_tofile,
+	pkcs11rsa_parse,
+	NULL, /*%< cleanup */
+	pkcs11rsa_fromlabel,
+	NULL, /*%< dump */
+	NULL, /*%< restore */
+};
+
+isc_result_t
+dst__pkcs11rsa_init(dst_func_t **funcp) {
+	REQUIRE(funcp != NULL);
+
+	if (*funcp == NULL)
+		*funcp = &pkcs11rsa_functions;
+	return (ISC_R_SUCCESS);
+}
+
+#else /* PKCS11CRYPTO */
+
+#include <isc/util.h>
+
+EMPTY_TRANSLATION_UNIT
+
+#endif /* PKCS11CRYPTO */
+/*! \file */
diff --git a/lib/dns/rdata/generic/dlv_32769.c b/lib/dns/rdata/generic/dlv_32769.c
index 5751ad8..732abdb 100644
--- a/lib/dns/rdata/generic/dlv_32769.c
+++ b/lib/dns/rdata/generic/dlv_32769.c
@@ -28,6 +28,7 @@
 
 #include <dns/ds.h>
 
+#include "dst_gost.h"
 
 static inline isc_result_t
 fromtext_dlv(ARGS_FROMTEXT) {
@@ -81,9 +82,11 @@ fromtext_dlv(ARGS_FROMTEXT) {
 	case DNS_DSDIGEST_SHA256:
 		length = ISC_SHA256_DIGESTLENGTH;
 		break;
+#ifdef ISC_GOST_DIGESTLENGTH
 	case DNS_DSDIGEST_GOST:
 		length = ISC_GOST_DIGESTLENGTH;
 		break;
+#endif
 	case DNS_DSDIGEST_SHA384:
 		length = ISC_SHA384_DIGESTLENGTH;
 		break;
@@ -168,8 +171,10 @@ fromwire_dlv(ARGS_FROMWIRE) {
 	     sr.length < 4 + ISC_SHA1_DIGESTLENGTH) ||
 	    (sr.base[3] == DNS_DSDIGEST_SHA256 &&
 	     sr.length < 4 + ISC_SHA256_DIGESTLENGTH) ||
+#ifdef ISC_GOST_DIGESTLENGTH
 	    (sr.base[3] == DNS_DSDIGEST_GOST &&
 	     sr.length < 4 + ISC_GOST_DIGESTLENGTH) ||
+#endif
 	    (sr.base[3] == DNS_DSDIGEST_SHA384 &&
 	     sr.length < 4 + ISC_SHA384_DIGESTLENGTH))
 		return (ISC_R_UNEXPECTEDEND);
@@ -183,8 +188,10 @@ fromwire_dlv(ARGS_FROMWIRE) {
 		sr.length = 4 + ISC_SHA1_DIGESTLENGTH;
 	else if (sr.base[3] == DNS_DSDIGEST_SHA256)
 		sr.length = 4 + ISC_SHA256_DIGESTLENGTH;
+#ifdef ISC_GOST_DIGESTLENGTH
 	else if (sr.base[3] == DNS_DSDIGEST_GOST)
 		sr.length = 4 + ISC_GOST_DIGESTLENGTH;
+#endif
 	else if (sr.base[3] == DNS_DSDIGEST_SHA384)
 		sr.length = 4 + ISC_SHA384_DIGESTLENGTH;
 
@@ -236,9 +243,11 @@ fromstruct_dlv(ARGS_FROMSTRUCT) {
 	case DNS_DSDIGEST_SHA256:
 		REQUIRE(dlv->length == ISC_SHA256_DIGESTLENGTH);
 		break;
+#ifdef ISC_GOST_DIGESTLENGTH
 	case DNS_DSDIGEST_GOST:
 		REQUIRE(dlv->length == ISC_GOST_DIGESTLENGTH);
 		break;
+#endif
 	case DNS_DSDIGEST_SHA384:
 		REQUIRE(dlv->length == ISC_SHA384_DIGESTLENGTH);
 		break;
diff --git a/lib/dns/rdata/generic/ds_43.c b/lib/dns/rdata/generic/ds_43.c
index dd47c8d..fc7f126 100644
--- a/lib/dns/rdata/generic/ds_43.c
+++ b/lib/dns/rdata/generic/ds_43.c
@@ -30,6 +30,8 @@
 
 #include <dns/ds.h>
 
+#include "dst_gost.h"
+
 static inline isc_result_t
 fromtext_ds(ARGS_FROMTEXT) {
 	isc_token_t token;
@@ -81,9 +83,11 @@ fromtext_ds(ARGS_FROMTEXT) {
 	case DNS_DSDIGEST_SHA256:
 		length = ISC_SHA256_DIGESTLENGTH;
 		break;
+#ifdef ISC_GOST_DIGESTLENGTH
 	case DNS_DSDIGEST_GOST:
 		length = ISC_GOST_DIGESTLENGTH;
 		break;
+#endif
 	case DNS_DSDIGEST_SHA384:
 		length = ISC_SHA384_DIGESTLENGTH;
 		break;
@@ -168,8 +172,10 @@ fromwire_ds(ARGS_FROMWIRE) {
 	     sr.length < 4 + ISC_SHA1_DIGESTLENGTH) ||
 	    (sr.base[3] == DNS_DSDIGEST_SHA256 &&
 	     sr.length < 4 + ISC_SHA256_DIGESTLENGTH) ||
+#ifdef ISC_GOST_DIGESTLENGTH
 	    (sr.base[3] == DNS_DSDIGEST_GOST &&
 	     sr.length < 4 + ISC_GOST_DIGESTLENGTH) ||
+#endif
 	    (sr.base[3] == DNS_DSDIGEST_SHA384 &&
 	     sr.length < 4 + ISC_SHA384_DIGESTLENGTH))
 		return (ISC_R_UNEXPECTEDEND);
@@ -183,8 +189,10 @@ fromwire_ds(ARGS_FROMWIRE) {
 		sr.length = 4 + ISC_SHA1_DIGESTLENGTH;
 	else if (sr.base[3] == DNS_DSDIGEST_SHA256)
 		sr.length = 4 + ISC_SHA256_DIGESTLENGTH;
+#ifdef ISC_GOST_DIGESTLENGTH
 	else if (sr.base[3] == DNS_DSDIGEST_GOST)
 		sr.length = 4 + ISC_GOST_DIGESTLENGTH;
+#endif
 	else if (sr.base[3] == DNS_DSDIGEST_SHA384)
 		sr.length = 4 + ISC_SHA384_DIGESTLENGTH;
 
@@ -236,9 +244,11 @@ fromstruct_ds(ARGS_FROMSTRUCT) {
 	case DNS_DSDIGEST_SHA256:
 		REQUIRE(ds->length == ISC_SHA256_DIGESTLENGTH);
 		break;
+#ifdef ISC_GOST_DIGESTLENGTH
 	case DNS_DSDIGEST_GOST:
 		REQUIRE(ds->length == ISC_GOST_DIGESTLENGTH);
 		break;
+#endif
 	case DNS_DSDIGEST_SHA384:
 		REQUIRE(ds->length == ISC_SHA384_DIGESTLENGTH);
 		break;
diff --git a/lib/dns/tests/Makefile.in b/lib/dns/tests/Makefile.in
index db47476..3b19784 100644
--- a/lib/dns/tests/Makefile.in
+++ b/lib/dns/tests/Makefile.in
@@ -26,8 +26,9 @@ top_srcdir =	@top_srcdir@
 
 @BIND9_MAKE_INCLUDES@
 
-CINCLUDES =	-I. -Iinclude ${DNS_INCLUDES} ${ISC_INCLUDES} @DST_OPENSSL_INC@
-CDEFINES =	@USE_OPENSSL@ -DTESTS="\"${top_builddir}/lib/dns/tests/\""
+CINCLUDES =	-I. -Iinclude ${DNS_INCLUDES} ${ISC_INCLUDES} \
+		@DST_OPENSSL_INC@
+CDEFINES =	@CRYPTO@ -DTESTS="\"${top_builddir}/lib/dns/tests/\""
 
 ISCLIBS =	../../isc/libisc.@A@
 ISCDEPLIBS =	../../isc/libisc.@A@
@@ -37,13 +38,13 @@ DNSDEPLIBS =	../libdns.@A@
 LIBS =		@LIBS@ @ATFLIBS@
 
 OBJS =		dnstest.@O@
-SRCS =		dnstest.c master_test.c dbiterator_test.c time_test.c \
+SRCS =		dnstest.c gost_test.c master_test.c dbiterator_test.c time_test.c \
 		private_test.c update_test.c zonemgr_test.c zt_test.c \
 		dbdiff_test.c dispatch_test.c nsec3_test.c \
 		rdataset_test.c rdata_test.c
 
 SUBDIRS =
-TARGETS =	master_test@EXEEXT@ dbiterator_test@EXEEXT@ time_test@EXEEXT@ \
+TARGETS =	gost_test@EXEEXT@ master_test@EXEEXT@ dbiterator_test@EXEEXT@ time_test@EXEEXT@ \
 		private_test@EXEEXT@ update_test@EXEEXT@ zonemgr_test@EXEEXT@ \
 		zt_test@EXEEXT@ dbversion_test@EXEEXT@ dbdiff_test@EXEEXT@ \
 		dispatch_test@EXEEXT@ nsec3_test@EXEEXT@ \
@@ -123,6 +124,11 @@ dispatch_test@EXEEXT@: dispatch_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
 			dispatch_test.@O@ dnstest.@O@ ${DNSLIBS} \
 				${ISCLIBS} ${LIBS}
 
+gost_test@EXEEXT@: gost_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+			gost_test.@O@ dnstest.@O@ ${DNSLIBS} \
+			${ISCLIBS} ${LIBS}
+
 unit::
 	sh ${top_srcdir}/unit/unittest.sh
 
diff --git a/lib/dns/tests/gost_test.c b/lib/dns/tests/gost_test.c
new file mode 100644
index 0000000..0dd9e55
--- /dev/null
+++ b/lib/dns/tests/gost_test.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id$ */
+
+/* ! \file */
+
+#include <config.h>
+
+#include <atf-c.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include <isc/util.h>
+#include <isc/string.h>
+
+#include "dnstest.h"
+
+#if defined(HAVE_OPENSSL_GOST) || defined(HAVE_PKCS11_GOST)
+
+#include "../dst_gost.h"
+
+/*
+ * Test data from Wikipedia GOST (hash function)
+ */
+
+unsigned char digest[ISC_GOST_DIGESTLENGTH];
+unsigned char buffer[1024];
+const char *s;
+char str[ISC_GOST_DIGESTLENGTH];
+int i = 0;
+
+isc_result_t
+tohexstr(unsigned char *d, unsigned int len, char *out);
+/*
+ * Precondition: a hexadecimal number in *d, the length of that number in len,
+ *   and a pointer to a character array to put the output (*out).
+ * Postcondition: A String representation of the given hexadecimal number is
+ *   placed into the array *out
+ *
+ * 'out' MUST point to an array of at least len / 2 + 1
+ *
+ * Return values: ISC_R_SUCCESS if the operation is sucessful
+ */
+
+isc_result_t
+tohexstr(unsigned char *d, unsigned int len, char *out) {
+
+	out[0]='\0';
+	char c_ret[] = "AA";
+	unsigned int i;
+	strcat(out, "0x");
+	for (i = 0; i < len; i++) {
+		sprintf(c_ret, "%02X", d[i]);
+		strcat(out, c_ret);
+	}
+	strcat(out, "\0");
+	return (ISC_R_SUCCESS);
+}
+
+
+#define TEST_INPUT(x) (x), sizeof(x)-1
+
+typedef struct hash_testcase {
+	const char *input;
+	size_t input_len;
+	const char *result;
+	int repeats;
+} hash_testcase_t;
+
+ATF_TC(isc_gost);
+ATF_TC_HEAD(isc_gost, tc) {
+	atf_tc_set_md_var(tc, "descr",
+			  "GOST R 34.11-94 examples from Wikipedia");
+}
+ATF_TC_BODY(isc_gost, tc) {
+	isc_gost_t gost;
+	isc_result_t result;
+
+	UNUSED(tc);
+
+	/*
+	 * These are the various test vectors.  All of these are passed
+	 * through the hash function and the results are compared to the
+	 * result specified here.
+	 */
+	hash_testcase_t testcases[] = {
+		/* Test 1 */
+		{
+			TEST_INPUT(""),
+			"0x981E5F3CA30C841487830F84FB433E1"
+			"3AC1101569B9C13584AC483234CD656C0",
+			1
+		},
+		/* Test 2 */
+		{
+			TEST_INPUT("a"),
+			"0xE74C52DD282183BF37AF0079C9F7805"
+			"5715A103F17E3133CEFF1AACF2F403011",
+			1
+		},
+		/* Test 3 */
+		{
+			TEST_INPUT("abc"),
+			"0xB285056DBF18D7392D7677369524DD1"
+			"4747459ED8143997E163B2986F92FD42C",
+			1
+		},
+		/* Test 4 */
+		{
+			TEST_INPUT("message digest"),
+			"0xBC6041DD2AA401EBFA6E9886734174F"
+			"EBDB4729AA972D60F549AC39B29721BA0",
+			1
+		},
+		/* Test 5 */
+		{
+			TEST_INPUT("The quick brown fox jumps "
+				   "over the lazy dog"),
+			"0x9004294A361A508C586FE53D1F1B027"
+			"46765E71B765472786E4770D565830A76",
+			1
+		},
+		/* Test 6 */
+		{
+			TEST_INPUT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcde"
+				   "fghijklmnopqrstuvwxyz0123456789"),
+			"0x73B70A39497DE53A6E08C67B6D4DB85"
+			"3540F03E9389299D9B0156EF7E85D0F61",
+			1
+		},
+		/* Test 7 */
+		{
+			TEST_INPUT("1234567890123456789012345678901"
+				   "2345678901234567890123456789012"
+				   "345678901234567890"),
+			"0x6BC7B38989B28CF93AE8842BF9D7529"
+			"05910A7528A61E5BCE0782DE43E610C90",
+			1
+		},
+		/* Test 8 */
+		{
+			TEST_INPUT("This is message, length=32 bytes"),
+			"0x2CEFC2F7B7BDC514E18EA57FA74FF35"
+			"7E7FA17D652C75F69CB1BE7893EDE48EB",
+			1
+		},
+		/* Test 9 */
+		{
+			TEST_INPUT("Suppose the original message "
+				   "has length = 50 bytes"),
+			"0xC3730C5CBCCACF915AC292676F21E8B"
+			"D4EF75331D9405E5F1A61DC3130A65011",
+			1
+		},
+		/* Test 10 */
+		{
+			TEST_INPUT("U") /* times 128 */,
+			"0x1C4AC7614691BBF427FA2316216BE8F"
+			"10D92EDFD37CD1027514C1008F649C4E8",
+			128
+		},
+		/* Test 11 */
+		{
+			TEST_INPUT("a") /* times 1000000 */,
+			"0x8693287AA62F9478F7CB312EC0866B6"
+			"C4E4A0F11160441E8F4FFCD2715DD554F",
+			1000000
+		},
+		{ NULL, 0, NULL, 1 }
+	};
+
+	result = dns_test_begin(NULL, ISC_FALSE);
+	ATF_REQUIRE(result == ISC_R_SUCCESS);
+
+	hash_testcase_t *testcase = testcases;
+
+	while (testcase->input != NULL && testcase->result != NULL) {
+		result = isc_gost_init(&gost);
+		ATF_REQUIRE(result == ISC_R_SUCCESS);
+		for(i = 0; i < testcase->repeats; i++) {
+			result = isc_gost_update(&gost,
+					(const isc_uint8_t *) testcase->input,
+					testcase->input_len);
+			ATF_REQUIRE(result == ISC_R_SUCCESS);
+		}
+		result = isc_gost_final(&gost, digest);
+		ATF_REQUIRE(result == ISC_R_SUCCESS);
+		tohexstr(digest, ISC_GOST_DIGESTLENGTH, str);
+		ATF_CHECK_STREQ(str, testcase->result);
+
+		testcase++;
+	}
+
+	dns_test_end();
+}
+#else
+ATF_TC(untested);
+ATF_TC_HEAD(untested, tc) {
+	atf_tc_set_md_var(tc, "descr", "skipping gost test");
+}
+ATF_TC_BODY(untested, tc) {
+	UNUSED(tc);
+	atf_tc_skip("GOST hash not available");
+}
+#endif
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+#if defined(HAVE_OPENSSL_GOST) || defined(HAVE_PKCS11_GOST)
+	ATF_TP_ADD_TC(tp, isc_gost);
+#else
+	ATF_TP_ADD_TC(tp, untested);
+#endif
+	return (atf_no_error());
+}
+
diff --git a/lib/dns/tkey.c b/lib/dns/tkey.c
index 161c188..20c98e5 100644
--- a/lib/dns/tkey.c
+++ b/lib/dns/tkey.c
@@ -45,8 +45,14 @@
 #include <dst/dst.h>
 #include <dst/gssapi.h>
 
+#include "dst_internal.h"
+
 #define TKEY_RANDOM_AMOUNT 16
 
+#ifdef PKCS11CRYPTO
+#include <pk11/pk11.h>
+#endif
+
 #define RETERR(x) do { \
 	result = (x); \
 	if (result != ISC_R_SUCCESS) \
@@ -382,8 +388,8 @@ process_dhtkey(dns_message_t *msg, dns_name_t *signer, dns_name_t *name,
 	if (randomdata == NULL)
 		goto failure;
 
-	result = isc_entropy_getdata(tctx->ectx, randomdata,
-				     TKEY_RANDOM_AMOUNT, NULL, 0);
+	result = dst__entropy_getdata(randomdata, TKEY_RANDOM_AMOUNT,
+				      ISC_FALSE);
 	if (result != ISC_R_SUCCESS) {
 		tkey_log("process_dhtkey: failed to obtain entropy: %s",
 			 isc_result_totext(result));
diff --git a/lib/dns/tsig.c b/lib/dns/tsig.c
index c7768f4..3239bff 100644
--- a/lib/dns/tsig.c
+++ b/lib/dns/tsig.c
@@ -946,8 +946,9 @@ dns_tsig_sign(dns_message_t *msg) {
 		isc_buffer_t headerbuf;
 		isc_uint16_t digestbits;
 
-		ret = dst_context_create2(key->key, mctx,
-					  DNS_LOGCATEGORY_DNSSEC, &ctx);
+		ret = dst_context_create3(key->key, mctx,
+					  DNS_LOGCATEGORY_DNSSEC,
+					  ISC_TRUE, &ctx);
 		if (ret != ISC_R_SUCCESS)
 			return (ret);
 
@@ -1345,8 +1346,9 @@ dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg,
 		sig_r.base = tsig.signature;
 		sig_r.length = tsig.siglen;
 
-		ret = dst_context_create2(key, mctx,
-					  DNS_LOGCATEGORY_DNSSEC, &ctx);
+		ret = dst_context_create3(key, mctx,
+					  DNS_LOGCATEGORY_DNSSEC,
+					  ISC_FALSE, &ctx);
 		if (ret != ISC_R_SUCCESS)
 			return (ret);
 
@@ -1577,9 +1579,9 @@ tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) {
 	key = tsigkey->key;
 
 	if (msg->tsigctx == NULL) {
-		ret = dst_context_create2(key, mctx,
+		ret = dst_context_create3(key, mctx,
 					  DNS_LOGCATEGORY_DNSSEC,
-					  &msg->tsigctx);
+					  ISC_FALSE, &msg->tsigctx);
 		if (ret != ISC_R_SUCCESS)
 			goto cleanup_querystruct;
 
diff --git a/lib/export/dns/Makefile.in b/lib/export/dns/Makefile.in
index 1e4540f..e10bf59 100644
--- a/lib/export/dns/Makefile.in
+++ b/lib/export/dns/Makefile.in
@@ -28,10 +28,10 @@ export_srcdir =	@top_srcdir@/lib/export
 
 @BIND9_MAKE_INCLUDES@
 
-CINCLUDES =	-I. -Iinclude ${DNS_INCLUDES} -I${export_srcdir}/isc/include \
+CINCLUDES =	-I. -I${top_srcdir}/lib/dns -Iinclude ${DNS_INCLUDES} -I${export_srcdir}/isc/include \
 		${ISC_INCLUDES} @DST_OPENSSL_INC@ @DST_GSSAPI_INC@
 
-CDEFINES =	-DUSE_MD5 @USE_OPENSSL@ @USE_GSSAPI@
+CDEFINES =	-DUSE_MD5 @CRYPTO@ @USE_GSSAPI@
 
 CWARNINGS =
 
diff --git a/lib/export/isc/Makefile.in b/lib/export/isc/Makefile.in
index 62e5acd..a5f8bd0 100644
--- a/lib/export/isc/Makefile.in
+++ b/lib/export/isc/Makefile.in
@@ -27,7 +27,7 @@ CINCLUDES =	-I${srcdir}/unix/include \
 		-I${srcdir}/@ISC_ARCH_DIR@/include \
 		-I${export_srcdir}/isc/include -I${srcdir}/include \
 		@ISC_OPENSSL_INC@
-CDEFINES =	@USE_OPENSSL@ -DUSE_APPIMPREGISTER -DUSE_MEMIMPREGISTER \
+CDEFINES =	@CRYPTO@ -DUSE_APPIMPREGISTER -DUSE_MEMIMPREGISTER \
 		-DUSE_SOCKETIMPREGISTER -DUSE_TASKIMPREGISTER \
 		-DUSE_TIMERIMPREGISTER
 CWARNINGS =
@@ -48,7 +48,8 @@ UNIXOBJS =	@ISC_ISCIPV6_O@ \
 		unix/file.@O@ \
 		unix/fsaccess.@O@ \
 		unix/stdio.@O@ \
-		unix/stdtime.@O@ unix/strerror.@O@ unix/time.@O@
+		unix/stdtime.@O@ unix/strerror.@O@ unix/time.@O@ unix/entropy.@O@ \
+		unix/keyboard.@O@
 
 NLSOBJS =	nls/msgcat.@O@
 
diff --git a/lib/export/isc/unix/Makefile.in b/lib/export/isc/unix/Makefile.in
index 1873202..a904615 100644
--- a/lib/export/isc/unix/Makefile.in
+++ b/lib/export/isc/unix/Makefile.in
@@ -40,6 +40,8 @@ OBJS =		@ISC_IPV6_O@ \
 		file.@O@ fsaccess.@O@ \
 		stdio.@O@ stdtime.@O@ strerror.@O@ \
 		time.@O@ \
+		entropy.@O@ \
+		keyboard.@O@ \
 		${ISCDRIVEROBJS}
 
 # Alphabetically
@@ -51,6 +53,8 @@ SRCS =		@ISC_IPV6_C@ \
 		file.c fsaccess.c \
 		stdio.c stdtime.c strerror.c \
 		time.c \
+		entropy.c \
+		keyboard.c \
 		${ISCDRIVERSRCS}
 
 SUBDIRS =	include
diff --git a/lib/isc/Makefile.in b/lib/isc/Makefile.in
index eb718fd..df62ec9 100644
--- a/lib/isc/Makefile.in
+++ b/lib/isc/Makefile.in
@@ -23,16 +23,20 @@ top_srcdir =	@top_srcdir@
 
 @LIBISC_API@
 
+@BIND9_MAKE_INCLUDES@
+
+PROVIDER =	@PKCS11_PROVIDER@
+
 CINCLUDES =	-I${srcdir}/unix/include \
 		-I${srcdir}/@ISC_THREAD_DIR@/include \
 		-I${srcdir}/@ISC_ARCH_DIR@/include \
 		-I./include \
-		-I${srcdir}/include @ISC_OPENSSL_INC@
-CDEFINES =	@USE_OPENSSL@
+		-I${srcdir}/include @ISC_OPENSSL_INC@ ${DNS_INCLUDES}
+CDEFINES =	@CRYPTO@ -DPK11_LIB_LOCATION=\"${PROVIDER}\"
 CWARNINGS =
 
 # Alphabetically
-UNIXOBJS =	@ISC_ISCIPV6_O@ \
+UNIXOBJS =	@ISC_ISCIPV6_O@ @ISC_ISCPK11_API_O@ \
 		unix/app.@O@ unix/dir.@O@ unix/entropy.@O@ \
 		unix/errno2result.@O@ unix/file.@O@ unix/fsaccess.@O@ \
 		unix/interfaceiter.@O@ unix/keyboard.@O@ unix/net.@O@ \
@@ -50,7 +54,7 @@ WIN32OBJS = 	win32/condition.@O@ win32/dir.@O@ win32/file.@O@ \
 		win32/thread.@O@ win32/time.@O@
 
 # Alphabetically
-OBJS =		@ISC_EXTRA_OBJS@ \
+OBJS =		@ISC_EXTRA_OBJS@ @ISC_PK11_O@ @ISC_PK11_RESULT_O@ \
 		assertions.@O@ backtrace.@O@ base32.@O@ base64.@O@ \
 		bitstring.@O@ buffer.@O@ bufferlist.@O@ commandline.@O@ \
 		counter.@O@ error.@O@ event.@O@ \
@@ -68,7 +72,7 @@ OBJS =		@ISC_EXTRA_OBJS@ \
 SYMTBLOBJS =	backtrace-emptytbl.@O@
 
 # Alphabetically
-SRCS =		@ISC_EXTRA_SRCS@ \
+SRCS =		@ISC_EXTRA_SRCS@ @ISC_PK11_C@ @ISC_PK11_RESULT_C@ \
 		assertions.c backtrace.c base32.c base64.c bitstring.c \
 		buffer.c bufferlist.c commandline.c counter.c \
 		error.c event.c heap.c hex.c hmacmd5.c hmacsha.c \
diff --git a/lib/isc/entropy.c b/lib/isc/entropy.c
index da9e81f..ae882d8 100644
--- a/lib/isc/entropy.c
+++ b/lib/isc/entropy.c
@@ -46,6 +46,9 @@
 #include <isc/time.h>
 #include <isc/util.h>
 
+#ifdef PKCS11CRYPTO
+#include <pk11/pk11.h>
+#endif
 
 #define ENTROPY_MAGIC		ISC_MAGIC('E', 'n', 't', 'e')
 #define SOURCE_MAGIC		ISC_MAGIC('E', 'n', 't', 's')
@@ -1236,6 +1239,11 @@ isc_entropy_usebestsource(isc_entropy_t *ectx, isc_entropysource_t **source,
 		use_keyboard == ISC_ENTROPY_KEYBOARDNO  ||
 		use_keyboard == ISC_ENTROPY_KEYBOARDMAYBE);
 
+#ifdef PKCS11CRYPTO
+	if (randomfile != NULL)
+		pk11_rand_seed_fromfile(randomfile);
+#endif
+
 #ifdef PATH_RANDOMDEV
 	if (randomfile == NULL) {
 		randomfile = PATH_RANDOMDEV;
diff --git a/lib/isc/hmacmd5.c b/lib/isc/hmacmd5.c
index 4c4046d..79ec24a 100644
--- a/lib/isc/hmacmd5.c
+++ b/lib/isc/hmacmd5.c
@@ -33,6 +33,11 @@
 #include <isc/types.h>
 #include <isc/util.h>
 
+#if PKCS11CRYPTO || PKCS11CRYPTOWITHHMAC
+#include <pk11/internal.h>
+#include <pk11/pk11.h>
+#endif
+
 #ifdef ISC_PLATFORM_OPENSSLHASH
 
 void
@@ -60,6 +65,167 @@ isc_hmacmd5_sign(isc_hmacmd5_t *ctx, unsigned char *digest) {
 	HMAC_CTX_cleanup(ctx);
 }
 
+#elif PKCS11CRYPTOWITHHMAC
+
+static CK_BBOOL truevalue = TRUE;
+static CK_BBOOL falsevalue = FALSE;
+
+void
+isc_hmacmd5_init(isc_hmacmd5_t *ctx, const unsigned char *key,
+		 unsigned int len)
+{
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_MD5_HMAC, NULL, 0 };
+	CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
+	CK_KEY_TYPE keyType = CKK_MD5_HMAC;
+	CK_ATTRIBUTE keyTemplate[] =
+	{
+		{ CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_VALUE, NULL, (CK_ULONG) len }
+	};
+
+	DE_CONST(key, keyTemplate[5].pValue);
+	RUNTIME_CHECK(pk11_get_session(ctx, OP_DIGEST, ISC_TRUE, ISC_FALSE,
+				       ISC_FALSE, NULL, 0) == ISC_R_SUCCESS);
+	ctx->object = CK_INVALID_HANDLE;
+	PK11_FATALCHECK(pkcs_C_CreateObject,
+			(ctx->session, keyTemplate,
+			 (CK_ULONG) 6, &ctx->object));
+	INSIST(ctx->object != CK_INVALID_HANDLE);
+	PK11_FATALCHECK(pkcs_C_SignInit, (ctx->session, &mech, ctx->object));
+}
+
+void
+isc_hmacmd5_invalidate(isc_hmacmd5_t *ctx) {
+	CK_BYTE garbage[ISC_MD5_DIGESTLENGTH];
+	CK_ULONG len = ISC_MD5_DIGESTLENGTH;
+
+	if (ctx->handle == NULL)
+		return;
+	(void) pkcs_C_SignFinal(ctx->session, garbage, &len);
+	memset(garbage, 0, sizeof(garbage));
+	if (ctx->object != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(ctx->session, ctx->object);
+	ctx->object = CK_INVALID_HANDLE;
+	pk11_return_session(ctx);
+}
+
+void
+isc_hmacmd5_update(isc_hmacmd5_t *ctx, const unsigned char *buf,
+		   unsigned int len)
+{
+	CK_RV rv;
+	CK_BYTE_PTR pPart;
+
+	DE_CONST(buf, pPart);
+	PK11_FATALCHECK(pkcs_C_SignUpdate,
+			(ctx->session, pPart, (CK_ULONG) len));
+}
+
+void
+isc_hmacmd5_sign(isc_hmacmd5_t *ctx, unsigned char *digest) {
+	CK_RV rv;
+	CK_ULONG len = ISC_MD5_DIGESTLENGTH;
+
+	PK11_FATALCHECK(pkcs_C_SignFinal,
+			(ctx->session, (CK_BYTE_PTR) digest, &len));
+	if (ctx->object != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(ctx->session, ctx->object);
+	ctx->object = CK_INVALID_HANDLE;
+	pk11_return_session(ctx);
+}
+
+#elif PKCS11CRYPTO
+
+#define PADLEN 64
+#define IPAD 0x36
+#define OPAD 0x5C
+
+void
+isc_hmacmd5_init(isc_hmacmd5_t *ctx, const unsigned char *key,
+		 unsigned int len)
+{
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_MD5, NULL, 0 };
+	unsigned char ipad[PADLEN];
+	unsigned int i;
+
+	RUNTIME_CHECK(pk11_get_session(ctx, OP_DIGEST, ISC_TRUE, ISC_FALSE,
+				       ISC_FALSE, NULL, 0) == ISC_R_SUCCESS);
+	RUNTIME_CHECK((ctx->key = pk11_mem_get(PADLEN)) != NULL);
+	if (len > PADLEN) {
+		CK_BYTE_PTR kPart;
+		CK_ULONG kl;
+
+		PK11_FATALCHECK(pkcs_C_DigestInit, (ctx->session, &mech));
+		DE_CONST(key, kPart);
+		PK11_FATALCHECK(pkcs_C_DigestUpdate,
+				(ctx->session, kPart, (CK_ULONG) len));
+		kl = ISC_MD5_DIGESTLENGTH;
+		PK11_FATALCHECK(pkcs_C_DigestFinal,
+				(ctx->session, (CK_BYTE_PTR) ctx->key, &kl));
+	} else
+		memcpy(ctx->key, key, len);
+	PK11_FATALCHECK(pkcs_C_DigestInit, (ctx->session, &mech));
+	memset(ipad, IPAD, PADLEN);
+	for (i = 0; i < PADLEN; i++)
+		ipad[i] ^= ctx->key[i];
+	PK11_FATALCHECK(pkcs_C_DigestUpdate,
+			(ctx->session, ipad, (CK_ULONG) PADLEN));
+}
+
+void
+isc_hmacmd5_invalidate(isc_hmacmd5_t *ctx) {
+	if (ctx->key != NULL)
+		pk11_mem_put(ctx->key, PADLEN);
+	ctx->key = NULL;
+	isc_md5_invalidate(ctx);
+}
+
+void
+isc_hmacmd5_update(isc_hmacmd5_t *ctx, const unsigned char *buf,
+		   unsigned int len)
+{
+	CK_RV rv;
+	CK_BYTE_PTR pPart;
+
+	DE_CONST(buf, pPart);
+	PK11_FATALCHECK(pkcs_C_DigestUpdate,
+			(ctx->session, pPart, (CK_ULONG) len));
+}
+
+void
+isc_hmacmd5_sign(isc_hmacmd5_t *ctx, unsigned char *digest) {
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_MD5, NULL, 0 };
+	CK_ULONG len = ISC_MD5_DIGESTLENGTH;
+	CK_BYTE opad[PADLEN];
+	unsigned int i;
+
+	PK11_FATALCHECK(pkcs_C_DigestFinal,
+			(ctx->session, (CK_BYTE_PTR) digest,
+			 (CK_ULONG_PTR) &len));
+	memset(opad, OPAD, PADLEN);
+	for (i = 0; i < PADLEN; i++)
+		opad[i] ^= ctx->key[i];
+	pk11_mem_put(ctx->key, PADLEN);
+	ctx->key = NULL;
+	PK11_FATALCHECK(pkcs_C_DigestInit, (ctx->session, &mech));
+	PK11_FATALCHECK(pkcs_C_DigestUpdate,
+			(ctx->session, opad, (CK_ULONG) PADLEN));
+	PK11_FATALCHECK(pkcs_C_DigestUpdate,
+			(ctx->session, (CK_BYTE_PTR) digest, len));
+	PK11_FATALCHECK(pkcs_C_DigestFinal,
+			(ctx->session,
+			 (CK_BYTE_PTR) digest,
+			 (CK_ULONG_PTR) &len));
+	pk11_return_session(ctx);
+}
+
 #else
 
 #define PADLEN 64
diff --git a/lib/isc/hmacsha.c b/lib/isc/hmacsha.c
index 3870963..9b79bc7 100644
--- a/lib/isc/hmacsha.c
+++ b/lib/isc/hmacsha.c
@@ -34,6 +34,11 @@
 #include <isc/types.h>
 #include <isc/util.h>
 
+#if PKCS11CRYPTO
+#include <pk11/internal.h>
+#include <pk11/pk11.h>
+#endif
+ 
 #ifdef ISC_PLATFORM_OPENSSLHASH
 
 void
@@ -191,6 +196,376 @@ isc_hmacsha512_sign(isc_hmacsha512_t *ctx, unsigned char *digest, size_t len) {
 	memset(newdigest, 0, sizeof(newdigest));
 }
 
+#elif PKCS11CRYPTO
+
+static CK_BBOOL truevalue = TRUE;
+static CK_BBOOL falsevalue = FALSE;
+
+void
+isc_hmacsha1_init(isc_hmacsha1_t *ctx, const unsigned char *key,
+		 unsigned int len)
+{
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_SHA_1_HMAC, NULL, 0 };
+	CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
+	CK_KEY_TYPE keyType = CKK_SHA_1_HMAC;
+	CK_ATTRIBUTE keyTemplate[] =
+	{
+		{ CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_VALUE, NULL, (CK_ULONG) len }
+	};
+
+	DE_CONST(key, keyTemplate[5].pValue);
+	RUNTIME_CHECK(pk11_get_session(ctx, OP_DIGEST, ISC_TRUE, ISC_FALSE,
+				       ISC_FALSE, NULL, 0) == ISC_R_SUCCESS);
+	ctx->object = CK_INVALID_HANDLE;
+	PK11_FATALCHECK(pkcs_C_CreateObject,
+			(ctx->session, keyTemplate,
+			 (CK_ULONG) 6, &ctx->object));
+	INSIST(ctx->object != CK_INVALID_HANDLE);
+	PK11_FATALCHECK(pkcs_C_SignInit, (ctx->session, &mech, ctx->object));
+}
+
+void
+isc_hmacsha1_invalidate(isc_hmacsha1_t *ctx) {
+	CK_BYTE garbage[ISC_SHA1_DIGESTLENGTH];
+	CK_ULONG len = ISC_SHA1_DIGESTLENGTH;
+
+	if (ctx->handle == NULL)
+		return;
+	(void) pkcs_C_SignFinal(ctx->session, garbage, &len);
+	memset(garbage, 0, sizeof(garbage));
+	if (ctx->object != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(ctx->session, ctx->object);
+	ctx->object = CK_INVALID_HANDLE;
+	pk11_return_session(ctx);
+}
+
+void
+isc_hmacsha1_update(isc_hmacsha1_t *ctx, const unsigned char *buf,
+		   unsigned int len)
+{
+	CK_RV rv;
+	CK_BYTE_PTR pPart;
+
+	DE_CONST(buf, pPart);
+	PK11_FATALCHECK(pkcs_C_SignUpdate,
+			(ctx->session, pPart, (CK_ULONG) len));
+}
+
+void
+isc_hmacsha1_sign(isc_hmacsha1_t *ctx, unsigned char *digest, size_t len) {
+	CK_RV rv;
+	CK_BYTE newdigest[ISC_SHA1_DIGESTLENGTH];
+	CK_ULONG psl = ISC_SHA1_DIGESTLENGTH;
+
+	REQUIRE(len <= ISC_SHA1_DIGESTLENGTH);
+
+	PK11_FATALCHECK(pkcs_C_SignFinal, (ctx->session, newdigest, &psl));
+	if (ctx->object != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(ctx->session, ctx->object);
+	ctx->object = CK_INVALID_HANDLE;
+	pk11_return_session(ctx);
+	memcpy(digest, newdigest, len);
+	memset(newdigest, 0, sizeof(newdigest));
+}
+
+void
+isc_hmacsha224_init(isc_hmacsha224_t *ctx, const unsigned char *key,
+		    unsigned int len)
+{
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_SHA224_HMAC, NULL, 0 };
+	CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
+	CK_KEY_TYPE keyType = CKK_SHA224_HMAC;
+	CK_ATTRIBUTE keyTemplate[] =
+	{
+		{ CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_VALUE, NULL, (CK_ULONG) len }
+	};
+
+	DE_CONST(key, keyTemplate[5].pValue);
+	RUNTIME_CHECK(pk11_get_session(ctx, OP_DIGEST, ISC_TRUE, ISC_FALSE,
+				       ISC_FALSE, NULL, 0) == ISC_R_SUCCESS);
+	ctx->object = CK_INVALID_HANDLE;
+	PK11_FATALCHECK(pkcs_C_CreateObject,
+			(ctx->session, keyTemplate,
+			 (CK_ULONG) 6, &ctx->object));
+	INSIST(ctx->object != CK_INVALID_HANDLE);
+	PK11_FATALCHECK(pkcs_C_SignInit, (ctx->session, &mech, ctx->object));
+}
+
+void
+isc_hmacsha224_invalidate(isc_hmacsha224_t *ctx) {
+	CK_BYTE garbage[ISC_SHA224_DIGESTLENGTH];
+	CK_ULONG len = ISC_SHA224_DIGESTLENGTH;
+
+	if (ctx->handle == NULL)
+		return;
+	(void) pkcs_C_SignFinal(ctx->session, garbage, &len);
+	memset(garbage, 0, sizeof(garbage));
+	if (ctx->object != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(ctx->session, ctx->object);
+	ctx->object = CK_INVALID_HANDLE;
+	pk11_return_session(ctx);
+}
+
+void
+isc_hmacsha224_update(isc_hmacsha224_t *ctx, const unsigned char *buf,
+		      unsigned int len)
+{
+	CK_RV rv;
+	CK_BYTE_PTR pPart;
+
+	DE_CONST(buf, pPart);
+	PK11_FATALCHECK(pkcs_C_SignUpdate,
+			(ctx->session, pPart, (CK_ULONG) len));
+}
+
+void
+isc_hmacsha224_sign(isc_hmacsha224_t *ctx, unsigned char *digest, size_t len) {
+	CK_RV rv;
+	CK_BYTE newdigest[ISC_SHA224_DIGESTLENGTH];
+	CK_ULONG psl = ISC_SHA224_DIGESTLENGTH;
+
+	REQUIRE(len <= ISC_SHA224_DIGESTLENGTH);
+
+	PK11_FATALCHECK(pkcs_C_SignFinal, (ctx->session, newdigest, &psl));
+	if (ctx->object != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(ctx->session, ctx->object);
+	ctx->object = CK_INVALID_HANDLE;
+	pk11_return_session(ctx);
+	memcpy(digest, newdigest, len);
+	memset(newdigest, 0, sizeof(newdigest));
+}
+
+void
+isc_hmacsha256_init(isc_hmacsha256_t *ctx, const unsigned char *key,
+		    unsigned int len)
+{
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_SHA256_HMAC, NULL, 0 };
+	CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
+	CK_KEY_TYPE keyType = CKK_SHA256_HMAC;
+	CK_ATTRIBUTE keyTemplate[] =
+	{
+		{ CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_VALUE, NULL, (CK_ULONG) len }
+	};
+
+	DE_CONST(key, keyTemplate[5].pValue);
+	RUNTIME_CHECK(pk11_get_session(ctx, OP_DIGEST, ISC_TRUE, ISC_FALSE,
+				       ISC_FALSE, NULL, 0) == ISC_R_SUCCESS);
+	ctx->object = CK_INVALID_HANDLE;
+	PK11_FATALCHECK(pkcs_C_CreateObject,
+			(ctx->session, keyTemplate,
+			 (CK_ULONG) 6, &ctx->object));
+	INSIST(ctx->object != CK_INVALID_HANDLE);
+	PK11_FATALCHECK(pkcs_C_SignInit, (ctx->session, &mech, ctx->object));
+}
+
+void
+isc_hmacsha256_invalidate(isc_hmacsha256_t *ctx) {
+	CK_BYTE garbage[ISC_SHA256_DIGESTLENGTH];
+	CK_ULONG len = ISC_SHA256_DIGESTLENGTH;
+
+	if (ctx->handle == NULL)
+		return;
+	(void) pkcs_C_SignFinal(ctx->session, garbage, &len);
+	memset(garbage, 0, sizeof(garbage));
+	if (ctx->object != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(ctx->session, ctx->object);
+	ctx->object = CK_INVALID_HANDLE;
+	pk11_return_session(ctx);
+}
+
+void
+isc_hmacsha256_update(isc_hmacsha256_t *ctx, const unsigned char *buf,
+		      unsigned int len)
+{
+	CK_RV rv;
+	CK_BYTE_PTR pPart;
+
+	DE_CONST(buf, pPart);
+	PK11_FATALCHECK(pkcs_C_SignUpdate,
+			(ctx->session, pPart, (CK_ULONG) len));
+}
+
+void
+isc_hmacsha256_sign(isc_hmacsha256_t *ctx, unsigned char *digest, size_t len) {
+	CK_RV rv;
+	CK_BYTE newdigest[ISC_SHA256_DIGESTLENGTH];
+	CK_ULONG psl = ISC_SHA256_DIGESTLENGTH;
+
+	REQUIRE(len <= ISC_SHA256_DIGESTLENGTH);
+
+	PK11_FATALCHECK(pkcs_C_SignFinal, (ctx->session, newdigest, &psl));
+	if (ctx->object != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(ctx->session, ctx->object);
+	ctx->object = CK_INVALID_HANDLE;
+	pk11_return_session(ctx);
+	memcpy(digest, newdigest, len);
+	memset(newdigest, 0, sizeof(newdigest));
+}
+
+void
+isc_hmacsha384_init(isc_hmacsha384_t *ctx, const unsigned char *key,
+		    unsigned int len)
+{
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_SHA384_HMAC, NULL, 0 };
+	CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
+	CK_KEY_TYPE keyType = CKK_SHA384_HMAC;
+	CK_ATTRIBUTE keyTemplate[] =
+	{
+		{ CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_VALUE, NULL, (CK_ULONG) len }
+	};
+
+	DE_CONST(key, keyTemplate[5].pValue);
+	RUNTIME_CHECK(pk11_get_session(ctx, OP_DIGEST, ISC_TRUE, ISC_FALSE,
+				       ISC_FALSE, NULL, 0) == ISC_R_SUCCESS);
+	ctx->object = CK_INVALID_HANDLE;
+	PK11_FATALCHECK(pkcs_C_CreateObject,
+			(ctx->session, keyTemplate,
+			 (CK_ULONG) 6, &ctx->object));
+	INSIST(ctx->object != CK_INVALID_HANDLE);
+	PK11_FATALCHECK(pkcs_C_SignInit, (ctx->session, &mech, ctx->object));
+}
+
+void
+isc_hmacsha384_invalidate(isc_hmacsha384_t *ctx) {
+	CK_BYTE garbage[ISC_SHA384_DIGESTLENGTH];
+	CK_ULONG len = ISC_SHA384_DIGESTLENGTH;
+
+	if (ctx->handle == NULL)
+		return;
+	(void) pkcs_C_SignFinal(ctx->session, garbage, &len);
+	memset(garbage, 0, sizeof(garbage));
+	if (ctx->object != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(ctx->session, ctx->object);
+	ctx->object = CK_INVALID_HANDLE;
+	pk11_return_session(ctx);
+}
+
+void
+isc_hmacsha384_update(isc_hmacsha384_t *ctx, const unsigned char *buf,
+		      unsigned int len)
+{
+	CK_RV rv;
+	CK_BYTE_PTR pPart;
+
+	DE_CONST(buf, pPart);
+	PK11_FATALCHECK(pkcs_C_SignUpdate,
+			(ctx->session, pPart, (CK_ULONG) len));
+}
+
+void
+isc_hmacsha384_sign(isc_hmacsha384_t *ctx, unsigned char *digest, size_t len) {
+	CK_RV rv;
+	CK_BYTE newdigest[ISC_SHA384_DIGESTLENGTH];
+	CK_ULONG psl = ISC_SHA384_DIGESTLENGTH;
+
+	REQUIRE(len <= ISC_SHA384_DIGESTLENGTH);
+
+	PK11_FATALCHECK(pkcs_C_SignFinal, (ctx->session, newdigest, &psl));
+	if (ctx->object != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(ctx->session, ctx->object);
+	ctx->object = CK_INVALID_HANDLE;
+	pk11_return_session(ctx);
+	memcpy(digest, newdigest, len);
+	memset(newdigest, 0, sizeof(newdigest));
+}
+
+void
+isc_hmacsha512_init(isc_hmacsha512_t *ctx, const unsigned char *key,
+		    unsigned int len)
+{
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_SHA512_HMAC, NULL, 0 };
+	CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
+	CK_KEY_TYPE keyType = CKK_SHA512_HMAC;
+	CK_ATTRIBUTE keyTemplate[] =
+	{
+		{ CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) },
+		{ CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) },
+		{ CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) },
+		{ CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) },
+		{ CKA_VALUE, NULL, (CK_ULONG) len }
+	};
+
+	DE_CONST(key, keyTemplate[5].pValue);
+	RUNTIME_CHECK(pk11_get_session(ctx, OP_DIGEST, ISC_TRUE, ISC_FALSE,
+				       ISC_FALSE, NULL, 0) == ISC_R_SUCCESS);
+	ctx->object = CK_INVALID_HANDLE;
+	PK11_FATALCHECK(pkcs_C_CreateObject,
+			(ctx->session, keyTemplate,
+			 (CK_ULONG) 6, &ctx->object));
+	INSIST(ctx->object != CK_INVALID_HANDLE);
+	PK11_FATALCHECK(pkcs_C_SignInit, (ctx->session, &mech, ctx->object));
+}
+
+void
+isc_hmacsha512_invalidate(isc_hmacsha512_t *ctx) {
+	CK_BYTE garbage[ISC_SHA512_DIGESTLENGTH];
+	CK_ULONG len = ISC_SHA512_DIGESTLENGTH;
+
+	if (ctx->handle == NULL)
+		return;
+	(void) pkcs_C_SignFinal(ctx->session, garbage, &len);
+	memset(garbage, 0, sizeof(garbage));
+	if (ctx->object != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(ctx->session, ctx->object);
+	ctx->object = CK_INVALID_HANDLE;
+	pk11_return_session(ctx);
+}
+
+void
+isc_hmacsha512_update(isc_hmacsha512_t *ctx, const unsigned char *buf,
+		      unsigned int len)
+{
+	CK_RV rv;
+	CK_BYTE_PTR pPart;
+
+	DE_CONST(buf, pPart);
+	PK11_FATALCHECK(pkcs_C_SignUpdate,
+			(ctx->session, pPart, (CK_ULONG) len));
+}
+
+void
+isc_hmacsha512_sign(isc_hmacsha512_t *ctx, unsigned char *digest, size_t len) {
+	CK_RV rv;
+	CK_BYTE newdigest[ISC_SHA512_DIGESTLENGTH];
+	CK_ULONG psl = ISC_SHA512_DIGESTLENGTH;
+
+	REQUIRE(len <= ISC_SHA512_DIGESTLENGTH);
+
+	PK11_FATALCHECK(pkcs_C_SignFinal, (ctx->session, newdigest, &psl));
+	if (ctx->object != CK_INVALID_HANDLE)
+		(void) pkcs_C_DestroyObject(ctx->session, ctx->object);
+	ctx->object = CK_INVALID_HANDLE;
+	pk11_return_session(ctx);
+	memcpy(digest, newdigest, len);
+	memset(newdigest, 0, sizeof(newdigest));
+}
+
 #else
 
 #define IPAD 0x36
diff --git a/lib/isc/include/Makefile.in b/lib/isc/include/Makefile.in
index 70c165e..c92ad45 100644
--- a/lib/isc/include/Makefile.in
+++ b/lib/isc/include/Makefile.in
@@ -19,7 +19,7 @@ srcdir =	@srcdir@
 VPATH =		@srcdir@
 top_srcdir =	@top_srcdir@
 
-SUBDIRS =	isc
+SUBDIRS =	isc pk11 pkcs11
 TARGETS =
 
 @BIND9_MAKE_RULES@
diff --git a/lib/isc/include/isc/hmacmd5.h b/lib/isc/include/isc/hmacmd5.h
index 9ecad45..e008328 100644
--- a/lib/isc/include/isc/hmacmd5.h
+++ b/lib/isc/include/isc/hmacmd5.h
@@ -37,6 +37,11 @@
 
 typedef HMAC_CTX isc_hmacmd5_t;
 
+#elif PKCS11CRYPTO
+#include <pk11/pk11.h>
+
+typedef pk11_context_t isc_hmacmd5_t;
+
 #else
 
 typedef struct {
diff --git a/lib/isc/include/isc/hmacsha.h b/lib/isc/include/isc/hmacsha.h
index 1d0e184..c223897 100644
--- a/lib/isc/include/isc/hmacsha.h
+++ b/lib/isc/include/isc/hmacsha.h
@@ -45,6 +45,15 @@ typedef HMAC_CTX isc_hmacsha256_t;
 typedef HMAC_CTX isc_hmacsha384_t;
 typedef HMAC_CTX isc_hmacsha512_t;
 
+#elif PKCS11CRYPTO
+#include <pk11/pk11.h>
+
+typedef pk11_context_t isc_hmacsha1_t;
+typedef pk11_context_t isc_hmacsha224_t;
+typedef pk11_context_t isc_hmacsha256_t;
+typedef pk11_context_t isc_hmacsha384_t;
+typedef pk11_context_t isc_hmacsha512_t;
+
 #else
 
 typedef struct {
diff --git a/lib/isc/include/isc/md5.h b/lib/isc/include/isc/md5.h
index dfa586d..a2e00b3 100644
--- a/lib/isc/include/isc/md5.h
+++ b/lib/isc/include/isc/md5.h
@@ -55,6 +55,11 @@
 
 typedef EVP_MD_CTX isc_md5_t;
 
+#elif PKCS11CRYPTO
+#include <pk11/pk11.h>
+
+typedef pk11_context_t isc_md5_t;
+
 #else
 
 typedef struct {
diff --git a/lib/isc/include/isc/resultclass.h b/lib/isc/include/isc/resultclass.h
index d91e800..44b0eb4 100644
--- a/lib/isc/include/isc/resultclass.h
+++ b/lib/isc/include/isc/resultclass.h
@@ -46,6 +46,6 @@
 #define	ISC_RESULTCLASS_OMAPI		ISC_RESULTCLASS_FROMNUM(4)
 #define	ISC_RESULTCLASS_ISCCC		ISC_RESULTCLASS_FROMNUM(5)
 #define	ISC_RESULTCLASS_DHCP		ISC_RESULTCLASS_FROMNUM(6)
-
+#define	ISC_RESULTCLASS_PK11		ISC_RESULTCLASS_FROMNUM(7)
 
 #endif /* ISC_RESULTCLASS_H */
diff --git a/lib/isc/include/isc/sha1.h b/lib/isc/include/isc/sha1.h
index 313ff96..f11a783 100644
--- a/lib/isc/include/isc/sha1.h
+++ b/lib/isc/include/isc/sha1.h
@@ -40,6 +40,11 @@
 
 typedef EVP_MD_CTX isc_sha1_t;
 
+#elif PKCS11CRYPTO
+#include <pk11/pk11.h>
+
+typedef pk11_context_t isc_sha1_t;
+
 #else
 
 typedef struct {
diff --git a/lib/isc/include/isc/sha2.h b/lib/isc/include/isc/sha2.h
index 439bbb9..14faa6e 100644
--- a/lib/isc/include/isc/sha2.h
+++ b/lib/isc/include/isc/sha2.h
@@ -84,6 +84,12 @@
 typedef EVP_MD_CTX isc_sha256_t;
 typedef EVP_MD_CTX isc_sha512_t;
 
+#elif PKCS11CRYPTO
+#include <pk11/pk11.h>
+
+typedef pk11_context_t isc_sha256_t;
+typedef pk11_context_t isc_sha512_t;
+
 #else
 
 /*
diff --git a/lib/isc/include/pk11/Makefile.in b/lib/isc/include/pk11/Makefile.in
new file mode 100644
index 0000000..744c40e
--- /dev/null
+++ b/lib/isc/include/pk11/Makefile.in
@@ -0,0 +1,38 @@
+# Copyright (C) 2014  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+srcdir =	@srcdir@
+VPATH =		@srcdir@
+top_srcdir =	@top_srcdir@
+
+@BIND9_VERSION@
+
+#
+# Only list headers that are to be installed and are not
+# machine generated.  The latter are handled specially in the
+# install target below.
+#
+HEADERS =	constants.h internal.h pk11.h result.h
+SUBDIRS =
+TARGETS =
+
+@BIND9_MAKE_RULES@
+
+installdirs:
+	$(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${includedir}/pk11
+
+install:: installdirs
+	for i in ${HEADERS}; do \
+		${INSTALL_DATA} ${srcdir}/$$i ${DESTDIR}${includedir}/pk11 ; \
+	done
diff --git a/lib/isc/include/pk11/constants.h b/lib/isc/include/pk11/constants.h
new file mode 100644
index 0000000..e1e0581
--- /dev/null
+++ b/lib/isc/include/pk11/constants.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2014  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id$ */
+
+#ifndef PK11_CONSTANTS_H
+#define PK11_CONSTANTS_H 1
+
+/*! \file pk11/constants.h */
+
+/*%
+ * Static arrays of data used for key template initalization
+ */
+#ifdef WANT_ECC_CURVES
+static CK_BYTE pk11_ecc_prime256v1[] = {
+	0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07
+};
+static CK_BYTE pk11_ecc_secp384r1[] = {
+	0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22
+};
+#endif
+
+#ifdef WANT_DH_PRIMES
+static CK_BYTE pk11_dh_bn2[] = { 2 };
+static CK_BYTE pk11_dh_bn768[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34,
+	0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1,
+	0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74,
+	0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22,
+	0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd,
+	0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b,
+	0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37,
+	0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45,
+	0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6,
+	0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x3a, 0x36, 0x20,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+static CK_BYTE pk11_dh_bn1024[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34,
+	0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1,
+	0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74,
+	0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22,
+	0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd,
+	0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b,
+	0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37,
+	0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45,
+	0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6,
+	0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x37, 0xed, 0x6b,
+	0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed,
+	0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5,
+	0xae, 0x9f, 0x24, 0x11, 0x7c, 0x4b, 0x1f, 0xe6,
+	0x49, 0x28, 0x66, 0x51, 0xec, 0xe6, 0x53, 0x81,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+static CK_BYTE pk11_dh_bn1536[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34,
+	0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1,
+	0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74,
+	0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22,
+	0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd,
+	0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b,
+	0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37,
+	0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45,
+	0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6,
+	0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x37, 0xed, 0x6b,
+	0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed,
+	0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5,
+	0xae, 0x9f, 0x24, 0x11, 0x7c, 0x4b, 0x1f, 0xe6,
+	0x49, 0x28, 0x66, 0x51, 0xec, 0xe4, 0x5b, 0x3d,
+	0xc2, 0x00, 0x7c, 0xb8, 0xa1, 0x63, 0xbf, 0x05,
+	0x98, 0xda, 0x48, 0x36, 0x1c, 0x55, 0xd3, 0x9a,
+	0x69, 0x16, 0x3f, 0xa8, 0xfd, 0x24, 0xcf, 0x5f,
+	0x83, 0x65, 0x5d, 0x23, 0xdc, 0xa3, 0xad, 0x96,
+	0x1c, 0x62, 0xf3, 0x56, 0x20, 0x85, 0x52, 0xbb,
+	0x9e, 0xd5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6d,
+	0x67, 0x0c, 0x35, 0x4e, 0x4a, 0xbc, 0x98, 0x04,
+	0xf1, 0x74, 0x6c, 0x08, 0xca, 0x23, 0x73, 0x27,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+#endif
+
+#ifdef WANT_GOST_PARAMS
+static CK_BYTE pk11_gost_a_paramset[] = {
+	0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01
+};
+static CK_BYTE pk11_gost_paramset[] = {
+	0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01
+};
+#endif
+
+#endif /* PK11_CONSTANTS_H */
diff --git a/lib/isc/include/pk11/internal.h b/lib/isc/include/pk11/internal.h
new file mode 100644
index 0000000..14bef3c
--- /dev/null
+++ b/lib/isc/include/pk11/internal.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id$ */
+
+#ifndef PK11_INTERNAL_H
+#define PK11_INTERNAL_H 1
+
+/*! \file pk11/internal.h */
+
+ISC_LANG_BEGINDECLS
+
+const char *pk11_get_lib_name(void);
+
+void *pk11_mem_get(size_t size);
+
+void pk11_mem_put(void *ptr, size_t size);
+
+CK_SLOT_ID pk11_get_best_token(pk11_optype_t optype);
+
+unsigned int pk11_numbits(CK_BYTE_PTR data, unsigned int bytecnt);
+
+CK_ATTRIBUTE *pk11_attribute_first(const pk11_object_t *obj);
+
+CK_ATTRIBUTE *pk11_attribute_next(const pk11_object_t *obj,
+				  CK_ATTRIBUTE *attr);
+
+CK_ATTRIBUTE *pk11_attribute_bytype(const pk11_object_t *obj,
+				    CK_ATTRIBUTE_TYPE type);
+
+ISC_LANG_ENDDECLS
+
+#endif /* PK11_INTERNAL_H */
diff --git a/lib/isc/include/pk11/pk11.h b/lib/isc/include/pk11/pk11.h
new file mode 100644
index 0000000..964a2a7
--- /dev/null
+++ b/lib/isc/include/pk11/pk11.h
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2014  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef PK11_PK11_H
+#define PK11_PK11_H 1
+
+/*! \file pk11/pk11.h */
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/types.h>
+
+#define PK11_FATALCHECK(func, args) \
+	((void) (((rv = (func) args) == CKR_OK) || \
+		 ((pk11_error_fatalcheck)(__FILE__, __LINE__, #func, rv), 0)))
+
+#include <pkcs11/cryptoki.h>
+
+ISC_LANG_BEGINDECLS
+
+#define SES_MAGIC	ISC_MAGIC('P','K','S','S')
+#define TOK_MAGIC	ISC_MAGIC('P','K','T','K')
+
+#define VALID_SES(x)	ISC_MAGIC_VALID(x, SES_MAGIC)
+#define VALID_TOK(x)	ISC_MAGIC_VALID(x, TOK_MAGIC)
+
+typedef struct pk11_context pk11_context_t;
+
+struct pk11_object {
+	CK_OBJECT_HANDLE	object;
+	CK_SLOT_ID		slot;
+	CK_BBOOL		ontoken;
+	CK_BBOOL		reqlogon;
+	CK_BYTE			attrcnt;
+	CK_ATTRIBUTE		*repr;
+};
+
+struct pk11_context {
+	void			*handle;
+	CK_SESSION_HANDLE	session;
+	CK_BBOOL		ontoken;
+	CK_OBJECT_HANDLE	object;
+#ifndef PKCS11CRYPTOWITHHMAC
+	unsigned char		*key;
+#endif
+};
+
+typedef struct pk11_object pk11_object_t;
+
+typedef enum {
+	OP_ANY = 0,
+	OP_RAND = 1,
+	OP_RSA = 2,
+	OP_DSA = 3,
+	OP_DH = 4,
+	OP_DIGEST = 5,
+	OP_EC = 6,
+	OP_GOST = 7,
+	OP_AES = 8,
+	OP_MAX = 9
+} pk11_optype_t;
+
+/*%
+ * Function prototypes
+ */
+
+void pk11_set_lib_name(const char *lib_name);
+/*%<
+ * Set the PKCS#11 provider (aka library) path/name.
+ */
+
+isc_result_t pk11_initialize(isc_mem_t *mctx, const char *engine);
+/*%<
+ * Initialize PKCS#11 device
+ *
+ * mctx:   memory context to attach to pk11_mctx.
+ * engine: PKCS#11 provider (aka library) path/name.
+ *
+ * returns:
+ *         ISC_R_SUCCESS
+ *         PK11_R_NOPROVIDER: can't load the provider
+ *         PK11_R_INITFAILED: C_Initialize() failed
+ *         PK11_R_NORANDOMSERVICE: can't find required random service
+ *         PK11_R_NODIGESTSERVICE: can't find required digest service
+ *         PK11_R_NOAESSERVICE: can't find required AES service
+ */
+
+isc_result_t pk11_get_session(pk11_context_t *ctx,
+			      pk11_optype_t optype,
+			      isc_boolean_t need_services,
+			      isc_boolean_t rw,
+			      isc_boolean_t logon,
+			      const char *pin,
+			      CK_SLOT_ID slot);
+/*%<
+ * Initialize PKCS#11 device and acquire a session.
+ *
+ * need_services:
+ * 	  if ISC_TRUE, this session requires full PKCS#11 API
+ * 	  support including random and digest services, and
+ * 	  the lack of these services will cause the session not
+ * 	  to be initialized.  If ISC_FALSE, the function will return
+ * 	  an error code indicating the missing service, but the
+ * 	  session will be usable for other purposes.
+ * rw:    if ISC_TRUE, session will be read/write (useful for
+ *        generating or destroying keys); otherwise read-only.
+ * login: indicates whether to log in to the device
+ * pin:   optional PIN, overriding any PIN currently associated
+ *        with the
+ * slot:  device slot ID
+ */
+
+void pk11_return_session(pk11_context_t *ctx);
+/*%<
+ * Release an active PKCS#11 session for reuse.
+ */
+
+isc_result_t pk11_finalize(void);
+/*%<
+ * Shut down PKCS#11 device and free all sessions.
+ */
+
+isc_result_t pk11_rand_bytes(unsigned char *buf, int num);
+
+void pk11_rand_seed_fromfile(const char *randomfile);
+
+isc_result_t pk11_parse_uri(pk11_object_t *obj, const char *label,
+			    isc_mem_t *mctx, pk11_optype_t optype);
+
+ISC_PLATFORM_NORETURN_PRE void
+pk11_error_fatalcheck(const char *file, int line,
+		      const char *funcname, CK_RV rv)
+ISC_PLATFORM_NORETURN_POST;
+
+void pk11_dump_tokens(void);
+
+CK_RV
+pkcs_C_Initialize(CK_VOID_PTR pReserved);
+
+CK_RV
+pkcs_C_Finalize(CK_VOID_PTR pReserved);
+
+CK_RV
+pkcs_C_GetSlotList(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList,
+		   CK_ULONG_PTR pulCount);
+
+CK_RV
+pkcs_C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo);
+
+CK_RV
+pkcs_C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type,
+			CK_MECHANISM_INFO_PTR pInfo);
+
+CK_RV
+pkcs_C_OpenSession(CK_SLOT_ID slotID, CK_FLAGS flags,
+		   CK_VOID_PTR pApplication,
+		   CK_RV  (*Notify) (CK_SESSION_HANDLE hSession,
+				     CK_NOTIFICATION event,
+				     CK_VOID_PTR pApplication),
+		   CK_SESSION_HANDLE_PTR phSession);
+
+CK_RV
+pkcs_C_CloseSession(CK_SESSION_HANDLE hSession);
+
+CK_RV
+pkcs_C_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType,
+	     CK_CHAR_PTR pPin, CK_ULONG usPinLen);
+
+CK_RV
+pkcs_C_Logout(CK_SESSION_HANDLE hSession);
+
+CK_RV
+pkcs_C_CreateObject(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate,
+		    CK_ULONG usCount, CK_OBJECT_HANDLE_PTR phObject);
+
+CK_RV
+pkcs_C_DestroyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject);
+
+CK_RV
+pkcs_C_GetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
+			 CK_ATTRIBUTE_PTR pTemplate, CK_ULONG usCount);
+
+CK_RV
+pkcs_C_SetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
+			 CK_ATTRIBUTE_PTR pTemplate, CK_ULONG usCount);
+
+CK_RV
+pkcs_C_FindObjectsInit(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate,
+		       CK_ULONG usCount);
+
+CK_RV
+pkcs_C_FindObjects(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE_PTR phObject,
+		   CK_ULONG usMaxObjectCount, CK_ULONG_PTR pusObjectCount);
+
+CK_RV
+pkcs_C_FindObjectsFinal(CK_SESSION_HANDLE hSession);
+
+CK_RV
+pkcs_C_EncryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
+		   CK_OBJECT_HANDLE hKey);
+
+CK_RV
+pkcs_C_Encrypt(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData,
+	       CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData,
+	       CK_ULONG_PTR pulEncryptedDataLen);
+
+CK_RV
+pkcs_C_DigestInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism);
+
+CK_RV
+pkcs_C_DigestUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart,
+		    CK_ULONG ulPartLen);
+
+CK_RV
+pkcs_C_DigestFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pDigest,
+		   CK_ULONG_PTR pulDigestLen);
+
+CK_RV
+pkcs_C_SignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
+		CK_OBJECT_HANDLE hKey);
+
+CK_RV
+pkcs_C_Sign(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData,
+	    CK_ULONG ulDataLen, CK_BYTE_PTR pSignature,
+	    CK_ULONG_PTR pulSignatureLen);
+
+CK_RV
+pkcs_C_SignUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart,
+		  CK_ULONG ulPartLen);
+
+CK_RV
+pkcs_C_SignFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature,
+		 CK_ULONG_PTR pulSignatureLen);
+
+CK_RV
+pkcs_C_VerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
+		  CK_OBJECT_HANDLE hKey);
+
+CK_RV
+pkcs_C_Verify(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData,
+	      CK_ULONG ulDataLen, CK_BYTE_PTR pSignature,
+	      CK_ULONG ulSignatureLen);
+
+CK_RV
+pkcs_C_VerifyUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart,
+		    CK_ULONG ulPartLen);
+
+CK_RV
+pkcs_C_VerifyFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature,
+		   CK_ULONG ulSignatureLen);
+
+CK_RV
+pkcs_C_GenerateKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
+		   CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount,
+		   CK_OBJECT_HANDLE_PTR phKey);
+
+CK_RV
+pkcs_C_GenerateKeyPair(CK_SESSION_HANDLE hSession,
+		       CK_MECHANISM_PTR pMechanism,
+		       CK_ATTRIBUTE_PTR pPublicKeyTemplate,
+		       CK_ULONG usPublicKeyAttributeCount,
+		       CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
+		       CK_ULONG usPrivateKeyAttributeCount,
+		       CK_OBJECT_HANDLE_PTR phPrivateKey,
+		       CK_OBJECT_HANDLE_PTR phPublicKey);
+
+CK_RV
+pkcs_C_DeriveKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
+		 CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate,
+		 CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey);
+
+CK_RV
+pkcs_C_SeedRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed,
+		  CK_ULONG ulSeedLen);
+
+CK_RV
+pkcs_C_GenerateRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR RandomData,
+		      CK_ULONG ulRandomLen);
+
+ISC_LANG_ENDDECLS
+
+#endif /* PK11_PK11_H */
diff --git a/lib/isc/include/pk11/result.h b/lib/isc/include/pk11/result.h
new file mode 100644
index 0000000..f624140
--- /dev/null
+++ b/lib/isc/include/pk11/result.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef PK11_RESULT_H
+#define PK11_RESULT_H 1
+
+/*! \file pk11/result.h */
+
+#include <isc/lang.h>
+#include <isc/resultclass.h>
+
+/*
+ * Nothing in this file truly depends on <isc/result.h>, but the
+ * PK11 result codes are considered to be publicly derived from
+ * the ISC result codes, so including this file buys you the ISC_R_
+ * namespace too.
+ */
+#include <isc/result.h>		/* Contractual promise. */
+
+#define PK11_R_INITFAILED		(ISC_RESULTCLASS_PK11 + 0)
+#define PK11_R_NOPROVIDER		(ISC_RESULTCLASS_PK11 + 1)
+#define PK11_R_NORANDOMSERVICE		(ISC_RESULTCLASS_PK11 + 2)
+#define PK11_R_NODIGESTSERVICE		(ISC_RESULTCLASS_PK11 + 3)
+#define PK11_R_NOAESSERVICE		(ISC_RESULTCLASS_PK11 + 4)
+
+#define PK11_R_NRESULTS			5	/* Number of results */
+
+ISC_LANG_BEGINDECLS
+
+LIBISC_EXTERNAL_DATA extern isc_msgcat_t *pk11_msgcat;
+
+void
+pk11_initmsgcat(void);
+
+const char *
+pk11_result_totext(isc_result_t);
+
+void
+pk11_result_register(void);
+
+ISC_LANG_ENDDECLS
+
+#endif /* PK11_RESULT_H */
diff --git a/lib/isc/include/pkcs11/Makefile.in b/lib/isc/include/pkcs11/Makefile.in
new file mode 100644
index 0000000..6e98688
--- /dev/null
+++ b/lib/isc/include/pkcs11/Makefile.in
@@ -0,0 +1,40 @@
+# Copyright (C) 2014  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# $Id: Makefile.in,v 1.7 2007/06/19 23:47:22 tbox Exp $
+
+srcdir =	@srcdir@
+VPATH =		@srcdir@
+top_srcdir =	@top_srcdir@
+
+@BIND9_VERSION@
+
+#
+# Only list headers that are to be installed and are not
+# machine generated.  The latter are handled specially in the
+# install target below.
+#
+HEADERS =	pkcs11f.h pkcs11.h pkcs11t.h
+SUBDIRS =
+TARGETS =
+
+@BIND9_MAKE_RULES@
+
+installdirs:
+	$(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${includedir}/pkcs11
+
+install:: installdirs
+	for i in ${HEADERS}; do \
+		${INSTALL_DATA} ${srcdir}/$$i ${DESTDIR}${includedir}/pkcs11 ; \
+	done
diff --git a/lib/isc/include/pkcs11/pkcs11.h b/lib/isc/include/pkcs11/pkcs11.h
new file mode 100644
index 0000000..9261e1e
--- /dev/null
+++ b/lib/isc/include/pkcs11/pkcs11.h
@@ -0,0 +1,299 @@
+/* pkcs11.h include file for PKCS #11. */
+/* $Revision: 1.2 $ */
+
+/* License to copy and use this software is granted provided that it is
+ * identified as "RSA Security Inc. PKCS #11 Cryptographic Token Interface
+ * (Cryptoki)" in all material mentioning or referencing this software.
+
+ * License is also granted to make and use derivative works provided that
+ * such works are identified as "derived from the RSA Security Inc. PKCS #11
+ * Cryptographic Token Interface (Cryptoki)" in all material mentioning or 
+ * referencing the derived work.
+
+ * RSA Security Inc. makes no representations concerning either the 
+ * merchantability of this software or the suitability of this software for
+ * any particular purpose. It is provided "as is" without express or implied
+ * warranty of any kind.
+ */
+
+#ifndef _PKCS11_H_
+#define _PKCS11_H_ 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Before including this file (pkcs11.h) (or pkcs11t.h by
+ * itself), 6 platform-specific macros must be defined.  These
+ * macros are described below, and typical definitions for them
+ * are also given.  Be advised that these definitions can depend
+ * on both the platform and the compiler used (and possibly also
+ * on whether a Cryptoki library is linked statically or
+ * dynamically).
+ *
+ * In addition to defining these 6 macros, the packing convention
+ * for Cryptoki structures should be set.  The Cryptoki
+ * convention on packing is that structures should be 1-byte
+ * aligned.
+ *
+ * If you're using Microsoft Developer Studio 5.0 to produce
+ * Win32 stuff, this might be done by using the following
+ * preprocessor directive before including pkcs11.h or pkcs11t.h:
+ *
+ * #pragma pack(push, cryptoki, 1)
+ *
+ * and using the following preprocessor directive after including
+ * pkcs11.h or pkcs11t.h:
+ *
+ * #pragma pack(pop, cryptoki)
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to produce Win16 stuff, this might be done by using
+ * the following preprocessor directive before including
+ * pkcs11.h or pkcs11t.h:
+ *
+ * #pragma pack(1)
+ *
+ * In a UNIX environment, you're on your own for this.  You might
+ * not need to do (or be able to do!) anything.
+ *
+ *
+ * Now for the macros:
+ *
+ *
+ * 1. CK_PTR: The indirection string for making a pointer to an
+ * object.  It can be used like this:
+ *
+ * typedef CK_BYTE CK_PTR CK_BYTE_PTR;
+ *
+ * If you're using Microsoft Developer Studio 5.0 to produce
+ * Win32 stuff, it might be defined by:
+ *
+ * #define CK_PTR *
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to produce Win16 stuff, it might be defined by:
+ *
+ * #define CK_PTR far *
+ *
+ * In a typical UNIX environment, it might be defined by:
+ *
+ * #define CK_PTR *
+ *
+ *
+ * 2. CK_DEFINE_FUNCTION(returnType, name): A macro which makes
+ * an exportable Cryptoki library function definition out of a
+ * return type and a function name.  It should be used in the
+ * following fashion to define the exposed Cryptoki functions in
+ * a Cryptoki library:
+ *
+ * CK_DEFINE_FUNCTION(CK_RV, C_Initialize)(
+ *   CK_VOID_PTR pReserved
+ * )
+ * {
+ *   ...
+ * }
+ *
+ * If you're using Microsoft Developer Studio 5.0 to define a
+ * function in a Win32 Cryptoki .dll, it might be defined by:
+ *
+ * #define CK_DEFINE_FUNCTION(returnType, name) \
+ *   returnType __declspec(dllexport) name
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to define a function in a Win16 Cryptoki .dll, it
+ * might be defined by:
+ *
+ * #define CK_DEFINE_FUNCTION(returnType, name) \
+ *   returnType __export _far _pascal name
+ *
+ * In a UNIX environment, it might be defined by:
+ *
+ * #define CK_DEFINE_FUNCTION(returnType, name) \
+ *   returnType name
+ *
+ *
+ * 3. CK_DECLARE_FUNCTION(returnType, name): A macro which makes
+ * an importable Cryptoki library function declaration out of a
+ * return type and a function name.  It should be used in the
+ * following fashion:
+ *
+ * extern CK_DECLARE_FUNCTION(CK_RV, C_Initialize)(
+ *   CK_VOID_PTR pReserved
+ * );
+ *
+ * If you're using Microsoft Developer Studio 5.0 to declare a
+ * function in a Win32 Cryptoki .dll, it might be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION(returnType, name) \
+ *   returnType __declspec(dllimport) name
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to declare a function in a Win16 Cryptoki .dll, it
+ * might be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION(returnType, name) \
+ *   returnType __export _far _pascal name
+ *
+ * In a UNIX environment, it might be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION(returnType, name) \
+ *   returnType name
+ *
+ *
+ * 4. CK_DECLARE_FUNCTION_POINTER(returnType, name): A macro
+ * which makes a Cryptoki API function pointer declaration or
+ * function pointer type declaration out of a return type and a
+ * function name.  It should be used in the following fashion:
+ *
+ * // Define funcPtr to be a pointer to a Cryptoki API function
+ * // taking arguments args and returning CK_RV.
+ * CK_DECLARE_FUNCTION_POINTER(CK_RV, funcPtr)(args);
+ *
+ * or
+ *
+ * // Define funcPtrType to be the type of a pointer to a
+ * // Cryptoki API function taking arguments args and returning
+ * // CK_RV, and then define funcPtr to be a variable of type
+ * // funcPtrType.
+ * typedef CK_DECLARE_FUNCTION_POINTER(CK_RV, funcPtrType)(args);
+ * funcPtrType funcPtr;
+ *
+ * If you're using Microsoft Developer Studio 5.0 to access
+ * functions in a Win32 Cryptoki .dll, in might be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
+ *   returnType __declspec(dllimport) (* name)
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to access functions in a Win16 Cryptoki .dll, it might
+ * be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
+ *   returnType __export _far _pascal (* name)
+ *
+ * In a UNIX environment, it might be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
+ *   returnType (* name)
+ *
+ *
+ * 5. CK_CALLBACK_FUNCTION(returnType, name): A macro which makes
+ * a function pointer type for an application callback out of
+ * a return type for the callback and a name for the callback.
+ * It should be used in the following fashion:
+ *
+ * CK_CALLBACK_FUNCTION(CK_RV, myCallback)(args);
+ *
+ * to declare a function pointer, myCallback, to a callback
+ * which takes arguments args and returns a CK_RV.  It can also
+ * be used like this:
+ *
+ * typedef CK_CALLBACK_FUNCTION(CK_RV, myCallbackType)(args);
+ * myCallbackType myCallback;
+ *
+ * If you're using Microsoft Developer Studio 5.0 to do Win32
+ * Cryptoki development, it might be defined by:
+ *
+ * #define CK_CALLBACK_FUNCTION(returnType, name) \
+ *   returnType (* name)
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to do Win16 development, it might be defined by:
+ *
+ * #define CK_CALLBACK_FUNCTION(returnType, name) \
+ *   returnType _far _pascal (* name)
+ *
+ * In a UNIX environment, it might be defined by:
+ *
+ * #define CK_CALLBACK_FUNCTION(returnType, name) \
+ *   returnType (* name)
+ *
+ *
+ * 6. NULL_PTR: This macro is the value of a NULL pointer.
+ *
+ * In any ANSI/ISO C environment (and in many others as well),
+ * this should best be defined by
+ *
+ * #ifndef NULL_PTR
+ * #define NULL_PTR 0
+ * #endif
+ */
+
+
+/* All the various Cryptoki types and #define'd values are in the
+ * file pkcs11t.h. */
+#include "pkcs11t.h"
+
+#define __PASTE(x,y)      x##y
+
+
+/* ==============================================================
+ * Define the "extern" form of all the entry points.
+ * ==============================================================
+ */
+
+#define CK_NEED_ARG_LIST  1
+#define CK_PKCS11_FUNCTION_INFO(name) \
+  extern CK_DECLARE_FUNCTION(CK_RV, name)
+
+/* pkcs11f.h has all the information about the Cryptoki
+ * function prototypes. */
+#include "pkcs11f.h"
+
+#undef CK_NEED_ARG_LIST
+#undef CK_PKCS11_FUNCTION_INFO
+
+
+/* ==============================================================
+ * Define the typedef form of all the entry points.  That is, for
+ * each Cryptoki function C_XXX, define a type CK_C_XXX which is
+ * a pointer to that kind of function.
+ * ==============================================================
+ */
+
+#define CK_NEED_ARG_LIST  1
+#define CK_PKCS11_FUNCTION_INFO(name) \
+  typedef CK_DECLARE_FUNCTION_POINTER(CK_RV, __PASTE(CK_,name))
+
+/* pkcs11f.h has all the information about the Cryptoki
+ * function prototypes. */
+#include "pkcs11f.h"
+
+#undef CK_NEED_ARG_LIST
+#undef CK_PKCS11_FUNCTION_INFO
+
+
+/* ==============================================================
+ * Define structed vector of entry points.  A CK_FUNCTION_LIST
+ * contains a CK_VERSION indicating a library's Cryptoki version
+ * and then a whole slew of function pointers to the routines in
+ * the library.  This type was declared, but not defined, in
+ * pkcs11t.h.
+ * ==============================================================
+ */
+
+#define CK_PKCS11_FUNCTION_INFO(name) \
+  __PASTE(CK_,name) name;
+  
+struct CK_FUNCTION_LIST {
+
+  CK_VERSION    version;  /* Cryptoki version */
+
+/* Pile all the function pointers into the CK_FUNCTION_LIST. */
+/* pkcs11f.h has all the information about the Cryptoki
+ * function prototypes. */
+#include "pkcs11f.h"
+
+};
+
+#undef CK_PKCS11_FUNCTION_INFO
+
+
+#undef __PASTE
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/isc/include/pkcs11/pkcs11f.h b/lib/isc/include/pkcs11/pkcs11f.h
new file mode 100644
index 0000000..dec6315
--- /dev/null
+++ b/lib/isc/include/pkcs11/pkcs11f.h
@@ -0,0 +1,912 @@
+/* pkcs11f.h include file for PKCS #11. */
+/* $Revision: 1.2 $ */
+
+/* License to copy and use this software is granted provided that it is
+ * identified as "RSA Security Inc. PKCS #11 Cryptographic Token Interface
+ * (Cryptoki)" in all material mentioning or referencing this software.
+
+ * License is also granted to make and use derivative works provided that
+ * such works are identified as "derived from the RSA Security Inc. PKCS #11
+ * Cryptographic Token Interface (Cryptoki)" in all material mentioning or 
+ * referencing the derived work.
+
+ * RSA Security Inc. makes no representations concerning either the 
+ * merchantability of this software or the suitability of this software for
+ * any particular purpose. It is provided "as is" without express or implied
+ * warranty of any kind.
+ */
+
+/* This header file contains pretty much everything about all the */
+/* Cryptoki function prototypes.  Because this information is */
+/* used for more than just declaring function prototypes, the */
+/* order of the functions appearing herein is important, and */
+/* should not be altered. */
+
+/* General-purpose */
+
+/* C_Initialize initializes the Cryptoki library. */
+CK_PKCS11_FUNCTION_INFO(C_Initialize)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_VOID_PTR   pInitArgs  /* if this is not NULL_PTR, it gets
+                            * cast to CK_C_INITIALIZE_ARGS_PTR
+                            * and dereferenced */
+);
+#endif
+
+
+/* C_Finalize indicates that an application is done with the
+ * Cryptoki library. */
+CK_PKCS11_FUNCTION_INFO(C_Finalize)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_VOID_PTR   pReserved  /* reserved.  Should be NULL_PTR */
+);
+#endif
+
+
+/* C_GetInfo returns general information about Cryptoki. */
+CK_PKCS11_FUNCTION_INFO(C_GetInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_INFO_PTR   pInfo  /* location that receives information */
+);
+#endif
+
+
+/* C_GetFunctionList returns the function list. */
+CK_PKCS11_FUNCTION_INFO(C_GetFunctionList)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_FUNCTION_LIST_PTR_PTR ppFunctionList  /* receives pointer to
+                                            * function list */
+);
+#endif
+
+
+
+/* Slot and token management */
+
+/* C_GetSlotList obtains a list of slots in the system. */
+CK_PKCS11_FUNCTION_INFO(C_GetSlotList)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_BBOOL       tokenPresent,  /* only slots with tokens? */
+  CK_SLOT_ID_PTR pSlotList,     /* receives array of slot IDs */
+  CK_ULONG_PTR   pulCount       /* receives number of slots */
+);
+#endif
+
+
+/* C_GetSlotInfo obtains information about a particular slot in
+ * the system. */
+CK_PKCS11_FUNCTION_INFO(C_GetSlotInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SLOT_ID       slotID,  /* the ID of the slot */
+  CK_SLOT_INFO_PTR pInfo    /* receives the slot information */
+);
+#endif
+
+
+/* C_GetTokenInfo obtains information about a particular token
+ * in the system. */
+CK_PKCS11_FUNCTION_INFO(C_GetTokenInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SLOT_ID        slotID,  /* ID of the token's slot */
+  CK_TOKEN_INFO_PTR pInfo    /* receives the token information */
+);
+#endif
+
+
+/* C_GetMechanismList obtains a list of mechanism types
+ * supported by a token. */
+CK_PKCS11_FUNCTION_INFO(C_GetMechanismList)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SLOT_ID            slotID,          /* ID of token's slot */
+  CK_MECHANISM_TYPE_PTR pMechanismList,  /* gets mech. array */
+  CK_ULONG_PTR          pulCount         /* gets # of mechs. */
+);
+#endif
+
+
+/* C_GetMechanismInfo obtains information about a particular
+ * mechanism possibly supported by a token. */
+CK_PKCS11_FUNCTION_INFO(C_GetMechanismInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SLOT_ID            slotID,  /* ID of the token's slot */
+  CK_MECHANISM_TYPE     type,    /* type of mechanism */
+  CK_MECHANISM_INFO_PTR pInfo    /* receives mechanism info */
+);
+#endif
+
+
+/* C_InitToken initializes a token. */
+CK_PKCS11_FUNCTION_INFO(C_InitToken)
+#ifdef CK_NEED_ARG_LIST
+/* pLabel changed from CK_CHAR_PTR to CK_UTF8CHAR_PTR for v2.10 */
+(
+  CK_SLOT_ID      slotID,    /* ID of the token's slot */
+  CK_UTF8CHAR_PTR pPin,      /* the SO's initial PIN */
+  CK_ULONG        ulPinLen,  /* length in bytes of the PIN */
+  CK_UTF8CHAR_PTR pLabel     /* 32-byte token label (blank padded) */
+);
+#endif
+
+
+/* C_InitPIN initializes the normal user's PIN. */
+CK_PKCS11_FUNCTION_INFO(C_InitPIN)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,  /* the session's handle */
+  CK_UTF8CHAR_PTR   pPin,      /* the normal user's PIN */
+  CK_ULONG          ulPinLen   /* length in bytes of the PIN */
+);
+#endif
+
+
+/* C_SetPIN modifies the PIN of the user who is logged in. */
+CK_PKCS11_FUNCTION_INFO(C_SetPIN)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,  /* the session's handle */
+  CK_UTF8CHAR_PTR   pOldPin,   /* the old PIN */
+  CK_ULONG          ulOldLen,  /* length of the old PIN */
+  CK_UTF8CHAR_PTR   pNewPin,   /* the new PIN */
+  CK_ULONG          ulNewLen   /* length of the new PIN */
+);
+#endif
+
+
+
+/* Session management */
+
+/* C_OpenSession opens a session between an application and a
+ * token. */
+CK_PKCS11_FUNCTION_INFO(C_OpenSession)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SLOT_ID            slotID,        /* the slot's ID */
+  CK_FLAGS              flags,         /* from CK_SESSION_INFO */
+  CK_VOID_PTR           pApplication,  /* passed to callback */
+  CK_NOTIFY             Notify,        /* callback function */
+  CK_SESSION_HANDLE_PTR phSession      /* gets session handle */
+);
+#endif
+
+
+/* C_CloseSession closes a session between an application and a
+ * token. */
+CK_PKCS11_FUNCTION_INFO(C_CloseSession)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession  /* the session's handle */
+);
+#endif
+
+
+/* C_CloseAllSessions closes all sessions with a token. */
+CK_PKCS11_FUNCTION_INFO(C_CloseAllSessions)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SLOT_ID     slotID  /* the token's slot */
+);
+#endif
+
+
+/* C_GetSessionInfo obtains information about the session. */
+CK_PKCS11_FUNCTION_INFO(C_GetSessionInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE   hSession,  /* the session's handle */
+  CK_SESSION_INFO_PTR pInfo      /* receives session info */
+);
+#endif
+
+
+/* C_GetOperationState obtains the state of the cryptographic operation
+ * in a session. */
+CK_PKCS11_FUNCTION_INFO(C_GetOperationState)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,             /* session's handle */
+  CK_BYTE_PTR       pOperationState,      /* gets state */
+  CK_ULONG_PTR      pulOperationStateLen  /* gets state length */
+);
+#endif
+
+
+/* C_SetOperationState restores the state of the cryptographic
+ * operation in a session. */
+CK_PKCS11_FUNCTION_INFO(C_SetOperationState)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,            /* session's handle */
+  CK_BYTE_PTR      pOperationState,      /* holds state */
+  CK_ULONG         ulOperationStateLen,  /* holds state length */
+  CK_OBJECT_HANDLE hEncryptionKey,       /* en/decryption key */
+  CK_OBJECT_HANDLE hAuthenticationKey    /* sign/verify key */
+);
+#endif
+
+
+/* C_Login logs a user into a token. */
+CK_PKCS11_FUNCTION_INFO(C_Login)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,  /* the session's handle */
+  CK_USER_TYPE      userType,  /* the user type */
+  CK_UTF8CHAR_PTR   pPin,      /* the user's PIN */
+  CK_ULONG          ulPinLen   /* the length of the PIN */
+);
+#endif
+
+
+/* C_Logout logs a user out from a token. */
+CK_PKCS11_FUNCTION_INFO(C_Logout)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession  /* the session's handle */
+);
+#endif
+
+
+
+/* Object management */
+
+/* C_CreateObject creates a new object. */
+CK_PKCS11_FUNCTION_INFO(C_CreateObject)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,    /* the session's handle */
+  CK_ATTRIBUTE_PTR  pTemplate,   /* the object's template */
+  CK_ULONG          ulCount,     /* attributes in template */
+  CK_OBJECT_HANDLE_PTR phObject  /* gets new object's handle. */
+);
+#endif
+
+
+/* C_CopyObject copies an object, creating a new object for the
+ * copy. */
+CK_PKCS11_FUNCTION_INFO(C_CopyObject)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE    hSession,    /* the session's handle */
+  CK_OBJECT_HANDLE     hObject,     /* the object's handle */
+  CK_ATTRIBUTE_PTR     pTemplate,   /* template for new object */
+  CK_ULONG             ulCount,     /* attributes in template */
+  CK_OBJECT_HANDLE_PTR phNewObject  /* receives handle of copy */
+);
+#endif
+
+
+/* C_DestroyObject destroys an object. */
+CK_PKCS11_FUNCTION_INFO(C_DestroyObject)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,  /* the session's handle */
+  CK_OBJECT_HANDLE  hObject    /* the object's handle */
+);
+#endif
+
+
+/* C_GetObjectSize gets the size of an object in bytes. */
+CK_PKCS11_FUNCTION_INFO(C_GetObjectSize)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,  /* the session's handle */
+  CK_OBJECT_HANDLE  hObject,   /* the object's handle */
+  CK_ULONG_PTR      pulSize    /* receives size of object */
+);
+#endif
+
+
+/* C_GetAttributeValue obtains the value of one or more object
+ * attributes. */
+CK_PKCS11_FUNCTION_INFO(C_GetAttributeValue)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,   /* the session's handle */
+  CK_OBJECT_HANDLE  hObject,    /* the object's handle */
+  CK_ATTRIBUTE_PTR  pTemplate,  /* specifies attrs; gets vals */
+  CK_ULONG          ulCount     /* attributes in template */
+);
+#endif
+
+
+/* C_SetAttributeValue modifies the value of one or more object
+ * attributes */
+CK_PKCS11_FUNCTION_INFO(C_SetAttributeValue)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,   /* the session's handle */
+  CK_OBJECT_HANDLE  hObject,    /* the object's handle */
+  CK_ATTRIBUTE_PTR  pTemplate,  /* specifies attrs and values */
+  CK_ULONG          ulCount     /* attributes in template */
+);
+#endif
+
+
+/* C_FindObjectsInit initializes a search for token and session
+ * objects that match a template. */
+CK_PKCS11_FUNCTION_INFO(C_FindObjectsInit)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,   /* the session's handle */
+  CK_ATTRIBUTE_PTR  pTemplate,  /* attribute values to match */
+  CK_ULONG          ulCount     /* attrs in search template */
+);
+#endif
+
+
+/* C_FindObjects continues a search for token and session
+ * objects that match a template, obtaining additional object
+ * handles. */
+CK_PKCS11_FUNCTION_INFO(C_FindObjects)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE    hSession,          /* session's handle */
+ CK_OBJECT_HANDLE_PTR phObject,          /* gets obj. handles */
+ CK_ULONG             ulMaxObjectCount,  /* max handles to get */
+ CK_ULONG_PTR         pulObjectCount     /* actual # returned */
+);
+#endif
+
+
+/* C_FindObjectsFinal finishes a search for token and session
+ * objects. */
+CK_PKCS11_FUNCTION_INFO(C_FindObjectsFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession  /* the session's handle */
+);
+#endif
+
+
+
+/* Encryption and decryption */
+
+/* C_EncryptInit initializes an encryption operation. */
+CK_PKCS11_FUNCTION_INFO(C_EncryptInit)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,    /* the session's handle */
+  CK_MECHANISM_PTR  pMechanism,  /* the encryption mechanism */
+  CK_OBJECT_HANDLE  hKey         /* handle of encryption key */
+);
+#endif
+
+
+/* C_Encrypt encrypts single-part data. */
+CK_PKCS11_FUNCTION_INFO(C_Encrypt)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,            /* session's handle */
+  CK_BYTE_PTR       pData,               /* the plaintext data */
+  CK_ULONG          ulDataLen,           /* bytes of plaintext */
+  CK_BYTE_PTR       pEncryptedData,      /* gets ciphertext */
+  CK_ULONG_PTR      pulEncryptedDataLen  /* gets c-text size */
+);
+#endif
+
+
+/* C_EncryptUpdate continues a multiple-part encryption
+ * operation. */
+CK_PKCS11_FUNCTION_INFO(C_EncryptUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,           /* session's handle */
+  CK_BYTE_PTR       pPart,              /* the plaintext data */
+  CK_ULONG          ulPartLen,          /* plaintext data len */
+  CK_BYTE_PTR       pEncryptedPart,     /* gets ciphertext */
+  CK_ULONG_PTR      pulEncryptedPartLen /* gets c-text size */
+);
+#endif
+
+
+/* C_EncryptFinal finishes a multiple-part encryption
+ * operation. */
+CK_PKCS11_FUNCTION_INFO(C_EncryptFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,                /* session handle */
+  CK_BYTE_PTR       pLastEncryptedPart,      /* last c-text */
+  CK_ULONG_PTR      pulLastEncryptedPartLen  /* gets last size */
+);
+#endif
+
+
+/* C_DecryptInit initializes a decryption operation. */
+CK_PKCS11_FUNCTION_INFO(C_DecryptInit)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,    /* the session's handle */
+  CK_MECHANISM_PTR  pMechanism,  /* the decryption mechanism */
+  CK_OBJECT_HANDLE  hKey         /* handle of decryption key */
+);
+#endif
+
+
+/* C_Decrypt decrypts encrypted data in a single part. */
+CK_PKCS11_FUNCTION_INFO(C_Decrypt)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,           /* session's handle */
+  CK_BYTE_PTR       pEncryptedData,     /* ciphertext */
+  CK_ULONG          ulEncryptedDataLen, /* ciphertext length */
+  CK_BYTE_PTR       pData,              /* gets plaintext */
+  CK_ULONG_PTR      pulDataLen          /* gets p-text size */
+);
+#endif
+
+
+/* C_DecryptUpdate continues a multiple-part decryption
+ * operation. */
+CK_PKCS11_FUNCTION_INFO(C_DecryptUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,            /* session's handle */
+  CK_BYTE_PTR       pEncryptedPart,      /* encrypted data */
+  CK_ULONG          ulEncryptedPartLen,  /* input length */
+  CK_BYTE_PTR       pPart,               /* gets plaintext */
+  CK_ULONG_PTR      pulPartLen           /* p-text size */
+);
+#endif
+
+
+/* C_DecryptFinal finishes a multiple-part decryption
+ * operation. */
+CK_PKCS11_FUNCTION_INFO(C_DecryptFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,       /* the session's handle */
+  CK_BYTE_PTR       pLastPart,      /* gets plaintext */
+  CK_ULONG_PTR      pulLastPartLen  /* p-text size */
+);
+#endif
+
+
+
+/* Message digesting */
+
+/* C_DigestInit initializes a message-digesting operation. */
+CK_PKCS11_FUNCTION_INFO(C_DigestInit)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,   /* the session's handle */
+  CK_MECHANISM_PTR  pMechanism  /* the digesting mechanism */
+);
+#endif
+
+
+/* C_Digest digests data in a single part. */
+CK_PKCS11_FUNCTION_INFO(C_Digest)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,     /* the session's handle */
+  CK_BYTE_PTR       pData,        /* data to be digested */
+  CK_ULONG          ulDataLen,    /* bytes of data to digest */
+  CK_BYTE_PTR       pDigest,      /* gets the message digest */
+  CK_ULONG_PTR      pulDigestLen  /* gets digest length */
+);
+#endif
+
+
+/* C_DigestUpdate continues a multiple-part message-digesting
+ * operation. */
+CK_PKCS11_FUNCTION_INFO(C_DigestUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,  /* the session's handle */
+  CK_BYTE_PTR       pPart,     /* data to be digested */
+  CK_ULONG          ulPartLen  /* bytes of data to be digested */
+);
+#endif
+
+
+/* C_DigestKey continues a multi-part message-digesting
+ * operation, by digesting the value of a secret key as part of
+ * the data already digested. */
+CK_PKCS11_FUNCTION_INFO(C_DigestKey)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,  /* the session's handle */
+  CK_OBJECT_HANDLE  hKey       /* secret key to digest */
+);
+#endif
+
+
+/* C_DigestFinal finishes a multiple-part message-digesting
+ * operation. */
+CK_PKCS11_FUNCTION_INFO(C_DigestFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,     /* the session's handle */
+  CK_BYTE_PTR       pDigest,      /* gets the message digest */
+  CK_ULONG_PTR      pulDigestLen  /* gets byte count of digest */
+);
+#endif
+
+
+
+/* Signing and MACing */
+
+/* C_SignInit initializes a signature (private key encryption)
+ * operation, where the signature is (will be) an appendix to
+ * the data, and plaintext cannot be recovered from the
+ *signature. */
+CK_PKCS11_FUNCTION_INFO(C_SignInit)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,    /* the session's handle */
+  CK_MECHANISM_PTR  pMechanism,  /* the signature mechanism */
+  CK_OBJECT_HANDLE  hKey         /* handle of signature key */
+);
+#endif
+
+
+/* C_Sign signs (encrypts with private key) data in a single
+ * part, where the signature is (will be) an appendix to the
+ * data, and plaintext cannot be recovered from the signature. */
+CK_PKCS11_FUNCTION_INFO(C_Sign)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,        /* the session's handle */
+  CK_BYTE_PTR       pData,           /* the data to sign */
+  CK_ULONG          ulDataLen,       /* count of bytes to sign */
+  CK_BYTE_PTR       pSignature,      /* gets the signature */
+  CK_ULONG_PTR      pulSignatureLen  /* gets signature length */
+);
+#endif
+
+
+/* C_SignUpdate continues a multiple-part signature operation,
+ * where the signature is (will be) an appendix to the data, 
+ * and plaintext cannot be recovered from the signature. */
+CK_PKCS11_FUNCTION_INFO(C_SignUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,  /* the session's handle */
+  CK_BYTE_PTR       pPart,     /* the data to sign */
+  CK_ULONG          ulPartLen  /* count of bytes to sign */
+);
+#endif
+
+
+/* C_SignFinal finishes a multiple-part signature operation, 
+ * returning the signature. */
+CK_PKCS11_FUNCTION_INFO(C_SignFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,        /* the session's handle */
+  CK_BYTE_PTR       pSignature,      /* gets the signature */
+  CK_ULONG_PTR      pulSignatureLen  /* gets signature length */
+);
+#endif
+
+
+/* C_SignRecoverInit initializes a signature operation, where
+ * the data can be recovered from the signature. */
+CK_PKCS11_FUNCTION_INFO(C_SignRecoverInit)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,   /* the session's handle */
+  CK_MECHANISM_PTR  pMechanism, /* the signature mechanism */
+  CK_OBJECT_HANDLE  hKey        /* handle of the signature key */
+);
+#endif
+
+
+/* C_SignRecover signs data in a single operation, where the
+ * data can be recovered from the signature. */
+CK_PKCS11_FUNCTION_INFO(C_SignRecover)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,        /* the session's handle */
+  CK_BYTE_PTR       pData,           /* the data to sign */
+  CK_ULONG          ulDataLen,       /* count of bytes to sign */
+  CK_BYTE_PTR       pSignature,      /* gets the signature */
+  CK_ULONG_PTR      pulSignatureLen  /* gets signature length */
+);
+#endif
+
+
+
+/* Verifying signatures and MACs */
+
+/* C_VerifyInit initializes a verification operation, where the
+ * signature is an appendix to the data, and plaintext cannot
+ *  cannot be recovered from the signature (e.g. DSA). */
+CK_PKCS11_FUNCTION_INFO(C_VerifyInit)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,    /* the session's handle */
+  CK_MECHANISM_PTR  pMechanism,  /* the verification mechanism */
+  CK_OBJECT_HANDLE  hKey         /* verification key */ 
+);
+#endif
+
+
+/* C_Verify verifies a signature in a single-part operation, 
+ * where the signature is an appendix to the data, and plaintext
+ * cannot be recovered from the signature. */
+CK_PKCS11_FUNCTION_INFO(C_Verify)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,       /* the session's handle */
+  CK_BYTE_PTR       pData,          /* signed data */
+  CK_ULONG          ulDataLen,      /* length of signed data */
+  CK_BYTE_PTR       pSignature,     /* signature */
+  CK_ULONG          ulSignatureLen  /* signature length*/
+);
+#endif
+
+
+/* C_VerifyUpdate continues a multiple-part verification
+ * operation, where the signature is an appendix to the data, 
+ * and plaintext cannot be recovered from the signature. */
+CK_PKCS11_FUNCTION_INFO(C_VerifyUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,  /* the session's handle */
+  CK_BYTE_PTR       pPart,     /* signed data */
+  CK_ULONG          ulPartLen  /* length of signed data */
+);
+#endif
+
+
+/* C_VerifyFinal finishes a multiple-part verification
+ * operation, checking the signature. */
+CK_PKCS11_FUNCTION_INFO(C_VerifyFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,       /* the session's handle */
+  CK_BYTE_PTR       pSignature,     /* signature to verify */
+  CK_ULONG          ulSignatureLen  /* signature length */
+);
+#endif
+
+
+/* C_VerifyRecoverInit initializes a signature verification
+ * operation, where the data is recovered from the signature. */
+CK_PKCS11_FUNCTION_INFO(C_VerifyRecoverInit)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,    /* the session's handle */
+  CK_MECHANISM_PTR  pMechanism,  /* the verification mechanism */
+  CK_OBJECT_HANDLE  hKey         /* verification key */
+);
+#endif
+
+
+/* C_VerifyRecover verifies a signature in a single-part
+ * operation, where the data is recovered from the signature. */
+CK_PKCS11_FUNCTION_INFO(C_VerifyRecover)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,        /* the session's handle */
+  CK_BYTE_PTR       pSignature,      /* signature to verify */
+  CK_ULONG          ulSignatureLen,  /* signature length */
+  CK_BYTE_PTR       pData,           /* gets signed data */
+  CK_ULONG_PTR      pulDataLen       /* gets signed data len */
+);
+#endif
+
+
+
+/* Dual-function cryptographic operations */
+
+/* C_DigestEncryptUpdate continues a multiple-part digesting
+ * and encryption operation. */
+CK_PKCS11_FUNCTION_INFO(C_DigestEncryptUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,            /* session's handle */
+  CK_BYTE_PTR       pPart,               /* the plaintext data */
+  CK_ULONG          ulPartLen,           /* plaintext length */
+  CK_BYTE_PTR       pEncryptedPart,      /* gets ciphertext */
+  CK_ULONG_PTR      pulEncryptedPartLen  /* gets c-text length */
+);
+#endif
+
+
+/* C_DecryptDigestUpdate continues a multiple-part decryption and
+ * digesting operation. */
+CK_PKCS11_FUNCTION_INFO(C_DecryptDigestUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,            /* session's handle */
+  CK_BYTE_PTR       pEncryptedPart,      /* ciphertext */
+  CK_ULONG          ulEncryptedPartLen,  /* ciphertext length */
+  CK_BYTE_PTR       pPart,               /* gets plaintext */
+  CK_ULONG_PTR      pulPartLen           /* gets plaintext len */
+);
+#endif
+
+
+/* C_SignEncryptUpdate continues a multiple-part signing and
+ * encryption operation. */
+CK_PKCS11_FUNCTION_INFO(C_SignEncryptUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,            /* session's handle */
+  CK_BYTE_PTR       pPart,               /* the plaintext data */
+  CK_ULONG          ulPartLen,           /* plaintext length */
+  CK_BYTE_PTR       pEncryptedPart,      /* gets ciphertext */
+  CK_ULONG_PTR      pulEncryptedPartLen  /* gets c-text length */
+);
+#endif
+
+
+/* C_DecryptVerifyUpdate continues a multiple-part decryption and
+ * verify operation. */
+CK_PKCS11_FUNCTION_INFO(C_DecryptVerifyUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,            /* session's handle */
+  CK_BYTE_PTR       pEncryptedPart,      /* ciphertext */
+  CK_ULONG          ulEncryptedPartLen,  /* ciphertext length */
+  CK_BYTE_PTR       pPart,               /* gets plaintext */
+  CK_ULONG_PTR      pulPartLen           /* gets p-text length */
+);
+#endif
+
+
+
+/* Key management */
+
+/* C_GenerateKey generates a secret key, creating a new key
+ * object. */
+CK_PKCS11_FUNCTION_INFO(C_GenerateKey)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE    hSession,    /* the session's handle */
+  CK_MECHANISM_PTR     pMechanism,  /* key generation mech. */
+  CK_ATTRIBUTE_PTR     pTemplate,   /* template for new key */
+  CK_ULONG             ulCount,     /* # of attrs in template */
+  CK_OBJECT_HANDLE_PTR phKey        /* gets handle of new key */
+);
+#endif
+
+
+/* C_GenerateKeyPair generates a public-key/private-key pair, 
+ * creating new key objects. */
+CK_PKCS11_FUNCTION_INFO(C_GenerateKeyPair)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE    hSession,                    /* session
+                                                     * handle */
+  CK_MECHANISM_PTR     pMechanism,                  /* key-gen
+                                                     * mech. */
+  CK_ATTRIBUTE_PTR     pPublicKeyTemplate,          /* template
+                                                     * for pub.
+                                                     * key */
+  CK_ULONG             ulPublicKeyAttributeCount,   /* # pub.
+                                                     * attrs. */
+  CK_ATTRIBUTE_PTR     pPrivateKeyTemplate,         /* template
+                                                     * for priv.
+                                                     * key */
+  CK_ULONG             ulPrivateKeyAttributeCount,  /* # priv.
+                                                     * attrs. */
+  CK_OBJECT_HANDLE_PTR phPublicKey,                 /* gets pub.
+                                                     * key
+                                                     * handle */
+  CK_OBJECT_HANDLE_PTR phPrivateKey                 /* gets
+                                                     * priv. key
+                                                     * handle */
+);
+#endif
+
+
+/* C_WrapKey wraps (i.e., encrypts) a key. */
+CK_PKCS11_FUNCTION_INFO(C_WrapKey)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,        /* the session's handle */
+  CK_MECHANISM_PTR  pMechanism,      /* the wrapping mechanism */
+  CK_OBJECT_HANDLE  hWrappingKey,    /* wrapping key */
+  CK_OBJECT_HANDLE  hKey,            /* key to be wrapped */
+  CK_BYTE_PTR       pWrappedKey,     /* gets wrapped key */
+  CK_ULONG_PTR      pulWrappedKeyLen /* gets wrapped key size */
+);
+#endif
+
+
+/* C_UnwrapKey unwraps (decrypts) a wrapped key, creating a new
+ * key object. */
+CK_PKCS11_FUNCTION_INFO(C_UnwrapKey)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE    hSession,          /* session's handle */
+  CK_MECHANISM_PTR     pMechanism,        /* unwrapping mech. */
+  CK_OBJECT_HANDLE     hUnwrappingKey,    /* unwrapping key */
+  CK_BYTE_PTR          pWrappedKey,       /* the wrapped key */
+  CK_ULONG             ulWrappedKeyLen,   /* wrapped key len */
+  CK_ATTRIBUTE_PTR     pTemplate,         /* new key template */
+  CK_ULONG             ulAttributeCount,  /* template length */
+  CK_OBJECT_HANDLE_PTR phKey              /* gets new handle */
+);
+#endif
+
+
+/* C_DeriveKey derives a key from a base key, creating a new key
+ * object. */
+CK_PKCS11_FUNCTION_INFO(C_DeriveKey)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE    hSession,          /* session's handle */
+  CK_MECHANISM_PTR     pMechanism,        /* key deriv. mech. */
+  CK_OBJECT_HANDLE     hBaseKey,          /* base key */
+  CK_ATTRIBUTE_PTR     pTemplate,         /* new key template */
+  CK_ULONG             ulAttributeCount,  /* template length */
+  CK_OBJECT_HANDLE_PTR phKey              /* gets new handle */
+);
+#endif
+
+
+
+/* Random number generation */
+
+/* C_SeedRandom mixes additional seed material into the token's
+ * random number generator. */
+CK_PKCS11_FUNCTION_INFO(C_SeedRandom)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,  /* the session's handle */
+  CK_BYTE_PTR       pSeed,     /* the seed material */
+  CK_ULONG          ulSeedLen  /* length of seed material */
+);
+#endif
+
+
+/* C_GenerateRandom generates random data. */
+CK_PKCS11_FUNCTION_INFO(C_GenerateRandom)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession,    /* the session's handle */
+  CK_BYTE_PTR       RandomData,  /* receives the random data */
+  CK_ULONG          ulRandomLen  /* # of bytes to generate */
+);
+#endif
+
+
+
+/* Parallel function management */
+
+/* C_GetFunctionStatus is a legacy function; it obtains an
+ * updated status of a function running in parallel with an
+ * application. */
+CK_PKCS11_FUNCTION_INFO(C_GetFunctionStatus)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession  /* the session's handle */
+);
+#endif
+
+
+/* C_CancelFunction is a legacy function; it cancels a function
+ * running in parallel. */
+CK_PKCS11_FUNCTION_INFO(C_CancelFunction)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_SESSION_HANDLE hSession  /* the session's handle */
+);
+#endif
+
+
+
+/* Functions added in for Cryptoki Version 2.01 or later */
+
+/* C_WaitForSlotEvent waits for a slot event (token insertion,
+ * removal, etc.) to occur. */
+CK_PKCS11_FUNCTION_INFO(C_WaitForSlotEvent)
+#ifdef CK_NEED_ARG_LIST
+(
+  CK_FLAGS flags,        /* blocking/nonblocking flag */
+  CK_SLOT_ID_PTR pSlot,  /* location that receives the slot ID */
+  CK_VOID_PTR pRserved   /* reserved.  Should be NULL_PTR */
+);
+#endif
diff --git a/lib/isc/include/pkcs11/pkcs11t.h b/lib/isc/include/pkcs11/pkcs11t.h
new file mode 100644
index 0000000..92a80bb
--- /dev/null
+++ b/lib/isc/include/pkcs11/pkcs11t.h
@@ -0,0 +1,1977 @@
+/* pkcs11t.h include file for PKCS #11. */
+/* $Revision: 1.2 $ */
+
+/* License to copy and use this software is granted provided that it is
+ * identified as "RSA Security Inc. PKCS #11 Cryptographic Token Interface
+ * (Cryptoki)" in all material mentioning or referencing this software.
+
+ * License is also granted to make and use derivative works provided that
+ * such works are identified as "derived from the RSA Security Inc. PKCS #11
+ * Cryptographic Token Interface (Cryptoki)" in all material mentioning or
+ * referencing the derived work.
+
+ * RSA Security Inc. makes no representations concerning either the
+ * merchantability of this software or the suitability of this software for
+ * any particular purpose. It is provided "as is" without express or implied
+ * warranty of any kind.
+ */
+
+/* See top of pkcs11.h for information about the macros that
+ * must be defined and the structure-packing conventions that
+ * must be set before including this file. */
+
+#ifndef _PKCS11T_H_
+#define _PKCS11T_H_ 1
+
+#define CRYPTOKI_VERSION_MAJOR 2
+#define CRYPTOKI_VERSION_MINOR 30
+#define CRYPTOKI_VERSION_REVISION 0
+#define CRYPTOKI_VERSION_AMENDMENT 0
+
+#define CK_TRUE 1
+#define CK_FALSE 0
+
+#ifndef CK_DISABLE_TRUE_FALSE
+#ifndef FALSE
+#define FALSE CK_FALSE
+#endif
+
+#ifndef TRUE
+#define TRUE CK_TRUE
+#endif
+#endif
+
+/* an unsigned 8-bit value */
+typedef unsigned char     CK_BYTE;
+
+/* an unsigned 8-bit character */
+typedef CK_BYTE           CK_CHAR;
+
+/* an 8-bit UTF-8 character */
+typedef CK_BYTE           CK_UTF8CHAR;
+
+/* a BYTE-sized Boolean flag */
+typedef CK_BYTE           CK_BBOOL;
+
+/* an unsigned value, at least 32 bits long */
+typedef unsigned long int CK_ULONG;
+
+/* a signed value, the same size as a CK_ULONG */
+/* CK_LONG is new for v2.0 */
+typedef long int          CK_LONG;
+
+/* at least 32 bits; each bit is a Boolean flag */
+typedef CK_ULONG          CK_FLAGS;
+
+
+/* some special values for certain CK_ULONG variables */
+#define CK_UNAVAILABLE_INFORMATION (~0UL)
+#define CK_EFFECTIVELY_INFINITE    0
+
+
+typedef CK_BYTE     CK_PTR   CK_BYTE_PTR;
+typedef CK_CHAR     CK_PTR   CK_CHAR_PTR;
+typedef CK_UTF8CHAR CK_PTR   CK_UTF8CHAR_PTR;
+typedef CK_ULONG    CK_PTR   CK_ULONG_PTR;
+typedef void        CK_PTR   CK_VOID_PTR;
+
+/* Pointer to a CK_VOID_PTR-- i.e., pointer to pointer to void */
+typedef CK_VOID_PTR CK_PTR CK_VOID_PTR_PTR;
+
+
+/* The following value is always invalid if used as a session */
+/* handle or object handle */
+#define CK_INVALID_HANDLE 0
+
+
+typedef struct CK_VERSION {
+  CK_BYTE       major;  /* integer portion of version number */
+  CK_BYTE       minor;  /* 1/100ths portion of version number */
+} CK_VERSION;
+
+typedef CK_VERSION CK_PTR CK_VERSION_PTR;
+
+
+typedef struct CK_INFO {
+  /* manufacturerID and libraryDecription have been changed from
+   * CK_CHAR to CK_UTF8CHAR for v2.10 */
+  CK_VERSION    cryptokiVersion;     /* Cryptoki interface ver */
+  CK_UTF8CHAR   manufacturerID[32];  /* blank padded */
+  CK_FLAGS      flags;               /* must be zero */
+
+  /* libraryDescription and libraryVersion are new for v2.0 */
+  CK_UTF8CHAR   libraryDescription[32];  /* blank padded */
+  CK_VERSION    libraryVersion;          /* version of library */
+} CK_INFO;
+
+typedef CK_INFO CK_PTR    CK_INFO_PTR;
+
+
+/* CK_NOTIFICATION enumerates the types of notifications that
+ * Cryptoki provides to an application */
+/* CK_NOTIFICATION has been changed from an enum to a CK_ULONG
+ * for v2.0 */
+typedef CK_ULONG CK_NOTIFICATION;
+#define CKN_SURRENDER       0
+
+/* The following notification is new for PKCS #11 v2.20 amendment 3 */
+#define CKN_OTP_CHANGED     1
+
+
+typedef CK_ULONG          CK_SLOT_ID;
+
+typedef CK_SLOT_ID CK_PTR CK_SLOT_ID_PTR;
+
+
+/* CK_SLOT_INFO provides information about a slot */
+typedef struct CK_SLOT_INFO {
+  /* slotDescription and manufacturerID have been changed from
+   * CK_CHAR to CK_UTF8CHAR for v2.10 */
+  CK_UTF8CHAR   slotDescription[64];  /* blank padded */
+  CK_UTF8CHAR   manufacturerID[32];   /* blank padded */
+  CK_FLAGS      flags;
+
+  /* hardwareVersion and firmwareVersion are new for v2.0 */
+  CK_VERSION    hardwareVersion;  /* version of hardware */
+  CK_VERSION    firmwareVersion;  /* version of firmware */
+} CK_SLOT_INFO;
+
+/* flags: bit flags that provide capabilities of the slot
+ *      Bit Flag              Mask        Meaning
+ */
+#define CKF_TOKEN_PRESENT     0x00000001  /* a token is there */
+#define CKF_REMOVABLE_DEVICE  0x00000002  /* removable devices*/
+#define CKF_HW_SLOT           0x00000004  /* hardware slot */
+
+typedef CK_SLOT_INFO CK_PTR CK_SLOT_INFO_PTR;
+
+
+/* CK_TOKEN_INFO provides information about a token */
+typedef struct CK_TOKEN_INFO {
+  /* label, manufacturerID, and model have been changed from
+   * CK_CHAR to CK_UTF8CHAR for v2.10 */
+  CK_UTF8CHAR   label[32];           /* blank padded */
+  CK_UTF8CHAR   manufacturerID[32];  /* blank padded */
+  CK_UTF8CHAR   model[16];           /* blank padded */
+  CK_CHAR       serialNumber[16];    /* blank padded */
+  CK_FLAGS      flags;               /* see below */
+
+  /* ulMaxSessionCount, ulSessionCount, ulMaxRwSessionCount,
+   * ulRwSessionCount, ulMaxPinLen, and ulMinPinLen have all been
+   * changed from CK_USHORT to CK_ULONG for v2.0 */
+  CK_ULONG      ulMaxSessionCount;     /* max open sessions */
+  CK_ULONG      ulSessionCount;        /* sess. now open */
+  CK_ULONG      ulMaxRwSessionCount;   /* max R/W sessions */
+  CK_ULONG      ulRwSessionCount;      /* R/W sess. now open */
+  CK_ULONG      ulMaxPinLen;           /* in bytes */
+  CK_ULONG      ulMinPinLen;           /* in bytes */
+  CK_ULONG      ulTotalPublicMemory;   /* in bytes */
+  CK_ULONG      ulFreePublicMemory;    /* in bytes */
+  CK_ULONG      ulTotalPrivateMemory;  /* in bytes */
+  CK_ULONG      ulFreePrivateMemory;   /* in bytes */
+
+  /* hardwareVersion, firmwareVersion, and time are new for
+   * v2.0 */
+  CK_VERSION    hardwareVersion;       /* version of hardware */
+  CK_VERSION    firmwareVersion;       /* version of firmware */
+  CK_CHAR       utcTime[16];           /* time */
+} CK_TOKEN_INFO;
+
+/* The flags parameter is defined as follows:
+ *      Bit Flag                    Mask        Meaning
+ */
+#define CKF_RNG                     0x00000001  /* has random #
+                                                 * generator */
+#define CKF_WRITE_PROTECTED         0x00000002  /* token is
+                                                 * write-
+                                                 * protected */
+#define CKF_LOGIN_REQUIRED          0x00000004  /* user must
+                                                 * login */
+#define CKF_USER_PIN_INITIALIZED    0x00000008  /* normal user's
+                                                 * PIN is set */
+
+/* CKF_RESTORE_KEY_NOT_NEEDED is new for v2.0.  If it is set,
+ * that means that *every* time the state of cryptographic
+ * operations of a session is successfully saved, all keys
+ * needed to continue those operations are stored in the state */
+#define CKF_RESTORE_KEY_NOT_NEEDED  0x00000020
+
+/* CKF_CLOCK_ON_TOKEN is new for v2.0.  If it is set, that means
+ * that the token has some sort of clock.  The time on that
+ * clock is returned in the token info structure */
+#define CKF_CLOCK_ON_TOKEN          0x00000040
+
+/* CKF_PROTECTED_AUTHENTICATION_PATH is new for v2.0.  If it is
+ * set, that means that there is some way for the user to login
+ * without sending a PIN through the Cryptoki library itself */
+#define CKF_PROTECTED_AUTHENTICATION_PATH 0x00000100
+
+/* CKF_DUAL_CRYPTO_OPERATIONS is new for v2.0.  If it is true,
+ * that means that a single session with the token can perform
+ * dual simultaneous cryptographic operations (digest and
+ * encrypt; decrypt and digest; sign and encrypt; and decrypt
+ * and sign) */
+#define CKF_DUAL_CRYPTO_OPERATIONS  0x00000200
+
+/* CKF_TOKEN_INITIALIZED if new for v2.10. If it is true, the
+ * token has been initialized using C_InitializeToken or an
+ * equivalent mechanism outside the scope of PKCS #11.
+ * Calling C_InitializeToken when this flag is set will cause
+ * the token to be reinitialized. */
+#define CKF_TOKEN_INITIALIZED       0x00000400
+
+/* CKF_SECONDARY_AUTHENTICATION if new for v2.10. If it is
+ * true, the token supports secondary authentication for
+ * private key objects. This flag is deprecated in v2.11 and
+   onwards. */
+#define CKF_SECONDARY_AUTHENTICATION  0x00000800
+
+/* CKF_USER_PIN_COUNT_LOW if new for v2.10. If it is true, an
+ * incorrect user login PIN has been entered at least once
+ * since the last successful authentication. */
+#define CKF_USER_PIN_COUNT_LOW       0x00010000
+
+/* CKF_USER_PIN_FINAL_TRY if new for v2.10. If it is true,
+ * supplying an incorrect user PIN will it to become locked. */
+#define CKF_USER_PIN_FINAL_TRY       0x00020000
+
+/* CKF_USER_PIN_LOCKED if new for v2.10. If it is true, the
+ * user PIN has been locked. User login to the token is not
+ * possible. */
+#define CKF_USER_PIN_LOCKED          0x00040000
+
+/* CKF_USER_PIN_TO_BE_CHANGED if new for v2.10. If it is true,
+ * the user PIN value is the default value set by token
+ * initialization or manufacturing, or the PIN has been
+ * expired by the card. */
+#define CKF_USER_PIN_TO_BE_CHANGED   0x00080000
+
+/* CKF_SO_PIN_COUNT_LOW if new for v2.10. If it is true, an
+ * incorrect SO login PIN has been entered at least once since
+ * the last successful authentication. */
+#define CKF_SO_PIN_COUNT_LOW         0x00100000
+
+/* CKF_SO_PIN_FINAL_TRY if new for v2.10. If it is true,
+ * supplying an incorrect SO PIN will it to become locked. */
+#define CKF_SO_PIN_FINAL_TRY         0x00200000
+
+/* CKF_SO_PIN_LOCKED if new for v2.10. If it is true, the SO
+ * PIN has been locked. SO login to the token is not possible.
+ */
+#define CKF_SO_PIN_LOCKED            0x00400000
+
+/* CKF_SO_PIN_TO_BE_CHANGED if new for v2.10. If it is true,
+ * the SO PIN value is the default value set by token
+ * initialization or manufacturing, or the PIN has been
+ * expired by the card. */
+#define CKF_SO_PIN_TO_BE_CHANGED     0x00800000
+
+/* CKF_ERROR_STATE if new for v2.30. If it is true,
+ * the token failed a FIPS 140-2 self-test and
+ * entered an error state. */
+#define CKF_ERROR_STATE              0x01000000
+
+typedef CK_TOKEN_INFO CK_PTR CK_TOKEN_INFO_PTR;
+
+
+/* CK_SESSION_HANDLE is a Cryptoki-assigned value that
+ * identifies a session */
+typedef CK_ULONG          CK_SESSION_HANDLE;
+
+typedef CK_SESSION_HANDLE CK_PTR CK_SESSION_HANDLE_PTR;
+
+
+/* CK_USER_TYPE enumerates the types of Cryptoki users */
+/* CK_USER_TYPE has been changed from an enum to a CK_ULONG for
+ * v2.0 */
+typedef CK_ULONG          CK_USER_TYPE;
+/* Security Officer */
+#define CKU_SO    0
+/* Normal user */
+#define CKU_USER  1
+/* Context specific (added in v2.20) */
+#define CKU_CONTEXT_SPECIFIC   2
+
+/* CK_STATE enumerates the session states */
+/* CK_STATE has been changed from an enum to a CK_ULONG for
+ * v2.0 */
+typedef CK_ULONG          CK_STATE;
+#define CKS_RO_PUBLIC_SESSION  0
+#define CKS_RO_USER_FUNCTIONS  1
+#define CKS_RW_PUBLIC_SESSION  2
+#define CKS_RW_USER_FUNCTIONS  3
+#define CKS_RW_SO_FUNCTIONS    4
+
+
+/* CK_SESSION_INFO provides information about a session */
+typedef struct CK_SESSION_INFO {
+  CK_SLOT_ID    slotID;
+  CK_STATE      state;
+  CK_FLAGS      flags;          /* see below */
+
+  /* ulDeviceError was changed from CK_USHORT to CK_ULONG for
+   * v2.0 */
+  CK_ULONG      ulDeviceError;  /* device-dependent error code */
+} CK_SESSION_INFO;
+
+/* The flags are defined in the following table:
+ *      Bit Flag                Mask        Meaning
+ */
+#define CKF_RW_SESSION          0x00000002  /* session is r/w */
+#define CKF_SERIAL_SESSION      0x00000004  /* no parallel */
+
+typedef CK_SESSION_INFO CK_PTR CK_SESSION_INFO_PTR;
+
+
+/* CK_OBJECT_HANDLE is a token-specific identifier for an
+ * object  */
+typedef CK_ULONG          CK_OBJECT_HANDLE;
+
+typedef CK_OBJECT_HANDLE CK_PTR CK_OBJECT_HANDLE_PTR;
+
+
+/* CK_OBJECT_CLASS is a value that identifies the classes (or
+ * types) of objects that Cryptoki recognizes.  It is defined
+ * as follows: */
+/* CK_OBJECT_CLASS was changed from CK_USHORT to CK_ULONG for
+ * v2.0 */
+typedef CK_ULONG          CK_OBJECT_CLASS;
+
+/* The following classes of objects are defined: */
+/* CKO_HW_FEATURE is new for v2.10 */
+/* CKO_DOMAIN_PARAMETERS is new for v2.11 */
+/* CKO_MECHANISM is new for v2.20 */
+#define CKO_DATA              0x00000000
+#define CKO_CERTIFICATE       0x00000001
+#define CKO_PUBLIC_KEY        0x00000002
+#define CKO_PRIVATE_KEY       0x00000003
+#define CKO_SECRET_KEY        0x00000004
+#define CKO_HW_FEATURE        0x00000005
+#define CKO_DOMAIN_PARAMETERS 0x00000006
+#define CKO_MECHANISM         0x00000007
+
+/* CKO_OTP_KEY is new for PKCS #11 v2.20 amendment 1 */
+#define CKO_OTP_KEY           0x00000008
+
+#define CKO_VENDOR_DEFINED    0x80000000
+
+typedef CK_OBJECT_CLASS CK_PTR CK_OBJECT_CLASS_PTR;
+
+/* CK_HW_FEATURE_TYPE is new for v2.10. CK_HW_FEATURE_TYPE is a
+ * value that identifies the hardware feature type of an object
+ * with CK_OBJECT_CLASS equal to CKO_HW_FEATURE. */
+typedef CK_ULONG          CK_HW_FEATURE_TYPE;
+
+/* The following hardware feature types are defined */
+/* CKH_USER_INTERFACE is new for v2.20 */
+#define CKH_MONOTONIC_COUNTER  0x00000001
+#define CKH_CLOCK           0x00000002
+#define CKH_USER_INTERFACE  0x00000003
+#define CKH_VENDOR_DEFINED  0x80000000
+
+/* CK_KEY_TYPE is a value that identifies a key type */
+/* CK_KEY_TYPE was changed from CK_USHORT to CK_ULONG for v2.0 */
+typedef CK_ULONG          CK_KEY_TYPE;
+
+/* the following key types are defined: */
+#define CKK_RSA             0x00000000
+#define CKK_DSA             0x00000001
+#define CKK_DH              0x00000002
+
+/* CKK_ECDSA and CKK_KEA are new for v2.0 */
+/* CKK_ECDSA is deprecated in v2.11, CKK_EC is preferred. */
+#define CKK_ECDSA           0x00000003
+#define CKK_EC              0x00000003
+#define CKK_X9_42_DH        0x00000004
+#define CKK_KEA             0x00000005
+
+#define CKK_GENERIC_SECRET  0x00000010
+#define CKK_RC2             0x00000011
+#define CKK_RC4             0x00000012
+#define CKK_DES             0x00000013
+#define CKK_DES2            0x00000014
+#define CKK_DES3            0x00000015
+
+/* all these key types are new for v2.0 */
+#define CKK_CAST            0x00000016
+#define CKK_CAST3           0x00000017
+/* CKK_CAST5 is deprecated in v2.11, CKK_CAST128 is preferred. */
+#define CKK_CAST5           0x00000018
+#define CKK_CAST128         0x00000018
+#define CKK_RC5             0x00000019
+#define CKK_IDEA            0x0000001A
+#define CKK_SKIPJACK        0x0000001B
+#define CKK_BATON           0x0000001C
+#define CKK_JUNIPER         0x0000001D
+#define CKK_CDMF            0x0000001E
+#define CKK_AES             0x0000001F
+
+/* BlowFish and TwoFish are new for v2.20 */
+#define CKK_BLOWFISH        0x00000020
+#define CKK_TWOFISH         0x00000021
+
+/* SecurID, HOTP, and ACTI are new for PKCS #11 v2.20 amendment 1 */
+#define CKK_SECURID         0x00000022
+#define CKK_HOTP            0x00000023
+#define CKK_ACTI            0x00000024
+
+/* Camellia is new for PKCS #11 v2.20 amendment 3 */
+#define CKK_CAMELLIA                   0x00000025
+/* ARIA is new for PKCS #11 v2.20 amendment 3 */
+#define CKK_ARIA                       0x00000026
+
+/* From PKCS #11 v2.20 amendment 4 draft 2 */
+#define CKK_MD5_HMAC        0x00000027
+#define CKK_SHA_1_HMAC      0x00000028
+#define CKK_RIPEMD128_HMAC  0x00000029
+#define CKK_RIPEMD160_HMAC  0x0000002A
+#define CKK_SHA256_HMAC     0x0000002B
+#define CKK_SHA384_HMAC     0x0000002C
+#define CKK_SHA512_HMAC     0x0000002D
+#define CKK_SHA224_HMAC     0x0000002E
+
+/* From PKCS #11 v2.30 */
+#define CKK_SEED            0x0000002F
+#define CKK_GOSTR3410       0x00000030
+#define CKK_GOSTR3411       0x00000031
+#define CKK_GOST28147       0x00000032
+
+#define CKK_VENDOR_DEFINED  0x80000000
+
+
+/* CK_CERTIFICATE_TYPE is a value that identifies a certificate
+ * type */
+/* CK_CERTIFICATE_TYPE was changed from CK_USHORT to CK_ULONG
+ * for v2.0 */
+typedef CK_ULONG          CK_CERTIFICATE_TYPE;
+
+/* The following certificate types are defined: */
+/* CKC_X_509_ATTR_CERT is new for v2.10 */
+/* CKC_WTLS is new for v2.20 */
+#define CKC_X_509           0x00000000
+#define CKC_X_509_ATTR_CERT 0x00000001
+#define CKC_WTLS            0x00000002
+#define CKC_VENDOR_DEFINED  0x80000000
+
+
+/* CK_ATTRIBUTE_TYPE is a value that identifies an attribute
+ * type */
+/* CK_ATTRIBUTE_TYPE was changed from CK_USHORT to CK_ULONG for
+ * v2.0 */
+typedef CK_ULONG          CK_ATTRIBUTE_TYPE;
+
+/* The CKF_ARRAY_ATTRIBUTE flag identifies an attribute which
+   consists of an array of values. */
+#define CKF_ARRAY_ATTRIBUTE    0x40000000
+
+/* The following OTP-related defines are new for PKCS #11 v2.20 amendment 1
+   and relates to the CKA_OTP_FORMAT attribute */
+#define CK_OTP_FORMAT_DECIMAL      0
+#define CK_OTP_FORMAT_HEXADECIMAL  1
+#define CK_OTP_FORMAT_ALPHANUMERIC 2
+#define CK_OTP_FORMAT_BINARY       3
+
+/* The following OTP-related defines are new for PKCS #11 v2.20 amendment 1
+   and relates to the CKA_OTP_..._REQUIREMENT attributes */
+#define CK_OTP_PARAM_IGNORED       0
+#define CK_OTP_PARAM_OPTIONAL      1
+#define CK_OTP_PARAM_MANDATORY     2
+
+/* The following attribute types are defined: */
+#define CKA_CLASS              0x00000000
+#define CKA_TOKEN              0x00000001
+#define CKA_PRIVATE            0x00000002
+#define CKA_LABEL              0x00000003
+#define CKA_APPLICATION        0x00000010
+#define CKA_VALUE              0x00000011
+
+/* CKA_OBJECT_ID is new for v2.10 */
+#define CKA_OBJECT_ID          0x00000012
+
+#define CKA_CERTIFICATE_TYPE   0x00000080
+#define CKA_ISSUER             0x00000081
+#define CKA_SERIAL_NUMBER      0x00000082
+
+/* CKA_AC_ISSUER, CKA_OWNER, and CKA_ATTR_TYPES are new
+ * for v2.10 */
+#define CKA_AC_ISSUER          0x00000083
+#define CKA_OWNER              0x00000084
+#define CKA_ATTR_TYPES         0x00000085
+
+/* CKA_TRUSTED is new for v2.11 */
+#define CKA_TRUSTED            0x00000086
+
+/* CKA_CERTIFICATE_CATEGORY ...
+ * CKA_CHECK_VALUE are new for v2.20 */
+#define CKA_CERTIFICATE_CATEGORY        0x00000087
+#define CKA_JAVA_MIDP_SECURITY_DOMAIN   0x00000088
+#define CKA_URL                         0x00000089
+#define CKA_HASH_OF_SUBJECT_PUBLIC_KEY  0x0000008A
+#define CKA_HASH_OF_ISSUER_PUBLIC_KEY   0x0000008B
+/* One from v2.30? */
+#define CKA_NAME_HASH_ALGORITH          0x0000008C
+#define CKA_CHECK_VALUE                 0x00000090
+
+#define CKA_KEY_TYPE           0x00000100
+#define CKA_SUBJECT            0x00000101
+#define CKA_ID                 0x00000102
+#define CKA_SENSITIVE          0x00000103
+#define CKA_ENCRYPT            0x00000104
+#define CKA_DECRYPT            0x00000105
+#define CKA_WRAP               0x00000106
+#define CKA_UNWRAP             0x00000107
+#define CKA_SIGN               0x00000108
+#define CKA_SIGN_RECOVER       0x00000109
+#define CKA_VERIFY             0x0000010A
+#define CKA_VERIFY_RECOVER     0x0000010B
+#define CKA_DERIVE             0x0000010C
+#define CKA_START_DATE         0x00000110
+#define CKA_END_DATE           0x00000111
+#define CKA_MODULUS            0x00000120
+#define CKA_MODULUS_BITS       0x00000121
+#define CKA_PUBLIC_EXPONENT    0x00000122
+#define CKA_PRIVATE_EXPONENT   0x00000123
+#define CKA_PRIME_1            0x00000124
+#define CKA_PRIME_2            0x00000125
+#define CKA_EXPONENT_1         0x00000126
+#define CKA_EXPONENT_2         0x00000127
+#define CKA_COEFFICIENT        0x00000128
+#define CKA_PRIME              0x00000130
+#define CKA_SUBPRIME           0x00000131
+#define CKA_BASE               0x00000132
+
+/* CKA_PRIME_BITS and CKA_SUB_PRIME_BITS are new for v2.11 */
+#define CKA_PRIME_BITS         0x00000133
+#define CKA_SUBPRIME_BITS      0x00000134
+#define CKA_SUB_PRIME_BITS     CKA_SUBPRIME_BITS
+/* (To retain backwards-compatibility) */
+
+#define CKA_VALUE_BITS         0x00000160
+#define CKA_VALUE_LEN          0x00000161
+
+/* CKA_EXTRACTABLE, CKA_LOCAL, CKA_NEVER_EXTRACTABLE,
+ * CKA_ALWAYS_SENSITIVE, CKA_MODIFIABLE, CKA_ECDSA_PARAMS,
+ * and CKA_EC_POINT are new for v2.0 */
+#define CKA_EXTRACTABLE        0x00000162
+#define CKA_LOCAL              0x00000163
+#define CKA_NEVER_EXTRACTABLE  0x00000164
+#define CKA_ALWAYS_SENSITIVE   0x00000165
+
+/* CKA_KEY_GEN_MECHANISM is new for v2.11 */
+#define CKA_KEY_GEN_MECHANISM  0x00000166
+
+#define CKA_MODIFIABLE         0x00000170
+
+/* From v2.30? */
+#define CKA_COPYABLE           0x00000171
+
+/* CKA_ECDSA_PARAMS is deprecated in v2.11,
+ * CKA_EC_PARAMS is preferred. */
+#define CKA_ECDSA_PARAMS       0x00000180
+#define CKA_EC_PARAMS          0x00000180
+
+#define CKA_EC_POINT           0x00000181
+
+/* CKA_SECONDARY_AUTH, CKA_AUTH_PIN_FLAGS,
+ * are new for v2.10. Deprecated in v2.11 and onwards. */
+#define CKA_SECONDARY_AUTH     0x00000200
+#define CKA_AUTH_PIN_FLAGS     0x00000201
+
+/* CKA_ALWAYS_AUTHENTICATE ...
+ * CKA_UNWRAP_TEMPLATE are new for v2.20 */
+#define CKA_ALWAYS_AUTHENTICATE  0x00000202
+
+#define CKA_WRAP_WITH_TRUSTED    0x00000210
+#define CKA_WRAP_TEMPLATE        (CKF_ARRAY_ATTRIBUTE|0x00000211)
+#define CKA_UNWRAP_TEMPLATE      (CKF_ARRAY_ATTRIBUTE|0x00000212)
+
+/* CKA_OTP... atttributes are new for PKCS #11 v2.20 amendment 3. */
+#define CKA_OTP_FORMAT                0x00000220
+#define CKA_OTP_LENGTH                0x00000221
+#define CKA_OTP_TIME_INTERVAL         0x00000222
+#define CKA_OTP_USER_FRIENDLY_MODE    0x00000223
+#define CKA_OTP_CHALLENGE_REQUIREMENT 0x00000224
+#define CKA_OTP_TIME_REQUIREMENT      0x00000225
+#define CKA_OTP_COUNTER_REQUIREMENT   0x00000226
+#define CKA_OTP_PIN_REQUIREMENT       0x00000227
+#define CKA_OTP_COUNTER               0x0000022E
+#define CKA_OTP_TIME                  0x0000022F
+#define CKA_OTP_USER_IDENTIFIER       0x0000022A
+#define CKA_OTP_SERVICE_IDENTIFIER    0x0000022B
+#define CKA_OTP_SERVICE_LOGO          0x0000022C
+#define CKA_OTP_SERVICE_LOGO_TYPE     0x0000022D
+
+/* CKA_GOST... */
+#define CKA_GOSTR3410_PARAMS          0x00000250
+#define CKA_GOSTR3411_PARAMS          0x00000251
+#define CKA_GOST28147_PARAMS          0x00000252
+
+/* CKA_HW_FEATURE_TYPE, CKA_RESET_ON_INIT, and CKA_HAS_RESET
+ * are new for v2.10 */
+#define CKA_HW_FEATURE_TYPE    0x00000300
+#define CKA_RESET_ON_INIT      0x00000301
+#define CKA_HAS_RESET          0x00000302
+
+/* The following attributes are new for v2.20 */
+#define CKA_PIXEL_X                     0x00000400
+#define CKA_PIXEL_Y                     0x00000401
+#define CKA_RESOLUTION                  0x00000402
+#define CKA_CHAR_ROWS                   0x00000403
+#define CKA_CHAR_COLUMNS                0x00000404
+#define CKA_COLOR                       0x00000405
+#define CKA_BITS_PER_PIXEL              0x00000406
+#define CKA_CHAR_SETS                   0x00000480
+#define CKA_ENCODING_METHODS            0x00000481
+#define CKA_MIME_TYPES                  0x00000482
+#define CKA_MECHANISM_TYPE              0x00000500
+#define CKA_REQUIRED_CMS_ATTRIBUTES     0x00000501
+#define CKA_DEFAULT_CMS_ATTRIBUTES      0x00000502
+#define CKA_SUPPORTED_CMS_ATTRIBUTES    0x00000503
+#define CKA_ALLOWED_MECHANISMS          (CKF_ARRAY_ATTRIBUTE|0x00000600)
+/* From v2.30? */
+#define CKA_WRAP_TEMPLATE               (CKF_ARRAY_ATTRIBUTE|0x00000211)
+#define CKA_UNWRAP_TEMPLATE             (CKF_ARRAY_ATTRIBUTE|0x00000212)
+#define CKA_DERIVE_TEMPLATE             (CKF_ARRAY_ATTRIBUTE|0x00000213)
+
+#define CKA_VENDOR_DEFINED     0x80000000
+
+/* CK_ATTRIBUTE is a structure that includes the type, length
+ * and value of an attribute */
+typedef struct CK_ATTRIBUTE {
+  CK_ATTRIBUTE_TYPE type;
+  CK_VOID_PTR       pValue;
+
+  /* ulValueLen went from CK_USHORT to CK_ULONG for v2.0 */
+  CK_ULONG          ulValueLen;  /* in bytes */
+} CK_ATTRIBUTE;
+
+typedef CK_ATTRIBUTE CK_PTR CK_ATTRIBUTE_PTR;
+
+
+/* CK_DATE is a structure that defines a date */
+typedef struct CK_DATE{
+  CK_CHAR       year[4];   /* the year ("1900" - "9999") */
+  CK_CHAR       month[2];  /* the month ("01" - "12") */
+  CK_CHAR       day[2];    /* the day   ("01" - "31") */
+} CK_DATE;
+
+
+/* CK_MECHANISM_TYPE is a value that identifies a mechanism
+ * type */
+/* CK_MECHANISM_TYPE was changed from CK_USHORT to CK_ULONG for
+ * v2.0 */
+typedef CK_ULONG          CK_MECHANISM_TYPE;
+
+/* the following mechanism types are defined: */
+#define CKM_RSA_PKCS_KEY_PAIR_GEN      0x00000000
+#define CKM_RSA_PKCS                   0x00000001
+#define CKM_RSA_9796                   0x00000002
+#define CKM_RSA_X_509                  0x00000003
+
+/* CKM_MD2_RSA_PKCS, CKM_MD5_RSA_PKCS, and CKM_SHA1_RSA_PKCS
+ * are new for v2.0.  They are mechanisms which hash and sign */
+#define CKM_MD2_RSA_PKCS               0x00000004
+#define CKM_MD5_RSA_PKCS               0x00000005
+#define CKM_SHA1_RSA_PKCS              0x00000006
+
+/* CKM_RIPEMD128_RSA_PKCS, CKM_RIPEMD160_RSA_PKCS, and
+ * CKM_RSA_PKCS_OAEP are new for v2.10 */
+#define CKM_RIPEMD128_RSA_PKCS         0x00000007
+#define CKM_RIPEMD160_RSA_PKCS         0x00000008
+#define CKM_RSA_PKCS_OAEP              0x00000009
+
+/* CKM_RSA_X9_31_KEY_PAIR_GEN, CKM_RSA_X9_31, CKM_SHA1_RSA_X9_31,
+ * CKM_RSA_PKCS_PSS, and CKM_SHA1_RSA_PKCS_PSS are new for v2.11 */
+#define CKM_RSA_X9_31_KEY_PAIR_GEN     0x0000000A
+#define CKM_RSA_X9_31                  0x0000000B
+#define CKM_SHA1_RSA_X9_31             0x0000000C
+#define CKM_RSA_PKCS_PSS               0x0000000D
+#define CKM_SHA1_RSA_PKCS_PSS          0x0000000E
+
+#define CKM_DSA_KEY_PAIR_GEN           0x00000010
+#define CKM_DSA                        0x00000011
+#define CKM_DSA_SHA1                   0x00000012
+/* Other DSAs */
+#define CKM_DSA_SHA224                 0x00000013
+#define CKM_DSA_SHA256                 0x00000014
+#define CKM_DSA_SHA384                 0x00000015
+#define CKM_DSA_SHA512                 0x00000016
+
+#define CKM_DH_PKCS_KEY_PAIR_GEN       0x00000020
+#define CKM_DH_PKCS_DERIVE             0x00000021
+
+/* CKM_X9_42_DH_KEY_PAIR_GEN, CKM_X9_42_DH_DERIVE,
+ * CKM_X9_42_DH_HYBRID_DERIVE, and CKM_X9_42_MQV_DERIVE are new for
+ * v2.11 */
+#define CKM_X9_42_DH_KEY_PAIR_GEN      0x00000030
+#define CKM_X9_42_DH_DERIVE            0x00000031
+#define CKM_X9_42_DH_HYBRID_DERIVE     0x00000032
+#define CKM_X9_42_MQV_DERIVE           0x00000033
+
+/* CKM_SHA256/384/512 are new for v2.20 */
+#define CKM_SHA256_RSA_PKCS            0x00000040
+#define CKM_SHA384_RSA_PKCS            0x00000041
+#define CKM_SHA512_RSA_PKCS            0x00000042
+#define CKM_SHA256_RSA_PKCS_PSS        0x00000043
+#define CKM_SHA384_RSA_PKCS_PSS        0x00000044
+#define CKM_SHA512_RSA_PKCS_PSS        0x00000045
+
+/* SHA-224 RSA mechanisms are new for PKCS #11 v2.20 amendment 3 */
+#define CKM_SHA224_RSA_PKCS            0x00000046
+#define CKM_SHA224_RSA_PKCS_PSS        0x00000047
+
+#define CKM_RC2_KEY_GEN                0x00000100
+#define CKM_RC2_ECB                    0x00000101
+#define CKM_RC2_CBC                    0x00000102
+#define CKM_RC2_MAC                    0x00000103
+
+/* CKM_RC2_MAC_GENERAL and CKM_RC2_CBC_PAD are new for v2.0 */
+#define CKM_RC2_MAC_GENERAL            0x00000104
+#define CKM_RC2_CBC_PAD                0x00000105
+
+#define CKM_RC4_KEY_GEN                0x00000110
+#define CKM_RC4                        0x00000111
+#define CKM_DES_KEY_GEN                0x00000120
+#define CKM_DES_ECB                    0x00000121
+#define CKM_DES_CBC                    0x00000122
+#define CKM_DES_MAC                    0x00000123
+
+/* CKM_DES_MAC_GENERAL and CKM_DES_CBC_PAD are new for v2.0 */
+#define CKM_DES_MAC_GENERAL            0x00000124
+#define CKM_DES_CBC_PAD                0x00000125
+
+#define CKM_DES2_KEY_GEN               0x00000130
+#define CKM_DES3_KEY_GEN               0x00000131
+#define CKM_DES3_ECB                   0x00000132
+#define CKM_DES3_CBC                   0x00000133
+#define CKM_DES3_MAC                   0x00000134
+
+/* CKM_DES3_MAC_GENERAL, CKM_DES3_CBC_PAD, CKM_CDMF_KEY_GEN,
+ * CKM_CDMF_ECB, CKM_CDMF_CBC, CKM_CDMF_MAC,
+ * CKM_CDMF_MAC_GENERAL, and CKM_CDMF_CBC_PAD are new for v2.0,
+ * CKM_DES3_CMAC_GENERAL and CKM_DES3_CMAC are from v2.30? */
+#define CKM_DES3_MAC_GENERAL           0x00000135
+#define CKM_DES3_CBC_PAD               0x00000136
+#define CKM_DES3_CMAC_GENERAL          0x00000137
+#define CKM_DES3_CMAC                  0x00000138
+#define CKM_CDMF_KEY_GEN               0x00000140
+#define CKM_CDMF_ECB                   0x00000141
+#define CKM_CDMF_CBC                   0x00000142
+#define CKM_CDMF_MAC                   0x00000143
+#define CKM_CDMF_MAC_GENERAL           0x00000144
+#define CKM_CDMF_CBC_PAD               0x00000145
+
+/* the following four DES mechanisms are new for v2.20 */
+#define CKM_DES_OFB64                  0x00000150
+#define CKM_DES_OFB8                   0x00000151
+#define CKM_DES_CFB64                  0x00000152
+#define CKM_DES_CFB8                   0x00000153
+
+#define CKM_MD2                        0x00000200
+
+/* CKM_MD2_HMAC and CKM_MD2_HMAC_GENERAL are new for v2.0 */
+#define CKM_MD2_HMAC                   0x00000201
+#define CKM_MD2_HMAC_GENERAL           0x00000202
+
+#define CKM_MD5                        0x00000210
+
+/* CKM_MD5_HMAC and CKM_MD5_HMAC_GENERAL are new for v2.0 */
+#define CKM_MD5_HMAC                   0x00000211
+#define CKM_MD5_HMAC_GENERAL           0x00000212
+
+#define CKM_SHA_1                      0x00000220
+
+/* CKM_SHA_1_HMAC and CKM_SHA_1_HMAC_GENERAL are new for v2.0 */
+#define CKM_SHA_1_HMAC                 0x00000221
+#define CKM_SHA_1_HMAC_GENERAL         0x00000222
+
+/* CKM_RIPEMD128, CKM_RIPEMD128_HMAC,
+ * CKM_RIPEMD128_HMAC_GENERAL, CKM_RIPEMD160, CKM_RIPEMD160_HMAC,
+ * and CKM_RIPEMD160_HMAC_GENERAL are new for v2.10 */
+#define CKM_RIPEMD128                  0x00000230
+#define CKM_RIPEMD128_HMAC             0x00000231
+#define CKM_RIPEMD128_HMAC_GENERAL     0x00000232
+#define CKM_RIPEMD160                  0x00000240
+#define CKM_RIPEMD160_HMAC             0x00000241
+#define CKM_RIPEMD160_HMAC_GENERAL     0x00000242
+
+/* CKM_SHA256/384/512 are new for v2.20 */
+#define CKM_SHA256                     0x00000250
+#define CKM_SHA256_HMAC                0x00000251
+#define CKM_SHA256_HMAC_GENERAL        0x00000252
+
+/* SHA-224 is new for PKCS #11 v2.20 amendment 3 */
+#define CKM_SHA224                     0x00000255
+#define CKM_SHA224_HMAC                0x00000256
+#define CKM_SHA224_HMAC_GENERAL        0x00000257
+
+#define CKM_SHA384                     0x00000260
+#define CKM_SHA384_HMAC                0x00000261
+#define CKM_SHA384_HMAC_GENERAL        0x00000262
+#define CKM_SHA512                     0x00000270
+#define CKM_SHA512_HMAC                0x00000271
+#define CKM_SHA512_HMAC_GENERAL        0x00000272
+
+/* SecurID is new for PKCS #11 v2.20 amendment 1 */
+#define CKM_SECURID_KEY_GEN            0x00000280
+#define CKM_SECURID                    0x00000282
+
+/* HOTP is new for PKCS #11 v2.20 amendment 1 */
+#define CKM_HOTP_KEY_GEN    0x00000290
+#define CKM_HOTP            0x00000291
+
+/* ACTI is new for PKCS #11 v2.20 amendment 1 */
+#define CKM_ACTI            0x000002A0
+#define CKM_ACTI_KEY_GEN    0x000002A1
+
+/* All of the following mechanisms are new for v2.0 */
+/* Note that CAST128 and CAST5 are the same algorithm */
+#define CKM_CAST_KEY_GEN               0x00000300
+#define CKM_CAST_ECB                   0x00000301
+#define CKM_CAST_CBC                   0x00000302
+#define CKM_CAST_MAC                   0x00000303
+#define CKM_CAST_MAC_GENERAL           0x00000304
+#define CKM_CAST_CBC_PAD               0x00000305
+#define CKM_CAST3_KEY_GEN              0x00000310
+#define CKM_CAST3_ECB                  0x00000311
+#define CKM_CAST3_CBC                  0x00000312
+#define CKM_CAST3_MAC                  0x00000313
+#define CKM_CAST3_MAC_GENERAL          0x00000314
+#define CKM_CAST3_CBC_PAD              0x00000315
+#define CKM_CAST5_KEY_GEN              0x00000320
+#define CKM_CAST128_KEY_GEN            0x00000320
+#define CKM_CAST5_ECB                  0x00000321
+#define CKM_CAST128_ECB                0x00000321
+#define CKM_CAST5_CBC                  0x00000322
+#define CKM_CAST128_CBC                0x00000322
+#define CKM_CAST5_MAC                  0x00000323
+#define CKM_CAST128_MAC                0x00000323
+#define CKM_CAST5_MAC_GENERAL          0x00000324
+#define CKM_CAST128_MAC_GENERAL        0x00000324
+#define CKM_CAST5_CBC_PAD              0x00000325
+#define CKM_CAST128_CBC_PAD            0x00000325
+#define CKM_RC5_KEY_GEN                0x00000330
+#define CKM_RC5_ECB                    0x00000331
+#define CKM_RC5_CBC                    0x00000332
+#define CKM_RC5_MAC                    0x00000333
+#define CKM_RC5_MAC_GENERAL            0x00000334
+#define CKM_RC5_CBC_PAD                0x00000335
+#define CKM_IDEA_KEY_GEN               0x00000340
+#define CKM_IDEA_ECB                   0x00000341
+#define CKM_IDEA_CBC                   0x00000342
+#define CKM_IDEA_MAC                   0x00000343
+#define CKM_IDEA_MAC_GENERAL           0x00000344
+#define CKM_IDEA_CBC_PAD               0x00000345
+#define CKM_GENERIC_SECRET_KEY_GEN     0x00000350
+#define CKM_CONCATENATE_BASE_AND_KEY   0x00000360
+#define CKM_CONCATENATE_BASE_AND_DATA  0x00000362
+#define CKM_CONCATENATE_DATA_AND_BASE  0x00000363
+#define CKM_XOR_BASE_AND_DATA          0x00000364
+#define CKM_EXTRACT_KEY_FROM_KEY       0x00000365
+#define CKM_SSL3_PRE_MASTER_KEY_GEN    0x00000370
+#define CKM_SSL3_MASTER_KEY_DERIVE     0x00000371
+#define CKM_SSL3_KEY_AND_MAC_DERIVE    0x00000372
+
+/* CKM_SSL3_MASTER_KEY_DERIVE_DH, CKM_TLS_PRE_MASTER_KEY_GEN,
+ * CKM_TLS_MASTER_KEY_DERIVE, CKM_TLS_KEY_AND_MAC_DERIVE, and
+ * CKM_TLS_MASTER_KEY_DERIVE_DH are new for v2.11 */
+#define CKM_SSL3_MASTER_KEY_DERIVE_DH  0x00000373
+#define CKM_TLS_PRE_MASTER_KEY_GEN     0x00000374
+#define CKM_TLS_MASTER_KEY_DERIVE      0x00000375
+#define CKM_TLS_KEY_AND_MAC_DERIVE     0x00000376
+#define CKM_TLS_MASTER_KEY_DERIVE_DH   0x00000377
+
+/* CKM_TLS_PRF is new for v2.20 */
+#define CKM_TLS_PRF                    0x00000378
+
+#define CKM_SSL3_MD5_MAC               0x00000380
+#define CKM_SSL3_SHA1_MAC              0x00000381
+#define CKM_MD5_KEY_DERIVATION         0x00000390
+#define CKM_MD2_KEY_DERIVATION         0x00000391
+#define CKM_SHA1_KEY_DERIVATION        0x00000392
+
+/* CKM_SHA256/384/512 are new for v2.20 */
+#define CKM_SHA256_KEY_DERIVATION      0x00000393
+#define CKM_SHA384_KEY_DERIVATION      0x00000394
+#define CKM_SHA512_KEY_DERIVATION      0x00000395
+
+/* SHA-224 key derivation is new for PKCS #11 v2.20 amendment 3 */
+#define CKM_SHA224_KEY_DERIVATION      0x00000396
+
+#define CKM_PBE_MD2_DES_CBC            0x000003A0
+#define CKM_PBE_MD5_DES_CBC            0x000003A1
+#define CKM_PBE_MD5_CAST_CBC           0x000003A2
+#define CKM_PBE_MD5_CAST3_CBC          0x000003A3
+#define CKM_PBE_MD5_CAST5_CBC          0x000003A4
+#define CKM_PBE_MD5_CAST128_CBC        0x000003A4
+#define CKM_PBE_SHA1_CAST5_CBC         0x000003A5
+#define CKM_PBE_SHA1_CAST128_CBC       0x000003A5
+#define CKM_PBE_SHA1_RC4_128           0x000003A6
+#define CKM_PBE_SHA1_RC4_40            0x000003A7
+#define CKM_PBE_SHA1_DES3_EDE_CBC      0x000003A8
+#define CKM_PBE_SHA1_DES2_EDE_CBC      0x000003A9
+#define CKM_PBE_SHA1_RC2_128_CBC       0x000003AA
+#define CKM_PBE_SHA1_RC2_40_CBC        0x000003AB
+
+/* CKM_PKCS5_PBKD2 is new for v2.10 */
+#define CKM_PKCS5_PBKD2                0x000003B0
+
+#define CKM_PBA_SHA1_WITH_SHA1_HMAC    0x000003C0
+
+/* WTLS mechanisms are new for v2.20 */
+#define CKM_WTLS_PRE_MASTER_KEY_GEN         0x000003D0
+#define CKM_WTLS_MASTER_KEY_DERIVE          0x000003D1
+#define CKM_WTLS_MASTER_KEY_DERIVE_DH_ECC   0x000003D2
+#define CKM_WTLS_PRF                        0x000003D3
+#define CKM_WTLS_SERVER_KEY_AND_MAC_DERIVE  0x000003D4
+#define CKM_WTLS_CLIENT_KEY_AND_MAC_DERIVE  0x000003D5
+
+#define CKM_KEY_WRAP_LYNKS             0x00000400
+#define CKM_KEY_WRAP_SET_OAEP          0x00000401
+
+/* CKM_CMS_SIG is new for v2.20 */
+#define CKM_CMS_SIG                    0x00000500
+
+/* CKM_KIP mechanisms are new for PKCS #11 v2.20 amendment 2 */
+#define CKM_KIP_DERIVE                 0x00000510
+#define CKM_KIP_WRAP                   0x00000511
+#define CKM_KIP_MAC                    0x00000512
+
+/* Camellia is new for PKCS #11 v2.20 amendment 3 */
+#define CKM_CAMELLIA_KEY_GEN           0x00000550
+#define CKM_CAMELLIA_ECB               0x00000551
+#define CKM_CAMELLIA_CBC               0x00000552
+#define CKM_CAMELLIA_MAC               0x00000553
+#define CKM_CAMELLIA_MAC_GENERAL       0x00000554
+#define CKM_CAMELLIA_CBC_PAD           0x00000555
+#define CKM_CAMELLIA_ECB_ENCRYPT_DATA  0x00000556
+#define CKM_CAMELLIA_CBC_ENCRYPT_DATA  0x00000557
+#define CKM_CAMELLIA_CTR               0x00000558
+
+/* ARIA is new for PKCS #11 v2.20 amendment 3 */
+#define CKM_ARIA_KEY_GEN               0x00000560
+#define CKM_ARIA_ECB                   0x00000561
+#define CKM_ARIA_CBC                   0x00000562
+#define CKM_ARIA_MAC                   0x00000563
+#define CKM_ARIA_MAC_GENERAL           0x00000564
+#define CKM_ARIA_CBC_PAD               0x00000565
+#define CKM_ARIA_ECB_ENCRYPT_DATA      0x00000566
+#define CKM_ARIA_CBC_ENCRYPT_DATA      0x00000567
+
+/* SEED is new from PKCS #11 v2.30? */
+#define CKM_SEED_KEY_GEN               0x00000650
+#define CKM_SEED_ECB                   0x00000651
+#define CKM_SEED_CBC                   0x00000652
+#define CKM_SEED_MAC                   0x00000653
+#define CKM_SEED_MAC_GENERAL           0x00000654
+#define CKM_SEED_CBC_PAD               0x00000655
+#define CKM_SEED_ECB_ENCRYPT_DATA      0x00000656
+#define CKM_SEED_CBC_ENCRYPT_DATA      0x00000657
+
+/* Fortezza mechanisms */
+#define CKM_SKIPJACK_KEY_GEN           0x00001000
+#define CKM_SKIPJACK_ECB64             0x00001001
+#define CKM_SKIPJACK_CBC64             0x00001002
+#define CKM_SKIPJACK_OFB64             0x00001003
+#define CKM_SKIPJACK_CFB64             0x00001004
+#define CKM_SKIPJACK_CFB32             0x00001005
+#define CKM_SKIPJACK_CFB16             0x00001006
+#define CKM_SKIPJACK_CFB8              0x00001007
+#define CKM_SKIPJACK_WRAP              0x00001008
+#define CKM_SKIPJACK_PRIVATE_WRAP      0x00001009
+#define CKM_SKIPJACK_RELAYX            0x0000100a
+#define CKM_KEA_KEY_PAIR_GEN           0x00001010
+#define CKM_KEA_KEY_DERIVE             0x00001011
+#define CKM_FORTEZZA_TIMESTAMP         0x00001020
+#define CKM_BATON_KEY_GEN              0x00001030
+#define CKM_BATON_ECB128               0x00001031
+#define CKM_BATON_ECB96                0x00001032
+#define CKM_BATON_CBC128               0x00001033
+#define CKM_BATON_COUNTER              0x00001034
+#define CKM_BATON_SHUFFLE              0x00001035
+#define CKM_BATON_WRAP                 0x00001036
+
+/* CKM_ECDSA_KEY_PAIR_GEN is deprecated in v2.11,
+ * CKM_EC_KEY_PAIR_GEN is preferred */
+#define CKM_ECDSA_KEY_PAIR_GEN         0x00001040
+#define CKM_EC_KEY_PAIR_GEN            0x00001040
+
+#define CKM_ECDSA                      0x00001041
+#define CKM_ECDSA_SHA1                 0x00001042
+
+/* From v2.30? */
+#define CKM_ECDSA_SHA224               0x00001043
+#define CKM_ECDSA_SHA256               0x00001044
+#define CKM_ECDSA_SHA384               0x00001045
+#define CKM_ECDSA_SHA512               0x00001046
+
+/* CKM_ECDH1_DERIVE, CKM_ECDH1_COFACTOR_DERIVE, and CKM_ECMQV_DERIVE
+ * are new for v2.11 */
+#define CKM_ECDH1_DERIVE               0x00001050
+#define CKM_ECDH1_COFACTOR_DERIVE      0x00001051
+#define CKM_ECMQV_DERIVE               0x00001052
+
+#define CKM_JUNIPER_KEY_GEN            0x00001060
+#define CKM_JUNIPER_ECB128             0x00001061
+#define CKM_JUNIPER_CBC128             0x00001062
+#define CKM_JUNIPER_COUNTER            0x00001063
+#define CKM_JUNIPER_SHUFFLE            0x00001064
+#define CKM_JUNIPER_WRAP               0x00001065
+#define CKM_FASTHASH                   0x00001070
+
+/* CKM_AES_KEY_GEN, CKM_AES_ECB, CKM_AES_CBC, CKM_AES_MAC,
+ * CKM_AES_MAC_GENERAL, CKM_AES_CBC_PAD, CKM_DSA_PARAMETER_GEN,
+ * CKM_DH_PKCS_PARAMETER_GEN, and CKM_X9_42_DH_PARAMETER_GEN are
+ * new for v2.11 */
+#define CKM_AES_KEY_GEN                0x00001080
+#define CKM_AES_ECB                    0x00001081
+#define CKM_AES_CBC                    0x00001082
+#define CKM_AES_MAC                    0x00001083
+#define CKM_AES_MAC_GENERAL            0x00001084
+#define CKM_AES_CBC_PAD                0x00001085
+
+/* AES counter mode is new for PKCS #11 v2.20 amendment 3 */
+#define CKM_AES_CTR                    0x00001086
+
+/* Missing CKM_AES_GCM and co! */
+
+/* BlowFish and TwoFish are new for v2.20 */
+#define CKM_BLOWFISH_KEY_GEN           0x00001090
+#define CKM_BLOWFISH_CBC               0x00001091
+#define CKM_TWOFISH_KEY_GEN            0x00001092
+#define CKM_TWOFISH_CBC                0x00001093
+
+
+/* CKM_xxx_ENCRYPT_DATA mechanisms are new for v2.20 */
+#define CKM_DES_ECB_ENCRYPT_DATA       0x00001100
+#define CKM_DES_CBC_ENCRYPT_DATA       0x00001101
+#define CKM_DES3_ECB_ENCRYPT_DATA      0x00001102
+#define CKM_DES3_CBC_ENCRYPT_DATA      0x00001103
+#define CKM_AES_ECB_ENCRYPT_DATA       0x00001104
+#define CKM_AES_CBC_ENCRYPT_DATA       0x00001105
+
+/* GOST mechanism from v2.30? */
+#define CKM_GOSTR3410_KEY_PAIR_GEN     0x00001200
+#define CKM_GOSTR3410                  0x00001201
+#define CKM_GOSTR3410_WITH_GOSTR3411   0x00001202
+#define CKM_GOSTR3410_KEY_WRAP         0x00001203
+#define CKM_GOSTR3410_DERIVE           0x00001204
+#define CKM_GOSTR3411                  0x00001210
+#define CKM_GOSTR3411_HMAC             0x00001211
+#define CKM_GOST28147_KEY_GEN          0x00001220
+#define CKM_GOST28147_ECB              0x00001221
+#define CKM_GOST28147                  0x00001222
+#define CKM_GOST28147_MAC              0x00001223
+#define CKM_GOST28147_KEY_WRAP         0x00001224
+
+#define CKM_DSA_PARAMETER_GEN          0x00002000
+#define CKM_DH_PKCS_PARAMETER_GEN      0x00002001
+#define CKM_X9_42_DH_PARAMETER_GEN     0x00002002
+
+/* Missing AES_OFB and co, and RSA_PKCS 1_1 */
+
+#define CKM_VENDOR_DEFINED             0x80000000
+
+typedef CK_MECHANISM_TYPE CK_PTR CK_MECHANISM_TYPE_PTR;
+
+
+/* CK_MECHANISM is a structure that specifies a particular
+ * mechanism  */
+typedef struct CK_MECHANISM {
+  CK_MECHANISM_TYPE mechanism;
+  CK_VOID_PTR       pParameter;
+
+  /* ulParameterLen was changed from CK_USHORT to CK_ULONG for
+   * v2.0 */
+  CK_ULONG          ulParameterLen;  /* in bytes */
+} CK_MECHANISM;
+
+typedef CK_MECHANISM CK_PTR CK_MECHANISM_PTR;
+
+
+/* CK_MECHANISM_INFO provides information about a particular
+ * mechanism */
+typedef struct CK_MECHANISM_INFO {
+    CK_ULONG    ulMinKeySize;
+    CK_ULONG    ulMaxKeySize;
+    CK_FLAGS    flags;
+} CK_MECHANISM_INFO;
+
+/* The flags are defined as follows:
+ *      Bit Flag               Mask        Meaning */
+#define CKF_HW                 0x00000001  /* performed by HW */
+
+/* The flags CKF_ENCRYPT, CKF_DECRYPT, CKF_DIGEST, CKF_SIGN,
+ * CKG_SIGN_RECOVER, CKF_VERIFY, CKF_VERIFY_RECOVER,
+ * CKF_GENERATE, CKF_GENERATE_KEY_PAIR, CKF_WRAP, CKF_UNWRAP,
+ * and CKF_DERIVE are new for v2.0.  They specify whether or not
+ * a mechanism can be used for a particular task */
+#define CKF_ENCRYPT            0x00000100
+#define CKF_DECRYPT            0x00000200
+#define CKF_DIGEST             0x00000400
+#define CKF_SIGN               0x00000800
+#define CKF_SIGN_RECOVER       0x00001000
+#define CKF_VERIFY             0x00002000
+#define CKF_VERIFY_RECOVER     0x00004000
+#define CKF_GENERATE           0x00008000
+#define CKF_GENERATE_KEY_PAIR  0x00010000
+#define CKF_WRAP               0x00020000
+#define CKF_UNWRAP             0x00040000
+#define CKF_DERIVE             0x00080000
+
+/* CKF_EC_F_P, CKF_EC_F_2M, CKF_EC_ECPARAMETERS, CKF_EC_NAMEDCURVE,
+ * CKF_EC_UNCOMPRESS, and CKF_EC_COMPRESS are new for v2.11. They
+ * describe a token's EC capabilities not available in mechanism
+ * information. */
+#define CKF_EC_F_P             0x00100000
+#define CKF_EC_F_2M            0x00200000
+#define CKF_EC_ECPARAMETERS    0x00400000
+#define CKF_EC_NAMEDCURVE      0x00800000
+#define CKF_EC_UNCOMPRESS      0x01000000
+#define CKF_EC_COMPRESS        0x02000000
+
+#define CKF_EXTENSION          0x80000000 /* FALSE for this version */
+
+typedef CK_MECHANISM_INFO CK_PTR CK_MECHANISM_INFO_PTR;
+
+
+/* CK_RV is a value that identifies the return value of a
+ * Cryptoki function */
+/* CK_RV was changed from CK_USHORT to CK_ULONG for v2.0 */
+typedef CK_ULONG          CK_RV;
+
+#define CKR_OK                                0x00000000
+#define CKR_CANCEL                            0x00000001
+#define CKR_HOST_MEMORY                       0x00000002
+#define CKR_SLOT_ID_INVALID                   0x00000003
+
+/* CKR_FLAGS_INVALID was removed for v2.0 */
+
+/* CKR_GENERAL_ERROR and CKR_FUNCTION_FAILED are new for v2.0 */
+#define CKR_GENERAL_ERROR                     0x00000005
+#define CKR_FUNCTION_FAILED                   0x00000006
+
+/* CKR_ARGUMENTS_BAD, CKR_NO_EVENT, CKR_NEED_TO_CREATE_THREADS,
+ * and CKR_CANT_LOCK are new for v2.01 */
+#define CKR_ARGUMENTS_BAD                     0x00000007
+#define CKR_NO_EVENT                          0x00000008
+#define CKR_NEED_TO_CREATE_THREADS            0x00000009
+#define CKR_CANT_LOCK                         0x0000000A
+
+#define CKR_ATTRIBUTE_READ_ONLY               0x00000010
+#define CKR_ATTRIBUTE_SENSITIVE               0x00000011
+#define CKR_ATTRIBUTE_TYPE_INVALID            0x00000012
+#define CKR_ATTRIBUTE_VALUE_INVALID           0x00000013
+/* New CKR_COPY_PROHIBITED in v2.30? */
+#define CKR_COPY_PROHIBITED                   0x0000001A
+#define CKR_DATA_INVALID                      0x00000020
+#define CKR_DATA_LEN_RANGE                    0x00000021
+#define CKR_DEVICE_ERROR                      0x00000030
+#define CKR_DEVICE_MEMORY                     0x00000031
+#define CKR_DEVICE_REMOVED                    0x00000032
+#define CKR_ENCRYPTED_DATA_INVALID            0x00000040
+#define CKR_ENCRYPTED_DATA_LEN_RANGE          0x00000041
+#define CKR_FUNCTION_CANCELED                 0x00000050
+#define CKR_FUNCTION_NOT_PARALLEL             0x00000051
+
+/* CKR_FUNCTION_NOT_SUPPORTED is new for v2.0 */
+#define CKR_FUNCTION_NOT_SUPPORTED            0x00000054
+
+#define CKR_KEY_HANDLE_INVALID                0x00000060
+
+/* CKR_KEY_SENSITIVE was removed for v2.0 */
+
+#define CKR_KEY_SIZE_RANGE                    0x00000062
+#define CKR_KEY_TYPE_INCONSISTENT             0x00000063
+
+/* CKR_KEY_NOT_NEEDED, CKR_KEY_CHANGED, CKR_KEY_NEEDED,
+ * CKR_KEY_INDIGESTIBLE, CKR_KEY_FUNCTION_NOT_PERMITTED,
+ * CKR_KEY_NOT_WRAPPABLE, and CKR_KEY_UNEXTRACTABLE are new for
+ * v2.0 */
+#define CKR_KEY_NOT_NEEDED                    0x00000064
+#define CKR_KEY_CHANGED                       0x00000065
+#define CKR_KEY_NEEDED                        0x00000066
+#define CKR_KEY_INDIGESTIBLE                  0x00000067
+#define CKR_KEY_FUNCTION_NOT_PERMITTED        0x00000068
+#define CKR_KEY_NOT_WRAPPABLE                 0x00000069
+#define CKR_KEY_UNEXTRACTABLE                 0x0000006A
+
+#define CKR_MECHANISM_INVALID                 0x00000070
+#define CKR_MECHANISM_PARAM_INVALID           0x00000071
+
+/* CKR_OBJECT_CLASS_INCONSISTENT and CKR_OBJECT_CLASS_INVALID
+ * were removed for v2.0 */
+#define CKR_OBJECT_HANDLE_INVALID             0x00000082
+#define CKR_OPERATION_ACTIVE                  0x00000090
+#define CKR_OPERATION_NOT_INITIALIZED         0x00000091
+#define CKR_PIN_INCORRECT                     0x000000A0
+#define CKR_PIN_INVALID                       0x000000A1
+#define CKR_PIN_LEN_RANGE                     0x000000A2
+
+/* CKR_PIN_EXPIRED and CKR_PIN_LOCKED are new for v2.0 */
+#define CKR_PIN_EXPIRED                       0x000000A3
+#define CKR_PIN_LOCKED                        0x000000A4
+
+#define CKR_SESSION_CLOSED                    0x000000B0
+#define CKR_SESSION_COUNT                     0x000000B1
+#define CKR_SESSION_HANDLE_INVALID            0x000000B3
+#define CKR_SESSION_PARALLEL_NOT_SUPPORTED    0x000000B4
+#define CKR_SESSION_READ_ONLY                 0x000000B5
+#define CKR_SESSION_EXISTS                    0x000000B6
+
+/* CKR_SESSION_READ_ONLY_EXISTS and
+ * CKR_SESSION_READ_WRITE_SO_EXISTS are new for v2.0 */
+#define CKR_SESSION_READ_ONLY_EXISTS          0x000000B7
+#define CKR_SESSION_READ_WRITE_SO_EXISTS      0x000000B8
+
+#define CKR_SIGNATURE_INVALID                 0x000000C0
+#define CKR_SIGNATURE_LEN_RANGE               0x000000C1
+#define CKR_TEMPLATE_INCOMPLETE               0x000000D0
+#define CKR_TEMPLATE_INCONSISTENT             0x000000D1
+#define CKR_TOKEN_NOT_PRESENT                 0x000000E0
+#define CKR_TOKEN_NOT_RECOGNIZED              0x000000E1
+#define CKR_TOKEN_WRITE_PROTECTED             0x000000E2
+#define CKR_UNWRAPPING_KEY_HANDLE_INVALID     0x000000F0
+#define CKR_UNWRAPPING_KEY_SIZE_RANGE         0x000000F1
+#define CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT  0x000000F2
+
+/* private extra values */
+#define CKR_LIBRARY_ALREADY_INITIALIZED       0x000000FD
+#define CKR_LIBRARY_FAILED_TO_LOAD            0x000000FE
+#define CKR_SYMBOL_RESOLUTION_FAILED          0x000000FF
+
+#define CKR_USER_ALREADY_LOGGED_IN            0x00000100
+#define CKR_USER_NOT_LOGGED_IN                0x00000101
+#define CKR_USER_PIN_NOT_INITIALIZED          0x00000102
+#define CKR_USER_TYPE_INVALID                 0x00000103
+
+/* CKR_USER_ANOTHER_ALREADY_LOGGED_IN and CKR_USER_TOO_MANY_TYPES
+ * are new to v2.01 */
+#define CKR_USER_ANOTHER_ALREADY_LOGGED_IN    0x00000104
+#define CKR_USER_TOO_MANY_TYPES               0x00000105
+
+#define CKR_WRAPPED_KEY_INVALID               0x00000110
+#define CKR_WRAPPED_KEY_LEN_RANGE             0x00000112
+#define CKR_WRAPPING_KEY_HANDLE_INVALID       0x00000113
+#define CKR_WRAPPING_KEY_SIZE_RANGE           0x00000114
+#define CKR_WRAPPING_KEY_TYPE_INCONSISTENT    0x00000115
+#define CKR_RANDOM_SEED_NOT_SUPPORTED         0x00000120
+
+/* These are new to v2.0 */
+#define CKR_RANDOM_NO_RNG                     0x00000121
+
+/* These are new to v2.11 */
+#define CKR_DOMAIN_PARAMS_INVALID             0x00000130
+
+/* These are new to v2.0 */
+#define CKR_BUFFER_TOO_SMALL                  0x00000150
+#define CKR_SAVED_STATE_INVALID               0x00000160
+#define CKR_INFORMATION_SENSITIVE             0x00000170
+#define CKR_STATE_UNSAVEABLE                  0x00000180
+
+/* These are new to v2.01 */
+#define CKR_CRYPTOKI_NOT_INITIALIZED          0x00000190
+#define CKR_CRYPTOKI_ALREADY_INITIALIZED      0x00000191
+#define CKR_MUTEX_BAD                         0x000001A0
+#define CKR_MUTEX_NOT_LOCKED                  0x000001A1
+
+/* The following return values are new for PKCS #11 v2.20 amendment 3 */
+#define CKR_NEW_PIN_MODE                      0x000001B0
+#define CKR_NEXT_OTP                          0x000001B1
+
+/* New from v2.30? */
+#define CKR_EXCEEDED_MAX_ITERATIONS           0x000001B5
+#define CKR_FIPS_SELF_TEST_FAILED             0x000001B6
+#define CKR_LIBRARY_LOAD_FAILED               0x000001B7
+#define CKR_PIN_TOO_WEAK                      0x000001B8
+#define CKR_PUBLIC_KEY_INVALID                0x000001B9
+
+/* This is new to v2.20 */
+#define CKR_FUNCTION_REJECTED                 0x00000200
+
+#define CKR_VENDOR_DEFINED                    0x80000000
+
+
+/* CK_NOTIFY is an application callback that processes events */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_NOTIFY)(
+  CK_SESSION_HANDLE hSession,     /* the session's handle */
+  CK_NOTIFICATION   event,
+  CK_VOID_PTR       pApplication  /* passed to C_OpenSession */
+);
+
+
+/* CK_FUNCTION_LIST is a structure holding a Cryptoki spec
+ * version and pointers of appropriate types to all the
+ * Cryptoki functions */
+/* CK_FUNCTION_LIST is new for v2.0 */
+typedef struct CK_FUNCTION_LIST CK_FUNCTION_LIST;
+
+typedef CK_FUNCTION_LIST CK_PTR CK_FUNCTION_LIST_PTR;
+
+typedef CK_FUNCTION_LIST_PTR CK_PTR CK_FUNCTION_LIST_PTR_PTR;
+
+
+/* CK_CREATEMUTEX is an application callback for creating a
+ * mutex object */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_CREATEMUTEX)(
+  CK_VOID_PTR_PTR ppMutex  /* location to receive ptr to mutex */
+);
+
+
+/* CK_DESTROYMUTEX is an application callback for destroying a
+ * mutex object */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_DESTROYMUTEX)(
+  CK_VOID_PTR pMutex  /* pointer to mutex */
+);
+
+
+/* CK_LOCKMUTEX is an application callback for locking a mutex */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_LOCKMUTEX)(
+  CK_VOID_PTR pMutex  /* pointer to mutex */
+);
+
+
+/* CK_UNLOCKMUTEX is an application callback for unlocking a
+ * mutex */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_UNLOCKMUTEX)(
+  CK_VOID_PTR pMutex  /* pointer to mutex */
+);
+
+
+/* CK_C_INITIALIZE_ARGS provides the optional arguments to
+ * C_Initialize */
+typedef struct CK_C_INITIALIZE_ARGS {
+  CK_CREATEMUTEX CreateMutex;
+  CK_DESTROYMUTEX DestroyMutex;
+  CK_LOCKMUTEX LockMutex;
+  CK_UNLOCKMUTEX UnlockMutex;
+  CK_FLAGS flags;
+  CK_VOID_PTR pReserved;
+} CK_C_INITIALIZE_ARGS;
+
+/* flags: bit flags that provide capabilities of the slot
+ *      Bit Flag                           Mask       Meaning
+ */
+#define CKF_LIBRARY_CANT_CREATE_OS_THREADS 0x00000001
+#define CKF_OS_LOCKING_OK                  0x00000002
+
+typedef CK_C_INITIALIZE_ARGS CK_PTR CK_C_INITIALIZE_ARGS_PTR;
+
+
+/* additional flags for parameters to functions */
+
+/* CKF_DONT_BLOCK is for the function C_WaitForSlotEvent */
+#define CKF_DONT_BLOCK     1
+
+/* CK_RSA_PKCS_OAEP_MGF_TYPE is new for v2.10.
+ * CK_RSA_PKCS_OAEP_MGF_TYPE  is used to indicate the Message
+ * Generation Function (MGF) applied to a message block when
+ * formatting a message block for the PKCS #1 OAEP encryption
+ * scheme. */
+typedef CK_ULONG CK_RSA_PKCS_MGF_TYPE;
+
+typedef CK_RSA_PKCS_MGF_TYPE CK_PTR CK_RSA_PKCS_MGF_TYPE_PTR;
+
+/* The following MGFs are defined */
+/* CKG_MGF1_SHA256, CKG_MGF1_SHA384, and CKG_MGF1_SHA512
+ * are new for v2.20 */
+#define CKG_MGF1_SHA1         0x00000001
+#define CKG_MGF1_SHA256       0x00000002
+#define CKG_MGF1_SHA384       0x00000003
+#define CKG_MGF1_SHA512       0x00000004
+/* SHA-224 is new for PKCS #11 v2.20 amendment 3 */
+#define CKG_MGF1_SHA224       0x00000005
+
+/* CK_RSA_PKCS_OAEP_SOURCE_TYPE is new for v2.10.
+ * CK_RSA_PKCS_OAEP_SOURCE_TYPE  is used to indicate the source
+ * of the encoding parameter when formatting a message block
+ * for the PKCS #1 OAEP encryption scheme. */
+typedef CK_ULONG CK_RSA_PKCS_OAEP_SOURCE_TYPE;
+
+typedef CK_RSA_PKCS_OAEP_SOURCE_TYPE CK_PTR CK_RSA_PKCS_OAEP_SOURCE_TYPE_PTR;
+
+/* The following encoding parameter sources are defined */
+#define CKZ_DATA_SPECIFIED    0x00000001
+
+/* CK_RSA_PKCS_OAEP_PARAMS is new for v2.10.
+ * CK_RSA_PKCS_OAEP_PARAMS provides the parameters to the
+ * CKM_RSA_PKCS_OAEP mechanism. */
+typedef struct CK_RSA_PKCS_OAEP_PARAMS {
+        CK_MECHANISM_TYPE hashAlg;
+        CK_RSA_PKCS_MGF_TYPE mgf;
+        CK_RSA_PKCS_OAEP_SOURCE_TYPE source;
+        CK_VOID_PTR pSourceData;
+        CK_ULONG ulSourceDataLen;
+} CK_RSA_PKCS_OAEP_PARAMS;
+
+typedef CK_RSA_PKCS_OAEP_PARAMS CK_PTR CK_RSA_PKCS_OAEP_PARAMS_PTR;
+
+/* CK_RSA_PKCS_PSS_PARAMS is new for v2.11.
+ * CK_RSA_PKCS_PSS_PARAMS provides the parameters to the
+ * CKM_RSA_PKCS_PSS mechanism(s). */
+typedef struct CK_RSA_PKCS_PSS_PARAMS {
+        CK_MECHANISM_TYPE    hashAlg;
+        CK_RSA_PKCS_MGF_TYPE mgf;
+        CK_ULONG             sLen;
+} CK_RSA_PKCS_PSS_PARAMS;
+
+typedef CK_RSA_PKCS_PSS_PARAMS CK_PTR CK_RSA_PKCS_PSS_PARAMS_PTR;
+
+/* CK_EC_KDF_TYPE is new for v2.11. */
+typedef CK_ULONG CK_EC_KDF_TYPE;
+
+/* The following EC Key Derivation Functions are defined */
+#define CKD_NULL                 0x00000001
+#define CKD_SHA1_KDF             0x00000002
+
+/* CK_ECDH1_DERIVE_PARAMS is new for v2.11.
+ * CK_ECDH1_DERIVE_PARAMS provides the parameters to the
+ * CKM_ECDH1_DERIVE and CKM_ECDH1_COFACTOR_DERIVE mechanisms,
+ * where each party contributes one key pair.
+ */
+typedef struct CK_ECDH1_DERIVE_PARAMS {
+  CK_EC_KDF_TYPE kdf;
+  CK_ULONG ulSharedDataLen;
+  CK_BYTE_PTR pSharedData;
+  CK_ULONG ulPublicDataLen;
+  CK_BYTE_PTR pPublicData;
+} CK_ECDH1_DERIVE_PARAMS;
+
+typedef CK_ECDH1_DERIVE_PARAMS CK_PTR CK_ECDH1_DERIVE_PARAMS_PTR;
+
+
+/* CK_ECDH2_DERIVE_PARAMS is new for v2.11.
+ * CK_ECDH2_DERIVE_PARAMS provides the parameters to the
+ * CKM_ECMQV_DERIVE mechanism, where each party contributes two key pairs. */
+typedef struct CK_ECDH2_DERIVE_PARAMS {
+  CK_EC_KDF_TYPE kdf;
+  CK_ULONG ulSharedDataLen;
+  CK_BYTE_PTR pSharedData;
+  CK_ULONG ulPublicDataLen;
+  CK_BYTE_PTR pPublicData;
+  CK_ULONG ulPrivateDataLen;
+  CK_OBJECT_HANDLE hPrivateData;
+  CK_ULONG ulPublicDataLen2;
+  CK_BYTE_PTR pPublicData2;
+} CK_ECDH2_DERIVE_PARAMS;
+
+typedef CK_ECDH2_DERIVE_PARAMS CK_PTR CK_ECDH2_DERIVE_PARAMS_PTR;
+
+typedef struct CK_ECMQV_DERIVE_PARAMS {
+  CK_EC_KDF_TYPE kdf;
+  CK_ULONG ulSharedDataLen;
+  CK_BYTE_PTR pSharedData;
+  CK_ULONG ulPublicDataLen;
+  CK_BYTE_PTR pPublicData;
+  CK_ULONG ulPrivateDataLen;
+  CK_OBJECT_HANDLE hPrivateData;
+  CK_ULONG ulPublicDataLen2;
+  CK_BYTE_PTR pPublicData2;
+  CK_OBJECT_HANDLE publicKey;
+} CK_ECMQV_DERIVE_PARAMS;
+
+typedef CK_ECMQV_DERIVE_PARAMS CK_PTR CK_ECMQV_DERIVE_PARAMS_PTR;
+
+/* Typedefs and defines for the CKM_X9_42_DH_KEY_PAIR_GEN and the
+ * CKM_X9_42_DH_PARAMETER_GEN mechanisms (new for PKCS #11 v2.11) */
+typedef CK_ULONG CK_X9_42_DH_KDF_TYPE;
+typedef CK_X9_42_DH_KDF_TYPE CK_PTR CK_X9_42_DH_KDF_TYPE_PTR;
+
+/* The following X9.42 DH key derivation functions are defined
+   (besides CKD_NULL already defined : */
+#define CKD_SHA1_KDF_ASN1        0x00000003
+#define CKD_SHA1_KDF_CONCATENATE 0x00000004
+
+/* CK_X9_42_DH1_DERIVE_PARAMS is new for v2.11.
+ * CK_X9_42_DH1_DERIVE_PARAMS provides the parameters to the
+ * CKM_X9_42_DH_DERIVE key derivation mechanism, where each party
+ * contributes one key pair */
+typedef struct CK_X9_42_DH1_DERIVE_PARAMS {
+  CK_X9_42_DH_KDF_TYPE kdf;
+  CK_ULONG ulOtherInfoLen;
+  CK_BYTE_PTR pOtherInfo;
+  CK_ULONG ulPublicDataLen;
+  CK_BYTE_PTR pPublicData;
+} CK_X9_42_DH1_DERIVE_PARAMS;
+
+typedef struct CK_X9_42_DH1_DERIVE_PARAMS CK_PTR CK_X9_42_DH1_DERIVE_PARAMS_PTR;
+
+/* CK_X9_42_DH2_DERIVE_PARAMS is new for v2.11.
+ * CK_X9_42_DH2_DERIVE_PARAMS provides the parameters to the
+ * CKM_X9_42_DH_HYBRID_DERIVE and CKM_X9_42_MQV_DERIVE key derivation
+ * mechanisms, where each party contributes two key pairs */
+typedef struct CK_X9_42_DH2_DERIVE_PARAMS {
+  CK_X9_42_DH_KDF_TYPE kdf;
+  CK_ULONG ulOtherInfoLen;
+  CK_BYTE_PTR pOtherInfo;
+  CK_ULONG ulPublicDataLen;
+  CK_BYTE_PTR pPublicData;
+  CK_ULONG ulPrivateDataLen;
+  CK_OBJECT_HANDLE hPrivateData;
+  CK_ULONG ulPublicDataLen2;
+  CK_BYTE_PTR pPublicData2;
+} CK_X9_42_DH2_DERIVE_PARAMS;
+
+typedef CK_X9_42_DH2_DERIVE_PARAMS CK_PTR CK_X9_42_DH2_DERIVE_PARAMS_PTR;
+
+typedef struct CK_X9_42_MQV_DERIVE_PARAMS {
+  CK_X9_42_DH_KDF_TYPE kdf;
+  CK_ULONG ulOtherInfoLen;
+  CK_BYTE_PTR pOtherInfo;
+  CK_ULONG ulPublicDataLen;
+  CK_BYTE_PTR pPublicData;
+  CK_ULONG ulPrivateDataLen;
+  CK_OBJECT_HANDLE hPrivateData;
+  CK_ULONG ulPublicDataLen2;
+  CK_BYTE_PTR pPublicData2;
+  CK_OBJECT_HANDLE publicKey;
+} CK_X9_42_MQV_DERIVE_PARAMS;
+
+typedef CK_X9_42_MQV_DERIVE_PARAMS CK_PTR CK_X9_42_MQV_DERIVE_PARAMS_PTR;
+
+/* CK_KEA_DERIVE_PARAMS provides the parameters to the
+ * CKM_KEA_DERIVE mechanism */
+/* CK_KEA_DERIVE_PARAMS is new for v2.0 */
+typedef struct CK_KEA_DERIVE_PARAMS {
+  CK_BBOOL      isSender;
+  CK_ULONG      ulRandomLen;
+  CK_BYTE_PTR   pRandomA;
+  CK_BYTE_PTR   pRandomB;
+  CK_ULONG      ulPublicDataLen;
+  CK_BYTE_PTR   pPublicData;
+} CK_KEA_DERIVE_PARAMS;
+
+typedef CK_KEA_DERIVE_PARAMS CK_PTR CK_KEA_DERIVE_PARAMS_PTR;
+
+
+/* CK_RC2_PARAMS provides the parameters to the CKM_RC2_ECB and
+ * CKM_RC2_MAC mechanisms.  An instance of CK_RC2_PARAMS just
+ * holds the effective keysize */
+typedef CK_ULONG          CK_RC2_PARAMS;
+
+typedef CK_RC2_PARAMS CK_PTR CK_RC2_PARAMS_PTR;
+
+
+/* CK_RC2_CBC_PARAMS provides the parameters to the CKM_RC2_CBC
+ * mechanism */
+typedef struct CK_RC2_CBC_PARAMS {
+  /* ulEffectiveBits was changed from CK_USHORT to CK_ULONG for
+   * v2.0 */
+  CK_ULONG      ulEffectiveBits;  /* effective bits (1-1024) */
+
+  CK_BYTE       iv[8];            /* IV for CBC mode */
+} CK_RC2_CBC_PARAMS;
+
+typedef CK_RC2_CBC_PARAMS CK_PTR CK_RC2_CBC_PARAMS_PTR;
+
+
+/* CK_RC2_MAC_GENERAL_PARAMS provides the parameters for the
+ * CKM_RC2_MAC_GENERAL mechanism */
+/* CK_RC2_MAC_GENERAL_PARAMS is new for v2.0 */
+typedef struct CK_RC2_MAC_GENERAL_PARAMS {
+  CK_ULONG      ulEffectiveBits;  /* effective bits (1-1024) */
+  CK_ULONG      ulMacLength;      /* Length of MAC in bytes */
+} CK_RC2_MAC_GENERAL_PARAMS;
+
+typedef CK_RC2_MAC_GENERAL_PARAMS CK_PTR \
+  CK_RC2_MAC_GENERAL_PARAMS_PTR;
+
+
+/* CK_RC5_PARAMS provides the parameters to the CKM_RC5_ECB and
+ * CKM_RC5_MAC mechanisms */
+/* CK_RC5_PARAMS is new for v2.0 */
+typedef struct CK_RC5_PARAMS {
+  CK_ULONG      ulWordsize;  /* wordsize in bits */
+  CK_ULONG      ulRounds;    /* number of rounds */
+} CK_RC5_PARAMS;
+
+typedef CK_RC5_PARAMS CK_PTR CK_RC5_PARAMS_PTR;
+
+
+/* CK_RC5_CBC_PARAMS provides the parameters to the CKM_RC5_CBC
+ * mechanism */
+/* CK_RC5_CBC_PARAMS is new for v2.0 */
+typedef struct CK_RC5_CBC_PARAMS {
+  CK_ULONG      ulWordsize;  /* wordsize in bits */
+  CK_ULONG      ulRounds;    /* number of rounds */
+  CK_BYTE_PTR   pIv;         /* pointer to IV */
+  CK_ULONG      ulIvLen;     /* length of IV in bytes */
+} CK_RC5_CBC_PARAMS;
+
+typedef CK_RC5_CBC_PARAMS CK_PTR CK_RC5_CBC_PARAMS_PTR;
+
+
+/* CK_RC5_MAC_GENERAL_PARAMS provides the parameters for the
+ * CKM_RC5_MAC_GENERAL mechanism */
+/* CK_RC5_MAC_GENERAL_PARAMS is new for v2.0 */
+typedef struct CK_RC5_MAC_GENERAL_PARAMS {
+  CK_ULONG      ulWordsize;   /* wordsize in bits */
+  CK_ULONG      ulRounds;     /* number of rounds */
+  CK_ULONG      ulMacLength;  /* Length of MAC in bytes */
+} CK_RC5_MAC_GENERAL_PARAMS;
+
+typedef CK_RC5_MAC_GENERAL_PARAMS CK_PTR \
+  CK_RC5_MAC_GENERAL_PARAMS_PTR;
+
+
+/* CK_MAC_GENERAL_PARAMS provides the parameters to most block
+ * ciphers' MAC_GENERAL mechanisms.  Its value is the length of
+ * the MAC */
+/* CK_MAC_GENERAL_PARAMS is new for v2.0 */
+typedef CK_ULONG          CK_MAC_GENERAL_PARAMS;
+
+typedef CK_MAC_GENERAL_PARAMS CK_PTR CK_MAC_GENERAL_PARAMS_PTR;
+
+/* CK_DES/AES_ECB/CBC_ENCRYPT_DATA_PARAMS are new for v2.20 */
+typedef struct CK_DES_CBC_ENCRYPT_DATA_PARAMS {
+  CK_BYTE      iv[8];
+  CK_BYTE_PTR  pData;
+  CK_ULONG     length;
+} CK_DES_CBC_ENCRYPT_DATA_PARAMS;
+
+typedef CK_DES_CBC_ENCRYPT_DATA_PARAMS CK_PTR CK_DES_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+typedef struct CK_AES_CBC_ENCRYPT_DATA_PARAMS {
+  CK_BYTE      iv[16];
+  CK_BYTE_PTR  pData;
+  CK_ULONG     length;
+} CK_AES_CBC_ENCRYPT_DATA_PARAMS;
+
+typedef CK_AES_CBC_ENCRYPT_DATA_PARAMS CK_PTR CK_AES_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+/* CK_SKIPJACK_PRIVATE_WRAP_PARAMS provides the parameters to the
+ * CKM_SKIPJACK_PRIVATE_WRAP mechanism */
+/* CK_SKIPJACK_PRIVATE_WRAP_PARAMS is new for v2.0 */
+typedef struct CK_SKIPJACK_PRIVATE_WRAP_PARAMS {
+  CK_ULONG      ulPasswordLen;
+  CK_BYTE_PTR   pPassword;
+  CK_ULONG      ulPublicDataLen;
+  CK_BYTE_PTR   pPublicData;
+  CK_ULONG      ulPAndGLen;
+  CK_ULONG      ulQLen;
+  CK_ULONG      ulRandomLen;
+  CK_BYTE_PTR   pRandomA;
+  CK_BYTE_PTR   pPrimeP;
+  CK_BYTE_PTR   pBaseG;
+  CK_BYTE_PTR   pSubprimeQ;
+} CK_SKIPJACK_PRIVATE_WRAP_PARAMS;
+
+typedef CK_SKIPJACK_PRIVATE_WRAP_PARAMS CK_PTR \
+  CK_SKIPJACK_PRIVATE_WRAP_PTR;
+
+
+/* CK_SKIPJACK_RELAYX_PARAMS provides the parameters to the
+ * CKM_SKIPJACK_RELAYX mechanism */
+/* CK_SKIPJACK_RELAYX_PARAMS is new for v2.0 */
+typedef struct CK_SKIPJACK_RELAYX_PARAMS {
+  CK_ULONG      ulOldWrappedXLen;
+  CK_BYTE_PTR   pOldWrappedX;
+  CK_ULONG      ulOldPasswordLen;
+  CK_BYTE_PTR   pOldPassword;
+  CK_ULONG      ulOldPublicDataLen;
+  CK_BYTE_PTR   pOldPublicData;
+  CK_ULONG      ulOldRandomLen;
+  CK_BYTE_PTR   pOldRandomA;
+  CK_ULONG      ulNewPasswordLen;
+  CK_BYTE_PTR   pNewPassword;
+  CK_ULONG      ulNewPublicDataLen;
+  CK_BYTE_PTR   pNewPublicData;
+  CK_ULONG      ulNewRandomLen;
+  CK_BYTE_PTR   pNewRandomA;
+} CK_SKIPJACK_RELAYX_PARAMS;
+
+typedef CK_SKIPJACK_RELAYX_PARAMS CK_PTR \
+  CK_SKIPJACK_RELAYX_PARAMS_PTR;
+
+
+typedef struct CK_PBE_PARAMS {
+  CK_BYTE_PTR      pInitVector;
+  CK_UTF8CHAR_PTR  pPassword;
+  CK_ULONG         ulPasswordLen;
+  CK_BYTE_PTR      pSalt;
+  CK_ULONG         ulSaltLen;
+  CK_ULONG         ulIteration;
+} CK_PBE_PARAMS;
+
+typedef CK_PBE_PARAMS CK_PTR CK_PBE_PARAMS_PTR;
+
+
+/* CK_KEY_WRAP_SET_OAEP_PARAMS provides the parameters to the
+ * CKM_KEY_WRAP_SET_OAEP mechanism */
+/* CK_KEY_WRAP_SET_OAEP_PARAMS is new for v2.0 */
+typedef struct CK_KEY_WRAP_SET_OAEP_PARAMS {
+  CK_BYTE       bBC;     /* block contents byte */
+  CK_BYTE_PTR   pX;      /* extra data */
+  CK_ULONG      ulXLen;  /* length of extra data in bytes */
+} CK_KEY_WRAP_SET_OAEP_PARAMS;
+
+typedef CK_KEY_WRAP_SET_OAEP_PARAMS CK_PTR \
+  CK_KEY_WRAP_SET_OAEP_PARAMS_PTR;
+
+
+typedef struct CK_SSL3_RANDOM_DATA {
+  CK_BYTE_PTR  pClientRandom;
+  CK_ULONG     ulClientRandomLen;
+  CK_BYTE_PTR  pServerRandom;
+  CK_ULONG     ulServerRandomLen;
+} CK_SSL3_RANDOM_DATA;
+
+
+typedef struct CK_SSL3_MASTER_KEY_DERIVE_PARAMS {
+  CK_SSL3_RANDOM_DATA RandomInfo;
+  CK_VERSION_PTR pVersion;
+} CK_SSL3_MASTER_KEY_DERIVE_PARAMS;
+
+typedef struct CK_SSL3_MASTER_KEY_DERIVE_PARAMS CK_PTR \
+  CK_SSL3_MASTER_KEY_DERIVE_PARAMS_PTR;
+
+
+typedef struct CK_SSL3_KEY_MAT_OUT {
+  CK_OBJECT_HANDLE hClientMacSecret;
+  CK_OBJECT_HANDLE hServerMacSecret;
+  CK_OBJECT_HANDLE hClientKey;
+  CK_OBJECT_HANDLE hServerKey;
+  CK_BYTE_PTR      pIVClient;
+  CK_BYTE_PTR      pIVServer;
+} CK_SSL3_KEY_MAT_OUT;
+
+typedef CK_SSL3_KEY_MAT_OUT CK_PTR CK_SSL3_KEY_MAT_OUT_PTR;
+
+
+typedef struct CK_SSL3_KEY_MAT_PARAMS {
+  CK_ULONG                ulMacSizeInBits;
+  CK_ULONG                ulKeySizeInBits;
+  CK_ULONG                ulIVSizeInBits;
+  CK_BBOOL                bIsExport;
+  CK_SSL3_RANDOM_DATA     RandomInfo;
+  CK_SSL3_KEY_MAT_OUT_PTR pReturnedKeyMaterial;
+} CK_SSL3_KEY_MAT_PARAMS;
+
+typedef CK_SSL3_KEY_MAT_PARAMS CK_PTR CK_SSL3_KEY_MAT_PARAMS_PTR;
+
+/* CK_TLS_PRF_PARAMS is new for version 2.20 */
+typedef struct CK_TLS_PRF_PARAMS {
+  CK_BYTE_PTR  pSeed;
+  CK_ULONG     ulSeedLen;
+  CK_BYTE_PTR  pLabel;
+  CK_ULONG     ulLabelLen;
+  CK_BYTE_PTR  pOutput;
+  CK_ULONG_PTR pulOutputLen;
+} CK_TLS_PRF_PARAMS;
+
+typedef CK_TLS_PRF_PARAMS CK_PTR CK_TLS_PRF_PARAMS_PTR;
+
+/* WTLS is new for version 2.20 */
+typedef struct CK_WTLS_RANDOM_DATA {
+  CK_BYTE_PTR pClientRandom;
+  CK_ULONG    ulClientRandomLen;
+  CK_BYTE_PTR pServerRandom;
+  CK_ULONG    ulServerRandomLen;
+} CK_WTLS_RANDOM_DATA;
+
+typedef CK_WTLS_RANDOM_DATA CK_PTR CK_WTLS_RANDOM_DATA_PTR;
+
+typedef struct CK_WTLS_MASTER_KEY_DERIVE_PARAMS {
+  CK_MECHANISM_TYPE   DigestMechanism;
+  CK_WTLS_RANDOM_DATA RandomInfo;
+  CK_BYTE_PTR         pVersion;
+} CK_WTLS_MASTER_KEY_DERIVE_PARAMS;
+
+typedef CK_WTLS_MASTER_KEY_DERIVE_PARAMS CK_PTR \
+  CK_WTLS_MASTER_KEY_DERIVE_PARAMS_PTR;
+
+typedef struct CK_WTLS_PRF_PARAMS {
+  CK_MECHANISM_TYPE DigestMechanism;
+  CK_BYTE_PTR       pSeed;
+  CK_ULONG          ulSeedLen;
+  CK_BYTE_PTR       pLabel;
+  CK_ULONG          ulLabelLen;
+  CK_BYTE_PTR       pOutput;
+  CK_ULONG_PTR      pulOutputLen;
+} CK_WTLS_PRF_PARAMS;
+
+typedef CK_WTLS_PRF_PARAMS CK_PTR CK_WTLS_PRF_PARAMS_PTR;
+
+typedef struct CK_WTLS_KEY_MAT_OUT {
+  CK_OBJECT_HANDLE hMacSecret;
+  CK_OBJECT_HANDLE hKey;
+  CK_BYTE_PTR      pIV;
+} CK_WTLS_KEY_MAT_OUT;
+
+typedef CK_WTLS_KEY_MAT_OUT CK_PTR CK_WTLS_KEY_MAT_OUT_PTR;
+
+typedef struct CK_WTLS_KEY_MAT_PARAMS {
+  CK_MECHANISM_TYPE       DigestMechanism;
+  CK_ULONG                ulMacSizeInBits;
+  CK_ULONG                ulKeySizeInBits;
+  CK_ULONG                ulIVSizeInBits;
+  CK_ULONG                ulSequenceNumber;
+  CK_BBOOL                bIsExport;
+  CK_WTLS_RANDOM_DATA     RandomInfo;
+  CK_WTLS_KEY_MAT_OUT_PTR pReturnedKeyMaterial;
+} CK_WTLS_KEY_MAT_PARAMS;
+
+typedef CK_WTLS_KEY_MAT_PARAMS CK_PTR CK_WTLS_KEY_MAT_PARAMS_PTR;
+
+/* CMS is new for version 2.20 */
+typedef struct CK_CMS_SIG_PARAMS {
+  CK_OBJECT_HANDLE      certificateHandle;
+  CK_MECHANISM_PTR      pSigningMechanism;
+  CK_MECHANISM_PTR      pDigestMechanism;
+  CK_UTF8CHAR_PTR       pContentType;
+  CK_BYTE_PTR           pRequestedAttributes;
+  CK_ULONG              ulRequestedAttributesLen;
+  CK_BYTE_PTR           pRequiredAttributes;
+  CK_ULONG              ulRequiredAttributesLen;
+} CK_CMS_SIG_PARAMS;
+
+typedef CK_CMS_SIG_PARAMS CK_PTR CK_CMS_SIG_PARAMS_PTR;
+
+typedef struct CK_KEY_DERIVATION_STRING_DATA {
+  CK_BYTE_PTR pData;
+  CK_ULONG    ulLen;
+} CK_KEY_DERIVATION_STRING_DATA;
+
+typedef CK_KEY_DERIVATION_STRING_DATA CK_PTR \
+  CK_KEY_DERIVATION_STRING_DATA_PTR;
+
+
+/* The CK_EXTRACT_PARAMS is used for the
+ * CKM_EXTRACT_KEY_FROM_KEY mechanism.  It specifies which bit
+ * of the base key should be used as the first bit of the
+ * derived key */
+/* CK_EXTRACT_PARAMS is new for v2.0 */
+typedef CK_ULONG CK_EXTRACT_PARAMS;
+
+typedef CK_EXTRACT_PARAMS CK_PTR CK_EXTRACT_PARAMS_PTR;
+
+/* CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE is new for v2.10.
+ * CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE is used to
+ * indicate the Pseudo-Random Function (PRF) used to generate
+ * key bits using PKCS #5 PBKDF2. */
+typedef CK_ULONG CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE;
+
+typedef CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE CK_PTR CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE_PTR;
+
+/* The following PRFs are defined in PKCS #5 v2.0. */
+#define CKP_PKCS5_PBKD2_HMAC_SHA1 0x00000001
+
+
+/* CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE is new for v2.10.
+ * CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE is used to indicate the
+ * source of the salt value when deriving a key using PKCS #5
+ * PBKDF2. */
+typedef CK_ULONG CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE;
+
+typedef CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE CK_PTR CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE_PTR;
+
+/* The following salt value sources are defined in PKCS #5 v2.0. */
+#define CKZ_SALT_SPECIFIED        0x00000001
+
+/* CK_PKCS5_PBKD2_PARAMS is new for v2.10.
+ * CK_PKCS5_PBKD2_PARAMS is a structure that provides the
+ * parameters to the CKM_PKCS5_PBKD2 mechanism. */
+typedef struct CK_PKCS5_PBKD2_PARAMS {
+        CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE           saltSource;
+        CK_VOID_PTR                                pSaltSourceData;
+        CK_ULONG                                   ulSaltSourceDataLen;
+        CK_ULONG                                   iterations;
+        CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE prf;
+        CK_VOID_PTR                                pPrfData;
+        CK_ULONG                                   ulPrfDataLen;
+        CK_UTF8CHAR_PTR                            pPassword;
+        CK_ULONG_PTR                               ulPasswordLen;
+} CK_PKCS5_PBKD2_PARAMS;
+
+typedef CK_PKCS5_PBKD2_PARAMS CK_PTR CK_PKCS5_PBKD2_PARAMS_PTR;
+
+/* All CK_OTP structs are new for PKCS #11 v2.20 amendment 3 */
+
+typedef CK_ULONG CK_OTP_PARAM_TYPE;
+typedef CK_OTP_PARAM_TYPE CK_PARAM_TYPE; /* B/w compatibility */
+
+typedef struct CK_OTP_PARAM {
+    CK_OTP_PARAM_TYPE type;
+    CK_VOID_PTR pValue;
+    CK_ULONG ulValueLen;
+} CK_OTP_PARAM;
+
+typedef CK_OTP_PARAM CK_PTR CK_OTP_PARAM_PTR;
+
+typedef struct CK_OTP_PARAMS {
+    CK_OTP_PARAM_PTR pParams;
+    CK_ULONG ulCount;
+} CK_OTP_PARAMS;
+
+typedef CK_OTP_PARAMS CK_PTR CK_OTP_PARAMS_PTR;
+
+typedef struct CK_OTP_SIGNATURE_INFO {
+    CK_OTP_PARAM_PTR pParams;
+    CK_ULONG ulCount;
+} CK_OTP_SIGNATURE_INFO;
+
+typedef CK_OTP_SIGNATURE_INFO CK_PTR CK_OTP_SIGNATURE_INFO_PTR;
+
+/* The following OTP-related defines are new for PKCS #11 v2.20 amendment 1 */
+#define CK_OTP_VALUE          0
+#define CK_OTP_PIN            1
+#define CK_OTP_CHALLENGE      2
+#define CK_OTP_TIME           3
+#define CK_OTP_COUNTER        4
+#define CK_OTP_FLAGS          5
+#define CK_OTP_OUTPUT_LENGTH  6
+#define CK_OTP_OUTPUT_FORMAT  7
+
+/* The following OTP-related defines are new for PKCS #11 v2.20 amendment 1 */
+#define CKF_NEXT_OTP          0x00000001
+#define CKF_EXCLUDE_TIME      0x00000002
+#define CKF_EXCLUDE_COUNTER   0x00000004
+#define CKF_EXCLUDE_CHALLENGE 0x00000008
+#define CKF_EXCLUDE_PIN       0x00000010
+#define CKF_USER_FRIENDLY_OTP 0x00000020
+
+/* CK_KIP_PARAMS is new for PKCS #11 v2.20 amendment 2 */
+typedef struct CK_KIP_PARAMS {
+    CK_MECHANISM_PTR  pMechanism;
+    CK_OBJECT_HANDLE  hKey;
+    CK_BYTE_PTR       pSeed;
+    CK_ULONG          ulSeedLen;
+} CK_KIP_PARAMS;
+
+typedef CK_KIP_PARAMS CK_PTR CK_KIP_PARAMS_PTR;
+
+/* CK_AES_CTR_PARAMS is new for PKCS #11 v2.20 amendment 3 */
+typedef struct CK_AES_CTR_PARAMS {
+    CK_ULONG ulCounterBits;
+    CK_BYTE cb[16];
+} CK_AES_CTR_PARAMS;
+
+typedef CK_AES_CTR_PARAMS CK_PTR CK_AES_CTR_PARAMS_PTR;
+
+/* CK_CAMELLIA_CTR_PARAMS is new for PKCS #11 v2.20 amendment 3 */
+typedef struct CK_CAMELLIA_CTR_PARAMS {
+    CK_ULONG ulCounterBits;
+    CK_BYTE cb[16];
+} CK_CAMELLIA_CTR_PARAMS;
+
+typedef CK_CAMELLIA_CTR_PARAMS CK_PTR CK_CAMELLIA_CTR_PARAMS_PTR;
+
+/* CK_CAMELLIA_CBC_ENCRYPT_DATA_PARAMS is new for PKCS #11 v2.20 amendment 3 */
+typedef struct CK_CAMELLIA_CBC_ENCRYPT_DATA_PARAMS {
+    CK_BYTE      iv[16];
+    CK_BYTE_PTR  pData;
+    CK_ULONG     length;
+} CK_CAMELLIA_CBC_ENCRYPT_DATA_PARAMS;
+
+typedef CK_CAMELLIA_CBC_ENCRYPT_DATA_PARAMS CK_PTR CK_CAMELLIA_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+/* CK_ARIA_CBC_ENCRYPT_DATA_PARAMS is new for PKCS #11 v2.20 amendment 3 */
+typedef struct CK_ARIA_CBC_ENCRYPT_DATA_PARAMS {
+    CK_BYTE      iv[16];
+    CK_BYTE_PTR  pData;
+    CK_ULONG     length;
+} CK_ARIA_CBC_ENCRYPT_DATA_PARAMS;
+
+typedef CK_ARIA_CBC_ENCRYPT_DATA_PARAMS CK_PTR CK_ARIA_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+#endif
diff --git a/lib/isc/md5.c b/lib/isc/md5.c
index 7c6419b..2e3cf9a 100644
--- a/lib/isc/md5.c
+++ b/lib/isc/md5.c
@@ -41,6 +41,12 @@
 #include <isc/platform.h>
 #include <isc/string.h>
 #include <isc/types.h>
+
+#if PKCS11CRYPTO
+#include <pk11/internal.h>
+#include <pk11/pk11.h>
+#endif
+
 #include <isc/util.h>
 
 #ifdef ISC_PLATFORM_OPENSSLHASH
@@ -65,6 +71,50 @@ isc_md5_final(isc_md5_t *ctx, unsigned char *digest) {
 	EVP_DigestFinal(ctx, digest, NULL);
 }
 
+#elif PKCS11CRYPTO
+
+void
+isc_md5_init(isc_md5_t *ctx) {
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_MD5, NULL, 0 };
+
+	RUNTIME_CHECK(pk11_get_session(ctx, OP_DIGEST, ISC_TRUE, ISC_FALSE,
+				       ISC_FALSE, NULL, 0) == ISC_R_SUCCESS);
+	PK11_FATALCHECK(pkcs_C_DigestInit, (ctx->session, &mech));
+}
+
+void
+isc_md5_invalidate(isc_md5_t *ctx) {
+	CK_BYTE garbage[ISC_MD5_DIGESTLENGTH];
+	CK_ULONG len = ISC_MD5_DIGESTLENGTH;
+
+	if (ctx->handle == NULL)
+		return;
+	(void) pkcs_C_DigestFinal(ctx->session, garbage, &len);
+	memset(garbage, 0, sizeof(garbage));
+	pk11_return_session(ctx);
+}
+
+void
+isc_md5_update(isc_md5_t *ctx, const unsigned char *buf, unsigned int len) {
+	CK_RV rv;
+	CK_BYTE_PTR pPart;
+
+	DE_CONST(buf, pPart);
+	PK11_FATALCHECK(pkcs_C_DigestUpdate,
+			(ctx->session, pPart, (CK_ULONG) len));
+}
+
+void
+isc_md5_final(isc_md5_t *ctx, unsigned char *digest) {
+	CK_RV rv;
+	CK_ULONG len = ISC_MD5_DIGESTLENGTH;
+
+	PK11_FATALCHECK(pkcs_C_DigestFinal,
+			(ctx->session, (CK_BYTE_PTR) digest, &len));
+	pk11_return_session(ctx);
+}
+
 #else
 
 static void
diff --git a/lib/isc/pk11.c b/lib/isc/pk11.c
new file mode 100644
index 0000000..015bff2
--- /dev/null
+++ b/lib/isc/pk11.c
@@ -0,0 +1,1327 @@
+/*
+ * Copyright (C) 2014  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Portions copyright (c) 2008 Nominet UK.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This product includes software developed by the OpenSSL Project for
+ * use in the OpenSSL Toolkit (http://www.openssl.org/).
+ *
+ * This project also referenced hw_pkcs11-0.9.7b.patch written by
+ * Afchine Madjlessi.
+ */
+/*
+ * ====================================================================
+ * Copyright (c) 2000-2001 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+/* $Id$ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/log.h>
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/platform.h>
+#include <isc/stdio.h>
+#include <isc/thread.h>
+#include <isc/util.h>
+
+#include <dst/result.h>
+
+#include <pk11/pk11.h>
+#include <pk11/internal.h>
+#include <pk11/result.h>
+
+#include <pkcs11/cryptoki.h>
+#include <pkcs11/pkcs11.h>
+
+#define PINLEN	32
+
+#ifndef PK11_NO_LOGERR
+#define PK11_NO_LOGERR 1
+#endif
+
+static isc_once_t once = ISC_ONCE_INIT;
+static isc_mem_t *pk11_mctx = NULL;
+static isc_int32_t allocsize = 0;
+static isc_boolean_t initialized = ISC_FALSE;
+
+typedef struct pk11_session pk11_session_t;
+typedef struct pk11_token pk11_token_t;
+typedef ISC_LIST(pk11_session_t) pk11_sessionlist_t;
+
+struct pk11_session {
+	unsigned int		magic;
+	CK_SESSION_HANDLE	session;
+	ISC_LINK(pk11_session_t) link;
+	pk11_token_t		*token;
+};
+
+struct pk11_token {
+	unsigned int		magic;
+	unsigned int		operations;
+	ISC_LINK(pk11_token_t)	link;
+	CK_SLOT_ID		slotid;
+	pk11_sessionlist_t	sessions;
+	isc_boolean_t		logged;
+	char			name[32];
+	char			manuf[32];
+	char			model[16];
+	char			serial[16];
+	char			pin[PINLEN];
+};
+static ISC_LIST(pk11_token_t) tokens;
+
+static pk11_token_t *rand_token;
+static pk11_token_t *best_rsa_token;
+static pk11_token_t *best_dsa_token;
+static pk11_token_t *best_dh_token;
+static pk11_token_t *digest_token;
+static pk11_token_t *best_ec_token;
+static pk11_token_t *best_gost_token;
+static pk11_token_t *aes_token;
+
+static isc_result_t free_all_sessions(void);
+static isc_result_t free_session_list(pk11_sessionlist_t *slist);
+static isc_result_t setup_session(pk11_session_t *sp,
+				  pk11_token_t *token,
+				  isc_boolean_t rw);
+static void choose_slots(void);
+static isc_result_t token_login(pk11_session_t *sp);
+static char *percent_decode(char *x, size_t *len);
+static isc_boolean_t pk11strcmp(const char *x, size_t lenx,
+				const char *y, size_t leny);
+static CK_ATTRIBUTE *push_attribute(pk11_object_t *obj,
+				    isc_mem_t *mctx,
+				    size_t len);
+
+static isc_mutex_t alloclock;
+static isc_mutex_t sessionlock;
+
+static pk11_sessionlist_t actives;
+
+static CK_C_INITIALIZE_ARGS pk11_init_args = {
+	NULL_PTR,               /* CreateMutex */
+	NULL_PTR,               /* DestroyMutex */
+	NULL_PTR,               /* LockMutex */
+	NULL_PTR,               /* UnlockMutex */
+	CKF_OS_LOCKING_OK,      /* flags */
+	NULL_PTR,               /* pReserved */
+};
+
+#ifndef PK11_LIB_LOCATION
+#define PK11_LIB_LOCATION	"unknown_provider"
+#endif
+
+#ifndef WIN32
+static const char *lib_name = PK11_LIB_LOCATION;
+#else
+static const char *lib_name = PK11_LIB_LOCATION ".dll";
+#endif
+
+void
+pk11_set_lib_name(const char *name) {
+	lib_name = name;
+}
+
+const char *
+pk11_get_lib_name(void) {
+	return (lib_name);
+}
+
+static void
+initialize(void) {
+	char *pk11_provider;
+
+	RUNTIME_CHECK(isc_mutex_init(&alloclock) == ISC_R_SUCCESS);
+	RUNTIME_CHECK(isc_mutex_init(&sessionlock) == ISC_R_SUCCESS);
+
+	pk11_provider = getenv("PKCS11_PROVIDER");
+	if (pk11_provider != NULL)
+		lib_name = pk11_provider;
+}
+
+void *
+pk11_mem_get(size_t size) {
+	void *ptr;
+
+	LOCK(&alloclock);
+	if (pk11_mctx != NULL)
+		ptr = isc_mem_get(pk11_mctx, size);
+	else {
+		ptr = malloc(size);
+		if (ptr != NULL)
+			allocsize += (int)size;
+	}
+	UNLOCK(&alloclock);
+
+	if (ptr != NULL)
+		memset(ptr, 0, size);
+	return (ptr);
+}
+
+void
+pk11_mem_put(void *ptr, size_t size) {
+	if (ptr != NULL)
+		memset(ptr, 0, size);
+	LOCK(&alloclock);
+	if (pk11_mctx != NULL)
+		isc_mem_put(pk11_mctx, ptr, size);
+	else {
+		if (ptr != NULL)
+			allocsize -= (int)size;
+		free(ptr);
+	}
+	UNLOCK(&alloclock);
+}
+
+isc_result_t
+pk11_initialize(isc_mem_t *mctx, const char *engine) {
+	isc_result_t result;
+	CK_RV rv;
+
+	RUNTIME_CHECK(isc_once_do(&once, initialize) == ISC_R_SUCCESS);
+
+	LOCK(&alloclock);
+	if ((mctx != NULL) && (pk11_mctx == NULL) && (allocsize == 0))
+		isc_mem_attach(mctx, &pk11_mctx);
+	if (initialized) {
+		UNLOCK(&alloclock);
+		return (ISC_R_SUCCESS);
+	} else {
+		LOCK(&sessionlock);
+		initialized = ISC_TRUE;
+		UNLOCK(&alloclock);
+	}
+
+	ISC_LIST_INIT(tokens);
+	ISC_LIST_INIT(actives);
+
+	if (engine != NULL)
+		lib_name = engine;
+
+	/* Initialize the CRYPTOKI library */
+	rv = pkcs_C_Initialize((CK_VOID_PTR) &pk11_init_args);
+
+	if (rv == 0xfe) {
+		result = PK11_R_NOPROVIDER;
+		goto unlock;
+	}
+	if (rv != CKR_OK) {
+		result = PK11_R_INITFAILED;
+		goto unlock;
+	}
+
+	choose_slots();
+#ifdef PKCS11CRYPTO
+	if (rand_token == NULL) {
+		result = PK11_R_NORANDOMSERVICE;
+		goto unlock;
+	}
+	if (digest_token == NULL) {
+		result = PK11_R_NODIGESTSERVICE;
+		goto unlock;
+	}
+#if defined(ISC_PLATFORM_USESIT) && defined(AES_SIT)
+	if (aes_token == NULL) {
+		result = PK11_R_NOAESSERVICE;
+		goto unlock;
+	}
+#endif
+#endif /* PKCS11CRYPTO */
+	result = ISC_R_SUCCESS;
+ unlock:
+	UNLOCK(&sessionlock);
+	return (result);
+}
+
+isc_result_t
+pk11_finalize(void) {
+	pk11_token_t *token, *next;
+	isc_result_t ret;
+
+	ret = free_all_sessions();
+	(void) pkcs_C_Finalize(NULL_PTR);
+	token = ISC_LIST_HEAD(tokens);
+	while (token != NULL) {
+		next = ISC_LIST_NEXT(token, link);
+		ISC_LIST_UNLINK(tokens, token, link);
+		if (token == rand_token)
+			rand_token = NULL;
+		if (token == best_rsa_token)
+			best_rsa_token = NULL;
+		if (token == best_dsa_token)
+			best_dsa_token = NULL;
+		if (token == best_dh_token)
+			best_dh_token = NULL;
+		if (token == digest_token)
+			digest_token = NULL;
+		if (token == best_ec_token)
+			best_ec_token = NULL;
+		if (token == best_gost_token)
+			best_gost_token = NULL;
+		if (token == aes_token)
+			aes_token = NULL;
+		pk11_mem_put(token, sizeof(*token));
+		token = next;
+	}
+	if (pk11_mctx != NULL)
+		isc_mem_detach(&pk11_mctx);
+	initialized = ISC_FALSE;
+	return (ret);
+}
+
+isc_result_t
+pk11_rand_bytes(unsigned char *buf, int num) {
+	isc_result_t ret;
+	CK_RV rv;
+	pk11_context_t ctx;
+
+	ret = pk11_get_session(&ctx, OP_RAND, ISC_FALSE, ISC_FALSE,
+			       ISC_FALSE, NULL, 0);
+	if ((ret != ISC_R_SUCCESS) &&
+	    (ret != PK11_R_NODIGESTSERVICE) &&
+	    (ret != PK11_R_NOAESSERVICE))
+		return (ret);
+	RUNTIME_CHECK(ctx.session != CK_INVALID_HANDLE);
+	rv = pkcs_C_GenerateRandom(ctx.session,
+				   (CK_BYTE_PTR) buf, (CK_ULONG) num);
+	pk11_return_session(&ctx);
+	if (rv == CKR_OK)
+		return (ISC_R_SUCCESS);
+	else
+		return (DST_R_CRYPTOFAILURE);
+}
+
+#define SEEDSIZE	1024
+
+static CK_BYTE seed[SEEDSIZE];
+
+void
+pk11_rand_seed_fromfile(const char *randomfile) {
+	pk11_context_t ctx;
+	FILE *stream = NULL;
+	size_t cc = 0;
+	isc_result_t ret;
+
+	ret = pk11_get_session(&ctx, OP_RAND, ISC_FALSE, ISC_FALSE,
+			       ISC_FALSE, NULL, 0);
+	if ((ret != ISC_R_SUCCESS) &&
+	    (ret != PK11_R_NODIGESTSERVICE) &&
+	    (ret != PK11_R_NOAESSERVICE))
+		return;
+	RUNTIME_CHECK(ctx.session != CK_INVALID_HANDLE);
+	ret = isc_stdio_open(randomfile, "r", &stream);
+	if (ret != ISC_R_SUCCESS)
+		goto cleanup;
+	ret = isc_stdio_read(seed, 1, SEEDSIZE, stream, &cc);
+	if (ret!= ISC_R_SUCCESS)
+		goto cleanup;
+	ret = isc_stdio_close(stream);
+	stream = NULL;
+	if (ret!= ISC_R_SUCCESS)
+		goto cleanup;
+	(void) pkcs_C_SeedRandom(ctx.session, seed, (CK_ULONG) cc);
+
+    cleanup:
+	if (stream != NULL)
+		(void) isc_stdio_close(stream);
+	pk11_return_session(&ctx);
+}
+
+isc_result_t
+pk11_get_session(pk11_context_t *ctx, pk11_optype_t optype,
+		 isc_boolean_t need_services, isc_boolean_t rw,
+		 isc_boolean_t logon, const char *pin, CK_SLOT_ID slot)
+{
+	pk11_token_t *token = NULL;
+	pk11_sessionlist_t *freelist;
+	pk11_session_t *sp;
+	isc_result_t ret;
+#ifdef PKCS11CRYPTO
+	isc_result_t service_ret = ISC_R_SUCCESS;
+#else
+	UNUSED(need_services);
+#endif
+
+	memset(ctx, 0, sizeof(pk11_context_t));
+	ctx->handle = NULL;
+	ctx->session = CK_INVALID_HANDLE;
+
+	ret = pk11_initialize(NULL, NULL);
+#ifdef PKCS11CRYPTO
+	if (ret == PK11_R_NORANDOMSERVICE ||
+	    ret == PK11_R_NODIGESTSERVICE ||
+	    ret == PK11_R_NOAESSERVICE) {
+		if (need_services)
+			return (ret);
+		service_ret = ret;
+	}
+	else
+#endif /* PKCS11CRYPTO */
+	if (ret != ISC_R_SUCCESS)
+		return (ret);
+
+	LOCK(&sessionlock);
+	/* wait for initialization to finish */
+	UNLOCK(&sessionlock);
+
+	switch(optype) {
+#ifdef PKCS11CRYPTO
+	case OP_RAND:
+		token = rand_token;
+		break;
+	case OP_DIGEST:
+		token = digest_token;
+		break;
+	case OP_AES:
+		token = aes_token;
+		break;
+	case OP_ANY:
+		for (token = ISC_LIST_HEAD(tokens);
+		     token != NULL;
+		     token = ISC_LIST_NEXT(token, link))
+			if (token->slotid == slot)
+				break;
+		break;
+#endif
+	default:
+		for (token = ISC_LIST_HEAD(tokens);
+		     token != NULL;
+		     token = ISC_LIST_NEXT(token, link))
+			if (token->slotid == slot)
+				break;
+#ifdef PKCS11CRYPTO
+		if ((token == NULL) ||
+		    ((token->operations & (1 << optype)) == 0))
+			return (ISC_R_NOTFOUND);
+#endif
+		break;
+	}
+	if (token == NULL)
+		return (ISC_R_NOTFOUND);
+
+	/* Override the token's PIN */
+	if (logon && pin != NULL && *pin != '\0') {
+		memset(token->pin, 0, PINLEN);
+		strncpy(token->pin, pin, PINLEN);
+	}
+
+	freelist = &token->sessions;
+
+	LOCK(&sessionlock);
+	sp = ISC_LIST_HEAD(*freelist);
+	if (sp != NULL) {
+		ISC_LIST_UNLINK(*freelist, sp, link);
+		ISC_LIST_APPEND(actives, sp, link);
+		UNLOCK(&sessionlock);
+		if (logon)
+			ret = token_login(sp);
+		ctx->handle = sp;
+		ctx->session = sp->session;
+		return (ret);
+	}
+	UNLOCK(&sessionlock);
+
+	sp = pk11_mem_get(sizeof(*sp));
+	if (sp == NULL)
+		return (ISC_R_NOMEMORY);
+	sp->magic = SES_MAGIC;
+	sp->token = token;
+	sp->session = CK_INVALID_HANDLE;
+	ISC_LINK_INIT(sp, link);
+	ret = setup_session(sp, token, rw);
+	if ((ret == ISC_R_SUCCESS) && logon)
+		ret = token_login(sp);
+	LOCK(&sessionlock);
+	ISC_LIST_APPEND(actives, sp, link);
+	UNLOCK(&sessionlock);
+	ctx->handle = sp;
+	ctx->session = sp->session;
+#ifdef PKCS11CRYPTO
+	if (ret == ISC_R_SUCCESS)
+		ret = service_ret;
+#endif
+	return (ret);
+}
+
+void
+pk11_return_session(pk11_context_t *ctx) {
+	pk11_session_t *sp = (pk11_session_t *) ctx->handle;
+
+	if (sp == NULL)
+		return;
+	ctx->handle = NULL;
+	ctx->session = CK_INVALID_HANDLE;
+
+	LOCK(&sessionlock);
+	ISC_LIST_UNLINK(actives, sp, link);
+	UNLOCK(&sessionlock);
+	if (sp->session == CK_INVALID_HANDLE) {
+		pk11_mem_put(sp, sizeof(*sp));
+		return;
+	}
+
+	LOCK(&sessionlock);
+	ISC_LIST_APPEND(sp->token->sessions, sp, link);
+	UNLOCK(&sessionlock);
+}
+
+static isc_result_t
+free_all_sessions(void) {
+	pk11_token_t *token;
+	isc_result_t ret = ISC_R_SUCCESS;
+	isc_result_t oret;
+
+	for (token = ISC_LIST_HEAD(tokens);
+	     token != NULL;
+	     token = ISC_LIST_NEXT(token, link)) {
+		oret = free_session_list(&token->sessions);
+		if (oret != ISC_R_SUCCESS)
+			ret = oret;
+	}
+	if (!ISC_LIST_EMPTY(actives)) {
+		ret = ISC_R_ADDRINUSE;
+		oret = free_session_list(&actives);
+		if (oret != ISC_R_SUCCESS)
+			ret = oret;
+	}
+	return (ret);
+}
+
+static isc_result_t
+free_session_list(pk11_sessionlist_t *slist) {
+	pk11_session_t *sp;
+	CK_RV rv;
+	isc_result_t ret;
+
+	ret = ISC_R_SUCCESS;
+	LOCK(&sessionlock);
+	while (!ISC_LIST_EMPTY(*slist)) {
+		sp = ISC_LIST_HEAD(*slist);
+		UNLOCK(&sessionlock);
+		if (sp->session != CK_INVALID_HANDLE) {
+			rv = pkcs_C_CloseSession(sp->session);
+			if (rv != CKR_OK)
+				ret = DST_R_CRYPTOFAILURE;
+		}
+		LOCK(&sessionlock);
+		ISC_LIST_UNLINK(*slist, sp, link);
+		pk11_mem_put(sp, sizeof(*sp));
+	}
+	UNLOCK(&sessionlock);
+
+	return (ret);
+}
+
+static isc_result_t
+setup_session(pk11_session_t *sp, pk11_token_t *token,
+	      isc_boolean_t rw)
+{
+	CK_RV rv;
+	CK_FLAGS flags = CKF_SERIAL_SESSION;
+
+	if (rw)
+		flags += CKF_RW_SESSION;
+
+	rv = pkcs_C_OpenSession(token->slotid, flags, NULL_PTR,
+				NULL_PTR, &sp->session);
+	if (rv != CKR_OK)
+		return (DST_R_CRYPTOFAILURE);
+	return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+token_login(pk11_session_t *sp) {
+	CK_RV rv;
+	pk11_token_t *token = sp->token;
+	isc_result_t ret = ISC_R_SUCCESS;
+
+	LOCK(&sessionlock);
+	if (!token->logged) {
+		rv = pkcs_C_Login(sp->session, CKU_USER,
+				  (CK_UTF8CHAR_PTR) token->pin,
+				  (CK_ULONG) strlen(token->pin));
+		if (rv != CKR_OK) {
+			ret = ISC_R_NOPERM;
+#if PK11_NO_LOGERR
+			pk11_error_fatalcheck(__FILE__, __LINE__,
+					      "pkcs_C_Login", rv);
+#endif
+		} else
+			token->logged = ISC_TRUE;
+	}
+	UNLOCK(&sessionlock);
+	return (ret);
+}
+
+static void
+choose_slots(void) {
+	CK_MECHANISM_INFO mechInfo;
+	CK_TOKEN_INFO tokenInfo;
+	CK_RV rv;
+	CK_SLOT_ID slot;
+	CK_SLOT_ID_PTR slotList;
+	CK_ULONG slotCount;
+	pk11_token_t *token;
+	unsigned int i;
+
+	slotCount = 0;
+	PK11_FATALCHECK(pkcs_C_GetSlotList, (CK_FALSE, NULL_PTR, &slotCount));
+	/* it's not an error if we didn't find any providers */
+	if (slotCount == 0)
+		return;
+	slotList = pk11_mem_get(sizeof(CK_SLOT_ID_PTR) * slotCount);
+	RUNTIME_CHECK(slotList != NULL);
+	PK11_FATALCHECK(pkcs_C_GetSlotList, (CK_FALSE, slotList, &slotCount));
+
+	for (i = 0; i < slotCount; i++) {
+		slot = slotList[i];
+
+		rv = pkcs_C_GetTokenInfo(slot, &tokenInfo);
+		if (rv != CKR_OK)
+			continue;
+		token = pk11_mem_get(sizeof(*token));
+		RUNTIME_CHECK(token != NULL);
+		token->magic = TOK_MAGIC;
+		token->slotid = slot;
+		ISC_LINK_INIT(token, link);
+		ISC_LIST_INIT(token->sessions);
+		memmove(token->name, tokenInfo.label, 32);
+		memmove(token->manuf, tokenInfo.manufacturerID, 32);
+		memmove(token->model, tokenInfo.model, 16);
+		memmove(token->serial, tokenInfo.serialNumber, 16);
+		ISC_LIST_APPEND(tokens, token, link);
+		if ((tokenInfo.flags & CKF_RNG) == 0)
+			goto try_rsa;
+		token->operations |= 1 << OP_RAND;
+		if (rand_token == NULL)
+			rand_token = token;
+
+	try_rsa:
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_RSA_PKCS_KEY_PAIR_GEN,
+					     &mechInfo);
+		if ((rv != CKR_OK) ||
+		    ((mechInfo.flags & CKF_GENERATE_KEY_PAIR) == 0))
+			goto try_dsa;
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_MD5_RSA_PKCS,
+					     &mechInfo);
+		if ((rv != CKR_OK) ||
+		    ((mechInfo.flags & CKF_SIGN) == 0) ||
+		    ((mechInfo.flags & CKF_VERIFY) == 0))
+			goto try_dsa;
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA1_RSA_PKCS,
+					     &mechInfo);
+		if ((rv != CKR_OK) ||
+		    ((mechInfo.flags & CKF_SIGN) == 0) ||
+		    ((mechInfo.flags & CKF_VERIFY) == 0))
+			goto try_dsa;
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA256_RSA_PKCS,
+					     &mechInfo);
+		if ((rv != CKR_OK) ||
+		    ((mechInfo.flags & CKF_SIGN) == 0) ||
+		    ((mechInfo.flags & CKF_VERIFY) == 0))
+			goto try_dsa;
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA512_RSA_PKCS,
+					     &mechInfo);
+		if ((rv != CKR_OK) ||
+		    ((mechInfo.flags & CKF_SIGN) == 0) ||
+		    ((mechInfo.flags & CKF_VERIFY) == 0))
+			goto try_dsa;
+		token->operations |= 1 << OP_RSA;
+		if (best_rsa_token == NULL)
+			best_rsa_token = token;
+
+	try_dsa:
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_DSA_PARAMETER_GEN,
+					     &mechInfo);
+		if ((rv != CKR_OK) || ((mechInfo.flags & CKF_GENERATE) == 0))
+			goto try_dh;
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_DSA_KEY_PAIR_GEN,
+					     &mechInfo);
+		if ((rv != CKR_OK) ||
+		    ((mechInfo.flags & CKF_GENERATE_KEY_PAIR) == 0))
+			goto try_dh;
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_DSA_SHA1, &mechInfo);
+		if ((rv != CKR_OK) ||
+		    ((mechInfo.flags & CKF_SIGN) == 0) ||
+		    ((mechInfo.flags & CKF_VERIFY) == 0))
+			goto try_dh;
+		token->operations |= 1 << OP_DSA;
+		if (best_dsa_token == NULL)
+			best_dsa_token = token;
+
+	try_dh:
+#ifdef notdef
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_DH_PKCS_PARAMETER_GEN,
+					     &mechInfo);
+		if ((rv != CKR_OK) || ((mechInfo.flags & CKF_GENERATE) == 0))
+			goto try_digest;
+#endif
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_DH_PKCS_KEY_PAIR_GEN,
+					     &mechInfo);
+		if ((rv != CKR_OK) ||
+		    ((mechInfo.flags & CKF_GENERATE_KEY_PAIR) == 0))
+			goto try_digest;
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_DH_PKCS_DERIVE,
+					     &mechInfo);
+		if ((rv != CKR_OK) || ((mechInfo.flags & CKF_DERIVE) == 0))
+			goto try_digest;
+		token->operations |= 1 << OP_DH;
+		if (best_dh_token == NULL)
+			best_dh_token = token;
+
+	try_digest:
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_MD5, &mechInfo);
+		if ((rv != CKR_OK) || ((mechInfo.flags & CKF_DIGEST) == 0))
+			continue;
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA_1, &mechInfo);
+		if ((rv != CKR_OK) || ((mechInfo.flags & CKF_DIGEST) == 0))
+			continue;
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA224, &mechInfo);
+		if ((rv != CKR_OK) || ((mechInfo.flags & CKF_DIGEST) == 0))
+			continue;
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA256, &mechInfo);
+		if ((rv != CKR_OK) || ((mechInfo.flags & CKF_DIGEST) == 0))
+			continue;
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA384, &mechInfo);
+		if ((rv != CKR_OK) || ((mechInfo.flags & CKF_DIGEST) == 0))
+			continue;
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA512, &mechInfo);
+		if ((rv != CKR_OK) || ((mechInfo.flags & CKF_DIGEST) == 0))
+			continue;
+#ifdef PKCS11CRYPTOWITHHMAC
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_MD5_HMAC, &mechInfo);
+		if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0))
+			continue;
+#endif
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA_1_HMAC, &mechInfo);
+		if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0))
+			continue;
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA224_HMAC, &mechInfo);
+		if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0))
+			continue;
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA256_HMAC, &mechInfo);
+		if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0))
+			continue;
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA384_HMAC, &mechInfo);
+		if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0))
+			continue;
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA512_HMAC, &mechInfo);
+		if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0))
+			continue;
+		token->operations |= 1 << OP_DIGEST;
+		if (digest_token == NULL)
+			digest_token = token;
+
+		/* ECDSA requires digest */
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_EC_KEY_PAIR_GEN,
+					     &mechInfo);
+		if ((rv != CKR_OK) ||
+		    ((mechInfo.flags & CKF_GENERATE_KEY_PAIR) == 0))
+			goto try_gost;
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_ECDSA, &mechInfo);
+		if ((rv != CKR_OK) ||
+		    ((mechInfo.flags & CKF_SIGN) == 0) ||
+		    ((mechInfo.flags & CKF_VERIFY) == 0))
+			goto try_gost;
+		token->operations |= 1 << OP_EC;
+		if (best_ec_token == NULL)
+			best_ec_token = token;
+
+	try_gost:
+		/* does GOST require digest too? */
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_GOSTR3411, &mechInfo);
+		if ((rv != CKR_OK) || ((mechInfo.flags & CKF_DIGEST) == 0))
+			goto try_aes;
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_GOSTR3410_KEY_PAIR_GEN,
+					     &mechInfo);
+		if ((rv != CKR_OK) ||
+		    ((mechInfo.flags & CKF_GENERATE_KEY_PAIR) == 0))
+			goto try_aes;
+		rv = pkcs_C_GetMechanismInfo(slot,
+					     CKM_GOSTR3410_WITH_GOSTR3411,
+					     &mechInfo);
+		if ((rv != CKR_OK) ||
+		    ((mechInfo.flags & CKF_SIGN) == 0) ||
+		    ((mechInfo.flags & CKF_VERIFY) == 0))
+			goto try_aes;
+		token->operations |= 1 << OP_GOST;
+		if (best_gost_token == NULL)
+			best_gost_token = token;
+
+	try_aes:
+		rv = pkcs_C_GetMechanismInfo(slot, CKM_AES_ECB, &mechInfo);
+		if ((rv != CKR_OK) || ((mechInfo.flags & CKF_ENCRYPT) == 0))
+			continue;
+		token->operations |= 1 << OP_AES;
+		if (aes_token == NULL)
+			aes_token = token;
+	}
+
+	if (slotList != NULL)
+		pk11_mem_put(slotList, sizeof(CK_SLOT_ID_PTR) * slotCount);
+}
+
+CK_SLOT_ID
+pk11_get_best_token(pk11_optype_t optype) {
+	pk11_token_t *token = NULL;
+
+	switch (optype) {
+	case OP_RAND:
+		token = rand_token;
+		break;
+	case OP_RSA:
+		token = best_rsa_token;
+		break;
+	case OP_DSA:
+		token = best_dsa_token;
+		break;
+	case OP_DH:
+		token = best_dh_token;
+		break;
+	case OP_DIGEST:
+		token = digest_token;
+		break;
+	case OP_EC:
+		token = best_ec_token;
+		break;
+	case OP_GOST:
+		token = best_gost_token;
+		break;
+	case OP_AES:
+		token = aes_token;
+		break;
+	default:
+		break;
+	}
+	if (token == NULL)
+		return (0);
+	return (token->slotid);
+}
+
+unsigned int
+pk11_numbits(CK_BYTE_PTR data, unsigned int bytecnt) {
+	unsigned int bitcnt, i;
+	CK_BYTE top;
+
+	if (bytecnt == 0)
+		return (0);
+	bitcnt = bytecnt * 8;
+	for (i = 0; i < bytecnt; i++) {
+		top = data[i];
+		if (top == 0) {
+			bitcnt -= 8;
+			continue;
+		}
+		if (top & 0x80)
+			return (bitcnt);
+		if (top & 0x40)
+			return (bitcnt - 1);
+		if (top & 0x20)
+			return (bitcnt - 2);
+		if (top & 0x10)
+			return (bitcnt - 3);
+		if (top & 0x08)
+			return (bitcnt - 4);
+		if (top & 0x04)
+			return (bitcnt - 5);
+		if (top & 0x02)
+			return (bitcnt - 6);
+		if (top & 0x01)
+			return (bitcnt - 7);
+		break;
+	}
+	INSIST(0);
+}
+
+CK_ATTRIBUTE *
+pk11_attribute_first(const pk11_object_t *obj) {
+	return (obj->repr);
+}
+
+CK_ATTRIBUTE *
+pk11_attribute_next(const pk11_object_t *obj, CK_ATTRIBUTE *attr) {
+	CK_ATTRIBUTE *next;
+
+	next = attr + 1;
+	if ((next - obj->repr) >= obj->attrcnt)
+		return (NULL);
+	return (next);
+}
+
+CK_ATTRIBUTE *
+pk11_attribute_bytype(const pk11_object_t *obj, CK_ATTRIBUTE_TYPE type) {
+	CK_ATTRIBUTE *attr;
+
+	for(attr = pk11_attribute_first(obj);
+	    attr != NULL;
+	    attr = pk11_attribute_next(obj, attr))
+		if (attr->type == type)
+			return (attr);
+	return (NULL);
+}
+
+static char *
+percent_decode(char *x, size_t *len) {
+	char *p, *c;
+	unsigned char v;
+
+	INSIST(len != NULL);
+
+	for (p = c = x; p[0] != '\0'; p++, c++) {
+		switch (p[0]) {
+		case '%':
+			v = 0;
+			switch (p[1]) {
+			case '0':
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+				v = (p[1] - '0') << 4;
+				break;
+			case 'A':
+			case 'B':
+			case 'C':
+			case 'D':
+			case 'E':
+			case 'F':
+				v = (p[1] - 'A' + 10) << 4;
+				break;
+			case 'a':
+			case 'b':
+			case 'c':
+			case 'd':
+			case 'e':
+			case 'f':
+				v = (p[1] - 'a' + 10) << 4;
+				break;
+			default:
+				return (NULL);
+			}
+			switch (p[2]) {
+			case '0':
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+				v |= (p[2] - '0') & 0x0f;
+				break;
+			case 'A':
+			case 'B':
+			case 'C':
+			case 'D':
+			case 'E':
+			case 'F':
+				v = (p[2] - 'A' + 10) & 0x0f;
+				break;
+			case 'a':
+			case 'b':
+			case 'c':
+			case 'd':
+			case 'e':
+			case 'f':
+				v = (p[2] - 'a' + 10) & 0x0f;
+				break;
+			default:
+				return (NULL);
+			}
+			p += 2;
+			*c = (char) v;
+			(*len)++;
+			break;
+		default:
+			*c = *p;
+			(*len)++;
+		}
+	}
+	return (x);
+}
+
+static isc_boolean_t
+pk11strcmp(const char *x, size_t lenx, const char *y, size_t leny) {
+	char buf[32];
+
+	INSIST((leny == 32) || (leny == 16));
+
+	memset(buf, ' ', 32);
+	if (lenx > leny)
+		lenx = leny;
+	memmove(buf, x, lenx);
+	return (ISC_TF(memcmp(buf, y, leny) == 0));
+}
+
+static CK_ATTRIBUTE *
+push_attribute(pk11_object_t *obj, isc_mem_t *mctx, size_t len) {
+	CK_ATTRIBUTE *old = obj->repr;
+	CK_ATTRIBUTE *attr;
+	CK_BYTE cnt = obj->attrcnt;
+
+	obj->repr = isc_mem_get(mctx, (cnt + 1) * sizeof(*attr));
+	if (obj->repr == NULL) {
+		obj->repr = old;
+		return (NULL);
+	}
+	memset(obj->repr, 0, (cnt + 1) * sizeof(*attr));
+	memmove(obj->repr, old, cnt * sizeof(*attr));
+	attr = obj->repr + cnt;
+	attr->ulValueLen = (CK_ULONG) len;
+	attr->pValue = isc_mem_get(mctx, len);
+	if (attr->pValue == NULL) {
+		memset(obj->repr, 0, (cnt + 1) * sizeof(*attr));
+		isc_mem_put(mctx, obj->repr, (cnt + 1) * sizeof(*attr));
+		obj->repr = old;
+		return (NULL);
+	}
+	memset(attr->pValue, 0, len);
+	if (old != NULL) {
+		memset(old, 0, cnt * sizeof(*attr));
+		isc_mem_put(mctx, old, cnt * sizeof(*attr));
+	}
+	obj->attrcnt++;
+	return (attr);
+}
+
+#define DST_RET(a)	{ ret = a; goto err; }
+
+isc_result_t
+pk11_parse_uri(pk11_object_t *obj, const char *label,
+	       isc_mem_t *mctx, pk11_optype_t optype)
+{
+	CK_ATTRIBUTE *attr;
+	pk11_token_t *token = NULL;
+	char *uri, *p, *a, *na, *v;
+	size_t len, l;
+	FILE *stream = NULL;
+	char pin[PINLEN];
+	isc_boolean_t gotpin = ISC_FALSE;
+	isc_result_t ret;
+
+	/* get values to work on */
+	len = strlen(label) + 1;
+	uri = isc_mem_get(mctx, len);
+	if (uri == NULL)
+		return (ISC_R_NOMEMORY);
+	memmove(uri, label, len);
+
+	/* get the URI scheme */
+	p = strchr(uri, ':');
+	if (p == NULL)
+		DST_RET(PK11_R_NOPROVIDER);
+	*p++ = '\0';
+	if (strcmp(uri, "pkcs11") != 0)
+		DST_RET(PK11_R_NOPROVIDER);
+
+	/* get attributes */
+	for (na = p; na != NULL;) {
+		a = na;
+		p = strchr(a, ';');
+		if (p == NULL) {
+			/* last attribute */
+			na = NULL;
+		} else {
+			*p++ = '\0';
+			na = p;
+		}
+		p = strchr(a, '=');
+		if (p != NULL) {
+			*p++ = '\0';
+			v = p;
+		} else
+			v = a;
+		l = 0;
+		v = percent_decode(v, &l);
+		if (v == NULL)
+			DST_RET(PK11_R_NOPROVIDER);
+		if ((a == v) || (strcmp(a, "object") == 0)) {
+			/* object: CKA_LABEL */
+			attr = pk11_attribute_bytype(obj, CKA_LABEL);
+			if (attr != NULL)
+				DST_RET(PK11_R_NOPROVIDER);
+			attr = push_attribute(obj, mctx, l);
+			if (attr == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			attr->type = CKA_LABEL;
+			memmove(attr->pValue, v, l);
+		} else if (strcmp(a, "token") == 0) {
+			/* token: CK_TOKEN_INFO label */
+			if (token == NULL)
+				for (token = ISC_LIST_HEAD(tokens);
+				     token != NULL;
+				     token = ISC_LIST_NEXT(token, link))
+					if (pk11strcmp(v, l, token->name, 32))
+						break;
+		} else if (strcmp(a, "manufacturer") == 0) {
+			/* manufacturer: CK_TOKEN_INFO manufacturerID */
+			if (token == NULL)
+				for (token = ISC_LIST_HEAD(tokens);
+				     token != NULL;
+				     token = ISC_LIST_NEXT(token, link))
+					if (pk11strcmp(v, l, token->manuf, 32))
+						break;
+		} else if (strcmp(a, "serial") == 0) {
+			/* serial: CK_TOKEN_INFO serialNumber */
+			if (token == NULL)
+				for (token = ISC_LIST_HEAD(tokens);
+				     token != NULL;
+				     token = ISC_LIST_NEXT(token, link))
+					if (pk11strcmp(v, l, token->serial, 16))
+						break;
+		} else if (strcmp(a, "model") == 0) {
+			/* model: CK_TOKEN_INFO model */
+			if (token == NULL)
+				for (token = ISC_LIST_HEAD(tokens);
+				     token != NULL;
+				     token = ISC_LIST_NEXT(token, link))
+					if (pk11strcmp(v, l, token->model, 16))
+						break;
+		} else if (strcmp(a, "library-manufacturer") == 0) {
+			/* ignored */
+		} else if (strcmp(a, "library-description") == 0) {
+			/* ignored */
+		} else if (strcmp(a, "library-version") == 0) {
+			/* ignored */
+		} else if (strcmp(a, "object-type") == 0) {
+			/* object-type: CKA_CLASS */
+			/* only private makes sense */
+			if (strcmp(v, "private") != 0)
+				DST_RET(PK11_R_NOPROVIDER);
+		} else if (strcmp(a, "id") == 0) {
+			/* id: CKA_ID */
+			attr = pk11_attribute_bytype(obj, CKA_ID);
+			if (attr != NULL)
+				DST_RET(PK11_R_NOPROVIDER);
+			attr = push_attribute(obj, mctx, l);
+			if (attr == NULL)
+				DST_RET(ISC_R_NOMEMORY);
+			attr->type = CKA_ID;
+			memmove(attr->pValue, v, l);
+		} else if (strcmp(a, "pin-source") == 0) {
+			/* pin-source: PIN */
+			ret = isc_stdio_open(v, "r", &stream);
+			if (ret != ISC_R_SUCCESS)
+				goto err;
+			memset(pin, 0, PINLEN);
+			ret = isc_stdio_read(pin, 1, PINLEN - 1, stream, NULL);
+			if ((ret != ISC_R_SUCCESS) && (ret != ISC_R_EOF))
+				goto err;
+			ret = isc_stdio_close(stream);
+			stream = NULL;
+			if (ret != ISC_R_SUCCESS)
+				goto err;
+			gotpin = ISC_TRUE;
+		} else
+			DST_RET(PK11_R_NOPROVIDER);
+	}
+
+	if ((pk11_attribute_bytype(obj, CKA_LABEL) == NULL) &&
+	    (pk11_attribute_bytype(obj, CKA_ID) == NULL))
+		DST_RET(ISC_R_NOTFOUND);
+
+	if (token == NULL) {
+		if (optype == OP_RSA)
+			token = best_rsa_token;
+		else if (optype == OP_DSA)
+			token = best_dsa_token;
+		else if (optype == OP_DH)
+			token = best_dh_token;
+		else if (optype == OP_EC)
+			token = best_ec_token;
+	}
+	if (token == NULL)
+		DST_RET(ISC_R_NOTFOUND);
+	obj->slot = token->slotid;
+	if (gotpin) {
+		memmove(token->pin, pin, PINLEN);
+		obj->reqlogon = ISC_TRUE;
+	}
+
+	ret = ISC_R_SUCCESS;
+
+  err:
+	if (stream != NULL)
+		(void) isc_stdio_close(stream);
+	isc_mem_put(mctx, uri, len);
+	return (ret);
+}
+
+void
+pk11_error_fatalcheck(const char *file, int line,
+		      const char *funcname, CK_RV rv)
+{
+	isc_error_fatal(file, line, "%s: Error = 0x%.8lX\n", funcname, rv);
+}
+
+void
+pk11_dump_tokens(void)
+{
+	pk11_token_t *token;
+	isc_boolean_t first;
+
+	printf("DEFAULTS\n");
+	printf("\trand_token=%p\n", rand_token);
+	printf("\tbest_rsa_token=%p\n", best_rsa_token);
+	printf("\tbest_dsa_token=%p\n", best_dsa_token);
+	printf("\tbest_dh_token=%p\n", best_dh_token);
+	printf("\tdigest_token=%p\n", digest_token);
+	printf("\tbest_ec_token=%p\n", best_ec_token);
+	printf("\tbest_gost_token=%p\n", best_gost_token);
+	printf("\taes_token=%p\n", aes_token);
+
+	for (token = ISC_LIST_HEAD(tokens);
+	     token != NULL;
+	     token = ISC_LIST_NEXT(token, link)) {
+		printf("\nTOKEN\n");
+		printf("\taddress=%p\n", token);
+		printf("\tslotID=%lu\n", token->slotid);
+		printf("\tlabel=%.32s\n", token->name);
+		printf("\tmanufacturerID=%.32s\n", token->manuf);
+		printf("\tmodel=%.16s\n", token->model);
+		printf("\tserialNumber=%.16s\n", token->serial);
+		printf("\tsupported operations=0x%x (", token->operations);
+		first = ISC_TRUE;
+		if (token->operations & (1 << OP_RAND)) {
+			if (!first)
+				printf(",");
+			first = ISC_FALSE;
+			printf("RAND");
+		}
+		if (token->operations & (1 << OP_RSA)) {
+			if (!first)
+				printf(",");
+			first = ISC_FALSE;
+			printf("RSA");
+		}
+		if (token->operations & (1 << OP_DSA)) {
+			if (!first)
+				printf(",");
+			first = ISC_FALSE;
+			printf("DSA");
+		}
+		if (token->operations & (1 << OP_DH)) {
+			if (!first)
+				printf(",");
+			first = ISC_FALSE;
+			printf("DH");
+		}
+		if (token->operations & (1 << OP_DIGEST)) {
+			if (!first)
+				printf(",");
+			first = ISC_FALSE;
+			printf("DIGEST");
+		}
+		if (token->operations & (1 << OP_EC)) {
+			if (!first)
+				printf(",");
+			first = ISC_FALSE;
+			printf("EC");
+		}
+		printf(")\n");
+	}
+}
diff --git a/lib/isc/pk11_result.c b/lib/isc/pk11_result.c
new file mode 100644
index 0000000..0ada753
--- /dev/null
+++ b/lib/isc/pk11_result.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+#include <stddef.h>
+
+#include <isc/once.h>
+#include <isc/msgcat.h>
+#include <isc/util.h>
+
+#include <pk11/result.h>
+
+LIBISC_EXTERNAL_DATA isc_msgcat_t *		pk11_msgcat = NULL;
+
+static isc_once_t		msgcat_once = ISC_ONCE_INIT;
+
+static const char *text[PK11_R_NRESULTS] = {
+	"PKCS#11 initialization failed",		/*%< 0 */
+	"no PKCS#11 provider",				/*%< 1 */
+	"PKCS#11 provider has no random service",	/*%< 2 */
+	"PKCS#11 provider has no digest service",	/*%< 3 */
+	"PKCS#11 provider has no AES service",		/*%< 4 */
+};
+
+#define PK11_RESULT_RESULTSET			2
+
+static isc_once_t		once = ISC_ONCE_INIT;
+
+static void
+open_msgcat(void) {
+	isc_msgcat_open("libpk11.cat", &pk11_msgcat);
+}
+
+void
+pk11_initmsgcat(void) {
+
+	/*
+	 * Initialize the PKCS#11 support's message catalog,
+	 * pk11_msgcat, if it has not already been initialized.
+	 */
+
+	RUNTIME_CHECK(isc_once_do(&msgcat_once, open_msgcat) == ISC_R_SUCCESS);
+}
+
+static void
+initialize_action(void) {
+	isc_result_t result;
+
+	result = isc_result_register(ISC_RESULTCLASS_PK11, PK11_R_NRESULTS,
+				     text, pk11_msgcat, PK11_RESULT_RESULTSET);
+	if (result != ISC_R_SUCCESS)
+		UNEXPECTED_ERROR(__FILE__, __LINE__,
+				 "isc_result_register() failed: %u", result);
+}
+
+static void
+initialize(void) {
+	pk11_initmsgcat();
+	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
+}
+
+const char *
+pk11_result_totext(isc_result_t result) {
+	initialize();
+
+	return (isc_result_totext(result));
+}
+
+void
+pk11_result_register(void) {
+	initialize();
+}
diff --git a/lib/isc/sha1.c b/lib/isc/sha1.c
index cce9603..caa721e 100644
--- a/lib/isc/sha1.c
+++ b/lib/isc/sha1.c
@@ -44,8 +44,12 @@
 #include <isc/types.h>
 #include <isc/util.h>
 
-#ifdef ISC_PLATFORM_OPENSSLHASH
+#if PKCS11CRYPTO
+#include <pk11/internal.h>
+#include <pk11/pk11.h>
+#endif
 
+#ifdef ISC_PLATFORM_OPENSSLHASH
 void
 isc_sha1_init(isc_sha1_t *context)
 {
@@ -77,6 +81,50 @@ isc_sha1_final(isc_sha1_t *context, unsigned char *digest) {
 	EVP_DigestFinal(context, digest, NULL);
 }
 
+#elif PKCS11CRYPTO
+
+void
+isc_sha1_init(isc_sha1_t *ctx) {
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_SHA_1, NULL, 0 };
+
+	RUNTIME_CHECK(pk11_get_session(ctx, OP_DIGEST, ISC_TRUE, ISC_FALSE,
+				       ISC_FALSE, NULL, 0) == ISC_R_SUCCESS);
+	PK11_FATALCHECK(pkcs_C_DigestInit, (ctx->session, &mech));
+}
+
+void
+isc_sha1_invalidate(isc_sha1_t *ctx) {
+	CK_BYTE garbage[ISC_SHA1_DIGESTLENGTH];
+	CK_ULONG len = ISC_SHA1_DIGESTLENGTH;
+
+	if (ctx->handle == NULL)
+		return;
+	(void) pkcs_C_DigestFinal(ctx->session, garbage, &len);
+	memset(garbage, 0, sizeof(garbage));
+	pk11_return_session(ctx);
+}
+
+void
+isc_sha1_update(isc_sha1_t *ctx, const unsigned char *buf, unsigned int len) {
+	CK_RV rv;
+	CK_BYTE_PTR pPart;
+
+	DE_CONST(buf, pPart);
+	PK11_FATALCHECK(pkcs_C_DigestUpdate,
+			(ctx->session, pPart, (CK_ULONG) len));
+}
+
+void
+isc_sha1_final(isc_sha1_t *ctx, unsigned char *digest) {
+	CK_RV rv;
+	CK_ULONG len = ISC_SHA1_DIGESTLENGTH;
+
+	PK11_FATALCHECK(pkcs_C_DigestFinal,
+			(ctx->session, (CK_BYTE_PTR) digest, &len));
+	pk11_return_session(ctx);
+}
+
 #else
 
 #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
diff --git a/lib/isc/sha2.c b/lib/isc/sha2.c
index db2e349..d7e0cf6 100644
--- a/lib/isc/sha2.c
+++ b/lib/isc/sha2.c
@@ -63,6 +63,11 @@
 #include <isc/string.h>
 #include <isc/util.h>
 
+#if PKCS11CRYPTO
+#include <pk11/internal.h>
+#include <pk11/pk11.h>
+#endif
+
 #ifdef ISC_PLATFORM_OPENSSLHASH
 
 void
@@ -219,6 +224,272 @@ isc_sha384_final(isc_uint8_t digest[], isc_sha384_t *context) {
 	}
 }
 
+#elif PKCS11CRYPTO
+
+void
+isc_sha224_init(isc_sha224_t *context) {
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_SHA224, NULL, 0 };
+
+	if (context == (isc_sha224_t *)0) {
+		return;
+	}
+	RUNTIME_CHECK(pk11_get_session(context, OP_DIGEST, ISC_TRUE, ISC_FALSE,
+				       ISC_FALSE, NULL, 0) == ISC_R_SUCCESS);
+	PK11_FATALCHECK(pkcs_C_DigestInit, (context->session, &mech));
+}
+
+void
+isc_sha224_invalidate(isc_sha224_t *context) {
+	CK_BYTE garbage[ISC_SHA224_DIGESTLENGTH];
+	CK_ULONG len = ISC_SHA224_DIGESTLENGTH;
+
+	if (context->handle == NULL)
+		return;
+	(void) pkcs_C_DigestFinal(context->session, garbage, &len);
+	memset(garbage, 0, sizeof(garbage));
+	pk11_return_session(context);
+}
+
+void
+isc_sha224_update(isc_sha224_t *context, const isc_uint8_t* data, size_t len) {
+	CK_RV rv;
+	CK_BYTE_PTR pPart;
+
+	if (len == 0U) {
+		/* Calling with no data is valid - we do nothing */
+		return;
+	}
+
+	/* Sanity check: */
+	REQUIRE(context != (isc_sha224_t *)0 && data != (isc_uint8_t*)0);
+
+	DE_CONST(data, pPart);
+	PK11_FATALCHECK(pkcs_C_DigestUpdate,
+			(context->session, pPart, (CK_ULONG) len));
+}
+
+void
+isc_sha224_final(isc_uint8_t digest[], isc_sha224_t *context) {
+	CK_RV rv;
+	CK_ULONG len = ISC_SHA224_DIGESTLENGTH;
+
+	/* Sanity check: */
+	REQUIRE(context != (isc_sha224_t *)0);
+
+	/* If no digest buffer is passed, we don't bother doing this: */
+	if (digest != (isc_uint8_t*)0) {
+		PK11_FATALCHECK(pkcs_C_DigestFinal,
+				(context->session,
+				 (CK_BYTE_PTR) digest,
+				 &len));
+	} else {
+		CK_BYTE garbage[ISC_SHA224_DIGESTLENGTH];
+
+		(void) pkcs_C_DigestFinal(context->session, garbage, &len);
+		memset(garbage, 0, sizeof(garbage));
+	}
+	pk11_return_session(context);
+}
+
+void
+isc_sha256_init(isc_sha256_t *context) {
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_SHA256, NULL, 0 };
+
+	if (context == (isc_sha256_t *)0) {
+		return;
+	}
+	RUNTIME_CHECK(pk11_get_session(context, OP_DIGEST, ISC_TRUE, ISC_FALSE,
+				       ISC_FALSE, NULL, 0) == ISC_R_SUCCESS);
+	PK11_FATALCHECK(pkcs_C_DigestInit, (context->session, &mech));
+}
+
+void
+isc_sha256_invalidate(isc_sha256_t *context) {
+	CK_BYTE garbage[ISC_SHA256_DIGESTLENGTH];
+	CK_ULONG len = ISC_SHA256_DIGESTLENGTH;
+
+	if (context->handle == NULL)
+		return;
+	(void) pkcs_C_DigestFinal(context->session, garbage, &len);
+	memset(garbage, 0, sizeof(garbage));
+	pk11_return_session(context);
+}
+
+void
+isc_sha256_update(isc_sha256_t *context, const isc_uint8_t* data, size_t len) {
+	CK_RV rv;
+	CK_BYTE_PTR pPart;
+
+	if (len == 0U) {
+		/* Calling with no data is valid - we do nothing */
+		return;
+	}
+
+	/* Sanity check: */
+	REQUIRE(context != (isc_sha256_t *)0 && data != (isc_uint8_t*)0);
+
+	DE_CONST(data, pPart);
+	PK11_FATALCHECK(pkcs_C_DigestUpdate,
+			(context->session, pPart, (CK_ULONG) len));
+}
+
+void
+isc_sha256_final(isc_uint8_t digest[], isc_sha256_t *context) {
+	CK_RV rv;
+	CK_ULONG len = ISC_SHA256_DIGESTLENGTH;
+
+	/* Sanity check: */
+	REQUIRE(context != (isc_sha256_t *)0);
+
+	/* If no digest buffer is passed, we don't bother doing this: */
+	if (digest != (isc_uint8_t*)0) {
+		PK11_FATALCHECK(pkcs_C_DigestFinal,
+				(context->session,
+				 (CK_BYTE_PTR) digest,
+				 &len));
+	} else {
+		CK_BYTE garbage[ISC_SHA256_DIGESTLENGTH];
+
+		(void) pkcs_C_DigestFinal(context->session, garbage, &len);
+		memset(garbage, 0, sizeof(garbage));
+	}
+	pk11_return_session(context);
+}
+
+void
+isc_sha512_init(isc_sha512_t *context) {
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_SHA512, NULL, 0 };
+
+	if (context == (isc_sha512_t *)0) {
+		return;
+	}
+	RUNTIME_CHECK(pk11_get_session(context, OP_DIGEST, ISC_TRUE, ISC_FALSE,
+				       ISC_FALSE, NULL, 0) == ISC_R_SUCCESS);
+	PK11_FATALCHECK(pkcs_C_DigestInit, (context->session, &mech));
+}
+
+void
+isc_sha512_invalidate(isc_sha512_t *context) {
+	CK_BYTE garbage[ISC_SHA512_DIGESTLENGTH];
+	CK_ULONG len = ISC_SHA512_DIGESTLENGTH;
+
+	if (context->handle == NULL)
+		return;
+	(void) pkcs_C_DigestFinal(context->session, garbage, &len);
+	memset(garbage, 0, sizeof(garbage));
+	pk11_return_session(context);
+}
+
+void
+isc_sha512_update(isc_sha512_t *context, const isc_uint8_t* data, size_t len) {
+	CK_RV rv;
+	CK_BYTE_PTR pPart;
+
+	if (len == 0U) {
+		/* Calling with no data is valid - we do nothing */
+		return;
+	}
+
+	/* Sanity check: */
+	REQUIRE(context != (isc_sha512_t *)0 && data != (isc_uint8_t*)0);
+
+	DE_CONST(data, pPart);
+	PK11_FATALCHECK(pkcs_C_DigestUpdate,
+			(context->session, pPart, (CK_ULONG) len));
+}
+
+void
+isc_sha512_final(isc_uint8_t digest[], isc_sha512_t *context) {
+	CK_RV rv;
+	CK_ULONG len = ISC_SHA512_DIGESTLENGTH;
+
+	/* Sanity check: */
+	REQUIRE(context != (isc_sha512_t *)0);
+
+	/* If no digest buffer is passed, we don't bother doing this: */
+	if (digest != (isc_uint8_t*)0) {
+		PK11_FATALCHECK(pkcs_C_DigestFinal,
+				(context->session,
+				 (CK_BYTE_PTR) digest,
+				 &len));
+	} else {
+		CK_BYTE garbage[ISC_SHA512_DIGESTLENGTH];
+
+		(void) pkcs_C_DigestFinal(context->session, garbage, &len);
+		memset(garbage, 0, sizeof(garbage));
+	}
+	pk11_return_session(context);
+}
+
+void
+isc_sha384_init(isc_sha384_t *context) {
+	CK_RV rv;
+	CK_MECHANISM mech = { CKM_SHA384, NULL, 0 };
+
+	if (context == (isc_sha384_t *)0) {
+		return;
+	}
+	RUNTIME_CHECK(pk11_get_session(context, OP_DIGEST, ISC_TRUE, ISC_FALSE,
+				       ISC_FALSE, NULL, 0) == ISC_R_SUCCESS);
+	PK11_FATALCHECK(pkcs_C_DigestInit, (context->session, &mech));
+}
+
+void
+isc_sha384_invalidate(isc_sha384_t *context) {
+	CK_BYTE garbage[ISC_SHA384_DIGESTLENGTH];
+	CK_ULONG len = ISC_SHA384_DIGESTLENGTH;
+
+	if (context->handle == NULL)
+		return;
+	(void) pkcs_C_DigestFinal(context->session, garbage, &len);
+	memset(garbage, 0, sizeof(garbage));
+	pk11_return_session(context);
+}
+
+void
+isc_sha384_update(isc_sha384_t *context, const isc_uint8_t* data, size_t len) {
+	CK_RV rv;
+	CK_BYTE_PTR pPart;
+
+	if (len == 0U) {
+		/* Calling with no data is valid - we do nothing */
+		return;
+	}
+
+	/* Sanity check: */
+	REQUIRE(context != (isc_sha384_t *)0 && data != (isc_uint8_t*)0);
+
+	DE_CONST(data, pPart);
+	PK11_FATALCHECK(pkcs_C_DigestUpdate,
+			(context->session, pPart, (CK_ULONG) len));
+}
+
+void
+isc_sha384_final(isc_uint8_t digest[], isc_sha384_t *context) {
+	CK_RV rv;
+	CK_ULONG len = ISC_SHA384_DIGESTLENGTH;
+
+	/* Sanity check: */
+	REQUIRE(context != (isc_sha384_t *)0);
+
+	/* If no digest buffer is passed, we don't bother doing this: */
+	if (digest != (isc_uint8_t*)0) {
+		PK11_FATALCHECK(pkcs_C_DigestFinal,
+				(context->session,
+				 (CK_BYTE_PTR) digest,
+				 &len));
+	} else {
+		CK_BYTE garbage[ISC_SHA384_DIGESTLENGTH];
+
+		(void) pkcs_C_DigestFinal(context->session, garbage, &len);
+		memset(garbage, 0, sizeof(garbage));
+	}
+	pk11_return_session(context);
+}
+
 #else
 
 /*
@@ -1312,6 +1583,8 @@ isc_sha224_end(isc_sha224_t *context, char buffer[]) {
 	} else {
 #ifdef ISC_PLATFORM_OPENSSLHASH
 		EVP_MD_CTX_cleanup(context);
+#elif PKCS11CRYPTO
+		pk11_return_session(context);
 #else
 		memset(context, 0, sizeof(*context));
 #endif
@@ -1351,6 +1624,8 @@ isc_sha256_end(isc_sha256_t *context, char buffer[]) {
 	} else {
 #ifdef ISC_PLATFORM_OPENSSLHASH
 		EVP_MD_CTX_cleanup(context);
+#elif PKCS11CRYPTO
+		pk11_return_session(context);
 #else
 		memset(context, 0, sizeof(*context));
 #endif
@@ -1390,6 +1665,8 @@ isc_sha512_end(isc_sha512_t *context, char buffer[]) {
 	} else {
 #ifdef ISC_PLATFORM_OPENSSLHASH
 		EVP_MD_CTX_cleanup(context);
+#elif PKCS11CRYPTO
+		pk11_return_session(context);
 #else
 		memset(context, 0, sizeof(*context));
 #endif
@@ -1429,6 +1706,8 @@ isc_sha384_end(isc_sha384_t *context, char buffer[]) {
 	} else {
 #ifdef ISC_PLATFORM_OPENSSLHASH
 		EVP_MD_CTX_cleanup(context);
+#elif PKCS11CRYPTO
+		pk11_return_session(context);
 #else
 		memset(context, 0, sizeof(*context));
 #endif
diff --git a/lib/isc/unix/Makefile.in b/lib/isc/unix/Makefile.in
index c1411cb..0595fa2 100644
--- a/lib/isc/unix/Makefile.in
+++ b/lib/isc/unix/Makefile.in
@@ -29,14 +29,14 @@ CDEFINES =
 CWARNINGS =
 
 # Alphabetically
-OBJS =		@ISC_IPV6_O@ \
+OBJS =		@ISC_IPV6_O@ @ISC_PK11_API_O@ \
 		app.@O@ dir.@O@ entropy.@O@ errno2result.@O@ file.@O@ \
 		fsaccess.@O@ interfaceiter.@O@ keyboard.@O@ net.@O@ \
 		os.@O@ resource.@O@ socket.@O@ stdio.@O@ stdtime.@O@ \
 		strerror.@O@ syslog.@O@ time.@O@
 
 # Alphabetically
-SRCS =		@ISC_IPV6_C@ \
+SRCS =		@ISC_IPV6_C@ @ISC_PK11_API_C@ \
 		app.c dir.c entropy.c errno2result.c file.c \
 		fsaccess.c interfaceiter.c keyboard.c net.c \
 		os.c resource.c socket.c stdio.c stdtime.c \
diff --git a/lib/isc/unix/include/Makefile.in b/lib/isc/unix/include/Makefile.in
index 46c243e..354e6c8 100644
--- a/lib/isc/unix/include/Makefile.in
+++ b/lib/isc/unix/include/Makefile.in
@@ -19,7 +19,7 @@ srcdir =	@srcdir@
 VPATH =		@srcdir@
 top_srcdir =	@top_srcdir@
 
-SUBDIRS =	isc
+SUBDIRS =	isc pkcs11
 TARGETS =
 
 @BIND9_MAKE_RULES@
diff --git a/lib/isc/unix/include/pkcs11/Makefile.in b/lib/isc/unix/include/pkcs11/Makefile.in
new file mode 100644
index 0000000..8b175f4
--- /dev/null
+++ b/lib/isc/unix/include/pkcs11/Makefile.in
@@ -0,0 +1,33 @@
+# Copyright (C) 2014  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# $Id: Makefile.in,v 1.4 2007/06/19 23:47:23 tbox Exp $
+
+srcdir =	@srcdir@
+VPATH =		@srcdir@
+top_srcdir =	@top_srcdir@
+
+HEADERS =	cryptoki.h
+SUBDIRS =
+TARGETS =
+
+@BIND9_MAKE_RULES@
+
+installdirs:
+	$(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${includedir}/pkcs11
+
+install:: installdirs
+	for i in ${HEADERS}; do \
+		${INSTALL_DATA} $(srcdir)/$$i ${DESTDIR}${includedir}/pkcs11 ; \
+	done
diff --git a/lib/isc/unix/include/pkcs11/cryptoki.h b/lib/isc/unix/include/pkcs11/cryptoki.h
new file mode 100644
index 0000000..7dc48b0
--- /dev/null
+++ b/lib/isc/unix/include/pkcs11/cryptoki.h
@@ -0,0 +1,66 @@
+/* cryptoki.h include file for PKCS #11. */
+/*
+ * Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/* $Revision: 1.3 $ */
+
+/*
+ * Portions Copyright RSA Security Inc.
+ *
+ * License to copy and use this software is granted provided that it is
+ * identified as "RSA Security Inc. PKCS #11 Cryptographic Token Interface
+ * (Cryptoki)" in all material mentioning or referencing this software.
+
+ * License is also granted to make and use derivative works provided that
+ * such works are identified as "derived from the RSA Security Inc. PKCS #11
+ * Cryptographic Token Interface (Cryptoki)" in all material mentioning or 
+ * referencing the derived work.
+
+ * RSA Security Inc. makes no representations concerning either the 
+ * merchantability of this software or the suitability of this software for
+ * any particular purpose. It is provided "as is" without express or implied
+ * warranty of any kind.
+ */
+
+/* This is a sample file containing the top level include directives
+ * for building Unix Cryptoki libraries and applications.
+ */
+
+#ifndef ___CRYPTOKI_H_INC___
+#define ___CRYPTOKI_H_INC___
+
+#define CK_PTR *
+
+#define CK_DEFINE_FUNCTION(returnType, name) \
+  returnType name
+
+#define CK_DECLARE_FUNCTION(returnType, name) \
+  returnType name
+
+#define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
+  returnType (* name)
+
+#define CK_CALLBACK_FUNCTION(returnType, name) \
+  returnType (* name)
+
+/* NULL is in unistd.h */
+#include <unistd.h>
+#define NULL_PTR NULL
+
+#undef CK_PKCS11_FUNCTION_INFO
+
+#include <pkcs11/pkcs11.h>
+
+#endif /* ___CRYPTOKI_H_INC___ */
diff --git a/lib/isc/unix/pk11_api.c b/lib/isc/unix/pk11_api.c
new file mode 100644
index 0000000..9ccb959
--- /dev/null
+++ b/lib/isc/unix/pk11_api.c
@@ -0,0 +1,673 @@
+/*
+ * Copyright (C) 2014  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id$ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <string.h>
+#include <dlfcn.h>
+
+#include <isc/log.h>
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/stdio.h>
+#include <isc/thread.h>
+#include <isc/util.h>
+
+#include <pkcs11/cryptoki.h>
+#include <pkcs11/pkcs11.h>
+
+#define KEEP_PKCS11_NAMES
+#include <pk11/pk11.h>
+#include <pk11/internal.h>
+
+static void *hPK11 = NULL;
+
+CK_RV
+pkcs_C_Initialize(CK_VOID_PTR pReserved) {
+	CK_C_Initialize sym;
+
+	if (hPK11 != NULL)
+		return (CKR_LIBRARY_ALREADY_INITIALIZED);
+
+	hPK11 = dlopen(pk11_get_lib_name(), RTLD_NOW);
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	sym = (CK_C_Initialize)dlsym(hPK11, "C_Initialize");
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(pReserved);
+}
+
+CK_RV
+pkcs_C_Finalize(CK_VOID_PTR pReserved) {
+	CK_C_Finalize sym;
+	CK_RV rv;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	sym = (CK_C_Finalize)dlsym(hPK11, "C_Finalize");
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	rv = (*sym)(pReserved);
+	if ((rv == CKR_OK) && (dlclose(hPK11) != 0))
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	hPK11 = NULL;
+	return (rv);
+}
+
+CK_RV
+pkcs_C_GetSlotList(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList,
+		   CK_ULONG_PTR pulCount)
+{
+	static CK_C_GetSlotList sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_GetSlotList)dlsym(hPK11, "C_GetSlotList");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(tokenPresent, pSlotList, pulCount);
+}
+
+CK_RV
+pkcs_C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) {
+	static CK_C_GetTokenInfo sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_GetTokenInfo)dlsym(hPK11, "C_GetTokenInfo");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(slotID, pInfo);
+}
+
+CK_RV
+pkcs_C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type,
+			CK_MECHANISM_INFO_PTR pInfo)
+{
+	static CK_C_GetMechanismInfo sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_GetMechanismInfo)dlsym(hPK11,
+						   "C_GetMechanismInfo");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(slotID, type, pInfo);
+}
+
+CK_RV
+pkcs_C_OpenSession(CK_SLOT_ID slotID, CK_FLAGS flags,
+		   CK_VOID_PTR pApplication,
+		   CK_RV  (*Notify) (CK_SESSION_HANDLE hSession,
+				     CK_NOTIFICATION event,
+				     CK_VOID_PTR pApplication),
+		   CK_SESSION_HANDLE_PTR phSession)
+{
+	static CK_C_OpenSession sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		hPK11 = dlopen(pk11_get_lib_name(), RTLD_NOW);
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_OpenSession)dlsym(hPK11, "C_OpenSession");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(slotID, flags, pApplication, Notify, phSession);
+}
+
+CK_RV
+pkcs_C_CloseSession(CK_SESSION_HANDLE hSession) {
+	static CK_C_CloseSession sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_CloseSession)dlsym(hPK11, "C_CloseSession");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession);
+}
+
+CK_RV
+pkcs_C_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType,
+	     CK_CHAR_PTR pPin, CK_ULONG usPinLen)
+{
+	static CK_C_Login sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_Login)dlsym(hPK11, "C_Login");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession, userType, pPin, usPinLen);
+}
+
+CK_RV
+pkcs_C_Logout(CK_SESSION_HANDLE hSession) {
+	static CK_C_Logout sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_Logout)dlsym(hPK11, "C_Logout");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession);
+}
+
+CK_RV
+pkcs_C_CreateObject(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate,
+		    CK_ULONG usCount, CK_OBJECT_HANDLE_PTR phObject)
+{
+	static CK_C_CreateObject sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_CreateObject)dlsym(hPK11, "C_CreateObject");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession, pTemplate, usCount, phObject);
+}
+
+CK_RV
+pkcs_C_DestroyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject) {
+	static CK_C_DestroyObject sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_DestroyObject)dlsym(hPK11, "C_DestroyObject");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession, hObject);
+}
+
+CK_RV
+pkcs_C_GetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
+			 CK_ATTRIBUTE_PTR pTemplate, CK_ULONG usCount)
+{
+	static CK_C_GetAttributeValue sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_GetAttributeValue)dlsym(hPK11,
+						    "C_GetAttributeValue");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession, hObject, pTemplate, usCount);
+}
+
+CK_RV
+pkcs_C_SetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
+			 CK_ATTRIBUTE_PTR pTemplate, CK_ULONG usCount)
+{
+	static CK_C_SetAttributeValue sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_SetAttributeValue)dlsym(hPK11,
+						    "C_SetAttributeValue");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession, hObject, pTemplate, usCount);
+}
+
+CK_RV
+pkcs_C_FindObjectsInit(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate,
+		       CK_ULONG usCount)
+{
+	static CK_C_FindObjectsInit sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_FindObjectsInit)dlsym(hPK11, "C_FindObjectsInit");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession, pTemplate, usCount);
+}
+
+CK_RV
+pkcs_C_FindObjects(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE_PTR phObject,
+		   CK_ULONG usMaxObjectCount, CK_ULONG_PTR pusObjectCount)
+{
+	static CK_C_FindObjects sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_FindObjects)dlsym(hPK11, "C_FindObjects");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession, phObject, usMaxObjectCount, pusObjectCount);
+}
+
+CK_RV
+pkcs_C_FindObjectsFinal(CK_SESSION_HANDLE hSession)
+{
+	static CK_C_FindObjectsFinal sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_FindObjectsFinal)dlsym(hPK11,
+						   "C_FindObjectsFinal");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession);
+}
+
+CK_RV
+pkcs_C_EncryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
+		   CK_OBJECT_HANDLE hKey)
+{
+	static CK_C_EncryptInit sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_EncryptInit)dlsym(hPK11, "C_EncryptInit");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession, pMechanism, hKey);
+}
+
+CK_RV
+pkcs_C_Encrypt(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData,
+	       CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData,
+	       CK_ULONG_PTR pulEncryptedDataLen)
+{
+	static CK_C_Encrypt sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_Encrypt)dlsym(hPK11, "C_Encrypt");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession, pData, ulDataLen,
+		      pEncryptedData, pulEncryptedDataLen);
+}
+
+CK_RV
+pkcs_C_DigestInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism) {
+	static CK_C_DigestInit sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_DigestInit)dlsym(hPK11, "C_DigestInit");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession, pMechanism);
+}
+
+CK_RV
+pkcs_C_DigestUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart,
+		    CK_ULONG ulPartLen)
+{
+	static CK_C_DigestUpdate sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_DigestUpdate)dlsym(hPK11, "C_DigestUpdate");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession, pPart, ulPartLen);
+}
+
+CK_RV
+pkcs_C_DigestFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pDigest,
+		   CK_ULONG_PTR pulDigestLen)
+{
+	static CK_C_DigestFinal sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_DigestFinal)dlsym(hPK11, "C_DigestFinal");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession, pDigest, pulDigestLen);
+}
+
+CK_RV
+pkcs_C_SignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
+		CK_OBJECT_HANDLE hKey)
+{
+	static CK_C_SignInit sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_SignInit)dlsym(hPK11, "C_SignInit");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession, pMechanism, hKey);
+}
+
+CK_RV
+pkcs_C_Sign(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData,
+	    CK_ULONG ulDataLen, CK_BYTE_PTR pSignature,
+	    CK_ULONG_PTR pulSignatureLen)
+{
+	static CK_C_Sign sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_Sign)dlsym(hPK11, "C_Sign");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession, pData, ulDataLen, pSignature, pulSignatureLen);
+}
+
+CK_RV
+pkcs_C_SignUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart,
+		  CK_ULONG ulPartLen)
+{
+	static CK_C_SignUpdate sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_SignUpdate)dlsym(hPK11, "C_SignUpdate");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession, pPart, ulPartLen);
+}
+
+CK_RV
+pkcs_C_SignFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature,
+		 CK_ULONG_PTR pulSignatureLen)
+{
+	static CK_C_SignFinal sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_SignFinal)dlsym(hPK11, "C_SignFinal");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession, pSignature, pulSignatureLen);
+}
+
+CK_RV
+pkcs_C_VerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
+		  CK_OBJECT_HANDLE hKey)
+{
+	static CK_C_VerifyInit sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_VerifyInit)dlsym(hPK11, "C_VerifyInit");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession, pMechanism, hKey);
+}
+
+CK_RV
+pkcs_C_Verify(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData,
+	      CK_ULONG ulDataLen, CK_BYTE_PTR pSignature,
+	      CK_ULONG ulSignatureLen)
+{
+	static CK_C_Verify sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_Verify)dlsym(hPK11, "C_Verify");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession, pData, ulDataLen, pSignature, ulSignatureLen);
+}
+
+CK_RV
+pkcs_C_VerifyUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart,
+		    CK_ULONG ulPartLen)
+{
+	static CK_C_VerifyUpdate sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_VerifyUpdate)dlsym(hPK11, "C_VerifyUpdate");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession, pPart, ulPartLen);
+}
+
+CK_RV
+pkcs_C_VerifyFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature,
+		   CK_ULONG ulSignatureLen)
+{
+	static CK_C_VerifyFinal sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_VerifyFinal)dlsym(hPK11, "C_VerifyFinal");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession, pSignature, ulSignatureLen);
+}
+
+CK_RV
+pkcs_C_GenerateKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
+		   CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount,
+		   CK_OBJECT_HANDLE_PTR phKey)
+{
+	static CK_C_GenerateKey sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_GenerateKey)dlsym(hPK11, "C_GenerateKey");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession, pMechanism, pTemplate, ulCount, phKey);
+}
+
+CK_RV
+pkcs_C_GenerateKeyPair(CK_SESSION_HANDLE hSession,
+		       CK_MECHANISM_PTR pMechanism,
+		       CK_ATTRIBUTE_PTR pPublicKeyTemplate,
+		       CK_ULONG usPublicKeyAttributeCount,
+		       CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
+		       CK_ULONG usPrivateKeyAttributeCount,
+		       CK_OBJECT_HANDLE_PTR phPrivateKey,
+		       CK_OBJECT_HANDLE_PTR phPublicKey)
+{
+	static CK_C_GenerateKeyPair sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_GenerateKeyPair)dlsym(hPK11, "C_GenerateKeyPair");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession,
+		      pMechanism,
+		      pPublicKeyTemplate,
+		      usPublicKeyAttributeCount,
+		      pPrivateKeyTemplate,
+		      usPrivateKeyAttributeCount,
+		      phPrivateKey,
+		      phPublicKey);
+}
+
+CK_RV
+pkcs_C_DeriveKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
+		 CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate,
+		 CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey)
+{
+	static CK_C_DeriveKey sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_DeriveKey)dlsym(hPK11, "C_DeriveKey");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession,
+		      pMechanism,
+		      hBaseKey,
+		      pTemplate,
+		      ulAttributeCount,
+		      phKey);
+}
+
+CK_RV
+pkcs_C_SeedRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed,
+		  CK_ULONG ulSeedLen)
+{
+	static CK_C_SeedRandom sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_SeedRandom)dlsym(hPK11, "C_SeedRandom");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession, pSeed, ulSeedLen);
+}
+
+CK_RV
+pkcs_C_GenerateRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR RandomData,
+		      CK_ULONG ulRandomLen)
+{
+	static CK_C_GenerateRandom sym = NULL;
+	static void *pPK11 = NULL;
+
+	if (hPK11 == NULL)
+		return (CKR_LIBRARY_FAILED_TO_LOAD);
+	if ((sym == NULL) || (hPK11 != pPK11)) {
+		pPK11 = hPK11;
+		sym = (CK_C_GenerateRandom)dlsym(hPK11, "C_GenerateRandom");
+	}
+	if (sym == NULL)
+		return (CKR_SYMBOL_RESOLUTION_FAILED);
+	return (*sym)(hSession, RandomData, ulRandomLen);
+}
-- 
2.1.0