Browse Source

Use trunnel for crypto_pwbox encoding/decoding.

This reduces the likelihood that I have made any exploitable errors
in the encoding/decoding.

This commit also imports the trunnel runtime source into Tor.
Nick Mathewson 10 years ago
parent
commit
3b7d0ed08e

+ 4 - 0
.gitignore

@@ -170,6 +170,10 @@
 /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						\

+ 56 - 42
src/common/crypto_pwbox.c

@@ -4,8 +4,9 @@
 #include "crypto_pwbox.h"
 #include "di_ops.h"
 #include "util.h"
+#include "pwbox.h"
 
-/* 7 bytes "TORBOX0"
+/* 8 bytes "TORBOX00"
    1 byte: header len (H)
    H bytes: header, denoting secret key algorithm.
    16 bytes: IV
@@ -16,7 +17,7 @@
    32 bytes: HMAC-SHA256 of all previous bytes.
 */
 
-#define MAX_OVERHEAD (S2K_MAXLEN + 8 + 32 + CIPHER_IV_LEN)
+#define MAX_OVERHEAD (S2K_MAXLEN + 8 + 1 + 32 + CIPHER_IV_LEN)
 
 /**
  * Make an authenticated passphrase-encrypted blob to encode the
@@ -32,31 +33,34 @@ crypto_pwbox(uint8_t **out, size_t *outlen_out,
              const char *secret, size_t secret_len,
              unsigned s2k_flags)
 {
-  uint8_t *result, *encrypted_portion, *hmac, *iv;
+  uint8_t *result = NULL, *encrypted_portion;
   size_t encrypted_len = 128 * CEIL_DIV(input_len+4, 128);
-  size_t result_len = encrypted_len + MAX_OVERHEAD;
+  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;
 
-  /* Allocate a buffer and put things in the right place. */
-  result = tor_malloc_zero(result_len);
-  memcpy(result, "TORBOX0", 7);
+  enc = pwbox_encoded_new();
+
+  pwbox_encoded_setlen_skey_header(enc, S2K_MAXLEN);
 
-  spec_len = secret_to_key_make_specifier(result + 8,
-                                          result_len - 8,
-                                          s2k_flags);
-  if (spec_len < 0 || spec_len > 255)
+  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;
-  result[7] = (uint8_t) spec_len;
+  pwbox_encoded_setlen_skey_header(enc, spec_len);
+  enc->header_len = spec_len;
 
-  tor_assert(8 + spec_len + CIPHER_IV_LEN + encrypted_len <= result_len);
+  crypto_rand((char*)enc->iv, sizeof(enc->iv));
 
-  iv = result + 8 + spec_len;
-  encrypted_portion = result + 8 + spec_len + CIPHER_IV_LEN;
-  hmac = encrypted_portion + encrypted_len;
+  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);
@@ -64,24 +68,32 @@ crypto_pwbox(uint8_t **out, size_t *outlen_out,
   /* Now that all the data is in position, derive some keys, encrypt, and
    * digest */
   if (secret_to_key_derivekey(keys, sizeof(keys),
-                              result+8, spec_len,
+                              pwbox_encoded_getarray_skey_header(enc),
+                              spec_len,
                               secret, secret_len) < 0)
     goto err;
 
-  crypto_rand((char*)iv, CIPHER_IV_LEN);
-
-  cipher = crypto_cipher_new_with_iv((char*)keys, (char*)iv);
+  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);
 
-  crypto_hmac_sha256((char*)hmac,
+  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,
-                                             sizeof(keys)-CIPHER_KEY_LEN,
+                     DIGEST256_LEN,
                      (const char*)result,
-                     8 + CIPHER_IV_LEN + spec_len + encrypted_len);
+                     result_len - 32);
 
   *out = result;
-  *outlen_out = 8 + CIPHER_IV_LEN + spec_len + encrypted_len + DIGEST256_LEN;
+  *outlen_out = result_len;
   rv = 0;
   goto out;
 
@@ -90,6 +102,7 @@ crypto_pwbox(uint8_t **out, size_t *outlen_out,
   rv = -1;
 
  out:
+  pwbox_encoded_free(enc);
   memwipe(keys, 0, sizeof(keys));
   return rv;
 }
@@ -109,47 +122,47 @@ crypto_unpwbox(uint8_t **out, size_t *outlen_out,
                const char *secret, size_t secret_len)
 {
   uint8_t *result = NULL;
-  const uint8_t *encrypted, *iv;
+  const uint8_t *encrypted;
   uint8_t keys[CIPHER_KEY_LEN + DIGEST256_LEN];
-  size_t spec_bytes;
   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;
 
-  if (input_len < 32)
-    goto err;
-
-  if (tor_memneq(inp, "TORBOX0", 7))
-    goto err;
+  pwbox_encoded_t *enc = NULL;
 
-  spec_bytes = inp[7];
-  if (input_len < 8 + spec_bytes)
+  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),
-                              inp+8, spec_bytes,
+                              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, sizeof(keys)-CIPHER_KEY_LEN,
-               (const char*)inp, input_len - DIGEST256_LEN);
+                     (const char*)keys + CIPHER_KEY_LEN, DIGEST256_LEN,
+                     (const char*)inp, input_len - DIGEST256_LEN);
 
-  if (tor_memneq(hmac, inp + input_len - DIGEST256_LEN, DIGEST256_LEN)) {
+  if (tor_memneq(hmac, enc->hmac, DIGEST256_LEN)) {
     rv = UNPWBOX_BAD_SECRET;
     goto err;
   }
 
-  iv = inp + 8 + spec_bytes;
-  encrypted = inp + 8 + spec_bytes + CIPHER_IV_LEN;
-
   /* How long is the plaintext? */
-  cipher = crypto_cipher_new_with_iv((char*)keys, (char*)iv);
+  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 (input_len < 8 + spec_bytes + 4 + result_len + 32)
+  if (encrypted_len < result_len + 4)
     goto err;
 
   /* Allocate a buffer and decrypt */
@@ -167,6 +180,7 @@ crypto_unpwbox(uint8_t **out, size_t *outlen_out,
 
  out:
   crypto_cipher_free(cipher);
+  pwbox_encoded_free(enc);
   memwipe(keys, 0, sizeof(keys));
   return rv;
 }

+ 3 - 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,6 +69,7 @@ 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)
 
@@ -80,6 +81,7 @@ LIBOR_CRYPTO_A_SOURCES = \
   src/common/crypto_format.c	\
   src/common/torgzip.c		\
   src/common/tortls.c		\
+  src/trunnel/pwbox.c	 	\
   $(libcrypto_extra_source)
 
 LIBOR_EVENT_A_SOURCES = \

+ 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
+

+ 1 - 1
src/test/include.am

@@ -59,7 +59,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@
 

+ 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