]> git.ipfire.org Git - thirdparty/tor.git/commitdiff
Allow SENDME tags to be 16 or 20 bytes.
authorNick Mathewson <nickm@torproject.org>
Wed, 28 May 2025 16:02:39 +0000 (12:02 -0400)
committerNick Mathewson <nickm@torproject.org>
Tue, 10 Jun 2025 23:06:47 +0000 (19:06 -0400)
src/core/crypto/relay_crypto.c
src/core/crypto/relay_crypto.h
src/core/or/crypt_path.c
src/core/or/crypt_path.h
src/core/or/sendme.c
src/core/or/sendme.h
src/test/test_crypto_cgo.c
src/test/test_sendme.c
src/trunnel/sendme_cell.c
src/trunnel/sendme_cell.h
src/trunnel/sendme_cell.trunnel

index 9b9f623cc324c8bfced20884e15c7083dd5d6075..baddd704e0230eb73ad3723305af2a34b836c85f 100644 (file)
@@ -28,7 +28,8 @@
 // XXXX: Remove this definition once I'm done refactoring.
 #define pvt_crypto crypto_crypt_path_private_field
 
-/** Return the sendme_digest within the <b>crypto</b> object.
+/** Return the sendme tag within the <b>crypto</b> object,
+ * along with its length.
  *
  * This is the digest from the most recent cell that we originated
  * or recognized, _in either direction_.
  * this digest.
  */
 const uint8_t *
-relay_crypto_get_sendme_digest(relay_crypto_t *crypto)
+relay_crypto_get_sendme_tag(relay_crypto_t *crypto,
+                            size_t *len_out)
 {
   tor_assert(crypto);
+  *len_out = DIGEST_LEN;
   return crypto->sendme_digest;
 }
 
index 34bf189b736b2ada1a546ad2e7284723447a138c..5340c073388ad8dca884780fcbcb0b38c6c29bbb 100644 (file)
@@ -27,6 +27,7 @@ void relay_crypto_clear(relay_crypto_t *crypto);
 
 void relay_crypto_assert_ok(const relay_crypto_t *crypto);
 
-const uint8_t *relay_crypto_get_sendme_digest(relay_crypto_t *crypto);
+const uint8_t *relay_crypto_get_sendme_tag(relay_crypto_t *crypto,
+                                           size_t *len_out);
 
 #endif /* !defined(TOR_RELAY_CRYPTO_H) */
index 56be7b23118b74e00295e7786ad74d79796cc7af..bf2e4df74e9b6a6a1b802b5b4edc10416059793a 100644 (file)
@@ -177,11 +177,12 @@ cpath_free(crypt_path_t *victim)
 
 /************ cpath sendme API ***************************/
 
-/** Return the sendme_digest of this <b>cpath</b>. */
+/** Return the sendme tag of this <b>cpath</b>,
+ * along with its length. */
 const uint8_t *
-cpath_get_sendme_digest(crypt_path_t *cpath)
+cpath_get_sendme_tag(crypt_path_t *cpath, size_t *len_out)
 {
-  return relay_crypto_get_sendme_digest(&cpath->pvt_crypto);
+  return relay_crypto_get_sendme_tag(&cpath->pvt_crypto, len_out);
 }
 
 /************ other cpath functions ***************************/
index 2cf36b02b17e126c2a63e62052eb5b9de76572cc..36bd2bd81d7f3e38507c1b212403a077af103e85 100644 (file)
@@ -25,7 +25,7 @@ crypt_path_t *cpath_get_next_non_open_hop(crypt_path_t *cpath);
 
 void cpath_sendme_circuit_record_inbound_cell(crypt_path_t *cpath);
 
-const uint8_t *cpath_get_sendme_digest(crypt_path_t *cpath);
+const uint8_t *cpath_get_sendme_tag(crypt_path_t *cpath, size_t *len_out);
 
 #if defined(TOR_UNIT_TESTS)
 unsigned int cpath_get_n_hops(crypt_path_t **head_ptr);
