Browse Source

Merge branch 'libscrypt_trunnel_squashed'

Conflicts:
	src/test/test_crypto.c
Nick Mathewson 9 years ago
parent
commit
764e008092

+ 4 - 0
.gitignore

@@ -174,6 +174,10 @@ cscope.*
 /src/tools/Makefile
 /src/tools/Makefile.in
 
+# /src/trunnel/
+/src/trunnel/libor-trunnel-testing.a
+/src/trunnel/libor-trunnel.a
+
 # /src/tools/tor-fw-helper/
 /src/tools/tor-fw-helper/tor-fw-helper
 /src/tools/tor-fw-helper/tor-fw-helper.exe

+ 0 - 1
Makefile.am

@@ -23,7 +23,6 @@ include src/include.am
 include doc/include.am
 include contrib/include.am
 
-
 EXTRA_DIST+= \
 	ChangeLog					\
 	INSTALL						\

+ 11 - 0
configure.ac

@@ -153,6 +153,9 @@ AC_ARG_ENABLE(tool-name-check,
 AC_ARG_ENABLE(seccomp,
      AS_HELP_STRING(--disable-seccomp, do not attempt to use libseccomp))
 
+AC_ARG_ENABLE(libscrypt,
+     AS_HELP_STRING(--disable-libscrypt, do not attempt to use libscrypt))
+
 dnl check for the correct "ar" when cross-compiling
 AN_MAKEVAR([AR], [AC_PROG_AR])
 AN_PROGRAM([ar], [AC_PROG_AR])
@@ -722,6 +725,14 @@ if test "x$enable_seccomp" != "xno"; then
   AC_SEARCH_LIBS(seccomp_init, [seccomp])
 fi
 
+dnl ============================================================
+dnl Check for libscrypt
+
+if test "x$enable_libscrypt" != "xno"; then
+  AC_CHECK_HEADERS([libscrypt.h])
+  AC_SEARCH_LIBS(libscrypt_scrypt, [scrypt])
+fi
+
 dnl ============================================================
 dnl We need an implementation of curve25519.
 

+ 0 - 44
src/common/crypto.c

@@ -3001,50 +3001,6 @@ base32_decode(char *dest, size_t destlen, const char *src, size_t srclen)
   return 0;
 }
 
-/** Implement RFC2440-style iterated-salted S2K conversion: convert the
- * <b>secret_len</b>-byte <b>secret</b> into a <b>key_out_len</b> byte
- * <b>key_out</b>.  As in RFC2440, the first 8 bytes of s2k_specifier
- * are a salt; the 9th byte describes how much iteration to do.
- * Does not support <b>key_out_len</b> &gt; DIGEST_LEN.
- */
-void
-secret_to_key(char *key_out, size_t key_out_len, const char *secret,
-              size_t secret_len, const char *s2k_specifier)
-{
-  crypto_digest_t *d;
-  uint8_t c;
-  size_t count, tmplen;
-  char *tmp;
-  tor_assert(key_out_len < SIZE_T_CEILING);
-
-#define EXPBIAS 6
-  c = s2k_specifier[8];
-  count = ((uint32_t)16 + (c & 15)) << ((c >> 4) + EXPBIAS);
-#undef EXPBIAS
-
-  tor_assert(key_out_len <= DIGEST_LEN);
-
-  d = crypto_digest_new();
-  tmplen = 8+secret_len;
-  tmp = tor_malloc(tmplen);
-  memcpy(tmp,s2k_specifier,8);
-  memcpy(tmp+8,secret,secret_len);
-  secret_len += 8;
-  while (count) {
-    if (count >= secret_len) {
-      crypto_digest_add_bytes(d, tmp, secret_len);
-      count -= secret_len;
-    } else {
-      crypto_digest_add_bytes(d, tmp, count);
-      count = 0;
-    }
-  }
-  crypto_digest_get_digest(d, key_out, key_out_len);
-  memwipe(tmp, 0, tmplen);
-  tor_free(tmp);
-  crypto_digest_free(d);
-}
-
 /**
  * Destroy the <b>sz</b> bytes of data stored at <b>mem</b>, setting them to
  * the value <b>byte</b>.

+ 0 - 6
src/common/crypto.h

@@ -280,12 +280,6 @@ int digest_from_base64(char *digest, const char *d64);
 int digest256_to_base64(char *d64, const char *digest);
 int digest256_from_base64(char *digest, const char *d64);
 
-/** Length of RFC2440-style S2K specifier: the first 8 bytes are a salt, the
- * 9th describes how much iteration to do. */
-#define S2K_SPECIFIER_LEN 9
-void secret_to_key(char *key_out, size_t key_out_len, const char *secret,
-                   size_t secret_len, const char *s2k_specifier);
-
 /** OpenSSL-based utility functions. */
 void memwipe(void *mem, uint8_t byte, size_t sz);
 

+ 187 - 0
src/common/crypto_pwbox.c

@@ -0,0 +1,187 @@
+
+#include "crypto.h"
+#include "crypto_s2k.h"
+#include "crypto_pwbox.h"
+#include "di_ops.h"
+#include "util.h"
+#include "pwbox.h"
+
+/* 8 bytes "TORBOX00"
+   1 byte: header len (H)
+   H bytes: header, denoting secret key algorithm.
+   16 bytes: IV
+   Round up to multiple of 128 bytes, then encrypt:
+      4 bytes: data len
+      data
+      zeros
+   32 bytes: HMAC-SHA256 of all previous bytes.
+*/
+
+#define MAX_OVERHEAD (S2K_MAXLEN + 8 + 1 + 32 + CIPHER_IV_LEN)
+
+/**
+ * Make an authenticated passphrase-encrypted blob to encode the
+ * <b>input_len</b> bytes in <b>input</b> using the passphrase
+ * <b>secret</b> of <b>secret_len</b> bytes.  Allocate a new chunk of memory
+ * to hold the encrypted data, and store a pointer to that memory in
+ * *<b>out</b>, and its size in <b>outlen_out</b>.  Use <b>s2k_flags</b> as an
+ * argument to the passphrase-hashing function.
+ */
+int
+crypto_pwbox(uint8_t **out, size_t *outlen_out,
+             const uint8_t *input, size_t input_len,
+             const char *secret, size_t secret_len,
+             unsigned s2k_flags)
+{
+  uint8_t *result = NULL, *encrypted_portion;
+  size_t encrypted_len = 128 * CEIL_DIV(input_len+4, 128);
+  ssize_t result_len;
+  int spec_len;
+  uint8_t keys[CIPHER_KEY_LEN + DIGEST256_LEN];
+  pwbox_encoded_t *enc = NULL;
+  ssize_t enc_len;
+
+  crypto_cipher_t *cipher;
+  int rv;
+
+  enc = pwbox_encoded_new();
+
+  pwbox_encoded_setlen_skey_header(enc, S2K_MAXLEN);
+
+  spec_len = secret_to_key_make_specifier(
+                                      pwbox_encoded_getarray_skey_header(enc),
+                                      S2K_MAXLEN,
+                                      s2k_flags);
+  if (spec_len < 0 || spec_len > S2K_MAXLEN)
+    goto err;
+  pwbox_encoded_setlen_skey_header(enc, spec_len);
+  enc->header_len = spec_len;
+
+  crypto_rand((char*)enc->iv, sizeof(enc->iv));
+
+  pwbox_encoded_setlen_data(enc, encrypted_len);
+  encrypted_portion = pwbox_encoded_getarray_data(enc);
+
+  set_uint32(encrypted_portion, htonl(input_len));
+  memcpy(encrypted_portion+4, input, input_len);
+
+  /* Now that all the data is in position, derive some keys, encrypt, and
+   * digest */
+  if (secret_to_key_derivekey(keys, sizeof(keys),
+                              pwbox_encoded_getarray_skey_header(enc),
+                              spec_len,
+                              secret, secret_len) < 0)
+    goto err;
+
+  cipher = crypto_cipher_new_with_iv((char*)keys, (char*)enc->iv);
+  crypto_cipher_crypt_inplace(cipher, (char*)encrypted_portion, encrypted_len);
+  crypto_cipher_free(cipher);
+
+  result_len = pwbox_encoded_encoded_len(enc);
+  if (result_len < 0)
+    goto err;
+  result = tor_malloc(result_len);
+  enc_len = pwbox_encoded_encode(result, result_len, enc);
+  if (enc_len < 0)
+    goto err;
+  tor_assert(enc_len == result_len);
+
+  crypto_hmac_sha256((char*) result + result_len - 32,
+                     (const char*)keys + CIPHER_KEY_LEN,
+                     DIGEST256_LEN,
+                     (const char*)result,
+                     result_len - 32);
+
+  *out = result;
+  *outlen_out = result_len;
+  rv = 0;
+  goto out;
+
+ err:
+  tor_free(result);
+  rv = -1;
+
+ out:
+  pwbox_encoded_free(enc);
+  memwipe(keys, 0, sizeof(keys));
+  return rv;
+}
+
+/**
+ * Try to decrypt the passphrase-encrypted blob of <b>input_len</b> bytes in
+ * <b>input</b> using the passphrase <b>secret</b> of <b>secret_len</b> bytes.
+ * On success, return 0 and allocate a new chunk of memory to hold the
+ * decrypted data, and store a pointer to that memory in *<b>out</b>, and its
+ * size in <b>outlen_out</b>.  On failure, return UNPWBOX_BAD_SECRET if
+ * the passphrase might have been wrong, and UNPWBOX_CORRUPT if the object is
+ * definitely corrupt.
+ */
+int
+crypto_unpwbox(uint8_t **out, size_t *outlen_out,
+               const uint8_t *inp, size_t input_len,
+               const char *secret, size_t secret_len)
+{
+  uint8_t *result = NULL;
+  const uint8_t *encrypted;
+  uint8_t keys[CIPHER_KEY_LEN + DIGEST256_LEN];
+  uint8_t hmac[DIGEST256_LEN];
+  uint32_t result_len;
+  size_t encrypted_len;
+  crypto_cipher_t *cipher = NULL;
+  int rv = UNPWBOX_CORRUPTED;
+  ssize_t got_len;
+
+  pwbox_encoded_t *enc = NULL;
+
+  got_len = pwbox_encoded_parse(&enc, inp, input_len);
+  if (got_len < 0 || (size_t)got_len != input_len)
+    goto err;
+
+  /* Now derive the keys and check the hmac. */
+  if (secret_to_key_derivekey(keys, sizeof(keys),
+                              pwbox_encoded_getarray_skey_header(enc),
+                              pwbox_encoded_getlen_skey_header(enc),
+                              secret, secret_len) < 0)
+    goto err;
+
+  crypto_hmac_sha256((char *)hmac,
+                     (const char*)keys + CIPHER_KEY_LEN, DIGEST256_LEN,
+                     (const char*)inp, input_len - DIGEST256_LEN);
+
+  if (tor_memneq(hmac, enc->hmac, DIGEST256_LEN)) {
+    rv = UNPWBOX_BAD_SECRET;
+    goto err;
+  }
+
+  /* How long is the plaintext? */
+  encrypted = pwbox_encoded_getarray_data(enc);
+  encrypted_len = pwbox_encoded_getlen_data(enc);
+  if (encrypted_len < 4)
+    goto err;
+
+  cipher = crypto_cipher_new_with_iv((char*)keys, (char*)enc->iv);
+  crypto_cipher_decrypt(cipher, (char*)&result_len, (char*)encrypted, 4);
+  result_len = ntohl(result_len);
+  if (encrypted_len < result_len + 4)
+    goto err;
+
+  /* Allocate a buffer and decrypt */
+  result = tor_malloc_zero(result_len);
+  crypto_cipher_decrypt(cipher, (char*)result, (char*)encrypted+4, result_len);
+
+  *out = result;
+  *outlen_out = result_len;
+
+  rv = UNPWBOX_OKAY;
+  goto out;
+
+ err:
+  tor_free(result);
+
+ out:
+  crypto_cipher_free(cipher);
+  pwbox_encoded_free(enc);
+  memwipe(keys, 0, sizeof(keys));
+  return rv;
+}
+

