aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2018-10-15 21:08:04 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2018-10-17 15:02:42 +0300
commitde91921561092689369b56c54950474e0a86e66f (patch)
treea9949058021d911db1106b1a2e4d9e0e9281de16
parentfb65c93daaf369157bd712f2c4c20161c4840b94 (diff)
Add implementation
-rw-r--r--INSTALL6
-rw-r--r--LICENSE30
-rw-r--r--NEWS3
-rw-r--r--README63
-rw-r--r--build/.gitignore3
-rw-r--r--build/bootstrap.build11
-rw-r--r--build/export.build13
-rw-r--r--build/root.build25
-rw-r--r--buildfile10
-rw-r--r--manifest20
-rw-r--r--openssl/.gitignore9
-rw-r--r--openssl/agent/pkcs11/agent.cxx621
-rw-r--r--openssl/agent/pkcs11/options.cli79
-rw-r--r--openssl/agent/pkcs11/pkcs11.cxx214
-rw-r--r--openssl/agent/pkcs11/pkcs11.h1436
-rw-r--r--openssl/agent/pkcs11/pkcs11.hxx63
-rw-r--r--openssl/agent/pkcs11/private-key.cxx492
-rw-r--r--openssl/agent/pkcs11/private-key.hxx95
-rw-r--r--openssl/agent/pkcs11/private-key.test.cxx72
-rw-r--r--openssl/agent/pkcs11/private-key.test.testscript22
-rw-r--r--openssl/agent/pkcs11/url.cxx303
-rw-r--r--openssl/agent/pkcs11/url.hxx246
-rw-r--r--openssl/agent/pkcs11/url.test.cxx46
-rw-r--r--openssl/agent/pkcs11/url.test.testscript34
-rw-r--r--openssl/buildfile75
-rw-r--r--openssl/client/client.cxx191
-rw-r--r--openssl/client/options.cli109
-rw-r--r--openssl/diagnostics.cxx35
-rw-r--r--openssl/diagnostics.hxx115
-rw-r--r--openssl/options.cli9
-rw-r--r--openssl/protocol.cxx132
-rw-r--r--openssl/protocol.hxx74
-rw-r--r--openssl/types-parsers.cxx34
-rw-r--r--openssl/types-parsers.hxx31
-rw-r--r--openssl/types.cxx29
-rw-r--r--openssl/types.hxx103
-rw-r--r--openssl/utility.cxx41
-rw-r--r--openssl/utility.hxx74
-rw-r--r--openssl/version.hxx.in44
-rw-r--r--repositories.manifest6
-rw-r--r--tests/.gitignore2
-rw-r--r--tests/agent-pkcs11.testscript71
-rw-r--r--tests/build/.gitignore3
-rw-r--r--tests/build/bootstrap.build9
-rw-r--r--tests/build/root.build8
-rw-r--r--tests/buildfile8
-rw-r--r--tests/client.testscript65
47 files changed, 5184 insertions, 0 deletions
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..fa24d35
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,6 @@
+The easiest way to build this package is with the bpkg package manager:
+
+$ bpkg build openssl-agent
+
+But if you don't want to use the package manager, then you can also build it
+manually using the standard build2 build system.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..3023f3a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Note that this project is not affiliated with OpenSSL. OpenSSL is a
+registered trademark owned by OpenSSL Software Foundation.
+
+openssl/agent/pkcs11/pkcs11.h:
+
+From OpenSC/libp11 with "unlimited permission to copy and/or distribute",
+see the file header for details.
+
+The rest:
+
+Copyright (c) 2014-2018 Code Synthesis Ltd
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..32bf3ce
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,3 @@
+Version 0.9.0
+
+ * First public release.
diff --git a/README b/README
new file mode 100644
index 0000000..6b11e46
--- /dev/null
+++ b/README
@@ -0,0 +1,63 @@
+This package contains the OpenSSL key agent and client utilities.
+
+The aim of these utilities is to provide an openssl-rsautl(1) drop-in
+replacement for performing cryptographic operations using a private key
+that is unlocked for the session, similar to OpenSSH's ssh-agent(1).
+
+The typical usage is as follows:
+
+1. Start the OpenSSL key agent which prompts for a password/PIN for the
+ specified private key. The agent then forks off the daemon which unlocks
+ the key, opens a UNIX domain socket, and waits for cryptographic operation
+ requests from the OpenSSL client.
+
+ The agent also prints to stdout a shell script fragment that sets the
+ environment variables necessary for locating the agent.
+
+2. Source the script fragment printed by the agent into the shell.
+
+3. Run the OpenSSL client to perform a cryptographic operation.
+
+For example:
+
+$ openssl-agent-pkcs11 "pkcs11:object=SIGN%20key" >agent.env
+Enter PIN for PKCS#11:*******
+
+$ source agent.env
+Agent pid 14696
+
+$ openssl-client rsautl -sign -keyform engine -engine pkcs11 \
+-inkey "pkcs11:object=SIGN%20key" <<<test >signature
+
+...
+
+$ kill 14696
+
+By default, openssl-agent-pkcs11(1) uses opensc-pkcs11 module, unless a
+different one is explicitly specified in the private key URL (see RFC7512
+Section "The PKCS #11 URI Scheme" for details).
+
+To use opensc-pkcs11, make sure that the following packages are installed in
+the system:
+
+Debian/Ubuntu: opensc-pkcs11
+Fedora/RHEL: opensc
+
+Note that for some Debian versions the opensc-pkcs11 package has the missing
+libpcsclite1 dependency, so make sure it is also installed.
+
+For more information see:
+
+https://build2.org/
+
+See the NEWS file for the user-visible changes from the previous release.
+
+See the LICENSE file for the distribution conditions.
+
+See the INSTALL file for the prerequisites and installation instructions.
+
+See the doc/ directory for documentation.
+
+Send questions, bug reports, or any other feedback to the users@build2.org
+mailing list. You can post without subscribing. See https://lists.build2.org
+for searchable archives, posting guidelines, etc.
diff --git a/build/.gitignore b/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/build/bootstrap.build b/build/bootstrap.build
new file mode 100644
index 0000000..5850db0
--- /dev/null
+++ b/build/bootstrap.build
@@ -0,0 +1,11 @@
+# file : build/bootstrap.build
+# copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+project = openssl-agent
+
+using version
+using config
+using test
+using install
+using dist
diff --git a/build/export.build b/build/export.build
new file mode 100644
index 0000000..0fed1bd
--- /dev/null
+++ b/build/export.build
@@ -0,0 +1,13 @@
+# file : build/export.build
+# copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+$out_root/
+{
+ include openssl/
+}
+
+if ($import.target == exe{openssl-agent-pkcs11})
+ export $out_root/openssl/exe{openssl-agent-pkcs11}
+elif ($import.target == exe{openssl-client})
+ export $out_root/openssl/exe{openssl-client}
diff --git a/build/root.build b/build/root.build
new file mode 100644
index 0000000..39b60a6
--- /dev/null
+++ b/build/root.build
@@ -0,0 +1,25 @@
+# file : build/root.build
+# copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+using cxx
+
+hxx{*}: extension = hxx
+ixx{*}: extension = ixx
+txx{*}: extension = txx
+cxx{*}: extension = cxx
+
+# Note that we bundle pkcs11.h from OpenSC project (see LICENSE file for
+# details).
+#
+h{*}: extension = h
+
+cxx.poptions =+ "-I$out_root" "-I$src_root"
+
+# Load the cli module but only if it's available. This way a distribution
+# that includes pre-generated files can be built without installing cli.
+# This is also the reason why we need to explicitly spell out individual
+# source file prerequisites instead of using the cli.cxx{} group (it won't
+# be there unless the module is configured).
+#
+using? cli
diff --git a/buildfile b/buildfile
new file mode 100644
index 0000000..dbe9a39
--- /dev/null
+++ b/buildfile
@@ -0,0 +1,10 @@
+# file : buildfile -*- C++ -*-
+# copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+./: {*/ -build/} doc{INSTALL LICENSE NEWS README} manifest
+
+# Don't install tests or the INSTALL file.
+#
+tests/: install = false
+doc{INSTALL}@./: install = false
diff --git a/manifest b/manifest
new file mode 100644
index 0000000..6b20188
--- /dev/null
+++ b/manifest
@@ -0,0 +1,20 @@
+: 1
+name: openssl-agent
+version: 0.9.0-a.0.z
+project: build2
+summary: OpenSSL key agent
+license: MIT
+tags: c++, openssl, key, agent, pkcs#11, pkcs11, signing
+description-file: README
+changes-file: NEWS
+url: https://build2.org
+doc-url: https://build2.org/doc.xhtml
+src-url: https://git.build2.org/cgit/openssl-agent/tree/
+email: users@build2.org
+build-email: builds@build2.org
+build-include: linux*
+build-exclude: * ; Currently only supported on Linux.
+requires: c++14
+depends: * build2 >= 0.8.0-
+depends: * bpkg >= 0.8.0-
+depends: libbutl [0.9.0-a.0.1 0.9.0-a.1)
diff --git a/openssl/.gitignore b/openssl/.gitignore
new file mode 100644
index 0000000..39832e3
--- /dev/null
+++ b/openssl/.gitignore
@@ -0,0 +1,9 @@
+openssl-client
+openssl-agent-*
+*.test
+options.?xx
+version.hxx
+
+# Testscript output directory (can be symlink).
+#
+test-*.test
diff --git a/openssl/agent/pkcs11/agent.cxx b/openssl/agent/pkcs11/agent.cxx
new file mode 100644
index 0000000..12b273c
--- /dev/null
+++ b/openssl/agent/pkcs11/agent.cxx
@@ -0,0 +1,621 @@
+// file : openssl/agent/pkcs11/agent.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <sys/un.h> // sockaddr_un
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <signal.h> // kill(), sigaction(), sigemptyset(), SIG*
+#include <unistd.h> // fork(), getpid(), dup2(), setsid()
+#include <termios.h> // tcgetattr(), tcsetattr()
+
+#include <csignal> // sig_atomic_t
+#include <cstring> // strlen(), strcpy(), memset(), memcmp()
+#include <iostream>
+
+#include <iostream> // cout
+
+#include <libbutl/pager.mxx>
+
+#include <openssl/protocol.hxx>
+#include <openssl/diagnostics.hxx>
+
+#include <openssl/agent/pkcs11/url.hxx>
+#include <openssl/agent/pkcs11/options.hxx>
+#include <openssl/agent/pkcs11/private-key.hxx>
+
+namespace openssl
+{
+ namespace agent
+ {
+ namespace pkcs11
+ {
+ using namespace std;
+ using namespace butl;
+
+ static void
+ pin_prompt (const string& prompt, char*, size_t);
+
+ // The agent daemon top-level function.
+ //
+ static int
+ daemon (const options&,
+ identity&&,
+ access&&,
+ auto_fd&& sock,
+ char* pin) noexcept;
+
+ static int
+ main (int argc, char* argv[])
+ try
+ {
+ cli::argv_scanner scan (argc, argv);
+
+ // Parse options.
+ //
+ options ops;
+ ops.parse (scan);
+
+ // Version.
+ //
+ if (ops.version ())
+ {
+ cout << "openssl-agent-pkcs11 " << OPENSSL_AGENT_VERSION_ID << endl
+ << "libbutl " << LIBBUTL_VERSION_ID << endl
+ << "Copyright (c) 2014-2018 Code Synthesis Ltd" << endl
+ << "This is free software released under the MIT license."
+ << endl;
+
+ return 0;
+ }
+
+ // Help.
+ //
+ if (ops.help ())
+ {
+ pager p ("openssl-agent-pkcs11 help", false);
+ print_openssl_agent_pkcs11_usage (p.stream ());
+
+ // If the pager failed, assume it has issued some diagnostics.
+ //
+ return p.wait () ? 0 : 1;
+ }
+
+ // Parse arguments.
+ //
+ identity key_identity;
+ access key_access;
+
+ try
+ {
+ string u (scan.more () ? scan.next () : "");
+ if (u.empty ())
+ fail << "private key URL argument expected";
+
+ url key_url (u);
+
+ key_identity = identity (key_url);
+ key_access = access (key_url);
+ }
+ catch (const invalid_argument& e)
+ {
+ fail << "invalid PKCS#11 URL: " << e;
+ }
+
+ try
+ {
+ // Prompt the user for a token PIN unless it is already specified as
+ // an access attribute in the URL.
+ //
+ char pin[65]; // NULL character-terminated PIN.
+
+ if (!key_access.pin_value)
+ pin_prompt ("Enter PIN for PKCS#11", pin, sizeof (pin));
+
+ // Open the agent communication socket and fail if the path is
+ // already taken. We could probably deal with the faulty case
+ // ourselves but let's not complicate things for now.
+ //
+ auto_fd sock (socket (AF_UNIX, SOCK_STREAM, 0));
+
+ if (sock.get () == -1)
+ throw_system_error (errno);
+
+ path sock_path (path::temp_path ("openssl-agent-pkcs11"));
+
+ struct sockaddr_un addr;
+ memset (&addr, 0, sizeof (addr));
+ addr.sun_family = AF_UNIX;
+
+ if (sock_path.string ().size () >= sizeof (addr.sun_path))
+ throw_generic_error (ENAMETOOLONG);
+
+ strcpy (addr.sun_path, sock_path.string ().c_str ());
+
+ // Creates the socket filesystem entry.
+ //
+ if (bind (sock.get (),
+ reinterpret_cast<sockaddr*> (&addr),
+ sizeof (addr)) == -1)
+ throw_system_error (errno);
+
+ auto_rmfile rm (sock_path);
+
+ if (listen (sock.get (), 20 /* backlog */) == -1)
+ throw_system_error (errno);
+
+ // Fork off the agent daemon.
+ //
+ pid_t pid (fork ());
+
+ if (pid == -1)
+ throw_system_error (errno);
+
+ // Run the agent daemon in the child process.
+ //
+ if (pid == 0)
+ return daemon (ops,
+ move (key_identity),
+ move (key_access),
+ move (sock),
+ !key_access.pin_value ? pin : nullptr);
+
+ // Erase the no longer needed PIN.
+ //
+ mem_clear (pin, sizeof (pin));
+
+ // The child now is in charge for the socket filesystem entry.
+ //
+ rm.cancel ();
+
+ sock.close ();
+
+ // Send the init request to the agent to make sure that it is alive
+ // and has successfully initialized the key. Issue the received from
+ // the agent diagnostics and exit with non-zero status if the
+ // initialization failed. Note that the daemon will also exit right
+ // after the response in this case.
+ //
+ {
+ sock = connect (sock_path);
+
+ ifdstream is (fddup (sock.get ()));
+ ofdstream os (move (sock));
+
+ os << request ("init");
+ os.close ();
+
+ response r;
+ is >> r;
+
+ if (r.status != 0)
+ {
+ text << r.error;
+ return r.status;
+ }
+ }
+
+ // Let's mimic the 'ssh-agent -s' command output.
+ //
+ cout << "OPENSSL_AGENT_PKCS11_SOCK=" << sock_path << "; "
+ << "export OPENSSL_AGENT_PKCS11_SOCK;\n"
+ << "OPENSSL_AGENT_PKCS11_PID=" << pid << "; "
+ << "export OPENSSL_AGENT_PKCS11_PID;\n"
+ << "echo Agent pid " << pid << endl;
+
+ return 0;
+ }
+ catch (const io_error&)
+ {
+ fail << "unable to communicate with daemon";
+ }
+ catch (const system_error& e)
+ {
+ fail << "unable to start daemon: " << e;
+ }
+
+ return 0;
+ }
+ catch (const failed&)
+ {
+ return 1; // Diagnostics has already been issued.
+ }
+ catch (const cli::exception& e)
+ {
+ error << e;
+ return 1;
+ }
+
+ // Prompt for the PIN.
+ //
+ // We would be happy to use getpass() functions but it is mentioned as
+ // obsolete in the man page and is likely to be removed from the glibc
+ // future versions. Thus, we will provide our own implementation of the
+ // function that is inspired by the openssh implementation.
+ //
+ // Note: _NSIG is Linux-specic.
+ //
+ static volatile sig_atomic_t received_signals[_NSIG];
+
+ extern "C" void
+ handle_signal (int sig)
+ {
+ received_signals[sig] = 1;
+ }
+
+ static void
+ pin_prompt (const string& prompt, char* buf, size_t max_len)
+ {
+ // Note that openssh tries to open /dev/tty for read/write access but
+ // falls back to stdin/stdout on failure. Let's skip the fallback for
+ // simplicity.
+ //
+ try
+ {
+ auto_fd ifd (fdopen ("/dev/tty", fdopen_mode::in));
+ ofdstream os (fdopen ("/dev/tty", fdopen_mode::out));
+
+ // Disable the echo.
+ //
+ termios term_attrs;
+ if (tcgetattr (os.fd (), &term_attrs) == -1)
+ throw_system_error (errno);
+
+ termios na (term_attrs);
+ na.c_lflag &= ~ECHO;
+
+ if (tcsetattr (os.fd (), TCSAFLUSH, &na) == -1)
+ throw_system_error (errno);
+
+ // Catch signals that would otherwise cause the user to end up with
+ // echo turned off in the shell.
+ //
+ for (size_t i (0); i < _NSIG; ++i)
+ received_signals[i] = 0;
+
+ using sigaction_type = struct sigaction;
+
+ sigaction_type sa;
+ memset (&sa, 0, sizeof (sa));
+ sigemptyset (&sa.sa_mask);
+ sa.sa_handler = handle_signal;
+
+ // Save the current signal handlers while setting up the new ones.
+ //
+ vector<pair<int, sigaction_type>> sig_handlers;
+
+ const vector<int> sigs ({
+ SIGALRM,
+ SIGHUP,
+ SIGINT,
+ SIGPIPE,
+ SIGQUIT,
+ SIGTERM,
+ SIGTSTP,
+ SIGTTIN,
+ SIGTTOU});
+
+ for (int s: sigs)
+ {
+ sig_handlers.push_back (make_pair (s, sigaction_type ()));
+
+ if (sigaction (s, &sa, &sig_handlers.back ().second) == -1)
+ throw_system_error (errno);
+ }
+
+ // Restore the echo state and signal handlers and resend the
+ // received signals.
+ //
+ auto restore = [&os, &term_attrs, &sig_handlers] ()
+ {
+ if (tcsetattr (os.fd (), TCSAFLUSH, &term_attrs) == -1)
+ throw_system_error (errno);
+
+ for (const pair<int, sigaction_type>& sa: sig_handlers)
+ {
+ if (sigaction (sa.first, &sa.second, nullptr) == -1)
+ throw_system_error (errno);
+ }
+
+ pid_t pid (getpid ());
+ for (int i (0); i < _NSIG; i++)
+ {
+ if (received_signals[i] != 0 && kill (pid, i) == -1)
+ throw_system_error (errno);
+ }
+ };
+
+ auto rst (make_exception_guard ([&restore] ()
+ {
+ try
+ {
+ restore ();
+ }
+ catch (const system_error&)
+ {
+ // Not much we can do here.
+ //
+ }
+ }));
+
+ // Print prompt.
+ //
+ os << prompt << ": " << flush;
+
+ // Read the PIN.
+ //
+ // Reserve space for the terminating NULL character.
+ //
+ size_t n (max_len - 1);
+ size_t i (0);
+ for (; i < n; ++i)
+ {
+ ssize_t r (read (ifd.get (), buf + i, 1));
+ if (r == -1)
+ throw_system_error (errno);
+
+ if (r == 0 || buf[i] == '\n')
+ break;
+ }
+
+ restore ();
+
+ os << endl;
+ os.close ();
+
+ if (i == n)
+ fail << "PIN is too long";
+
+ buf[i] = '\0';
+ }
+ catch (const system_error& e)
+ {
+ fail << "failed to read PIN: " << e;
+ }
+ }
+
+ // Terminate the daemon.
+ //
+ static volatile sig_atomic_t exit_requested (0); // Can be used as bool.
+
+ extern "C" void
+ handle_term (int /* sig */)
+ {
+ exit_requested = 1;
+
+ // Now accept() will wakeup, set errno to EINTR, and return with -1.
+ //
+ }
+
+ // Run the daemon.
+ //
+ static int
+ daemon (const options& ops,
+ identity&& ki,
+ access&& ka,
+ auto_fd&& sc,
+ char* pin) noexcept
+ try
+ {
+ // Demonize ourselves.
+ //
+ {
+ if (setsid () == -1)
+ throw_system_error (errno);
+
+ path ("/").current_directory ();
+
+ auto_fd nd (fdnull ());
+
+ auto redir = [&nd] (int sd)
+ {
+ if (dup2 (nd.get (), sd) == -1)
+ throw_system_error (errno);
+ };
+
+ redir (0);
+ redir (1);
+ redir (2);
+ }
+
+ auto_fd sock (move (sc));
+ identity key_identity (move (ki));
+ access key_access (move (ka));
+
+ struct sigaction sa;
+ memset (&sa, 0, sizeof (sa));
+ sigemptyset (&sa.sa_mask);
+ sa.sa_handler = handle_term;
+
+ if (sigaction (SIGTERM, &sa, nullptr) == -1)
+ throw_system_error (errno);
+
+ // It would be natural to create the key at the time of receiving the
+ // init request. However, we need to erase the PIN as soon as
+ // possible. Thus, we will create it now. In case of failure we will
+ // save the error to use for the upcoming init request response.
+ //
+ private_key key;
+
+ // If present, indicates that we still expect the init request. Will
+ // set it to nullopt after init request processing.
+ //
+ optional<string> init_error;
+
+ try
+ {
+ optional<simulate_outcome> s;
+ if (ops.simulate_specified ())
+ s = ops.simulate ();
+
+ key = private_key (key_identity, key_access, pin, s);
+ init_error = "";
+ }
+ catch (const invalid_argument& e)
+ {
+ init_error = string ("error: ") + e.what ();
+ }
+ catch (const runtime_error& e)
+ {
+ init_error = string ("error: ") + e.what ();
+ }
+
+ // Erase the no longer needed PIN.
+ //
+ if (pin != nullptr)
+ mem_clear (pin, strlen (pin));
+
+ // Run the request processing loop.
+ //
+ while (true)
+ {
+ // Accept the client connection.
+ //
+ auto_fd fd (
+ accept (sock.get (), nullptr /* addr */, nullptr /* addrlen */));
+
+ if (exit_requested)
+ break;
+
+ if (fd.get () == -1)
+ {
+ if (errno == EINTR || errno == ECONNABORTED || errno == EPROTO)
+ continue;
+
+ throw_system_error (errno);
+ }
+
+ // If we fail to duplicate the file descriptor, then we are probably
+ // out of resources. Anyway we can't do much about it and will bail
+ // out.
+ //
+ ifdstream is (fddup (fd.get ()));
+
+ try
+ {
+ ofdstream os (move (fd));
+
+ // Respond to the client until he sends valid requests.
+ //
+ while (is.peek () != ifdstream::traits_type::eof ())
+ {
+ request rq;
+ is >> rq;
+
+ // The init request must be the first and only one.
+ //
+ if (rq.cmd == "init")
+ {
+ if (!init_error)
+ {
+ os << response ("error: already initialized");
+ break;
+ }
+
+ if (!init_error->empty ())
+ {
+ os << response (*init_error);
+ os.close ();
+
+ throw runtime_error (*init_error);
+ }
+
+ init_error = nullopt; // We don't expect init anymore.
+ os << response (); // Success.
+ continue;
+ }
+
+ if (init_error)
+ {
+ os << response ("error: not initialized");
+ break;
+ }
+
+ // Process the sign request.
+ //
+ if (rq.cmd == "sign")
+ {
+ // Parse and validate the request arguments (key URL and
+ // simulate outcome).
+ //
+ if (rq.args.size () != 2)
+ {
+ os << response ("error: invalid args");
+ break;
+ }
+
+ identity k;
+
+ try
+ {
+ k = identity (url (rq.args[0]));
+ }
+ catch (const invalid_argument& e)
+ {
+ os << response (
+ string ("error: invalid private key URL: ") + e.what ());
+
+ break;
+ }
+
+ optional<simulate_outcome> sim;
+
+ if (!rq.args[1].empty ())
+ try
+ {
+ sim = to_simulate_outcome (rq.args[1]);
+ }
+ catch (const invalid_argument& e)
+ {
+ os << response (string ("error: ") + e.what ());
+ break;
+ }
+
+ if (k != key_identity)
+ {
+ os << response ("error: private key doesn't match");
+ break;
+ }
+
+ try
+ {
+ os << response (key.sign (rq.input, sim));
+ }
+ catch (const runtime_error& e)
+ {
+ os << response (string ("error: ") + e.what ());
+ break;
+ }
+ }
+ }
+
+ // Close the client connection.
+ //
+ is.close ();
+ os.close ();
+ }
+ catch (const io_error&)
+ {
+ // The client is presumably dead.
+ //
+ }
+ }
+
+ return 0;
+ }
+ catch (const std::exception&)
+ {
+ // There is no way we can complain.
+ //
+ return 1;
+ }
+ }
+ }
+}
+
+int
+main (int argc, char* argv[])
+{
+ return openssl::agent::pkcs11::main (argc, argv);
+}
diff --git a/openssl/agent/pkcs11/options.cli b/openssl/agent/pkcs11/options.cli
new file mode 100644
index 0000000..9c31b53
--- /dev/null
+++ b/openssl/agent/pkcs11/options.cli
@@ -0,0 +1,79 @@
+// file : openssl/agent/pkcs11/options.cli
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+include <openssl/options.cli>;
+
+"\section=1"
+"\name=openssl-agent-pkcs11"
+"\summary=OpenSSL PKCS#11 key agent"
+
+namespace openssl
+{
+ namespace agent
+ {
+ namespace pkcs11
+ {
+ {
+ "<options> <url>",
+
+ "
+ \h|SYNOPSIS|
+
+ \c{\b{openssl-agent-pkcs11 --help}\n
+ \b{openssl-agent-pkcs11 --version}\n
+ \b{openssl-agent-pkcs11} [<options>] <url>}
+
+ \h|DESCRIPTION|
+
+ The \cb{PKCS#11} key agent prompts for a PIN for the specified token
+ and forks off the daemon, which unlocks the key, opens a UNIX domain
+ socket, and waits for cryptographic operation requests from the
+ OpenSSL client \l{openssl-client(1)}. The agent also prints to
+ \cb{stdout} a shell script fragment that sets the environment
+ variables necessary for locating the agent. See the ENVIRONMENT
+ section for details.
+
+ The daemon can be run in the simulation mode without actually logging
+ into the \cb{PKCS#11} token. If the \cb{--simulate} option is
+ specified with the \cb{success} outcome, the daemon pretends to unlock
+ the requested key and responds with a dummy signature to the
+ subsequent data signing requests. The \cb{failure} outcome causes the
+ daemon to exit with non-zero status, as if it failed to find the key.
+ This mode is mostly useful for testing.
+ "
+ }
+
+ class options
+ {
+ "\h|OPTIONS|"
+
+ bool --help {"Print usage information and exit."}
+ bool --version {"Print version and exit."}
+
+ simulate_outcome --simulate
+ {
+ "<outcome>",
+ "Run the daemon in the simulation mode."
+ }
+ };
+
+ "
+ \h|ENVIRONMENT|
+
+ The printed shell script fragment sets the
+ \cb{OPENSSL_AGENT_PKCS11_SOCK} and \cb{OPENSSL_AGENT_PKCS11_PID}
+ environment variables. The former refers to the Unix-domain socket that
+ should be used by \cb{openssl-client(1)} for communicating with the
+ daemon. The latter contains the daemon process id that can be used to
+ terminate the daemon by sending it the \cb{SIGTERM} signal.
+ "
+
+ "
+ \h|EXIT STATUS|
+
+ Non-zero exit status is returned in case of an error.
+ "
+ }
+ }
+}
diff --git a/openssl/agent/pkcs11/pkcs11.cxx b/openssl/agent/pkcs11/pkcs11.cxx
new file mode 100644
index 0000000..1cd541d
--- /dev/null
+++ b/openssl/agent/pkcs11/pkcs11.cxx
@@ -0,0 +1,214 @@
+// file : openssl/agent/pkcs11/pkcs11.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <openssl/agent/pkcs11/pkcs11.hxx>
+
+#include <dlfcn.h>
+
+#include <libbutl/utility.mxx> // function_cast()
+
+namespace openssl
+{
+ namespace agent
+ {
+ namespace pkcs11
+ {
+ using namespace butl;
+
+ // Deleters.
+ //
+ struct module_deleter
+ {
+ void
+ operator() (void* p) const {if (p != nullptr) dlclose (p);}
+ };
+
+ struct functions_deleter
+ {
+ void
+ operator() (CK_FUNCTION_LIST* p) const
+ {
+ if (p != nullptr)
+ p->C_Finalize (nullptr);
+ }
+ };
+
+ // API.
+ //
+ static unique_ptr<void, module_deleter> module;
+ static unique_ptr<CK_FUNCTION_LIST, functions_deleter> functions;
+
+ CK_FUNCTION_LIST*
+ api (const path& p, bool ignore_nonexistent)
+ {
+ if (functions != nullptr)
+ return functions.get ();
+
+ // Load the PKCS#11 module.
+ //
+ unique_ptr<void, module_deleter> mod (
+ dlopen (p.string ().c_str (), RTLD_NOW));
+
+ if (mod == nullptr)
+ {
+ // Note that we cannot distinguish the module absence from other
+ // failure reasons (not readable, has a wrong format, etc) and will
+ // reduce all possible reasons to the 'not found' case.
+ //
+ if (ignore_nonexistent)
+ return nullptr;
+
+ char* e (dlerror ());
+ assert (e != nullptr);
+ throw runtime_error (e);
+ }
+
+ // Initialize the API.
+ //
+ CK_FUNCTION_LIST* fs;
+ {
+ CK_RV (*f) (CK_FUNCTION_LIST**) =
+ function_cast<decltype(f)> (
+ dlsym (mod.get (), "C_GetFunctionList"));
+
+ if (f == nullptr)
+ throw runtime_error ("unable to find PKCS#11 entry point");
+
+ CK_RV r (f (&fs));
+ if (r != CKR_OK)
+ throw_api_error (r, "unable to get PKCS#11 functions");
+ }
+
+ // We don't suppose the Cryptoki to be called from multiple threads
+ // and so passing NULL to the initialization function.
+ //
+ CK_RV r (fs->C_Initialize (nullptr /* pInitArgs */));
+ if (r != CKR_OK)
+ throw_api_error (r, "unable to initialize PKCS#11 API");
+
+ functions.reset (fs);
+ module = move (mod);
+
+ return functions.get ();
+ }
+
+ CK_FUNCTION_LIST*
+ api ()
+ {
+ assert (functions != nullptr);
+ return functions.get ();
+ }
+
+ struct error_reason
+ {
+ CK_RV error;
+ const char* reason;
+ };
+
+ static const error_reason error_reasons[] = {
+ {CKR_CANCEL, "cancel"},
+ {CKR_HOST_MEMORY, "host memory error"},
+ {CKR_SLOT_ID_INVALID, "invalid slot id"},
+ {CKR_GENERAL_ERROR, "general error"},
+ {CKR_FUNCTION_FAILED, "function failed"},
+ {CKR_ARGUMENTS_BAD, "invalid arguments"},
+ {CKR_NO_EVENT, "no event"},
+ {CKR_NEED_TO_CREATE_THREADS, "need to create threads"},
+ {CKR_CANT_LOCK, "cannot lock"},
+ {CKR_ATTRIBUTE_READ_ONLY, "attribute read only"},
+ {CKR_ATTRIBUTE_SENSITIVE, "attribute sensitive"},
+ {CKR_ATTRIBUTE_TYPE_INVALID, "attribute type invalid"},
+ {CKR_ATTRIBUTE_VALUE_INVALID, "attribute value invalid"},
+ {CKR_DATA_INVALID, "data invalid"},
+ {CKR_DATA_LEN_RANGE, "data len range"},
+ {CKR_DEVICE_ERROR, "device error"},
+ {CKR_DEVICE_MEMORY, "device memory"},
+ {CKR_DEVICE_REMOVED, "device removed"},
+ {CKR_ENCRYPTED_DATA_INVALID, "encrypted data invalid"},
+ {CKR_ENCRYPTED_DATA_LEN_RANGE, "encrypted data len range"},
+ {CKR_FUNCTION_CANCELED, "function canceled"},
+ {CKR_FUNCTION_NOT_PARALLEL, "function not parallel"},
+ {CKR_FUNCTION_NOT_SUPPORTED, "function not supported"},
+ {CKR_KEY_HANDLE_INVALID, "key handle invalid"},
+ {CKR_KEY_SIZE_RANGE, "key size range"},
+ {CKR_KEY_TYPE_INCONSISTENT, "key type inconsistent"},
+ {CKR_KEY_NOT_NEEDED, "key not needed"},
+ {CKR_KEY_CHANGED, "key changed"},
+ {CKR_KEY_NEEDED, "key needed"},
+ {CKR_KEY_INDIGESTIBLE, "key indigestible"},
+ {CKR_KEY_FUNCTION_NOT_PERMITTED, "key function not permitted"},
+ {CKR_KEY_NOT_WRAPPABLE, "key not wrappable"},
+ {CKR_KEY_UNEXTRACTABLE, "key unextractable"},
+ {CKR_MECHANISM_INVALID, "mechanism invalid"},
+ {CKR_MECHANISM_PARAM_INVALID, "mechanism param invalid"},
+ {CKR_OBJECT_HANDLE_INVALID, "object handle invalid"},
+ {CKR_OPERATION_ACTIVE, "operation active"},
+ {CKR_OPERATION_NOT_INITIALIZED, "operation not initialized"},
+ {CKR_PIN_INCORRECT, "PIN incorrect"},
+ {CKR_PIN_INVALID, "PIN invalid"},
+ {CKR_PIN_LEN_RANGE, "invalid PIN length"},
+ {CKR_PIN_EXPIRED, "PIN expired"},
+ {CKR_PIN_LOCKED, "PIN locked"},
+ {CKR_SESSION_CLOSED, "session closed"},
+ {CKR_SESSION_COUNT, "session count"},
+ {CKR_SESSION_HANDLE_INVALID, "session handle invalid"},
+ {CKR_SESSION_PARALLEL_NOT_SUPPORTED, "session parallel not supported"},
+ {CKR_SESSION_READ_ONLY, "session read only"},
+ {CKR_SESSION_EXISTS, "session exists"},
+ {CKR_SESSION_READ_ONLY_EXISTS, "read-only session exists"},
+ {CKR_SESSION_READ_WRITE_SO_EXISTS, "read/write SO session exists"},
+ {CKR_SIGNATURE_INVALID, "signature invalid"},
+ {CKR_SIGNATURE_LEN_RANGE, "signature len range"},
+ {CKR_TEMPLATE_INCOMPLETE, "incomplete template"},
+ {CKR_TEMPLATE_INCONSISTENT, "inconsistent template"},
+ {CKR_TOKEN_NOT_PRESENT, "no PKCS#11 token present"},
+ {CKR_TOKEN_NOT_RECOGNIZED, "PKCS#11 token not recognized"},
+ {CKR_TOKEN_WRITE_PROTECTED, "token write protected"},
+ {CKR_UNWRAPPING_KEY_HANDLE_INVALID, "unwrapping key handle invalid"},
+ {CKR_UNWRAPPING_KEY_SIZE_RANGE, "unwrapping key size range"},
+ {CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT, "unwrapping key type inconsistent"},
+ {CKR_USER_ALREADY_LOGGED_IN, "user already logged in"},
+ {CKR_USER_NOT_LOGGED_IN, "user not logged in"},
+ {CKR_USER_PIN_NOT_INITIALIZED, "user PIN not initialized"},
+ {CKR_USER_TYPE_INVALID, "user type invalid"},
+ {CKR_USER_ANOTHER_ALREADY_LOGGED_IN, "user another is already logged in"},
+ {CKR_USER_TOO_MANY_TYPES, "uxsser too many types"},
+ {CKR_WRAPPED_KEY_INVALID, "wrapped key invalid"},
+ {CKR_WRAPPED_KEY_LEN_RANGE, "wrapped key len range"},
+ {CKR_WRAPPING_KEY_HANDLE_INVALID, "wrapping key handle invalid"},
+ {CKR_WRAPPING_KEY_SIZE_RANGE, "wrapping key size range"},
+ {CKR_WRAPPING_KEY_TYPE_INCONSISTENT, "wrapping key type inconsistent"},
+ {CKR_RANDOM_SEED_NOT_SUPPORTED, "random seed not supported"},
+ {CKR_RANDOM_NO_RNG, "random no rng"},
+ {CKR_DOMAIN_PARAMS_INVALID, "domain params invalid"},
+ {CKR_BUFFER_TOO_SMALL, "buffer too small"},
+ {CKR_SAVED_STATE_INVALID, "saved state invalid"},
+ {CKR_INFORMATION_SENSITIVE, "information sensitive"},
+ {CKR_STATE_UNSAVEABLE, "state unsaveable"},
+ {CKR_CRYPTOKI_NOT_INITIALIZED, "cryptoki not initialized"},
+ {CKR_CRYPTOKI_ALREADY_INITIALIZED, "cryptoki already initialized"},
+ {CKR_MUTEX_BAD, "mutex bad"},
+ {CKR_MUTEX_NOT_LOCKED, "mutex not locked"},
+ {CKR_VENDOR_DEFINED, "vendor defined"},
+ {CKR_OK, nullptr}
+ };
+
+ [[noreturn]] void
+ throw_api_error (CK_RV error, string what)
+ {
+ what += ": ";
+
+ const error_reason* e (error_reasons);
+ for (; e->error != error && e->reason != nullptr; ++e) ;
+
+ if (e->reason != nullptr)
+ what += e->reason;
+ else
+ what += "unknown error " + to_string (error);
+
+ throw runtime_error (what);
+ }
+ }
+ }
+}
diff --git a/openssl/agent/pkcs11/pkcs11.h b/openssl/agent/pkcs11/pkcs11.h
new file mode 100644
index 0000000..8219b96
--- /dev/null
+++ b/openssl/agent/pkcs11/pkcs11.h
@@ -0,0 +1,1436 @@
+/* pkcs11.h
+ Copyright 2006, 2007 g10 Code GmbH
+ Copyright 2006 Andreas Jellinghaus
+
+ This file is free software; as a special exception the author gives
+ unlimited permission to copy and/or distribute it, with or without
+ modifications, as long as this notice is preserved.
+
+ This file is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY, to the extent permitted by law; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. */
+
+/* Please submit changes back to the Scute project at
+ http://www.scute.org/ (or send them to marcus@g10code.com), so that
+ they can be picked up by other projects from there as well. */
+
+/* This file is a modified implementation of the PKCS #11 standard by
+ RSA Security Inc. It is mostly a drop-in replacement, with the
+ following change:
+
+ This header file does not require any macro definitions by the user
+ (like CK_DEFINE_FUNCTION etc). In fact, it defines those macros
+ for you (if useful, some are missing, let me know if you need
+ more).
+
+ There is an additional API available that does comply better to the
+ GNU coding standard. It can be switched on by defining
+ CRYPTOKI_GNU before including this header file. For this, the
+ following changes are made to the specification:
+
+ All structure types are changed to a "struct ck_foo" where CK_FOO
+ is the type name in PKCS #11.
+
+ All non-structure types are changed to ck_foo_t where CK_FOO is the
+ lowercase version of the type name in PKCS #11. The basic types
+ (CK_ULONG et al.) are removed without substitute.
+
+ All members of structures are modified in the following way: Type
+ indication prefixes are removed, and underscore characters are
+ inserted before words. Then the result is lowercased.
+
+ Note that function names are still in the original case, as they
+ need for ABI compatibility.
+
+ CK_FALSE, CK_TRUE and NULL_PTR are removed without substitute. Use
+ <stdbool.h>.
+
+ If CRYPTOKI_COMPAT is defined before including this header file,
+ then none of the API changes above take place, and the API is the
+ one defined by the PKCS #11 standard. */
+
+#ifndef PKCS11_H
+#define PKCS11_H 1
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* The version of cryptoki we implement. The revision is changed with
+ each modification of this file. If you do not use the "official"
+ version of this file, please consider deleting the revision macro
+ (you may use a macro with a different name to keep track of your
+ versions). */
+#define CRYPTOKI_VERSION_MAJOR 2
+#define CRYPTOKI_VERSION_MINOR 20
+#define CRYPTOKI_VERSION_REVISION 6
+
+
+/* Compatibility interface is default, unless CRYPTOKI_GNU is
+ given. */
+#ifndef CRYPTOKI_GNU
+#ifndef CRYPTOKI_COMPAT
+#define CRYPTOKI_COMPAT 1
+#endif
+#endif
+
+/* System dependencies. */
+
+#if defined(_WIN32) || defined(CRYPTOKI_FORCE_WIN32)
+
+/* There is a matching pop below. */
+#pragma pack(push, cryptoki, 1)
+
+#ifdef CRYPTOKI_EXPORTS
+#define CK_SPEC __declspec(dllexport)
+#else
+#define CK_SPEC __declspec(dllimport)
+#endif
+
+#else
+
+#define CK_SPEC
+
+#endif
+
+#ifdef CRYPTOKI_COMPAT
+ /* If we are in compatibility mode, switch all exposed names to the
+ PKCS #11 variant. There are corresponding #undefs below. */
+
+#define ck_flags_t CK_FLAGS
+#define ck_version _CK_VERSION
+
+#define ck_info _CK_INFO
+#define cryptoki_version cryptokiVersion
+#define manufacturer_id manufacturerID
+#define library_description libraryDescription
+#define library_version libraryVersion
+
+#define ck_notification_t CK_NOTIFICATION
+#define ck_slot_id_t CK_SLOT_ID
+
+#define ck_slot_info _CK_SLOT_INFO
+#define slot_description slotDescription
+#define hardware_version hardwareVersion
+#define firmware_version firmwareVersion
+
+#define ck_token_info _CK_TOKEN_INFO
+#define serial_number serialNumber
+#define max_session_count ulMaxSessionCount
+#define session_count ulSessionCount
+#define max_rw_session_count ulMaxRwSessionCount
+#define rw_session_count ulRwSessionCount
+#define max_pin_len ulMaxPinLen
+#define min_pin_len ulMinPinLen
+#define total_public_memory ulTotalPublicMemory
+#define free_public_memory ulFreePublicMemory
+#define total_private_memory ulTotalPrivateMemory
+#define free_private_memory ulFreePrivateMemory
+#define utc_time utcTime
+
+#define ck_session_handle_t CK_SESSION_HANDLE
+#define ck_user_type_t CK_USER_TYPE
+#define ck_state_t CK_STATE
+
+#define ck_session_info _CK_SESSION_INFO
+#define slot_id slotID
+#define device_error ulDeviceError
+
+#define ck_object_handle_t CK_OBJECT_HANDLE
+#define ck_object_class_t CK_OBJECT_CLASS
+#define ck_hw_feature_type_t CK_HW_FEATURE_TYPE
+#define ck_key_type_t CK_KEY_TYPE
+#define ck_certificate_type_t CK_CERTIFICATE_TYPE
+#define ck_attribute_type_t CK_ATTRIBUTE_TYPE
+
+#define ck_attribute _CK_ATTRIBUTE
+#define value pValue
+#define value_len ulValueLen
+
+#define ck_date _CK_DATE
+
+#define ck_mechanism_type_t CK_MECHANISM_TYPE
+
+#define ck_rsa_pkcs_mgf_type_t CK_RSA_PKCS_MGF_TYPE
+
+#define ck_mechanism _CK_MECHANISM
+#define parameter pParameter
+#define parameter_len ulParameterLen
+
+#define ck_mechanism_info _CK_MECHANISM_INFO
+#define min_key_size ulMinKeySize
+#define max_key_size ulMaxKeySize
+
+#define ck_rv_t CK_RV
+#define ck_notify_t CK_NOTIFY
+
+#define ck_function_list _CK_FUNCTION_LIST
+
+#define ck_createmutex_t CK_CREATEMUTEX
+#define ck_destroymutex_t CK_DESTROYMUTEX
+#define ck_lockmutex_t CK_LOCKMUTEX
+#define ck_unlockmutex_t CK_UNLOCKMUTEX
+
+#define ck_c_initialize_args _CK_C_INITIALIZE_ARGS
+#define create_mutex CreateMutex
+#define destroy_mutex DestroyMutex
+#define lock_mutex LockMutex
+#define unlock_mutex UnlockMutex
+#define reserved pReserved
+
+#endif /* CRYPTOKI_COMPAT */
+
+
+typedef unsigned long ck_flags_t;
+
+struct ck_version
+{
+ unsigned char major;
+ unsigned char minor;
+};
+
+
+struct ck_info
+{
+ struct ck_version cryptoki_version;
+ unsigned char manufacturer_id[32];
+ ck_flags_t flags;
+ unsigned char library_description[32];
+ struct ck_version library_version;
+};
+
+
+typedef unsigned long ck_notification_t;
+
+#define CKN_SURRENDER (0UL)
+
+
+typedef unsigned long ck_slot_id_t;
+
+
+struct ck_slot_info
+{
+ unsigned char slot_description[64];
+ unsigned char manufacturer_id[32];
+ ck_flags_t flags;
+ struct ck_version hardware_version;
+ struct ck_version firmware_version;
+};
+
+
+#define CKF_TOKEN_PRESENT (1UL << 0)
+#define CKF_REMOVABLE_DEVICE (1UL << 1)
+#define CKF_HW_SLOT (1UL << 2)
+#define CKF_ARRAY_ATTRIBUTE (1UL << 30)
+
+
+struct ck_token_info
+{
+ unsigned char label[32];
+ unsigned char manufacturer_id[32];
+ unsigned char model[16];
+ unsigned char serial_number[16];
+ ck_flags_t flags;
+ unsigned long max_session_count;
+ unsigned long session_count;
+ unsigned long max_rw_session_count;
+ unsigned long rw_session_count;
+ unsigned long max_pin_len;
+ unsigned long min_pin_len;
+ unsigned long total_public_memory;
+ unsigned long free_public_memory;
+ unsigned long total_private_memory;
+ unsigned long free_private_memory;
+ struct ck_version hardware_version;
+ struct ck_version firmware_version;
+ unsigned char utc_time[16];
+};
+
+
+#define CKF_RNG (1UL << 0)
+#define CKF_WRITE_PROTECTED (1UL << 1)
+#define CKF_LOGIN_REQUIRED (1UL << 2)
+#define CKF_USER_PIN_INITIALIZED (1UL << 3)
+#define CKF_RESTORE_KEY_NOT_NEEDED (1UL << 5)
+#define CKF_CLOCK_ON_TOKEN (1UL << 6)
+#define CKF_PROTECTED_AUTHENTICATION_PATH (1UL << 8)
+#define CKF_DUAL_CRYPTO_OPERATIONS (1UL << 9)
+#define CKF_TOKEN_INITIALIZED (1UL << 10)
+#define CKF_SECONDARY_AUTHENTICATION (1UL << 11)
+#define CKF_USER_PIN_COUNT_LOW (1UL << 16)
+#define CKF_USER_PIN_FINAL_TRY (1UL << 17)
+#define CKF_USER_PIN_LOCKED (1UL << 18)
+#define CKF_USER_PIN_TO_BE_CHANGED (1UL << 19)
+#define CKF_SO_PIN_COUNT_LOW (1UL << 20)
+#define CKF_SO_PIN_FINAL_TRY (1UL << 21)
+#define CKF_SO_PIN_LOCKED (1UL << 22)
+#define CKF_SO_PIN_TO_BE_CHANGED (1UL << 23)
+
+#define CK_UNAVAILABLE_INFORMATION ((unsigned long) -1)
+#define CK_EFFECTIVELY_INFINITE (0UL)
+
+
+typedef unsigned long ck_session_handle_t;
+
+#define CK_INVALID_HANDLE (0UL)
+
+
+typedef unsigned long ck_user_type_t;
+
+#define CKU_SO (0UL)
+#define CKU_USER (1UL)
+#define CKU_CONTEXT_SPECIFIC (2UL)
+
+
+typedef unsigned long ck_state_t;
+
+#define CKS_RO_PUBLIC_SESSION (0UL)
+#define CKS_RO_USER_FUNCTIONS (1UL)
+#define CKS_RW_PUBLIC_SESSION (2UL)
+#define CKS_RW_USER_FUNCTIONS (3UL)
+#define CKS_RW_SO_FUNCTIONS (4UL)
+
+
+struct ck_session_info
+{
+ ck_slot_id_t slot_id;
+ ck_state_t state;
+ ck_flags_t flags;
+ unsigned long device_error;
+};
+
+#define CKF_RW_SESSION (1UL << 1)
+#define CKF_SERIAL_SESSION (1UL << 2)
+
+
+typedef unsigned long ck_object_handle_t;
+
+
+typedef unsigned long ck_object_class_t;
+
+#define CKO_DATA (0UL)
+#define CKO_CERTIFICATE (1UL)
+#define CKO_PUBLIC_KEY (2UL)
+#define CKO_PRIVATE_KEY (3UL)
+#define CKO_SECRET_KEY (4UL)
+#define CKO_HW_FEATURE (5UL)
+#define CKO_DOMAIN_PARAMETERS (6UL)
+#define CKO_MECHANISM (7UL)
+#define CKO_VENDOR_DEFINED (1UL << 31)
+
+
+typedef unsigned long ck_hw_feature_type_t;
+
+#define CKH_MONOTONIC_COUNTER (1UL)
+#define CKH_CLOCK (2UL)
+#define CKH_USER_INTERFACE (3UL)
+#define CKH_VENDOR_DEFINED (1UL << 31)
+
+
+typedef unsigned long ck_key_type_t;
+
+#define CKK_RSA (0UL)
+#define CKK_DSA (1UL)
+#define CKK_DH (2UL)
+#define CKK_ECDSA (3UL)
+#define CKK_EC (3UL)
+#define CKK_X9_42_DH (4UL)
+#define CKK_KEA (5UL)
+#define CKK_GENERIC_SECRET (0x10UL)
+#define CKK_RC2 (0x11UL)
+#define CKK_RC4 (0x12UL)
+#define CKK_DES (0x13UL)
+#define CKK_DES2 (0x14UL)
+#define CKK_DES3 (0x15UL)
+#define CKK_CAST (0x16UL)
+#define CKK_CAST3 (0x17UL)
+#define CKK_CAST128 (0x18UL)
+#define CKK_RC5 (0x19UL)
+#define CKK_IDEA (0x1aUL)
+#define CKK_SKIPJACK (0x1bUL)
+#define CKK_BATON (0x1cUL)
+#define CKK_JUNIPER (0x1dUL)
+#define CKK_CDMF (0x1eUL)
+#define CKK_AES (0x1fUL)
+#define CKK_BLOWFISH (0x20UL)
+#define CKK_TWOFISH (0x21UL)
+#define CKK_GOSTR3410 (0x30UL)
+#define CKK_GOSTR3411 (0x31UL)
+#define CKK_GOST28147 (0x32UL)
+#define CKK_VENDOR_DEFINED (1UL << 31)
+
+
+typedef unsigned long ck_certificate_type_t;
+
+#define CKC_X_509 (0UL)
+#define CKC_X_509_ATTR_CERT (1UL)
+#define CKC_WTLS (2UL)
+#define CKC_VENDOR_DEFINED (1UL << 31)
+
+
+typedef unsigned long ck_attribute_type_t;
+
+#define CKA_CLASS (0UL)
+#define CKA_TOKEN (1UL)
+#define CKA_PRIVATE (2UL)
+#define CKA_LABEL (3UL)
+#define CKA_APPLICATION (0x10UL)
+#define CKA_VALUE (0x11UL)
+#define CKA_OBJECT_ID (0x12UL)
+#define CKA_CERTIFICATE_TYPE (0x80UL)
+#define CKA_ISSUER (0x81UL)
+#define CKA_SERIAL_NUMBER (0x82UL)
+#define CKA_AC_ISSUER (0x83UL)
+#define CKA_OWNER (0x84UL)
+#define CKA_ATTR_TYPES (0x85UL)
+#define CKA_TRUSTED (0x86UL)
+#define CKA_CERTIFICATE_CATEGORY (0x87UL)
+#define CKA_JAVA_MIDP_SECURITY_DOMAIN (0x88UL)
+#define CKA_URL (0x89UL)
+#define CKA_HASH_OF_SUBJECT_PUBLIC_KEY (0x8aUL)
+#define CKA_HASH_OF_ISSUER_PUBLIC_KEY (0x8bUL)
+#define CKA_CHECK_VALUE (0x90UL)
+#define CKA_KEY_TYPE (0x100UL)
+#define CKA_SUBJECT (0x101UL)
+#define CKA_ID (0x102UL)
+#define CKA_SENSITIVE (0x103UL)
+#define CKA_ENCRYPT (0x104UL)
+#define CKA_DECRYPT (0x105UL)
+#define CKA_WRAP (0x106UL)
+#define CKA_UNWRAP (0x107UL)
+#define CKA_SIGN (0x108UL)
+#define CKA_SIGN_RECOVER (0x109UL)
+#define CKA_VERIFY (0x10aUL)
+#define CKA_VERIFY_RECOVER (0x10bUL)
+#define CKA_DERIVE (0x10cUL)
+#define CKA_START_DATE (0x110UL)
+#define CKA_END_DATE (0x111UL)
+#define CKA_MODULUS (0x120UL)
+#define CKA_MODULUS_BITS (0x121UL)
+#define CKA_PUBLIC_EXPONENT (0x122UL)
+#define CKA_PRIVATE_EXPONENT (0x123UL)
+#define CKA_PRIME_1 (0x124UL)
+#define CKA_PRIME_2 (0x125UL)
+#define CKA_EXPONENT_1 (0x126UL)
+#define CKA_EXPONENT_2 (0x127UL)
+#define CKA_COEFFICIENT (0x128UL)
+#define CKA_PRIME (0x130UL)
+#define CKA_SUBPRIME (0x131UL)
+#define CKA_BASE (0x132UL)
+#define CKA_PRIME_BITS (0x133UL)
+#define CKA_SUB_PRIME_BITS (0x134UL)
+#define CKA_VALUE_BITS (0x160UL)
+#define CKA_VALUE_LEN (0x161UL)
+#define CKA_EXTRACTABLE (0x162UL)
+#define CKA_LOCAL (0x163UL)
+#define CKA_NEVER_EXTRACTABLE (0x164UL)
+#define CKA_ALWAYS_SENSITIVE (0x165UL)
+#define CKA_KEY_GEN_MECHANISM (0x166UL)
+#define CKA_MODIFIABLE (0x170UL)
+#define CKA_ECDSA_PARAMS (0x180UL)
+#define CKA_EC_PARAMS (0x180UL)
+#define CKA_EC_POINT (0x181UL)
+#define CKA_SECONDARY_AUTH (0x200UL)
+#define CKA_AUTH_PIN_FLAGS (0x201UL)
+#define CKA_ALWAYS_AUTHENTICATE (0x202UL)
+#define CKA_WRAP_WITH_TRUSTED (0x210UL)
+#define CKA_GOSTR3410_PARAMS (0x250UL)
+#define CKA_GOSTR3411_PARAMS (0x251UL)
+#define CKA_GOST28147_PARAMS (0x252UL)
+#define CKA_HW_FEATURE_TYPE (0x300UL)
+#define CKA_RESET_ON_INIT (0x301UL)
+#define CKA_HAS_RESET (0x302UL)
+#define CKA_PIXEL_X (0x400UL)
+#define CKA_PIXEL_Y (0x401UL)
+#define CKA_RESOLUTION (0x402UL)
+#define CKA_CHAR_ROWS (0x403UL)
+#define CKA_CHAR_COLUMNS (0x404UL)
+#define CKA_COLOR (0x405UL)
+#define CKA_BITS_PER_PIXEL (0x406UL)
+#define CKA_CHAR_SETS (0x480UL)
+#define CKA_ENCODING_METHODS (0x481UL)
+#define CKA_MIME_TYPES (0x482UL)
+#define CKA_MECHANISM_TYPE (0x500UL)
+#define CKA_REQUIRED_CMS_ATTRIBUTES (0x501UL)
+#define CKA_DEFAULT_CMS_ATTRIBUTES (0x502UL)
+#define CKA_SUPPORTED_CMS_ATTRIBUTES (0x503UL)
+#define CKA_WRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x211UL)
+#define CKA_UNWRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x212UL)
+#define CKA_ALLOWED_MECHANISMS (CKF_ARRAY_ATTRIBUTE | 0x600UL)
+#define CKA_VENDOR_DEFINED (1UL << 31)
+
+
+struct ck_attribute
+{
+ ck_attribute_type_t type;
+ void *value;
+ unsigned long value_len;
+};
+
+
+struct ck_date
+{
+ unsigned char year[4];
+ unsigned char month[2];
+ unsigned char day[2];
+};
+
+
+typedef unsigned long ck_mechanism_type_t;
+
+#define CKM_RSA_PKCS_KEY_PAIR_GEN (0UL)
+#define CKM_RSA_PKCS (1UL)
+#define CKM_RSA_9796 (2UL)
+#define CKM_RSA_X_509 (3UL)
+#define CKM_MD2_RSA_PKCS (4UL)
+#define CKM_MD5_RSA_PKCS (5UL)
+#define CKM_SHA1_RSA_PKCS (6UL)
+#define CKM_RIPEMD128_RSA_PKCS (7UL)
+#define CKM_RIPEMD160_RSA_PKCS (8UL)
+#define CKM_RSA_PKCS_OAEP (9UL)
+#define CKM_RSA_X9_31_KEY_PAIR_GEN (0xaUL)
+#define CKM_RSA_X9_31 (0xbUL)
+#define CKM_SHA1_RSA_X9_31 (0xcUL)
+#define CKM_RSA_PKCS_PSS (0xdUL)
+#define CKM_SHA1_RSA_PKCS_PSS (0xeUL)
+#define CKM_DSA_KEY_PAIR_GEN (0x10UL)
+#define CKM_DSA (0x11UL)
+#define CKM_DSA_SHA1 (0x12UL)
+#define CKM_DH_PKCS_KEY_PAIR_GEN (0x20UL)
+#define CKM_DH_PKCS_DERIVE (0x21UL)
+#define CKM_X9_42_DH_KEY_PAIR_GEN (0x30UL)
+#define CKM_X9_42_DH_DERIVE (0x31UL)
+#define CKM_X9_42_DH_HYBRID_DERIVE (0x32UL)
+#define CKM_X9_42_MQV_DERIVE (0x33UL)
+#define CKM_SHA256_RSA_PKCS (0x40UL)
+#define CKM_SHA384_RSA_PKCS (0x41UL)
+#define CKM_SHA512_RSA_PKCS (0x42UL)
+#define CKM_SHA256_RSA_PKCS_PSS (0x43UL)
+#define CKM_SHA384_RSA_PKCS_PSS (0x44UL)
+#define CKM_SHA512_RSA_PKCS_PSS (0x45UL)
+#define CKM_SHA224_RSA_PKCS (0x46UL)
+#define CKM_SHA224_RSA_PKCS_PSS (0x47UL)
+#define CKM_RC2_KEY_GEN (0x100UL)
+#define CKM_RC2_ECB (0x101UL)
+#define CKM_RC2_CBC (0x102UL)
+#define CKM_RC2_MAC (0x103UL)
+#define CKM_RC2_MAC_GENERAL (0x104UL)
+#define CKM_RC2_CBC_PAD (0x105UL)
+#define CKM_RC4_KEY_GEN (0x110UL)
+#define CKM_RC4 (0x111UL)
+#define CKM_DES_KEY_GEN (0x120UL)
+#define CKM_DES_ECB (0x121UL)
+#define CKM_DES_CBC (0x122UL)
+#define CKM_DES_MAC (0x123UL)
+#define CKM_DES_MAC_GENERAL (0x124UL)
+#define CKM_DES_CBC_PAD (0x125UL)
+#define CKM_DES2_KEY_GEN (0x130UL)
+#define CKM_DES3_KEY_GEN (0x131UL)
+#define CKM_DES3_ECB (0x132UL)
+#define CKM_DES3_CBC (0x133UL)
+#define CKM_DES3_MAC (0x134UL)
+#define CKM_DES3_MAC_GENERAL (0x135UL)
+#define CKM_DES3_CBC_PAD (0x136UL)
+#define CKM_CDMF_KEY_GEN (0x140UL)
+#define CKM_CDMF_ECB (0x141UL)
+#define CKM_CDMF_CBC (0x142UL)
+#define CKM_CDMF_MAC (0x143UL)
+#define CKM_CDMF_MAC_GENERAL (0x144UL)
+#define CKM_CDMF_CBC_PAD (0x145UL)
+#define CKM_MD2 (0x200UL)
+#define CKM_MD2_HMAC (0x201UL)
+#define CKM_MD2_HMAC_GENERAL (0x202UL)
+#define CKM_MD5 (0x210UL)
+#define CKM_MD5_HMAC (0x211UL)
+#define CKM_MD5_HMAC_GENERAL (0x212UL)
+#define CKM_SHA_1 (0x220UL)
+#define CKM_SHA_1_HMAC (0x221UL)
+#define CKM_SHA_1_HMAC_GENERAL (0x222UL)
+#define CKM_RIPEMD128 (0x230UL)
+#define CKM_RIPEMD128_HMAC (0x231UL)
+#define CKM_RIPEMD128_HMAC_GENERAL (0x232UL)
+#define CKM_RIPEMD160 (0x240UL)
+#define CKM_RIPEMD160_HMAC (0x241UL)
+#define CKM_RIPEMD160_HMAC_GENERAL (0x242UL)
+#define CKM_SHA256 (0x250UL)
+#define CKM_SHA256_HMAC (0x251UL)
+#define CKM_SHA256_HMAC_GENERAL (0x252UL)
+#define CKM_SHA224 (0x255UL)
+#define CKM_SHA224_HMAC (0x256UL)
+#define CKM_SHA224_HMAC_GENERAL (0x257UL)
+#define CKM_SHA384 (0x260UL)
+#define CKM_SHA384_HMAC (0x261UL)
+#define CKM_SHA384_HMAC_GENERAL (0x262UL)
+#define CKM_SHA512 (0x270UL)
+#define CKM_SHA512_HMAC (0x271UL)
+#define CKM_SHA512_HMAC_GENERAL (0x272UL)
+#define CKM_CAST_KEY_GEN (0x300UL)
+#define CKM_CAST_ECB (0x301UL)
+#define CKM_CAST_CBC (0x302UL)
+#define CKM_CAST_MAC (0x303UL)
+#define CKM_CAST_MAC_GENERAL (0x304UL)
+#define CKM_CAST_CBC_PAD (0x305UL)
+#define CKM_CAST3_KEY_GEN (0x310UL)
+#define CKM_CAST3_ECB (0x311UL)
+#define CKM_CAST3_CBC (0x312UL)
+#define CKM_CAST3_MAC (0x313UL)
+#define CKM_CAST3_MAC_GENERAL (0x314UL)
+#define CKM_CAST3_CBC_PAD (0x315UL)
+#define CKM_CAST5_KEY_GEN (0x320UL)
+#define CKM_CAST128_KEY_GEN (0x320UL)
+#define CKM_CAST5_ECB (0x321UL)
+#define CKM_CAST128_ECB (0x321UL)
+#define CKM_CAST5_CBC (0x322UL)
+#define CKM_CAST128_CBC (0x322UL)
+#define CKM_CAST5_MAC (0x323UL)
+#define CKM_CAST128_MAC (0x323UL)
+#define CKM_CAST5_MAC_GENERAL (0x324UL)
+#define CKM_CAST128_MAC_GENERAL (0x324UL)
+#define CKM_CAST5_CBC_PAD (0x325UL)
+#define CKM_CAST128_CBC_PAD (0x325UL)
+#define CKM_RC5_KEY_GEN (0x330UL)
+#define CKM_RC5_ECB (0x331UL)
+#define CKM_RC5_CBC (0x332UL)
+#define CKM_RC5_MAC (0x333UL)
+#define CKM_RC5_MAC_GENERAL (0x334UL)
+#define CKM_RC5_CBC_PAD (0x335UL)
+#define CKM_IDEA_KEY_GEN (0x340UL)
+#define CKM_IDEA_ECB (0x341UL)
+#define CKM_IDEA_CBC (0x342UL)
+#define CKM_IDEA_MAC (0x343UL)
+#define CKM_IDEA_MAC_GENERAL (0x344UL)
+#define CKM_IDEA_CBC_PAD (0x345UL)
+#define CKM_GENERIC_SECRET_KEY_GEN (0x350UL)
+#define CKM_CONCATENATE_BASE_AND_KEY (0x360UL)
+#define CKM_CONCATENATE_BASE_AND_DATA (0x362UL)
+#define CKM_CONCATENATE_DATA_AND_BASE (0x363UL)
+#define CKM_XOR_BASE_AND_DATA (0x364UL)
+#define CKM_EXTRACT_KEY_FROM_KEY (0x365UL)
+#define CKM_SSL3_PRE_MASTER_KEY_GEN (0x370UL)
+#define CKM_SSL3_MASTER_KEY_DERIVE (0x371UL)
+#define CKM_SSL3_KEY_AND_MAC_DERIVE (0x372UL)
+#define CKM_SSL3_MASTER_KEY_DERIVE_DH (0x373UL)
+#define CKM_TLS_PRE_MASTER_KEY_GEN (0x374UL)
+#define CKM_TLS_MASTER_KEY_DERIVE (0x375UL)
+#define CKM_TLS_KEY_AND_MAC_DERIVE (0x376UL)
+#define CKM_TLS_MASTER_KEY_DERIVE_DH (0x377UL)
+#define CKM_SSL3_MD5_MAC (0x380UL)
+#define CKM_SSL3_SHA1_MAC (0x381UL)
+#define CKM_MD5_KEY_DERIVATION (0x390UL)
+#define CKM_MD2_KEY_DERIVATION (0x391UL)
+#define CKM_SHA1_KEY_DERIVATION (0x392UL)
+#define CKM_PBE_MD2_DES_CBC (0x3a0UL)
+#define CKM_PBE_MD5_DES_CBC (0x3a1UL)
+#define CKM_PBE_MD5_CAST_CBC (0x3a2UL)
+#define CKM_PBE_MD5_CAST3_CBC (0x3a3UL)
+#define CKM_PBE_MD5_CAST5_CBC (0x3a4UL)
+#define CKM_PBE_MD5_CAST128_CBC (0x3a4UL)
+#define CKM_PBE_SHA1_CAST5_CBC (0x3a5UL)
+#define CKM_PBE_SHA1_CAST128_CBC (0x3a5UL)
+#define CKM_PBE_SHA1_RC4_128 (0x3a6UL)
+#define CKM_PBE_SHA1_RC4_40 (0x3a7UL)
+#define CKM_PBE_SHA1_DES3_EDE_CBC (0x3a8UL)
+#define CKM_PBE_SHA1_DES2_EDE_CBC (0x3a9UL)
+#define CKM_PBE_SHA1_RC2_128_CBC (0x3aaUL)
+#define CKM_PBE_SHA1_RC2_40_CBC (0x3abUL)
+#define CKM_PKCS5_PBKD2 (0x3b0UL)
+#define CKM_PBA_SHA1_WITH_SHA1_HMAC (0x3c0UL)
+#define CKM_KEY_WRAP_LYNKS (0x400UL)
+#define CKM_KEY_WRAP_SET_OAEP (0x401UL)
+#define CKM_SKIPJACK_KEY_GEN (0x1000UL)
+#define CKM_SKIPJACK_ECB64 (0x1001UL)
+#define CKM_SKIPJACK_CBC64 (0x1002UL)
+#define CKM_SKIPJACK_OFB64 (0x1003UL)
+#define CKM_SKIPJACK_CFB64 (0x1004UL)
+#define CKM_SKIPJACK_CFB32 (0x1005UL)
+#define CKM_SKIPJACK_CFB16 (0x1006UL)
+#define CKM_SKIPJACK_CFB8 (0x1007UL)
+#define CKM_SKIPJACK_WRAP (0x1008UL)
+#define CKM_SKIPJACK_PRIVATE_WRAP (0x1009UL)
+#define CKM_SKIPJACK_RELAYX (0x100aUL)
+#define CKM_KEA_KEY_PAIR_GEN (0x1010UL)
+#define CKM_KEA_KEY_DERIVE (0x1011UL)
+#define CKM_FORTEZZA_TIMESTAMP (0x1020UL)
+#define CKM_BATON_KEY_GEN (0x1030UL)
+#define CKM_BATON_ECB128 (0x1031UL)
+#define CKM_BATON_ECB96 (0x1032UL)
+#define CKM_BATON_CBC128 (0x1033UL)
+#define CKM_BATON_COUNTER (0x1034UL)
+#define CKM_BATON_SHUFFLE (0x1035UL)
+#define CKM_BATON_WRAP (0x1036UL)
+#define CKM_ECDSA_KEY_PAIR_GEN (0x1040UL)
+#define CKM_EC_KEY_PAIR_GEN (0x1040UL)
+#define CKM_ECDSA (0x1041UL)
+#define CKM_ECDSA_SHA1 (0x1042UL)
+#define CKM_ECDSA_SHA224 (0x1043UL)
+#define CKM_ECDSA_SHA256 (0x1044UL)
+#define CKM_ECDSA_SHA384 (0x1045UL)
+#define CKM_ECDSA_SHA512 (0x1046UL)
+#define CKM_ECDH1_DERIVE (0x1050UL)
+#define CKM_ECDH1_COFACTOR_DERIVE (0x1051UL)
+#define CKM_ECMQV_DERIVE (0x1052UL)
+#define CKM_JUNIPER_KEY_GEN (0x1060UL)
+#define CKM_JUNIPER_ECB128 (0x1061UL)
+#define CKM_JUNIPER_CBC128 (0x1062UL)
+#define CKM_JUNIPER_COUNTER (0x1063UL)
+#define CKM_JUNIPER_SHUFFLE (0x1064UL)
+#define CKM_JUNIPER_WRAP (0x1065UL)
+#define CKM_FASTHASH (0x1070UL)
+#define CKM_AES_KEY_GEN (0x1080UL)
+#define CKM_AES_ECB (0x1081UL)
+#define CKM_AES_CBC (0x1082UL)
+#define CKM_AES_MAC (0x1083UL)
+#define CKM_AES_MAC_GENERAL (0x1084UL)
+#define CKM_AES_CBC_PAD (0x1085UL)
+#define CKM_AES_CTR (0x1086UL)
+#define CKM_AES_GCM (0x1087UL)
+#define CKM_AES_CCM (0x1088UL)
+#define CKM_AES_CTS (0x1089UL)
+#define CKM_BLOWFISH_KEY_GEN (0x1090UL)
+#define CKM_BLOWFISH_CBC (0x1091UL)
+#define CKM_TWOFISH_KEY_GEN (0x1092UL)
+#define CKM_TWOFISH_CBC (0x1093UL)
+#define CKM_GOSTR3410_KEY_PAIR_GEN (0x1200UL)
+#define CKM_GOSTR3410 (0x1201UL)
+#define CKM_GOSTR3410_WITH_GOSTR3411 (0x1202UL)
+#define CKM_GOSTR3410_KEY_WRAP (0x1203UL)
+#define CKM_GOSTR3410_DERIVE (0x1204UL)
+#define CKM_GOSTR3411 (0x1210UL)
+#define CKM_GOSTR3411_HMAC (0x1211UL)
+#define CKM_GOST28147_KEY_GEN (0x1220UL)
+#define CKM_GOST28147_ECB (0x1221UL)
+#define CKM_GOST28147 (0x1222UL)
+#define CKM_GOST28147_MAC (0x1223UL)
+#define CKM_GOST28147_KEY_WRAP (0x1224UL)
+
+#define CKM_DSA_PARAMETER_GEN (0x2000UL)
+#define CKM_DH_PKCS_PARAMETER_GEN (0x2001UL)
+#define CKM_X9_42_DH_PARAMETER_GEN (0x2002UL)
+#define CKM_VENDOR_DEFINED (1UL << 31)
+
+
+struct ck_mechanism
+{
+ ck_mechanism_type_t mechanism;
+ void *parameter;
+ unsigned long parameter_len;
+};
+
+
+struct ck_mechanism_info
+{
+ unsigned long min_key_size;
+ unsigned long max_key_size;
+ ck_flags_t flags;
+};
+
+#define CKF_HW (1UL << 0)
+#define CKF_ENCRYPT (1UL << 8)
+#define CKF_DECRYPT (1UL << 9)
+#define CKF_DIGEST (1UL << 10)
+#define CKF_SIGN (1UL << 11)
+#define CKF_SIGN_RECOVER (1UL << 12)
+#define CKF_VERIFY (1UL << 13)
+#define CKF_VERIFY_RECOVER (1UL << 14)
+#define CKF_GENERATE (1UL << 15)
+#define CKF_GENERATE_KEY_PAIR (1UL << 16)
+#define CKF_WRAP (1UL << 17)
+#define CKF_UNWRAP (1UL << 18)
+#define CKF_DERIVE (1UL << 19)
+#define CKF_EXTENSION (1UL << 31)
+
+#define CKF_EC_F_P (1UL << 20)
+#define CKF_EC_F_2M (1UL << 21)
+#define CKF_EC_ECPARAMETERS (1UL << 22)
+#define CKF_EC_NAMEDCURVE (1UL << 23)
+#define CKF_EC_UNCOMPRESS (1UL << 24)
+#define CKF_EC_COMPRESS (1UL << 25)
+
+/* Flags for C_WaitForSlotEvent. */
+#define CKF_DONT_BLOCK (1UL)
+
+/* Flags for Key derivation */
+#define CKD_NULL (1UL << 0)
+
+typedef struct CK_ECDH1_DERIVE_PARAMS {
+ unsigned long kdf;
+ unsigned long ulSharedDataLen;
+ unsigned char * pSharedData;
+ unsigned long ulPublicDataLen;
+ unsigned char * pPublicData;
+} CK_ECDH1_DERIVE_PARAMS;
+
+typedef unsigned long ck_rsa_pkcs_mgf_type_t;
+typedef unsigned long CK_RSA_PKCS_OAEP_SOURCE_TYPE;
+
+typedef struct CK_RSA_PKCS_OAEP_PARAMS {
+ CK_MECHANISM_TYPE hashAlg;
+ CK_RSA_PKCS_MGF_TYPE mgf;
+ CK_RSA_PKCS_OAEP_SOURCE_TYPE source;
+ void *pSourceData;
+ unsigned long ulSourceDataLen;
+} CK_RSA_PKCS_OAEP_PARAMS;
+
+typedef struct CK_RSA_PKCS_PSS_PARAMS {
+ ck_mechanism_type_t hashAlg;
+ CK_RSA_PKCS_MGF_TYPE mgf;
+ unsigned long sLen;
+} CK_RSA_PKCS_PSS_PARAMS;
+
+#define CKG_MGF1_SHA1 (0x00000001UL)
+#define CKG_MGF1_SHA224 (0x00000005UL)
+#define CKG_MGF1_SHA256 (0x00000002UL)
+#define CKG_MGF1_SHA384 (0x00000003UL)
+#define CKG_MGF1_SHA512 (0x00000004UL)
+
+#define CKZ_DATA_SPECIFIED (0x00000001UL)
+
+typedef unsigned long ck_rv_t;
+
+
+typedef ck_rv_t (*ck_notify_t) (ck_session_handle_t session,
+ ck_notification_t event, void *application);
+
+/* Forward reference. */
+struct ck_function_list;
+
+#define _CK_DECLARE_FUNCTION(name, args) \
+typedef ck_rv_t (*CK_ ## name) args; \
+ck_rv_t CK_SPEC name args
+
+_CK_DECLARE_FUNCTION (C_Initialize, (void *init_args));
+_CK_DECLARE_FUNCTION (C_Finalize, (void *reserved));
+_CK_DECLARE_FUNCTION (C_GetInfo, (struct ck_info *info));
+_CK_DECLARE_FUNCTION (C_GetFunctionList,
+ (struct ck_function_list **function_list));
+
+_CK_DECLARE_FUNCTION (C_GetSlotList,
+ (unsigned char token_present, ck_slot_id_t *slot_list,
+ unsigned long *count));
+_CK_DECLARE_FUNCTION (C_GetSlotInfo,
+ (ck_slot_id_t slot_id, struct ck_slot_info *info));
+_CK_DECLARE_FUNCTION (C_GetTokenInfo,
+ (ck_slot_id_t slot_id, struct ck_token_info *info));
+_CK_DECLARE_FUNCTION (C_WaitForSlotEvent,
+ (ck_flags_t flags, ck_slot_id_t *slot, void *reserved));
+_CK_DECLARE_FUNCTION (C_GetMechanismList,
+ (ck_slot_id_t slot_id,
+ ck_mechanism_type_t *mechanism_list,
+ unsigned long *count));
+_CK_DECLARE_FUNCTION (C_GetMechanismInfo,
+ (ck_slot_id_t slot_id, ck_mechanism_type_t type,
+ struct ck_mechanism_info *info));
+_CK_DECLARE_FUNCTION (C_InitToken,
+ (ck_slot_id_t slot_id, unsigned char *pin,
+ unsigned long pin_len, unsigned char *label));
+_CK_DECLARE_FUNCTION (C_InitPIN,
+ (ck_session_handle_t session, unsigned char *pin,
+ unsigned long pin_len));
+_CK_DECLARE_FUNCTION (C_SetPIN,
+ (ck_session_handle_t session, unsigned char *old_pin,
+ unsigned long old_len, unsigned char *new_pin,
+ unsigned long new_len));
+
+_CK_DECLARE_FUNCTION (C_OpenSession,
+ (ck_slot_id_t slot_id, ck_flags_t flags,
+ void *application, ck_notify_t notify,
+ ck_session_handle_t *session));
+_CK_DECLARE_FUNCTION (C_CloseSession, (ck_session_handle_t session));
+_CK_DECLARE_FUNCTION (C_CloseAllSessions, (ck_slot_id_t slot_id));
+_CK_DECLARE_FUNCTION (C_GetSessionInfo,
+ (ck_session_handle_t session,
+ struct ck_session_info *info));
+_CK_DECLARE_FUNCTION (C_GetOperationState,
+ (ck_session_handle_t session,
+ unsigned char *operation_state,
+ unsigned long *operation_state_len));
+_CK_DECLARE_FUNCTION (C_SetOperationState,
+ (ck_session_handle_t session,
+ unsigned char *operation_state,
+ unsigned long operation_state_len,
+ ck_object_handle_t encryption_key,
+ ck_object_handle_t authentication_key));
+_CK_DECLARE_FUNCTION (C_Login,
+ (ck_session_handle_t session, ck_user_type_t user_type,
+ unsigned char *pin, unsigned long pin_len));
+_CK_DECLARE_FUNCTION (C_Logout, (ck_session_handle_t session));
+
+_CK_DECLARE_FUNCTION (C_CreateObject,
+ (ck_session_handle_t session,
+ struct ck_attribute *templ,
+ unsigned long count, ck_object_handle_t *object));
+_CK_DECLARE_FUNCTION (C_CopyObject,
+ (ck_session_handle_t session, ck_object_handle_t object,
+ struct ck_attribute *templ, unsigned long count,
+ ck_object_handle_t *new_object));
+_CK_DECLARE_FUNCTION (C_DestroyObject,
+ (ck_session_handle_t session,
+ ck_object_handle_t object));
+_CK_DECLARE_FUNCTION (C_GetObjectSize,
+ (ck_session_handle_t session,
+ ck_object_handle_t object,
+ unsigned long *size));
+_CK_DECLARE_FUNCTION (C_GetAttributeValue,
+ (ck_session_handle_t session,
+ ck_object_handle_t object,
+ struct ck_attribute *templ,
+ unsigned long count));
+_CK_DECLARE_FUNCTION (C_SetAttributeValue,
+ (ck_session_handle_t session,
+ ck_object_handle_t object,
+ struct ck_attribute *templ,
+ unsigned long count));
+_CK_DECLARE_FUNCTION (C_FindObjectsInit,
+ (ck_session_handle_t session,
+ struct ck_attribute *templ,
+ unsigned long count));
+_CK_DECLARE_FUNCTION (C_FindObjects,
+ (ck_session_handle_t session,
+ ck_object_handle_t *object,
+ unsigned long max_object_count,
+ unsigned long *object_count));
+_CK_DECLARE_FUNCTION (C_FindObjectsFinal,
+ (ck_session_handle_t session));
+
+_CK_DECLARE_FUNCTION (C_EncryptInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_Encrypt,
+ (ck_session_handle_t session,
+ unsigned char *data, unsigned long data_len,
+ unsigned char *encrypted_data,
+ unsigned long *encrypted_data_len));
+_CK_DECLARE_FUNCTION (C_EncryptUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len,
+ unsigned char *encrypted_part,
+ unsigned long *encrypted_part_len));
+_CK_DECLARE_FUNCTION (C_EncryptFinal,
+ (ck_session_handle_t session,
+ unsigned char *last_encrypted_part,
+ unsigned long *last_encrypted_part_len));
+
+_CK_DECLARE_FUNCTION (C_DecryptInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_Decrypt,
+ (ck_session_handle_t session,
+ unsigned char *encrypted_data,
+ unsigned long encrypted_data_len,
+ unsigned char *data, unsigned long *data_len));
+_CK_DECLARE_FUNCTION (C_DecryptUpdate,
+ (ck_session_handle_t session,
+ unsigned char *encrypted_part,
+ unsigned long encrypted_part_len,
+ unsigned char *part, unsigned long *part_len));
+_CK_DECLARE_FUNCTION (C_DecryptFinal,
+ (ck_session_handle_t session,
+ unsigned char *last_part,
+ unsigned long *last_part_len));
+
+_CK_DECLARE_FUNCTION (C_DigestInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism));
+_CK_DECLARE_FUNCTION (C_Digest,
+ (ck_session_handle_t session,
+ unsigned char *data, unsigned long data_len,
+ unsigned char *digest,
+ unsigned long *digest_len));
+_CK_DECLARE_FUNCTION (C_DigestUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len));
+_CK_DECLARE_FUNCTION (C_DigestKey,
+ (ck_session_handle_t session, ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_DigestFinal,
+ (ck_session_handle_t session,
+ unsigned char *digest,
+ unsigned long *digest_len));
+
+_CK_DECLARE_FUNCTION (C_SignInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_Sign,
+ (ck_session_handle_t session,
+ unsigned char *data, unsigned long data_len,
+ unsigned char *signature,
+ unsigned long *signature_len));
+_CK_DECLARE_FUNCTION (C_SignUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len));
+_CK_DECLARE_FUNCTION (C_SignFinal,
+ (ck_session_handle_t session,
+ unsigned char *signature,
+ unsigned long *signature_len));
+_CK_DECLARE_FUNCTION (C_SignRecoverInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_SignRecover,
+ (ck_session_handle_t session,
+ unsigned char *data, unsigned long data_len,
+ unsigned char *signature,
+ unsigned long *signature_len));
+
+_CK_DECLARE_FUNCTION (C_VerifyInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_Verify,
+ (ck_session_handle_t session,
+ unsigned char *data, unsigned long data_len,
+ unsigned char *signature,
+ unsigned long signature_len));
+_CK_DECLARE_FUNCTION (C_VerifyUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len));
+_CK_DECLARE_FUNCTION (C_VerifyFinal,
+ (ck_session_handle_t session,
+ unsigned char *signature,
+ unsigned long signature_len));
+_CK_DECLARE_FUNCTION (C_VerifyRecoverInit,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t key));
+_CK_DECLARE_FUNCTION (C_VerifyRecover,
+ (ck_session_handle_t session,
+ unsigned char *signature,
+ unsigned long signature_len,
+ unsigned char *data,
+ unsigned long *data_len));
+
+_CK_DECLARE_FUNCTION (C_DigestEncryptUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len,
+ unsigned char *encrypted_part,
+ unsigned long *encrypted_part_len));
+_CK_DECLARE_FUNCTION (C_DecryptDigestUpdate,
+ (ck_session_handle_t session,
+ unsigned char *encrypted_part,
+ unsigned long encrypted_part_len,
+ unsigned char *part,
+ unsigned long *part_len));
+_CK_DECLARE_FUNCTION (C_SignEncryptUpdate,
+ (ck_session_handle_t session,
+ unsigned char *part, unsigned long part_len,
+ unsigned char *encrypted_part,
+ unsigned long *encrypted_part_len));
+_CK_DECLARE_FUNCTION (C_DecryptVerifyUpdate,
+ (ck_session_handle_t session,
+ unsigned char *encrypted_part,
+ unsigned long encrypted_part_len,
+ unsigned char *part,
+ unsigned long *part_len));
+
+_CK_DECLARE_FUNCTION (C_GenerateKey,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ struct ck_attribute *templ,
+ unsigned long count,
+ ck_object_handle_t *key));
+_CK_DECLARE_FUNCTION (C_GenerateKeyPair,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ struct ck_attribute *public_key_template,
+ unsigned long public_key_attribute_count,
+ struct ck_attribute *private_key_template,
+ unsigned long private_key_attribute_count,
+ ck_object_handle_t *public_key,
+ ck_object_handle_t *private_key));
+_CK_DECLARE_FUNCTION (C_WrapKey,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t wrapping_key,
+ ck_object_handle_t key,
+ unsigned char *wrapped_key,
+ unsigned long *wrapped_key_len));
+_CK_DECLARE_FUNCTION (C_UnwrapKey,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t unwrapping_key,
+ unsigned char *wrapped_key,
+ unsigned long wrapped_key_len,
+ struct ck_attribute *templ,
+ unsigned long attribute_count,
+ ck_object_handle_t *key));
+_CK_DECLARE_FUNCTION (C_DeriveKey,
+ (ck_session_handle_t session,
+ struct ck_mechanism *mechanism,
+ ck_object_handle_t base_key,
+ struct ck_attribute *templ,
+ unsigned long attribute_count,
+ ck_object_handle_t *key));
+
+_CK_DECLARE_FUNCTION (C_SeedRandom,
+ (ck_session_handle_t session, unsigned char *seed,
+ unsigned long seed_len));
+_CK_DECLARE_FUNCTION (C_GenerateRandom,
+ (ck_session_handle_t session,
+ unsigned char *random_data,
+ unsigned long random_len));
+
+_CK_DECLARE_FUNCTION (C_GetFunctionStatus, (ck_session_handle_t session));
+_CK_DECLARE_FUNCTION (C_CancelFunction, (ck_session_handle_t session));
+
+
+struct ck_function_list
+{
+ struct ck_version version;
+ CK_C_Initialize C_Initialize;
+ CK_C_Finalize C_Finalize;
+ CK_C_GetInfo C_GetInfo;
+ CK_C_GetFunctionList C_GetFunctionList;
+ CK_C_GetSlotList C_GetSlotList;
+ CK_C_GetSlotInfo C_GetSlotInfo;
+ CK_C_GetTokenInfo C_GetTokenInfo;
+ CK_C_GetMechanismList C_GetMechanismList;
+ CK_C_GetMechanismInfo C_GetMechanismInfo;
+ CK_C_InitToken C_InitToken;
+ CK_C_InitPIN C_InitPIN;
+ CK_C_SetPIN C_SetPIN;
+ CK_C_OpenSession C_OpenSession;
+ CK_C_CloseSession C_CloseSession;
+ CK_C_CloseAllSessions C_CloseAllSessions;
+ CK_C_GetSessionInfo C_GetSessionInfo;
+ CK_C_GetOperationState C_GetOperationState;
+ CK_C_SetOperationState C_SetOperationState;
+ CK_C_Login C_Login;
+ CK_C_Logout C_Logout;
+ CK_C_CreateObject C_CreateObject;
+ CK_C_CopyObject C_CopyObject;
+ CK_C_DestroyObject C_DestroyObject;
+ CK_C_GetObjectSize C_GetObjectSize;
+ CK_C_GetAttributeValue C_GetAttributeValue;
+ CK_C_SetAttributeValue C_SetAttributeValue;
+ CK_C_FindObjectsInit C_FindObjectsInit;
+ CK_C_FindObjects C_FindObjects;
+ CK_C_FindObjectsFinal C_FindObjectsFinal;
+ CK_C_EncryptInit C_EncryptInit;
+ CK_C_Encrypt C_Encrypt;
+ CK_C_EncryptUpdate C_EncryptUpdate;
+ CK_C_EncryptFinal C_EncryptFinal;
+ CK_C_DecryptInit C_DecryptInit;
+ CK_C_Decrypt C_Decrypt;
+ CK_C_DecryptUpdate C_DecryptUpdate;
+ CK_C_DecryptFinal C_DecryptFinal;
+ CK_C_DigestInit C_DigestInit;
+ CK_C_Digest C_Digest;
+ CK_C_DigestUpdate C_DigestUpdate;
+ CK_C_DigestKey C_DigestKey;
+ CK_C_DigestFinal C_DigestFinal;
+ CK_C_SignInit C_SignInit;
+ CK_C_Sign C_Sign;
+ CK_C_SignUpdate C_SignUpdate;
+ CK_C_SignFinal C_SignFinal;
+ CK_C_SignRecoverInit C_SignRecoverInit;
+ CK_C_SignRecover C_SignRecover;
+ CK_C_VerifyInit C_VerifyInit;
+ CK_C_Verify C_Verify;
+ CK_C_VerifyUpdate C_VerifyUpdate;
+ CK_C_VerifyFinal C_VerifyFinal;
+ CK_C_VerifyRecoverInit C_VerifyRecoverInit;
+ CK_C_VerifyRecover C_VerifyRecover;
+ CK_C_DigestEncryptUpdate C_DigestEncryptUpdate;
+ CK_C_DecryptDigestUpdate C_DecryptDigestUpdate;
+ CK_C_SignEncryptUpdate C_SignEncryptUpdate;
+ CK_C_DecryptVerifyUpdate C_DecryptVerifyUpdate;
+ CK_C_GenerateKey C_GenerateKey;
+ CK_C_GenerateKeyPair C_GenerateKeyPair;
+ CK_C_WrapKey C_WrapKey;
+ CK_C_UnwrapKey C_UnwrapKey;
+ CK_C_DeriveKey C_DeriveKey;
+ CK_C_SeedRandom C_SeedRandom;
+ CK_C_GenerateRandom C_GenerateRandom;
+ CK_C_GetFunctionStatus C_GetFunctionStatus;
+ CK_C_CancelFunction C_CancelFunction;
+ CK_C_WaitForSlotEvent C_WaitForSlotEvent;
+};
+
+
+typedef ck_rv_t (*ck_createmutex_t) (void **mutex);
+typedef ck_rv_t (*ck_destroymutex_t) (void *mutex);
+typedef ck_rv_t (*ck_lockmutex_t) (void *mutex);
+typedef ck_rv_t (*ck_unlockmutex_t) (void *mutex);
+
+
+struct ck_c_initialize_args
+{
+ ck_createmutex_t create_mutex;
+ ck_destroymutex_t destroy_mutex;
+ ck_lockmutex_t lock_mutex;
+ ck_unlockmutex_t unlock_mutex;
+ ck_flags_t flags;
+ void *reserved;
+};
+
+
+#define CKF_LIBRARY_CANT_CREATE_OS_THREADS (1UL << 0)
+#define CKF_OS_LOCKING_OK (1UL << 1)
+
+#define CKR_OK (0UL)
+#define CKR_CANCEL (1UL)
+#define CKR_HOST_MEMORY (2UL)
+#define CKR_SLOT_ID_INVALID (3UL)
+#define CKR_GENERAL_ERROR (5UL)
+#define CKR_FUNCTION_FAILED (6UL)
+#define CKR_ARGUMENTS_BAD (7UL)
+#define CKR_NO_EVENT (8UL)
+#define CKR_NEED_TO_CREATE_THREADS (9UL)
+#define CKR_CANT_LOCK (0xaUL)
+#define CKR_ATTRIBUTE_READ_ONLY (0x10UL)
+#define CKR_ATTRIBUTE_SENSITIVE (0x11UL)
+#define CKR_ATTRIBUTE_TYPE_INVALID (0x12UL)
+#define CKR_ATTRIBUTE_VALUE_INVALID (0x13UL)
+#define CKR_DATA_INVALID (0x20UL)
+#define CKR_DATA_LEN_RANGE (0x21UL)
+#define CKR_DEVICE_ERROR (0x30UL)
+#define CKR_DEVICE_MEMORY (0x31UL)
+#define CKR_DEVICE_REMOVED (0x32UL)
+#define CKR_ENCRYPTED_DATA_INVALID (0x40UL)
+#define CKR_ENCRYPTED_DATA_LEN_RANGE (0x41UL)
+#define CKR_FUNCTION_CANCELED (0x50UL)
+#define CKR_FUNCTION_NOT_PARALLEL (0x51UL)
+#define CKR_FUNCTION_NOT_SUPPORTED (0x54UL)
+#define CKR_KEY_HANDLE_INVALID (0x60UL)
+#define CKR_KEY_SIZE_RANGE (0x62UL)
+#define CKR_KEY_TYPE_INCONSISTENT (0x63UL)
+#define CKR_KEY_NOT_NEEDED (0x64UL)
+#define CKR_KEY_CHANGED (0x65UL)
+#define CKR_KEY_NEEDED (0x66UL)
+#define CKR_KEY_INDIGESTIBLE (0x67UL)
+#define CKR_KEY_FUNCTION_NOT_PERMITTED (0x68UL)
+#define CKR_KEY_NOT_WRAPPABLE (0x69UL)
+#define CKR_KEY_UNEXTRACTABLE (0x6aUL)
+#define CKR_MECHANISM_INVALID (0x70UL)
+#define CKR_MECHANISM_PARAM_INVALID (0x71UL)
+#define CKR_OBJECT_HANDLE_INVALID (0x82UL)
+#define CKR_OPERATION_ACTIVE (0x90UL)
+#define CKR_OPERATION_NOT_INITIALIZED (0x91UL)
+#define CKR_PIN_INCORRECT (0xa0UL)
+#define CKR_PIN_INVALID (0xa1UL)
+#define CKR_PIN_LEN_RANGE (0xa2UL)
+#define CKR_PIN_EXPIRED (0xa3UL)
+#define CKR_PIN_LOCKED (0xa4UL)
+#define CKR_SESSION_CLOSED (0xb0UL)
+#define CKR_SESSION_COUNT (0xb1UL)
+#define CKR_SESSION_HANDLE_INVALID (0xb3UL)
+#define CKR_SESSION_PARALLEL_NOT_SUPPORTED (0xb4UL)
+#define CKR_SESSION_READ_ONLY (0xb5UL)
+#define CKR_SESSION_EXISTS (0xb6UL)
+#define CKR_SESSION_READ_ONLY_EXISTS (0xb7UL)
+#define CKR_SESSION_READ_WRITE_SO_EXISTS (0xb8UL)
+#define CKR_SIGNATURE_INVALID (0xc0UL)
+#define CKR_SIGNATURE_LEN_RANGE (0xc1UL)
+#define CKR_TEMPLATE_INCOMPLETE (0xd0UL)
+#define CKR_TEMPLATE_INCONSISTENT (0xd1UL)
+#define CKR_TOKEN_NOT_PRESENT (0xe0UL)
+#define CKR_TOKEN_NOT_RECOGNIZED (0xe1UL)
+#define CKR_TOKEN_WRITE_PROTECTED (0xe2UL)
+#define CKR_UNWRAPPING_KEY_HANDLE_INVALID (0xf0UL)
+#define CKR_UNWRAPPING_KEY_SIZE_RANGE (0xf1UL)
+#define CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT (0xf2UL)
+#define CKR_USER_ALREADY_LOGGED_IN (0x100UL)
+#define CKR_USER_NOT_LOGGED_IN (0x101UL)
+#define CKR_USER_PIN_NOT_INITIALIZED (0x102UL)
+#define CKR_USER_TYPE_INVALID (0x103UL)
+#define CKR_USER_ANOTHER_ALREADY_LOGGED_IN (0x104UL)
+#define CKR_USER_TOO_MANY_TYPES (0x105UL)
+#define CKR_WRAPPED_KEY_INVALID (0x110UL)
+#define CKR_WRAPPED_KEY_LEN_RANGE (0x112UL)
+#define CKR_WRAPPING_KEY_HANDLE_INVALID (0x113UL)
+#define CKR_WRAPPING_KEY_SIZE_RANGE (0x114UL)
+#define CKR_WRAPPING_KEY_TYPE_INCONSISTENT (0x115UL)
+#define CKR_RANDOM_SEED_NOT_SUPPORTED (0x120UL)
+#define CKR_RANDOM_NO_RNG (0x121UL)
+#define CKR_DOMAIN_PARAMS_INVALID (0x130UL)
+#define CKR_BUFFER_TOO_SMALL (0x150UL)
+#define CKR_SAVED_STATE_INVALID (0x160UL)
+#define CKR_INFORMATION_SENSITIVE (0x170UL)
+#define CKR_STATE_UNSAVEABLE (0x180UL)
+#define CKR_CRYPTOKI_NOT_INITIALIZED (0x190UL)
+#define CKR_CRYPTOKI_ALREADY_INITIALIZED (0x191UL)
+#define CKR_MUTEX_BAD (0x1a0UL)
+#define CKR_MUTEX_NOT_LOCKED (0x1a1UL)
+#define CKR_FUNCTION_REJECTED (0x200UL)
+#define CKR_VENDOR_DEFINED (1UL << 31)
+
+
+/* Compatibility layer. */
+
+#ifdef CRYPTOKI_COMPAT
+
+#undef CK_DEFINE_FUNCTION
+#define CK_DEFINE_FUNCTION(retval, name) retval CK_SPEC name
+
+/* For NULL. */
+#include <stddef.h>
+
+typedef unsigned char CK_BYTE;
+typedef unsigned char CK_CHAR;
+typedef unsigned char CK_UTF8CHAR;
+typedef unsigned char CK_BBOOL;
+typedef unsigned long int CK_ULONG;
+typedef long int CK_LONG;
+typedef CK_BYTE *CK_BYTE_PTR;
+typedef CK_CHAR *CK_CHAR_PTR;
+typedef CK_UTF8CHAR *CK_UTF8CHAR_PTR;
+typedef CK_ULONG *CK_ULONG_PTR;
+typedef void *CK_VOID_PTR;
+typedef void **CK_VOID_PTR_PTR;
+#define CK_FALSE 0
+#define CK_TRUE 1
+#ifndef CK_DISABLE_TRUE_FALSE
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+#endif
+
+typedef struct ck_version CK_VERSION;
+typedef struct ck_version *CK_VERSION_PTR;
+
+typedef struct ck_info CK_INFO;
+typedef struct ck_info *CK_INFO_PTR;
+
+typedef ck_slot_id_t *CK_SLOT_ID_PTR;
+
+typedef struct ck_slot_info CK_SLOT_INFO;
+typedef struct ck_slot_info *CK_SLOT_INFO_PTR;
+
+typedef struct ck_token_info CK_TOKEN_INFO;
+typedef struct ck_token_info *CK_TOKEN_INFO_PTR;
+
+typedef ck_session_handle_t *CK_SESSION_HANDLE_PTR;
+
+typedef struct ck_session_info CK_SESSION_INFO;
+typedef struct ck_session_info *CK_SESSION_INFO_PTR;
+
+typedef ck_object_handle_t *CK_OBJECT_HANDLE_PTR;
+
+typedef ck_object_class_t *CK_OBJECT_CLASS_PTR;
+
+typedef struct ck_attribute CK_ATTRIBUTE;
+typedef struct ck_attribute *CK_ATTRIBUTE_PTR;
+
+typedef struct ck_date CK_DATE;
+typedef struct ck_date *CK_DATE_PTR;
+
+typedef ck_mechanism_type_t *CK_MECHANISM_TYPE_PTR;
+
+typedef ck_rsa_pkcs_mgf_type_t *CK_RSA_PKCS_MGF_TYPE_PTR;
+
+typedef struct ck_mechanism CK_MECHANISM;
+typedef struct ck_mechanism *CK_MECHANISM_PTR;
+
+typedef struct ck_mechanism_info CK_MECHANISM_INFO;
+typedef struct ck_mechanism_info *CK_MECHANISM_INFO_PTR;
+
+typedef struct ck_function_list CK_FUNCTION_LIST;
+typedef struct ck_function_list *CK_FUNCTION_LIST_PTR;
+typedef struct ck_function_list **CK_FUNCTION_LIST_PTR_PTR;
+
+typedef struct ck_c_initialize_args CK_C_INITIALIZE_ARGS;
+typedef struct ck_c_initialize_args *CK_C_INITIALIZE_ARGS_PTR;
+
+#define NULL_PTR NULL
+
+/* Delete the helper macros defined at the top of the file. */
+#undef ck_flags_t
+#undef ck_version
+
+#undef ck_info
+#undef cryptoki_version
+#undef manufacturer_id
+#undef library_description
+#undef library_version
+
+#undef ck_notification_t
+#undef ck_slot_id_t
+
+#undef ck_slot_info
+#undef slot_description
+#undef hardware_version
+#undef firmware_version
+
+#undef ck_token_info
+#undef serial_number
+#undef max_session_count
+#undef session_count
+#undef max_rw_session_count
+#undef rw_session_count
+#undef max_pin_len
+#undef min_pin_len
+#undef total_public_memory
+#undef free_public_memory
+#undef total_private_memory
+#undef free_private_memory
+#undef utc_time
+
+#undef ck_session_handle_t
+#undef ck_user_type_t
+#undef ck_state_t
+
+#undef ck_session_info
+#undef slot_id
+#undef device_error
+
+#undef ck_object_handle_t
+#undef ck_object_class_t
+#undef ck_hw_feature_type_t
+#undef ck_key_type_t
+#undef ck_certificate_type_t
+#undef ck_attribute_type_t
+
+#undef ck_attribute
+#undef value
+#undef value_len
+
+#undef ck_date
+
+#undef ck_mechanism_type_t
+
+#undef ck_rsa_pkcs_mgf_type_t
+
+#undef ck_mechanism
+#undef parameter
+#undef parameter_len
+
+#undef ck_mechanism_info
+#undef min_key_size
+#undef max_key_size
+
+#undef ck_rv_t
+#undef ck_notify_t
+
+#undef ck_function_list
+
+#undef ck_createmutex_t
+#undef ck_destroymutex_t
+#undef ck_lockmutex_t
+#undef ck_unlockmutex_t
+
+#undef ck_c_initialize_args
+#undef create_mutex
+#undef destroy_mutex
+#undef lock_mutex
+#undef unlock_mutex
+#undef reserved
+
+#endif /* CRYPTOKI_COMPAT */
+
+/* System dependencies. */
+#if defined(_WIN32) || defined(CRYPTOKI_FORCE_WIN32)
+#pragma pack(pop, cryptoki)
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* PKCS11_H */
diff --git a/openssl/agent/pkcs11/pkcs11.hxx b/openssl/agent/pkcs11/pkcs11.hxx
new file mode 100644
index 0000000..e1c90b1
--- /dev/null
+++ b/openssl/agent/pkcs11/pkcs11.hxx
@@ -0,0 +1,63 @@
+// file : openssl/agent/pkcs11/pkcs11.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef OPENSSL_AGENT_PKCS11_PKCS11_HXX
+#define OPENSSL_AGENT_PKCS11_PKCS11_HXX
+
+// PKCS#11 API (Cryptoki) definitions.
+//
+#include <openssl/agent/pkcs11/pkcs11.h>
+
+#include <openssl/types.hxx>
+#include <openssl/utility.hxx>
+
+namespace openssl
+{
+ namespace agent
+ {
+ namespace pkcs11
+ {
+ // For simplicity we will not handle multiple PKCS#11 modules
+ // simultaneously. The first one loaded will stay till the end of the
+ // process lifetime.
+ //
+
+ // Return the PKCS#11 API pointer. If requested, ignore non-existent
+ // module returning NULL.
+ //
+ // On the first call load the PKCS#11 module using the specified path
+ // and initialize the API. Return the same pointer on the subsequent
+ // calls regardless of the path. Throw runtime_error if anything goes
+ // wrong.
+ //
+ CK_FUNCTION_LIST*
+ api (const path&, bool ignore_nonexistent = false);
+
+ // Return a pointer to the previously initialized PKCS#11 API.
+ //
+ CK_FUNCTION_LIST*
+ api ();
+
+ // Throw runtime_error describing a PKCS#11 API error.
+ //
+ [[noreturn]] void
+ throw_api_error (CK_RV error, string what);
+
+ // Convert API string representation to a regular one.
+ //
+ // PKCS#11 API struct string members are fixed-sized unsigned character
+ // arrays right-padded with the space character. Return such a string
+ // with the trailing spaces stripped.
+ //
+ inline string
+ api_string (const unsigned char* s, size_t n)
+ {
+ for (; n != 0 && s[n - 1] == ' '; --n) ;
+ return string (reinterpret_cast<const char*> (s), n);
+ }
+ }
+ }
+}
+
+#endif // OPENSSL_AGENT_PKCS11_PKCS11_HXX
diff --git a/openssl/agent/pkcs11/private-key.cxx b/openssl/agent/pkcs11/private-key.cxx
new file mode 100644
index 0000000..e85d815
--- /dev/null
+++ b/openssl/agent/pkcs11/private-key.cxx
@@ -0,0 +1,492 @@
+// file : openssl/agent/pkcs11/private-key.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <openssl/agent/pkcs11/private-key.hxx>
+
+#include <cstring> // memset(), strlen()
+
+#include <openssl/agent/pkcs11/pkcs11.hxx>
+
+#define API_STRING(S) api_string (S, sizeof (S))
+
+namespace openssl
+{
+ namespace agent
+ {
+ namespace pkcs11
+ {
+ using namespace std;
+
+ static void
+ close_session (CK_SESSION_HANDLE* p)
+ {
+ if (p != nullptr)
+ api ()->C_CloseSession (*p);
+ }
+
+ private_key::
+ private_key (): session_ (nullptr, close_session)
+ {
+ }
+
+ private_key::
+ private_key (private_key&& k): private_key ()
+ {
+ *this = move (k);
+ }
+
+ private_key& private_key::
+ operator= (private_key&& k)
+ {
+ description = move (k.description);
+ session_ = move (k.session_);
+ handle_ = k.handle_;
+ simulate_ = move (k.simulate_);
+ return *this;
+ }
+
+ private_key::
+ private_key (const identity& idn,
+ const access& acc,
+ const char* pin,
+ optional<simulate_outcome> sim)
+ : private_key ()
+ {
+ simulate_ = move (sim); // Can't initialize because of delegate ctor.
+
+ // Verify the identity and access attributes.
+ //
+ if (idn.type && *idn.type != "private")
+ throw invalid_argument ("unexpected object type " + *idn.type);
+
+ if (pin == nullptr && acc.pin_value)
+ pin = acc.pin_value->c_str ();
+
+ if (pin == nullptr)
+ throw invalid_argument ("PIN is required");
+
+ auto no_key = [] ()
+ {
+ throw runtime_error ("no matching private key found");
+ };
+
+ auto simulate = [this, &no_key] ()
+ {
+ assert (simulate_ && session_ == nullptr);
+
+ switch (*simulate_)
+ {
+ case simulate_outcome::success:
+ {
+ description = "simulated private key";
+ return;
+ }
+ case simulate_outcome::failure:
+ {
+ no_key ();
+ }
+ }
+ };
+
+ // Get the Cryptoki functions pointer.
+ //
+ CK_FUNCTION_LIST* fs;
+ {
+ path p;
+
+ if (acc.module_path)
+ {
+ if (acc.module_name)
+ p = *acc.module_path / (*acc.module_name + ".so");
+ else if (file_exists (*acc.module_path))
+ p = *acc.module_path;
+ else
+ p = *acc.module_path / "opensc-pkcs11.so";
+ }
+ else if (acc.module_name)
+ p = *acc.module_name + ".so";
+ else
+ p = path ("opensc-pkcs11.so");
+
+ // Ignore non-existent PKCS#11 module in the simulated mode. Note
+ // that if the module exists we will simulate as far as possible,
+ // up to opening token sessions (but not logging into them).
+ //
+ fs = api (p, simulate_.has_value ());
+
+ if (fs == nullptr)
+ {
+ assert (simulate_);
+ simulate ();
+ return;
+ }
+ }
+
+ // Search for the private key.
+ //
+ // The overall plan is to match the PKCS#11 module, go though all the
+ // matching slots it provides, log into all the matching tokens these
+ // slots may contain and see if the tokens contain a matching RSA
+ // private keys. Fail if unable to log into the matching tokens or
+ // none or multiple private keys match. Otherwise keep both the
+ // matching token session and the key handle.
+ //
+ auto mult_keys = [] ()
+ {
+ throw runtime_error ("multiple private keys match");
+ };
+
+ auto match = [] (const auto& attr, const auto& value)
+ {
+ return !attr || *attr == value;
+ };
+
+ // Match the module.
+ //
+ CK_INFO pi;
+ CK_RV r (fs->C_GetInfo (&pi));
+
+ if (r != CKR_OK)
+ throw_api_error (r, "unable to obtain PKCS#11 module info");
+
+ if (!match (idn.library_manufacturer,
+ API_STRING (pi.manufacturerID)) ||
+
+ !match (idn.library_version,
+ library_version (pi.libraryVersion.major,
+ pi.libraryVersion.minor)) ||
+
+ !match (idn.library_description,
+ API_STRING (pi.libraryDescription)))
+ no_key ();
+
+ // Traverse through slots.
+ //
+ // In the PKCS#11 terminology a slot is something that may have token
+ // be inserted into it. Not to be confused with Yubikey's 9a, 9c, etc
+ // slots.
+ //
+ // Note that a set of slots is checked at the time when
+ // C_GetSlotList() is called to obtain slots count (pSlotList argument
+ // is NULL and the tokenPresent argument is false).
+ //
+ CK_ULONG nslots;
+ r = fs->C_GetSlotList (false /* tokenPresent */,
+ nullptr /* pSlotList */,
+ &nslots);
+
+ if (r != CKR_OK)
+ throw_api_error (r, "unable to obtain slots count");
+
+ vector<CK_SLOT_ID> slot_ids (nslots);
+ r = fs->C_GetSlotList (false /* tokenPresent */,
+ slot_ids.data (),
+ &nslots);
+
+ if (r != CKR_OK)
+ throw_api_error (r, "unable to obtain slot ids");
+
+ for (CK_SLOT_ID sid: slot_ids)
+ {
+ // Match the slot information.
+ //
+ if (!match (idn.slot_id, sid))
+ continue;
+
+ CK_SLOT_INFO si;
+ r = fs->C_GetSlotInfo (sid, &si);
+
+ if (r != CKR_OK)
+ throw_api_error (
+ r, "unable to obtain slot " + to_string (sid) + " info");
+
+ if ((si.flags & CKF_TOKEN_PRESENT) == 0 ||
+ !match (idn.slot_manufacturer, API_STRING (si.manufacturerID)) ||
+ !match (idn.slot_description, API_STRING (si.slotDescription)))
+ continue;
+
+ auto slot_desc = [&sid, &si] ()
+ {
+ string d (API_STRING (si.slotDescription));
+ return "slot " + to_string (sid) + " (" +
+ (!d.empty () ? d : API_STRING (si.manufacturerID)) + ")";
+ };
+
+ // Match the token information.
+ //
+ CK_TOKEN_INFO ti;
+ r = fs->C_GetTokenInfo (sid, &ti);
+
+ if (r == CKR_TOKEN_NOT_PRESENT || r == CKR_TOKEN_NOT_RECOGNIZED)
+ continue;
+
+ if (r != CKR_OK)
+ throw_api_error (
+ r, "unable to obtain token info for " + slot_desc ());
+
+ if ((ti.flags & CKF_TOKEN_INITIALIZED) == 0 ||
+ (ti.flags & CKF_USER_PIN_INITIALIZED) == 0 ||
+ (ti.flags & CKF_LOGIN_REQUIRED) == 0 ||
+ (ti.flags & CKF_PROTECTED_AUTHENTICATION_PATH) != 0 || // Pinpad?
+
+ !match (idn.serial, API_STRING (ti.serialNumber)) ||
+ !match (idn.token, API_STRING (ti.label)) ||
+ !match (idn.model, API_STRING (ti.model)) ||
+ !match (idn.manufacturer, API_STRING (ti.manufacturerID)))
+ continue;
+
+ auto token_desc = [&ti] ()
+ {
+ string r ("token ");
+ string l (API_STRING (ti.label));
+
+ r += !l.empty ()
+ ? "'" + l + "'"
+ : "'" + API_STRING (ti.model) + "' by " +
+ API_STRING (ti.manufacturerID);
+
+ return r;
+ };
+
+ // Search for the matching RSA private key in the token.
+ //
+ // Open the read-only session with the token. Note that we can't see
+ // private objects until login.
+ //
+ CK_SESSION_HANDLE sh;
+ r = fs->C_OpenSession (sid,
+ CKF_SERIAL_SESSION,
+ nullptr /* pApplication */,
+ nullptr /* Notify */,
+ &sh);
+
+ if (r == CKR_DEVICE_REMOVED ||
+ r == CKR_TOKEN_NOT_PRESENT ||
+ r == CKR_TOKEN_NOT_RECOGNIZED)
+ continue;
+
+ if (r != CKR_OK)
+ throw_api_error (
+ r, "unable to open session with " + token_desc ());
+
+ session_ptr session (new CK_SESSION_HANDLE (sh), close_session);
+
+ // Log into the token unless simulating.
+ //
+ if (simulate_)
+ continue;
+
+ // Note that all sessions with a token share login, so need to login
+ // once per all token sessions.
+ //
+ // Also note that there is no need to logout explicitly if you want
+ // to keep all token sessions logged in during their lifetime.
+ //
+ r = fs->C_Login (
+ *session,
+ CKU_USER,
+ reinterpret_cast<unsigned char*> (const_cast<char*> (pin)),
+ strlen (pin));
+
+ // We can fail because of the wrong PIN or trying to apply the PIN
+ // to a wrong token. Should we just skip the token if that's the
+ // case? Probably we should throw. Otherwise who knows how many pins
+ // we will reset going forward.
+ //
+ if (r != CKR_USER_ALREADY_LOGGED_IN && r != CKR_OK)
+ throw_api_error (r, "unable to login to " + token_desc ());
+
+ // Search for the private key.
+ //
+ // Fill the search attributes.
+ //
+ CK_OBJECT_CLASS oc (CKO_PRIVATE_KEY);
+ CK_KEY_TYPE kt (CKK_RSA);
+
+ vector<CK_ATTRIBUTE> sa ({{CKA_CLASS, &oc, sizeof (oc)},
+ {CKA_KEY_TYPE, &kt, sizeof (kt)}});
+ if (idn.id)
+ sa.push_back (
+ CK_ATTRIBUTE {CKA_ID,
+ const_cast<unsigned char*> (idn.id->data ()),
+ idn.id->size ()});
+
+ if (idn.object)
+ sa.push_back (CK_ATTRIBUTE {CKA_LABEL,
+ const_cast<char*> (idn.object->c_str ()),
+ idn.object->size ()});
+
+ // Initialize the search.
+ //
+ r = fs->C_FindObjectsInit (*session, sa.data (), sa.size ());
+
+ if (r != CKR_OK)
+ throw_api_error (
+ r, "unable to enumerate private keys in " + token_desc ());
+
+ // Finally, search for the key.
+ //
+ // Note that we will query 2 keys to handle the 'multiple keys
+ // match' case.
+ //
+ CK_OBJECT_HANDLE key (0);
+ {
+ session_ptr search_deleter (
+ session.get (),
+ [] (CK_SESSION_HANDLE* p) {api ()->C_FindObjectsFinal (*p);});
+
+ CK_ULONG n;
+ CK_OBJECT_HANDLE keys[2];
+ r = fs->C_FindObjects (*session,
+ keys,
+ 2 /* ulMaxObjectCount */,
+ &n);
+
+ if (r != CKR_OK)
+ throw_api_error (
+ r,
+ "unable to obtain private key handles from " + token_desc ());
+
+ if (n == 1)
+ key = keys[0]; // Exactly one key matches.
+ else if (n == 0)
+ continue; // No key matches.
+ else
+ mult_keys (); // Multiple keys match.
+ }
+
+ if (session_ != nullptr)
+ mult_keys ();
+
+ // Produce description for the found key.
+ //
+ description = "private key ";
+ {
+ CK_ATTRIBUTE attr {CKA_LABEL, nullptr, 0};
+ r = fs->C_GetAttributeValue (*session,
+ key,
+ &attr,
+ 1 /* ulCount */);
+
+ if (r == CKR_OK && attr.ulValueLen != 0)
+ {
+ vector<char> label (attr.ulValueLen);
+ attr.pValue = label.data ();
+
+ r = fs->C_GetAttributeValue (*session,
+ key,
+ &attr,
+ 1 /* ulCount */);
+ if (r == CKR_OK)
+ description += "'" + string (label.data (), label.size ()) +
+ "' ";
+ }
+ }
+
+ description += "in " + token_desc ();
+
+ // Note that for Yubikey 4 we cannot rely on the key's
+ // CKA_ALWAYS_AUTHENTICATE attribute value for detecting if
+ // authentication is required for the sign operation. For the
+ // private key imported into the 9c (SIGN key) slot with the
+ // --pin-policy=never option the sign operation doesn't require
+ // authentication but the CKA_ALWAYS_AUTHENTICATE value is still on.
+ // This seems to be some yubico-piv-tool issue (or deliberate
+ // behavior) as for the 9a (PIV AUTH key) slot the sign
+ // authentication is not required and the CKA_ALWAYS_AUTHENTICATE
+ // value is off. This issue makes it impossible to evaluate if the
+ // key requires sign authentication before the sign attempt. This is
+ // probably not a big deal as, if we want, we can always check this
+ // using some dummy data.
+ //
+#if 0
+ CK_BBOOL always_auth (CK_FALSE);
+
+ CK_ATTRIBUTE attr {
+ CKA_ALWAYS_AUTHENTICATE, &always_auth, sizeof (always_auth)};
+
+ r = fs->C_GetAttributeValue (*session,
+ key,
+ &attr,
+ 1 /* ulCount */);
+
+ if (r != CKR_OK)
+ throw_api_error (
+ r,
+ "unable to obtain 'always auth' attribute for " + description);
+#endif
+
+ // Despite the fact that the key is found we will continue to iterate
+ // over slots to make sure that a single key matches the identity
+ // attributes.
+ //
+ session_ = move (session);
+ handle_ = key;
+ }
+
+ if (simulate_)
+ simulate ();
+ else if (session_ == nullptr)
+ no_key ();
+ }
+
+ vector<char> private_key::
+ sign (const vector<char>& data, const optional<simulate_outcome>& sim)
+ {
+ assert (!empty ());
+
+ if (sim && *sim == simulate_outcome::failure)
+ throw runtime_error ("unable to sign using " + description);
+
+ if (sim || simulate_)
+ {
+ // Otherwise would fail in the private_key constructor.
+ //
+ assert (!simulate_ || *simulate_ == simulate_outcome::success);
+
+ return vector<char> ({
+ 's', 'i', 'g', 'n', 'a', 't', 'u', 'r', 'e', '\n'});
+ }
+
+ CK_FUNCTION_LIST* fs (api ());
+
+ CK_MECHANISM mech;
+ memset (&mech, 0, sizeof (mech));
+ mech.mechanism = CKM_RSA_PKCS;
+
+ CK_RV r (fs->C_SignInit (*session_, &mech, handle_));
+
+ if (r != CKR_OK)
+ throw_api_error (
+ r, "unable to init sign operation using " + description);
+
+ for (vector<char> signature; true; signature_size_ *= 2)
+ {
+ signature.resize (signature_size_);
+
+ CK_ULONG n (signature_size_);
+
+ r = fs->C_Sign (*session_,
+ reinterpret_cast<unsigned char*> (
+ const_cast<char*> (data.data ())),
+ data.size (),
+ reinterpret_cast<unsigned char*> (
+ signature.data ()),
+ &n);
+
+ if (r == CKR_BUFFER_TOO_SMALL)
+ continue;
+
+ if (r != CKR_OK)
+ throw_api_error (r, "unable to sign using " + description);
+
+ assert (n != 0 && n <= signature_size_);
+
+ signature.resize (n);
+ return signature;
+ }
+ }
+ }
+ }
+}
diff --git a/openssl/agent/pkcs11/private-key.hxx b/openssl/agent/pkcs11/private-key.hxx
new file mode 100644
index 0000000..967f057
--- /dev/null
+++ b/openssl/agent/pkcs11/private-key.hxx
@@ -0,0 +1,95 @@
+// file : openssl/agent/pkcs11/private-key.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef OPENSSL_AGENT_PKCS11_PRIVATE_KEY_HXX
+#define OPENSSL_AGENT_PKCS11_PRIVATE_KEY_HXX
+
+#include <openssl/types.hxx>
+#include <openssl/utility.hxx>
+
+#include <openssl/agent/pkcs11/url.hxx>
+#include <openssl/agent/pkcs11/pkcs11.hxx>
+
+namespace openssl
+{
+ namespace agent
+ {
+ namespace pkcs11
+ {
+ // Unlocked private key.
+ //
+ // Keeps the authenticated session with the token containing the key.
+ //
+ // Note that the key should be configured not to require authentication
+ // of the sign operation. For Yubikey 4 this is achieved by passing the
+ // --pin-policy=never option to the yubico-piv-tool import-key command.
+ //
+ class private_key
+ {
+ public:
+ string description;
+
+ // Open PKCS#11 authenticated session with the token containing the
+ // private key that matches the identity attributes using the
+ // specified access attributes. Throw invalid_argument if there is
+ // something wrong with the identity or access attributes. Throw
+ // runtime_error if the matching private key cannot be found, accessed
+ // or multiple keys match the criteria.
+ //
+ // The token PIN is expected to be specified either as the pin
+ // argument (preferred) or as the pin_value access attribute. The
+ // pin_source access attribute is ignored. The pin argument is
+ // normally allocated on the stack and is overwritten with zeros
+ // after the usage. Using the pin_value attribute is insecure (may
+ // stay in memory) and is discouraged unless for debugging and
+ // testing.
+ //
+ private_key (const identity&,
+ const access&,
+ const char* pin,
+ optional<simulate_outcome> = nullopt);
+
+ // Create a special empty key that may not be used for any
+ // cryptographic operations.
+ //
+ private_key ();
+
+ // Movable-only type.
+ //
+ private_key (private_key&&);
+ private_key& operator= (private_key&&);
+ private_key (const private_key&) = delete;
+ private_key& operator= (const private_key&) = delete;
+
+ // Sign the data. Throw runtime_error if something goes wrong.
+ //
+ vector<char>
+ sign (const vector<char>&,
+ const optional<simulate_outcome>& = nullopt);
+
+ bool
+ empty () const {return session_ == nullptr && !simulate_;}
+
+ private:
+ using session_ptr = unique_ptr<CK_SESSION_HANDLE,
+ void (*)(CK_SESSION_HANDLE*)>;
+ session_ptr session_;
+
+ CK_OBJECT_HANDLE handle_; // Meaningless if session_ is NULL.
+
+ optional<simulate_outcome> simulate_;
+
+ // Normally the signature buffer size is the private key length.
+ // However, calculating this length is not trivial and would require
+ // using libcrypto library. To avoid this we will start with the 256
+ // bytes, doubling it while the sign operation complains about it, and
+ // cacheing the resulting size for future sign() calls.
+ //
+ CK_ULONG signature_size_ = 256;
+ };
+ }
+ }
+}
+
+#endif // OPENSSL_AGENT_PKCS11_PRIVATE_KEY_HXX
diff --git a/openssl/agent/pkcs11/private-key.test.cxx b/openssl/agent/pkcs11/private-key.test.cxx
new file mode 100644
index 0000000..52e6186
--- /dev/null
+++ b/openssl/agent/pkcs11/private-key.test.cxx
@@ -0,0 +1,72 @@
+// file : openssl/agent/pkcs11/private-key.test.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <iostream>
+
+#include <openssl/agent/pkcs11/url.hxx>
+#include <openssl/agent/pkcs11/private-key.hxx>
+
+// Usage: argv[0] <pkcs11-url>
+//
+// Create private_key object referenced by the <pkcs11-url>. Read data from
+// stdin, sign it with the private key, and print the signature to stdout.
+//
+int
+main (int argc, char* argv[])
+{
+ using namespace std;
+ using namespace openssl::agent::pkcs11;
+
+ if (argc != 2)
+ {
+ cerr << "usage: " << argv[0] << " <pkcs11-url>" << endl;
+ return 1;
+ }
+
+ cin.exceptions (ios::badbit);
+ cout.exceptions (ios::failbit | ios::badbit);
+
+ try
+ {
+ url u (argv[1]);
+ identity idn (u);
+ access acc (u);
+
+ vector<char> data ((istreambuf_iterator<char> (cin)),
+ istreambuf_iterator<char> ());
+
+ vector<char> signature;
+
+ // Stress the API a bit recreating, reusing and having concurrent keys.
+ //
+ for (size_t i (0); i < 5; ++i)
+ {
+ private_key key1 (idn, acc, nullptr /* secure_pin */);
+ private_key key2 (idn, acc, nullptr /* secure_pin */);
+
+ for (size_t i (0); i < 10; ++i)
+ {
+ vector<char> sign ((i % 2 == 0 ? key1 : key2).sign (data));
+
+ if (signature.empty ())
+ signature = move (sign);
+ else if (sign != signature)
+ throw runtime_error ("sign operation is unreliable");
+ }
+ }
+
+ cout.write (signature.data (), signature.size ());
+ return 0;
+ }
+ catch (const invalid_argument& e)
+ {
+ cerr << e << endl;
+ return 1;
+ }
+ catch (const runtime_error& e)
+ {
+ cerr << e << endl;
+ return 1;
+ }
+}
diff --git a/openssl/agent/pkcs11/private-key.test.testscript b/openssl/agent/pkcs11/private-key.test.testscript
new file mode 100644
index 0000000..0adaed1
--- /dev/null
+++ b/openssl/agent/pkcs11/private-key.test.testscript
@@ -0,0 +1,22 @@
+# file : openssl/agent/pkcs11/private-key.test.testscript -*- C++ -*-
+# copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+url = 'pkcs11:token=name:cppget.org;object=SIGN%20key?pin-value=123123'
+
+: sign
+:
+: Enable this test with care as this is not a simulation and the above URL
+: must match the token PIN. In the worst case the test may reset the token
+# PIN.
+:
+if (false)
+{
+ $* "$url" <'test' | base64 >>EOO
+ jbTa3H3F2KHGbzDW/FaI5oeqPQknxyKhgbl6eDZlEzwaXKhRXueQNC1TMwc5HgWtuUWoJYkdTmpI
+ 20X4rBfECYIYSwZPBWCypgWn8/mnZEEtL+sfoDXrm/MRBQhdg3FJorL9lGy8+OFH83RNPwic+2yZ
+ ItoReDFjlzkOWmsym3BPhDDrOOQ7DEAdaQDmJXJJsNvpIWmr2957CDoieyCx8yUc8zVR1viOsY47
+ rdZrXuJ5BH+cda4tmLthoI0PhHxNn6f56qfYxboXSE6hvGo6DLjipCLNVfROsIHJO1RSEkFt2lQ+
+ jVi7ZST0ZTsZI4lb/t8b6LoHumTQeteMBnMyCQ==
+ EOO
+}
diff --git a/openssl/agent/pkcs11/url.cxx b/openssl/agent/pkcs11/url.cxx
new file mode 100644
index 0000000..0b9c3ac
--- /dev/null
+++ b/openssl/agent/pkcs11/url.cxx
@@ -0,0 +1,303 @@
+// file : openssl/agent/pkcs11/url.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <openssl/agent/pkcs11/url.hxx>
+
+#include <cerrno>
+#include <cstdlib> // strtoull()
+#include <iterator> // back_inserter()
+
+namespace openssl
+{
+ namespace agent
+ {
+ namespace pkcs11
+ {
+ using namespace std;
+
+ // Convenience functions.
+ //
+ static uint64_t
+ parse_uint64 (const string& s,
+ uint64_t min,
+ uint64_t max,
+ const char* what)
+ {
+ if (s[0] != '-' && s[0] != '+') // strtoull() allows these.
+ {
+ const char* b (s.c_str ());
+ char* e (nullptr);
+ uint64_t v (strtoull (b, &e, 10)); // Can't throw.
+
+ if (errno != ERANGE && e == b + s.size () && v >= min && v <= max)
+ return v;
+ }
+
+ throw invalid_argument (string ("invalid ") + what + " '" + s + "'");
+ }
+
+ // url_traits
+ //
+ optional<url_traits::scheme_type> url_traits::
+ translate_scheme (const string_type& /* url */,
+ string_type&& scheme,
+ optional<authority_type>& authority,
+ optional<path_type>& path,
+ optional<string_type>& /* query */,
+ optional<string_type>& fragment,
+ bool& rootless)
+ {
+ // If something is wrong with the URL leave the basic_url constructor
+ // to throw.
+ //
+ if (scheme.empty ())
+ return nullopt;
+
+ if (casecmp (scheme, "pkcs11") != 0)
+ throw invalid_argument ("invalid scheme");
+
+ if (authority)
+ throw invalid_argument ("unexpected authority");
+
+ if (path && (!rootless || path->find ('/') != string::npos))
+ throw invalid_argument ("one-level path expected");
+
+ if (fragment)
+ throw invalid_argument ("unexpected fragment");
+
+ return move (scheme);
+ }
+
+ url_traits::string_type url_traits::
+ translate_scheme (string_type& /* url */,
+ const scheme_type& scheme,
+ const optional<authority_type>& /* authority */,
+ const optional<path_type>& /* path */,
+ const optional<string_type>& /* query */,
+ const optional<string_type>& /* fragment */,
+ bool /* rootless */)
+ {
+ return scheme;
+ }
+
+ url_traits::path_type url_traits::
+ translate_path (string_type&& path)
+ {
+ return move (path);
+ }
+
+ url_traits::string_type url_traits::
+ translate_path (const path_type& path)
+ {
+ return path;
+ }
+
+ // library_version
+ //
+ library_version::
+ library_version (const string& s)
+ {
+ auto num = [] (const string& s, const char* what)
+ {
+ return static_cast<unsigned char> (parse_uint64 (s, 0, 255, what));
+ };
+
+ size_t p (s.find ('.'));
+
+ if (p != string::npos)
+ {
+ major = num (string (s, 0, p), "library major version");
+ minor = num (string (s, p + 1), "library minor version");
+ }
+ else
+ {
+ major = num (s, "library major version");
+ minor = 0;
+ }
+ }
+
+ // Parse the attribute name=value representation. The value can contain
+ // binary data.
+ //
+ // It would probably be cleaner to return pair<string, vector<char>>,
+ // but this would uglify a client code quite a bit and make it less
+ // efficient.
+ //
+ static pair<string, string>
+ attribute (const string& s, size_t b, size_t n)
+ {
+ size_t i (b);
+ size_t e (b + n);
+
+ for (; i != e && s[i] != '='; ++i) ;
+
+ if (i == e)
+ throw invalid_argument (
+ "no value for attribute '" + string (s, b, n) + "'");
+
+ string a;
+ url::decode (s.begin () + b, s.begin () + i, back_inserter (a));
+
+ string v;
+ url::decode (s.begin () + i + 1, s.begin () + e, back_inserter (v));
+
+ return make_pair (move (a), move (v));
+ }
+
+ // identity
+ //
+ identity::
+ identity (const url& u)
+ {
+ const optional<string>& path (u.path);
+
+ // If URL path component is absent then create the identity that
+ // matches all PKCS#11 entities in the system.
+ //
+ if (!path)
+ return;
+
+ for (size_t b (0), e (0), n; (n = next_word (*path, b, e, ';')); )
+ {
+ pair<string, string> a (attribute (*path, b, n));
+
+ const string& an (a.first);
+ string& av (a.second);
+
+ auto set = [&an] (auto& attr, auto&& val)
+ {
+ if (attr)
+ throw invalid_argument ("duplicate attribute '" + an + "'");
+
+ attr = move (val);
+ };
+
+ // Module.
+ //
+ if (an == "library-manufacturer")
+ set (library_manufacturer, move (av));
+ else if (an == "library-version")
+ set (library_version, library_version_type (av));
+ else if (an == "library-description")
+ set (library_description, move (av));
+
+ // Slot.
+ //
+ else if (an == "slot-id")
+ set (slot_id,
+ static_cast<unsigned long> (
+ parse_uint64 (av, 0, ~0UL, "slot-id attribute value")));
+ else if (an == "slot-manufacturer")
+ set (slot_manufacturer, move (av));
+ else if (an == "slot-description")
+ set (slot_description, move (av));
+
+ // Token.
+ //
+ else if (an == "serial")
+ set (serial, move (av));
+ else if (an == "token")
+ set (token, move (av));
+ else if (an == "model")
+ set (model, move (av));
+ else if (an == "manufacturer")
+ set (manufacturer, move (av));
+
+ // Storage object.
+ //
+ else if (an == "id")
+ set (id, vector<unsigned char> (av.begin (), av.end ()));
+ else if (an == "object")
+ set (object, move (av));
+ else if (an == "type")
+ set (type, move (av));
+ else
+ throw invalid_argument ("unknown attribute '" + an + "'");
+ }
+ }
+
+ // access
+ //
+ access::
+ access (const url& u)
+ {
+ const optional<string>& query (u.query);
+
+ // If URL query component is absent then create an object that
+ // provides no access attributes.
+ //
+ if (!query)
+ return;
+
+ for (size_t b (0), e (0), n; (n = next_word (*query, b, e, ';')); )
+ {
+ pair<string, string> a (attribute (*query, b, n));
+
+ const string& an (a.first);
+ string& av (a.second);
+
+ auto set = [&an] (auto& attr, auto&& val)
+ {
+ if (attr)
+ throw invalid_argument ("duplicate attribute '" + an + "'");
+
+ attr = move (val);
+ };
+
+ // Note that unrecognized attributes are ignored (see the traits
+ // class notes for details).
+ //
+ if (an == "pin-source")
+ set (pin_source, move (av));
+ else if (an == "pin-value")
+ set (pin_value, move (av));
+ else if (an == "module-name")
+ {
+ try
+ {
+ path p (av);
+
+ if (!p.empty () && p.simple ())
+ {
+ set (module_name, move (p));
+ continue;
+ }
+
+ // Fall through.
+ }
+ catch (const invalid_path& e)
+ {
+ // Fall through.
+ }
+
+ throw invalid_argument (
+ "invalid value '" + av + "' for module-name attribute");
+ }
+ else if (an == "module-path")
+ {
+ try
+ {
+ path p (move (av));
+
+ if (p.relative ())
+ throw invalid_argument ("relative path '" + p.string () +
+ "' for module-path attribute");
+
+ set (module_path, move (p));
+ }
+ catch (const invalid_path& e)
+ {
+ throw invalid_argument (
+ "invalid path '" + e.path + "' for module-path attribute");
+ }
+ }
+ }
+
+ if (pin_source && pin_value)
+ throw invalid_argument (
+ "both pin-source and pin-value attributes specified");
+ }
+ }
+ }
+}
diff --git a/openssl/agent/pkcs11/url.hxx b/openssl/agent/pkcs11/url.hxx
new file mode 100644
index 0000000..b8ca056
--- /dev/null
+++ b/openssl/agent/pkcs11/url.hxx
@@ -0,0 +1,246 @@
+// file : openssl/agent/pkcs11/url.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef OPENSSL_AGENT_PKCS11_URL_HXX
+#define OPENSSL_AGENT_PKCS11_URL_HXX
+
+#include <libbutl/url.mxx>
+
+#include <openssl/types.hxx>
+#include <openssl/utility.hxx>
+
+// glibc defines these macros in its <sys/types.h>.
+//
+#ifdef major
+# undef major
+#endif
+
+#ifdef minor
+# undef minor
+#endif
+
+namespace openssl
+{
+ namespace agent
+ {
+ namespace pkcs11
+ {
+ // RFC7512 The PKCS #11 URI Scheme.
+ //
+ // Traits class for the PKCS#11 URL object.
+ //
+ // Notes:
+ //
+ // - The 'pkcs11:' URI scheme is rootless and doesn't use the fragment
+ // component.
+ //
+ // - The one-level path component is a sequence of semicolon-separated
+ // attribute name/value pairs that identify the referenced entity. It
+ // is stored in the URL-encoded representation as the id attribute
+ // contains binary data.
+ //
+ // - The query is a sequence of ampersand-separated access attribute
+ // name/value pairs that may or may not be used by the URL consumer.
+ //
+ // - All attribute values are textual, except for the id attribute which
+ // is binary.
+ //
+ // - There shouldn't be duplicate attributes in the path and query
+ // components.
+ //
+ // - Both path and query components can contain vendor-specific
+ // attributes.
+ //
+ // - An unrecognized attribute in the path component results in the
+ // parsing error. An unrecognized attribute in the query component is
+ // skipped.
+ //
+ // - The URL-referenced entity kind is context-specific. For example,
+ // the pkcs11:token=abc URL can refer the abc token or all objects
+ // stored in the abc token.
+ //
+ // - An attribute that is not present in the URL path component matches
+ // everything. Each additional attribute present in the path component
+ // further restricts the selection. Thus, in particular, the 'pkcs11:'
+ // URL is valid and matches everything.
+ //
+ struct url_traits
+ {
+ using string_type = string;
+ using path_type = string;
+
+ using scheme_type = string;
+ using authority_type = butl::basic_url_authority<string_type>;
+
+ static optional<scheme_type>
+ translate_scheme (const string_type&,
+ string_type&&,
+ optional<authority_type>&,
+ optional<path_type>&,
+ optional<string_type>&,
+ optional<string_type>&,
+ bool&);
+
+ static string_type
+ translate_scheme (string_type&,
+ const scheme_type&,
+ const optional<authority_type>&,
+ const optional<path_type>&,
+ const optional<string_type>&,
+ const optional<string_type>&,
+ bool);
+
+ static path_type
+ translate_path (string_type&&);
+
+ static string_type
+ translate_path (const path_type&);
+ };
+
+ using url = butl::basic_url<string, url_traits>;
+
+ // PKCS#11 entity (storage object, token, slot, or module) identity
+ // attributes.
+ //
+ struct library_version
+ {
+ unsigned char major;
+ unsigned char minor;
+
+ // Create the version object from its string representation. Throw
+ // invalid_argument on the parsing error.
+ //
+ explicit
+ library_version (const string&);
+
+ library_version (unsigned char mj, unsigned char mn)
+ : major (mj), minor (mn) {}
+ };
+
+ class identity
+ {
+ public:
+ // Module.
+ //
+ using library_version_type = pkcs11::library_version;
+
+ optional<string> library_manufacturer;
+ optional<library_version_type> library_version;
+ optional<string> library_description;
+
+ // Slot.
+ //
+ // Note that not all PKCS#11 libraries guarantee slot id to be stable
+ // across initializations.
+ //
+ optional<unsigned long> slot_id;
+ optional<string> slot_manufacturer;
+ optional<string> slot_description;
+
+ // Token.
+ //
+ optional<string> serial;
+ optional<string> token; // Token label.
+ optional<string> model;
+ optional<string> manufacturer;
+
+ // Storage object.
+ //
+ optional<vector<unsigned char>> id;
+
+ optional<string> object; // Object label.
+ optional<string> type; // cert, data, private, public, or
+ // secret-key.
+
+ // Parse the path component of pkcs11_url object. Throw
+ // invalid_argument if invalid attribute is encountered.
+ //
+ explicit
+ identity (const url&);
+
+ // Create an identity that matches all PKCS#11 entities in the system.
+ //
+ identity () = default;
+ };
+
+ // PKCS#11 entity access attributes.
+ //
+ class access
+ {
+ public:
+ optional<string> pin_value;
+ optional<string> pin_source;
+
+ // The library name with an extension and "lib" prefix stripped.
+ //
+ optional<path> module_name;
+
+ // The library absolute path or the directory path where the PKCS#11
+ // libraries are located.
+ //
+ optional<path> module_path;
+
+ // Parse the query component of pkcs11_url object. Throw
+ // invalid_argument if invalid attribute is encountered.
+ //
+ explicit
+ access (const url&);
+
+ // Create an object that provides no access attributes.
+ //
+ access () = default;
+ };
+
+ inline bool
+ operator== (const library_version& x, const library_version& y)
+ {
+ return x.major == y.major && x.minor == y.minor;
+ }
+
+ inline bool
+ operator!= (const library_version& x, const library_version& y)
+ {
+ return !(x == y);
+ }
+
+ inline bool
+ operator== (const identity& x, const identity& y)
+ {
+ return
+ // Module.
+ //
+ x.library_manufacturer == y.library_manufacturer &&
+ x.library_version == y.library_version &&
+ x.library_description == y.library_description &&
+
+ // Slot.
+ //
+ x.slot_id == y.slot_id &&
+ x.slot_manufacturer == y.slot_manufacturer &&
+ x.slot_description == y.slot_description &&
+
+ // Token.
+ //
+ x.serial == y.serial &&
+ x.token == y.token &&
+ x.model == y.model &&
+ x.manufacturer == y.manufacturer &&
+
+ // Storage object.
+ //
+ x.id == y.id &&
+ x.object == y.object &&
+ x.type == y.type;
+ }
+
+ inline bool
+ operator!= (const identity& x, const identity& y)
+ {
+ return !(x == y);
+ }
+ }
+ }
+}
+
+#endif // OPENSSL_AGENT_PKCS11_URL_HXX
diff --git a/openssl/agent/pkcs11/url.test.cxx b/openssl/agent/pkcs11/url.test.cxx
new file mode 100644
index 0000000..501137a
--- /dev/null
+++ b/openssl/agent/pkcs11/url.test.cxx
@@ -0,0 +1,46 @@
+// file : openssl/agent/pkcs11/url.test.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <iostream>
+
+#include <openssl/agent/pkcs11/url.hxx>
+
+// Usage: argv[0]
+//
+// Create pkcs11::url objects from stdin lines, and for each of them print its
+// string representation to stdout, one per line.
+//
+int
+main ()
+{
+ using namespace std;
+ using namespace openssl;
+ using namespace openssl::agent::pkcs11;
+
+ cin.exceptions (ios::badbit);
+ cout.exceptions (ios::failbit | ios::badbit);
+
+ try
+ {
+ string s;
+ while (!eof (getline (cin, s)))
+ {
+ url u (s);
+
+ // Validate the URL attributes.
+ //
+ identity idn (u);
+ access acc (u);
+
+ cout << u.string () << endl;
+ }
+
+ return 0;
+ }
+ catch (const exception& e)
+ {
+ cerr << e << endl;
+ return 1;
+ }
+}
diff --git a/openssl/agent/pkcs11/url.test.testscript b/openssl/agent/pkcs11/url.test.testscript
new file mode 100644
index 0000000..807fad9
--- /dev/null
+++ b/openssl/agent/pkcs11/url.test.testscript
@@ -0,0 +1,34 @@
+# file : openssl/agent/pkcs11/url.test.testscript -*- C++ -*-
+# copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+: valid
+:
+$* <<EOF >>EOF
+pkcs11:token=unused;object=SIGN%20key
+pkcs11:token=unused;object=SIGN%20key?pin-value=123123
+pkcs11:
+pkcs11:?pin=123123
+pkcs11:library-version=1.2
+pkcs11:?module-path=/usr/lib
+pkcs11:id=%01%00%38%02;token=unused
+pkcs11:?a=b
+EOF
+
+: invalid
+:
+{
+ $* <'file:/abc' 2>'invalid scheme' != 0 : invalid-scheme
+ $* <'pkcs11:/abc' 2>'one-level path expected' != 0 : root
+ $* <'pkcs11:a/bc' 2>'one-level path expected' != 0 : multi-level
+ $* <'pkcs11://a/abc' 2>'unexpected authority' != 0 : authority
+ $* <'pkcs11:abc#x' 2>'unexpected fragment' != 0 : fragment
+
+ $* <'pkcs11:slot-id=a' 2>"invalid slot-id attribute value 'a'"!= 0 : slot-id
+
+ $* <'pkcs11:a=b' 2>"unknown attribute 'a'" != 0 : unknown-ident
+
+ : lib-min-ver
+ :
+ $* <'pkcs11:library-version=1.a' 2>"invalid library minor version 'a'" != 0
+}
diff --git a/openssl/buildfile b/openssl/buildfile
new file mode 100644
index 0000000..ec54bb6
--- /dev/null
+++ b/openssl/buildfile
@@ -0,0 +1,75 @@
+# file : openssl/buildfile -*- C++ -*-
+# copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+import libs = libbutl%lib{butl}
+
+./: exe{openssl-client} exe{openssl-agent-pkcs11}
+
+exe{openssl-client}: client/{hxx ixx txx cxx}{* -options} \
+ client/{hxx ixx cxx}{options} libue{openssl}
+
+exe{openssl-agent-pkcs11}: agent/pkcs11/cxx{agent} agent/pkcs11/libue{openssl}
+
+agent/pkcs11/
+{
+ libue{openssl}: bin.whole = false
+ libue{openssl}: cxx.libs += -ldl
+ libue{openssl}: {hxx ixx txx cxx}{* -agent -options -*.test...} \
+ {hxx ixx cxx}{options} h{pkcs11} ../../libue{openssl}
+}
+
+libue{openssl}: bin.whole = false
+libue{openssl}: {hxx ixx txx cxx}{* -options -version -*.test...} \
+ {hxx ixx cxx}{options} {hxx}{version} $libs
+
+hxx{version}: in{version} $src_root/manifest
+
+# Unit tests.
+#
+exe{*.test}: test = true
+exe{*.test}: install = false
+
+for t: cxx{**.test...}
+{
+ d = $directory($t)
+ n = $name($t)...
+
+ ./: $d/exe{$n}
+ $d/exe{$n}: $t $d/{hxx ixx txx}{+$n} $d/testscript{+$n}
+ $d/exe{$n}: $d/libue{openssl}
+}
+
+# Generated options parser.
+#
+if $cli.configured
+{
+ cli.cxx{options}: cli{options}
+ client/cli.cxx{options}: client/cli{options}
+ agent/pkcs11/cli.cxx{options}: agent/pkcs11/cli{options}
+
+ cli.options += -I $src_root --include-with-brackets \
+--cxx-prologue "#include <openssl/types-parsers.hxx>" \
+--cli-namespace openssl::cli --generate-specifier --generate-parse
+
+ cli.cxx{options}: cli.options += --include-prefix openssl \
+--guard-prefix OPENSSL # No usage.
+
+ # Usage options.
+ #
+ cli.options += --suppress-undocumented --long-usage --ansi-color \
+--page-usage 'openssl::print_$name$_' --option-length 20
+
+ client/cli.cxx{options}: cli.options += --include-prefix openssl/client \
+--guard-prefix OPENSSL_CLIENT
+
+ agent/pkcs11/cli.cxx{options}: cli.options += \
+--include-prefix openssl/agent/pkcs11 --guard-prefix OPENSSL_AGENT_PKCS11
+
+ # Include the generated cli files into the distribution and don't remove
+ # them when cleaning in src (so that clean results in a state identical to
+ # distributed).
+ #
+ cli.cxx{*}: dist = true
+ cli.cxx{*}: clean = ($src_root != $out_root)
+}
diff --git a/openssl/client/client.cxx b/openssl/client/client.cxx
new file mode 100644
index 0000000..42c4f92
--- /dev/null
+++ b/openssl/client/client.cxx
@@ -0,0 +1,191 @@
+// file : openssl/client/client.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <iostream> // cin, cout
+
+#include <libbutl/pager.mxx>
+
+#include <openssl/protocol.hxx>
+#include <openssl/diagnostics.hxx>
+
+#include <openssl/client/options.hxx>
+
+namespace openssl
+{
+ namespace client
+ {
+ using namespace std;
+ using namespace butl;
+
+ static int
+ main (int argc, char* argv[])
+ try
+ {
+ // Parse the command/options.
+ //
+ string cmd;
+ options ops;
+
+ int i (1);
+ if (argc > i && *argv[i] != '-')
+ cmd = argv[i++];
+
+ cli::argv_scanner scan (i, argc, argv);
+
+ ops.parse (scan);
+
+ // Version.
+ //
+ if (ops.version ())
+ {
+ cout << "openssl-client " << OPENSSL_AGENT_VERSION_ID << endl
+ << "libbutl " << LIBBUTL_VERSION_ID << endl
+ << "Copyright (c) 2014-2018 Code Synthesis Ltd" << endl
+ << "This is free software released under the MIT license."
+ << endl;
+
+ return 0;
+ }
+
+ // Help.
+ //
+ if (ops.help ())
+ {
+ pager p ("openssl-client help", false);
+ print_openssl_client_usage (p.stream ());
+
+ // If the pager failed, assume it has issued some diagnostics.
+ //
+ return p.wait () ? 0 : 1;
+ }
+
+ if (cmd != "rsautl")
+ fail << "openssl-client command expected" <<
+ info << "run '" << argv[0] << " --help' for more information";
+
+ if (!ops.sign ())
+ fail << "-sign option is required";
+
+ if (!ops.keyform_specified ())
+ fail << "-keyform option is required";
+
+ if (ops.keyform () != "engine")
+ fail << "invalid value '" << ops.keyform ()
+ << "' for option -keyform";
+
+ if (!ops.engine_specified ())
+ fail << "-engine option is required";
+
+ if (ops.engine () != "pkcs11")
+ fail << "invalid value '" << ops.engine () << "' for option -engine";
+
+ if (!ops.inkey_specified ())
+ fail << "-inkey option is required";
+
+ // Obtain the agent socket path.
+ //
+ path sock_path;
+
+ try
+ {
+ optional<string> p (getenv ("OPENSSL_AGENT_PKCS11_SOCK"));
+
+ if (!p)
+ fail << "OPENSSL_AGENT_PKCS11_SOCK environment variable is not set";
+
+ sock_path = path (move (*p));
+ }
+ catch (const invalid_path& e)
+ {
+ fail << "invalid OPENSSL_AGENT_PKCS11_SOCK environment variable "
+ << "value '" << e.path << "'";
+ }
+
+ // Read the data to sign from stdin.
+ //
+ vector<char> data;
+
+ try
+ {
+ stdin_fdmode (fdstream_mode::binary);
+
+ cin.exceptions (ostream::badbit | ostream::failbit);
+
+ data = vector<char> (istreambuf_iterator<char> (cin),
+ istreambuf_iterator<char> ());
+ }
+ catch (const io_error&)
+ {
+ fail << "unable to read data from stdin";
+ }
+
+ // Sign the data.
+ //
+ vector<char> signature;
+
+ try
+ {
+ auto_fd sock (connect (sock_path));
+
+ ifdstream is (fddup (sock.get ()));
+ ofdstream os (move (sock));
+
+ string s (ops.simulate_specified ()
+ ? to_string (ops.simulate ())
+ : "");
+
+ os << request ("sign",
+ strings ({ops.inkey (), move (s)}),
+ move (data));
+ os.close ();
+
+ response r;
+ is >> r;
+
+ if (r.status != 0)
+ {
+ text << r.error;
+ return r.status;
+ }
+
+ signature = move (r.output);
+ }
+ catch (const io_error&)
+ {
+ fail << "unable to communicate with openssl-agent-pkcs11";
+ }
+
+ // Write it out.
+ //
+ try
+ {
+ stdout_fdmode (fdstream_mode::binary);
+
+ cout.exceptions (ostream::badbit | ostream::failbit);
+ cout.write (signature.data (), signature.size ());
+ }
+ catch (const io_error&)
+ {
+ fail << "error: unable to write signature to stdout";
+ }
+
+ return 0;
+ }
+ catch (const failed&)
+ {
+ return 1; // Diagnostics has already been issued.
+ }
+ catch (const cli::exception& e)
+ {
+ error << e;
+ return 1;
+ }
+ }
+}
+
+int
+main (int argc, char* argv[])
+{
+ return openssl::client::main (argc, argv);
+}
diff --git a/openssl/client/options.cli b/openssl/client/options.cli
new file mode 100644
index 0000000..4a2872a
--- /dev/null
+++ b/openssl/client/options.cli
@@ -0,0 +1,109 @@
+// file : openssl/client/options.cli
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+include <openssl/options.cli>;
+
+"\section=1"
+"\name=openssl-client"
+"\summary=OpenSSL client"
+
+namespace openssl
+{
+ namespace client
+ {
+ {
+ "<options>",
+
+ "
+ \h|SYNOPSIS|
+
+ \c{\b{openssl-client --help}\n
+ \b{openssl-client --version}\n
+ \b{openssl-client} rsautl [<options>]}
+
+ \h|DESCRIPTION|
+
+ The \cb{rsautl} command is a drop-in replacement for the
+ \cb{openssl-rsautl(1)} cryptographic operations. Instead of performing
+ the operations itself, it forwards the request to an OpenSSL key agent
+ that keeps the private key unlocked for the session.
+
+ Currently, data signing with a private key stored in a \cb{PKCS#11}
+ token is the only supported arrangement. This limits the
+ \cb{openssl-rsautl(1)} options and values to the following usage:
+
+ \
+ $ openssl-client rsautl -sign -keyform engine -engine pkcs11 -inkey pkcs11:...
+ \
+
+ This command reads data from \cb{stdin}, asks
+ \cb{openssl-agent-pkcs11(1)} to sign it using the specified unlocked
+ private key, and prints the resulting signature to \cb{stdout}.
+
+ The command can be simulated without actually performing any
+ cryptographic operations. If the \cb{--simulate} option is specified
+ with the \cb{success} outcome, then the command prints a dummy signature
+ produced by the agent and exits with zero status. The \cb{failure}
+ outcome causes it to print the diagnostics to \cb{stderr} and exit with
+ non-zero status. This mode is mostly useful for OpenSSL key agents
+ testing.
+ "
+ }
+
+ class options
+ {
+ "\h|OPTIONS|"
+
+ bool --help {"Print usage information and exit."}
+ bool --version {"Print version and exit."}
+
+ bool -sign
+ {
+ "Sign data read from \cb{stdin}."
+ }
+
+ string -keyform
+ {
+ "<form>",
+ "Private key format. The only supported format is \cb{engine}."
+ }
+
+ string -engine
+ {
+ "<engine>",
+ "Engine to use for the cryptographic operation. The only supported
+ engine is \cb{pkcs11}."
+ }
+
+ string -inkey
+ {
+ "<location>",
+ "Private key location. Its format (file path, URL, etc) depends on the
+ engine used. For the \cb{pkcs11} engine it should be a \cb{PKCS#11}
+ URL."
+ }
+
+ simulate_outcome --simulate
+ {
+ "<outcome>",
+ "Ask the agent to simulate the cryptographic operation instead of
+ performing it for real."
+ }
+ };
+
+ "
+ \h|ENVIRONMENT|
+
+ If \cb{-engine} is \cb{pkcs11}, then the \cb{OPENSSL_AGENT_PKCS11_SOCK}
+ environment variable should be set to the Unix-domain socket of the
+ \cb{openssl-agent-pkcs11(1)} daemon.
+ "
+
+ "
+ \h|EXIT STATUS|
+
+ Non-zero exit status is returned in case of an error.
+ "
+ }
+}
diff --git a/openssl/diagnostics.cxx b/openssl/diagnostics.cxx
new file mode 100644
index 0000000..4dd13e0
--- /dev/null
+++ b/openssl/diagnostics.cxx
@@ -0,0 +1,35 @@
+// file : openssl/diagnostics.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <openssl/diagnostics.hxx>
+
+namespace openssl
+{
+ // Diagnostic facility, project specifics.
+ //
+
+ void simple_prologue_base::
+ operator() (const diag_record& r) const
+ {
+ if (type_ != nullptr)
+ r << type_;
+
+ if (name_ != nullptr)
+ r << name_;
+
+ if (data_ != nullptr)
+ r << '(' << data_ << ')';
+
+ if (name_ != nullptr || data_ != nullptr)
+ r << ": ";
+ }
+
+ basic_mark error ("error: ");
+ basic_mark warn ("warning: ");
+ basic_mark info ("info: ");
+ basic_mark text (nullptr);
+ fail_mark fail ("error: ");
+
+ const fail_end endf;
+}
diff --git a/openssl/diagnostics.hxx b/openssl/diagnostics.hxx
new file mode 100644
index 0000000..3b51c4d
--- /dev/null
+++ b/openssl/diagnostics.hxx
@@ -0,0 +1,115 @@
+// file : openssl/diagnostics.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef OPENSSL_DIAGNOSTICS_HXX
+#define OPENSSL_DIAGNOSTICS_HXX
+
+#include <libbutl/diagnostics.mxx>
+
+#include <openssl/types.hxx> // Note: not <openssl/utility.hxx>.
+
+namespace openssl
+{
+ using butl::diag_record;
+
+ // Throw this exception to terminate the process. The handler should
+ // assume that the diagnostics has already been issued.
+ //
+ class failed: public std::exception {};
+
+ // Diagnostic facility, base infrastructure.
+ //
+ using butl::diag_stream;
+ using butl::diag_epilogue;
+
+ // Diagnostic facility, project specifics.
+ //
+ struct simple_prologue_base
+ {
+ explicit
+ simple_prologue_base (const char* type, const char* name, const char* data)
+ : type_ (type), name_ (name), data_ (data) {}
+
+ void
+ operator() (const diag_record& r) const;
+
+ private:
+ const char* type_;
+ const char* name_;
+ const char* data_;
+ };
+
+ struct basic_mark_base
+ {
+ using simple_prologue = butl::diag_prologue<simple_prologue_base>;
+
+ // If data if not NULL, then we print it as (data) after name. For
+ // example:
+ //
+ // error: main(foo): bar
+ //
+ explicit
+ basic_mark_base (const char* type,
+ const char* name = nullptr,
+ const char* data = nullptr,
+ diag_epilogue* epilogue = nullptr)
+ : type_ (type), name_ (name), data_ (data), epilogue_ (epilogue) {}
+
+ simple_prologue
+ operator() () const
+ {
+ return simple_prologue (epilogue_, type_, name_, data_);
+ }
+
+ public:
+ const char* type_;
+ const char* name_;
+ const char* data_;
+
+ diag_epilogue* const epilogue_;
+ };
+ using basic_mark = butl::diag_mark<basic_mark_base>;
+
+ extern basic_mark error;
+ extern basic_mark warn;
+ extern basic_mark info;
+ extern basic_mark text;
+
+ // fail
+ //
+ struct fail_mark_base: basic_mark_base
+ {
+ explicit
+ fail_mark_base (const char* type, const char* data = nullptr)
+ : basic_mark_base (type,
+ nullptr,
+ data,
+ [](const diag_record& r)
+ {
+ r.flush ();
+ throw failed ();
+ }) {}
+ };
+
+ using fail_mark = butl::diag_mark<fail_mark_base>;
+
+ struct fail_end_base
+ {
+ [[noreturn]] void
+ operator() (const diag_record& r) const
+ {
+ // If we just throw then the record's destructor will see an active
+ // exception and will not flush the record.
+ //
+ r.flush ();
+ throw failed ();
+ }
+ };
+ using fail_end = butl::diag_noreturn_end<fail_end_base>;
+
+ extern fail_mark fail;
+ extern const fail_end endf;
+}
+
+#endif // OPENSSL_DIAGNOSTICS_HXX
diff --git a/openssl/options.cli b/openssl/options.cli
new file mode 100644
index 0000000..1b4bc98
--- /dev/null
+++ b/openssl/options.cli
@@ -0,0 +1,9 @@
+// file : openssl/options.cli
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+include <openssl/types.hxx>;
+
+namespace openssl
+{
+}
diff --git a/openssl/protocol.cxx b/openssl/protocol.cxx
new file mode 100644
index 0000000..e74b35e
--- /dev/null
+++ b/openssl/protocol.cxx
@@ -0,0 +1,132 @@
+// file : openssl/protocol.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <openssl/protocol.hxx>
+
+#include <sys/un.h> // sockaddr_un
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <cstring> // strcpy()
+
+namespace openssl
+{
+ using namespace std;
+
+ auto_fd
+ connect (const path& p)
+ {
+ auto_fd sock (socket (AF_UNIX, SOCK_STREAM, 0));
+
+ if (sock.get () == -1)
+ throw_system_ios_failure (errno);
+
+ struct sockaddr_un addr;
+ memset (&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+
+ if (p.string ().size () >= sizeof (addr.sun_path))
+ throw_generic_ios_failure (ENAMETOOLONG);
+
+ strcpy (addr.sun_path, p.string ().c_str ());
+
+ if (connect (sock.get (),
+ reinterpret_cast<sockaddr*> (&addr),
+ sizeof (addr)) == -1)
+ throw_system_ios_failure (errno);
+
+ return sock;
+ }
+
+ ostream&
+ operator<< (ostream& os, const request& r)
+ {
+ // Write the header: command, arguments count and input data length.
+ //
+ os << r.cmd << ' ' << r.args.size () << ' ' << r.input.size () << '\n';
+
+ // Write the arguments.
+ //
+ for (const string& a: r.args)
+ os.write (a.c_str (), a.size () + 1); // Includes the trailing '\0';
+
+ // Write the input data.
+ //
+ os.write (r.input.data (), r.input.size ());
+ return os << flush;
+ }
+
+ istream&
+ operator>> (istream& is, request& r)
+ {
+ // Read the header: command, arguments count and input data length.
+ //
+ size_t na, ni;
+ is >> r.cmd >> na >> ni;
+
+ if (is.get () != '\n')
+ {
+ is.setstate (istream::failbit);
+ return is;
+ }
+
+ // Read the arguments.
+ //
+ for (string l; na != 0 && !eof (getline (is, l, '\0')); --na)
+ r.args.push_back (move (l));
+
+ if (na != 0)
+ {
+ is.setstate (istream::failbit);
+ return is;
+ }
+
+ // Read the input data.
+ //
+ r.input.resize (ni);
+ return is.read (r.input.data (), ni);
+ }
+
+ ostream&
+ operator<< (ostream& os, const response& r)
+ {
+ // Write the header: status and output/error data lengths.
+ //
+ os << r.status << ' ' << r.output.size () << ' ' << r.error.size ()
+ << '\n';
+
+ // Write the output and error data.
+ //
+ os.write (r.output.data (), r.output.size ());
+ os.write (r.error.data (), r.error.size ());
+ return os << flush;
+ }
+
+ istream&
+ operator>> (istream& is, response& r)
+ {
+ // Read the header: status and output/error data lengths.
+ //
+ size_t no, ne;
+ is >> r.status >> no >> ne;
+
+ if (is.get () != '\n')
+ {
+ is.setstate (istream::failbit);
+ return is;
+ }
+
+ // Read the output data.
+ //
+ r.output.resize (no);
+ is.read (r.output.data (), no);
+
+ // Read the error (text) data.
+ //
+ for (r.error.reserve (ne); ne != 0; --ne)
+ r.error.push_back (is.get ());
+
+ return is;
+ }
+}
diff --git a/openssl/protocol.hxx b/openssl/protocol.hxx
new file mode 100644
index 0000000..c676a8b
--- /dev/null
+++ b/openssl/protocol.hxx
@@ -0,0 +1,74 @@
+// file : openssl/protocol.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef OPENSSL_PROTOCOL_HXX
+#define OPENSSL_PROTOCOL_HXX
+
+#include <openssl/types.hxx>
+#include <openssl/utility.hxx>
+
+namespace openssl
+{
+ // Simple client/agent communication protocol.
+ //
+
+ // Return the file descriptor connected to the agent's Unix-domain socket.
+ // Throw io_error on the underlying OS error.
+ //
+ auto_fd
+ connect (const path& socket);
+
+ class request
+ {
+ public:
+ string cmd;
+ strings args; // Shouldn't contain NULL characters.
+ vector<char> input;
+
+ // Create an empty request, normally before reading it from a stream.
+ //
+ request () = default;
+
+ request (string c, strings a, vector<char> i)
+ : cmd (move (c)), args (move (a)), input (move (i)) {}
+
+ explicit
+ request (string c): cmd (move (c)) {}
+ request (string c, strings a): cmd (move (c)), args (move (a)) {}
+ request (string c, vector<char> i): cmd (move (c)), input (move (i)) {}
+ };
+
+ class response
+ {
+ public:
+ uint8_t status;
+ vector<char> output;
+ string error;
+
+ response (uint8_t s, vector<char> o, string e)
+ : status (s), output (move (o)), error (move (e)) {}
+
+ // Success.
+ //
+ response (): status (0) {}
+
+ explicit
+ response (vector<char> o): status (0), output (move (o)) {}
+
+ // Error.
+ //
+ explicit
+ response (string e, uint8_t s = 1): status (s), error (move (e)) {}
+ };
+
+ // The stream's exception mask should have at least failbit and badbit set
+ // (that is the default for fdstreams).
+ //
+ ostream& operator<< (ostream&, const request&);
+ istream& operator>> (istream&, request&);
+ ostream& operator<< (ostream&, const response&);
+ istream& operator>> (istream&, response&);
+}
+
+#endif // OPENSSL_PROTOCOL_HXX
diff --git a/openssl/types-parsers.cxx b/openssl/types-parsers.cxx
new file mode 100644
index 0000000..4b96c65
--- /dev/null
+++ b/openssl/types-parsers.cxx
@@ -0,0 +1,34 @@
+// file : openssl/types-parsers.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <openssl/types-parsers.hxx>
+
+#include <openssl/options.hxx> // openssl::cli namespace
+
+namespace openssl
+{
+ namespace cli
+ {
+ void parser<simulate_outcome>::
+ parse (simulate_outcome& x, bool& xs, scanner& s)
+ {
+ xs = true;
+ const char* o (s.next ());
+
+ if (!s.more ())
+ throw missing_value (o);
+
+ const string v (s.next ());
+
+ try
+ {
+ x = to_simulate_outcome (v);
+ }
+ catch (const invalid_argument&)
+ {
+ throw invalid_value (o, v);
+ }
+ }
+ }
+}
diff --git a/openssl/types-parsers.hxx b/openssl/types-parsers.hxx
new file mode 100644
index 0000000..b79f27e
--- /dev/null
+++ b/openssl/types-parsers.hxx
@@ -0,0 +1,31 @@
+// file : openssl/types-parsers.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+// CLI parsers, included into the generated source files.
+//
+
+#ifndef OPENSSL_TYPES_PARSERS_HXX
+#define OPENSSL_TYPES_PARSERS_HXX
+
+#include <openssl/types.hxx>
+
+namespace openssl
+{
+ namespace cli
+ {
+ class scanner;
+
+ template <typename T>
+ struct parser;
+
+ template <>
+ struct parser<simulate_outcome>
+ {
+ static void
+ parse (simulate_outcome&, bool&, scanner&);
+ };
+ }
+}
+
+#endif // OPENSSL_TYPES_PARSERS_HXX
diff --git a/openssl/types.cxx b/openssl/types.cxx
new file mode 100644
index 0000000..6acde44
--- /dev/null
+++ b/openssl/types.cxx
@@ -0,0 +1,29 @@
+// file : openssl/types.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <openssl/types.hxx>
+
+namespace openssl
+{
+ string
+ to_string (simulate_outcome s)
+ {
+ switch (s)
+ {
+ case simulate_outcome::success: return "success";
+ case simulate_outcome::failure: return "failure";
+ }
+
+ assert (false);
+ return string ();
+ }
+
+ simulate_outcome
+ to_simulate_outcome (const string& s)
+ {
+ if (s == "success") return simulate_outcome::success;
+ else if (s == "failure") return simulate_outcome::failure;
+ else throw invalid_argument ("invalid simulate outcome '" + s + "'");
+ }
+}
diff --git a/openssl/types.hxx b/openssl/types.hxx
new file mode 100644
index 0000000..c574493
--- /dev/null
+++ b/openssl/types.hxx
@@ -0,0 +1,103 @@
+// file : openssl/types.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef OPENSSL_TYPES_HXX
+#define OPENSSL_TYPES_HXX
+
+#include <vector>
+#include <string>
+#include <memory> // unique_ptr, shared_ptr
+#include <cstddef> // size_t, nullptr_t
+#include <cstdint> // uint{8,16,32,64}_t
+#include <istream>
+#include <ostream>
+
+#include <ios> // ios_base::failure
+#include <exception> // exception
+#include <stdexcept> // logic_error, invalid_argument, runtime_error
+#include <system_error>
+
+#include <libbutl/path.mxx>
+#include <libbutl/path-io.mxx>
+#include <libbutl/optional.mxx>
+#include <libbutl/fdstream.mxx>
+#include <libbutl/small-vector.mxx>
+
+namespace openssl
+{
+ // Commonly-used types.
+ //
+ using std::uint8_t;
+ using std::uint16_t;
+ using std::uint32_t;
+ using std::uint64_t;
+
+ using std::size_t;
+ using std::nullptr_t;
+
+ using std::pair;
+ using std::string;
+ using std::function;
+ using std::reference_wrapper;
+
+ using std::unique_ptr;
+ using std::shared_ptr;
+ using std::weak_ptr;
+
+ using std::vector;
+ using butl::small_vector; // <libbutl/small-vector.mxx>
+
+ using strings = vector<string>;
+ using cstrings = vector<const char*>;
+
+ using std::istream;
+ using std::ostream;
+
+ // Exceptions. While <exception> is included, there is no using for
+ // std::exception -- use qualified.
+ //
+ using std::logic_error;
+ using std::invalid_argument;
+ using std::runtime_error;
+ using std::system_error;
+ using io_error = std::ios_base::failure;
+
+ // <libbutl/optional.mxx>
+ //
+ using butl::optional;
+ using butl::nullopt;
+
+ // <libbutl/path.mxx>
+ //
+ using butl::path;
+ using butl::dir_path;
+ using butl::basic_path;
+ using butl::invalid_path;
+
+ using butl::path_cast;
+
+ // <libbutl/fdstream.mxx>
+ //
+ using butl::auto_fd;
+ using butl::ifdstream;
+ using butl::ofdstream;
+ using butl::fdopen_mode;
+ using butl::fdstream_mode;
+
+ // Simulation outcomes.
+ //
+ enum class simulate_outcome
+ {
+ success,
+ failure
+ };
+
+ string
+ to_string (simulate_outcome);
+
+ simulate_outcome
+ to_simulate_outcome (const string&); // Throws invalid_argument.
+}
+
+#endif // OPENSSL_TYPES_HXX
diff --git a/openssl/utility.cxx b/openssl/utility.cxx
new file mode 100644
index 0000000..b9c974d
--- /dev/null
+++ b/openssl/utility.cxx
@@ -0,0 +1,41 @@
+// file : openssl/utility.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <openssl/utility.hxx>
+
+#include <strings.h> // explicit_bzero()
+
+#include <cstdlib> // exit()
+#include <cstring> // memset()
+
+namespace openssl
+{
+ using namespace std;
+
+ void
+ mem_clear (void* p, size_t n)
+ {
+ // Note that explicit_bzero() is only available in glibc starting 2.25.
+ //
+#if defined (__GLIBC__) && \
+ defined (__GLIBC_MINOR__) && \
+ (__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)
+
+ explicit_bzero (p, n);
+
+#else
+
+ memset (p, 0, n);
+
+ // Make sure the function is not optimized out.
+ //
+ for (char *i (reinterpret_cast<char*> (p)), *e (i + n); i != e; ++i)
+ {
+ if (*i != '\0')
+ exit (*i);
+ }
+
+#endif
+ }
+}
diff --git a/openssl/utility.hxx b/openssl/utility.hxx
new file mode 100644
index 0000000..5a905ab
--- /dev/null
+++ b/openssl/utility.hxx
@@ -0,0 +1,74 @@
+// file : openssl/utility.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef OPENSSL_UTILITY_HXX
+#define OPENSSL_UTILITY_HXX
+
+#include <string> // to_string()
+#include <utility> // move(), forward(), declval(), make_pair()
+#include <cassert> // assert()
+
+#include <libbutl/utility.mxx> // casecmp(), reverse_iterate(), etc
+#include <libbutl/fdstream.mxx>
+#include <libbutl/filesystem.mxx>
+
+#include <openssl/types.hxx>
+#include <openssl/version.hxx>
+
+namespace openssl
+{
+ using std::move;
+ using std::forward;
+ using std::declval;
+
+ using std::make_pair;
+ using std::to_string;
+
+ // <libbutl/utility.mxx>
+ //
+ using butl::ucase;
+ using butl::lcase;
+ using butl::casecmp;
+
+ using butl::trim;
+ using butl::next_word;
+
+ using butl::reverse_iterate;
+
+ using butl::make_guard;
+ using butl::make_exception_guard;
+
+ using butl::getenv;
+ using butl::setenv;
+ using butl::unsetenv;
+
+ using butl::eof;
+
+ using butl::throw_generic_error;
+ using butl::throw_system_error;
+
+ using butl::throw_generic_ios_failure;
+ using butl::throw_system_ios_failure;
+
+ // <libbutl/filesystem.mxx>
+ //
+ using butl::file_exists;
+
+ using butl::auto_rmfile;
+ using butl::auto_rmdir;
+
+ // <libbutl/fdstream.mxx>
+ //
+ using butl::stdin_fdmode;
+ using butl::stdout_fdmode;
+ using butl::fdnull;
+ using butl::fddup;
+
+ // Securely clear a memory buffer.
+ //
+ void
+ mem_clear (void*, size_t);
+}
+
+#endif // OPENSSL_UTILITY_HXX
diff --git a/openssl/version.hxx.in b/openssl/version.hxx.in
new file mode 100644
index 0000000..805987d
--- /dev/null
+++ b/openssl/version.hxx.in
@@ -0,0 +1,44 @@
+// file : openssl/version.hxx.in -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef OPENSSL_AGENT_VERSION // Note: using the version macro itself.
+
+// Note: using build2 standard versioning scheme. The numeric version format
+// is AAABBBCCCDDDE where:
+//
+// AAA - major version number
+// BBB - minor version number
+// CCC - bugfix version number
+// DDD - alpha / beta (DDD + 500) version number
+// E - final (0) / snapshot (1)
+//
+// When DDDE is not 0, 1 is subtracted from AAABBBCCC. For example:
+//
+// Version AAABBBCCCDDDE
+//
+// 0.1.0 0000010000000
+// 0.1.2 0000010010000
+// 1.2.3 0010020030000
+// 2.2.0-a.1 0020019990010
+// 3.0.0-b.2 0029999995020
+// 2.2.0-a.1.z 0020019990011
+//
+#define OPENSSL_AGENT_VERSION $openssl_agent.version.project_number$ULL
+#define OPENSSL_AGENT_VERSION_STR "$openssl_agent.version.project$"
+#define OPENSSL_AGENT_VERSION_ID "$openssl_agent.version.project_id$"
+
+#define OPENSSL_AGENT_VERSION_MAJOR $openssl_agent.version.major$
+#define OPENSSL_AGENT_VERSION_MINOR $openssl_agent.version.minor$
+#define OPENSSL_AGENT_VERSION_PATCH $openssl_agent.version.patch$
+
+#define OPENSSL_AGENT_PRE_RELEASE $openssl_agent.version.pre_release$
+
+#define OPENSSL_AGENT_SNAPSHOT $openssl_agent.version.snapshot_sn$ULL
+#define OPENSSL_AGENT_SNAPSHOT_ID "$openssl_agent.version.snapshot_id$"
+
+#include <libbutl/version.hxx>
+
+$libbutl.check(LIBBUTL_VERSION, LIBBUTL_SNAPSHOT)$
+
+#endif // OPENSSL_AGENT_VERSION
diff --git a/repositories.manifest b/repositories.manifest
new file mode 100644
index 0000000..00a05ed
--- /dev/null
+++ b/repositories.manifest
@@ -0,0 +1,6 @@
+: 1
+summary: OpenSSL key agent repository
+
+:
+role: prerequisite
+location: ../libbutl.git##HEAD
diff --git a/tests/.gitignore b/tests/.gitignore
new file mode 100644
index 0000000..35ec43f
--- /dev/null
+++ b/tests/.gitignore
@@ -0,0 +1,2 @@
+test/
+test-*/
diff --git a/tests/agent-pkcs11.testscript b/tests/agent-pkcs11.testscript
new file mode 100644
index 0000000..de7ecf2
--- /dev/null
+++ b/tests/agent-pkcs11.testscript
@@ -0,0 +1,71 @@
+# file : tests/agent-pkcs11.testscript
+# copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+: args
+:
+{
+ : none
+ :
+ $* 2>'error: private key URL argument expected' != 0
+
+ : invalid-url
+ :
+ $* 'http://key' 2>'error: invalid PKCS#11 URL: invalid scheme' != 0
+}
+
+: existent-module
+:
+{
+ url = 'pkcs11:?pin-value=123123'
+
+ : failure
+ :
+ $* --simulate failure "$url" 2>>EOE != 0
+ error: no matching private key found
+ EOE
+
+ : success
+ :
+ {
+ $* --simulate success "$url" | set script;
+
+ sed -n -e 's/^OPENSSL_AGENT_PKCS11_PID=(.+);.+;$/\1/p' <"$script" | \
+ set pid;
+
+ echo "$script" >>~"%EOO%";
+ %OPENSSL_AGENT_PKCS11_SOCK=.+; export OPENSSL_AGENT_PKCS11_SOCK;%
+ OPENSSL_AGENT_PKCS11_PID=$pid; export OPENSSL_AGENT_PKCS11_PID;
+ echo Agent pid $pid
+ EOO
+
+ kill -0 "$pid"; # Make sure the agent is running.
+ kill "$pid"; # Signal the agent to terminate.
+ sleep 2; # Wait a bit while the agent is terminating.
+ kill -0 "$pid" 2>! != 0 # Make sure the agent is not running.
+ }
+}
+
+: non-existent-module
+:
+{
+ url = 'pkcs11:?pin-value=123123&module-name=non-existing-pkcs11-module'
+
+ : failure
+ :
+ $* --simulate failure "$url" 2>>EOE != 0
+ error: no matching private key found
+ EOE
+
+ : success
+ :
+ {
+ $* --simulate success "$url" | set script;
+
+ sed -n -e 's/^OPENSSL_AGENT_PKCS11_PID=(.+);.+;$/\1/p' <"$script" | \
+ set pid;
+
+ kill -0 "$pid"; # Make sure the agent is running.
+ kill "$pid" # Signal the agent to terminate.
+ }
+}
diff --git a/tests/build/.gitignore b/tests/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/tests/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/tests/build/bootstrap.build b/tests/build/bootstrap.build
new file mode 100644
index 0000000..91bc3e9
--- /dev/null
+++ b/tests/build/bootstrap.build
@@ -0,0 +1,9 @@
+# file : tests/build/bootstrap.build
+# copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+project = # Unnamed subproject.
+
+using config
+using dist
+using test
diff --git a/tests/build/root.build b/tests/build/root.build
new file mode 100644
index 0000000..494ad82
--- /dev/null
+++ b/tests/build/root.build
@@ -0,0 +1,8 @@
+# file : tests/build/root.build
+# copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+# Setup the client and agents that we are testing.
+#
+import openssl_client = openssl-agent%exe{openssl-client}
+import openssl_agent_pkcs11 = openssl-agent%exe{openssl-agent-pkcs11}
diff --git a/tests/buildfile b/tests/buildfile
new file mode 100644
index 0000000..f76786c
--- /dev/null
+++ b/tests/buildfile
@@ -0,0 +1,8 @@
+# file : tests/buildfile
+# copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+./: testscript{*} $openssl_client $openssl_agent_pkcs11
+
+testscript{client}@./: test = $openssl_client
+testscript{agent-pkcs11}@./: test = $openssl_agent_pkcs11
diff --git a/tests/client.testscript b/tests/client.testscript
new file mode 100644
index 0000000..dd950cd
--- /dev/null
+++ b/tests/client.testscript
@@ -0,0 +1,65 @@
+# file : tests/client.testscript
+# copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+test.arguments += rsautl -sign -keyform engine -engine pkcs11
+
+: args
+:
+{
+ : none
+ :
+ $* 2>'error: -inkey option is required' != 0
+
+ : no-sock
+ :
+ env --unset=OPENSSL_AGENT_PKCS11_SOCK - $* -inkey 'pkcs11:' 2>>EOE != 0
+ error: OPENSSL_AGENT_PKCS11_SOCK environment variable is not set
+ EOE
+}
+
+: pkcs11
+:
+{
+ +sed -e 's/-client$/-agent-pkcs11/' <"$0" | set agent
+
+ : communication
+ :
+ {
+ # Start the agent.
+ #
+ +$agent --simulate success 'pkcs11:?pin-value=123123' | set script
+
+ +sed -n -e 's/^OPENSSL_AGENT_PKCS11_PID=(.+);.+$/\1/p' <"$script" | set pid
+ +sed -n -e 's/^OPENSSL_AGENT_PKCS11_SOCK=(.+);.+;$/\1/p' <"$script" | set sock
+
+ sign = env - OPENSSL_AGENT_PKCS11_SOCK="$sock" $*
+
+ : sign
+ :
+ {
+ $sign --simulate success -inkey 'pkcs11:' >'signature' : simulate-opt
+ $sign -inkey 'pkcs11:' >'signature' : no-simulate-opt
+ }
+
+ : failure
+ :
+ {
+ $sign --simulate failure -inkey 'pkcs11:' 2>>EOE != 0
+ error: unable to sign using simulated private key
+ EOE
+ }
+
+ : wrong-key
+ :
+ {
+ $sign --simulate success -inkey 'pkcs11:object=key' 2>>EOE != 0
+ error: private key doesn't match
+ EOE
+ }
+
+ # Stop the agent.
+ #
+ -kill "$pid"
+ }
+}