index 61288453736af04488da54588ce02695a4304dfe..a02f3a53761cd0649e2362dcc46f0765e7ded64b 100644 (file)
 #include "lib/ctime/di_ops.h"
 #include "trunnel/sendme_cell.h"
 
+#define SHORT_TAG_LEN 16
+#define LONG_TAG_LEN 20
+
+/**
+ * Return true iff tag_len is some length we recognize.
+ */
+static inline bool
+tag_len_ok(size_t tag_len)
+{
+  return tag_len == SHORT_TAG_LEN || tag_len == LONG_TAG_LEN;
+}
+
 /* Return the minimum version given by the consensus (if any) that should be
  * used when emitting a SENDME cell. */
 STATIC int
@@ -71,17 +83,18 @@ pop_first_cell_digest(const circuit_t *circ)
   return circ_digest;
 }
 
-/* Return true iff the given cell digest matches the first digest in the
+/* Return true iff the given cell tag matches the first digest in the
  * circuit sendme list. */
 static bool
-v1_digest_matches(const uint8_t *circ_digest, const uint8_t *cell_digest)
+v1_tag_matches(const uint8_t *circ_digest,
+               const uint8_t *cell_tag, size_t tag_len)
 {
   tor_assert(circ_digest);
-  tor_assert(cell_digest);
+  tor_assert(cell_tag);
 
   /* Compare the digest with the one in the SENDME. This cell is invalid
    * without a perfect match. */
-  if (tor_memneq(circ_digest, cell_digest, TRUNNEL_SENDME_V1_DIGEST_LEN)) {
+  if (tor_memneq(circ_digest, cell_tag, tag_len)) {
     log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
            "SENDME v1 cell digest do not match.");
     return false;
@@ -103,8 +116,16 @@ cell_v1_is_valid(const sendme_cell_t *cell, const uint8_t *circ_digest)
   tor_assert(cell);
   tor_assert(circ_digest);
 
+  // XXXX TODO: make sure that length is the length we _expected_.
+
+  size_t tag_len = sendme_cell_get_data_len(cell);
+  if (! tag_len_ok(tag_len))
+    return false;
+  if (sendme_cell_getlen_data_v1_digest(cell) < tag_len)
+    return false;
+
   const uint8_t *cell_digest = sendme_cell_getconstarray_data_v1_digest(cell);
-  return v1_digest_matches(circ_digest, cell_digest);
+  return v1_tag_matches(circ_digest, cell_digest, tag_len);
 }
 
 /* Return true iff the given cell version can be handled or if the minimum
@@ -234,12 +255,14 @@ sendme_is_valid(const circuit_t *circ, const uint8_t *cell_payload,
  * Return the size in bytes of the encoded cell in payload. A negative value
  * is returned on encoding failure. */
 STATIC ssize_t
-build_cell_payload_v1(const uint8_t *cell_digest, uint8_t *payload)
+build_cell_payload_v1(const uint8_t *cell_tag, const size_t tag_len,
+                      uint8_t *payload)
 {
   ssize_t len = -1;
   sendme_cell_t *cell = NULL;
 
-  tor_assert(cell_digest);
+  tor_assert(cell_tag);
+  tor_assert(tag_len_ok(tag_len));
   tor_assert(payload);
 
   cell = sendme_cell_new();
@@ -247,11 +270,11 @@ build_cell_payload_v1(const uint8_t *cell_digest, uint8_t *payload)
   /* Building a payload for version 1. */
   sendme_cell_set_version(cell, 0x01);
   /* Set the data length field for v1. */
-  sendme_cell_set_data_len(cell, TRUNNEL_SENDME_V1_DIGEST_LEN);
+  sendme_cell_set_data_len(cell, tag_len);
+  sendme_cell_setlen_data_v1_digest(cell, tag_len);
 
   /* Copy the digest into the data payload. */
-  memcpy(sendme_cell_getarray_data_v1_digest(cell), cell_digest,
-         sendme_cell_get_data_len(cell));
+  memcpy(sendme_cell_getarray_data_v1_digest(cell), cell_tag, tag_len);
 
   /* Finally, encode the cell into the payload. */
   len = sendme_cell_encode(payload, RELAY_PAYLOAD_SIZE_MAX, cell);
@@ -267,19 +290,19 @@ build_cell_payload_v1(const uint8_t *cell_digest, uint8_t *payload)
  * because we failed to send the cell on it. */
 static int
 send_circuit_level_sendme(circuit_t *circ, crypt_path_t *layer_hint,
-                          const uint8_t *cell_digest)
+                          const uint8_t *cell_tag, size_t tag_len)
 {
   uint8_t emit_version;
   uint8_t payload[RELAY_PAYLOAD_SIZE_MAX];
   ssize_t payload_len;
 
   tor_assert(circ);
-  tor_assert(cell_digest);
+  tor_assert(cell_tag);
 
   emit_version = get_emit_min_version();
   switch (emit_version) {
   case 0x01:
-    payload_len = build_cell_payload_v1(cell_digest, payload);
+    payload_len = build_cell_payload_v1(cell_tag, tag_len, payload);
     if (BUG(payload_len < 0)) {
       /* Unable to encode the cell, abort. We can recover from this by closing
        * the circuit but in theory it should never happen. */
@@ -307,19 +330,33 @@ send_circuit_level_sendme(circuit_t *circ, crypt_path_t *layer_hint,
   return 0;
 }
 
-/* Record the cell digest only if the next cell is expected to be a SENDME. */
+/* Record the sendme tag as expected in a future SENDME, */
 static void
-record_cell_digest_on_circ(circuit_t *circ, const uint8_t *sendme_digest)
+record_cell_digest_on_circ(circuit_t *circ,
+                           const uint8_t *sendme_tag,
+                           size_t tag_len)
 {
   tor_assert(circ);
-  tor_assert(sendme_digest);
+  tor_assert(sendme_tag);
 
   /* Add the digest to the last seen list in the circuit. */
   if (circ->sendme_last_digests == NULL) {
     circ->sendme_last_digests = smartlist_new();
   }
-  smartlist_add(circ->sendme_last_digests,
-                tor_memdup(sendme_digest, DIGEST_LEN));
+  // We always allocate the largest possible tag here to
+  // make sure we don't have heap overflow bugs.
+  uint8_t *tag;
+  if (tag_len == SHORT_TAG_LEN) {
+    tag = tor_malloc(sizeof(LONG_TAG_LEN));
+    memcpy(tag, sendme_tag, tag_len);
+    memset(tag+SHORT_TAG_LEN, 0, LONG_TAG_LEN - SHORT_TAG_LEN);
+  } else if (tag_len == LONG_TAG_LEN) {
+    tag = tor_memdup(sendme_tag, LONG_TAG_LEN);
+  } else {
+    tor_assert_unreached();
+  }
+
+  smartlist_add(circ->sendme_last_digests, tag);
 }
 
 /*
@@ -421,7 +458,8 @@ void
 sendme_circuit_consider_sending(circuit_t *circ, crypt_path_t *layer_hint)
 {
   bool sent_one_sendme = false;
-  const uint8_t *digest;
+  const uint8_t *tag;
+  size_t tag_len = 0;
   int sendme_inc = sendme_get_inc_count(circ, layer_hint);
 
   while ((layer_hint ? layer_hint->deliver_window : circ->deliver_window) <=
@@ -429,12 +467,13 @@ sendme_circuit_consider_sending(circuit_t *circ, crypt_path_t *layer_hint)
     log_debug(LD_CIRC,"Queuing circuit sendme.");
     if (layer_hint) {
       layer_hint->deliver_window += sendme_inc;
-      digest = cpath_get_sendme_digest(layer_hint);
+      tag = cpath_get_sendme_tag(layer_hint, &tag_len);
     } else {
       circ->deliver_window += sendme_inc;
-      digest = relay_crypto_get_sendme_digest(&TO_OR_CIRCUIT(circ)->crypto);
+      tag = relay_crypto_get_sendme_tag(&TO_OR_CIRCUIT(circ)->crypto,
+                                        &tag_len);
     }
-    if (send_circuit_level_sendme(circ, layer_hint, digest) < 0) {
+    if (send_circuit_level_sendme(circ, layer_hint, tag, tag_len) < 0) {
       return; /* The circuit's closed, don't continue */
     }
     /* Current implementation is not suppose to send multiple SENDME at once
@@ -698,7 +737,8 @@ sendme_note_stream_data_packaged(edge_connection_t *conn, size_t len)
 void
 sendme_record_cell_digest_on_circ(circuit_t *circ, crypt_path_t *cpath)
 {
-  const uint8_t *sendme_digest;
+  const uint8_t *sendme_tag;
+  size_t tag_len = 0;
 
   tor_assert(circ);
 
@@ -712,11 +752,11 @@ sendme_record_cell_digest_on_circ(circuit_t *circ, crypt_path_t *cpath)
   /* Getting the digest is expensive so we only do it once we are certain to
    * record it on the circuit. */
   if (cpath) {
-    sendme_digest = cpath_get_sendme_digest(cpath);
+    sendme_tag = cpath_get_sendme_tag(cpath, &tag_len);
   } else {
-    sendme_digest =
-      relay_crypto_get_sendme_digest(&TO_OR_CIRCUIT(circ)->crypto);
+    sendme_tag =
+      relay_crypto_get_sendme_tag(&TO_OR_CIRCUIT(circ)->crypto, &tag_len);
   }
 
-  record_cell_digest_on_circ(circ, sendme_digest);
+  record_cell_digest_on_circ(circ, sendme_tag, tag_len);
 }
index 8bb293471bdc434a4b284dce47fd26507622dc22..c21726f5c8e45d7a31c06d81165daf2798c9336e 100644 (file)
@@ -65,7 +65,8 @@ STATIC int get_accept_min_version(void);
 
 STATIC bool cell_version_can_be_handled(uint8_t cell_version);
 
-STATIC ssize_t build_cell_payload_v1(const uint8_t *cell_digest,
+STATIC ssize_t build_cell_payload_v1(const uint8_t *cell_tag,
+                                     size_t tag_len,
                                      uint8_t *payload);
 STATIC bool sendme_is_valid(const circuit_t *circ,
                             const uint8_t *cell_payload,
index 403dcaa98bdc78793a405de152853496822a328b..5aea89e984395ed92f7fe0ab58f60aa199b6aab3 100644 (file)
@@ -255,6 +255,8 @@ test_crypto_cgo_uiv_testvec(void *arg)
   cgo_uiv_clear(&uiv);
 }
 
+#include "core/or/relay_crypto_st.h"
+
 static void
 test_crypto_cgo_uiv_update_testvec(void *arg)
 {
index 93a828643b4640a0f771936b9604ec8e06f901bc..ff24ec96c67c9256a88ccb165a6143269523c9f7 100644 (file)
@@ -153,7 +153,7 @@ test_v1_build_cell(void *arg)
   smartlist_add(circ->sendme_last_digests, tor_memdup(digest, sizeof(digest)));
 
   /* SENDME v1 payload is 3 bytes + 20 bytes digest. See spec. */
-  ret = build_cell_payload_v1(digest, payload);
+  ret = build_cell_payload_v1(digest, 20, payload);
   tt_int_op(ret, OP_EQ, 23);
 
   /* Validation. */
index b9f8fe967f41323150b6ac5edb43f6e00d226468..0b51a133b03c67f5571d0a43cb55c6e4ce231047 100644 (file)
@@ -34,6 +34,7 @@ sendme_cell_new(void)
   sendme_cell_t *val = trunnel_calloc(1, sizeof(sendme_cell_t));
   if (NULL == val)
     return NULL;
+  val->data_len = 16;
   return val;
 }
 
@@ -43,6 +44,8 @@ static void
 sendme_cell_clear(sendme_cell_t *obj)
 {
   (void) obj;
+  TRUNNEL_DYNARRAY_WIPE(&obj->data_v1_digest);
+  TRUNNEL_DYNARRAY_CLEAR(&obj->data_v1_digest);
 }
 
 void
@@ -78,20 +81,23 @@ sendme_cell_get_data_len(const sendme_cell_t *inp)
 int
 sendme_cell_set_data_len(sendme_cell_t *inp, uint16_t val)
 {
+  if (! ((val == 16 || val == 20))) {
+     TRUNNEL_SET_ERROR_CODE(inp);
+     return -1;
+  }
   inp->data_len = val;
   return 0;
 }
 size_t
 sendme_cell_getlen_data_v1_digest(const sendme_cell_t *inp)
 {
-  (void)inp;  return TRUNNEL_SENDME_V1_DIGEST_LEN;
+  return TRUNNEL_DYNARRAY_LEN(&inp->data_v1_digest);
 }
 
 uint8_t
 sendme_cell_get_data_v1_digest(sendme_cell_t *inp, size_t idx)
 {
-  trunnel_assert(idx < TRUNNEL_SENDME_V1_DIGEST_LEN);
-  return inp->data_v1_digest[idx];
+  return TRUNNEL_DYNARRAY_GET(&inp->data_v1_digest, idx);
 }
 
 uint8_t
@@ -102,21 +108,45 @@ sendme_cell_getconst_data_v1_digest(const sendme_cell_t *inp, size_t idx)
 int
 sendme_cell_set_data_v1_digest(sendme_cell_t *inp, size_t idx, uint8_t elt)
 {
-  trunnel_assert(idx < TRUNNEL_SENDME_V1_DIGEST_LEN);
-  inp->data_v1_digest[idx] = elt;
+  TRUNNEL_DYNARRAY_SET(&inp->data_v1_digest, idx, elt);
+  return 0;
+}
+int
+sendme_cell_add_data_v1_digest(sendme_cell_t *inp, uint8_t elt)
+{
+  TRUNNEL_DYNARRAY_ADD(uint8_t, &inp->data_v1_digest, elt, {});
   return 0;
+ trunnel_alloc_failed:
+  TRUNNEL_SET_ERROR_CODE(inp);
+  return -1;
 }
 
 uint8_t *
 sendme_cell_getarray_data_v1_digest(sendme_cell_t *inp)
 {
-  return inp->data_v1_digest;
+  return inp->data_v1_digest.elts_;
 }
 const uint8_t  *
 sendme_cell_getconstarray_data_v1_digest(const sendme_cell_t *inp)
 {
   return (const uint8_t  *)sendme_cell_getarray_data_v1_digest((sendme_cell_t*)inp);
 }
+int
+sendme_cell_setlen_data_v1_digest(sendme_cell_t *inp, size_t newlen)
+{
+  uint8_t *newptr;
+  newptr = trunnel_dynarray_setlen(&inp->data_v1_digest.allocated_,
+                 &inp->data_v1_digest.n_, inp->data_v1_digest.elts_, newlen,
+                 sizeof(inp->data_v1_digest.elts_[0]), (trunnel_free_fn_t) NULL,
+                 &inp->trunnel_error_code_);
+  if (newlen != 0 && newptr == NULL)
+    goto trunnel_alloc_failed;
+  inp->data_v1_digest.elts_ = newptr;
+  return 0;
+ trunnel_alloc_failed:
+  TRUNNEL_SET_ERROR_CODE(inp);
+  return -1;
+}
 const char *
 sendme_cell_check(const sendme_cell_t *obj)
 {
@@ -126,6 +156,8 @@ sendme_cell_check(const sendme_cell_t *obj)
     return "A set function failed on this object";
   if (! (obj->version == 0 || obj->version == 1))
     return "Integer out of bounds";
+  if (! (obj->data_len == 16 || obj->data_len == 20))
+    return "Integer out of bounds";
   switch (obj->version) {
 
     case 0:
@@ -153,7 +185,7 @@ sendme_cell_encoded_len(const sendme_cell_t *obj)
   /* Length of u8 version IN [0, 1] */
   result += 1;
 
-  /* Length of u16 data_len */
+  /* Length of u16 data_len IN [16, 20] */
   result += 2;
   switch (obj->version) {
 
@@ -162,8 +194,8 @@ sendme_cell_encoded_len(const sendme_cell_t *obj)
 
     case 1:
 
-      /* Length of u8 data_v1_digest[TRUNNEL_SENDME_V1_DIGEST_LEN] */
-      result += TRUNNEL_SENDME_V1_DIGEST_LEN;
+      /* Length of u8 data_v1_digest[] */
+      result += TRUNNEL_DYNARRAY_LEN(&obj->data_v1_digest);
       break;
 
     default:
@@ -206,7 +238,7 @@ sendme_cell_encode(uint8_t *output, const size_t avail, const sendme_cell_t *obj
   trunnel_set_uint8(ptr, (obj->version));
   written += 1; ptr += 1;
 
-  /* Encode u16 data_len */
+  /* Encode u16 data_len IN [16, 20] */
   backptr_data_len = ptr;
   trunnel_assert(written <= avail);
   if (avail - written < 2)
@@ -225,12 +257,16 @@ sendme_cell_encode(uint8_t *output, const size_t avail, const sendme_cell_t *obj
 
       case 1:
 
-        /* Encode u8 data_v1_digest[TRUNNEL_SENDME_V1_DIGEST_LEN] */
-        trunnel_assert(written <= avail);
-        if (avail - written < TRUNNEL_SENDME_V1_DIGEST_LEN)
-          goto truncated;
-        memcpy(ptr, obj->data_v1_digest, TRUNNEL_SENDME_V1_DIGEST_LEN);
-        written += TRUNNEL_SENDME_V1_DIGEST_LEN; ptr += TRUNNEL_SENDME_V1_DIGEST_LEN;
+        /* Encode u8 data_v1_digest[] */
+        {
+          size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->data_v1_digest);
+          trunnel_assert(written <= avail);
+          if (avail - written < elt_len)
+            goto truncated;
+          if (elt_len)
+            memcpy(ptr, obj->data_v1_digest.elts_, elt_len);
+          written += elt_len; ptr += elt_len;
+        }
         break;
 
       default:
@@ -287,10 +323,12 @@ sendme_cell_parse_into(sendme_cell_t *obj, const uint8_t *input, const size_t le
   if (! (obj->version == 0 || obj->version == 1))
     goto fail;
 
-  /* Parse u16 data_len */
+  /* Parse u16 data_len IN [16, 20] */
   CHECK_REMAINING(2, truncated);
   obj->data_len = trunnel_ntohs(trunnel_get_uint16(ptr));
   remaining -= 2; ptr += 2;
+  if (! (obj->data_len == 16 || obj->data_len == 20))
+    goto fail;
   {
     size_t remaining_after;
     CHECK_REMAINING(obj->data_len, truncated);
@@ -307,10 +345,12 @@ sendme_cell_parse_into(sendme_cell_t *obj, const uint8_t *input, const size_t le
 
       case 1:
 
-        /* Parse u8 data_v1_digest[TRUNNEL_SENDME_V1_DIGEST_LEN] */
-        CHECK_REMAINING(TRUNNEL_SENDME_V1_DIGEST_LEN, fail);
-        memcpy(obj->data_v1_digest, ptr, TRUNNEL_SENDME_V1_DIGEST_LEN);
-        remaining -= TRUNNEL_SENDME_V1_DIGEST_LEN; ptr += TRUNNEL_SENDME_V1_DIGEST_LEN;
+        /* Parse u8 data_v1_digest[] */
+        TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->data_v1_digest, remaining, {});
+        obj->data_v1_digest.n_ = remaining;
+        if (remaining)
+          memcpy(obj->data_v1_digest.elts_, ptr, remaining);
+        ptr += remaining; remaining -= remaining;
         break;
 
       default:
@@ -326,6 +366,8 @@ sendme_cell_parse_into(sendme_cell_t *obj, const uint8_t *input, const size_t le
 
  truncated:
   return -2;
+ trunnel_alloc_failed:
+  return -1;
  fail:
   result = -1;
   return result;
index 45efb9f10dc206d758316e459ef77baf5dff5f6f..3d642d279d0a8d3115d06b5090de7e6f4dd2cb25 100644 (file)
@@ -8,12 +8,11 @@
 #include <stdint.h>
 #include "trunnel.h"
 
-#define TRUNNEL_SENDME_V1_DIGEST_LEN 20
 #if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_SENDME_CELL)
 struct sendme_cell_st {
   uint8_t version;
   uint16_t data_len;
-  uint8_t data_v1_digest[TRUNNEL_SENDME_V1_DIGEST_LEN];
+  TRUNNEL_DYNARRAY_HEAD(, uint8_t) data_v1_digest;
   uint8_t trunnel_error_code_;
 };
 #endif
@@ -71,11 +70,11 @@ uint16_t sendme_cell_get_data_len(const sendme_cell_t *inp);
  * 'inp' on failure.
  */
 int sendme_cell_set_data_len(sendme_cell_t *inp, uint16_t val);
-/** Return the (constant) length of the array holding the
- * data_v1_digest field of the sendme_cell_t in 'inp'.
+/** Return the length of the dynamic array holding the data_v1_digest
+ * field of the sendme_cell_t in 'inp'.
  */
 size_t sendme_cell_getlen_data_v1_digest(const sendme_cell_t *inp);
-/** Return the element at position 'idx' of the fixed array field
+/** Return the element at position 'idx' of the dynamic array field
  * data_v1_digest of the sendme_cell_t in 'inp'.
  */
 uint8_t sendme_cell_get_data_v1_digest(sendme_cell_t *inp, size_t idx);
@@ -83,19 +82,29 @@ uint8_t sendme_cell_get_data_v1_digest(sendme_cell_t *inp, size_t idx);
  * pointer
  */
 uint8_t sendme_cell_getconst_data_v1_digest(const sendme_cell_t *inp, size_t idx);
-/** Change the element at position 'idx' of the fixed array field
+/** Change the element at position 'idx' of the dynamic array field
  * data_v1_digest of the sendme_cell_t in 'inp', so that it will hold
  * the value 'elt'.
  */
 int sendme_cell_set_data_v1_digest(sendme_cell_t *inp, size_t idx, uint8_t elt);
-/** Return a pointer to the TRUNNEL_SENDME_V1_DIGEST_LEN-element array
- * field data_v1_digest of 'inp'.
+/** Append a new element 'elt' to the dynamic array field
+ * data_v1_digest of the sendme_cell_t in 'inp'.
+ */
+int sendme_cell_add_data_v1_digest(sendme_cell_t *inp, uint8_t elt);
+/** Return a pointer to the variable-length array field data_v1_digest
+ * of 'inp'.
  */
 uint8_t * sendme_cell_getarray_data_v1_digest(sendme_cell_t *inp);
 /** As sendme_cell_get_data_v1_digest, but take and return a const
  * pointer
  */
 const uint8_t  * sendme_cell_getconstarray_data_v1_digest(const sendme_cell_t *inp);
+/** Change the length of the variable-length array field
+ * data_v1_digest 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 sendme_cell_setlen_data_v1_digest(sendme_cell_t *inp, size_t newlen);
 
 
 #endif
index 300963e67963f097fd6b5a801f7eb358d404ceac..f3ff26cb5a40462d75f644b880514a0de36d34ee 100644 (file)
@@ -1,19 +1,19 @@
 /* This file contains the SENDME cell definition. */
 
-/* v1 digest length in bytes. */
-const TRUNNEL_SENDME_V1_DIGEST_LEN = 20;
-
 /* SENDME cell declaration. */
 struct sendme_cell {
   /* Version field. */
   u8 version IN [0x00, 0x01];
 
   /* Length of data contained in this cell. */
-  u16 data_len;
+  u16 data_len IN [16, 20];
 
   /* The data content depends on the version. */
   union data[version] with length data_len {
     0x00: ignore;
-    0x01: u8 v1_digest[TRUNNEL_SENDME_V1_DIGEST_LEN];
+    /* I'd like to use data_len here, but trunnel doesn't
+     * allow that.  Instead have v1_digest run to the end of the cell.
+     */
+    0x01: u8 v1_digest[];
   };
 }