+ 20 - 0
src/common/crypto_pwbox.h

@@ -0,0 +1,20 @@
+#ifndef CRYPTO_PWBOX_H_INCLUDED_
+#define CRYPTO_PWBOX_H_INCLUDED_
+
+#include "torint.h"
+
+#define UNPWBOX_OKAY 0
+#define UNPWBOX_BAD_SECRET -1
+#define UNPWBOX_CORRUPTED -2
+
+int crypto_pwbox(uint8_t **out, size_t *outlen_out,
+                 const uint8_t *inp, size_t input_len,
+                 const char *secret, size_t secret_len,
+                 unsigned s2k_flags);
+
+int crypto_unpwbox(uint8_t **out, size_t *outlen_out,
+                   const uint8_t *inp, size_t input_len,
+                   const char *secret, size_t secret_len);
+
+#endif
+

+ 457 - 0
src/common/crypto_s2k.c

@@ -0,0 +1,457 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define CRYPTO_S2K_PRIVATE
+
+#include "crypto.h"
+#include "util.h"
+#include "compat.h"
+#include "crypto_s2k.h"
+
+#include <openssl/evp.h>
+
+#ifdef HAVE_LIBSCRYPT_H
+#define HAVE_SCRYPT
+#include <libscrypt.h>
+#endif
+
+/* Encoded secrets take the form:
+
+     u8 type;
+     u8 salt_and_parameters[depends on type];
+     u8 key[depends on type];
+
+   As a special case, if the encoded secret is exactly 29 bytes long,
+   type 0 is understood.
+
+   Recognized types are:
+       00 -- RFC2440. salt_and_parameters is 9 bytes. key is 20 bytes.
+                salt_and_parameters is 8 bytes random salt,
+                1 byte iteration info.
+       01 -- PKBDF2_SHA1. salt_and_parameters is 17 bytes. key is 20 bytes.
+                salt_and_parameters is 16 bytes random salt,
+                1 byte iteration info.
+       02 -- SCRYPT_SALSA208_SHA256. salt_and_parameters is 18 bytes. key is
+             32 bytes.
+                salt_and_parameters is 18 bytes random salt, 2 bytes iteration
+                info.
+*/
+
+#define S2K_TYPE_RFC2440 0
+#define S2K_TYPE_PBKDF2  1
+#define S2K_TYPE_SCRYPT  2
+
+#define PBKDF2_SPEC_LEN 17
+#define PBKDF2_KEY_LEN 20
+
+#define SCRYPT_SPEC_LEN 18
+#define SCRYPT_KEY_LEN 32
+
+/** Given an algorithm ID (one of S2K_TYPE_*), return the length of the
+ * specifier part of it, without the prefix type byte. */
+static int
+secret_to_key_spec_len(uint8_t type)
+{
+  switch (type) {
+    case S2K_TYPE_RFC2440:
+      return S2K_RFC2440_SPECIFIER_LEN;
+    case S2K_TYPE_PBKDF2:
+      return PBKDF2_SPEC_LEN;
+    case S2K_TYPE_SCRYPT:
+      return SCRYPT_SPEC_LEN;
+    default:
+      return -1;
+  }
+}
+
+/** Given an algorithm ID (one of S2K_TYPE_*), return the length of the
+ * its preferred output. */
+static int
+secret_to_key_key_len(uint8_t type)
+{
+  switch (type) {
+    case S2K_TYPE_RFC2440:
+      return DIGEST_LEN;
+    case S2K_TYPE_PBKDF2:
+      return DIGEST_LEN;
+    case S2K_TYPE_SCRYPT:
+      return DIGEST256_LEN;
+    default:
+      return -1;
+  }
+}
+
+/** Given a specifier in <b>spec_and_key</b> of length
+ * <b>spec_and_key_len</b>, along with its prefix algorithm ID byte, and along
+ * with a key if <b>key_included</b> is true, check whether the whole
+ * specifier-and-key is of valid length, and return the algorithm type if it
+ * is.  Set *<b>legacy_out</b> to 1 iff this is a legacy password hash or
+ * legacy specifier.  Return an error code on failure.
+ */
+static int
+secret_to_key_get_type(const uint8_t *spec_and_key, size_t spec_and_key_len,
+                       int key_included, int *legacy_out)
+{
+  size_t legacy_len = S2K_RFC2440_SPECIFIER_LEN;
+  uint8_t type;
+  int total_len;
+
+  if (key_included)
+    legacy_len += DIGEST_LEN;
+
+  if (spec_and_key_len == legacy_len) {
+    *legacy_out = 1;
+    return S2K_TYPE_RFC2440;
+  }
+
+  *legacy_out = 0;
+  if (spec_and_key_len == 0)
+    return S2K_BAD_LEN;
+
+  type = spec_and_key[0];
+  total_len = secret_to_key_spec_len(type);
+  if (total_len < 0)
+    return S2K_BAD_ALGORITHM;
+  if (key_included) {
+    int keylen = secret_to_key_key_len(type);
+    if (keylen < 0)
+      return S2K_BAD_ALGORITHM;
+    total_len += keylen;
+  }
+
+  if ((size_t)total_len + 1 == spec_and_key_len)
+    return type;
+  else
+    return S2K_BAD_LEN;
+}
+
+/**
+ * Write a new random s2k specifier of type <b>type</b>, without prefixing
+ * type byte, to <b>spec_out</b>, which must have enough room.  May adjust
+ * parameter choice based on <b>flags</b>.
+ */
+static int
+make_specifier(uint8_t *spec_out, uint8_t type, unsigned flags)
+{
+  int speclen = secret_to_key_spec_len(type);
+  if (speclen < 0)
+      return S2K_BAD_ALGORITHM;
+
+  crypto_rand((char*)spec_out, speclen);
+  switch (type) {
+    case S2K_TYPE_RFC2440:
+      /* Hash 64 k of data. */
+      spec_out[S2K_RFC2440_SPECIFIER_LEN-1] = 96;
+      break;
+    case S2K_TYPE_PBKDF2:
+      /* 131 K iterations */
+      spec_out[PBKDF2_SPEC_LEN-1] = 17;
+      break;
+    case S2K_TYPE_SCRYPT:
+      if (flags & S2K_FLAG_LOW_MEM) {
+        /* N = 1<<12 */
+        spec_out[SCRYPT_SPEC_LEN-2] = 12;
+      } else {
+        /* N = 1<<15 */
+        spec_out[SCRYPT_SPEC_LEN-2] = 15;
+      }
+      /* r = 8; p = 2. */
+      spec_out[SCRYPT_SPEC_LEN-1] = (3u << 4) | (1u << 0);
+      break;
+    default:
+      tor_fragile_assert();
+      return S2K_BAD_ALGORITHM;
+  }
+
+  return speclen;
+}
+
+/** Implement RFC2440-style iterated-salted S2K conversion: convert the
+ * <b>secret_len</b>-byte <b>secret</b> into a <b>key_out_len</b> byte
+ * <b>key_out</b>.  As in RFC2440, the first 8 bytes of s2k_specifier
+ * are a salt; the 9th byte describes how much iteration to do.
+ * If <b>key_out_len</b> &gt; DIGEST_LEN, use HDKF to expand the result.
+ */
+void
+secret_to_key_rfc2440(char *key_out, size_t key_out_len, const char *secret,
+              size_t secret_len, const char *s2k_specifier)
+{
+  crypto_digest_t *d;
+  uint8_t c;
+  size_t count, tmplen;
+  char *tmp;
+  uint8_t buf[DIGEST_LEN];
+  tor_assert(key_out_len < SIZE_T_CEILING);
+
+#define EXPBIAS 6
+  c = s2k_specifier[8];
+  count = ((uint32_t)16 + (c & 15)) << ((c >> 4) + EXPBIAS);
+#undef EXPBIAS
+
+  d = crypto_digest_new();
+  tmplen = 8+secret_len;
+  tmp = tor_malloc(tmplen);
+  memcpy(tmp,s2k_specifier,8);
+  memcpy(tmp+8,secret,secret_len);
+  secret_len += 8;
+  while (count) {
+    if (count >= secret_len) {
+      crypto_digest_add_bytes(d, tmp, secret_len);
+      count -= secret_len;
+    } else {
+      crypto_digest_add_bytes(d, tmp, count);
+      count = 0;
+    }
+  }
+  crypto_digest_get_digest(d, (char*)buf, sizeof(buf));
+
+  if (key_out_len <= sizeof(buf)) {
+    memcpy(key_out, buf, key_out_len);
+  } else {
+    crypto_expand_key_material_rfc5869_sha256(buf, DIGEST_LEN,
+                                           (const uint8_t*)s2k_specifier, 8,
+                                           (const uint8_t*)"EXPAND", 6,
+                                           (uint8_t*)key_out, key_out_len);
+  }
+  memwipe(tmp, 0, tmplen);
+  memwipe(buf, 0, sizeof(buf));
+  tor_free(tmp);
+  crypto_digest_free(d);
+}
+
+/**
+ * Helper: given a valid specifier without prefix type byte in <b>spec</b>,
+ * whose length must be correct, and given a secret passphrase <b>secret</b>
+ * of length <b>secret_len</b>, compute the key and store it into
+ * <b>key_out</b>, which must have enough room for secret_to_key_key_len(type)
+ * bytes.  Return the number of bytes written on success and an error code
+ * on failure.
+ */
+STATIC int
+secret_to_key_compute_key(uint8_t *key_out, size_t key_out_len,
+                          const uint8_t *spec, size_t spec_len,
+                          const char *secret, size_t secret_len,
+                          int type)
+{
+  int rv;
+  if (key_out_len > INT_MAX)
+    return S2K_BAD_LEN;
+
+  switch (type) {
+    case S2K_TYPE_RFC2440:
+      secret_to_key_rfc2440((char*)key_out, key_out_len, secret, secret_len,
+                            (const char*)spec);
+      return (int)key_out_len;
+
+    case S2K_TYPE_PBKDF2: {
+      uint8_t log_iters;
+      if (spec_len < 1 || secret_len > INT_MAX || spec_len > INT_MAX)
+        return S2K_BAD_LEN;
+      log_iters = spec[spec_len-1];
+      if (log_iters > 31)
+        return S2K_BAD_PARAMS;
+      rv = PKCS5_PBKDF2_HMAC_SHA1(secret, (int)secret_len,
+                                  spec, (int)spec_len-1,
+                                  (1<<log_iters),
+                                  (int)key_out_len, key_out);
+      if (rv < 0)
+        return S2K_FAILED;
+      return (int)key_out_len;
+    }
+
+    case S2K_TYPE_SCRYPT: {
+#ifdef HAVE_SCRYPT
+      uint8_t log_N, log_r, log_p;
+      uint64_t N;
+      uint32_t r, p;
+      if (spec_len < 2)
+        return S2K_BAD_LEN;
+      log_N = spec[spec_len-2];
+      log_r = (spec[spec_len-1]) >> 4;
+      log_p = (spec[spec_len-1]) & 15;
+      if (log_N > 63)
+        return S2K_BAD_PARAMS;
+      N = ((uint64_t)1) << log_N;
+      r = 1u << log_r;
+      p = 1u << log_p;
+      rv = libscrypt_scrypt((const uint8_t*)secret, secret_len,
+                            spec, spec_len-2, N, r, p, key_out, key_out_len);
+      if (rv != 0)
+        return S2K_FAILED;
+      return (int)key_out_len;
+#else
+      return S2K_NO_SCRYPT_SUPPORT;
+#endif
+    }
+    default:
+      return S2K_BAD_ALGORITHM;
+  }
+}
+
+/**
+ * Given a specifier previously constructed with secret_to_key_make_specifier
+ * in <b>spec</b> of length <b>spec_len</b>, and a secret password in
+ * <b>secret</b> of length <b>secret_len</b>, generate <b>key_out_len</b>
+ * bytes of cryptographic material in <b>key_out</b>.  The native output of
+ * the secret-to-key function will be truncated if key_out_len is short, and
+ * expanded with HKDF if key_out_len is long.  Returns S2K_OKAY on success,
+ * and an error code on failure.
+ */
+int
+secret_to_key_derivekey(uint8_t *key_out, size_t key_out_len,
+                        const uint8_t *spec, size_t spec_len,
+                        const char *secret, size_t secret_len)
+{
+  int legacy_format = 0;
+  int type = secret_to_key_get_type(spec, spec_len, 0, &legacy_format);
+  int r;
+
+  if (type < 0)
+    return type;
+#ifndef HAVE_SCRYPT
+  if (type == S2K_TYPE_SCRYPT)
+    return S2K_NO_SCRYPT_SUPPORT;
+ #endif
+
+  if (! legacy_format) {
+    ++spec;
+    --spec_len;
+  }
+
+  r = secret_to_key_compute_key(key_out, key_out_len, spec, spec_len,
+                                secret, secret_len, type);
+  if (r < 0)
+    return r;
+  else
+    return S2K_OKAY;
+}
+
+/**
+ * Construct a new s2k algorithm specifier and salt in <b>buf</b>, according
+ * to the bitwise-or of some S2K_FLAG_* options in <b>flags</b>.  Up to
+ * <b>buf_len</b> bytes of storage may be used in <b>buf</b>.  Return the
+ * number of bytes used on success and an error code on failure.
+ */
+int
+secret_to_key_make_specifier(uint8_t *buf, size_t buf_len, unsigned flags)
+{
+  int rv;
+  int spec_len;
+#ifdef HAVE_SCRYPT
+  uint8_t type = S2K_TYPE_SCRYPT;
+#else
+  uint8_t type = S2K_TYPE_RFC2440;
+#endif
+
+  if (flags & S2K_FLAG_NO_SCRYPT)
+    type = S2K_TYPE_RFC2440;
+  if (flags & S2K_FLAG_USE_PBKDF2)
+    type = S2K_TYPE_PBKDF2;
+
+  spec_len = secret_to_key_spec_len(type);
+
+  if ((int)buf_len < spec_len + 1)
+    return S2K_TRUNCATED;
+
+  buf[0] = type;
+  rv = make_specifier(buf+1, type, flags);
+  if (rv < 0)
+    return rv;
+  else
+    return rv + 1;
+}
+
+/**
+ * Hash a passphrase from <b>secret</b> of length <b>secret_len</b>, according
+ * to the bitwise-or of some S2K_FLAG_* options in <b>flags</b>, and store the
+ * hash along with salt and hashing parameters into <b>buf</b>.  Up to
+ * <b>buf_len</b> bytes of storage may be used in <b>buf</b>.  Set
+ * *<b>len_out</b> to the number of bytes used and return S2K_OKAY on success;
+ * and return an error code on failure.
+ */
+int
+secret_to_key_new(uint8_t *buf,
+                  size_t buf_len,
+                  size_t *len_out,
+                  const char *secret, size_t secret_len,
+                  unsigned flags)
+{
+  int key_len;
+  int spec_len;
+  int type;
+  int rv;
+
+  spec_len = secret_to_key_make_specifier(buf, buf_len, flags);
+
+  if (spec_len < 0)
+    return spec_len;
+
+  type = buf[0];
+  key_len = secret_to_key_key_len(type);
+
+  if ((int)buf_len < key_len + spec_len)
+    return S2K_TRUNCATED;
+
+  rv = secret_to_key_compute_key(buf + spec_len, key_len,
+                                 buf + 1, spec_len-1,
+                                 secret, secret_len, type);
+  if (rv < 0)
+    return rv;
+
+  *len_out = spec_len + key_len;
+
+  return S2K_OKAY;
+}
+
+/**
+ * Given a hashed passphrase in <b>spec_and_key</b> of length
+ * <b>spec_and_key_len</b> as generated by secret_to_key_new(), verify whether
+ * it is a hash of the passphrase <b>secret</b> of length <b>secret_len</b>.
+ * Return S2K_OKAY on a match, S2K_BAD_SECRET on a well-formed hash that
+ * doesn't match this secret, and another error code on other errors.
+ */
+int
+secret_to_key_check(const uint8_t *spec_and_key, size_t spec_and_key_len,
+                    const char *secret, size_t secret_len)
+{
+  int is_legacy = 0;
+  int type = secret_to_key_get_type(spec_and_key, spec_and_key_len,
+                                    1, &is_legacy);
+  uint8_t buf[32];
+  int spec_len;
+  int key_len;
+  int rv;
+
+  if (type < 0)
+    return type;
+
+  if (! is_legacy) {
+    spec_and_key++;
+    spec_and_key_len--;
+  }
+
+  spec_len = secret_to_key_spec_len(type);
+  key_len = secret_to_key_key_len(type);
+  tor_assert(spec_len > 0);
+  tor_assert(key_len > 0);
+  tor_assert(key_len <= (int) sizeof(buf));
+  tor_assert((int)spec_and_key_len == spec_len + key_len);
+  rv = secret_to_key_compute_key(buf, key_len,
+                                 spec_and_key, spec_len,
+                                 secret, secret_len, type);
+  if (rv < 0)
+    goto done;
+
+  if (tor_memeq(buf, spec_and_key + spec_len, key_len))
+    rv = S2K_OKAY;
+  else
+    rv = S2K_BAD_SECRET;
+
+ done:
+  memwipe(buf, 0, sizeof(buf));
+  return rv;
+}
+

