[Cryptech-Commits] [user/sra/pkcs11] 01/02: First public commit of PKCS #11 implementation.

git at cryptech.is git at cryptech.is
Tue Apr 28 22:32:31 UTC 2015


This is an automated email from the git hooks/post-receive script.

sra at hactrn.net pushed a commit to branch master
in repository user/sra/pkcs11.

commit 0c8d1d765783bbc09cc1ca63ffdd233f0ce31613
Author: Rob Austein <sra at hactrn.net>
Date:   Tue Apr 28 15:29:12 2015 -0400

    First public commit of PKCS #11 implementation.
---
 .gitignore                        |    9 +
 GNUmakefile                       |   63 +
 README.md                         |  153 ++
 attributes.yaml                   |  646 +++++++
 pkcs11.c                          | 3802 +++++++++++++++++++++++++++++++++++++
 pkcs11.h                          |  301 +++
 pkcs11f.h                         |  912 +++++++++
 pkcs11t.h                         | 1789 +++++++++++++++++
 schema.sql                        |  117 ++
 scripts/build-attributes          |  403 ++++
 scripts/convert-schema.sed        |   66 +
 scripts/format-attribute-comments |   85 +
 scripts/test-hsmcheck             |  180 ++
 13 files changed, 8526 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d3522c6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+*.[oa]
