// 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;
}
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) */
/************ 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 ***************************/
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);
#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
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;
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
* 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();
/* 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);
* 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. */
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);
}
/*
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) <=
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
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);
/* 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);
}
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,
cgo_uiv_clear(&uiv);
}
+#include "core/or/relay_crypto_st.h"
+
static void
test_crypto_cgo_uiv_update_testvec(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. */
sendme_cell_t *val = trunnel_calloc(1, sizeof(sendme_cell_t));
if (NULL == val)
return NULL;
+ val->data_len = 16;
return val;
}
sendme_cell_clear(sendme_cell_t *obj)
{
(void) obj;
+ TRUNNEL_DYNARRAY_WIPE(&obj->data_v1_digest);
+ TRUNNEL_DYNARRAY_CLEAR(&obj->data_v1_digest);
}
void
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
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)
{
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:
/* 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) {
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:
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)
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:
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);
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:
truncated:
return -2;
+ trunnel_alloc_failed:
+ return -1;
fail:
result = -1;
return result;
#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
* '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);
* 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
/* 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[];
};
}