Browse Source

Split the slow unit tests into their own binary

This can run in parallel with the faster ones and the other tests.
Nick Mathewson 10 years ago
parent
commit
7322de15dc
10 changed files with 1151 additions and 1034 deletions
  1. 2 0
      .gitignore
  2. 3 0
      changes/ticket13243
  3. 20 2
      src/test/include.am
  4. 1 270
      src/test/test.c
  5. 0 393
      src/test/test_crypto.c
  6. 410 0
      src/test/test_crypto_slow.c
  7. 29 0
      src/test/test_slow.c
  8. 0 369
      src/test/test_util.c
  9. 388 0
      src/test/test_util_slow.c
  10. 298 0
      src/test/testing_common.c

+ 2 - 0
.gitignore

@@ -160,11 +160,13 @@ cscope.*
 /src/test/bench
 /src/test/bench.exe
 /src/test/test
+/src/test/test-slow
 /src/test/test-bt-cl
 /src/test/test-child
 /src/test/test-ntor-cl
 /src/test/test_workqueue
 /src/test/test.exe
+/src/test/test-slow.exe
 /src/test/test-bt-cl.exe
 /src/test/test-child.exe
 /src/test/test-ntor-cl.exe

+ 3 - 0
changes/ticket13243

@@ -0,0 +1,3 @@
+  o Testing:
+    - Move the slower unit tests into a new "./src/test/test-slow" binary
+      that can be run independently of the other tests. Closes ticket 13243.

+ 20 - 2
src/test/include.am

@@ -1,8 +1,12 @@
-TESTS += src/test/test
+TESTS += src/test/test src/test/test-slow
 
 noinst_PROGRAMS+= src/test/bench
 if UNITTESTS_ENABLED
-noinst_PROGRAMS+= src/test/test src/test/test-child src/test/test_workqueue
+noinst_PROGRAMS+= \
+	src/test/test \
+	src/test/test-slow \
+	src/test/test-child \
+	src/test/test_workqueue
 endif
 
 src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
@@ -57,8 +61,17 @@ src_test_test_SOURCES = \
 	src/test/test_status.c \
 	src/test/test_threads.c \
 	src/test/test_util.c \
+	src/test/testing_common.c \
 	src/ext/tinytest.c
 
+src_test_test_slow_SOURCES = \
+	src/test/test_slow.c \
+	src/test/test_crypto_slow.c \
+	src/test/test_util_slow.c \
+	src/test/testing_common.c \
+	src/ext/tinytest.c
+
+
 src_test_test_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
 
 src_test_test_CPPFLAGS= $(src_test_AM_CPPFLAGS)
@@ -80,6 +93,11 @@ src_test_test_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \
 	@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \
 	@TOR_SYSTEMD_LIBS@
 
+src_test_test_slow_CPPFLAGS = $(src_test_test_CPPFLAGS)
+src_test_test_slow_CFLAGS = $(src_test_test_CFLAGS)
+src_test_test_slow_LDADD = $(src_test_test_LDADD)
+src_test_test_slow_LDFLAGS = $(src_test_test_LDFLAGS)
+
 src_test_bench_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
         @TOR_LDFLAGS_libevent@
 src_test_bench_LDADD = src/or/libtor.a src/common/libor.a \

+ 1 - 270
src/test/test.c

@@ -3,10 +3,6 @@
  * Copyright (c) 2007-2015, The Tor Project, Inc. */
 /* See LICENSE for licensing information */
 
-/* Ordinarily defined in tor_main.c; this bit is just here to provide one
- * since we're not linking to tor_main.c */
-const char tor_git_revision[] = "";
-
 /**
  * \file test.c
  * \brief Unit tests for many pieces of the lower level Tor modules.
@@ -67,171 +63,6 @@ double fabs(double x);
 #include "crypto_curve25519.h"
 #include "onion_ntor.h"
 
-#ifdef USE_DMALLOC
-#include <dmalloc.h>
-#include <openssl/crypto.h>
-#include "main.h"
-#endif
-
-/** Set to true if any unit test has failed.  Mostly, this is set by the macros
- * in test.h */
-int have_failed = 0;
-
-/** Temporary directory (set up by setup_directory) under which we store all
- * our files during testing. */
-static char temp_dir[256];
-#ifdef _WIN32
-#define pid_t int
-#endif
-static pid_t temp_dir_setup_in_pid = 0;
-
-/** Select and create the temporary directory we'll use to run our unit tests.
- * Store it in <b>temp_dir</b>.  Exit immediately if we can't create it.
- * idempotent. */
-static void
-setup_directory(void)
-{
-  static int is_setup = 0;
-  int r;
-  char rnd[256], rnd32[256];
-  if (is_setup) return;
-
-/* Due to base32 limitation needs to be a multiple of 5. */
-#define RAND_PATH_BYTES 5
-  crypto_rand(rnd, RAND_PATH_BYTES);
-  base32_encode(rnd32, sizeof(rnd32), rnd, RAND_PATH_BYTES);
-
-#ifdef _WIN32
-  {
-    char buf[MAX_PATH];
-    const char *tmp = buf;
-    const char *extra_backslash = "";
-    /* If this fails, we're probably screwed anyway */
-    if (!GetTempPathA(sizeof(buf),buf))
-      tmp = "c:\\windows\\temp\\";
-    if (strcmpend(tmp, "\\")) {
-      /* According to MSDN, it should be impossible for GetTempPath to give us
-       * an answer that doesn't end with \.  But let's make sure. */
-      extra_backslash = "\\";
-    }
-    tor_snprintf(temp_dir, sizeof(temp_dir),
-                 "%s%stor_test_%d_%s", tmp, extra_backslash,
-                 (int)getpid(), rnd32);
-    r = mkdir(temp_dir);
-  }
-#else
-  tor_snprintf(temp_dir, sizeof(temp_dir), "/tmp/tor_test_%d_%s",
-               (int) getpid(), rnd32);
-  r = mkdir(temp_dir, 0700);
-  if (!r) {
-    /* undo sticky bit so tests don't get confused. */
-    r = chown(temp_dir, getuid(), getgid());
-  }
-#endif
-  if (r) {
-    fprintf(stderr, "Can't create directory %s:", temp_dir);
-    perror("");
-    exit(1);
-  }
-  is_setup = 1;
-  temp_dir_setup_in_pid = getpid();
-}
-
-/** Return a filename relative to our testing temporary directory */
-const char *
-get_fname(const char *name)
-{
-  static char buf[1024];
-  setup_directory();
-  if (!name)
-    return temp_dir;
-  tor_snprintf(buf,sizeof(buf),"%s/%s",temp_dir,name);
-  return buf;
-}
-
-/* Remove a directory and all of its subdirectories */
-static void
-rm_rf(const char *dir)
-{
-  struct stat st;
-  smartlist_t *elements;
-
-  elements = tor_listdir(dir);
-  if (elements) {
-    SMARTLIST_FOREACH_BEGIN(elements, const char *, cp) {
-         char *tmp = NULL;
-         tor_asprintf(&tmp, "%s"PATH_SEPARATOR"%s", dir, cp);
-         if (0 == stat(tmp,&st) && (st.st_mode & S_IFDIR)) {
-           rm_rf(tmp);
-         } else {
-           if (unlink(tmp)) {
-             fprintf(stderr, "Error removing %s: %s\n", tmp, strerror(errno));
-           }
-         }
-         tor_free(tmp);
-    } SMARTLIST_FOREACH_END(cp);
-    SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
-    smartlist_free(elements);
-  }
-  if (rmdir(dir))
-    fprintf(stderr, "Error removing directory %s: %s\n", dir, strerror(errno));
-}
-
-/** Remove all files stored under the temporary directory, and the directory
- * itself.  Called by atexit(). */
-static void
-remove_directory(void)
-{
-  if (getpid() != temp_dir_setup_in_pid) {
-    /* Only clean out the tempdir when the main process is exiting. */
-    return;
-  }
-
-  rm_rf(temp_dir);
-}
-
-/** Define this if unit tests spend too much time generating public keys*/
-#undef CACHE_GENERATED_KEYS
-
-static crypto_pk_t *pregen_keys[5] = {NULL, NULL, NULL, NULL, NULL};
-#define N_PREGEN_KEYS ARRAY_LENGTH(pregen_keys)
-
-/** Generate and return a new keypair for use in unit tests.  If we're using
- * the key cache optimization, we might reuse keys: we only guarantee that
- * keys made with distinct values for <b>idx</b> are different.  The value of
- * <b>idx</b> must be at least 0, and less than N_PREGEN_KEYS. */
-crypto_pk_t *
-pk_generate(int idx)
-{
-#ifdef CACHE_GENERATED_KEYS
-  tor_assert(idx < N_PREGEN_KEYS);
-  if (! pregen_keys[idx]) {
-    pregen_keys[idx] = crypto_pk_new();
-    tor_assert(!crypto_pk_generate_key(pregen_keys[idx]));
-  }
-  return crypto_pk_dup_key(pregen_keys[idx]);
-#else
-  crypto_pk_t *result;
-  (void) idx;
-  result = crypto_pk_new();
-  tor_assert(!crypto_pk_generate_key(result));
-  return result;
-#endif
-}
-
-/** Free all storage used for the cached key optimization. */
-static void
-free_pregenerated_keys(void)
-{
-  unsigned idx;
-  for (idx = 0; idx < N_PREGEN_KEYS; ++idx) {
-    if (pregen_keys[idx]) {
-      crypto_pk_free(pregen_keys[idx]);
-      pregen_keys[idx] = NULL;
-    }
-  }
-}
-
 /** Run unit tests for the onion handshake code. */
 static void
 test_onion_handshake(void *arg)
@@ -1258,23 +1089,6 @@ test_stats(void *arg)
   tor_free(s);
 }
 