+*.l[oa]
+*.so
+*.so.*
+*~
+.libs
+TAGS
+attributes.h
+schema.h
diff --git a/GNUmakefile b/GNUmakefile
new file mode 100644
index 0000000..a12aaa5
--- /dev/null
+++ b/GNUmakefile
@@ -0,0 +1,63 @@
+# (GNU) Makefile for Cryptech PKCS #11 implementation.
+#
+# Author: Rob Austein
+# Copyright (c) 2015, SUNET
+# 
+# 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 COPYRIGHT HOLDERS AND CONTRIBUTORS 
+# "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 
+# COPYRIGHT OWNER OR 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.
+
+CRYPTLIB_DIR = ../cryptlib/build
+SQLITE3_DIR  = ../sqlite3
+
+CFLAGS	+= -g -I${CRYPTLIB_DIR} -I${SQLITE3_DIR} -fPIC -DENABLE_CRYPTLIB_DEVICE=0 -Wall
+LIBS	:= ${CRYPTLIB_DIR}/libcl.a ${SQLITE3_DIR}/libsqlite3.a
+
+all: libpkcs11.so
+
+clean:
+	rm -rf pkcs11.o pkcs11.so libpkcs11.so* schema.h attributes.h
+
+distclean: clean
+	rm -f TAGS
+
+schema.h: schema.sql scripts/convert-schema.sed GNUmakefile
+	sed -f scripts/convert-schema.sed <schema.sql >schema.h
+
+attributes.h: attributes.yaml scripts/build-attributes GNUmakefile
+	python scripts/build-attributes attributes.yaml attributes.h
+
+pkcs11.o: pkcs11.c schema.h attributes.h
+	${CC} ${CFLAGS} -c $<
+
+pkcs11.so: pkcs11.o ${LIBS}
+	${CC} -shared -o $@ -Wl,-Bsymbolic-functions -Wl,-Bsymbolic -Wl,-z,noexecstack -g $^
+
+libpkcs11.so: pkcs11.so
+	objcopy -w -G 'C_*' $< $@
+
+tags: TAGS
+
+TAGS: *.[ch] ${CRYPTLIB_DIR}/cryptlib.h ${SQLITE3_DIR}/sqlite3.h
+	etags $^
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..be84f6b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,153 @@
+PKCS #11
+========
+
+## Introduction ##
+
+This is an implementation of the [PKCS11][] API for the [Cryptech][]
+project.  Like most PKCS #11 implementations, this one is incomplete
+and probably always will be: PKCS #11 is very open-ended, and the
+specification includes enough rope for an unwary developer to hang not
+only himself, but all of his friends, relations, and casual
+acquaintances.
+
+
+## Novel design features ##
+
+[PKCS11][]'s data model involves an n-level-deep hierarchy of object
+classes, which is somewhat tedious to implement correctly in C,
+particularly if one wants the correspondence between specification and
+code to be at all obvious.  In order to automate much of the drudge
+work involved, this implementation uses an external representation of
+the object class hierarchy, which is processed at compile time by a
+Python script to generate tables which drive the C code which performs
+the necessary type checking.
+
+
+## Current status ##
+
+As of this writing, the implementation supports only the RSA, SHA-1,
+and SHA-2 algorithms, but the design is intended to be extensible.
+
+Underlying cryptographic support is via [Cryptlib][], which may need
+to change (more on this below).
+
+The object store is currently implemented using [SQLite3][], which may
+also need to change (more on this below too).
+
+Testing to date has been done using the `bin/pkcs11/` tools from the
+BIND9 distribution and the `hsmcheck` tool from the OpenDNSSEC
+distribution.  Beyond the test results (such as they are) reported by
+these tools, the primary test of whether the PKCS #11 code is working
+as expected has been validation of the signed DNSSEC data generated by
+`hsmcheck -s`, via a script using [DNSPython][].
+
+In a nutshell, the current state is that the code runs without
+throwing any obvious errors, and generates what DNSPython thinks are
+good signatures.  Much more testing will be required, by a much wider
+variety of test tools.
+
+
+## Open issues ##
+
+Two critical design choices in this software were made with full
+knowledge that we might need to change them later:
+
+* The use of Cryptlib as a provider for the underlying cryptographic
+  operations, and
+
+* The use of SQLite3 as the object store.
+
+
+### Cryptlib ###
+
+[Cryptlib][] is a very nice piece of software, but it's not really
+designed for use under something like [PKCS11][].  It seemed worth a
+try anyway, because it would have been nice to be able to use
+Cryptlib's RPC mechanism, take advantage of Cryptlib's rule-based data
+protection system, and so forth, but implementing PKCS #11 requires
+doing various things which Cryptlib quite correctly discourages.  So,
+not a perfect fit.
+
+The current code works with Cryptlib's software RSA implementation,
+primarily due to an oddity of RSA: once one has handled the PKCS #1.5
+padding, the RSA signature and decryption (sic) operations are
+mathematically identical.  Fine so far as this goes, but:
+
+* This probably does not hold for other signature algorithms (well,
+  the math certainly does not, I haven't yet investigated what the
+  Cryptlib API does if one attempts to "decrypt" using an ECDSA key);
+  and
+
+* The Cryptlib manual says that there are some extra protections
+  around keys stored in hardware devices that would forbid using this
+  trick with an FPGA implementation of RSA.
+
+The latter (extra protections) is probably something we could work
+around if necessary, but the former may make this a moot point.
+
+All of the above notwithstanding, Cryptlib was a reasonable choice for
+the initial implementation, as we had no FPGA RSA to work with and
+needed to develop with *something*.  Surprisingly little effort has
+gone into Cryptlib-specific code (probably less than would have been
+required with, eg, OpenSSL, because the Cryptlib API is cleaner).
+
+Bottom line: we haven't lost anything by this approach, we're just not
+done yet.
+
+There are a few other issues with using Cryptlib in this context which
+I will detail if they become relevant, but I'll skip them for now
+since I don't think they'll end up being relevant here.
+
+
+### SQLite3 ###
+
+The choice of [SQLite3][] for the data store was made with several
+factors in mind:
+
+* Relative ease of development (it's all just SQL schemas and queries);
+
+* Relative ease of data normalization (foreign key constraints,
+  etcetera) and debugging (command line tool available for arbitrary
+  direct queries against stored data);
+
+* Licensing (SQLite3 is explictly public domain);
+
+* Support for embedded systems; and
+
+* Surprisingly small object code size (everything I found that was
+  significantly smaller had license issues, eg, gdbm).
+
+Overall, this has worked relatively well, but it's not necessarily
+what we want in the long run: it fails the minimum complexity test,
+and at least in the current implementation requires two separate kinds
+of storage, one for keys (currently a PKCS #15 keyring) and one for
+attributes (the SQLite3 database).
+
+The current implementation keeps much of the SQL data in an in-memory
+database: only "token objects" are stored in on disk.  This matches
+the required PKCS #11 semantics, and using the same mechanism to
+handle both session objects and token objects simplifies the code
+considerably, but it does mean that much of the SQL code is really
+just dealing with a weird encoding of in-memory data structures.
+
+At this point the schema may be stable enough that it would make sense
+to consider reimplementing without SQL.  It's not urgent as long as
+we're just doing proof-of-concept work, but is something we should
+consider seriously before deciding that this is ready for "production"
+status.
+
+
+## Copyright status ##
+
+The [PKCS11][] header files are "derived from the RSA Security Inc.
+PKCS #11 Cryptographic Token Interface (Cryptoki)".  See the
+`pkcs11*.h` header files for details.
+
+Code written for the [Cryptech][] project is under the usual Cryptech
+BSD-style license.
+
+[PKCS11]:    http://www.cryptsoft.com/pkcs11doc/STANDARD/       "PKCS #11"
+[Cryptlib]:  https://www.cs.auckland.ac.nz/~pgut001/cryptlib/   "Cryptlib"
+[SQLite3]:   https://www.sqlite.org/                            "SQLite3"
+[DNSPython]: http://www.dnspython.org/                          "DNSPython"
+[Cryptech]:  https://cryptech.is/                               "Cryptech"
diff --git a/attributes.yaml b/attributes.yaml
new file mode 100644
index 0000000..ad7a9b6
--- /dev/null
+++ b/attributes.yaml
@@ -0,0 +1,646 @@
+########################################################################
+#
+# PKCS #11 attribute definitions.
+#
+# The architecture of PKCS #11 is heavily based on an n-level-deep
+# object inheritance hierarcy.  Concrete object types inherit
+# attribute definitions, default values, usage constraints etc from
+# abstract types.  Fine if one happens to be writing in a language
+# that supports this, but C doesn't, and C++ is an abomination.
+#
+# So we handle all this inheritance-related fun here, by specifying
+# object types and attributes in a (relatively) readable way and using
+# a Python script to translate from this into "descriptors" (read-only
+# C tables) we can use to automate some of the most tedious attribute
+# checking in the C code.
+#
+# A secondary goal is to provide enough of a machine-readable
+# description of the PKCS #11 object hierarchy that we can use it to
+# drive automated test scripts, but that's not implemented yet.
+#
+# The base language here is YAML, with a somewhat ad-hoc data layout
+# on top of it.  The exact semantics are a bit of a moving target, but
+# the overall layout is:
+#
+# - The top-level data object is a YAML sequence (indicated in YAML by
+#   the leading "- " marker, converts to Python list).
+#
+# - Each entry in the sequence describes one object, represented as a
+#   YAML mapping (converts to Python dict).  Each object description
+#   has at least one required field ("name"), several optional fields,
+#   and one or more attribute descriptions.
+#
+# - An attribute description is a YAML mapping (Python dict)
+#   containing one or more fields describing the attribute.
+#
+# So the overall structure is a sequence of maps of maps.
+#
+# Attribute definitions within the hierarchy are combined, so that,
+# eg, the "rsa_public_key" type inherits the CKA_CLASS definition from
+# the the root object type, the CKA_KEY_TYPE definition from the "key"
+# type, a value of CKO_PUBLIC_KEY for the CKA_CLASS from the
+# "public_key" type, and provides its own value of CKK_RSA for the
+# CKA_KEY_TYPE.
+#
+# No doubt the error checking in the Python script could become much
+# more rigorous than it is now.
+#
+########################################################################
+#
+# Currently-defined object fields:
+#
+# - "name": String, required.  Name of this object class.  For
+#   concrete object types, this controls the name of the corresponding
+#   C descriptor.
+#
+# - "concrete": Boolean, optional, default false.  If true, this
+#   object type should generate a C descriptor.
+#
+# - "superclass": String, optional but present for all but one type.
+#   Contains name of parent type.
+#
+# New object fields may be defined at a later date as needed.
+#
+# Any entry in an object mapping whose key starts with "CKA_" is
+# assumed to be an attribute description.
+#
+# Keys in an object mapping which do not start with CKA_ and are not
+# known object fields should result in an error during parsing.
+#
+########################################################################
+# 
+# Currently-defined attribute fields:
+#
+# - "type": a PKCS #11 type name (CK_*) or one of a few other types
+#   described in the PKCS #11 specification: "rfc2279string",
+#   "biginteger", or "bytearray".
+#
+# - "default": data-value (see below) to be used as default if neither
+#    the application template nor the PKCS #11 software itself
+#    supplies an explicit value.  As a special case, the null string
+#    ("") means that the default value of the attribute is empty (this
+#    is allowed for a few rfc2279string attributes such as CKA_LABEL).
+#
+# - "value": data-value (see below) for this field.  If the
+#   application specifies a value for this attribute, it must match;
+#   otherwise, behaves like default.  The special handling of the null
+#   string ("") used with default does not apply here.
+#
+# - "footnotes": Sequence (Python list) of integers in the range 1-12.
+#   If present, this indicates that the attribute's definition in the
+#   PKCS #11 specification has been tagged with the listed footnote
+#   numbers from the "common footnotes" in "Table 15" of the
+#   specification.  These footnotes specify various constraints on the
+#   attributes behavior, and the Python script translates them into
+#   flags with more meaningful names, but since the specification
+#   itself is written in terms of these silly footnote numbers, using
+#   the footnote numbers in the YAML makes it easier to check the
+#   attribute descriptions in the YAML against the specification.
+#
+# - "unimplemented": boolean, default false.  If true, the attribute
+#   is known to be in the specification but is not (yet?) supported by
+#   the Python script and the C code.  This flag is set on a small
+#   number of relatively obscure attributes whose internal structure
+#   makes them tedious to represent in the attribute database; this is
+#   a placeholder for attributes which should be implemented
+#   eventually but which were not deemed to be on the critical path.
+#
+# As with object mappings, attribute mappings with unrecognized keys
+# should result in an error during parsing.
+#
+# "data-value" fields ("default" and "value") in an attribute can take
+# one of several forms:
+#
+# - A string value naming a PKCS #11 constant (eg, CK_TRUE);
+#
+# - A sequence of eight bit unsigned numeric values (ie, bytes)
+#   specifying a literal value; or
+#
+# - An integer (Python long) specifying a numeric value for a
+#   biginteger field, to be converted into a literal value using the
+#   smallest possible number of bytes.
+#
+########################################################################
+#
+# Author: Rob Austein
+# Copyright (c) 2015, SUNET
+# 
+# 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 COPYRIGHT HOLDERS AND CONTRIBUTORS 
+# "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 
+# COPYRIGHT OWNER OR 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.
+#
+########################################################################
+
+###
+# Root of the object tree
+###
+
+- name: object
+
+  CKA_CLASS:
+    footnotes: [1]
+    type: CK_OBJECT_CLASS
+
+###
+# Storage objects
+###
+
+- name: storage
+  superclass: object
+
+  CKA_TOKEN:
+    type: CK_BBOOL
+    default: CK_FALSE
+
+  CKA_PRIVATE:
+    type: CK_BBOOL
+    default: CK_TRUE
+
+  CKA_MODIFIABLE:
+    type: CK_BBOOL
+    default: CK_TRUE
+
+  CKA_LABEL:
+    type: rfc2279string
+    default: ""
+
+###
+# Data objects
+###
+
+- name: data
+  superclass: storage
+
+  CKA_CLASS:
+    value: CKO_DATA
+
+  CKA_APPLICATION:
+    type: rfc2279string
+    default: ""
+
+  CKA_OBJECT_ID:
+    type: bytearray
+    default: ""
+
+  CKA_VALUE:
+    type: bytearray
+    default: ""
+
+###
+# Certificate objects
+###
+
+- name: certificate
+  superclass: storage
+
+  CKA_CLASS:
+    value: CKO_CERTIFICATE
+
+  CKA_CERTIFICATE_TYPE:
+    footnotes: [1]
+    type: CK_CERTIFICATE_TYPE
+
+  CKA_TRUSTED:
+    footnotes: [10]
+    type: CK_BBOOL
+    default: CK_FALSE
+
+  CKA_CERTIFICATE_CATEGORY:
+    type: CK_ULONG
+    default: 0
+
+  CKA_CHECK_VALUE:
+    type: bytearray
+
+  CKA_START_DATE:
+    type: CK_DATE
+    default: ""
+
+  CKA_END_DATE:
+    type: CK_DATE
+    default: ""
+
+###
+# X.509 public key certificate objects
+###
+
+# NB: For some reason, numeric footnotes in the table describing X.509
+#     certificate attributes are NOT the common attribute footnotes
+#     from Table 15.  Be careful!
+
+- name: x509_public_key_certificate
+  superclass: certificate
+
+  CKA_SUBJECT:
+    type: bytearray
+
+  CKA_ID:
+    type: bytearray
+    default: ""
+
+  CKA_ISSUER:
+    type: bytearray
+    default: ""
+
+  CKA_SERIAL_NUMBER:
+    type: bytearray
+    default: ""
+
+  CKA_VALUE:
+    type: bytearray
+
+  CKA_URL:
+    type: rfc2279string
+    default: ""
+
+  CKA_HASH_OF_SUBJECT_PUBLIC_KEY:
+    type: bytearray
+    default: ""
+
+  CKA_HASH_OF_ISSUER_PUBLIC_KEY:
+    type: bytearray
+    default: ""
+
+  CKA_JAVA_MIDP_SECURITY_DOMAIN:
+    type: CK_ULONG
+    default: 0
+
+  CKA_NAME_HASH_ALGORITHM:
+    type: CK_MECHANISM_TYPE
+    default: CKM_SHA_1
+
+###
+# Key objects
+###
+
+- name: key
+  superclass: storage
+
+  CKA_KEY_TYPE:
+    footnotes: [1, 5]
+    type: CK_KEY_TYPE
+
+  CKA_ID:
+    footnotes: [8]
+    type: bytearray
+    default: ""
+
+  CKA_START_DATE:
+    footnotes: [8]
+    type: CK_DATE
+    default: ""
+
+  CKA_END_DATE:
+    footnotes: [8]
+    type: CK_DATE
+    default: ""
+
+  CKA_DERIVE:
+    footnotes: [8]
+    type: CK_BBOOL
+    default: CK_FALSE
+
+  CKA_LOCAL:
+    footnotes: [2, 4, 6]
+    type: CK_BBOOL
+    default: CK_FALSE
+
+  CKA_KEY_GEN_MECHANISM:
+    footnotes: [2, 4, 6]
+    type: CK_MECHANISM_TYPE
+    default: CK_UNAVAILABLE_INFORMATION
+
+  CKA_ALLOWED_MECHANISMS:
+    unimplemented: true
+
+###
+# Public key objects
+###
+
+- name: public_key
+  superclass: key
+
+  CKA_CLASS:
+    value: CKO_PUBLIC_KEY
+
+  CKA_SUBJECT:
+    footnotes: [8]
+    type: bytearray
+    default: ""
+
+  CKA_ENCRYPT:
+    footnotes: [8, 9]
+    type: CK_BBOOL
+    default: CK_FALSE
+
+  CKA_VERIFY:
+    footnotes: [8, 9]
+    type: CK_BBOOL
+    default: CK_FALSE
+
+  CKA_VERIFY_RECOVER:
+    footnotes: [8, 9]
+    type: CK_BBOOL
+    default: CK_FALSE
+
+  CKA_WRAP:
+    footnotes: [8, 9]
+    type: CK_BBOOL
+    default: CK_FALSE
+
+  CKA_TRUSTED:
+    footnotes: [10]
+    type: CK_BBOOL
+    default: CK_FALSE
+
+  CKA_WRAP_TEMPLATE:
+    unimplemented: true
+
+###
+# Private key objects
+###
+
+- name: private_key
+  superclass: key
+
+  CKA_CLASS:
+    value: CKO_PRIVATE_KEY
+
+  CKA_SUBJECT:
+    footnotes: [8]
+    type: bytearray
+    default: ""
+
+  CKA_SENSITIVE:
+    footnotes: [8, 9, 11]
+    type: CK_BBOOL
+    default: CK_TRUE
+
+  CKA_DECRYPT:
+    footnotes: [8, 9]
+    type: CK_BBOOL
+    default: CK_FALSE
+
+  CKA_SIGN:
+    footnotes: [8, 9]
+    type: CK_BBOOL
+    default: CK_FALSE
+
+  CKA_SIGN_RECOVER:
+    footnotes: [8, 9]
+    type: CK_BBOOL
+    default: CK_FALSE
+
+  CKA_UNWRAP:
+    footnotes: [8, 9]
+    type: CK_BBOOL
+    default: CK_FALSE
+
+  CKA_EXTRACTABLE:
+    footnotes: [8, 9, 12]
+    type: CK_BBOOL
+    default: CK_FALSE
+
+  CKA_ALWAYS_SENSITIVE:
+    footnotes: [2, 4, 6]
+    type: CK_BBOOL
+
+  CKA_NEVER_EXTRACTABLE:
+    footnotes: [2, 4, 6]
+    type: CK_BBOOL
+
+  CKA_WRAP_WITH_TRUSTED:
+    footnotes: [11]
+    type: CK_BBOOL
+    default: CK_FALSE
+
+  CKA_UNWRAP_TEMPLATE:
+    unimplemented: true
+
+###
+# Secret key objects
+###
+
+- name: secret_key
+  superclass: key
+
+  CKA_CLASS:
+    value: CKO_SECRET_KEY
+
+  CKA_SENSITIVE:
+    footnotes: [8, 11]
+    type: CK_BBOOL
+    default: CK_FALSE
+
+  CKA_ENCRYPT:
+    footnotes: [8, 9]
+    type: CK_BBOOL
+
+  CKA_DECRYPT:
+    footnotes: [8, 9]
+    type: CK_BBOOL
+
+  CKA_SIGN:
+    footnotes: [8, 9]
+    type: CK_BBOOL
+
+  CKA_VERIFY:
+    footnotes: [8, 9]
+    type: CK_BBOOL
+
+  CKA_WRAP:
+    footnotes: [8, 9]
+    type: CK_BBOOL
+
+  CKA_UNWRAP:
+    footnotes: [8, 9]
+    type: CK_BBOOL
+
+  CKA_EXTRACTABLE:
+    footnotes: [8, 9, 12]
+    type: CK_BBOOL
+
+  CKA_ALWAYS_SENSITIVE:
+    footnotes: [2, 4, 6]
+    type: CK_BBOOL
+
+  CKA_NEVER_EXTRACTABLE:
+    footnotes: [2, 4, 6]
+    type: CK_BBOOL
+
+  CKA_CHECK_VALUE:
+    type: bytearray
+
+  CKA_WRAP_WITH_TRUSTED:
+    footnotes: [11]
+    type: CK_BBOOL
+    default: CK_FALSE
+
+  CKA_TRUSTED:
+    footnotes: [10]
+    type: CK_BBOOL
+    default: CK_FALSE
+
+  CKA_WRAP_TEMPLATE:
+    unimplemented: true
+
+  CKA_UNWRAP_TEMPLATE:
+    unimplemented: true
+
+###
+# Domain parameter objects
+###
+
+- name: domain_parameters
+  superclass: storage
+
+  CKA_CLASS:
+    value: CKO_DOMAIN_PARAMETERS
+
+  CKA_KEY_TYPE:
+    footnotes: [1]
+    type: CK_KEY_TYPE
+
+  CKA_LOCAL:
+    footnotes: [2, 4]
+    type: CK_BBOOL
+
+###
+# Mechanism objects
+###
+
+- name: mechanism
+  superclass: object
+
+  CKA_CLASS:
+    value: CKO_MECHANISM_INFO
+
+  CKA_MECHANISM_TYPE:
+    type: CK_MECHANISM_TYPE
+
+###
+# RSA public key objects
+###
+
+- name: rsa_public_key
+  superclass: public_key
+  concrete: true
+
+  CKA_KEY_TYPE:
+    value: CKK_RSA
+
+  CKA_MODULUS:
+    footnotes: [1, 4]
+    type: biginteger
+
+  CKA_MODULUS_BITS:
+    footnotes: [2, 3]
+    type: CK_ULONG
+
+  CKA_PUBLIC_EXPONENT:
+    footnotes: [1]
+    type: biginteger
+    value: 0x10001              # We only allow F4 as public exponent
+    
+###
+# RSA private key objects
+###
+
+- name: rsa_private_key
+  superclass: private_key
+  concrete: true
+
+  CKA_KEY_TYPE:
+    value: CKK_RSA
+
+  CKA_MODULUS:
+    footnotes: [1, 4, 6]
+    type: biginteger
+
+  CKA_PUBLIC_EXPONENT:
+    footnotes: [4, 6]
+    type: biginteger
+    value: 0x10001              # We only allow F4 as public exponent
+
+  CKA_PRIVATE_EXPONENT:
+    footnotes: [1, 4, 6, 7]
+    type: biginteger
+
+  CKA_PRIME_1:
+    footnotes: [4, 6, 7]
+    type: biginteger
+
+  CKA_PRIME_2:
+    footnotes: [4, 6, 7]
+    type: biginteger
+
+  CKA_EXPONENT_1:
+    footnotes: [4, 6, 7]
+    type: biginteger
+
+  CKA_EXPONENT_2:
+    footnotes: [4, 6, 7]
+    type: biginteger
+
+  CKA_COEFFICIENT:
+    footnotes: [4, 6, 7]
+    type: biginteger
+
+###
+# Eliptic curve public key objects
+###
+
+- name: ec_public_key
+  superclass: public_key
+  concrete: true
+
+  CKA_KEY_TYPE:
+    value: CKK_EC
+
+  CKA_EC_PARAMS:
+    footnotes: [1, 3]
+    type: bytearray
+
+  CKA_EC_POINT:
+    footnotes: [1, 4]
+    type: bytearray
+
+###
+# Elliptic curve private key objects
+###
+
+- name: ec_private_key
+  superclass: private_key
+  concrete: true
+
+  CKA_KEY_TYPE:
+    value: CKK_EC
+
+  CKA_EC_PARAMS:
+    footnotes: [1, 4, 6]
+    type: bytearray
+
+  CKA_VALUE:
+    footnotes: [1, 4, 6, 7]
+    type: biginteger
diff --git a/pkcs11.c b/pkcs11.c
new file mode 100644
index 0000000..5b7576f
--- /dev/null
+++ b/pkcs11.c
@@ -0,0 +1,3802 @@
+/* 
+ * pkcs11.c
+ * --------
+ *
+ * This is a partial implementation of PKCS #11 on top of Cryptlib on
+ * top of a HAL connecting to the Cryptech FPGA cores.
+ *
+ * This is still at a very early stage and should not (yet?) be used
+ * for any serious purpose.  Among other things, it's not yet entirely
+ * clear whether this approach really is workable.
+ *
+ * Author: Rob Austein
+ * Copyright (c) 2015, SUNET
+ * 
+ * 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 COPYRIGHT HOLDERS AND CONTRIBUTORS 
+ * "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 
+ * COPYRIGHT OWNER OR 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.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h> 
+#include <assert.h>
+
+#include <sqlite3.h>
+
+#include "cryptlib.h"
+
+/*
+ * Magic PKCS #11 macros that must be defined before including
+ * pkcs11.h.  For now these are only the Unix versions, add others
+ * later (which may require minor refactoring).
+ */
+
+#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)
+#ifndef NULL_PTR
+#define NULL_PTR                                        NULL
+#endif
+
+#include "pkcs11.h"
+#include "attributes.h"
+
+/*
+ * This PKCS #11 implementation is hardwired with one slot, the token
+ * for which is always present (so we return the same answer
+ * regardless of the value of tokenPresent).
+ */
+
+#define P11_ONE_AND_ONLY_SLOT   0
+
+/*
+ * Placeholders for PIN length limits.  Figure out real values later.
+ */
+
+#warning Figure out PIN length limits
+#define P11_MIN_PIN_LENGTH      16
+#define P11_MAX_PIN_LENGTH      4096
+
+/*
+ * Version numbers.  Placeholders for now.  Cryptlib has a version
+ * number, but from PKCS #11's point of view, Cryptlib is part of the
+ * "hardware", and we're probably going to need something other than
+ * Cryptlib's version number for the hardware, because we have to
+ * represent the version number of the attached Cryptech FPGA cores.
+ *
+ * Software version number is just the version of this PKCS #11
+ * implementation.  Probably.
+ */
+
+#warning Figure out hardware and software version numbers
+#define P11_VERSION_SW_MAJOR    0
+#define P11_VERSION_SW_MINOR    0
+#define P11_VERSION_HW_MAJOR    0
+#define P11_VERSION_HW_MINOR    0
+
+/*
+ * A value that can't possibly be a valid Cryptlib handle.
+ */
+
+#ifndef CRYPT_HANDLE_NONE
+#define CRYPT_HANDLE_NONE       (-1)
+#endif
+
+/*
+ * Whether to enable hardware (FPGA) support.  This option may go away
+ * eventually, once we have enough algorithms implemented in Verilog.
+ */
+
+#ifndef ENABLE_CRYPTLIB_DEVICE
+#define ENABLE_CRYPTLIB_DEVICE  1
+#endif
+
+/*
+ * Whehter to enable software algorithms.  This is not really an
+ * option at the moment, as the code won't run or even build properly
+ * if this is disabled.  It's a placeholder to let us flag bits of
+ * code that probably should go away if and when we're doing all the
+ * crypto algorithms on the FPGA.
+ */
+
+#ifndef ENABLE_CRYPTLIB_SOFTWARE
+#define ENABLE_CRYPTLIB_SOFTWARE 1
+#endif
+
+#if !ENABLE_CRYPTLIB_SOFTWARE
+#error Code will not work correctly with software algorithm support disabled
+#endif
+
+/*
+ * Debugging control.
+ */
+
+#ifndef DEBUG_SQL
+#define DEBUG_SQL       1
+#endif
+
+/*
+ * Default filename for SQL database lives.  Can be overriden at
+ * runtime by setting PKCS11_DATABASE environment variable.
+ */
+
+#ifndef SQL_DATABASE
+#define SQL_DATABASE ".cryptech-pkcs11.db"
+#endif
+
+/*
+ * Default name for PKCS #15 keyring.  Can be overriden at runtime by
+ * setting PKCS11_KEYRING environment variable.
+ *
+ * In the long term this probably goes away, as all keys should live
+ * behind the Cryptlib hardware interface, but we need something for
+ * initial testing.
+ */
+
+#ifndef PKCS15_KEYRING
+#define PKCS15_KEYRING ".cryptech-pkcs11.p15"
+#endif
+
+

+
+/*
+ * PKCS #11 session.
+ */
+
+/*
+ * Cryptlib handles in the session structure are defined via a silly
+ * macro so that we can automate initialization and finalization
+ * without accidently missing any of the handles.
+ *
+ * Note that the encryption and decryption cases (other than raw
+ * encryption with no symmetric cipher algorithm) will need to use the
+ * enveloping API: see pp 61-62, 70-71, 190 of the Cryptlib manual.
+ * We may not really need to keep all the contexts around in this case
+ * once we've bound them into the envelope, drive off that bridge when
+ * we get to it.
+ *
+ * Syntax: One handle per line, as calls to to-be-defined macros
+ * SESSION_CRYPTLIB_CONTEXT() or SESSION_CRYPTLIB_ENVELOPE(), entries
+ * separated by semicolons, no semicolon after last entry.
+ */
+
+#define SESSION_CRYPTLIB_HANDLES                        \
+  SESSION_CRYPTLIB_CONTEXT(sign_key_context);           \
+  SESSION_CRYPTLIB_CONTEXT(sign_digest_context);        \
+  SESSION_CRYPTLIB_CONTEXT(verify_key_context);         \
+  SESSION_CRYPTLIB_CONTEXT(verify_digest_context);      \
+  SESSION_CRYPTLIB_CONTEXT(digest_context)
+
+#if 0
+  SESSION_CRYPTLIB_CONTEXT(encrypt_key_context);
+  SESSION_CRYPTLIB_CONTEXT(encrypt_cipher_context);
+  SESSION_CRYPTLIB_CONTEXT(decrypt_key_context);
+  SESSION_CRYPTLIB_CONTEXT(decrypt_cipher_context);
+  SESSION_CRYPTLIB_ENVELOPE(encrypt_envelope);
+  SESSION_CRYPTLIB_ENVELOPE(decrypt_envelope);
+#endif
+
+typedef struct p11_session {
+  CK_SESSION_HANDLE handle;             /* Session handle */
+  struct p11_session *link;             /* Next session in list */
+  CK_STATE state;                       /* State (CKS_*) of this session */
+  CK_NOTIFY notify;                     /* Notification callback */
+  CK_VOID_PTR application;              /* Application data */
+  sqlite3_stmt *find_query;             /* FindObject*() query state */
+  int find_query_done;                  /* find_query has terminated */
+
+#define SESSION_CRYPTLIB_CONTEXT(_ctx_)         CRYPT_CONTEXT  _ctx_
+#define SESSION_CRYPTLIB_ENVELOPE(_env_)        CRYPT_ENVELOPE _env_
+  SESSION_CRYPTLIB_HANDLES;
+#undef  SESSION_CRYPTLIB_ENVELOPE
+#undef  SESSION_CRYPTLIB_CONTEXT
+
+} p11_session_t;
+
+/*
+ * PKCS #11 handle management.  PKCS #11 has two kinds of handles:
+ * session handles and object handles.  We subdivide object handles
+ * into token object handles (handles for objects that live on the
+ * token) and session object handles (handles for objects that live
+ * only as long as the session does), and we steal a bit of the object
+ * handle as a flag to distinguish between our two kinds of object
+ * handles, considerably simplifing the objected-related SQL code.
+ */
+
+typedef enum {
+  handle_flavor_session,
+  handle_flavor_token_object,
+  handle_flavor_session_object
+} handle_flavor_t;
+
+#define FLAG_HANDLE_TOKEN               0x80000000
+
+#define is_token_handle(_handle_)       (((_handle_) & FLAG_HANDLE_TOKEN) != 0)
+
+

+
+/*
+ * Current logged-in user.
+ */
+
+static enum {
+  not_logged_in,
+  logged_in_as_user,
+  logged_in_as_so
+} logged_in_as = not_logged_in;
+
+/*
+ * PKCS #11 sessions for this application.
+ */
+
+static p11_session_t *p11_sessions;
+
+/*
+ * SQL database.
+ */
+
+static sqlite3 *sqldb = NULL;
+
+/*
+ * Saved copy of PIN (sigh).
+ *
+ * We'd like to do better than this, but as long as we're supporting
+ * software keysets which require a password every time we read or
+ * write a private key, we need this.  Once we're dealing with just
+ * the hardware interface we should be able to skip this.
+ */
+
+#if ENABLE_CRYPTLIB_SOFTWARE
+static char *pin = NULL;
+#endif
+
+/*
+ * Next PKCS #11 handle to allocate.  We use a single handle space for
+ * both session and object handles, and we just keep incrementing
+ * until it wraps, to reduce the amount of time we have to spend
+ * on SQL probes to avoid handle conflicts.
+ */
+
+static CK_ULONG next_handle;
+
+/*
+ * Cryptlib handle for hardware device.
+ */
+
+#if ENABLE_CRYPTLIB_DEVICE
+static CRYPT_DEVICE cryptlib_device = CRYPT_HANDLE_NONE;
+#endif
+
+/*
+ * Filenames for SQL database and PKCS #15 keyring.
+ */
+
+static char *database_filename = NULL;
+static char *keyring_filename = NULL;
+
+

+
+/*
+ * Syntactic sugar for functions returning CK_RV complex enough to
+ * need cleanup actions on failure.  Also does very basic logging
+ * for debug-by-printf().
+ */
+
+#define lose(_ck_rv_code_)                                              \
+  do {                                                                  \
+    rv = (_ck_rv_code_);                                                \
+    fprintf(stderr, "%s:%u: %s\n", __FILE__, __LINE__, #_ck_rv_code_);  \
+    goto fail;                                                          \
+  } while (0)
+
+/*
+ * Error checking for SQLite calls.
+ */
+
+#if DEBUG_SQL
+#define sql_whine(_expr_)                                               \
+  (fprintf(stderr, "%s:%u: %s returned %s\n",                           \
+           __FILE__, __LINE__, #_expr_, sqlite3_errmsg(sqldb)),         \
+   sql_breakpoint())
+#else
+#define sql_whine(_expr_)                                               \
+  ((void) 0)
+#endif
+
+#define sql_check(_good_, _expr_)                                       \
+  ((_expr_) == (_good_) ? 1 : (sql_whine(_expr_), 0))
+
+#define sql_check_ok(_expr_)    sql_check(SQLITE_OK, _expr_)
+#define sql_check_row(_expr_)   sql_check(SQLITE_ROW, _expr_)
+#define sql_check_done(_expr_)  sql_check(SQLITE_DONE, _expr_)
+#define sql_whine_step()        sql_whine(sqlite3_step())
+
+

+
+/*
+ * Filename utilities.
+ */
+
+/*
+ * Construct name of configuration file if we don't already have it cached.
+ */
+
+static char *cf_generate(char **fn,                     /* Output filename */
+                         const char * const env,        /* Name of environment variable */
+                         const char * const base)       /* Filename in home directory */
+{
+  char *var;
+
+  assert(fn != NULL && env != NULL && base != NULL);
+
+  if (*fn != NULL)
+    return *fn;
+
+  if ((var = getenv(env)) != NULL && (*fn = malloc(strlen(var) + 1)) != NULL)
+    strcpy(*fn, var);
+
+  else if (var == NULL && (var = getenv("HOME")) != NULL && (*fn = malloc(strlen(var) + strlen(base) + 2)) != NULL)
+    sprintf(*fn, "%s/%s", var, base);
+  
+  else if (var == NULL && (*fn = malloc(strlen(base) + 1)) != NULL)
+    strcpy(*fn, base);
+
+  return *fn;
+}
+
+/*
+ * Closures over cf_generate() for particular filenames.
+ */
+
+static char *cf_sql_database(void)
+{
+  return cf_generate(&database_filename, "PKCS11_DATABASE", SQL_DATABASE);
+}
+
+static char *cf_pkcs15_keyring(void)
+{
+  return cf_generate(&keyring_filename, "PKCS11_KEYRING", PKCS15_KEYRING);
+}
+
+

+
+/*
+ * Wrappers around some of Cryptlib's context functions, so that the
+ * rest of the code can mostly ignore whether a particular algorithm
+ * is implemented in hardware or not.  In theory, we could achieve
+ * this simply by always trying cryptDeviceCreateContext() and
+ * checking its return code to see whether we should fall back to
+ * CryptCreateContext(), but for the moment I'm more comfortable with
+ * explictly coding the list of algorithms we expect to be supported
+ * here.  This may change at some future date, once the HAL code is a
+ * little further along.
+ */
+
+static int cryptlib_implemented_in_hardware(const CRYPT_ALGO_TYPE algo)
+{
+#if ENABLE_CRYPTLIB_DEVICE
+  switch (algo) {
+  case CRYPT_ALGO_YOU_NEED_TO_SPECIFY_SOMETHING_HERE_BOZO:
+    return 1;
+  }
+#endif
+
+  return 0;
+}
+
+/*
+ * Create a context -- hardware if supported, software otherwise.
+ */
+
+static C_RET cryptlib_create_context(CRYPT_CONTEXT *ctx, const CRYPT_ALGO_TYPE algo)
+{
+#if ENABLE_CRYPTLIB_DEVICE
+  if (cryptlib_implemented_in_hardware(algo))
+    return cryptDeviceCreateContext(cryptlib_device, ctx, algo);
+#endif
+
+  return cryptCreateContext(ctx, CRYPT_UNUSED, algo);
+}
+
+/*
+ * Store a key.  This is a no-op for hardware contexts (the hardware
+ * device functions as a key store), but requires writing to the PKCS
+ * #15 keyring for software contexts.
+ */
+
+static C_RET cryptlib_store_key(const CRYPT_CONTEXT ctx)
+{
+  CRYPT_KEYSET keyset;
+  int ret, algo;
+
+  if ((ret = cryptGetAttribute(ctx, CRYPT_CTXINFO_ALGO, &algo)) != CRYPT_OK)
+    return ret;
+
+  if (cryptlib_implemented_in_hardware(algo))
+    return CRYPT_OK;
+
+  ret = cryptKeysetOpen(&keyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, cf_pkcs15_keyring(), CRYPT_KEYOPT_NONE);
+
+  if (ret == CRYPT_ERROR_OPEN || ret == CRYPT_ERROR_NOTFOUND)
+    ret = cryptKeysetOpen(&keyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, cf_pkcs15_keyring(), CRYPT_KEYOPT_CREATE);
+
+  if (ret != CRYPT_OK)
+    return ret;
+
+  ret = cryptAddPrivateKey(keyset, ctx, pin);
+
+  cryptKeysetClose(keyset);
+
+  return ret;
+}
+
+/*
+ * Load a key.  This creates a new context.
+ */
+
+static C_RET cryptlib_load_key(CRYPT_CONTEXT *ctx, const char *keyid)
+{
+  CRYPT_KEYSET keyset;
+  int ret;
+
+  assert(ctx != NULL);
+
+  *ctx = CRYPT_HANDLE_NONE;
+
+#if ENABLE_CRYPTLIB_DEVICE
+  if ((ret = cryptGetPrivateKey(cryptlib_device, ctx, CRYPT_KEYID_NAME, keyid, NULL)) == CRYPT_OK)
+    return ret;
+#endif
+
+  if ((ret = cryptKeysetOpen(&keyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, cf_pkcs15_keyring(), CRYPT_KEYOPT_READONLY)) != CRYPT_OK)
+    return ret;
+
+  ret = cryptGetPrivateKey(keyset, ctx, CRYPT_KEYID_NAME, keyid, pin);
+
+  cryptKeysetClose(keyset);
+
+  return ret;
+}
+
+/*
+ * Delete a key.
+ */
+
+static C_RET cryptlib_delete_key(const char *keyid)
+{
+  CRYPT_KEYSET keyset;
+  int ret;
+
+#if ENABLE_CRYPTLIB_DEVICE
+  if ((ret = cryptDeleteKey(cryptlib_device, CRYPT_KEYID_NAME, keyid)) == CRYPT_OK)
+    return ret;
+#endif
+
+  if ((ret = cryptKeysetOpen(&keyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, cf_pkcs15_keyring(), CRYPT_KEYOPT_NONE)) != CRYPT_OK)
+    return ret;
+
+  ret = cryptDeleteKey(keyset, CRYPT_KEYID_NAME, keyid);
+
+  cryptKeysetClose(keyset);
+
+  return ret;
+}
+
+

+
+/*
+ * SQL utilities.
+ */
+
+/*
+ * Hook on which to hang a debugger breakpoint on SQL errors.
+ */
+
+#if DEBUG_SQL
+static void sql_breakpoint(void)
+{
+  fprintf(stderr, "[sql_breakpoint]\n");
+}
+#endif
+
+/*
+ * Execute SQL code that doesn't require a prepared query.
+ */
+
+static int sql_exec(const char *cmd)
+{
+  char *msg = NULL;
+
+  if (sql_check_ok(sqlite3_exec(sqldb, cmd, NULL, NULL, &msg)))
+    return 1;
+
+#if DEBUG_SQL
+  if (msg != NULL)
+    fprintf(stderr, "[%s]\n", msg);
+#endif
+
+  return 0;
+}
+
+/*
+ * Initialize SQL.  This includes loading our schema, portions of
+ * which live in the temp (memory) database thus always need to be
+ * created on startup.
+ */
+
+static int sql_init(void)
+{
+  static const char schema[] =
+#include "schema.h"
+    ;
+
+  assert(sqldb == NULL);
+
+  return sql_check_ok(sqlite3_open(cf_sql_database(), &sqldb)) && sql_exec(schema);
+}
+
+/*
+ * Shut down SQL.
+ *
+ * Yes, this can return failure, although it's not clear what we're
+ * meant to do about that if the application is going to shut down
+ * regardless of what we do.  I guess we could loop retrying a few
+ * times for errors like SQLITE_BUSY, but that's about it.
+ */
+
+static int sql_fini(void)
+{
+  if (!sql_check_ok(sqlite3_close(sqldb)))
+    return 0;
+
+  sqldb = NULL;
+  return 1;
+}
+
+/*
+ * GCC attribute declaration to help catch format string errors,
+ * ignored by other compilers.
+ */
+
+#ifdef __GNUC__
+static int sql_prepare(sqlite3_stmt **q,
+                       const char *format, ...)
+  __attribute__ ((format (printf, 2, 3)));
+#endif
+
+/*
+ * Prepare an SQLite3 query, using vsnprintf() to format the query.
+ *
+ *             WARNING WARNING WARNING WARNING
+ *
+ * Do not use this formatting mechanism for anything involving
+ * user-supplied data.  It's only intended to handle things like
+ * selecting between two parallel table structures or queries using
+ * manifest constants that are only available in C header files.
+ */
+
+static int sql_prepare(sqlite3_stmt **q, const char *format, ...)
+{
+  char buffer[2048];
+  va_list ap;
+  size_t n;
+
+  va_start(ap, format);
+  n = vsnprintf(buffer, sizeof(buffer), format, ap);
+  va_end(ap);
+
+  if (n >= sizeof(buffer))
+    return SQLITE_TOOBIG;
+
+  return sqlite3_prepare_v2(sqldb, buffer, -1, q, NULL);
+}
+
+

+
+/*
+ * (Extremely) minimal ASN.1 parser, just good enough to pick a few
+ * fields out of something like a well-formed ASN.1 DER representation
+ * of a certificate.
+ */
+
+#define ASN1_UNIVERSAL          0x00
+#define ASN1_APPLICATION        0x40
+#define ASN1_CONTEXT_SPECIFIC   0x80
+#define ASN1_PRIVATE            0xC0
+
+#define ASN1_PRIMITIVE          0x00
+#define ASN1_CONSTRUCTED        0x20
+
+#define ASN1_TAG_MASK           0x1F
+
+#define ASN1_INTEGER            (ASN1_PRIMITIVE   | 0x02)
+#define ASN1_BIT_STRING         (ASN1_PRIMITIVE   | 0x03)
+#define ASN1_OCTET_STRING       (ASN1_PRIMITIVE   | 0x04)
+#define ASN1_NULL               (ASN1_PRIMITIVE   | 0x05)
+#define ASN1_OBJECT_IDENTIFIER  (ASN1_PRIMITIVE   | 0x06)
+#define ASN1_SEQUENCE           (ASN1_CONSTRUCTED | 0x10)
+#define ASN1_SET                (ASN1_CONSTRUCTED | 0x11)
+
+#define ASN1_EXPLICIT_CONTEXT   (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED)
+#define ASN1_EXPLICIT_0         (ASN1_EXPLICIT_CONTEXT + 0)
+
+/*
+ * Common setup code for asn1_dive() and asn1_skip().
+ *
+ * Check ASN.1 tag and various errors, decode length field.
+ * Outputs are length of header (tag + length) and value.
+ */
+
+static int asn1_prep(const unsigned char tag,
+                     const unsigned char * const der,
+                     const size_t len,
+                     size_t *phlen,
+                     size_t *pvlen)
+{
+  size_t i, hlen, vlen;
+
+  if (der == NULL || len < 2 || phlen == NULL || pvlen == NULL || der[0] != tag || der[1] > 0x84)
+    return 0;
+
+  if ((der[1] & 0x80) == 0) {
+    hlen = 2;
+    vlen = der[1];
+  }
+
+  else {
+    hlen = 2 + (der[1] & 0x7F);
+    vlen = 0;
+
+    if (hlen >= len)
+      return 0;
+
+    for (i = 2; i < hlen; i++)
+      vlen = (vlen << 8) + der[i];
+  }
+
+  if (hlen + vlen > len)
+    return 0;
+
+  *phlen = hlen;
+  *pvlen = vlen;
+  return 1;
+}
+
+/*
+ * Dive into an ASN.1 object.
+ *
+ * The special handling for BIT STRING is only appropriate for the
+ * intended use, where BIT STRING always encapsulates another ASN.1
+ * object like SubjectPublicKeyInfo and is thus always required to be
+ * a multiple of 8 bits in length.  If we ever need to use this code
+ * to deal with real bit strings, the special handling will need to
+ * move to a separate function which we can call when appropriate.
+ */
+ 
+static int asn1_dive(const unsigned char tag,
+                     const unsigned char **der,
+                     size_t *len)
+{
+  size_t hlen, vlen;
+
+  if (der == NULL || len == NULL || !asn1_prep(tag, *der, *len, &hlen, &vlen))
+    return 0;
+
+  if (tag == ASN1_BIT_STRING) {
+    if (vlen == 0 || hlen >= *len || (*der)[hlen] != 0x00)
+      return 0;
+    hlen++, vlen--;
+  }
+
+  assert(hlen + vlen <= *len);
+  *der += hlen;                 /* Advance past the header */
+  *len = vlen;                  /* Shrink range to be just the content */
+  return 1;  
+}
+
+/*
+ * Skip over an ASN.1 object.
+ */
+
+static int asn1_skip(const unsigned char tag,
+                     const unsigned char **der,
+                     size_t *len)
+{
+  size_t hlen, vlen;
+
+  if (der == NULL || len == NULL || !asn1_prep(tag, *der, *len, &hlen, &vlen))
+    return 0;
+
+  assert(hlen + vlen <= *len);
+  *der += hlen + vlen;          /* Advance past entire object */
+  *len -= hlen + vlen;          /* Reduce range by length of object */
+  return 1;
+}
+
+/*
+ * Grovel through a DER encoded X.509v3 certificate object until we
+ * find the subjectPublicKey field.  See the ASN.1 in RFC 5280.
+ *
+ * This is much too simplistic for general use, but should suffice to
+ * pick the subjectPublicKey data out of a certificate generated for
+ * us by Cryptlib.
+ */
+
+static int asn1_find_x509_spki(const unsigned char **der, size_t *len)
+{
+  return (asn1_dive(ASN1_SEQUENCE,      der, len) && /* Dive into certificate */
+          asn1_dive(ASN1_SEQUENCE,      der, len) && /* Dive into tbsCertificate */
+          asn1_skip(ASN1_EXPLICIT_0,    der, len) && /* Skip version */
+          asn1_skip(ASN1_INTEGER,       der, len) && /* Skip serialNumber */
+          asn1_skip(ASN1_SEQUENCE,      der, len) && /* Skip signature */
+          asn1_skip(ASN1_SEQUENCE,      der, len) && /* Skip issuer */
+          asn1_skip(ASN1_SEQUENCE,      der, len) && /* skip validity */
+          asn1_skip(ASN1_SEQUENCE,      der, len) && /* Skip subject */
+          asn1_dive(ASN1_SEQUENCE,      der, len) && /* Dive into subjectPublicKeyInfo */
+          asn1_skip(ASN1_SEQUENCE,      der, len) && /* Skip algorithm */
+          asn1_dive(ASN1_BIT_STRING,    der, len));  /* Dive into subjectPublicKey */
+}
+
+

+
+/*
+ * Find an unused handle.
+ *
+ * Note that zero is an excluded value (CK_INVALID_HANDLE), hence the
+ * slightly odd arithmetic.
+ *
+ * For object handles, we steal the high-order bit to flag whether the
+ * handle represents a session object or token object.
+ */
+
+static CK_ULONG p11_allocate_unused_handle(const handle_flavor_t flavor)
+{
+  static const char select_format[] =
+    " SELECT %s_id FROM %s WHERE %s_handle = ?";
+
+  const char *table = flavor == handle_flavor_session ? "session" : "object";
+  sqlite3_stmt *q = NULL;
+  CK_ULONG handle;
+  int ret;
+
+  if (!sql_check_ok(sql_prepare(&q, select_format, table, table, table)))
+    goto fail;
+
+  for (;;) {
+
+    handle = ++next_handle;
+    next_handle %= 0xFFFFFFFF;
+
+    switch (flavor) {
+    case handle_flavor_session:
+      break;
+    case handle_flavor_token_object:
+      handle |= FLAG_HANDLE_TOKEN;
+      break;
+    case handle_flavor_session_object:
+      handle &= ~FLAG_HANDLE_TOKEN;
+      break;
+    }
+
+    assert(handle != CK_INVALID_HANDLE);
+
+    if (!sql_check_ok(sqlite3_reset(q)) ||
+        !sql_check_ok(sqlite3_bind_int64(q, 1, handle)))
+      goto fail;
+
+    if ((ret = sqlite3_step(q)) == SQLITE_ROW)
+      continue;
+
+    if (ret == SQLITE_DONE)
+      break;
+
+    sql_whine_step();
+    goto fail;
+
+  }
+
+  sqlite3_finalize(q);
+  return handle;
+
+ fail:
+  sqlite3_finalize(q);
+  return CK_INVALID_HANDLE;
+}
+
+/*
+ * Translate CKA_TOKEN value to handle flavor.
+ */
+
+static handle_flavor_t p11_handle_flavor_from_cka_token(const CK_BBOOL *bbool)
+{
+  assert(bbool != NULL);
+  return  *bbool ? handle_flavor_token_object : handle_flavor_session_object;
+}
+
+

+
+/*
+ * Attribute methods.
+ */
+
+/*
+ * Set an attribute for a given object.
+ *
+ * It would be trivial to generalize this to take a CK_ATTRIBUTE_PTR
+ * template instead of a single attribute, at the cost of losing the
+ * const specifiers (CK_ATTRIBUTE_PTR has an internal non-const void*).
+ */
+
+static int p11_attribute_set(const CK_OBJECT_HANDLE object_handle,
+                             const CK_ATTRIBUTE_TYPE type,
+                             const void * const value,
+                             const CK_ULONG length)
+{
+  static const char insert_format[] =
+    " INSERT OR REPLACE INTO %s_attribute (%s_object_id, type, value)"
+    " VALUES ((SELECT %s_object_id FROM object WHERE object_handle = ?1), ?2, ?3)";
+
+  const char *flavor = is_token_handle(object_handle) ? "token" : "session";
+
+  sqlite3_stmt *q = NULL;
+  int ok = 0;
+
+  if (!sql_check_ok(sql_prepare(&q, insert_format, flavor, flavor, flavor))     ||
+      !sql_check_ok(sqlite3_bind_int64(q, 1, object_handle))                    ||
+      !sql_check_ok(sqlite3_bind_int64(q, 2, type))                             ||
+      !sql_check_ok(sqlite3_bind_blob( q, 3, value, length, NULL))              ||
+      !sql_check_done(sqlite3_step(q)))
+    goto fail;
+
+  ok = 1;
+
+ fail:
+  sqlite3_finalize(q);
+  return ok;
+}
+
+/*
+ * Get a single attribute from a given object.
+ *
+ * This could easily be generalized to take a CK_ATTRIBUTE_PTR, at the
+ * cost of more complicated error semantics.
+ */
+
+static int p11_attribute_get(const CK_OBJECT_HANDLE object_handle,
+                             const CK_ATTRIBUTE_TYPE type,
+                             void *value,
+                             CK_ULONG *length,
+                             const CK_ULONG maxlength)
+{
+  static const char select_format[] =
+    " SELECT value FROM %s_attribute NATURAL JOIN object"
+    " WHERE object_handle = ?1 AND type = ?2";
+
+  const char *flavor = is_token_handle(object_handle) ? "token" : "session";
+
+  sqlite3_stmt *q = NULL;
+  int ret, ok = 0;
+  CK_ULONG len;
+
+  if (!sql_check_ok(sql_prepare(&q, select_format, flavor))     ||
+      !sql_check_ok(sqlite3_bind_int64(q, 1, object_handle))    ||
+      !sql_check_ok(sqlite3_bind_int64(q, 2, type)))
+    goto fail;
+
+  ret = sqlite3_step(q);
+
+  if (ret == SQLITE_DONE)
+    goto fail;
+
+  if (ret != SQLITE_ROW) {
+    sql_whine_step();
+    goto fail;
+  }
+
+  len = sqlite3_column_bytes(q, 0);
+
+  if (length != NULL)
+    *length = len;
+
+  if (value != NULL && maxlength < len)
+    goto fail;
+  
+  if (value != NULL)
+    memcpy(value, sqlite3_column_blob(q, 0), len);
+
+  ok = 1;
+
+ fail:
+  sqlite3_finalize(q);
+  return ok;
+}
+
+/*
+ * Wrappers to set and get CK_BBOOL and CK_ULONG values.
+ */
+
+static int p11_attribute_set_bbool(const CK_OBJECT_HANDLE object_handle, const CK_ATTRIBUTE_TYPE type, const CK_BBOOL value)
+{
+  return p11_attribute_set(object_handle, type, &value, sizeof(value));
+}
+
+static int p11_attribute_set_ulong(const CK_OBJECT_HANDLE object_handle, const CK_ATTRIBUTE_TYPE type, const CK_ULONG value)
+{
+  return p11_attribute_set(object_handle, type, &value, sizeof(value));
+}
+
+static int p11_attribute_get_bbool(const CK_OBJECT_HANDLE object_handle, const CK_ATTRIBUTE_TYPE type, CK_BBOOL *value)
+{
+  CK_ULONG length;
+  return p11_attribute_get(object_handle, type, value, &length, sizeof(*value)) && length == sizeof(*value);
+}
+
+static int p11_attribute_get_ulong(const CK_OBJECT_HANDLE object_handle, const CK_ATTRIBUTE_TYPE type, CK_ULONG *value)
+{
+  CK_ULONG length;
+  return p11_attribute_get(object_handle, type, value, &length, sizeof(*value)) && length == sizeof(*value);
+}
+
+/*
+ * Find an attribute in a CK_ATTRIBUTE_PTR template.  Returns index
+ * into template, or -1 if not found.
+ */
+
+static int p11_attribute_find_in_template(const CK_ATTRIBUTE_TYPE type,
+                                          const CK_ATTRIBUTE_PTR template,
+                                          const CK_ULONG length)
+{
+  int i;
+
+  if (template != NULL)
+    for (i = 0; i < length; i++)
+      if (template[i].type == type)
+        return i;
+
+  return -1;
+}
+
+/*
+ * Map a keyusage-related attribute to a keyusage bit flag.
+ *
+ * Assumes that calling code has already checked whether this
+ * attribute is legal for this object class, that attribute which
+ * should be CK_BBOOLs are of the correct length, etcetera.
+ *
+ * To handle all the possible permutations of specified and default
+ * values, it may be necessary to defer calling this method until
+ * after the default and mandatory values have been merged into the
+ * values supplied by the application-supplied template.
+ */
+
+static void p11_attribute_apply_keyusage(unsigned *keyusage, const CK_ATTRIBUTE_TYPE type, const CK_BBOOL *value)
+{
+  unsigned flag;
+
+  assert(keyusage != NULL && value != NULL);
+
+  switch (type) {
+  case CKA_SIGN:                /* Generate signature */
+  case CKA_VERIFY:              /* Verify signature */
+    flag = CRYPT_KEYUSAGE_DIGITALSIGNATURE;
+    break;
+  case CKA_ENCRYPT:             /* Encrypt bulk data (seldom used) */
+  case CKA_DECRYPT:             /* Bulk decryption (seldom used) */
+    flag = CRYPT_KEYUSAGE_DATAENCIPHERMENT;
+    break;
+  case CKA_WRAP:                /* Wrap key (normal way of doing encryption) */
+  case CKA_UNWRAP:              /* Unwrap key (normal way of doing decryption) */
+    flag = CRYPT_KEYUSAGE_KEYENCIPHERMENT;
+    break;
+  default:
+    return;                     /* Attribute not related to key usage */
+  }
+
+  if (*value)
+    *keyusage |=  flag;
+  else
+    *keyusage &= ~flag;
+}
+
+

+
+/*
+ * Descriptor methods.  Descriptors are generated at compile time by
+ * an auxiliary Python script, see attributes.* for details.
+ */
+
+/*
+ * Return the descriptor associated with a particular object class and
+ * key type.
+ */
+
+static const p11_descriptor_t *p11_descriptor_from_key_type(const CK_OBJECT_CLASS object_class,
+                                                            const CK_KEY_TYPE key_type)
+{
+  int i;
+
+  for (i = 0; i < sizeof(p11_descriptor_keyclass_map)/sizeof(*p11_descriptor_keyclass_map); i++) {
+    const p11_descriptor_keyclass_map_t * const m = &p11_descriptor_keyclass_map[i];
+    if (m->object_class == object_class && m->key_type == key_type)
+      return m->descriptor;
+  }
+
+  return NULL;
+}
+
+/*
+ * Find the entry for a particular attribute in a descriptor.
+ */
+
+static const p11_attribute_descriptor_t *p11_find_attribute_in_descriptor(const p11_descriptor_t *descriptor,
+                                                                          const CK_ATTRIBUTE_TYPE type)
+{
+  int i;
+
+  if (descriptor != NULL && descriptor->attributes != NULL)
+    for (i = 0; i < descriptor->n_attributes; i++)
+      if (descriptor->attributes[i].type == type)
+        return &descriptor->attributes[i];
+
+  return NULL;
+}
+
+/*
+ * Check whether an attribute is marked as sensitive.  If we don't
+ * recognize the attribute, report it as sensitive (safer than the
+ * alternative).
+ */
+
+static int p11_attribute_is_sensitive(const p11_descriptor_t *descriptor,
+                                      const CK_ATTRIBUTE_TYPE type)
+{
+  const p11_attribute_descriptor_t *a = p11_find_attribute_in_descriptor(descriptor, type);
+  return a == NULL || (a->flags & P11_DESCRIPTOR_SENSITIVE) != 0;
+}
+
+

+
+/*
+ * Object methods.
+ */
+
+/*
+ * Check access rights for an object.
+ */
+
+typedef enum { p11_object_access_read, p11_object_access_write } p11_object_access_t;
+
+static CK_RV p11_object_check_rights(const p11_session_t *session,
+                                     const CK_OBJECT_HANDLE object_handle,
+                                     const p11_object_access_t rights)
+{
+  static const char session_handle_query[] =
+    " SELECT session_handle FROM session NATURAL JOIN object WHERE object_handle = ?1";
+
+  CK_BBOOL object_is_private;
+  sqlite3_stmt *q = NULL;
+  CK_RV rv;
+
+  if (session == NULL)
+    lose(CKR_SESSION_HANDLE_INVALID);
+
+  /*
+   * Read-only sessions are, um, read-only.
+   */
+
+  switch (session->state) {
+  case CKS_RO_PUBLIC_SESSION:
+  case CKS_RO_USER_FUNCTIONS:
+    if (rights == p11_object_access_write)
+      lose(CKR_SESSION_READ_ONLY);
+  }
+
+  /*
+   * Private objects don't for sessions in the wrong state.
+   */
+
+  switch (session->state) {
+  case CKS_RO_PUBLIC_SESSION:
+  case CKS_RW_PUBLIC_SESSION:
+  case CKS_RW_SO_FUNCTIONS:
+    if (!p11_attribute_get_bbool(object_handle, CKA_PRIVATE, &object_is_private) || object_is_private)
+      lose(CKR_OBJECT_HANDLE_INVALID);
+  }
+
+  /*
+   * Session objects are only visible to the session which created them.
+   */
+
+  if (!is_token_handle(object_handle)                           &&
+      (!sql_check_ok(sql_prepare(&q, session_handle_query))     ||
+       !sql_check_ok(sqlite3_bind_int64(q, 1, object_handle))   ||
+       !sql_check_row(sqlite3_step(q))                          ||
+       sqlite3_column_int64(q, 0) != session->handle))
+    lose(CKR_OBJECT_HANDLE_INVALID);
+
+  /*
+   * Ran out of reasons to reject, guess we should allow it.
+   */
+
+  rv = CKR_OK;
+
+ fail:
+  sqlite3_finalize(q);
+  return rv;
+}
+
+/*
+ * Delete all private objects, probably because user logged out.
+ *
+ * In the case of token objects, the object itself remains in the
+ * token, we're just deleting our handle for the object.
+ *
+ * In the case of session objects, the object itself goes away.
+ */
+
+static int p11_object_delete_all_private(void)
+{
+  static const char select_format[] =
+    " WITH"
+    "   private AS (SELECT session_object_id FROM session_attribute WHERE type = %u AND value <> X'00')"
+    " SELECT keyid FROM session_object WHERE keyid IS NOT NULL AND session_object_id IN private";
+
+  static const char delete_format[] =
+    " WITH"
+    "  s AS (SELECT session_object_id FROM session_attribute WHERE type = %u AND value <> X'00'),"
+    "  t AS (SELECT token_object_id   FROM token_attribute   WHERE type = %u AND value <> X'00')"
+    " DELETE FROM object WHERE token_object_id IN t OR session_object_id IN s";
+
+  sqlite3_stmt *q = NULL;
+  int ret, ok = 0;
+
+  if (!sql_check_ok(sql_prepare(&q, select_format, CKA_PRIVATE)))
+    goto fail;
+
+  while ((ret = sqlite3_step(q)) == SQLITE_ROW)
+    if (cryptlib_delete_key((const char *) sqlite3_column_text(q, 0)) != CRYPT_OK)
+      goto fail;
+
+  if (ret != SQLITE_DONE) {
+    sql_whine_step();
+    goto fail;
+  }
+
+  sqlite3_finalize(q);
+  q = NULL;
+
+  if (!sql_check_ok(sql_prepare(&q, delete_format, CKA_PRIVATE, CKA_PRIVATE))   ||
+      !sql_check_done(sqlite3_step(q)))
+    goto fail;
+
+  ok = 1;
+
+ fail:
+  sqlite3_finalize(q);
+  return ok;
+}
+
+/*
+ * Create a new object.
+ *
+ * This is a bit nasty due to the SQL foreign key constraints and the
+ * different handling required for session and token objects.
+ */
+
+static CK_OBJECT_HANDLE p11_object_create(const p11_session_t *session,
+                                          const handle_flavor_t flavor,
+                                          const CK_ATTRIBUTE_PTR template,
+                                          const CK_ULONG template_length,
+                                          const p11_descriptor_t * const descriptor,
+                                          const CK_MECHANISM_PTR mechanism)
+{
+  static const char insert_object[] =
+    " INSERT INTO object (object_handle)"
+    " VALUES (?)";
+
+  static const char insert_token_object[] =
+    " INSERT INTO token_object DEFAULT VALUES";
+
+  static const char insert_session_object[] =
+    " INSERT INTO session_object (object_id) VALUES (?)";
+
+  static const char update_object_session_object[] =
+    " UPDATE object SET"
+    "   session_id = (SELECT session_id FROM session WHERE session_handle = ?1),"
+    "   session_object_id = ?2"
+    " WHERE object_id = ?3";
+
+  static const char update_object_token_object[] =
+    " UPDATE object SET token_object_id = ?1 WHERE object_id = ?2";
+
+  static const char insert_token_attribute[] =
+    " INSERT OR REPLACE INTO token_attribute (token_object_id, type, value)"
+    " VALUES (?1, ?2, ?3)";
+
+  static const char insert_session_attribute[] =
+    " INSERT OR REPLACE INTO session_attribute (session_object_id, type, value)"
+    " VALUES (?1, ?2, ?3)";
+
+  CK_OBJECT_HANDLE object_handle = p11_allocate_unused_handle(flavor);;
+  sqlite3_int64 object_id, session_object_id, token_object_id;
+  sqlite3_stmt *q = NULL;
+  int i, ok = 0;
+
+  assert(session != NULL && template != NULL && descriptor != NULL &&
+         (flavor == handle_flavor_token_object ||
+          flavor == handle_flavor_session_object));
+
+  if (!sql_check_ok(sql_prepare(&q, insert_object))             ||
+      !sql_check_ok(sqlite3_bind_int64(q, 1, object_handle))    ||
+      !sql_check_done(sqlite3_step(q)))
+    goto fail;
+
+  object_id = sqlite3_last_insert_rowid(sqldb);
+
+  sqlite3_finalize(q);
+  q = NULL;
+
+  switch (flavor) {
+
+  case handle_flavor_token_object:
+    if (!sql_check_ok(sql_prepare(&q, insert_token_object))             ||
+        !sql_check_done(sqlite3_step(q)))
+      goto fail;
+    token_object_id = sqlite3_last_insert_rowid(sqldb);
+    sqlite3_finalize(q);
+    q = NULL;
+    if (!sql_check_ok(sql_prepare(&q, update_object_token_object))      ||
+        !sql_check_ok(sqlite3_bind_int64(q, 1, token_object_id))        ||
+        !sql_check_ok(sqlite3_bind_int64(q, 2, object_id))              ||
+        !sql_check_done(sqlite3_step(q)))
+      goto fail;
+    sqlite3_finalize(q);
+    q = NULL;
+    if (!sql_check_ok(sql_prepare(&q, insert_token_attribute))          ||
+        !sql_check_ok(sqlite3_bind_int64(q, 1, token_object_id)))
+      goto fail;
+    break;
+
+  case handle_flavor_session_object:
+    if (!sql_check_ok(sql_prepare(&q, insert_session_object))           ||
+        !sql_check_ok(sqlite3_bind_int64(q, 1, object_id))              ||
+        !sql_check_done(sqlite3_step(q)))
+      goto fail;
+    session_object_id = sqlite3_last_insert_rowid(sqldb);
+    sqlite3_finalize(q);
+    q = NULL;
+    if (!sql_check_ok(sql_prepare(&q, update_object_session_object))    ||
+        !sql_check_ok(sqlite3_bind_int64(q, 1, session->handle))        ||
+        !sql_check_ok(sqlite3_bind_int64(q, 2, session_object_id))      ||
+        !sql_check_ok(sqlite3_bind_int64(q, 3, object_id))              ||
+        !sql_check_done(sqlite3_step(q)))
+      goto fail;
+    sqlite3_finalize(q);
+    q = NULL;
+    if (!sql_check_ok(sql_prepare(&q, insert_session_attribute))        ||
+        !sql_check_ok(sqlite3_bind_int64(q, 1, session_object_id)))
+      goto fail;
+    break;
+
+  default:                      /* Suppress GCC warning */
+    goto fail;
+  }
+
+  /*
+   * Now populate attributes, starting with the application's
+   * template, which we assume has already been blessed by the API
+   * function that called this method.
+   */
+
+  for (i = 0; i < template_length; i++) {
+    const CK_ATTRIBUTE_TYPE type = template[i].type;
+    const void *             val = template[i].pValue;
+    const int                len = template[i].ulValueLen;
+
+    if (!sql_check_ok(sqlite3_reset(q))                         ||
+        !sql_check_ok(sqlite3_bind_int64(q, 2, type))           ||
+        !sql_check_ok(sqlite3_bind_blob( q, 3, val, len, NULL)) ||
+        !sql_check_done(sqlite3_step(q)))
+      goto fail;
+  }
+
+  /*
+   * Next, add defaults from the descriptor.
+   */
+
+  for (i = 0; i < descriptor->n_attributes; i++) {
+    const CK_ATTRIBUTE_TYPE type = descriptor->attributes[i].type;
+    const void *             val = descriptor->attributes[i].value;
+    const int                len = descriptor->attributes[i].length;
+    const unsigned         flags = descriptor->attributes[i].flags;
+
+    if (val == NULL && (flags & P11_DESCRIPTOR_DEFAULT_VALUE) != 0)
+      val = "";
+
+    if (val == NULL || p11_attribute_find_in_template(type, template, template_length) >= 0)
+      continue;
+
+    if (!sql_check_ok(sqlite3_reset(q))                         ||
+        !sql_check_ok(sqlite3_bind_int64(q, 2, type))           ||
+        !sql_check_ok(sqlite3_bind_blob( q, 3, val, len, NULL)) ||
+        !sql_check_done(sqlite3_step(q)))
+      goto fail;
+  }
+
+  /*
+   * Finally, add generation mechanism attributes as needed.
+   */
+  
+  if (mechanism != NULL &&
+      (!sql_check_ok(sqlite3_reset(q))                                                                          ||
+       !sql_check_ok(sqlite3_bind_int64(q, 2, CKA_LOCAL))                                                       ||
+       !sql_check_ok(sqlite3_bind_blob( q, 3, &const_CK_TRUE, sizeof(const_CK_TRUE), NULL))                     ||
+       !sql_check_done(sqlite3_step(q))                                                                         ||
+       !sql_check_ok(sqlite3_reset(q))                                                                          ||
+       !sql_check_ok(sqlite3_bind_int64(q, 2, CKA_KEY_GEN_MECHANISM))                                           ||
+       !sql_check_ok(sqlite3_bind_blob( q, 3, &mechanism->mechanism, sizeof(mechanism->mechanism), NULL))       ||
+       !sql_check_done(sqlite3_step(q))))
+    goto fail;
+
+  /*
+   * If we made it past all that, we're happy.
+   */
+
+  ok = 1;
+
+ fail:
+  sqlite3_finalize(q);
+  return ok ? object_handle : CK_INVALID_HANDLE;
+}
+
+/*
+ * Get the keyid for an object.
+ *
+ * This may require calculating the keyid from the CKA_ID attribute.
+ */
+
+static int p11_object_get_keyid(const CK_OBJECT_HANDLE object_handle,
+                                char *keyid,
+                                const size_t maxkeyid)
+{
+  static const char select_format[] =
+    " SELECT keyid FROM %s_object NATURAL JOIN object WHERE object_handle = ?";
+
+  static const char update_format[] =
+    " UPDATE %s_object SET keyid = ?1"
+    " WHERE %s_object_id = (SELECT %s_object_id FROM object WHERE object_handle =?2)";
+
+  const char *flavor = is_token_handle(object_handle) ? "token" : "session";
+
+  sqlite3_stmt *q = NULL;
+  int ok = 0;
+
+  if (!sql_check_ok(sql_prepare(&q, select_format, flavor))     ||
+      !sql_check_ok(sqlite3_bind_int64(q, 1, object_handle))    ||
+      !sql_check_row(sqlite3_step(q)))
+    goto fail;
+
+  if (sqlite3_column_type(q, 0) == SQLITE_NULL) {
+
+    /*
+     * No keyid set yet, have to create one.  We use the CKA_ID
+     * attribute for this, zero-filling or truncating as necessary.
+     */
+
+    const CK_ULONG target_length = (CRYPT_MAX_TEXTSIZE < maxkeyid ? CRYPT_MAX_TEXTSIZE : (maxkeyid - 1)) / 2;
+    unsigned char id[CRYPT_MAX_HASHSIZE];
+    CK_ULONG len;
+    int i;
+
+    assert(target_length > 0 && target_length <= sizeof(id) && target_length * 2 < maxkeyid);
+
+    if (!p11_attribute_get(object_handle, CKA_ID, id, &len, sizeof(id)))
+      goto fail;
+
+    if (len < target_length) {
+      memmove(id + target_length - len, id, len);
+      memset(id, 0x00, target_length - len);
+    }
+
+    for (i = 0; i < target_length; i++)
+      sprintf(keyid + (2 * i), "%02x", id[i]);
+    keyid[target_length * 2] = '\0';
+
+    sqlite3_finalize(q);
+    q = NULL;
+
+    if (!sql_check_ok(sql_prepare(&q, update_format, flavor, flavor, flavor))   ||
+        !sql_check_ok(sqlite3_bind_text( q, 1, keyid, strlen(keyid), NULL))     ||
+        !sql_check_ok(sqlite3_bind_int64(q, 2, object_handle))                  ||
+        !sql_check_done(sqlite3_step(q)))
+      goto fail;
+
+  } else {
+
+    /*
+     * Already had a keyid, just have to copy it.
+     */
+
+    int len = sqlite3_column_bytes(q, 0);
+
+    if (len >= maxkeyid)
+      goto fail;
+
+    memcpy(keyid, sqlite3_column_text(q, 0), len);
+    keyid[len] = '\0';
+
+  }
+
+  ok = 1;
+
+ fail:
+  sqlite3_finalize(q);
+  return ok;
+}
+
+/*
+ * Add attributes representing the SPKI value of a key we've
+ * generated.
+ *
+ * Cryptlib does such a complete job of protecting our keys that it's
+ * rather tedious to extract the raw subjectPublicKeyInfo, but the
+ * PKCS #11 client needs that information, so we have to jump through
+ * some silly hoops.  This routine does most of the work, but uses a
+ * separate handler (supplied as an argument) to generate attributes
+ * based on mechanism-specific data from the subjectPublicKey.
+ *
+ * Basic approach here is to generate a temporary certificate from the
+ * key, export that as DER, parse the DER for the data we need, and
+ * destroy the temporary certificate.
+ */
+
+static int p11_object_add_spki(const CK_OBJECT_HANDLE public_handle,
+                               const CK_OBJECT_HANDLE private_handle,
+                               const CRYPT_CONTEXT key,
+                               int (*handler)(const CK_OBJECT_HANDLE,
+                                              const CK_OBJECT_HANDLE,
+                                              const unsigned char *,
+                                              const size_t))
+{
+  static const char label[] = "Don't care";
+  CRYPT_CERTIFICATE cert = CRYPT_HANDLE_NONE;
+  unsigned char *buffer = NULL;
+  const unsigned char *der;
+  int ilen, ok = 0;
+  size_t ulen;
+
+  if (handler                                                                           == NULL     ||
+      cryptCreateCert(&cert, CRYPT_UNUSED, CRYPT_CERTTYPE_CERTIFICATE)                  != CRYPT_OK ||
+      cryptSetAttribute(cert, CRYPT_CERTINFO_SUBJECTPUBLICKEYINFO, key)                 != CRYPT_OK ||
+      cryptSetAttribute(cert, CRYPT_CERTINFO_XYZZY, 1)                                  != CRYPT_OK ||
+      cryptSetAttributeString(cert, CRYPT_CERTINFO_COMMONNAME, label, sizeof(label))    != CRYPT_OK ||
+      cryptSignCert(cert, key)                                                          != CRYPT_OK ||
+      cryptExportCert(NULL, 0, &ilen, CRYPT_CERTFORMAT_CERTIFICATE, cert)               != CRYPT_OK ||
+      (der = buffer = malloc(ulen = (size_t) ilen))                                     == NULL     ||
+      cryptExportCert(buffer, ilen, &ilen, CRYPT_CERTFORMAT_CERTIFICATE, cert)          != CRYPT_OK ||
+      !asn1_find_x509_spki(&der, &ulen)                                                             ||
+      !handler(public_handle, private_handle, der, ulen))
+    goto fail;
+
+  ok = 1;
+
+ fail:
+  if (buffer != NULL)
+    free(buffer);
+  if (cert != CRYPT_HANDLE_NONE)
+    cryptDestroyCert(cert);
+  return ok;
+}
+
+/*
+ * RSA-specific handler to go with p11_object_add_spki().
+ *
+ * Extract RSA modulus and public exponent from the subjectPublicKey
+ * and adds the appropriate attributes to the public and private keys.
+ */
+
+static int p11_object_add_spki_rsa(const CK_OBJECT_HANDLE public_handle,
+                                   const CK_OBJECT_HANDLE private_handle,
+                                   const unsigned char *der,
+                                   const size_t len)
+{
+  const unsigned char *modulus = der, *publicExponent = der;
+  size_t modulus_len = len, publicExponent_len = len;
+
+  /*
+   * Dig the relevant integers out of the ASN.1.
+   */
+  if (!asn1_dive(ASN1_SEQUENCE, &modulus,        &modulus_len)          ||
+      !asn1_dive(ASN1_INTEGER,  &modulus,        &modulus_len)          ||
+      !asn1_dive(ASN1_SEQUENCE, &publicExponent, &publicExponent_len)   ||
+      !asn1_skip(ASN1_INTEGER,  &publicExponent, &publicExponent_len)   ||
+      !asn1_dive(ASN1_INTEGER,  &publicExponent, &publicExponent_len))
+    return 0;
+
+  /*
+   * ASN.1 INTEGERs are signed while PKCS #11 "big integers" are
+   * unsigned, so skip leading zero byte, if present.
+   */
+
+  if (modulus_len > 0 && *modulus == 0x00)
+    modulus_len--, modulus++;
+
+  if (publicExponent_len > 0 && *publicExponent == 0x00)
+    publicExponent_len--, publicExponent++;
+  
+  /*
+   * Insert the attributes and we're done.
+   */
+
+  return (p11_attribute_set(public_handle,  CKA_MODULUS,         modulus,        modulus_len)           &&
+          p11_attribute_set(public_handle,  CKA_PUBLIC_EXPONENT, publicExponent, publicExponent_len)    &&
+          p11_attribute_set(private_handle, CKA_MODULUS,         modulus,        modulus_len)           &&
+          p11_attribute_set(private_handle, CKA_PUBLIC_EXPONENT, publicExponent, publicExponent_len));
+}
+
+

+
+/*
+ * Session methods.
+ */
+
+/*
+ * Create a new session.
+ */
+
+static p11_session_t *p11_session_new(void)
+{
+  p11_session_t *session = malloc(sizeof(*session));
+  if (session == NULL)
+    return NULL;
+  memset(session, 0, sizeof(*session));
+
+#define SESSION_CRYPTLIB_CONTEXT(_ctx_)         session->_ctx_ = CRYPT_HANDLE_NONE
+#define SESSION_CRYPTLIB_ENVELOPE(_env_)        session->_env_ = CRYPT_HANDLE_NONE
+  SESSION_CRYPTLIB_HANDLES;
+#undef  SESSION_CRYPTLIB_ENVELOPE
+#undef  SESSION_CRYPTLIB_CONTEXT
+
+  return session;
+}
+
+/*
+ * Free a session.
+ */
+
+static void p11_session_free(p11_session_t *session)
+{
+  if (session == NULL)
+    return;
+
+  if (session->find_query != NULL)
+    sqlite3_finalize(session->find_query);
+
+#define SESSION_CRYPTLIB_CONTEXT(_ctx_)         if (session->_ctx_ != CRYPT_HANDLE_NONE) cryptDestroyContext(session->_ctx_)
+#define SESSION_CRYPTLIB_ENVELOPE(_env_)        if (session->_env_ != CRYPT_HANDLE_NONE) cryptDestroyEnvelope(session->_env_)
+  SESSION_CRYPTLIB_HANDLES;
+#undef  SESSION_CRYPTLIB_ENVELOPE
+#undef  SESSION_CRYPTLIB_CONTEXT
+
+  free(session);
+}
+
+/*
+ * Assign a handle to a session and add the session to SQL.
+ */
+
+static int p11_session_add(p11_session_t *session)
+{
+  static const char insert_session[] =
+    " INSERT INTO session (session_handle) VALUES (?)";
+
+  sqlite3_stmt *q = NULL;
+  int ok = 0;
+
+  assert(session != NULL);
+
+  session->handle = p11_allocate_unused_handle(handle_flavor_session);
+
+  if (!sql_check_ok(sql_prepare(&q, insert_session))            ||
+      !sql_check_ok(sqlite3_bind_int64(q, 1, session->handle))  ||
+      !sql_check_done(sqlite3_step(q)))
+    goto fail;
+
+  session->link = p11_sessions;
+  p11_sessions = session;
+  ok = 1;
+
+ fail:
+  sqlite3_finalize(q);
+  return ok;
+}
+
+/*
+ * Find a session.
+ *
+ * Since we don't expect the total number of sessions to be all that
+ * high, we use a linked list with a move-to-the-front search.  Some
+ * of the other session methods assume this behavior, so be careful if
+ * you decide to change it.
+ */
+
+static p11_session_t *p11_session_find(const CK_SESSION_HANDLE session_handle)
+{
+  p11_session_t **link, *session;
+
+  for (link = &p11_sessions;
+       (session = *link) != NULL && session->handle != session_handle;
+       link = &session->link)
+    ;
+
+  if (session != NULL && link != &p11_sessions) {
+    *link = session->link;
+    session->link = p11_sessions;
+    p11_sessions = session;
+  }
+
+  return session;
+}
+
+/*
+ * Delete a session: remove it from SQL and free the session data
+ * structure.
+ *
+ * Since this destroys all associated session objects, we also have to
+ * delete any keys we might be holding for session objects.
+ *
+ * This method assumes the move-to-the-front behavior of
+ * p11_session_find().
+ */
+
+static CK_RV p11_session_delete(const CK_SESSION_HANDLE session_handle)
+{
+  static const char select_keyid[] =
+    " SELECT keyid FROM session NATURAL JOIN session_object"
+    " WHERE session_handle = ?1 AND keyid IS NOT NULL";
+
+  static const char delete_session[] =
+    " DELETE FROM session WHERE session_handle = ?";
+
+  p11_session_t *session = p11_session_find(session_handle);
+  sqlite3_stmt *q = NULL;
+  CK_RV rv = CKR_OK;
+  int ret;
+
+  if (session == NULL)
+    return CKR_SESSION_HANDLE_INVALID;
+  
+  if (!sql_check_ok(sql_prepare(&q, select_keyid)) ||
+      !sql_check_ok(sqlite3_bind_int64(q, 1, session_handle)))
+    lose(CKR_FUNCTION_FAILED);
+
+  while ((ret = sqlite3_step(q)) == SQLITE_ROW)
+    if (cryptlib_delete_key((const char *) sqlite3_column_text(q, 0)) != CRYPT_OK)
+      lose(CKR_FUNCTION_FAILED);
+
+  if (ret != SQLITE_DONE) {
+    sql_whine_step();
+    lose(CKR_FUNCTION_FAILED);
+  }
+
+  sqlite3_finalize(q);
+  q = NULL;
+
+  if (!sql_check_ok(sql_prepare(&q, delete_session))            ||
+      !sql_check_ok(sqlite3_bind_int64(q, 1, session_handle))   ||
+      !sql_check_done(sqlite3_step(q)))
+    lose(CKR_FUNCTION_FAILED);
+
+  /* Check that move-to-the-front behaved as expected */
+  assert(p11_sessions == session);
+
+  p11_sessions = session->link;
+  p11_session_free(session);
+
+ fail:
+  sqlite3_finalize(q);
+  return rv;
+}
+
+/*
+ * Delete all sessions.
+ *
+ * Like p11_session_delete(), this must also delete any keys held in
+ * session objects.
+ */
+
+static CK_RV p11_session_delete_all(void)
+{
+  static const char select_keys[] =
+    " SELECT keyid FROM session_object WHERE keyid IS NOT NULL";
+
+#warning Should this also clear the object table?
+
+  static const char delete_all_sessions[] =
+    " DELETE FROM session";
+
+  p11_session_t *session;
+  sqlite3_stmt *q = NULL;
+  int ret = SQLITE_OK;
+  CK_RV rv = CKR_OK;
+
+  if (!sql_check_ok(sql_prepare(&q, select_keys)))
+    lose(CKR_FUNCTION_FAILED);
+
+  while ((ret = sqlite3_step(q)) == SQLITE_ROW)
+    if (cryptlib_delete_key((const char *) sqlite3_column_text(q, 0)) != CRYPT_OK)
+      lose(CKR_FUNCTION_FAILED);
+
+  if (ret != SQLITE_DONE) {
+    sql_whine_step();
+    lose(CKR_FUNCTION_FAILED);
+  }
+
+  sqlite3_finalize(q);
+  q = NULL;
+
+  if (!sql_exec(delete_all_sessions))
+    lose(CKR_FUNCTION_FAILED);
+
+  while (p11_sessions != NULL) {
+    session = p11_sessions;
+    p11_sessions = session->link;
+    p11_session_free(session);
+  }
+
+ fail:
+  sqlite3_finalize(q);
+  return rv;
+}
+
+/*
+ * Check session database against login state for consistency.
+ *
+ * This is mostly useful in assertions.
+ */
+
+static int p11_session_consistent_login(void)
+{
+  p11_session_t *session;
+
+  switch (logged_in_as) {
+
+  case not_logged_in:
+    for (session = p11_sessions; session != NULL; session = session->link)
+      if (session->state != CKS_RO_PUBLIC_SESSION && session->state != CKS_RW_PUBLIC_SESSION)
+        return 0;
+    return 1;
+
+  case logged_in_as_user:
+    for (session = p11_sessions; session != NULL; session = session->link)
+      if (session->state != CKS_RO_USER_FUNCTIONS && session->state != CKS_RW_USER_FUNCTIONS)
+        return 0;
+    return 1;
+
+  case logged_in_as_so:
+    for (session = p11_sessions; session != NULL; session = session->link)
+      if (session->state != CKS_RW_SO_FUNCTIONS)
+        return 0;
+    return 1;
+
+  default:
+    return 0;
+  }
+}
+
+

+
+/*
+ * PKCS #11 likes space-padded rather than null-terminated strings.
+ */
+
+static int psnprintf(void *buffer_, size_t size, const char *format, ...)
+{
+  char *buffer = buffer_;
+  size_t i, n;
+  va_list ap;
+
+  va_start(ap, format);
+  i = n = vsnprintf(buffer, size, format, ap);
+  va_end(ap);
+
+  while (i < size)
+    buffer[i++] = ' ';
+
+  return n;
+}
+
+

+
+/*
+ * Template checking and key generation.
+ *
+ * This may need refactoring at some point, eg, when we add support
+ * for C_CreateObject().
+ */
+
+/*
+ * First pass: called once per template entry during initial pass over
+ * template to handle generic checks that apply regardless of
+ * attribute type.
+ */
+
+static CK_RV p11_check_keypair_attributes_check_template_1(const CK_ATTRIBUTE_TYPE type,
+                                                           const void * const val,
+                                                           const size_t len,
+                                                           const p11_descriptor_t * const descriptor)
+{
+  const p11_attribute_descriptor_t * const atd = p11_find_attribute_in_descriptor(descriptor, type);
+  CK_RV rv;
+
+  /* Attribute not allowed or not allowed for key generation */
+  if (atd == NULL || (atd->flags & P11_DESCRIPTOR_FORBIDDEN_BY_GENERATE) != 0)
+    lose(CKR_ATTRIBUTE_TYPE_INVALID);
+
+  /* NULL or wrong-sized attribute values */
+  if (val == NULL || (atd->size != 0 && len != atd->size))
+    lose(CKR_ATTRIBUTE_VALUE_INVALID);
+
+  /* Attributes which only the SO user is allowed to set to CK_TRUE */
+  if ((atd->flags & P11_DESCRIPTOR_ONLY_SO_USER_CAN_SET) != 0 && logged_in_as != logged_in_as_so && *(CK_BBOOL *) val)
+      lose(CKR_ATTRIBUTE_VALUE_INVALID);
+
+  /* Attributes which don't match mandatory values */
+  if (atd->value != NULL && (atd->flags & P11_DESCRIPTOR_DEFAULT_VALUE) == 0 && memcmp(val, atd->value, atd->length) != 0)
+    lose(CKR_TEMPLATE_INCONSISTENT);
+
+  rv = CKR_OK;
+
+ fail:
+  return rv;
+}
+
+/*
+ * Second pass: called once per template to check that each attribute
+ * required for that template has been specified exactly once.
+ */
+
+static CK_RV p11_check_keypair_attributes_check_template_2(const p11_session_t *session,
+                                                           const p11_descriptor_t * const descriptor,
+                                                           const CK_ATTRIBUTE_PTR template,
+                                                           const CK_ULONG template_length)
+{
+  const CK_BBOOL *object_is_private;
+  CK_RV rv;
+  int i, j;
+
+  /*
+   * Some session states aren't allowed to play with private objects.
+   */
+
+  switch (session->state) {
+  case CKS_RO_PUBLIC_SESSION:
+  case CKS_RW_PUBLIC_SESSION:
+  case CKS_RW_SO_FUNCTIONS:
+    if ((i = p11_attribute_find_in_template(CKA_PRIVATE, template, template_length)) >= 0) {
+      assert(template[i].pValue != NULL);
+      object_is_private = template[i].pValue;
+    }
+    else {
+      const p11_attribute_descriptor_t * const atd = p11_find_attribute_in_descriptor(descriptor, CKA_PRIVATE);
+      assert(atd != NULL && atd->value != NULL);
+      object_is_private = atd->value;
+    }
+    if (*object_is_private)
+      lose(CKR_TEMPLATE_INCONSISTENT);
+  }
+
+  for (i = 0; i < descriptor->n_attributes; i++) {
+    const p11_attribute_descriptor_t * const atd = &descriptor->attributes[i];
+    const int required_by_api  = (atd->flags & P11_DESCRIPTOR_REQUIRED_BY_GENERATE) != 0;
+    const int forbidden_by_api = (atd->flags & P11_DESCRIPTOR_FORBIDDEN_BY_GENERATE) != 0;
+    const int in_descriptor    = (atd->flags & P11_DESCRIPTOR_DEFAULT_VALUE) != 0 || atd->value != NULL;
+    const int pos_in_template  = p11_attribute_find_in_template(atd->type, template, template_length);
+
+    /* Multiple entries for same attribute */
+    if (pos_in_template >= 0)
+      for (j = pos_in_template + 1; j < template_length; j++)
+        if (template[j].type == atd->type)
+          lose(CKR_TEMPLATE_INCONSISTENT);
+
+    /* Required attribute missing from template */
+    if (!forbidden_by_api && (required_by_api || !in_descriptor) && pos_in_template < 0) {
+      fprintf(stderr, "[Missing attribute 0x%lx]\n", atd->type); /* XXX */
+      lose(CKR_TEMPLATE_INCOMPLETE);
+    }
+  }
+
+  rv = CKR_OK;
+
+ fail:
+  return rv;
+}
+
+/*
+ * Mechanism-independent checks for templates and descriptors when
+ * generating new keypairs.
+ *
+ * PKCS #11 gives the application far too much rope (including but not
+ * limited to the ability to supply completely unrelated templates for
+ * public and private keys in a keypair), so we need to do a fair
+ * amount of checking.  We automate as much of the dumb stuff as
+ * possible through the object descriptor.
+ * 
+ * Key usage handling here is based on RFC 5280 4.2.1.3, same as
+ * Cryptlib.  We reuse Cryptlib's bit flags because they're
+ * convenient.
+ *
+ * We use the PKCS #11 CKA_ID attribute to generate the Cryptlib key
+ * label.  PKCS #11 suggests but does not require CKA_ID values for
+ * public and private key to match; we do insist on this, because we
+ * really only have one key label which applies to both the public and
+ * private keys.
+ */
+
+static CK_RV p11_check_keypair_attributes(const p11_session_t *session,
+                                          const CK_ATTRIBUTE_PTR pPublicKeyTemplate,
+                                          const CK_ULONG ulPublicKeyAttributeCount,
+                                          const p11_descriptor_t * const public_descriptor,
+                                          const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
+                                          const CK_ULONG ulPrivateKeyAttributeCount,
+                                          const p11_descriptor_t * const private_descriptor)
+{
+  unsigned public_keyusage = 0, private_keyusage = 0;
+  const CK_BYTE *id = NULL;
+  size_t id_len = 0;
+  CK_RV rv = CKR_OK;
+  int i;
+
+  assert(session             != NULL &&
+         pPublicKeyTemplate  != NULL && public_descriptor  != NULL &&
+         pPrivateKeyTemplate != NULL && private_descriptor != NULL);
+
+  /*
+   * Read-only sessions can't create keys, doh.
+   */
+
+  switch (session->state) {
+  case CKS_RO_PUBLIC_SESSION:
+  case CKS_RO_USER_FUNCTIONS:
+    lose(CKR_SESSION_READ_ONLY);
+  }
+
+  /*
+   * Check values provided in the public and private templates.
+   */
+
+  for (i = 0; i < ulPublicKeyAttributeCount; i++) {
+    const CK_ATTRIBUTE_TYPE type = pPublicKeyTemplate[i].type;
+    const void * const       val = pPublicKeyTemplate[i].pValue;
+    const size_t             len = pPublicKeyTemplate[i].ulValueLen;
+
+    if ((rv = p11_check_keypair_attributes_check_template_1(type, val, len, public_descriptor)) != CKR_OK)
+      goto fail;
+
+    p11_attribute_apply_keyusage(&public_keyusage, type, val);
+
+    if (type == CKA_ID) {
+      id = val;
+      id_len = len;
+    }
+  }
+
+  for (i = 0; i < ulPrivateKeyAttributeCount; i++) {
+    const CK_ATTRIBUTE_TYPE type = pPrivateKeyTemplate[i].type;
+    const void * const       val = pPrivateKeyTemplate[i].pValue;
+    const size_t             len = pPrivateKeyTemplate[i].ulValueLen;
+
+    if ((rv = p11_check_keypair_attributes_check_template_1(type, val, len, private_descriptor)) != CKR_OK)
+      goto fail;
+
+    p11_attribute_apply_keyusage(&private_keyusage, type, val);
+
+    if (type == CKA_ID && id == NULL) {
+      id = val;
+      id_len = len;
+    }
+
+    if (type == CKA_ID && (len != id_len || memcmp(id, val, len)))
+      lose(CKR_TEMPLATE_INCONSISTENT);
+  }
+
+  /*
+   * We insist that keyusage be specified for both public and private
+   * key, and that they match.  May not need to be this strict.
+   */
+
+  if (public_keyusage != private_keyusage || public_keyusage == 0)
+    lose(CKR_TEMPLATE_INCONSISTENT);
+
+  /*
+   * We require a key ID.
+   */
+
+  if (id == NULL || id_len == 0)
+    lose(CKR_TEMPLATE_INCOMPLETE);
+
+  /*
+   * Check that all required attributes have been specified.
+   */
+
+  if ((rv = p11_check_keypair_attributes_check_template_2(session,
+                                                          public_descriptor,
+                                                          pPublicKeyTemplate,
+                                                          ulPublicKeyAttributeCount))  != CKR_OK ||
+      (rv = p11_check_keypair_attributes_check_template_2(session,
+                                                          private_descriptor,
+                                                          pPrivateKeyTemplate, 
+                                                          ulPrivateKeyAttributeCount)) != CKR_OK)
+    goto fail;
+
+  /*
+   * If we get this far, we're happy.  Maybe.
+   */
+
+  rv = CKR_OK;
+
+ fail:
+  return rv;
+}
+
+/*
+ * CKM_RSA_PKCS_KEY_PAIR_GEN key pair generation implemetation.
+ *
+ * Much mechanism-independent code has already been factored out of
+ * this function, no doubt much remains that will require further
+ * refactoring once we implement other mechanisms.
+ */
+
+static CK_RV generate_keypair_rsa_pkcs(p11_session_t *session,
+                                       const CK_MECHANISM_PTR pMechanism,
+                                       const CK_ATTRIBUTE_PTR pPublicKeyTemplate,
+                                       const CK_ULONG ulPublicKeyAttributeCount,
+                                       const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
+                                       const CK_ULONG ulPrivateKeyAttributeCount,
+                                       CK_OBJECT_HANDLE_PTR phPublicKey,
+                                       CK_OBJECT_HANDLE_PTR phPrivateKey)
+{
+  CK_OBJECT_HANDLE private_handle = CK_INVALID_HANDLE;
+  CK_OBJECT_HANDLE public_handle = CK_INVALID_HANDLE;
+  handle_flavor_t public_handle_flavor = handle_flavor_session_object;
+  handle_flavor_t private_handle_flavor = handle_flavor_session_object;
+  char keyid[CRYPT_MAX_HASHSIZE * 2 + 1];
+  CRYPT_CONTEXT ctx = CRYPT_HANDLE_NONE;
+  const CK_BYTE *id = NULL;
+  CK_ULONG keysize = 0;
+  size_t id_len = 0;
+  CK_RV rv;
+  int i;
+
+  /*
+   * Do mechanism-independent checks before anything else.
+   */
+
+  rv = p11_check_keypair_attributes(session,
+                                    pPublicKeyTemplate,  ulPublicKeyAttributeCount,  &p11_descriptor_rsa_public_key,
+                                    pPrivateKeyTemplate, ulPrivateKeyAttributeCount, &p11_descriptor_rsa_private_key);
+  if (rv != CKR_OK)
+    return rv;
+
+  assert(session             != NULL && pMechanism   != NULL &&
+         pPublicKeyTemplate  != NULL && phPublicKey  != NULL && 
+         pPrivateKeyTemplate != NULL && phPrivateKey != NULL);
+
+  memset(keyid, 0, sizeof(keyid));
+
+  /*
+   * Grab values and perform mechanism-specific checks.
+   */
+
+  for (i = 0; i < ulPublicKeyAttributeCount; i++) {
+    const CK_ATTRIBUTE_TYPE type = pPublicKeyTemplate[i].type;
+    const void * const       val = pPublicKeyTemplate[i].pValue;
+    const size_t             len = pPublicKeyTemplate[i].ulValueLen;
+
+    assert(val != NULL);
+
+    switch (type) {
+
+    case CKA_TOKEN:             /* Object stored on token */
+      public_handle_flavor = p11_handle_flavor_from_cka_token(val);
+      continue;
+
+    case CKA_ID:                /* We use PKCS #11 "ID" as Cryptlib label */
+      id = val;
+      id_len = len;
+      continue;
+
+    case CKA_MODULUS_BITS:      /* Keysize in bits -- Cryptlib only allows multiples of 8 */
+      keysize = *(CK_ULONG *) val;
+      if ((keysize & 7) != 0)
+        return CKR_ATTRIBUTE_VALUE_INVALID;
+      continue;
+
+    }
+  }
+
+  for (i = 0; i < ulPrivateKeyAttributeCount; i++) {
+    const CK_ATTRIBUTE_TYPE type = pPrivateKeyTemplate[i].type;
+    const void * const       val = pPrivateKeyTemplate[i].pValue;
+    const size_t             len = pPrivateKeyTemplate[i].ulValueLen;
+
+    assert (val != NULL);
+
+    switch (type) {
+
+    case CKA_TOKEN:             /* Object stored on token */
+      private_handle_flavor = p11_handle_flavor_from_cka_token(val);
+      continue;
+
+    case CKA_ID:                /* We use PKCS #11 "ID" as Cryptlib label */
+      id = val;
+      id_len = len;
+      continue;
+
+    }
+  }
+
+  /*
+   * We require a key ID and a key size, and if either key is a token
+   * object, the other must be too.
+   */
+  if (id == NULL || id_len == 0 || keysize == 0 || public_handle_flavor != private_handle_flavor)
+    return CKR_TEMPLATE_INCOMPLETE;
+
+  /*
+   * If we got this far, create the PKCS #11 objects.
+   */
+
+  if (!sql_exec("BEGIN"))
+    lose(CKR_FUNCTION_FAILED);
+
+  public_handle = p11_object_create(session, public_handle_flavor,
+                                    pPublicKeyTemplate, ulPublicKeyAttributeCount,
+                                    &p11_descriptor_rsa_public_key, pMechanism);
+
+  private_handle = p11_object_create(session, private_handle_flavor,
+                                     pPrivateKeyTemplate,  ulPrivateKeyAttributeCount,
+                                     &p11_descriptor_rsa_private_key, pMechanism);
+
+  if (public_handle == CK_INVALID_HANDLE || private_handle == CK_INVALID_HANDLE)
+    lose(CKR_FUNCTION_FAILED);    
+
+  /*
+   * Generate the keypair.
+   */
+
+  if (!p11_object_get_keyid(private_handle, keyid, sizeof(keyid))                           ||
+      cryptlib_create_context(&ctx, CRYPT_ALGO_RSA)                             != CRYPT_OK ||
+      cryptSetAttributeString(ctx, CRYPT_CTXINFO_LABEL, keyid, strlen(keyid))   != CRYPT_OK ||
+      cryptSetAttribute(ctx, CRYPT_CTXINFO_KEYSIZE, keysize / 8)                != CRYPT_OK ||
+      cryptGenerateKey(ctx)                                                     != CRYPT_OK ||
+      !p11_object_add_spki(public_handle, private_handle, ctx, p11_object_add_spki_rsa)     ||
+      cryptlib_store_key(ctx)                                                   != CRYPT_OK ||
+      cryptDestroyContext(ctx)                                                  != CRYPT_OK)
+    lose(CKR_FUNCTION_FAILED);
+
+  /*
+   * Commit the SQL transaction.
+   */
+
+  if (!sql_exec("COMMIT"))
+    lose(CKR_FUNCTION_FAILED);
+
+  /*
+   * All went well, return handles and we're done.
+   */
+  *phPublicKey  = public_handle;
+  *phPrivateKey = private_handle;
+  return CKR_OK;
+
+ fail:
+
+  if (ctx != CRYPT_HANDLE_NONE)
+    cryptDestroyContext(ctx);
+
+  if (ctx != CRYPT_HANDLE_NONE && keyid[0] != 0x00)
+    (void) cryptlib_delete_key(keyid);
+
+  if (!sql_exec("ROLLBACK"))
+    rv = CKR_GENERAL_ERROR;
+
+  return rv;
+}
+
+

+
+/*
+ * PKCS #11 API functions.
+ */
+
+CK_RV C_Initialize(CK_VOID_PTR pInitArgs)
+{
+  int initialized_sql = 0, initialized_cryptlib = 0;
+  CK_C_INITIALIZE_ARGS_PTR a = pInitArgs;
+  CK_RV rv;
+
+  if (a != NULL) {
+    int functions_provided = ((a->CreateMutex  != NULL) +
+                              (a->DestroyMutex != NULL) +
+                              (a->LockMutex    != NULL) +
+                              (a->UnlockMutex  != NULL));
+
+    /*
+     * Reserved is, um, reserved.
+     * Threading parameters must either all be present or all be absent.
+     */
+
+    if (a->pReserved != NULL || (functions_provided & 3) != 0)
+      lose(CKR_ARGUMENTS_BAD);
+
+    /*
+     * At present we don't support threads or locking.  This may be a
+     * problem for OpenDNSSEC.  Need to figure out what the "obvious"
+     * system threading mechanism is supposed to be, or make it
+     * configurable, or something.  For the moment, just return the
+     * correct error code to report that we're lame.
+     */
+
+#warning Thread support check disabled, this needs to be fixed
+#if 0
+    if (functions_provided || (a->flags & CKF_OS_LOCKING_OK) != 0)
+      lose(CKR_CANT_LOCK);
+#endif
+  }
+
+  /*
+   * Initialize SQLite3, opening the database(s) and loading the
+   * schema and views.
+   */
+
+  if (!sql_init())
+    lose(CKR_GENERAL_ERROR);
+
+  initialized_sql = 1;
+
+  /*
+   * Initialize cryptlib and open the hardware crypto device (our FPGA).
+   *
+   * The option settings are to make sure that internal stuff like the
+   * PKCS #15 keyset code uses algorithms we like.
+   */
+
+  if (cryptInit() != CRYPT_OK)
+    lose(CKR_GENERAL_ERROR);
+
+  initialized_cryptlib = 1;
+
+  if (cryptSetAttribute(CRYPT_UNUSED, CRYPT_OPTION_ENCR_ALGO, CRYPT_ALGO_AES)  != CRYPT_OK ||
+      cryptSetAttribute(CRYPT_UNUSED, CRYPT_OPTION_ENCR_HASH, CRYPT_ALGO_SHA2) != CRYPT_OK)
+    lose(CKR_GENERAL_ERROR);
+
+#if ENABLE_CRYPTLIB_DEVICE
+  if (cryptDeviceOpen(&cryptlib_device, CRYPT_UNUSED, CRYPT_DEVICE_HARDWARE, NULL) != CRYPT_OK)
+    lose(CKR_GENERAL_ERROR);
+#endif
+
+  return CKR_OK;
+
+ fail:
+
+#if ENABLE_CRYPTLIB_DEVICE
+  if (cryptlib_device != CRYPT_HANDLE_NONE) {
+    cryptDeviceClose(cryptlib_device);
+    cryptlib_device = CRYPT_HANDLE_NONE;
+  }
+#endif
+
+  if (initialized_cryptlib)
+    cryptEnd();
+
+  if (initialized_sql)
+    sql_fini();
+
+  return rv;
+}
+
+CK_RV C_Finalize(CK_VOID_PTR pReserved)
+{
+  if (pReserved != NULL)
+    return CKR_ARGUMENTS_BAD;
+
+  /*
+   * Destroy all current sessions.
+   */
+
+  p11_session_delete_all();
+
+  /*
+   * Shut down SQLite3.
+   */
+
+  if (!sql_fini())
+    return CKR_GENERAL_ERROR;
+
+  /*
+   * Shut down hardware device and exit cryptlib.  Is there any point
+   * in checking error codes here?
+   */
+
+#if ENABLE_CRYPTLIB_DEVICE
+  if (cryptlib_device != CRYPT_HANDLE_NONE)
+    cryptDeviceClose(cryptlib_device);
+  cryptlib_device = CRYPT_HANDLE_NONE;
+#endif
+
+  cryptEnd();
+  return CKR_OK;
+}
+
+CK_RV C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList)
+{
+  /*
+   * Use pkcs11f.h to build dispatch vector for C_GetFunctionList().
+   * This should be const, but that's not what PKCS #11 says, oh well.
+   */
+
+  static CK_FUNCTION_LIST ck_function_list = {
+    { CRYPTOKI_VERSION_MAJOR, CRYPTOKI_VERSION_MINOR },
+#define CK_PKCS11_FUNCTION_INFO(name) name,
+#include "pkcs11f.h"
+#undef  CK_PKCS11_FUNCTION_INFO
+  };
+
+  if (ppFunctionList == NULL)
+    return CKR_ARGUMENTS_BAD;
+
+  *ppFunctionList = &ck_function_list;
+
+  return CKR_OK;
+}
+
+CK_RV C_GetSlotList(CK_BBOOL tokenPresent,
+                    CK_SLOT_ID_PTR pSlotList,
+                    CK_ULONG_PTR pulCount)
+{
+  /*
+   * We only have one slot, and it's hardwired.
+   */
+
+  if (pulCount == NULL)
+    return CKR_ARGUMENTS_BAD;
+
+  if (pSlotList != NULL && *pulCount < 1)
+    return CKR_BUFFER_TOO_SMALL;
+
+  *pulCount = 1;
+
+  if (pSlotList != NULL)
+    pSlotList[0] = P11_ONE_AND_ONLY_SLOT;
+
+  return CKR_OK;
+}
+
+CK_RV C_GetTokenInfo(CK_SLOT_ID slotID,
+                     CK_TOKEN_INFO_PTR pInfo)
+{
+  if (pInfo == NULL)
+    return CKR_ARGUMENTS_BAD;
+
+  if (slotID != P11_ONE_AND_ONLY_SLOT)
+    return CKR_SLOT_ID_INVALID;
+
+  memset(pInfo, 0, sizeof(*pInfo));
+
+  /*
+   * No real idea (yet) how we get many of the following parameters.
+   * See cryptlib's CRYPT_DEVINFO_* attributes for some hints.
+   *
+   * pInfo->label is supposed to be set when the token is initialized.
+   * Not yet sure what that means in our context, but need something
+   * here or the libhsm test programs will bomb trying to find the
+   * right token, so hard-wire something for now.
+   */
+
+  psnprintf(pInfo->label, sizeof(pInfo->label),
+            "Cryptech Token");
+
+  psnprintf(pInfo->manufacturerID, sizeof(pInfo->manufacturerID),
+            "Cryptech Project");
+
+  psnprintf(pInfo->model, sizeof(pInfo->model),
+            "%04x%04x%04x%04x",
+            P11_VERSION_HW_MAJOR, P11_VERSION_HW_MINOR,
+            P11_VERSION_SW_MAJOR, P11_VERSION_SW_MINOR);
+
+  psnprintf(pInfo->serialNumber, sizeof(pInfo->serialNumber),
+            "007");
+
+  pInfo->flags = CKF_RNG | CKF_LOGIN_REQUIRED;
+
+#warning Have not yet sorted out token flags
+#if 0
+    CKF_RNG
+    CKF_WRITE_PROTECTED
+    CKF_LOGIN_REQUIRED
+    CKF_USER_PIN_INITIALIZED
+    CKF_RESTORE_KEY_NOT_NEEDED
+    CKF_CLOCK_ON_TOKEN
+    CKF_PROTECTED_AUTHENTICATION_PATH
+    CKF_DUAL_CRYPTO_OPERATIONS
+    CKF_TOKEN_INITIALIZED
+    CKF_SECONDARY_AUTHENTICATION
+    CKF_USER_PIN_COUNT_LOW
+    CKF_USER_PIN_FINAL_TRY
+    CKF_USER_PIN_LOCKED
+    CKF_USER_PIN_TO_BE_CHANGED
+    CKF_SO_PIN_COUNT_LOW
+    CKF_SO_PIN_FINAL_TRY
+    CKF_SO_PIN_LOCKED
+    CKF_SO_PIN_TO_BE_CHANGED
+    CKF_ERROR_STATE
+#endif
+
+#warning Much of the TOKEN_INFO we return is nonsense
+  pInfo->ulMaxSessionCount      = CK_EFFECTIVELY_INFINITE;
+  pInfo->ulSessionCount         = CK_UNAVAILABLE_INFORMATION;
+  pInfo->ulMaxRwSessionCount    = CK_EFFECTIVELY_INFINITE;
+  pInfo->ulRwSessionCount       = CK_UNAVAILABLE_INFORMATION;
+  pInfo->ulMaxPinLen            = P11_MAX_PIN_LENGTH;
+  pInfo->ulMinPinLen            = P11_MIN_PIN_LENGTH;
+  pInfo->ulTotalPublicMemory    = CK_UNAVAILABLE_INFORMATION;
+  pInfo->ulFreePublicMemory     = CK_UNAVAILABLE_INFORMATION;
+  pInfo->ulTotalPrivateMemory   = CK_UNAVAILABLE_INFORMATION;
+  pInfo->ulFreePrivateMemory    = CK_UNAVAILABLE_INFORMATION;
+  pInfo->hardwareVersion.major  = P11_VERSION_HW_MAJOR;
+  pInfo->hardwareVersion.minor  = P11_VERSION_HW_MINOR;
+  pInfo->firmwareVersion.major  = P11_VERSION_SW_MAJOR;
+  pInfo->firmwareVersion.minor  = P11_VERSION_SW_MINOR;
+
+#warning Need to sort out hardware clock
+#if 0
+  /*
+   * Eventually we expect cryptech devices to have their own hardware
+   * clocks.  Not implemented yet.
+   */
+  pInfo->utcTime;
+#endif
+
+  return CKR_OK;
+}
+
+CK_RV C_OpenSession(CK_SLOT_ID slotID,
+                    CK_FLAGS flags,
+                    CK_VOID_PTR pApplication,
+                    CK_NOTIFY Notify,
+                    CK_SESSION_HANDLE_PTR phSession)
+{
+  const int parallel_session = (flags & CKF_SERIAL_SESSION) == 0;
+  const int read_only_session = (flags & CKF_RW_SESSION) == 0;
+  p11_session_t *session = NULL;
+  CK_RV rv;
+
+  if (slotID != P11_ONE_AND_ONLY_SLOT)
+    lose(CKR_SLOT_ID_INVALID);
+
+  if (phSession == NULL)
+    lose(CKR_ARGUMENTS_BAD);
+
+  if (parallel_session)
+    lose(CKR_SESSION_PARALLEL_NOT_SUPPORTED);
+
+  if ((session = p11_session_new()) == NULL)
+    lose(CKR_HOST_MEMORY);
+
+  switch (logged_in_as) {
+
+  case not_logged_in:
+    session->state = read_only_session ? CKS_RO_PUBLIC_SESSION : CKS_RW_PUBLIC_SESSION;
+    break;
+
+  case logged_in_as_user:
+    session->state = read_only_session ? CKS_RO_USER_FUNCTIONS : CKS_RW_USER_FUNCTIONS;
+    break;
+
+  case logged_in_as_so:
+    if (read_only_session)
+      lose(CKR_SESSION_READ_WRITE_SO_EXISTS);
+    session->state = CKS_RW_SO_FUNCTIONS;
+    break;
+  }    
+
+  session->notify = Notify;
+  session->application = pApplication;
+
+  if (!p11_session_add(session))
+    lose(CKR_FUNCTION_FAILED);
+
+  assert(p11_session_consistent_login());
+
+  *phSession = session->handle;
+  return CKR_OK;
+
+ fail:
+  p11_session_free(session);
+  return rv;
+}
+
+CK_RV C_CloseSession(CK_SESSION_HANDLE hSession)
+{
+  return p11_session_delete(hSession);
+}
+
+CK_RV C_CloseAllSessions(CK_SLOT_ID slotID)
+{
+  if (slotID != P11_ONE_AND_ONLY_SLOT)
+    return CKR_SLOT_ID_INVALID;
+
+  p11_session_delete_all();
+
+  return CKR_OK;
+}
+
+CK_RV C_Login(CK_SESSION_HANDLE hSession,
+              CK_USER_TYPE userType,
+              CK_UTF8CHAR_PTR pPin,
+              CK_ULONG ulPinLen)
+{
+  p11_session_t *session;
+  int crypt_cmd;
+
+  if (pPin == NULL)
+    return CKR_ARGUMENTS_BAD;
+
+  /*
+   * Mind, I don't really know why this function takes a session
+   * handle, given that the semantics don't seem to call upon us to do
+   * anything special for "this" session.
+   */
+
+  if (p11_session_find(hSession) == NULL)
+    return CKR_SESSION_HANDLE_INVALID;
+
+  /*
+   * This is where the combination of pure-software and hardware
+   * starts to get confusing.  See the CRYPT_DEVINFO_* attributes for
+   * the operations we can do during device setup (setting PINs,
+   * logging in using pins, etc).
+   *
+   * All fine, but behaves somewhat differently from the case where
+   * we're doing everything in software and using the PIN primarily as
+   * the encryption password for the PKCS #15 keyring.
+   *
+   * In the long run just want the hardware interface.  This will
+   * require cleanup.
+   */
+
+  /*
+   * We don't currently support re-login without an intervening
+   * logout, so reject the login attempt if we're already logged in.
+   */
+
+  if (logged_in_as != not_logged_in)
+      return CKR_USER_ALREADY_LOGGED_IN;    
+
+  /*
+   * We don't (yet?) support CKU_CONTEXT_SPECIFIC.
+   */
+
+  switch (userType) {
+  case CKU_USER:                crypt_cmd = CRYPT_DEVINFO_AUTHENT_USER;         break;
+  case CKU_SO:                  crypt_cmd = CRYPT_DEVINFO_AUTHENT_SUPERVISOR;   break;
+  case CKU_CONTEXT_SPECIFIC:    return CKR_OPERATION_NOT_INITIALIZED;
+  default:                      return CKR_USER_TYPE_INVALID;
+  }
+
+  /*
+   * Read-only SO is an illegal state, so reject the login attempt if
+   * we have any read-only sessions and we're trying to log in as SO.
+   */
+
+  if (userType == CKU_SO)
+    for (session = p11_sessions; session != NULL; session = session->link)
+      if (session->state == CKS_RO_PUBLIC_SESSION)
+        return CKR_SESSION_READ_ONLY_EXISTS;
+
+  /*
+   * Ask Cryptlib to log us in.  We may need to examine cryptlib error
+   * return more closely than this.
+   */
+
+#if ENABLE_CRYPTLIB_DEVICE
+  if (cryptSetAttributeString(cryptlib_device, crypt_cmd, pPin, ulPinLen) != CRYPT_OK)
+    return CKR_PIN_INCORRECT;
+#endif
+
+#if ENABLE_CRYPTLIB_SOFTWARE
+  {
+    char *newpin;
+    if (memchr(pPin, '\0', ulPinLen) != NULL)
+      return CKR_PIN_INCORRECT;
+    if ((newpin = malloc(ulPinLen + 1)) == NULL)
+      return CKR_HOST_MEMORY;
+    memcpy(newpin, pPin, ulPinLen);
+    newpin[ulPinLen] = '\0';
+    if (pin != NULL)
+      free(pin);
+    pin = newpin;
+  }
+#endif
+
+  /*
+   * Update global login state, then whack each session into correct new state.
+   */
+
+  assert(p11_session_consistent_login());
+
+  logged_in_as = userType == CKU_SO ? logged_in_as_so : logged_in_as_user;
+
+  for (session = p11_sessions; session != NULL; session = session->link) {
+    switch (session->state) {
+
+    case CKS_RO_PUBLIC_SESSION:
+      assert(userType == CKU_USER);
+      session->state = CKS_RO_USER_FUNCTIONS;
+      continue;
+
+    case CKS_RW_PUBLIC_SESSION:
+      session->state = userType == CKU_SO ? CKS_RW_SO_FUNCTIONS : CKS_RW_USER_FUNCTIONS;
+      continue;
+
+    }
+  }
+
+  assert(p11_session_consistent_login());
+
+  return CKR_OK;
+}
+
+CK_RV C_Logout(CK_SESSION_HANDLE hSession)
+{
+  p11_session_t *session;
+  int crypt_cmd;
+
+  /*
+   * Mind, I don't really know why this function takes a session
+   * handle, given that the semantics don't seem to call upon us to do
+   * anything special for "this" session.
+   */
+
+  if (p11_session_find(hSession) == NULL)
+    return CKR_SESSION_HANDLE_INVALID;
+
+  switch (logged_in_as) {
+  case logged_in_as_user:       crypt_cmd = CRYPT_DEVINFO_AUTHENT_USER;         break;
+  case logged_in_as_so:         crypt_cmd = CRYPT_DEVINFO_AUTHENT_SUPERVISOR;   break;
+  case not_logged_in:           return CKR_USER_NOT_LOGGED_IN;
+  }
+
+  /*
+   * This is a bit problematic, because Cryptlib doesn't have a logout
+   * function per se.  For lack of a better idea, construe logout as a
+   * new authentication attempt with an empty PIN.  This is a little
+   * weird, but at least it's something we can use as a relatively
+   * clear signal to the HAL, and it's consistent with the way
+   * cryptlib does things like terminating digest inputs.
+   */
+
+#if ENABLE_CRYPTLIB_DEVICE
+  if (cryptSetAttributeString(cryptlib_device, crypt_cmd, "", 0) != CRYPT_OK)
+    return CKR_FUNCTION_FAILED;
+#endif
+
+#if ENABLE_CRYPTLIB_SOFTWARE
+  if (pin != NULL)
+    free(pin);
+  pin = NULL;
+#endif
+
+  /*
+   * Update global login state, then delete any private objects and
+   * whack every existing session into the right state.
+   */
+
+  assert(p11_session_consistent_login());
+
+  logged_in_as = not_logged_in;
+
+  p11_object_delete_all_private();
+
+  for (session = p11_sessions; session != NULL; session = session->link) {
+    switch (session->state) {
+
+    case CKS_RO_USER_FUNCTIONS:
+      session->state = CKS_RO_PUBLIC_SESSION;
+      continue;
+
+    case CKS_RW_USER_FUNCTIONS:
+    case CKS_RW_SO_FUNCTIONS:
+      session->state = CKS_RW_PUBLIC_SESSION;
+      continue;
+
+    }
+  }
+
+  assert(p11_session_consistent_login());
+
+  return CKR_OK;
+}
+
+CK_RV C_DestroyObject(CK_SESSION_HANDLE hSession,
+                      CK_OBJECT_HANDLE hObject)
+{
+  static const char select_format[] =
+    " SELECT %s_object_id, keyid FROM object NATURAL JOIN %s_object WHERE object_handle = ?1";
+
+  static const char delete_object[] =
+    " DELETE FROM object WHERE object_handle = ?";
+
+  static const char delete_token_object[] =
+    " DELETE FROM token_object WHERE token_object_id = ?";
+
+  const char *flavor = is_token_handle(hObject) ? "token" : "session";
+
+  p11_session_t *session = p11_session_find(hSession);
+  sqlite3_stmt *q = NULL;
+  CK_RV rv = CKR_OK;
+  sqlite3_int64 id;
+
+  if ((rv = p11_object_check_rights(session, hObject, p11_object_access_write)) != CKR_OK)
+    goto fail;
+
+  if (!sql_check_ok(sql_prepare(&q, select_format, flavor, flavor)) ||
+      !sql_check_ok(sqlite3_bind_int64(q, 1, hObject)))
+    lose(CKR_FUNCTION_FAILED);
+
+  switch (sqlite3_step(q)) {
+  case SQLITE_ROW:
+    break;
+  case SQLITE_DONE:
+    lose(CKR_OBJECT_HANDLE_INVALID);
+  default:
+    sql_whine_step();
+    lose(CKR_FUNCTION_FAILED);
+  }
+
+  id = sqlite3_column_int64(q, 0);
+
+  if (sqlite3_column_type(q, 1) == SQLITE_TEXT &&
+      cryptlib_delete_key((const char *) sqlite3_column_text(q, 1)) != CRYPT_OK)
+    lose(CKR_FUNCTION_FAILED);
+
+  sqlite3_finalize(q);
+  q = NULL;
+
+  if (is_token_handle(hObject)                                  &&
+      (!sql_check_ok(sql_prepare(&q, delete_token_object))      ||
+       !sql_check_ok(sqlite3_bind_int64(q, 1, id))              ||
+       !sql_check_done(sqlite3_step(q))))
+    lose(CKR_FUNCTION_FAILED);
+
+  sqlite3_finalize(q);
+  q = NULL;
+
+  if (!sql_check_ok(sql_prepare(&q, delete_object))     ||
+      !sql_check_ok(sqlite3_bind_int64(q, 1, hObject))  ||
+      !sql_check_done(sqlite3_step(q)))
+    lose(CKR_FUNCTION_FAILED);
+
+ fail:
+  sqlite3_finalize(q);
+  return rv;
+}
+
+CK_RV C_GetAttributeValue(CK_SESSION_HANDLE hSession,
+                          CK_OBJECT_HANDLE hObject,
+                          CK_ATTRIBUTE_PTR pTemplate,
+                          CK_ULONG ulCount)
+{
+  static const char select_format[] =
+    " SELECT value FROM %s_attribute NATURAL JOIN object"
+    " WHERE object_handle = ?1 AND type = ?2";
+
+  const char *flavor = is_token_handle(hObject) ? "token" : "session";
+
+  p11_session_t *session = p11_session_find(hSession);
+  const p11_descriptor_t *descriptor = NULL;
+  CK_BBOOL cka_sensitive, cka_extractable;
+  CK_OBJECT_CLASS cka_class;
+  CK_KEY_TYPE cka_key_type;
+  int sensitive_object = 0;
+  sqlite3_stmt *q = NULL;
+  CK_RV rv = CKR_OK;
+  int ret, i;
+
+  if (pTemplate == NULL)
+    lose(CKR_ARGUMENTS_BAD);
+
+  if ((rv = p11_object_check_rights(session, hObject, p11_object_access_read)) != CKR_OK)
+    goto fail;
+
+  if (!p11_attribute_get_ulong(hObject, CKA_CLASS, &cka_class))
+    lose(CKR_OBJECT_HANDLE_INVALID);
+
+  switch (cka_class) {
+
+  case CKO_PRIVATE_KEY:
+  case CKO_SECRET_KEY:
+    if (!p11_attribute_get_bbool(hObject, CKA_EXTRACTABLE, &cka_extractable) ||
+        !p11_attribute_get_bbool(hObject, CKA_SENSITIVE,   &cka_sensitive))
+      lose(CKR_OBJECT_HANDLE_INVALID);
+
+    sensitive_object = cka_sensitive || !cka_extractable;
+
+    /* Fall through */
+
+  case CKO_PUBLIC_KEY:
+    if (!p11_attribute_get_ulong(hObject, CKA_KEY_TYPE, &cka_key_type))
+      lose(CKR_OBJECT_HANDLE_INVALID);
+    descriptor = p11_descriptor_from_key_type(cka_class, cka_key_type);
+  }
+
+  if (!sql_check_ok(sql_prepare(&q, select_format, flavor)) ||
+      !sql_check_ok(sqlite3_bind_int64(q, 1, hObject)))
+    lose(CKR_FUNCTION_FAILED);
+
+  for (i = 0; i < ulCount; i++) {
+
+    if (sensitive_object && p11_attribute_is_sensitive(descriptor, pTemplate[i].type)) {
+      pTemplate[i].ulValueLen = -1;
+      rv = CKR_ATTRIBUTE_SENSITIVE;
+    }
+
+    else if (!sql_check_ok(sqlite3_reset(q)) ||
+             !sql_check_ok(sqlite3_bind_int64(q, 2, pTemplate[i].type)) ||
+             (ret = sqlite3_step(q)) != SQLITE_ROW) {
+      if (ret != SQLITE_DONE)
+        sql_whine_step();
+      pTemplate[i].ulValueLen = -1;
+      rv = CKR_ATTRIBUTE_TYPE_INVALID;
+    }
+
+    else if (pTemplate[i].pValue == NULL) {
+      pTemplate[i].ulValueLen = sqlite3_column_bytes(q, 0);
+    }
+
+    else if (pTemplate[i].ulValueLen >= sqlite3_column_bytes(q, 0)) {
+      pTemplate[i].ulValueLen = sqlite3_column_bytes(q, 0);
+      memcpy(pTemplate[i].pValue, sqlite3_column_blob(q, 0), pTemplate[i].ulValueLen);
+    }
+
+    else {
+      pTemplate[i].ulValueLen = -1;
+      rv = CKR_BUFFER_TOO_SMALL;
+    }
+
+  }
+
+ fail:
+  sqlite3_finalize(q);
+  return rv;
+}
+
+CK_RV C_FindObjectsInit(CK_SESSION_HANDLE hSession,
+                        CK_ATTRIBUTE_PTR pTemplate,
+                        CK_ULONG ulCount)
+{
+  static const char select_missing[] =
+    " WITH"
+    "   known AS (SELECT token_object_id FROM object WHERE token_object_id IS NOT NULL)"
+    " SELECT token_object_id FROM token_object WHERE token_object_id NOT IN known";
+
+  static const char insert_missing[] =
+    " INSERT INTO object (object_handle, token_object_id) VALUES (?1, ?2)";
+
+  static const char create_format[] =
+    " CREATE TEMPORARY TABLE findobjects_%lu AS"
+    " SELECT object_id FROM object NATURAL LEFT JOIN session"
+    " WHERE session_handle IS NULL OR session_handle = ?1";
+
+  static const char drop_format[] =
+    " DROP TABLE IF EXISTS findobjects_%lu";
+
+  static const char delete_format[] =
+    " WITH"
+    "   matches AS (SELECT object_id"
+    "                 FROM object NATURAL JOIN session_attribute"
+    "                 WHERE type = ?1 AND value = ?2"
+    "               UNION"
+    "               SELECT object_id"
+    "                 FROM object NATURAL JOIN token_attribute"
+    "                 WHERE type = ?1 AND value = ?2)"
+    " DELETE FROM findobjects_%lu WHERE object_id NOT IN matches";
+
+  static const char select_format[] =
+    " SELECT object_handle FROM findobjects_%lu NATURAL JOIN object ORDER BY object_id";
+
+  p11_session_t *session = p11_session_find(hSession);
+  sqlite3_stmt *q1 = NULL, *q2 = NULL;
+  CK_RV rv = CKR_OK;
+  int i, ret;
+
+  if (session == NULL)
+    lose(CKR_SESSION_HANDLE_INVALID);
+
+  if (ulCount > 0  && pTemplate == NULL)
+    lose(CKR_ARGUMENTS_BAD);
+
+  if (session->find_query != NULL)
+    lose(CKR_OPERATION_ACTIVE);
+
+  /*
+   * Assign handles to any token objects that don't have them yet.
+   */
+
+  if (!sql_check_ok(sql_prepare(&q1, select_missing))   ||
+      !sql_check_ok(sql_prepare(&q2, insert_missing)))
+    lose(CKR_FUNCTION_FAILED);
+
+  while ((ret = sqlite3_step(q1)) == SQLITE_ROW) {
+    sqlite3_int64 token_object_id = sqlite3_column_int64(q1, 0);
+    CK_OBJECT_HANDLE object_handle = p11_allocate_unused_handle(handle_flavor_token_object);
+
+    if (!sql_check_ok(sqlite3_reset(q2))                                ||
+        !sql_check_ok(sqlite3_bind_int64(q2, 1, object_handle))         ||
+        !sql_check_ok(sqlite3_bind_int64(q2, 2, token_object_id))       ||
+        !sql_check_done(sqlite3_step(q2)))
+      lose(CKR_FUNCTION_FAILED);
+  }
+
+  if (ret != SQLITE_DONE) {
+    sql_whine_step();
+    lose(CKR_FUNCTION_FAILED);
+  }
+
+  sqlite3_finalize(q1);
+  sqlite3_finalize(q2);
+  q1 = q2 = NULL;
+
+  /*
+   * Create a temporary table to hold this session's FindObjects
+   * state.  Populate this with every object this session knows about,
+   * then prune based on login status and whatever filter attributes
+   * the caller supplied.
+   */
+
+  if (!sql_check_ok(sql_prepare(&q1, drop_format, hSession))    ||
+      !sql_check_done(sqlite3_step(q1))                         ||
+      !sql_check_ok(sql_prepare(&q2, create_format, hSession))  ||
+      !sql_check_ok(sqlite3_bind_int64(q2, 1, hSession))        ||
+      !sql_check_done(sqlite3_step(q2)))
+    lose(CKR_FUNCTION_FAILED);
+
+  sqlite3_finalize(q1);
+  sqlite3_finalize(q2);
+  q1 = q2 = NULL;
+
+  if (!sql_check_ok(sql_prepare(&q1, delete_format, hSession)))
+    lose(CKR_FUNCTION_FAILED);
+
+  /*
+   * We only see private objects when logged in as the regular user.
+   */
+
+  if (logged_in_as != logged_in_as_user) {
+    if (!sql_check_ok(sqlite3_bind_int64(q1, 1, CKA_PRIVATE)) ||
+        !sql_check_ok(sqlite3_bind_blob(q1, 2, &const_CK_FALSE, sizeof(const_CK_FALSE), NULL)) ||
+        !sql_check_done(sqlite3_step(q1)))
+      lose(CKR_FUNCTION_FAILED);
+  }
+
+  /*
+   * Filter through the caller-supplied template.
+   *
+   * NB: This doesn't support some of the more obscure searches, such
+   * as searches for sessions or hardware features.  Too much rope
+   * already, worry about those if we ever really need them.
+   */
+
+  for (i = 0; i < ulCount; i++)
+    if (!sql_check_ok(sqlite3_reset(q1))                                ||
+        !sql_check_ok(sqlite3_bind_int64(q1, 1, pTemplate[i].type))     ||
+        !sql_check_ok(sqlite3_bind_blob( q1, 2, pTemplate[i].pValue,
+                                        pTemplate[i].ulValueLen, NULL)) ||
+        !sql_check_done(sqlite3_step(q1)))
+      lose(CKR_FUNCTION_FAILED);
+
+  /*
+   * Stash a prepared query in the session object which will return
+   * whatever object handles survived all that filtering.
+   */
+
+  if (!sql_check_ok(sql_prepare(&session->find_query, select_format, hSession)))
+    lose(CKR_FUNCTION_FAILED);  
+  session->find_query_done = 0;
+
+ fail:
+  sqlite3_finalize(q1);
+  sqlite3_finalize(q2);
+  return rv;
+}
+
+CK_RV C_FindObjects(CK_SESSION_HANDLE hSession,
+                    CK_OBJECT_HANDLE_PTR phObject,
+                    CK_ULONG ulMaxObjectCount,
+                    CK_ULONG_PTR pulObjectCount)
+{
+  p11_session_t *session = p11_session_find(hSession);
+  int i, ret = SQLITE_OK;
+  CK_RV rv = CKR_OK;
+
+  if (session == NULL)
+    lose(CKR_SESSION_HANDLE_INVALID);
+
+  if (session->find_query == NULL)
+    lose(CKR_OPERATION_NOT_INITIALIZED);
+
+  if (phObject == NULL || pulObjectCount == NULL)
+    lose(CKR_ARGUMENTS_BAD);
+
+  /*
+   * C_FindObjectsInit() did all the heavy lifting, we just have to
+   * return the resulting handles.
+   */
+
+  i = 0;
+
+  if (!session->find_query_done)
+    while (i < ulMaxObjectCount && (ret = sqlite3_step(session->find_query)) == SQLITE_ROW)
+      phObject[i++] = (CK_OBJECT_HANDLE) sqlite3_column_int64(session->find_query, 0);
+
+  switch (ret) {
+
+  case SQLITE_DONE:
+    session->find_query_done = 1;
+    break;
+
+  case SQLITE_OK:
+  case SQLITE_ROW:
+    break;
+
+  default:
+    sql_whine_step();
+    lose(CKR_FUNCTION_FAILED);
+
+  }
+
+  *pulObjectCount = i;
+
+ fail:
+  return rv;
+}
+
+CK_RV C_FindObjectsFinal(CK_SESSION_HANDLE hSession)
+{
+  static const char drop_format[] =
+    " DROP TABLE IF EXISTS findobjects_%lu";
+
+  p11_session_t *session = p11_session_find(hSession);
+  sqlite3_stmt *q = NULL;
+  CK_RV rv = CKR_OK;
+  
+  if (session == NULL)
+    lose(CKR_SESSION_HANDLE_INVALID);
+
+  if (session->find_query == NULL)
+    lose(CKR_OPERATION_NOT_INITIALIZED);
+
+  /*
+   * Clean up result query and temporary table.
+   */
+
+  sqlite3_finalize(session->find_query);
+  session->find_query = NULL;
+
+  if (!sql_check_ok(sql_prepare(&q, drop_format, hSession)) ||
+      !sql_check_done(sqlite3_step(q)))
+    lose(CKR_FUNCTION_FAILED);
+
+ fail:
+  sqlite3_finalize(q);
+  return rv;
+}
+
+CK_RV C_DigestInit(CK_SESSION_HANDLE hSession,
+                   CK_MECHANISM_PTR pMechanism)
+{
+  p11_session_t *session = p11_session_find(hSession);
+  CRYPT_ALGO_TYPE algo;
+  unsigned hash_len;
+  CK_RV rv;
+
+  if (session == NULL)
+    return CKR_SESSION_HANDLE_INVALID;
+
+  if (pMechanism == NULL)
+    return CKR_ARGUMENTS_BAD;
+
+  if (session->digest_context != CRYPT_HANDLE_NONE)
+    return CKR_OPERATION_ACTIVE;
+
+  switch (pMechanism->mechanism) {
+  case CKM_SHA_1:       algo = CRYPT_ALGO_SHA1; break;
+  case CKM_SHA256:      algo = CRYPT_ALGO_SHA2; hash_len = 256; break;
+  case CKM_SHA384:      algo = CRYPT_ALGO_SHA2; hash_len = 384; break;
+  case CKM_SHA512:      algo = CRYPT_ALGO_SHA2; hash_len = 512; break;
+  default:              return CKR_MECHANISM_INVALID;
+  }
+
+  assert((hash_len & 7) == 0);
+
+  if (cryptlib_create_context(&session->digest_context, algo) != CRYPT_OK)
+    lose(CKR_FUNCTION_FAILED);    
+
+  if (algo == CRYPT_ALGO_SHA2 &&
+      cryptSetAttribute(session->digest_context, CRYPT_CTXINFO_BLOCKSIZE, hash_len >> 3) != CRYPT_OK)
+    lose(CKR_FUNCTION_FAILED);
+
+  return CKR_OK;
+
+ fail:
+  if (session->digest_context != CRYPT_HANDLE_NONE)
+    cryptDestroyContext(session->digest_context);
+  session->digest_context = CRYPT_HANDLE_NONE;
+  return rv;
+}
+
+CK_RV C_Digest(CK_SESSION_HANDLE hSession,
+               CK_BYTE_PTR pData,
+               CK_ULONG ulDataLen,
+               CK_BYTE_PTR pDigest,
+               CK_ULONG_PTR pulDigestLen)
+{
+  p11_session_t *session = p11_session_find(hSession);
+  CK_RV rv;
+  int len;
+
+  if (session == NULL)
+    return CKR_SESSION_HANDLE_INVALID;
+
+  if (pData == NULL || pulDigestLen == NULL)
+    return CKR_ARGUMENTS_BAD;
+
+  if (session->digest_context == CRYPT_HANDLE_NONE)
+    return CKR_OPERATION_NOT_INITIALIZED;
+
+  if (pDigest == NULL) {
+    if (cryptGetAttribute(session->digest_context, CRYPT_CTXINFO_BLOCKSIZE, &len) != CRYPT_OK)
+      lose(CKR_FUNCTION_FAILED);
+    *pulDigestLen = len;
+    return CKR_OK;
+  }
+
+  if (cryptEncrypt(session->digest_context, pData, ulDataLen) != CRYPT_OK)
+    lose(CKR_FUNCTION_FAILED);
+
+  if (ulDataLen != 0 &&
+      cryptEncrypt(session->digest_context, pData, 0) != CRYPT_OK)
+    lose(CKR_FUNCTION_FAILED);
+
+  if (cryptGetAttributeString(session->digest_context,
+                              CRYPT_CTXINFO_HASHVALUE, NULL, &len) != CRYPT_OK)
+    lose(CKR_FUNCTION_FAILED);
+
+  if (len > *pulDigestLen)
+    lose(CKR_BUFFER_TOO_SMALL);
+
+  if (cryptGetAttributeString(session->digest_context,
+                              CRYPT_CTXINFO_HASHVALUE, pDigest, &len) != CRYPT_OK)
+    lose(CKR_FUNCTION_FAILED);
+
+  *pulDigestLen = len;
+
+  rv = CKR_OK;                  /* Fall through */
+
+ fail:
+  cryptDestroyContext(session->digest_context);
+  session->digest_context = CRYPT_HANDLE_NONE;
+  return rv;
+}
+
+CK_RV C_SignInit(CK_SESSION_HANDLE hSession,
+                 CK_MECHANISM_PTR pMechanism,
+                 CK_OBJECT_HANDLE hKey)
+{
+  p11_session_t *session = p11_session_find(hSession);
+  char keyid[CRYPT_MAX_HASHSIZE * 2 + 1];
+  CRYPT_ALGO_TYPE sign_algo, hash_algo;
+  unsigned hash_size = 0;
+  int key_algo;
+  CK_RV rv;
+
+  if (session == NULL)
+    return CKR_SESSION_HANDLE_INVALID;
+
+  if (pMechanism == NULL)
+    return CKR_ARGUMENTS_BAD;
+
+  if (session->sign_key_context    != CRYPT_HANDLE_NONE ||
+      session->sign_digest_context != CRYPT_HANDLE_NONE)
+    return CKR_OPERATION_ACTIVE;
+
+  if ((rv = p11_object_check_rights(session, hKey, p11_object_access_read)) != CKR_OK)
+    goto fail;
+
+  switch (pMechanism->mechanism) {
+  case CKM_RSA_PKCS:            sign_algo = CRYPT_ALGO_RSA; hash_algo = CRYPT_ALGO_NONE; break;
+  case CKM_SHA1_RSA_PKCS:       sign_algo = CRYPT_ALGO_RSA; hash_algo = CRYPT_ALGO_SHA1; break;
+  case CKM_SHA256_RSA_PKCS:     sign_algo = CRYPT_ALGO_RSA; hash_algo = CRYPT_ALGO_SHA2; hash_size = 256; break;
+  case CKM_SHA384_RSA_PKCS:     sign_algo = CRYPT_ALGO_RSA; hash_algo = CRYPT_ALGO_SHA2; hash_size = 384; break;
+  case CKM_SHA512_RSA_PKCS:     sign_algo = CRYPT_ALGO_RSA; hash_algo = CRYPT_ALGO_SHA2; hash_size = 512; break;
+  default:                      return CKR_MECHANISM_INVALID;
+  }
+
+  assert((hash_size & 7) == 0);
+
+  if (!p11_object_get_keyid(hKey, keyid, sizeof(keyid)) ||
+      cryptlib_load_key(&session->sign_key_context, keyid) != CRYPT_OK ||
+      cryptGetAttribute(session->sign_key_context, CRYPT_CTXINFO_ALGO, &key_algo) != CRYPT_OK)
+    lose(CKR_KEY_HANDLE_INVALID);
+
+  if (sign_algo != key_algo)
+    lose(CKR_KEY_TYPE_INCONSISTENT);
+
+  if (hash_algo != CRYPT_ALGO_NONE &&
+      cryptlib_create_context(&session->sign_digest_context, hash_algo) != CRYPT_OK)
+    lose(CKR_FUNCTION_FAILED);    
+
+  if (hash_algo == CRYPT_ALGO_SHA2 &&
+      cryptSetAttribute(session->sign_digest_context,
+                        CRYPT_CTXINFO_BLOCKSIZE, hash_size >> 3) != CRYPT_OK)
+    lose(CKR_FUNCTION_FAILED);
+
+  return CKR_OK;
+
+ fail:
+  if (session->sign_key_context != CRYPT_HANDLE_NONE)
+    cryptDestroyContext(session->sign_key_context);
+  session->sign_key_context = CRYPT_HANDLE_NONE;
+
+  if (session->sign_digest_context != CRYPT_HANDLE_NONE)
+    cryptDestroyContext(session->sign_digest_context);
+  session->sign_digest_context = CRYPT_HANDLE_NONE;
+
+  return rv;
+}
+
+CK_RV C_Sign(CK_SESSION_HANDLE hSession,
+             CK_BYTE_PTR pData,
+             CK_ULONG ulDataLen,
+             CK_BYTE_PTR pSignature,
+             CK_ULONG_PTR pulSignatureLen)
+{
+  p11_session_t *session = p11_session_find(hSession);
+  int len, algo;
+  CK_RV rv;
+
+  if (session == NULL)
+    return CKR_SESSION_HANDLE_INVALID;
+
+  if (pData == NULL || pulSignatureLen == NULL)
+    return CKR_ARGUMENTS_BAD;
+
+  if (session->sign_key_context == CRYPT_HANDLE_NONE)
+    return CKR_OPERATION_NOT_INITIALIZED;
+
+  if (pSignature == NULL) {
+    /*
+     * Caller just wants to know the signature length, which we can
+     * get from cryptCreateSignature(), using a dummy hash context if
+     * necessary.
+     *
+     * There may be an easier way: at least for RSA, reading the key's
+     * CRYPT_CTXINFO_KEYSIZE would give us the answer.  But the
+     * constraint that messages_size == key_size doesn't necessarily
+     * hold for all asymmetric algorithms, so best to be safe here.
+     */
+
+    CRYPT_CONTEXT ctx = session->sign_digest_context;
+
+    if (ctx == CRYPT_HANDLE_NONE && cryptCreateContext(&ctx, CRYPT_UNUSED, CRYPT_ALGO_SHA2) != CRYPT_OK)
+      lose(CKR_FUNCTION_FAILED);
+
+    if (cryptCreateSignature(NULL, 0, &len, session->sign_key_context, ctx) != CRYPT_OK)
+      len = 0;
+
+    if (session->sign_digest_context == CRYPT_HANDLE_NONE)
+      cryptDestroyContext(ctx);
+
+    if (len == 0)
+      lose(CKR_FUNCTION_FAILED);
+  }
+
+  else if (session->sign_digest_context != CRYPT_HANDLE_NONE) {
+    /*
+     * Caller wanted a hash-and-sign operation, so we can use cryptCreateSignature().
+     */
+
+    if (cryptEncrypt(session->sign_digest_context, pData, ulDataLen) != CRYPT_OK)
+      lose(CKR_FUNCTION_FAILED);
+
+    if (ulDataLen != 0 &&
+        cryptEncrypt(session->sign_digest_context, pData, 0) != CRYPT_OK)
+      lose(CKR_FUNCTION_FAILED);
+
+    if (cryptCreateSignature(pSignature, *pulSignatureLen, &len,
+                             session->sign_key_context,
+                             session->sign_digest_context) != CRYPT_OK)
+      lose(CKR_FUNCTION_FAILED);
+  }
+
+  else {
+
+    /*
+     * Caller wanted a pure-signature operation, have to use
+     * cryptDeCrypt() [sic].
+     *
+     * At the moment we just blindly sign this without checking that
+     * what we're signing really is (eg) a valid DigestInfo SEQUENCE.
+     * Should we bother checking the syntax here, given that we have
+     * no way of checking the digest itself (if we get here, we've
+     * never seen the original plaintext, just the purported digest)?
+     */
+
+    if (cryptGetAttribute(session->sign_key_context, CRYPT_CTXINFO_ALGO,   &algo) != CRYPT_OK ||
+        cryptGetAttribute(session->sign_key_context, CRYPT_CTXINFO_KEYSIZE, &len) != CRYPT_OK)
+      lose(CKR_FUNCTION_FAILED);
+
+    switch (algo) {
+
+    case CRYPT_ALGO_RSA:
+
+      /*
+       * Congregation will now please turn to RFC 2313 8.1 as we
+       * construct a PKCS #1.5 type 01 encryption block.
+       */
+
+      if (len > *pulSignatureLen)
+        lose(CKR_BUFFER_TOO_SMALL);
+
+      if (ulDataLen > len - 11)
+        return CKR_DATA_LEN_RANGE;
+
+      pSignature[0] = 0x00;
+      pSignature[1] = 0x01;
+      memset(pSignature + 2, 0xFF, len - 3 - ulDataLen);
+      pSignature[len - ulDataLen - 1] = 0x00;
+      memcpy(pSignature + len - ulDataLen, pData, ulDataLen);
+
+#if 0
+      /* XXX */
+      {
+        int i;
+        fprintf(stderr, "[PKCS #1.5 len %lu ulDataLen %lu block ", len, ulDataLen);
+        for (i = 0; i < len; i++)
+          fprintf(stderr, "%s%02x", i == 0 ? "" : ":", pSignature[i]);
+        fprintf(stderr, "]\n");
+      }
+#endif
+
+      break;
+
+    default:
+      lose(CKR_FUNCTION_FAILED);
+    }
+
+    /*
+     * The terms "encrypt" and "decrypt" get weird when one goes this
+     * far past the API that a sane person would be using.  As
+     * explained in RFC 3447, the RSASP1 (signature generation)
+     * operation is the same mathematical operation as the RSADP
+     * (decryption) operation, so we have to use cryptDecrypt(), not
+     * cryptEncrypt() here.  No, really.
+     *
+     * Well, this works for RSA, anyway.  ECDSA may turn out to be a
+     * whole different bucket of monkey guts.
+     */
+
+    if (cryptDecrypt(session->sign_key_context, pSignature, len) != CRYPT_OK)
+      lose(CKR_FUNCTION_FAILED);
+  }
+
+  *pulSignatureLen = len;
+
+  rv = CKR_OK;                  /* Fall through */
+
+ fail:
+
+  if (session->sign_digest_context != CRYPT_HANDLE_NONE)
+    cryptDestroyContext(session->sign_digest_context);
+  session->sign_digest_context = CRYPT_HANDLE_NONE;
+
+  cryptDestroyContext(session->sign_key_context);
+  session->sign_key_context = CRYPT_HANDLE_NONE;
+
+  return rv;
+}
+
+/*
+ * libhsm only uses C_GenerateKey() for DSA parameter generation.
+ * More general use presumably wants this for things like generating
+ * symmetric keys for later wrapping by asymmetric keys.
+ */
+CK_RV C_GenerateKey(CK_SESSION_HANDLE hSession,
+                    CK_MECHANISM_PTR pMechanism,
+                    CK_ATTRIBUTE_PTR pTemplate,
+                    CK_ULONG ulCount,
+                    CK_OBJECT_HANDLE_PTR phKey)
+{
+  return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE hSession,
+                        CK_MECHANISM_PTR pMechanism,
+                        CK_ATTRIBUTE_PTR pPublicKeyTemplate,
+                        CK_ULONG ulPublicKeyAttributeCount,
+                        CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
+                        CK_ULONG ulPrivateKeyAttributeCount,
+                        CK_OBJECT_HANDLE_PTR phPublicKey,
+                        CK_OBJECT_HANDLE_PTR phPrivateKey)
+{
+  p11_session_t *session = p11_session_find(hSession);
+
+  if (session == NULL)
+    return CKR_SESSION_HANDLE_INVALID;
+
+  if (pMechanism          == NULL ||
+      pPublicKeyTemplate  == NULL || phPublicKey  == NULL ||
+      pPrivateKeyTemplate == NULL || phPrivateKey == NULL)
+    return CKR_ARGUMENTS_BAD;
+
+  switch (pMechanism->mechanism) {
+  case CKM_RSA_PKCS_KEY_PAIR_GEN:
+    return generate_keypair_rsa_pkcs(session, pMechanism,
+                                     pPublicKeyTemplate, ulPublicKeyAttributeCount,
+                                     pPrivateKeyTemplate, ulPrivateKeyAttributeCount,
+                                     phPublicKey, phPrivateKey);
+  default:
+    return CKR_MECHANISM_INVALID;
+  }
+}
+
+CK_RV C_GenerateRandom(CK_SESSION_HANDLE hSession,
+                       CK_BYTE_PTR RandomData,
+                       CK_ULONG ulRandomLen)
+{
+  p11_session_t *session = p11_session_find(hSession);
+  CRYPT_CONTEXT ctx = CRYPT_HANDLE_NONE;
+  CK_RV rv = CKR_OK;
+
+  if (session == NULL)
+    return CKR_SESSION_HANDLE_INVALID;
+
+  if (RandomData == NULL)
+    return CKR_ARGUMENTS_BAD;
+
+  /*
+   * Cryptlib doesn't expose the raw TRNG, but, per the manual, block
+   * cipher encryption output with a randomly generated key is good
+   * enough for most sane purposes.
+   *
+   * Not certain why the Cryptlib manual suggests using CFB mode
+   * instead of OFB mode here, but going with the manual for now.
+   */
+
+  if (cryptCreateContext(&ctx, CRYPT_UNUSED, CRYPT_ALGO_AES)     != CRYPT_OK ||
+      cryptSetAttribute(ctx, CRYPT_CTXINFO_MODE, CRYPT_MODE_CFB) != CRYPT_OK ||
+      cryptGenerateKey(ctx)                                      != CRYPT_OK ||
+      cryptEncrypt(ctx, RandomData, ulRandomLen)                 != CRYPT_OK)
+    lose(CKR_FUNCTION_FAILED);
+
+ fail:
+  if (ctx != CRYPT_HANDLE_NONE)
+    (void) cryptDestroyContext(ctx);
+
+  return rv;
+}
+
+

+
+/*
+ * Stubs for unsupported functions below here.  Per the PKCS #11
+ * specification, it's OK to skip implementing almost any function in
+ * the API, but if one does so, one must provide a stub which returns
+ * CKR_FUNCTION_NOT_SUPPORTED, because every slot in the dispatch
+ * vector must be populated.  We could reuse a single stub for all the
+ * unimplemented slots, but the type signatures wouldn't match, which
+ * would require some nasty casts I'd rather avoid.
+ *
+ * Many of these functions would be straightforward to implement, but
+ * there are enough bald yaks in this saga already.
+ */
+
+CK_RV C_GetInfo(CK_INFO_PTR pInfo)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_GetSlotInfo(CK_SLOT_ID slotID,
+                    CK_SLOT_INFO_PTR pInfo)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_GetMechanismList(CK_SLOT_ID slotID,
+                         CK_MECHANISM_TYPE_PTR pMechanismList,
+                         CK_ULONG_PTR pulCount)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_GetMechanismInfo(CK_SLOT_ID slotID,
+                         CK_MECHANISM_TYPE type,
+                         CK_MECHANISM_INFO_PTR pInfo)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_InitToken(CK_SLOT_ID slotID,
+                  CK_UTF8CHAR_PTR pPin,
+                  CK_ULONG ulPinLen,
+                  CK_UTF8CHAR_PTR pLabel)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_InitPIN(CK_SESSION_HANDLE hSession,
+                CK_UTF8CHAR_PTR pPin,
+                CK_ULONG ulPinLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_SetPIN(CK_SESSION_HANDLE hSession,
+               CK_UTF8CHAR_PTR pOldPin,
+               CK_ULONG ulOldLen,
+               CK_UTF8CHAR_PTR pNewPin,
+               CK_ULONG ulNewLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_GetSessionInfo(CK_SESSION_HANDLE hSession,
+                       CK_SESSION_INFO_PTR pInfo)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_GetOperationState(CK_SESSION_HANDLE hSession,
+                          CK_BYTE_PTR pOperationState,
+                          CK_ULONG_PTR pulOperationStateLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_SetOperationState(CK_SESSION_HANDLE hSession,
+                          CK_BYTE_PTR pOperationState,
+                          CK_ULONG ulOperationStateLen,
+                          CK_OBJECT_HANDLE hEncryptionKey,
+                          CK_OBJECT_HANDLE hAuthenticationKey)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_CreateObject(CK_SESSION_HANDLE hSession,
+                     CK_ATTRIBUTE_PTR pTemplate,
+                     CK_ULONG ulCount,
+                     CK_OBJECT_HANDLE_PTR phObject)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_CopyObject(CK_SESSION_HANDLE hSession,
+                   CK_OBJECT_HANDLE hObject,
+                   CK_ATTRIBUTE_PTR pTemplate,
+                   CK_ULONG ulCount,
+                   CK_OBJECT_HANDLE_PTR phNewObject)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_GetObjectSize(CK_SESSION_HANDLE hSession,
+                      CK_OBJECT_HANDLE hObject,
+                      CK_ULONG_PTR pulSize)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_SetAttributeValue(CK_SESSION_HANDLE hSession,
+                          CK_OBJECT_HANDLE hObject,
+                          CK_ATTRIBUTE_PTR pTemplate,
+                          CK_ULONG ulCount)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_EncryptInit(CK_SESSION_HANDLE hSession,
+                    CK_MECHANISM_PTR pMechanism,
+                    CK_OBJECT_HANDLE hKey)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_Encrypt(CK_SESSION_HANDLE hSession,
+                CK_BYTE_PTR pData,
+                CK_ULONG ulDataLen,
+                CK_BYTE_PTR pEncryptedData,
+                CK_ULONG_PTR pulEncryptedDataLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_EncryptUpdate(CK_SESSION_HANDLE hSession,
+                      CK_BYTE_PTR pPart,
+                      CK_ULONG ulPartLen,
+                      CK_BYTE_PTR pEncryptedPart,
+                      CK_ULONG_PTR pulEncryptedPartLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_EncryptFinal(CK_SESSION_HANDLE hSession,
+                     CK_BYTE_PTR pLastEncryptedPart,
+                     CK_ULONG_PTR pulLastEncryptedPartLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_DecryptInit(CK_SESSION_HANDLE hSession,
+                    CK_MECHANISM_PTR pMechanism,
+                    CK_OBJECT_HANDLE hKey)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_Decrypt(CK_SESSION_HANDLE hSession,
+                CK_BYTE_PTR pEncryptedData,
+                CK_ULONG ulEncryptedDataLen,
+                CK_BYTE_PTR pData,
+                CK_ULONG_PTR pulDataLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_DecryptUpdate(CK_SESSION_HANDLE hSession,
+                      CK_BYTE_PTR pEncryptedPart,
+                      CK_ULONG ulEncryptedPartLen,
+                      CK_BYTE_PTR pPart,
+                      CK_ULONG_PTR pulPartLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_DecryptFinal(CK_SESSION_HANDLE hSession,
+                     CK_BYTE_PTR pLastPart,
+                     CK_ULONG_PTR pulLastPartLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_DigestUpdate(CK_SESSION_HANDLE hSession,
+                     CK_BYTE_PTR pPart,
+                     CK_ULONG ulPartLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_DigestKey(CK_SESSION_HANDLE hSession,
+                  CK_OBJECT_HANDLE hKey)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_DigestFinal(CK_SESSION_HANDLE hSession,
+                    CK_BYTE_PTR pDigest,
+                    CK_ULONG_PTR pulDigestLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_SignUpdate(CK_SESSION_HANDLE hSession,
+                   CK_BYTE_PTR pPart,
+                   CK_ULONG ulPartLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_SignFinal(CK_SESSION_HANDLE hSession,
+                  CK_BYTE_PTR pSignature,
+                  CK_ULONG_PTR pulSignatureLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_SignRecoverInit(CK_SESSION_HANDLE hSession,
+                        CK_MECHANISM_PTR pMechanism,
+                        CK_OBJECT_HANDLE hKey)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_SignRecover(CK_SESSION_HANDLE hSession,
+                    CK_BYTE_PTR pData,
+                    CK_ULONG ulDataLen,
+                    CK_BYTE_PTR pSignature,
+                    CK_ULONG_PTR pulSignatureLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_VerifyInit(CK_SESSION_HANDLE hSession,
+                   CK_MECHANISM_PTR pMechanism,
+                   CK_OBJECT_HANDLE hKey )
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_Verify(CK_SESSION_HANDLE hSession,
+               CK_BYTE_PTR pData,
+               CK_ULONG ulDataLen,
+               CK_BYTE_PTR pSignature,
+               CK_ULONG ulSignatureLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_VerifyUpdate(CK_SESSION_HANDLE hSession,
+                     CK_BYTE_PTR pPart,
+                     CK_ULONG ulPartLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_VerifyFinal(CK_SESSION_HANDLE hSession,
+                    CK_BYTE_PTR pSignature,
+                    CK_ULONG ulSignatureLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_VerifyRecoverInit(CK_SESSION_HANDLE hSession,
+                          CK_MECHANISM_PTR pMechanism,
+                          CK_OBJECT_HANDLE hKey)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_VerifyRecover(CK_SESSION_HANDLE hSession,
+                      CK_BYTE_PTR pSignature,
+                      CK_ULONG ulSignatureLen,
+                      CK_BYTE_PTR pData,
+                      CK_ULONG_PTR pulDataLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_DigestEncryptUpdate(CK_SESSION_HANDLE hSession,
+                            CK_BYTE_PTR pPart,
+                            CK_ULONG ulPartLen,
+                            CK_BYTE_PTR pEncryptedPart,
+                            CK_ULONG_PTR pulEncryptedPartLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_DecryptDigestUpdate(CK_SESSION_HANDLE hSession,
+                            CK_BYTE_PTR pEncryptedPart,
+                            CK_ULONG ulEncryptedPartLen,
+                            CK_BYTE_PTR pPart,
+                            CK_ULONG_PTR pulPartLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_SignEncryptUpdate(CK_SESSION_HANDLE hSession,
+                          CK_BYTE_PTR pPart,
+                          CK_ULONG ulPartLen,
+                          CK_BYTE_PTR pEncryptedPart,
+                          CK_ULONG_PTR pulEncryptedPartLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_DecryptVerifyUpdate(CK_SESSION_HANDLE hSession,
+                            CK_BYTE_PTR pEncryptedPart,
+                            CK_ULONG ulEncryptedPartLen,
+                            CK_BYTE_PTR pPart,
+                            CK_ULONG_PTR pulPartLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_WrapKey(CK_SESSION_HANDLE hSession,
+                CK_MECHANISM_PTR pMechanism,
+                CK_OBJECT_HANDLE hWrappingKey,
+                CK_OBJECT_HANDLE hKey,
+                CK_BYTE_PTR pWrappedKey,
+                CK_ULONG_PTR pulWrappedKeyLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_UnwrapKey(CK_SESSION_HANDLE hSession,
+                  CK_MECHANISM_PTR pMechanism,
+                  CK_OBJECT_HANDLE hUnwrappingKey,
+                  CK_BYTE_PTR pWrappedKey,
+                  CK_ULONG ulWrappedKeyLen,
+                  CK_ATTRIBUTE_PTR pTemplate,
+                  CK_ULONG ulAttributeCount,
+                  CK_OBJECT_HANDLE_PTR phKey)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV 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)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_SeedRandom(CK_SESSION_HANDLE hSession,
+                   CK_BYTE_PTR pSeed,
+                   CK_ULONG ulSeedLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_GetFunctionStatus(CK_SESSION_HANDLE hSession)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_CancelFunction(CK_SESSION_HANDLE hSession)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_WaitForSlotEvent(CK_FLAGS flags,
+                         CK_SLOT_ID_PTR pSlot,
+                         CK_VOID_PTR pRserved)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+/*
+ * "Any programmer who fails to comply with the standard naming, formatting,
+ *  or commenting conventions should be shot.  If it so happens that it is
+ *  inconvenient to shoot him, then he is to be politely requested to recode
+ *  his program in adherence to the above standard."
+ *                      -- Michael Spier, Digital Equipment Corporation
+ *
+ * Local variables:
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/pkcs11.h b/pkcs11.h
new file mode 100644
index 0000000..996b4db
--- /dev/null
+++ b/pkcs11.h
@@ -0,0 +1,301 @@
+/* 02-Sep-2008 from ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-11/v2-20/ */
+
+/* pkcs11.h include file for PKCS #11. */
+/* $Revision: 1.4 $ */
+
+/* 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/pkcs11f.h b/pkcs11f.h
new file mode 100644
index 0000000..a479384
--- /dev/null
+++ b/pkcs11f.h
@@ -0,0 +1,912 @@
+/* pkcs11f.h include file for PKCS #11. */
+/* $Revision: 1.4 $ */
+
+/* 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/pkcs11t.h b/pkcs11t.h
new file mode 100644
index 0000000..386bb04
--- /dev/null
+++ b/pkcs11t.h
@@ -0,0 +1,1789 @@
+/* pkcs11t.h include file for PKCS #11 V 2.30 - draft 1 */
+
+/* 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_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 */
+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 */
+
+  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 */
+typedef CK_ULONG CK_NOTIFICATION;
+#define CKN_SURRENDER       0
+#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;
+
+  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 */
+
+  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 */
+  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.  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.  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.  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.  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 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 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 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 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 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 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 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 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 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
+
+#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 */
+typedef CK_ULONG          CK_USER_TYPE;
+/* Security Officer */
+#define CKU_SO    0
+/* Normal user */
+#define CKU_USER  1
+/* Context specific */
+#define CKU_CONTEXT_SPECIFIC   2
+
+/* CK_STATE enumerates the session states */
+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 */
+  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: */
+typedef CK_ULONG          CK_OBJECT_CLASS;
+
+/* The following classes of objects are defined: */
+#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
+#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 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 */
+#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 */
+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 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
+#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
+#define CKK_BLOWFISH        0x00000020
+#define CKK_TWOFISH         0x00000021
+#define CKK_SECURID         0x00000022
+#define CKK_HOTP            0x00000023
+#define CKK_ACTI            0x00000024
+#define CKK_CAMELLIA        0x00000025
+#define CKK_ARIA            0x00000026
+#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
+#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 */
+typedef CK_ULONG          CK_CERTIFICATE_TYPE;
+
+/* The following certificate types are defined: */
+#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 */
+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 relate 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 relate 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
+#define CKA_OBJECT_ID          0x00000012
+#define CKA_CERTIFICATE_TYPE   0x00000080
+#define CKA_ISSUER             0x00000081
+#define CKA_SERIAL_NUMBER      0x00000082
+#define CKA_AC_ISSUER          0x00000083
+#define CKA_OWNER              0x00000084
+#define CKA_ATTR_TYPES         0x00000085
+#define CKA_TRUSTED            0x00000086
+#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
+#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
+
+#define CKA_PRIME_BITS         0x00000133
+#define CKA_SUBPRIME_BITS      0x00000134
+/* (To retain backwards-compatibility) */
+#define CKA_SUB_PRIME_BITS     CKA_SUBPRIME_BITS
+
+#define CKA_VALUE_BITS         0x00000160
+#define CKA_VALUE_LEN          0x00000161
+#define CKA_EXTRACTABLE        0x00000162
+#define CKA_LOCAL              0x00000163
+#define CKA_NEVER_EXTRACTABLE  0x00000164
+#define CKA_ALWAYS_SENSITIVE   0x00000165
+#define CKA_KEY_GEN_MECHANISM  0x00000166
+
+#define CKA_MODIFIABLE         0x00000170
+
+/* 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
+
+#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)
+#define CKA_DERIVE_TEMPLATE      (CKF_ARRAY_ATTRIBUTE|0x00000213)
+
+#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
+
+#define CKA_GOSTR3410_PARAMS           0x00000250
+#define CKA_GOSTR3411_PARAMS           0x00000251
+#define CKA_GOST28147_PARAMS           0x00000252
+
+#define CKA_HW_FEATURE_TYPE    0x00000300
+#define CKA_RESET_ON_INIT      0x00000301
+#define CKA_HAS_RESET          0x00000302
+
+#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)
+
+#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;
+  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 */
+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
+
+#define CKM_MD2_RSA_PKCS               0x00000004
+#define CKM_MD5_RSA_PKCS               0x00000005
+#define CKM_SHA1_RSA_PKCS              0x00000006
+
+#define CKM_RIPEMD128_RSA_PKCS         0x00000007
+#define CKM_RIPEMD160_RSA_PKCS         0x00000008
+#define CKM_RSA_PKCS_OAEP              0x00000009
+
+#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
+#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
+
+#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
+
+#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
+
+#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
+
+#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
+
+#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
+
+#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
+
+#define CKM_DES_OFB64                  0x00000150
+#define CKM_DES_OFB8                   0x00000151
+#define CKM_DES_CFB64                  0x00000152
+#define CKM_DES_CFB8                   0x00000153
+
+#define CKM_MD2                        0x00000200
+
+#define CKM_MD2_HMAC                   0x00000201
+#define CKM_MD2_HMAC_GENERAL           0x00000202
+
+#define CKM_MD5                        0x00000210
+
+#define CKM_MD5_HMAC                   0x00000211
+#define CKM_MD5_HMAC_GENERAL           0x00000212
+
+#define CKM_SHA_1                      0x00000220
+
+#define CKM_SHA_1_HMAC                 0x00000221
+#define CKM_SHA_1_HMAC_GENERAL         0x00000222
+
+#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
+
+#define CKM_SHA256                     0x00000250
+#define CKM_SHA256_HMAC                0x00000251
+#define CKM_SHA256_HMAC_GENERAL        0x00000252
+
+#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
+#define CKM_SECURID_KEY_GEN            0x00000280
+#define CKM_SECURID                    0x00000282
+#define CKM_HOTP_KEY_GEN               0x00000290
+#define CKM_HOTP                       0x00000291
+#define CKM_ACTI                       0x000002A0
+#define CKM_ACTI_KEY_GEN               0x000002A1
+
+#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
+/* Note that CAST128 and CAST5 are the same algorithm */
+#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
+
+#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
+
+#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
+
+#define CKM_SHA256_KEY_DERIVATION      0x00000393
+#define CKM_SHA384_KEY_DERIVATION      0x00000394
+#define CKM_SHA512_KEY_DERIVATION      0x00000395
+
+#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
+
+#define CKM_PKCS5_PBKD2                0x000003B0
+
+#define CKM_PBA_SHA1_WITH_SHA1_HMAC    0x000003C0
+
+#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
+
+#define CKM_CMS_SIG                    0x00000500
+
+#define CKM_KIP_DERIVE	               0x00000510
+#define CKM_KIP_WRAP	               0x00000511
+#define CKM_KIP_MAC	                   0x00000512
+
+#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
+
+#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
+
+#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
+
+#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
+#define CKM_ECDSA_SHA224               0x00001043
+#define CKM_ECDSA_SHA256               0x00001044
+#define CKM_ECDSA_SHA384               0x00001045
+#define CKM_ECDSA_SHA512               0x00001046
+
+#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
+
+#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
+#define CKM_AES_CTR                    0x00001086
+#define CKM_AES_CTS                    0x00001089
+#define CKM_AES_CMAC                   0x0000108A
+#define CKM_AES_CMAC_GENERAL           0x0000108B
+
+#define CKM_BLOWFISH_KEY_GEN           0x00001090
+#define CKM_BLOWFISH_CBC               0x00001091
+#define CKM_TWOFISH_KEY_GEN            0x00001092
+#define CKM_TWOFISH_CBC                0x00001093
+
+#define CKM_AES_GCM                    0x00001087
+#define CKM_AES_CCM                    0x00001088
+#define CKM_AES_KEY_WRAP               0x00001090
+#define CKM_AES_KEY_WRAP_PAD           0x00001091
+
+#define CKM_BLOWFISH_CBC_PAD           0x00001094 
+#define CKM_TWOFISH_CBC_PAD            0x00001095
+
+#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
+
+#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
+
+#define CKM_AES_OFB                    0x00002104
+#define CKM_AES_CFB64                  0x00002105
+#define CKM_AES_CFB8                   0x00002106
+#define CKM_AES_CFB128                 0x00002107
+
+#define CKM_RSA_PKCS_TPM_1_1           0x00004001
+#define CKM_RSA_PKCS_OAEP_TPM_1_1      0x00004002
+
+#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;
+  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 */
+
+/* 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
+
+/* 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 */
+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 */
+
+#define CKR_GENERAL_ERROR                     0x00000005
+#define CKR_FUNCTION_FAILED                   0x00000006
+
+#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
+#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
+
+#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
+
+#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
+
+#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
+
+#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
+#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
+
+#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
+
+#define CKR_RANDOM_NO_RNG                     0x00000121
+
+#define CKR_DOMAIN_PARAMS_INVALID             0x00000130
+
+#define CKR_BUFFER_TOO_SMALL                  0x00000150
+#define CKR_SAVED_STATE_INVALID               0x00000160
+#define CKR_INFORMATION_SENSITIVE             0x00000170
+#define CKR_STATE_UNSAVEABLE                  0x00000180
+
+#define CKR_CRYPTOKI_NOT_INITIALIZED          0x00000190
+#define CKR_CRYPTOKI_ALREADY_INITIALIZED      0x00000191
+#define CKR_MUTEX_BAD                         0x000001A0
+#define CKR_MUTEX_NOT_LOCKED                  0x000001A1
+
+#define CKR_NEW_PIN_MODE                      0x000001B0
+#define CKR_NEXT_OTP                          0x000001B1
+#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
+
+#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 */
+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 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 */
+#define CKG_MGF1_SHA1         0x00000001
+#define CKG_MGF1_SHA256       0x00000002
+#define CKG_MGF1_SHA384       0x00000003
+#define CKG_MGF1_SHA512       0x00000004
+#define CKG_MGF1_SHA224       0x00000005
+
+/*
+ * 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 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 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;
+
+typedef CK_ULONG CK_EC_KDF_TYPE;
+
+/* The following EC Key Derivation Functions are defined */
+#define CKD_NULL                 0x00000001
+#define CKD_SHA1_KDF             0x00000002
+/* The following X9.42 DH key derivation functions are defined */
+#define CKD_SHA1_KDF_ASN1        0x00000003
+#define CKD_SHA1_KDF_CONCATENATE 0x00000004
+#define CKD_SHA224_KDF           0x00000005
+#define CKD_SHA256_KDF           0x00000006
+#define CKD_SHA384_KDF           0x00000007
+#define CKD_SHA512_KDF           0x00000008
+#define CKD_CPDIVERSIFY_KDF      0x00000009
+
+
+/*
+ * 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 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 */
+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;
+
+/*
+ * 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 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 */
+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 {
+  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 */
+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 */
+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 */
+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 */
+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 */
+typedef CK_ULONG          CK_MAC_GENERAL_PARAMS;
+
+typedef CK_MAC_GENERAL_PARAMS CK_PTR CK_MAC_GENERAL_PARAMS_PTR;
+
+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 */
+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 */
+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 */
+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;
+
+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;
+
+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;
+
+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 */
+typedef CK_ULONG CK_EXTRACT_PARAMS;
+
+typedef CK_EXTRACT_PARAMS CK_PTR CK_EXTRACT_PARAMS_PTR;
+
+/*
+ * 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;
+
+#define CKP_PKCS5_PBKD2_HMAC_SHA1 0x00000001
+
+
+/*
+ * 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 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;
+
+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;
+
+#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
+
+#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
+
+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;
+
+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;
+
+typedef struct CK_AES_GCM_PARAMS {
+  CK_BYTE_PTR pIv;
+  CK_ULONG ulIvLen;
+  CK_ULONG ulIvBits;
+  CK_BYTE_PTR pAAD;
+  CK_ULONG ulAADLen;
+  CK_ULONG ulTagBits;
+} CK_AES_GCM_PARAMS;
+
+typedef CK_AES_GCM_PARAMS CK_PTR CK_AES_GCM_PARAMS_PTR;
+
+typedef struct CK_AES_CCM_PARAMS {
+	CK_ULONG ulDataLen; /*plaintext or ciphertext*/
+	CK_BYTE_PTR pNonce;
+	CK_ULONG ulNonceLen;
+	CK_BYTE_PTR pAAD;
+	CK_ULONG ulAADLen;
+	CK_ULONG ulMACLen;
+} CK_AES_CCM_PARAMS;
+
+typedef CK_AES_CCM_PARAMS CK_PTR CK_AES_CCM_PARAMS_PTR;
+
+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;
+
+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;
+
+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/schema.sql b/schema.sql
new file mode 100644
index 0000000..82d9482
--- /dev/null
+++ b/schema.sql
@@ -0,0 +1,117 @@
+-- SQLite3 schema for Cryptech PKCS #11 implementation.
+--
+-- Author: Rob Austein
+-- Copyright (c) 2015, SUNET
+-- 
+-- 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 COPYRIGHT HOLDERS AND CONTRIBUTORS 
+-- "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 
+-- COPYRIGHT OWNER OR 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.
+
+-- Notes:
+--
+-- The CHECK constraints in the attribute tables are checking
+-- CKA_TOKEN, to make sure we don't accidently file token objects in
+-- the session table or vice versa.
+--
+-- temp.object.token_object_id is a foreign-key reference to
+-- main.token_object.id, but we can't use a real foreign key reference
+-- because they're in different databases.  If we're careful about how
+-- we do our joins, this is harmless, but may lead to some clutter if
+-- a long running session has handles on token objects which some
+-- other process deletes from the database.  If this happens and we
+-- care for some reason, we can clean up such clutter with something
+-- like:
+--
+--     WITH
+--         known AS (SELECT token_object_id FROM token_object)
+--     DELETE FROM object
+--     WHERE  token_object_id IS NOT NULL
+--     AND    token_object_id NOT IN known;
+
+PRAGMA foreign_keys = ON;
+
+CREATE TEMPORARY TABLE IF NOT EXISTS session (
+        session_id              INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+        session_handle          INTEGER NOT NULL UNIQUE
+                                CHECK (session_handle > 0 AND session_handle <= 0xFFFFFFFF)
+);
+
+CREATE TEMPORARY TABLE IF NOT EXISTS object (
+        object_id               INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+        object_handle           INTEGER NOT NULL UNIQUE
+                                CHECK (object_handle > 0 AND object_handle <= 0xFFFFFFFF),
+        session_id              INTEGER REFERENCES session
+                                ON DELETE CASCADE ON UPDATE CASCADE
+                                DEFERRABLE INITIALLY DEFERRED,
+        token_object_id         INTEGER,
+        session_object_id       INTEGER REFERENCES session_object
+                                ON DELETE CASCADE ON UPDATE CASCADE
+                                DEFERRABLE INITIALLY DEFERRED,
+        CHECK                   (token_object_id IS NULL OR (session_id IS NULL AND session_object_id IS NULL)),
+        UNIQUE                  (token_object_id),
+        UNIQUE                  (session_id, session_object_id)
+);
+
+CREATE TEMPORARY TABLE IF NOT EXISTS session_object (
+        session_object_id       INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+        keyid                   TEXT UNIQUE,
+        object_id               INTEGER NOT NULL UNIQUE
+                                REFERENCES object
+                                ON DELETE CASCADE ON UPDATE CASCADE
+);
+
+CREATE TEMPORARY TABLE IF NOT EXISTS session_attribute (
+        type                    INTEGER NOT NULL,
+        session_object_id       INTEGER NOT NULL REFERENCES session_object
+                                ON DELETE CASCADE ON UPDATE CASCADE,
+        value                   BLOB NOT NULL,
+        UNIQUE                  (type, session_object_id),
+        CHECK                   (type <> 1 OR value = X'00')
+);
+
+CREATE TABLE IF NOT EXISTS token_object (
+        token_object_id         INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+        keyid                   TEXT UNIQUE
+);
+
+CREATE TABLE IF NOT EXISTS token_attribute (
+        type                    INTEGER NOT NULL,
+        token_object_id         INTEGER NOT NULL REFERENCES token_object
+                                ON DELETE CASCADE ON UPDATE CASCADE,
+        value                   BLOB NOT NULL,
+        UNIQUE                  (type, token_object_id),
+        CHECK                   (type <> 1 OR value <> X'00')
+);
+
+-- http://sqlite.org/foreignkeys.html says we might want these.
+
+CREATE INDEX IF NOT EXISTS temp.object__session                         ON object(session_id);
+CREATE INDEX IF NOT EXISTS temp.object__session_object                  ON object(session_object_id);
+CREATE INDEX IF NOT EXISTS temp.session_object__object                  ON session_object(object_id);
+CREATE INDEX IF NOT EXISTS temp.session_attribute__session_object       ON session_attribute(session_object_id);
+CREATE INDEX IF NOT EXISTS token_attribute__token_object                ON token_attribute(token_object_id);
+
+-- Local variables:
+-- indent-tabs-mode: nil
+-- End:
diff --git a/scripts/build-attributes b/scripts/build-attributes
new file mode 100755
index 0000000..891bdb6
--- /dev/null
+++ b/scripts/build-attributes
@@ -0,0 +1,403 @@
+#!/usr/bin/env python
+
+"""
+Generate a C header file based on a YAML description of PKCS #11
+attributes.  See comments in attributes.yaml for details.
+"""
+
+# Author: Rob Austein
+# Copyright (c) 2015, SUNET
+# 
+# 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 COPYRIGHT HOLDERS AND CONTRIBUTORS 
+# "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 
+# COPYRIGHT OWNER OR 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 requires a third-party YAML parser.  On Debian-family Linux,
+# you can install this with:
+#
+#   sudo apt-get install python-yaml
+
+import os
+import sys
+import yaml
+import argparse
+
+
+def define_flags(flag_names):
+  """
+  Flag definitions.  Called later, here at front of program just to
+  make them easier to find.
+  """
+
+  flag_names.create("DEFAULT_VALUE", "Value field contains default")
+  flag_names.footnote( 1, "REQUIRED_BY_CREATEOBJECT")
+  flag_names.footnote( 2, "FORBIDDEN_BY_CREATEOBJECT")
+  flag_names.footnote( 3, "REQUIRED_BY_GENERATE")
+  flag_names.footnote( 4, "FORBIDDEN_BY_GENERATE")
+  flag_names.footnote( 5, "REQUIRED_BY_UNWRAP")
+  flag_names.footnote( 6, "FORBIDDEN_BY_UNWRAP")
+  flag_names.footnote( 7, "SENSITIVE")
+  flag_names.footnote( 8, "PERHAPS_MODIFIABLE")
+  flag_names.footnote( 9, "DEFAULT_IS_TOKEN_SPECIFIC")
+  flag_names.footnote(10, "ONLY_SO_USER_CAN_SET")
+  flag_names.footnote(11, "LATCHES_WHEN_TRUE")
+  flag_names.footnote(12, "LATCHES_WHEN_FALSE")
+
+
+class PKCS11ParseError(Exception):
+  "Failure parsing PCKS #11 object definitions from YAML data."
+
+
+def write_lines(*lines, **d):
+  """
+  Utility to simplify writing formatted text to the output stream.
+  """
+
+  for line in lines:
+    args.output_file.write((line % d) + "\n")
+
+
+class Flags(object):
+  """
+  Descriptor flag database.
+
+  Many of these are derived from PKCS #11 Table 15  footnotes
+  """
+
+  prefix = "P11_DESCRIPTOR_"            # Prefix string for all descriptor flags
+
+  def __init__(self):
+    self.names = []
+    self.notes = {}
+    self.width = 0
+
+  def create(self, name, comment = None):
+    """
+    Create a descriptor flag.
+    """
+
+    assert len(self.names) < 32
+    name = self.prefix + name
+    self.names.append((name, comment))
+    if len(name) > self.width:
+      self.width = len(name)
+
+  def footnote(self, number, name):
+    """
+    Create a descriptor flag for a PKCS #11 table 15 footnote.
+    """
+
+    assert number not in self.notes
+    self.create(name, "Section 10.2 table 15 footnote #%2d" % number)
+    self.notes[number] = self.prefix + name
+
+  def write(self):
+    """
+    Generate the flags, assigning bit positions as we go.
+    """
+
+    assert len(self.names) < 32
+    self.width = (((self.width + 4) >> 2) << 2) - 1
+    bit = 1
+    for name, comment in self.names:
+      format = "#define %(name)s 0x%(bit)08x"
+      if comment is not None:
+        format += "  /* %(comment)s */"
+      write_lines(format, bit = bit, comment = comment, name = "%-*s" % (self.width, name))
+      bit <<= 1
+
+
+class AttributeNumbers(dict):
+  """
+  Attribute names and numbers scraped (yuck) from pkcs11t.h.
+  """
+
+  def __init__(self, filename):
+    with open(filename, "r") as f:
+      for line in f:
+        word = line.split()
+        if len(word) <= 2 or word[0] != "#define" or not word[1].startswith("CKA_"):
+          continue
+        if word[2] in self:
+          continue
+        if word[2].startswith("(CKF_ARRAY_ATTRIBUTE|"):
+          word[2] = word[2].translate(None, "()").split("|")[1]
+        self[word[1]] = int(word[2], 16)
+
+
+class Attribute(object):
+  """
+  Definition of one attribute.
+  """
+
+  def __init__(self, name, type = None, footnotes = None, default = None, value = None, unimplemented = False):
+    assert value is None or default is None
+    self.name = name
+    self.type = type
+    self.footnotes = footnotes
+    self.default = self.convert_integers(default)
+    self.value   = self.convert_integers(value)
+    self.unimplemented = unimplemented
+
+  @staticmethod
+  def convert_integers(val):
+    """
+    Convert a non-negative integer initialization value into a byte array.
+    """
+
+    if not isinstance(val, (int, long)):
+      return val
+    if val < 0:
+      raise ValueError("Negative integers not legal here: %s" % val)
+    bytes = []
+    while val > 0:
+      bytes.insert(0, val & 0xFF)
+      val >>= 8
+    return bytes or [0]
+
+  def inherit(self, other):
+    """
+    Merge values from paraent attribute definition, if any.
+    """
+
+    for k in ("type", "footnotes", "default", "value"):
+      if getattr(self, k) is None:
+        setattr(self, k, getattr(other, k))
+    self.unimplemented = self.unimplemented or other.unimplemented
+
+  def format_flags(self):
+    """
+    Generate the descriptor flags field.
+    """
+
+    flags = []
+    if self.footnotes:
+      flags.extend(flag_names.notes[f] for f in self.footnotes)
+    if self.value is None and self.default is not None:
+      flags.append("P11_DESCRIPTOR_DEFAULT_VALUE")
+    flags = " | ".join(flags)
+    return flags or "0"
+
+  def format_size(self):
+    """
+    Generate the descriptor size field.
+    """
+
+    if isinstance(self.type, str) and self.type.startswith("CK_"):
+      return "sizeof(%s)" % self.type
+    elif self.type in ("rfc2279string", "biginteger", "bytearray"):
+      return "0"
+    else:
+      raise PKCS11ParseError("Unknown meta-type %r" % self.type)
+
+  def format_length(self):
+    """
+    Generate the descriptor length field.
+    """
+
+    value = self.value or self.default
+    if isinstance(value, list):
+      return "sizeof(const_0x%s)" % "".join("%02x" % v for v in value)
+    elif value and isinstance(self.type, str) and self.type.startswith("CK_"):
+      return "sizeof(%s)" % self.type
+    else:
+      return "0"
+
+  def format_value(self):
+    """
+    Generate the descriptor value field.
+    """
+
+    value = self.value or self.default
+    if not value:
+      return "NULL_PTR"
+    elif isinstance(value, list):
+      return "const_0x" + "".join("%02x" % v for v in value)
+    else:
+      return "&const_" + value
+
+  def format_constant(self, constants):
+    """
+    Generate constant initializer values.  These are merged so that we
+    only end up declaring one copy of each initializer value no matter
+    how many attributes use it.
+    """
+
+    value = self.value or self.default
+    if not self.unimplemented and value:
+      if isinstance(value, list):
+        constants.add("static const CK_BYTE const_%s[] = { %s };" % (
+          "0x" + "".join("%02x" % v for v in value),
+          ", ".join("0x%02x" % v for v in value)))
+      else:
+        constants.add("static const %s const_%s = %s;" % (self.type, value, value))
+
+  def generate(self):
+    """
+    Generate the descriptor line for this attribute.
+    """
+
+    if not self.unimplemented:
+      args.output_file.write("  { %s, %s, %s, %s, %s },\n" % (
+        self.name, self.format_size(), self.format_length(), self.format_value(), self.format_flags()))
+
+
+class Class(object):
+  """
+  A PKCS #11 class.
+  """
+
+  def __init__(self, db, name, superclass = None, concrete = False, **attrs):
+    assert all(a.startswith("CKA_") for a in attrs), "Non-attribute: %r" % [a for a in attrs if not a.startswith("CKA_")]
+    self.attributes = dict((k, Attribute(k, **v)) for k, v in attrs.iteritems())
+    self.db = db
+    self.name = name
+    self.superclass = superclass
+    self.concrete = concrete
+
+  def inherit(self, other):
+    """
+    Inherit attributes from parent type.
+    """
+
+    for k, v in other.attributes.iteritems():
+      if k not in self.attributes:
+        self.attributes[k] = v
+      else:
+        self.attributes[k].inherit(v)
+
+  def collect_constants(self, constants):
+    """
+    Collect initialization constants for all attributes.
+    """
+
+    if self.concrete:
+      for a in self.attributes.itervalues():
+        a.format_constant(constants)
+
+  def generate(self):
+    """
+    Generate a descriptor for this type.
+    """
+
+    if self.concrete:
+
+      write_lines("",
+                  "static const p11_attribute_descriptor_t p11_attribute_descriptor_%(name)s[] = {",
+                  name = self.name)
+
+      for a in sorted(self.attributes, key = lambda x: attribute_numbers[x]):
+        self.attributes[a].generate()
+
+      write_lines("};",
+                  "",
+                  "static const p11_descriptor_t p11_descriptor_%(name)s = {",
+                  "  p11_attribute_descriptor_%(name)s,",
+                  "  sizeof(p11_attribute_descriptor_%(name)s)/sizeof(p11_attribute_descriptor_t)",
+                  "};",
+                  name = self.name)
+
+  def keyclassmap(self):
+    """
+    Generate a keyclass map entry if this is a concrete key type.
+    """
+
+    if self.concrete and all(k in self.attributes and self.attributes[k].value for k in ("CKA_CLASS", "CKA_KEY_TYPE")):
+      write_lines(" { %s, %s, &p11_descriptor_%s }," % (
+        self.attributes["CKA_CLASS"].value, self.attributes["CKA_KEY_TYPE"].value, self.name))
+
+
+class DB(object):
+  """
+  Object type database parsed from YAML
+  """
+
+  def __init__(self, y):
+    self.ordered = [Class(self, **y) for y in y]
+    self.named = dict((c.name, c) for c in self.ordered)
+    for c in self.ordered:
+      if c.superclass is not None:
+        c.inherit(self.named[c.superclass])
+
+  def generate(self):
+    """
+    Generate output for everything in the database.
+    """
+
+    constants = set()
+    for c in self.ordered:
+      c.collect_constants(constants)
+    for constant in sorted(constants):
+      write_lines(constant)
+    for c in self.ordered:
+      c.generate()
+    write_lines("",
+                "static const p11_descriptor_keyclass_map_t p11_descriptor_keyclass_map[] = {")
+    for c in self.ordered:
+      c.keyclassmap()
+    write_lines("};")
+
+# Main program
+
+parser = argparse.ArgumentParser(description = __doc__, formatter_class = argparse.ArgumentDefaultsHelpFormatter)
+parser.add_argument("--pkcs11t-file", help = "Alternate location for pkcs11t.h",                            default = "pkcs11t.h")
+parser.add_argument("yaml_file",      help = "Input YAML file", nargs = "?", type = argparse.FileType("r"), default = sys.stdin)
+parser.add_argument("output_file",    help = "Output .h file",  nargs = "?", type = argparse.FileType("w"), default = sys.stdout)
+args = parser.parse_args()
+
+attribute_numbers = AttributeNumbers(args.pkcs11t_file)
+
+db = DB(yaml.load(args.yaml_file))
+
+args.output_file.write('''\
+/*
+ * This file was generated automatically from %(input)s by %(script)s.  Do not edit this file directly.
+ */
+
+typedef struct {
+  CK_ATTRIBUTE_TYPE type;
+  CK_ULONG size;                        /* Size in bytes if this is a fixed-length attribute */
+  CK_ULONG length;                      /* Length in bytes of the object to which value points */
+  const void *value;                    /* Default or constant depending on P11_DESCRIPTOR_DEFAULT_VALUE */
+  unsigned long flags;                  /* (NULL value with P11_DESCRIPTOR_DEFAULT_VALUE means zero length default */
+} p11_attribute_descriptor_t;
+
+typedef struct {
+  const p11_attribute_descriptor_t *attributes;
+  CK_ULONG n_attributes;
+} p11_descriptor_t;
+
+typedef struct {
+  CK_OBJECT_CLASS object_class;
+  CK_KEY_TYPE key_type;
+  const p11_descriptor_t *descriptor;
+} p11_descriptor_keyclass_map_t;
+
+''' % dict(script = os.path.basename(sys.argv[0]), input  = args.yaml_file.name))
+
+flag_names = Flags()
+define_flags(flag_names)
+flag_names.write()
+write_lines("")
+db.generate()
diff --git a/scripts/convert-schema.sed b/scripts/convert-schema.sed
new file mode 100644
index 0000000..55aaadc
--- /dev/null
+++ b/scripts/convert-schema.sed
@@ -0,0 +1,66 @@
+# Generate schema.h from schema.sql.
+#
+# If this script gets any more complicated, it should probably be
+# recoded in Python and have done.
+#
+# Author: Rob Austein
+# Copyright (c) 2015, SUNET
+# 
+# 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 COPYRIGHT HOLDERS AND CONTRIBUTORS 
+# "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 
+# COPYRIGHT OWNER OR 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.
+
+
+# Add header.  Note that both newlines and leading spaces need to be
+# quoted with backslashes, be careful....
+1i\
+ /*\
+\ * Automatically generated from schema.sql, edit that file instead of this one.\
+\ */\
+\
+
+# Debugging hack: ordinarily we keep all the per-session stuff in the
+# "temp" database, but debugging is easier when we let it all go to
+# disk.  Uncomment these lines to remove all the "TEMPORARY" and
+# "temp." qualifiers.
+#s/ TEMPORARY / /g
+#s/ temp[.]/ /g
+
+# Delete comment lines, trailing whitespace, and blank lines.
+/^[ 	]*--/d
+s/[ 	]*$//
+/^$/d
+
+# Quote backslashes and doublequotes, if any.
+s/\\/\\\\/g
+s/"/\\"/g
+
+# Quote each line of text.  Literal transcription would be:
+#
+#   s/^.*$/"&\\n"/
+#
+# but SQL doesn't need the line breaks, so we can use
+# whitespace to generate something a bit more readable.
+#
+s/^.*$/" &"/
diff --git a/scripts/format-attribute-comments b/scripts/format-attribute-comments
new file mode 100755
index 0000000..3c13bba
--- /dev/null
+++ b/scripts/format-attribute-comments
@@ -0,0 +1,85 @@
+#!/bin/sh -
+#
+# Script to extract tables from the PKCS #11 specification and format
+# them as YAML comment blocks.
+#
+# This isn't even half-assed, more like quarter-assed.  If I thought
+# we'd be using it a lot I'd rewrite it in Python.
+#
+# Author: Rob Austein
+# Copyright (c) 2015, SUNET
+# 
+# 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 COPYRIGHT HOLDERS AND CONTRIBUTORS 
+# "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 
+# COPYRIGHT OWNER OR 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.
+
+url=http://www.cryptsoft.com/pkcs11doc/download/pkcs11doc-v230.tgz
+
+tar=${url##*/}
+
+test -r $tar ||
+wget $url ||
+exit
+
+tar -tf $tar |
+
+awk '
+  /group__SEC__(9|11)__.*\.html/ {
+
+    n = split($0, a, "[/.]");
+    title = a[n-1];
+
+    n = split($0, a, /__/);
+    s1 = a[3];
+    s2 = (a[4] ~ /^[0-9]+$/) ? a[4] : 0;
+    s3 = (a[5] ~ /^[0-9]+$/) ? a[5] : 0;
+    idx = sprintf("%04d%04d%04d", s1, s2, s3);
+
+    print idx, $0, title;
+  }
+' |
+
+sort -n |
+
+while read idx fn title
+do
+
+  tar -xOf $tar $fn |
+
+  w3m -dump -O us-ascii -T text/html |
+
+  awk -v title=$title '
+    BEGIN {
+      print "";
+      print "###";
+      print "#", title;
+      print "###";
+      print "";
+    }
+    /^[|+]/ {
+      print "#", $0;
+    }
+  '
+
+done
diff --git a/scripts/test-hsmcheck b/scripts/test-hsmcheck
new file mode 100755
index 0000000..b7a5643
--- /dev/null
+++ b/scripts/test-hsmcheck
@@ -0,0 +1,180 @@
+#!/usr/bin/env python
+
+"""
+Run the OpenDNSSEC libhsm/check/hsmcheck tool with Cryptech PKCS #11,
+using DNSpython to verify the DNSSEC data produced by"hsmcheck -s".
+
+This script knows far too much about the output generated by hsmcheck,
+but what were you expecting from an ad hoc test tool that gets its
+input by screen scraping the output of another ad hoc test tool?
+"""
+
+# Author: Rob Austein
+# Copyright (c) 2015, SUNET
+# 
+# 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 COPYRIGHT HOLDERS AND CONTRIBUTORS 
+# "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 
+# COPYRIGHT OWNER OR 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.
+
+import os
+import sys
+
+from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter, FileType as ArgumentFileType
+from tempfile import NamedTemporaryFile
+from subprocess import check_call, check_output
+from xml.etree.ElementTree import ElementTree, Element, SubElement
+
+
+def write_config():
+  """
+  Write hsmcheck configuration file.
+  """
+
+  e = Element("Configuration")
+  r = SubElement(e, "RepositoryList")
+  r = SubElement(r, "Repository", name = "default")
+  SubElement(r, "Module").text = args.driver
+  SubElement(r, "TokenLabel").text = args.token_label
+  SubElement(r, "PIN").text = args.pin
+  ElementTree(e).write(args.write_config)
+  args.write_config.flush()  
+
+
+def hsmcheck(flag):
+  """
+  Run hsmcheck program with appropriate options and verbosity.
+  """
+
+  assert flag in "rgsd"
+  cmd = (args.hsmcheck_binary, "-c", args.write_config.name, "-" + flag)
+  if args.verbose:
+    sys.stdout.write("Running: %s\n" % " ".join(cmd))
+  if flag == "s":
+    text = check_output(cmd)
+    sys.stdout.write(text)
+    if not args.no_dnssec:
+      check_dnssec(text)
+  else:
+    check_call(cmd)
+
+
+def check_dnssec(text):
+  """
+  Use DNSPython to attempt DNSSEC validation on "hsmcheck -s" output.
+
+  This requires the DNSPython toolkit, which in turn requires
+  PyCrypto; ECDSA support (not yet tested) requires a third package.
+  On Debian-family Linux, you can install these with:
+  
+      sudo apt-get install python-dnspython python-crypto python-ecdsa
+
+  Equivalent packages exist for other platforms.
+  """
+
+  try:
+    from dns.exception import DNSException
+    import dns.dnssec
+    import dns.rrset
+    import Crypto.PublicKey.RSA
+    #import ecdsa.ecdsa
+  except ImportError:
+    sys.exit("Problem importing DNSPython or supporting crypto packages, are they installed?")
+
+  wired_ttl     = "3600"
+  wired_rdclass = "IN"
+
+  rrs = {}
+
+  for line in text.splitlines():
+
+    try:
+      name, ttl, rdclass, rdtype, rdata = line.split(None, 4)
+    except ValueError:
+      continue
+
+    if ttl != wired_ttl or rdclass != wired_rdclass:
+      continue
+
+    try:
+      rrs[name, rdtype].append(rdata)
+    except KeyError:
+      rrs[name, rdtype] = [rdata]
+
+  # Done parsing.  We expect to have seen an A RRset, an RRSIG of that
+  # A RRset, and the DNSKEY that we'll need to verify the RRSIG.
+
+  if len(rrs) != 3:
+    sys.exit("Expected two RRsets and an RRSIG, got %r" % rrs)
+
+  rrs = dict((rdtype, dns.rrset.from_text_list(name, int(wired_ttl), wired_rdclass, rdtype, rrs[name, rdtype]))
+                for name, rdtype in rrs)
+
+  try:
+    dns.dnssec.validate(rrs["A"], rrs["RRSIG"], { rrs["DNSKEY"].name : rrs["DNSKEY"] })
+  except DNSException, e:
+    sys.exit("DNSSEC verification failed: %s" % e)
+
+  sys.stdout.write("\nDNSSEC verification successful!\n\n")
+
+
+# Main program.
+
+try:
+  default_config   = NamedTemporaryFile()
+  default_hsmcheck = os.getenv("HSMCHECK", "hsmcheck")
+  default_driver   = os.getenv("PKCS11_DRIVER",
+                               os.path.realpath(os.path.join(os.path.dirname(sys.argv[0]), "..", "libpkcs11.so")))
+
+  parser = ArgumentParser(description = __doc__, formatter_class = ArgumentDefaultsHelpFormatter)
+  one_of = parser.add_mutually_exclusive_group()
+  one_of.add_argument("-a", "--all", "--rgsd", const = "rgsd", dest = "test", action = "store_const", help = "run all tests")
+  one_of.add_argument("-r", "--random",   const = "r", dest = "test", action = "store_const", help = "just test random numbers")
+  one_of.add_argument("-g", "--generate", const = "g", dest = "test", action = "store_const", help = "just test key generation")
+  one_of.add_argument("-s", "--sign",     const = "s", dest = "test", action = "store_const", help = "just test DNSSEC-signature")
+  one_of.add_argument("-d", "--delete",   const = "d", dest = "test", action = "store_const", help = "just delete key")
+  parser.add_argument("-b", "--hsmcheck-binary",  default = default_hsmcheck, help = "location of hsmcheck program")
+  parser.add_argument("-p", "--pin",              default = "12345",          help = "HSM PIN to use for tests")
+  parser.add_argument("-t", "--token-label",      default = "Cryptech Token", help = "PKCS #11 label of Cryptech token")
+  parser.add_argument("-n", "--no-dnssec",        action = "store_true",      help = "do not attempt DNSSEC validation")
+  parser.add_argument("-v", "--verbose",          action = "store_true",      help = "bark more")
+  parser.add_argument("-D", "--driver",           default = default_driver,   help = "location of PKCS #11 driver")
+  parser.add_argument("-w", "--write-config",     default = default_config,   help = "write generated configuration to this file",
+                      type = ArgumentFileType("w"))
+  parser.add_argument("--debug",                  action = "store_true",      help = "debug this script")
+  parser.set_defaults(test = "rgsd")
+  args = parser.parse_args()
+
+  try:
+    write_config()
+    for flag in args.test:
+      hsmcheck(flag)
+
+  except Exception as e:
+    if args.debug:
+      raise
+    sys.exit("Failed: %s" % e)
+
+finally:
+  default_config.close()
+



More information about the Commits mailing list