+ 73 - 0
src/common/crypto_s2k.h

@@ -0,0 +1,73 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_CRYPTO_S2K_H_INCLUDED
+#define TOR_CRYPTO_S2K_H_INCLUDED
+
+#include <stdio.h>
+#include "torint.h"
+
+/** Length of RFC2440-style S2K specifier: the first 8 bytes are a salt, the
+ * 9th describes how much iteration to do. */
+#define S2K_RFC2440_SPECIFIER_LEN 9
+void secret_to_key_rfc2440(
+                   char *key_out, size_t key_out_len, const char *secret,
+                   size_t secret_len, const char *s2k_specifier);
+
+/** Flag for secret-to-key function: do not use scrypt. */
+#define S2K_FLAG_NO_SCRYPT  (1u<<0)
+/** Flag for secret-to-key functions: if using a memory-tuned s2k function,
+ * assume that we have limited memory. */
+#define S2K_FLAG_LOW_MEM    (1u<<1)
+/** Flag for secret-to-key functions: force use of pbkdf2.  Without this, we
+ * default to scrypt, then RFC2440. */
+#define S2K_FLAG_USE_PBKDF2 (1u<<2)
+
+/** Maximum possible output length from secret_to_key_new. */
+#define S2K_MAXLEN 64
+
+/** Error code from secret-to-key functions: all is well */
+#define S2K_OKAY 0
+/** Error code from secret-to-key functions: generic failure */
+#define S2K_FAILED -1
+/** Error code from secret-to-key functions: provided secret didn't match */
+#define S2K_BAD_SECRET -2
+/** Error code from secret-to-key functions: didn't recognize the algorithm */
+#define S2K_BAD_ALGORITHM -3
+/** Error code from secret-to-key functions: specifier wasn't valid */
+#define S2K_BAD_PARAMS -4
+/** Error code from secret-to-key functions: compiled without scrypt */
+#define S2K_NO_SCRYPT_SUPPORT -5
+/** Error code from secret-to-key functions: not enough space to write output.
+ */
+#define S2K_TRUNCATED -6
+/** Error code from secret-to-key functions: Wrong length for specifier. */
+#define S2K_BAD_LEN -7
+
+int secret_to_key_new(uint8_t *buf,
+                      size_t buf_len,
+                      size_t *len_out,
+                      const char *secret, size_t secret_len,
+                      unsigned flags);
+
+int secret_to_key_make_specifier(uint8_t *buf, size_t buf_len, unsigned flags);
+
+int secret_to_key_check(const uint8_t *spec_and_key, size_t spec_and_key_len,
+                          const char *secret, size_t secret_len);
+
+int secret_to_key_derivekey(uint8_t *key_out, size_t key_out_len,
+                            const uint8_t *spec, size_t spec_len,
+                            const char *secret, size_t secret_len);
+
+#ifdef CRYPTO_S2K_PRIVATE
+STATIC int secret_to_key_compute_key(uint8_t *key_out, size_t key_out_len,
+                                     const uint8_t *spec, size_t spec_len,
+                                     const char *secret, size_t secret_len,
+                                     int type);
+#endif
+
+#endif
+

+ 7 - 1
src/common/include.am

@@ -16,7 +16,7 @@ EXTRA_DIST+= \
   src/common/Makefile.nmake
 
 #CFLAGS  = -Wall -Wpointer-arith -O2
-AM_CPPFLAGS += -I$(srcdir)/src/common -Isrc/common
+AM_CPPFLAGS += -I$(srcdir)/src/common -Isrc/common -I$(srcdir)/src/ext/trunnel -I$(srcdir)/src/trunnel
 
 if USE_OPENBSD_MALLOC
 libor_extra_source=src/ext/OpenBSD_malloc_Linux.c
@@ -69,15 +69,19 @@ LIBOR_A_SOURCES = \
   src/common/util_process.c				\
   src/common/sandbox.c					\
   src/ext/csiphash.c					\
+  src/ext/trunnel/trunnel.c				\
   $(libor_extra_source)					\
   $(libor_mempool_source)
 
 LIBOR_CRYPTO_A_SOURCES = \
   src/common/aes.c		\
   src/common/crypto.c		\
+  src/common/crypto_pwbox.c     \
+  src/common/crypto_s2k.c	\
   src/common/crypto_format.c	\
   src/common/torgzip.c		\
   src/common/tortls.c		\
+  src/trunnel/pwbox.c	 	\
   $(libcrypto_extra_source)
 
 LIBOR_EVENT_A_SOURCES = \
@@ -110,6 +114,8 @@ COMMONHEADERS = \
   src/common/container.h			\
   src/common/crypto.h				\
   src/common/crypto_curve25519.h		\
+  src/common/crypto_pwbox.h			\
+  src/common/crypto_s2k.h			\
   src/common/di_ops.h				\
   src/common/memarea.h				\
   src/common/linux_syscalls.inc			\

+ 306 - 0
src/ext/trunnel/trunnel-impl.h

