Freenet Transport Protocol (FrTP)
Note: This document is a work in progress and is subject to change, it is currently out-of-sync with the codebase and should be updated to reflect the current state of the codebase once it has stabilized.
Introduction
The Freenet Transport Protocol (FrTP) is a UDP-based system designed to ensure reliable and encrypted message transmission. This document outlines the key elements of FrTP, including connection establishment, message handling, and rate limiting.
Overview
- Firewall Traversal: FrTP allows peers behind firewalls to establish direct connections.
- Security: All messages are encrypted using AES128GCM, with RSA public key exchange for connection establishment, should effectively thwart man-in-the-middle attacks.
- Streaming: Large messages can be streamed, meaning that a peer can start forwarding data before the entire message is received.
- Covert: FrTP can run on any UDP port and FrTP packets look like random data, although more sophisticated analysis of packet timing and size could be used to identify FrTP traffic. FrTP can't be port-scanned as it won't respond to packets unless encrypted with the peer's public key.
- Efficient: FrTP is designed to minimize bandwidth usage, with rate limiting and confirmation message batching.
Connection Establishment
Scenario 1: Both Peers Behind NAT
This describes how to establish one side of a two-way connection, allowing Bob to send messages to Alice. The process is symmetric in the other direction.
Actors
- Alice and Bob are both peers behind firewalls.
Terminology
Bob_public_key
: Bob's RSA public key.Bob_private_key
: Bob's RSA private key.Alice_inbound_symmetric_key
: AES128GCM symmetric key generated by Alice, used for decrypting inbound messages from Bob.hello_message(A->B)
: Message sent from Alice to Bob, containingAlice_inbound_symmetric_key
and a u16 protocol version number, encrypted usingB_public_key
.hello_ack(B->A)
: Message sent from Bob to Alice acknowledginghello_message(A->B)
, encrypted usingAlice_inbound_symmetric_key
.
Steps
-
Key Generation: Alice generates a random AES128GCM symmetric key, called
Alice_inbound_symmetric_key
. -
Outbound Hello Message: Alice encrypts
Alice_inbound_symmetric_key
withBob_public_key
and a u16 protocol version number with Peer B's public key, to createhello_message(A->B)
. -
Sending Outbound Hello: Alice repeatedly sends
hello_message(A->B)
every 200ms until ahello_ack(B->A)
from Bob is received or a 5-second timeout occurs, indicating connection failure. -
Receiving Inbound Hello: Bob receives
hello_message(A->B)
and decrypts it usingBob_private_key
. If the protocol version is not supported, then Bob sends ahello_ack(B->A)
with an error code and terminates the connection. -
Hello Acknowledgement: Upon receiving
hello_ack(B->A)
, Alice stops sendinghello_message(A->B)
and the inbound side of the connection is established. -
Unexpected Hello Messages: If Bob receives a
hello_message(A->B)
from Alice after it has already sent ahello_ack(B->A)
, then it should resend thehello_ack(B->A)
and otherwise ignore the message (this may occur if the initialhello_ack(B->A)
is lost).
Scenario 2: Peer behind NAT connects to Gateway peer
Actors
- Alice is a peer behind a firewall, Gateway isn't behind a firewall and is configured to act as a gateway peer for new peers to assimiate into the network.
Terminology
Gateway_public_key
: Gateway's RSA public key.Gateway_private_key
: Gateway's RSA private key.Alice_bidirectional_symmetric_key
: AES128GCM symmetric key generated by Alice, used for encrypting and decrypting messages to/from Gateway.
Steps
-
Key Generation: Alice generates a random AES128GCM symmetric key, called
Alice_bidrectional_symmetric_key
. -
Outbound Hello Message: Alice encrypts
Alice_bidrectional_symmetric_key
withGateway_public_key
and a u16 protocol version number with Gateway's public key, to createhello_message(A->G)
. -
Sending Outbound Hello: Alice repeatedly sends
hello_message(A->B)
every 200ms until ahello_ack(G->A)
from Gateway is received or a 5-second timeout occurs, indicating connection failure. -
Receiving Inbound Hello: Gateway receives
hello_message(A->G)
and decrypts it usingGateway_private_key
. If the protocol version is not supported, then Gateway sends ahello_ack(G->A)
with an error code and terminates the connection, otherwise it sends ahello_ack(G->A)
to Alice. -
Hello Acknowledgement: Upon receiving
hello_ack(G->A)
, Alice stops sendinghello_message(A->G)
, and the the connection is established, Alice should useAlice_bidirectional_symmetric_key
for both encryption and decryption of packets sent to and received from Gateway.
Keep-Alive Protocol
To maintain an open connection, keep_alive
messages are exchanged every 30 seconds. A connection
is terminated if a peer fails to receive any message within 120 seconds.
Symmetric Message Schema
#![allow(unused)] fn main() { pub(super) struct SymmetricMessage { pub packet_id: PacketId, pub confirm_receipt: Vec<PacketId>, pub payload: SymmetricMessagePayload, } pub(super) enum SymmetricMessagePayload { AckConnection { // if we successfully connected to a remote we attempt to connect to initially // then we return our TransportPublicKey so they can enroute other peers to us result: Result<(), Cow<'static, str>>, }, GatewayConnection { // a gateway acknowledges a connection and returns the private key to use // for communication key: [u8; 16], }, ShortMessage { payload: MessagePayload, }, StreamFragment { stream_id: StreamId, total_length_bytes: u64, // we shouldn't allow messages larger than u32, that's already crazy big fragment_number: u32, payload: MessagePayload, }, NoOp, } pub enum HelloError { UnsupportedProtocolVersion { min_supported: u16, max_supported: u16, your_version: u16 }, } }
Message Handling
Dropped and Out-of-Order Messages
- Duplicate Detection: Messages are checked for duplicate
message_id
. Duplicates trigger an immediateNoOperation
message with a reconfirmation inconfirm_receipt
. - Acknowledgement Timeout: Messages are resent if not acknowledged within 2 seconds
(
MESSAGE_CONFIRMATION_TIMEOUT
).
Confirmation Batching
- Batching Strategy: Receipts can be delayed up to 500ms (
MAX_CONFIRMATION_DELAY
) to enable batch confirmation. - Queue Management: Receipt queues exceeding 20 messages prompt immediate confirmation to prevent overflow.
Message Types
- Short Messages: Contained within a single UDP packet (up to 1kb).
- Long Messages: Split into fragments for larger payloads, enabling efficient data forwarding.
Rate Limiting
- Initial Setup: Upstream bandwidth set 50% above desired usage to allow for traffic bursts.
- Dynamic Adjustment: Future adaptations may use isotonic regression for optimizing bandwidth and packet loss balance.
- Implementation: Bandwidth monitoring over 10-second windows (
BANDWIDTH_MEASUREMENT_WINDOW
). Exceeding limits triggers a 10ms sleep (BANDWIDTH_CONTROL_SLEEP_DURATION
), with periodic reassessment.
Implementation Notes
Serialization
- Try to avoid unnecessary copies of data, especially for large messages.
- Ensure serialization format is robust against untrustedf data.
- Note that there will be nested layers of serialization, both internal to FrTP and by the FrTP user.
Consider:
Conclusion
The Freenet Transport Protocol provides a robust framework for secure and efficient data transmission. Its design considers NAT challenges, message integrity, and bandwidth management, ensuring reliable communication in various network conditions.