summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/CMakeLists.txt2
-rw-r--r--tests/pkd/CMakeLists.txt35
-rw-r--r--tests/pkd/pkd_client.h69
-rw-r--r--tests/pkd/pkd_daemon.c497
-rw-r--r--tests/pkd/pkd_daemon.h40
-rw-r--r--tests/pkd/pkd_hello.c534
-rw-r--r--tests/pkd/pkd_keyutil.c138
-rw-r--r--tests/pkd/pkd_keyutil.h40
-rw-r--r--tests/pkd/pkd_util.c45
-rw-r--r--tests/pkd/pkd_util.h16
10 files changed, 1416 insertions, 0 deletions
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index e1a8166b..cba1d303 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -38,6 +38,7 @@ set(TEST_TARGET_LIBRARIES
)
add_subdirectory(unittests)
+
if (WITH_CLIENT_TESTING)
add_subdirectory(client)
endif (WITH_CLIENT_TESTING)
@@ -46,3 +47,4 @@ if (WITH_BENCHMARKS)
add_subdirectory(benchmarks)
endif (WITH_BENCHMARKS)
+add_subdirectory(pkd)
diff --git a/tests/pkd/CMakeLists.txt b/tests/pkd/CMakeLists.txt
new file mode 100644
index 00000000..d4389595
--- /dev/null
+++ b/tests/pkd/CMakeLists.txt
@@ -0,0 +1,35 @@
+project(pkd C)
+
+if (UNIX AND NOT WIN32)
+
+include_directories(
+ ${LIBSSH_PUBLIC_INCLUDE_DIRS}
+ ${CMOCKA_INCLUDE_DIR}
+ ${OPENSSL_INCLUDE_DIRS}
+ ${GCRYPT_INCLUDE_DIRS}
+ ${ZLIB_INCLUDE_DIRS}
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/src
+ ${CMAKE_CURRENT_SOURCE_DIR}
+)
+
+set(pkd_hello_src
+ pkd_daemon.c
+ pkd_hello.c
+ pkd_keyutil.c
+ pkd_util.c
+)
+
+set(pkd_libs
+ ${CMOCKA_LIBRARY}
+ ${LIBSSH_STATIC_LIBRARY}
+ ${LIBSSH_LINK_LIBRARIES}
+ ${LIBSSH_THREADS_STATIC_LIBRARY}
+ ${LIBSSH_THREADS_LINK_LIBRARIES}
+ ${ARGP_LIBRARIES}
+)
+
+add_executable(pkd_hello ${pkd_hello_src})
+target_link_libraries(pkd_hello ${pkd_libs})
+
+endif (UNIX AND NOT WIN32)
diff --git a/tests/pkd/pkd_client.h b/tests/pkd/pkd_client.h
new file mode 100644
index 00000000..c4a8a601
--- /dev/null
+++ b/tests/pkd/pkd_client.h
@@ -0,0 +1,69 @@
+/*
+ * pkd_client.h -- macros for generating client-specific command
+ * invocations for use with pkd testing
+ *
+ * (c) 2014 Jon Simons
+ */
+
+#ifndef __PKD_CLIENT_H__
+#define __PKD_CLIENT_H__
+
+/* OpenSSH */
+
+#define OPENSSH_BINARY "ssh"
+#define OPENSSH_KEYGEN "ssh-keygen"
+
+#define OPENSSH_CMD_START \
+ OPENSSH_BINARY " " \
+ "-o UserKnownHostsFile=/dev/null " \
+ "-o StrictHostKeyChecking=no " \
+ "-i " CLIENT_ID_FILE " " \
+ "1> %s.out " \
+ "2> %s.err " \
+ "-vvv "
+
+#define OPENSSH_CMD_END "-p 1234 localhost ls"
+
+#define OPENSSH_CMD \
+ OPENSSH_CMD_START OPENSSH_CMD_END
+
+#define OPENSSH_KEX_CMD(kexalgo) \
+ OPENSSH_CMD_START "-o KexAlgorithms=" kexalgo " " OPENSSH_CMD_END
+
+#define OPENSSH_CIPHER_CMD(ciphers) \
+ OPENSSH_CMD_START "-c " ciphers " " OPENSSH_CMD_END
+
+#define OPENSSH_MAC_CMD(macs) \
+ OPENSSH_CMD_START "-o MACs=" macs " " OPENSSH_CMD_END
+
+
+/* Dropbear */
+
+#define DROPBEAR_BINARY "dbclient"
+#define DROPBEAR_KEYGEN "dropbearkey"
+
+#define DROPBEAR_CMD_START \
+ DROPBEAR_BINARY " " \
+ "-y -y " \
+ "-i " CLIENT_ID_FILE " " \
+ "-v " \
+ "1> %s.out " \
+ "2> %s.err "
+
+#define DROPBEAR_CMD_END "-p 1234 localhost ls"
+
+#define DROPBEAR_CMD \
+ DROPBEAR_CMD_START DROPBEAR_CMD_END
+
+#if 0 /* dbclient does not expose control over kex algo */
+#define DROPBEAR_KEX_CMD(kexalgo) \
+ DROPBEAR_CMD
+#endif
+
+#define DROPBEAR_CIPHER_CMD(ciphers) \
+ DROPBEAR_CMD_START "-c " ciphers " " DROPBEAR_CMD_END
+
+#define DROPBEAR_MAC_CMD(macs) \
+ DROPBEAR_CMD_START "-m " macs " " DROPBEAR_CMD_END
+
+#endif /* __PKD_CLIENT_H__ */
diff --git a/tests/pkd/pkd_daemon.c b/tests/pkd/pkd_daemon.c
new file mode 100644
index 00000000..de4e5369
--- /dev/null
+++ b/tests/pkd/pkd_daemon.c
@@ -0,0 +1,497 @@
+/*
+ * pkd_daemon.c -- a sample public-key testing daemon using libssh
+ *
+ * Uses public key authentication to establish an exec channel and
+ * echo back payloads to the user.
+ *
+ * (c) 2014 Jon Simons
+ */
+
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <libssh/callbacks.h>
+#include <libssh/libssh.h>
+#include <libssh/server.h>
+
+#include "pkd_daemon.h"
+
+#include <setjmp.h> // for cmocka
+#include <cmocka.h>
+
+static int pkdout_enabled;
+static int pkderr_enabled;
+
+static void pkdout(const char *fmt, ...) PRINTF_ATTRIBUTE(1, 2);
+static void pkderr(const char *fmt, ...) PRINTF_ATTRIBUTE(1, 2);
+
+static void pkdout(const char *fmt, ...) {
+ va_list vargs;
+ if (pkdout_enabled) {
+ va_start(vargs, fmt);
+ vfprintf(stdout, fmt, vargs);
+ va_end(vargs);
+ }
+}
+
+static void pkderr(const char *fmt, ...) {
+ va_list vargs;
+ if (pkderr_enabled) {
+ va_start(vargs, fmt);
+ vfprintf(stderr, fmt, vargs);
+ va_end(vargs);
+ }
+}
+
+/*
+ * pkd state: only one thread can run pkd at a time ---------------------
+ */
+
+static struct {
+ int rc;
+ pthread_t tid;
+ int keep_going;
+ int pkd_ready;
+} ctx;
+
+static struct {
+ int server_fd;
+ int req_exec_received;
+ int close_received;
+ int eof_received;
+} pkd_state;
+
+static void pkd_sighandler(int signum) {
+ (void) signum;
+}
+
+static int pkd_init_libssh() {
+ int rc = ssh_threads_set_callbacks(ssh_threads_get_pthread());
+ return (rc == SSH_OK) ? 0 : 1;
+}
+
+static int pkd_init_server_fd(short port) {
+ int rc = 0;
+ int yes = 1;
+ struct sockaddr_in addr;
+
+ int server_fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (server_fd < 0) {
+ rc = -1;
+ goto out;
+ }
+
+ rc = setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
+ if (rc != 0) {
+ goto outclose;
+ }
+
+ memset(&addr, 0x0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = INADDR_ANY;
+ rc = bind(server_fd, (struct sockaddr *)&addr, sizeof(addr));
+ if (rc != 0) {
+ goto outclose;
+ }
+
+ rc = listen(server_fd, 128);
+ if (rc == 0) {
+ goto out;
+ }
+
+outclose:
+ close(server_fd);
+ server_fd = -1;
+out:
+ pkd_state.server_fd = server_fd;
+ return rc;
+}
+
+static int pkd_accept_fd() {
+ int fd = -1;
+ struct sockaddr_in addr;
+ socklen_t len = sizeof(addr);
+
+ do {
+ fd = accept(pkd_state.server_fd, (struct sockaddr *) &addr, &len);
+ } while ((ctx.keep_going != 0) && (fd < 0) && (errno == EINTR));
+
+ return fd;
+}
+
+static void pkd_eof(ssh_session session,
+ ssh_channel channel,
+ void *userdata) {
+ (void) session;
+ (void) channel;
+ (void) userdata;
+ pkdout("pkd_eof\n");
+ pkd_state.eof_received = 1;
+}
+
+static void pkd_chan_close(ssh_session session,
+ ssh_channel channel,
+ void *userdata) {
+ (void) session;
+ (void) channel;
+ (void) userdata;
+ pkdout("pkd_chan_close\n");
+ pkd_state.close_received = 1;
+}
+
+static int pkd_req_exec(ssh_session s,
+ ssh_channel c,
+ const char *cmd,
+ void *userdata) {
+ (void) s;
+ (void) c;
+ (void) cmd;
+ (void) userdata;
+ /* assumes pubkey authentication has already succeeded */
+ pkdout("pkd_req_exec\n");
+ pkd_state.req_exec_received = 1;
+ return 0;
+}
+
+/* assumes there is only ever a single channel */
+static struct ssh_channel_callbacks_struct pkd_channel_cb = {
+ .channel_eof_function = pkd_eof,
+ .channel_close_function = pkd_chan_close,
+ .channel_exec_request_function = pkd_req_exec,
+};
+
+static int pkd_auth_pubkey_cb(ssh_session s,
+ const char *user,
+ ssh_key key,
+ char state,
+ void *userdata) {
+ (void) s;
+ (void) user;
+ (void) key;
+ (void) state;
+ (void) userdata;
+ pkdout("pkd_auth_pubkey_cb keytype %s, state: %d\n",
+ ssh_key_type_to_char(ssh_key_type(key)), state);
+ if ((state == SSH_PUBLICKEY_STATE_NONE) ||
+ (state == SSH_PUBLICKEY_STATE_VALID)) {
+ return SSH_AUTH_SUCCESS;
+ }
+ return SSH_AUTH_DENIED;
+}
+
+static int pkd_service_request_cb(ssh_session session,
+ const char *service,
+ void *userdata) {
+ (void) session;
+ (void) userdata;
+ pkdout("pkd_service_request_cb: %s\n", service);
+ return (0 == (strcmp(service, "ssh-userauth"))) ? 0 : -1;
+}
+
+static ssh_channel pkd_channel_openreq_cb(ssh_session s,
+ void *userdata) {
+ ssh_channel c = NULL;
+ ssh_channel *out = (ssh_channel *) userdata;
+
+ /* assumes pubkey authentication has already succeeded */
+ pkdout("pkd_channel_openreq_cb\n");
+
+ c = ssh_channel_new(s);
+ if (c == NULL) {
+ pkderr("ssh_channel_new: %s\n", ssh_get_error(s));
+ return NULL;
+ }
+
+ ssh_callbacks_init(&pkd_channel_cb);
+ pkd_channel_cb.userdata = userdata;
+ if (ssh_set_channel_callbacks(c, &pkd_channel_cb) != SSH_OK) {
+ pkderr("ssh_set_channel_callbacks: %s\n", ssh_get_error(s));
+ ssh_channel_free(c);
+ c = NULL;
+ }
+
+ *out = c;
+
+ return c;
+}
+
+static struct ssh_server_callbacks_struct pkd_server_cb = {
+ .auth_pubkey_function = pkd_auth_pubkey_cb,
+ .service_request_function = pkd_service_request_cb,
+ .channel_open_request_session_function = pkd_channel_openreq_cb,
+};
+
+static int pkd_exec_hello(int fd, struct pkd_daemon_args *args) {
+ int rc = -1;
+ ssh_bind b = NULL;
+ ssh_session s = NULL;
+ ssh_event e = NULL;
+ ssh_channel c = NULL;
+ enum ssh_bind_options_e opts = -1;
+
+ int level = args->opts.libssh_log_level;
+ enum pkd_hostkey_type_e type = args->type;
+ const char *hostkeypath = args->hostkeypath;
+
+ pkd_state.eof_received = 0;
+ pkd_state.close_received = 0;
+ pkd_state.req_exec_received = 0;
+
+ b = ssh_bind_new();
+ if (b == NULL) {
+ pkderr("ssh_bind_new\n");
+ goto outclose;
+ }
+
+ if (type == PKD_RSA) {
+ opts = SSH_BIND_OPTIONS_RSAKEY;
+ } else if (type == PKD_DSA) {
+ opts = SSH_BIND_OPTIONS_DSAKEY;
+ } else if (type == PKD_ECDSA) {
+ opts = SSH_BIND_OPTIONS_ECDSAKEY;
+ } else {
+ pkderr("unknown kex algorithm: %d\n", type);
+ rc = -1;
+ goto outclose;
+ }
+
+ rc = ssh_bind_options_set(b, opts, hostkeypath);
+ if (rc != 0) {
+ pkderr("ssh_bind_options_set: %s\n", ssh_get_error(b));
+ goto outclose;
+ }
+
+ rc = ssh_bind_options_set(b, SSH_BIND_OPTIONS_LOG_VERBOSITY, &level);
+ if (rc != 0) {
+ pkderr("ssh_bind_options_set log verbosity: %s\n", ssh_get_error(b));
+ goto outclose;
+ }
+
+ s = ssh_new();
+ if (s == NULL) {
+ pkderr("ssh_new\n");
+ goto outclose;
+ }
+
+ /*
+ * ssh_bind_accept loads host key as side-effect. If this
+ * succeeds, the given 'fd' will be closed upon 'ssh_free(s)'.
+ */
+ rc = ssh_bind_accept_fd(b, s, fd);
+ if (rc != SSH_OK) {
+ pkderr("ssh_bind_accept_fd: %s\n", ssh_get_error(b));
+ goto outclose;
+ }
+
+ /* accept only publickey-based auth */
+ ssh_set_auth_methods(s, SSH_AUTH_METHOD_PUBLICKEY);
+
+ /* initialize callbacks */
+ ssh_callbacks_init(&pkd_server_cb);
+ pkd_server_cb.userdata = &c;
+ rc = ssh_set_server_callbacks(s, &pkd_server_cb);
+ if (rc != SSH_OK) {
+ pkderr("ssh_set_server_callbacks: %s\n", ssh_get_error(s));
+ goto out;
+ }
+
+ /* first do key exchange */
+ rc = ssh_handle_key_exchange(s);
+ if (rc != SSH_OK) {
+ pkderr("ssh_handle_key_exchange: %s\n", ssh_get_error(s));
+ goto out;
+ }
+
+ /* setup and pump event to carry out exec channel */
+ e = ssh_event_new();
+ if (e == NULL) {
+ pkderr("ssh_event_new\n");
+ goto out;
+ }
+
+ rc = ssh_event_add_session(e, s);
+ if (rc != SSH_OK) {
+ pkderr("ssh_event_add_session\n");
+ goto out;
+ }
+
+ /* poll until exec channel established */
+ while ((ctx.keep_going != 0) &&
+ (rc != SSH_ERROR) && (pkd_state.req_exec_received == 0)) {
+ rc = ssh_event_dopoll(e, -1 /* infinite timeout */);
+ }
+
+ if (rc == SSH_ERROR) {
+ pkderr("ssh_event_dopoll\n");
+ goto out;
+ } else if (c == NULL) {
+ pkderr("poll loop exited but exec channel not ready\n");
+ rc = -1;
+ goto out;
+ }
+
+ rc = ssh_channel_write(c, "hello\n", 6); /* XXX: customizable payloads */
+ if (rc != 6) {
+ pkderr("ssh_channel_write partial (%d)\n", rc);
+ }
+
+ rc = ssh_channel_request_send_exit_status(c, 0);
+ if (rc != SSH_OK) {
+ pkderr("ssh_channel_request_send_exit_status: %s\n",
+ ssh_get_error(s));
+ goto out;
+ }
+
+ rc = ssh_channel_send_eof(c);
+ if (rc != SSH_OK) {
+ pkderr("ssh_channel_send_eof: %s\n", ssh_get_error(s));
+ goto out;
+ }
+
+ rc = ssh_channel_close(c);
+ if (rc != SSH_OK) {
+ pkderr("ssh_channel_close: %s\n", ssh_get_error(s));
+ goto out;
+ }
+
+ while ((ctx.keep_going != 0) &&
+ (pkd_state.eof_received == 0) &&
+ (pkd_state.close_received == 0) &&
+ (ssh_channel_is_closed(c) == 0)) {
+ rc = ssh_event_dopoll(e, 1000 /* milliseconds */);
+ if (rc == SSH_ERROR) {
+ pkderr("ssh_event_dopoll for eof + close: %s\n", ssh_get_error(s));
+ break;
+ } else {
+ rc = 0;
+ }
+ }
+ goto out;
+
+outclose:
+ close(fd);
+out:
+ if (c != NULL) {
+ ssh_channel_free(c);
+ }
+ if (e != NULL) {
+ ssh_event_remove_session(e, s);
+ ssh_event_free(e);
+ }
+ if (s != NULL) {
+ ssh_disconnect(s);
+ ssh_free(s);
+ }
+ if (b != NULL) {
+ ssh_bind_free(b);
+ }
+ return rc;
+}
+
+/*
+ * main loop ------------------------------------------------------------
+ */
+
+static void *pkd_main(void *args) {
+ int rc = -1;
+ struct pkd_daemon_args *a = (struct pkd_daemon_args *) args;
+
+ struct sigaction act = { .sa_handler = pkd_sighandler, };
+
+ pkd_state.server_fd = -1;
+ pkd_state.req_exec_received = 0;
+ pkd_state.close_received = 0;
+ pkd_state.eof_received = 0;
+
+ /* SIGUSR1 is used to interrupt 'pkd_accept_fd'. */
+ rc = sigaction(SIGUSR1, &act, NULL);
+ if (rc != 0) {
+ pkderr("sigaction: %d\n", rc);
+ goto out;
+ }
+
+ rc = pkd_init_libssh();
+ if (rc != 0) {
+ pkderr("pkd_init_libssh: %d\n", rc);
+ goto out;
+ }
+
+ rc = pkd_init_server_fd(1234);
+ if (rc != 0) {
+ pkderr("pkd_init_server_fd: %d\n", rc);
+ goto out;
+ }
+
+ ctx.pkd_ready = 1;
+
+ while (ctx.keep_going != 0) {
+ int fd = pkd_accept_fd();
+ if (fd < 0) {
+ if (ctx.keep_going != 0) {
+ pkderr("pkd_accept_fd");
+ rc = -1;
+ } else {
+ rc = 0;
+ }
+ break;
+ }
+
+ rc = pkd_exec_hello(fd, a);
+ if (rc != 0) {
+ pkderr("pkd_exec_hello: %d\n", rc);
+ break;
+ }
+ }
+
+ close(pkd_state.server_fd);
+ pkd_state.server_fd = -1;
+out:
+ ctx.rc = rc;
+
+ return NULL;
+}
+
+/*
+ * pkd start and stop used by setup/teardown test scaffolding -----------
+ */
+
+int pkd_start(struct pkd_daemon_args *args) {
+ int rc = 0;
+
+ pkdout_enabled = args->opts.log_stdout;
+ pkderr_enabled = args->opts.log_stderr;
+
+ /* Initialize the pkd context. */
+ ctx.rc = -1;
+ ctx.keep_going = 1;
+ ctx.pkd_ready = 0;
+ rc = pthread_create(&ctx.tid, NULL, &pkd_main, args);
+ assert_int_equal(rc, 0);
+
+ /* Busy-spin until pkd thread is ready. */
+ while (ctx.pkd_ready == 0);
+
+ return rc;
+}
+
+void pkd_stop(struct pkd_result *out) {
+ int rc = 0;
+
+ ctx.keep_going = 0;
+
+ rc = pthread_kill(ctx.tid, SIGUSR1);
+ assert_int_equal(rc, 0);
+
+ rc = pthread_join(ctx.tid, NULL);
+ assert_int_equal(rc, 0);
+
+ assert_non_null(out);
+ out->ok = (ctx.rc == 0);
+
+ return;
+}
diff --git a/tests/pkd/pkd_daemon.h b/tests/pkd/pkd_daemon.h
new file mode 100644
index 00000000..c42573c1
--- /dev/null
+++ b/tests/pkd/pkd_daemon.h
@@ -0,0 +1,40 @@
+/*
+ * pkd_daemon.h -- tests use this interface to start, stop pkd
+ * instances and get results
+ *
+ * (c) 2014 Jon Simons
+ */
+
+#ifndef __PKD_DAEMON_H__
+#define __PKD_DAEMON_H__
+
+enum pkd_hostkey_type_e {
+ PKD_RSA,
+ PKD_DSA,
+ PKD_ECDSA
+};
+
+struct pkd_daemon_args {
+ enum pkd_hostkey_type_e type;
+ const char *hostkeypath;
+
+ struct {
+ int list;
+
+ int log_stdout;
+ int log_stderr;
+ int libssh_log_level;
+
+ const char *testname;
+ unsigned int iterations;
+ } opts;
+};
+
+struct pkd_result {
+ int ok;
+};
+
+int pkd_start(struct pkd_daemon_args *args);
+void pkd_stop(struct pkd_result *out);
+
+#endif /* __PKD_DAEMON_H__ */
diff --git a/tests/pkd/pkd_hello.c b/tests/pkd/pkd_hello.c
new file mode 100644
index 00000000..2183f897
--- /dev/null
+++ b/tests/pkd/pkd_hello.c
@@ -0,0 +1,534 @@
+/*
+ * pkd_hello.c --
+ *
+ * (c) 2014 Jon Simons
+ */
+
+#include <setjmp.h> // for cmocka
+#include <stdarg.h> // for cmocka
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h> // for cmocka
+#include <cmocka.h>
+
+#include "libssh/priv.h"
+
+#include "pkd_client.h"
+#include "pkd_daemon.h"
+#include "pkd_keyutil.h"
+#include "pkd_util.h"
+
+#define DEFAULT_ITERATIONS 10
+static struct pkd_daemon_args pkd_dargs;
+
+#ifdef HAVE_ARGP_H
+#include <argp.h>
+#define PROGNAME "pkd_hello"
+#define ARGP_PROGNAME "libssh " PROGNAME
+const char *argp_program_version = ARGP_PROGNAME " 2014-04-12";
+const char *argp_program_bug_address = "Jon Simons <jon@jonsimons.org>";
+//static char **cmdline;
+static char doc[] = \
+ "\nExample usage:\n\n"
+ " " PROGNAME "\n"
+ " Run all tests with default number of iterations.\n"
+ " " PROGNAME " --list\n"
+ " List available individual test names.\n"
+ " " PROGNAME " -i 1000 -t torture_pkd_rsa_ecdh_sha2_nistp256\n"
+ " Run only the torture_pkd_rsa_ecdh_sha2_nistp256 testcase 1000 times.\n"
+ " " PROGNAME " -v -v -v -v -e -o\n"
+ " Run all tests with maximum libssh and pkd logging.\n"
+;
+
+static struct argp_option options[] = {
+ { "stderr", 'e', NULL, 0,
+ "Emit pkd stderr messages", 0 },
+ { "list", 'l', NULL, 0,
+ "List available individual test names", 0 },
+ { "iterations", 'i', "number", 0,
+ "Run each test for the given number of iterations (default is 10)", 0 },
+ { "stdout", 'o', NULL, 0,
+ "Emit pkd stdout messages", 0 },
+ { "test", 't', "testname", 0,
+ "Run tests matching the given testname", 0 },
+ { "verbose", 'v', NULL, 0,
+ "Increase libssh verbosity (can be used multiple times)", 0 },
+ { NULL, 0, NULL, 0,
+ NULL, 0 },
+};
+
+static error_t parse_opt(int key, char *arg, struct argp_state *state) {
+ (void) arg;
+ (void) state;
+
+ switch(key) {
+ case 'e':
+ pkd_dargs.opts.log_stderr = 1;
+ break;
+ case 'l':
+ pkd_dargs.opts.list = 1;
+ break;
+ case 'i':
+ pkd_dargs.opts.iterations = atoi(arg);
+ break;
+ case 'o':
+ pkd_dargs.opts.log_stdout = 1;
+ break;
+ case 't':
+ pkd_dargs.opts.testname = arg;
+ break;
+ case 'v':
+ pkd_dargs.opts.libssh_log_level += 1;
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+static struct argp parser = {
+ options,
+ parse_opt,
+ NULL,
+ doc,
+ NULL,
+ NULL,
+ NULL
+};
+#endif /* HAVE_ARGP_H */
+
+static struct pkd_state *torture_pkd_setup(enum pkd_hostkey_type_e type,
+ const char *hostkeypath) {
+ int rc = 0;
+
+ pkd_dargs.type = type;
+ pkd_dargs.hostkeypath = hostkeypath;
+
+ rc = pkd_start(&pkd_dargs);
+ assert_int_equal(rc, 0);
+
+ return NULL;
+}
+
+static void torture_pkd_teardown(void **state) {
+ struct pkd_result result = { .ok = 0 };
+
+ (void) state;
+
+ pkd_stop(&result);
+ assert_int_equal(result.ok, 1);
+}
+
+/*
+ * one setup for each server keytype ------------------------------------
+ */
+
+static void torture_pkd_setup_noop(void **state) {
+ *state = (void *) torture_pkd_setup(PKD_RSA, NULL /*path*/);
+}
+
+static void torture_pkd_setup_rsa(void **state) {
+ setup_rsa_key();
+ *state = (void *) torture_pkd_setup(PKD_RSA, LIBSSH_RSA_TESTKEY);
+}
+
+static void torture_pkd_setup_dsa(void **state) {
+ setup_dsa_key();
+ *state = (void *) torture_pkd_setup(PKD_DSA, LIBSSH_DSA_TESTKEY);
+}
+
+static void torture_pkd_setup_ecdsa_256(void **state) {
+ setup_ecdsa_keys();
+ *state = (void *) torture_pkd_setup(PKD_ECDSA, LIBSSH_ECDSA_256_TESTKEY);
+}
+
+static void torture_pkd_setup_ecdsa_384(void **state) {
+ setup_ecdsa_keys();
+ *state = (void *) torture_pkd_setup(PKD_ECDSA, LIBSSH_ECDSA_384_TESTKEY);
+}
+
+static void torture_pkd_setup_ecdsa_521(void **state) {
+ setup_ecdsa_keys();
+ *state = (void *) torture_pkd_setup(PKD_ECDSA, LIBSSH_ECDSA_521_TESTKEY);
+}
+
+/*
+ * Test matrices: f(clientname, testname, ssh-command, setup-function, teardown-function).
+ */
+
+#define PKDTESTS_DEFAULT(f, client, cmd) \
+ /* Default passes by server key type. */ \
+ f(client, rsa_default, cmd, setup_rsa, teardown) \
+ f(client, dsa_default, cmd, setup_dsa, teardown) \
+ f(client, ecdsa_256_default, cmd, setup_ecdsa_256, teardown) \
+ f(client, ecdsa_384_default, cmd, setup_ecdsa_384, teardown) \
+ f(client, ecdsa_521_default, cmd, setup_ecdsa_521, teardown)
+
+#define PKDTESTS_KEX(f, client, kexcmd) \
+ /* Kex algorithms. */ \
+ f(client, rsa_curve25519_sha256, kexcmd("curve25519-sha256@libssh.org"), setup_rsa, teardown) \
+ f(client, rsa_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256 "), setup_rsa, teardown) \
+ f(client, rsa_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_rsa, teardown) \
+ f(client, rsa_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_rsa, teardown) \
+ f(client, dsa_curve25519_sha256, kexcmd("curve25519-sha256@libssh.org"), setup_dsa, teardown) \
+ f(client, dsa_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256 "), setup_dsa, teardown) \
+ f(client, dsa_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_dsa, teardown) \
+ f(client, dsa_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_dsa, teardown) \
+ f(client, ecdsa_256_curve25519_sha256, kexcmd("curve25519-sha256@libssh.org"), setup_ecdsa_256, teardown) \
+ f(client, ecdsa_256_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256 "), setup_ecdsa_256, teardown) \
+ f(client, ecdsa_256_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_ecdsa_256, teardown) \
+ f(client, ecdsa_256_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ecdsa_256, teardown) \
+ f(client, ecdsa_384_curve25519_sha256, kexcmd("curve25519-sha256@libssh.org"), setup_ecdsa_384, teardown) \
+ f(client, ecdsa_384_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256 "), setup_ecdsa_384, teardown) \
+ f(client, ecdsa_384_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_ecdsa_384, teardown) \
+ f(client, ecdsa_384_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ecdsa_384, teardown) \
+ f(client, ecdsa_521_curve25519_sha256, kexcmd("curve25519-sha256@libssh.org"), setup_ecdsa_521, teardown) \
+ f(client, ecdsa_521_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256 "), setup_ecdsa_521, teardown) \
+ f(client, ecdsa_521_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_ecdsa_521, teardown) \
+ f(client, ecdsa_521_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ecdsa_521, teardown)
+
+#define PKDTESTS_CIPHER(f, client, ciphercmd) \
+ /* Ciphers. */ \
+ f(client, rsa_3des_cbc, ciphercmd("3des-cbc"), setup_rsa, teardown) \
+ f(client, rsa_aes128_cbc, ciphercmd("aes128-cbc"), setup_rsa, teardown) \
+ f(client, rsa_aes128_ctr, ciphercmd("aes128-ctr"), setup_rsa, teardown) \
+ f(client, rsa_aes256_cbc, ciphercmd("aes256-cbc"), setup_rsa, teardown) \
+ f(client, rsa_aes256_ctr, ciphercmd("aes256-ctr"), setup_rsa, teardown) \
+ f(client, rsa_blowfish_cbc, ciphercmd("blowfish-cbc"), setup_rsa, teardown) \
+ f(client, dsa_3des_cbc, ciphercmd("3des-cbc"), setup_dsa, teardown) \
+ f(client, dsa_aes128_cbc, ciphercmd("aes128-cbc"), setup_dsa, teardown) \
+ f(client, dsa_aes128_ctr, ciphercmd("aes128-ctr"), setup_dsa, teardown) \
+ f(client, dsa_aes256_cbc, ciphercmd("aes256-cbc"), setup_dsa, teardown) \
+ f(client, dsa_aes256_ctr, ciphercmd("aes256-ctr"), setup_dsa, teardown) \
+ f(client, dsa_blowfish_cbc, ciphercmd("blowfish-cbc"), setup_dsa, teardown) \
+ f(client, ecdsa_256_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_256, teardown) \
+ f(client, ecdsa_256_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_256, teardown) \
+ f(client, ecdsa_256_aes128_ctr, ciphercmd("aes128-ctr"), setup_ecdsa_256, teardown) \
+ f(client, ecdsa_256_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_256, teardown) \
+ f(client, ecdsa_256_aes256_ctr, ciphercmd("aes256-ctr"), setup_ecdsa_256, teardown) \
+ f(client, ecdsa_256_blowfish_cbc, ciphercmd("blowfish-cbc"), setup_ecdsa_256, teardown) \
+ f(client, ecdsa_384_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_384, teardown) \
+ f(client, ecdsa_384_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_384, teardown) \
+ f(client, ecdsa_384_aes128_ctr, ciphercmd("aes128-ctr"), setup_ecdsa_384, teardown) \
+ f(client, ecdsa_384_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_384, teardown) \
+ f(client, ecdsa_384_aes256_ctr, ciphercmd("aes256-ctr"), setup_ecdsa_384, teardown) \
+ f(client, ecdsa_384_blowfish_cbc, ciphercmd("blowfish-cbc"), setup_ecdsa_384, teardown) \
+ f(client, ecdsa_521_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_521, teardown) \
+ f(client, ecdsa_521_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_521, teardown) \
+ f(client, ecdsa_521_aes128_ctr, ciphercmd("aes128-ctr"), setup_ecdsa_521, teardown) \
+ f(client, ecdsa_521_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_521, teardown) \
+ f(client, ecdsa_521_aes256_ctr, ciphercmd("aes256-ctr"), setup_ecdsa_521, teardown) \
+ f(client, ecdsa_521_blowfish_cbc, ciphercmd("blowfish-cbc"), setup_ecdsa_521, teardown)
+
+#define PKDTESTS_CIPHER_AES192(f, client, ciphercmd) \
+ /* Ciphers. */ \
+ f(client, rsa_aes192_cbc, ciphercmd("aes192-cbc"), setup_rsa, teardown) \
+ f(client, rsa_aes192_ctr, ciphercmd("aes192-ctr"), setup_rsa, teardown) \
+ f(client, dsa_aes192_cbc, ciphercmd("aes192-cbc"), setup_dsa, teardown) \
+ f(client, dsa_aes192_ctr, ciphercmd("aes192-ctr"), setup_dsa, teardown) \
+ f(client, ecdsa_256_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_256, teardown) \
+ f(client, ecdsa_256_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_256, teardown) \
+ f(client, ecdsa_384_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_384, teardown) \
+ f(client, ecdsa_384_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_384, teardown) \
+ f(client, ecdsa_521_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_521, teardown) \
+ f(client, ecdsa_521_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_521, teardown)
+
+#define PKDTESTS_MAC(f, client, maccmd) \
+ /* MACs. */ \
+ f(client, rsa_hmac_sha1, maccmd("hmac-sha1"), setup_rsa, teardown) \
+ f(client, dsa_hmac_sha1, maccmd("hmac-sha1"), setup_dsa, teardown) \
+ f(client, ecdsa_256_hmac_sha1, maccmd("hmac-sha1"), setup_ecdsa_256, teardown) \
+ f(client, ecdsa_384_hmac_sha1, maccmd("hmac-sha1"), setup_ecdsa_384, teardown) \
+ f(client, ecdsa_521_hmac_sha1, maccmd("hmac-sha1"), setup_ecdsa_521, teardown) \
+ f(client, rsa_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_rsa, teardown) \
+ f(client, dsa_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_dsa, teardown) \
+ f(client, ecdsa_256_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ecdsa_256, teardown) \
+ f(client, ecdsa_384_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ecdsa_384, teardown) \
+ f(client, ecdsa_521_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ecdsa_521, teardown) \
+ f(client, rsa_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_rsa, teardown) \
+ f(client, dsa_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_dsa, teardown) \
+ f(client, ecdsa_256_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ecdsa_256, teardown) \
+ f(client, ecdsa_384_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ecdsa_384, teardown) \
+ f(client, ecdsa_521_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ecdsa_521, teardown)
+
+static void torture_pkd_client_noop(void **state) {
+ struct pkd_state *pstate = (struct pkd_state *) (*state);
+ (void) pstate;
+ return;
+}
+
+static void torture_pkd_runtest(const char *testname,
+ const char *testcmd)
+{
+ int i, rc;
+ char logfile[1024] = { 0 };
+ int iterations =
+ (pkd_dargs.opts.iterations != 0) ? pkd_dargs.opts.iterations
+ : DEFAULT_ITERATIONS;
+
+ for (i = 0; i < iterations; i++) {
+ rc = system_checked(testcmd);
+ assert_int_equal(rc, 0);
+ }
+
+ /* Asserts did not trip: cleanup logs. */
+ snprintf(&logfile[0], sizeof(logfile), "%s.out", testname);
+ unlink(logfile);
+ snprintf(&logfile[0], sizeof(logfile), "%s.err", testname);
+ unlink(logfile);
+}
+
+/*
+ * Though each keytest function body is the same, separate functions are
+ * defined here to result in distinct output when running the tests.
+ */
+
+#define emit_keytest(client, testname, sshcmd, setup, teardown) \
+ static void torture_pkd_## client ## _ ## testname(void **state) { \
+ const char *tname = "torture_pkd_" #client "_" #testname; \
+ char testcmd[1024] = { 0 }; \
+ (void) state; \
+ snprintf(&testcmd[0], sizeof(testcmd), sshcmd, tname, tname); \
+ torture_pkd_runtest(tname, testcmd); \
+ }
+
+/*
+ * Actual test functions are emitted here.
+ */
+
+#define CLIENT_ID_FILE OPENSSH_DSA_TESTKEY
+PKDTESTS_DEFAULT(emit_keytest, openssh_dsa, OPENSSH_CMD)
+PKDTESTS_KEX(emit_keytest, openssh_dsa, OPENSSH_KEX_CMD)
+PKDTESTS_CIPHER(emit_keytest, openssh_dsa, OPENSSH_CIPHER_CMD)
+PKDTESTS_CIPHER_AES192(emit_keytest, openssh_dsa, OPENSSH_CIPHER_CMD)
+PKDTESTS_MAC(emit_keytest, openssh_dsa, OPENSSH_MAC_CMD)
+#undef CLIENT_ID_FILE
+
+#define CLIENT_ID_FILE OPENSSH_RSA_TESTKEY
+PKDTESTS_DEFAULT(emit_keytest, openssh_rsa, OPENSSH_CMD)
+PKDTESTS_KEX(emit_keytest, openssh_rsa, OPENSSH_KEX_CMD)
+PKDTESTS_CIPHER(emit_keytest, openssh_rsa, OPENSSH_CIPHER_CMD)
+PKDTESTS_CIPHER_AES192(emit_keytest, openssh_rsa, OPENSSH_CIPHER_CMD)
+PKDTESTS_MAC(emit_keytest, openssh_rsa, OPENSSH_MAC_CMD)
+#undef CLIENT_ID_FILE
+
+#define CLIENT_ID_FILE OPENSSH_ECDSA256_TESTKEY
+PKDTESTS_DEFAULT(emit_keytest, openssh_e256, OPENSSH_CMD)
+PKDTESTS_KEX(emit_keytest, openssh_e256, OPENSSH_KEX_CMD)
+PKDTESTS_CIPHER(emit_keytest, openssh_e256, OPENSSH_CIPHER_CMD)
+PKDTESTS_CIPHER_AES192(emit_keytest, openssh_e256, OPENSSH_CIPHER_CMD)
+PKDTESTS_MAC(emit_keytest, openssh_e256, OPENSSH_MAC_CMD)
+#undef CLIENT_ID_FILE
+
+/* Could add these passes, too: */
+//#define CLIENT_ID_FILE OPENSSH_ECDSA384_TESTKEY
+//#define CLIENT_ID_FILE OPENSSH_ECDSA521_TESTKEY
+
+#define CLIENT_ID_FILE OPENSSH_ED25519_TESTKEY
+PKDTESTS_DEFAULT(emit_keytest, openssh_ed, OPENSSH_CMD)
+PKDTESTS_KEX(emit_keytest, openssh_ed, OPENSSH_KEX_CMD)
+PKDTESTS_CIPHER(emit_keytest, openssh_ed, OPENSSH_CIPHER_CMD)
+PKDTESTS_CIPHER_AES192(emit_keytest, openssh_ed, OPENSSH_CIPHER_CMD)
+PKDTESTS_MAC(emit_keytest, openssh_ed, OPENSSH_MAC_CMD)
+#undef CLIENT_ID_FILE
+
+#define CLIENT_ID_FILE DROPBEAR_RSA_TESTKEY
+PKDTESTS_DEFAULT(emit_keytest, dropbear, DROPBEAR_CMD)
+PKDTESTS_CIPHER(emit_keytest, dropbear, DROPBEAR_CIPHER_CMD)
+PKDTESTS_MAC(emit_keytest, dropbear, DROPBEAR_MAC_CMD)
+#undef CLIENT_ID_FILE
+
+/*
+ * Define an array of testname strings mapped to their associated
+ * test function. Enables running tests individually by name from
+ * the command line.
+ */
+
+#define emit_testmap(client, testname, sshcmd, setup, teardown) \
+ { "torture_pkd_" #client "_" #testname, \
+ { emit_unit_test(client, testname, sshcmd, setup, teardown) } },
+
+#define emit_unit_test(client, testname, sshcmd, setup, teardown) \
+ unit_test_setup_teardown(torture_pkd_ ## client ## _ ## testname, \
+ torture_pkd_ ## setup, \
+ torture_pkd_ ## teardown)
+
+#define emit_unit_test_comma(client, testname, sshcmd, setup, teardown) \
+ emit_unit_test(client, testname, sshcmd, setup, teardown),
+
+struct {
+ const char *testname;
+ const UnitTest test[3]; /* requires setup + test + teardown */
+} testmap[] = {
+ /* OpenSSH */
+ PKDTESTS_DEFAULT(emit_testmap, openssh_dsa, OPENSSH_CMD)
+ PKDTESTS_KEX(emit_testmap, openssh_dsa, OPENSSH_KEX_CMD)
+ PKDTESTS_CIPHER(emit_testmap, openssh_dsa, OPENSSH_CIPHER_CMD)
+ PKDTESTS_CIPHER_AES192(emit_testmap, openssh_dsa, OPENSSH_CIPHER_CMD)
+ PKDTESTS_MAC(emit_testmap, openssh_dsa, OPENSSH_MAC_CMD)
+
+ PKDTESTS_DEFAULT(emit_testmap, openssh_rsa, OPENSSH_CMD)
+ PKDTESTS_KEX(emit_testmap, openssh_rsa, OPENSSH_KEX_CMD)
+ PKDTESTS_CIPHER(emit_testmap, openssh_rsa, OPENSSH_CIPHER_CMD)
+ PKDTESTS_CIPHER_AES192(emit_testmap, openssh_rsa, OPENSSH_CIPHER_CMD)
+ PKDTESTS_MAC(emit_testmap, openssh_rsa, OPENSSH_MAC_CMD)
+
+ PKDTESTS_DEFAULT(emit_testmap, openssh_e256, OPENSSH_CMD)
+ PKDTESTS_KEX(emit_testmap, openssh_e256, OPENSSH_KEX_CMD)
+ PKDTESTS_CIPHER(emit_testmap, openssh_e256, OPENSSH_CIPHER_CMD)
+ PKDTESTS_CIPHER_AES192(emit_testmap, openssh_e256, OPENSSH_CIPHER_CMD)
+ PKDTESTS_MAC(emit_testmap, openssh_e256, OPENSSH_MAC_CMD)
+
+ PKDTESTS_DEFAULT(emit_testmap, openssh_ed, OPENSSH_CMD)
+ PKDTESTS_KEX(emit_testmap, openssh_ed, OPENSSH_KEX_CMD)
+ PKDTESTS_CIPHER(emit_testmap, openssh_ed, OPENSSH_CIPHER_CMD)
+ PKDTESTS_CIPHER_AES192(emit_testmap, openssh_ed, OPENSSH_CIPHER_CMD)
+ PKDTESTS_MAC(emit_testmap, openssh_ed, OPENSSH_MAC_CMD)
+
+ /* Dropbear */
+ PKDTESTS_DEFAULT(emit_testmap, dropbear, DROPBEAR_CMD)
+ PKDTESTS_CIPHER(emit_testmap, dropbear, DROPBEAR_CIPHER_CMD)
+ PKDTESTS_MAC(emit_testmap, dropbear, DROPBEAR_MAC_CMD)
+
+ /* Noop */
+ emit_testmap(client, noop, "", setup_noop, teardown)
+
+ /* NULL tail entry */
+ { NULL, { { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 } } }
+};
+
+static int pkd_run_tests(void) {
+ int rc = -1;
+ int tindex = 0;
+
+ const UnitTest openssh_tests[] = {
+ PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_dsa, OPENSSH_CMD)
+ PKDTESTS_KEX(emit_unit_test_comma, openssh_dsa, OPENSSH_KEX_CMD)
+ PKDTESTS_CIPHER(emit_unit_test_comma, openssh_dsa, OPENSSH_CIPHER_CMD)
+ PKDTESTS_CIPHER_AES192(emit_unit_test_comma, openssh_dsa, OPENSSH_CIPHER_CMD)
+ PKDTESTS_MAC(emit_unit_test_comma, openssh_dsa, OPENSSH_MAC_CMD)
+
+ PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_rsa, OPENSSH_CMD)
+ PKDTESTS_KEX(emit_unit_test_comma, openssh_rsa, OPENSSH_KEX_CMD)
+ PKDTESTS_CIPHER(emit_unit_test_comma, openssh_rsa, OPENSSH_CIPHER_CMD)
+ PKDTESTS_CIPHER_AES192(emit_unit_test_comma, openssh_rsa, OPENSSH_CIPHER_CMD)
+ PKDTESTS_MAC(emit_unit_test_comma, openssh_rsa, OPENSSH_MAC_CMD)
+
+ PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_e256, OPENSSH_CMD)
+ PKDTESTS_KEX(emit_unit_test_comma, openssh_e256, OPENSSH_KEX_CMD)
+ PKDTESTS_CIPHER(emit_unit_test_comma, openssh_e256, OPENSSH_CIPHER_CMD)
+ PKDTESTS_CIPHER_AES192(emit_unit_test_comma, openssh_e256, OPENSSH_CIPHER_CMD)
+ PKDTESTS_MAC(emit_unit_test_comma, openssh_e256, OPENSSH_MAC_CMD)
+
+ PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_ed, OPENSSH_CMD)
+ PKDTESTS_KEX(emit_unit_test_comma, openssh_ed, OPENSSH_KEX_CMD)
+ PKDTESTS_CIPHER(emit_unit_test_comma, openssh_ed, OPENSSH_CIPHER_CMD)
+ PKDTESTS_CIPHER_AES192(emit_unit_test_comma, openssh_ed, OPENSSH_CIPHER_CMD)
+ PKDTESTS_MAC(emit_unit_test_comma, openssh_ed, OPENSSH_MAC_CMD)
+ };
+
+ const UnitTest dropbear_tests[] = {
+ PKDTESTS_DEFAULT(emit_unit_test_comma, dropbear, DROPBEAR_CMD)
+ PKDTESTS_CIPHER(emit_unit_test_comma, dropbear, DROPBEAR_CIPHER_CMD)
+ PKDTESTS_MAC(emit_unit_test_comma, dropbear, DROPBEAR_MAC_CMD)
+ };
+
+ const UnitTest noop_tests[] = {
+ emit_unit_test(client, noop, "", setup_noop, teardown)
+ };
+
+ /* Test list is populated depending on which clients are enabled. */
+ UnitTest all_tests[(sizeof(openssh_tests) / sizeof(openssh_tests[0])) +
+ (sizeof(dropbear_tests) / sizeof(dropbear_tests[0])) +
+ (sizeof(noop_tests) / sizeof(noop_tests[0]))];
+ memset(&all_tests[0], 0x0, sizeof(all_tests));
+
+ /* Generate client keys and populate test list for each enabled client. */
+ if (is_openssh_client_enabled()) {
+ setup_openssh_client_keys();
+ memcpy(&all_tests[tindex], &openssh_tests[0], sizeof(openssh_tests));
+ tindex += (sizeof(openssh_tests) / sizeof(openssh_tests[0]));
+ }
+
+ if (is_dropbear_client_enabled()) {
+ setup_dropbear_client_rsa_key();
+ memcpy(&all_tests[tindex], &dropbear_tests[0], sizeof(dropbear_tests));
+ tindex += (sizeof(dropbear_tests) / sizeof(dropbear_tests[0]));
+ }
+
+ memcpy(&all_tests[tindex], &noop_tests[0], sizeof(noop_tests));
+ tindex += (sizeof(noop_tests) / sizeof(noop_tests[0]));
+
+ if (pkd_dargs.opts.testname == NULL) {
+ rc = _run_tests(all_tests, tindex);
+ } else {
+ int i = 0;
+ const UnitTest *found = NULL;
+ const char *testname = pkd_dargs.opts.testname;
+
+ while (testmap[i].testname != NULL) {
+ if (strcmp(testmap[i].testname, testname) == 0) {
+ found = &testmap[i].test[0];
+ break;
+ }
+ i += 1;
+ }
+
+ if (found != NULL) {
+ rc = _run_tests(found, 3);
+ } else {
+ fprintf(stderr, "Did not find test '%s'\n", testname);
+ }
+ }
+
+ /* Clean up client keys for each enabled client. */
+ if (is_dropbear_client_enabled()) {
+ cleanup_dropbear_client_rsa_key();
+ }
+
+ if (is_openssh_client_enabled()) {
+ cleanup_openssh_client_keys();
+ }
+
+ /* Clean up any server keys that were generated. */
+ cleanup_rsa_key();
+ cleanup_dsa_key();
+ cleanup_ecdsa_keys();
+
+ return rc;
+}
+
+int main(int argc, char **argv) {
+ int i = 0;
+ int rc = 0;
+
+ unsetenv("SSH_AUTH_SOCK");
+
+ rc = ssh_init();
+ if (rc != 0) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+#ifdef HAVE_ARGP_H
+ argp_parse(&parser, argc, argv, 0, 0, NULL);
+#else /* HAVE_ARGP_H */
+ (void) argc; (void) argv;
+#endif /* HAVE_ARGP_H */
+
+ if (pkd_dargs.opts.list != 0) {
+ while (testmap[i].testname != NULL) {
+ printf("%s\n", testmap[i++].testname);
+ }
+ } else {
+ rc = pkd_run_tests();
+ }
+
+ rc = ssh_finalize();
+ if (rc != 0) {
+ fprintf(stderr, "ssh_finalize: %d\n", rc);
+ }
+out:
+ return rc;
+}
diff --git a/tests/pkd/pkd_keyutil.c b/tests/pkd/pkd_keyutil.c
new file mode 100644
index 00000000..e1e1ecb8
--- /dev/null
+++ b/tests/pkd/pkd_keyutil.c
@@ -0,0 +1,138 @@
+/*
+ * pkd_keyutil.c -- pkd test key utilities
+ *
+ * (c) 2014 Jon Simons
+ */
+
+#include <setjmp.h> // for cmocka
+#include <stdarg.h> // for cmocka
+#include <unistd.h> // for cmocka
+#include <cmocka.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pkd_client.h"
+#include "pkd_keyutil.h"
+#include "pkd_util.h"
+
+void setup_rsa_key() {
+ int rc = 0;
+ if (access(LIBSSH_RSA_TESTKEY, F_OK) != 0) {
+ rc = system_checked(OPENSSH_KEYGEN " -t rsa -q -N \"\" -f "
+ LIBSSH_RSA_TESTKEY);
+ }
+ assert_int_equal(rc, 0);
+}
+
+void setup_dsa_key() {
+ int rc = 0;
+ if (access(LIBSSH_DSA_TESTKEY, F_OK) != 0) {
+ rc = system_checked(OPENSSH_KEYGEN " -t dsa -q -N \"\" -f "
+ LIBSSH_DSA_TESTKEY);
+ }
+ assert_int_equal(rc, 0);
+}
+
+void setup_ecdsa_keys() {
+ int rc = 0;
+
+ if (access(LIBSSH_ECDSA_256_TESTKEY, F_OK) != 0) {
+ rc = system_checked(OPENSSH_KEYGEN " -t ecdsa -b 256 -q -N \"\" -f "
+ LIBSSH_ECDSA_256_TESTKEY);
+ assert_int_equal(rc, 0);
+ }
+ if (access(LIBSSH_ECDSA_384_TESTKEY, F_OK) != 0) {
+ rc = system_checked(OPENSSH_KEYGEN " -t ecdsa -b 384 -q -N \"\" -f "
+ LIBSSH_ECDSA_384_TESTKEY);
+ assert_int_equal(rc, 0);
+ }
+ if (access(LIBSSH_ECDSA_521_TESTKEY, F_OK) != 0) {
+ rc = system_checked(OPENSSH_KEYGEN " -t ecdsa -b 521 -q -N \"\" -f "
+ LIBSSH_ECDSA_521_TESTKEY);
+ assert_int_equal(rc, 0);
+ }
+}
+
+static void cleanup_key(const char *privkey, const char *pubkey) {
+ unlink(privkey);
+ unlink(pubkey);
+}
+
+void cleanup_rsa_key() {
+ cleanup_key(LIBSSH_RSA_TESTKEY, LIBSSH_RSA_TESTKEY ".pub");
+}
+
+void cleanup_dsa_key() {
+ cleanup_key(LIBSSH_DSA_TESTKEY, LIBSSH_DSA_TESTKEY ".pub");
+}
+
+void cleanup_ecdsa_keys() {
+ cleanup_key(LIBSSH_ECDSA_256_TESTKEY, LIBSSH_ECDSA_256_TESTKEY ".pub");
+ cleanup_key(LIBSSH_ECDSA_384_TESTKEY, LIBSSH_ECDSA_384_TESTKEY ".pub");
+ cleanup_key(LIBSSH_ECDSA_521_TESTKEY, LIBSSH_ECDSA_521_TESTKEY ".pub");
+}
+
+void setup_openssh_client_keys() {
+ int rc = 0;
+
+ if (access(OPENSSH_DSA_TESTKEY, F_OK) != 0) {
+ rc = system_checked(OPENSSH_KEYGEN " -t dsa -q -N \"\" -f "
+ OPENSSH_DSA_TESTKEY);
+ }
+ assert_int_equal(rc, 0);
+
+ if (access(OPENSSH_RSA_TESTKEY, F_OK) != 0) {
+ rc = system_checked(OPENSSH_KEYGEN " -t rsa -q -N \"\" -f "
+ OPENSSH_RSA_TESTKEY);
+ }
+ assert_int_equal(rc, 0);
+
+ if (access(OPENSSH_ECDSA256_TESTKEY, F_OK) != 0) {
+ rc = system_checked(OPENSSH_KEYGEN " -t ecdsa -b 256 -q -N \"\" -f "
+ OPENSSH_ECDSA256_TESTKEY);
+ }
+ assert_int_equal(rc, 0);
+
+ if (access(OPENSSH_ECDSA384_TESTKEY, F_OK) != 0) {
+ rc = system_checked(OPENSSH_KEYGEN " -t ecdsa -b 384 -q -N \"\" -f "
+ OPENSSH_ECDSA384_TESTKEY);
+ }
+ assert_int_equal(rc, 0);
+
+ if (access(OPENSSH_ECDSA521_TESTKEY, F_OK) != 0) {
+ rc = system_checked(OPENSSH_KEYGEN " -t ecdsa -b 521 -q -N \"\" -f "
+ OPENSSH_ECDSA521_TESTKEY);
+ }
+ assert_int_equal(rc, 0);
+
+ if (access(OPENSSH_ED25519_TESTKEY, F_OK) != 0) {
+ rc = system_checked(OPENSSH_KEYGEN " -t ed25519 -q -N \"\" -f "
+ OPENSSH_ED25519_TESTKEY);
+ }
+ assert_int_equal(rc, 0);
+}
+
+void cleanup_openssh_client_keys() {
+ cleanup_key(OPENSSH_DSA_TESTKEY, OPENSSH_DSA_TESTKEY ".pub");
+ cleanup_key(OPENSSH_RSA_TESTKEY, OPENSSH_RSA_TESTKEY ".pub");
+ cleanup_key(OPENSSH_ECDSA256_TESTKEY, OPENSSH_ECDSA256_TESTKEY ".pub");
+ cleanup_key(OPENSSH_ECDSA384_TESTKEY, OPENSSH_ECDSA384_TESTKEY ".pub");
+ cleanup_key(OPENSSH_ECDSA521_TESTKEY, OPENSSH_ECDSA521_TESTKEY ".pub");
+ cleanup_key(OPENSSH_ED25519_TESTKEY, OPENSSH_ED25519_TESTKEY ".pub");
+}
+
+void setup_dropbear_client_rsa_key() {
+ int rc = 0;
+ if (access(DROPBEAR_RSA_TESTKEY, F_OK) != 0) {
+ rc = system_checked(DROPBEAR_KEYGEN " -t rsa -f "
+ DROPBEAR_RSA_TESTKEY " 1>/dev/null 2>/dev/null");
+ }
+ assert_int_equal(rc, 0);
+}
+
+void cleanup_dropbear_client_rsa_key() {
+ unlink(DROPBEAR_RSA_TESTKEY);
+}
diff --git a/tests/pkd/pkd_keyutil.h b/tests/pkd/pkd_keyutil.h
new file mode 100644
index 00000000..8e9de009
--- /dev/null
+++ b/tests/pkd/pkd_keyutil.h
@@ -0,0 +1,40 @@
+/*
+ * pkd_keyutil.h --
+ *
+ * (c) 2014 Jon Simons
+ */
+
+#ifndef __PKD_KEYUTIL_H__
+#define __PKD_KEYUTIL_H__
+
+/* Server keys. */
+#define LIBSSH_DSA_TESTKEY "libssh_testkey.id_dsa"
+#define LIBSSH_RSA_TESTKEY "libssh_testkey.id_rsa"
+#define LIBSSH_ECDSA_256_TESTKEY "libssh_testkey.id_ecdsa256"
+#define LIBSSH_ECDSA_384_TESTKEY "libssh_testkey.id_ecdsa384"
+#define LIBSSH_ECDSA_521_TESTKEY "libssh_testkey.id_ecdsa521"
+
+void setup_dsa_key(void);
+void setup_rsa_key(void);
+void setup_ecdsa_keys(void);
+void cleanup_dsa_key(void);
+void cleanup_rsa_key(void);
+void cleanup_ecdsa_keys(void);
+
+/* Client keys. */
+#define OPENSSH_DSA_TESTKEY "openssh_testkey.id_dsa"
+#define OPENSSH_RSA_TESTKEY "openssh_testkey.id_rsa"
+#define OPENSSH_ECDSA256_TESTKEY "openssh_testkey.id_ecdsa256"
+#define OPENSSH_ECDSA384_TESTKEY "openssh_testkey.id_ecdsa384"
+#define OPENSSH_ECDSA521_TESTKEY "openssh_testkey.id_ecdsa521"
+#define OPENSSH_ED25519_TESTKEY "openssh_testkey.id_ed25519"
+
+#define DROPBEAR_RSA_TESTKEY "dropbear_testkey.id_rsa"
+
+void setup_openssh_client_keys(void);
+void cleanup_openssh_client_keys(void);
+
+void setup_dropbear_client_rsa_key(void);
+void cleanup_dropbear_client_rsa_key(void);
+
+#endif /* __PKD_KEYUTIL_H__ */
diff --git a/tests/pkd/pkd_util.c b/tests/pkd/pkd_util.c
new file mode 100644
index 00000000..95d3be65
--- /dev/null
+++ b/tests/pkd/pkd_util.c
@@ -0,0 +1,45 @@
+/*
+ * pkd_util.c -- pkd utilities
+ *
+ * (c) 2014 Jon Simons
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "pkd_client.h"
+#include "pkd_util.h"
+
+/**
+ * @brief runs system(3); exits if that is interrupted with SIGINT/QUIT
+ * @returns 0 upon success, non-zero otherwise
+ */
+int system_checked(const char *cmd) {
+ int rc = system(cmd);
+
+ if (WIFSIGNALED(rc) &&
+ ((WTERMSIG(rc) == SIGINT) || (WTERMSIG(rc) == SIGQUIT))) {
+ exit(1);
+ }
+
+ if (rc == -1) {
+ return -1;
+ }
+
+ return WEXITSTATUS(rc);
+}
+
+static int bin_exists(const char *binary) {
+ char bin[1024] = { 0 };
+ snprintf(&bin[0], sizeof(bin), "type %s 1>/dev/null 2>/dev/null", binary);
+ return (system_checked(bin) == 0);
+}
+
+int is_openssh_client_enabled(void) {
+ return (bin_exists(OPENSSH_BINARY) && bin_exists(OPENSSH_KEYGEN));
+}
+
+int is_dropbear_client_enabled(void) {
+ return (bin_exists(DROPBEAR_BINARY) && bin_exists(DROPBEAR_KEYGEN));
+}
diff --git a/tests/pkd/pkd_util.h b/tests/pkd/pkd_util.h
new file mode 100644
index 00000000..aedbbe9f
--- /dev/null
+++ b/tests/pkd/pkd_util.h
@@ -0,0 +1,16 @@
+/*
+ * pkd_keyutil.h --
+ *
+ * (c) 2014 Jon Simons
+ */
+
+#ifndef __PKD_UTIL_H__
+#define __PKD_UTIL_H__
+
+int system_checked(const char *cmd);
+
+/* Is client 'X' enabled? */
+int is_openssh_client_enabled(void);
+int is_dropbear_client_enabled(void);
+
+#endif /* __PKD_UTIL_H__ */