@@ -0,0 +1,306 @@
+/* trunnel-impl.h -- Implementation helpers for trunnel, included by
+ * generated trunnel files
+ *
+ * Copyright 2014, The Tor Project, Inc.
+ * See license at the end of this file for copying information.
+ */
+
+#ifndef TRUNNEL_IMPL_H_INCLUDED_
+#define TRUNNEL_IMPL_H_INCLUDED_
+#include "trunnel.h"
+#include <assert.h>
+#include <string.h>
+#ifdef TRUNNEL_LOCAL_H
+#include "trunnel-local.h"
+#endif
+
+#ifdef _MSC_VER
+#define uint8_t unsigned char
+#define uint16_t unsigned short
+#define uint32_t unsigned int
+#define uint64_t unsigned __int64
+#define inline __inline
+#else
+#include <stdint.h>
+#endif
+
+#ifdef _WIN32
+uint32_t trunnel_htonl(uint32_t a);
+uint32_t trunnel_ntohl(uint32_t a);
+uint16_t trunnel_htons(uint16_t a);
+uint16_t trunnel_ntohs(uint16_t a);
+#else
+#include <arpa/inet.h>
+#define trunnel_htonl(x) htonl(x)
+#define trunnel_htons(x) htons(x)
+#define trunnel_ntohl(x) ntohl(x)
+#define trunnel_ntohs(x) ntohs(x)
+#endif
+uint64_t trunnel_htonll(uint64_t a);
+uint64_t trunnel_ntohll(uint64_t a);
+
+#ifndef trunnel_assert
+#define trunnel_assert(x) assert(x)
+#endif
+
+static inline void
+trunnel_set_uint64(void *p, uint64_t v) {
+  memcpy(p, &v, 8);
+}
+static inline void
+trunnel_set_uint32(void *p, uint32_t v) {
+  memcpy(p, &v, 4);
+}
+static inline void
+trunnel_set_uint16(void *p, uint16_t v) {
+  memcpy(p, &v, 2);
+}
+static inline void
+trunnel_set_uint8(void *p, uint8_t v) {
+  memcpy(p, &v, 1);
+}
+
+static inline uint64_t
+trunnel_get_uint64(const void *p) {
+  uint64_t x;
+  memcpy(&x, p, 8);
+  return x;
+}
+static inline uint32_t
+trunnel_get_uint32(const void *p) {
+  uint32_t x;
+  memcpy(&x, p, 4);
+  return x;
+}
+static inline uint16_t
+trunnel_get_uint16(const void *p) {
+  uint16_t x;
+  memcpy(&x, p, 2);
+  return x;
+}
+static inline uint8_t
+trunnel_get_uint8(const void *p) {
+  return *(const uint8_t*)p;
+}
+
+
+#ifdef TRUNNEL_DEBUG_FAILING_ALLOC
+extern int trunnel_provoke_alloc_failure;
+
+static inline void *
+trunnel_malloc(size_t n)
+{
+   if (trunnel_provoke_alloc_failure) {
+     if (--trunnel_provoke_alloc_failure == 0)
+       return NULL;
+   }
+   return malloc(n);
+}
+static inline void *
+trunnel_calloc(size_t a, size_t b)
+{
+   if (trunnel_provoke_alloc_failure) {
+     if (--trunnel_provoke_alloc_failure == 0)
+       return NULL;
+   }
+   return calloc(a,b);
+}
+static inline char *
+trunnel_strdup(const char *s)
+{
+   if (trunnel_provoke_alloc_failure) {
+     if (--trunnel_provoke_alloc_failure == 0)
+       return NULL;
+   }
+   return strdup(s);
+}
+#else
+#ifndef trunnel_malloc
+#define trunnel_malloc(x) (malloc((x)))
+#endif
+#ifndef trunnel_calloc
+#define trunnel_calloc(a,b) (calloc((a),(b)))
+#endif
+#ifndef trunnel_strdup
+#define trunnel_strdup(s) (strdup((s)))
+#endif
+#endif
+
+#ifndef trunnel_realloc
+#define trunnel_realloc(a,b) realloc((a),(b))
+#endif
+
+#ifndef trunnel_free_
+#define trunnel_free_(x) (free(x))
+#endif
+#define trunnel_free(x) ((x) ? (trunnel_free_(x),0) : (0))
+
+#ifndef trunnel_abort
+#define trunnel_abort() abort()
+#endif
+
+#ifndef trunnel_memwipe
+#define trunnel_memwipe(mem, len) ((void)0)
+#define trunnel_wipestr(s) ((void)0)
+#else
+#define trunnel_wipestr(s) do {                 \
+    if (s)                                      \
+      trunnel_memwipe(s, strlen(s));            \
+  } while (0)
+#endif
+
+/* ====== dynamic arrays ======== */
+
+#ifdef NDEBUG
+#define TRUNNEL_DYNARRAY_GET(da, n)             \
+  ((da)->elts_[(n)])
+#else
+/** Return the 'n'th element of 'da'. */
+#define TRUNNEL_DYNARRAY_GET(da, n)             \
+  (((n) >= (da)->n_ ? (trunnel_abort(),0) : 0), (da)->elts_[(n)])
+#endif
+
+/** Change the 'n'th element of 'da' to 'v'. */
+#define TRUNNEL_DYNARRAY_SET(da, n, v) do {                    \
+    trunnel_assert((n) < (da)->n_);                            \
+    (da)->elts_[(n)] = (v);                                    \
+  } while (0)
+
+/** Expand the dynamic array 'da' of 'elttype' so that it can hold at least
+ * 'howmanymore' elements than its current capacity. Always tries to increase
+ * the length of the array. On failure, run the code in 'on_fail' and goto
+ * trunnel_alloc_failed. */
+#define TRUNNEL_DYNARRAY_EXPAND(elttype, da, howmanymore, on_fail) do { \
+    elttype *newarray;                                               \
+    newarray = trunnel_dynarray_expand(&(da)->allocated_,            \
+                                       (da)->elts_, (howmanymore),   \
+                                       sizeof(elttype));             \
+    if (newarray == NULL) {                                          \
+      on_fail;                                                       \
+      goto trunnel_alloc_failed;                                     \
+    }                                                                \
+    (da)->elts_ = newarray;                                          \
+  } while (0)
+
+/** Add 'v' to the end of the dynamic array 'da' of 'elttype', expanding it if
+ * necessary.  code in 'on_fail' and goto trunnel_alloc_failed. */
+#define TRUNNEL_DYNARRAY_ADD(elttype, da, v, on_fail) do { \
+      if ((da)->n_ == (da)->allocated_) {                  \
+        TRUNNEL_DYNARRAY_EXPAND(elttype, da, 1, on_fail);  \
+      }                                                    \
+      (da)->elts_[(da)->n_++] = (v);                       \
+    } while (0)
+
+/** Return the number of elements in 'da'. */
+#define TRUNNEL_DYNARRAY_LEN(da) ((da)->n_)
+
+/** Remove all storage held by 'da' and set it to be empty.  Does not free
+ * storage held by the elements themselves. */
+#define TRUNNEL_DYNARRAY_CLEAR(da) do {           \
+    trunnel_free((da)->elts_);                    \
+    (da)->elts_ = NULL;                           \
+    (da)->n_ = (da)->allocated_ = 0;              \
+  } while (0)
+
+/** Remove all storage held by 'da' and set it to be empty.  Does not free
+ * storage held by the elements themselves. */
+#define TRUNNEL_DYNARRAY_WIPE(da) do {                                  \
+    trunnel_memwipe((da)->elts_, (da)->allocated_ * sizeof((da)->elts_[0])); \
+  } while (0)
+
+/** Helper: wraps or implements an OpenBSD-style reallocarray.  Behaves
+ * as realloc(a, x*y), but verifies that no overflow will occur in the
+ * multiplication. Returns NULL on failure. */
+#ifndef trunnel_reallocarray
+void *trunnel_reallocarray(void *a, size_t x, size_t y);
+#endif
+
+/** Helper to expand a dynamic array. Behaves as TRUNNEL_DYNARRAY_EXPAND(),
+ * taking the array of elements in 'ptr', a pointer to thethe current number
+ * of allocated elements in allocated_p, the minimum numbeer of elements to
+ * add in 'howmanymore', and the size of a single element in 'eltsize'.
+ *
+ * On success, adjust *allocated_p, and return the new value for the array of
+ * elements.  On failure, adjust nothing and return NULL.
+ */
+void *trunnel_dynarray_expand(size_t *allocated_p, void *ptr,
+                              size_t howmanymore, size_t eltsize);
+
+/** Type for a function to free members of a dynarray of pointers. */
+typedef void (*trunnel_free_fn_t)(void *);
+
+/**
+ * Helper to change the length of a dynamic array. Takes pointers to the
+ * current allocated and n fields of the array in 'allocated_p' and 'len_p',
+ * and the current array of elements in 'ptr'; takes the length of a single
+ * element in 'eltsize'.  Changes the length to 'newlen'.  If 'newlen' is
+ * greater than the current length, pads the new elements with 0.  If newlen
+ * is less than the current length, and free_fn is non-NULL, treat the
+ * array as an array of void *, and invoke free_fn() on each removed element.
+ *
+ * On success, adjust *allocated_p and *len_p, and return the new value for
+ * the array of elements.  On failure, adjust nothing, set *errcode_ptr to 1,
+ * and return NULL.
+ */
+void *trunnel_dynarray_setlen(size_t *allocated_p, size_t *len_p,
+                              void *ptr, size_t newlen,
+                              size_t eltsize, trunnel_free_fn_t free_fn,
+                              uint8_t *errcode_ptr);
+
+/**
+ * Helper: return a pointer to the value of 'str' as a NUL-terminated string.
+ * Might have to reallocate the storage for 'str' in order to fit in the final
+ * NUL character.  On allocation failure, return NULL.
+ */
+const char *trunnel_string_getstr(trunnel_string_t *str);
+
+/**
+ * Helper: change the contents of 'str' to hold the 'len'-byte string in
+ * 'inp'.  Adjusts the storage to have a terminating NUL that doesn't count
+ * towards the length of the string.  On success, return 0.  On failure, set
+ * *errcode_ptr to 1 and return -1.
+ */
+int trunnel_string_setstr0(trunnel_string_t *str, const char *inp, size_t len,
+                           uint8_t *errcode_ptr);
+
+/**
+ * As trunnel_dynarray_setlen, but adjusts a string rather than a dynamic
+ * array, and ensures that the new string is NUL-terminated.
+ */
+int trunnel_string_setlen(trunnel_string_t *str, size_t newlen,
+                           uint8_t *errcode_ptr);
+
+#endif
+
+
+/*
+Copyright 2014  The Tor Project, Inc.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+    * Neither the names of the copyright owners nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/

+ 242 - 0
src/ext/trunnel/trunnel.c

@@ -0,0 +1,242 @@
+/* trunnel.c -- Helper functions to implement trunnel.
+ *
+ * Copyright 2014, The Tor Project, Inc.
+ * See license at the end of this file for copying information.
+ *
+ * See trunnel-impl.h for documentation of these functions.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "trunnel-impl.h"
+
+#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
+	__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#  define IS_LITTLE_ENDIAN 1
+#elif defined(BYTE_ORDER) && defined(ORDER_LITTLE_ENDIAN) &&     \
+	BYTE_ORDER == __ORDER_LITTLE_ENDIAN
+#  define IS_LITTLE_ENDIAN 1
+#elif defined(_WIN32)
+#  define IS_LITTLE_ENDIAN 1
+#elif defined(__APPLE__)
+#  include <libkern/OSByteOrder.h>
+#  define BSWAP64(x) OSSwapLittleToHostInt64(x)
+#elif defined(sun) || defined(__sun)
+#  include <sys/byteorder.h>
+#  ifndef _BIG_ENDIAN
+#    define IS_LITTLE_ENDIAN
+#  endif
+#else
+# if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
+#  include <sys/endian.h>
+# else
+#  include <endian.h>
+# endif
+#  if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
+	__BYTE_ORDER == __LITTLE_ENDIAN
+#    define IS_LITTLE_ENDIAN
+#  endif
+#endif
+
+#ifdef _WIN32
+uint16_t
+trunnel_htons(uint16_t s)
+{
+  return (s << 8) | (s >> 8);
+}
+uint16_t
+trunnel_ntohs(uint16_t s)
+{
+  return (s << 8) | (s >> 8);
+}
+uint32_t
+trunnel_htonl(uint32_t s)
+{
+  return (s << 24) |
+         ((s << 8)&0xff0000) |
+         ((s >> 8)&0xff00) |
+         (s >> 24);
+}
+uint32_t
+trunnel_ntohl(uint32_t s)
+{
+  return (s << 24) |
+         ((s << 8)&0xff0000) |
+         ((s >> 8)&0xff00) |
+         (s >> 24);
+}
+#endif
+
+uint64_t
+trunnel_htonll(uint64_t a)
+{
+#ifdef IS_LITTLE_ENDIAN
+  return trunnel_htonl(a>>32) | (((uint64_t)trunnel_htonl(a))<<32);
+#else
+  return a;
+#endif
+}
+
+uint64_t
+trunnel_ntohll(uint64_t a)
+{
+  return trunnel_htonll(a);
+}
+
+#ifdef TRUNNEL_DEBUG_FAILING_ALLOC
+/** Used for debugging and running tricky test cases: Makes the nth
+ * memoryation allocation call from now fail.
+ */
+int trunnel_provoke_alloc_failure = 0;
+#endif
+
+void *
+trunnel_dynarray_expand(size_t *allocated_p, void *ptr,
+                        size_t howmanymore, size_t eltsize)
+{
+  size_t newsize = howmanymore + *allocated_p;
+  void *newarray = NULL;
+  if (newsize < 8)
+    newsize = 8;
+  if (newsize < *allocated_p * 2)
+    newsize = *allocated_p * 2;
+  if (newsize <= *allocated_p || newsize < howmanymore)
+    return NULL;
+  newarray = trunnel_reallocarray(ptr, newsize, eltsize);
+  if (newarray == NULL)
+    return NULL;
+
+  *allocated_p = newsize;
+  return newarray;
+}
+
+#ifndef trunnel_reallocarray
+void *
+trunnel_reallocarray(void *a, size_t x, size_t y)
+{
+#ifdef TRUNNEL_DEBUG_FAILING_ALLOC
+   if (trunnel_provoke_alloc_failure) {
+     if (--trunnel_provoke_alloc_failure == 0)
+       return NULL;
+   }
+#endif
+   if (x > SIZE_MAX / y)
+     return NULL;
+   return trunnel_realloc(a, x * y);
+}
+#endif
+
+const char *
+trunnel_string_getstr(trunnel_string_t *str)
+{
+  trunnel_assert(str->allocated_ >= str->n_);
+  if (str->allocated_ == str->n_) {
+    TRUNNEL_DYNARRAY_EXPAND(char, str, 1, {});
+  }
+  str->elts_[str->n_] = 0;
+  return str->elts_;
+trunnel_alloc_failed:
+  return NULL;
+}
+
+int
+trunnel_string_setstr0(trunnel_string_t *str, const char *val, size_t len,
+                       uint8_t *errcode_ptr)
+{
+  if (len == SIZE_MAX)
+    goto trunnel_alloc_failed;
+  if (str->allocated_ <= len) {
+    TRUNNEL_DYNARRAY_EXPAND(char, str, len + 1 - str->allocated_, {});
+  }
+  memcpy(str->elts_, val, len);
+  str->n_ = len;
+  str->elts_[len] = 0;
+  return 0;
+trunnel_alloc_failed:
+  *errcode_ptr = 1;
+  return -1;
+}
+
+int
+trunnel_string_setlen(trunnel_string_t *str, size_t newlen,
+                      uint8_t *errcode_ptr)
+{
+  if (newlen == SIZE_MAX)
+    goto trunnel_alloc_failed;
+  if (str->allocated_ < newlen + 1) {
+    TRUNNEL_DYNARRAY_EXPAND(char, str, newlen + 1 - str->allocated_, {});
+  }
+  if (str->n_ < newlen) {
+    memset(& (str->elts_[str->n_]), 0, (newlen - str->n_));
+  }
+  str->n_ = newlen;
+  str->elts_[newlen] = 0;
+  return 0;
+
+ trunnel_alloc_failed:
+  *errcode_ptr = 1;
+  return -1;
+}
+
+void *
+trunnel_dynarray_setlen(size_t *allocated_p, size_t *len_p,
+                        void *ptr, size_t newlen,
+                        size_t eltsize, trunnel_free_fn_t free_fn,
+                        uint8_t *errcode_ptr)
+{
+  if (*allocated_p < newlen) {
+    void *newptr = trunnel_dynarray_expand(allocated_p, ptr,
+                                           newlen - *allocated_p, eltsize);
+    if (newptr == NULL)
+      goto trunnel_alloc_failed;
+    ptr = newptr;
+  }
+  if (free_fn && *len_p > newlen) {
+    size_t i;
+    void **elts = (void **) ptr;
+    for (i = newlen; i < *len_p; ++i) {
+      free_fn(elts[i]);
+      elts[i] = NULL;
+    }
+  }
+  if (*len_p < newlen) {
+    memset( ((char*)ptr) + (eltsize * *len_p), 0, (newlen - *len_p) * eltsize);
+  }
+  *len_p = newlen;
+  return ptr;
+ trunnel_alloc_failed:
+  *errcode_ptr = 1;
+  return NULL;
+}
+
+/*
+Copyright 2014  The Tor Project, Inc.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+    * Neither the names of the copyright owners nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/

+ 60 - 0
src/ext/trunnel/trunnel.h

@@ -0,0 +1,60 @@
+/* trunnel.h -- Public declarations for trunnel, to be included
+ * in trunnel header files.
+
+ * Copyright 2014, The Tor Project, Inc.
+ * See license at the end of this file for copying information.
+ */
+
+#ifndef TRUNNEL_H_INCLUDED_
+#define TRUNNEL_H_INCLUDED_
+
+#include <sys/types.h>
+
+/** Macro to declare a variable-length dynamically allocated array.  Trunnel
+ * uses these to store all variable-length arrays. */
+#define TRUNNEL_DYNARRAY_HEAD(name, elttype)       \
+  struct name {                                    \
+    size_t n_;                                     \
+    size_t allocated_;                             \
+    elttype *elts_;                                \
+  }
+
+/** Initializer for a dynamic array of a given element type. */
+#define TRUNNEL_DYNARRAY_INIT(elttype) { 0, 0, (elttype*)NULL }
+
+/** Typedef used for storing variable-length arrays of char. */
+typedef TRUNNEL_DYNARRAY_HEAD(trunnel_string_st, char) trunnel_string_t;
+
+#endif
+
+/*
+Copyright 2014  The Tor Project, Inc.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+    * Neither the names of the copyright owners nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/

+ 2 - 0
src/include.am

@@ -1,7 +1,9 @@
 include src/ext/include.am
+include src/trunnel/include.am
 include src/common/include.am
 include src/or/include.am
 include src/test/include.am
 include src/tools/include.am
 include src/win32/include.am
 include src/config/include.am
+

+ 11 - 6
src/or/control.c

@@ -47,6 +47,7 @@
 #include <sys/resource.h>
 #endif
 
+#include "crypto_s2k.h"
 #include "procmon.h"
 
 /** Yield true iff <b>s</b> is the state of a control_connection_t that has
@@ -995,7 +996,8 @@ handle_control_setevents(control_connection_t *conn, uint32_t len,
 
 /** Decode the hashed, base64'd passwords stored in <b>passwords</b>.
  * Return a smartlist of acceptable passwords (unterminated strings of
- * length S2K_SPECIFIER_LEN+DIGEST_LEN) on success, or NULL on failure.
+ * length S2K_RFC2440_SPECIFIER_LEN+DIGEST_LEN) on success, or NULL on
+ * failure.
  */
 smartlist_t *
 decode_hashed_passwords(config_line_t *passwords)
