tor_assert_unreached();
}
+/** Return the length of SENDME tags generated by `crypto`. */
+size_t
+relay_crypto_sendme_tag_len(const relay_crypto_t *crypto)
+{
+ tor_assert(crypto);
+ switch (crypto->kind) {
+ case RCK_TOR1:
+ return SENDME_TAG_LEN_TOR1;
+ case RCK_CGO:
+ return SENDME_TAG_LEN_CGO;
+ }
+ tor_assert_unreached();
+}
+
/**
* Handle a single layer of client-side backward encryption
* with crypto of an arbitary type.
const uint8_t *relay_crypto_get_sendme_tag(relay_crypto_t *crypto,
size_t *len_out);
+size_t relay_crypto_sendme_tag_len(const relay_crypto_t *crypto);
#endif /* !defined(TOR_RELAY_CRYPTO_H) */
* For example, position 2 (starting at 0) means that we've received 300
* cells so the 300th cell digest is kept at index 2.
*
- * At maximum, this list contains 200 bytes plus the smartlist overhead. */
+ * At maximum, this list contains 200 bytes plus the smartlist overhead.
+ *
+ * The elements in this list are always of length SENDME_TAG_LEN_TOR1
+ * (== DIGEST_LEN, == 20). The actual digests stored in those elements
+ * may be smaller, however, if another relay crypto algorithm is in use.
+ **/
+ /* Note that this is a per-circuit field, although logically it might make
+ * more sense for it to be a per-hop field. That doesn't matter in C tor,
+ * since we don't send more than a single window of cells to any given
+ * relay except for the exit.
+ */
smartlist_t *sendme_last_digests;
/** Temporary field used during circuits_handle_oom. */
* creating/parsing cells and handling the content.
*/
+// For access to cpath pvt_crypto field.
#define SENDME_PRIVATE
+#define CRYPT_PATH_PRIVATE
#include "core/or/or.h"
* cell we saw which tells us that the other side has in fact seen that cell.
* See proposal 289 for more details. */
static bool
-cell_v1_is_valid(const sendme_cell_t *cell, const uint8_t *circ_digest)
+cell_v1_is_valid(const sendme_cell_t *cell, const uint8_t *circ_digest,
+ size_t circ_digest_len)
{
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;
+ if (tag_len != circ_digest_len)
+ return false;
const uint8_t *cell_digest = sendme_cell_getconstarray_data_v1_digest(cell);
return v1_tag_matches(circ_digest, cell_digest, tag_len);
* This is the main critical function to make sure we can continue to
* send/recv cells on a circuit. If the SENDME is invalid, the circuit should
* be marked for close by the caller. */
+/*
+ * NOTE: This function uses `layer_hint` to determine
+ * what the sendme tag length will be, and nothing else.
+ * Notably, we _don't_ keep a separate queue
+ * of expected tags for each layer!
+ */
STATIC bool
-sendme_is_valid(const circuit_t *circ, const uint8_t *cell_payload,
+sendme_is_valid(const circuit_t *circ,
+ const crypt_path_t *layer_hint,
+ const uint8_t *cell_payload,
size_t cell_payload_len)
{
uint8_t cell_version;
goto invalid;
}
+ /* Determine the expected tag length for this sendme. */
+ size_t circ_expects_tag_len;
+ if (layer_hint) {
+ circ_expects_tag_len =
+ relay_crypto_sendme_tag_len(&layer_hint->pvt_crypto);
+ } else if (CIRCUIT_IS_ORCIRC(circ)) {
+ const or_circuit_t *or_circ = CONST_TO_OR_CIRCUIT(circ);
+ circ_expects_tag_len = relay_crypto_sendme_tag_len(&or_circ->crypto);
+ } else {
+ tor_assert_nonfatal_unreached();
+ goto invalid;
+ }
+
/* Pop the first element that was added (FIFO). We do that regardless of the
* version so we don't accumulate on the circuit if v0 is used by the other
* end point. */
"We received a SENDME but we have no cell digests to match. "
"Closing circuit.");
goto invalid;
- }
-
- /* Validate depending on the version now. */
+ } /* Validate depending on the version now. */
switch (cell_version) {
case 0x01:
- if (!cell_v1_is_valid(cell, circ_digest)) {
+ if (!cell_v1_is_valid(cell, circ_digest, circ_expects_tag_len)) {
goto invalid;
}
break;
/* Validate the SENDME cell. Depending on the version, different validation
* can be done. An invalid SENDME requires us to close the circuit. */
- if (!sendme_is_valid(circ, cell_payload, cell_payload_len)) {
+ if (!sendme_is_valid(circ, layer_hint, cell_payload, cell_payload_len)) {
return -END_CIRC_REASON_TORPROTOCOL;
}
size_t tag_len,
uint8_t *payload);
STATIC bool sendme_is_valid(const circuit_t *circ,
+ const crypt_path_t *layer_hint,
const uint8_t *cell_payload,
size_t cell_payload_len);
STATIC bool circuit_sendme_cell_is_next(int deliver_window,
/* Validation. */
/* An empty payload means SENDME version 0 thus valid. */
- tt_int_op(sendme_is_valid(circ, payload, 0), OP_EQ, true);
+ tt_int_op(sendme_is_valid(circ, NULL, payload, 0), OP_EQ, true);
/* Current phoney digest should have been popped. */
tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 0);
/* An unparseable cell means invalid. */
setup_full_capture_of_logs(LOG_INFO);
- tt_int_op(sendme_is_valid(circ, (const uint8_t *) "A", 1), OP_EQ, false);
+ tt_int_op(sendme_is_valid(circ, NULL, (const uint8_t *) "A", 1),
+ OP_EQ, false);
expect_log_msg_containing("Unparseable SENDME cell received. "
"Closing circuit.");
teardown_capture_of_logs();
/* No cell digest recorded for this. */
setup_full_capture_of_logs(LOG_INFO);
- tt_int_op(sendme_is_valid(circ, payload, sizeof(payload)), OP_EQ, false);
+ tt_int_op(sendme_is_valid(circ, NULL, payload, sizeof(payload)),
+ OP_EQ, false);
expect_log_msg_containing("We received a SENDME but we have no cell digests "
"to match. Closing circuit.");
teardown_capture_of_logs();
sendme_record_cell_digest_on_circ(circ, NULL);
tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 1);
setup_full_capture_of_logs(LOG_INFO);
- tt_int_op(sendme_is_valid(circ, payload, sizeof(payload)), OP_EQ, false);
+ tt_int_op(sendme_is_valid(circ, NULL, payload, sizeof(payload)),
+ OP_EQ, false);
/* After a validation, the last digests is always popped out. */
tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 0);
expect_log_msg_containing("SENDME v1 cell digest do not match.");
circ->package_window = CIRCWINDOW_INCREMENT + 1;
sendme_record_cell_digest_on_circ(circ, NULL);
tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 1);
- tt_int_op(sendme_is_valid(circ, payload, sizeof(payload)), OP_EQ, true);
+ tt_int_op(sendme_is_valid(circ, NULL, payload, sizeof(payload)),
+ OP_EQ, true);
/* After a validation, the last digests is always popped out. */
tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 0);