-static void *
-passthrough_test_setup(const struct testcase_t *testcase)
-{
-  return testcase->setup_data;
-}
-static int
-passthrough_test_cleanup(const struct testcase_t *testcase, void *ptr)
-{
-  (void)testcase;
-  (void)ptr;
-  return 1;
-}
-
-const struct testcase_setup_t passthrough_setup = {
-  passthrough_test_setup, passthrough_test_cleanup
-};
-
 #define ENT(name)                                                       \
   { #name, test_ ## name , 0, NULL, NULL }
 #define FORK(name)                                                      \
@@ -1335,7 +1149,7 @@ extern struct testcase_t status_tests[];
 extern struct testcase_t thread_tests[];
 extern struct testcase_t util_tests[];
 
-static struct testgroup_t testgroups[] = {
+struct testgroup_t testgroups[] = {
   { "", test_array },
   { "accounting/", accounting_tests },
   { "addr/", addr_tests },
@@ -1379,86 +1193,3 @@ static struct testgroup_t testgroups[] = {
   END_OF_GROUPS
 };
 
-/** Main entry point for unit test code: parse the command line, and run
- * some unit tests. */
-int
-main(int c, const char **v)
-{
-  or_options_t *options;
-  char *errmsg = NULL;
-  int i, i_out;
-  int loglevel = LOG_ERR;
-  int accel_crypto = 0;
-
-#ifdef USE_DMALLOC
-  {
-    int r = CRYPTO_set_mem_ex_functions(tor_malloc_, tor_realloc_, tor_free_);
-    tor_assert(r);
-  }
-#endif
-
-  update_approx_time(time(NULL));
-  options = options_new();
-  tor_threads_init();
-  init_logging(1);
-  configure_backtrace_handler(get_version());
-
-  for (i_out = i = 1; i < c; ++i) {
-    if (!strcmp(v[i], "--warn")) {
-      loglevel = LOG_WARN;
-    } else if (!strcmp(v[i], "--notice")) {
-      loglevel = LOG_NOTICE;
-    } else if (!strcmp(v[i], "--info")) {
-      loglevel = LOG_INFO;
-    } else if (!strcmp(v[i], "--debug")) {
-      loglevel = LOG_DEBUG;
-    } else if (!strcmp(v[i], "--accel")) {
-      accel_crypto = 1;
-    } else {
-      v[i_out++] = v[i];
-    }
-  }
-  c = i_out;
-
-  {
-    log_severity_list_t s;
-    memset(&s, 0, sizeof(s));
-    set_log_severity_config(loglevel, LOG_ERR, &s);
-    add_stream_log(&s, "", fileno(stdout));
-  }
-
-  options->command = CMD_RUN_UNITTESTS;
-  if (crypto_global_init(accel_crypto, NULL, NULL)) {
-    printf("Can't initialize crypto subsystem; exiting.\n");
-    return 1;
-  }
-  crypto_set_tls_dh_prime(NULL);
-  crypto_seed_rng(1);
-  rep_hist_init();
-  network_init();
-  setup_directory();
-  options_init(options);
-  options->DataDirectory = tor_strdup(temp_dir);
-  options->EntryStatistics = 1;
-  if (set_options(options, &errmsg) < 0) {
-    printf("Failed to set initial options: %s\n", errmsg);
-    tor_free(errmsg);
-    return 1;
-  }
-
-  atexit(remove_directory);
-
-  have_failed = (tinytest_main(c, v, testgroups) != 0);
-
-  free_pregenerated_keys();
-#ifdef USE_DMALLOC
-  tor_free_all(0);
-  dmalloc_log_unfreed();
-#endif
-
-  if (have_failed)
-    return 1;
-  else
-    return 0;
-}
-

+ 0 - 393
src/test/test_crypto.c

@@ -5,7 +5,6 @@
 
 #include "orconfig.h"
 #define CRYPTO_CURVE25519_PRIVATE
-#define CRYPTO_S2K_PRIVATE
 #include "or.h"
 #include "test.h"
 #include "aes.h"
@@ -14,8 +13,6 @@
 #include "crypto_curve25519.h"
 #include "crypto_ed25519.h"
 #include "ed25519_vectors.inc"
-#include "crypto_s2k.h"
-#include "crypto_pwbox.h"
 
 extern const char AUTHORITY_SIGNKEY_3[];
 extern const char AUTHORITY_SIGNKEY_A_DIGEST[];
@@ -712,379 +709,6 @@ test_crypto_formats(void *arg)
   tor_free(data3);
 }
 
-/** Run unit tests for our secret-to-key passphrase hashing functionality. */
-static void
-test_crypto_s2k_rfc2440(void *arg)
-{
-  char buf[29];
-  char buf2[29];
-  char *buf3 = NULL;
-  int i;
-
-  (void)arg;
-  memset(buf, 0, sizeof(buf));
-  memset(buf2, 0, sizeof(buf2));
-  buf3 = tor_malloc(65536);
-  memset(buf3, 0, 65536);
-
-  secret_to_key_rfc2440(buf+9, 20, "", 0, buf);
-  crypto_digest(buf2+9, buf3, 1024);
-  tt_mem_op(buf,OP_EQ, buf2, 29);
-
-  memcpy(buf,"vrbacrda",8);
-  memcpy(buf2,"vrbacrda",8);
-  buf[8] = 96;
-  buf2[8] = 96;
-  secret_to_key_rfc2440(buf+9, 20, "12345678", 8, buf);
-  for (i = 0; i < 65536; i += 16) {
-    memcpy(buf3+i, "vrbacrda12345678", 16);
-  }
-  crypto_digest(buf2+9, buf3, 65536);
-  tt_mem_op(buf,OP_EQ, buf2, 29);
-
- done:
-  tor_free(buf3);
-}
-
-static void
-run_s2k_tests(const unsigned flags, const unsigned type,
-              int speclen, const int keylen, int legacy)
-{
-  uint8_t buf[S2K_MAXLEN], buf2[S2K_MAXLEN], buf3[S2K_MAXLEN];
-  int r;
-  size_t sz;
-  const char pw1[] = "You can't come in here unless you say swordfish!";
-  const char pw2[] = "Now, I give you one more guess.";
-
-  r = secret_to_key_new(buf, sizeof(buf), &sz,
-                        pw1, strlen(pw1), flags);
-  tt_int_op(r, OP_EQ, S2K_OKAY);
-  tt_int_op(buf[0], OP_EQ, type);
-
-  tt_int_op(sz, OP_EQ, keylen + speclen);
-
-  if (legacy) {
-    memmove(buf, buf+1, sz-1);
-    --sz;
-    --speclen;
-  }
-
-  tt_int_op(S2K_OKAY, OP_EQ,
-            secret_to_key_check(buf, sz, pw1, strlen(pw1)));
-
-  tt_int_op(S2K_BAD_SECRET, OP_EQ,
-            secret_to_key_check(buf, sz, pw2, strlen(pw2)));
-
-  /* Move key to buf2, and clear it. */
-  memset(buf3, 0, sizeof(buf3));
-  memcpy(buf2, buf+speclen, keylen);
-  memset(buf+speclen, 0, sz - speclen);
-
-  /* Derivekey should produce the same results. */
-  tt_int_op(S2K_OKAY, OP_EQ,
-      secret_to_key_derivekey(buf3, keylen, buf, speclen, pw1, strlen(pw1)));
-
-  tt_mem_op(buf2, OP_EQ, buf3, keylen);
-
-  /* Derivekey with a longer output should fill the output. */
-  memset(buf2, 0, sizeof(buf2));
-  tt_int_op(S2K_OKAY, OP_EQ,
-   secret_to_key_derivekey(buf2, sizeof(buf2), buf, speclen,
-                           pw1, strlen(pw1)));
-
-  tt_mem_op(buf2, OP_NE, buf3, sizeof(buf2));
-
-  memset(buf3, 0, sizeof(buf3));
-  tt_int_op(S2K_OKAY, OP_EQ,
-            secret_to_key_derivekey(buf3, sizeof(buf3), buf, speclen,
-                                    pw1, strlen(pw1)));
-  tt_mem_op(buf2, OP_EQ, buf3, sizeof(buf3));
-  tt_assert(!tor_mem_is_zero((char*)buf2+keylen, sizeof(buf2)-keylen));
-
- done:
-  ;
-}
-
-static void
-test_crypto_s2k_general(void *arg)
-{
-  const char *which = arg;
-
-  if (!strcmp(which, "scrypt")) {
-    run_s2k_tests(0, 2, 19, 32, 0);
-  } else if (!strcmp(which, "scrypt-low")) {
-    run_s2k_tests(S2K_FLAG_LOW_MEM, 2, 19, 32, 0);
-  } else if (!strcmp(which, "pbkdf2")) {
-    run_s2k_tests(S2K_FLAG_USE_PBKDF2, 1, 18, 20, 0);
-  } else if (!strcmp(which, "rfc2440")) {
-    run_s2k_tests(S2K_FLAG_NO_SCRYPT, 0, 10, 20, 0);
-  } else if (!strcmp(which, "rfc2440-legacy")) {
-    run_s2k_tests(S2K_FLAG_NO_SCRYPT, 0, 10, 20, 1);
-  } else {
-    tt_fail();
-  }
-}
-
-static void
-test_crypto_s2k_errors(void *arg)
-{
-  uint8_t buf[S2K_MAXLEN], buf2[S2K_MAXLEN];
-  size_t sz;
-
-  (void)arg;
-
-  /* Bogus specifiers: simple */
-  tt_int_op(S2K_BAD_LEN, OP_EQ,
-            secret_to_key_derivekey(buf, sizeof(buf),
-                                    (const uint8_t*)"", 0, "ABC", 3));
-  tt_int_op(S2K_BAD_ALGORITHM, OP_EQ,
-            secret_to_key_derivekey(buf, sizeof(buf),
-                                    (const uint8_t*)"\x10", 1, "ABC", 3));
-  tt_int_op(S2K_BAD_LEN, OP_EQ,
-            secret_to_key_derivekey(buf, sizeof(buf),
-                                    (const uint8_t*)"\x01\x02", 2, "ABC", 3));
-
-  tt_int_op(S2K_BAD_LEN, OP_EQ,
-            secret_to_key_check((const uint8_t*)"", 0, "ABC", 3));
-  tt_int_op(S2K_BAD_ALGORITHM, OP_EQ,
-            secret_to_key_check((const uint8_t*)"\x10", 1, "ABC", 3));
-  tt_int_op(S2K_BAD_LEN, OP_EQ,
-            secret_to_key_check((const uint8_t*)"\x01\x02", 2, "ABC", 3));
-
-  /* too long gets "BAD_LEN" too */
-  memset(buf, 0, sizeof(buf));
-  buf[0] = 2;
-  tt_int_op(S2K_BAD_LEN, OP_EQ,
-            secret_to_key_derivekey(buf2, sizeof(buf2),
-                                    buf, sizeof(buf), "ABC", 3));
-
-  /* Truncated output */
-#ifdef HAVE_LIBSCRYPT_H
-  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 50, &sz,
-                                                 "ABC", 3, 0));
-  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 50, &sz,
-                                                 "ABC", 3, S2K_FLAG_LOW_MEM));
-#endif
-  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 37, &sz,
-                                              "ABC", 3, S2K_FLAG_USE_PBKDF2));
-  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 29, &sz,
-                                              "ABC", 3, S2K_FLAG_NO_SCRYPT));
-
-#ifdef HAVE_LIBSCRYPT_H
-  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 18, 0));
-  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 18,
-                                                 S2K_FLAG_LOW_MEM));
-#endif
-  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 17,
-                                                 S2K_FLAG_USE_PBKDF2));
-  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 9,
-                                                 S2K_FLAG_NO_SCRYPT));
-
-  /* Now try using type-specific bogus specifiers. */
-
-  /* It's a bad pbkdf2 buffer if it has an iteration count that would overflow
-   * int32_t. */
-  memset(buf, 0, sizeof(buf));
-  buf[0] = 1; /* pbkdf2 */
-  buf[17] = 100; /* 1<<100 is much bigger than INT32_MAX */
-  tt_int_op(S2K_BAD_PARAMS, OP_EQ,
-            secret_to_key_derivekey(buf2, sizeof(buf2),
-                                    buf, 18, "ABC", 3));
-
-#ifdef HAVE_LIBSCRYPT_H
-  /* It's a bad scrypt buffer if N would overflow uint64 */
-  memset(buf, 0, sizeof(buf));
-  buf[0] = 2; /* scrypt */
-  buf[17] = 100; /* 1<<100 is much bigger than UINT64_MAX */
-  tt_int_op(S2K_BAD_PARAMS, OP_EQ,
-            secret_to_key_derivekey(buf2, sizeof(buf2),
-                                    buf, 19, "ABC", 3));
-#endif
-
- done:
-  ;
-}
-
-static void
-test_crypto_scrypt_vectors(void *arg)
-{
-  char *mem_op_hex_tmp = NULL;
-  uint8_t spec[64], out[64];
-
-  (void)arg;
-#ifndef HAVE_LIBSCRYPT_H
-  if (1)
-    tt_skip();
-#endif
-
-  /* Test vectors from
-     http://tools.ietf.org/html/draft-josefsson-scrypt-kdf-00 section 11.
-
-     Note that the names of 'r' and 'N' are switched in that section. Or
-     possibly in libscrypt.
-  */
-
-  base16_decode((char*)spec, sizeof(spec),
-                "0400", 4);
-  memset(out, 0x00, sizeof(out));
-  tt_int_op(64, OP_EQ,
-            secret_to_key_compute_key(out, 64, spec, 2, "", 0, 2));
-  test_memeq_hex(out,
-                 "77d6576238657b203b19ca42c18a0497"
-                 "f16b4844e3074ae8dfdffa3fede21442"
-                 "fcd0069ded0948f8326a753a0fc81f17"
-                 "e8d3e0fb2e0d3628cf35e20c38d18906");
-
-  base16_decode((char*)spec, sizeof(spec),
-                "4e61436c" "0A34", 12);
-  memset(out, 0x00, sizeof(out));
-  tt_int_op(64, OP_EQ,
-            secret_to_key_compute_key(out, 64, spec, 6, "password", 8, 2));
-  test_memeq_hex(out,
-                 "fdbabe1c9d3472007856e7190d01e9fe"
-                 "7c6ad7cbc8237830e77376634b373162"
-                 "2eaf30d92e22a3886ff109279d9830da"
-                 "c727afb94a83ee6d8360cbdfa2cc0640");
-
-  base16_decode((char*)spec, sizeof(spec),
-                "536f6469756d43686c6f72696465" "0e30", 32);
-  memset(out, 0x00, sizeof(out));
-  tt_int_op(64, OP_EQ,
-            secret_to_key_compute_key(out, 64, spec, 16,
-                                      "pleaseletmein", 13, 2));
-  test_memeq_hex(out,
-                 "7023bdcb3afd7348461c06cd81fd38eb"
-                 "fda8fbba904f8e3ea9b543f6545da1f2"
-                 "d5432955613f0fcf62d49705242a9af9"
-                 "e61e85dc0d651e40dfcf017b45575887");
-
-  base16_decode((char*)spec, sizeof(spec),
-                "536f6469756d43686c6f72696465" "1430", 32);
-  memset(out, 0x00, sizeof(out));
-  tt_int_op(64, OP_EQ,
-            secret_to_key_compute_key(out, 64, spec, 16,
-                                      "pleaseletmein", 13, 2));
-  test_memeq_hex(out,
-                 "2101cb9b6a511aaeaddbbe09cf70f881"
-                 "ec568d574a2ffd4dabe5ee9820adaa47"
-                 "8e56fd8f4ba5d09ffa1c6d927c40f4c3"
-                 "37304049e8a952fbcbf45c6fa77a41a4");
-
- done:
-  tor_free(mem_op_hex_tmp);
-}
-
-static void
-test_crypto_pbkdf2_vectors(void *arg)
-{
-  char *mem_op_hex_tmp = NULL;
-  uint8_t spec[64], out[64];
-  (void)arg;
-
-  /* Test vectors from RFC6070, section 2 */
-  base16_decode((char*)spec, sizeof(spec),
-                "73616c74" "00" , 10);
-  memset(out, 0x00, sizeof(out));
-  tt_int_op(20, OP_EQ,
-            secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
-  test_memeq_hex(out, "0c60c80f961f0e71f3a9b524af6012062fe037a6");
-
-  base16_decode((char*)spec, sizeof(spec),
-                "73616c74" "01" , 10);
-  memset(out, 0x00, sizeof(out));
-  tt_int_op(20, OP_EQ,
-            secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
-  test_memeq_hex(out, "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957");
-
-  base16_decode((char*)spec, sizeof(spec),
-                "73616c74" "0C" , 10);
-  memset(out, 0x00, sizeof(out));
-  tt_int_op(20, OP_EQ,
-            secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
-  test_memeq_hex(out, "4b007901b765489abead49d926f721d065a429c1");
-
-  base16_decode((char*)spec, sizeof(spec),
-                "73616c74" "18" , 10);
-  memset(out, 0x00, sizeof(out));
-  tt_int_op(20, OP_EQ,
-            secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
-  test_memeq_hex(out, "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984");
-
-  base16_decode((char*)spec, sizeof(spec),
-                "73616c7453414c5473616c7453414c5473616c745"
-                "3414c5473616c7453414c5473616c74" "0C" , 74);
-  memset(out, 0x00, sizeof(out));
-  tt_int_op(25, OP_EQ,
-            secret_to_key_compute_key(out, 25, spec, 37,
-                                      "passwordPASSWORDpassword", 24, 1));
-  test_memeq_hex(out, "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038");
-
-  base16_decode((char*)spec, sizeof(spec),
-                "7361006c74" "0c" , 12);
-  memset(out, 0x00, sizeof(out));
-  tt_int_op(16, OP_EQ,
-            secret_to_key_compute_key(out, 16, spec, 6, "pass\0word", 9, 1));
-  test_memeq_hex(out, "56fa6aa75548099dcc37d7f03425e0c3");
-
- done:
-  tor_free(mem_op_hex_tmp);
-}
-
-static void
-test_crypto_pwbox(void *arg)
-{
-  uint8_t *boxed=NULL, *decoded=NULL;
-  size_t len, dlen;
-  unsigned i;
-  const char msg[] = "This bunny reminds you that you still have a "
-    "salamander in your sylladex. She is holding the bunny Dave got you. "
-    "It’s sort of uncanny how similar they are, aside from the knitted "
-    "enhancements. Seriously, what are the odds?? So weird.";
-  const char pw[] = "I'm a night owl and a wise bird too";
-
-  const unsigned flags[] = { 0,
-                             S2K_FLAG_NO_SCRYPT,
-                             S2K_FLAG_LOW_MEM,
-                             S2K_FLAG_NO_SCRYPT|S2K_FLAG_LOW_MEM,
-                             S2K_FLAG_USE_PBKDF2 };
-  (void)arg;
-
-  for (i = 0; i < ARRAY_LENGTH(flags); ++i) {
-    tt_int_op(0, OP_EQ, crypto_pwbox(&boxed, &len,
-                                  (const uint8_t*)msg, strlen(msg),
-                                  pw, strlen(pw), flags[i]));
-    tt_assert(boxed);
-    tt_assert(len > 128+32);
-
-    tt_int_op(0, OP_EQ, crypto_unpwbox(&decoded, &dlen, boxed, len,
-                                    pw, strlen(pw)));
-
-    tt_assert(decoded);
-    tt_uint_op(dlen, OP_EQ, strlen(msg));
-    tt_mem_op(decoded, OP_EQ, msg, dlen);
-
-    tor_free(decoded);
-
-    tt_int_op(UNPWBOX_BAD_SECRET, OP_EQ, crypto_unpwbox(&decoded, &dlen,
-                                                     boxed, len,
-                                                     pw, strlen(pw)-1));
-    boxed[len-1] ^= 1;
-    tt_int_op(UNPWBOX_BAD_SECRET, OP_EQ, crypto_unpwbox(&decoded, &dlen,
-                                                     boxed, len,
-                                                     pw, strlen(pw)));
-    boxed[0] = 255;
-    tt_int_op(UNPWBOX_CORRUPTED, OP_EQ, crypto_unpwbox(&decoded, &dlen,
-                                                    boxed, len,
-                                                    pw, strlen(pw)));
-
-    tor_free(boxed);
-  }
-
- done:
-  tor_free(boxed);
-  tor_free(decoded);
-}
-
 /** Test AES-CTR encryption and decryption with IV. */
 static void
 test_crypto_aes_iv(void *arg)
@@ -1988,23 +1612,6 @@ struct testcase_t crypto_tests[] = {
   { "pk_fingerprints", test_crypto_pk_fingerprints, TT_FORK, NULL, NULL },
   CRYPTO_LEGACY(digests),
   CRYPTO_LEGACY(dh),
-  CRYPTO_LEGACY(s2k_rfc2440),
-#ifdef HAVE_LIBSCRYPT_H
-  { "s2k_scrypt", test_crypto_s2k_general, 0, &passthrough_setup,
-    (void*)"scrypt" },
-  { "s2k_scrypt_low", test_crypto_s2k_general, 0, &passthrough_setup,
-    (void*)"scrypt-low" },
-#endif
-  { "s2k_pbkdf2", test_crypto_s2k_general, 0, &passthrough_setup,
-    (void*)"pbkdf2" },
-  { "s2k_rfc2440_general", test_crypto_s2k_general, 0, &passthrough_setup,
-    (void*)"rfc2440" },
-  { "s2k_rfc2440_legacy", test_crypto_s2k_general, 0, &passthrough_setup,
-    (void*)"rfc2440-legacy" },
-  { "s2k_errors", test_crypto_s2k_errors, 0, NULL, NULL },
-  { "scrypt_vectors", test_crypto_scrypt_vectors, 0, NULL, NULL },
-  { "pbkdf2_vectors", test_crypto_pbkdf2_vectors, 0, NULL, NULL },
-  { "pwbox", test_crypto_pwbox, 0, NULL, NULL },
   { "aes_iv_AES", test_crypto_aes_iv, TT_FORK, &passthrough_setup,
     (void*)"aes" },
   { "aes_iv_EVP", test_crypto_aes_iv, TT_FORK, &passthrough_setup,

+ 410 - 0
src/test/test_crypto_slow.c

@@ -0,0 +1,410 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#define CRYPTO_S2K_PRIVATE
+#include "or.h"
+#include "test.h"
+#include "crypto_s2k.h"
+#include "crypto_pwbox.h"
+
+
+/** Run unit tests for our secret-to-key passphrase hashing functionality. */
+static void
+test_crypto_s2k_rfc2440(void *arg)
+{
+  char buf[29];
+  char buf2[29];
+  char *buf3 = NULL;
+  int i;
+
+  (void)arg;
+  memset(buf, 0, sizeof(buf));
+  memset(buf2, 0, sizeof(buf2));
+  buf3 = tor_malloc(65536);
+  memset(buf3, 0, 65536);
+
+  secret_to_key_rfc2440(buf+9, 20, "", 0, buf);
+  crypto_digest(buf2+9, buf3, 1024);
+  tt_mem_op(buf,OP_EQ, buf2, 29);
+
+  memcpy(buf,"vrbacrda",8);
+  memcpy(buf2,"vrbacrda",8);
+  buf[8] = 96;
+  buf2[8] = 96;
+  secret_to_key_rfc2440(buf+9, 20, "12345678", 8, buf);
+  for (i = 0; i < 65536; i += 16) {
+    memcpy(buf3+i, "vrbacrda12345678", 16);
+  }
+  crypto_digest(buf2+9, buf3, 65536);
+  tt_mem_op(buf,OP_EQ, buf2, 29);
+
+ done:
+  tor_free(buf3);
+}
+
+static void
+run_s2k_tests(const unsigned flags, const unsigned type,
+              int speclen, const int keylen, int legacy)
+{
+  uint8_t buf[S2K_MAXLEN], buf2[S2K_MAXLEN], buf3[S2K_MAXLEN];
+  int r;
+  size_t sz;
+  const char pw1[] = "You can't come in here unless you say swordfish!";
+  const char pw2[] = "Now, I give you one more guess.";
+
+  r = secret_to_key_new(buf, sizeof(buf), &sz,
+                        pw1, strlen(pw1), flags);
+  tt_int_op(r, OP_EQ, S2K_OKAY);
+  tt_int_op(buf[0], OP_EQ, type);
+
+  tt_int_op(sz, OP_EQ, keylen + speclen);
+
+  if (legacy) {
+    memmove(buf, buf+1, sz-1);
+    --sz;
+    --speclen;
+  }
+
+  tt_int_op(S2K_OKAY, OP_EQ,
+            secret_to_key_check(buf, sz, pw1, strlen(pw1)));
+
+  tt_int_op(S2K_BAD_SECRET, OP_EQ,
+            secret_to_key_check(buf, sz, pw2, strlen(pw2)));
+
+  /* Move key to buf2, and clear it. */
+  memset(buf3, 0, sizeof(buf3));
+  memcpy(buf2, buf+speclen, keylen);
+  memset(buf+speclen, 0, sz - speclen);
+
+  /* Derivekey should produce the same results. */
+  tt_int_op(S2K_OKAY, OP_EQ,
+      secret_to_key_derivekey(buf3, keylen, buf, speclen, pw1, strlen(pw1)));
+
+  tt_mem_op(buf2, OP_EQ, buf3, keylen);
+
+  /* Derivekey with a longer output should fill the output. */
+  memset(buf2, 0, sizeof(buf2));
+  tt_int_op(S2K_OKAY, OP_EQ,
+   secret_to_key_derivekey(buf2, sizeof(buf2), buf, speclen,
+                           pw1, strlen(pw1)));
+
+  tt_mem_op(buf2, OP_NE, buf3, sizeof(buf2));
+
+  memset(buf3, 0, sizeof(buf3));
+  tt_int_op(S2K_OKAY, OP_EQ,
+            secret_to_key_derivekey(buf3, sizeof(buf3), buf, speclen,
+                                    pw1, strlen(pw1)));
+  tt_mem_op(buf2, OP_EQ, buf3, sizeof(buf3));
+  tt_assert(!tor_mem_is_zero((char*)buf2+keylen, sizeof(buf2)-keylen));
+
+ done:
+  ;
+}
+
+static void
+test_crypto_s2k_general(void *arg)
+{
+  const char *which = arg;
+
+  if (!strcmp(which, "scrypt")) {
+    run_s2k_tests(0, 2, 19, 32, 0);
+  } else if (!strcmp(which, "scrypt-low")) {
+    run_s2k_tests(S2K_FLAG_LOW_MEM, 2, 19, 32, 0);
+  } else if (!strcmp(which, "pbkdf2")) {
+    run_s2k_tests(S2K_FLAG_USE_PBKDF2, 1, 18, 20, 0);
+  } else if (!strcmp(which, "rfc2440")) {
+    run_s2k_tests(S2K_FLAG_NO_SCRYPT, 0, 10, 20, 0);
+  } else if (!strcmp(which, "rfc2440-legacy")) {
+    run_s2k_tests(S2K_FLAG_NO_SCRYPT, 0, 10, 20, 1);
+  } else {
+    tt_fail();
+  }
+}
+
+static void
+test_crypto_s2k_errors(void *arg)
+{
+  uint8_t buf[S2K_MAXLEN], buf2[S2K_MAXLEN];
+  size_t sz;
+
+  (void)arg;
+
+  /* Bogus specifiers: simple */
+  tt_int_op(S2K_BAD_LEN, OP_EQ,
+            secret_to_key_derivekey(buf, sizeof(buf),
+                                    (const uint8_t*)"", 0, "ABC", 3));
+  tt_int_op(S2K_BAD_ALGORITHM, OP_EQ,
+            secret_to_key_derivekey(buf, sizeof(buf),
+                                    (const uint8_t*)"\x10", 1, "ABC", 3));
+  tt_int_op(S2K_BAD_LEN, OP_EQ,
+            secret_to_key_derivekey(buf, sizeof(buf),
+                                    (const uint8_t*)"\x01\x02", 2, "ABC", 3));
+
+  tt_int_op(S2K_BAD_LEN, OP_EQ,
+            secret_to_key_check((const uint8_t*)"", 0, "ABC", 3));
+  tt_int_op(S2K_BAD_ALGORITHM, OP_EQ,
+            secret_to_key_check((const uint8_t*)"\x10", 1, "ABC", 3));
+  tt_int_op(S2K_BAD_LEN, OP_EQ,
+            secret_to_key_check((const uint8_t*)"\x01\x02", 2, "ABC", 3));
+
+  /* too long gets "BAD_LEN" too */
+  memset(buf, 0, sizeof(buf));
+  buf[0] = 2;
+  tt_int_op(S2K_BAD_LEN, OP_EQ,
+            secret_to_key_derivekey(buf2, sizeof(buf2),
+                                    buf, sizeof(buf), "ABC", 3));
+
+  /* Truncated output */
+#ifdef HAVE_LIBSCRYPT_H
+  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 50, &sz,
+                                                 "ABC", 3, 0));
+  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 50, &sz,
+                                                 "ABC", 3, S2K_FLAG_LOW_MEM));
+#endif
+  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 37, &sz,
+                                              "ABC", 3, S2K_FLAG_USE_PBKDF2));
+  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 29, &sz,
+                                              "ABC", 3, S2K_FLAG_NO_SCRYPT));
+
+#ifdef HAVE_LIBSCRYPT_H
+  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 18, 0));
+  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 18,
+                                                 S2K_FLAG_LOW_MEM));
+#endif
+  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 17,
+                                                 S2K_FLAG_USE_PBKDF2));
+  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 9,
+                                                 S2K_FLAG_NO_SCRYPT));
+
+  /* Now try using type-specific bogus specifiers. */
+
+  /* It's a bad pbkdf2 buffer if it has an iteration count that would overflow
+   * int32_t. */
+  memset(buf, 0, sizeof(buf));
+  buf[0] = 1; /* pbkdf2 */
+  buf[17] = 100; /* 1<<100 is much bigger than INT32_MAX */
+  tt_int_op(S2K_BAD_PARAMS, OP_EQ,
+            secret_to_key_derivekey(buf2, sizeof(buf2),
+                                    buf, 18, "ABC", 3));
+
+#ifdef HAVE_LIBSCRYPT_H
+  /* It's a bad scrypt buffer if N would overflow uint64 */
+  memset(buf, 0, sizeof(buf));
+  buf[0] = 2; /* scrypt */
+  buf[17] = 100; /* 1<<100 is much bigger than UINT64_MAX */
+  tt_int_op(S2K_BAD_PARAMS, OP_EQ,
+            secret_to_key_derivekey(buf2, sizeof(buf2),
+                                    buf, 19, "ABC", 3));
+#endif
+
+ done:
+  ;
+}
+
+static void
+test_crypto_scrypt_vectors(void *arg)
+{
+  char *mem_op_hex_tmp = NULL;
+  uint8_t spec[64], out[64];
+
+  (void)arg;
+#ifndef HAVE_LIBSCRYPT_H
+  if (1)
+    tt_skip();
+#endif
+
+  /* Test vectors from
+     http://tools.ietf.org/html/draft-josefsson-scrypt-kdf-00 section 11.
+
+     Note that the names of 'r' and 'N' are switched in that section. Or
+     possibly in libscrypt.
+  */
+
+  base16_decode((char*)spec, sizeof(spec),
+                "0400", 4);
+  memset(out, 0x00, sizeof(out));
+  tt_int_op(64, OP_EQ,
+            secret_to_key_compute_key(out, 64, spec, 2, "", 0, 2));
+  test_memeq_hex(out,
+                 "77d6576238657b203b19ca42c18a0497"
+                 "f16b4844e3074ae8dfdffa3fede21442"
+                 "fcd0069ded0948f8326a753a0fc81f17"
+                 "e8d3e0fb2e0d3628cf35e20c38d18906");
+
+  base16_decode((char*)spec, sizeof(spec),
+                "4e61436c" "0A34", 12);
+  memset(out, 0x00, sizeof(out));
+  tt_int_op(64, OP_EQ,
+            secret_to_key_compute_key(out, 64, spec, 6, "password", 8, 2));
+  test_memeq_hex(out,
+                 "fdbabe1c9d3472007856e7190d01e9fe"
+                 "7c6ad7cbc8237830e77376634b373162"
+                 "2eaf30d92e22a3886ff109279d9830da"
+                 "c727afb94a83ee6d8360cbdfa2cc0640");
+
+  base16_decode((char*)spec, sizeof(spec),
+                "536f6469756d43686c6f72696465" "0e30", 32);
+  memset(out, 0x00, sizeof(out));
+  tt_int_op(64, OP_EQ,
+            secret_to_key_compute_key(out, 64, spec, 16,
+                                      "pleaseletmein", 13, 2));
+  test_memeq_hex(out,
+                 "7023bdcb3afd7348461c06cd81fd38eb"
+                 "fda8fbba904f8e3ea9b543f6545da1f2"
+                 "d5432955613f0fcf62d49705242a9af9"
+                 "e61e85dc0d651e40dfcf017b45575887");
+
+  base16_decode((char*)spec, sizeof(spec),
+                "536f6469756d43686c6f72696465" "1430", 32);
+  memset(out, 0x00, sizeof(out));
+  tt_int_op(64, OP_EQ,
+            secret_to_key_compute_key(out, 64, spec, 16,
+                                      "pleaseletmein", 13, 2));
+  test_memeq_hex(out,
+                 "2101cb9b6a511aaeaddbbe09cf70f881"
+                 "ec568d574a2ffd4dabe5ee9820adaa47"
+                 "8e56fd8f4ba5d09ffa1c6d927c40f4c3"
+                 "37304049e8a952fbcbf45c6fa77a41a4");
+
+ done:
+  tor_free(mem_op_hex_tmp);
+}
+
+static void
+test_crypto_pbkdf2_vectors(void *arg)
+{
+  char *mem_op_hex_tmp = NULL;
+  uint8_t spec[64], out[64];
+  (void)arg;
+
+  /* Test vectors from RFC6070, section 2 */
+  base16_decode((char*)spec, sizeof(spec),
+                "73616c74" "00" , 10);
+  memset(out, 0x00, sizeof(out));
+  tt_int_op(20, OP_EQ,
+            secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
+  test_memeq_hex(out, "0c60c80f961f0e71f3a9b524af6012062fe037a6");
+
+  base16_decode((char*)spec, sizeof(spec),
+                "73616c74" "01" , 10);
+  memset(out, 0x00, sizeof(out));
+  tt_int_op(20, OP_EQ,
+            secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
+  test_memeq_hex(out, "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957");
+
+  base16_decode((char*)spec, sizeof(spec),
+                "73616c74" "0C" , 10);
+  memset(out, 0x00, sizeof(out));
+  tt_int_op(20, OP_EQ,
+            secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
+  test_memeq_hex(out, "4b007901b765489abead49d926f721d065a429c1");
+
+  base16_decode((char*)spec, sizeof(spec),
+                "73616c74" "18" , 10);
+  memset(out, 0x00, sizeof(out));
+  tt_int_op(20, OP_EQ,
+            secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
+  test_memeq_hex(out, "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984");
+
+  base16_decode((char*)spec, sizeof(spec),
+                "73616c7453414c5473616c7453414c5473616c745"
+                "3414c5473616c7453414c5473616c74" "0C" , 74);
+  memset(out, 0x00, sizeof(out));
+  tt_int_op(25, OP_EQ,
+            secret_to_key_compute_key(out, 25, spec, 37,
+                                      "passwordPASSWORDpassword", 24, 1));
+  test_memeq_hex(out, "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038");
+
+  base16_decode((char*)spec, sizeof(spec),
+                "7361006c74" "0c" , 12);
+  memset(out, 0x00, sizeof(out));
+  tt_int_op(16, OP_EQ,
+            secret_to_key_compute_key(out, 16, spec, 6, "pass\0word", 9, 1));
+  test_memeq_hex(out, "56fa6aa75548099dcc37d7f03425e0c3");
+
+ done:
+  tor_free(mem_op_hex_tmp);
+}
+
+static void
+test_crypto_pwbox(void *arg)
+{
+  uint8_t *boxed=NULL, *decoded=NULL;
+  size_t len, dlen;
+  unsigned i;
+  const char msg[] = "This bunny reminds you that you still have a "
+    "salamander in your sylladex. She is holding the bunny Dave got you. "
+    "It’s sort of uncanny how similar they are, aside from the knitted "
+    "enhancements. Seriously, what are the odds?? So weird.";
+  const char pw[] = "I'm a night owl and a wise bird too";
+
+  const unsigned flags[] = { 0,
+                             S2K_FLAG_NO_SCRYPT,
+                             S2K_FLAG_LOW_MEM,
+                             S2K_FLAG_NO_SCRYPT|S2K_FLAG_LOW_MEM,
+                             S2K_FLAG_USE_PBKDF2 };
+  (void)arg;
+
+  for (i = 0; i < ARRAY_LENGTH(flags); ++i) {
+    tt_int_op(0, OP_EQ, crypto_pwbox(&boxed, &len,
+                                  (const uint8_t*)msg, strlen(msg),
+                                  pw, strlen(pw), flags[i]));
+    tt_assert(boxed);
+    tt_assert(len > 128+32);
+
+    tt_int_op(0, OP_EQ, crypto_unpwbox(&decoded, &dlen, boxed, len,
+                                    pw, strlen(pw)));
+
+    tt_assert(decoded);
+    tt_uint_op(dlen, OP_EQ, strlen(msg));
+    tt_mem_op(decoded, OP_EQ, msg, dlen);
+
+    tor_free(decoded);
+
+    tt_int_op(UNPWBOX_BAD_SECRET, OP_EQ, crypto_unpwbox(&decoded, &dlen,
+                                                     boxed, len,
+                                                     pw, strlen(pw)-1));
+    boxed[len-1] ^= 1;
+    tt_int_op(UNPWBOX_BAD_SECRET, OP_EQ, crypto_unpwbox(&decoded, &dlen,
+                                                     boxed, len,
+                                                     pw, strlen(pw)));
+    boxed[0] = 255;
+    tt_int_op(UNPWBOX_CORRUPTED, OP_EQ, crypto_unpwbox(&decoded, &dlen,
+                                                    boxed, len,
+                                                    pw, strlen(pw)));
+
+    tor_free(boxed);
+  }
+
+ done:
+  tor_free(boxed);
+  tor_free(decoded);
+}
+
+#define CRYPTO_LEGACY(name)                                            \
+  { #name, test_crypto_ ## name , 0, NULL, NULL }
+
+struct testcase_t slow_crypto_tests[] = {
+  CRYPTO_LEGACY(s2k_rfc2440),
+#ifdef HAVE_LIBSCRYPT_H
+  { "s2k_scrypt", test_crypto_s2k_general, 0, &passthrough_setup,
+    (void*)"scrypt" },
+  { "s2k_scrypt_low", test_crypto_s2k_general, 0, &passthrough_setup,
+    (void*)"scrypt-low" },
+#endif
+  { "s2k_pbkdf2", test_crypto_s2k_general, 0, &passthrough_setup,
+    (void*)"pbkdf2" },
+  { "s2k_rfc2440_general", test_crypto_s2k_general, 0, &passthrough_setup,
+    (void*)"rfc2440" },
+  { "s2k_rfc2440_legacy", test_crypto_s2k_general, 0, &passthrough_setup,
+    (void*)"rfc2440-legacy" },
+  { "s2k_errors", test_crypto_s2k_errors, 0, NULL, NULL },
+  { "scrypt_vectors", test_crypto_scrypt_vectors, 0, NULL, NULL },
+  { "pbkdf2_vectors", test_crypto_pbkdf2_vectors, 0, NULL, NULL },
+  { "pwbox", test_crypto_pwbox, 0, NULL, NULL },
+  END_OF_TESTCASES
+};
+

+ 29 - 0
src/test/test_slow.c

@@ -0,0 +1,29 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_slow.c
+ * \brief Slower unit tests for many pieces of the lower level Tor modules.
+ **/
+
+#include "orconfig.h"
+
+#include <stdio.h>
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include "or.h"
+#include "test.h"
+
+extern struct testcase_t slow_crypto_tests[];
+extern struct testcase_t slow_util_tests[];
+
+struct testgroup_t testgroups[] = {
+  { "slow/crypto/", slow_crypto_tests },
+  { "slow/util/", slow_util_tests },
+  END_OF_GROUPS
+};
+

+ 0 - 369
src/test/test_util.c

@@ -3380,370 +3380,6 @@ test_util_fgets_eagain(void *ptr)
 }
 #endif
 
-#ifndef BUILDDIR
-#define BUILDDIR "."
-#endif
-
-#ifdef _WIN32
-#define notify_pending_waitpid_callbacks() STMT_NIL
-#define TEST_CHILD "test-child.exe"
-#define EOL "\r\n"
-#else
-#define TEST_CHILD (BUILDDIR "/src/test/test-child")
-#define EOL "\n"
-#endif
-
-#ifdef _WIN32
-/* I've assumed Windows doesn't have the gap between fork and exec
- * that causes the race condition on unix-like platforms */
-#define MATCH_PROCESS_STATUS(s1,s2)         ((s1) == (s2))
-
-#else
-/* work around a race condition of the timing of SIGCHLD handler updates
- * to the process_handle's fields, and checks of those fields
- *
- * TODO: Once we can signal failure to exec, change PROCESS_STATUS_RUNNING to
- * PROCESS_STATUS_ERROR (and similarly with *_OR_NOTRUNNING) */
-#define PROCESS_STATUS_RUNNING_OR_NOTRUNNING  (PROCESS_STATUS_RUNNING+1)
-#define IS_RUNNING_OR_NOTRUNNING(s)           \
-  ((s) == PROCESS_STATUS_RUNNING || (s) == PROCESS_STATUS_NOTRUNNING)
-/* well, this is ugly */
-#define MATCH_PROCESS_STATUS(s1,s2)           \
-  (  (s1) == (s2)                                         \
-     ||((s1) == PROCESS_STATUS_RUNNING_OR_NOTRUNNING      \
-        && IS_RUNNING_OR_NOTRUNNING(s2))                  \
-     ||((s2) == PROCESS_STATUS_RUNNING_OR_NOTRUNNING      \
-        && IS_RUNNING_OR_NOTRUNNING(s1)))
-
-#endif // _WIN32
-
-/** Helper function for testing tor_spawn_background */
-static void
-run_util_spawn_background(const char *argv[], const char *expected_out,
-                          const char *expected_err, int expected_exit,
-                          int expected_status)
-{
-  int retval, exit_code;
-  ssize_t pos;
-  process_handle_t *process_handle=NULL;
-  char stdout_buf[100], stderr_buf[100];
-  int status;
-
-  /* Start the program */
-#ifdef _WIN32
-  status = tor_spawn_background(NULL, argv, NULL, &process_handle);
-#else
-  status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
-#endif
-
-  notify_pending_waitpid_callbacks();
-
-  /* the race condition doesn't affect status,
-   * because status isn't updated by the SIGCHLD handler,
-   * but we still need to handle PROCESS_STATUS_RUNNING_OR_NOTRUNNING */
-  tt_assert(MATCH_PROCESS_STATUS(expected_status, status));
-  if (status == PROCESS_STATUS_ERROR) {
-    tt_ptr_op(process_handle, OP_EQ, NULL);
-    return;
-  }
-
-  tt_assert(process_handle != NULL);
-
-  /* When a spawned process forks, fails, then exits very quickly,
-   * (this typically occurs when exec fails)
-   * there is a race condition between the SIGCHLD handler
-   * updating the process_handle's fields, and this test
-   * checking the process status in those fields.
-   * The SIGCHLD update can occur before or after the code below executes.
-   * This causes intermittent failures in spawn_background_fail(),
-   * typically when the machine is under load.
-   * We use PROCESS_STATUS_RUNNING_OR_NOTRUNNING to avoid this issue. */
-
-  /* the race condition affects the change in
-   * process_handle->status from RUNNING to NOTRUNNING */
-  tt_assert(MATCH_PROCESS_STATUS(expected_status, process_handle->status));
-
-#ifndef _WIN32
-  notify_pending_waitpid_callbacks();
-  /* the race condition affects the change in
-   * process_handle->waitpid_cb to NULL,
-   * so we skip the check if expected_status is ambiguous,
-   * that is, PROCESS_STATUS_RUNNING_OR_NOTRUNNING */
-  tt_assert(process_handle->waitpid_cb != NULL
-              || expected_status == PROCESS_STATUS_RUNNING_OR_NOTRUNNING);
-#endif
-
-#ifdef _WIN32
-  tt_assert(process_handle->stdout_pipe != INVALID_HANDLE_VALUE);
-  tt_assert(process_handle->stderr_pipe != INVALID_HANDLE_VALUE);
-#else
-  tt_assert(process_handle->stdout_pipe >= 0);
-  tt_assert(process_handle->stderr_pipe >= 0);
-#endif
-
-  /* Check stdout */
-  pos = tor_read_all_from_process_stdout(process_handle, stdout_buf,
-                                         sizeof(stdout_buf) - 1);
-  tt_assert(pos >= 0);
-  stdout_buf[pos] = '\0';
-  tt_int_op(strlen(expected_out),OP_EQ, pos);
-  tt_str_op(expected_out,OP_EQ, stdout_buf);
-
-  notify_pending_waitpid_callbacks();
-
-  /* Check it terminated correctly */
-  retval = tor_get_exit_code(process_handle, 1, &exit_code);
-  tt_int_op(PROCESS_EXIT_EXITED,OP_EQ, retval);
-  tt_int_op(expected_exit,OP_EQ, exit_code);
-  // TODO: Make test-child exit with something other than 0
-
-#ifndef _WIN32
-  notify_pending_waitpid_callbacks();
-  tt_ptr_op(process_handle->waitpid_cb, OP_EQ, NULL);
-#endif
-
-  /* Check stderr */
-  pos = tor_read_all_from_process_stderr(process_handle, stderr_buf,
-                                         sizeof(stderr_buf) - 1);
-  tt_assert(pos >= 0);
-  stderr_buf[pos] = '\0';
-  tt_str_op(expected_err,OP_EQ, stderr_buf);
-  tt_int_op(strlen(expected_err),OP_EQ, pos);
-
-  notify_pending_waitpid_callbacks();
-
- done:
-  if (process_handle)
-    tor_process_handle_destroy(process_handle, 1);
-}
-
-/** Check that we can launch a process and read the output */
-static void
-test_util_spawn_background_ok(void *ptr)
-{
-  const char *argv[] = {TEST_CHILD, "--test", NULL};
-  const char *expected_out = "OUT"EOL "--test"EOL "SLEEPING"EOL "DONE" EOL;
-  const char *expected_err = "ERR"EOL;
-
-  (void)ptr;
-
-  run_util_spawn_background(argv, expected_out, expected_err, 0,
-      PROCESS_STATUS_RUNNING);
-}
-
-/** Check that failing to find the executable works as expected */
-static void
-test_util_spawn_background_fail(void *ptr)
-{
-  const char *argv[] = {BUILDDIR "/src/test/no-such-file", "--test", NULL};
-  const char *expected_err = "";
-  char expected_out[1024];
-  char code[32];
-#ifdef _WIN32
-  const int expected_status = PROCESS_STATUS_ERROR;
-#else
-  /* TODO: Once we can signal failure to exec, set this to be
-   * PROCESS_STATUS_RUNNING_OR_ERROR */
-  const int expected_status = PROCESS_STATUS_RUNNING_OR_NOTRUNNING;
-#endif
-
-  memset(expected_out, 0xf0, sizeof(expected_out));
-  memset(code, 0xf0, sizeof(code));
-
-  (void)ptr;
-
-  tor_snprintf(code, sizeof(code), "%x/%x",
-    9 /* CHILD_STATE_FAILEXEC */ , ENOENT);
-  tor_snprintf(expected_out, sizeof(expected_out),
-    "ERR: Failed to spawn background process - code %s\n", code);
-
-  run_util_spawn_background(argv, expected_out, expected_err, 255,
-      expected_status);
-}
-
-/** Test that reading from a handle returns a partial read rather than
- * blocking */
-static void
-test_util_spawn_background_partial_read_impl(int exit_early)
-{
-  const int expected_exit = 0;
-  const int expected_status = PROCESS_STATUS_RUNNING;
-
-  int retval, exit_code;
-  ssize_t pos = -1;
-  process_handle_t *process_handle=NULL;
-  int status;
-  char stdout_buf[100], stderr_buf[100];
-
-  const char *argv[] = {TEST_CHILD, "--test", NULL};
-  const char *expected_out[] = { "OUT" EOL "--test" EOL "SLEEPING" EOL,
-                                 "DONE" EOL,
-                                 NULL };
-  const char *expected_err = "ERR" EOL;
-
-#ifndef _WIN32
-  int eof = 0;
-#endif
-  int expected_out_ctr;
-
-  if (exit_early) {
-    argv[1] = "--hang";
-    expected_out[0] = "OUT"EOL "--hang"EOL "SLEEPING" EOL;
-  }
-
-  /* Start the program */
-#ifdef _WIN32
-  status = tor_spawn_background(NULL, argv, NULL, &process_handle);
-#else
-  status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
-#endif
-  tt_int_op(expected_status,OP_EQ, status);
-  tt_assert(process_handle);
-  tt_int_op(expected_status,OP_EQ, process_handle->status);
-
-  /* Check stdout */
-  for (expected_out_ctr = 0; expected_out[expected_out_ctr] != NULL;) {
-#ifdef _WIN32
-    pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
-                              sizeof(stdout_buf) - 1, NULL);
-#else
-    /* Check that we didn't read the end of file last time */
-    tt_assert(!eof);
-    pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf,
-                              sizeof(stdout_buf) - 1, NULL, &eof);
-#endif
-    log_info(LD_GENERAL, "tor_read_all_handle() returned %d", (int)pos);
-
-    /* We would have blocked, keep on trying */
-    if (0 == pos)
-      continue;
-
-    tt_assert(pos > 0);
-    stdout_buf[pos] = '\0';
-    tt_str_op(expected_out[expected_out_ctr],OP_EQ, stdout_buf);
-    tt_int_op(strlen(expected_out[expected_out_ctr]),OP_EQ, pos);
-    expected_out_ctr++;
-  }
-
-  if (exit_early) {
-    tor_process_handle_destroy(process_handle, 1);
-    process_handle = NULL;
-    goto done;
-  }
-
-  /* The process should have exited without writing more */
-#ifdef _WIN32
-  pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
-                            sizeof(stdout_buf) - 1,
-                            process_handle);
-  tt_int_op(0,OP_EQ, pos);
-#else
-  if (!eof) {
-    /* We should have got all the data, but maybe not the EOF flag */
-    pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf,
-                              sizeof(stdout_buf) - 1,
-                              process_handle, &eof);
-    tt_int_op(0,OP_EQ, pos);
-    tt_assert(eof);
-  }
-  /* Otherwise, we got the EOF on the last read */
-#endif
-
-  /* Check it terminated correctly */
-  retval = tor_get_exit_code(process_handle, 1, &exit_code);
-  tt_int_op(PROCESS_EXIT_EXITED,OP_EQ, retval);
-  tt_int_op(expected_exit,OP_EQ, exit_code);
-
-  // TODO: Make test-child exit with something other than 0
-
-  /* Check stderr */
-  pos = tor_read_all_from_process_stderr(process_handle, stderr_buf,
-                                         sizeof(stderr_buf) - 1);
-  tt_assert(pos >= 0);
-  stderr_buf[pos] = '\0';
-  tt_str_op(expected_err,OP_EQ, stderr_buf);
-  tt_int_op(strlen(expected_err),OP_EQ, pos);
-
- done:
-  tor_process_handle_destroy(process_handle, 1);
-}
-
-static void
-test_util_spawn_background_partial_read(void *arg)
-{
-  (void)arg;
-  test_util_spawn_background_partial_read_impl(0);
-}
-
-static void
-test_util_spawn_background_exit_early(void *arg)
-{
-  (void)arg;
-  test_util_spawn_background_partial_read_impl(1);
-}
-
-static void
-test_util_spawn_background_waitpid_notify(void *arg)
-{
-  int retval, exit_code;
-  process_handle_t *process_handle=NULL;
-  int status;
-  int ms_timer;
-
-  const char *argv[] = {TEST_CHILD, "--fast", NULL};
-
-  (void) arg;
-
-#ifdef _WIN32
-  status = tor_spawn_background(NULL, argv, NULL, &process_handle);
-#else
-  status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
-#endif
-
-  tt_int_op(status, OP_EQ, PROCESS_STATUS_RUNNING);
-  tt_ptr_op(process_handle, OP_NE, NULL);
-
-  /* We're not going to look at the stdout/stderr output this time. Instead,
-   * we're testing whether notify_pending_waitpid_calbacks() can report the
-   * process exit (on unix) and/or whether tor_get_exit_code() can notice it
-   * (on windows) */
-
-#ifndef _WIN32
-  ms_timer = 30*1000;
-  tt_ptr_op(process_handle->waitpid_cb, OP_NE, NULL);
-  while (process_handle->waitpid_cb && ms_timer > 0) {
-    tor_sleep_msec(100);
-    ms_timer -= 100;
-    notify_pending_waitpid_callbacks();
-  }
-  tt_int_op(ms_timer, OP_GT, 0);
-  tt_ptr_op(process_handle->waitpid_cb, OP_EQ, NULL);
-#endif
-
-  ms_timer = 30*1000;
-  while (((retval = tor_get_exit_code(process_handle, 0, &exit_code))
-                == PROCESS_EXIT_RUNNING) && ms_timer > 0) {
-    tor_sleep_msec(100);
-    ms_timer -= 100;
-  }
-  tt_int_op(ms_timer, OP_GT, 0);
-
-  tt_int_op(retval, OP_EQ, PROCESS_EXIT_EXITED);
-
- done:
-  tor_process_handle_destroy(process_handle, 1);
-}
-
-#undef TEST_CHILD
-#undef EOL
-
-#undef MATCH_PROCESS_STATUS
-
-#ifndef _WIN32
-#undef PROCESS_STATUS_RUNNING_OR_NOTRUNNING
-#undef IS_RUNNING_OR_NOTRUNNING
-#endif
-
 /**
  * Test for format_hex_number_sigsafe()
  */