@@ -1011,16 +1013,17 @@ decode_hashed_passwords(config_line_t *passwords)
 
     if (!strcmpstart(hashed, "16:")) {
       if (base16_decode(decoded, sizeof(decoded), hashed+3, strlen(hashed+3))<0
-          || strlen(hashed+3) != (S2K_SPECIFIER_LEN+DIGEST_LEN)*2) {
+          || strlen(hashed+3) != (S2K_RFC2440_SPECIFIER_LEN+DIGEST_LEN)*2) {
         goto err;
       }
     } else {
         if (base64_decode(decoded, sizeof(decoded), hashed, strlen(hashed))
-            != S2K_SPECIFIER_LEN+DIGEST_LEN) {
+            != S2K_RFC2440_SPECIFIER_LEN+DIGEST_LEN) {
           goto err;
         }
     }
-    smartlist_add(sl, tor_memdup(decoded, S2K_SPECIFIER_LEN+DIGEST_LEN));
+    smartlist_add(sl,
+                  tor_memdup(decoded, S2K_RFC2440_SPECIFIER_LEN+DIGEST_LEN));
   }
 
   return sl;
@@ -1173,8 +1176,10 @@ handle_control_authenticate(control_connection_t *conn, uint32_t len,
     } else {
       SMARTLIST_FOREACH(sl, char *, expected,
       {
-        secret_to_key(received,DIGEST_LEN,password,password_len,expected);
-        if (tor_memeq(expected+S2K_SPECIFIER_LEN, received, DIGEST_LEN))
+        secret_to_key_rfc2440(received,DIGEST_LEN,
+                              password,password_len,expected);
+        if (tor_memeq(expected + S2K_RFC2440_SPECIFIER_LEN,
+                      received, DIGEST_LEN))
           goto ok;
       });
       SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));

+ 5 - 4
src/or/main.c

@@ -28,6 +28,7 @@
 #include "connection_or.h"
 #include "control.h"
 #include "cpuworker.h"
+#include "crypto_s2k.h"
 #include "directory.h"
 #include "dirserv.h"
 #include "dirvote.h"
