Tor issueshttps://gitlab.torproject.org/tpo/core/tor/-/issues2023-06-09T13:26:45Zhttps://gitlab.torproject.org/tpo/core/tor/-/issues/40422[CircuitPadding] circpad_add_matching_machines() should be called when a cir...2023-06-09T13:26:45ZJaym[CircuitPadding] circpad_add_matching_machines() should be called when a circuit has opened.### Summary
The circuit padding framework supports negotiating padding upon various events. Among them, CIRCPAD_CIRC_OPENED states that a given padding machine should be applied to a circuit when a circuit has opened.
However, no code ...### Summary
The circuit padding framework supports negotiating padding upon various events. Among them, CIRCPAD_CIRC_OPENED states that a given padding machine should be applied to a circuit when a circuit has opened.
However, no code seems to trigger this mechanism. When a circuit has built, the function circpad_machine_event_circ_built() is called and checks whether some machine may be removed/added to the circuit. However, at this stage of the circuit building process, the circuit has built but is not marked as open yet.
### Bug
If some machine uses `client_machine->conditions.apply_state_mask = CIRCPAD_CIRC_OPENED;` the machine would only be applied when another event than a circ building/opening triggers the function circpad_add_matching_machines() (e.g., ap conn links a stream, or the circ purpose changes from general to something else).
### What is the expected behavior?
When circuituse.c calls circuit_has_opened(), it should also call the circpad module; e.g., a new function circpad_machine_event_circ_opened() that checks for adding machine to the circuit.
### Environment
Running a version forked from 0.4.5.7
### Relevant logs and/or screenshots
Contains some logs showing a call to circpad_machine_event_circ_built() while the circuit is still marked as building. Also contains custom logs:
```Jun 30 11:23:50.000 [info] circuit_finish_handshake(): Finished building circuit hop:
Jun 30 11:23:50.000 [info] internal (high-uptime) circ (length 3, last hop test000a): $22BA781A60C0CBB7FFAEA8858128427F67F60038(open) $7684DE04DCBB44538554E2CD1D14CDF836D5AF4D(open) $C7ADB1DBCE99F0B2ED2812B1953E4986EE9846DB(open)
Jun 30 11:23:50.000 [debug] dispatch_send_msg_unchecked(): Queued: ocirc_cevent (<gid=7 evtype=2 reason=0 onehop=0>) from or, on ocirc.
Jun 30 11:23:50.000 [debug] dispatcher_run_msg_cbs(): Delivering: ocirc_cevent (<gid=7 evtype=2 reason=0 onehop=0>) from or, on ocirc:
Jun 30 11:23:50.000 [debug] dispatcher_run_msg_cbs(): Delivering to btrack.
Jun 30 11:23:50.000 [debug] btc_cevent_rcvr(): CIRC gid=7 evtype=2 reason=0 onehop=0
Jun 30 11:23:50.000 [debug] circuit_build_times_add_time(): Adding circuit build time 43
Jun 30 11:23:50.000 [debug] circpad_machine_conditions_apply(): Checking circuit purpose, 5
Jun 30 11:23:50.000 [debug] circpad_machine_conditions_apply(): Checking condition state mask 21 vs condition: 2
Jun 30 11:23:50.000 [debug] circpad_machine_conditions_apply(): Checking circuit purpose, 5
Jun 30 11:23:50.000 [debug] circpad_machine_conditions_apply(): Checking circuit purpose, 5
Jun 30 11:23:50.000 [debug] circpad_machine_event_circ_built(): Circpad module event circ built -- circ state: 0
Jun 30 11:23:50.000 [debug] circpad_machine_conditions_apply(): Checking circuit purpose, 5
Jun 30 11:23:50.000 [debug] circpad_machine_conditions_apply(): Checking condition state mask 21 vs condition: 2
Jun 30 11:23:50.000 [debug] circpad_machine_conditions_apply(): Checking circuit purpose, 5
Jun 30 11:23:50.000 [debug] circpad_machine_conditions_apply(): Checking circuit purpose, 5
Jun 30 11:23:50.000 [debug] invoke_plugin_operation_or_default(): Plugin found for caller calling a plugin in the circpad module when a circuit has built
Jun 30 11:23:50.000 [info] circpad_dropmark_activate_when_built(): Looks like the client_dropmark_def machine does not exist over this circuit
Jun 30 11:23:50.000 [debug] plugin_run(): Plugin execution returned -2147483648
Jun 30 11:23:50.000 [debug] plugin_run(): vm error message: (null)
Jun 30 11:23:50.000 [info] entry_guards_note_guard_success(): Recorded success for primary confirmed guard test002r ($22BA781A60C0CBB7FFAEA8858128427F67F60038)
Jun 30 11:23:50.000 [debug] dispatch_send_msg_unchecked(): Queued: ocirc_state (<gid=7 state=4 onehop=0>) from or, on ocirc.
Jun 30 11:23:50.000 [debug] dispatcher_run_msg_cbs(): Delivering: ocirc_state (<gid=7 state=4 onehop=0>) from or, on ocirc:
Jun 30 11:23:50.000 [debug] dispatcher_run_msg_cbs(): Delivering to btrack.
Jun 30 11:23:50.000 [debug] btc_state_rcvr(): CIRC gid=7 state=4 onehop=0
Jun 30 11:23:50.000 [info] circuit_build_no_more_hops(): circuit built!
Jun 30 11:23:50.000 [info] pathbias_count_build_success(): Got success count 3.000000/3.000000 for guard test002r ($22BA781A60C0CBB7FFAEA8858128427F67F60038)
Jun 30 11:23:50.000 [debug] circuit_has_opened(): calling circuit_has_opened()
```
### Possible fixes
Add a new function circpad_machine_event_circ_opened() called from circuituse.c when the circuit has opened.Tor: 0.4.8.x-freezeMike PerryMike Perryhttps://gitlab.torproject.org/tpo/core/tor/-/issues/40277Circuit Padding attack scenario through circpad_global_max_padding_pct2023-06-09T13:26:55ZGhost UserCircuit Padding attack scenario through circpad_global_max_padding_pctHello *,
I am from [RadicallyOpenSecurity](https://www.radicallyopensecurity.com/) and currently performing a short review of the [Padding Machines for Tor](https://nlnet.nl/project/TorPaddingMachines/) NGI Zero PET project.
During dis...Hello *,
I am from [RadicallyOpenSecurity](https://www.radicallyopensecurity.com/) and currently performing a short review of the [Padding Machines for Tor](https://nlnet.nl/project/TorPaddingMachines/) NGI Zero PET project.
During discussion with the padding machine author Tobias Pulls (@pulls), I noticed a general attack scenario that could be of relevance for the padding framework implementation.
The general idea of the attack is that a malicious Tor client can, under some circumstances, repeatedly force the circuit padding logic of middle relays to reply with more padding bytes than non-padding bytes. After some time of sustained attacks, the target middle relay will develop a statistical circuit padding overhead percentage which is over the the network-wide `circpad_global_max_padding_pct` limit (see [section 3.5](https://gitweb.torproject.org/tor.git/tree/doc/HACKING/CircuitPaddingDevelopment.md?id=33380f6b2770a3c4d9e2a9cc8a4b18c71a40571b#n614) of the documentation), at which point it will stop sending padding on any of its circuits.
Once the attack stops, the ratio between sent padding and sent non-padding of the relay goes back to normal over time while the relay serves non-padded cell data to legitimate clients, so it reverts automatically to a safe state again where it has working padding machines.
However, there are several notable details:
* To our knowledge, the currently rolled out padding machines can not be used to trigger this problem. This is likely only a problem for future machines with high volume that defend against website fingerprinting attacks.
* Due to the event-based nature of client padding machines, the suppression of padding generation at the middle relay means that the `CIRCPAD_EVENT_PADDING_RECV` transition on the client is never taken. This results in the client not sending any padding, or performing only a very limited subset of the regular padding behavior (depending on the machine definition). This amplifies the practical impact of the missing padding from the relay.
* Since the relay behavior change affects all of its circuits at once and may happen several times over a short time-span, the resulting traffic anomalies may be a fingerprinting opportunity for adversaries that are performing traffic analysis on the network.
* According to @pulls, the consequences of this attack on the relay server would only be logged at `info` level and may go unnoticed by relay operators.
* There is a threshold mechanism at the individual circuit machine level through the `relay->max_padding_percent` setting. At first glance, this appears to mitigate the described attack scenario. However, note that this limit is not applied to the first `relay->allowed_padding_count` number of cells, so an attacker can circumvent this limitation by repeatedly triggering new padding machines. Since it appears to be beneficial for padding machines to have this initial level of freedom over padding for their effectiveness against fingerprinting, the attack is unlikely to be mitigated with restrictive machine limits without also impacting the usefulness of the machines themselves.
* The attack can also be performed in the other direction by a malicious middle relay against clients. In this case, it would disable/degrade padding on all of the client circuits. However, we regard this variant as less impactful than client->relay attacks, in part because this attack is harder to scale.
Some initial recommendations:
* Higher severity logging when exceeding `circpad_global_max_padding_pct`.
* The consensus-provided global limits could be enforced *per circuit* instead of truly globally. The root of the problem is shared state across circuits. This would still make the global padding limits useful, since in the future there might be multiple machines active throughout the lifetime of a circuit.
* Based on the information from @pulls , the `CircuitPaddingDisabled` (see issue [28693](https://gitlab.torproject.org/legacy/trac/-/issues/28693)) switch might be sufficient as an emergency fail-safe in case of network-wide padding problems.
I would like to thank @pulls for walking me through the various aspects of related network behavior and providing other helpful suggestions.Mike PerryMike Perryhttps://gitlab.torproject.org/tpo/core/tor/-/issues/31787Full HMM support for circuit padding state transition2023-06-08T18:27:55ZMike PerryFull HMM support for circuit padding state transitionThe circuit padding state transition system is quasi-deterministic. A non-deterministic state machine can be built from this using many states through clever use of the infinity bin, but this will lead to excessively complicated machines...The circuit padding state transition system is quasi-deterministic. A non-deterministic state machine can be built from this using many states through clever use of the infinity bin, but this will lead to excessively complicated machines.
One can imagine a non-deterministic state machine/HMM that simulates an entire web browsing session on a circuit. If this kind of machine seems useful, it may be better simply to transform the circpad_state_t.next_state transition field into an array of structs that specifies the next state as well as a probability for transitioning to that state for that event.
We would accept patches for this modification if it seems useful.https://gitlab.torproject.org/tpo/core/tor/-/issues/31636Circuit padding: Add meta probability distribution type2023-06-08T18:27:55ZMike PerryCircuit padding: Add meta probability distribution typeTobias Pulls pointed out that his APE system actually randomized the *parameters* of the probability distributions that he used, for each circuit.
If this is indeed helpful (it probably is), we should find out which form or forms of per...Tobias Pulls pointed out that his APE system actually randomized the *parameters* of the probability distributions that he used, for each circuit.
If this is indeed helpful (it probably is), we should find out which form or forms of per-circuit parameter randomization is best to use, and get it supported by the circuit padding framework.https://gitlab.torproject.org/tpo/core/tor/-/issues/29083WTF-PAD: Specify exit policy for machine conditions2022-09-01T21:29:27ZGeorge KadianakisWTF-PAD: Specify exit policy for machine conditionsFrom the TODO file:
```
- Specify exit policy for machine conditions?
- short_policy_t looks good, except for its flexible array member :/
- Can we make our own struct with a small, fixed number of policy
entries? Say...From the TODO file:
```
- Specify exit policy for machine conditions?
- short_policy_t looks good, except for its flexible array member :/
- Can we make our own struct with a small, fixed number of policy
entries? Say 3-4? Or is that a bad idea to lose this flexibility?
- Check conditions based on attached streams on the circuit
- Accept should mean "only apply if matched"
- Reject should mean "don't apply if matched"
- If a policy is specified, Reject *:* is implicit default (so reject
policies need an Accept entry).
- With no policy, Accept *:* is implicit default.
```