tor/src/test/test_sendme.c
David Goulet cede93b2d8 tests: Implement unit tests for SENDME v1
Part of #26288

Signed-off-by: David Goulet <dgoulet@torproject.org>
2019-04-29 12:17:57 -04:00

223 lines
7.3 KiB
C

/* Copyright (c) 2014-2019, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/* Unit tests for handling different kinds of relay cell */
#define CIRCUITLIST_PRIVATE
#define NETWORKSTATUS_PRIVATE
#define SENDME_PRIVATE
#include "core/or/circuit_st.h"
#include "core/or/or_circuit_st.h"
#include "core/or/origin_circuit_st.h"
#include "core/or/circuitlist.h"
#include "core/or/sendme.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/networkstatus_st.h"
#include "test/test.h"
#include "test/log_test_helpers.h"
static void
setup_mock_consensus(void)
{
current_md_consensus = current_ns_consensus =
tor_malloc_zero(sizeof(networkstatus_t));
current_md_consensus->net_params = smartlist_new();
current_md_consensus->routerstatus_list = smartlist_new();
}
static void
free_mock_consensus(void)
{
SMARTLIST_FOREACH(current_md_consensus->routerstatus_list, void *, r,
tor_free(r));
smartlist_free(current_md_consensus->routerstatus_list);
smartlist_free(current_ns_consensus->net_params);
tor_free(current_ns_consensus);
}
static void
test_v1_note_digest(void *arg)
{
or_circuit_t *or_circ = NULL;
origin_circuit_t *orig_circ = NULL;
circuit_t *circ = NULL;
(void) arg;
/* Create our dummy circuits. */
orig_circ = origin_circuit_new();
tt_assert(orig_circ);
or_circ = or_circuit_new(1, NULL);
/* Start by pointing to the origin circuit. */
circ = TO_CIRCUIT(orig_circ);
circ->purpose = CIRCUIT_PURPOSE_S_REND_JOINED;
/* We should never note SENDME digest on origin circuit. */
sendme_note_cell_digest(circ);
tt_assert(!circ->sendme_last_digests);
/* We do not need the origin circuit for now. */
orig_circ = NULL;
circuit_free_(circ);
/* Points it to the OR circuit now. */
circ = TO_CIRCUIT(or_circ);
or_circ->crypto.sendme_digest = crypto_digest_new();
/* The package window has to be a multiple of CIRCWINDOW_INCREMENT minus 1
* in order to catched the CIRCWINDOW_INCREMENT-nth cell. Try something that
* shouldn't be noted. */
circ->package_window = CIRCWINDOW_INCREMENT;
sendme_note_cell_digest(circ);
tt_assert(!circ->sendme_last_digests);
/* This should work now. Package window at CIRCWINDOW_INCREMENT + 1. */
circ->package_window++;
sendme_note_cell_digest(circ);
tt_assert(circ->sendme_last_digests);
tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 1);
/* Next cell in the package window shouldn't do anything. */
circ->package_window++;
sendme_note_cell_digest(circ);
tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 1);
/* The next CIRCWINDOW_INCREMENT should add one more digest. */
circ->package_window = (CIRCWINDOW_INCREMENT * 2) + 1;
sendme_note_cell_digest(circ);
tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 2);
done:
circuit_free_(circ);
}
static void
test_v1_consensus_params(void *arg)
{
(void) arg;
setup_mock_consensus();
tt_assert(current_md_consensus);
/* Both zeroes. */
smartlist_add(current_md_consensus->net_params,
(void *) "sendme_emit_min_version=0");
smartlist_add(current_md_consensus->net_params,
(void *) "sendme_accept_min_version=0");
tt_int_op(get_emit_min_version(), OP_EQ, 0);
tt_int_op(get_accept_min_version(), OP_EQ, 0);
smartlist_clear(current_md_consensus->net_params);
/* Both ones. */
smartlist_add(current_md_consensus->net_params,
(void *) "sendme_emit_min_version=1");
smartlist_add(current_md_consensus->net_params,
(void *) "sendme_accept_min_version=1");
tt_int_op(get_emit_min_version(), OP_EQ, 1);
tt_int_op(get_accept_min_version(), OP_EQ, 1);
smartlist_clear(current_md_consensus->net_params);
/* Different values from each other. */
smartlist_add(current_md_consensus->net_params,
(void *) "sendme_emit_min_version=1");
smartlist_add(current_md_consensus->net_params,
(void *) "sendme_accept_min_version=0");
tt_int_op(get_emit_min_version(), OP_EQ, 1);
tt_int_op(get_accept_min_version(), OP_EQ, 0);
smartlist_clear(current_md_consensus->net_params);
/* Validate is the cell version is coherent with our internal default value
* and the one in the consensus. */
smartlist_add(current_md_consensus->net_params,
(void *) "sendme_accept_min_version=1");
/* Minimum acceptable value is 1. */
tt_int_op(cell_version_is_valid(1), OP_EQ, true);
/* Minimum acceptable value is 1 so a cell version of 0 is refused. */
tt_int_op(cell_version_is_valid(0), OP_EQ, false);
done:
free_mock_consensus();
}
static void
test_v1_build_cell(void *arg)
{
uint8_t payload[RELAY_PAYLOAD_SIZE];
ssize_t ret;
crypto_digest_t *cell_digest = NULL;
or_circuit_t *or_circ = NULL;
circuit_t *circ = NULL;
(void) arg;
or_circ = or_circuit_new(1, NULL);
circ = TO_CIRCUIT(or_circ);
cell_digest = crypto_digest_new();
crypto_digest_add_bytes(cell_digest, "AAAA", 4);
tt_assert(cell_digest);
/* SENDME v1 payload is 7 bytes. See spec. */
ret = build_cell_payload_v1(cell_digest, payload);
tt_int_op(ret, OP_EQ, 7);
/* Validation. */
/* An empty payload means SENDME version 0 thus valid. */
tt_int_op(sendme_is_valid(circ, payload, 0), OP_EQ, true);
/* 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);
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);
expect_log_msg_containing("We received a SENDME but we have no cell digests "
"to match. Closing circuit.");
teardown_capture_of_logs();
/* Note the wrong digest in the circuit, cell should fail validation. */
or_circ->crypto.sendme_digest = crypto_digest_new();
circ->package_window = CIRCWINDOW_INCREMENT + 1;
sendme_note_cell_digest(circ);
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);
/* 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.");
teardown_capture_of_logs();
/* Cleanup */
crypto_digest_free(or_circ->crypto.sendme_digest);
/* Record the cell digest into the circuit, cell should validate. */
or_circ->crypto.sendme_digest = crypto_digest_dup(cell_digest);
circ->package_window = CIRCWINDOW_INCREMENT + 1;
sendme_note_cell_digest(circ);
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);
/* After a validation, the last digests is always popped out. */
tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 0);
done:
crypto_digest_free(cell_digest);
circuit_free_(circ);
}
struct testcase_t sendme_tests[] = {
{ "v1_note_digest", test_v1_note_digest, TT_FORK,
NULL, NULL },
{ "v1_consensus_params", test_v1_consensus_params, TT_FORK,
NULL, NULL },
{ "v1_build_cell", test_v1_build_cell, TT_FORK,
NULL, NULL },
END_OF_TESTCASES
};