Browse Source

Add trunnel-generated items for link handshake code.

This includes the link handshake variations for proposal220.

We'll use this for testing first, and then use it to extend our
current code to support prop220.
Nick Mathewson 10 years ago
parent
commit
df05e195ee

+ 2 - 2
scripts/codegen/run_trunnel.sh

@@ -5,7 +5,7 @@ if test "x$TRUNNEL_PATH" != "x"; then
   export PYTHONPATH
 fi
 
-python -m trunnel --require-version=1.2 ./src/trunnel/*.trunnel
+python -m trunnel --require-version=1.4 ./src/trunnel/*.trunnel
 
-python -m trunnel --require-version=1.2 --write-c-files --target-dir=./src/ext/trunnel/
+python -m trunnel --require-version=1.4 --write-c-files --target-dir=./src/ext/trunnel/
 

+ 3 - 0
src/trunnel/include.am

@@ -11,11 +11,13 @@ AM_CPPFLAGS += -I$(srcdir)/src/ext/trunnel -I$(srcdir)/src/trunnel
 
 TRUNNELINPUTS = \
 	src/trunnel/ed25519_cert.trunnel \
+	src/trunnel/link_handshake.trunnel \
 	src/trunnel/pwbox.trunnel
 
 TRUNNELSOURCES = \
 	src/ext/trunnel/trunnel.c \
 	src/trunnel/ed25519_cert.c \
+	src/trunnel/link_handshake.c \
 	src/trunnel/pwbox.c
 
 TRUNNELHEADERS = \
@@ -23,6 +25,7 @@ TRUNNELHEADERS = \
 	src/ext/trunnel/trunnel-impl.h		\
 	src/trunnel/trunnel-local.h 		\
 	src/trunnel/ed25519_cert.h		\
+	src/trunnel/link_handshake.h	 	\
 	src/trunnel/pwbox.h
 
 src_trunnel_libor_trunnel_a_SOURCES = $(TRUNNELSOURCES)

+ 1885 - 0
src/trunnel/link_handshake.c

@@ -0,0 +1,1885 @@
+/* link_handshake.c -- generated by Trunnel v1.4-pre.
+ * https://gitweb.torproject.org/trunnel.git
+ * You probably shouldn't edit this file.
+ */
+#include <stdlib.h>
+#include "trunnel-impl.h"
+
+#include "link_handshake.h"
+
+#define TRUNNEL_SET_ERROR_CODE(obj) \
+  do {                              \
+    (obj)->trunnel_error_code_ = 1; \
+  } while (0)
+
+#if defined(__COVERITY__) || defined(__clang_analyzer__)
+/* If we're runnning a static analysis tool, we don't want it to complain
+ * that some of our remaining-bytes checks are dead-code. */
+int linkhandshake_deadcode_dummy__ = 0;
+#define OR_DEADCODE_DUMMY || linkhandshake_deadcode_dummy__
+#else
+#define OR_DEADCODE_DUMMY
+#endif
+
+#define CHECK_REMAINING(nbytes, label)                           \
+  do {                                                           \
+    if (remaining < (nbytes) OR_DEADCODE_DUMMY) {                \
+      goto label;                                                \
+    }                                                            \
+  } while (0)
+
+auth_challenge_cell_t *
+auth_challenge_cell_new(void)
+{
+  auth_challenge_cell_t *val = trunnel_calloc(1, sizeof(auth_challenge_cell_t));
+  if (NULL == val)
+    return NULL;
+  return val;
+}
+
+/** Release all storage held inside 'obj', but do not free 'obj'.
+ */
+static void
+auth_challenge_cell_clear(auth_challenge_cell_t *obj)
+{
+  (void) obj;
+  TRUNNEL_DYNARRAY_WIPE(&obj->methods);
+  TRUNNEL_DYNARRAY_CLEAR(&obj->methods);
+}
+
+void
+auth_challenge_cell_free(auth_challenge_cell_t *obj)
+{
+  if (obj == NULL)
+    return;
+  auth_challenge_cell_clear(obj);
+  trunnel_memwipe(obj, sizeof(auth_challenge_cell_t));
+  trunnel_free_(obj);
+}
+
+size_t
+auth_challenge_cell_getlen_challenge(const auth_challenge_cell_t *inp)
+{
+  (void)inp;  return 32;
+}
+
+uint8_t
+auth_challenge_cell_get_challenge(const auth_challenge_cell_t *inp, size_t idx)
+{
+  trunnel_assert(idx < 32);
+  return inp->challenge[idx];
+}
+
+int
+auth_challenge_cell_set_challenge(auth_challenge_cell_t *inp, size_t idx, uint8_t elt)
+{
+  trunnel_assert(idx < 32);
+  inp->challenge[idx] = elt;
+  return 0;
+}
+
+uint8_t *
+auth_challenge_cell_getarray_challenge(auth_challenge_cell_t *inp)
+{
+  return inp->challenge;
+}
+uint16_t
+auth_challenge_cell_get_n_methods(auth_challenge_cell_t *inp)
+{
+  return inp->n_methods;
+}
+int
+auth_challenge_cell_set_n_methods(auth_challenge_cell_t *inp, uint16_t val)
+{
+  inp->n_methods = val;
+  return 0;
+}
+size_t
+auth_challenge_cell_getlen_methods(const auth_challenge_cell_t *inp)
+{
+  return TRUNNEL_DYNARRAY_LEN(&inp->methods);
+}
+
+uint16_t
+auth_challenge_cell_get_methods(auth_challenge_cell_t *inp, size_t idx)
+{
+  return TRUNNEL_DYNARRAY_GET(&inp->methods, idx);
+}
+
+int
+auth_challenge_cell_set_methods(auth_challenge_cell_t *inp, size_t idx, uint16_t elt)
+{
+  TRUNNEL_DYNARRAY_SET(&inp->methods, idx, elt);
+  return 0;
+}
+int
+auth_challenge_cell_add_methods(auth_challenge_cell_t *inp, uint16_t elt)
+{
+#if SIZE_MAX >= UINT16_MAX
+  if (inp->methods.n_ == UINT16_MAX)
+    goto trunnel_alloc_failed;
+#endif
+  TRUNNEL_DYNARRAY_ADD(uint16_t, &inp->methods, elt, {});
+  return 0;
+ trunnel_alloc_failed:
+  TRUNNEL_SET_ERROR_CODE(inp);
+  return -1;
+}
+
+uint16_t *
+auth_challenge_cell_getarray_methods(auth_challenge_cell_t *inp)
+{
+  return inp->methods.elts_;
+}
+int
+auth_challenge_cell_setlen_methods(auth_challenge_cell_t *inp, size_t newlen)
+{
+  uint16_t *newptr;
+#if UINT16_MAX < SIZE_MAX
+  if (newlen > UINT16_MAX)
+    goto trunnel_alloc_failed;
+#endif
+  newptr = trunnel_dynarray_setlen(&inp->methods.allocated_,
+                 &inp->methods.n_, inp->methods.elts_, newlen,
+                 sizeof(inp->methods.elts_[0]), (trunnel_free_fn_t) NULL,
+                 &inp->trunnel_error_code_);
+  if (newptr == NULL)
+    goto trunnel_alloc_failed;
+  inp->methods.elts_ = newptr;
+  return 0;
+ trunnel_alloc_failed:
+  TRUNNEL_SET_ERROR_CODE(inp);
+  return -1;
+}
+const char *
+auth_challenge_cell_check(const auth_challenge_cell_t *obj)
+{
+  if (obj == NULL)
+    return "Object was NULL";
+  if (obj->trunnel_error_code_)
+    return "A set function failed on this object";
+  if (TRUNNEL_DYNARRAY_LEN(&obj->methods) != obj->n_methods)
+    return "Length mismatch for methods";
+  return NULL;
+}
+
+ssize_t
+auth_challenge_cell_encoded_len(const auth_challenge_cell_t *obj)
+{
+  ssize_t result = 0;
+
+  if (NULL != auth_challenge_cell_check(obj))
+     return -1;
+
+
+  /* Length of u8 challenge[32] */
+  result += 32;
+
+  /* Length of u16 n_methods */
+  result += 2;
+
+  /* Length of u16 methods[n_methods] */
+  result += 2 * TRUNNEL_DYNARRAY_LEN(&obj->methods);
+  return result;
+}
+int
+auth_challenge_cell_clear_errors(auth_challenge_cell_t *obj)
+{
+  int r = obj->trunnel_error_code_;
+  obj->trunnel_error_code_ = 0;
+  return r;
+}
+ssize_t
+auth_challenge_cell_encode(uint8_t *output, const size_t avail, const auth_challenge_cell_t *obj)
+{
+  ssize_t result = 0;
+  size_t written = 0;
+  uint8_t *ptr = output;
+  const char *msg;
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+  const ssize_t encoded_len = auth_challenge_cell_encoded_len(obj);
+#endif
+
+  if (NULL != (msg = auth_challenge_cell_check(obj)))
+    goto check_failed;
+
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+  trunnel_assert(encoded_len >= 0);
+#endif
+
+  /* Encode u8 challenge[32] */
+  trunnel_assert(written <= avail);
+  if (avail - written < 32)
+    goto truncated;
+  memcpy(ptr, obj->challenge, 32);
+  written += 32; ptr += 32;
+
+  /* Encode u16 n_methods */
+  trunnel_assert(written <= avail);
+  if (avail - written < 2)
+    goto truncated;
+  trunnel_set_uint16(ptr, trunnel_htons(obj->n_methods));
+  written += 2; ptr += 2;
+
+  /* Encode u16 methods[n_methods] */
+  {
+
+    unsigned idx;
+    for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->methods); ++idx) {
+      trunnel_assert(written <= avail);
+      if (avail - written < 2)
+        goto truncated;
+      trunnel_set_uint16(ptr, trunnel_htons(TRUNNEL_DYNARRAY_GET(&obj->methods, idx)));
+      written += 2; ptr += 2;
+    }
+  }
+
+
+  trunnel_assert(ptr == output + written);
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+  {
+    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 auth_challenge_cell_parse(), but do not allocate the output
+ * object.
+ */
+static ssize_t
+auth_challenge_cell_parse_into(auth_challenge_cell_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 u8 challenge[32] */
+  CHECK_REMAINING(32, truncated);
+  memcpy(obj->challenge, ptr, 32);
+  remaining -= 32; ptr += 32;
+
+  /* Parse u16 n_methods */
+  CHECK_REMAINING(2, truncated);
+  obj->n_methods = trunnel_ntohs(trunnel_get_uint16(ptr));
+  remaining -= 2; ptr += 2;
+
+  /* Parse u16 methods[n_methods] */
+  TRUNNEL_DYNARRAY_EXPAND(uint16_t, &obj->methods, obj->n_methods, {});
+  {
+    uint16_t elt;
+    unsigned idx;
+    for (idx = 0; idx < obj->n_methods; ++idx) {
+      CHECK_REMAINING(2, truncated);
+      elt = trunnel_ntohs(trunnel_get_uint16(ptr));
+      remaining -= 2; ptr += 2;
+      TRUNNEL_DYNARRAY_ADD(uint16_t, &obj->methods, elt, {});
+    }
+  }
+  trunnel_assert(ptr + remaining == input + len_in);
+  return len_in - remaining;
+
+ truncated:
+  return -2;
+ trunnel_alloc_failed:
+  return -1;
+}
+
+ssize_t
+auth_challenge_cell_parse(auth_challenge_cell_t **output, const uint8_t *input, const size_t len_in)
+{
+  ssize_t result;
+  *output = auth_challenge_cell_new();
+  if (NULL == *output)
+    return -1;
+  result = auth_challenge_cell_parse_into(*output, input, len_in);
+  if (result < 0) {
+    auth_challenge_cell_free(*output);
+    *output = NULL;
+  }
+  return result;
+}
+auth_ctx_t *
+auth_ctx_new(void)
+{
+  auth_ctx_t *val = trunnel_calloc(1, sizeof(auth_ctx_t));
+  if (NULL == val)
+    return NULL;
+  return val;
+}
+
+/** Release all storage held inside 'obj', but do not free 'obj'.
+ */
+static void
+auth_ctx_clear(auth_ctx_t *obj)
+{
+  (void) obj;
+}
+
+void
+auth_ctx_free(auth_ctx_t *obj)
+{
+  if (obj == NULL)
+    return;
+  auth_ctx_clear(obj);
+  trunnel_memwipe(obj, sizeof(auth_ctx_t));
+  trunnel_free_(obj);
+}
+
+uint8_t
+auth_ctx_get_is_ed(auth_ctx_t *inp)
+{
+  return inp->is_ed;
+}
+int
+auth_ctx_set_is_ed(auth_ctx_t *inp, uint8_t val)
+{
+  inp->is_ed = val;
+  return 0;
+}
+certs_cell_cert_t *
+certs_cell_cert_new(void)
+{
+  certs_cell_cert_t *val = trunnel_calloc(1, sizeof(certs_cell_cert_t));
+  if (NULL == val)
+    return NULL;
+  return val;
+}
+
+/** Release all storage held inside 'obj', but do not free 'obj'.
+ */
+static void
+certs_cell_cert_clear(certs_cell_cert_t *obj)
+{
+  (void) obj;
+  TRUNNEL_DYNARRAY_WIPE(&obj->body);
+  TRUNNEL_DYNARRAY_CLEAR(&obj->body);
+}
+
+void
+certs_cell_cert_free(certs_cell_cert_t *obj)
+{
+  if (obj == NULL)
+    return;
+  certs_cell_cert_clear(obj);
+  trunnel_memwipe(obj, sizeof(certs_cell_cert_t));
+  trunnel_free_(obj);
+}
+
+uint8_t
+certs_cell_cert_get_cert_type(certs_cell_cert_t *inp)
+{
+  return inp->cert_type;
+}
+int
+certs_cell_cert_set_cert_type(certs_cell_cert_t *inp, uint8_t val)
+{
+  inp->cert_type = val;
+  return 0;
+}
+uint16_t
+certs_cell_cert_get_cert_len(certs_cell_cert_t *inp)
+{
+  return inp->cert_len;
+}
+int
+certs_cell_cert_set_cert_len(certs_cell_cert_t *inp, uint16_t val)
+{
+  inp->cert_len = val;
+  return 0;
+}
+size_t
+certs_cell_cert_getlen_body(const certs_cell_cert_t *inp)
+{
+  return TRUNNEL_DYNARRAY_LEN(&inp->body);
+}
+
+uint8_t
+certs_cell_cert_get_body(certs_cell_cert_t *inp, size_t idx)
+{
+  return TRUNNEL_DYNARRAY_GET(&inp->body, idx);
+}
+
+int
+certs_cell_cert_set_body(certs_cell_cert_t *inp, size_t idx, uint8_t elt)
+{
+  TRUNNEL_DYNARRAY_SET(&inp->body, idx, elt);
+  return 0;
+}
+int
+certs_cell_cert_add_body(certs_cell_cert_t *inp, uint8_t elt)
+{
+#if SIZE_MAX >= UINT16_MAX
+  if (inp->body.n_ == UINT16_MAX)
+    goto trunnel_alloc_failed;
+#endif
+  TRUNNEL_DYNARRAY_ADD(uint8_t, &inp->body, elt, {});
+  return 0;
+ trunnel_alloc_failed:
+  TRUNNEL_SET_ERROR_CODE(inp);
+  return -1;
+}
+
+uint8_t *
+certs_cell_cert_getarray_body(certs_cell_cert_t *inp)
+{
+  return inp->body.elts_;
+}
+int
+certs_cell_cert_setlen_body(certs_cell_cert_t *inp, size_t newlen)
+{
+  uint8_t *newptr;
+#if UINT16_MAX < SIZE_MAX
+  if (newlen > UINT16_MAX)
+    goto trunnel_alloc_failed;
+#endif
+  newptr = trunnel_dynarray_setlen(&inp->body.allocated_,
+                 &inp->body.n_, inp->body.elts_, newlen,
+                 sizeof(inp->body.elts_[0]), (trunnel_free_fn_t) NULL,
+                 &inp->trunnel_error_code_);
+  if (newptr == NULL)
+    goto trunnel_alloc_failed;
+  inp->body.elts_ = newptr;
+  return 0;
+ trunnel_alloc_failed:
+  TRUNNEL_SET_ERROR_CODE(inp);
+  return -1;
+}
+const char *
+certs_cell_cert_check(const certs_cell_cert_t *obj)
+{
+  if (obj == NULL)
+    return "Object was NULL";
+  if (obj->trunnel_error_code_)
+    return "A set function failed on this object";
+  if (TRUNNEL_DYNARRAY_LEN(&obj->body) != obj->cert_len)
+    return "Length mismatch for body";
+  return NULL;
+}
+
+ssize_t
+certs_cell_cert_encoded_len(const certs_cell_cert_t *obj)
+{
+  ssize_t result = 0;
+
+  if (NULL != certs_cell_cert_check(obj))
+     return -1;
+
+
+  /* Length of u8 cert_type */
+  result += 1;
+
+  /* Length of u16 cert_len */
+  result += 2;
+
+  /* Length of u8 body[cert_len] */
+  result += TRUNNEL_DYNARRAY_LEN(&obj->body);
+  return result;
+}
+int
+certs_cell_cert_clear_errors(certs_cell_cert_t *obj)
+{
+  int r = obj->trunnel_error_code_;
+  obj->trunnel_error_code_ = 0;
+  return r;
+}
+ssize_t
+certs_cell_cert_encode(uint8_t *output, const size_t avail, const certs_cell_cert_t *obj)
+{
+  ssize_t result = 0;
+  size_t written = 0;
+  uint8_t *ptr = output;
+  const char *msg;
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+  const ssize_t encoded_len = certs_cell_cert_encoded_len(obj);
+#endif
+
+  if (NULL != (msg = certs_cell_cert_check(obj)))
+    goto check_failed;
+
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+  trunnel_assert(encoded_len >= 0);
+#endif
+
+  /* Encode u8 cert_type */
+  trunnel_assert(written <= avail);
+  if (avail - written < 1)
+    goto truncated;
+  trunnel_set_uint8(ptr, (obj->cert_type));
+  written += 1; ptr += 1;
+
+  /* Encode u16 cert_len */
+  trunnel_assert(written <= avail);
+  if (avail - written < 2)
+    goto truncated;
+  trunnel_set_uint16(ptr, trunnel_htons(obj->cert_len));
+  written += 2; ptr += 2;
+
+  /* Encode u8 body[cert_len] */
+  {
+    size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->body);
+    trunnel_assert(obj->cert_len == elt_len);
+    trunnel_assert(written <= avail);
+    if (avail - written < elt_len)
+      goto truncated;
+    memcpy(ptr, obj->body.elts_, elt_len);
+    written += elt_len; ptr += elt_len;
+  }
+
+
+  trunnel_assert(ptr == output + written);
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+  {
+    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 certs_cell_cert_parse(), but do not allocate the output object.
+ */
+static ssize_t
+certs_cell_cert_parse_into(certs_cell_cert_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 u8 cert_type */
+  CHECK_REMAINING(1, truncated);
+  obj->cert_type = (trunnel_get_uint8(ptr));
+  remaining -= 1; ptr += 1;
+
+  /* Parse u16 cert_len */
+  CHECK_REMAINING(2, truncated);
+  obj->cert_len = trunnel_ntohs(trunnel_get_uint16(ptr));
+  remaining -= 2; ptr += 2;
+
+  /* Parse u8 body[cert_len] */
+  CHECK_REMAINING(obj->cert_len, truncated);
+  TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->body, obj->cert_len, {});
+  obj->body.n_ = obj->cert_len;
+  memcpy(obj->body.elts_, ptr, obj->cert_len);
+  ptr += obj->cert_len; remaining -= obj->cert_len;
+  trunnel_assert(ptr + remaining == input + len_in);
+  return len_in - remaining;
+
+ truncated:
+  return -2;
+ trunnel_alloc_failed:
+  return -1;
+}
+
+ssize_t
+certs_cell_cert_parse(certs_cell_cert_t **output, const uint8_t *input, const size_t len_in)
+{
+  ssize_t result;
+  *output = certs_cell_cert_new();
+  if (NULL == *output)
+    return -1;
+  result = certs_cell_cert_parse_into(*output, input, len_in);
+  if (result < 0) {
+    certs_cell_cert_free(*output);
+    *output = NULL;
+  }
+  return result;
+}
+rsa_ed_crosscert_t *
+rsa_ed_crosscert_new(void)
+{
+  rsa_ed_crosscert_t *val = trunnel_calloc(1, sizeof(rsa_ed_crosscert_t));
+  if (NULL == val)
+    return NULL;
+  return val;
+}
+
+/** Release all storage held inside 'obj', but do not free 'obj'.
+ */
+static void
+rsa_ed_crosscert_clear(rsa_ed_crosscert_t *obj)
+{
+  (void) obj;
+  TRUNNEL_DYNARRAY_WIPE(&obj->sig);
+  TRUNNEL_DYNARRAY_CLEAR(&obj->sig);
+}
+
+void
+rsa_ed_crosscert_free(rsa_ed_crosscert_t *obj)
+{
+  if (obj == NULL)
+    return;
+  rsa_ed_crosscert_clear(obj);
+  trunnel_memwipe(obj, sizeof(rsa_ed_crosscert_t));
+  trunnel_free_(obj);
+}
+
+size_t
+rsa_ed_crosscert_getlen_ed_key(const rsa_ed_crosscert_t *inp)
+{
+  (void)inp;  return 32;
+}
+
+uint8_t
+rsa_ed_crosscert_get_ed_key(const rsa_ed_crosscert_t *inp, size_t idx)
+{
+  trunnel_assert(idx < 32);
+  return inp->ed_key[idx];
+}
+
+int
+rsa_ed_crosscert_set_ed_key(rsa_ed_crosscert_t *inp, size_t idx, uint8_t elt)
+{
+  trunnel_assert(idx < 32);
+  inp->ed_key[idx] = elt;
+  return 0;
+}
+
+uint8_t *
+rsa_ed_crosscert_getarray_ed_key(rsa_ed_crosscert_t *inp)
+{
+  return inp->ed_key;
+}
+uint32_t
+rsa_ed_crosscert_get_expiration(rsa_ed_crosscert_t *inp)
+{
+  return inp->expiration;
+}
+int
+rsa_ed_crosscert_set_expiration(rsa_ed_crosscert_t *inp, uint32_t val)
+{
+  inp->expiration = val;
+  return 0;
+}
+const uint8_t *
+rsa_ed_crosscert_get_end_of_signed(const rsa_ed_crosscert_t *inp)
+{
+  return inp->end_of_signed;
+}
+uint8_t
+rsa_ed_crosscert_get_sig_len(rsa_ed_crosscert_t *inp)
+{
+  return inp->sig_len;
+}
+int
+rsa_ed_crosscert_set_sig_len(rsa_ed_crosscert_t *inp, uint8_t val)
+{
+  inp->sig_len = val;
+  return 0;
+}
+size_t
+rsa_ed_crosscert_getlen_sig(const rsa_ed_crosscert_t *inp)
+{
+  return TRUNNEL_DYNARRAY_LEN(&inp->sig);
+}
+
+uint8_t
+rsa_ed_crosscert_get_sig(rsa_ed_crosscert_t *inp, size_t idx)
+{
+  return TRUNNEL_DYNARRAY_GET(&inp->sig, idx);
+}
+
+int
+rsa_ed_crosscert_set_sig(rsa_ed_crosscert_t *inp, size_t idx, uint8_t elt)
+{
+  TRUNNEL_DYNARRAY_SET(&inp->sig, idx, elt);
+  return 0;
+}
+int
+rsa_ed_crosscert_add_sig(rsa_ed_crosscert_t *inp, uint8_t elt)
+{
+#if SIZE_MAX >= UINT8_MAX
+  if (inp->sig.n_ == UINT8_MAX)
+    goto trunnel_alloc_failed;
+#endif
+  TRUNNEL_DYNARRAY_ADD(uint8_t, &inp->sig, elt, {});
+  return 0;
+ trunnel_alloc_failed:
+  TRUNNEL_SET_ERROR_CODE(inp);
+  return -1;
+}
+
+uint8_t *
+rsa_ed_crosscert_getarray_sig(rsa_ed_crosscert_t *inp)
+{
+  return inp->sig.elts_;
+}
+int
+rsa_ed_crosscert_setlen_sig(rsa_ed_crosscert_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->sig.allocated_,
+                 &inp->sig.n_, inp->sig.elts_, newlen,
+                 sizeof(inp->sig.elts_[0]), (trunnel_free_fn_t) NULL,
+                 &inp->trunnel_error_code_);
+  if (newptr == NULL)
+    goto trunnel_alloc_failed;
+  inp->sig.elts_ = newptr;
+  return 0;
+ trunnel_alloc_failed:
+  TRUNNEL_SET_ERROR_CODE(inp);
+  return -1;
+}
+const char *
+rsa_ed_crosscert_check(const rsa_ed_crosscert_t *obj)
+{
+  if (obj == NULL)
+    return "Object was NULL";
+  if (obj->trunnel_error_code_)
+    return "A set function failed on this object";
+  if (TRUNNEL_DYNARRAY_LEN(&obj->sig) != obj->sig_len)
+    return "Length mismatch for sig";
+  return NULL;
+}
+
+ssize_t
+rsa_ed_crosscert_encoded_len(const rsa_ed_crosscert_t *obj)
+{
+  ssize_t result = 0;
+
+  if (NULL != rsa_ed_crosscert_check(obj))
+     return -1;
+
+
+  /* Length of u8 ed_key[32] */
+  result += 32;
+
+  /* Length of u32 expiration */
+  result += 4;
+
+  /* Length of u8 sig_len */
+  result += 1;
+
+  /* Length of u8 sig[sig_len] */
+  result += TRUNNEL_DYNARRAY_LEN(&obj->sig);
+  return result;
+}
+int
+rsa_ed_crosscert_clear_errors(rsa_ed_crosscert_t *obj)
+{
+  int r = obj->trunnel_error_code_;
+  obj->trunnel_error_code_ = 0;
+  return r;
+}
+ssize_t
+rsa_ed_crosscert_encode(uint8_t *output, const size_t avail, const rsa_ed_crosscert_t *obj)
+{
+  ssize_t result = 0;
+  size_t written = 0;
+  uint8_t *ptr = output;
+  const char *msg;
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+  const ssize_t encoded_len = rsa_ed_crosscert_encoded_len(obj);
+#endif
+
+  if (NULL != (msg = rsa_ed_crosscert_check(obj)))
+    goto check_failed;
+
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+  trunnel_assert(encoded_len >= 0);
+#endif
+
+  /* Encode u8 ed_key[32] */
+  trunnel_assert(written <= avail);
+  if (avail - written < 32)
+    goto truncated;
+  memcpy(ptr, obj->ed_key, 32);
+  written += 32; ptr += 32;
+
+  /* Encode u32 expiration */
+  trunnel_assert(written <= avail);
+  if (avail - written < 4)
+    goto truncated;
+  trunnel_set_uint32(ptr, trunnel_htonl(obj->expiration));
+  written += 4; ptr += 4;
+
+  /* Encode u8 sig_len */
+  trunnel_assert(written <= avail);
+  if (avail - written < 1)
+    goto truncated;
+  trunnel_set_uint8(ptr, (obj->sig_len));
+  written += 1; ptr += 1;
+
+  /* Encode u8 sig[sig_len] */
+  {
+    size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->sig);
+    trunnel_assert(obj->sig_len == elt_len);
+    trunnel_assert(written <= avail);
+    if (avail - written < elt_len)
+      goto truncated;
+    memcpy(ptr, obj->sig.elts_, elt_len);
+    written += elt_len; ptr += elt_len;
+  }
+
+
+  trunnel_assert(ptr == output + written);
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+  {
+    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 rsa_ed_crosscert_parse(), but do not allocate the output
+ * object.
+ */
+static ssize_t
+rsa_ed_crosscert_parse_into(rsa_ed_crosscert_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 u8 ed_key[32] */
+  CHECK_REMAINING(32, truncated);
+  memcpy(obj->ed_key, ptr, 32);
+  remaining -= 32; ptr += 32;
+
+  /* Parse u32 expiration */
+  CHECK_REMAINING(4, truncated);
+  obj->expiration = trunnel_ntohl(trunnel_get_uint32(ptr));
+  remaining -= 4; ptr += 4;
+  obj->end_of_signed = ptr;
+
+  /* Parse u8 sig_len */
+  CHECK_REMAINING(1, truncated);
+  obj->sig_len = (trunnel_get_uint8(ptr));
+  remaining -= 1; ptr += 1;
+
+  /* Parse u8 sig[sig_len] */
+  CHECK_REMAINING(obj->sig_len, truncated);
+  TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->sig, obj->sig_len, {});
+  obj->sig.n_ = obj->sig_len;
+  memcpy(obj->sig.elts_, ptr, obj->sig_len);
+  ptr += obj->sig_len; remaining -= obj->sig_len;
+  trunnel_assert(ptr + remaining == input + len_in);
+  return len_in - remaining;
+
+ truncated:
+  return -2;
+ trunnel_alloc_failed:
+  return -1;
+}
+
+ssize_t
+rsa_ed_crosscert_parse(rsa_ed_crosscert_t **output, const uint8_t *input, const size_t len_in)
+{
+  ssize_t result;
+  *output = rsa_ed_crosscert_new();
+  if (NULL == *output)
+    return -1;
+  result = rsa_ed_crosscert_parse_into(*output, input, len_in);
+  if (result < 0) {
+    rsa_ed_crosscert_free(*output);
+    *output = NULL;
+  }
+  return result;
+}
+auth1_t *
+auth1_new(void)
+{
+  auth1_t *val = trunnel_calloc(1, sizeof(auth1_t));
+  if (NULL == val)
+    return NULL;
+  return val;
+}
+
+/** Release all storage held inside 'obj', but do not free 'obj'.
+ */
+static void
+auth1_clear(auth1_t *obj)
+{
+  (void) obj;
+  TRUNNEL_DYNARRAY_WIPE(&obj->sig);
+  TRUNNEL_DYNARRAY_CLEAR(&obj->sig);
+}
+
+void
+auth1_free(auth1_t *obj)
+{
+  if (obj == NULL)
+    return;
+  auth1_clear(obj);
+  trunnel_memwipe(obj, sizeof(auth1_t));
+  trunnel_free_(obj);
+}
+
+size_t
+auth1_getlen_type(const auth1_t *inp)
+{
+  (void)inp;  return 8;
+}
+
+uint8_t
+auth1_get_type(const auth1_t *inp, size_t idx)
+{
+  trunnel_assert(idx < 8);
+  return inp->type[idx];
+}
+
+int
+auth1_set_type(auth1_t *inp, size_t idx, uint8_t elt)
+{
+  trunnel_assert(idx < 8);
+  inp->type[idx] = elt;
+  return 0;
+}
+
+uint8_t *
+auth1_getarray_type(auth1_t *inp)
+{
+  return inp->type;
+}
+size_t
+auth1_getlen_cid(const auth1_t *inp)
+{
+  (void)inp;  return 32;
+}
+
+uint8_t
+auth1_get_cid(const auth1_t *inp, size_t idx)
+{
+  trunnel_assert(idx < 32);
+  return inp->cid[idx];
+}
+
+int
+auth1_set_cid(auth1_t *inp, size_t idx, uint8_t elt)
+{
+  trunnel_assert(idx < 32);
+  inp->cid[idx] = elt;
+  return 0;
+}
+
+uint8_t *
+auth1_getarray_cid(auth1_t *inp)
+{
+  return inp->cid;
+}
+size_t
+auth1_getlen_sid(const auth1_t *inp)
+{
+  (void)inp;  return 32;
+}
+
+uint8_t
+auth1_get_sid(const auth1_t *inp, size_t idx)
+{
+  trunnel_assert(idx < 32);
+  return inp->sid[idx];
+}
+
+int
+auth1_set_sid(auth1_t *inp, size_t idx, uint8_t elt)
+{
+  trunnel_assert(idx < 32);
+  inp->sid[idx] = elt;
+  return 0;
+}
+
+uint8_t *
+auth1_getarray_sid(auth1_t *inp)
+{
+  return inp->sid;
+}
+size_t
+auth1_getlen_u1_cid_ed(const auth1_t *inp)
+{
+  (void)inp;  return 32;
+}
+
+uint8_t
+auth1_get_u1_cid_ed(const auth1_t *inp, size_t idx)
+{
+  trunnel_assert(idx < 32);
+  return inp->u1_cid_ed[idx];
+}
+
+int
+auth1_set_u1_cid_ed(auth1_t *inp, size_t idx, uint8_t elt)
+{
+  trunnel_assert(idx < 32);
+  inp->u1_cid_ed[idx] = elt;
+  return 0;
+}
+
+uint8_t *
+auth1_getarray_u1_cid_ed(auth1_t *inp)
+{
+  return inp->u1_cid_ed;
+}
+size_t
+auth1_getlen_u1_sid_ed(const auth1_t *inp)
+{
+  (void)inp;  return 32;
+}
+
+uint8_t
+auth1_get_u1_sid_ed(const auth1_t *inp, size_t idx)
+{
+  trunnel_assert(idx < 32);
+  return inp->u1_sid_ed[idx];
+}
+
+int
+auth1_set_u1_sid_ed(auth1_t *inp, size_t idx, uint8_t elt)
+{
+  trunnel_assert(idx < 32);
+  inp->u1_sid_ed[idx] = elt;
+  return 0;
+}
+
+uint8_t *
+auth1_getarray_u1_sid_ed(auth1_t *inp)
+{
+  return inp->u1_sid_ed;
+}
+size_t
+auth1_getlen_slog(const auth1_t *inp)
+{
+  (void)inp;  return 32;
+}
+
+uint8_t
+auth1_get_slog(const auth1_t *inp, size_t idx)
+{
+  trunnel_assert(idx < 32);
+  return inp->slog[idx];
+}
+
+int
+auth1_set_slog(auth1_t *inp, size_t idx, uint8_t elt)
+{
+  trunnel_assert(idx < 32);
+  inp->slog[idx] = elt;
+  return 0;
+}
+
+uint8_t *
+auth1_getarray_slog(auth1_t *inp)
+{
+  return inp->slog;
+}
+size_t
+auth1_getlen_clog(const auth1_t *inp)
+{
+  (void)inp;  return 32;
+}
+
+uint8_t
+auth1_get_clog(const auth1_t *inp, size_t idx)
+{
+  trunnel_assert(idx < 32);
+  return inp->clog[idx];
+}
+
+int
+auth1_set_clog(auth1_t *inp, size_t idx, uint8_t elt)
+{
+  trunnel_assert(idx < 32);
+  inp->clog[idx] = elt;
+  return 0;
+}
+
+uint8_t *
+auth1_getarray_clog(auth1_t *inp)
+{
+  return inp->clog;
+}
+size_t
+auth1_getlen_scert(const auth1_t *inp)
+{
+  (void)inp;  return 32;
+}
+
+uint8_t
+auth1_get_scert(const auth1_t *inp, size_t idx)
+{
+  trunnel_assert(idx < 32);
+  return inp->scert[idx];
+}
+
+int
+auth1_set_scert(auth1_t *inp, size_t idx, uint8_t elt)
+{
+  trunnel_assert(idx < 32);
+  inp->scert[idx] = elt;
+  return 0;
+}
+
+uint8_t *
+auth1_getarray_scert(auth1_t *inp)
+{
+  return inp->scert;
+}
+size_t
+auth1_getlen_tlssecrets(const auth1_t *inp)
+{
+  (void)inp;  return 32;
+}
+
+uint8_t
+auth1_get_tlssecrets(const auth1_t *inp, size_t idx)
+{
+  trunnel_assert(idx < 32);
+  return inp->tlssecrets[idx];
+}
+
+int
+auth1_set_tlssecrets(auth1_t *inp, size_t idx, uint8_t elt)
+{
+  trunnel_assert(idx < 32);
+  inp->tlssecrets[idx] = elt;
+  return 0;
+}
+
+uint8_t *
+auth1_getarray_tlssecrets(auth1_t *inp)
+{
+  return inp->tlssecrets;
+}
+const uint8_t *
+auth1_get_end_of_fixed_part(const auth1_t *inp)
+{
+  return inp->end_of_fixed_part;
+}
+size_t
+auth1_getlen_rand(const auth1_t *inp)
+{
+  (void)inp;  return 24;
+}
+
+uint8_t
+auth1_get_rand(const auth1_t *inp, size_t idx)
+{
+  trunnel_assert(idx < 24);
+  return inp->rand[idx];
+}
+
+int
+auth1_set_rand(auth1_t *inp, size_t idx, uint8_t elt)
+{
+  trunnel_assert(idx < 24);
+  inp->rand[idx] = elt;
+  return 0;
+}
+
+uint8_t *
+auth1_getarray_rand(auth1_t *inp)
+{
+  return inp->rand;
+}
+const uint8_t *
+auth1_get_end_of_signed(const auth1_t *inp)
+{
+  return inp->end_of_signed;
+}
+size_t
+auth1_getlen_sig(const auth1_t *inp)
+{
+  return TRUNNEL_DYNARRAY_LEN(&inp->sig);
+}
+
+uint8_t
+auth1_get_sig(auth1_t *inp, size_t idx)
+{
+  return TRUNNEL_DYNARRAY_GET(&inp->sig, idx);
+}
+
+int
+auth1_set_sig(auth1_t *inp, size_t idx, uint8_t elt)
+{
+  TRUNNEL_DYNARRAY_SET(&inp->sig, idx, elt);
+  return 0;
+}
+int
+auth1_add_sig(auth1_t *inp, uint8_t elt)
+{
+  TRUNNEL_DYNARRAY_ADD(uint8_t, &inp->sig, elt, {});
+  return 0;
+ trunnel_alloc_failed:
+  TRUNNEL_SET_ERROR_CODE(inp);
+  return -1;
+}
+
+uint8_t *
+auth1_getarray_sig(auth1_t *inp)
+{
+  return inp->sig.elts_;
+}
+int
+auth1_setlen_sig(auth1_t *inp, size_t newlen)
+{
+  uint8_t *newptr;
+  newptr = trunnel_dynarray_setlen(&inp->sig.allocated_,
+                 &inp->sig.n_, inp->sig.elts_, newlen,
+                 sizeof(inp->sig.elts_[0]), (trunnel_free_fn_t) NULL,
+                 &inp->trunnel_error_code_);
+  if (newptr == NULL)
+    goto trunnel_alloc_failed;
+  inp->sig.elts_ = newptr;
+  return 0;
+ trunnel_alloc_failed:
+  TRUNNEL_SET_ERROR_CODE(inp);
+  return -1;
+}
+const char *
+auth1_check(const auth1_t *obj, const auth_ctx_t *auth_ctx_ctx)
+{
+  if (obj == NULL)
+    return "Object was NULL";
+  if (obj->trunnel_error_code_)
+    return "A set function failed on this object";
+  if (auth_ctx_ctx == NULL)
+    return "Context was NULL";
+  switch (auth_ctx_ctx->is_ed) {
+
+    case 0:
+      break;
+
+    case 1:
+      break;
+
+    default:
+        return "Bad tag for union";
+      break;
+  }
+  return NULL;
+}
+
+ssize_t
+auth1_encoded_len(const auth1_t *obj, const auth_ctx_t *auth_ctx_ctx)
+{
+  ssize_t result = 0;
+
+  if (NULL != auth1_check(obj, auth_ctx_ctx))
+     return -1;
+
+
+  /* Length of u8 type[8] */
+  result += 8;
+
+  /* Length of u8 cid[32] */
+  result += 32;
+
+  /* Length of u8 sid[32] */
+  result += 32;
+  switch (auth_ctx_ctx->is_ed) {
+
+    case 0:
+      break;
+
+    case 1:
+
+      /* Length of u8 u1_cid_ed[32] */
+      result += 32;
+
+      /* Length of u8 u1_sid_ed[32] */
+      result += 32;
+      break;
+
+    default:
+      trunnel_assert(0);
+      break;
+  }
+
+  /* Length of u8 slog[32] */
+  result += 32;
+
+  /* Length of u8 clog[32] */
+  result += 32;
+
+  /* Length of u8 scert[32] */
+  result += 32;
+
+  /* Length of u8 tlssecrets[32] */
+  result += 32;
+
+  /* Length of u8 rand[24] */
+  result += 24;
+
+  /* Length of u8 sig[] */
+  result += TRUNNEL_DYNARRAY_LEN(&obj->sig);
+  return result;
+}
+int
+auth1_clear_errors(auth1_t *obj)
+{
+  int r = obj->trunnel_error_code_;
+  obj->trunnel_error_code_ = 0;
+  return r;
+}
+ssize_t
+auth1_encode(uint8_t *output, const size_t avail, const auth1_t *obj, const auth_ctx_t *auth_ctx_ctx)
+{
+  ssize_t result = 0;
+  size_t written = 0;
+  uint8_t *ptr = output;
+  const char *msg;
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+  const ssize_t encoded_len = auth1_encoded_len(obj, auth_ctx_ctx);
+#endif
+
+  if (NULL != (msg = auth1_check(obj, auth_ctx_ctx)))
+    goto check_failed;
+
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+  trunnel_assert(encoded_len >= 0);
+#endif
+
+  /* Encode u8 type[8] */
+  trunnel_assert(written <= avail);
+  if (avail - written < 8)
+    goto truncated;
+  memcpy(ptr, obj->type, 8);
+  written += 8; ptr += 8;
+
+  /* Encode u8 cid[32] */
+  trunnel_assert(written <= avail);
+  if (avail - written < 32)
+    goto truncated;
+  memcpy(ptr, obj->cid, 32);
+  written += 32; ptr += 32;
+
+  /* Encode u8 sid[32] */
+  trunnel_assert(written <= avail);
+  if (avail - written < 32)
+    goto truncated;
+  memcpy(ptr, obj->sid, 32);
+  written += 32; ptr += 32;
+
+  /* Encode union u1[auth_ctx.is_ed] */
+  trunnel_assert(written <= avail);
+  switch (auth_ctx_ctx->is_ed) {
+
+    case 0:
+      break;
+
+    case 1:
+
+      /* Encode u8 u1_cid_ed[32] */
+      trunnel_assert(written <= avail);
+      if (avail - written < 32)
+        goto truncated;
+      memcpy(ptr, obj->u1_cid_ed, 32);
+      written += 32; ptr += 32;
+
+      /* Encode u8 u1_sid_ed[32] */
+      trunnel_assert(written <= avail);
+      if (avail - written < 32)
+        goto truncated;
+      memcpy(ptr, obj->u1_sid_ed, 32);
+      written += 32; ptr += 32;
+      break;
+
+    default:
+      trunnel_assert(0);
+      break;
+  }
+
+  /* Encode u8 slog[32] */
+  trunnel_assert(written <= avail);
+  if (avail - written < 32)
+    goto truncated;
+  memcpy(ptr, obj->slog, 32);
+  written += 32; ptr += 32;
+
+  /* Encode u8 clog[32] */
+  trunnel_assert(written <= avail);
+  if (avail - written < 32)
+    goto truncated;
+  memcpy(ptr, obj->clog, 32);
+  written += 32; ptr += 32;
+
+  /* Encode u8 scert[32] */
+  trunnel_assert(written <= avail);
+  if (avail - written < 32)
+    goto truncated;
+  memcpy(ptr, obj->scert, 32);
+  written += 32; ptr += 32;
+
+  /* Encode u8 tlssecrets[32] */
+  trunnel_assert(written <= avail);
+  if (avail - written < 32)
+    goto truncated;
+  memcpy(ptr, obj->tlssecrets, 32);
+  written += 32; ptr += 32;
+
+  /* Encode u8 rand[24] */
+  trunnel_assert(written <= avail);
+  if (avail - written < 24)
+    goto truncated;
+  memcpy(ptr, obj->rand, 24);
+  written += 24; ptr += 24;
+
+  /* Encode u8 sig[] */
+  {
+    size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->sig);
+    trunnel_assert(written <= avail);
+    if (avail - written < elt_len)
+      goto truncated;
+    memcpy(ptr, obj->sig.elts_, elt_len);
+    written += elt_len; ptr += elt_len;
+  }
+
+
+  trunnel_assert(ptr == output + written);
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+  {
+    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 auth1_parse(), but do not allocate the output object.
+ */
+static ssize_t
+auth1_parse_into(auth1_t *obj, const uint8_t *input, const size_t len_in, const auth_ctx_t *auth_ctx_ctx)
+{
+  const uint8_t *ptr = input;
+  size_t remaining = len_in;
+  ssize_t result = 0;
+  (void)result;
+  if (auth_ctx_ctx == NULL)
+    return -1;
+
+  /* Parse u8 type[8] */
+  CHECK_REMAINING(8, truncated);
+  memcpy(obj->type, ptr, 8);
+  remaining -= 8; ptr += 8;
+
+  /* Parse u8 cid[32] */
+  CHECK_REMAINING(32, truncated);
+  memcpy(obj->cid, ptr, 32);
+  remaining -= 32; ptr += 32;
+
+  /* Parse u8 sid[32] */
+  CHECK_REMAINING(32, truncated);
+  memcpy(obj->sid, ptr, 32);
+  remaining -= 32; ptr += 32;
+
+  /* Parse union u1[auth_ctx.is_ed] */
+  switch (auth_ctx_ctx->is_ed) {
+
+    case 0:
+      break;
+
+    case 1:
+
+      /* Parse u8 u1_cid_ed[32] */
+      CHECK_REMAINING(32, truncated);
+      memcpy(obj->u1_cid_ed, ptr, 32);
+      remaining -= 32; ptr += 32;
+
+      /* Parse u8 u1_sid_ed[32] */
+      CHECK_REMAINING(32, truncated);
+      memcpy(obj->u1_sid_ed, ptr, 32);
+      remaining -= 32; ptr += 32;
+      break;
+
+    default:
+      goto fail;
+      break;
+  }
+
+  /* Parse u8 slog[32] */
+  CHECK_REMAINING(32, truncated);
+  memcpy(obj->slog, ptr, 32);
+  remaining -= 32; ptr += 32;
+
+  /* Parse u8 clog[32] */
+  CHECK_REMAINING(32, truncated);
+  memcpy(obj->clog, ptr, 32);
+  remaining -= 32; ptr += 32;
+
+  /* Parse u8 scert[32] */
+  CHECK_REMAINING(32, truncated);
+  memcpy(obj->scert, ptr, 32);
+  remaining -= 32; ptr += 32;
+
+  /* Parse u8 tlssecrets[32] */
+  CHECK_REMAINING(32, truncated);
+  memcpy(obj->tlssecrets, ptr, 32);
+  remaining -= 32; ptr += 32;
+  obj->end_of_fixed_part = ptr;
+
+  /* Parse u8 rand[24] */
+  CHECK_REMAINING(24, truncated);
+  memcpy(obj->rand, ptr, 24);
+  remaining -= 24; ptr += 24;
+  obj->end_of_signed = ptr;
+
+  /* Parse u8 sig[] */
+  TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->sig, remaining, {});
+  obj->sig.n_ = remaining;
+  memcpy(obj->sig.elts_, ptr, remaining);
+  ptr += remaining; remaining -= remaining;
+  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
+auth1_parse(auth1_t **output, const uint8_t *input, const size_t len_in, const auth_ctx_t *auth_ctx_ctx)
+{
+  ssize_t result;
+  *output = auth1_new();
+  if (NULL == *output)
+    return -1;
+  result = auth1_parse_into(*output, input, len_in, auth_ctx_ctx);
+  if (result < 0) {
+    auth1_free(*output);
+    *output = NULL;
+  }
+  return result;
+}
+certs_cell_t *
+certs_cell_new(void)
+{
+  certs_cell_t *val = trunnel_calloc(1, sizeof(certs_cell_t));
+  if (NULL == val)
+    return NULL;
+  return val;
+}
+
+/** Release all storage held inside 'obj', but do not free 'obj'.
+ */
+static void
+certs_cell_clear(certs_cell_t *obj)
+{
+  (void) obj;
+  {
+
+    unsigned idx;
+    for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->certs); ++idx) {
+      certs_cell_cert_free(TRUNNEL_DYNARRAY_GET(&obj->certs, idx));
+    }
+  }
+  TRUNNEL_DYNARRAY_WIPE(&obj->certs);
+  TRUNNEL_DYNARRAY_CLEAR(&obj->certs);
+}
+
+void
+certs_cell_free(certs_cell_t *obj)
+{
+  if (obj == NULL)
+    return;
+  certs_cell_clear(obj);
+  trunnel_memwipe(obj, sizeof(certs_cell_t));
+  trunnel_free_(obj);
+}
+
+uint8_t
+certs_cell_get_n_certs(certs_cell_t *inp)
+{
+  return inp->n_certs;
+}
+int
+certs_cell_set_n_certs(certs_cell_t *inp, uint8_t val)
+{
+  inp->n_certs = val;
+  return 0;
+}
+size_t
+certs_cell_getlen_certs(const certs_cell_t *inp)
+{
+  return TRUNNEL_DYNARRAY_LEN(&inp->certs);
+}
+
+struct certs_cell_cert_st *
+certs_cell_get_certs(certs_cell_t *inp, size_t idx)
+{
+  return TRUNNEL_DYNARRAY_GET(&inp->certs, idx);
+}
+
+int
+certs_cell_set_certs(certs_cell_t *inp, size_t idx, struct certs_cell_cert_st * elt)
+{
+  certs_cell_cert_t *oldval = TRUNNEL_DYNARRAY_GET(&inp->certs, idx);
+  if (oldval && oldval != elt)
+    certs_cell_cert_free(oldval);
+  return certs_cell_set0_certs(inp, idx, elt);
+}
+int
+certs_cell_set0_certs(certs_cell_t *inp, size_t idx, struct certs_cell_cert_st * elt)
+{
+  TRUNNEL_DYNARRAY_SET(&inp->certs, idx, elt);
+  return 0;
+}
+int
+certs_cell_add_certs(certs_cell_t *inp, struct certs_cell_cert_st * elt)
+{
+#if SIZE_MAX >= UINT8_MAX
+  if (inp->certs.n_ == UINT8_MAX)
+    goto trunnel_alloc_failed;
+#endif
+  TRUNNEL_DYNARRAY_ADD(struct certs_cell_cert_st *, &inp->certs, elt, {});
+  return 0;
+ trunnel_alloc_failed:
+  TRUNNEL_SET_ERROR_CODE(inp);
+  return -1;
+}
+
+struct certs_cell_cert_st * *
+certs_cell_getarray_certs(certs_cell_t *inp)
+{
+  return inp->certs.elts_;
+}
+int
+certs_cell_setlen_certs(certs_cell_t *inp, size_t newlen)
+{
+  struct certs_cell_cert_st * *newptr;
+#if UINT8_MAX < SIZE_MAX
+  if (newlen > UINT8_MAX)
+    goto trunnel_alloc_failed;
+#endif
+  newptr = trunnel_dynarray_setlen(&inp->certs.allocated_,
+                 &inp->certs.n_, inp->certs.elts_, newlen,
+                 sizeof(inp->certs.elts_[0]), (trunnel_free_fn_t) certs_cell_cert_free,
+                 &inp->trunnel_error_code_);
+  if (newptr == NULL)
+    goto trunnel_alloc_failed;
+  inp->certs.elts_ = newptr;
+  return 0;
+ trunnel_alloc_failed:
+  TRUNNEL_SET_ERROR_CODE(inp);
+  return -1;
+}
+const char *
+certs_cell_check(const certs_cell_t *obj)
+{
+  if (obj == NULL)
+    return "Object was NULL";
+  if (obj->trunnel_error_code_)
+    return "A set function failed on this object";
+  {
+    const char *msg;
+
+    unsigned idx;
+    for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->certs); ++idx) {
+      if (NULL != (msg = certs_cell_cert_check(TRUNNEL_DYNARRAY_GET(&obj->certs, idx))))
+        return msg;
+    }
+  }
+  if (TRUNNEL_DYNARRAY_LEN(&obj->certs) != obj->n_certs)
+    return "Length mismatch for certs";
+  return NULL;
+}
+
+ssize_t
+certs_cell_encoded_len(const certs_cell_t *obj)
+{
+  ssize_t result = 0;
+
+  if (NULL != certs_cell_check(obj))
+     return -1;
+
+
+  /* Length of u8 n_certs */
+  result += 1;
+
+  /* Length of struct certs_cell_cert certs[n_certs] */
+  {
+
+    unsigned idx;
+    for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->certs); ++idx) {
+      result += certs_cell_cert_encoded_len(TRUNNEL_DYNARRAY_GET(&obj->certs, idx));
+    }
+  }
+  return result;
+}
+int
+certs_cell_clear_errors(certs_cell_t *obj)
+{
+  int r = obj->trunnel_error_code_;
+  obj->trunnel_error_code_ = 0;
+  return r;
+}
+ssize_t
+certs_cell_encode(uint8_t *output, const size_t avail, const certs_cell_t *obj)
+{
+  ssize_t result = 0;
+  size_t written = 0;
+  uint8_t *ptr = output;
+  const char *msg;
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+  const ssize_t encoded_len = certs_cell_encoded_len(obj);
+#endif
+
+  if (NULL != (msg = certs_cell_check(obj)))
+    goto check_failed;
+
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+  trunnel_assert(encoded_len >= 0);
+#endif
+
+  /* Encode u8 n_certs */
+  trunnel_assert(written <= avail);
+  if (avail - written < 1)
+    goto truncated;
+  trunnel_set_uint8(ptr, (obj->n_certs));
+  written += 1; ptr += 1;
+
+  /* Encode struct certs_cell_cert certs[n_certs] */
+  {
+
+    unsigned idx;
+    for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->certs); ++idx) {
+      trunnel_assert(written <= avail);
+      result = certs_cell_cert_encode(ptr, avail - written, TRUNNEL_DYNARRAY_GET(&obj->certs, idx));
+      if (result < 0)
+        goto fail; /* XXXXXXX !*/
+      written += result; ptr += result;
+    }
+  }
+
+
+  trunnel_assert(ptr == output + written);
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+  {
+    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 certs_cell_parse(), but do not allocate the output object.
+ */
+static ssize_t
+certs_cell_parse_into(certs_cell_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 u8 n_certs */
+  CHECK_REMAINING(1, truncated);
+  obj->n_certs = (trunnel_get_uint8(ptr));
+  remaining -= 1; ptr += 1;
+
+  /* Parse struct certs_cell_cert certs[n_certs] */
+  TRUNNEL_DYNARRAY_EXPAND(certs_cell_cert_t *, &obj->certs, obj->n_certs, {});
+  {
+    certs_cell_cert_t * elt;
+    unsigned idx;
+    for (idx = 0; idx < obj->n_certs; ++idx) {
+      result = certs_cell_cert_parse(&elt, ptr, remaining);
+      if (result < 0)
+        goto relay_fail;
+      trunnel_assert((size_t)result <= remaining);
+      remaining -= result; ptr += result;
+      TRUNNEL_DYNARRAY_ADD(certs_cell_cert_t *, &obj->certs, elt, {certs_cell_cert_free(elt);});
+    }
+  }
+  trunnel_assert(ptr + remaining == input + len_in);
+  return len_in - remaining;
+
+ truncated:
+  return -2;
+ relay_fail:
+  if (result >= 0) result = -1;
+  return result;
+ trunnel_alloc_failed:
+  return -1;
+}
+
+ssize_t
+certs_cell_parse(certs_cell_t **output, const uint8_t *input, const size_t len_in)
+{
+  ssize_t result;
+  *output = certs_cell_new();
+  if (NULL == *output)
+    return -1;
+  result = certs_cell_parse_into(*output, input, len_in);
+  if (result < 0) {
+    certs_cell_free(*output);
+    *output = NULL;
+  }
+  return result;
+}

+ 654 - 0
src/trunnel/link_handshake.h

@@ -0,0 +1,654 @@
+/* link_handshake.h -- generated by by Trunnel v1.4-pre.
+ * https://gitweb.torproject.org/trunnel.git
+ * You probably shouldn't edit this file.
+ */
+#ifndef TRUNNEL_LINK_HANDSHAKE_H
+#define TRUNNEL_LINK_HANDSHAKE_H
+
+#include <stdint.h>
+#include "trunnel.h"
+
+#define CERTTYPE_RSA1024_ID_LINK 1
+#define CERTTYPE_RSA1024_ID_ID 2
+#define CERTTYPE_RSA1024_ID_AUTH 3
+#define CERTTYPE_ED_ID_SIGN 4
+#define CERTTYPE_ED_SIGN_LINK 5
+#define CERTTYPE_ED_SIGN_AUTH 6
+#define CERTTYPE_RSA1024_ID_EDID 7
+#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_AUTH_CHALLENGE_CELL)
+struct auth_challenge_cell_st {
+  uint8_t challenge[32];
+  uint16_t n_methods;
+  TRUNNEL_DYNARRAY_HEAD(, uint16_t) methods;
+  uint8_t trunnel_error_code_;
+};
+#endif
+typedef struct auth_challenge_cell_st auth_challenge_cell_t;
+#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_AUTH_CTX)
+struct auth_ctx_st {
+  uint8_t is_ed;
+  uint8_t trunnel_error_code_;
+};
+#endif
+typedef struct auth_ctx_st auth_ctx_t;
+#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_CERTS_CELL_CERT)
+struct certs_cell_cert_st {
+  uint8_t cert_type;
+  uint16_t cert_len;
+  TRUNNEL_DYNARRAY_HEAD(, uint8_t) body;
+  uint8_t trunnel_error_code_;
+};
+#endif
+typedef struct certs_cell_cert_st certs_cell_cert_t;
+#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_RSA_ED_CROSSCERT)
+struct rsa_ed_crosscert_st {
+  uint8_t ed_key[32];
+  uint32_t expiration;
+  const uint8_t *end_of_signed;
+  uint8_t sig_len;
+  TRUNNEL_DYNARRAY_HEAD(, uint8_t) sig;
+  uint8_t trunnel_error_code_;
+};
+#endif
+typedef struct rsa_ed_crosscert_st rsa_ed_crosscert_t;
+#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_AUTH1)
+struct auth1_st {
+  uint8_t type[8];
+  uint8_t cid[32];
+  uint8_t sid[32];
+  uint8_t u1_cid_ed[32];
+  uint8_t u1_sid_ed[32];
+  uint8_t slog[32];
+  uint8_t clog[32];
+  uint8_t scert[32];
+  uint8_t tlssecrets[32];
+  const uint8_t *end_of_fixed_part;
+  uint8_t rand[24];
+  const uint8_t *end_of_signed;
+  TRUNNEL_DYNARRAY_HEAD(, uint8_t) sig;
+  uint8_t trunnel_error_code_;
+};
+#endif
+typedef struct auth1_st auth1_t;
+#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_CERTS_CELL)
+struct certs_cell_st {
+  uint8_t n_certs;
+  TRUNNEL_DYNARRAY_HEAD(, struct certs_cell_cert_st *) certs;
+  uint8_t trunnel_error_code_;
+};
+#endif
+typedef struct certs_cell_st certs_cell_t;
+/** Return a newly allocated auth_challenge_cell with all elements set
+ * to zero.
+ */
+auth_challenge_cell_t *auth_challenge_cell_new(void);
+/** Release all storage held by the auth_challenge_cell in 'victim'.
+ * (Do nothing if 'victim' is NULL.)
+ */
+void auth_challenge_cell_free(auth_challenge_cell_t *victim);
+/** Try to parse a auth_challenge_cell 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 auth_challenge_cell_t. On failure, return -2 if the input
+ * appears truncated, and -1 if the input is otherwise invalid.
+ */
+ssize_t auth_challenge_cell_parse(auth_challenge_cell_t **output, const uint8_t *input, const size_t len_in);
+/** Return the number of bytes we expect to need to encode the
+ * auth_challenge_cell 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 auth_challenge_cell_encoded_len(const auth_challenge_cell_t *obj);
+/** Try to encode the auth_challenge_cell 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 auth_challenge_cell_encode(uint8_t *output, const size_t avail, const auth_challenge_cell_t *input);
+/** Check whether the internal state of the auth_challenge_cell in
+ * 'obj' is consistent. Return NULL if it is, and a short message if
+ * it is not.
+ */
+const char *auth_challenge_cell_check(const auth_challenge_cell_t *obj);
+/** Clear any errors that were set on the object 'obj' by its setter
+ * functions. Return true iff errors were cleared.
+ */
+int auth_challenge_cell_clear_errors(auth_challenge_cell_t *obj);
+/** Return the (constant) length of the array holding the challenge
+ * field of the auth_challenge_cell_t in 'inp'.
+ */
+size_t auth_challenge_cell_getlen_challenge(const auth_challenge_cell_t *inp);
+/** Return the element at position 'idx' of the fixed array field
+ * challenge of the auth_challenge_cell_t in 'inp'.
+ */
+uint8_t auth_challenge_cell_get_challenge(const auth_challenge_cell_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field
+ * challenge of the auth_challenge_cell_t in 'inp', so that it will
+ * hold the value 'elt'.
+ */
+int auth_challenge_cell_set_challenge(auth_challenge_cell_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 32-element array field challenge of 'inp'.
+ */
+uint8_t * auth_challenge_cell_getarray_challenge(auth_challenge_cell_t *inp);
+/** Return the value of the n_methods field of the
+ * auth_challenge_cell_t in 'inp'
+ */
+uint16_t auth_challenge_cell_get_n_methods(auth_challenge_cell_t *inp);
+/** Set the value of the n_methods field of the auth_challenge_cell_t
+ * in 'inp' to 'val'. Return 0 on success; return -1 and set the error
+ * code on 'inp' on failure.
+ */
+int auth_challenge_cell_set_n_methods(auth_challenge_cell_t *inp, uint16_t val);
+/** Return the length of the dynamic array holding the methods field
+ * of the auth_challenge_cell_t in 'inp'.
+ */
+size_t auth_challenge_cell_getlen_methods(const auth_challenge_cell_t *inp);
+/** Return the element at position 'idx' of the dynamic array field
+ * methods of the auth_challenge_cell_t in 'inp'.
+ */
+uint16_t auth_challenge_cell_get_methods(auth_challenge_cell_t *inp, size_t idx);
+/** Change the element at position 'idx' of the dynamic array field
+ * methods of the auth_challenge_cell_t in 'inp', so that it will hold
+ * the value 'elt'.
+ */
+int auth_challenge_cell_set_methods(auth_challenge_cell_t *inp, size_t idx, uint16_t elt);
+/** Append a new element 'elt' to the dynamic array field methods of
+ * the auth_challenge_cell_t in 'inp'.
+ */
+int auth_challenge_cell_add_methods(auth_challenge_cell_t *inp, uint16_t elt);
+/** Return a pointer to the variable-length array field methods of
+ * 'inp'.
+ */
+uint16_t * auth_challenge_cell_getarray_methods(auth_challenge_cell_t *inp);
+/** Change the length of the variable-length array field methods 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 auth_challenge_cell_setlen_methods(auth_challenge_cell_t *inp, size_t newlen);
+/** Return a newly allocated auth_ctx with all elements set to zero.
+ */
+auth_ctx_t *auth_ctx_new(void);
+/** Release all storage held by the auth_ctx in 'victim'. (Do nothing
+ * if 'victim' is NULL.)
+ */
+void auth_ctx_free(auth_ctx_t *victim);
+/** Return the value of the is_ed field of the auth_ctx_t in 'inp'
+ */
+uint8_t auth_ctx_get_is_ed(auth_ctx_t *inp);
+/** Set the value of the is_ed field of the auth_ctx_t in 'inp' to
+ * 'val'. Return 0 on success; return -1 and set the error code on
+ * 'inp' on failure.
+ */
+int auth_ctx_set_is_ed(auth_ctx_t *inp, uint8_t val);
+/** Return a newly allocated certs_cell_cert with all elements set to
+ * zero.
+ */
+certs_cell_cert_t *certs_cell_cert_new(void);
+/** Release all storage held by the certs_cell_cert in 'victim'. (Do
+ * nothing if 'victim' is NULL.)
+ */
+void certs_cell_cert_free(certs_cell_cert_t *victim);
+/** Try to parse a certs_cell_cert 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
+ * certs_cell_cert_t. On failure, return -2 if the input appears
+ * truncated, and -1 if the input is otherwise invalid.
+ */
+ssize_t certs_cell_cert_parse(certs_cell_cert_t **output, const uint8_t *input, const size_t len_in);
+/** Return the number of bytes we expect to need to encode the
+ * certs_cell_cert 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 certs_cell_cert_encoded_len(const certs_cell_cert_t *obj);
+/** Try to encode the certs_cell_cert 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 certs_cell_cert_encode(uint8_t *output, const size_t avail, const certs_cell_cert_t *input);
+/** Check whether the internal state of the certs_cell_cert in 'obj'
+ * is consistent. Return NULL if it is, and a short message if it is
+ * not.
+ */
+const char *certs_cell_cert_check(const certs_cell_cert_t *obj);
+/** Clear any errors that were set on the object 'obj' by its setter
+ * functions. Return true iff errors were cleared.
+ */
+int certs_cell_cert_clear_errors(certs_cell_cert_t *obj);
+/** Return the value of the cert_type field of the certs_cell_cert_t
+ * in 'inp'
+ */
+uint8_t certs_cell_cert_get_cert_type(certs_cell_cert_t *inp);
+/** Set the value of the cert_type field of the certs_cell_cert_t in
+ * 'inp' to 'val'. Return 0 on success; return -1 and set the error
+ * code on 'inp' on failure.
+ */
+int certs_cell_cert_set_cert_type(certs_cell_cert_t *inp, uint8_t val);
+/** Return the value of the cert_len field of the certs_cell_cert_t in
+ * 'inp'
+ */
+uint16_t certs_cell_cert_get_cert_len(certs_cell_cert_t *inp);
+/** Set the value of the cert_len field of the certs_cell_cert_t in
+ * 'inp' to 'val'. Return 0 on success; return -1 and set the error
+ * code on 'inp' on failure.
+ */
+int certs_cell_cert_set_cert_len(certs_cell_cert_t *inp, uint16_t val);
+/** Return the length of the dynamic array holding the body field of
+ * the certs_cell_cert_t in 'inp'.
+ */
+size_t certs_cell_cert_getlen_body(const certs_cell_cert_t *inp);
+/** Return the element at position 'idx' of the dynamic array field
+ * body of the certs_cell_cert_t in 'inp'.
+ */
+uint8_t certs_cell_cert_get_body(certs_cell_cert_t *inp, size_t idx);
+/** Change the element at position 'idx' of the dynamic array field
+ * body of the certs_cell_cert_t in 'inp', so that it will hold the
+ * value 'elt'.
+ */
+int certs_cell_cert_set_body(certs_cell_cert_t *inp, size_t idx, uint8_t elt);
+/** Append a new element 'elt' to the dynamic array field body of the
+ * certs_cell_cert_t in 'inp'.
+ */
+int certs_cell_cert_add_body(certs_cell_cert_t *inp, uint8_t elt);
+/** Return a pointer to the variable-length array field body of 'inp'.
+ */
+uint8_t * certs_cell_cert_getarray_body(certs_cell_cert_t *inp);
+/** Change the length of the variable-length array field body 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 certs_cell_cert_setlen_body(certs_cell_cert_t *inp, size_t newlen);
+/** Return a newly allocated rsa_ed_crosscert with all elements set to
+ * zero.
+ */
+rsa_ed_crosscert_t *rsa_ed_crosscert_new(void);
+/** Release all storage held by the rsa_ed_crosscert in 'victim'. (Do
+ * nothing if 'victim' is NULL.)
+ */
+void rsa_ed_crosscert_free(rsa_ed_crosscert_t *victim);
+/** Try to parse a rsa_ed_crosscert 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
+ * rsa_ed_crosscert_t. On failure, return -2 if the input appears
+ * truncated, and -1 if the input is otherwise invalid.
+ */
+ssize_t rsa_ed_crosscert_parse(rsa_ed_crosscert_t **output, const uint8_t *input, const size_t len_in);
+/** Return the number of bytes we expect to need to encode the
+ * rsa_ed_crosscert 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 rsa_ed_crosscert_encoded_len(const rsa_ed_crosscert_t *obj);
+/** Try to encode the rsa_ed_crosscert 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 rsa_ed_crosscert_encode(uint8_t *output, const size_t avail, const rsa_ed_crosscert_t *input);
+/** Check whether the internal state of the rsa_ed_crosscert in 'obj'
+ * is consistent. Return NULL if it is, and a short message if it is
+ * not.
+ */
+const char *rsa_ed_crosscert_check(const rsa_ed_crosscert_t *obj);
+/** Clear any errors that were set on the object 'obj' by its setter
+ * functions. Return true iff errors were cleared.
+ */
+int rsa_ed_crosscert_clear_errors(rsa_ed_crosscert_t *obj);
+/** Return the (constant) length of the array holding the ed_key field
+ * of the rsa_ed_crosscert_t in 'inp'.
+ */
+size_t rsa_ed_crosscert_getlen_ed_key(const rsa_ed_crosscert_t *inp);
+/** Return the element at position 'idx' of the fixed array field
+ * ed_key of the rsa_ed_crosscert_t in 'inp'.
+ */
+uint8_t rsa_ed_crosscert_get_ed_key(const rsa_ed_crosscert_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field
+ * ed_key of the rsa_ed_crosscert_t in 'inp', so that it will hold the
+ * value 'elt'.
+ */
+int rsa_ed_crosscert_set_ed_key(rsa_ed_crosscert_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 32-element array field ed_key of 'inp'.
+ */
+uint8_t * rsa_ed_crosscert_getarray_ed_key(rsa_ed_crosscert_t *inp);
+/** Return the value of the expiration field of the rsa_ed_crosscert_t
+ * in 'inp'
+ */
+uint32_t rsa_ed_crosscert_get_expiration(rsa_ed_crosscert_t *inp);
+/** Set the value of the expiration field of the rsa_ed_crosscert_t in
+ * 'inp' to 'val'. Return 0 on success; return -1 and set the error
+ * code on 'inp' on failure.
+ */
+int rsa_ed_crosscert_set_expiration(rsa_ed_crosscert_t *inp, uint32_t val);
+/** Return the position for end_of_signed when we parsed this object
+ */
+const uint8_t * rsa_ed_crosscert_get_end_of_signed(const rsa_ed_crosscert_t *inp);
+/** Return the value of the sig_len field of the rsa_ed_crosscert_t in
+ * 'inp'
+ */
+uint8_t rsa_ed_crosscert_get_sig_len(rsa_ed_crosscert_t *inp);
+/** Set the value of the sig_len field of the rsa_ed_crosscert_t in
+ * 'inp' to 'val'. Return 0 on success; return -1 and set the error
+ * code on 'inp' on failure.
+ */
+int rsa_ed_crosscert_set_sig_len(rsa_ed_crosscert_t *inp, uint8_t val);
+/** Return the length of the dynamic array holding the sig field of
+ * the rsa_ed_crosscert_t in 'inp'.
+ */
+size_t rsa_ed_crosscert_getlen_sig(const rsa_ed_crosscert_t *inp);
+/** Return the element at position 'idx' of the dynamic array field
+ * sig of the rsa_ed_crosscert_t in 'inp'.
+ */
+uint8_t rsa_ed_crosscert_get_sig(rsa_ed_crosscert_t *inp, size_t idx);
+/** Change the element at position 'idx' of the dynamic array field
+ * sig of the rsa_ed_crosscert_t in 'inp', so that it will hold the
+ * value 'elt'.
+ */
+int rsa_ed_crosscert_set_sig(rsa_ed_crosscert_t *inp, size_t idx, uint8_t elt);
+/** Append a new element 'elt' to the dynamic array field sig of the
+ * rsa_ed_crosscert_t in 'inp'.
+ */
+int rsa_ed_crosscert_add_sig(rsa_ed_crosscert_t *inp, uint8_t elt);
+/** Return a pointer to the variable-length array field sig of 'inp'.
+ */
+uint8_t * rsa_ed_crosscert_getarray_sig(rsa_ed_crosscert_t *inp);
+/** Change the length of the variable-length array field sig 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 rsa_ed_crosscert_setlen_sig(rsa_ed_crosscert_t *inp, size_t newlen);
+/** Return a newly allocated auth1 with all elements set to zero.
+ */
+auth1_t *auth1_new(void);
+/** Release all storage held by the auth1 in 'victim'. (Do nothing if
+ * 'victim' is NULL.)
+ */
+void auth1_free(auth1_t *victim);
+/** Try to parse a auth1 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 auth1_t.
+ * On failure, return -2 if the input appears truncated, and -1 if the
+ * input is otherwise invalid.
+ */
+ssize_t auth1_parse(auth1_t **output, const uint8_t *input, const size_t len_in, const auth_ctx_t *auth_ctx_ctx);
+/** Return the number of bytes we expect to need to encode the auth1
+ * 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 auth1_encoded_len(const auth1_t *obj, const auth_ctx_t *auth_ctx_ctx);
+/** Try to encode the auth1 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 auth1_encode(uint8_t *output, const size_t avail, const auth1_t *input, const auth_ctx_t *auth_ctx_ctx);
+/** Check whether the internal state of the auth1 in 'obj' is
+ * consistent. Return NULL if it is, and a short message if it is not.
+ */
+const char *auth1_check(const auth1_t *obj, const auth_ctx_t *auth_ctx_ctx);
+/** Clear any errors that were set on the object 'obj' by its setter
+ * functions. Return true iff errors were cleared.
+ */
+int auth1_clear_errors(auth1_t *obj);
+/** Return the (constant) length of the array holding the type field
+ * of the auth1_t in 'inp'.
+ */
+size_t auth1_getlen_type(const auth1_t *inp);
+/** Return the element at position 'idx' of the fixed array field type
+ * of the auth1_t in 'inp'.
+ */
+uint8_t auth1_get_type(const auth1_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field type
+ * of the auth1_t in 'inp', so that it will hold the value 'elt'.
+ */
+int auth1_set_type(auth1_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 8-element array field type of 'inp'.
+ */
+uint8_t * auth1_getarray_type(auth1_t *inp);
+/** Return the (constant) length of the array holding the cid field of
+ * the auth1_t in 'inp'.
+ */
+size_t auth1_getlen_cid(const auth1_t *inp);
+/** Return the element at position 'idx' of the fixed array field cid
+ * of the auth1_t in 'inp'.
+ */
+uint8_t auth1_get_cid(const auth1_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field cid
+ * of the auth1_t in 'inp', so that it will hold the value 'elt'.
+ */
+int auth1_set_cid(auth1_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 32-element array field cid of 'inp'.
+ */
+uint8_t * auth1_getarray_cid(auth1_t *inp);
+/** Return the (constant) length of the array holding the sid field of
+ * the auth1_t in 'inp'.
+ */
+size_t auth1_getlen_sid(const auth1_t *inp);
+/** Return the element at position 'idx' of the fixed array field sid
+ * of the auth1_t in 'inp'.
+ */
+uint8_t auth1_get_sid(const auth1_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field sid
+ * of the auth1_t in 'inp', so that it will hold the value 'elt'.
+ */
+int auth1_set_sid(auth1_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 32-element array field sid of 'inp'.
+ */
+uint8_t * auth1_getarray_sid(auth1_t *inp);
+/** Return the (constant) length of the array holding the u1_cid_ed
+ * field of the auth1_t in 'inp'.
+ */
+size_t auth1_getlen_u1_cid_ed(const auth1_t *inp);
+/** Return the element at position 'idx' of the fixed array field
+ * u1_cid_ed of the auth1_t in 'inp'.
+ */
+uint8_t auth1_get_u1_cid_ed(const auth1_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field
+ * u1_cid_ed of the auth1_t in 'inp', so that it will hold the value
+ * 'elt'.
+ */
+int auth1_set_u1_cid_ed(auth1_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 32-element array field u1_cid_ed of 'inp'.
+ */
+uint8_t * auth1_getarray_u1_cid_ed(auth1_t *inp);
+/** Return the (constant) length of the array holding the u1_sid_ed
+ * field of the auth1_t in 'inp'.
+ */
+size_t auth1_getlen_u1_sid_ed(const auth1_t *inp);
+/** Return the element at position 'idx' of the fixed array field
+ * u1_sid_ed of the auth1_t in 'inp'.
+ */
+uint8_t auth1_get_u1_sid_ed(const auth1_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field
+ * u1_sid_ed of the auth1_t in 'inp', so that it will hold the value
+ * 'elt'.
+ */
+int auth1_set_u1_sid_ed(auth1_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 32-element array field u1_sid_ed of 'inp'.
+ */
+uint8_t * auth1_getarray_u1_sid_ed(auth1_t *inp);
+/** Return the (constant) length of the array holding the slog field
+ * of the auth1_t in 'inp'.
+ */
+size_t auth1_getlen_slog(const auth1_t *inp);
+/** Return the element at position 'idx' of the fixed array field slog
+ * of the auth1_t in 'inp'.
+ */
+uint8_t auth1_get_slog(const auth1_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field slog
+ * of the auth1_t in 'inp', so that it will hold the value 'elt'.
+ */
+int auth1_set_slog(auth1_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 32-element array field slog of 'inp'.
+ */
+uint8_t * auth1_getarray_slog(auth1_t *inp);
+/** Return the (constant) length of the array holding the clog field
+ * of the auth1_t in 'inp'.
+ */
+size_t auth1_getlen_clog(const auth1_t *inp);
+/** Return the element at position 'idx' of the fixed array field clog
+ * of the auth1_t in 'inp'.
+ */
+uint8_t auth1_get_clog(const auth1_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field clog
+ * of the auth1_t in 'inp', so that it will hold the value 'elt'.
+ */
+int auth1_set_clog(auth1_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 32-element array field clog of 'inp'.
+ */
+uint8_t * auth1_getarray_clog(auth1_t *inp);
+/** Return the (constant) length of the array holding the scert field
+ * of the auth1_t in 'inp'.
+ */
+size_t auth1_getlen_scert(const auth1_t *inp);
+/** Return the element at position 'idx' of the fixed array field
+ * scert of the auth1_t in 'inp'.
+ */
+uint8_t auth1_get_scert(const auth1_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field
+ * scert of the auth1_t in 'inp', so that it will hold the value
+ * 'elt'.
+ */
+int auth1_set_scert(auth1_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 32-element array field scert of 'inp'.
+ */
+uint8_t * auth1_getarray_scert(auth1_t *inp);
+/** Return the (constant) length of the array holding the tlssecrets
+ * field of the auth1_t in 'inp'.
+ */
+size_t auth1_getlen_tlssecrets(const auth1_t *inp);
+/** Return the element at position 'idx' of the fixed array field
+ * tlssecrets of the auth1_t in 'inp'.
+ */
+uint8_t auth1_get_tlssecrets(const auth1_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field
+ * tlssecrets of the auth1_t in 'inp', so that it will hold the value
+ * 'elt'.
+ */
+int auth1_set_tlssecrets(auth1_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 32-element array field tlssecrets of
+ * 'inp'.
+ */
+uint8_t * auth1_getarray_tlssecrets(auth1_t *inp);
+/** Return the position for end_of_fixed_part when we parsed this
+ * object
+ */
+const uint8_t * auth1_get_end_of_fixed_part(const auth1_t *inp);
+/** Return the (constant) length of the array holding the rand field
+ * of the auth1_t in 'inp'.
+ */
+size_t auth1_getlen_rand(const auth1_t *inp);
+/** Return the element at position 'idx' of the fixed array field rand
+ * of the auth1_t in 'inp'.
+ */
+uint8_t auth1_get_rand(const auth1_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field rand
+ * of the auth1_t in 'inp', so that it will hold the value 'elt'.
+ */
+int auth1_set_rand(auth1_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 24-element array field rand of 'inp'.
+ */
+uint8_t * auth1_getarray_rand(auth1_t *inp);
+/** Return the position for end_of_signed when we parsed this object
+ */
+const uint8_t * auth1_get_end_of_signed(const auth1_t *inp);
+/** Return the length of the dynamic array holding the sig field of
+ * the auth1_t in 'inp'.
+ */
+size_t auth1_getlen_sig(const auth1_t *inp);
+/** Return the element at position 'idx' of the dynamic array field
+ * sig of the auth1_t in 'inp'.
+ */
+uint8_t auth1_get_sig(auth1_t *inp, size_t idx);
+/** Change the element at position 'idx' of the dynamic array field
+ * sig of the auth1_t in 'inp', so that it will hold the value 'elt'.
+ */
+int auth1_set_sig(auth1_t *inp, size_t idx, uint8_t elt);
+/** Append a new element 'elt' to the dynamic array field sig of the
+ * auth1_t in 'inp'.
+ */
+int auth1_add_sig(auth1_t *inp, uint8_t elt);
+/** Return a pointer to the variable-length array field sig of 'inp'.
+ */
+uint8_t * auth1_getarray_sig(auth1_t *inp);
+/** Change the length of the variable-length array field sig 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 auth1_setlen_sig(auth1_t *inp, size_t newlen);
+/** Return a newly allocated certs_cell with all elements set to zero.
+ */
+certs_cell_t *certs_cell_new(void);
+/** Release all storage held by the certs_cell in 'victim'. (Do
+ * nothing if 'victim' is NULL.)
+ */
+void certs_cell_free(certs_cell_t *victim);
+/** Try to parse a certs_cell 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
+ * certs_cell_t. On failure, return -2 if the input appears truncated,
+ * and -1 if the input is otherwise invalid.
+ */
+ssize_t certs_cell_parse(certs_cell_t **output, const uint8_t *input, const size_t len_in);
+/** Return the number of bytes we expect to need to encode the
+ * certs_cell 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 certs_cell_encoded_len(const certs_cell_t *obj);
+/** Try to encode the certs_cell 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 certs_cell_encode(uint8_t *output, const size_t avail, const certs_cell_t *input);
+/** Check whether the internal state of the certs_cell in 'obj' is
+ * consistent. Return NULL if it is, and a short message if it is not.
+ */
+const char *certs_cell_check(const certs_cell_t *obj);
+/** Clear any errors that were set on the object 'obj' by its setter
+ * functions. Return true iff errors were cleared.
+ */
+int certs_cell_clear_errors(certs_cell_t *obj);
+/** Return the value of the n_certs field of the certs_cell_t in 'inp'
+ */
+uint8_t certs_cell_get_n_certs(certs_cell_t *inp);
+/** Set the value of the n_certs field of the certs_cell_t in 'inp' to
+ * 'val'. Return 0 on success; return -1 and set the error code on
+ * 'inp' on failure.
+ */
+int certs_cell_set_n_certs(certs_cell_t *inp, uint8_t val);
+/** Return the length of the dynamic array holding the certs field of
+ * the certs_cell_t in 'inp'.
+ */
+size_t certs_cell_getlen_certs(const certs_cell_t *inp);
+/** Return the element at position 'idx' of the dynamic array field
+ * certs of the certs_cell_t in 'inp'.
+ */
+struct certs_cell_cert_st * certs_cell_get_certs(certs_cell_t *inp, size_t idx);
+/** Change the element at position 'idx' of the dynamic array field
+ * certs of the certs_cell_t in 'inp', so that it will hold the value
+ * 'elt'. Free the previous value, if any.
+ */
+int certs_cell_set_certs(certs_cell_t *inp, size_t idx, struct certs_cell_cert_st * elt);
+/** As certs_cell_set_certs, but does not free the previous value.
+ */
+int certs_cell_set0_certs(certs_cell_t *inp, size_t idx, struct certs_cell_cert_st * elt);
+/** Append a new element 'elt' to the dynamic array field certs of the
+ * certs_cell_t in 'inp'.
+ */
+int certs_cell_add_certs(certs_cell_t *inp, struct certs_cell_cert_st * elt);
+/** Return a pointer to the variable-length array field certs of
+ * 'inp'.
+ */
+struct certs_cell_cert_st * * certs_cell_getarray_certs(certs_cell_t *inp);
+/** Change the length of the variable-length array field certs of
+ * 'inp' to 'newlen'.Fill extra elements with NULL; free removed
+ * elements. Return 0 on success; return -1 and set the error code on
+ * 'inp' on failure.
+ */
+int certs_cell_setlen_certs(certs_cell_t *inp, size_t newlen);
+
+
+#endif

+ 57 - 0
src/trunnel/link_handshake.trunnel

@@ -0,0 +1,57 @@
+
+struct certs_cell {
+  u8 n_certs;
+  struct certs_cell_cert certs[n_certs];
+}
+
+const CERTTYPE_RSA1024_ID_LINK = 1;
+const CERTTYPE_RSA1024_ID_ID   = 2;
+const CERTTYPE_RSA1024_ID_AUTH = 3;
+const CERTTYPE_ED_ID_SIGN      = 4;
+const CERTTYPE_ED_SIGN_LINK    = 5;
+const CERTTYPE_ED_SIGN_AUTH    = 6;
+const CERTTYPE_RSA1024_ID_EDID = 7;
+
+struct certs_cell_cert {
+  u8 cert_type;
+  u16 cert_len;
+  u8 body[cert_len];
+}
+
+struct rsa_ed_crosscert {
+  u8 ed_key[32];
+  u32 expiration;
+  @ptr end_of_signed;
+  u8 sig_len;
+  u8 sig[sig_len]; // mismatches spec.
+}
+
+struct auth_challenge_cell {
+  u8 challenge[32];
+  u16 n_methods;
+  u16 methods[n_methods];
+}
+
+context auth_ctx {
+  u8 is_ed;
+}
+
+struct auth1 with context auth_ctx {
+  u8 type[8];
+  u8 cid[32];
+  u8 sid[32];
+  union u1[auth_ctx.is_ed] {
+    0 : ;
+    1 : u8 cid_ed[32];
+        u8 sid_ed[32];
+    default: fail;
+  };
+  u8 slog[32];
+  u8 clog[32];
+  u8 scert[32];
+  u8 tlssecrets[32];
+  @ptr end_of_fixed_part;
+  u8 rand[24];
+  @ptr end_of_signed;
+  u8 sig[];
+}