Analysis of RLN for Mix

This post documents a work-in-progress analysis of integrating Rate Limiting Nullifier (RLN) with libp2p-mix for DoS and sybil protection. The full specification is documented here.

TL;DR: The RLN integration introduces two potential security gaps: i) an observable epoch field per hop, creating a periodic deanonymization window, and ii) a high blanket rate limit for outgoing packets that malicious nodes can exploit. Both gaps are medium severity.

1. Epoch Boundary Attack

Background

In Sphinx, all packets are fixed-size encrypted blobs with no observable per-packet metadata. A passive adversary watching a mix node sees indistinguishable ciphertexts arriving and departing. With random per-hop delays, the adversary is reduced to random guessing when attempting to map incoming packets to outgoing ones. For a batch of k incoming packets, the probability of correctly mapping one of them is 1/k, which is the intended security baseline. Mix delays exist precisely to hold the adversary at this level.

The RLN integration breaks this baseline for a specific, recurring class of packets by introducing an observable epoch field into every forwarded packet.

The Attack (Severity: Medium)

Each mix node generates a fresh RateLimitProof for each outgoing packet. The epoch value in that proof reflects the epoch at which the node generates the proof. Since the RateLimitProof is attached outside the Sphinx encrypted layer, it is observable to a passive adversary on the inter-node link.

Consider a mix node that receives k packets near the end of epoch N, with a small expected per-hop delay (e.g., one second). k-1 packets are processed before the epoch boundary; one is processed after. A passive adversary observing the outgoing stream sees a packet with epoch field N+1. Given small delays and no new incoming packet at epoch N+1 yet, this packet was almost certainly the last one received in epoch N — the one that arrived closest to the boundary.

This allows the adversary to map the packet with epoch field N+1 to the last-received incoming packet in the previous epoch with high confidence, reducing its anonymity from 1/k to ~1. The attack is most effective when the expected delay is small relative to the epoch duration, which is the common low-latency case. It recurs periodically at every epoch boundary.

Comparison with Sphinx

This attack does not exist in Sphinx. Without per-packet observable metadata, the adversary has no way to distinguish one outgoing packet from another. The epoch field is precisely what enables this attack.

Cover traffic mitigates this by increasing the overall traffic density, diluting the impact of such a per-packet signalling field. However, this makes cover traffic a harder requirement for anonymity than in standard mixnet design — a stronger assumption the specification MUST acknowledge.

Mitigation

The RateLimitProof can be encrypted for the next hop, using a component analogous to α in the Sphinx packet. Each forwarding node encrypts the proof using an ephemeral key derived from the next hop’s public key. The next hop then decrypts and verifies the proof locally. A passive adversary now sees only an encrypted blob and is reduced to random guessing even at epoch boundaries. However, this approach does not integrate cleanly as it requires extending the wire format and the per-hop processing pipeline. Alternative solutions need to be explored.

2. Rate Limit Amplification Gap

Background

A mix node is not exclusively an intermediary — it can also originate packets, acting as a sender. By design, forwarded and originated packets are indistinguishable. This is fundamental to sender anonymity.

The specification defines a rate limit R as the maximum number of outgoing packets per mix node in an epoch. RLN enforces this by detecting double-signalling: if there are more than R outgoing packets from a mix node in an epoch, its nullifier reuse is detectable and its secret key can be reconstructed for slashing. Both originated and forwarded packets count against this single rate limit R as they are indistinguishable.

The Attack (Severity: Medium)

Consider an honest mix node X. It can be chosen as an intermediary in many concurrent paths. Its total number of outgoing packets per epoch, P, scales with network traffic and is not bounded by R. Given that X generates a fresh RLN proof for each outgoing forwarded packet, forwarding counts against X’s own rate limit — not the sender’s. Therefore, to avoid being slashed, X requires a rate limit of at least P. But given that limit, X can even originate up to P packets per epoch.

Ideally, we want to enforce a forwarding limit of P and a sending limit of R packets per epoch. However, this is not possible since forwarded and originated packets are indistinguishable by design.

A malicious node, having obtained membership, can send up to P packets per epoch and remain entirely undetected. That is, the high rate limit set for intermediary operation effectively becomes a free allowance for malicious mix nodes — they can send more spam with no consequence.

Zero-knowledge proof generation can be a deterrent for malicious nodes. However, the cost is fixed per proof and does not escalate with the damage caused.

Even the membership stake is a fixed, one-time cost — a barrier to entry, not a rate-proportional deterrent.

Discussion

The following discussion is based on comments from @haelius and @prem.

One proposed design approach is to set R to be a generous limit — much larger than expected per-node traffic — so that forwarding load plus originated messages remain well below R for honest nodes, with cover traffic filling the remainder. Under this model, intermediaries that receive more than R messages simply drop the excess rather than requiring a higher registered rate limit.

This partially reframes the attack: the concern now becomes a targeted degradation attack — an adversary with N memberships floods a specific intermediary, filling its R forwarding slots with spam and starving legitimate traffic. The mitigation proposed is that senders route around degraded nodes with enough redundancy. However, this relies on detection mechanisms not currently defined in the spec, and does not hold if multiple nodes are targeted simultaneously.

Note that this assumes the attacker could register N memberships, which is costly.

Open Questions

  • If mix nodes are required to send at a constant rate up to R then each node generates R RLN proofs per epoch. This has cost and performance implications. How should R be chosen to ensure honest nodes are not flagged while keeping overall proof generation overhead minimal?
  • Is the degradation attack sufficiently mitigated by redundant path selection, and what detection mechanisms are needed?
  • How membership is obtained remains an open question — the entire approach assumes some degree of sybil resistance, which is lost if memberships are easily obtainable. Token-based approaches (as used in Blend/Nym) may be needed long-term but introduce deeper on-chain dependencies.
2 Likes

did not get how this is possible. if a node generates more than R signals (i.e sends or forwards messages) than per epoch the node can loose its membership.

I doubt a sender node can do that otherwise the whole point of RLN itself is defeated right. Unless I am missing something here.

Yes, but can’t we build a mechanism to not forward more than R packets in an epoch? This way the node X can stop forwarding packets beyond rate R. This means that path becomes unhealthy (we can design a basic path health monitoring mechanism to run locally to determine nodes that are failing to forward and can loop that input into mix node selection), which would lead to sender nodes choosing other paths. With such a mechanism, wouldn’t the network stabilize over time? This is a general problem with free-route style mixnets anyways where certain intermediary nodes may end up becoming hotspots and hence having to handle too much traffic and eventually begin to fail forwarding at some point. This could be a mitigation to handle a node having to handle more packets per epoch.

In current implementation of zerokit(which is used for zk proof generation and verification for RLN) if a node tries to generate more than R signals per epoch, zk-proof generation itself fails.

Yes, this has always been the consideration for using RLN. The whole protocol fails if memberships are easily obtained. Which is why it becomes important to design how memberships are obtained, all we can do at this point is highlight possible criteria that can be defined as to how memberships can be provided because that would in-turn depend on each instantiation of mixnet.

Aren’t these approaches similar to stake based memberships or am i missing something? iirc Nym goes beyond stake and uses additional reputation mechanisms to determine if a node can function as core mix node(for forwarding etc).