@@ -2674,11 +2675,11 @@ do_hash_password(void)
 {
 
   char output[256];
-  char key[S2K_SPECIFIER_LEN+DIGEST_LEN];
+  char key[S2K_RFC2440_SPECIFIER_LEN+DIGEST_LEN];
 
-  crypto_rand(key, S2K_SPECIFIER_LEN-1);
-  key[S2K_SPECIFIER_LEN-1] = (uint8_t)96; /* Hash 64 K of data. */
-  secret_to_key(key+S2K_SPECIFIER_LEN, DIGEST_LEN,
+  crypto_rand(key, S2K_RFC2440_SPECIFIER_LEN-1);
+  key[S2K_RFC2440_SPECIFIER_LEN-1] = (uint8_t)96; /* Hash 64 K of data. */
+  secret_to_key_rfc2440(key+S2K_RFC2440_SPECIFIER_LEN, DIGEST_LEN,
                 get_options()->command_arg, strlen(get_options()->command_arg),
                 key);
   base16_encode(output, sizeof(output), key, sizeof(key));

+ 1 - 1
src/test/include.am

@@ -60,7 +60,7 @@ src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
         @TOR_LDFLAGS_libevent@
 src_test_test_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \
 	src/common/libor-crypto-testing.a $(LIBDONNA) \
-	src/common/libor-event-testing.a \
+	src/common/libor-event-testing.a src/trunnel/libor-trunnel-testing.a \
 	@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
 	@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
 

+ 359 - 4
src/test/test_crypto.c

@@ -5,6 +5,7 @@
 
 #include "orconfig.h"
 #define CRYPTO_CURVE25519_PRIVATE
+#define CRYPTO_S2K_PRIVATE
 #include "or.h"
 #include "test.h"
 #include "aes.h"
@@ -13,6 +14,8 @@
 #ifdef CURVE25519_ENABLED
 #include "crypto_curve25519.h"
 #endif
+#include "crypto_s2k.h"
+#include "crypto_pwbox.h"
 
 extern const char AUTHORITY_SIGNKEY_3[];
 extern const char AUTHORITY_SIGNKEY_A_DIGEST[];
@@ -710,7 +713,7 @@ test_crypto_formats(void *arg)
 
 /** Run unit tests for our secret-to-key passphrase hashing functionality. */
 static void
-test_crypto_s2k(void *arg)
+test_crypto_s2k_rfc2440(void *arg)
 {
   char buf[29];
   char buf2[29];
@@ -723,7 +726,7 @@ test_crypto_s2k(void *arg)
   buf3 = tor_malloc(65536);
   memset(buf3, 0, 65536);
 
-  secret_to_key(buf+9, 20, "", 0, buf);
+  secret_to_key_rfc2440(buf+9, 20, "", 0, buf);
   crypto_digest(buf2+9, buf3, 1024);
   tt_mem_op(buf,==, buf2, 29);
 
@@ -731,7 +734,7 @@ test_crypto_s2k(void *arg)
   memcpy(buf2,"vrbacrda",8);
   buf[8] = 96;
   buf2[8] = 96;
-  secret_to_key(buf+9, 20, "12345678", 8, buf);
+  secret_to_key_rfc2440(buf+9, 20, "12345678", 8, buf);
   for (i = 0; i < 65536; i += 16) {
     memcpy(buf3+i, "vrbacrda12345678", 16);
   }
@@ -742,6 +745,342 @@ test_crypto_s2k(void *arg)
   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, ==, S2K_OKAY);
+  tt_int_op(buf[0], ==, type);
+
+  tt_int_op(sz, ==, keylen + speclen);
+
+  if (legacy) {
+    memmove(buf, buf+1, sz-1);
+    --sz;
+    --speclen;
+  }
+
+  tt_int_op(S2K_OKAY, ==,
+            secret_to_key_check(buf, sz, pw1, strlen(pw1)));
+
+  tt_int_op(S2K_BAD_SECRET, ==,
+            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, ==,
+      secret_to_key_derivekey(buf3, keylen, buf, speclen, pw1, strlen(pw1)));
+
+  tt_mem_op(buf2, ==, buf3, keylen);
+
+  /* Derivekey with a longer output should fill the output. */
+  memset(buf2, 0, sizeof(buf2));
+  tt_int_op(S2K_OKAY, ==,
+   secret_to_key_derivekey(buf2, sizeof(buf2), buf, speclen,
+                           pw1, strlen(pw1)));
+
+  tt_mem_op(buf2, !=, buf3, sizeof(buf2));
+
+  memset(buf3, 0, sizeof(buf3));
+  tt_int_op(S2K_OKAY, ==,
+            secret_to_key_derivekey(buf3, sizeof(buf3), buf, speclen,
+                                    pw1, strlen(pw1)));
+  tt_mem_op(buf2, ==, 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, ==,
+            secret_to_key_derivekey(buf, sizeof(buf),
+                                    (const uint8_t*)"", 0, "ABC", 3));
+  tt_int_op(S2K_BAD_ALGORITHM, ==,
+            secret_to_key_derivekey(buf, sizeof(buf),
+                                    (const uint8_t*)"\x10", 1, "ABC", 3));
+  tt_int_op(S2K_BAD_LEN, ==,
+            secret_to_key_derivekey(buf, sizeof(buf),
+                                    (const uint8_t*)"\x01\x02", 2, "ABC", 3));
+
+  tt_int_op(S2K_BAD_LEN, ==,
+            secret_to_key_check((const uint8_t*)"", 0, "ABC", 3));
+  tt_int_op(S2K_BAD_ALGORITHM, ==,
+            secret_to_key_check((const uint8_t*)"\x10", 1, "ABC", 3));
+  tt_int_op(S2K_BAD_LEN, ==,
+            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, ==,
+            secret_to_key_derivekey(buf2, sizeof(buf2),
+                                    buf, sizeof(buf), "ABC", 3));
+
+  /* Truncated output */
+#ifdef HAVE_LIBSCRYPT_H
+  tt_int_op(S2K_TRUNCATED, ==, secret_to_key_new(buf, 50, &sz,
+                                                 "ABC", 3, 0));
+  tt_int_op(S2K_TRUNCATED, ==, secret_to_key_new(buf, 50, &sz,
+                                                 "ABC", 3, S2K_FLAG_LOW_MEM));
+#endif
+  tt_int_op(S2K_TRUNCATED, ==, secret_to_key_new(buf, 37, &sz,
+                                              "ABC", 3, S2K_FLAG_USE_PBKDF2));
+  tt_int_op(S2K_TRUNCATED, ==, secret_to_key_new(buf, 29, &sz,
+                                              "ABC", 3, S2K_FLAG_NO_SCRYPT));
+
+#ifdef HAVE_LIBSCRYPT_H
+  tt_int_op(S2K_TRUNCATED, ==, secret_to_key_make_specifier(buf, 18, 0));
+  tt_int_op(S2K_TRUNCATED, ==, secret_to_key_make_specifier(buf, 18,
+                                                 S2K_FLAG_LOW_MEM));
+#endif
+  tt_int_op(S2K_TRUNCATED, ==, secret_to_key_make_specifier(buf, 17,
+                                                 S2K_FLAG_USE_PBKDF2));
+  tt_int_op(S2K_TRUNCATED, ==, 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, ==,
+            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, ==,
+            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, ==,
+            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, ==,
+            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, ==,
+            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, ==,
+            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, ==,
+            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, ==,
+            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, ==,
+            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, ==,
+            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, ==,
+            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, ==,
+            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, ==, 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, ==, crypto_unpwbox(&decoded, &dlen, boxed, len,
+                                    pw, strlen(pw)));
+
+    tt_assert(decoded);
+    tt_uint_op(dlen, ==, strlen(msg));
+    tt_mem_op(decoded, ==, msg, dlen);
+
+    tor_free(decoded);
+
+    tt_int_op(UNPWBOX_BAD_SECRET, ==, crypto_unpwbox(&decoded, &dlen, boxed, len,
+                                                     pw, strlen(pw)-1));
+    boxed[len-1] ^= 1;
+    tt_int_op(UNPWBOX_BAD_SECRET, ==, crypto_unpwbox(&decoded, &dlen, boxed, len,
+                                                     pw, strlen(pw)));
+    boxed[0] = 255;
+    tt_int_op(UNPWBOX_CORRUPTED, ==, 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)
@@ -1304,7 +1643,23 @@ struct testcase_t crypto_tests[] = {
   { "pk_fingerprints", test_crypto_pk_fingerprints, TT_FORK, NULL, NULL },
   CRYPTO_LEGACY(digests),
   CRYPTO_LEGACY(dh),
-  CRYPTO_LEGACY(s2k),
+  CRYPTO_LEGACY(s2k_rfc2440),
+#ifdef HAVE_LIBSCRYPT_H
+  { "s2k_scrypt", test_crypto_s2k_general, 0, &pass_data,
+    (void*)"scrypt" },
+  { "s2k_scrypt_low", test_crypto_s2k_general, 0, &pass_data,
+    (void*)"scrypt-low" },
+#endif
+  { "s2k_pbkdf2", test_crypto_s2k_general, 0, &pass_data,
+    (void*)"pbkdf2" },
+  { "s2k_rfc2440_general", test_crypto_s2k_general, 0, &pass_data,
+    (void*)"rfc2440" },
+  { "s2k_rfc2440_legacy", test_crypto_s2k_general, 0, &pass_data,
+    (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, &pass_data, (void*)"aes" },
   { "aes_iv_EVP", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"evp" },
   CRYPTO_LEGACY(base32_decode),

+ 29 - 0
src/trunnel/include.am

@@ -0,0 +1,29 @@
+
+noinst_LIBRARIES += \
+	src/trunnel/libor-trunnel.a
+
+if UNITTESTS_ENABLED
+noinst_LIBRARIES += \
+	src/trunnel/libor-trunnel-testing.a
+endif
+
+AM_CPPFLAGS += -I$(srcdir)/src/ext/trunnel -I$(srcdir)/src/trunnel
+
+TRUNNELSOURCES = \
+  src/ext/trunnel/trunnel.c \
+  src/trunnel/pwbox.c
+
+TRUNNELHEADERS = \
+  src/ext/trunnel/trunnel.h \
+  src/ext/trunnel/trunnel-impl.h \
+  src/trunnel/trunnel-local.h \
+  src/trunnel/pwbox.h
+
+src_trunnel_libor_trunnel_a_SOURCES = $(TRUNNELSOURCES)
+src_trunnel_libor_trunnel_a_CPPFLAGS = -DTRUNNEL_LOCAL_H $(AM_CPPFLAGS)
+
+src_trunnel_libor_trunnel_testing_a_SOURCES = $(TRUNNELSOURCES)
+src_trunnel_libor_trunnel_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS -DTRUNNEL_LOCAL_H $(AM_CPPFLAGS)
+src_trunnel_libor_trunnel_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+
+noinst_HEADERS+= $(TRUNNELHEADERS)

+ 509 - 0
src/trunnel/pwbox.c

@@ -0,0 +1,509 @@
+
+/* pwbox.c -- generated by trunnel. */
+#include <stdlib.h>
+#include "trunnel-impl.h"
+
+#include "pwbox.h"
+
+#define TRUNNEL_SET_ERROR_CODE(obj) \
+  do {                              \
+    (obj)->trunnel_error_code_ = 1; \
+  } while (0)
+
+pwbox_encoded_t *
+pwbox_encoded_new(void)
+{
+  pwbox_encoded_t *val = trunnel_calloc(1, sizeof(pwbox_encoded_t));
+  if (NULL == val)
+    return NULL;
+  val->fixedbytes0 = PWBOX0_CONST0;
+  val->fixedbytes1 = PWBOX0_CONST1;
+  return val;
+}
+
+/** Release all storage held inside 'obj', but do not free 'obj'.
+ */
+static void
+pwbox_encoded_clear(pwbox_encoded_t *obj)
+{
+  (void) obj;
+  TRUNNEL_DYNARRAY_WIPE(&obj->skey_header);
+  TRUNNEL_DYNARRAY_CLEAR(&obj->skey_header);
+  TRUNNEL_DYNARRAY_WIPE(&obj->data);
+  TRUNNEL_DYNARRAY_CLEAR(&obj->data);
+}
+
+void
+pwbox_encoded_free(pwbox_encoded_t *obj)
+{
+  if (obj == NULL)
+    return;
+  pwbox_encoded_clear(obj);
+  trunnel_memwipe(obj, sizeof(pwbox_encoded_t));
+  trunnel_free_(obj);
+}
+
+uint32_t
+pwbox_encoded_get_fixedbytes0(pwbox_encoded_t *inp)
+{
+  return inp->fixedbytes0;
+}
+int
+pwbox_encoded_set_fixedbytes0(pwbox_encoded_t *inp, uint32_t val)
+{
+  if (! ((val == PWBOX0_CONST0))) {
+     TRUNNEL_SET_ERROR_CODE(inp);
+     return -1;
+  }
+  inp->fixedbytes0 = val;
+  return 0;
+}
+uint32_t
+pwbox_encoded_get_fixedbytes1(pwbox_encoded_t *inp)
+{
+  return inp->fixedbytes1;
+}
+int
+pwbox_encoded_set_fixedbytes1(pwbox_encoded_t *inp, uint32_t val)
+{
+  if (! ((val == PWBOX0_CONST1))) {
+     TRUNNEL_SET_ERROR_CODE(inp);
+     return -1;
+  }
+  inp->fixedbytes1 = val;
+  return 0;
+}
+uint8_t
+pwbox_encoded_get_header_len(pwbox_encoded_t *inp)
+{
+  return inp->header_len;
+}
+int
+pwbox_encoded_set_header_len(pwbox_encoded_t *inp, uint8_t val)
+{
+  inp->header_len = val;
+  return 0;
+}
+size_t
+pwbox_encoded_getlen_skey_header(const pwbox_encoded_t *inp)
+{
+  return TRUNNEL_DYNARRAY_LEN(&inp->skey_header);
+}
+
+uint8_t
+pwbox_encoded_get_skey_header(pwbox_encoded_t *inp, size_t idx)
+{
+  return TRUNNEL_DYNARRAY_GET(&inp->skey_header, idx);
+}
+
+int
+pwbox_encoded_set_skey_header(pwbox_encoded_t *inp, size_t idx, uint8_t elt)
+{
+  TRUNNEL_DYNARRAY_SET(&inp->skey_header, idx, elt);
+  return 0;
+}
+int
+pwbox_encoded_add_skey_header(pwbox_encoded_t *inp, uint8_t elt)
+{
+#if SIZE_MAX >= UINT8_MAX
+  if (inp->skey_header.n_ == UINT8_MAX)
+    goto trunnel_alloc_failed;
+#endif
+  TRUNNEL_DYNARRAY_ADD(uint8_t, &inp->skey_header, elt, {});
+  return 0;
+ trunnel_alloc_failed:
+  TRUNNEL_SET_ERROR_CODE(inp);
+  return -1;
+}
+
+uint8_t *
+pwbox_encoded_getarray_skey_header(pwbox_encoded_t *inp)
+{
+  return inp->skey_header.elts_;
+}
+int
+pwbox_encoded_setlen_skey_header(pwbox_encoded_t *inp, size_t newlen)
+{
+  uint8_t *newptr;
+#if UINT8_MAX < SIZE_MAX
+  if (newlen > UINT8_MAX)
+    goto trunnel_alloc_failed;
+#endif
+  newptr = trunnel_dynarray_setlen(&inp->skey_header.allocated_,
+                 &inp->skey_header.n_, inp->skey_header.elts_, newlen,
+                 sizeof(inp->skey_header.elts_[0]), (trunnel_free_fn_t) NULL,
+                 &inp->trunnel_error_code_);
+  if (newptr == NULL)
+    goto trunnel_alloc_failed;
+  inp->skey_header.elts_ = newptr;
+  return 0;
+ trunnel_alloc_failed:
+  TRUNNEL_SET_ERROR_CODE(inp);
+  return -1;
+}
+size_t
+pwbox_encoded_getlen_iv(const pwbox_encoded_t *inp)
+{
+  (void)inp;  return 16;
+}
+
+uint8_t
+pwbox_encoded_get_iv(const pwbox_encoded_t *inp, size_t idx)
+{
+  trunnel_assert(idx < 16);
+  return inp->iv[idx];
+}
+
+int
+pwbox_encoded_set_iv(pwbox_encoded_t *inp, size_t idx, uint8_t elt)
+{
+  trunnel_assert(idx < 16);
+  inp->iv[idx] = elt;
+  return 0;
+}
+
+uint8_t *
+pwbox_encoded_getarray_iv(pwbox_encoded_t *inp)
+{
+  return inp->iv;
+}
+size_t
+pwbox_encoded_getlen_data(const pwbox_encoded_t *inp)
+{
+  return TRUNNEL_DYNARRAY_LEN(&inp->data);
+}
+
+uint8_t
+pwbox_encoded_get_data(pwbox_encoded_t *inp, size_t idx)
+{
+  return TRUNNEL_DYNARRAY_GET(&inp->data, idx);
+}
+
+int
+pwbox_encoded_set_data(pwbox_encoded_t *inp, size_t idx, uint8_t elt)
+{
+  TRUNNEL_DYNARRAY_SET(&inp->data, idx, elt);
+  return 0;
+}
+int
+pwbox_encoded_add_data(pwbox_encoded_t *inp, uint8_t elt)
+{
+  TRUNNEL_DYNARRAY_ADD(uint8_t, &inp->data, elt, {});
+  return 0;
+ trunnel_alloc_failed:
+  TRUNNEL_SET_ERROR_CODE(inp);
+  return -1;
+}
+
+uint8_t *
+pwbox_encoded_getarray_data(pwbox_encoded_t *inp)
+{
+  return inp->data.elts_;
+}
+int
+pwbox_encoded_setlen_data(pwbox_encoded_t *inp, size_t newlen)
+{
+  uint8_t *newptr;
+  newptr = trunnel_dynarray_setlen(&inp->data.allocated_,
+                 &inp->data.n_, inp->data.elts_, newlen,
+                 sizeof(inp->data.elts_[0]), (trunnel_free_fn_t) NULL,
+                 &inp->trunnel_error_code_);
+  if (newptr == NULL)
+    goto trunnel_alloc_failed;
+  inp->data.elts_ = newptr;
+  return 0;
+ trunnel_alloc_failed:
+  TRUNNEL_SET_ERROR_CODE(inp);
+  return -1;
+}
+size_t
+pwbox_encoded_getlen_hmac(const pwbox_encoded_t *inp)
+{
+  (void)inp;  return 32;
+}
+
+uint8_t
+pwbox_encoded_get_hmac(const pwbox_encoded_t *inp, size_t idx)
+{
+  trunnel_assert(idx < 32);
+  return inp->hmac[idx];
+}
+
+int
+pwbox_encoded_set_hmac(pwbox_encoded_t *inp, size_t idx, uint8_t elt)
+{
+  trunnel_assert(idx < 32);
+  inp->hmac[idx] = elt;
+  return 0;
+}
+
+uint8_t *
+pwbox_encoded_getarray_hmac(pwbox_encoded_t *inp)
+{
+  return inp->hmac;
+}
+const char *
+pwbox_encoded_check(const pwbox_encoded_t *obj)
+{
+  if (obj == NULL)
+    return "Object was NULL";
+  if (obj->trunnel_error_code_)
+    return "A set function failed on this object";
+  if (! (obj->fixedbytes0 == PWBOX0_CONST0))
+    return "Integer out of bounds";
+  if (! (obj->fixedbytes1 == PWBOX0_CONST1))
+    return "Integer out of bounds";
+  if (TRUNNEL_DYNARRAY_LEN(&obj->skey_header) != obj->header_len)
+    return "Length mismatch for skey_header";
+  return NULL;
+}
+
+ssize_t
+pwbox_encoded_encoded_len(const pwbox_encoded_t *obj)
+{
+  ssize_t result = 0;
+
+  if (NULL != pwbox_encoded_check(obj))
+     return -1;
+
+
+  /* Length of u32 fixedbytes0 IN [PWBOX0_CONST0] */
+  result += 4;
+
+  /* Length of u32 fixedbytes1 IN [PWBOX0_CONST1] */
+  result += 4;
+
+  /* Length of u8 header_len */
+  result += 1;
+
+  /* Length of u8 skey_header[header_len] */
+  result += TRUNNEL_DYNARRAY_LEN(&obj->skey_header);
+
+  /* Length of u8 iv[16] */
+  result += 16;
+
+  /* Length of u8 data[] */
+  result += TRUNNEL_DYNARRAY_LEN(&obj->data);
+
+  /* Length of u8 hmac[32] */
+  result += 32;
+  return result;
+}
+int
+pwbox_encoded_clear_errors(pwbox_encoded_t *obj)
+{
+  int r = obj->trunnel_error_code_;
+  obj->trunnel_error_code_ = 0;
+  return r;
+}
+ssize_t
+pwbox_encoded_encode(uint8_t *output, size_t avail, const pwbox_encoded_t *obj)
+{
+  ssize_t result = 0;
+  size_t written = 0;
+  uint8_t *ptr = output;
+  const char *msg;
+  int enforce_avail = 0;
+  const size_t avail_orig = avail;
+
+  if (NULL != (msg = pwbox_encoded_check(obj)))
+    goto check_failed;
+
+
+  /* Encode u32 fixedbytes0 IN [PWBOX0_CONST0] */
+  trunnel_assert(written <= avail);
+  if (avail - written < 4)
+    goto truncated;
+  trunnel_set_uint32(ptr, trunnel_htonl(obj->fixedbytes0));
+  written += 4; ptr += 4;
+
+  /* Encode u32 fixedbytes1 IN [PWBOX0_CONST1] */
+  trunnel_assert(written <= avail);
+  if (avail - written < 4)
+    goto truncated;
+  trunnel_set_uint32(ptr, trunnel_htonl(obj->fixedbytes1));
+  written += 4; ptr += 4;
+
+  /* Encode u8 header_len */
+  trunnel_assert(written <= avail);
+  if (avail - written < 1)
+    goto truncated;
+  trunnel_set_uint8(ptr, (obj->header_len));
+  written += 1; ptr += 1;
+
+  /* Encode u8 skey_header[header_len] */
+  {
+    size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->skey_header);
+    trunnel_assert(obj->header_len == elt_len);
+    trunnel_assert(written <= avail);
+    if (avail - written < elt_len)
+      goto truncated;
+    memcpy(ptr, obj->skey_header.elts_, elt_len);
+    written += elt_len; ptr += elt_len;
+  }
+
+  /* Encode u8 iv[16] */
+  trunnel_assert(written <= avail);
+  if (avail - written < 16)
+    goto truncated;
+  memcpy(ptr, obj->iv, 16);
+  written += 16; ptr += 16;
+  {
+
+    /* Encode u8 data[] */
+    {
+      size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->data);
+      trunnel_assert(written <= avail);
+      if (avail - written < elt_len)
+        goto truncated;
+      memcpy(ptr, obj->data.elts_, elt_len);
+      written += elt_len; ptr += elt_len;
+    }
+    trunnel_assert(written <= avail);
+    if (avail - written < 32)
+      goto truncated;
+    avail = written + 32;
+    enforce_avail = 1;
+  }
+
+  /* Encode u8 hmac[32] */
+  trunnel_assert(written <= avail);
+  if (avail - written < 32) {
+    if (avail_orig - written < 32)
+      goto truncated;
+    else
+      goto check_failed;
+  }
+  memcpy(ptr, obj->hmac, 32);
+  written += 32; ptr += 32;
+
+
+  trunnel_assert(ptr == output + written);
+  if (enforce_avail && avail != written)
+    goto check_failed;
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+  {
+    ssize_t encoded_len = pwbox_encoded_encoded_len(obj);
+    trunnel_assert(encoded_len >= 0);
+    trunnel_assert((size_t)encoded_len == written);
+  }
+
+#endif
+
+  return written;
+
+ truncated:
+  result = -2;
+  goto fail;
+ check_failed:
+  (void)msg;
+  result = -1;
+  goto fail;
+ fail:
+  trunnel_assert(result < 0);
+  return result;
+}
+
+/** As pwbox_encoded_parse(), but do not allocate the output object.
+ */
+static ssize_t
+pwbox_encoded_parse_into(pwbox_encoded_t *obj, const uint8_t *input, const size_t len_in)
+{
+  const uint8_t *ptr = input;
+  size_t remaining = len_in;
+  ssize_t result = 0;
+  (void)result;
+
+  /* Parse u32 fixedbytes0 IN [PWBOX0_CONST0] */
+  if (remaining < 4)
+    goto truncated;
+  obj->fixedbytes0 = trunnel_ntohl(trunnel_get_uint32(ptr));
+  remaining -= 4; ptr += 4;
+  if (! (obj->fixedbytes0 == PWBOX0_CONST0))
+    goto fail;
+
+  /* Parse u32 fixedbytes1 IN [PWBOX0_CONST1] */
+  if (remaining < 4)
+    goto truncated;
+  obj->fixedbytes1 = trunnel_ntohl(trunnel_get_uint32(ptr));
+  remaining -= 4; ptr += 4;
+  if (! (obj->fixedbytes1 == PWBOX0_CONST1))
+    goto fail;
+
+  /* Parse u8 header_len */
+  if (remaining < 1)
+    goto truncated;
+  obj->header_len = (trunnel_get_uint8(ptr));
+  remaining -= 1; ptr += 1;
+
+  /* Parse u8 skey_header[header_len] */
+  if (remaining < obj->header_len)
+    goto truncated;
+  TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->skey_header, obj->header_len, {});
+  obj->skey_header.n_ = obj->header_len;
+  memcpy(obj->skey_header.elts_, ptr, obj->header_len);
+  ptr += obj->header_len; remaining -= obj->header_len;
+
+  /* Parse u8 iv[16] */
+  if (remaining < (16))
+    goto truncated;
+  memcpy(obj->iv, ptr, 16);
+  {
+    unsigned idx;
+    for (idx = 0; idx < 16; ++idx)
+      obj->iv[idx] = (obj->iv[idx]);
+  }
+  remaining -= 16; ptr += 16;
+  {
+    size_t remaining_after;
+    if (remaining < 32)
+       goto truncated;
+    remaining_after = 32;
+    remaining = remaining - 32;
+
+    /* Parse u8 data[] */
+    TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->data, remaining, {});
+    obj->data.n_ = remaining;
+    memcpy(obj->data.elts_, ptr, remaining);
+    ptr += remaining; remaining -= remaining;
+    if (remaining != 0)
+      goto fail;
+    remaining = remaining_after;
+  }
+
+  /* Parse u8 hmac[32] */
+  if (remaining < (32))
+    goto truncated;
+  memcpy(obj->hmac, ptr, 32);
+  {
+    unsigned idx;
+    for (idx = 0; idx < 32; ++idx)
+      obj->hmac[idx] = (obj->hmac[idx]);
+  }
+  remaining -= 32; ptr += 32;
+  trunnel_assert(ptr + remaining == input + len_in);
+  return len_in - remaining;
+
+ truncated:
+  return -2;
+ trunnel_alloc_failed:
+  return -1;
+ fail:
+  result = -1;
+  return result;
+}
+
+ssize_t
+pwbox_encoded_parse(pwbox_encoded_t **output, const uint8_t *input, const size_t len_in)
+{
+  ssize_t result;
+  *output = pwbox_encoded_new();
+  if (NULL == *output)
+    return -1;
+  result = pwbox_encoded_parse_into(*output, input, len_in);
+  if (result < 0) {
+    pwbox_encoded_free(*output);
+    *output = NULL;
+  }
+  return result;
+}

