mirror of
https://github.com/torproject/torspec.git
synced 2024-11-26 19:30:32 +00:00
273 lines
11 KiB
Plaintext
273 lines
11 KiB
Plaintext
Filename: 253-oob-hmac.txt
|
|
Title: Out of Band Circuit HMACs
|
|
Authors: Mike Perry
|
|
Created: 01 September 2015
|
|
Status: Dead
|
|
|
|
|
|
0. Motivation
|
|
|
|
|
|
It is currently possible for Guard nodes (and MITM adversaries that
|
|
steal their identity keys) to perform a "tagging" attack to influence
|
|
circuit construction and resulting relay usage[1].
|
|
|
|
Because Tor uses AES as a stream cipher, malicious or intercepted Guard
|
|
nodes can simply XOR a unique identifier into the circuit cipherstream
|
|
during circuit setup and usage. If this identifier is not removed by a
|
|
colluding exit (either by performing another XOR, or making use of known
|
|
plaintext regions of a cell to directly extract a complete side-channel
|
|
value), then the circuit will fail. In this way, malicious or
|
|
intercepted Guard nodes can ensure that all client traffic is directed
|
|
only to colluding exit nodes, who can observe the destinations and
|
|
deanonymize users.
|
|
|
|
Most code paths in the Tor relay source code will emit loud warnings for
|
|
the most obvious instances circuit failure caused by this attack.
|
|
However, it is very difficult to ensure that all such error conditions
|
|
are properly covered such that warnings will be emitted.
|
|
|
|
This proposal aims to provide a mechanism to ensure that tagging and
|
|
related malleability attacks are cryptographically detectable when they
|
|
happen.
|
|
|
|
|
|
1. Overview
|
|
|
|
Since Tor Relays are already storing a running hash of all data
|
|
transmitted on their circuits (via the or_circuit_t::n_digest and
|
|
or_circuit_t::p_digest properties), it is possible to compute an
|
|
out-of-band HMAC on circuit data, and verify that it is as expected.
|
|
|
|
This proposal first defines an OOB_HMAC primitive that can be included
|
|
standalone in a new relay cell command type, and additionally in other
|
|
cell types.
|
|
|
|
Use of the standalone relay cell command serves to ensure that circuits
|
|
that are successfully built and used were not manipulated at a previous
|
|
point.
|
|
|
|
By altering the RELAY_COMMAND_TRUNCATED and CELL_DESTROY cells to also
|
|
include the OOB_HMAC information, it is similarly possible to detect
|
|
alteration of circuit contents that cause failures before the point of
|
|
usage.
|
|
|
|
|
|
2. The OOB_HMAC primitive
|
|
|
|
The OOB_HMAC primitive uses the existing rolling hashes present in
|
|
or_circuit_t to provide a Tor OP (aka client) with the hash history of
|
|
the traffic that a given relay has seen it so far.
|
|
|
|
Note that to avoid storing an additional 64 bytes of SHA256 digest for
|
|
every circuit at every relay, we use SHA1 for the hash logs, since the
|
|
circuits are already storing SHA1 hashes. It's not immediately clear how
|
|
to upgrade the existing SHA1 digests to SHA256 with the current circuit
|
|
protocol, either, since matching hash algorithms are essential to the
|
|
'recognized' relay cell forwarding behavior. The version field exists
|
|
primarily for this reason, should the rolling circuit hashes ever
|
|
upgrade to SHA256.
|
|
|
|
The OOB_HMAC primitive is specified in Trunnel as follows:
|
|
|
|
struct oob_hmac_body {
|
|
/* Version of this section. Must be 1 */
|
|
u8 version;
|
|
|
|
/* SHA1 hash of all client-originating data on this circuit
|
|
(obtained from or_circuit_t::n_digest). */
|
|
u8 client_hash_log[20];
|
|
/* Number of cells processed in this hash, mod 2^32. Used
|
|
to spot-check hash position */
|
|
u32 client_cell_count;
|
|
|
|
/* SHA1 hash of all server-originating data on this circuit
|
|
(obtained from or_circuit_t::p_digest). */
|
|
u8 server_hash_log[20];
|
|
/* Number of cells processed in this hash, mod 2^32. Used
|
|
to spot-check hash position.
|
|
XXX: Technically the server-side is not needed. */
|
|
u32 server_cell_count;
|
|
|
|
/* HMAC-SHA-256 of the entire cell contents up to this point,
|
|
using or_circuit_t::p_crypto as the hmac key.
|
|
XXX: Should we use a KDF here instead of p_crypto directly? */
|
|
u8 cell_hmac_256[32];
|
|
};
|
|
|
|
|
|
3. Usage of OOB_HMAC
|
|
|
|
The OOB_HMAC body will be included in three places:
|
|
|
|
1. In a new relay cell command RELAY_COMMAND_HMAC_SEND, which is sent in
|
|
response to a client-originating RELAY_COMMAND_HMAC_GET on stream 0.
|
|
2. In CELL_DESTROY, immediately after the error code
|
|
3. In RELAY_COMMAND_TRUNCATED, immediately after the CELL_DESTROY
|
|
contents
|
|
|
|
3.1. RELAY_COMMAND_HMAC_GET/SEND relay commands
|
|
|
|
Clients should use leaky-pipe topology to send RELAY_COMMAND_HMAC_GET to
|
|
the second-to-last node (typically the middle node) in the circuit at
|
|
three points during circuit construction and usage:
|
|
|
|
1. Immediately after the last RELAY_EARLY cell is sent
|
|
2. Upon any stream detachment, timeout, or failure.
|
|
3. Upon any OP-initiated circuit teardown (including timed-out partially
|
|
built circuits).
|
|
|
|
We use RELAY_EARLY as the point at which to send these cells to avoid
|
|
leaking the path length to the middle hop.
|
|
|
|
3.2. Alteration of CELL_DESTROY and RELAY_COMMAND_TRUNCATED
|
|
|
|
In order to provide an HMAC even when a circuit is torn down before use
|
|
due to failure, the behavior for generating and handling CELL_DESTROY
|
|
and RELAY_COMMAND_TRUNCATED should be modified as follows:
|
|
|
|
Whenever an OR sends a CELL_DESTROY for a circuit towards the OP, if
|
|
that circuit was already properly established, the OR should include the
|
|
contents of oob_hmac_body immediately after the reason field. The HMAC
|
|
must cover the error code from CELL_DESTROY.
|
|
|
|
Upon receipt of a CELL_DESTROY, and in any other case where an OR would
|
|
generate a RELAY_COMMAND_TRUNCATED due to error, a conformant relay
|
|
would include the CELL_DESTROY oob_hmac_body, as well as its own
|
|
locally created oob_hmac_body. The locally created oob_hmac_body must
|
|
cover the entire payload contents of RELAY_COMMAND_TRUNCATED, including
|
|
the error code and the CELL_DESTROY oob_hmac_body.
|
|
|
|
Here is a new Trunnel specification for RELAY_COMMAND_TRUNCATED:
|
|
|
|
struct relay_command_truncated {
|
|
/* Error code */
|
|
u8 error_code;
|
|
|
|
/* Number of oob_hmacs. Must be 0, 1, or 2 */
|
|
u8 num_hmac;
|
|
|
|
/* If there are 2 hmacs, the first one is from the CELL_DESTROY,
|
|
and the second one is from the truncating relay. If num_hmac
|
|
is 0, then this came from a relay without support for
|
|
oob_hmac. */
|
|
struct oob_hmac_body[num_hmac];
|
|
};
|
|
|
|
The usage of a strong HMAC to cover the entire CELL_DESTROY contents
|
|
also allows an OP to properly authenticate the reason a remote node
|
|
needed to close a circuit, without relying on the previous hop to be
|
|
honest about it.
|
|
|
|
|
|
4. Ensuring proper ordering with respect to hashes
|
|
|
|
4.1. RELAY_COMMAND_HMAC_GET/SEND
|
|
|
|
The in-order delivery guarantee of circuits will mean that the incoming
|
|
hashes will match upon receipt of the RELAY_COMMAND_HMAC_SEND cell, but
|
|
any outgoing traffic the OP sent since RELAY_COMMAND_HMAC_GET will
|
|
not have been seen by the responding OR.
|
|
|
|
Therefore, immediately upon sending a RELAY_COMMAND_HMAC_GET, the OP
|
|
must record and store its current outgoing hash state for that circuit,
|
|
until the RELAY_COMMAND_HMAC_SEND arrives, and use that stored hash
|
|
value for comparison against the oob_hmac_body's client_hash_log field.
|
|
|
|
The server_hash_log should be checked against the corresponding
|
|
crypt_path_t entry in origin_circuit_t for the relay that the command
|
|
was sent to.
|
|
|
|
4.2. RELAY_COMMAND_TRUNCATED
|
|
|
|
Since RELAY_COMMAND_TRUNCATED may be sent in response to any error
|
|
condition generated by a cell in either direction, the OP must check
|
|
that its local cell counts match those present in the oob_hmac_body for
|
|
that hop.
|
|
|
|
If the counts do not match, the OP may generate a RELAY_COMMAND_HMAC_GET
|
|
to the hop that sent RELAY_COMMAND_TRUNCATED, prior to tearing down the
|
|
circuit.
|
|
|
|
4.3. CELL_DESTROY
|
|
|
|
If the cell counts of the destroy cell's oob_hmac_body do not match what
|
|
the client sent for that hop, unfortunately that hash must be discarded.
|
|
Otherwise, it may be checked against values held from before processing
|
|
the RELAY_COMMAND_TRUNCATED envelope.
|
|
|
|
|
|
5. Security concerns and mitigations
|
|
|
|
5.1. Silent circuit failure attacks
|
|
|
|
The primary way to game this oob-hmac is to omit or block cells
|
|
containing HMACs from reaching the OP, or otherwise tear down circuits
|
|
before responses arrive with proof of tampering.
|
|
|
|
If a large fraction of circuits somehow fail without any
|
|
RELAY_COMMAND_TRUNCATED oob_hmac_body payloads present, and without any
|
|
responses to RELAY_COMMAND_HMAC_GET requests, the user should be alerted
|
|
of this fact as well.
|
|
|
|
This rate of silent circuit failure should be kept as an additional,
|
|
separate per-Guard Path Bias statistic, and the user should be warned if
|
|
this failure rate exceeds some (low) threshold for circuits containing
|
|
relays that should have supported this proposal.
|
|
|
|
5.2. Malicious/colluding middle nodes
|
|
|
|
If the adversary is prevented from causing silent circuit failure
|
|
without the client being able to notice and react, their next available
|
|
vector is to ensure that circuits are only built to middle nodes that
|
|
are malicious and colluding with them (or that do not support this
|
|
proposal), so that they may lie about the proper hash values that they
|
|
see (or omit them).
|
|
|
|
Right now, the current path bias code also does not count circuit
|
|
failures to the middle hop as circuit attempts. This was done to reduce
|
|
the effect of ambient circuit failure on the path bias accounting (since
|
|
an average ambient circuit failure of X per-hop causes the total circuit
|
|
failure middle+exit circuits to be 2X). Unfortunately, not counting
|
|
middle hop failure allows the adversary to only allow circuits to
|
|
colluding middle hops to complete, so that they may lie about their hash
|
|
logs. All failed circuits to non-colluding middle nodes could be torn
|
|
down before RELAY_COMMAND_TRUNCATED is sent.
|
|
|
|
For this reason, the per-Guard Path Bias counts should be augmented to
|
|
additionally track middle-node-only failure as a separate statistic as
|
|
well, and the user should be warned if middle-node failure drops below a
|
|
similar threshold as the current end-to-end failure.
|
|
|
|
5.3. Side channel issues, mitigations, and limitations
|
|
|
|
Unfortunately, leaking information about circuit usage to the middle
|
|
node prevents us from sending RELAY_COMMAND_HMAC_GET cells at more
|
|
optimal points in circuit usage (such as immediately upon open,
|
|
immediately after stream usage, etc).
|
|
|
|
As such, we are limited to waiting until RELAY_EARLY cells stop being
|
|
sent. It is debatable if we should send hashes periodically (perhaps
|
|
with windowing information updates?) instead.
|
|
|
|
|
|
6. Alternatives
|
|
|
|
A handful of alternatives to this proposal have already been discussed,
|
|
but have been dismissed for various reasons. Per-hop cell HMACs were
|
|
ruled out because they will leak the total path length, as well as the
|
|
current hop's position in the circuit.
|
|
|
|
Wide-block ciphers have been discussed, which would provide the property
|
|
that attempts to alter a cell at a previous hop would render it
|
|
completely corrupted upon its final destination, thus preventing
|
|
untagging and recovery, even by a colluding malicious peer.
|
|
|
|
Unfortunately, performance analysis of modern provably secure versions
|
|
of wide-block ciphers has shown them to be at least 10X slower than
|
|
AES-NI[2].
|
|
|
|
|
|
1. https://lists.torproject.org/pipermail/tor-dev/2012-March/003347.html
|
|
2. http://archives.seul.org/tor/dev/Mar-2015/msg00137.html
|