mirror of
https://github.com/vacp2p/rfc-index.git
synced 2026-01-07 23:04:09 -05:00
libp2p Mix Protocol Spec Draft (#97)
Old PR can be found here: [Mix Protocol Spec #85](https://github.com/vacp2p/rfc-index/pull/85)
This commit is contained in:
744
vac/raw/mix.md
Normal file
744
vac/raw/mix.md
Normal file
@@ -0,0 +1,744 @@
|
||||
---
|
||||
title: LIBP2P-MIX
|
||||
name: Libp2p Mix Protocol
|
||||
status: raw
|
||||
category: Standards Track
|
||||
tags:
|
||||
editor: Akshaya Mani <akshaya@status.im>
|
||||
contributors:
|
||||
---
|
||||
|
||||
## Abstract
|
||||
|
||||
This document specifies the Mix protocol, a custom protocol within the
|
||||
[libp2p](https://libp2p.io) framework designed to enable anonymous communication
|
||||
in peer-to-peer networks. The Mix protocol allows libp2p nodes to send messages
|
||||
without revealing the sender's identity to intermediary nodes or the recipient.
|
||||
It achieves this by using the [Sphinx packet format](https://www.researchgate.net/publication/220713667_Sphinx_A_Compact_and_Provably_Secure_Mix_Format),
|
||||
which encrypts and routes messages through a series of nodes (mix nodes)
|
||||
before reaching the recipient.
|
||||
|
||||
Key features of the protocol include:
|
||||
|
||||
i. Path selection for choosing a random route through the network via multiple
|
||||
mix nodes.\
|
||||
ii. Sphinx packet construction and processing, providing cryptographic
|
||||
guarantees of anonymity and security.\
|
||||
iii. Pluggable spam protection mechanism to prevent abuse of the mix network.\
|
||||
iv. Delayed message forwarding to thwart timing analysis attacks.
|
||||
|
||||
**Protocol identifier:** `"/mix/1.0.0"`
|
||||
|
||||
Note: The Mix Protocol is designed to work alongside existing libp2p protocols,
|
||||
allowing for seamless integration with current libp2p applications while
|
||||
providing enhanced privacy features. For example, it can encapsulate messages
|
||||
from protocols like [GossipSub](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.2.md)
|
||||
to ensure sender anonymity.
|
||||
|
||||
## Background
|
||||
|
||||
libp2p protocols do not inherently protect sender identities.
|
||||
|
||||
The Mix protocol enhances anonymity in libp2p by implementing a mix network,
|
||||
where messages are anonymized through multiple relay nodes before reaching the
|
||||
intended recipient. The Sphinx packet format is a well-researched component which
|
||||
this specification leverages to offer strong anonymity properties by concealing
|
||||
sender and recipient information at each relay.
|
||||
|
||||
Using this approach, even the nodes relaying messages cannot determine the
|
||||
sender or final recipient, within a robust adversarial model. This decentralized
|
||||
solution distributes trust among participants, eliminating single points of
|
||||
failure and enhancing overall network resilience. Additionally, pluggable
|
||||
spam protection mechanism and delayed forwarding address common attacks on
|
||||
anonymity networks, such as spam and timing analysis.
|
||||
|
||||
The Mix protocol is designed with flexibility in mind, allowing for
|
||||
pluggable components such as spam protection, peer discovery, and
|
||||
incentivization mechanisms. This design choice enables the protocol to evolve
|
||||
and adapt to different network requirements and constraints. This also leaves
|
||||
room for future enhancements such as cover traffic generation.
|
||||
|
||||
By incorporating these features, the Mix protocol aims to provide a robust
|
||||
anonymity layer within the libp2p ecosystem, enabling developers to easily
|
||||
incorporate privacy features into their applications.
|
||||
|
||||
## Specification
|
||||
|
||||
### 1. Protocol Identifier
|
||||
|
||||
The Mix protocol is identified by the string `"/mix/1.0.0"`.
|
||||
|
||||
### 2. Custom Mix Protocol
|
||||
|
||||
The Mix protocol is designed as a standalone protocol,
|
||||
identified by the protocol identifier `"/mix/1.0.0"`.
|
||||
This approach allows the Mix protocol to operate independently,
|
||||
decoupled from specific applications,
|
||||
providing greater flexibility and reusability across various libp2p protocols.
|
||||
By doing so, the Mix protocol can evolve independently,
|
||||
focusing on its core functionality without being tied to the development
|
||||
and maintenance cycles of other protocols.
|
||||
|
||||
#### 2.1 Mix Nodes Roles
|
||||
|
||||
All nodes participating in the Mix protocol are considered as mix nodes. They
|
||||
have the capability to create/process and forward Sphinx packets. Mix nodes can
|
||||
play different roles depending on their position in a particular message path:
|
||||
|
||||
- **Sender Node**
|
||||
- A mix node that initiates the anonymous message publishing process.
|
||||
- Responsible for:
|
||||
- Path selection.
|
||||
- Sphinx packet creation.
|
||||
- Initiating the message routing through the mix network.
|
||||
- Must run both the Mix protocol instance and the instance of the libp2p
|
||||
protocol for the message being published (_e.g.,_ GossipSub, Ping, etc.).
|
||||
- **Intermediary Mix Node**
|
||||
- A mix node that is neither the sender nor the exit node in a message path.
|
||||
- Responsible for:
|
||||
- Receiving Sphinx packets.
|
||||
- Processing (decrypting and re-encrypting) Sphinx packets.
|
||||
- Forwarding processed packets to the next node in the path.
|
||||
- Only needs to run the Mix protocol instance.
|
||||
- **Exit Node**
|
||||
- The final mix node in a message path.
|
||||
- Responsible for:
|
||||
- Receiving and processing the final Sphinx packet.
|
||||
- Extracting the original message.
|
||||
- Disseminating the decrypted message using the appropriate libp2p protocol.
|
||||
- Must run both the Mix protocol instance and the instance of the libp2p
|
||||
protocol for the message being published.
|
||||
|
||||
#### 2.2 Roles Flexibility
|
||||
|
||||
A single mix node can play different roles in different paths:
|
||||
|
||||
- It can be a sender node for messages it initiates.
|
||||
- It can be an intermediary node for messages it is forwarding.
|
||||
- It can be an exit node for messages it is disseminating.
|
||||
|
||||
#### 2.3 Incentives
|
||||
|
||||
To publish an anonymous libp2p message (_e.g.,_ GossipSub, Ping, etc.), nodes
|
||||
MUST run a mix node instance. This requirement serves as an incentive for nodes
|
||||
to participate in the mix network, as it allows them to benefit from the
|
||||
anonymity features while also contributing to the network's overall anonymity
|
||||
and robustness.
|
||||
|
||||
#### 2.4 Node Discovery
|
||||
|
||||
All mix nodes participate in the discovery process and maintain a list of
|
||||
discovered nodes.
|
||||
|
||||
- **Bootstrap Nodes**
|
||||
|
||||
i. The network has a set of well-known bootstrap nodes that new mix nodes
|
||||
can connect to when joining the network.\
|
||||
ii. The bootstrap nodes help new mix nodes discover other active mix nodes in
|
||||
the network.
|
||||
|
||||
- **Discovery**
|
||||
|
||||
i. All mix nodes publish their Ethereum Node Records (ENRs) containing:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "v4",
|
||||
"multiaddr": "/ip4/192.0.2.1/udp/9000/quic",
|
||||
"ed25519": "0x5a6fcd3e9d6a5e4d5f71e7e5b4cfa9b7b73d9f5f7e9a8b9c5d7f9e8d5a6f7c9e",
|
||||
"mix": "/mix/1.0.0",
|
||||
"supported_protocols": ["ping", "gossipsub"]
|
||||
}
|
||||
```
|
||||
|
||||
**Field Explanations**
|
||||
|
||||
- `id`: Indicates the ENR format version (_e.g.,_ `"v4"`).
|
||||
- `multiaddr`: The node's multiaddress, including the transport protocol
|
||||
(_e.g.,_ QUIC) and IP address/port.
|
||||
- `ed25519`: The node's Ed25519 public key, used for Sphinx encryption.
|
||||
- `mix`: Indicates the supported Mix protocol version (_e.g.,_ `"/mix/1.0.0"`).
|
||||
- `supported_protocols`: A list of other libp2p protocols supported by the
|
||||
node (_e.g.,_ Ping, GossipSub, etc.).
|
||||
- Additional fields may be included based on the node's requirements.
|
||||
|
||||
ii. The mix nodes use a peer discovery protocol like [WAKU](https://waku.org)/[Discv5](https://github.com/ethereum/devp2p/blob/master/discv5/discv5.md):
|
||||
|
||||
- Connect to a set of bootstrap nodes when joining the network.
|
||||
- Regularly update their list of known peers.
|
||||
- Obtain a random sample of nodes that is representative of the network.
|
||||
|
||||
- **Path Selection (Message Senders Only)**
|
||||
|
||||
To send an anonymous message, a mix node performs the following actions:
|
||||
|
||||
i. Choose a random exit node that supports the required libp2p protocol for
|
||||
the message.\
|
||||
ii. Select remaining L-1 unique mix nodes randomly without replacement from
|
||||
the list of discovered nodes.
|
||||
|
||||
- **Forwarding To Next Hop (Intermediary Nodes Only)**
|
||||
|
||||
When a mix node receives an incoming Sphinx packet, it performs the following
|
||||
actions:
|
||||
|
||||
i. Decrypts the packet to obtain the next hop multiaddress\
|
||||
ii. Checks if the next hop is in the list of discovered nodes.\
|
||||
iii. If not, performs discovery for that specific node.\
|
||||
iv. Forwards the Sphinx packet to the next hop.
|
||||
|
||||
#### 2.5 Protocol Registration
|
||||
|
||||
The protocol is registered with the libp2p host using the `"/mix/1.0.0"`
|
||||
identifier. This identifier is used to establish connections and negotiate the
|
||||
protocol between libp2p peers.
|
||||
|
||||
#### 2.6 Transport Layer
|
||||
|
||||
The Mix protocol uses secure transport protocols to ensure confidentiality and
|
||||
integrity of communications. The recommended transport protocols are
|
||||
[QUIC](https://datatracker.ietf.org/doc/rfc9000/) or TLS (preferably QUIC
|
||||
due to its performance benefits and built-in features
|
||||
such as low latency and efficient multiplexing).
|
||||
|
||||
#### 2.7 Connection Establishment
|
||||
|
||||
- The sender initiates a secure connection (TLS or QUIC) to the first mix node
|
||||
using the libp2p transport.
|
||||
- The sender uses the `"/mix/1.0.0"` protocol identifier to convey that the
|
||||
connection is for the Mix protocol.
|
||||
- Once the connection is established, the sender can forward Sphinx packets
|
||||
using the Mix protocol.
|
||||
- Subsequent mix nodes in the path follow the same process when forwarding
|
||||
messages to other mix nodes.
|
||||
|
||||
### 3. Cryptographic Primitives and Security Parameter
|
||||
|
||||
- **Security Parameter:** $\kappa = 128$ bits provides a balance between
|
||||
security and efficiency.
|
||||
- **Cryptographic Primitives**
|
||||
- **Group G**: Curve25519 elliptic curve offers 128-bit security with small
|
||||
(32-byte) group elements, efficient for both encryption and key exchange.
|
||||
- **Hash function H**: SHA-256.
|
||||
- **KDF:** SHA-256 (truncated to 128 bits).
|
||||
- **AES-CTR:** AES-128 in counter mode.
|
||||
- **Inputs:** Key `k` (16 bytes), Initialization Vector `iv` (16 bytes),
|
||||
Plaintext `p`
|
||||
- **Initialization Vector (IV)**: 16 bytes, chosen randomly for each
|
||||
encryption.
|
||||
- **Plaintext**: Data to be encrypted (_e.g.,_ routing information, message
|
||||
payload).
|
||||
- **Output**: Ciphertext `c` (same size as plaintext `p`).
|
||||
- **Operation**: AES-CTR mode uses key and the counter (`iv`) to produce a
|
||||
keystream, which is XORed with the plaintext to produce the ciphertext.
|
||||
- **HMAC-SHA-256:** 256-bit MAC (truncated to 128 bits).
|
||||
- **Inputs:** Key `k` (16 bytes), Message `m`
|
||||
- **Message**: Data to be authenticated (_e.g.,_ $β$ component).
|
||||
- **Output**: MAC `mac` (truncated to 128 bits).
|
||||
- **Operation**: HMAC-SHA-256 uses the key and the message to produce a
|
||||
hash-based message authentication code.
|
||||
|
||||
### 4. Sphinx Packet Format
|
||||
|
||||
#### 4.1 Packet Components and Sizes
|
||||
|
||||
1. **Alpha ($α$)**: 32 bytes
|
||||
|
||||
- Represents a Curve25519 group element (x-coordinate in GF(2^255 - 19)).
|
||||
- Used by mix nodes to extract shared session key using their private key.
|
||||
|
||||
2. **Beta ($β$)**: $((t+1)r + 1)\kappa$ bytes typically, where $r$ is the maximum
|
||||
path length.
|
||||
|
||||
- Contains the encrypted routing information.
|
||||
- We recommend a reasonable maximum path length of $r=5$, considering
|
||||
latency/anonymity trade-offs.
|
||||
- This gives a reasonable size of $336$ bytes, when $t = 3$ (refer
|
||||
Section 5.2.10 for the choice of $t$).
|
||||
- We extend $β$ to accommodate next hop address and delay below.
|
||||
|
||||
3. **Gamma ($γ$)**: $\kappa$ bytes (16 bytes)
|
||||
|
||||
- Output of HMAC-SHA-256, truncated to 128 bits.
|
||||
- Ensures the integrity of the header information.
|
||||
|
||||
4. **Delta ($δ$)**: The encrypted payload, which can be of variable size.
|
||||
- According to the [MixMatch](https://petsymposium.org/popets/2024/popets-2024-0050.pdf)
|
||||
paper, the Nym network uses Sphinx packets of a fixed
|
||||
size (2413 bytes).
|
||||
- Considering this, the maximum $δ$ size can be chosen as 2413 bytes minus
|
||||
the header length (which will be derived below).
|
||||
|
||||
#### 4.2 Address Format and Delay Specification
|
||||
|
||||
In the original
|
||||
[Sphinx](https://cypherpunks.ca/~iang/pubs/Sphinx_Oakland09.pdf) paper, the
|
||||
authors use node IDs of size $\kappa$ ($16$ bytes) to represent the next hop
|
||||
addresses. To accommodate larger addresses, we'll use a combined size of
|
||||
$t\kappa$ bytes for the address and delay, where $t$ is small (_e.g.,_ $t = 2$
|
||||
or $3$).
|
||||
|
||||
- **Delay**: 2 bytes
|
||||
Allows delays up to 65,535 milliseconds ≈ 65 seconds.
|
||||
- **Address**: $t\kappa-2$ bytes
|
||||
This flexible format can accommodate various address types, including:
|
||||
- libp2p multiaddress (variable length, typically 32-64 bytes).
|
||||
- Custom format with:
|
||||
- IP address (IPv4 or IPv6, 4 or 16 bytes)
|
||||
- TCP/UDP port number (2 bytes)
|
||||
- QUIC/TLS protocol identifier flag (1 byte)
|
||||
- Peer ID (32 bytes for Ed25519 or Secp256k1).
|
||||
|
||||
The entire Sphinx packet header ($α$, $β$, and $γ$) can fit within a fixed size
|
||||
of $32 + (r(t+1)+1)\kappa + 16 = 384$ bytes, leaving ample room for a large $δ$ of
|
||||
up to $2413 - 384 = 2029$ bytes.
|
||||
|
||||
#### 4.3 Message Format
|
||||
|
||||
The Mix protocol uses the Sphinx packet format to encapsulate messages and
|
||||
routing information.
|
||||
|
||||
```proto
|
||||
message SphinxPacket {
|
||||
bytes alpha = 1; // 32 bytes
|
||||
bytes beta = 2; // 304 - 384 bytes
|
||||
bytes gamma = 3; // 16 bytes
|
||||
bytes delta = 4; // variable size, max 2029 bytes
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Handler Function
|
||||
|
||||
The [handler function](https://docs.libp2p.io/concepts/fundamentals/protocols/#handler-functions)
|
||||
is responsible for processing connections and messages for
|
||||
the Mix protocol. It operates according to the mix node roles (_i.e.,_ sender,
|
||||
intermediary mix node, or exit node) defined in
|
||||
[Section 2.1](#21-mix-nodes-roles). This function is crucial for implementing
|
||||
the core functionality of the mixnet protocol within the libp2p framework.
|
||||
|
||||
When a node receives a new stream for the `"/mix/1.0.0"` protocol, the handler
|
||||
function is invoked. It performs different operations based on the node's role
|
||||
in the current message path:
|
||||
|
||||
- **Role Determination**
|
||||
|
||||
The handler first determines the node's role for the incoming message. This
|
||||
is typically done by examining the packet structure and the node's position
|
||||
in the network.
|
||||
|
||||
- **Packet Processing**
|
||||
|
||||
Depending on the role, the handler processes the Sphinx packet differently:
|
||||
|
||||
- For senders, it creates and sends new Sphinx packets.
|
||||
- For intermediary nodes, it processes and forwards existing packets.
|
||||
- For exit nodes, it decrypts the final layer and disseminates the original message.
|
||||
|
||||
- **Error Handling**
|
||||
|
||||
It manages any errors that occur during packet processing, such as invalid
|
||||
MACs or decryption failures.
|
||||
|
||||
- **Logging and Metrics**
|
||||
|
||||
The handler is also be responsible for logging important events and
|
||||
collecting metrics for network analysis and debugging.
|
||||
|
||||
The specific implementation of the handler function for each role (_i.e.,_
|
||||
sender, intermediary, and exit node) is detailed in the following subsections.
|
||||
|
||||
#### 5.1 Sender
|
||||
|
||||
1. **Convert the libp2p Message to Bytes**
|
||||
|
||||
Serialize the libp2p message to bytes and store the result in
|
||||
`libp2p_message`. This can be done using Protocol Buffers or another
|
||||
serialization method.
|
||||
|
||||
2. **Apply Spam Protection**
|
||||
|
||||
Apply the chosen spam protection mechanism to the `libp2p_message`.
|
||||
This could be Proof of Work (PoW), Verifiable Delay Function (VDF),
|
||||
Rate Limiting Nullifier (RLN), or other suitable approaches.
|
||||
|
||||
Refer to [Appendix A](#appendix-a-example-spam-protection-using-proof-of-work)
|
||||
for details on the current implementation using PoW.
|
||||
|
||||
3. **Prepare the Message**
|
||||
|
||||
Prepare the `message` by combining the `libp2p_message` with any necessary data
|
||||
from the spam protection mechanism. The exact format of `message` will depend
|
||||
on the chosen spam protection method.
|
||||
|
||||
Note: The spam protection mechanism is designed as a pluggable interface,
|
||||
allowing for different methods to be implemented based on network requirements.
|
||||
This flexibility extends to other components such as peer discovery and incentivization,
|
||||
which are not specified in detail to allow for future optimizations and adaptations.
|
||||
|
||||
4. **Perform Path Selection** (refer [Section 2.4](#24-node-discovery))
|
||||
|
||||
- Let the Ed25519 public keys of the mix nodes in the path be
|
||||
$y_0,\ y_1,\ \ldots,\ y_{L-1}$.
|
||||
|
||||
5. **Wrap Final Message in Sphinx Packet**
|
||||
Perform the following steps to wrap `message` in a Sphinx packet:
|
||||
|
||||
a. **Compute** **Alphas ($α_i$**, **$i=0$** to **$L-1$)**
|
||||
|
||||
- Select a random exponent $x$ from $\mathbb{Z}_q^*$.
|
||||
- Compute initial alpha $α_0$, shared secret $s_0$, and blinding factor $b_0$:
|
||||
- $α_0 = g^x$ using Curve25519 scalar multiplication.
|
||||
- $s_0 = y_0^x$, where $y_0$ is the public key of the first hop.
|
||||
- $b_0 = H(α_0\ |\ s_0)$, where $H$ is the SHA-256 hash function (refer
|
||||
_[Section 3](#3-cryptographic-primitives-and-security-parameter)_ for details).
|
||||
- For each node $i$ (from $1$ to $L-1$):
|
||||
- $α_i = α_{i-1}^{b_{i-1}}$ using Curve25519 scalar multiplication.
|
||||
- $s_i = y_{i}^{x\prod_{\text{j=0}}^{\text{i-1}} b_{j}}$, where $y_{i}$ is
|
||||
the public key of the i-th hop.
|
||||
- $b_i = H(α_i\ |\ s_i)$, where $H$ is the SHA-256 hash function.
|
||||
|
||||
Note that $\alpha_i$ and $s_i$ are group elements, each 32 bytes long.
|
||||
|
||||
b. **Compute** **Filler Strings ($\phi_i$**, **$i=0$** to **$L-1$)**
|
||||
|
||||
- Initialize $\phi_0$ as an empty string.
|
||||
- For each $i$ (from $1$ to $L-1$):
|
||||
|
||||
- Derive the AES key and IV:
|
||||
|
||||
$`\text{φ\_aes\_key}_{i-1} = KDF(\text{"aes\_key"}\ |\ s_{i-1})`$
|
||||
|
||||
$`\text{φ\_iv}_{i-1} = H(\text{"iv"}\ |\ s_{i-1})`$ (truncated to 128 bits)
|
||||
|
||||
- Compute the filler string $\phi_i$ using $\text{AES-CTR}^\prime_i$,
|
||||
which is AES-CTR encryption with the keystream starting from
|
||||
index $((t+1)(r-i)+t+2)\kappa$ :
|
||||
|
||||
$`\phi_i = \text{AES-CTR}^\prime_i(\text{φ\_aes\_key}_{i-1},\ \text{φ\_iv}_{i-1},
|
||||
\ \phi_{i-1}\ |\ 0_{(t+1)\kappa})`$,
|
||||
where $0_{(t+1)\kappa}$ is the string of $0$ bits of length $(t+1)\kappa$.
|
||||
|
||||
Note that the length of $\phi_i$ is $(t+1)i\kappa$.
|
||||
|
||||
c. **Compute** **Betas and Gammas ($\beta_i$**, $\gamma_i$, **$i=0$** to **$L-1$)**
|
||||
|
||||
For each $i$ (from $L-1$ to $0$):
|
||||
|
||||
- Derive the AES key, MAC key, and IV:
|
||||
|
||||
$`\text{β\_aes\_key}_{i} = KDF(\text{"aes\_key"}\ |\ s_{i})`$
|
||||
|
||||
$`\text{mac\_key}_{i} = KDF(\text{"mac\_key"}\ |\ s_{i})`$
|
||||
|
||||
$`\text{β\_iv}_{i} = H(\text{"iv"}\ |\ s_{i})`$ (truncated to 128 bits)
|
||||
|
||||
- Generate random $`\text{delay\_i}`$, a 16-bit unsigned integer (0-65535 milliseconds).
|
||||
|
||||
Note that top-level applications can use other probability distributions,
|
||||
such as an exponential distribution, where shorter delays are more likely
|
||||
than longer delays. This can mimic real-world traffic patterns and provide
|
||||
robust anonymity against traffic analysis. The trade-off lies in balancing
|
||||
the need for flexible delay handling with the risk of exposing
|
||||
application-specific traffic patterns.
|
||||
|
||||
- If $i = L-1$ (_i.e.,_ exit node):
|
||||
|
||||
$`\beta_i = \text{AES-CTR}(\text{β\_aes\_key}_{i},\ \text{β\_iv}_{i},\ 0_{((t+1)
|
||||
(r-L)+t+2)\kappa})\ |\ \phi_{L-1}`$
|
||||
|
||||
- Otherwise (_i.e.,_ intermediary node):
|
||||
|
||||
$`\beta_i = \text{AES-CTR}(\text{β\_aes\_key}_{i},\ \text{β\_iv}_{i},\ \text
|
||||
{addr}_{i+1} \ |\ \text{delay}_{i+1}\ | \ \gamma_{i+1}\ |\ {\beta_{i+1}}_
|
||||
{[0\ldots(r(t+1)-t)\kappa−1]})`$
|
||||
|
||||
Note that the length of $\beta_i$ is $(r(t+1)+1)\kappa$, $0 \leq i \leq L-1$,
|
||||
where $t$ is the combined length of next hop address and delay.
|
||||
|
||||
- $`\gamma_i = \text{HMAC-SHA-256}(\text{mac\_key}_i,\ β_i)`$\
|
||||
Note that the length of $\gamma_i$ is $\kappa$.
|
||||
|
||||
d. **Compute** **Deltas (**$\delta_i$, **$i=0$** to **$L-1$)**
|
||||
|
||||
For each $i$ (from $L-1$ to $0$):
|
||||
|
||||
- Derive the AES key and IV:
|
||||
|
||||
$`\text{δ\_aes\_key}_{i} = KDF(\text{"δ\_aes\_key"}\ |\ s_{i})`$
|
||||
|
||||
$`\text{δ\_iv}_{i} = H(\text{"δ\_iv"}\ |\ s_{i})`$ (truncated to 128 bits)
|
||||
|
||||
- If $i = L-1$ (_i.e.,_ exit node):
|
||||
|
||||
$`\delta_i = \text{AES-CTR}(\text{δ\_aes\_key}_{i},\ \text{δ\_iv}_{i},
|
||||
\ 0_{\kappa}\ |\ m)`$, where $m$ is the `message`.
|
||||
|
||||
- Otherwise (_i.e.,_ intermediary node):
|
||||
|
||||
$`\delta_i = \text{AES-CTR}(\text{δ\_aes\_key}_{i},\ \text{δ\_iv}_{i},\ \delta_{i+1})`$
|
||||
|
||||
Note that the length of $\delta$ is $|m| + \kappa$.
|
||||
|
||||
Given that the derived size of $\delta$ is $2029$ bytes, this allows
|
||||
`message` to be of length $2029-16 = 2013$ bytes. This means smaller
|
||||
messages may need to be padded up to $2013$ bytes (e.g., using PKCS#7
|
||||
padding).
|
||||
|
||||
e. **Construct Final Sphinx Packet**
|
||||
|
||||
- Initialize header
|
||||
|
||||
```pseudocode
|
||||
alpha = alpha_0 // 32 bytes
|
||||
beta = beta_0 // $(r(t+1)+1)\kappa$ bytes
|
||||
gamma = gamma_0 // 16 bytes
|
||||
```
|
||||
|
||||
As discussed earlier, for a maximum path length of $r = 5$, and combined
|
||||
length of address and delay $t = 3\kappa = 48$ bytes, the header size is
|
||||
just $384$ bytes.
|
||||
- Initialize payload
|
||||
|
||||
`delta = delta_0 // variable size, max 2029 bytes`
|
||||
|
||||
For a fixed Sphinx packet size of $2413$ bytes and given the header length
|
||||
of $384$ bytes, `delta` size is $2029$ bytes.
|
||||
|
||||
6. **Serialize the Sphinx Packet** using Protocol Buffers.
|
||||
|
||||
7. **Send the Serialized Packet** to the first mix node using the
|
||||
`"/mix/1.0.0"` protocol.
|
||||
|
||||
#### 5.2 Intermediary Mix Node
|
||||
|
||||
Let $`x_i \in \mathbb{Z}_q^*`$ be the intermediary node’s private key
|
||||
corresponding to the public key $y_i \in G^*$. It performs the following steps
|
||||
to relay a message:
|
||||
|
||||
1. **Receive and Deserialize** the Sphinx packet using Protocol Buffers.
|
||||
2. **Compute Shared Secret $s = \alpha^{x_{i}}$**.
|
||||
|
||||
3. **Check If Previously Seen**
|
||||
|
||||
a. Compute tag $H(s)$, where $H$ is the SHA-256 hash function.
|
||||
|
||||
b. If tag is in the previously seen list of tags, discard the message.
|
||||
|
||||
c. This list can be reset whenever the node rotates its private key
|
||||
|
||||
4. **Compute MAC**
|
||||
|
||||
a. Derive MAC key
|
||||
|
||||
$`\text{mac\_key} = KDF(\text{"mac\_key"}\ |\ s)`$
|
||||
|
||||
b. Check if $`\gamma = \text{HMAC-SHA-256}(\text{mac\_key},\ β)`$ . If not,
|
||||
discard the message.
|
||||
|
||||
c. Otherwise, store tag $H(s)$ in the list of seen message tags.
|
||||
|
||||
5. **Decrypt One Layer**
|
||||
|
||||
a. Derive the AES key, MAC key, and IV:
|
||||
|
||||
$`\text{β\_aes\_key} = KDF(\text{"aes\_key"}\ |\ s)`$
|
||||
|
||||
$`\text{β\_iv} = H(\text{"iv"}\ |\ s)`$ (truncated to 128 bits)
|
||||
|
||||
b. Compute
|
||||
$`B = \text{AES-CTR}(\text{β\_aes\_key},\ \text{β\_iv},\ \beta\ |\ 0_{(t+1)k})`$.
|
||||
|
||||
c. Uniquely parse prefix of $B$
|
||||
|
||||
If $B$ has a prefix of **$0_{((t+1)(r-L)+t+2)\kappa}$,** the current node is the
|
||||
exit node (refer exit node operations below).
|
||||
|
||||
Otherwise, it is an intermediary node and it performs the followings steps
|
||||
to relay the message.
|
||||
|
||||
d. **Extract Routing Information**
|
||||
|
||||
$`\text{next\_hop} = B_{[0\ldots(t\kappa-17)]}`$ (first $t\kappa-2$ bytes).
|
||||
|
||||
e. **Extract Delay**
|
||||
|
||||
$`\text{delay} = B_{[(t\kappa-16)\ldots(t\kappa-1)]}`$ (following $2$ bytes).
|
||||
|
||||
f. **Extract Gamma**
|
||||
|
||||
$`{\gamma}' = B_{[t\kappa\ldots(t\kappa+\kappa-1)]}`$ (following $\kappa$ bytes).
|
||||
|
||||
g. **Extract Beta**
|
||||
|
||||
$`\beta' = B_{[(t\kappa+\kappa)\ldots(r(t+1)+t+2)\kappa-1]}`$ (following
|
||||
$((t+1)r + 1)\kappa$ bytes).
|
||||
|
||||
h. **Compute Alpha**
|
||||
|
||||
- Compute blinding factor $b = H(α\ |\ s)$, where $H$ is the SHA-256 hash
|
||||
function.
|
||||
- Compute $α^′ = α^b$.
|
||||
|
||||
i. **Compute Delta**
|
||||
|
||||
- Derive the AES key and IV:
|
||||
$`\text{δ\_aes\_key} = KDF(\text{"δ\_aes\_key"}\ |\ s)`$
|
||||
$`\text{δ\_iv} = H(\text{"δ\_iv"}\ |\ s)$` (truncated to 128 bits)
|
||||
- Compute $`\delta' = \text{AES-CTR}(\text{δ\_aes\_key},\ \text{δ\_iv},\ \delta)`$
|
||||
|
||||
6. **Construct Final Sphinx Packet**
|
||||
|
||||
a. Initialize header
|
||||
|
||||
```pseudocode
|
||||
alpha = alpha' // 32 bytes
|
||||
beta = beta' // $((t+1)r + 1)\kappa$ bytes
|
||||
gamma = gamma' // 16 bytes
|
||||
```
|
||||
|
||||
b. Initialize payload
|
||||
|
||||
`delta = delta' // variable size, max 2029 bytes`
|
||||
|
||||
7. **Serialize the Sphinx Packet** using Protocol Buffers.
|
||||
8. **Introduce A Delay** of $`\text{delay}`$ milliseconds.
|
||||
9. **Send the Serialized Packet** to $`\text{next\_hop}`$ using the
|
||||
`"/mix/1.0.0"` protocol.
|
||||
|
||||
#### 5.3 Exit Node
|
||||
|
||||
1. **Perform _Steps i. to v. b._ Above**, similar to an intermediary node. If
|
||||
$B$ has a prefix of $0_{((t+1)(r-L)+t+2)\kappa}$ (in _step 5. c._ above), the
|
||||
current node is the exit node. It performs the following steps to
|
||||
disseminate the message via the respective libp2p protocol.
|
||||
|
||||
2. **Compute Delta**
|
||||
|
||||
- Derive the AES key and IV:
|
||||
|
||||
$`\text{δ\_aes\_key} = KDF(\text{"δ\_aes\_key"}\ |\ s)`$
|
||||
|
||||
$`\text{δ\_iv} = H(\text{"δ\_iv"}\ |\ s)`$ (truncated to 128 bits)
|
||||
|
||||
- Compute $`\delta' = \text{AES-CTR}(\text{δ\_aes\_key},\ \text{δ\_iv},\ \delta)`$.
|
||||
|
||||
3. **Extract Message**
|
||||
|
||||
$m = \delta'_{[\kappa\ldots]}$ (remove first $\kappa$ bytes).
|
||||
|
||||
4. **Remove Any Padding** from $m$ to obtain the `message` including any
|
||||
necessary spam protection data.
|
||||
|
||||
5. **Verify Spam Protection**
|
||||
|
||||
Verify the spam protection mechanism applied to the `message`.
|
||||
If the verification fails, discard the `message`.
|
||||
Refer to [Appendix A](#appendix-a-example-spam-protection-using-proof-of-work)
|
||||
for details on the current implementation using PoW.
|
||||
|
||||
6. **Deserialize the extracted message** using the respective libp2p protocol's
|
||||
definition.
|
||||
|
||||
7. **Disseminate the message** via the respective libp2p protocol (_e.g.,_
|
||||
GossipSub).
|
||||
|
||||
## Copyright
|
||||
|
||||
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
|
||||
|
||||
## References
|
||||
|
||||
### Normative
|
||||
|
||||
[Handler function](https://docs.libp2p.io/concepts/fundamentals/protocols/#handler-functions)
|
||||
[libp2p](https://libp2p.io)\
|
||||
[Sphinx](https://cypherpunks.ca/~iang/pubs/Sphinx_Oakland09.pdf)
|
||||
|
||||
### Informative
|
||||
|
||||
[PoW](https://bitcoin.org/bitcoin.pdf)\
|
||||
[Sphinx packet size](https://petsymposium.org/popets/2024/popets-2024-0050.pdf)
|
||||
|
||||
## Appendix A. Example Spam Protection using Proof of Work
|
||||
|
||||
The current implementation uses a Proof of Work mechanism for spam protection.
|
||||
This section details the specific steps for attaching and verifying the PoW.
|
||||
|
||||
### Structure
|
||||
|
||||
The sender appends the PoW to the serialized libp2p message, `libp2p_message`,
|
||||
in a structured format, making it easy to parse and verify by the exit node.
|
||||
The sender includes the PoW as follows:
|
||||
|
||||
`message = <libp2p_message_bytes | timestamp | nonce>`
|
||||
|
||||
where:
|
||||
|
||||
`<libp2p_message_bytes>`: Serialized libp2p message (variable length).
|
||||
|
||||
`<timestamp>`: The current Unix timestamp in seconds (4 bytes).
|
||||
|
||||
`<nonce>`: The nonce that satisfies the PoW difficulty criterion (4 bytes).
|
||||
|
||||
**Nonce Size:** The nonce size should be large enough to ensure a sufficiently large
|
||||
search space. It should be chosen so that the range of possible nonce values
|
||||
allows for the difficulty target to be met. However, larger nonce sizes can increase
|
||||
the time and computational effort required to find a valid nonce. We use
|
||||
a 4-byte nonce to ensure sufficiently large search space with reasonable
|
||||
computational effort.
|
||||
|
||||
**Difficulty Level:** The difficulty level is usually expressed as the number of
|
||||
leading zeros required in the hash output. It is chosen such that the
|
||||
computational effort required is significant but not prohibitive.
|
||||
We recommend a reasonable difficulty level that requires around
|
||||
16-18 leading zeros in the SHA-256 hash. This would roughly take
|
||||
0.65 to 2.62 seconds to compute in a low-grade CPU,
|
||||
capable of computing 100,000 hashes per second.
|
||||
|
||||
### Calculate Proof of Work (PoW)
|
||||
|
||||
The sender performs the following steps to compute the PoW challenge and response:
|
||||
|
||||
i. **Create Challenge**
|
||||
|
||||
Retrieves the current Unix timestamp, `timestamp`, in seconds (4 bytes).
|
||||
|
||||
ii. **Find A Valid Nonce**
|
||||
|
||||
- Initializes the `nonce` to a 4-byte value (initially zero).
|
||||
|
||||
- Increments the `nonce` until the SHA-256 hash of
|
||||
`<libp2p_message_bytes | timestamp | nonce>` has at least
|
||||
18 leading zeros.
|
||||
|
||||
- The final value of the `nonce` is considered valid.
|
||||
|
||||
### Attach the PoW to the libp2p Message
|
||||
|
||||
Append the 4-byte `timestamp` and the valid `nonce` to
|
||||
the `libp2p_message_bytes` to form the `message`.
|
||||
|
||||
`message = <libp2p_message_bytes | timestamp | nonce>`
|
||||
|
||||
### Verify PoW
|
||||
|
||||
i. **Extract Timestamp and Nonce**
|
||||
|
||||
Split `message` into 4-byte `nonce` (last 4 bytes), 4-byte `timestamp`
|
||||
(the 4 bytes before the nonce), and the serialized libp2p message
|
||||
to be published, `libp2p_message_bytes` (the remaining bytes).
|
||||
|
||||
ii. **Verify Timestamp**
|
||||
|
||||
- Check the `timestamp` is within the last 5 minutes.
|
||||
|
||||
- If the timestamp is outside the acceptable window, the exit node
|
||||
discards the message.
|
||||
|
||||
iii. **Verifiy Response**
|
||||
|
||||
- Compute the SHA-256 hash of the `message` and check if the hash
|
||||
meets the difficulty requirement, _i.e._, has at least 18 leading zeros.
|
||||
|
||||
- If the hash is not valid, the exit node discards the message. Otherwise,
|
||||
it follows the steps to publish the message.
|
||||
Reference in New Issue
Block a user