Summary
The 3c.3 relay's Trojan-front discriminator decides tunnel-vs-decoy by deobfuscating the first framed message with the network obf_psk and checking it is a fresh Register. But yip_obf::deobfuscate is an unauthenticated keystream XOR (SipHash-CTR, "NOT an AEAD" per its own docs) — there is no MAC. So the "proof of obf_psk knowledge" reduces to a structural match on the recovered bytes: ptype == RDV_TYPE (8 bits) + Register tag 0x00 (8 bits) + a length field in a plausible range — only ~16–21 effective bits.
Impact
A blind prober (no obf_psk) sending a random frame over a completed TLS session lands a false Upgrade with probability ≈ 2⁻²¹ per connection. On that event the relay writes an obfuscated, rendezvous-shaped reply and enters run_tunnel — i.e. it emits the "tunnel behavior" the spec promises a non-holder never observes, instead of serving the decoy.
This contradicts design §6.1's original "a prober without obf_psk cannot forge a Register" (corrected to a probabilistic statement in the spec, referencing this issue).
Severity
Low in practice: each attempt costs a full real-cert TLS handshake, so ~10⁶ handshakes to unmask once, and the payoff is only "this endpoint answered non-decoy." But it means probe-resistance against a blind forger is probabilistic, not absolute.
Fix directions
Surfaced by the 3c.3 final whole-branch review.
Summary
The 3c.3 relay's Trojan-front discriminator decides tunnel-vs-decoy by deobfuscating the first framed message with the network
obf_pskand checking it is a freshRegister. Butyip_obf::deobfuscateis an unauthenticated keystream XOR (SipHash-CTR, "NOT an AEAD" per its own docs) — there is no MAC. So the "proof ofobf_pskknowledge" reduces to a structural match on the recovered bytes:ptype == RDV_TYPE(8 bits) +Registertag0x00(8 bits) + a length field in a plausible range — only ~16–21 effective bits.Impact
A blind prober (no
obf_psk) sending a random frame over a completed TLS session lands a falseUpgradewith probability ≈ 2⁻²¹ per connection. On that event the relay writes an obfuscated, rendezvous-shaped reply and entersrun_tunnel— i.e. it emits the "tunnel behavior" the spec promises a non-holder never observes, instead of serving the decoy.This contradicts design §6.1's original "a prober without
obf_pskcannot forge aRegister" (corrected to a probabilistic statement in the spec, referencing this issue).Severity
Low in practice: each attempt costs a full real-cert TLS handshake, so ~10⁶ handshakes to unmask once, and the payoff is only "this endpoint answered non-decoy." But it means probe-resistance against a blind forger is probabilistic, not absolute.
Fix directions
Surfaced by the 3c.3 final whole-branch review.