+ 171 - 0
src/trunnel/pwbox.h

@@ -0,0 +1,171 @@
+
+/* pwbox.h -- generated by trunnel. */
+#ifndef TRUNNEL_PWBOX_H
+#define TRUNNEL_PWBOX_H
+
+#include <stdint.h>
+#include "trunnel.h"
+
+#define PWBOX0_CONST0 1414484546
+#define PWBOX0_CONST1 1331179568
+#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_PWBOX_ENCODED)
+struct pwbox_encoded_st {
+  uint32_t fixedbytes0;
+  uint32_t fixedbytes1;
+  uint8_t header_len;
+  TRUNNEL_DYNARRAY_HEAD(, uint8_t) skey_header;
+  uint8_t iv[16];
+  TRUNNEL_DYNARRAY_HEAD(, uint8_t) data;
+  uint8_t hmac[32];
+  uint8_t trunnel_error_code_;
+};
+#endif
+typedef struct pwbox_encoded_st pwbox_encoded_t;
+/** Return a newly allocated pwbox_encoded with all elements set to
+ * zero.
+ */
+pwbox_encoded_t *pwbox_encoded_new(void);
+/** Release all storage held by the pwbox_encoded in 'victim'. (Do
+ * nothing if 'victim' is NULL.)
+ */
+void pwbox_encoded_free(pwbox_encoded_t *victim);
+/** Try to parse a pwbox_encoded from the buffer in 'input', using up
+ * to 'len_in' bytes from the input buffer. On success, return the
+ * number of bytes consumed and set *output to the newly allocated
+ * pwbox_encoded_t. On failure, return -2 if the input appears
+ * truncated, and -1 if the input is otherwise invalid.
+ */
+ssize_t pwbox_encoded_parse(pwbox_encoded_t **output, const uint8_t *input, const size_t len_in);
+/** Return the number of bytes we expect to need to encode the
+ * pwbox_encoded in 'obj'. On failure, return a negative value. Note
+ * that this value may be an overestimate, and can even be an
+ * underestimate for certain unencodeable objects.
+ */
+ssize_t pwbox_encoded_encoded_len(const pwbox_encoded_t *obj);
+/** Try to encode the pwbox_encoded from 'input' into the buffer at
+ * 'output', using up to 'avail' bytes of the output buffer. On
+ * success, return the number of bytes used. On failure, return -2 if
+ * the buffer was not long enough, and -1 if the input was invalid.
+ */
+ssize_t pwbox_encoded_encode(uint8_t *output, const size_t avail, const pwbox_encoded_t *input);
+/** Check whether the internal state of the pwbox_encoded in 'obj' is
+ * consistent. Return NULL if it is, and a short message if it is not.
+ */
+const char *pwbox_encoded_check(const pwbox_encoded_t *obj);
+/** Clear any errors that were set on the object 'obj' by its setter
+ * functions. Return true iff errors were cleared.
+ */
+int pwbox_encoded_clear_errors(pwbox_encoded_t *obj);
+/** Return the value of the fixedbytes0 field of the pwbox_encoded_t
+ * in 'inp'
+ */
+uint32_t pwbox_encoded_get_fixedbytes0(pwbox_encoded_t *inp);
+/** Set the value of the fixedbytes0 field of the pwbox_encoded_t in
+ * 'inp' to 'val'. Return 0 on success; return -1 and set the error
+ * code on 'inp' on failure.
+ */
+int pwbox_encoded_set_fixedbytes0(pwbox_encoded_t *inp, uint32_t val);
+/** Return the value of the fixedbytes1 field of the pwbox_encoded_t
+ * in 'inp'
+ */
+uint32_t pwbox_encoded_get_fixedbytes1(pwbox_encoded_t *inp);
+/** Set the value of the fixedbytes1 field of the pwbox_encoded_t in
+ * 'inp' to 'val'. Return 0 on success; return -1 and set the error
+ * code on 'inp' on failure.
+ */
+int pwbox_encoded_set_fixedbytes1(pwbox_encoded_t *inp, uint32_t val);
+/** Return the value of the header_len field of the pwbox_encoded_t in
+ * 'inp'
+ */
+uint8_t pwbox_encoded_get_header_len(pwbox_encoded_t *inp);
+/** Set the value of the header_len field of the pwbox_encoded_t in
+ * 'inp' to 'val'. Return 0 on success; return -1 and set the error
+ * code on 'inp' on failure.
+ */
+int pwbox_encoded_set_header_len(pwbox_encoded_t *inp, uint8_t val);
+/** Return the length of the dynamic array holding the skey_header
+ * field of the pwbox_encoded_t in 'inp'.
+ */
+size_t pwbox_encoded_getlen_skey_header(const pwbox_encoded_t *inp);
+/** Return the element at position 'idx' of the dynamic array field
+ * skey_header of the pwbox_encoded_t in 'inp'.
+ */
+uint8_t pwbox_encoded_get_skey_header(pwbox_encoded_t *inp, size_t idx);
+/** Change the element at position 'idx' of the dynamic array field
+ * skey_header of the pwbox_encoded_t in 'inp', so that it will hold
+ * the value 'elt'.
+ */
+int pwbox_encoded_set_skey_header(pwbox_encoded_t *inp, size_t idx, uint8_t elt);
+/** Append a new element 'elt' to the dynamic array field skey_header
+ * of the pwbox_encoded_t in 'inp'.
+ */
+int pwbox_encoded_add_skey_header(pwbox_encoded_t *inp, uint8_t elt);
+/** Return a pointer to the variable-length array field skey_header of
+ * 'inp'.
+ */
+uint8_t * pwbox_encoded_getarray_skey_header(pwbox_encoded_t *inp);
+/** Change the length of the variable-length array field skey_header
+ * of 'inp' to 'newlen'.Fill extra elements with 0. Return 0 on
+ * success; return -1 and set the error code on 'inp' on failure.
+ */
+int pwbox_encoded_setlen_skey_header(pwbox_encoded_t *inp, size_t newlen);
+/** Return the (constant) length of the array holding the iv field of
+ * the pwbox_encoded_t in 'inp'.
+ */
+size_t pwbox_encoded_getlen_iv(const pwbox_encoded_t *inp);
+/** Return the element at position 'idx' of the fixed array field iv
+ * of the pwbox_encoded_t in 'inp'.
+ */
+uint8_t pwbox_encoded_get_iv(const pwbox_encoded_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field iv
+ * of the pwbox_encoded_t in 'inp', so that it will hold the value
+ * 'elt'.
+ */
+int pwbox_encoded_set_iv(pwbox_encoded_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 16-element array field iv of 'inp'.
+ */
+uint8_t * pwbox_encoded_getarray_iv(pwbox_encoded_t *inp);
+/** Return the length of the dynamic array holding the data field of
+ * the pwbox_encoded_t in 'inp'.
+ */
+size_t pwbox_encoded_getlen_data(const pwbox_encoded_t *inp);
+/** Return the element at position 'idx' of the dynamic array field
+ * data of the pwbox_encoded_t in 'inp'.
+ */
+uint8_t pwbox_encoded_get_data(pwbox_encoded_t *inp, size_t idx);
+/** Change the element at position 'idx' of the dynamic array field
+ * data of the pwbox_encoded_t in 'inp', so that it will hold the
+ * value 'elt'.
+ */
+int pwbox_encoded_set_data(pwbox_encoded_t *inp, size_t idx, uint8_t elt);
+/** Append a new element 'elt' to the dynamic array field data of the
+ * pwbox_encoded_t in 'inp'.
+ */
+int pwbox_encoded_add_data(pwbox_encoded_t *inp, uint8_t elt);
+/** Return a pointer to the variable-length array field data of 'inp'.
+ */
+uint8_t * pwbox_encoded_getarray_data(pwbox_encoded_t *inp);
+/** Change the length of the variable-length array field data of 'inp'
+ * to 'newlen'.Fill extra elements with 0. Return 0 on success; return
+ * -1 and set the error code on 'inp' on failure.
+ */
+int pwbox_encoded_setlen_data(pwbox_encoded_t *inp, size_t newlen);
+/** Return the (constant) length of the array holding the hmac field
+ * of the pwbox_encoded_t in 'inp'.
+ */
+size_t pwbox_encoded_getlen_hmac(const pwbox_encoded_t *inp);
+/** Return the element at position 'idx' of the fixed array field hmac
+ * of the pwbox_encoded_t in 'inp'.
+ */
+uint8_t pwbox_encoded_get_hmac(const pwbox_encoded_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field hmac
+ * of the pwbox_encoded_t in 'inp', so that it will hold the value
+ * 'elt'.
+ */
+int pwbox_encoded_set_hmac(pwbox_encoded_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 32-element array field hmac of 'inp'.
+ */
+uint8_t * pwbox_encoded_getarray_hmac(pwbox_encoded_t *inp);
+
+
+#endif

+ 14 - 0
src/trunnel/pwbox.trunnel

@@ -0,0 +1,14 @@
+
+const PWBOX0_CONST0 = 0x544f5242; // TORB
+const PWBOX0_CONST1 = 0x4f583030; // OX00
+
+struct pwbox_encoded {
+  u32 fixedbytes0 IN [PWBOX0_CONST0];
+  u32 fixedbytes1 IN [PWBOX0_CONST1];
+  u8 header_len;
+  u8 skey_header[header_len];
+  u8 iv[16];
+  u8 data[..-32];
+  u8 hmac[32];
+};
+

+ 18 - 0
src/trunnel/trunnel-local.h

@@ -0,0 +1,18 @@
+
+#ifndef TRUNNEL_LOCAL_H_INCLUDED
+#define TRUNNEL_LOCAL_H_INCLUDED
+
+#include "util.h"
+#include "compat.h"
+#include "crypto.h"
+
+#define trunnel_malloc tor_malloc
+#define trunnel_calloc tor_calloc
+#define trunnel_strdup tor_strdup
+#define trunnel_free_ tor_free_
+#define trunnel_realloc tor_realloc
+#define trunnel_reallocarray tor_reallocarray
+#define trunnel_assert tor_assert
+#define trunnel_memwipe(mem, len) memwipe((mem), 0, (len))
+
+#endif