From de91921561092689369b56c54950474e0a86e66f Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Mon, 15 Oct 2018 21:08:04 +0300 Subject: Add implementation --- INSTALL | 6 + LICENSE | 30 + NEWS | 3 + README | 63 + build/.gitignore | 3 + build/bootstrap.build | 11 + build/export.build | 13 + build/root.build | 25 + buildfile | 10 + manifest | 20 + openssl/.gitignore | 9 + openssl/agent/pkcs11/agent.cxx | 621 ++++++++++ openssl/agent/pkcs11/options.cli | 79 ++ openssl/agent/pkcs11/pkcs11.cxx | 214 ++++ openssl/agent/pkcs11/pkcs11.h | 1436 ++++++++++++++++++++++ openssl/agent/pkcs11/pkcs11.hxx | 63 + openssl/agent/pkcs11/private-key.cxx | 492 ++++++++ openssl/agent/pkcs11/private-key.hxx | 95 ++ openssl/agent/pkcs11/private-key.test.cxx | 72 ++ openssl/agent/pkcs11/private-key.test.testscript | 22 + openssl/agent/pkcs11/url.cxx | 303 +++++ openssl/agent/pkcs11/url.hxx | 246 ++++ openssl/agent/pkcs11/url.test.cxx | 46 + openssl/agent/pkcs11/url.test.testscript | 34 + openssl/buildfile | 75 ++ openssl/client/client.cxx | 191 +++ openssl/client/options.cli | 109 ++ openssl/diagnostics.cxx | 35 + openssl/diagnostics.hxx | 115 ++ openssl/options.cli | 9 + openssl/protocol.cxx | 132 ++ openssl/protocol.hxx | 74 ++ openssl/types-parsers.cxx | 34 + openssl/types-parsers.hxx | 31 + openssl/types.cxx | 29 + openssl/types.hxx | 103 ++ openssl/utility.cxx | 41 + openssl/utility.hxx | 74 ++ openssl/version.hxx.in | 44 + repositories.manifest | 6 + tests/.gitignore | 2 + tests/agent-pkcs11.testscript | 71 ++ tests/build/.gitignore | 3 + tests/build/bootstrap.build | 9 + tests/build/root.build | 8 + tests/buildfile | 8 + tests/client.testscript | 65 + 47 files changed, 5184 insertions(+) create mode 100644 INSTALL create mode 100644 LICENSE create mode 100644 NEWS create mode 100644 README create mode 100644 build/.gitignore create mode 100644 build/bootstrap.build create mode 100644 build/export.build create mode 100644 build/root.build create mode 100644 buildfile create mode 100644 manifest create mode 100644 openssl/.gitignore create mode 100644 openssl/agent/pkcs11/agent.cxx create mode 100644 openssl/agent/pkcs11/options.cli create mode 100644 openssl/agent/pkcs11/pkcs11.cxx create mode 100644 openssl/agent/pkcs11/pkcs11.h create mode 100644 openssl/agent/pkcs11/pkcs11.hxx create mode 100644 openssl/agent/pkcs11/private-key.cxx create mode 100644 openssl/agent/pkcs11/private-key.hxx create mode 100644 openssl/agent/pkcs11/private-key.test.cxx create mode 100644 openssl/agent/pkcs11/private-key.test.testscript create mode 100644 openssl/agent/pkcs11/url.cxx create mode 100644 openssl/agent/pkcs11/url.hxx create mode 100644 openssl/agent/pkcs11/url.test.cxx create mode 100644 openssl/agent/pkcs11/url.test.testscript create mode 100644 openssl/buildfile create mode 100644 openssl/client/client.cxx create mode 100644 openssl/client/options.cli create mode 100644 openssl/diagnostics.cxx create mode 100644 openssl/diagnostics.hxx create mode 100644 openssl/options.cli create mode 100644 openssl/protocol.cxx create mode 100644 openssl/protocol.hxx create mode 100644 openssl/types-parsers.cxx create mode 100644 openssl/types-parsers.hxx create mode 100644 openssl/types.cxx create mode 100644 openssl/types.hxx create mode 100644 openssl/utility.cxx create mode 100644 openssl/utility.hxx create mode 100644 openssl/version.hxx.in create mode 100644 repositories.manifest create mode 100644 tests/.gitignore create mode 100644 tests/agent-pkcs11.testscript create mode 100644 tests/build/.gitignore create mode 100644 tests/build/bootstrap.build create mode 100644 tests/build/root.build create mode 100644 tests/buildfile create mode 100644 tests/client.testscript 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" <<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 // sockaddr_un +#include +#include + +#include // kill(), sigaction(), sigemptyset(), SIG* +#include // fork(), getpid(), dup2(), setsid() +#include // tcgetattr(), tcsetattr() + +#include // sig_atomic_t +#include // strlen(), strcpy(), memset(), memcmp() +#include + +#include // cout + +#include + +#include +#include + +#include +#include +#include + +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 (&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> sig_handlers; + + const vector 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& 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 init_error; + + try + { + optional 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 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 ; + +"\section=1" +"\name=openssl-agent-pkcs11" +"\summary=OpenSSL PKCS#11 key agent" + +namespace openssl +{ + namespace agent + { + namespace pkcs11 + { + { + " ", + + " + \h|SYNOPSIS| + + \c{\b{openssl-agent-pkcs11 --help}\n + \b{openssl-agent-pkcs11 --version}\n + \b{openssl-agent-pkcs11} [] } + + \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 + { + "", + "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 + +#include + +#include // 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 module; + static unique_ptr functions; + + CK_FUNCTION_LIST* + api (const path& p, bool ignore_nonexistent) + { + if (functions != nullptr) + return functions.get (); + + // Load the PKCS#11 module. + // + unique_ptr 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 ( + 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 + . + + 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 + +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 + +#include +#include + +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 (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 + +#include // memset(), strlen() + +#include + +#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 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 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 (const_cast (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 sa ({{CKA_CLASS, &oc, sizeof (oc)}, + {CKA_KEY_TYPE, &kt, sizeof (kt)}}); + if (idn.id) + sa.push_back ( + CK_ATTRIBUTE {CKA_ID, + const_cast (idn.id->data ()), + idn.id->size ()}); + + if (idn.object) + sa.push_back (CK_ATTRIBUTE {CKA_LABEL, + const_cast (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 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 private_key:: + sign (const vector& data, const optional& 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 ({ + '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 signature; true; signature_size_ *= 2) + { + signature.resize (signature_size_); + + CK_ULONG n (signature_size_); + + r = fs->C_Sign (*session_, + reinterpret_cast ( + const_cast (data.data ())), + data.size (), + reinterpret_cast ( + 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 +#include + +#include +#include + +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 = 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 + sign (const vector&, + const optional& = nullopt); + + bool + empty () const {return session_ == nullptr && !simulate_;} + + private: + using session_ptr = unique_ptr; + session_ptr session_; + + CK_OBJECT_HANDLE handle_; // Meaningless if session_ is NULL. + + optional 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 + +#include +#include + +// Usage: argv[0] +// +// Create private_key object referenced by the . 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] << " " << 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 data ((istreambuf_iterator (cin)), + istreambuf_iterator ()); + + vector 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 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 + +#include +#include // strtoull() +#include // 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:: + translate_scheme (const string_type& /* url */, + string_type&& scheme, + optional& authority, + optional& path, + optional& /* query */, + optional& 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 */, + const optional& /* path */, + const optional& /* query */, + const optional& /* 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 (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>, + // but this would uglify a client code quite a bit and make it less + // efficient. + // + static pair + 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& 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 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 ( + 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 (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& 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 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 + +#include +#include + +// glibc defines these macros in its . +// +#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; + + static optional + translate_scheme (const string_type&, + string_type&&, + optional&, + optional&, + optional&, + optional&, + bool&); + + static string_type + translate_scheme (string_type&, + const scheme_type&, + const optional&, + const optional&, + const optional&, + const optional&, + bool); + + static path_type + translate_path (string_type&&); + + static string_type + translate_path (const path_type&); + }; + + using url = butl::basic_url; + + // 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 library_manufacturer; + optional library_version; + optional library_description; + + // Slot. + // + // Note that not all PKCS#11 libraries guarantee slot id to be stable + // across initializations. + // + optional slot_id; + optional slot_manufacturer; + optional slot_description; + + // Token. + // + optional serial; + optional token; // Token label. + optional model; + optional manufacturer; + + // Storage object. + // + optional> id; + + optional object; // Object label. + optional 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 pin_value; + optional pin_source; + + // The library name with an extension and "lib" prefix stripped. + // + optional module_name; + + // The library absolute path or the directory path where the PKCS#11 + // libraries are located. + // + optional 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 + +#include + +// 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 +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 " \ +--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 // cin, cout + +#include + +#include +#include + +#include + +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 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 data; + + try + { + stdin_fdmode (fdstream_mode::binary); + + cin.exceptions (ostream::badbit | ostream::failbit); + + data = vector (istreambuf_iterator (cin), + istreambuf_iterator ()); + } + catch (const io_error&) + { + fail << "unable to read data from stdin"; + } + + // Sign the data. + // + vector 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 ; + +"\section=1" +"\name=openssl-client" +"\summary=OpenSSL client" + +namespace openssl +{ + namespace client + { + { + "", + + " + \h|SYNOPSIS| + + \c{\b{openssl-client --help}\n + \b{openssl-client --version}\n + \b{openssl-client} rsautl []} + + \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 + { + "
", + "Private key format. The only supported format is \cb{engine}." + } + + string -engine + { + "", + "Engine to use for the cryptographic operation. The only supported + engine is \cb{pkcs11}." + } + + string -inkey + { + "", + "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 + { + "", + "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 + +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 + +#include // Note: not . + +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; + + // 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; + + 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; + + 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; + + 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 ; + +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 + +#include // sockaddr_un +#include +#include + +#include // 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 (&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 +#include + +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 input; + + // Create an empty request, normally before reading it from a stream. + // + request () = default; + + request (string c, strings a, vector 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 i): cmd (move (c)), input (move (i)) {} + }; + + class response + { + public: + uint8_t status; + vector output; + string error; + + response (uint8_t s, vector o, string e) + : status (s), output (move (o)), error (move (e)) {} + + // Success. + // + response (): status (0) {} + + explicit + response (vector 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 + +#include // openssl::cli namespace + +namespace openssl +{ + namespace cli + { + void parser:: + 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 + +namespace openssl +{ + namespace cli + { + class scanner; + + template + struct parser; + + template <> + struct parser + { + 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 + +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 +#include +#include // unique_ptr, shared_ptr +#include // size_t, nullptr_t +#include // uint{8,16,32,64}_t +#include +#include + +#include // ios_base::failure +#include // exception +#include // logic_error, invalid_argument, runtime_error +#include + +#include +#include +#include +#include +#include + +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; // + + using strings = vector; + using cstrings = vector; + + using std::istream; + using std::ostream; + + // Exceptions. While 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; + + // + // + using butl::optional; + using butl::nullopt; + + // + // + using butl::path; + using butl::dir_path; + using butl::basic_path; + using butl::invalid_path; + + using butl::path_cast; + + // + // + 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 + +#include // explicit_bzero() + +#include // exit() +#include // 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 (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 // to_string() +#include // move(), forward(), declval(), make_pair() +#include // assert() + +#include // casecmp(), reverse_iterate(), etc +#include +#include + +#include +#include + +namespace openssl +{ + using std::move; + using std::forward; + using std::declval; + + using std::make_pair; + using std::to_string; + + // + // + 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; + + // + // + using butl::file_exists; + + using butl::auto_rmfile; + using butl::auto_rmdir; + + // + // + 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.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" + } +} -- cgit v1.1