@@ -4796,11 +4432,6 @@ struct testcase_t util_tests[] = {
   UTIL_TEST(exit_status, 0),
   UTIL_TEST(fgets_eagain, 0),
 #endif
-  UTIL_TEST(spawn_background_ok, 0),
-  UTIL_TEST(spawn_background_fail, 0),
-  UTIL_TEST(spawn_background_partial_read, 0),
-  UTIL_TEST(spawn_background_exit_early, 0),
-  UTIL_TEST(spawn_background_waitpid_notify, 0),
   UTIL_TEST(format_hex_number, 0),
   UTIL_TEST(format_dec_number, 0),
   UTIL_TEST(join_win_cmdline, 0),

+ 388 - 0
src/test/test_util_slow.c

@@ -0,0 +1,388 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#define UTIL_PRIVATE
+#include "util.h"
+#include "util_process.h"
+#include "crypto.h"
+#include "torlog.h"
+#include "test.h"
+
+#ifndef BUILDDIR
+#define BUILDDIR "."
+#endif
+
+#ifdef _WIN32
+#define notify_pending_waitpid_callbacks() STMT_NIL
+#define TEST_CHILD "test-child.exe"
+#define EOL "\r\n"
+#else
+#define TEST_CHILD (BUILDDIR "/src/test/test-child")
+#define EOL "\n"
+#endif
+
+#ifdef _WIN32
+/* I've assumed Windows doesn't have the gap between fork and exec
+ * that causes the race condition on unix-like platforms */
+#define MATCH_PROCESS_STATUS(s1,s2)         ((s1) == (s2))
+
+#else
+/* work around a race condition of the timing of SIGCHLD handler updates
+ * to the process_handle's fields, and checks of those fields
+ *
+ * TODO: Once we can signal failure to exec, change PROCESS_STATUS_RUNNING to
+ * PROCESS_STATUS_ERROR (and similarly with *_OR_NOTRUNNING) */
+#define PROCESS_STATUS_RUNNING_OR_NOTRUNNING  (PROCESS_STATUS_RUNNING+1)
+#define IS_RUNNING_OR_NOTRUNNING(s)           \
+  ((s) == PROCESS_STATUS_RUNNING || (s) == PROCESS_STATUS_NOTRUNNING)
+/* well, this is ugly */
+#define MATCH_PROCESS_STATUS(s1,s2)           \
+  (  (s1) == (s2)                                         \
+     ||((s1) == PROCESS_STATUS_RUNNING_OR_NOTRUNNING      \
+        && IS_RUNNING_OR_NOTRUNNING(s2))                  \
+     ||((s2) == PROCESS_STATUS_RUNNING_OR_NOTRUNNING      \
+        && IS_RUNNING_OR_NOTRUNNING(s1)))
+
+#endif // _WIN32
+
+/** Helper function for testing tor_spawn_background */
+static void
+run_util_spawn_background(const char *argv[], const char *expected_out,
+                          const char *expected_err, int expected_exit,
+                          int expected_status)
+{
+  int retval, exit_code;
+  ssize_t pos;
+  process_handle_t *process_handle=NULL;
+  char stdout_buf[100], stderr_buf[100];
+  int status;
+
+  /* Start the program */
+#ifdef _WIN32
+  status = tor_spawn_background(NULL, argv, NULL, &process_handle);
+#else
+  status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
+#endif
+
+  notify_pending_waitpid_callbacks();
+
+  /* the race condition doesn't affect status,
+   * because status isn't updated by the SIGCHLD handler,
+   * but we still need to handle PROCESS_STATUS_RUNNING_OR_NOTRUNNING */
+  tt_assert(MATCH_PROCESS_STATUS(expected_status, status));
+  if (status == PROCESS_STATUS_ERROR) {
+    tt_ptr_op(process_handle, OP_EQ, NULL);
+    return;
+  }
+
+  tt_assert(process_handle != NULL);
+
+  /* When a spawned process forks, fails, then exits very quickly,
+   * (this typically occurs when exec fails)
+   * there is a race condition between the SIGCHLD handler
+   * updating the process_handle's fields, and this test
+   * checking the process status in those fields.
+   * The SIGCHLD update can occur before or after the code below executes.
+   * This causes intermittent failures in spawn_background_fail(),
+   * typically when the machine is under load.
+   * We use PROCESS_STATUS_RUNNING_OR_NOTRUNNING to avoid this issue. */
+
+  /* the race condition affects the change in
+   * process_handle->status from RUNNING to NOTRUNNING */
+  tt_assert(MATCH_PROCESS_STATUS(expected_status, process_handle->status));
+
+#ifndef _WIN32
+  notify_pending_waitpid_callbacks();
+  /* the race condition affects the change in
+   * process_handle->waitpid_cb to NULL,
+   * so we skip the check if expected_status is ambiguous,
+   * that is, PROCESS_STATUS_RUNNING_OR_NOTRUNNING */
+  tt_assert(process_handle->waitpid_cb != NULL
+              || expected_status == PROCESS_STATUS_RUNNING_OR_NOTRUNNING);
+#endif
+
+#ifdef _WIN32
+  tt_assert(process_handle->stdout_pipe != INVALID_HANDLE_VALUE);
+  tt_assert(process_handle->stderr_pipe != INVALID_HANDLE_VALUE);
+#else
+  tt_assert(process_handle->stdout_pipe >= 0);
+  tt_assert(process_handle->stderr_pipe >= 0);
+#endif
+
+  /* Check stdout */
+  pos = tor_read_all_from_process_stdout(process_handle, stdout_buf,
+                                         sizeof(stdout_buf) - 1);
+  tt_assert(pos >= 0);
+  stdout_buf[pos] = '\0';
+  tt_int_op(strlen(expected_out),OP_EQ, pos);
+  tt_str_op(expected_out,OP_EQ, stdout_buf);
+
+  notify_pending_waitpid_callbacks();
+
+  /* Check it terminated correctly */
+  retval = tor_get_exit_code(process_handle, 1, &exit_code);
+  tt_int_op(PROCESS_EXIT_EXITED,OP_EQ, retval);
+  tt_int_op(expected_exit,OP_EQ, exit_code);
+  // TODO: Make test-child exit with something other than 0
+
+#ifndef _WIN32
+  notify_pending_waitpid_callbacks();
+  tt_ptr_op(process_handle->waitpid_cb, OP_EQ, NULL);
+#endif
+
+  /* Check stderr */
+  pos = tor_read_all_from_process_stderr(process_handle, stderr_buf,
+                                         sizeof(stderr_buf) - 1);
+  tt_assert(pos >= 0);
+  stderr_buf[pos] = '\0';
+  tt_str_op(expected_err,OP_EQ, stderr_buf);
+  tt_int_op(strlen(expected_err),OP_EQ, pos);
+
+  notify_pending_waitpid_callbacks();
+
+ done:
+  if (process_handle)
+    tor_process_handle_destroy(process_handle, 1);
+}
+
+/** Check that we can launch a process and read the output */
+static void
+test_util_spawn_background_ok(void *ptr)
+{
+  const char *argv[] = {TEST_CHILD, "--test", NULL};
+  const char *expected_out = "OUT"EOL "--test"EOL "SLEEPING"EOL "DONE" EOL;
+  const char *expected_err = "ERR"EOL;
+
+  (void)ptr;
+
+  run_util_spawn_background(argv, expected_out, expected_err, 0,
+      PROCESS_STATUS_RUNNING);
+}
+
+/** Check that failing to find the executable works as expected */
+static void
+test_util_spawn_background_fail(void *ptr)
+{
+  const char *argv[] = {BUILDDIR "/src/test/no-such-file", "--test", NULL};
+  const char *expected_err = "";
+  char expected_out[1024];
+  char code[32];
+#ifdef _WIN32
+  const int expected_status = PROCESS_STATUS_ERROR;
+#else
+  /* TODO: Once we can signal failure to exec, set this to be
+   * PROCESS_STATUS_RUNNING_OR_ERROR */
+  const int expected_status = PROCESS_STATUS_RUNNING_OR_NOTRUNNING;
+#endif
+
+  memset(expected_out, 0xf0, sizeof(expected_out));
+  memset(code, 0xf0, sizeof(code));
+
+  (void)ptr;
+
+  tor_snprintf(code, sizeof(code), "%x/%x",
+    9 /* CHILD_STATE_FAILEXEC */ , ENOENT);
+  tor_snprintf(expected_out, sizeof(expected_out),
+    "ERR: Failed to spawn background process - code %s\n", code);
+
+  run_util_spawn_background(argv, expected_out, expected_err, 255,
+      expected_status);
+}
+
+/** Test that reading from a handle returns a partial read rather than
+ * blocking */
+static void
+test_util_spawn_background_partial_read_impl(int exit_early)
+{
+  const int expected_exit = 0;
+  const int expected_status = PROCESS_STATUS_RUNNING;
+
+  int retval, exit_code;
+  ssize_t pos = -1;
+  process_handle_t *process_handle=NULL;
+  int status;
+  char stdout_buf[100], stderr_buf[100];
+
+  const char *argv[] = {TEST_CHILD, "--test", NULL};
+  const char *expected_out[] = { "OUT" EOL "--test" EOL "SLEEPING" EOL,
+                                 "DONE" EOL,
+                                 NULL };
+  const char *expected_err = "ERR" EOL;
+
+#ifndef _WIN32
+  int eof = 0;
+#endif
+  int expected_out_ctr;
+
+  if (exit_early) {
+    argv[1] = "--hang";
+    expected_out[0] = "OUT"EOL "--hang"EOL "SLEEPING" EOL;
+  }
+
+  /* Start the program */
+#ifdef _WIN32
+  status = tor_spawn_background(NULL, argv, NULL, &process_handle);
+#else
+  status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
+#endif
+  tt_int_op(expected_status,OP_EQ, status);
+  tt_assert(process_handle);
+  tt_int_op(expected_status,OP_EQ, process_handle->status);
+
+  /* Check stdout */
+  for (expected_out_ctr = 0; expected_out[expected_out_ctr] != NULL;) {
+#ifdef _WIN32
+    pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
+                              sizeof(stdout_buf) - 1, NULL);
+#else
+    /* Check that we didn't read the end of file last time */
+    tt_assert(!eof);
+    pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf,
+                              sizeof(stdout_buf) - 1, NULL, &eof);
+#endif
+    log_info(LD_GENERAL, "tor_read_all_handle() returned %d", (int)pos);
+
+    /* We would have blocked, keep on trying */
+    if (0 == pos)
+      continue;
+
+    tt_assert(pos > 0);
+    stdout_buf[pos] = '\0';
+    tt_str_op(expected_out[expected_out_ctr],OP_EQ, stdout_buf);
+    tt_int_op(strlen(expected_out[expected_out_ctr]),OP_EQ, pos);
+    expected_out_ctr++;
+  }
+
+  if (exit_early) {
+    tor_process_handle_destroy(process_handle, 1);
+    process_handle = NULL;
+    goto done;
+  }
+
+  /* The process should have exited without writing more */
+#ifdef _WIN32
+  pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
+                            sizeof(stdout_buf) - 1,
+                            process_handle);
+  tt_int_op(0,OP_EQ, pos);
+#else
+  if (!eof) {
+    /* We should have got all the data, but maybe not the EOF flag */
+    pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf,
+                              sizeof(stdout_buf) - 1,
+                              process_handle, &eof);
+    tt_int_op(0,OP_EQ, pos);
+    tt_assert(eof);
+  }
+  /* Otherwise, we got the EOF on the last read */
+#endif
+
+  /* Check it terminated correctly */
+  retval = tor_get_exit_code(process_handle, 1, &exit_code);
+  tt_int_op(PROCESS_EXIT_EXITED,OP_EQ, retval);
+  tt_int_op(expected_exit,OP_EQ, exit_code);
+
+  // TODO: Make test-child exit with something other than 0
+
+  /* Check stderr */
+  pos = tor_read_all_from_process_stderr(process_handle, stderr_buf,
+                                         sizeof(stderr_buf) - 1);
+  tt_assert(pos >= 0);
+  stderr_buf[pos] = '\0';
+  tt_str_op(expected_err,OP_EQ, stderr_buf);
+  tt_int_op(strlen(expected_err),OP_EQ, pos);
+
+ done:
+  tor_process_handle_destroy(process_handle, 1);
+}
+
+static void
+test_util_spawn_background_partial_read(void *arg)
+{
+  (void)arg;
+  test_util_spawn_background_partial_read_impl(0);
+}
+
+static void
+test_util_spawn_background_exit_early(void *arg)
+{
+  (void)arg;
+  test_util_spawn_background_partial_read_impl(1);
+}
+
+static void
+test_util_spawn_background_waitpid_notify(void *arg)
+{
+  int retval, exit_code;
+  process_handle_t *process_handle=NULL;
+  int status;
+  int ms_timer;
+
+  const char *argv[] = {TEST_CHILD, "--fast", NULL};
+
+  (void) arg;
+
+#ifdef _WIN32
+  status = tor_spawn_background(NULL, argv, NULL, &process_handle);
+#else
+  status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
+#endif
+
+  tt_int_op(status, OP_EQ, PROCESS_STATUS_RUNNING);
+  tt_ptr_op(process_handle, OP_NE, NULL);
+
+  /* We're not going to look at the stdout/stderr output this time. Instead,
+   * we're testing whether notify_pending_waitpid_calbacks() can report the
+   * process exit (on unix) and/or whether tor_get_exit_code() can notice it
+   * (on windows) */
+
+#ifndef _WIN32
+  ms_timer = 30*1000;
+  tt_ptr_op(process_handle->waitpid_cb, OP_NE, NULL);
+  while (process_handle->waitpid_cb && ms_timer > 0) {
+    tor_sleep_msec(100);
+    ms_timer -= 100;
+    notify_pending_waitpid_callbacks();
+  }
+  tt_int_op(ms_timer, OP_GT, 0);
+  tt_ptr_op(process_handle->waitpid_cb, OP_EQ, NULL);
+#endif
+
+  ms_timer = 30*1000;
+  while (((retval = tor_get_exit_code(process_handle, 0, &exit_code))
+                == PROCESS_EXIT_RUNNING) && ms_timer > 0) {
+    tor_sleep_msec(100);
+    ms_timer -= 100;
+  }
+  tt_int_op(ms_timer, OP_GT, 0);
+
+  tt_int_op(retval, OP_EQ, PROCESS_EXIT_EXITED);
+
+ done:
+  tor_process_handle_destroy(process_handle, 1);
+}
+
+#undef TEST_CHILD
+#undef EOL
+
+#undef MATCH_PROCESS_STATUS
+
+#ifndef _WIN32
+#undef PROCESS_STATUS_RUNNING_OR_NOTRUNNING
+#undef IS_RUNNING_OR_NOTRUNNING
+#endif
+
+#define UTIL_TEST(name, flags)                          \
+  { #name, test_util_ ## name, flags, NULL, NULL }
+
+struct testcase_t slow_util_tests[] = {
+  UTIL_TEST(spawn_background_ok, 0),
+  UTIL_TEST(spawn_background_fail, 0),
+  UTIL_TEST(spawn_background_partial_read, 0),
+  UTIL_TEST(spawn_background_exit_early, 0),
+  UTIL_TEST(spawn_background_waitpid_notify, 0),
+  END_OF_TESTCASES
+};

+ 298 - 0
src/test/testing_common.c

@@ -0,0 +1,298 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/* Ordinarily defined in tor_main.c; this bit is just here to provide one
+ * since we're not linking to tor_main.c */
+const char tor_git_revision[] = "";
+
+/**
+ * \file test_common.c
+ * \brief Common pieces to implement unit tests.
+ **/
+
+#include "orconfig.h"
+#include "or.h"
+#include "config.h"
+#include "rephist.h"
+#include "backtrace.h"
+#include "test.h"
+
+#include <stdio.h>
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef _WIN32
+/* For mkdir() */
+#include <direct.h>
+#else
+#include <dirent.h>
+#endif
+
+#include "or.h"
+
+#ifdef USE_DMALLOC
+#include <dmalloc.h>
+#include <openssl/crypto.h>
+#include "main.h"
+#endif
+
+/** Temporary directory (set up by setup_directory) under which we store all
+ * our files during testing. */
+static char temp_dir[256];
+#ifdef _WIN32
+#define pid_t int
+#endif
+static pid_t temp_dir_setup_in_pid = 0;
+
+/** Select and create the temporary directory we'll use to run our unit tests.
+ * Store it in <b>temp_dir</b>.  Exit immediately if we can't create it.
+ * idempotent. */
+static void
+setup_directory(void)
+{
+  static int is_setup = 0;
+  int r;
+  char rnd[256], rnd32[256];
+  if (is_setup) return;
+
+/* Due to base32 limitation needs to be a multiple of 5. */
+#define RAND_PATH_BYTES 5
+  crypto_rand(rnd, RAND_PATH_BYTES);
+  base32_encode(rnd32, sizeof(rnd32), rnd, RAND_PATH_BYTES);
+
+#ifdef _WIN32
+  {
+    char buf[MAX_PATH];
+    const char *tmp = buf;
+    const char *extra_backslash = "";
+    /* If this fails, we're probably screwed anyway */
+    if (!GetTempPathA(sizeof(buf),buf))
+      tmp = "c:\\windows\\temp\\";
+    if (strcmpend(tmp, "\\")) {
+      /* According to MSDN, it should be impossible for GetTempPath to give us
+       * an answer that doesn't end with \.  But let's make sure. */
+      extra_backslash = "\\";
+    }
+    tor_snprintf(temp_dir, sizeof(temp_dir),
+                 "%s%stor_test_%d_%s", tmp, extra_backslash,
+                 (int)getpid(), rnd32);
+    r = mkdir(temp_dir);
+  }
+#else
+  tor_snprintf(temp_dir, sizeof(temp_dir), "/tmp/tor_test_%d_%s",
+               (int) getpid(), rnd32);
+  r = mkdir(temp_dir, 0700);
+  if (!r) {
+    /* undo sticky bit so tests don't get confused. */
+    r = chown(temp_dir, getuid(), getgid());
+  }
+#endif
+  if (r) {
+    fprintf(stderr, "Can't create directory %s:", temp_dir);
+    perror("");
+    exit(1);
+  }
+  is_setup = 1;
+  temp_dir_setup_in_pid = getpid();
+}
+
+/** Return a filename relative to our testing temporary directory */
+const char *
+get_fname(const char *name)
+{
+  static char buf[1024];
+  setup_directory();
+  if (!name)
+    return temp_dir;
+  tor_snprintf(buf,sizeof(buf),"%s/%s",temp_dir,name);
+  return buf;
+}
+
+/* Remove a directory and all of its subdirectories */
+static void
+rm_rf(const char *dir)
+{
+  struct stat st;
+  smartlist_t *elements;
+
+  elements = tor_listdir(dir);
+  if (elements) {
+    SMARTLIST_FOREACH_BEGIN(elements, const char *, cp) {
+         char *tmp = NULL;
+         tor_asprintf(&tmp, "%s"PATH_SEPARATOR"%s", dir, cp);
+         if (0 == stat(tmp,&st) && (st.st_mode & S_IFDIR)) {
+           rm_rf(tmp);
+         } else {
+           if (unlink(tmp)) {
+             fprintf(stderr, "Error removing %s: %s\n", tmp, strerror(errno));
+           }
+         }
+         tor_free(tmp);
+    } SMARTLIST_FOREACH_END(cp);
+    SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
+    smartlist_free(elements);
+  }
+  if (rmdir(dir))
+    fprintf(stderr, "Error removing directory %s: %s\n", dir, strerror(errno));
+}
+
+/** Remove all files stored under the temporary directory, and the directory
+ * itself.  Called by atexit(). */
+static void
+remove_directory(void)
+{
+  if (getpid() != temp_dir_setup_in_pid) {
+    /* Only clean out the tempdir when the main process is exiting. */
+    return;
+  }
+
+  rm_rf(temp_dir);
+}
+
+/** Define this if unit tests spend too much time generating public keys*/
+#undef CACHE_GENERATED_KEYS
+
+static crypto_pk_t *pregen_keys[5] = {NULL, NULL, NULL, NULL, NULL};
+#define N_PREGEN_KEYS ARRAY_LENGTH(pregen_keys)
+
+/** Generate and return a new keypair for use in unit tests.  If we're using
+ * the key cache optimization, we might reuse keys: we only guarantee that
+ * keys made with distinct values for <b>idx</b> are different.  The value of
+ * <b>idx</b> must be at least 0, and less than N_PREGEN_KEYS. */
+crypto_pk_t *
+pk_generate(int idx)
+{
+#ifdef CACHE_GENERATED_KEYS
+  tor_assert(idx < N_PREGEN_KEYS);
+  if (! pregen_keys[idx]) {
+    pregen_keys[idx] = crypto_pk_new();
+    tor_assert(!crypto_pk_generate_key(pregen_keys[idx]));
+  }
+  return crypto_pk_dup_key(pregen_keys[idx]);
+#else
+  crypto_pk_t *result;
+  (void) idx;
+  result = crypto_pk_new();
+  tor_assert(!crypto_pk_generate_key(result));
+  return result;
+#endif
+}
+
+/** Free all storage used for the cached key optimization. */
+static void
+free_pregenerated_keys(void)
+{
+  unsigned idx;
+  for (idx = 0; idx < N_PREGEN_KEYS; ++idx) {
+    if (pregen_keys[idx]) {
+      crypto_pk_free(pregen_keys[idx]);
+      pregen_keys[idx] = NULL;
+    }
+  }
+}
+
+static void *
+passthrough_test_setup(const struct testcase_t *testcase)
+{
+  return testcase->setup_data;
+}
+static int
+passthrough_test_cleanup(const struct testcase_t *testcase, void *ptr)
+{
+  (void)testcase;
+  (void)ptr;
+  return 1;
+}
+
+const struct testcase_setup_t passthrough_setup = {
+  passthrough_test_setup, passthrough_test_cleanup
+};
+
+extern struct testgroup_t testgroups[];
+
+/** Main entry point for unit test code: parse the command line, and run
+ * some unit tests. */
+int
+main(int c, const char **v)
+{
+  or_options_t *options;
+  char *errmsg = NULL;
+  int i, i_out;
+  int loglevel = LOG_ERR;
+  int accel_crypto = 0;
+
+#ifdef USE_DMALLOC
+  {
+    int r = CRYPTO_set_mem_ex_functions(tor_malloc_, tor_realloc_, tor_free_);
+    tor_assert(r);
+  }
+#endif
+
+  update_approx_time(time(NULL));
+  options = options_new();
+  tor_threads_init();
+  init_logging(1);
+  configure_backtrace_handler(get_version());
+
+  for (i_out = i = 1; i < c; ++i) {
+    if (!strcmp(v[i], "--warn")) {
+      loglevel = LOG_WARN;
+    } else if (!strcmp(v[i], "--notice")) {
+      loglevel = LOG_NOTICE;
+    } else if (!strcmp(v[i], "--info")) {
+      loglevel = LOG_INFO;
+    } else if (!strcmp(v[i], "--debug")) {
+      loglevel = LOG_DEBUG;
+    } else if (!strcmp(v[i], "--accel")) {
+      accel_crypto = 1;
+    } else {
+      v[i_out++] = v[i];
+    }
+  }
+  c = i_out;
+
+  {
+    log_severity_list_t s;
+    memset(&s, 0, sizeof(s));
+    set_log_severity_config(loglevel, LOG_ERR, &s);
+    add_stream_log(&s, "", fileno(stdout));
+  }
+
+  options->command = CMD_RUN_UNITTESTS;
+  if (crypto_global_init(accel_crypto, NULL, NULL)) {
+    printf("Can't initialize crypto subsystem; exiting.\n");
+    return 1;
+  }
+  crypto_set_tls_dh_prime(NULL);
+  crypto_seed_rng(1);
+  rep_hist_init();
+  network_init();
+  setup_directory();
+  options_init(options);
+  options->DataDirectory = tor_strdup(temp_dir);
+  options->EntryStatistics = 1;
+  if (set_options(options, &errmsg) < 0) {
+    printf("Failed to set initial options: %s\n", errmsg);
+    tor_free(errmsg);
+    return 1;
+  }
+
+  atexit(remove_directory);
+
+  int have_failed = (tinytest_main(c, v, testgroups) != 0);
+
+  free_pregenerated_keys();
+#ifdef USE_DMALLOC
+  tor_free_all(0);
+  dmalloc_log_unfreed();
+#endif
+
+  if (have_failed)
+    return 1;
+  else
+    return 0;
+}
+